Removed /usr/local from CDPATH
[clearscm.git] / maps / bin / detail.cgi
index b90f13d..0ecf224 100755 (executable)
@@ -1,5 +1,6 @@
 #!/usr/bin/perl
-#################################################################################
+################################################################################
+#
 # File:         $RCSfile: detail.cgi,v $
 # Revision:     $Revision: 1.1 $
 # Description:  Displays list of email addresses based on report type.
@@ -8,36 +9,39 @@
 # Modified:     $Date: 2013/06/12 14:05:47 $
 # Language:     perl
 #
-# (c) Copyright 2000-2006, Andrew@DeFaria.com, all rights reserved.
+# (c) Copyright 2000-2021, Andrew@DeFaria.com, all rights reserved.
 #
-################################################################################use strict;
+################################################################################
+use strict;
 use warnings;
 
 use MIME::Words qw(:all);
+
+use CGI qw(:standard *table start_td end_td start_Tr end_Tr start_div end_div);
+use CGI::Carp 'fatalsToBrowser';
+
 use FindBin;
-$0 = $FindBin::Script;
 
-use lib $FindBin::Bin;
+local $0 = $FindBin::Script;
+
+use lib "$FindBin::Bin/../lib";
+use lib "$FindBin::Bin/../../lib";
 
 use MAPS;
 use MAPSLog;
-use MAPSUtil;
 use MAPSWeb;
-use CGI qw (:standard *table start_td end_td start_Tr end_Tr start_div end_div);
-use CGI::Carp 'fatalsToBrowser';
+use DateUtils;
 
-my $type   = param ('type');
-my $next   = param ('next');
-my $lines  = param ('lines');
-my $date   = param ('date');
+my $type  = param 'type';
+my $list  = substr $type, 0, -4 if $type =~ /list$/; 
+my $next  = param 'next';
+my $lines = param 'lines';
+my $date  = param 'date';
 
 $date ||= '';
 
-my $userid;
-my $current;
-my $last;
-my $prev;
-my $total;
+my ($userid, $current, $last, $prev, $total);
+
 my $table_name = 'detail';
 
 my %types = (
@@ -71,8 +75,19 @@ my %types = (
   ]
 );
 
