source: main/trunk/greenstone3/web/interfaces/default/js/javascript-global-functions.js@ 38238

Last change on this file since 38238 was 38238, checked in by anupama, 9 months ago

Moving the handling of the silent gsdl_cgi errors (that result in perl die statements, but yet do not cause ajax error functions to be called, and the ajax success callback function is still called instead) to be processed at toplevel when first encountered: in javascript_global_functions.js::_callMetadataServer() and gsajaxapi.js::urlPostAsync and gsajaxapi.js::urlPostSync(). The latter two are untested, but I think (something like) this is needed there as well. Basically, if no errorcallback is registered, we see an alert message popup with the otherwise silent error message displayed. If an error callback is registered, then the silent error message is at least printed to console, and the errorcallback can handle the rest as the coder chooses.

File size: 42.5 KB
Line 
1var SUCCESS = 1;
2var ACCEPTED = 2;
3var ERROR = 3;
4var CONTINUING = 10;
5var COMPLETED = 11;
6var HALTED = 12;
7
8gs.functions = {};
9
10gs.jqGet = function(id)
11{
12 return $("#" + id.replace(/\./g, "\\.").replace(/:/g,"\\:"));
13}
14
15// Debugging function to print a string's non-basic chars in hex. So does string2hex on all non-basic and non-printable ASCII chars
16// Dr Bainbridge said that printing anything with charCode over 128 in hex is okay, but I'd already made extra allowances for non-printable ASCII
17// Based on https://stackoverflow.com/questions/36637146/javascript-encode-string-to-hex/36637293
18// https://stackoverflow.com/questions/21647928/javascript-unicode-string-to-hex
19gs.functions.debug_unicode_string = function(str) {
20 var hex, i;
21
22 var result = "";
23 for (i=0; i<str.length; i++) {
24 charcode = str.charCodeAt(i);
25 // ASCII table: https://cdn.sparkfun.com/assets/home_page_posts/2/1/2/1/ascii_table_black.png
26 // if the unicode character code pt is less than the ASCII code for space and greater than for tilda, let's display the char in hex (x0000 format)
27 if(charcode < 20 || charcode > 126) { //doesn't work: if(str.charAt(i) < ' ' || str.charAt(i) > '~') {
28 hex = charcode.toString(16);
29 result += "x{" + ("000"+hex).slice(-4) + "}"; // looks like: x{4-char-codepoint}
30 }
31 else {
32 result += str.charAt(i);
33 }
34 }
35
36 return result;
37}
38
39gs.functions.ajaxRequest = function()
40{
41 var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
42 if(window.ActiveXObject)
43 {
44 for (var i=0; i<activexmodes.length; i++)
45 {
46 try
47 {
48 return new ActiveXObject(activexmodes[i]);
49 }
50 catch(e){}
51 }
52 }
53 else if (window.XMLHttpRequest)
54 {
55 return new XMLHttpRequest();
56 }
57 else
58 {
59 return false
60 }
61}
62
63gs.functions.hasClass = function(elem, classVal)
64{
65 if(!elem || !elem.getAttribute("class"))
66 {
67 return false;
68 }
69
70 return (elem.getAttribute("class").search(classVal) != -1)
71}
72
73gs.functions.getElementsByClassName = function(cl)
74{
75 var nodes = new Array();
76 var classRegEx = new RegExp('\\b'+cl+'\\b');
77 var allElems = document.getElementsByTagName('*');
78
79 for (var i = 0; i < allElems.length; i++)
80 {
81 var classes = allElems[i].className;
82 if (classRegEx.test(classes))
83 {
84 nodes.push(allElems[i]);
85 }
86 }
87 return nodes;
88};
89
90gs.functions.makeToggle = function(buttons, divs)
91{
92 var buttonArray = (buttons.length) ? buttons : [buttons];
93 var divArray = (divs.length) ? divs : [divs];
94
95 for(var i = 0; i < buttonArray.length; i++)
96 {
97 buttonArray[i].onclick = function()
98 {
99 for(var j = 0; j < divArray.length; j++)
100 {
101 if(divArray[j].style.display == "none")
102 {
103 divArray[j].style.display = "block";
104 }
105 else
106 {
107 divArray[j].style.display = "none";
108 }
109 }
110
111 for(var j = 0; j < buttonArray.length; j++)
112 {
113 if(buttonArray[j].getAttribute("src") == gs.imageURLs.collapse)
114 {
115 buttonArray[j].setAttribute("src", gs.imageURLs.expand);
116 }
117 else if(buttonArray[j].getAttribute("src") == gs.imageURLs.expand)
118 {
119 buttonArray[j].setAttribute("src", gs.imageURLs.collapse);
120 }
121 }
122 };
123 }
124}
125
126gs.functions.checkForErrors = function(xml)
127{
128 var errorElems = xml.getElementsByTagName("error");
129
130 if(errorElems && errorElems.length > 0)
131 {
132 var errorString = gs.text.dse.error_saving_changes + ": ";
133 for(var i = 0; i < errorElems.length; i++)
134 {
135 errorString += " " + errorElems.item(i).firstChild.nodeValue;
136 }
137 alert(errorString);
138 return true;
139 }
140 return false; //No errors
141}
142
143gs.functions.validateXML = function(txt)
144{
145 // code for IE
146 if (window.ActiveXObject)
147 {
148 var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
149 xmlDoc.async = "false";
150 xmlDoc.loadXML(document.all(txt).value);
151
152 if(xmlDoc.parseError.errorCode!=0)
153 {
154 txt = dse.error_code + ": " + xmlDoc.parseError.errorCode + "\n";
155 txt = txt + dse.error_reason + ": " + xmlDoc.parseError.reason;
156 txt = txt + dse.error_line + ": " + xmlDoc.parseError.line;
157 console.log(txt);
158 return null;
159 }
160
161 return xmlDoc;
162 }
163 // code for Mozilla, Firefox, Opera, etc.
164 else if (document.implementation.createDocument)
165 {
166 var parser = new DOMParser();
167 var xmlDoc = parser.parseFromString(txt,"text/xml");
168
169 if (xmlDoc.getElementsByTagName("parsererror").length > 0)
170 {
171 console.log(gs.text.dse.xml_error);
172 return null;
173 }
174
175 return xmlDoc;
176 }
177 else
178 {
179 console.log(gs.text.dse.browse_cannot_validate_xml);
180 }
181 return null;
182}
183
184gs.functions.buildCollections = function(collections, finalFunction)
185{
186 if(!collections || collections.length == 0)
187 {
188 console.log("List of collections to build is empty");
189 return;
190 }
191
192 var counter = 0;
193 var buildFunction = function()
194 {
195 var ajax = new gs.functions.ajaxRequest();
196 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=BuildCollection&s1.collection=" + collections[counter]);
197 ajax.onreadystatechange = function()
198 {
199 if(ajax.readyState == 4 && ajax.status == 200)
200 {
201 var text = ajax.responseText;
202 var xml = gs.functions.validateXML(text);
203
204 if(!xml || gs.functions.checkForErrors(xml))
205 {
206 console.log("Could not build collection -> " + collections[counter] + ", aborting");
207 return;
208 }
209
210 var status = xml.getElementsByTagName("status")[0];
211 var pid = status.getAttribute("pid");
212
213 gs.functions.startCheckLoop(pid, "BuildCollection", function()
214 {
215 var localAjax = new gs.functions.ajaxRequest();
216 localAjax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ActivateCollection&s1.collection=" + collections[counter], true);
217 localAjax.onreadystatechange = function()
218 {
219 if(localAjax.readyState == 4 && localAjax.status == 200)
220 {
221 var localText = localAjax.responseText;
222 var localXML = gs.functions.validateXML(localText);
223
224 if(!xml || gs.functions.checkForErrors(xml))
225 {
226 console.log("Could not activate collection -> " + collections[counter] + ", aborting");
227 return;
228 }
229
230 var localStatus = localXML.getElementsByTagName("status")[0];
231 if(localStatus)
232 {
233 var localPID = localStatus.getAttribute("pid");
234 gs.functions.startCheckLoop(localPID, "ActivateCollection", function()
235 {
236 if (++counter == collections.length)
237 {
238 //Run this function once we are done building all the collections
239 if(finalFunction){finalFunction();}
240 }
241 else
242 {
243 buildFunction();
244 }
245 });
246 }
247 }
248 }
249 localAjax.send();
250 });
251 }
252 }
253 ajax.send();
254 }
255 buildFunction();
256}
257
258gs.functions.startCheckLoop = function(pid, serverFunction, callbackFunction)
259{
260 var ajaxFunction = function()
261 {
262 var ajax = new gs.functions.ajaxRequest();
263 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=s&ro=1&s=" + serverFunction + "&s1.pid=" + pid, true);
264 ajax.onreadystatechange = function()
265 {
266 if(ajax.readyState == 4 && ajax.status == 200)
267 {
268 var text = ajax.responseText;
269 var xml = gs.functions.validateXML(text);
270
271 if(!xml || gs.functions.checkForErrors(xml))
272 {
273 console.log("Could not check status of " + serverFunction + ", there was an error in the XML, aborting");
274 return;
275 }
276
277 var status = xml.getElementsByTagName("status")[0];
278 var code = status.getAttribute("code");
279
280 if (code == COMPLETED || code == SUCCESS)
281 {
282 callbackFunction();
283 }
284 else if (code == HALTED || code == ERROR)
285 {
286 console.log("Could not check status of " + serverFunction + ", there was an error on the server, aborting");
287 }
288 else
289 {
290 setTimeout(ajaxFunction, 1000);
291 }
292 }
293 }
294 ajax.send();
295 }
296 ajaxFunction();
297}
298
299function inc(a, b)
300{
301 var carry = 0;
302 var num = 0;
303 var i = 0;
304
305 while((carry || (i < a.length) || (i < b.length)) && (i < 100))
306 {
307 num = carry;
308 if(i < a.length){num += a[i];}
309 if(i < b.length){num += b[i];}
310
311 if(num >= 256)
312 {
313 num -= 256;
314 carry = 1;
315 }
316 else
317 {
318 carry = 0;
319 }
320
321 a[i] = num;
322
323 i++;
324 }
325}
326
327function ifposDec(a, b)
328{
329 var carry = 0;
330 var num = 0;
331 var i = 0;
332
333 if(b.length > a.length){return a;}
334 if(b.length == a.length)
335 {
336 i = a.length - 1;
337 while(i >= 0)
338 {
339 if(a[i] > b[i]){break;}
340 if(a[i] < b[i]){return a;}
341 i--;
342 }
343 }
344
345 i = 0;
346 var len = 0;
347 var outString = "";
348 while((i < a.length) || (i < b.length))
349 {
350 num = -carry;
351 if(i < a.length){num += a[i];}
352 if(i < b.length){num -= b[i];}
353
354 if(num < 0)
355 {
356 num += 256;
357 carry = 1;
358 }
359 else
360 {
361 carry = 0;
362 }
363
364 a[i] = num;
365 outString += num + ","
366 i++
367
368 if(num != 0){len = i}
369 }
370
371 if(len < a.length)
372 {
373 a = a.slice(0, len);
374 }
375
376 return a;
377}
378
379function convertNum(a)
380{
381 var result = new Array();
382 var i;
383 var convert = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
384
385 if(a.length == 0)
386 {
387 result.push("0");
388 return result;
389 }
390
391 for(i = a.length - 1; i >= 0; i--)
392 {
393 result.push(convert[Math.floor(a[i]/16)]);
394 result.push(convert[Math.floor(a[i]%16)]);
395 }
396
397 var resultString = "";
398 for(var j = 0; j < result.length; j++)
399 {
400 resultString += result[j];
401 }
402
403 return resultString;
404}
405
406gs.functions.hashString = function(str)
407{
408 var remainder = new Array();
409 var primePow = new Array();
410 var pow =
411 [
412 255, 255, 255,
413 255, 255, 255,
414 255, 255, 255,
415 255, 255, 1
416 ];
417
418 for(var i = 0; i < 8; i++)
419 {
420 primePow.push(pow.slice()); //The javascript way to do an array copy (yuck!)
421 inc(pow, pow);
422 }
423
424 for(var i = 0; i < str.length; i++)
425 {
426 var c = str.charCodeAt(i);
427
428 if(remainder.length == 99)
429 {
430 return null;
431 }
432
433 for(var j = remainder.length; j > 0; j--)
434 {
435 remainder[j] = remainder[j-1];
436 }
437 remainder[0] = c;
438
439 for(var j = 7; j >= 0; j--)
440 {
441 remainder = ifposDec(remainder, primePow[j]);
442 }
443 }
444
445 return convertNum(remainder);
446}
447
448// This method performs an AJAX call after working out, based on parameters and internal decision-making code,
449// if it's using GET or POST,
450// asynchronous or synchronous AJAX,
451// jQuery's .ajax() method or gsajaxapi.js' regular JavaScript way of calling AJAX (necessary functions
452// now ported from GS2 to GS3)
453// and whether it needs to transmit the payload in URL or data structure (Java object) form.
454// In the past, the AJAX calls to metadataserver.pl only dealt with URLs and used jQuery .ajax(). As a
455// consequence of this particular combination, the calls in the past were all GET operations.
456//
457// - payload param: contains both the URL form and the data object form of the package to transmit over
458// AJAX to metadataserver.pl. Based on the parameters and some internal variables, _callMetadataServer()
459// determines which to use.
460// - opts param: No function overloading in JavaScript. Can pass a custom object, however can pass opts,
461// see http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices
462//
463// BEWARE:
464// errorResponseFunction is at present only called on error if using jQuery ajax (sync/async) and not if using gsajaxapi (sync/async).
465gs.functions._callMetadataServer = function(callingFunction, payload, successResponseFunction, errorResponseFunction, opts)
466{
467
468 // async AJAX by default for get operations: because internal processing of 'read' operations (get meta)
469 // is not order dependent.
470 // Set/remove operations will switch to synchronous AJAX, unless opt["forceSync"] is set otherwise
471 var async_setting = true;
472 var method = "POST"; // GET was the default before
473
474 // Set to false if you wish to use the regular JavaScript AJAX way in gsajaxapi.js (will use payload.url)
475 // Set to true if using jQuery AJAX (will use payload.data).
476 var _use_jQuery_ajax_not_gsajaxapi = true;
477
478 // _use_payload_in_data_not_url_form is determined based on vars method and _use_jQuery_ajax_not_gsajaxapi
479 // If using AJAX with payload data (with jQuery) rather than using URLs containing data (payload in url):
480 // using data will allow us to use jQuery to POST stuff too.
481 // For gsajaxapi, payload to be transmitted over AJAX must be in URL form, whether GET or POST.
482 // For jQuery, AJAX calls ended up as GET when the payload is in URL form.
483 // Default used to be payload in url form. To get the default back,
484 // set method = "GET" (above, but also in calling functions that specify this optional parameter!)
485 // and set the default here below for _use_payload_in_data_not_url_form to false.
486 var _use_payload_in_data_not_url_form = true; // code will anyway set this to true for jQuery $.ajax() with POST
487
488 var _modifyingMeta = false;
489
490 var url = payload["url"]; // for jQuery GET, and for GET and POST using JavaScript AJAX
491 var data = payload["data"]; // for jQuery POST
492
493
494 // check for any caller overrides
495 if(opts != null) {
496 if(opts["requestMethod"] != null) {
497 method = opts["requestMethod"];
498 }
499 }
500
501 // sync or async? Generally, synchronous AJAX for set-meta operations, and asynchronous for get-meta ops
502 var metaServerCommand = (data["s1.a"] == null) ? data["a"] : data["s1.a"];
503 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
504 _modifyingMeta = true;
505 async_setting = false; // for 'write' operations (set/remove meta), we force sequential processing of the internal operation.
506
507 }
508 // check for any overrides by calling code that knows what it's doing
509 if (opts != null && opts["forceSync"] != null) {
510 async_setting = (!opts["forceSync"]);
511 }
512
513 if(_use_jQuery_ajax_not_gsajaxapi) {
514 if(method == "POST") {
515 _use_payload_in_data_not_url_form = true;
516 } // if GET, can use payload in URL form or in data form for jQuery AJAX
517 // to put it another way: can't do jQuery POST operations with payload in URL form
518
519 } else { // using gsajaxapi.js, which only accepts the payload in URL form, whether GET or POST
520 _use_payload_in_data_not_url_form = false;
521 }
522
523 // use the URL form or the data form to transmit the payload over AJAX?
524 // Payload in data form implies jQuery AJAX, not gsajaxapi calls,
525 // since we can't use gsajaxapi.js AJAX GET/POST calls without payload in URL form
526 if(_use_payload_in_data_not_url_form) { // using data payload to do AJAX (regardless of request method)
527
528 // for get-meta operations, go directly through metadata-server.pl
529 // for set-meta ops, should go via GS3 authentication, which is off the GS3 library servlet
530 url = (_modifyingMeta) ? gs.xsltParams.library_name : "cgi-bin/metadata-server.pl";
531
532 } else { // uses data encoded into URL, rather than a data structure.
533 data = null; // we're using the URL as payload, don't duplicate the payload to be transmitted into data
534
535 url = payload["url"]; // payload["url"] contains the URL + data encoded in URL form
536 // URL is already correct for get-meta vs meta-modification operations.
537 // For meta-modification ops, it will through GS3 authentication first rather than metadata-server.pl
538
539 }
540
541 // finally, can do the AJAX call
542
543 //console.log("*** Away to call: " + url);
544 var ajaxResponse = async_setting ? "*** No response received yet, async ajax request" : null;
545
546
547 if(_use_jQuery_ajax_not_gsajaxapi) {
548 // ajax calls default to using method GET, we want to do POST operations for get-meta and set-meta requests
549 // since get-meta-array and especially set-meta-array can be large, e.g. for user comments.
550 $.ajax({url: url, async: async_setting, type: method, data: data})
551 .done(function(response) {
552 ajaxResponse = response;
553// console.log("** (" + callingFunction + ") Response received from server: " + ajaxResponse);
554
555 //var xml = $.parseXML(response);
556 //console.log(xml);
557
558
559 // Handle the otherwise silent die statement of gsdl_cgi->generate_error(),
560 // forcing the errorResponseFunction route in such a case
561 if(response.indexOf("ERROR: ") !== -1 || response.indexOf("<Error>") !== -1) {
562 if(errorResponseFunction != null) {
563 console.log("(" + callingFunction + ") Failed with gsdlcgi error:\n" + response);
564 errorResponseFunction(response);
565 } else {
566 alert("(" + callingFunction + ") Failed\n" + response);
567 console.log("(" + callingFunction + ") Failed\n" + response);
568 }
569
570 }
571
572 else if(successResponseFunction != null) {
573 //console.log("XXXX callMetaServer - Got response: " + response);
574 successResponseFunction(response);
575 }
576 })
577 .fail(function(response) {
578 if(errorResponseFunction != null) {
579 console.log("(" + callingFunction + ") Failed\n" + response);
580 errorResponseFunction(response);
581 } else {
582 alert("(" + callingFunction + ") Failed\n" + response);
583 console.log("(" + callingFunction + ") Failed\n" + response);
584 }
585 });
586 }
587 else {
588 // USES GSAJAXAPI.JS to do AJAX. In this case, the payload must be in URL form
589
590 var splitURL = url.split("?");
591 url = splitURL[0]; // base URL
592 var params = splitURL[1]; // query-string
593
594 // Don't need to delete objects created with 'new' in JavaScript. Garbage collection will do it.
595 // http://stackoverflow.com/questions/4869712/new-without-delete-on-same-variable-in-javascript
596 var gsapi = new GSAjaxAPI(url);
597
598 // ajax calls default to using method GET, we want to do POST operations for get-meta and set-meta requests
599 // since get-meta-array and especially set-meta-array can be large, e.g. for user comments.
600
601 if(async_setting) {
602 gsapi.urlPostAsync(url, params, successResponseFunction, errorResponseFunction);
603 } else {
604 ajaxResponse = gsapi.urlPostSync(url, params);
605 //ajaxResponse = ajaxResponse;
606 }
607
608// console.log("*** (" + callingFunction + ") Response from server: " + ajaxResponse);
609
610 }
611
612// console.log("*** Finished ajax call to: " + url);
613// console.log("*** Got response: " + ajaxResponse);
614
615 return ajaxResponse;
616}
617
618// Prepare the payload (data package) to transmit to metadataserver.pl over AJAX.
619// These next 2 functions prepare both the URL version of the payload and the data object version
620// of the payload. Then calling functions, and the _callMetadataServer() function they call, will control
621// and determine which of the two forms ultimately gets used.
622// (Constructing both URL and data structure, as I could not successfully convert old URL way to data structure
623// http://stackoverflow.com/questions/8648892/convert-url-parameters-to-a-javascript-object)
624gs.functions.getBasicDataForMetadataServer = function(metaServerCommand, collection, site, documentID, metadataName,
625 metamode, metadataValue, prevMetadataValue, metadataPosition)
626{
627 // if we're doing set- or remove- metadata operations,
628 // then we need to change the data params that will make up the query string
629 // to make sure we go through GS3's authentication
630 // 1. prefix meta names with s1,
631 // 2. use s1.collection for collectionName since c is a special param name for GS2Construct
632 // 3. Additional parameters for rerouting through Authentication: a=g&rt=r&ro=1&s=ModifyMetadata
633
634 var modifyingMeta = false;
635 var prefix = "";
636 var colPropName = "c";
637 var baseURL = "cgi-bin/metadata-server.pl?";
638
639 // if we need authentication:
640 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
641 modifyingMeta = true;
642 prefix = "s1.";
643 colPropName = prefix+"collection"; // "s1.collection"
644 baseURL = gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ModifyMetadata&";
645 }
646
647
648 // 1. when using jQuery to POST, need to invoke AJAX with a data structure rather than a URL
649 var data = {};
650
651 // customizable portion of ajax call
652 data[prefix+"a"] = metaServerCommand;
653 data[colPropName] = collection;
654 data[prefix+"site"] = site;
655 data[prefix+"d"] = documentID;
656 data[prefix+"metaname"] = metadataName;
657 data[prefix+"metapos"] = metadataPosition;
658 data[prefix+"metavalue"] = metadataValue;
659 data[prefix+"prevmetavalue"] = prevMetadataValue;
660 data[prefix+"metamode"] = metamode;
661
662 if(modifyingMeta) {
663 // fixed portion of url: add the a=g&rt=r&ro=1&s=ModifyMetadata part of the GS3 URL for
664 // going through authentication. Don't prefix "s1." to these!
665 data["a"] = "g";
666 data["rt"] = "r";
667 data["ro"] = "1";
668 data["s"] = "ModifyMetadata";
669 }
670
671 // 2. Construct the URL version of the metadata-server.pl operation:
672 // for GET requests, the URL can contain the data.
673 // Regular JavaScript AJAX code in gsajaxapi.js can also POST data in URL form, but not jQuery's .ajax().
674
675
676 // If doing set- or remove- (not get-) metadata, then rewrite URLs to call GS2Construct's ModfiyMetadata service instead (which will ensure this only works when authenticated).
677 // From:
678 // <gs3server>/cgi-bin/metadata-server.pl?a=set-archives-metadata&c=smallcol&site=localsite&d=HASH01454f31011f6b6b26eaf8d7&metaname=Title&metavalue=Moo&prevmetavalue=Blabla&metamode=override
679 // To:
680 // <gs3server>/library?a=g&rt=r&ro=1&s=ModifyMetadata&s1.a=set-archives-metadata&s1.collection=smallcol&s1.site=localsite&s1.d=HASH01454f31011f6b6b26eaf8d7&s1.metaname=Title&s1.metavalue=Moo&s1.prevmetavalue=Blabla&s1.metamode=override
681
682 var extraParams = "";
683
684 if(metadataValue != null) {
685 extraParams += "&"+prefix+"metavalue=" + metadataValue;
686 }
687
688 if(metadataPosition != null)
689 {
690 extraParams += "&"+prefix+"metapos=" + metadataPosition;
691 }
692
693 if(prevMetadataValue != null) {
694 extraParams += "&"+prefix+"prevmetavalue=" + prevMetadataValue;
695 }
696
697 var url = baseURL + prefix+"a=" + metaServerCommand + "&"+colPropName+"=" + collection + "&"+prefix+"site=" + site + "&"+prefix+"d=" + documentID + "&"+prefix+"metaname=" + metadataName + extraParams + "&"+prefix+"metamode=" + metamode;
698
699 // 3. Return both the constructed url & data variants of the payload to be transmitted over ajax
700 var payload = {
701 url: url,
702 data: data
703 };
704
705 return payload;
706}
707
708// See description for getBasicDataForMetadataServer()
709gs.functions.getComplexDataForMetadataServer = function(metaServerCommand, collection, site, docArray, metamode, where)
710{
711 var docArrayJSON = JSON.stringify(docArray);
712
713 // if we're doing set- or remove- metadata operations,
714 // then we need change the data params that will make up the query string
715 // to make sure we go through GS3's authentication
716 // 1. prefix meta names with s1,
717 // 2. use s1.collection for collectionName since c is a special param name for GS2Construct
718 // 3. Additional parameters for rerouting through Authentication: a=g&rt=r&ro=1&s=ModifyMetadata
719
720 var modifyingMeta = false;
721 var prefix = "";
722 var colPropName = "c";
723 var baseURL = "cgi-bin/metadata-server.pl?";
724
725 // if we need authentication:
726 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
727 modifyingMeta = true;
728 prefix = "s1.";
729 colPropName = prefix+"collection"; // "s1.collection"
730 baseURL = gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ModifyMetadata&";
731 }
732
733 // 1. when using jQuery to POST, need to invoke AJAX with a data structure rather than a URL
734 var data = {};
735
736 // customizable portion of ajax call
737 data[prefix+"a"] = metaServerCommand;
738 data[colPropName] = collection;
739 data[prefix+"site"] = site;
740 data[prefix+"json"] = docArrayJSON;
741
742 if(where != null) {
743 data[prefix+"where"] = where;
744 }
745 if (metamode!=null) {
746 data[prefix+"metamode"] = metamode;
747 }
748
749 if(modifyingMeta) {
750 // fixed portion of url: add the a=g&rt=r&ro=1&s=ModifyMetadata part of the GS3 URL for
751 // going through authentication. Don't prefix "s1." to these!
752 data["a"] = "g";
753 data["rt"] = "r";
754 data["ro"] = "1";
755 data["s"] = "ModifyMetadata";
756 }
757
758
759 // 2. URL for when doing AJAX in URL mode. GET with jQuery allows the data to be part of the URL, but
760 // not jQuery POST. But our regular JavaScript AJAX code in gsajaxapi.js allows GET and POST with URLs
761 // containing the data.
762
763 var params = prefix+"a=" + escape(metaServerCommand); //"a=set-metadata-array";
764 if(where != null) {
765 params += "&"+prefix+"where=" + escape(where); // if where not specified, meta-server will default to setting index meta
766 //} else {
767 // params += "&"+prefix+"where=import|archives|index";
768 }
769 params += "&"+colPropName+"="+escape(collection);
770 params += "&"+prefix+"site="+escape(site);
771 params += "&"+prefix+"json="+escape(docArrayJSON);
772
773 if (metamode!=null) {
774 params += "&"+prefix+"metamode=" + escape(metamode);
775 }
776
777 // 3. Return both the constructed url & data variants of the payload to be transmitted over ajax
778 var payload = {
779 url: baseURL + params,
780 data: data
781 };
782
783 return payload;
784}
785
786/*************************
787* SET METADATA FUNCTIONS *
788*************************/
789
790gs.functions.setImportMetadata = function(collection, site, documentID, metadataName, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
791{
792 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
793
794 gs.functions._callMetadataServer(
795 "setImportMetadata",
796 gs.functions.getBasicDataForMetadataServer("set-import-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, null /*metapos*/),
797 successResponseFunction,
798 errorResponseFunction);
799
800}
801
802gs.functions.setArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
803{
804 if(metadataValue) console.log("metaval: " + metadataValue + " | " + gs.functions.debug_unicode_string(metadataValue)); //metadataValue.hexEncode()
805 if(prevMetadataValue) console.log("prevmetaval: " + prevMetadataValue + " | " + gs.functions.debug_unicode_string(prevMetadataValue));
806
807 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
808
809 if(metadataPosition != null) {
810 prevMetadataValue = null; // to force the same ultimate behaviour as in the old version of this code
811 }
812
813 gs.functions._callMetadataServer(
814 "setArchivesMetadata",
815 gs.functions.getBasicDataForMetadataServer("set-archives-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, metadataPosition),
816 successResponseFunction,
817 errorResponseFunction);
818
819}
820
821gs.functions.setIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
822{
823 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
824
825 if(metadataPosition != null) {
826 prevMetadataValue = null; // to force the same ultimate behaviour as in the old version of this code
827 }
828
829 // old version of this function would only call _callMetadataServer if either metapos
830 // or prevMetaValue had a value. So sticking to the same behaviour in rewriting this function.
831 if(metadataPosition != null || prevMetadataValue != null) {
832
833 gs.functions._callMetadataServer(
834 "setIndexMetadata",
835 gs.functions.getBasicDataForMetadataServer("set-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, metadataPosition),
836 successResponseFunction,
837 errorResponseFunction);
838 }
839}
840
841gs.functions.setMetadata = function(collection, site, documentID, metadataName, metadataValue, metamode, successResponseFunction, errorResponseFunction)
842{
843 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
844
845 var nameArray = ["setImportMetadata", "setArchivesMetadata", "setIndexMetadata"];
846 var functionArray = ["set-import-metadata", "set-archives-metadata", "set-metadata"];
847
848 for(var i = 0; i < nameArray.length; i++)
849 {
850 // previous version of this function did not allow setting metapos or prevMetavalue
851 // so leaving the behaviour the same along with function signature.
852 gs.functions._callMetadataServer(
853 nameArray[i],
854 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, metamode, metadataValue, null /*prevMetadataValue*/, null /*metadataPosition*/),
855 successResponseFunction,
856 errorResponseFunction);
857 }
858}
859
860// New. Modified version of the GS2 version of this method in gsajaxapi.js.
861// The where parameter can be specified as one or more of: import, archives, index, live
862// separated by |. If null, it is assumed to be index which is the original default
863// behaviour of calling set-metadata-array. E.g. where=import|archives|index
864// THIS METHOD IS SYNCHRONOUS by default. Set forceSync to false to override this default behaviour
865gs.functions.setMetadataArray = function(collection, site, docArray, metamode, where, successResponseFunction, forceSync, errorResponseFunction)
866{
867 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
868
869 var payload = gs.functions.getComplexDataForMetadataServer("set-metadata-array", collection, site, docArray, metamode, where);
870
871 // set operations are generally synchronous, but allow calling function to force ajax call
872 // to be synchronous or not. Default is synchronous, as it was for GS2
873 if(forceSync == null) {
874 forceSync = true;
875 }
876
877 //console.log("cgi-bin/metadata-server.pl?"+params);
878
879 var response = gs.functions._callMetadataServer("Setting metadata in "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync": forceSync, "requestMethod": "POST"});
880
881 return response;
882}
883
884
885/*************************
886* GET METADATA FUNCTIONS *
887*************************/
888
889// New. Modified version of the GS2 version of this method in gsajaxapi.js.
890// See description for setMetadataArray above for information about the 'where' parameter.
891// THIS METHOD IS SYNCHRONOUS BY DEFAULT. Set forceSync to false to override this default behaviour
892gs.functions.getMetadataArray = function(collection, site, docArray, where, successResponseFunction, forceSync, errorResponseFunction)
893{
894 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
895
896 var payload = gs.functions.getComplexDataForMetadataServer("get-metadata-array", collection, site, docArray, null /*metamode*/, where);
897
898 // get operations are generally asynchronous, but allow calling function to force ajax call
899 // to be synchronous or not. Default for get-metadata-array is synchronous, as it was for GS2
900 if(forceSync == null) {
901 forceSync = true;
902 }
903 // Objects/maps can use identifiers or strings for property names
904 // http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices
905 // https://www.w3schools.com/js/js_objects.asp
906 var response = gs.functions._callMetadataServer("Getting metadata from "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync":forceSync, "requestMethod": "POST"});
907
908 return response;
909}
910
911
912gs.functions.getImportMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
913{
914 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
915
916 var payload = gs.functions.getBasicDataForMetadataServer("get-import-metadata", collection, site, documentID, metadataName);
917 gs.functions._callMetadataServer("getImportMetadata", payload, function(responseText) {
918 var metadata = new GSMetadata(collection, site, documentID, metadataName, null, null, responseText);
919 if(successResponseFunction != null)
920 {
921 successResponseFunction(metadata);
922 }
923 },
924 errorResponseFunction);
925}
926
927gs.functions.getArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
928{
929 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
930
931 var payload = gs.functions.getBasicDataForMetadataServer("get-archives-metadata", collection, site, documentID, metadataName, null /*metamode*/, null /*metavalue*/, null /*prevmetavalue*/, metadataPosition);
932
933 gs.functions._callMetadataServer("getArchivesMetadata", payload, function(responseText) {
934 var metadata = new GSMetadata(collection, site, documentID, metadataName, null, metadataPosition, responseText); // indexPos, archivesPos, metaval (responseText)
935 if(successResponseFunction != null)
936 {
937 successResponseFunction(metadata);
938 }
939 },
940 errorResponseFunction);
941}
942
943gs.functions.getIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
944{
945 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
946
947 var payload = gs.functions.getBasicDataForMetadataServer("get-metadata", collection, site, documentID, metadataName, null /*metamode*/, null /*metavalue*/, null /*prevmetavalue*/, metadataPosition);
948
949 gs.functions._callMetadataServer("getIndexMetadata", payload, function(responseText) {
950 var metadata = new GSMetadata(collection, site, documentID, metadataName, metadataPosition, null, responseText); // indexPos, archivesPos, metaval (responseText)
951 if(successResponseFunction != null)
952 {
953 successResponseFunction(metadata);
954 }
955 },
956 errorResponseFunction);
957}
958
959/****************************
960* REMOVE METADATA FUNCTIONS *
961****************************/
962
963gs.functions.removeImportMetadata = function(collection, site, documentID, metadataName, metadataValue, successResponseFunction, errorResponseFunction)
964{
965 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
966
967 gs.functions._callMetadataServer(
968 "removeImportMetadata",
969 gs.functions.getBasicDataForMetadataServer("remove-import-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, null /*metapos*/),
970 successResponseFunction,
971 errorResponseFunction);
972}
973
974gs.functions.removeArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, successResponseFunction, errorResponseFunction)
975{
976 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
977
978 if(metadataPosition != null) {
979 metadataValue = null; // retaining behaviour of previous version of this function removeArchivesMetadata()
980 }
981
982 gs.functions._callMetadataServer(
983 "removeArchivesMetadata",
984 gs.functions.getBasicDataForMetadataServer("remove-archives-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, metadataPosition),
985 successResponseFunction,
986 errorResponseFunction);
987}
988
989gs.functions.removeIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, successResponseFunction, errorResponseFunction)
990{
991 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
992
993 if(metadataPosition != null) {
994 metadataValue = null; // retaining behaviour of previous version of this function removeIndexMetadata()
995 }
996
997 gs.functions._callMetadataServer(
998 "removeIndexMetadata",
999 gs.functions.getBasicDataForMetadataServer("remove-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, metadataPosition),
1000 successResponseFunction,
1001 errorResponseFunction);
1002}
1003
1004gs.functions.removeMetadata = function(collection, site, documentID, metadataName, metadataValue, successResponseFunction, errorResponseFunction)
1005{
1006 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1007
1008 var nameArray = ["removeImportMetadata", "removeArchivesMetadata", "removeIndexMetadata"];
1009 var functionArray = ["remove-import-metadata", "remove-archives-metadata", "remove-metadata"];
1010
1011 for(var i = 0; i < nameArray.length; i++)
1012 {
1013 gs.functions._callMetadataServer(
1014 nameArray[i],
1015 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, null /*metapos*/),
1016 successResponseFunction,
1017 errorResponseFunction);
1018 }
1019}
1020
1021gs.functions.removeMetadataAtPos = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
1022{
1023 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1024
1025 var nameArray = ["removeImportMetadata", "removeArchivesMetadata", "removeIndexMetadata"];
1026 var functionArray = ["remove-import-metadata", "remove-archives-metadata", "remove-metadata"];
1027
1028 for(var i = 0; i < nameArray.length; i++)
1029 {
1030 gs.functions._callMetadataServer(
1031 nameArray[i],
1032 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, null /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, metadataPosition),
1033 successResponseFunction,
1034 errorResponseFunction);
1035 }
1036}
1037
1038// metamode=override is in theory supported by remove_archives and remove_import metadata
1039// but not by remove_index and remove_live metadata. However, even in the former 2 cases,
1040// passing in metamode=override still appears to be ineffective, see recent svn revision 38193.
1041// And metamode accumulate prevents all removal.
1042// So for now, this function forces metamode to null, regardless of what is passed in.
1043// At least this ensures a more consistent effect with remove_index (and remove_live) functions.
1044gs.functions.removeMetadataArray = function(collection, site, docArray, metamode,
1045 where, successResponseFunction, forceSync, errorResponseFunction)
1046{
1047 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1048
1049 var payload = gs.functions.getComplexDataForMetadataServer("remove-metadata-array", collection, site, docArray, null/*metamode*/, where);
1050
1051 // set operations are generally synchronous, but allow calling function to force ajax call
1052 // to be synchronous or not. Default is synchronous, as it was for GS2
1053 if(forceSync == null) {
1054 forceSync = true;
1055 }
1056
1057 //console.log("cgi-bin/metadata-server.pl?"+params);
1058
1059 var response = gs.functions._callMetadataServer("Removing metadata from "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync": forceSync, "requestMethod": "POST"});
1060
1061 //console.log("in gs.functions.removeMetaArray: " + JSON.stringify(payload));
1062
1063 return response;
1064}
1065
1066
1067/***********************************************************
1068* ERASE METADATA FUNCTIONS *
1069* Erase all meta in docID (sectionID) that match metaname. *
1070***********************************************************/
1071
1072gs.functions.eraseImportMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1073{
1074 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1075
1076 gs.functions._callMetadataServer(
1077 "eraseImportMetadata",
1078 gs.functions.getBasicDataForMetadataServer("erase-import-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1079 successResponseFunction,
1080 errorResponseFunction);
1081}
1082
1083gs.functions.eraseArchivesMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1084{
1085 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1086
1087 gs.functions._callMetadataServer(
1088 "eraseArchivesMetadata",
1089 gs.functions.getBasicDataForMetadataServer("erase-archives-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1090 successResponseFunction,
1091 errorResponseFunction);
1092}
1093
1094gs.functions.eraseIndexMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1095{
1096 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1097
1098 gs.functions._callMetadataServer(
1099 "eraseIndexMetadata",
1100 gs.functions.getBasicDataForMetadataServer("erase-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1101 successResponseFunction,
1102 errorResponseFunction);
1103}
1104
1105gs.functions.eraseMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1106{
1107 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1108
1109 var nameArray = ["eraseImportMetadata", "eraseArchivesMetadata", "eraseIndexMetadata"];
1110 var functionArray = ["erase-import-metadata", "erase-archives-metadata", "erase-metadata"];
1111
1112 for(var i = 0; i < nameArray.length; i++)
1113 {
1114 gs.functions._callMetadataServer(
1115 nameArray[i],
1116 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1117 successResponseFunction,
1118 errorResponseFunction);
1119 }
1120}
Note: See TracBrowser for help on using the repository browser.