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