1 [% INCLUDE 'doc-head-open.inc' %]
2 <title>Koha › Tools › Quote uploader</title>
3 [% INCLUDE 'doc-head-close.inc' %]
4 <link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/css/uploader.css" />
5 <link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/css/datatables.css" />
6 <script type="text/javascript" src="/intranet-tmpl/prog/en/lib/jquery/plugins/jquery.dataTables.min.js"></script>
7 [% INCLUDE 'datatables-strings.inc' %]
9 <script type="text/javascript" src="/intranet-tmpl/prog/en/js/datatables.js"></script>
10 <script type="text/javascript" src="/intranet-tmpl/prog/en/js/jquery.jeditable.mini.js"></script>
11 <script type="text/javascript">
13 var oTable; //DataTable object
14 $(document).ready(function() {
17 // FileReader() code copied and hacked from:
18 // http://www.html5rocks.com/en/tutorials/file/dndfiles/
19 // fnCSVToArray() gratefully borrowed from:
20 // http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm
23 var progress = document.querySelector('.percent');
24 $("#server_response").hide();
26 function fnAbortRead() {
30 function fnErrorHandler(evt) {
31 switch(evt.target.error.code) {
32 case evt.target.error.NOT_FOUND_ERR:
33 alert('File Not Found!');
35 case evt.target.error.NOT_READABLE_ERR:
36 alert('File is not readable');
38 case evt.target.error.ABORT_ERR:
41 alert('An error occurred reading this file.');
45 function fnUpdateProgress(evt) {
46 // evt is an ProgressEvent.
47 if (evt.lengthComputable) {
48 var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
49 // Increase the progress bar length.
50 if (percentLoaded < 100) {
51 progress.style.width = percentLoaded + '%';
52 progress.textContent = percentLoaded + '%';
57 function fnCSVToArray( strData, strDelimiter ){
58 // This will parse a delimited string into an array of
59 // arrays. The default delimiter is the comma, but this
60 // can be overriden in the second argument.
62 // Check to see if the delimiter is defined. If not,
63 // then default to comma.
64 strDelimiter = (strDelimiter || ",");
66 // Create a regular expression to parse the CSV values.
67 var objPattern = new RegExp(
70 "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
72 "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
74 "([^\"\\" + strDelimiter + "\\r\\n]*))"
79 // Create an array to hold our data. Give the array
80 // a default empty first row.
83 // Create an array to hold our individual pattern
85 var arrMatches = null;
87 // Keep looping over the regular expression matches
88 // until we can no longer find a match.
89 while (arrMatches = objPattern.exec( strData )){
91 // Get the delimiter that was found.
92 var strMatchedDelimiter = arrMatches[ 1 ];
94 // Check to see if the given delimiter has a length
95 // (is not the start of string) and if it matches
96 // field delimiter. If it does not, then we know
97 // that this delimiter is a row delimiter.
98 if ( strMatchedDelimiter.length && (strMatchedDelimiter != strDelimiter) ){
99 // Since we have reached a new row of data,
100 // add an empty row to our data array.
101 // Note: if there is not more data, we will have to remove this row later
105 // Now that we have our delimiter out of the way,
106 // let's check to see which kind of value we
107 // captured (quoted or unquoted).
108 if (arrMatches[ 2 ]){
109 // We found a quoted value. When we capture
110 // this value, unescape any double quotes.
111 var strMatchedValue = arrMatches[ 2 ].replace(
112 new RegExp( "\"\"", "g" ),
115 } else if (arrMatches[3]){
116 // We found a non-quoted value.
117 var strMatchedValue = arrMatches[ 3 ];
119 // There is no more valid data so remove the row we added earlier
120 // Is there a better way? Perhaps a look-ahead regexp?
121 arrData.splice(arrData.length-1, 1);
124 // Now that we have our value string, let's add
125 // it to the data array.
126 arrData[ arrData.length - 1 ].push( strMatchedValue );
129 // Return the parsed data.
133 function fnDataTable(aaData) {
134 for(var i=0; i<aaData.length; i++) {
135 aaData[i].unshift(i+1); // Add a column w/quote number
137 $('#save_quotes').css("visibility","visible");
138 $('#file_uploader').css("visibility","hidden");
139 $('#file_uploader').css("position","absolute");
140 $('#file_uploader').css("top","-150px");
141 $('#quotes_editor').css("visibility","visible");
142 oSaveButton.on("click", yuiGetData);
143 oDeleteButton.on("click", fnClickDeleteRow);
144 oTable = $('#quotes_editor').dataTable( {
145 "bAutoWidth" : false,
148 "sPaginationType" : "full_numbers",
164 "fnPreDrawCallback": function(oSettings) {
167 "fnRowCallback": function( nRow, aData, iDisplayIndex ) {
168 /* do foo on various cells in the current row */
169 var quoteNum = $('td', nRow)[0].innerHTML;
170 $(nRow).attr("id", quoteNum); /* set row ids to quote number */
171 $('td:eq(0)', nRow).click(function() {$(this.parentNode).toggleClass('selected',this.clicked);}); /* add row selectors */
172 $('td:eq(0)', nRow).attr("title", "Click ID to select/deselect quote");
173 /* apply no_edit id to noEditFields */
174 noEditFields = [0]; /* number */
175 for (i=0; i<noEditFields.length; i++) {
176 $('td', nRow)[noEditFields[i]].setAttribute("id","no_edit");
180 "fnDrawCallback": function(oSettings) {
181 /* Apply the jEditable handlers to the table on all fields w/o the no_edit id */
182 $('#quotes_editor tbody td[id!="no_edit"]').editable( function(value, settings) {
183 var cellPosition = oTable.fnGetPosition( this );
184 oTable.fnUpdate(value, cellPosition[0], cellPosition[1], false, false);
188 "callback" : function( sValue, y ) {
189 oTable.fnDraw(false); /* no filter/sort or we lose our pagination */
195 $('#footer').css("visibility","visible");
198 function fnHandleFileSelect(evt) {
199 // Reset progress indicator on new file selection.
200 progress.style.width = '0%';
201 progress.textContent = '0%';
203 reader = new FileReader();
204 reader.onerror = fnErrorHandler;
205 reader.onprogress = fnUpdateProgress;
206 reader.onabort = function(e) {
207 alert('File read cancelled');
208 parent.location='quotes-upload.pl';
210 reader.onloadstart = function(e) {
211 $('#cancel_upload').css("visibility","visible");
212 $('#progress_bar').addClass("loading");
214 reader.onload = function(e) {
215 // Ensure that the progress bar displays 100% at the end.
216 progress.style.width = '100%';
217 progress.textContent = '100%';
218 $('#cancel_upload').css("visibility","hidden");
219 quotes = fnCSVToArray(e.target.result, ',');
223 // perform various sanity checks on the target file prior to uploading...
224 var fileType = evt.target.files[0].type || 'unknown';
225 var fileSizeInK = Math.round(evt.target.files[0].size/1024);
227 if (!fileType.match(/comma-separated-values|csv|excel/i)) {
228 alert('Incorrect filetype: '+fileType+'. Uploads limited to csv.');
229 parent.location='quotes-upload.pl';
232 if (fileSizeInK > 512) {
233 if (!confirm(evt.target.files[0].name+' is '+fileSizeInK+' K in size. Do you really want to upload this file?')) {
234 parent.location='quotes-upload.pl';
238 // Read in the image file as a text string.
239 reader.readAsText(evt.target.files[0]);
242 $('#file_upload').one('change', fnHandleFileSelect);
246 function fnGetData(element) {
248 url : "/cgi-bin/koha/tools/quotes/quotes-upload_ajax.pl",
250 contentType : "application/x-www-form-urlencoded", // we must claim this mimetype or CGI will not decode the URL encoding
253 "quote" : JSON.stringify(oTable.fnGetData()),
256 success : function(){
257 var response = JSON.parse(jqXHR.responseText);
258 if (response.success) {
259 $("#server_response").text(response.records+' quotes saved.');
262 $("#server_response").text('An error has occurred. '+response.records+' quotes saved. Please ask your administrator to check the server log for more details.');
264 $("#server_response").fadeIn(200);
269 function fnClickDeleteRow() {
270 var idsToDelete = oTable.$('.selected').map(function() {
274 alert('Please select a quote(s) by clicking the quote id(s) you desire to delete.');
276 else if (confirm('Are you sure you wish to delete quote(s) '+idsToDelete+'?')) {
277 oTable.$('.selected').each(function(){
278 oTable.fnDeleteRow(this);
283 function fnResetUpload() {
284 $('#server_response').fadeOut(200);
285 window.location.reload(true); // is this the best route?
291 <body id="tools_quotes" class="tools">
292 [% INCLUDE 'header.inc' %]
293 [% INCLUDE 'cat-search.inc' %]
295 <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> › <a href="/cgi-bin/koha/tools/quotes.pl">Quote editor</a> › Quote uploader</div>
297 <div id="doc3" class="yui-t2">
301 [% INCLUDE 'quotes-upload-toolbar.inc' %]
302 <h2>Quote uploader</h2>
303 <fieldset id="file_uploader" class="rows" style="visibility:visible;">
304 <legend>Upload quotes</legend>
305 <div id="file_upload" style="margin-left: 10px;">
306 <input type="file" name="file" />
307 <button id="cancel_upload" style="visibility:hidden;" onclick="fnAbortRead();">Cancel Upload</button>
308 <div id="progress_bar"><div class="percent">0%</div></div>
311 <div id="server_response" onclick='fnResetUpload()'>Server Response</div>
312 <table id="quotes_editor" style="float: left; width: 100%; visibility:hidden;">
321 <!-- tbody content is generated by DataTables -->
324 <td>Loading data...</td>
329 <fieldset id="footer" class="action" style="visibility:hidden; height:25px">
333 <div class="yui-b noprint">
334 [% INCLUDE 'tools-menu.inc' %]
337 [% INCLUDE 'intranet-bottom.inc' %]