01_FHEMWEB.pm: unsaved changes (Forum #32296)

git-svn-id: https://svn.fhem.de/fhem/trunk@7688 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2015-01-24 12:38:25 +00:00
parent 06fe0dd5b6
commit aab0aedc60
4 changed files with 55 additions and 33 deletions

View File

@ -7,7 +7,6 @@ use warnings;
use TcpServerUtils; use TcpServerUtils;
use HttpUtils; use HttpUtils;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw(gettimeofday);
use Errno qw(:POSIX);
######################### #########################
# Forward declaration # Forward declaration
@ -1265,8 +1264,6 @@ FW_roomOverview($)
my $class = "menu_$l1"; my $class = "menu_$l1";
$class =~ s/[^A-Z0-9]/_/gi; $class =~ s/[^A-Z0-9]/_/gi;
$class .= ($lastDefChange>$lastSavedChange) ? " changed" : ""
if($l1 eq "Save config");
# image tag if we have an icon, else empty # image tag if we have an icon, else empty
my $icoName = "ico$l1"; my $icoName = "ico$l1";
@ -1275,12 +1272,18 @@ FW_roomOverview($)
my $icon = FW_iconName($icoName) ? my $icon = FW_iconName($icoName) ?
FW_makeImage($icoName,$icoName,"icon")." " : ""; FW_makeImage($icoName,$icoName,"icon")." " : "";
if($l1 eq "Save config") {
$l1 .= '</a> <a id="saveCheck" class="changed" style="visibility:'.
(int(@structChangeHist) ? 'visible' : 'hidden').'">?</a>';
}
# Force external browser if FHEMWEB is installed as an offline app. # Force external browser if FHEMWEB is installed as an offline app.
if($l2 =~ m/.html$/ || $l2 =~ m/^http/) { if($l2 =~ m/.html$/ || $l2 =~ m/^http/) {
FW_pO "<td><div><a href=\"$l2\">$icon$l1</a></div></td>"; FW_pO "<td><div><a href=\"$l2\">$icon$l1</a></div></td>";
} else { } else {
FW_pH $l2, "$icon$l1", 1, $class; FW_pH $l2, "$icon$l1", 1, $class;
} }
FW_pO "</tr>"; FW_pO "</tr>";
} }
} }
@ -2268,13 +2271,9 @@ FW_Notify($$)
my ($ntfy, $dev) = @_; my ($ntfy, $dev) = @_;
if( $dev->{NAME} eq "global" ) { if( $dev->{NAME} eq "global" ) {
my $n = "#FHEMWEB:$ntfy->{NAME}"; my $vs = int(@structChangeHist) ? 'visible' : 'hidden';
if( grep(m/^SAVE|INITIALIZED|REREADCFG|SHUTDOWN$/, @{$dev->{CHANGED}}) ) { FW_directNotify( "#FHEMWEB:$ntfy->{NAME}",
FW_directNotify($n, '$(".menu_Save_config").removeClass("changed")', ''); "\$('#saveCheck').css('visibility','$vs')", '');
} elsif( grep(m/^DEFINED|MODIFIED|DELETED|ATTR|DELETEATTR$/,
@{$dev->{CHANGED}}) ) {
FW_directNotify($n, '$(".menu_Save_config").addClass("changed")', '');
}
} }
my $h = $ntfy->{inform}; my $h = $ntfy->{inform};

View File

@ -5,7 +5,6 @@ package main;
use strict; use strict;
use warnings; use warnings;
use IO::Socket; use IO::Socket;
use Errno qw(:POSIX);
sub sub
TcpServer_Open($$$) TcpServer_Open($$$)
@ -57,7 +56,7 @@ TcpServer_Accept($$)
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my @clientinfo = $hash->{SERVERSOCKET}->accept(); my @clientinfo = $hash->{SERVERSOCKET}->accept();
if(!@clientinfo) { if(!@clientinfo) {
Log3 $name, 1, "Accept failed ($name: $!)" if($! != EAGAIN()); Log3 $name, 1, "Accept failed ($name: $!)" if($! != EAGAIN);
return undef; return undef;
} }
$hash->{CONNECTS}++; $hash->{CONNECTS}++;

View File

