From ce741307766608cd601c6fdc28962ff78df00d24 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Tue, 9 Jun 2015 22:10:18 +0000 Subject: [PATCH] webstaff: Z39.50 search and import interface - fetch configured Z39.50 targets - can now select targets - search field form now active - can now retrieve results - check for empty query correctly - retain reference to target list - preserve search query input when toggling Z39.50 target selection - implement clear form handler - allow enter key to submit search - track index of Z39.50 results so that individual ones can be selected - note results of experiences to make the title be conditionally a hyperlink - implement show in catalog - implement direct import Signed-off-by: Galen Charlton Signed-off-by: Jason Stephenson --- .../src/templates/staff/cat/z3950/index.tt2 | 17 +++ .../src/templates/staff/cat/z3950/t_list.tt2 | 56 ++++++++ .../staff/cat/z3950/t_search_fields.tt2 | 8 ++ .../templates/staff/cat/z3950/t_target.tt2 | 19 +++ Open-ILS/src/templates/staff/navbar.tt2 | 6 + .../js/ui/default/staff/cat/services/z3950.js | 132 ++++++++++++++++++ .../web/js/ui/default/staff/cat/z3950/app.js | 119 ++++++++++++++++ 7 files changed, 357 insertions(+) create mode 100644 Open-ILS/src/templates/staff/cat/z3950/index.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/z3950/t_list.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/z3950/t_search_fields.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/z3950/t_target.tt2 create mode 100644 Open-ILS/web/js/ui/default/staff/cat/services/z3950.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/z3950/app.js diff --git a/Open-ILS/src/templates/staff/cat/z3950/index.tt2 b/Open-ILS/src/templates/staff/cat/z3950/index.tt2 new file mode 100644 index 0000000000..02f8d08f46 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/z3950/index.tt2 @@ -0,0 +1,17 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Z39.50"); + ctx.page_app = "egCatZ3950Search"; + ctx.page_ctrl = "Z3950SearchCtrl"; +%] + +[% BLOCK APP_JS %] + + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/cat/z3950/t_list.tt2 b/Open-ILS/src/templates/staff/cat/z3950/t_list.tt2 new file mode 100644 index 0000000000..dcd5840f15 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/z3950/t_list.tt2 @@ -0,0 +1,56 @@ +
+
+ [% l('Query') %] +
+ +
+
+
+ [% l('Service and Credentials') %] + +
+
+ +
+
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/cat/z3950/t_search_fields.tt2 b/Open-ILS/src/templates/staff/cat/z3950/t_search_fields.tt2 new file mode 100644 index 0000000000..377695e478 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/z3950/t_search_fields.tt2 @@ -0,0 +1,8 @@ +
+
+ +
+ +
+
+
diff --git a/Open-ILS/src/templates/staff/cat/z3950/t_target.tt2 b/Open-ILS/src/templates/staff/cat/z3950/t_target.tt2 new file mode 100644 index 0000000000..9365ec225b --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/z3950/t_target.tt2 @@ -0,0 +1,19 @@ +
+
+ +
+
[% l('Local Catalog') %]
+
{{target.settings.label}}
+
+
+ + +
+
+ + +
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/navbar.tt2 b/Open-ILS/src/templates/staff/navbar.tt2 index dd9cb38177..6e2e8f73d4 100644 --- a/Open-ILS/src/templates/staff/navbar.tt2 +++ b/Open-ILS/src/templates/staff/navbar.tt2 @@ -221,6 +221,12 @@
  • +
  • + + + [% l('Import Record from Z39.50') %] + +
  • diff --git a/Open-ILS/web/js/ui/default/staff/cat/services/z3950.js b/Open-ILS/web/js/ui/default/staff/cat/services/z3950.js new file mode 100644 index 0000000000..4ef7e70fac --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/services/z3950.js @@ -0,0 +1,132 @@ +angular.module('egZ3950Mod', ['egCoreMod', 'ui.bootstrap']) +.factory('egZ3950TargetSvc', + ['$q', 'egCore', 'egAuth', +function($q, egCore, egAuth) { + + var service = { + targets : [ ], + searchFields : { } + }; + + service.loadTargets = function() { + egCore.net.request( + 'open-ils.search', + 'open-ils.search.z3950.retrieve_services', + egAuth.token() + ).then(function(res) { + // keep the reference, just clear the list + service.targets.length = 0; + // native Evergreen search goes first + var localTarget = res['native-evergreen-catalog']; + delete res['native-evergreen-catalog']; + angular.forEach(res, function(value, key) { + this.push({ + code: key, + settings: value, + selected: false, + username: '', + password: '' + }); + }, service.targets); + service.targets.sort(function (a, b) { + a = a.settings.label; + b = b.settings.label; + return a < b ? -1 : (a > b ? 1 : 0); + }); + service.targets.unshift({ + code: 'native-evergreen-catalog', + settings: localTarget, + selected: false, + username: '', + password: '' + }); + }); + }; + + service.loadActiveSearchFields = function() { + // don't want to throw away the reference, otherwise + // directives bound to searchFields won't + // refresh + var curFormInput = {}; + for (var field in service.searchFields) { + curFormInput[field] = service.searchFields[field].query; + delete service.searchFields[field]; + } + angular.forEach(service.targets, function(target, idx) { + if (target.selected) { + angular.forEach(target.settings.attrs, function(attr, key) { + if (!(key in service.searchFields)) service.searchFields[key] = { + label : attr.label, + query : (key in curFormInput) ? curFormInput[key] : '' + }; + }); + } + }); + }; + + service.clearSearchFields = function() { + for (var field in service.searchFields) { + service.searchFields[field].query = ''; + } + } + + // return the selected Z39.50 targets and search strings + // in a format suitable for passing directly to + // open-ils.search.z3950.search_class + service.currentQuery = function() { + var query = { + service : [], + username : [], + password : [], + search : {} + }; + + angular.forEach(service.targets, function(target, idx) { + if (target.selected) { + query.service.push(target.code); + query.username.push(target.username); + query.password.push(target.password); + } + }); + angular.forEach(service.searchFields, function(value, key) { + if (value.query && value.query.trim()) { + query.search[key] = value.query.trim(); + } + }); + + return query; + } + + return service; +}]) +.directive("egZ3950TargetList", function () { + return { + transclude: true, + restrict: 'AE', + scope: { + + }, + templateUrl: './cat/z3950/t_target', + controller: + ['$scope','egZ3950TargetSvc', + function($scope , egZ3950TargetSvc) { + $scope.targets = egZ3950TargetSvc.targets; + $scope.$watch('targets', function(oldVal, newVal) { + egZ3950TargetSvc.loadActiveSearchFields(); + }, true); + }] + } +}) +.directive("egZ3950SearchFieldList", ['egZ3950TargetSvc', + function(egZ3950TargetSvc) { + return { + restrict: 'AE', + scope: { + }, + templateUrl: './cat/z3950/t_search_fields', + link: function(scope, elem, attr) { + scope.fields = egZ3950TargetSvc.searchFields; + } + }; + } +]); diff --git a/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js b/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js new file mode 100644 index 0000000000..8a6d7d6688 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js @@ -0,0 +1,119 @@ +/* + * Z39.50 search and import + */ + +angular.module('egCatZ3950Search', + ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod', 'egZ3950Mod']) + +.config(function($routeProvider, $locationProvider, $compileProvider) { + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export + + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + // search page shows the list view by default + $routeProvider.when('/cat/z3950/search', { + templateUrl: './cat/z3950/t_list', + controller: 'Z3950SearchCtrl', + resolve : resolver + }); + + // default page / bucket view + $routeProvider.otherwise({redirectTo : '/cat/z3950/search'}); +}) + +/** + * List view - grid stuff + */ +.controller('Z3950SearchCtrl', + ['$scope','$q','$location','$timeout','$window','egCore','egGridDataProvider','egZ3950TargetSvc', +function($scope , $q , $location , $timeout , $window, egCore , egGridDataProvider, egZ3950TargetSvc ) { + + // get list of targets + egZ3950TargetSvc.loadTargets(); + egZ3950TargetSvc.loadActiveSearchFields(); + + var provider = egGridDataProvider.instance({}); + + provider.get = function(offset, count) { + var deferred = $q.defer(); + + var query = egZ3950TargetSvc.currentQuery(); + if (Object.keys(query.search).length == 0) { + return $q.when(); + } + + query['limit'] = count; + query['offset'] = offset; + + var resultIndex = offset; + egCore.net.request( + 'open-ils.search', + 'open-ils.search.z3950.search_class', + egCore.auth.token(), + query + ).then( + function() { deferred.resolve() }, + null, // onerror + function(result) { + for (var i in result.records) { + result.records[i].mvr['service'] = result.service; + result.records[i].mvr['index'] = resultIndex++; + result.records[i].mvr['marcxml'] = result.records[i].marcxml; + deferred.notify(result.records[i].mvr); + } + } + ); + + return deferred.promise; + }; + + $scope.z3950SearchGridProvider = provider; + $scope.gridControls = {}; + + $scope.search = function() { + $scope.z3950SearchGridProvider.refresh(); + }; + $scope.clearForm = function() { + egZ3950TargetSvc.clearSearchFields(); + }; + + $scope.showInCatalog = function() { + var items = $scope.gridControls.selectedItems(); + // relying on cant_showInCatalog to protect us + var url = egCore.env.basePath + + 'cat/catalog/record/' + items[0].tcn(); + $timeout(function() { $window.open(url, '_blank') }); + }; + $scope.cant_showInCatalog = function() { + var items = $scope.gridControls.selectedItems(); + if (items.length != 1) return true; + if (items[0]['service'] == 'native-evergreen-catalog') return false; + return true; + }; + + $scope.import = function() { + var deferred = $q.defer(); + var items = $scope.gridControls.selectedItems(); + egCore.net.request( + 'open-ils.cat', + 'open-ils.cat.biblio.record.xml.import', + egCore.auth.token(), + items[0]['marcxml'] + // FIXME and more + ).then( + function() { deferred.resolve() }, + null, // onerror + function(result) { + console.debug('imported'); + } + ); + + return deferred.promise; + }; + $scope.cant_import = function() { + var items = $scope.gridControls.selectedItems(); + if (items.length == 1) return false; + return true; + }; +}]) -- 2.43.2