2 'html_init' => '<TABLE id="outerTable"><TR><TD>',
3 'body_etc' => $body_etc,
4 'name_singular' => 'template',
5 'table' => 'msg_template',
6 'viewall_dir' => 'browse',
9 'agent_null_right' => [ 'View global templates', 'Edit global templates' ],
13 'msgnum' => 'Template',
14 'agentnum' => 'Agent',
15 'msgname' => 'Template name',
16 'from_addr' => 'From: ',
17 'bcc_addr' => 'Bcc: ',
19 'subject' => 'Subject: ',
20 'body' => 'Message body',
22 'edit_callback' => \&edit_callback,
23 'error_callback' => \&edit_callback,
24 'html_bottom' => '</DIV>',
25 'html_table_bottom'=> \&html_table_bottom,
26 'html_foot' => ( $no_submit ? '' : "</TD>$sidebar</TR></TABLE>" ),
27 'no_submit' => $no_submit,
30 use FS::template_image;
32 my $curuser = $FS::CurrentUser::CurrentUser;
35 unless $curuser->access_right([ 'View templates', 'View global templates',
36 'Edit templates', 'Edit global templates',
40 $body_etc = q!onload="document.getElementById('locale').onchange()"!
41 if $cgi->param('locale') eq 'new';
43 my $msgnum = $cgi->param('msgnum');
44 my $msg_template = $msgnum ? qsearchs('msg_template', {msgnum=>$msgnum}) : '';
48 if ( $curuser->access_right('Edit global templates')
49 || ( $curuser->access_right('Edit templates')
51 && $msg_template->agentnum
52 && $curuser->agentnums_href->{$msg_template->agentnum}
57 { field => 'agentnum',
58 type => 'select-agent',
60 { field => 'msgname', size=>60, },
61 { field => 'from_addr', size=>60, },
62 { field => 'bcc_addr', size=>60, },
63 { type => 'tablebreak-tabs',
64 include_opt_callback => \&menubar_opt_callback,
66 # template_content fields
67 { field => 'locale', type => 'hidden' },
68 { field => 'subject', size=>60, },
72 config=> { extraPlugins => 'blockprotect' },
80 { field => 'agentnum',
81 type => 'select-agent',
84 { field => 'msgname', type => 'fixed', },
85 { field => 'from_addr', type => 'fixed', },
86 { field => 'bcc_addr', type => 'fixed', },
87 { type => 'tablebreak-tabs',
88 include_opt_callback => \&menubar_opt_callback,
90 # template_content fields
91 { field => 'locale', type => 'hidden' },
92 { field => 'subject', type => 'fixed', },
102 my ($cgi, $object, $fields_listref, $opt_hashref) = @_;
103 my $template_content = new FS::template_content { 'locale' => '' };
104 $object->{'Hash'} = { $object->hash, $template_content->hash };
108 my ($cgi, $object, $fields_listref, $opt_hashref) = @_;
109 $cgi->param('locale') =~ /^(\w*)$/ or die 'bad locale '.$cgi->param('locale');
112 # fetch the content object and merge its fields
114 'msgnum' => $object->msgnum,
117 my $template_content = qsearchs('template_content', \%args)
118 || new FS::template_content( { %args });
119 $object->{'Hash'} = { $object->hash, $template_content->hash };
121 # set up the locale selector if this is a new content
122 if ( $locale eq 'new' ) {
124 # make a list of available locales
125 my $content_locales = $object->content_locales;
126 my @locales = grep { !exists($content_locales->{$_}) }
127 FS::Conf->new->config('available-locales');
130 my %info = FS::Locales->locale_info($_);
131 $labels{$_} = $info{'label'};
133 unshift @locales, 'new';
134 $labels{'new'} = 'Select language';
138 $i++ until ( $fields_listref->[$i]->{'field'} eq 'locale' );
139 my $locale_field = $fields_listref->[$i];
141 my $onchange_locale = "document.getElementById('submit').disabled =
142 (this.options[this.selectedIndex].value == 'new');";
147 options => \@locales,
150 onchange => $onchange_locale,
155 sub menubar_opt_callback {
157 # generate no tabs for new msg_templates.
158 my $msgnum = $object->msgnum or return;
159 my (@tabs, @options, %labels);
160 push @tabs, mt('Default'), '';
163 foreach my $l (FS::Locales->locales) {
164 if ( exists $object->content_locales->{$l} ) {
165 my %info = FS::Locales->locale_info($l);
169 $selected = $info{'label'} if $object->locale eq $l;
172 $display_new = 1; # there is at least one unused locale left
175 push @tabs, mt('New'), ';locale=new' if $display_new;
176 $selected = mt('New') if $object->locale eq 'new';
177 $selected ||= mt('Default');
179 'url_base' => $p.'edit/msg_template.html?msgnum='.$msgnum,
180 'selected' => $selected,
185 my $onchange_locale = '';
189 my %substitutions = (
191 '$display_custnum'=> 'Customer#',
192 '$agentnum' => 'Agent#',
193 '$agent_name' => 'Agent name',
194 '$payby' => 'Payment method',
195 '$paymask' => 'Card/account# (masked)',
196 '$payname' => 'Name on card/bank name',
197 '$paytype' => 'Account type',
198 '$payip' => 'IP address used to submit payment info',
199 '$num_ncancelled_pkgs' => '# of active packages',
200 '$num_cancelled_pkgs' => '# of cancelled packages',
201 '$num_pkgs' => '# of packages',
202 '$classname' => 'Customer class',
203 '$categoryname' => 'Customer category',
204 '$balance' => 'Current balance',
205 '$credit_limit' => 'Credit limit',
206 '$invoicing_list_emailonly' => 'Billing email address',
207 #'$cust_status' => 'Status (raw internal label)',
208 '$cust_status_label' => 'Status (display label)',
209 '$cust_statuscolor' => 'Status color code',
210 '$company_name' => 'Our company name',
211 '$company_address'=> 'Our company address',
212 '$company_phonenum' => 'Our phone number',
213 '$selfservice_server_base_url' => 'Base URL of customer self-service',
215 'contact' => [ # duplicate this for shipping
216 '$name' => 'Company and contact name',
217 '$name_short' => 'Company or contact name',
218 '$company' => 'Company name',
219 '$contact' => 'Contact name (last, first)',
220 '$contact_firstlast'=> 'Contact name (first last)',
221 '$first' => 'First name',
222 '$last' => 'Last name',
223 '$address1' => 'Address line 1',
224 '$address2' => 'Address line 2',
226 '$county' => 'County',
229 '$country' => 'Country',
230 '$daytime' => 'Day phone',
231 '$night' => 'Night phone',
232 '$mobile' => 'Mobile phone',
236 '$ship_address1' => 'Address line 1',
237 '$ship_address2' => 'Address line 2',
238 '$ship_city' => 'City',
239 '$ship_county' => 'County',
240 '$ship_state' => 'State',
241 '$ship_zip' => 'Zip',
242 '$ship_country' => 'Country',
245 '$invnum' => 'Invoice#',
246 '$_date_pretty' => 'Invoice date',
247 '$due_date' => 'Invoice due date (timestamp)',
248 '$due_date2str' => 'Invoice due date (human readable)',
251 '$pkgnum' => 'Package#',
252 '$pkg' => 'Package description',
253 '$pkg_label' => 'Description + comment',
254 '$status' => 'Status',
255 '$statuscolor' => 'Status color code',
256 '$start_ymd' => 'Start date',
257 '$setup_ymd' => 'Setup date',
258 '$last_bill_ymd' => 'Last bill date',
259 '$next_bill_ymd' => 'Next bill date',
260 '$susp_ymd' => 'Suspended on date',
261 '$cancel_ymd' => 'Canceled on date',
262 '$adjourn_ymd' => 'Adjournment date',
263 '$expire_ymd' => 'Expiration date',
264 '$labels_short' => 'Service labels',
265 '$location_label' => 'Service location',
268 '$svcnum' => 'Service#',
269 '$username' => 'Login name',
270 '$password' => 'Password',
271 '$domain' => 'Domain name',
274 '$svcnum' => 'Service#',
275 '$domain' => 'Domain name',
276 '$registrar' => 'Registrar name',
277 '$catchall' => 'Catchall email',
280 '$svcnum' => 'Service#',
281 '$phonenum' => 'Phone number',
282 '$countrycode' => 'Country code',
283 '$domain' => 'Domain name'
286 '$svcnum' => 'Service#',
287 '$ip_addr' => 'IP address',
288 '$mac_addr' => 'MAC address',
289 '$speed_up' => 'Upstream speed',
290 '$speed_down' => 'Downstream speed',
293 '$paynum' => 'Payment#',
295 '$payby' => 'Payment method',
296 '$date' => 'Payment date',
297 '$payinfo' => 'Card/account# (masked)',
298 '$error' => 'Decline reason',
302 tie my %sections, 'Tie::IxHash', (
303 'contact' => 'Name and contact info (billing)',
304 'service' => 'Service address',
305 'cust_main' => 'Customer status and payment info',
306 'cust_pkg' => 'Package fields',
307 'cust_bill' => 'Invoice fields',
308 'cust_pay' => 'Payment fields',
309 'svc_acct' => 'Login service fields',
310 'svc_domain'=> 'Domain service fields',
311 'svc_phone' => 'Phone service fields',
312 'svc_broadband' => 'Broadband service fields',
315 my $widget = new HTML::Widgets::SelectLayers(
316 'options' => \%sections,
317 'form_name' => 'dummy',
318 'html_between'=>'</FORM><FONT SIZE=-1>',
319 'selected_layer'=>(keys(%sections))[0],
320 'layer_callback' => sub {
322 my $html = include('/elements/table-grid.html');
323 my @hints = @{ $substitutions{$section} };
325 my $key = shift @hints;
326 $html .= qq!\n<TR><TD><A href="javascript:insertHtml('{$key}')">$key</A></TD>!;
327 $html .= "\n<TD>".shift(@hints).'</TD></TR>';
329 $html .= "\n</TABLE>";
335 <SCRIPT TYPE="text/javascript">
336 function insertHtml(what) {
337 var oEditor = CKEDITOR.instances["body"];
338 oEditor.insertHtml(what);
341 function areyousure(url, message) {
342 if (confirm(message))
343 window.location.href = url;
346 <TD valign="top"><FORM name="dummy">
349 '<P>Click above links to insert substitution code.</P>
351 Enclose substitutions and other Perl expressions in braces:
352 <BR>{ $name } = ExampleCo (Smith, John)
353 <BR>{ time2str("%D", time) } = '.time2str("%D", time).'
355 $sidebar .= include('/elements/template_image-dialog.html',
356 'callback' => 'insertHtml'
358 $sidebar .= '<P><A HREF="javascript:insertImageDialog()">Insert Uploaded Image</A></P>
362 sub html_table_bottom {
364 $cgi->param('locale') =~ /^(\w+)$/;
367 if ( $locale and $locale ne 'new' ) {
368 # set up a delete link
369 my $msgnum = $object->msgnum;
370 my $url = $p."misc/delete-template_content.html?msgnum=$msgnum;locale=$1";
371 my $link = qq!<A HREF="javascript:areyousure('$url','Really delete this template?')">! .
372 'Delete this template' .
374 $html = qq!<TR><TD></TD>
375 <TD STYLE="font-style: italic; font-size: small">$link</TD></TR>!;