[freeside-commits] branch FREESIDE_3_BRANCH updated. 828f56957dcd4fb4409eccc8436bf935aa47a8c6

Mitch Jackson mitch at freeside.biz
Tue Oct 9 12:50:41 PDT 2018


The branch, FREESIDE_3_BRANCH has been updated
       via  828f56957dcd4fb4409eccc8436bf935aa47a8c6 (commit)
       via  b48b0eaaa748f14f8374065d97c3f0e7f2ccf1b7 (commit)
       via  354b9290110ed25ab800be47df56061b5c0c3088 (commit)
       via  ebd5b466b4717f201bc7f8d57ef0f7441e590d91 (commit)
       via  345dfc58a6230d9bc5e88c063a3fc14f3647e68d (commit)
       via  d80c2af2c03dc24ea8591459b44be0106aaf9275 (commit)
       via  4252e76a77bd4def206cfaa7466ba39bb1f027b6 (commit)
       via  ec1cb553b60314cadce24d5360de61e43c42e344 (commit)
       via  6d42808bbd3ac9c840c6259aef1dc79a0dc620ff (commit)
      from  61d5de1214428faa8bae0a50653ce8dff8d98ad8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 828f56957dcd4fb4409eccc8436bf935aa47a8c6
Author: Mitch Jackson <mitch at mitchjacksontech.com>
Date:   Tue Oct 9 14:02:15 2018 -0400

    RT# 38217 FS::msg_template, cust_main is optional to render template

diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm
index 0018a4586..13c8d9c49 100644
--- a/FS/FS/msg_template.pm
+++ b/FS/FS/msg_template.pm
@@ -239,7 +239,7 @@ Options are passed as a list of name/value pairs:
 
 =item cust_main
 
-Customer object
+Customer object (optional)
 
 =item object
 
