#21564, external message services: preview and send messages through the UI
[freeside.git] / httemplate / misc / email-customers.html
1 <%doc>
2
3 Allows emailing one or more customers, based on a search for customers.
4 Search can be specified either through cust_main fields as cgi params, or
5 through a base64 encoded frozen hash in the 'search' cgi param.  Form allows
6 selecting an existing msg_template, or creating a custom message, and shows a
7 preview of the message before sending.  If linked to as a popup, include the
8 cgi parameter 'popup' for proper header handling.
9
10 This may also be used as an element in other pages, enabling you to provide an
11 alternate initial form while using this for search freezing/thawing and 
12 preview/send actions, with the following options:
13
14 acl - the access right to use (defaults to 'Bulk send customer notices')
15
16 form_action - the URL to submit the form to
17
18 process_url - the URL for starting the JSRPC process
19
20 title - the title of the page
21
22 no_search_fields - arrayref of additional fields that are not search parameters
23
24 alternate_form - subroutine that returns alternate html for the initial form,
25 replaces msgnum/from/subject/html_body/action inputs and submit button, not
26 used if an action is specified
27
28 post_search_hook - sub hook for additional processing after search has been
29 processed from cgi, gets passed options 'conf' and 'search' (a reference to
30 the unfrozen %search hash), should be used to set msgnum or
31 from/subject/html_body cgi params
32
33 </%doc>
34 % if ($popup) {
35 <& /elements/header-popup.html, $title &>
36 % } else {
37 <& /elements/header.html, $title &>
38 % }
39
40
41 <FORM NAME="OneTrueForm" ACTION="<% $form_action %>" METHOD="POST">
42 <INPUT TYPE="hidden" NAME="table" VALUE="<% $table %>">
43 %# Mixing search params with from address, subject, etc. required special-case
44 %# handling of those, risked name conflicts, and caused massive problems with 
45 %# multi-valued search params.  We are no longer in search context, so we 
46 %# pack the search into a Storable string for later use.
47 <INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
48 <INPUT TYPE="hidden" NAME="popup" VALUE="<% $popup %>">
49 <INPUT TYPE="hidden" NAME="url" VALUE="<% $url | h %>">
50
51 % if ( $cgi->param('action') eq 'send' ) { 
52
53     <FONT SIZE="+2">Sending notice</FONT>
54
55     <& /elements/progress-init.html,
56                  'OneTrueForm',
57                  [ qw( search table from subject html_body text_body msgnum ) ],
58                  $process_url,
59                  $pdest,
60     &>
61
62 % } elsif ( $cgi->param('action') eq 'preview' ) {
63
64     <FONT SIZE="+2">Preview notice</FONT>
65
66 % }
67
68 % if ( $cgi->param('action') ) {
69
70     <TABLE CLASS="fsinnerbox">
71     <INPUT TYPE="hidden" NAME="msgnum" VALUE="<% scalar($cgi->param('msgnum')) %>">
72
73 %   if ( $msg_template ) {
74       <& /elements/tr-fixed.html,
75                    'label'      => 'Template:',
76                    'value'      => $msg_template->msgname,
77       &>
78 % }
79
80       <& /elements/tr-fixed.html,
81                    'field'      => 'from',
82                    'label'      => 'From:',
83                    'value'      => $from,
84       &>
85
86       <& /elements/tr-fixed.html,
87                    'field'      => 'subject',
88                    'label'      => 'Subject:',
89                    'value'      => $subject,
90       &>
91
92       <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $html_body |h %>">
93       <TR><TD COLSPAN=2>&nbsp;</TD></TR>
94       <TR>
95         <TH ALIGN="right" VALIGN="top">Message (HTML display): </TD>
96         <TD CLASS="background" ALIGN="left"><% $html_body %></TD>
97       </TR>
98
99 %     my $text_body = HTML::FormatText->new(leftmargin=>0)->format(
100 %                       HTML::TreeBuilder->new_from_content(
101 %                         $html_body
102 %                       )
103 %                     );
104       <INPUT TYPE="hidden" NAME="text_body" VALUE="<% $text_body |h %>">
105       <TR><TD COLSPAN=2>&nbsp;</TD></TR>
106       <TR>
107         <TH ALIGN="right" VALIGN="top">Message (Text display): </TD>
108         <TD CLASS="background" ALIGN="left">
109           <a href="javascript:void(0)" ID="email-message-text-view" style="color:#666666" onclick="showtext()">(view)</a>
110           <a href="javascript:void(0)" ID="email-message-text-hide" style="color:#666666; display: none;" onclick="hidetext()">(hide)</a>
111           <PRE id="email-message-text" style="display: none;"><% $text_body %></PRE>
112         </TD>
113       </TR>
114
115     </TABLE>
116
117 %   if ( $cgi->param('action') eq 'preview' ) {
118
119       <SCRIPT>
120
121         function showtext() {
122           $('#email-message-text-view').css('display','none');
123           $('#email-message-text-hide').css('display','');
124           $('#email-message-text').slideDown();
125         }
126
127         function hidetext() {
128           $('#email-message-text-view').css('display','');
129           $('#email-message-text-hide').css('display','none');
130           $('#email-message-text').slideUp();
131         }
132
133         function areyousure(href) {
134           return confirm("Send this notice to <% ($num_cust > 1) ? "$num_cust customers" : '1 customer' %> ?");
135         }
136       </SCRIPT>
137
138       <BR>
139       <INPUT TYPE="hidden" NAME="action" VALUE="send">
140       <INPUT TYPE="submit" VALUE="Send notice" onClick="return areyousure()">
141     
142 %   }
143
144 % } elsif ($opt{'alternate_form'}) {
145
146 <% &{$opt{'alternate_form'}}() %>
147
148 % } else {
149
150 <SCRIPT TYPE="text/javascript">
151 function toggle(obj) {
152   document.getElementById('table_no_template').style.display = (obj.value == 0) ? '' : 'none';
153 }
154
155 </SCRIPT>
156 Template: 
157     <& /elements/select-msg_template.html,
158          onchange => 'toggle(this)',
159     &>
160     <BR>
161   <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
162     <& /elements/tr-td-label.html, 'label' => 'From:' &>
163       <TD><& /elements/input-text.html,
164               'field' => 'from_name',
165               'value' => $conf->config('invoice_from_name', $agent_virt_agentnum) ||
166                          $conf->config('company_name', $agent_virt_agentnum), #?
167               'size'  => 20,
168           &>&nbsp;&lt;\
169           <& /elements/input-text.html,
170               'field' => 'from_addr',
171               'type'  => 'email', # HTML5, woot
172               'value' => $conf->config('invoice_from', $agent_virt_agentnum),
173               'size'  => 20,
174           &>&gt;</TD>
175  
176     <& /elements/tr-input-text.html,
177                  'field' => 'subject',
178                  'label' => 'Subject:',
179                  'size'  => 50,
180     &>
181
182     <TR>
183       <TD ALIGN="right" VALIGN="top" STYLE="padding-top:3px">Message: </TD>
184       <TD><& /elements/htmlarea.html, 
185                'field' => 'html_body',
186                'width' => 763,
187           &>
188       </TD>
189     </TR>
190
191   </TABLE>
192
193 %#Substitution vars:
194
195     <INPUT TYPE="hidden" NAME="action" VALUE="preview">
196     <INPUT TYPE="submit" VALUE="Preview notice">
197
198 % } #end not action or alternate form
199
200 </FORM>
201
202 % if ( $cgi->param('action') eq 'send' ) {
203     <SCRIPT TYPE="text/javascript">
204       process();
205     </SCRIPT>
206 % }
207
208 <& /elements/footer.html &>
209
210 <%init>
211
212 my %opt = @_;
213
214 $opt{'acl'} ||= 'Bulk send customer notices';
215
216 die "access denied"
217   unless $FS::CurrentUser::CurrentUser->access_right($opt{'acl'});
218
219 my $conf = FS::Conf->new;
220 my @no_search_fields = qw( action table from subject html_body text_body popup url );
221
222 my $form_action = $opt{'form_action'} || 'email-customers.html';
223 my $process_url = $opt{'process_url'} || 'process/email-customers.html';
224 my $title = $opt{'title'} || 'Send customer notices';
225 push( @no_search_fields, @{$opt{'no_search_fields'}} ) if $opt{'no_search_fields'};
226
227 my $table = $cgi->param('table') or die "'table' required";
228 my $agent_virt_agentnum = $cgi->param('agent_virt_agentnum') || '';
229
230 my $popup = $cgi->param('popup');
231 my $url   = $cgi->param('url');
232 my $pdest = { 'message' => "Notice sent" };
233 $pdest->{'url'} = $cgi->param('url') if $url;
234
235 my %search;
236 if ( $cgi->param('search') ) {
237   %search = %{ thaw(decode_base64( $cgi->param('search') )) };
238 }
239 else {
240   %search = $cgi->Vars;
241   delete $search{$_} for @no_search_fields;
242   # FS::$table->search is expected to know which parameters might be 
243   # multi-valued, and to accept scalar values for them also.  No good 
244   # solution to this since CGI can't tell whether a parameter _might_
245   # have had multiple values, only whether it does.
246   @search{keys %search} = map { /\0/ ? [ split /\0/, $_ ] : $_ } values %search;
247 }
248
249 &{$opt{'post_search_hook'}}(
250   'conf'   => $conf,
251   'search' => \%search,
252 ) if $opt{'post_search_hook'};
253
254 my $num_cust;
255 my $from = '';
256 if ( $cgi->param('from') ) {
257   $from = $cgi->param('from');
258 } elsif ( $cgi->param('from_name') ) {
259   $from = ($cgi->param('from_name') . ' <' . $cgi->param('from_addr') . '>');
260 } elsif ( $cgi->param('from_addr') ) {
261   $from = $cgi->param('from_addr');
262 }
263
264 my $subject = $cgi->param('subject') || '';
265 my $html_body = $cgi->param('html_body') || '';
266
267 my $msg_template = '';
268
269 if ( $cgi->param('action') eq 'preview' ) {
270
271   my $sql_query = "FS::$table"->search(\%search);
272   my $count_query = delete($sql_query->{'count_query'});
273   my $count_sth = dbh->prepare($count_query)
274     or die "Error preparing $count_query: ". dbh->errstr;
275   $count_sth->execute
276     or die "Error executing $count_query: ". $count_sth->errstr;
277   my $count_arrayref = $count_sth->fetchrow_arrayref;
278   $num_cust = $count_arrayref->[0];
279
280   if ( $cgi->param('msgnum') ) {
281     $msg_template = qsearchs('msg_template', 
282                              { msgnum => scalar($cgi->param('msgnum')) } )
283         or die "template not found: ".$cgi->param('msgnum');
284     $sql_query->{'extra_sql'} .= ' LIMIT 1';
285     $sql_query->{'select'} = "$table.*";
286     $sql_query->{'order_by'} = '';
287     my $object = qsearchs($sql_query);
288     my $cust = $object->cust_main;
289     my %msgopts = (
290       'cust_main' => $cust,
291       'object' => $object,
292     );
293
294     my $cust_msg = $msg_template->prepare(%msgopts);
295     $from = $cust_msg->env_from;
296     $html_body = $cust_msg->preview;
297     if ( $cust_msg->header =~ /^subject: (.*)/mi ) {
298       $subject = $1;
299     }
300   }
301 }
302
303 </%init>