]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/z3950/app.js
LP1786971 z39.50 add TCN to overlay popups as well
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / cat / z3950 / app.js
1 /*
2  * Z39.50 search and import
3  */
4
5 angular.module('egCatZ3950Search',
6     ['ngRoute', 'ui.bootstrap', 'ngOrderObjectBy', 'egCoreMod', 'egUiMod', 'egGridMod', 'egZ3950Mod', 'egMarcMod'])
7
8 .config(function($routeProvider, $locationProvider, $compileProvider) {
9     $locationProvider.html5Mode(true);
10     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
11         
12     var resolver = {delay : function(egStartup) {return egStartup.go()}};
13
14     // search page shows the list view by default
15     $routeProvider.when('/cat/z3950/search', {
16         templateUrl: './cat/z3950/t_list',
17         controller: 'Z3950SearchCtrl',
18         resolve : resolver
19     });
20
21     // default page / bucket view
22     $routeProvider.otherwise({redirectTo : '/cat/z3950/search'});
23 })
24
25 /**
26  * List view - grid stuff
27  */
28 .controller('Z3950SearchCtrl',
29        ['$scope','$q','$location','$timeout','$window','egCore','egGridDataProvider','egZ3950TargetSvc','$uibModal',
30         'egConfirmDialog','egAlertDialog',
31 function($scope , $q , $location , $timeout , $window,  egCore , egGridDataProvider,  egZ3950TargetSvc,  $uibModal,
32          egConfirmDialog, egAlertDialog) {
33
34     // get list of targets
35     egZ3950TargetSvc.loadTargets();
36     egZ3950TargetSvc.loadActiveSearchFields();
37
38     $scope.field_strip_groups = [];
39     egCore.startup.go().then(function() {
40         // and list of field strip groups; need to ensure
41         // that enough of the startup has happened so that
42         // we have the user WS
43         egCore.pcrud.search('vibtg',
44             {
45                 always_apply : 'f',
46                 owner : {
47                     'in' : {
48                         select : {
49                             aou : [{
50                                 column : 'id',
51                                 transform : 'actor.org_unit_ancestors',
52                                 result_field : 'id'
53                             }]
54                         },
55                         from : 'aou',
56                         where : {
57                             id : egCore.auth.user().ws_ou()
58                         }
59                     }
60                 }
61             },
62             { order_by : { vibtq : ['label'] } }
63         ).then(null, null, function(strip_group) {
64             strip_group.selected = false;
65             $scope.field_strip_groups.push(strip_group);
66         });
67     });
68
69     $scope.total_hits = 0;
70
71     var bib_sources = null;
72     egCore.pcrud.retrieveAll('cbs', {}, {atomic : true})
73         .then(function(l) { bib_sources = l; });
74
75     var provider = egGridDataProvider.instance({});
76
77     provider.get = function(offset, count) {
78         var deferred = $q.defer();
79
80         var query = egZ3950TargetSvc.currentQuery();
81         if (!query.raw_search && Object.keys(query.search).length == 0) {
82             return $q.when();
83         }
84
85         var method = query.raw_search ?
86                        'open-ils.search.z3950.search_service' :
87                        'open-ils.search.z3950.search_class';
88
89         if (query.raw_search) {
90             query.query = query.raw_search;
91             delete query['search'];
92             delete query['raw_search'];
93             query.service = query.service[0];
94             query.username = query.username[0];
95             query.password = query.password[0];
96         }
97
98         query['limit'] = count;
99         query['offset'] = offset;
100
101         var resultIndex = offset;
102         $scope.total_hits = 0;
103         $scope.searchInProgress = true;
104         egCore.net.request(
105             'open-ils.search',
106             method,
107             egCore.auth.token(),
108             query
109         ).then(
110             function() { $scope.searchInProgress = false; deferred.resolve() },
111             null, // onerror
112             function(result) {
113                 // FIXME when the search offset is > 0, the
114                 // total hits count can be wrong if one of the
115                 // Z39.50 targets has fewer than $offset hits; in that
116                 // case, result.count is not supplied.
117                 $scope.total_hits += (result.count || 0);
118                 for (var i in result.records) {
119                     result.records[i].mvr['bibid'] = result.records[i].bibid;
120                     result.records[i].mvr['service'] = result.service;
121                     result.records[i].mvr['index'] = resultIndex++;
122                     result.records[i].mvr['marcxml'] = result.records[i].marcxml;
123                     deferred.notify(result.records[i].mvr);
124                 }
125             }
126         );
127
128         return deferred.promise;
129     };
130
131     $scope.z3950SearchGridProvider = provider;
132     $scope.gridControls = {};
133
134     $scope.search = function() {
135         $scope.z3950SearchGridProvider.refresh();
136     };
137     $scope.clearForm = function() {
138         egZ3950TargetSvc.clearSearchFields();
139     };
140
141     $scope.saveDefaultZ3950Targets = function() {
142         egZ3950TargetSvc.saveDefaultZ3950Targets();
143     }
144
145     var display_form = true;
146     $scope.show_search_form = function() {
147         return display_form;
148     }
149     $scope.toggle_search_form = function() {
150         display_form = !display_form;
151     }
152
153     $scope.raw_search_impossible = function() {
154         return egZ3950TargetSvc.rawSearchImpossible();
155     }
156
157     $scope.get_bibsrc_name_from_id = function(bs_id){
158         // var sel_bib_src = bib_src.id ? bib_src.list.filter(s => s.id() == bib_src.id) : null;
159         // TODO can we use arrow syntax yet???
160         if (!bs_id) return null;
161         var cbs = bib_sources.filter(function(s){ return s.id() == bs_id });
162
163         return (cbs && cbs[0] ? cbs[0].source() : null);
164     };
165
166     $scope.showRawSearchForm = function() {
167         $uibModal.open({
168             templateUrl: './cat/z3950/t_raw_search',
169             backdrop: 'static',
170             size: 'md',
171             controller:
172                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
173                 egZ3950TargetSvc.setRawSearch('');
174                 $scope.focusMe = true;
175                 $scope.ok = function(args) { $uibModalInstance.close(args) }
176                 $scope.cancel = function () { $uibModalInstance.dismiss() }
177             }]
178         }).result.then(function (args) {
179             if (!args || !args.raw_search) return;
180             $scope.clearForm();
181             egZ3950TargetSvc.setRawSearch(args.raw_search);
182             $scope.z3950SearchGridProvider.refresh();
183         });
184     }
185
186     $scope.showInCatalog = function() {
187         var items = $scope.gridControls.selectedItems();
188         // relying on cant_showInCatalog to protect us
189         var url = '/eg2/staff/catalog/record/' + items[0]['bibid'];
190         $timeout(function() { $window.open(url, '_blank') });        
191     };
192     $scope.cant_showInCatalog = function() {
193         var items = $scope.gridControls.selectedItems();
194         if (items.length != 1) return true;
195         if (items[0]['service'] == 'native-evergreen-catalog') return false;
196         return true;
197     };
198
199     $scope.local_overlay_target = egCore.hatch.getLocalItem('eg.cat.marked_overlay_record') || 0;
200     $scope.local_overlay_target_tcn = egCore.hatch.getLocalItem('eg.cat.marked_overlay_tcn') || 0;
201     if($scope.local_overlay_target) {
202         var currTarget = $scope.local_overlay_target;
203         get_tcn(currTarget);
204     }
205     $scope.mark_as_overlay_target = function() {
206         var items = $scope.gridControls.selectedItems();
207         if ($scope.local_overlay_target == items[0]['bibid']) {
208             $scope.local_overlay_target = 0;
209             $scope.local_overlay_target_tcn = 0;
210         } else {
211             $scope.local_overlay_target = items[0]['bibid'];
212             var currTarget = items[0] ['bibid'];
213             get_tcn(currTarget);
214         }
215         egCore.hatch.setLocalItem('eg.cat.marked_overlay_record',$scope.local_overlay_target);
216         egCore.hatch.setLocalItem('eg.cat.marked_overlay_tcn',$scope.local_overlay_target_tcn);
217     }
218
219     function get_tcn(currTarget) {
220         egCore.pcrud.retrieve('bre', currTarget, {
221             select: {bre: ['tcn_value']}
222         }).then(function(rec) {
223             $scope.local_overlay_target_tcn = rec.tcn_value();
224         });
225         return;
226     };
227
228     $scope.cant_overlay = function() {
229         if (!$scope.local_overlay_target) return true;
230         var items = $scope.gridControls.selectedItems();
231         if (items.length != 1) return true;
232         if (
233                 items[0]['service'] == 'native-evergreen-catalog' &&
234                 items[0]['bibid'] == $scope.local_overlay_target
235            ) return true;
236         return false;
237     }
238
239     $scope.selectFieldStripGroups = function() {
240         var groups = [];
241         angular.forEach($scope.field_strip_groups, function(grp, idx) {
242             if (grp.selected) {
243                 groups.push(grp.id());
244             }
245         });
246         return groups;
247     };
248
249     $scope.import = function() {
250         var items = $scope.gridControls.selectedItems();
251         return $scope._import(items[0]['marcxml']);
252     };
253
254     $scope._import = function(marc_xml,bib_source) {
255
256         var bibsrc_name = $scope.get_bibsrc_name_from_id(bib_source);
257
258         var deferred = $q.defer();
259         egCore.net.request(
260             'open-ils.cat',
261             'open-ils.cat.biblio.record.xml.import',
262             egCore.auth.token(),
263             marc_xml,
264             bibsrc_name,
265             null,
266             null,
267             $scope.selectFieldStripGroups()
268         ).then(
269             function(result) { deferred.resolve(result) },
270             null, // onerror
271             function(result) {
272                 var evt = egCore.evt.parse(result);
273                 if (evt) {
274                      if (evt.textcode == 'TCN_EXISTS') {
275                        egAlertDialog.open(
276                             egCore.strings.TCN_EXISTS
277                       );
278                      } else {
279                        // we shouldn't get here
280                        egAlertDialog.open(egCore.strings.TCN_EXISTS_ERR);
281                      }
282                 } else {
283                     egConfirmDialog.open(
284                         egCore.strings.IMPORTED_RECORD_FROM_Z3950,
285                         egCore.strings.IMPORTED_RECORD_FROM_Z3950_AS_ID,
286                         { id : result.id() },
287                         egCore.strings.GO_TO_RECORD,
288                         egCore.strings.GO_BACK
289                     ).result.then(function() {
290                         $window.open('/eg2/staff/catalog/record/' + result.id());
291                     });
292                 }
293             }
294         );
295
296         return deferred.promise;
297     };
298     $scope.need_one_selected = function() {
299         var items = $scope.gridControls.selectedItems();
300         if (items.length == 1) return false;
301         return true;
302     };
303
304     $scope.spawn_editor = function() {
305         var items = $scope.gridControls.selectedItems();
306         var recId = 0;
307         var _import = $scope._import;
308         $uibModal.open({
309             templateUrl: './cat/z3950/t_marc_edit',
310             backdrop: 'static',
311             size: 'lg',
312             controller:
313                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
314                 $scope.focusMe = true;
315                 $scope.record_id = recId;
316                 $scope.dirty_flag = false;
317                 $scope.args = {};
318                 $scope.args.marc_xml = items[0]['marcxml'];
319                 $scope.args.bib_source = null;
320                 $scope.ok = function(args) { $uibModalInstance.close(args) }
321                 $scope.cancel = function () { $uibModalInstance.dismiss() }
322                 $scope.save_label = egCore.strings.IMPORT_BUTTON_LABEL;
323                 // Wiring up angular inPlaceMode for editing later
324                 $scope.in_place_mode = true;
325                 $scope.import_record_callback = function () {
326                     if($scope.in_place_mode) {
327                         // This timeout is required to allow angular to finish variable assignments
328                         // in the marcediter app. Allowing marc_xml to propigate here.
329                         $timeout( function() {
330                             _import($scope.args.marc_xml, $scope.args.bib_source).then( function(record_obj) {
331                                 if( record_obj.id ) {
332                                     $scope.record_id = record_obj.id();
333                                     $scope.save_label = egCore.strings.SAVE_BUTTON_LABEL;
334                                     // Successful import, no longer want this special z39.50 callback to execute.
335                                     $scope.in_place_mode = undefined;
336                                 }
337                             });
338                         });
339                     }
340                 };
341             }]
342         }).result.then(function () {
343             if (recId) {
344                 $window.location.href = '/eg2/staff/catalog/record/' + recId;
345             }
346         });
347     }
348
349     $scope.view_marc = function() {
350         var items = $scope.gridControls.selectedItems();
351         $uibModal.open({
352             templateUrl: './cat/z3950/t_marc_html',
353             backdrop: 'static',
354             size: 'lg',
355             controller:
356                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
357                 $scope.focusMe = true;
358                 $scope.marc_xml = items[0]['marcxml'];
359                 $scope.isbn = (items[0].isbn() || '').replace(/ .*/, '');
360                 $scope.ok = function(args) { $uibModalInstance.close(args) }
361                 $scope.cancel = function () { $uibModalInstance.dismiss() }
362             }]
363         }).result.then(function (args) {
364             if (!args || !args.name) return;
365         });
366     }
367
368     $scope.overlay_record = function() {
369         var items = $scope.gridControls.selectedItems();
370         var overlay_target = $scope.local_overlay_target;
371         var overlay_target_tcn = $scope.local_overlay_target_tcn;
372         var live_overlay_target = egCore.hatch.getLocalItem('eg.cat.marked_overlay_record') || 0;
373         var live_overlay_target_tcn = egCore.hatch.getLocalItem('eg.cat.marked_overlay_tcn') || 0;
374         var args = {
375             'marc_xml' : items[0]['marcxml'],
376             'bib_source' : null
377         };
378
379         $uibModal.open({
380             templateUrl: './cat/z3950/t_overlay',
381             backdrop: 'static',
382             size: 'lg',
383             controller:
384                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
385
386                 $scope.immediate_merge = function () {
387                     $scope.overlay_target.marc_xml = args.marc_xml;
388                     egCore.pcrud.retrieve('bre', $scope.overlay_target.id)
389                     .then(function(rec) {
390                         $scope.overlay_target.orig_marc_xml = rec.marc();
391                         $scope.merge_marc(); // in case a sticky value was already set
392                         $scope.overlay_target_tcn = overlay_target_tcn;
393                     });
394                 }
395
396                 $scope.merge_marc = function() {
397                     if (!$scope.merge_profile) return;
398                     egCore.net.request(
399                         'open-ils.cat',
400                         'open-ils.cat.merge.marc.per_profile',
401                         egCore.auth.token(),
402                         $scope.merge_profile,
403                         [ args.marc_xml, $scope.overlay_target.orig_marc_xml ]
404                     ).then(function(merged) {
405                         if (merged) {
406                             $scope.overlay_target.marc_xml = merged;
407                             $scope.overlay_target.merged = true;
408                         }
409                     });
410                 }
411
412                 $scope.editOverlayRecord = function() {
413                     $uibModal.open({
414                         templateUrl: './cat/z3950/t_edit_overlay_record',
415                         backdrop: 'static',
416                         size: 'lg',
417                         controller:
418                             ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
419                             $scope.focusMe = true;
420                             $scope.record_id = 0;
421                             $scope.dirty_flag = false;
422                             $scope.args = args;
423                             $scope.ok = function() { $uibModalInstance.close($scope.args) }
424                             $scope.cancel = function () { $uibModalInstance.dismiss() }
425                         }]
426                     }).result.then(function (args) {
427                         $scope.merge_marc();
428                         if (!args || !args.name) return;
429                     });
430                 };
431
432                 $scope.focusMe = true;
433                 $scope.merge_profile = null;
434                 $scope.overlay_target = {
435                     id : overlay_target,
436                     live_id : live_overlay_target,
437                     merged : false
438                 };
439
440                 $scope.$watch('merge_profile', function(newVal, oldVal) {
441                     if (newVal && newVal !== oldVal) {
442                         $scope.merge_marc();
443                     }
444                 });
445
446                 $scope.args = args;
447                 args.overlay_target = $scope.overlay_target;
448                 $scope.ok = function(args) { $uibModalInstance.close(args) };
449                 $scope.cancel = function () { $uibModalInstance.dismiss() };
450                 
451                 if (overlay_target != live_overlay_target) {
452                     var confirm_title = egCore.strings.OVERLAY_CHANGED_TITLE;
453                     var confirm_msg = egCore.strings.OVERLAY_CHANGED;
454
455                     if (live_overlay_target == 0) { // someone unset the target...
456                         confirm_title = egCore.strings.OVERLAY_REMOVED_TITLE;
457                         confirm_msg = egCore.strings.OVERLAY_REMOVED;
458                     }
459
460                     egConfirmDialog.open(
461                         confirm_title,
462                         confirm_msg,
463                         { id : overlay_target, live_id : live_overlay_target,
464                             tcn : overlay_target_tcn, live_tcn: live_overlay_target_tcn}
465                     ).result.then(
466                         function () { // proceed -- but check live overlay for unset-ness
467                             if (live_overlay_target != 0) {
468                                 $scope.overlay_target.id = $scope.overlay_target.live_id;
469                                 overlay_target = live_overlay_target;
470                             }
471                             $scope.immediate_merge();
472                         },
473                         function () {
474                             $scope.cancel();
475                         }
476                     );
477                 } else {
478                     $scope.immediate_merge();
479                 }
480
481             }]
482         }).result.then(function (args) {
483             var bibsrc_name = $scope.get_bibsrc_name_from_id(args.bib_source);
484             egCore.net.request(
485                 'open-ils.cat',
486                 'open-ils.cat.biblio.record.marc.replace',
487                 egCore.auth.token(),
488                 overlay_target,
489                 (args.overlay_target.merged ? args.overlay_target.marc_xml : args.marc_xml),
490                 bibsrc_name,
491                 null,
492                 $scope.selectFieldStripGroups()
493             ).then(
494                 function(result) {
495                     $scope.local_overlay_target = 0;
496                     $scope.local_overlay_target_tcn = 0;
497                     egCore.hatch.removeLocalItem('eg.cat.marked_overlay_record');
498                     egCore.hatch.removeLocalItem('eg.cat.marked_overlay_tcn');
499                     console.debug('overlay complete, target removed');
500                     $window.open('/eg2/staff/catalog/record/' + overlay_target);
501                 }
502             );            
503         });
504     }
505 }])