@@ -280,13 +280,18 @@ sub prepare {
   my $cust_main = $opt{'cust_main'}; # or die 'cust_main required';
   my $object = $opt{'object'}; # or die 'object required';
 
-  # localization
-  my $locale = $cust_main->locale || '';
+  my $locale = $cust_main ? $cust_main->locale : '';
+
   warn "no locale for cust#".$cust_main->custnum."; using default content\n"
-    if $DEBUG and !$locale;
-  my $content = $self->content($cust_main->locale);
-  warn "preparing template '".$self->msgname."' to cust#".$cust_main->custnum."\n"
-    if($DEBUG);
+    if $DEBUG and $cust_main and !$locale;
+
+  my $content = $self->content( $locale );
+
+  warn sprintf(
+    "preparing template '%s' to cust#%s\n",
+    $self->msgname,
+    $cust_main ? $cust_main->custnum : 'none'
+  ) if $DEBUG;
 
   my $subs = $self->substitutions;
 
@@ -294,8 +299,11 @@ sub prepare {
   # create substitution table
   ###  
   my %hash;
-  my @objects = ($cust_main);
-  my @prefixes = ('');
+  my ( @objects, @prefixes );
+  if ( $cust_main ) {
+    @objects  = ( $cust_main );
+    @prefixes = ( '' );
+  }
   my $svc;
   if( ref $object ) {
     if( ref($object) eq 'ARRAY' ) {
@@ -407,11 +415,11 @@ sub prepare {
   my $from_addr = $self->from_addr;
 
   if ( !$from_addr ) {
+    my @agentnum = ( $cust_main->agentnum ) if $cust_main;
     if ( $opt{'from_config'} ) {
-      $from_addr = scalar( $conf->config($opt{'from_config'}, 
-                                         $cust_main->agentnum) );
+      $from_addr = scalar( $conf->config( $opt{'from_config'}, @agentnum ));
     }
-    $from_addr ||= $conf->invoice_from_full($cust_main->agentnum);
+    $from_addr ||= $conf->invoice_from_full( @agentnum );
   }
 #  my @cust_msg = ();
 #  if ( $conf->exists('log_sent_mail') and !$opt{'preview'} ) {
@@ -429,11 +437,11 @@ sub prepare {
                       ->format( HTML::TreeBuilder->new_from_content($body) )
                   );
   (
-    'custnum' => $cust_main->custnum,
-    'msgnum'  => $self->msgnum,
-    'from' => $from_addr,
-    'to'   => \@to,
-    'bcc'  => $self->bcc_addr || undef,
+    'custnum'   => $cust_main ? $cust_main->custnum : undef,
+    'msgnum'    => $self->msgnum,
+    'from'      => $from_addr,
+    'to'        => \@to,
+    'bcc'       => $self->bcc_addr || undef,
     'subject'   => $subject,
     'html_body' => $body,
     'text_body' => $text_body,

commit b48b0eaaa748f14f8374065d97c3f0e7f2ccf1b7
Author: Mitch Jackson <mitch at mitchjacksontech.com>
Date:   Tue Oct 9 13:36:58 2018 -0400

    RT# 38217 Fix syntax warning from typo

diff --git a/FS/FS/cust_main_Mixin.pm b/FS/FS/cust_main_Mixin.pm
index ccd1b84c9..296a9e469 100644
--- a/FS/FS/cust_main_Mixin.pm
+++ b/FS/FS/cust_main_Mixin.pm
@@ -465,8 +465,7 @@ sub email_search_result {
       next; # unlinked object; nothing else we can do
     }
 
-    my %to = {};
-    if ($to) { $to{'to'} = $to; }
+    my %to = ( to => $to ) if $to;
 
     if ( $msg_template ) {
       # Now supports other context objects.

commit 354b9290110ed25ab800be47df56061b5c0c3088
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Jul 26 16:42:54 2016 -0500

    RT#38217: Send email when logging conditions are met [removed unwanted log levels, preserved level num mapping]

diff --git a/FS/FS/Log.pm b/FS/FS/Log.pm
index b11630bc9..247f8efef 100644
--- a/FS/FS/Log.pm
+++ b/FS/FS/Log.pm
@@ -5,13 +5,20 @@ use FS::Record qw(qsearch qsearchs);
 use FS::Conf;
 use FS::Log::Output;
 use FS::log;
-use vars qw(@STACK @LEVELS);
+use vars qw(@STACK %LEVELS);
 
 # override the stringification of @_ with something more sensible.
 BEGIN {
-  @LEVELS = qw(debug info notice warning error critical alert emergency);
+  # subset of Log::Dispatch levels
+  %LEVELS = (
+    0 => 'debug',
+    1 => 'info',
+    3 => 'warning',
+    4 => 'error',
+    5 => 'critical'
+  );
 
-  foreach my $l (@LEVELS) {
+  foreach my $l (values %LEVELS) {
     my $sub = sub {
       my $self = shift;
       $self->log( level => $l, message => @_ );
@@ -100,4 +107,24 @@ sub DESTROY {
   splice(@STACK, $self->{'index'}, 1); # delete the stack entry
 }
 
+=item levelnums
+
+Subroutine.  Returns ordered list of level nums.
+
+=cut
+
+sub levelnums {
+  sort keys %LEVELS;
+}
+
+=item levelmap
+
+Subroutine.  Returns ordered map of level num => level name.
+
+=cut
+
+sub levelmap {
+  map { $_ => $LEVELS{$_} } levelnums;
+}
+
 1;
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index f26c6a338..bbd1ee12b 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -335,6 +335,9 @@ sub upgrade_data {
     #fix whitespace - before cust_main
     'cust_location' => [],
 
+    #remap log levels
+    'log' => [],
+
     #cust_main (tokenizes cards, remove paycvv from history, locations, cust_payby, etc)
     # (handles payinfo encryption/tokenization across all relevant tables)
     'cust_main' => [],
diff --git a/FS/FS/log.pm b/FS/FS/log.pm
index 769d6fce1..75e17c847 100644
--- a/FS/FS/log.pm
+++ b/FS/FS/log.pm
@@ -6,6 +6,8 @@ use FS::Record qw( qsearch qsearchs dbdef );
 use FS::UID qw( dbh driver_name );
 use FS::log_context;
 use FS::log_email;
+use FS::upgrade_journal;
+use Tie::IxHash;
 
 =head1 NAME
 
@@ -113,7 +115,7 @@ sub insert {
       'msgtype'       => 'admin',
       'to'            => $log_email->to_addr,
       'substitutions' => {
-        'loglevel'   => $FS::Log::LEVELS[$self->level], # which has hopefully been loaded...
+        'loglevel'   => $FS::Log::LEVELS{$self->level}, # which has hopefully been loaded...
         'logcontext' => $log_email->context, # use the one that triggered the email
         'logmessage' => $self->message,
       },
@@ -381,6 +383,49 @@ sub search {
   };
 }
 
+sub _upgrade_data {
+  my ($class, %opts) = @_;
+
+  return if FS::upgrade_journal->is_done('log__remap_levels');
+
+  tie my %levelmap, 'Tie::IxHash', 
+    2 => 1, #notice -> info
+    6 => 5, #alert -> critical
+    7 => 5, #emergency -> critical
+  ;
+
+  # this method should never autocommit
+  # should have been set in upgrade, but just in case...
+  local $FS::UID::AutoCommit = 0;
+
+  # in practice, only debug/info/warning/error appear to have been used,
+  #   so this probably won't do anything, but just in case
+  foreach my $old (keys %levelmap) {
+    # FS::log has no replace method
+    my $sql = 'UPDATE log SET level=' . dbh->quote($levelmap{$old}) . ' WHERE level=' . dbh->quote($old);
+    warn $sql unless $opts{'quiet'};
+    my $sth = dbh->prepare($sql) or die dbh->errstr;
+    $sth->execute() or die $sth->errstr;
+    $sth->finish();
+  }
+
+  foreach my $log_email (
+    qsearch('log_email',{ 'min_level' => 2 }),
+    qsearch('log_email',{ 'min_level' => 6 }),
+    qsearch('log_email',{ 'min_level' => 7 }),
+  ) {
+    $log_email->min_level($levelmap{$log_email->min_level});
+    my $error = $log_email->replace;
+    if ($error) {
+      dbh->rollback;
+      die $error;
+    }
+  }
+
+  FS::upgrade_journal->set_done('log__remap_levels');
+
+}
+
 =back
 
 =head1 BUGS
diff --git a/httemplate/browse/log_email.html b/httemplate/browse/log_email.html
index 0f64dd454..007ea6f74 100644
--- a/httemplate/browse/log_email.html
+++ b/httemplate/browse/log_email.html
@@ -21,7 +21,7 @@
                       ],
      'fields'      => [ 'logemailnum',
                         sub { $_[0]->context || '(all)' },
-                        sub { $FS::Log::LEVELS[$_[0]->min_level] },
+                        sub { $FS::Log::LEVELS{$_[0]->min_level} },
                         'msgname',
                         'to_addr',
                         $actions,
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
index 709a24069..19b415d4d 100644
--- a/httemplate/edit/log_email.html
+++ b/httemplate/edit/log_email.html
@@ -10,8 +10,8 @@
                             },
                             { 'field' => 'min_level',
                               'type'  => 'select',
-                              'options' => [ 0..7 ],
-                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+                              'options' => [ &FS::Log::levelnums ],
+                              'labels' => { &FS::Log::levelmap },
                               'curr_value' => scalar($cgi->param('min_level')),
                             },
                             'to_addr',
diff --git a/httemplate/search/log.html b/httemplate/search/log.html
index 9a61a71e1..9c17feef5 100644
--- a/httemplate/search/log.html
+++ b/httemplate/search/log.html
@@ -81,16 +81,16 @@ a:visited {text-decoration: none}
   <TD>Level
     <& /elements/select.html,
       field      => 'min_level',
-      options    => [ 0..7 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
-      curr_value => scalar($cgi->param('min_level')),
+      options    => [ &FS::Log::levelnums ],
+      labels     => { &FS::Log::levelmap },
+      curr_value => $cgi->param('min_level'),
     &>
      to
     <& /elements/select.html,
       field      => 'max_level',
-      options    => [ 0..7 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
-      curr_value => scalar($cgi->param('max_level')),
+      options    => [ &FS::Log::levelnums ],
+      labels     => { &FS::Log::levelmap },
+      curr_value => $cgi->param('max_level'),
     &>
   </TD>
   <TD>
@@ -128,7 +128,7 @@ a:visited {text-decoration: none}
 <%once>
 my $date_sub = sub { time2str('%Y-%m-%d %T', $_[0]->_date) };
 
-my $level_sub = sub { $FS::Log::LEVELS[$_[0]->level] };
+my $level_sub = sub { $FS::Log::LEVELS{$_[0]->level} };
 
 my $context_sub = sub {
   my $log = shift;
@@ -191,18 +191,15 @@ my $object_link_sub = sub {
   }
 };
 
-my @colors = (
-  '404040', #debug
-  '0000aa', #info
-  '00aa00', #notice
-  'aa0066', #warning
-  '000000', #error
-  'aa0000', #critical
-  'ff0000', #alert
-  'ff0000', #emergency
+my %colors = (
+  0 => '404040', #debug, gray
+  1 => '000000', #info, black
+  3 => '0000aa', #warning, blue
+  4 => 'aa0066', #error, purple
+  5 => 'ff0000', #critical, red
 );
 
-my $color_sub = sub { $colors[ $_[0]->level ]; };
+my $color_sub = sub { $colors{ $_[0]->level }; };
 
 my @contexts = ('', sort FS::log_context->contexts);
 </%once>
@@ -212,10 +209,10 @@ die "access denied"
   unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
 
 my @menubar = ();
-push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>),
+push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>);
 
 $cgi->param('min_level', 0) unless defined($cgi->param('min_level'));
-$cgi->param('max_level', 7) unless defined($cgi->param('max_level'));
+$cgi->param('max_level', 5) unless defined($cgi->param('max_level'));
 
 my %search = ();
 $search{'date'} = [ FS::UI::Web::parse_beginning_ending($cgi) ];

commit ebd5b466b4717f201bc7f8d57ef0f7441e590d91
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Jul 26 15:04:54 2016 -0500

    Revert "RT#38217: Send email when logging conditions are met"
    
    This reverts commit 5f563d5ac7e6e1e93cca382baa42ee106f3db5a0.

diff --git a/FS/FS/Log.pm b/FS/FS/Log.pm
index 3facb97df..b11630bc9 100644
--- a/FS/FS/Log.pm
+++ b/FS/FS/Log.pm
@@ -9,7 +9,7 @@ use vars qw(@STACK @LEVELS);
 
 # override the stringification of @_ with something more sensible.
 BEGIN {
-  @LEVELS = qw(debug info warning error critical);
+  @LEVELS = qw(debug info notice warning error critical alert emergency);
 
   foreach my $l (@LEVELS) {
     my $sub = sub {
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index bbd1ee12b..f26c6a338 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -335,9 +335,6 @@ sub upgrade_data {
     #fix whitespace - before cust_main
     'cust_location' => [],
 
-    #remap log levels
-    'log' => [],
-
     #cust_main (tokenizes cards, remove paycvv from history, locations, cust_payby, etc)
     # (handles payinfo encryption/tokenization across all relevant tables)
     'cust_main' => [],
diff --git a/FS/FS/log.pm b/FS/FS/log.pm
index 2543aeaf2..769d6fce1 100644
--- a/FS/FS/log.pm
+++ b/FS/FS/log.pm
@@ -6,7 +6,6 @@ use FS::Record qw( qsearch qsearchs dbdef );
 use FS::UID qw( dbh driver_name );
 use FS::log_context;
 use FS::log_email;
-use FS::upgrade_journal;
 
 =head1 NAME
 
@@ -382,52 +381,6 @@ sub search {
   };
 }
 
-sub _upgrade_data {
-  my ($class, %opts) = @_;
-
-  return if FS::upgrade_journal->is_done('log__remap_levels');
-
-  tie my %levelmap, 'Tie::IxHash', 
-#    0 => 0, #debug
-#    1 => 1, #info
-    2 => 1, #notice -> info
-    3 => 2, #warning
-    4 => 3, #error
-    5 => 4, #critical
-    6 => 4, #alert -> critical
-    7 => 4, #emergency -> critical
-  ;
-
-  # this method should never autocommit
-  # should have been set in upgrade, but just in case...
-  local $FS::UID::AutoCommit = 0;
-
-  # FS::log has no replace method
-  # in practice, only debug/info/warning/error were used,
-  #   so this should only hit warning/error
-  foreach my $old (keys %levelmap) {
-    my $sql = 'UPDATE log SET level=' . dbh->quote($levelmap{$old}) . ' WHERE level=' . dbh->quote($old);
-    warn $sql unless $opts{'quiet'};
-    my $sth = dbh->prepare($sql) or die dbh->errstr;
-    $sth->execute() or die $sth->errstr;
-    $sth->finish();
-  }
-
-  foreach my $log_email (
-    qsearch('log_email',{ 'min_level' => { 'op' => '>=', 'value' => '2' } })
-  ) {
-    $log_email->min_level($levelmap{$log_email->min_level});
-    my $error = $log_email->replace;
-    if ($error) {
-      dbh->rollback;
-      die $error;
-    }
-  }
-
-  FS::upgrade_journal->set_done('log__remap_levels');
-
-}
-
 =back
 
 =head1 BUGS
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
index 4483190ec..709a24069 100644
--- a/httemplate/edit/log_email.html
+++ b/httemplate/edit/log_email.html
@@ -10,8 +10,8 @@
                             },
                             { 'field' => 'min_level',
                               'type'  => 'select',
-                              'options' => [ 0..4 ],
-                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
+                              'options' => [ 0..7 ],
+                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
                               'curr_value' => scalar($cgi->param('min_level')),
                             },
                             'to_addr',
diff --git a/httemplate/search/log.html b/httemplate/search/log.html
index fdec15b3f..9a61a71e1 100644
--- a/httemplate/search/log.html
+++ b/httemplate/search/log.html
@@ -81,15 +81,15 @@ a:visited {text-decoration: none}
   <TD>Level
     <& /elements/select.html,
       field      => 'min_level',
-      options    => [ 0..4 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
+      options    => [ 0..7 ],
+      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
       curr_value => scalar($cgi->param('min_level')),
     &>
      to
     <& /elements/select.html,
       field      => 'max_level',
-      options    => [ 0..4 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
+      options    => [ 0..7 ],
+      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
       curr_value => scalar($cgi->param('max_level')),
     &>
   </TD>
@@ -192,11 +192,14 @@ my $object_link_sub = sub {
 };
 
 my @colors = (
-  '404040', #debug, gray
-  '000000', #info, black
-  '0000aa', #warning, blue
-  'aa0066', #error, purple
-  'ff0000', #critical, red
+  '404040', #debug
+  '0000aa', #info
+  '00aa00', #notice
+  'aa0066', #warning
+  '000000', #error
+  'aa0000', #critical
+  'ff0000', #alert
+  'ff0000', #emergency
 );
 
 my $color_sub = sub { $colors[ $_[0]->level ]; };

commit 345dfc58a6230d9bc5e88c063a3fc14f3647e68d
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Jul 26 14:27:53 2016 -0500

    RT#38217: Send email when logging conditions are met

diff --git a/FS/FS/Log.pm b/FS/FS/Log.pm
index b11630bc9..3facb97df 100644
--- a/FS/FS/Log.pm
+++ b/FS/FS/Log.pm
@@ -9,7 +9,7 @@ use vars qw(@STACK @LEVELS);
 
 # override the stringification of @_ with something more sensible.
 BEGIN {
-  @LEVELS = qw(debug info notice warning error critical alert emergency);
+  @LEVELS = qw(debug info warning error critical);
 
   foreach my $l (@LEVELS) {
     my $sub = sub {
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index f26c6a338..bbd1ee12b 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -335,6 +335,9 @@ sub upgrade_data {
     #fix whitespace - before cust_main
     'cust_location' => [],
 
+    #remap log levels
+    'log' => [],
+
     #cust_main (tokenizes cards, remove paycvv from history, locations, cust_payby, etc)
     # (handles payinfo encryption/tokenization across all relevant tables)
     'cust_main' => [],
diff --git a/FS/FS/log.pm b/FS/FS/log.pm
index 769d6fce1..2543aeaf2 100644
--- a/FS/FS/log.pm
+++ b/FS/FS/log.pm
@@ -6,6 +6,7 @@ use FS::Record qw( qsearch qsearchs dbdef );
 use FS::UID qw( dbh driver_name );
 use FS::log_context;
 use FS::log_email;
+use FS::upgrade_journal;
 
 =head1 NAME
 
@@ -381,6 +382,52 @@ sub search {
   };
 }
 
+sub _upgrade_data {
+  my ($class, %opts) = @_;
+
+  return if FS::upgrade_journal->is_done('log__remap_levels');
+
+  tie my %levelmap, 'Tie::IxHash', 
+#    0 => 0, #debug
+#    1 => 1, #info
+    2 => 1, #notice -> info
+    3 => 2, #warning
+    4 => 3, #error
+    5 => 4, #critical
+    6 => 4, #alert -> critical
+    7 => 4, #emergency -> critical
+  ;
+
+  # this method should never autocommit
+  # should have been set in upgrade, but just in case...
+  local $FS::UID::AutoCommit = 0;
+
+  # FS::log has no replace method
+  # in practice, only debug/info/warning/error were used,
+  #   so this should only hit warning/error
+  foreach my $old (keys %levelmap) {
+    my $sql = 'UPDATE log SET level=' . dbh->quote($levelmap{$old}) . ' WHERE level=' . dbh->quote($old);
+    warn $sql unless $opts{'quiet'};
+    my $sth = dbh->prepare($sql) or die dbh->errstr;
+    $sth->execute() or die $sth->errstr;
+    $sth->finish();
+  }
+
+  foreach my $log_email (
+    qsearch('log_email',{ 'min_level' => { 'op' => '>=', 'value' => '2' } })
+  ) {
+    $log_email->min_level($levelmap{$log_email->min_level});
+    my $error = $log_email->replace;
+    if ($error) {
+      dbh->rollback;
+      die $error;
+    }
+  }
+
+  FS::upgrade_journal->set_done('log__remap_levels');
+
+}
+
 =back
 
 =head1 BUGS
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
index 709a24069..4483190ec 100644
--- a/httemplate/edit/log_email.html
+++ b/httemplate/edit/log_email.html
@@ -10,8 +10,8 @@
                             },
                             { 'field' => 'min_level',
                               'type'  => 'select',
-                              'options' => [ 0..7 ],
-                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+                              'options' => [ 0..4 ],
+                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
                               'curr_value' => scalar($cgi->param('min_level')),
                             },
                             'to_addr',
diff --git a/httemplate/search/log.html b/httemplate/search/log.html
index 9a61a71e1..fdec15b3f 100644
--- a/httemplate/search/log.html
+++ b/httemplate/search/log.html
@@ -81,15 +81,15 @@ a:visited {text-decoration: none}
   <TD>Level
     <& /elements/select.html,
       field      => 'min_level',
-      options    => [ 0..7 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+      options    => [ 0..4 ],
+      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
       curr_value => scalar($cgi->param('min_level')),
     &>
      to
     <& /elements/select.html,
       field      => 'max_level',
-      options    => [ 0..7 ],
-      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+      options    => [ 0..4 ],
+      labels     => { map {$_ => $FS::Log::LEVELS[$_]} 0..4 },
       curr_value => scalar($cgi->param('max_level')),
     &>
   </TD>
@@ -192,14 +192,11 @@ my $object_link_sub = sub {
 };
 
 my @colors = (
-  '404040', #debug
-  '0000aa', #info
-  '00aa00', #notice
-  'aa0066', #warning
-  '000000', #error
-  'aa0000', #critical
-  'ff0000', #alert
-  'ff0000', #emergency
+  '404040', #debug, gray
+  '000000', #info, black
+  '0000aa', #warning, blue
+  'aa0066', #error, purple
+  'ff0000', #critical, red
 );
 
 my $color_sub = sub { $colors[ $_[0]->level ]; };

commit d80c2af2c03dc24ea8591459b44be0106aaf9275
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Dec 23 22:21:01 2015 -0600

    RT#38217 Send email when logging conditions are met [fixed bad char in template]

diff --git a/FS/FS/msg_template/InitialData.pm b/FS/FS/msg_template/InitialData.pm
index cbb9ccd8f..c3d29ecb7 100644
--- a/FS/FS/msg_template/InitialData.pm
+++ b/FS/FS/msg_template/InitialData.pm
@@ -44,8 +44,8 @@ END
       _upgrade_journal => 'system_log_email_template',
       _insert_args => [ subject => '{ $company_name } system log',
                         body    => <<'END',
-Level: {$loglevel}<BR>
-Context: {$logcontext}<BR>
+Level: {$loglevel}<BR>
+Context: {$logcontext}<BR>
 <BR>
 {$logmessage}<BR>
 

commit 4252e76a77bd4def206cfaa7466ba39bb1f027b6
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Dec 23 21:46:53 2015 -0600

    RT#38217 Send email when logging conditions are met [default template & dump-email_to upgrade]

diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm
index 2388aba69..0018a4586 100644
--- a/FS/FS/msg_template.pm
+++ b/FS/FS/msg_template.pm
@@ -879,6 +879,25 @@ sub _upgrade_data {
   ###
   $self->_populate_initial_data;
 
+  ### Fix dump-email_to (needs to happen after _populate_initial_data)
+  if ($conf->config('dump-email_to')) {
+    # anyone who still uses dump-email_to should have just had this created
+    my ($msg_template) = qsearch('msg_template',{ msgname => 'System log' });
+    if ($msg_template) {
+      eval "use FS::log_email;";
+      die $@ if $@;
+      my $log_email = new FS::log_email {
+        'context' => 'Cron::backup',
+        'min_level' => 1,
+        'msgnum' => $msg_template->msgnum,
+        'to_addr' => $conf->config('dump-email_to'),
+      };
+      my $error = $log_email->insert;
+      die $error if $error;
+      $conf->delete('dump-email_to');
+    }
+  }
+
 }
 
 sub _populate_initial_data { #class method
@@ -887,18 +906,22 @@ sub _populate_initial_data { #class method
 
   eval "use FS::msg_template::InitialData;";
   die $@ if $@;
+  eval "use FS::upgrade_journal;";
+  die $@ if $@;
 
   my $initial_data = FS::msg_template::InitialData->_initial_data;
 
   foreach my $hash ( @$initial_data ) {
 
     next if $hash->{_conf} && $conf->config( $hash->{_conf} );
+    next if $hash->{_upgrade_journal} && FS::upgrade_journal->is_done( $hash->{_upgrade_journal} );
 
     my $msg_template = new FS::msg_template($hash);
     my $error = $msg_template->insert( @{ $hash->{_insert_args} || [] } );
     die $error if $error;
 
     $conf->set( $hash->{_conf}, $msg_template->msgnum ) if $hash->{_conf};
+    FS::upgrade_journal->set_done( $hash->{_upgrade_journal} );
   
   }
 
diff --git a/FS/FS/msg_template/InitialData.pm b/FS/FS/msg_template/InitialData.pm
index 7cf83d070..cbb9ccd8f 100644
--- a/FS/FS/msg_template/InitialData.pm
+++ b/FS/FS/msg_template/InitialData.pm
@@ -38,6 +38,20 @@ Amount:    {$refund}<BR>
 END
                       ],
     },
+    { msgname   => 'System log',
+      msgclass  => 'email',
+      mime_type => 'text/html',
+      _upgrade_journal => 'system_log_email_template',
+      _insert_args => [ subject => '{ $company_name } system log',
+                        body    => <<'END',
+Level: {$loglevel}<BR>
+Context: {$logcontext}<BR>
+<BR>
+{$logmessage}<BR>
+
+END
+                      ],
+    },
   ];
 }
 
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
index bbce7c708..709a24069 100644
--- a/httemplate/edit/log_email.html
+++ b/httemplate/edit/log_email.html
@@ -18,6 +18,7 @@
                             { 'field' => 'msgnum',
                               'type' => 'select-msg_template',
                               'empty_label' => 'Select template',
+                              'value' => $msgnum,
                               'required' => 1,
                             },
                           ],
@@ -42,4 +43,14 @@ my %opts = @_;
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
 
+my $msgnum = $cgi->param('msgnum');
+unless ($msgnum) {
+  my ($msg_template) = qsearch('msg_template',{ msgname => 'System log' });
+  # doesn't seem worth having a config just for the default selected template
+  # if they've deleted the system-generated one, just default to empty "Select template"
+  if ($msg_template) {
+    $msgnum = $msg_template->msgnum;
+  }
+}
+
 </%init>

commit ec1cb553b60314cadce24d5360de61e43c42e344
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Dec 22 02:58:34 2015 -0600

    RT#38217 Send email when logging conditions are met [added null_right and msgtype]

diff --git a/FS/FS/log.pm b/FS/FS/log.pm
index 18ba7c936..769d6fce1 100644
--- a/FS/FS/log.pm
+++ b/FS/FS/log.pm
@@ -110,6 +110,7 @@ sub insert {
       next;
     }
     my $emailerror = $msg_template->send(
+      'msgtype'       => 'admin',
       'to'            => $log_email->to_addr,
       'substitutions' => {
         'loglevel'   => $FS::Log::LEVELS[$self->level], # which has hopefully been loaded...
diff --git a/httemplate/search/cust_msg.html b/httemplate/search/cust_msg.html
index fdb72a503..ef96f1310 100644
--- a/httemplate/search/cust_msg.html
+++ b/httemplate/search/cust_msg.html
@@ -62,8 +62,10 @@ my $conf = new FS::Conf;
 my $title = 'Outgoing Message Log';
 
 #here is the agent virtualization
-my $agentnums_sql =
-  $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql(
+  'table' => 'cust_main',
+  'null_right' => [ 'View system logs', 'Configuration' ],
+);
 
 my @where = ( $agentnums_sql );
 

commit 6d42808bbd3ac9c840c6259aef1dc79a0dc620ff
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Oct 20 20:56:13 2015 -0500

    RT#38217: Send email when logging conditions are met

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 447a6bcad..ecd071e49 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4658,6 +4658,25 @@ sub tables_hashref {
       'index' => [],
     },
 
+    'log_email' => {
+      'columns' => [
+        'logemailnum', 'serial', '', '', '', '',
+        'context', 'varchar', 'NULL', $char_d, '', '',
+        'min_level', 'int',  'NULL', '', '', '',
+        'msgnum', 'int', '',  '', '', '',
+        'to_addr', 'varchar', 'NULL',     255, '', '',
+      ],
+      'primary_key'  => 'logemailnum',
+      'unique'       => [],
+      'index'        => [ ['context'], ['min_level'] ],
+      'foreign_keys' => [
+                          { columns    => [ 'msgnum' ],
+                            table      => 'msg_template',
+                            references => [ 'msgnum' ],
+                          },
+                        ],
+    },
+
     'svc_alarm' => {
       'columns' => [
 #       name               type        null   length   default local
diff --git a/FS/FS/log.pm b/FS/FS/log.pm
index 547beb7d0..18ba7c936 100644
--- a/FS/FS/log.pm
+++ b/FS/FS/log.pm
@@ -5,6 +5,7 @@ use base qw( FS::Record );
 use FS::Record qw( qsearch qsearchs dbdef );
 use FS::UID qw( dbh driver_name );
 use FS::log_context;
+use FS::log_email;
 
 =head1 NAME
 
@@ -71,6 +72,8 @@ otherwise returns false.
 
 CONTEXT may be a list of context tags to attach to this record.
 
+Will send emails according to the conditions in L<FS::log_email>.
+
 =cut
 
 sub insert {
@@ -78,6 +81,7 @@ sub insert {
   my $self = shift;
   my $error = $self->SUPER::insert;
   return $error if $error;
+  my $contexts = {}; #for quick checks when sending emails
   foreach ( @_ ) {
     my $context = FS::log_context->new({
         'lognum'  => $self->lognum,
@@ -85,11 +89,40 @@ sub insert {
     });
     $error = $context->insert;
     return $error if $error;
+    $contexts->{$_} = 1;
+  }
+  foreach my $log_email (
+    qsearch('log_email',
+      {
+        'disabled' => '',
+        'min_level' => {
+          'op' => '<=',
+          'value' => $self->level,
+        },
+      }
+    )
+  ) {
+    # shouldn't be a lot of these, so not packing this into the qsearch
+    next if $log_email->context && !$contexts->{$log_email->context};
+    my $msg_template = qsearchs('msg_template',{ 'msgnum' => $log_email->msgnum });
+    unless ($msg_template) {
+      warn "Could not send email when logging, could not load message template for logemailnum " . $log_email->logemailnum;
+      next;
+    }
+    my $emailerror = $msg_template->send(
+      'to'            => $log_email->to_addr,
+      'substitutions' => {
+        'loglevel'   => $FS::Log::LEVELS[$self->level], # which has hopefully been loaded...
+        'logcontext' => $log_email->context, # use the one that triggered the email
+        'logmessage' => $self->message,
+      },
+    );
+    warn "Could not send email when logging: $emailerror" if $emailerror;
   }
   '';
 }
 
-# the insert method can be inherited from FS::Record
+# these methods can be inherited from FS::Record
 
 sub delete  { die "Log entries can't be modified." };
 
diff --git a/FS/FS/log_email.pm b/FS/FS/log_email.pm
new file mode 100644
index 000000000..9c53c230a
--- /dev/null
+++ b/FS/FS/log_email.pm
@@ -0,0 +1,108 @@
+package FS::log_email;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs dbdef );
+use FS::UID qw( dbh driver_name );
+
+=head1 NAME
+
+FS::log_email - Object methods for log email records
+
+=head1 SYNOPSIS
+
+  use FS::log_email;
+
+  $record = new FS::log_email \%hash;
+  $record = new FS::log_email { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::log object represents the conditions for sending an email
+when a log entry is created.  FS::log inherits from FS::Record.  
+The following fields are currently supported:
+
+=over 4
+
+=item logemailnum - primary key
+
+=item context - the context that will trigger the email (all contexts if unspecified)
+
+=item min_level - the minimum log level that will trigger the email (all levels if unspecified)
+
+=item msgnum - the msg_template that will be used to send the email
+
+=item to_addr - who the email will be sent to (in addition to any bcc on the template)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new log_email entry.
+
+=cut
+
+sub table { 'log_email'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('logemailnum')
+    || $self->ut_textn('context') # not validating against list of contexts in log_context,
+                                  # because not even log_context check currently does so
+    || $self->ut_number('min_level')
+    || $self->ut_foreign_key('msgnum', 'msg_template', 'msgnum')
+    || $self->ut_textn('to_addr')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm
index 50a9b3f27..2388aba69 100644
--- a/FS/FS/msg_template.pm
+++ b/FS/FS/msg_template.pm
@@ -239,7 +239,7 @@ Options are passed as a list of name/value pairs:
 
 =item cust_main
 
-Customer object (required).
+Customer object
 
 =item object
 
@@ -277,8 +277,8 @@ A hash reference of additional substitutions
 sub prepare {
   my( $self, %opt ) = @_;
 
-  my $cust_main = $opt{'cust_main'} or die 'cust_main required';
-  my $object = $opt{'object'} or die 'object required';
+  my $cust_main = $opt{'cust_main'}; # or die 'cust_main required';
+  my $object = $opt{'object'}; # or die 'object required';
 
   # localization
   my $locale = $cust_main->locale || '';
diff --git a/httemplate/browse/log_email.html b/httemplate/browse/log_email.html
new file mode 100644
index 000000000..0f64dd454
--- /dev/null
+++ b/httemplate/browse/log_email.html
@@ -0,0 +1,92 @@
+<% include('/elements/init_overlib.html') %>
+<% include('/browse/elements/browse.html',
+     'title'         => 'Log email condition configuration',
+     'name_singular' => 'condition',
+     'html_init'     => '<P STYLE="margin-top: 0">'
+                        . $add_condition_link
+                        . ' | '
+                        . $system_log_link
+                        . '</P>'
+                        . '<SCRIPT>'
+                        . $areyousure
+                        . '</SCRIPT>',
+     'query'         => $query,
+     'count_query'   => $count_query,
+     'header'      => [ '#',
+                        'Context', 
+                        'Min. Level', 
+                        'Template', 
+                        'To',
+                        '',
+                      ],
+     'fields'      => [ 'logemailnum',
+                        sub { $_[0]->context || '(all)' },
+                        sub { $FS::Log::LEVELS[$_[0]->min_level] },
+                        'msgname',
+                        'to_addr',
+                        $actions,
+                      ],
+     'sort_fields' => [ 'logemailnum',
+                        'context',
+                        'min_level',
+                        'msgname',
+                        'to_addr',
+                        '',
+                      ],
+     'links'       => [ $editlink,
+                        $editlink,
+                        $editlink,
+                        $editlink,
+                        $editlink,
+                        '',
+                      ],
+
+   ) %>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
+
+my $add_condition_link = include('/elements/popup_link.html',
+  'action' => $p.'edit/log_email.html?popup=1',
+  'label'  => 'Add log email condition',
+  'actionlabel' => 'Add log email condition',
+);
+
+my $system_log_link = qq(<A HREF="${p}search/log.html">System Log</A>);
+
+my $query = {
+  'table'   => 'log_email',
+  'select'  => '*',
+  'addl_from' => 'LEFT JOIN msg_template USING (msgnum)',
+  'hashref' => { },
+};
+
+my $count_query = "SELECT COUNT(*) FROM log_email";
+
+my $actions = sub {
+  my $log_email = shift;
+  my $logemailnum = $log_email->logemailnum;
+  qq!<A HREF="javascript:areyousure_delete_log_email($logemailnum)">(delete)</A>!;
+};
+
+my $areyousure_onclick = include('/elements/popup_link_onclick.html',
+  'js_action' => q(') . $p . q(misc/delete-log_email.html?logemailnum=' + logemailnum),
+  'actionlabel' => 'Delete log email condition',
+);
+
+my $areyousure = <<EOF;
+function areyousure_delete_log_email(logemailnum) {
+  if (confirm('Are you sure you want to delete log email condition #'+logemailnum+'?')) {
+${areyousure_onclick}
+  }
+}
+EOF
+
+my $editlink = [ $p.'edit/log_email.html?logemailnum=', 'logemailnum' ];
+
+</%init>
+
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
new file mode 100644
index 000000000..bbce7c708
--- /dev/null
+++ b/httemplate/edit/log_email.html
@@ -0,0 +1,45 @@
+<% include( 'elements/edit.html',
+              'name_singular' => 'log email condition',
+              'table'  => 'log_email',
+              'fields' => [
+                            { 'field' => 'context',
+                              'type' => 'select',
+                              'options' => [ '', @contexts ],
+                              'labels' => { '' => '(all)', map { $_ => $_ } @contexts },
+                              'curr_value' => scalar($cgi->param('context')),
+                            },
+                            { 'field' => 'min_level',
+                              'type'  => 'select',
+                              'options' => [ 0..7 ],
+                              'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+                              'curr_value' => scalar($cgi->param('min_level')),
+                            },
+                            'to_addr',
+                            { 'field' => 'msgnum',
+                              'type' => 'select-msg_template',
+                              'empty_label' => 'Select template',
+                              'required' => 1,
+                            },
+                          ],
+              'labels' => { 
+                            'context' => 'Context',
+                            'min_level' => 'Min. Level',
+                            'to_addr' => 'To',
+                            'msgnum' => 'Message',
+                          },
+              'viewall_dir' => 'browse',
+              'popup' => $opts{'popup'},
+              'form_init' => $opts{'popup'} ? q(<INPUT TYPE="hidden" NAME="popup" VALUE="1">) : '',
+           )
+%>
+<%once>
+my @contexts = sort FS::log_context->contexts;
+</%once>
+<%init>
+
+my %opts = @_;
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
+
+</%init>
diff --git a/httemplate/edit/process/log_email.html b/httemplate/edit/process/log_email.html
new file mode 100644
index 000000000..769e180a8
--- /dev/null
+++ b/httemplate/edit/process/log_email.html
@@ -0,0 +1,18 @@
+<% include('elements/process.html',
+    'table' => 'log_email',
+    %processopts
+   ) %>
+<%init>
+
+my %opts = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
+
+my %processopts = $opts{'popup'}
+  ? ( 'popup_reload' => 'Logging email added' )
+  : ( 'redirect' => $fsurl.'browse/log_email.html?' ); # id will be needlessly appended, should be harmless
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 9b30b6cd0..759ae338c 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -801,6 +801,10 @@ $config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_cla
 $config_misc{'Upload targets'} = [ $fsurl.'browse/upload_target.html', 'Billing and payment upload destinations' ]
   if $curuser->access_right('Configuration');
 
+$config_misc{'System log emails'} = [ $fsurl.'browse/log_email.html', 'Configure conditions for sending email when logging' ]
+  if $curuser->access_right('View system logs')
+  || $curuser->access_right('Configuration');
+
 tie my %config_menu, 'Tie::IxHash';
 if ( $curuser->access_right('Configuration' ) ) {
   %config_menu = (
diff --git a/httemplate/elements/tr-select-msg_template.html b/httemplate/elements/tr-select-msg_template.html
new file mode 100644
index 000000000..1f899e01c
--- /dev/null
+++ b/httemplate/elements/tr-select-msg_template.html
@@ -0,0 +1,12 @@
+<% include('/elements/tr-td-label.html',
+     'label'    => $opt{'label'} || 'Message template: ',
+     'required' => $opt{'required'} ) %>
+  <TD><% include('select-msg_template.html', %opt) %></TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
+
diff --git a/httemplate/misc/delete-log_email.html b/httemplate/misc/delete-log_email.html
new file mode 100644
index 000000000..cc17b15a0
--- /dev/null
+++ b/httemplate/misc/delete-log_email.html
@@ -0,0 +1,20 @@
+% if ($error) {
+<P STYLE="color: red"><% $error %></P>
+% } else {
+<H1>Log email condition deleted</H1>
+<SCRIPT>
+window.top.location.reload();
+</SCRIPT>
+% }
+
+<%init>
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
+
+my $logemailnum = $cgi->param('logemailnum');
+$logemailnum =~ /^\d+$/ or die "bad logemailnum '$logemailnum'";
+my $log_email = FS::log_email->by_key($logemailnum)
+  or die "logemailnum '$logemailnum' not found";
+my $error = $log_email->delete;
+</%init>
+
diff --git a/httemplate/search/log.html b/httemplate/search/log.html
index 9aa37365d..9a61a71e1 100644
--- a/httemplate/search/log.html
+++ b/httemplate/search/log.html
@@ -1,6 +1,7 @@
 <& elements/search.html, 
   'title'         => 'System Log',
   'name_singular' => 'event',
+  'menubar'       => \@menubar,
   'html_init'     => include('.head'),
   'query'         => $query,
   'count_query'   => $count_query,
@@ -210,6 +211,9 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 die "access denied"
   unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
 
+my @menubar = ();
+push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>),
+
 $cgi->param('min_level', 0) unless defined($cgi->param('min_level'));
 $cgi->param('max_level', 7) unless defined($cgi->param('max_level'));
 

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Log.pm                                       |  33 ++++++-
 FS/FS/Schema.pm                                    |  19 ++++
 FS/FS/Upgrade.pm                                   |   3 +
 FS/FS/cust_main_Mixin.pm                           |   3 +-
 FS/FS/log.pm                                       |  81 +++++++++++++++-
 FS/FS/log_email.pm                                 | 108 +++++++++++++++++++++
 FS/FS/msg_template.pm                              |  69 +++++++++----
 FS/FS/msg_template/InitialData.pm                  |  14 +++
 httemplate/browse/log_email.html                   |  92 ++++++++++++++++++
 httemplate/edit/log_email.html                     |  56 +++++++++++
 httemplate/edit/process/log_email.html             |  18 ++++
 httemplate/elements/menu.html                      |   4 +
 ...dware_type.html => tr-select-msg_template.html} |   5 +-
 httemplate/misc/delete-log_email.html              |  20 ++++
 httemplate/search/cust_msg.html                    |   6 +-
 httemplate/search/log.html                         |  37 +++----
 16 files changed, 521 insertions(+), 47 deletions(-)
 create mode 100644 FS/FS/log_email.pm
 create mode 100644 httemplate/browse/log_email.html
 create mode 100644 httemplate/edit/log_email.html
 create mode 100644 httemplate/edit/process/log_email.html
 copy httemplate/elements/{tr-select-hardware_type.html => tr-select-msg_template.html} (50%)
 create mode 100644 httemplate/misc/delete-log_email.html





More information about the freeside-commits mailing list