@ -219,7 +219,7 @@ use vars qw(%readyfnlist); # devices which want a "readyfn"
use vars qw(%selectlist); # devices which want a "select" use vars qw(%selectlist); # devices which want a "select"
use vars qw(%value); # Current values, see commandref.html use vars qw(%value); # Current values, see commandref.html
use vars qw($lastDefChange); # number of last def/attr change use vars qw($lastDefChange); # number of last def/attr change
use vars qw($lastSavedChange); # will be synced with lastDefChange on save use vars qw(@structChangeHist); # Contains the last 10 structural changes
use vars qw($cmdFromAnalyze); # used by the warnings-sub use vars qw($cmdFromAnalyze); # used by the warnings-sub
my $AttrList = "verbose:0,1,2,3,4,5 room group comment alias ". my $AttrList = "verbose:0,1,2,3,4,5 room group comment alias ".
@ -242,7 +242,6 @@ my @cmdList; # Remaining commands in a chain. Used by sleep
$init_done = 0; $init_done = 0;
$lastDefChange = 0; $lastDefChange = 0;
$lastSavedChange = 0;
$readytimeout = ($^O eq "MSWin32") ? 0.1 : 5.0; $readytimeout = ($^O eq "MSWin32") ? 0.1 : 5.0;
@ -508,7 +507,6 @@ foreach my $d (keys %defs) {
} }
} }
$lastSavedChange = $lastDefChange;
DoTrigger("global", "INITIALIZED", 1); DoTrigger("global", "INITIALIZED", 1);
$fhem_started = time; $fhem_started = time;
@ -1234,7 +1232,7 @@ CommandRereadCfg($$)
$defs{$name} = $selectlist{$name} = $cl if($name && $name ne "__anonymous__"); $defs{$name} = $selectlist{$name} = $cl if($name && $name ne "__anonymous__");
$inform{$name} = $informMe if($informMe); $inform{$name} = $informMe if($informMe);
$lastSavedChange = $lastDefChange; @structChangeHist = ();
DoTrigger("global", "REREADCFG", 1); DoTrigger("global", "REREADCFG", 1);
$init_done = 1; $init_done = 1;
@ -1327,6 +1325,12 @@ CommandSave($$)
{ {
my ($cl, $param) = @_; my ($cl, $param) = @_;
if($param eq "?") {
return "No structural changes." if(!@structChangeHist);
return "Last 10 structural changes:\n ".join("\n ", @structChangeHist);
}
@structChangeHist = ();
DoTrigger("global", "SAVE", 1); DoTrigger("global", "SAVE", 1);
my $ret = WriteStatefile(); my $ret = WriteStatefile();
@ -1413,7 +1417,6 @@ CommandSave($$)
$ret .= "$key: $!" if(!close($fh{$key})); $ret .= "$key: $!" if(!close($fh{$key}));
} }
$lastSavedChange = $lastDefChange;
return ($ret ? $ret : "Wrote configuration to $param"); return ($ret ? $ret : "Wrote configuration to $param");
} }
@ -1619,9 +1622,9 @@ CommandDefine($$)
$modules{$m}{NotifyOrderPrefix} : "50-") . $name; $modules{$m}{NotifyOrderPrefix} : "50-") . $name;
} }
%ntfyHash = (); %ntfyHash = ();
addStructChange("define", $name, $def);
DoTrigger("global", "DEFINED $name", 1) if($init_done); DoTrigger("global", "DEFINED $name", 1) if($init_done);
} }
$lastDefChange++ if(!$hash{TEMPORARY});
return $ret; return $ret;
} }
@ -1647,11 +1650,11 @@ CommandModify($$)
if($ret) { if($ret) {
$hash->{DEF} = $hash->{OLDDEF}; $hash->{DEF} = $hash->{OLDDEF};
} else { } else {
addStructChange("modify", $a[0], $def);
DoTrigger("global", "MODIFIED $a[0]", 1) if($init_done); DoTrigger("global", "MODIFIED $a[0]", 1) if($init_done);
} }
delete($hash->{OLDDEF}); delete($hash->{OLDDEF});
$lastDefChange++ if(!$hash->{TEMPORARY});
return $ret; return $ret;
} }
@ -1714,7 +1717,7 @@ CommandDelete($$)
my ($cl, $def) = @_; my ($cl, $def) = @_;
return "Usage: delete <name>$namedef\n" if(!$def); return "Usage: delete <name>$namedef\n" if(!$def);
my (@rets, $isReal); my @rets;
foreach my $sdev (devspec2array($def)) { foreach my $sdev (devspec2array($def)) {
if(!defined($defs{$sdev})) { if(!defined($defs{$sdev})) {
push @rets, "Please define $sdev first"; push @rets, "Please define $sdev first";
@ -1732,7 +1735,6 @@ CommandDelete($$)
next; next;
} }
$isReal = 1 if(!$defs{$sdev}{TEMPORARY});
# Delete releated hashes # Delete releated hashes
foreach my $p (keys %selectlist) { foreach my $p (keys %selectlist) {
@ -1745,13 +1747,13 @@ CommandDelete($$)
if($readyfnlist{$p} && $readyfnlist{$p}{NAME} eq $sdev); if($readyfnlist{$p} && $readyfnlist{$p}{NAME} eq $sdev);
} }
delete($attr{$sdev});
my $temporary = $defs{$sdev}{TEMPORARY}; my $temporary = $defs{$sdev}{TEMPORARY};
delete($defs{$sdev}); # Remove the main entry addStructChange("delete", $sdev, $sdev) if(!$temporary);
delete($attr{$sdev});
delete($defs{$sdev});
DoTrigger("global", "DELETED $sdev", 1) if(!$temporary); DoTrigger("global", "DELETED $sdev", 1) if(!$temporary);
} }
$lastDefChange++ if($isReal);
return join("\n", @rets); return join("\n", @rets);
} }
@ -1764,7 +1766,7 @@ CommandDeleteAttr($$)
my @a = split(" ", $def, 2); my @a = split(" ", $def, 2);
return "Usage: deleteattr <name> [<attrname>]\n$namedef" if(@a < 1); return "Usage: deleteattr <name> [<attrname>]\n$namedef" if(@a < 1);
my (@rets, $isReal); my @rets;
foreach my $sdev (devspec2array($a[0])) { foreach my $sdev (devspec2array($a[0])) {
if(!defined($defs{$sdev})) { if(!defined($defs{$sdev})) {
@ -1784,21 +1786,20 @@ CommandDeleteAttr($$)
next; next;
} }
$isReal = 1 if(!$defs{$sdev}{TEMPORARY});
if(@a == 1) { if(@a == 1) {
delete($attr{$sdev}); delete($attr{$sdev});
addStructChange("deleteAttr", $sdev, $def);
DoTrigger("global", "DELETEATTR $sdev", 1) if($init_done); DoTrigger("global", "DELETEATTR $sdev", 1) if($init_done);
} else { } else {
delete($attr{$sdev}{$a[1]}) if(defined($attr{$sdev})); delete($attr{$sdev}{$a[1]}) if(defined($attr{$sdev}));
addStructChange("deleteAttr", $sdev, $def);
DoTrigger("global", "DELETEATTR $sdev $a[1]", 1) if($init_done); DoTrigger("global", "DELETEATTR $sdev $a[1]", 1) if($init_done);
} }
} }
$lastDefChange++ if($isReal);
return join("\n", @rets); return join("\n", @rets);
} }
@ -2101,8 +2102,8 @@ CommandRename($$)
CallFn($new, "RenameFn", $new,$old);# ignore replies CallFn($new, "RenameFn", $new,$old);# ignore replies
addStructChange("rename", $new, $param);
DoTrigger("global", "RENAMED $old $new", 1); DoTrigger("global", "RENAMED $old $new", 1);
$lastDefChange++ if(!$defs{$new}{TEMPORARY});
return undef; return undef;
} }
@ -2239,7 +2240,7 @@ sub
CommandAttr($$) CommandAttr($$)
{ {
my ($cl, $param) = @_; my ($cl, $param) = @_;
my ($ret, $isReal, @a); my ($ret, @a);
@a = split(" ", $param, 3) if($param); @a = split(" ", $param, 3) if($param);
@ -2315,8 +2316,6 @@ CommandAttr($$)
next; next;
} }
$isReal = 1 if(!$defs{$sdev}{TEMPORARY});
$a[0] = $sdev; $a[0] = $sdev;
$ret = CallFn($sdev, "AttrFn", "set", @a); $ret = CallFn($sdev, "AttrFn", "set", @a);
if($ret) { if($ret) {
@ -2326,6 +2325,12 @@ CommandAttr($$)
my $val = $a[2]; my $val = $a[2];
$val = 1 if(!defined($val)); $val = 1 if(!defined($val));
addStructChange("attr", $sdev, $param)
if(!($attr{$sdev} &&
defined($attr{$sdev}{$attrName}) &&
$attr{$sdev}{$attrName} eq $val));
$attr{$sdev}{$attrName} = $val; $attr{$sdev}{$attrName} = $val;
if($attrName eq "IODev") { if($attrName eq "IODev") {
@ -2342,7 +2347,6 @@ CommandAttr($$)
} }
$lastDefChange++ if($isReal);
Log 3, join(" ", @rets) if(!$cl && @rets); Log 3, join(" ", @rets) if(!$cl && @rets);
return join("\n", @rets); return join("\n", @rets);
} }
@ -4075,5 +4079,18 @@ setKeyValue($$)
return FileWrite($fName, @new); return FileWrite($fName, @new);
} }
sub
addStructChange($$$)
{
return if(!$init_done);
my ($cmd, $dev, $param) = @_;
return if(!$defs{$dev} || $defs{$dev}{TEMPORARY});
$lastDefChange++;
shift @structChangeHist if(@structChangeHist > 10);
$param = substr($param, 0, 40)."..." if(length($param) > 40);
push @structChangeHist, "$cmd $param";
}
1; 1;

View File

@ -112,6 +112,13 @@ FW_jqueryReadyFn()
}); });
*/ */
$("#saveCheck")
.css("cursor", "pointer")
.click(function(){
FW_cmd(FW_root+"?cmd=save ?&XHR=1", function(data) {
FW_okDialog('<pre>'+data+'</pre>');
});
});
} }