-<div>
-<strong>[% l('Search') %]</strong>
+<div class="row">
+ <div class="col-xs-6">
+ <strong>[% l('Query') %]</strong>
+ <eg-z3950-search-field-list></eg-z3950-search-field-list>
+ </div>
+ <div class="col-xs-6">
+ <strong>[% l('Service and Credentials') %]</strong>
+ <eg-z3950-target-list>
+ </div>
</div>
-<div>
-<strong>[% l('Targets') %]</strong>
-<eg-z3950-target-list></eg-z3950-target-list>
+<div class="row" id="z3950-search-form-row">
+ <form ng-submit="search()" id="z3950-search-form"
+ role="form" class="form-inline">
+ <div class="button-group">
+ <input type="submit" class="btn btn-primary" value="[% l('Search') %]"/>
+
+ <input type="reset" class="btn btn-primary" ng-click="clearForm()"
+ value="[% l('Clear Form') %]"/>
+ </div>
+ </form>
</div>
<eg-grid
id-field="index"
+ idl-class="mvr"
features="-display,-sort,-multisort"
main-label="[% l('Results') %]"
- items-provider="gridDataProvider"
+ items-provider="z3950SearchGridProvider"
grid-controls="gridControls"
persist-key="cat.z3950_results">
+ <eg-grid-field label="[% l('Title') %]" path="title" visible></eg-grid-field>
+ <eg-grid-field label="[% l('Author') %]" path="author" visible></eg-grid-field>
+ <eg-grid-field label="[% l('Edition') %]" path="edition" visible></eg-grid-field>
+ <eg-grid-field label="[% l('ISBN') %]" path="isbn" visible></eg-grid-field>
+ <eg-grid-field label="[% l('Publication Date') %]" path="pubdate" visible></eg-grid-field>
+ <eg-grid-field label="[% l('Publisher') %]" path="publisher" visible></eg-grid-field>
+ <eg-grid-field label="[% l('Service') %]" path="service" visible></eg-grid-field>
+ <eg-grid-field label="[% l('TCN') %]" path="tcn" visible></eg-grid-field>
+ <eg-grid-field label
+ <eg-grid-field path="*" hidden></eg-grid-field>
</eg-grid>
--- /dev/null
+<div class="form-horizontal">
+ <div ng-repeat="(code, search_field) in fields" class="z3950-search-field-list form-group">
+ <label for="z3950-field-{{code}}" class="col-xs-6 control-label">{{search_field.label}}</label>
+ <div class="col-xs-6">
+ <input type="text" class="form=control" id="z3950-field-{{code}}" ng-model="search_field.query">
+ </div>
+ </div>
+</div>
-<div ng-repeat="target in targets">
- <span>{{target.code}} / {{target.settings.label}}}</span>
- <span ng-if="target.settings.auth == 't'">requires auth</span>
+<div ng-repeat="target in targets" class="z3950-target-list">
+ <div class="checkbox">
+ <input ng-model="target.selected" type="checkbox">
+ <div>
+ <div ng-if="target.code == 'native-evergreen-catalog'">[% l('Local Catalog') %]</div>
+ <div ng-if="target.code != 'native-evergreen-catalog'">{{target.settings.label}}</div>
+ <div ng-if="target.settings.auth == 't'" class="form-inline row">
+ <div class="form-group col-xs-6">
+ <label for="username-for-z3950-{{target.code}}">[% l('Username') %]</label>
+ <input type="text" class="form-control" id="username-for-z3950-{{target.code}}" ng-model="target.username">
+ </div>
+ <div class="form-group col-xs-6">
+ <label for="password-for-z3950-{{target.code}}">[% l('Password') %]</label>
+ <input type="text" class="form-control" id="password-for-z3950-{{target.code}}" ng-model="target.password">
+ </div>
+ </div>
+</div>
+ </div>
</div>
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) {
+ service.targets = [];
+ // 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
+ for (var field in service.searchFields) {
+ 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 : ''
+ };
+ });
+ }
+ });
+ };
+
+ // 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,
},
templateUrl: './cat/z3950/t_target',
controller:
- ['$scope','egCore','egAuth',
- function($scope , egCore, egAuth) {
- function loadTargets() {
- egCore.net.request(
- 'open-ils.search',
- 'open-ils.search.z3950.retrieve_services',
- egAuth.token()
- ).then(function(res) {
- $scope.targets = [];
- // 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
- });
- }, $scope.targets);
- $scope.targets.sort(function (a, b) {
- a = a.settings.label;
- b = b.settings.label;
- return a < b ? -1 : (a > b ? 1 : 0);
- });
- $scope.targets.unshift({
- code: 'native-evergreen-catalog',
- settings: localTarget
- });
- });
- }
- loadTargets();
+ ['$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;
+ }
+ };
+ }
+]);
--- /dev/null
+/*
+ * 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','egCore','egGridDataProvider','egZ3950TargetSvc',
+function($scope , $q , $location , $timeout , 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();
+ console.debug(query);
+ if (query.search.length == 0) {
+ return $q.when();
+ }
+
+ query['limit'] = count;
+ query['offset'] = 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;
+ deferred.notify(result.records[i].mvr);
+ }
+ }
+ );
+
+ return deferred.promise;
+ };
+
+ $scope.z3950SearchGridProvider = provider;
+
+ $scope.search = function() {
+ $scope.z3950SearchGridProvider.refresh();
+ };
+}])