Bug 15758: Koha::Libraries - Remove GetBranchName
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / modules / tools / quotes-upload.tt
1     [% INCLUDE 'doc-head-open.inc' %]
2     <title>Koha &rsaquo; Tools &rsaquo; Quote uploader</title>
3     [% INCLUDE 'doc-head-close.inc' %]
4     <link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/uploader.css" />
5     <link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/quotes.css" />
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.jeditable.mini.js"></script>
9     <script type="text/javascript">
10     //<![CDATA[
11     var oTable; //DataTable object
12     $(document).ready(function() {
13
14         $("#cancel_upload").on("click",function(e){
15             e.preventDefault();
16             fnAbortRead();
17         });
18         $("#cancel_quotes").on("click",function(){
19             return confirm( _("Are you sure you want to cancel this import?") );
20         });
21
22     // Credits:
23     // FileReader() code copied and hacked from:
24     // http://www.html5rocks.com/en/tutorials/file/dndfiles/
25     // fnCSVToArray() gratefully borrowed from:
26     // http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm
27
28     var reader;
29     var progress = document.querySelector('.percent');
30     $("#server_response").hide();
31
32     function yuiGetData() {
33         fnGetData(document.getElementById('quotes_editor'));
34     }
35
36     function fnAbortRead() {
37         reader.abort();
38     }
39
40     function fnErrorHandler(evt) {
41         switch(evt.target.error.code) {
42             case evt.target.error.NOT_FOUND_ERR:
43                 alert('File Not Found!');
44                 break;
45             case evt.target.error.NOT_READABLE_ERR:
46                 alert('File is not readable');
47                 break;
48             case evt.target.error.ABORT_ERR:
49                 break; // noop
50             default:
51                 alert('An error occurred reading this file.');
52         };
53     }
54
55     function fnUpdateProgress(evt) {
56         // evt is an ProgressEvent.
57         if (evt.lengthComputable) {
58             var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
59             // Increase the progress bar length.
60             if (percentLoaded < 100) {
61                 progress.style.width = percentLoaded + '%';
62                 progress.textContent = percentLoaded + '%';
63             }
64         }
65     }
66
67     function fnCSVToArray( strData, strDelimiter ){
68         // This will parse a delimited string into an array of
69         // arrays. The default delimiter is the comma, but this
70         // can be overriden in the second argument.
71
72         // Check to see if the delimiter is defined. If not,
73         // then default to comma.
74         strDelimiter = (strDelimiter || ",");
75
76         // Create a regular expression to parse the CSV values.
77         var objPattern = new RegExp(
78         (
79             // Delimiters.
80             "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
81             // Quoted fields.
82             "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
83             // Standard fields.
84             "([^\"\\" + strDelimiter + "\\r\\n]*))"
85         ),
86             "gi"
87         );
88
89         // Create an array to hold our data. Give the array
90         // a default empty first row.
91         var arrData = [[]];
92
93         // Create an array to hold our individual pattern
94         // matching groups.
95         var arrMatches = null;
96
97         // Keep looping over the regular expression matches
98         // until we can no longer find a match.
99         while (arrMatches = objPattern.exec( strData )){
100
101             // Get the delimiter that was found.
102             var strMatchedDelimiter = arrMatches[ 1 ];
103
104             // Check to see if the given delimiter has a length
105             // (is not the start of string) and if it matches
106             // field delimiter. If it does not, then we know
107             // that this delimiter is a row delimiter.
108             if ( strMatchedDelimiter.length && (strMatchedDelimiter != strDelimiter) ){
109                 // Since we have reached a new row of data,
110                 // add an empty row to our data array.
111                 // Note: if there is not more data, we will have to remove this row later
112                 arrData.push( [] );
113             }
114
115             // Now that we have our delimiter out of the way,
116             // let's check to see which kind of value we
117             // captured (quoted or unquoted).
118             if (arrMatches[ 2 ]){
119                 // We found a quoted value. When we capture
120                 // this value, unescape any double quotes.
121                 var strMatchedValue = arrMatches[ 2 ].replace(
122                 new RegExp( "\"\"", "g" ),
123                     "\""
124                 );
125             } else if (arrMatches[3]){
126                 // We found a non-quoted value.
127                 var strMatchedValue = arrMatches[ 3 ];
128             } else {
129                 // There is no more valid data so remove the row we added earlier
130                 // Is there a better way? Perhaps a look-ahead regexp?
131                 arrData.splice(arrData.length-1, 1);
132             }
133
134             // Now that we have our value string, let's add
135             // it to the data array.
136             arrData[ arrData.length - 1 ].push( strMatchedValue );
137         }
138
139         // Return the parsed data.
140         return( arrData );
141     }
142
143     function fnDataTable(aaData) {
144         for(var i=0; i<aaData.length; i++) {
145             aaData[i].unshift(i+1); // Add a column w/quote number
146         }
147
148
149         /* Transition from the quote file uploader to the quote file editor interface */
150         $('#toolbar').css("visibility","visible");
151         $('#toolbar').css("position","");
152         $('#file_editor_inst').css("visibility", "visible");
153         $('#file_editor_inst').css("position", "");
154         $('#file_uploader_inst').css("visibility", "hidden");
155         $('#save_quotes').css("visibility","visible");
156         $('#file_uploader').css("visibility","hidden");
157         $('#file_uploader').css("position","absolute");
158         $('#file_uploader').css("top","-150px");
159         $('#quotes_editor').css("visibility","visible");
160         $("#save_quotes").on("click", yuiGetData);
161         $("#delete_quote").on("click", fnClickDeleteRow);
162
163
164
165         oTable = $('#quotes_editor').dataTable( {
166             "bAutoWidth"        : false,
167             "bPaginate"         : true,
168             "bSort"             : false,
169             "sPaginationType"   : "full_numbers",
170             "sDom": '<"top pager"iflp>rt<"bottom pager"flp><"clear">',
171             "aaData"            : aaData,
172             "aoColumns"         : [
173                 {
174                     "sTitle"  : "Number",
175                     "sWidth"  : "2%",
176                 },
177                 {
178                     "sTitle"  : "Source",
179                     "sWidth"  : "15%",
180                 },
181                 {
182                     "sTitle"  : "Quote",
183                     "sWidth"  : "83%",
184                 },
185             ],
186            "fnPreDrawCallback": function(oSettings) {
187                 return true;
188             },
189             "fnRowCallback": function( nRow, aData, iDisplayIndex ) {
190                 /* do foo on various cells in the current row */
191                 var quoteNum = $('td', nRow)[0].innerHTML;
192                 $(nRow).attr("id", quoteNum); /* set row ids to quote number */
193                 $('td:eq(0)', nRow).click(function() {$(this.parentNode).toggleClass('selected',this.clicked);}); /* add row selectors */
194                 $('td:eq(0)', nRow).attr("title", _("Click ID to select/deselect quote"));
195                 /* apply no_edit id to noEditFields */
196                 noEditFields = [0]; /* number */
197                 for (i=0; i<noEditFields.length; i++) {
198                     $('td', nRow)[noEditFields[i]].setAttribute("id","no_edit");
199                 }
200                 return nRow;
201             },
202            "fnDrawCallback": function(oSettings) {
203                 /* Apply the jEditable handlers to the table on all fields w/o the no_edit id */
204                 $('#quotes_editor tbody td[id!="no_edit"]').editable( function(value, settings) {
205                         var cellPosition = oTable.fnGetPosition( this );
206                         oTable.fnUpdate(value, cellPosition[0], cellPosition[1], false, false);
207                         return(value);
208                     },
209                     {
210                     "callback"      : function( sValue, y ) {
211                                           oTable.fnDraw(false); /* no filter/sort or we lose our pagination */
212                                       },
213                     "height"        : "14px",
214                 });
215            },
216         });
217         $('#footer').css("visibility","visible");
218     }
219
220     function fnHandleFileSelect(evt) {
221         // Reset progress indicator on new file selection.
222         progress.style.width = '0%';
223         progress.textContent = '0%';
224
225         reader = new FileReader();
226         reader.onerror = fnErrorHandler;
227         reader.onprogress = fnUpdateProgress;
228         reader.onabort = function(e) {
229             alert('File read cancelled');
230             parent.location='quotes-upload.pl';
231         };
232         reader.onloadstart = function(e) {
233             $('#cancel_upload').show();
234             $('#progress_bar').addClass("loading");
235         };
236         reader.onload = function(e) {
237             // Ensure that the progress bar displays 100% at the end.
238             progress.style.width = '100%';
239             progress.textContent = '100%';
240             $('#cancel_upload').hide();
241             quotes = fnCSVToArray(e.target.result, ',');
242             fnDataTable(quotes);
243         }
244
245         // perform various sanity checks on the target file prior to uploading...
246         var fileType = evt.target.files[0].type || 'unknown';
247         var fileSizeInK = Math.round(evt.target.files[0].size/1024);
248
249         if (!fileType.match(/comma-separated-values|csv|excel|calc/i)) {
250             alert(_("Uploads limited to csv. Incorrect filetype: %s").format(fileType));
251             parent.location='quotes-upload.pl';
252             return;
253         }
254         if (fileSizeInK > 512) {
255             if (!confirm(_("%s %s KB Do you really want to upload this file?").format(evt.target.files[0].name, fileSizeInK))) {
256                 parent.location='quotes-upload.pl';
257                 return;
258             }
259         }
260         // Read in the image file as a text string.
261         reader.readAsText(evt.target.files[0]);
262     }
263
264     $('#file_upload').one('change', fnHandleFileSelect);
265
266     });
267
268     function fnGetData(element) {
269         var jqXHR = $.ajax({
270             url         : "/cgi-bin/koha/tools/quotes/quotes-upload_ajax.pl",
271             type        : "POST",
272             contentType : "application/x-www-form-urlencoded", // we must claim this mimetype or CGI will not decode the URL encoding
273             dataType    : "json",
274             data        : {
275                             "quote"     : encodeURI ( JSON.stringify(oTable.fnGetData()) ),
276                             "action"    : "add",
277                           },
278             success     : function(){
279                             var response = JSON.parse(jqXHR.responseText);
280                             if (response.success) {
281                                 alert(_("%s quotes saved.").format(response.records));
282                                 window.location.reload(true);   // is this the best route?
283                             }
284                             else {
285                                 alert(_("%s quotes saved, but an error has occurred. Please ask your administrator to check the server log for more details.").format(response.records));
286                                 window.location.reload(true);   // is this the best route?
287                             }
288                           },
289         });
290     }
291
292     function fnClickDeleteRow() {
293         var idsToDelete = oTable.$('.selected').map(function() {
294               return this.id;
295         }).get().join(', ');
296         if (!idsToDelete) {
297             alert(_("Please select a quote(s) by clicking the quote id(s) you desire to delete."));
298         }
299         else if (confirm(_("Are you sure you wish to delete quote(s) %s?").format(idsToDelete))) {
300             oTable.$('.selected').each(function(){
301                 oTable.fnDeleteRow(this);
302             });
303         }
304     }
305
306     //]]>
307     </script>
308 </head>
309 <body id="tools_quotes" class="tools">
310 [% INCLUDE 'header.inc' %]
311 [% INCLUDE 'cat-search.inc' %]
312
313 <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; <a href="/cgi-bin/koha/tools/quotes.pl">Quote editor</a> &rsaquo; Quote uploader</div>
314
315 <div id="doc3" class="yui-t2">
316     <div id="bd">
317         <div id="yui-main">
318             <div class="yui-b">
319                 [% INCLUDE 'quotes-upload-toolbar.inc' %]
320                 <h2>Quote uploader</h2>
321                 <div id="instructions">
322                 <fieldset id="file_uploader_help" class="rows">
323                     <legend>Instructions</legend>
324                     <div id="file_uploader_inst">
325                         <ul>
326                         <li>The quote uploader accepts standard csv files with two columns: "source","text"</li>
327                         <li>Click the "Choose File" button and select the csv file to be uploaded.</li>
328                         <li>The file will be imported into an editable table for review prior to saving.</li>
329                         </ul>
330                     </div>
331                     <div id="file_editor_inst">
332                         <ul>
333                         <li>Click on any field to edit the contents; Press the &lt;Enter&gt; key to save edit.</li>
334                         <li>Click on one or more quote numbers to select entire quotes for deletion; Click the 'Delete Quote(s)' button to delete selected quotes.</li>
335                         <li>Click the 'Save Quotes' button in the toolbar to save the entire batch of quotes.</li>
336                         </ul>
337                     </div>
338                 </fieldset>
339                 </div>
340                 <fieldset id="file_uploader" class="rows">
341                     <legend>Upload quotes</legend>
342                     <div id="file_upload">
343                         <input type="file" name="file" />
344                         <button id="cancel_upload" style="display:none">Cancel upload</button>
345                         <div id="progress_bar"><div class="percent">0%</div></div>
346                     </div>
347                 </fieldset>
348                 <table id="quotes_editor" style="visibility: hidden;">
349                 <thead>
350                     <tr>
351                         <th>Source</th>
352                         <th>Text</th>
353                         <th>Actions</th>
354                     </tr>
355                 </thead>
356                 <tbody>
357                     <!-- tbody content is generated by DataTables -->
358                     <tr>
359                         <td></td>
360                         <td>Loading data...</td>
361                         <td></td>
362                     </tr>
363                 </tbody>
364                 </table>
365                 <fieldset id="footer" class="action" style="visibility: hidden;">
366                 </fieldset>
367             </div>
368         </div>
369     <div class="yui-b noprint">
370         [% INCLUDE 'tools-menu.inc' %]
371     </div>
372 </div>
373 [% INCLUDE 'intranet-bottom.inc' %]