Bug 19034: (followup) Fix letters.tt XSS flaw
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / modules / tools / letter.tt
1 [% USE Koha %]
2 [% USE Branches %]
3 [% INCLUDE 'doc-head-open.inc' %]
4 <title>Koha &rsaquo; Tools &rsaquo; Notices[% IF ( add_form or copy_form ) %][% IF ( modify ) %] &rsaquo; Modify notice[% ELSE %] &rsaquo; Add notice[% END %][% END %][% IF ( add_validate or copy_validate) %] &rsaquo; Notice added[% END %][% IF ( delete_confirm ) %] &rsaquo; Confirm deletion[% END %]</title>
5 [% INCLUDE 'doc-head-close.inc' %]
6 <link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" />
7 [% INCLUDE 'datatables.inc' %]
8 <script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery.fixFloat.js"></script>
9 <script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery.insertatcaret.js"></script>
10 <script type="text/javascript">
11 //<![CDATA[
12 $(document).ready(function() {
13     [% IF add_form or copy_form %]
14         $('#toolbar').fixFloat();
15     [% END %]
16     $("#lettert:has(tbody tr)").dataTable($.extend(true, {}, dataTablesDefaults, {
17         "sDom": 't',
18         "aoColumnDefs": [
19             { "bSortable": false, "bSearchable": false, 'aTargets': [ 'nosort' ] }
20         ],
21         "bPaginate": false
22     }));
23     [% IF no_op_set %]
24       $('#branch').change(function() {
25             $('#op').val("");
26             $('#selectlibrary').submit();
27       });
28       $('#newnotice').click(function() {
29             $('#op').val("add_form");
30             return true;
31       });
32     [% END %]
33
34     $("#newmodule").on("change",function(){
35         if( $("#branch").val() == ""){
36             var branchcode = "*";
37         } else {
38             var branchcode = $("#branch").val();
39         }
40         window.location.href = "/cgi-bin/koha/tools/letter.pl?op=add_form&module=" + $(this).val() + "&branchcode=" + branchcode;
41     });
42
43     $("#submit_form").click( function(event) {
44         event.preventDefault();
45         var at_least_one_exists = 0;
46         var are_valid = 1;
47         $("fieldset.mtt").each( function(){
48             var title = $(this).find('input[name="title"]').val();
49             var content = $(this).find('textarea[name="content"]').val();
50             if (
51                     ( title.length == 0 && content.length > 0 )
52                  || ( title.length > 0 && content.length == 0 )
53             ) {
54                 var mtt = $(this).find('input[name="message_transport_type"]').val();
55                 var msg = _("Please specify title and content for %s");
56                 msg = msg.replace( "%s", mtt );
57                 at_least_one_exists = 1;
58                 alert(msg);
59                 return are_valid = false;
60             } else if ( title.length > 0 && content.length > 0 ) {
61                 at_least_one_exists = 1;
62             }
63         } );
64         if ( ! at_least_one_exists ) {
65             alert( _("Please fill at least one template.") );
66             return false;
67         }
68         if ( ! are_valid ) {
69             return false;
70         }
71
72         // Test if code already exists in DB
73         var new_lettercode = $("#code").val();
74         var new_branchcode = $("#branch").val();
75         [% IF ( add_form and code ) # IF edit %]
76           if ( new_lettercode != '[% code %]' ) {
77         [% END %]
78           $.ajax({
79             data: { code: new_lettercode, branchcode: new_branchcode },
80             type: 'GET',
81             url: '/cgi-bin/koha/svc/letters/',
82             success: function (data) {
83               if ( data.letters.length > 0 ) {
84                 if( new_branchcode == '' ) {
85                     alert( _("A default letter with the code '%s' already exists.").format(new_lettercode) );
86                 } else {
87                     alert( _("A letter with the code '%s' already exists for '%s'.").format(new_lettercode, new_branchcode) );
88                 }
89                 return false;
90               } else {
91                 $("#add_notice").submit();
92               }
93             },
94           });
95         [% IF ( add_form and code ) %]
96           } else {
97             $("#add_notice").submit();
98           }
99         [% END %]
100     });
101
102     var sms_limit = 160;
103     $("#content_sms").on("keyup", function(){
104         var length = $(this).val().length;
105         $("#sms_counter").html(length + "/" + sms_limit + _(" characters"));
106         if ( length  > sms_limit ) {
107             $("#sms_counter").css("color", "red");
108         } else {
109             $("#sms_counter").css("color", "black");
110         }
111     });
112     $( ".transport-types" ).accordion({ collapsible: true, active:false, animate: 200 });
113     $(".insert").on("click",function(){
114         var containerid = $(this).data("containerid");
115         insertValueQuery( containerid );
116     })
117
118     $("#tabs").tabs();
119
120     $("#saveandcontinue").on("click",function(e){
121         e.preventDefault();
122         $("#redirect").val("just_save");
123         $("#submit_form").click();
124     });
125
126 });
127 [% IF add_form or copy_form %]
128         
129     function cancel(f) {
130         $('#op').val("");
131         f.method = "get";
132         f.submit();
133     }
134
135                 function isNotNull(f,noalert) {
136                         if (f.value.length ==0) {
137         return false;
138                         }
139                         return true;
140                 }
141
142                 function isNum(v,maybenull) {
143                 var n = new Number(v.value);
144                 if (isNaN(n)) {
145                         return false;
146                         }
147                 if (maybenull==0 && v.value=='') {
148                         return false;
149                 }
150                 return true;
151                 }
152         function insertValueQuery(containerid) {
153             var fieldset = $("#" + containerid);
154             var myQuery = $(fieldset).find('textarea[name="content"]');
155             var myListBox = $(fieldset).find('select[name="SQLfieldname"]');
156
157             if($(myListBox).find('option').length > 0) {
158                 $(myListBox).find('option').each( function (){
159                     if ( $(this).attr('selected') && $(this).val().length > 0 ) {
160                         $(myQuery).insertAtCaret("<<" + $(this).val() + ">>");
161                     }
162                 });
163             }
164         }
165         [% END %]
166                 //]]>
167                 </script>
168 </head>
169 <body id="tools_letter" class="tools">
170 [% INCLUDE 'header.inc' %]
171 [% INCLUDE 'letters-search.inc' %]
172
173 <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo; [% IF ( add_form or copy_form) %][% IF ( modify ) %]<a href="/cgi-bin/koha/tools/letter.pl">Notices &amp; Slips</a> &rsaquo; Modify notice[% ELSE %] <a href="/cgi-bin/koha/tools/letter.pl">Notices &amp; Slips</a> &rsaquo; Add notice[% END %][% ELSE %][% IF ( add_validate or copy_validate) %] <a href="/cgi-bin/koha/tools/letter.pl">Notices &amp; Slips</a> &rsaquo; Notice added[% ELSE %][% IF ( delete_confirm ) %] <a href="/cgi-bin/koha/tools/letter.pl">Notices &amp; Slips</a> &rsaquo; Confirm deletion[% ELSE %]Notices &amp; Slips[% END %][% END %][% END %]</div>
174
175 [% IF add_form or copy_form %]<div id="doc" class="yui-t7">[% ELSE %]<div id="doc3" class="yui-t2">[% END %]
176    
177    <div id="bd">
178         <div id="yui-main">
179         <div class="yui-b">
180
181 [% IF ( no_op_set ) %]
182     <h1>Notices and Slips</h1>
183     <form method="get" action="/cgi-bin/koha/tools/letter.pl" id="selectlibrary">
184       <input type="hidden" name="searchfield" value="[% searchfield %]" />
185     [% UNLESS independant_branch %]
186       <p>
187         Select a library :
188             <select name="branchcode" id="branch" style="width:20em;">
189                 <option value="*">All libraries</option>
190                 [% PROCESS options_for_libraries libraries => Branches.all( selected => branchcode ) %]
191             </select>
192       </p>
193     [% END %]
194       <div id="toolbar" class="btn-toolbar">
195           <button type="submit" class="btn btn-default btn-sm" id="newnotice"><i class="fa fa-plus"></i> New notice</button>
196         <input type="hidden" id="op" name="op" />
197       </div>
198     </form>
199
200                 [% IF ( search ) %]
201         <p>You searched for <b>[% searchfield | html %]</b></p>
202                 [% END %]
203                 [% IF ( letter && !independant_branch) %]
204             [% select_for_copy = BLOCK %]
205             <select name="branchcode">
206                 [% FOREACH l IN Branches.all() %]
207                 <option value="[% l.branchcode %]">Copy to [% l.branchname %]</option>
208                 [% END %]
209             </select>
210             [% END %]
211         [% END %]
212         [% IF letter %]
213           <table id="lettert">
214             <thead>
215               <tr>
216                 <th>Library</th>
217                 <th>Module</th>
218                 <th>Code</th>
219                 <th>Name</th>
220                 <th class="nosort">Copy notice</th>
221                 <th class="nosort">Actions</th>
222               </tr>
223             </thead>
224             <tbody>
225               [% FOREACH lette IN letter %]
226                 [% can_edit = lette.branchcode || !independant_branch %]
227                 <tr>
228                   <td>[% IF lette.branchname %][% lette.branchname %][% ELSE %](All libraries)[% END %]</td>
229                   <td>[% lette.module %]</td>
230                   <td>[% lette.code %]</td>
231                   <td>[% lette.name %]</td>
232                   <td class="actions">
233                     [% IF !independant_branch || !lette.branchcode %]
234                       <form method="post" action="/cgi-bin/koha/tools/letter.pl">
235                         <input type="hidden" name="op" value="copy_form" />
236                         <input type="hidden" name="oldbranchcode" value="[% lette.branchcode %]" />
237                         <input type="hidden" name="module" value="[% lette.module %]" />
238                         <input type="hidden" name="code" value="[% lette.code %]" />
239                         [% IF independant_branch %]
240                           <input type="hidden" name="branchcode" value="[% independant_branch %]" />
241                         [% ELSE %]
242                           [% select_for_copy %]
243                         [% END %]
244                         <button class="btn btn-default btn-xs"><i class="fa fa-clone"></i> Copy</button>
245                       </form>
246                     [% END %]
247                   </td>
248                   <td class="actions">
249                     [% IF can_edit %]
250                       <a class="btn btn-default btn-xs" href="/cgi-bin/koha/tools/letter.pl?op=add_form&amp;branchcode=[% lette.branchcode %]&amp;module=[% lette.module %]&amp;code=[% lette.code %]"><i class="fa fa-pencil"></i> Edit</a>
251                     [% END %]
252                     [% IF !lette.protected && can_edit %]
253                       <a class="btn btn-default btn-xs" href="/cgi-bin/koha/tools/letter.pl?op=delete_confirm&amp;branchcode=[%lette.branchcode %]&amp;module=[% lette.module %]&amp;code=[% lette.code %]"><i class="fa fa-trash"></i> Delete</a>
254                     [% END %]
255                   </td>
256                 </tr>
257               [% END %]
258             </tbody>
259           </table>
260         [% ELSE %]
261           <div class="dialog message">
262           [% IF ( branchcode ) %]
263              <p>There are no notices for this library.</p>
264           [% ELSE %]
265               <p>There are no notices.</p>
266           [% END %]
267           </div>
268         [% END %]
269 [% END %]
270
271         
272 [% IF add_form or copy_form %]
273 <h1>[% IF ( modify ) %]Modify notice[% ELSE %]Add notice[% END %]</h1>
274
275     <div id="toolbar" class="btn-toolbar">
276         <div class="btn-group">
277             <button class="btn btn-default btn-sm" id="submit_form"><i class="fa fa-save"></i> Save</button>
278             <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
279             <span class="caret"></span>
280             </button>
281             <ul class="dropdown-menu">
282                 <li><a id="saveandcontinue" href="#">Save and continue editing</a></li>
283             </ul>
284         </div>
285         <a class="btn btn-default btn-sm cancel" href="/cgi-bin/koha/tools/letter.pl"><i class="fa fa-remove"></i> Cancel</a>
286     </div>
287
288         <form id="add_notice" name="Aform" method="post" enctype="multipart/form-data" class="validate">
289         [% IF add_form %]
290           <input type="hidden" name="op" id="op" value="add_validate" />
291         [% ELSE %]
292           <input type="hidden" name="op" id="op" value="copy_validate" />
293         [% END %]
294
295                 <input type="hidden" name="checked" value="0" />
296                 [% IF ( modify ) %]
297                 <input type="hidden" name="add" value="0" />
298                 [% ELSE %]
299                 <input type="hidden" name="add" value="1" />
300                 [% END %]
301                 <fieldset class="rows">
302             <input type="hidden" name="oldbranchcode" value="[% oldbranchcode %]" />
303             <ol>
304             [% IF independant_branch %]
305                 <input type="hidden" name="branchcode" value="[% independant_branch %]" />
306             [% ELSE %]
307             <li>
308
309                 [% IF adding %]
310                     <label for="branch">Library:</label>
311                     <select name="branchcode" id="branch" style="width:20em;">
312                         <option value="">All libraries</option>
313                         [% PROCESS options_for_libraries libraries => Branches.all( selected => branchcode ) %]
314                     </select>
315                 [% ELSE %]
316                     <span class="label">Library:</span>
317                     <input type="hidden" id="branch" name="branchcode" value="[% branchcode %]" />
318                     [% IF ( branchcode ) %]
319                        [% Branches.GetName( branchcode ) %]
320                     [% ELSE %]
321                         All libraries
322                     [% END %]
323                 [% END %]
324             </li>
325             [% END %]
326                         <li>
327                                 <label for="module">Koha module:</label>
328                                 <input type="hidden" name="oldmodule" value="[% module %]" />
329                 [% IF adding  %]
330                   <select name="module" id="newmodule">
331                 [% ELSE %]
332                   <select name="module" id="module">
333                 [% END %]
334                                     [% IF ( module == "acquisition" ) %]
335                                       <option value="acquisition" selected="selected">Acquisition</option>
336                                     [% ELSE %]
337                                       <option value="acquisition" >Acquisition</option>
338                                     [% END %]
339                                     [% IF ( module == "catalogue" ) %]
340                                       <option value="catalogue" selected="selected">Catalog</option>
341                                     [% ELSE %]
342                                       <option value="catalogue" >Catalog</option>
343                                     [% END %]
344                                     [% IF ( module == "circulation" ) %]
345                                       <option value="circulation" selected="selected">Circulation</option>
346                                     [% ELSE %]
347                                       <option value="circulation">Circulation</option>
348                                     [% END %]
349                                     [% IF ( module == "orderacquisition" ) %]
350                                       <option value="orderacquisition" selected="selected">Order acquisition</option>
351                                     [% ELSE %]
352                                       <option value="orderacquisition">Order acquisition</option>
353                                     [% END %]
354                                     [% IF ( module == "claimacquisition" ) %]
355                                       <option value="claimacquisition" selected="selected">Claim acquisition</option>
356                                     [% ELSE %]
357                                       <option value="claimacquisition">Claim acquisition</option>
358                                     [% END %]
359                                     [% IF ( module == "claimissues" ) %]
360                                       <option value="claimissues" selected="selected">Claim serial issue</option>
361                                     [% ELSE %]
362                                       <option value="claimissues">Claim serial issue</option>
363                                     [% END %]
364                                     [% IF ( module == "reserves" ) %]
365                                       <option value="reserves" selected="selected">Holds</option>
366                                     [% ELSE %]
367                                       <option value="reserves">Holds</option>
368                                     [% END %]
369                                     [% IF ( module == "members" ) %]
370                                       <option value="members" selected="selected">Members</option>
371                                     [% ELSE %]
372                                       <option value="members">Members</option>
373                                     [% END %]
374                                     [% IF ( module == "serial" ) %]
375                                       <option value="serial" selected="selected">Serials (routing list)</option>
376                                     [% ELSE %]
377                                       <option value="serial">Serials (routing list)</option>
378                                     [% END %]
379                                     [% IF ( module == "suggestions" ) %]
380                                       <option value="suggestions" selected="selected">Suggestions</option>
381                                     [% ELSE %]
382                                       <option value="suggestions">Suggestions</option>
383                                     [% END %]
384                 </select>
385             </li>
386             <li>
387               [% IF adding %]
388                   <label for="code" class="required">Code:</label>
389                   <input type="text" id="code" name="code" size="20" maxlength="20" value="" required="required"/>
390                   <span class="required">Required</span>
391               [% ELSE %]
392                   <span class="label">Code:</span>
393                   <input type="hidden" id="code" name="code" value="[% code %]" />
394                   [% code %]
395               [% END %]
396             </li>
397             <li>
398               <label for="name" class="required">Name:</label>
399               <input type="text" id="name" name="name" size="60" value="[% letter_name %]" required="required" />
400               <span class="required">Required</span>
401             </li>
402         </ol>
403     </fieldset>
404         [% IF Koha.Preference('TranslateNotices') %]
405         <div style="clear:both"></div>
406             <div id="tabs">
407                 <ul>
408                     <li><a href="#lang_default">Default</a></li>
409                     [% FOR language IN languages %]
410                         [% FOR sublanguage IN language.sublanguages_loop %]
411                             [% IF language.plural %]
412                                 <li><a href="#lang_[% sublanguage.rfc4646_subtag %]">[% sublanguage.native_description %] [% sublanguage.region_description %] ([% sublanguage.rfc4646_subtag %])</a></li>
413                             [% ELSE %]
414                                 <li><a href="#lang_[% sublanguage.rfc4646_subtag %]">[% sublanguage.native_description %] ([% sublanguage.rfc4646_subtag %])</a></li>
415                             [% END %]
416                         [% END %]
417                     [% END %]
418                 </ul>
419         [% END %]
420
421         [% FOREACH lang IN letters.keys %]
422             <div id="lang_[% lang %]">
423         <div class="transport-types" style="clear:both">
424         [% FOR mtt IN letters.$lang.templates.keys.sort %]
425             [% SET letter = letters.$lang.templates.$mtt %]
426             <h3>
427                 [% SWITCH letter.message_transport_type %]
428                 [% CASE 'email' %]
429                   Email
430                 [% CASE 'print' %]
431                   Print
432                 [% CASE 'sms' %]
433                   SMS
434                 [% CASE 'feed' %]
435                   Feed
436                 [% CASE 'phone' %]
437                   Phone
438                 [% CASE %]
439                   [% letter.message_transport_type %]
440                 [% END %]
441             </h3>
442             [% IF letter.message_transport_type == "sms" and not Koha.Preference("SMSSendDriver") %]
443               <fieldset class="rows mtt" id="[% letter.message_transport_type %]_[% lang %]" disabled="disabled">
444                 <div class="dialog message">You should enable the SMSSendDriver preference to use the SMS templates.</div>
445             [% ELSIF letter.message_transport_type == "phone" and not Koha.Preference("TalkingTechItivaPhoneNotification") %]
446               <fieldset class="rows mtt" id="[% letter.message_transport_type %]_[% lang %]" disabled="disabled">
447                 <div class="dialog message">You should enable the TalkingTechItivaPhoneNotification preference to use the phone templates.</div>
448             [% ELSE %]
449               <fieldset class="rows mtt" id="[% letter.message_transport_type %]_[% lang %]">
450             [% END %]
451               <ol>
452                 <li>
453                   <input type="hidden" name="message_transport_type" value="[% letter.message_transport_type %]" />
454                   <input type="hidden" name="lang" value="[% lang %]" />
455                   <label for="is_html_[% letter.message_transport_type %]">HTML message:</label>
456                   [% IF letter.is_html %]
457                     <input type="checkbox" name="is_html_[% letter.message_transport_type %]" id="is_html_[% letter.message_transport_type %]" value="1" checked="checked" />
458                   [% ELSE %]
459                     <input type="checkbox" name="is_html_[% letter.message_transport_type %]" id="is_html_[% letter.message_transport_type %]" value="1" />
460                   [% END %]
461                 </li>
462                 <li>
463                   <label for="title_[% letter.message_transport_type %]">Message subject:</label><input type="text" id="title_[% letter.message_transport_type %]" name="title" size="60" value="[% letter.title %]" />
464                 </li>
465                 <li>
466                   <label for="SQLfieldname_[% letter.message_transport_type %]">Message body:</label>
467                   [% IF letter.message_transport_type == 'sms' %]
468                     <span id="sms_counter">[% IF letter.content && letter.content.length > 0 %][% letter.content.length %][% ELSE %]0[% END %]/160 characters</span>
469                   [% END %]
470                   <table>
471                     <tr>
472                       <td>
473                         <select name="SQLfieldname" id="SQLfieldname_[% letter.message_transport_type %]" multiple="multiple" size="9">
474                           [% FOREACH SQLfieldname IN SQLfieldnames %]
475                             <option value="[% SQLfieldname.value %]">[% SQLfieldname.text %]</option>
476                           [% END %]
477                         </select>
478                       </td>
479                         <td class="actions">
480                             <button type="button" data-containerid="[% letter.message_transport_type %]_[% lang %]" class="btn btn-default btn-sm insert">Insert <i class="fa fa-long-arrow-right"></i></button>
481                         </td>
482                       <td><textarea name="content" id="content_[% letter.message_transport_type %]" cols="80" rows="15">[% letter.content %]</textarea></td>
483                     </tr>
484                   </table>
485                 </li>
486               </ol>
487             </fieldset>
488             [% END %]
489             </div>
490         </div> <!-- / #transport-types -->
491         [% END %]
492         [% IF Koha.Preference('TranslateNotices') %]
493         </div>
494         [% END %]
495
496         [% IF code.search('DGST') %] <span class="overdue">Warning, this is a template for a Digest, as such, any references to branch data ( e.g. branches.branchname ) will refer to the borrower's home branch.</span> [% END %]
497
498         <input type="hidden" id="redirect" name="redirect" value="" />
499         <input type="hidden" name="searchfield" value="[% searchfield %]" />
500     </form>
501 [% END %]
502
503 [% IF ( add_validate or copy_validate) %]
504         Data recorded
505         <form action="[% action %]" method="post">
506         <input type="submit" value="OK" />
507         </form>
508 [% END %]
509
510 [% IF ( delete_confirm ) %]
511     <div class="dialog alert">
512         <h3>Delete notice?</h3>
513         <table>
514             <thead>
515             <tr>
516                 <th>Library</th>
517                 <th>Module</th>
518                 <th>Code</th>
519                 <th>Name</th>
520             </tr>
521             </thead>
522             <tr>
523                 <td>[% IF letter.branchcode %][% Branches.GetName( letter.branchcode ) %][% ELSE %](All libraries)[% END %]</td>
524                 <td>[% letter.module %]</td>
525                 <td>[% letter.code %]</td>
526                 <td>[% letter.name %]</td>
527             </tr>
528         </table>
529             <form action="[% action %]" method="post">
530             <input type="hidden" name="op" value="delete_confirmed">
531             <input type="hidden" name="branchcode" value="[% letter.branchcode %]" />
532             <input type="hidden" name="code" value="[% letter.code %]" />
533             <input type="hidden" name="module" value="[% letter.module %]" />
534             <button type="submit" class="approve"><i class="fa fa-check"></i> Yes, delete</button>
535         </form>
536
537         <form action="[% action %]" method="get">
538             <button type="submit" class="deny"><i class="fa fa-times"></i> No, do not delete</button>
539         </form>
540     </div>
541 [% END %]
542
543 [% IF ( delete_confirmed ) %]
544         Data deleted
545         <form action="[% action %]" method="post">
546         <input type="submit" value="OK" />
547         </form>
548 [% END %]
549
550 </div>
551 </div>
552 [% UNLESS add_form or copy_form %]
553     <div class="yui-b noprint">
554         [% INCLUDE 'tools-menu.inc' %]
555     </div>
556 [% END %]
557 </div>
558 [% INCLUDE 'intranet-bottom.inc' %]