-sub MakeButtons {
-  my $type = shift;
+sub formatRule($) {
+  my ($rec) = @_;
+
+  return '' unless $rec->{pattern} or $rec->{domain};
+
+  $rec->{pattern} //= '';
+  $rec->{domain}  //= '';
+
+  return "$rec->{pattern}\@$rec->{domain}";
+} # formatRule
+
+sub MakeButtons($) {
+  my ($type) = @_;
 
   my $prev_button = $prev >= 0 ?
     a ({-href      => "detail.cgi?type=$type;date=$date;next=$prev",
@@ -88,60 +103,67 @@ sub MakeButtons {
   if ($type eq 'whitelist') {
     $buttons = $buttons .
       submit ({-name    => 'action',
-               -value   => 'Blacklist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Blacklist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Nulllist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Nulllist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Reset Marks',
+               -value   => 'Reset',
                -onClick => 'return ClearAll (document.detail);'});
   } elsif ($type eq 'blacklist') {
     $buttons = $buttons .
       submit ({-name    => 'action',
-               -value   => 'Whitelist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Whitelist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Nulllist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Nulllist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Reset Marks',
+               -value   => 'Reset',
                -onClick => 'return ClearAll (document.detail);'});
   } elsif ($type eq 'nulllist') {
     $buttons = $buttons .
       submit ({-name    => 'action',
-               -value   => 'Whitelist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Whitelist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Blacklist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Blacklist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Reset Marks',
+               -value   => 'Reset',
                -onClick => 'return ClearAll (document.detail);'});
   } else {
     $buttons = $buttons .
       submit ({-name    => 'action',
-               -value   => 'Whitelist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Whitelist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Blacklist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Blacklist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Nulllist Marked',
-               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) .
+               -value   => 'Nulllist',
+               -onClick => 'return CheckAtLeast1Checked (document.detail);'}) . ' ' .
       submit ({-name    => 'action',
-               -value   => 'Reset Marks',
+               -value   => 'Reset',
                -onClick => 'return ClearAll (document.detail);'});
   } # if
 
-  return $buttons . $next_button;
+  print div {
+    -align => 'center',
+    -class => 'toolbar',
+  }, $buttons . $next_button;
+
+  return;
 } # MakeButtons
 
-sub PrintTable {
+sub Body($) {
   my ($type) = @_;
 
   my $current = $next + 1;
 
+  my ($onlist, $rec);
+
   print div {-align => 'center'}, b (
     '(' . $current . '-' . $last . ' of ' . $total . ')');
   print start_form {
@@ -149,155 +171,247 @@ sub PrintTable {
     -action => 'processaction.cgi',
     -name   => 'detail'
   };
-  print start_table ({-align        => 'center',
-                      -id           => $table_name,
-                      -border       => 0,
-                      -cellspacing  => 0,
-                      -cellpadding  => 0,
-                      -width        => '100%'}) . "\n";
 
-  my $buttons = MakeButtons $type;
+  MakeButtons $type;
+
+  print start_div {-id => 'highlightrow'};
+
+  print start_table({-align        => 'center',
+                     -id           => $table_name,
+                     -border       => 0,
+                     -cellspacing  => 0,
+                     -cellpadding  => 0,
+                     -width        => '100%'}) . "\n";
 
-  print start_div {-class => 'toolbar'};
   print
     Tr [
-      td {-class  => 'tablebordertopleft',
-          -valign => 'middle'},
-      td {-class  => 'tablebordertopright',
-          -valign => 'middle',
-          -align  => 'center'}, $buttons,
+      th {-class => 'tablebordertopleft'},  '',
+      th {-class => 'tableborder'},         'Sender',
+      th {-class => 'tableborder'},         'List',
+      th {-class => 'tableborder'},         'Hit Count',
+      th {-class => 'tableborder'},         'Rule',
+      th {-class => 'tableborder'},         'Retention',
+      th {-class => 'tablebordertopright'}, 'Comment/Date',
     ];
-  print end_div;
 
-  foreach my $sender (ReturnSenders $userid, $type, $next, $lines, $date) {
-    my @msgs = ReturnMessages $userid, $sender;
-    my @msgs2 = @msgs;
+  my @senders = ReturnSenders(
+    userid   => $userid,
+    type     => $type,
+    start_at => $next,
+    lines    => $lines,
+    date     => $date
+  );
+
+  for my $sender (@senders) {
+    my $msgs = ReturnMessages(
+      userid => $userid,
+      sender => $sender,
+    );
+
+    my $leftclass    = 'tableleftdata';
+    my $dataclass    = 'tabledata';
+    my $rightclass   = 'tablerightdata';
+    my $senderclass  = 'sender';
+    my $subjectclass = 'subject';
+
+    # Check to see if this is the last line
+    if ((($next + 1) % $lines) == (@senders % $lines)) {
+      # We always "bottom" the first column
+      $leftclass = 'tablebottomleft';
+
+      # Check to see if there any message lines to display
+      unless (@$msgs) {
+        $dataclass   = 'tablebottomdata';
+        $rightclass  = 'tablebottomright';
+        $senderclass = 'senderbottom';
+      } # unless
+    } # if
+
+    # This is for the purposes of supplying a subject line if the mailto address
+    # is clicked on. It's kludgy because we are simply grabbing the subject line
+    # of the first email sent where there may be many emails from this sender
+    # Still it is often the right subject (or a good enough one)
+    #
+    # A little tricky here because of transliteration. If I test for
+    # $msg->[0]{subject} when $msg->[0] is essentially empty I create the hash
+    # making it non empty. Therefore I need to first test if $msgs->[0] exists
+    # first.
+    my $heading = '';
+
+    if ($msgs->[0]) {
+      $heading = $msgs->[0]{subject} if $msgs->[0]{subject};
+    } # if
+
+    ($onlist, $rec) = OnWhitelist($sender, $userid, 0);
+
+    unless ($onlist) {
+      ($onlist, $rec) = OnBlacklist($sender, 0);
+
+      unless ($onlist) {
+        ($onlist, $rec) = OnNulllist($sender, 0);
+      } # unless
+    } # unless
 
     $next++;
-    print
-      start_Tr {-valign => 'middle'};
-    print
-      td {-class => 'tableborder'}, small ($next,
-        checkbox {-name  => "action$next",
-                  -label => ''}),
-          hidden ({-name     => "email$next",
-                   -default => $sender});
-    print
-      start_td {-align => 'left'};
-    print
-      start_table {-class       => 'tablerightdata',
-                   -cellpadding => 2,
-                   -callspacing => 0,
-                   -border      => 0,
-                   -width       => '100%',
-                   -bgcolor     => '#d4d0c8'};
-    print
-      td {-class   => 'tablelabel',
-          -valign  => 'middle',
-          -width   => '40'}, 'Sender:',
-      td {-class   => 'sender',
-          -valign  => 'middle'},
-        a {-href   => "mailto:$sender?subject=$msgs2[0][0]"}, $sender;
-    print
-      end_table;
-
-    my $messages = 1;
-
-    foreach (@msgs) {
-      my $msg_date = pop @{$_};
-      my $subject  = pop @{$_};
-
-      if ($date eq substr ($msg_date, 0, 10)) {
-        $msg_date = b font {-color => 'green'}, SQLDatetime2UnixDatetime $msg_date;
+
+    # Start Sender line
+    my $rowspan = @$msgs + 1;
+
+    print start_Tr {-valign => 'middle'};
+    print td {
+      -class   => $leftclass,
+      -align   => 'right',
+      -valign  => 'middle',
+      -rowspan => $rowspan,
+    }, $next,
+      checkbox {
+        -name   => "action$next",
+        -label  => '',
+        -valign => 'middle',
+      };
+
+      print hidden({
+        -name    => "email$next",
+        -default => $sender,
+      });
+
+    # Get subject line
+    $heading = "?subject=$heading" if $heading;
+
+    print td {
+      -class => $senderclass,
+    }, a {
+      -href  => "mailto:$sender$heading",
+    }, " $sender";
+
+    if ($rec) {
+      my $listlink = ($rec->{type} and $rec->{sequence}) ? "$rec->{type}:$rec->{sequence}" : '';
+
+      $rec->{comment}    //= '';
+      $rec->{retention} //= '';
+
+      print td {
+        -class => $dataclass,
+        -align => 'right',
+      }, a {
+        href  => "/maps/php/list.php?type=$rec->{type}&next=" . ($rec->{sequence} - 1),
+      }, $listlink,
+      td {
+        -class => $dataclass,
+        -align => 'right',
+      }, "$rec->{hit_count} ",
+      td {
+        -class => $dataclass,
+      }, formatRule($rec),
+      td {
+        -class => $dataclass,
+        -align => 'right',
+      }, "$rec->{retention} ",
+      td {
+        -class => $rightclass,
+      }, $rec->{comment};
+    } else {
+      # $rec will be undefined if this message will be returned
+      print td {-class => $dataclass},
+            td {-class => $dataclass},
+            td {-class => $dataclass},
+            td {-class => $dataclass},
+            td {-class => $rightclass};
+    } # if
+
+    print end_Tr;
+
+    my $msgnbr = 0;
+
+    for my $rec (@$msgs) {
+      $msgnbr++;
+
+      # We increased $next earlier so do not add 1 here
+      if (($next % $lines) == (@senders % $lines)) {
+        $dataclass    = 'tablebottomdata';
+        $rightclass   = 'tablebottomright' if $msgnbr == @$msgs;
+
+        # Only subjectbottom the last message
+        $subjectclass = 'subjectbottom' if $msgnbr == @$msgs;
+      } # if
+
+      if ($date eq substr ($rec->{timestamp}, 0, 10)) {
+        $rec->{date} = b font {-color => 'green'}, SQLDatetime2UnixDatetime $rec->{timestamp};
       } else {
-        $msg_date = SQLDatetime2UnixDatetime $msg_date;
+        $rec->{date} = SQLDatetime2UnixDatetime $rec->{timestamp};
       } # if
 
-      $subject = $subject eq '' ? '<Unspecified>' : $subject;
-      $subject = decode_mimewords ($subject);
-      $subject =~ s/\>/>/g;
-      $subject =~ s/\</&lt;/g;
+      $rec->{subject} //= '&lt;Unspecified&gt;';
+      $rec->{subject} = decode_mimewords ($rec->{subject});
+      $rec->{subject} =~ s/\>/&gt;/g;
+      $rec->{subject} =~ s/\</&lt;/g;
 
-      print
-        start_table {-class       => 'tablerightdata',
-                     -cellpadding => 2,
-                     -cellspacing => 2,
-                     -border      => 0,
-                     -width       => '100%'};
-      my $msg_nbr = $messages;
       print
         Tr [
-          td {-class   => 'msgnbr',
-              -valign  => 'middle',
-              -rowspan => 2,
-              -width   => '2%'}, $messages++,
-          td {-class   => 'tablelabel',
-              -valign  => 'middle',
-              -width   => '45'}, 'Subject:',
-          td {-class   => 'subject',
-              -valign  => 'middle',
-              -bgcolor => '#ffffff'},
-           a {-href    => "display.cgi?sender=$sender;msg_nbr=$msg_nbr"}, $subject,
-          td {-class   => 'date',
-              -width   => '130',
-              -valign  => 'middle'}, $msg_date
+          td {
+            -class   => $subjectclass,
+            -colspan => 5,
+          }, a {
+            -href    => "display.cgi?sender=$sender;msg_date=$rec->{timestamp}",
+           }, '&nbsp;&nbsp;&nbsp;&nbsp;' . $rec->{subject},
+          td {-class => $rightclass,
+              -width => '150',
+              -align => 'right'}, span {-class => 'date'}, $rec->{date} . '&nbsp',
         ];
-      print end_table;
-    } # foreach
-    print end_td;
-    print end_Tr;
-  } # foreach
+    } # for
+  } # for
 
-  print start_div {-class => 'toolbar'};
-  print
-    Tr [
-      td {-class  => 'tableborderbottomleft',
-          -valign => 'middle'},
-      td {-class  => 'tableborderbottomright',
-          -valign => 'middle'},
-      $buttons
-    ];
-  print end_div;
   print end_table;
+  print end_div;
+
+  MakeButtons $type;
+
   print end_form;
-} # PrintTable
+
+  return;
+} # Body
 
 # Main
+my $condition;
 my @scripts = ('ListActions.js');
 
-my $heading_date =$date ne '' ? ' on ' . FormatDate ($date) : '';
+my $heading_date =$date ne '' ? ' on ' . FormatDate ($date, 1) : '';
 
-$userid = Heading (
+$userid = Heading(
   'getcookie',
   '',
   (ucfirst ($type) . ' Report'),
-  $types {$type} [0],
-  $types {$type} [1] . $heading_date,
+  $types{$type} [0],
+  $types{$type} [1] . $heading_date,
   $table_name,
   @scripts
 );
 
 $userid ||= $ENV{USER};
 
-SetContext $userid;
-NavigationBar $userid;
+SetContext($userid);
+NavigationBar($userid);
 
 unless ($lines) {
-  my %options = GetUserOptions $userid;
+  my %options = GetUserOptions($userid);
   $lines = $options{'Page'};
 } # unless
 
 if ($date eq '') {
-  $condition .= "userid = '$userid' and type = '$type'";
+  $condition .= "type = '$type'";
 } else {
   my $sod = $date . ' 00:00:00';
   my $eod = $date . ' 23:59:59';
 
-  $condition .= "userid = '$userid' and type = '$type' "
-              . "and timestamp > '$sod' and timestamp < '$eod' ";
+  $condition .= "type = '$type' and timestamp > '$sod' and timestamp < '$eod'";
 } # if
 
-$total = MAPSDB::count_distinct ('log', 'sender', $condition);
+# Need to count distinct on sender
+$total = CountLogDistinct(
+  userid     => $userid,
+  column     => 'sender',
+  additional => $condition,
+);
 
 $next ||= 0;
 
@@ -306,11 +420,11 @@ $last = $next + $lines < $total ? $next + $lines : $total;
 if (($next - $lines) > 0) {
   $prev = $next - $lines;
 } else {
-  $prev = $next eq 0 ? -1 : 0;
+  $prev = $next == 0 ? -1 : 0;
 } # if
 
-PrintTable $type;
+Body($type);
 
-Footing $table_name;
+Footing($table_name);
 
 exit;