]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/network.js
refactor and .cached_request method
[working/Evergreen.git] / Open-ILS / xul / staff_client / chrome / content / util / network.js
1 dump('entering util/network.js\n');
2
3 if (typeof util == 'undefined') util = {};
4 util.network = function () {
5
6         JSAN.use('util.error'); this.error = new util.error();
7         JSAN.use('util.sound'); this.sound = new util.sound();
8
9         return this;
10 };
11
12 util.network.prototype = {
13
14         'link_id' : 0,
15
16         'NETWORK_FAILURE' : null,
17
18         'simple_request' : function(method_id,params,f,override_params) {
19                 if (typeof api[method_id] == 'undefined') throw( 'Method not found for ' + method_id );
20                 var secure = true; if (typeof api[method_id].secure != 'undefined') secure = api[method_id].secure;
21                 return this.request(api[method_id].app,api[method_id].method,params,f,override_params,{ 'secure' : secure, 'method_id' : method_id });
22         },
23
24         'cached_request' : function(method_id,params,f,override_params) {
25                 if (typeof api[method_id] == 'undefined') throw( 'Method not found for ' + method_id );
26                 var secure = true; if (typeof api[method_id].secure != 'undefined') secure = api[method_id].secure;
27                 return this.request(api[method_id].app,api[method_id].method,params,f,override_params,{ 'secure' : secure, 'want_cached' : true, 'method_id' : method_id });
28         },
29
30         'get_result' : function (req) {
31                 var obj = this;
32                 var result;
33                 try {
34                         result = req.getResultObject(); 
35                 } catch(E) {
36                         try {
37                                 if (instanceOf(E, NetworkFailure)) {
38                                         obj.NETWORK_FAILURE = E;
39                                 } else {
40                                         try { obj.NETWORK_FAILURE = js2JSON(E); } catch(F) { dump(F + '\n'); obj.NETWORK_FAILURE = E; };
41                                 }
42                         } catch(I) { 
43                                 obj.NETWORK_FAILURE = 'Unknown status';
44                         }
45                         result = null;
46                 }
47                 return result;
48         },
49
50         'is_cacheable' : function(method_id) {
51                 return (api[method_id].cacheable);
52         },
53
54         'request' : function (app,name,params,f,override_params,_params) {
55
56                 var obj = this;
57
58                 try { 
59
60                 if (_params && _params.want_cached) if ( this.is_cacheable(_params.method_id) ) {
61                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
62                         var key = (app + '+' + name + '+' + js2JSON(params)).replace(/\./g,'_');
63                         var x = data.cached_request[key];
64                         if (x) {
65                                 obj.error.sdump('D_CACHE','Cached request found for key = \n' + key + '\n' + js2JSON(x) + '\n');                
66                                 switch(x.status) {
67                                         case 'pending' :
68                                                 if (f) { // Setup a self-destroying observer on the pending request to handle this request
69                                                         obj.error.sdump('D_CACHE','Cached request pending, adding watch to handle current asynchronous request. key = \n' + key + '\n');
70                                                         var id = data.observers.add('cached_request.'+key+'.status',function(p,o,n) {
71                                                                         obj.error.sdump('D_OBSERVERS','Entering watch function for key = \n' + key + '\np = ' + p + '\no = ' + js2JSON(o) + '\nn = ' + js2JSON(n) + '\nx = ' + js2JSON(x) + '\n');
72                                                                         if (n == 'complete') {
73                                                                                 obj.error.sdump('D_CACHE','Cached request completed for key = \n' + key + '\nNow calling a previous async request watching this one.\n');
74                                                                                 f( { 'getResultObject' : function() { return JSON2js( js2JSON( x.request ) ); } } );
75                                                                                 setTimeout( function() { try { data.observers.remove(id); } catch(E) { alert(E); } }, 0 );
76                                                                         }
77                                                                         return n;
78                                                                 }
79                                                         );
80                                                         return null;
81                                                 } else {
82                                                         obj.error.sdump('D_CACHE','Pending request and synchronous request collision with key = \n' + key + '\nFalling through...\n');
83                                                 }
84                                         break;
85                                         case 'complete' :
86                                                 if ( Number( new Date() ) < x.expire_time ) {
87                                                         if (f) {
88                                                                 obj.error.sdump('D_CACHE','Cached request found completed, handling asynchronous request now. key = \n' + key + '\n');
89                                                                 f( { 'getResultObject' : function() { return JSON2js( js2JSON( x.request ) ); } } );
90                                                                 return null;
91                                                         } else {
92                                                                 obj.error.sdump('D_CACHE','Cached request found completed, key = \n' + key + '\nreturning value =\n' + x.request + '\n');
93                                                                 return JSON2js( js2JSON( x.request ) ); // FIXME -- cloning the object is a workaround, otherwise instanceOf somehow silently fails
94                                                         }
95                                                 } else {
96                                                         obj.error.sdump('D_CACHE','Cached request found completed, however, it has expired. key = \n' + key + '\nFalling through...\n');
97                                                 }
98                                         break;
99                                 }
100                         } else {
101                                 obj.error.sdump('D_CACHE','Cached request not found for key = \n' + key + '\n');                
102                         }
103                 }
104
105                 var request =  this._request(app,name,params,f,override_params,_params);
106                 if (request) {
107                         return this.get_result(request);
108                 } else {
109                         return null;
110                 }
111
112                 } catch(E) {
113                         alert(E); 
114                 }
115         },
116
117         '_request' : function (app,name,params,f,override_params,_params) {
118                 var obj = this;
119                 try {
120                         var sparams = js2JSON(params);
121                         obj.error.sdump('D_SES','request '+app+' '+name+' '+obj.error.pretty_print(sparams.slice(1,sparams.length-1))+
122                                 '\noverride_params = ' + override_params + '\n_params = ' + _params +
123                                 '\nResult #' + (++obj.link_id) + ( f ? ' asynced' : ' synced' ) );
124
125                         var key; var x; var data;
126                         if (_params && obj.is_cacheable(_params.method_id)) {
127                                 JSAN.use('OpenILS.data'); data = new OpenILS.data(); data.init({'via':'stash'});
128                                 key = (app + '+' + name + '+' + js2JSON(params)).replace(/\./g,'_');
129                                 data.cached_request[key] = { 'test' : 'test', 'status' : 'pending', 'status_time' : Number(new Date()) }; 
130                                 data.stash('cached_request');
131                                 x = data.cached_request[key];
132                                 obj.error.sdump('D_CACHE','Current request is cacheable, setting pending status for key = \n' + key + '\nUpdating cache with ' + js2JSON(x) + '\n');
133                         }
134
135                         var request = new RemoteRequest( app, name );
136                         if (_params && _params.secure) {
137                                 request.setSecure(true);
138                         } else {
139                                 request.setSecure(false);
140                         }
141                         for(var index in params) {
142                                 request.addParam(params[index]);
143                         }
144         
145                         if (f)  {
146                                 request.setCompleteCallback(
147                                         function(req) {
148                                                 try {
149                                                         var json_string = js2JSON(obj.get_result(req));
150                                                         obj.error.sdump('D_SES_RESULT','asynced result #' 
151                                                                 + obj.link_id + '\n\n' 
152                                                                 + (json_string.length > 80 ? obj.error.pretty_print(json_string) : json_string) 
153                                                                 + '\n\nOriginal Request:\n\n' 
154                                                                 + 'request '+app+' '+name+' '+ sparams.slice(1,sparams.length-1));
155                                                         req = obj.rerequest_on_session_timeout(app,name,params,req,override_params,_params);
156                                                         req = obj.rerequest_on_perm_failure(app,name,params,req,override_params,_params);
157                                                         if (override_params) {
158                                                                 req = obj.rerequest_on_override(app,name,params,req,override_params,_params);
159                                                         }
160                                                         req = obj.check_for_offline(app,name,params,req,override_params,_params);
161                                                         if (_params && obj.is_cacheable(_params.method_id)) {
162                                                                 x.request = obj.get_result(req);
163                                                                 x.status = 'complete'; x.status_time = Number(new Date()); x.expire_time = Number(x.status_time) + api[_params.method_id].ttl;
164                                                                 data.stash('cached_request');
165                                                                 obj.error.sdump('D_CACHE','Previously pending cached request is now complete for key = \n' + key + '\nUpdating cache with ' + js2JSON(x) + '\n');
166                                                         }
167                                                         f(req);
168                                                         obj.NETWORK_FAILURE = null;
169                                                 } catch(E) {
170                                                         try {
171                                                                 E.ilsevent = -2;
172                                                                 E.textcode = 'Server/Method Error';
173                                                         } catch(F) {}
174                                                         f( { 'getResultObject' : function() { return E; } } );
175                                                 }
176                                         }
177                                 );
178                                 try {
179                                         request.send(false);
180                                 } catch(E) {
181                                         throw(E);
182                                 }
183                                 return null;
184                         } else {
185                                 try {
186                                         request.send(true);
187                                 } catch(E) {
188                                         throw(E);
189                                 }
190                                 var result = obj.get_result(request);
191                                 var json_string = js2JSON(result);
192                                 this.error.sdump('D_SES_RESULT','synced result #' 
193                                         + obj.link_id + '\n\n' + ( json_string.length > 80 ? obj.error.pretty_print(json_string) : json_string ) 
194                                         + '\n\nOriginal Request:\n\n' 
195                                         + 'request '+app+' '+name+' '+ sparams.slice(1,sparams.length-1));
196                                 request = obj.rerequest_on_session_timeout(app,name,params,request,override_params,_params);
197                                 request = obj.rerequest_on_perm_failure(app,name,params,request,override_params,_params);
198                                 if (override_params) {
199                                         request = obj.rerequest_on_override(app,name,params,request,override_params,_params);
200                                 }
201                                 request = obj.check_for_offline(app,name,params,request,override_params,_params);
202                                 obj.NETWORK_FAILURE = null;
203                                 if (_params && obj.is_cacheable(_params.method_id)) {
204                                         x.request = result;
205                                         x.status = 'complete'; x.status_time = Number(new Date()); x.expire_time = Number(x.status_time) + api[_params.method_id].ttl;
206                                         data.stash('cached_request');
207                                         obj.error.sdump('D_CACHE','Previously pending cached request is now complete for key = \n' + key + '\nUpdating cache with ' + js2JSON(x) + '\n');
208                                 }
209                                 return request;
210                         }
211
212                 } catch(E) {
213                         alert(E);
214                         if (instanceOf(E,perm_ex)) {
215                                 alert('in util.network, _request : permission exception: ' + js2JSON(E));
216                         }
217                         throw(E);
218                 }
219         },
220
221         'check_for_offline' : function (app,name,params,req,override_params,_params) {
222                 var obj = this;
223                 var result = obj.get_result(req);
224                 if (result != null) return req;
225
226                 JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
227                 var proceed = true;
228
229                 while(proceed) {
230
231                         proceed = false;
232
233                         var r;
234
235                         if (data.proceed_offline) {
236
237                                 r = 1;
238
239                         } else {
240
241                                 var network_failure_string;
242                                 var network_failure_status_string;
243                                 var msg;
244
245                                 try { network_failure_string = String( obj.NETWORK_FAILURE ); } catch(E) { network_failure_string = E; }
246                                 try { network_failure_status_string = typeof obj.NETWORK_FAILURE == 'object' && typeof obj.NETWORK_FAILURE != 'null' && typeof obj.NETWORK_FAILURE.status == 'function' ? obj.NETWORK_FAILURE.status() : ''; } catch(E) { network_failure_status_string = ''; obj.error.sdump('D_ERROR', 'setting network_failure_status_string: ' + E); }
247                                 
248                                 try { msg = 'Network/server failure.  Please check your Internet connection to ' + data.server_unadorned + ' and choose Retry Network.  If you need to enter Offline Mode, choose Ignore Errors in this and subsequent dialogs.  If you believe this error is due to a bug in Evergreen and not network problems, please contact your helpdesk or friendly Evergreen admins, and give them this information:\nmethod=' + name + '\nparams=' + js2JSON(params) + '\nTHROWN:\n' + network_failure_string + '\nSTATUS:\n' + network_failure_status_string; } catch(E) { msg = E; }
249
250                                 try { obj.error.sdump('D_SES_ERROR',msg); } catch(E) { alert(E); }
251
252                                 r = obj.error.yns_alert(msg,'Network Failure','Retry Network','Ignore Errors',null,'Check here to confirm this message');
253                                 if (r == 1) {
254                                         data.proceed_offline = true; data.stash('proceed_offline');
255                                         dump('Remembering proceed_offline for 200000 ms.\n');
256                                         setTimeout(
257                                                 function() {
258                                                         data.proceed_offline = false; data.stash('proceed_offline');
259                                                         dump('Setting proceed_offline back to false.\n');
260                                                 }, 200000
261                                         );
262                                 }
263                         }
264
265                         dump( r == 0 ? 'Retry Network\n' : 'Ignore Errors\n' );
266
267                         switch(r) {
268                                 case 0: 
269                                         req = obj._request(app,name,params,null,override_params,_params);
270                                         if (obj.get_result(req)) proceed = true; /* daily WTF, why am I even doing this? :) */
271                                         return req;
272                                 break;
273                                 case 1: 
274                                         return { 'getResultObject' : function() { return { 'ilsevent' : -1, 'textcode' : 'Network/Server Problem' }; } };
275                                 break;
276                         }
277                 }
278         },
279
280         'reset_titlebars' : function(data) {
281                 var obj = this;
282                 data.stash_retrieve();
283                 try {
284                         JSAN.use('util.window'); var win =  new util.window();
285                         var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
286                         var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
287                         var enumerator = windowManagerInterface.getEnumerator(null);
288
289                         var w; // set title on all appshell windows
290                         while ( w = enumerator.getNext() ) {
291                                 if (w.document.title.match(/^\d/)) {
292                                         w.document.title = 
293                                                 win.appshell_name_increment() 
294                                                 + ': ' + data.list.au[0].usrname() 
295                                                 + '@' + data.ws_name;
296                                                 + '.' + data.server_unadorned 
297                                 }
298                         }
299                 } catch(E) {
300                         obj.error.standard_unexpected_error_alert('Error setting window titles to match new login',E);
301                 }
302         },
303
304         'get_new_session' : function(name,xulG,text) {
305                 var obj = this;
306                 try {
307
308                 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
309                 var url = urls.XUL_AUTH_SIMPLE;
310                 if (typeof xulG != 'undefined' && typeof xulG.url_prefix == 'function') url = xulG.url_prefix( url );
311                 window.open(
312                         url
313                         + '?login_type=staff'
314                         + '&desc_brief=' + window.escape( text ? 'Session Expired' : 'Operator Change' )
315                         + '&desc_full=' + window.escape( text ? 'Please enter the credentials for a new login session.' : 'Please enter the credentials for the new login session.  Note that the previous session is still active.'),
316                         'simple_auth' + (new Date()).toString(),
317                         'chrome,resizable,modal,width=700,height=500'
318                 );
319                 JSAN.use('OpenILS.data');
320                 var data = new OpenILS.data(); data.init({'via':'stash'});
321                 if (typeof data.temporary_session != 'undefined' && data.temporary_session != '') {
322                         data.session.key = data.temporary_session.key; 
323                         data.session.authtime = data.temporary_session.authtime; 
324                         data.stash('session');
325                         if (! data.list.au ) data.list.au = [];
326                         data.list.au[0] = JSON2js(data.temporary_session.usr);
327                         data.stash('list');
328                         obj.reset_titlebars(data);
329                         return true;
330                 }
331                 return false;
332
333                 } catch(E) {
334                         obj.error.standard_unexpected_error_alert('util.network.get_new_session',E);
335                 }
336         },
337
338         'rerequest_on_session_timeout' : function(app,name,params,req,override_params,_params) {
339                 try {
340                         var obj = this;
341                         var robj = obj.get_result(req);
342                         if (robj != null && robj.ilsevent && robj.ilsevent == 1001) {
343
344                                 if (obj.get_new_session(name,undefined,true)) {
345                                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
346                                         params[0] = data.session.key;
347                                         req = obj._request(app,name,params,null,override_params,_params);
348                                 }
349                         }
350                 } catch(E) {
351                         this.error.standard_unexpected_error_alert('rerequest_on_session_timeout',E);
352                 }
353                 return req;
354         },
355         
356         'rerequest_on_perm_failure' : function(app,name,params,req,override_params,_params) {
357                 try {
358                         var obj = this;
359                         var robj = obj.get_result(req);
360                         if (robj != null && robj.ilsevent && robj.ilsevent == 5000) {
361                                 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
362                                 if (location.href.match(/^chrome/)) {
363                                         //alert('Permission denied.');
364                                 } else {
365                                         window.open(
366                                                 urls.XUL_AUTH_SIMPLE
367                                                 + '?login_type=temp'
368                                                 + '&desc_brief=' + window.escape('Permission Denied: ' + robj.ilsperm)
369                                                 + '&desc_full=' + window.escape('Another staff member with the above permission may authorize this specific action.  Please notify your library administrator if you need this permission.  If you feel you have received this exception in error, inform your friendly Evergreen developers of the above permission and this debug information: ' + name),
370                                                 'simple_auth' + (new Date()).toString(),
371                                                 'chrome,resizable,modal,width=700,height=500'
372                                         );
373                                         JSAN.use('OpenILS.data');
374                                         var data = new OpenILS.data(); data.init({'via':'stash'});
375                                         if (typeof data.temporary_session != 'undefined' && data.temporary_session != '') {
376                                                 params[0] = data.temporary_session.key;
377                                                 req = obj._request(app,name,params,null,override_params,_params);
378                                         }
379                                 }
380                         }
381                 } catch(E) {
382                         this.error.sdump('D_ERROR',E);
383                 }
384                 return req;
385         },
386
387         'rerequest_on_override' : function (app,name,params,req,override_params,_params) {
388                 var obj = this;
389                 try {
390                         if (!override_params.text) override_params.text = {};
391                         function override(r) {
392                                 try {
393                                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
394                                         obj.sound.bad();
395                                         var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' + 
396                                                 '<groupbox><caption label="Exceptions"/>' + 
397                                                 '<grid><columns><column/><column/></columns><rows>';
398                                         for (var i = 0; i < r.length; i++) {
399                                                 var t1 = String(r[i].ilsevent).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
400                                                 var t2 = String(r[i].textcode).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
401                                                 var t3 = String((override_params.text[r[i].ilsevent] ? override_params.text[r[i].ilsevent](r[i]) : '')).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
402                                                 var t4 = String(r[i].desc).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
403                                                 xml += '<row>' + 
404                                                         '<description style="color: red" tooltiptext="' + t1 + '">' + t2 + '</description>' + 
405                                                         '<description>' + t3 + '</description>' + 
406                                                         '</row><row>' + '<description>' + t4 + '</description>' + '</row>';
407                                         }
408                                         xml += '</rows></grid></groupbox><groupbox><caption label="Override"/><hbox>' + 
409                                                 '<description>Force this action?</description>' + 
410                                                 '<button accesskey="N" label="No" name="fancy_cancel"/>' + 
411                                                 '<button id="override" accesskey="Y" label="Yes" name="fancy_submit" value="override"/></hbox></groupbox></vbox>';
412                                         JSAN.use('OpenILS.data');
413                                         var data = new OpenILS.data(); data.init({'via':'stash'});
414                                         data.temp_override_xml = xml; data.stash('temp_override_xml');
415                                         window.open(
416                                                 urls.XUL_FANCY_PROMPT
417                                                 + '?xml_in_stash=temp_override_xml'
418                                                 + '&title=' + window.escape(override_params.title),
419                                                 'fancy_prompt', 'chrome,resizable,modal,width=700,height=500'
420                                         );
421                                         data.init({'via':'stash'});
422                                         if (data.fancy_prompt_data != '') {
423                                                 req = obj._request(app,name + '.override',params);
424                                         }
425                                         return req;
426                                 } catch(E) {
427                                         alert('in util.network, rerequest_on_override, override:' + E);
428                                 }
429                         }
430
431                         var result = obj.get_result(req);
432                         if (!result) return req;
433
434                         if ( (typeof result.ilsevent != 'undefined') && (override_params.overridable_events.indexOf(result.ilsevent) != -1) ) {
435                                 req = override([result]);
436                         } else {
437                                 var found_good = false; var found_bad = false;
438                                 for (var i = 0; i < result.length; i++) {
439                                         if ( (result[i].ilsevent != 'undefined') && (override_params.overridable_events.indexOf(result[i].ilsevent) != -1) ) {
440                                                 found_good = true;
441                                         } else {
442                                                 found_bad = true;
443                                         }
444                                 }
445                                 if (found_good && (!found_bad)) req = override(result);
446                         }
447
448                         return req;
449                 } catch(E) {
450                         throw(E);
451                 }
452         },
453
454
455 }
456
457 /*
458 function sample_callback(request) {
459         var result = request.getResultObject();
460 }
461 */
462
463 dump('exiting util/network.js\n');