# $Id$ # vim: ts=2:et ################################################################ # # Copyright notice # # (c) 2013 Copyright: Martin Fischer (m_fischer at gmx dot de) # All rights reserved # # This file is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # ################################################################ package main; use strict; use warnings; use Time::Local; sub CommandNotice($$); sub notice_Confirmation($$$); sub notice_Get($$$); sub notice_List($$); sub notice_Read($$$); use vars qw(@locale); @locale = qw(de en); my $confirmationFile = ".notice-confirmation"; ######################################## sub notice_Initialize($$) { my %hash = ( Fn => "CommandNotice", Hlp => "[confirm|list|reset|view] ,view and confirmation of system messages", ); $cmds{notice} = \%hash; } ######################################## sub CommandNotice($$) { my ($cl,$param) = @_; my $modPath = (-d "updatefhem.dir" ? "updatefhem.dir":$attr{global}{modpath}); my $modDir = "$modPath/FHEM"; my $noticeDir = "$modDir/FhemUtils"; my $name = "notice"; my @commands = qw(confirm condition get list position reset view); my $ret; # split arguments my @args = split(/ +/,$param); $args[0] = "list" if(!defined($args[0])); if(!@args ||grep (m/^$args[0]$/, @commands)) { my $cmd = $args[0]; if($cmd eq "list") { # view all a list of notes my $type = "all"; if(defined($args[1])) { $type = $args[1]; } $ret = notice_List($noticeDir,$type); } elsif($cmd eq "view") { # view a single note return "notice view needs an argument" if(!defined($args[1])); my $id = $args[1]; my $notice_ref = {}; $notice_ref = notice_Read($notice_ref,$noticeDir,$args[1]); return "Nothing to view. Maybe wrong ID?" if(!keys %$notice_ref); my $locale = "en"; my $header = 1; if(@args == 3) { $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en"; $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en"; $header = ($args[2] eq "noheader") ? 0 : 1; } elsif(@args == 4) { if(grep (m/^$args[2]$/, @locale)) { $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en"; $header = ($args[3] eq "noheader") ? 0 : 1; } elsif($args[2] eq "noheader") { $header = ($args[2] eq "noheader") ? 0 : 1; $locale = (grep (m/^$args[3]$/, @locale)) ? $args[3] : "en"; } } if($header) { $ret = sprintf("%-10s: %s\n","ID",$id); $ret .= sprintf("%-10s: %s\n","From",$notice_ref->{$id}{from}) if(exists $notice_ref->{$id}{from}); $ret .= sprintf("%-10s: %s\n","Date",$notice_ref->{$id}{date}) if(exists $notice_ref->{$id}{from}); $ret .= sprintf("%-10s: %s\n","Expire",$notice_ref->{$id}{expire}) if(exists $notice_ref->{$id}{expire}); $ret .= sprintf("%-10s: %s\n","Title",$notice_ref->{$id}{locale}{$locale}{title}) if(exists $notice_ref->{$id}{locale}{$locale}{title}); $ret .= "### Start of Text\n"; } foreach my $line (@{$notice_ref->{$id}{locale}{$locale}{text}}) { $ret .= $line."\n"; } $ret .= "### End of Text\n" if($header); } elsif($cmd eq "confirm") { # confirm a note return "notice view needs an argument" if(!defined($args[1])); my $id = $args[1]; my $notice_ref = {}; $notice_ref = notice_Read($notice_ref,$noticeDir,$id); return "Nothing to view. Maybe wrong ID?" if(!keys %$notice_ref); if(!defined($notice_ref->{$id}{confirm}) || (defined($notice_ref->{$id}{confirm}) && $notice_ref->{$id}{confirm} == 0) ) { return "$id needs no confirmation."; } else { my $confirmation = 1; if(@args > 2) { shift @args; shift @args; $confirmation = "@args"; } $ret = notice_Confirmation($noticeDir,$id,$confirmation); } } elsif($cmd eq "get") { # get list of notes my $type = (defined($args[1])) ? $args[1] : "all"; my $value = (defined($args[2]) && $args[2] =~ /[0-8]/) ? $args[2] : 0; return notice_Get($noticeDir,$type,$value); } elsif($cmd eq "position") { # returns position of notice return "notice position needs an argument" if(!defined($args[1])); my $id = $args[1]; my $notice_ref = {}; $notice_ref = notice_Read($notice_ref,$noticeDir,$id); return (defined($notice_ref->{$id}{position})) ? $notice_ref->{$id}{position} : undef; } elsif($cmd eq "reset") { # reset all confirmations if(-e "$noticeDir/$confirmationFile") { if(defined($args[1] && lc($args[1]) eq "yes")) { my $cmdret = unlink "$noticeDir/$confirmationFile"; if(!$cmdret) { $ret = "an error occured while deleting file '$noticeDir/$confirmationFile': $!"; } else { $ret = "all confirmations deleted successfully."; } } else { $ret = "This command delete all confirmations.\n"; $ret .= "If you really want to do this, call 'notice reset yes'"; return $ret; } } else { $ret = "nothing to do. no confirmation exists."; } } elsif($cmd eq "condition") { # supplies a value of an embedded test return "condition view needs an argument" if(!defined($args[1])); my $id = $args[1]; my $notice_ref = {}; $notice_ref = notice_Read($notice_ref,$noticeDir,$id); return "Nothing to view. Maybe wrong ID?" if(!keys %$notice_ref); my %conditions; foreach my $key (sort %{$notice_ref->{$id}}) { my $order; if(lc($key) =~ /^key_/) { (undef,$order) = split("_",$key); if(defined($notice_ref->{$id}{"val_$order"})) { $conditions{$notice_ref->{$id}{$key}}{value} = ($notice_ref->{$id}{"val_$order"}) ? eval $notice_ref->{$id}{"val_$order"} : undef; $conditions{$notice_ref->{$id}{$key}}{condition} = (defined($notice_ref->{$id}{"con_$order"})) ? $notice_ref->{$id}{"con_$order"} : ""; Log 5, "notice id:$id condition key:".$notice_ref->{$id}{$key} . " " . "value:" .$conditions{$notice_ref->{$id}{$key}}{value} . " " . "condition:".$notice_ref->{$id}{"val_$order"}; } } } if(keys %conditions) { foreach my $key (sort keys %conditions) { Log 5, "notice id:$id condition key:$key value:$conditions{$key}{value} condition:$conditions{$key}{condition}"; $ret .= "$key:$conditions{$key}{value}:$conditions{$key}{condition}"; $ret .= "|"; } chop $ret; return $ret; } else { return undef; } } } else { return "Unknown argument $args[0]; choose one of " . join(" ", sort @commands); } return $ret; } ######################################## sub notice_List($$) { my ($noticeDir,$type) = @_; $type = ($type eq "all") ? ".*" : $type; my @dir; my $ret; if(opendir(my $DH, "$noticeDir")) { @dir = grep { /^$type-.*\d+$/ && -f "$noticeDir/$_" } readdir($DH); closedir $DH; my $notice_ref = {}; foreach my $file (@dir) { $notice_ref = notice_Read($notice_ref,$noticeDir,$file); } my @col1 = sort keys %{$notice_ref}; my $col1 = (reverse sort { $a <=> $b } map { length($_) } @col1)[0]; if(!keys %$notice_ref) { $ret = "==> nothing found"; } else { my @confirmationFile; if(open(my $FH, "<$noticeDir/$confirmationFile")) { Log 5, "notice read file: $noticeDir/$confirmationFile"; while(my $line = <$FH>) { chomp $line; push(@confirmationFile,$line); } close $FH; } foreach my $lang (sort @locale) { $ret .= "==> Language: $lang\n"; $ret .= sprintf(" %-*s %-10s %-10s %-10s %s\n",$col1,"ID","Published","Expired","Confirmed","Description"); foreach my $notice (sort keys %{$notice_ref}) { my ($dateTime,$oldConfirmation); next if(!exists $notice_ref->{$notice}{locale}{$lang}); foreach my $line (@confirmationFile) { if($line =~ /^$notice\s*/) { ($dateTime,$oldConfirmation) = $line =~ /^.*\s*(\d{4}-\d{2}-\d{2})\s\d{2}:\d{2}:\d{2}\s*(.*)$/; $dateTime = substr($dateTime,8,2).".".substr($dateTime,5,2).".".substr($dateTime,0,4); } } $ret .= sprintf(" %-*s %-10s %-10s %-10s %s\n", $col1, $notice, (defined($notice_ref->{$notice}{publish}) && $notice_ref->{$notice}{publish} ne "0") ? $notice_ref->{$notice}{publish} : "actually", (defined($notice_ref->{$notice}{expire}) && $notice_ref->{$notice}{expire} ne "0") ? $notice_ref->{$notice}{expire} : "never", ($dateTime) ? $dateTime : (defined($notice_ref->{$notice}{confirm}) && $notice_ref->{$notice}{confirm} ne "0") ? "no" : "not needed", $notice_ref->{$notice}{locale}{$lang}{title}); } $ret .= "\n"; } chomp $ret; } } else { $ret = "update could not open directory '$noticeDir': $!"; Log 1, $ret; } return $ret; } ######################################## # value: 0 = all # 1 = not confirmed # 2 = not expired # 3 = not confirmed, not expired # 4 = published # 5 = not confirmed, published # 6 = not expired, published # 7 = not confirmed, not expired, published # 8 = confirmed sub notice_Get($$$) { my ($noticeDir,$type,$value) = @_; $value = ($value) ? $value : 0; my @now = localtime(); my @dir; if(opendir(my $DH, "$noticeDir")) { my $search = ($type eq "all") ? ".*" : $type; @dir = grep { /^$search-.*\d+$/ && -f "$noticeDir/$_" } readdir($DH); closedir $DH; } else { Log 1, "notice could not open directory '$noticeDir': $!"; } my @confirmed; if($value == 1 || $value == 3 || $value == 5 || $value == 7 || $value == 8) { if(open(my $FH, "<$noticeDir/$confirmationFile")) { Log 5, "notice read file: $noticeDir/$confirmationFile"; while(my $line = <$FH>) { my ($id,undef) = split(" ",$line); if($type eq "all") { push(@confirmed,$id); } elsif($id =~ /^$type/) { push(@confirmed,$id); } } close $FH; } } if(@dir) { my $notice_ref = {}; foreach my $file (sort @dir) { $notice_ref = notice_Read($notice_ref,$noticeDir,$file); } if(!keys %$notice_ref) { return undef; } else { my $ret; if($value == 0) { # all $ret = join(",",sort @dir); } elsif($value == 1) { # not confirmed $ret = _notConfirmed($notice_ref,@confirmed); Log 5, "notice notConfirmed:$ret"; } elsif($value == 2) { # not expired $ret = _notExpired($notice_ref,@now); Log 5, "notice notExpired:$ret"; } elsif($value == 3) { # not confirmed, not expired my $notConfirmed = _notConfirmed($notice_ref,@confirmed); my $notExpired = _notExpired($notice_ref,@now); Log 5, "notice notConfirmed:$notConfirmed notExpired:$notExpired"; my @merged; foreach my $id (@dir) { push (@merged, $id) if($notConfirmed =~ /$id/ && $notExpired =~ /$id/); } $ret = join(",",sort @merged); } elsif($value == 4) { # published $ret = _published($notice_ref,@now); Log 5, "notice published:$ret"; } elsif($value == 5) { # not confirmed, published my $notConfirmed = _notConfirmed($notice_ref,@confirmed); my $published = _published($notice_ref,@now); Log 5, "notice notConfirmed:$notConfirmed published:$published"; my @merged; foreach my $id (sort @dir) { push (@merged, $id) if($notConfirmed =~ /$id/ && $published =~ /$id/); } $ret = join(",",sort @merged); } elsif($value == 6) { # not expired, published my $notExpired = _notExpired($notice_ref,@now); my $published = _published($notice_ref,@now); Log 5, "notice notExpired:$notExpired published:$published"; my @merged; foreach my $id (sort @dir) { push (@merged, $id) if($notExpired =~ /$id/ && $published =~ /$id/); } $ret = join(",",sort @merged); } elsif($value == 7) { # not confirmed, not expired, published my $notConfirmed = _notConfirmed($notice_ref,@confirmed); my $notExpired = _notExpired($notice_ref,@now); my $published = _published($notice_ref,@now); Log 5, "notice notConfirmed:$notConfirmed notExpired:$notExpired published:$published"; my @merged; foreach my $id (sort @dir) { push (@merged, $id) if($notConfirmed =~ /$id/ && $notExpired =~ /$id/ && $published =~ /$id/); } $ret = join(",",sort @merged); } elsif($value == 8) { # confirmed $ret = join(",",sort @confirmed); } return $ret; } } else { return undef; } } ######################################## sub _notConfirmed($@) { my ($notice_ref,@confirmed) = @_; my @ret; foreach my $id (sort keys %{$notice_ref}) { push(@ret,$id) if(defined($notice_ref->{$id}{confirm}) && $notice_ref->{$id}{confirm} != 0 && !grep (m/^$id$/,@confirmed)); } return join(",",@ret); } ######################################## sub _notExpired($@) { my ($notice_ref,@now) = @_; my @ret; foreach my $id (sort keys %{$notice_ref}) { my ($d,$m,$y); if(defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} =~ /\d{2}.\d{2}.\d{4}/) { $d = substr($notice_ref->{$id}{expire},0,2); $m = substr($notice_ref->{$id}{expire},3,2)-1; $y = substr($notice_ref->{$id}{expire},6,4)-1900; } push(@ret,$id) if(!defined($notice_ref->{$id}{expire}) || (defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} !~ /\d{2}.\d{2}.\d{4}/) || (defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} =~ /\d{2}.\d{2}.\d{4}/ && notice_epochDate($now[3],$now[4],$now[5]) <= notice_epochDate($d,$m,$y))); } return join(",",@ret); } ######################################## sub _published($@) { my ($notice_ref,@now) = @_; my @ret; foreach my $id (sort keys %{$notice_ref}) { my ($d,$m,$y); if(defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} =~ /\d{2}.\d{2}.\d{4}/) { $d = substr($notice_ref->{$id}{publish},0,2); $m = substr($notice_ref->{$id}{publish},3,2)-1; $y = substr($notice_ref->{$id}{publish},6,4)-1900; } push(@ret,$id) if(!defined($notice_ref->{$id}{publish}) || (defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} !~ /\d{2}.\d{2}.\d{4}/) || (defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} =~ /\d{2}.\d{2}.\d{4}/ && notice_epochDate($now[3],$now[4],$now[5]) >= notice_epochDate($d,$m,$y))); } return join(",",@ret); } ######################################## sub notice_epochDate($$$) { my ($day,$month,$year) = @_; return timelocal("0","0","0",$day,$month,$year); } ######################################## sub notice_Confirmation($$$) { my ($noticeDir,$id,$confirmation) = @_; my @file; my $confirmed = 0; my $oldConfirmation = ""; my $dateTime; my $now = TimeNow(); my $ret; if(open(my $FH, "<$noticeDir/$confirmationFile")) { Log 5, "notice read file: $noticeDir/$confirmationFile"; while(my $line = <$FH>) { chomp $line; if($line =~ /^$id\s*/) { ($dateTime,$oldConfirmation) = $line =~ /^.*\s*(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s*(.*)$/; $confirmed = 1; } push(@file,$line); } close $FH; } if($confirmed == 0) { push(@file,"$id $now $confirmation\n"); } if($oldConfirmation eq $confirmation) { $ret = "$id already confirmed on $dateTime: $oldConfirmation"; } else { if(open(my $FH, ">$noticeDir/$confirmationFile")) { Log 5, "notice write file: $noticeDir/$confirmationFile"; foreach my $line (sort @file) { if($line =~ /^$id\s*/) { print $FH "$id $now $confirmation\n"; if(!$oldConfirmation) { $ret = "$id confirmed on $now: $confirmation"; } else { $ret = "$id changed on $now: $confirmation"; } Log 1, "notice $ret"; } else { print $FH "$line\n"; } } } else { $ret = "error while writing file: $noticeDir/$confirmationFile: $!"; Log 1, "notice $ret"; } } return $ret; } ######################################## sub notice_Read($$$) { my ($notice_ref,$noticeDir,$noticeFile) = @_; my %notice = %$notice_ref if($notice_ref && ref($notice_ref) eq "HASH"); if(open(my $FH, "<$noticeDir/$noticeFile")) { Log 5, "notice read file: $noticeDir/$noticeFile"; my $key; my $value; my $locale; while(my $line = <$FH>) { chomp $line; if(uc($line) =~ /^#\s.*:.*$/ && uc($line) !~ /^#\s*NOTICE_\S{2}$/ && uc($line) !~ /^#\s*TITLE_\S{2}\s*:.*$/) { ($key,$value) = $line =~ /^#\s*(.*)\s*:\s*(.*)$/; $notice{$noticeFile}{lc($key)} = $value; } elsif (uc($line) =~ /^#\s*TITLE_\S{2}\s*:.*$/) { ($locale,$value) = $line =~ /^#\s*TITLE_(\S{2})\s*:\s*(.*)$/; $notice{$noticeFile}{locale}{lc($locale)}{title} = $value; } elsif (uc($line) =~ /^#\s*NOTICE_\S{2}$/) { ($locale) = $line =~ /^#\s*NOTICE_(\S{2})$/; } else { $locale = "EN" if(!$locale); push @{ $notice{$noticeFile}{locale}{lc($locale)}{text} }, $line; } } close $FH; } else { Log 1, "update could not open notice '$noticeDir/$noticeFile': $!"; return undef; } return \%notice; } =pod =begin html

notice

=end html 1;