Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / httemplate / misc / email-customers.html
index 201aed4..47e6a5b 100644 (file)
-<% include('/elements/header.html', $title) %>
+<%doc>
 
-<FORM NAME="OneTrueForm" ACTION="email-customers.html" METHOD="POST">
-% foreach my $key ( keys %search ) {
-%   my @values = ref($search{$key}) ? @{$search{$key}} : ( $search{$key} );
-%   foreach my $value ( @values ) {
-      <INPUT TYPE="hidden" NAME="<% $key %>" VALUE="<% $value %>">
-%   }
+Allows emailing one or more customers, based on a search for customers.  Search can
+be specified either through cust_main fields as cgi params, or through a base64 encoded
+frozen hash in the 'search' cgi param.  Form allows selecting an existing msg_template,
+or creating a custom message, and shows a preview of the message before sending.
+If linked to as a popup, include the cgi parameter 'popup' for proper header handling.
+
+This may also be used as an element in other pages, enabling you to provide an
+alternate initial form while using this for search freezing/thawing and 
+preview/send actions, with the following options:
+
+acl - the access right to use (defaults to 'Bulk send customer notices')
+
+form_action - the URL to submit the form to
+
+process_url - the URL for starting the JSRPC process
+
+title - the title of the page
+
+no_search_fields - arrayref of additional fields that are not search parameters
+
+alternate_form - subroutine that returns alternate html for the initial form,
+replaces msgnum/from/subject/html_body/action inputs and submit button,
+not used if an action is specified
+
+post_search_hook - sub hook for additional processing after search has been processed from cgi,
+gets passed options 'conf' and 'search' (a reference to the unfrozen %search hash),
+should be used to set msgnum or from/subject/html_body cgi params
+
+</%doc>
+% if ($popup) {
+<& /elements/header-popup.html, $title &>
+% } else {
+<& /elements/header.html, $title &>
 % }
 
-% if ( $cgi->param('magic') eq 'send' ) {
+
+<FORM NAME="OneTrueForm" ACTION="<% $form_action %>" METHOD="GET">
+<INPUT TYPE="hidden" NAME="table" VALUE="<% $table %>">
+%# Mixing search params with from address, subject, etc. required special-case
+%# handling of those, risked name conflicts, and caused massive problems with 
+%# multi-valued search params.  We are no longer in search context, so we 
+%# pack the search into a Storable string for later use.
+<INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
+<INPUT TYPE="hidden" NAME="popup" VALUE="<% $popup %>">
+<INPUT TYPE="hidden" NAME="url" VALUE="<% $url | h %>">
+
+% if ( $cgi->param('action') eq 'send' ) { 
 
     <FONT SIZE="+2">Sending notice</FONT>
 
-    <% include('/elements/progress-init.html',
+    <& /elements/progress-init.html,
                  'OneTrueForm',
-                 [ keys(%search), qw( from subject html_body text_body ) ],
-                 'process/email-customers.html',
-                 { 'message' => "Notice sent" }, #would be nice to show #, but..
-              )
-    %>
+                 [ qw( search table from subject html_body text_body msgnum ) ],
+                 $process_url,
+                 $pdest,
+    &>
 
-% } elsif ( $cgi->param('magic') eq 'preview' ) {
+% } elsif ( $cgi->param('action') eq 'preview' ) {
 
     <FONT SIZE="+2">Preview notice</FONT>
 
 % }
 
-% if ( $cgi->param('magic') ) {
+% if ( $cgi->param('action') ) {
 
-    <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+    <TABLE CLASS="fsinnerbox">
+    <INPUT TYPE="hidden" NAME="msgnum" VALUE="<% scalar($cgi->param('msgnum')) %>">
+
+%   if ( $msg_template ) {
+      <& /elements/tr-fixed.html,
+                   'label'      => 'Template:',
+                   'value'      => $msg_template->msgname,
+      &>
+% }
 
-      <% include('/elements/tr-fixed.html',
+      <& /elements/tr-fixed.html,
                    'field'      => 'from',
                    'label'      => 'From:',
-                   'value' => scalar( $cgi->param('from') ),
-                )
-      %>
+                   'value'      => $from,
+      &>
 
-      <% include('/elements/tr-fixed.html',
+      <& /elements/tr-fixed.html,
                    'field'      => 'subject',
                    'label'      => 'Subject:',
-                   'value' => scalar( $cgi->param('subject') ),
-                )
-      %>
+                   'value'      => $subject,
+      &>
 
-      <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $cgi->param('html_body') |h %>">
+      <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $html_body |h %>">
+      <TR><TD COLSPAN=2>&nbsp;</TD></TR>
       <TR>
-        <TD ALIGN="right" VALIGN="top">Message (HTML display): </TD>
-        <TD CLASS="background" ALIGN="left"><% $cgi->param('html_body') %></TD>
+        <TH ALIGN="right" VALIGN="top">Message (HTML display): </TD>
+        <TD CLASS="background" ALIGN="left"><% $html_body %></TD>
       </TR>
 
 %     my $text_body = HTML::FormatText->new(leftmargin=>0)->format(
 %                       HTML::TreeBuilder->new_from_content(
-%                         $cgi->param('html_body')
+%                         $html_body
 %                       )
 %                     );
       <INPUT TYPE="hidden" NAME="text_body" VALUE="<% $text_body |h %>">
+      <TR><TD COLSPAN=2>&nbsp;</TD></TR>
       <TR>
-        <TD ALIGN="right" VALIGN="top">Message (Text display): </TD>
-        <TD CLASS="background" ALIGN="left"><PRE><% $text_body %></PRE></TD>
+        <TH ALIGN="right" VALIGN="top">Message (Text display): </TD>
+        <TD CLASS="background" ALIGN="left">
+          <a href="javascript:void(0)" ID="email-message-text-view" style="color:#666666" onclick="showtext()">(view)</a>
+          <a href="javascript:void(0)" ID="email-message-text-hide" style="color:#666666; display: none;" onclick="hidetext()">(hide)</a>
+          <PRE id="email-message-text" style="display: none;"><% $text_body %></PRE>
+        </TD>
       </TR>
 
     </TABLE>
 
-% if ( $cgi->param('magic') eq 'preview' ) {
+%   if ( $cgi->param('action') eq 'preview' ) {
 
       <SCRIPT>
+
+        function showtext() {
+          $('#email-message-text-view').css('display','none');
+          $('#email-message-text-hide').css('display','');
+          $('#email-message-text').slideDown();
+        }
+
+        function hidetext() {
+          $('#email-message-text-view').css('display','');
+          $('#email-message-text-hide').css('display','none');
+          $('#email-message-text').slideUp();
+        }
+
         function areyousure(href) {
-          return confirm("Send this notice to <% $num_cust %> customers?");
+          return confirm("Send this notice to <% ($num_cust > 1) ? "$num_cust customers" : '1 customer' %> ?");
         }
       </SCRIPT>
 
       <BR>
-      <INPUT TYPE="hidden" NAME="magic" VALUE="send">
+      <INPUT TYPE="hidden" NAME="action" VALUE="send">
       <INPUT TYPE="submit" VALUE="Send notice" onClick="return areyousure()">
     
 %   }
 
-% } else {
+% } elsif ($opt{'alternate_form'}) {
 
-  <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%">
+<% &{$opt{'alternate_form'}}() %>
 
-    <% include('/elements/tr-input-text.html',
-                 'field' => 'from',
-                 'label' => 'From:',
-              )
-    %>
+% } else {
 
-    <% include('/elements/tr-input-text.html',
+<SCRIPT TYPE="text/javascript">
+function toggle(obj) {
+  document.getElementById('table_no_template').style.display = (obj.value == 0) ? '' : 'none';
+}
+
+</SCRIPT>
+Template: 
+    <& /elements/select-msg_template.html,
+         onchange => 'toggle(this)',
+    &>
+    <BR>
+  <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
+    <& /elements/tr-td-label.html, 'label' => 'From:' &>
+      <TD><& /elements/input-text.html,
+              'field' => 'from_name',
+              'value' => $conf->config('invoice_from_name', $agent_virt_agentnum) ||
+                         $conf->config('company_name', $agent_virt_agentnum), #?
+              'size'  => 20,
+          &>&nbsp;&lt;\
+          <& /elements/input-text.html,
+              'field' => 'from_addr',
+              'type'  => 'email', # HTML5, woot
+              'value' => $conf->config('invoice_from', $agent_virt_agentnum),
+              'size'  => 20,
+          &>&gt;</TD>
+    <& /elements/tr-input-text.html,
                  'field' => 'subject',
                  'label' => 'Subject:',
-              )
-    %>
+                 'size'  => 50,
+    &>
 
     <TR>
-      <TD ALIGN="right" VALIGN="top">Message: </TD>
-      <TD><% include('/elements/htmlarea.html', 'field'=>'html_body') %></TD>
+      <TD ALIGN="right" VALIGN="top" STYLE="padding-top:3px">Message: </TD>
+      <TD><& /elements/htmlarea.html, 
+               'field' => 'html_body',
+               'width' => 763,
+          &>
+      </TD>
     </TR>
 
   </TABLE>
 
 %#Substitution vars:
 
-    <BR><BR>
-    <INPUT TYPE="hidden" NAME="magic" VALUE="preview">
+    <INPUT TYPE="hidden" NAME="action" VALUE="preview">
     <INPUT TYPE="submit" VALUE="Preview notice">
 
-% }
+% } #end not action or alternate form
 
 </FORM>
 
-% if ( $cgi->param('magic') eq 'send' ) {
+% if ( $cgi->param('action') eq 'send' ) {
     <SCRIPT TYPE="text/javascript">
       process();
     </SCRIPT>
 % }
 
-<% include('/elements/footer.html') %>
+<& /elements/footer.html &>
 
 <%init>
 
+my %opt = @_;
+
+$opt{'acl'} ||= 'Bulk send customer notices';
+
 die "access denied"
-  unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
+  unless $FS::CurrentUser::CurrentUser->access_right($opt{'acl'});
+
+my $conf = FS::Conf->new;
+my @no_search_fields = qw( action table from subject html_body text_body popup url );
 
-my %search = $cgi->Vars;
-delete $search{$_} for qw( magic from subject html_body text_body );
-$search{$_} = [ split(/\0/, $search{$_}) ]
-  foreach grep { $_ eq 'payby' || $search{$_} =~ /\0/ } keys %search;
+my $form_action = $opt{'form_action'} || 'email-customers.html';
+my $process_url = $opt{'process_url'} || 'process/email-customers.html';
+my $title = $opt{'title'} || 'Send customer notices';
+push( @no_search_fields, @{$opt{'no_search_fields'}} ) if $opt{'no_search_fields'};
 
-my $title = 'Bulk send customer notices';
+my $table = $cgi->param('table') or die "'table' required";
+my $agent_virt_agentnum = $cgi->param('agent_virt_agentnum') || '';
+
+my $popup = $cgi->param('popup');
+my $url   = $cgi->param('url');
+my $pdest = { 'message' => "Notice sent" };
+$pdest->{'url'} = $cgi->param('url') if $url;
+
+my %search;
+if ( $cgi->param('search') ) {
+  %search = %{ thaw(decode_base64( $cgi->param('search') )) };
+}
+else {
+  %search = $cgi->Vars;
+  delete $search{$_} for @no_search_fields;
+  # FS::$table->search is expected to know which parameters might be 
+  # multi-valued, and to accept scalar values for them also.  No good 
+  # solution to this since CGI can't tell whether a parameter _might_
+  # have had multiple values, only whether it does.
+  @search{keys %search} = map { /\0/ ? [ split /\0/, $_ ] : $_ } values %search;
+}
+
+&{$opt{'post_search_hook'}}(
+  'conf'   => $conf,
+  'search' => \%search,
+) if $opt{'post_search_hook'};
 
 my $num_cust;
-if ( $cgi->param('magic') eq 'preview' ) {
-  my $sql_query = FS::cust_main->search(\%search);
+my $from = '';
+if ( $cgi->param('from') ) {
+  $from = $cgi->param('from');
+} elsif ( $cgi->param('from_name') ) {
+  $from = ($cgi->param('from_name') . ' <' . $cgi->param('from_addr') . '>');
+} elsif ( $cgi->param('from_addr') ) {
+  $from = $cgi->param('from_addr');
+}
+
+my $subject = $cgi->param('subject') || '';
+my $html_body = $cgi->param('html_body') || '';
+
+my $msg_template = '';
+
+if ( $cgi->param('action') eq 'preview' ) {
+
+  my $sql_query = "FS::$table"->search(\%search);
   my $count_query = delete($sql_query->{'count_query'});
   my $count_sth = dbh->prepare($count_query)
     or die "Error preparing $count_query: ". dbh->errstr;
@@ -140,6 +274,23 @@ if ( $cgi->param('magic') eq 'preview' ) {
     or die "Error executing $count_query: ". $count_sth->errstr;
   my $count_arrayref = $count_sth->fetchrow_arrayref;
   $num_cust = $count_arrayref->[0];
+
+  if ( $cgi->param('msgnum') ) {
+    $msg_template = qsearchs('msg_template', 
+                             { msgnum => scalar($cgi->param('msgnum')) } )
+        or die "template not found: ".$cgi->param('msgnum');
+    $sql_query->{'extra_sql'} .= ' LIMIT 1';
+    $sql_query->{'select'} = "$table.*";
+    $sql_query->{'order_by'} = '';
+    my $object = qsearchs($sql_query);
+    my $cust = $object->cust_main;
+    my %msgopts = (
+      'cust_main' => $cust,
+      'object' => $object,
+    );
+    my %message = $msg_template->prepare(%msgopts);
+    ($from, $subject, $html_body) = @message{'from', 'subject', 'html_body'};
+  }
 }
 
 </%init>