]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/z3950/app.js
LP2045292 Color contrast for AngularJS patron bills
[working/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['service'] = result.service;
120                     result.records[i].mvr['index'] = resultIndex++;
121                     result.records[i].mvr['marcxml'] = result.records[i].marcxml;
122                     deferred.notify(result.records[i].mvr);
123                 }
124             }
125         );
126
127         return deferred.promise;
128     };
129
130     $scope.z3950SearchGridProvider = provider;
131     $scope.gridControls = {};
132
133     $scope.search = function() {
134         $scope.z3950SearchGridProvider.refresh();
135     };
136     $scope.clearForm = function() {
137         egZ3950TargetSvc.clearSearchFields();
138     };
139
140     $scope.saveDefaultZ3950Targets = function() {
141         egZ3950TargetSvc.saveDefaultZ3950Targets();
142     }
143
144     var display_form = true;
145     $scope.show_search_form = function() {
146         return display_form;
147     }
148     $scope.toggle_search_form = function() {
149         display_form = !display_form;
150     }
151
152     $scope.raw_search_impossible = function() {
153         return egZ3950TargetSvc.rawSearchImpossible();
154     }
155
156     $scope.get_bibsrc_name_from_id = function(bs_id){
157         // var sel_bib_src = bib_src.id ? bib_src.list.filter(s => s.id() == bib_src.id) : null;
158         // TODO can we use arrow syntax yet???
159         if (!bs_id) return null;
160         var cbs = bib_sources.filter(function(s){ return s.id() == bs_id });
161
162         return (cbs && cbs[0] ? cbs[0].source() : null);
163     };
164
165     $scope.showRawSearchForm = function() {
166         $uibModal.open({
167             templateUrl: './cat/z3950/t_raw_search',
168             backdrop: 'static',
169             size: 'md',
170             controller:
171                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
172                 egZ3950TargetSvc.setRawSearch('');
173                 $scope.focusMe = true;
174                 $scope.ok = function(args) { $uibModalInstance.close(args) }
175                 $scope.cancel = function () { $uibModalInstance.dismiss() }
176             }]
177         }).result.then(function (args) {
178             if (!args || !args.raw_search) return;
179             $scope.clearForm();
180             egZ3950TargetSvc.setRawSearch(args.raw_search);
181             $scope.z3950SearchGridProvider.refresh();
182         });
183     }
184
185     $scope.showInCatalog = function() {
186         var items = $scope.gridControls.selectedItems();
187         // relying on cant_showInCatalog to protect us
188         var url = '/eg2/staff/catalog/record/' + items[0].tcn();
189         $timeout(function() { $window.open(url, '_blank') });        
190     };
191     $scope.cant_showInCatalog = function() {
192         var items = $scope.gridControls.selectedItems();
193         if (items.length != 1) return true;
194         if (items[0]['service'] == 'native-evergreen-catalog') return false;
195         return true;
196     };
197
198     $scope.local_overlay_target = egCore.hatch.getLocalItem('eg.cat.marked_overlay_record') || 0;
199     $scope.mark_as_overlay_target = function() {
200         var items = $scope.gridControls.selectedItems();
201         if ($scope.local_overlay_target == items[0].tcn()) {
202             $scope.local_overlay_target = 0;
203         } else {
204             $scope.local_overlay_target = items[0].tcn();
205         }
206         egCore.hatch.setLocalItem('eg.cat.marked_overlay_record',$scope.local_overlay_target);
207     }
208     $scope.cant_overlay = function() {
209         if (!$scope.local_overlay_target) return true;
210         var items = $scope.gridControls.selectedItems();
211         if (items.length != 1) return true;
212         if (
213                 items[0]['service'] == 'native-evergreen-catalog' &&
214                 items[0].tcn() == $scope.local_overlay_target
215            ) return true;
216         return false;
217     }
218
219     $scope.selectFieldStripGroups = function() {
220         var groups = [];
221         angular.forEach($scope.field_strip_groups, function(grp, idx) {
222             if (grp.selected) {
223                 groups.push(grp.id());
224             }
225         });
226         return groups;
227     };
228
229     $scope.import = function() {
230         var items = $scope.gridControls.selectedItems();
231         return $scope._import(items[0]['marcxml']);
232     };
233
234     $scope._import = function(marc_xml,bib_source) {
235
236         var bibsrc_name = $scope.get_bibsrc_name_from_id(bib_source);
237
238         var deferred = $q.defer();
239         egCore.net.request(
240             'open-ils.cat',
241             'open-ils.cat.biblio.record.xml.import',
242             egCore.auth.token(),
243             marc_xml,
244             bibsrc_name,
245             null,
246             null,
247             $scope.selectFieldStripGroups()
248         ).then(
249             function(result) { deferred.resolve(result) },
250             null, // onerror
251             function(result) {
252                 var evt = egCore.evt.parse(result);
253                 if (evt) {
254                      if (evt.textcode == 'TCN_EXISTS') {
255                        egAlertDialog.open(
256                             egCore.strings.TCN_EXISTS
257                       );
258                      } else {
259                        // we shouldn't get here
260                        egAlertDialog.open(egCore.strings.TCN_EXISTS_ERR);
261                      }
262                 } else {
263                     egConfirmDialog.open(
264                         egCore.strings.IMPORTED_RECORD_FROM_Z3950,
265                         egCore.strings.IMPORTED_RECORD_FROM_Z3950_AS_ID,
266                         { id : result.id() },
267                         egCore.strings.GO_TO_RECORD,
268                         egCore.strings.GO_BACK
269                     ).result.then(function() {
270                         $window.open('/eg2/staff/catalog/record/' + result.id());
271                     });
272                 }
273             }
274         );
275
276         return deferred.promise;
277     };
278     $scope.need_one_selected = function() {
279         var items = $scope.gridControls.selectedItems();
280         if (items.length == 1) return false;
281         return true;
282     };
283
284     $scope.spawn_editor = function() {
285         var items = $scope.gridControls.selectedItems();
286         var recId = 0;
287         var _import = $scope._import;
288         $uibModal.open({
289             templateUrl: './cat/z3950/t_marc_edit',
290             backdrop: 'static',
291             size: 'lg',
292             controller:
293                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
294                 $scope.focusMe = true;
295                 $scope.record_id = recId;
296                 $scope.dirty_flag = false;
297                 $scope.args = {};
298                 $scope.args.marc_xml = items[0]['marcxml'];
299                 $scope.args.bib_source = null;
300                 $scope.ok = function(args) { $uibModalInstance.close(args) }
301                 $scope.cancel = function () { $uibModalInstance.dismiss() }
302                 $scope.save_label = egCore.strings.IMPORT_BUTTON_LABEL;
303                 // Wiring up angular inPlaceMode for editing later
304                 $scope.in_place_mode = true;
305                 $scope.import_record_callback = function () {
306                     if($scope.in_place_mode) {
307                         // This timeout is required to allow angular to finish variable assignments
308                         // in the marcediter app. Allowing marc_xml to propigate here.
309                         $timeout( function() {
310                             _import($scope.args.marc_xml, $scope.args.bib_source).then( function(record_obj) {
311                                 if( record_obj.id ) {
312                                     $scope.record_id = record_obj.id();
313                                     $scope.save_label = egCore.strings.SAVE_BUTTON_LABEL;
314                                     // Successful import, no longer want this special z39.50 callback to execute.
315                                     $scope.in_place_mode = undefined;
316                                 }
317                             });
318                         });
319                     }
320                 };
321             }]
322         }).result.then(function () {
323             if (recId) {
324                 $window.location.href = '/eg2/staff/catalog/record/' + recId;
325             }
326         });
327     }
328
329     $scope.view_marc = function() {
330         var items = $scope.gridControls.selectedItems();
331         $uibModal.open({
332             templateUrl: './cat/z3950/t_marc_html',
333             backdrop: 'static',
334             size: 'lg',
335             controller:
336                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
337                 $scope.focusMe = true;
338                 $scope.marc_xml = items[0]['marcxml'];
339                 $scope.isbn = (items[0].isbn() || '').replace(/ .*/, '');
340                 $scope.ok = function(args) { $uibModalInstance.close(args) }
341                 $scope.cancel = function () { $uibModalInstance.dismiss() }
342             }]
343         }).result.then(function (args) {
344             if (!args || !args.name) return;
345         });
346     }
347
348     $scope.overlay_record = function() {
349         var items = $scope.gridControls.selectedItems();
350         var overlay_target = $scope.local_overlay_target;
351         var live_overlay_target = egCore.hatch.getLocalItem('eg.cat.marked_overlay_record') || 0;
352         var args = {
353             'marc_xml' : items[0]['marcxml'],
354             'bib_source' : null
355         };
356
357         $uibModal.open({
358             templateUrl: './cat/z3950/t_overlay',
359             backdrop: 'static',
360             size: 'lg',
361             controller:
362                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
363
364                 $scope.immediate_merge = function () {
365                     $scope.overlay_target.marc_xml = args.marc_xml;
366                     egCore.pcrud.retrieve('bre', $scope.overlay_target.id)
367                     .then(function(rec) {
368                         $scope.overlay_target.orig_marc_xml = rec.marc();
369                         $scope.merge_marc(); // in case a sticky value was already set
370                     });
371                 }
372
373                 $scope.merge_marc = function() {
374                     if (!$scope.merge_profile) return;
375                     egCore.net.request(
376                         'open-ils.cat',
377                         'open-ils.cat.merge.marc.per_profile',
378                         egCore.auth.token(),
379                         $scope.merge_profile,
380                         [ args.marc_xml, $scope.overlay_target.orig_marc_xml ]
381                     ).then(function(merged) {
382                         if (merged) {
383                             $scope.overlay_target.marc_xml = merged;
384                             $scope.overlay_target.merged = true;
385                         }
386                     });
387                 }
388
389                 $scope.editOverlayRecord = function() {
390                     $uibModal.open({
391                         templateUrl: './cat/z3950/t_edit_overlay_record',
392                         backdrop: 'static',
393                         size: 'lg',
394                         controller:
395                             ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
396                             $scope.focusMe = true;
397                             $scope.record_id = 0;
398                             $scope.dirty_flag = false;
399                             $scope.args = args;
400                             $scope.ok = function() { $uibModalInstance.close($scope.args) }
401                             $scope.cancel = function () { $uibModalInstance.dismiss() }
402                         }]
403                     }).result.then(function (args) {
404                         $scope.merge_marc();
405                         if (!args || !args.name) return;
406                     });
407                 };
408
409                 $scope.focusMe = true;
410                 $scope.merge_profile = null;
411                 $scope.overlay_target = {
412                     id : overlay_target,
413                     live_id : live_overlay_target,
414                     merged : false
415                 };
416
417                 $scope.$watch('merge_profile', function(newVal, oldVal) {
418                     if (newVal && newVal !== oldVal) {
419                         $scope.merge_marc();
420                     }
421                 });
422
423                 $scope.args = args;
424                 args.overlay_target = $scope.overlay_target;
425                 $scope.ok = function(args) { $uibModalInstance.close(args) };
426                 $scope.cancel = function () { $uibModalInstance.dismiss() };
427                 
428                 if (overlay_target != live_overlay_target) {
429                     var confirm_title = egCore.strings.OVERLAY_CHANGED_TITLE;
430                     var confirm_msg = egCore.strings.OVERLAY_CHANGED;
431
432                     if (live_overlay_target == 0) { // someone unset the target...
433                         confirm_title = egCore.strings.OVERLAY_REMOVED_TITLE;
434                         confirm_msg = egCore.strings.OVERLAY_REMOVED;
435                     }
436
437                     egConfirmDialog.open(
438                         confirm_title,
439                         confirm_msg,
440                         { id : overlay_target, live_id : live_overlay_target }
441                     ).result.then(
442                         function () { // proceed -- but check live overlay for unset-ness
443                             if (live_overlay_target != 0) {
444                                 $scope.overlay_target.id = $scope.overlay_target.live_id;
445                                 overlay_target = live_overlay_target;
446                             }
447                             $scope.immediate_merge();
448                         },
449                         function () {
450                             $scope.cancel();
451                         }
452                     );
453                 } else {
454                     $scope.immediate_merge();
455                 }
456
457             }]
458         }).result.then(function (args) {
459             var bibsrc_name = $scope.get_bibsrc_name_from_id(args.bib_source);
460             egCore.net.request(
461                 'open-ils.cat',
462                 'open-ils.cat.biblio.record.marc.replace',
463                 egCore.auth.token(),
464                 overlay_target,
465                 (args.overlay_target.merged ? args.overlay_target.marc_xml : args.marc_xml),
466                 bibsrc_name,
467                 null,
468                 $scope.selectFieldStripGroups()
469             ).then(
470                 function(result) {
471                     $scope.local_overlay_target = 0;
472                     egCore.hatch.removeLocalItem('eg.cat.marked_overlay_record');
473                     console.debug('overlay complete, target removed');
474                     $window.open('/eg2/staff/catalog/record/' + overlay_target);
475                 }
476             );            
477         });
478     }
479 }])