--- /dev/null
+<div class="container admin-splash-container">
+
+ <h2>[% l('Print / Storage Service ("Hatch")') %]</h2>
+
+ <div class="alert alert-success" ng-if="hatch_available">
+ [% l("Hatch is Available") %]
+ <span class="glyphicon glyphicon-thumbs-up"></span>
+ </div>
+
+ <div class="alert alert-danger" ng-if="!hatch_available">
+ [% l("Hatch is Not Installed In This Browser") %]
+ </div>
+
+ <div class="row">
+ <div class="col-md-6">
+ <div class="checkbox">
+ <label>
+ <input type="checkbox"
+ ng-model="hatch_printing">
+ [% l('Use Hatch For Printing') %]
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <div class="checkbox">
+ <label>
+ <input type="checkbox"
+ ng-model="hatch_settings">
+ [% l('Store Local Settings in Hatch') %]
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <div class="checkbox">
+ <label>
+ <input type="checkbox"
+ ng-model="hatch_offline">
+ [% l('Store Offline Transaction Data in Hatch') %]
+ </label>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+
<div class="row">
<div class="col-md-12">
- <h2>[% l('Printer Settings for Remote Printing') %]</h2>
- <div class="alert alert-warning" ng-show="hatchNotConnected()">
- [% l("Hatch is not connected") %]
+ <h2>[% l('Remote Printer Settings') %]</h2>
+
+ <div class="alert alert-warning" ng-if="!hatchIsOpen()">
+[% l('Remote printing is not available on this browser. The settings below will have no effect.') %]
+ </div>
+
+ <div class="alert alert-warning"
+ ng-if="hatchIsOpen() && !useHatchPrinting()">
+ <p>
+[% l("Remote printing is not enabled on this browser. The settings below will have no effect until remote printing is enabled.") %]
+ <a href="./admin/workstation/hatch" target="_self"
+ title="[% l('Hatch Administration') %]">
+ [% l('Enable Remote Printing.') %]
+ </a>
+ </p>
</div>
</div>
</div>
<div class="container admin-splash-container">
<div class="row">
- <div class="col-md-6">
- <div class="checkbox">
- <label>
- <input type="checkbox" ng-class="{disabled : !userHasAdminPerm}"
- ng-model="hatchRequired" ng-change="updateHatchRequired()">
-[% l('This workstation uses a remote print / storage service ("Hatch")?') %]
- </label>
+ <div class="col-md-6"><!-- left page column -->
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <span class="glyphicon glyphicon-pushpin"></span>
+ <a target="_self" href="./admin/workstation/workstations">
+ [% l('Registered Workstations') %]
+ </a>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <span class="glyphicon glyphicon-print"></span>
+ <a target="_self" href="./admin/workstation/print/config">
+ [% l('Printer Settings') %]
+ </a>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <span class="glyphicon glyphicon-film"></span>
+ <a target="_self" href="./admin/workstation/print/templates">
+ [% l('Print Templates') %]
+ </a>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <span class="glyphicon glyphicon-info-sign"></span>
+ <a target="_self" href="./admin/workstation/stored_prefs">
+ [% l('Stored Preferences') %]
+ </a>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-6">
+ <span class="glyphicon glyphicon-retweet"></span>
+ <a target="_self" href="./admin/workstation/hatch">
+ [% l('Print/Storage Service ("Hatch")') %]
+ </a>
+ </div>
+ </div>
+
+ <div class="row new-entry">
+ <div class="col-md-4">
+ <div class="checkbox">
+ <label>
+ <input type="checkbox"
+ ng-model="disable_sound"
+ ng-change="apply_sound()">
+ [% l('Disable Sounds?') %]
+ </label>
+ </div>
+ </div>
+ <div class="col-md-8">
+ <span>Test: </span>
+ <button class="btn btn-success" ng-class="{disabled : disable_sound}"
+ ng-click="test_audio('success')">[% l('Success') %]</button>
+ <button class="btn btn-info" ng-class="{disabled : disable_sound}"
+ ng-click="test_audio('info')">[% l('Info') %]</button>
+ <button class="btn btn-warning" ng-class="{disabled : disable_sound}"
+ ng-click="test_audio('warning')">[% l('Warning') %]</button>
+ <button class="btn btn-danger" ng-class="{disabled : disable_sound}"
+ ng-click="test_audio('error')">[% l('Error') %]</button>
+ </div>
+ </div>
+
+ </div><!-- left column -->
+ <div class="col-md-6"><!-- right column -->
+
+ <div class="row">
+ <div class="col-md-8">
+ <label for="search_lib_selector">[% ('Default Search Library') %]</label>
+ <p>[% l('The default search library setting determines what library is searched from the advanced search screen and portal page by default. Manual selection of a search library will override it. One recommendation is to set the search library to the highest point you would normally want to search.') %]</p>
+ </div>
+ <div class="col-md-4">
+ <eg-org-selector id="search_lib_selector"
+ selected="search_lib" nodefault
+ label="[% l('Select...') %]"
+ onchange="handle_search_lib_changed">
+ </eg-org-selector>
+ </div>
+ </div><!-- row -->
+
+ <div class="row new-entry">
+ <div class="col-md-8">
+ <label for="pref_lib_selector">[% ('Preferred Library') %]</label>
+ <p>[% l('The preferred library is used to show copies and URIs regardless of the library searched. One recommendation is to set this to your workstation library so that local copies show up first in search results.') %]</p>
+ </div>
+ <div class="col-md-4">
+ <eg-org-selector id="pref_lib_selector"
+ selected="pref_lib" nodefault
+ label="[% l('Select...') %]"
+ onchange="handle_pref_lib_changed">
+ </eg-org-selector>
+ </div>
</div>
- </div><!-- row -->
- </div>
-
-
- <div class="row new-entry">
- <div class="col-md-2">
- <div class="checkbox">
- <label>
- <input type="checkbox"
- ng-model="disable_sound"
- ng-change="apply_sound()">
- [% l('Disable Sounds?') %]
- </label>
+
+ <div class="row new-entry">
+ <div class="col-md-8">
+ <label for="adv_pane_selector">[% ('Advanced Search Default Pane') %]</label>
+ <p>[% l('Advanced search has secondary panes for Numeric and MARC Expert searching. You can change which one is loaded by default when opening a new catalog window here.') %]</p>
+ </div>
+ <div class="col-md-4">
+ <select id="adv_pane_selector" ng-model="adv_pane">
+ <option value="advanced">[% l('Advanced (default)') %]</option>
+ <option value="numeric" >[% l('Numeric') %]</option>
+ <option value="expert" >[% l('MARC Expert') %]</option>
+ </select>
+ </div>
</div>
- </div>
- <div class="col-md-4">
- <span>Test: </span>
- <button class="btn btn-success" ng-class="{disabled : disable_sound}"
- ng-click="test_audio('success')">[% l('Success') %]</button>
- <button class="btn btn-info" ng-class="{disabled : disable_sound}"
- ng-click="test_audio('info')">[% l('Info') %]</button>
- <button class="btn btn-warning" ng-class="{disabled : disable_sound}"
- ng-click="test_audio('warning')">[% l('Warning') %]</button>
- <button class="btn btn-danger" ng-class="{disabled : disable_sound}"
- ng-click="test_audio('error')">[% l('Error') %]</button>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-4">
- <label for="search_lib_selector">[% ('Default Search Library') %]</label>
- <p>[% l('The default search library setting determines what library is searched from the advanced search screen and portal page by default. Manual selection of a search library will override it. One recommendation is to set the search library to the highest point you would normally want to search.') %]</p>
- </div>
- <div class="col-md-2">
- <eg-org-selector id="search_lib_selector"
- selected="search_lib" nodefault
- label="[% l('Select...') %]"
- onchange="handle_search_lib_changed">
- </eg-org-selector>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-4">
- <label for="pref_lib_selector">[% ('Preferred Library') %]</label>
- <p>[% l('The preferred library is used to show copies and URIs regardless of the library searched. One recommendation is to set this to your workstation library so that local copies show up first in search results.') %]</p>
- </div>
- <div class="col-md-2">
- <eg-org-selector id="pref_lib_selector"
- selected="pref_lib" nodefault
- label="[% l('Select...') %]"
- onchange="handle_pref_lib_changed">
- </eg-org-selector>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-4">
- <label for="adv_pane_selector">[% ('Advanced Search Default Pane') %]</label>
- <p>[% l('Advanced search has secondary panes for Numeric and MARC Expert searching. You can change which one is loaded by default when opening a new catalog window here.') %]</p>
- </div>
- <div class="col-md-2">
- <select id="adv_pane_selector" ng-model="adv_pane">
- <option value="advanced">[% l('Advanced (default)') %]</option>
- <option value="numeric" >[% l('Numeric') %]</option>
- <option value="expert" >[% l('MARC Expert') %]</option>
- </select>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-6">
- <span class="glyphicon glyphicon-pushpin"></span>
- <a target="_self" href="./admin/workstation/workstations">
- [% l('Registered Workstations') %]
- </a>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-6">
- <span class="glyphicon glyphicon-print"></span>
- <a target="_self" href="./admin/workstation/print/config">
- [% l('Printer Settings') %]
- </a>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-6">
- <span class="glyphicon glyphicon-film"></span>
- <a target="_self" href="./admin/workstation/print/templates">
- [% l('Print Templates') %]
- </a>
- </div>
- </div>
-
- <div class="row new-entry">
- <div class="col-md-6">
- <span class="glyphicon glyphicon-info-sign"></span>
- <a target="_self" href="./admin/workstation/stored_prefs">
- [% l('Stored Preferences') %]
- </a>
- </div>
- </div>
+
+ </div><!-- col -->
+ </div><!-- row -->
+
+
+
</div>
<button class="btn btn-default"
ng-click="print_receipt()">[% l('Print Receipt') %]</button>
</div>
- <div class="checkbox" ng-if="using_hatch">
+ <div class="checkbox" ng-if="using_hatch_printer">
<label>
<input ng-model="show_print_dialog" type="checkbox"/>
[% l('Show Print Dialog') %]
</label>
</div>
- <div class="pad-horiz" ng-if="using_hatch"></div>
+ <div class="pad-horiz" ng-if="using_hatch_printer"></div>
<div class="checkbox">
<label>
<input ng-model="trim_list" type="checkbox"/>
[% l('Strict Barcode') %]
</label>
</div>
- <div class="pad-horiz" ng-if="using_hatch"></div>
- <div class="checkbox" ng-if="using_hatch">
+ <div class="pad-horiz" ng-if="using_hatch_printer"></div>
+ <div class="checkbox" ng-if="using_hatch_printer">
<label>
<input ng-model="show_print_dialog" type="checkbox"/>
[% l('Show Print Dialog') %]
resolve : resolver
});
+ $routeProvider.when('/admin/workstation/hatch', {
+ templateUrl: './admin/workstation/t_hatch',
+ controller: 'HatchCtrl',
+ resolve : resolver
+ });
// default page
$routeProvider.otherwise({
['$scope','$window','$location','egCore','egConfirmDialog',
function($scope , $window , $location , egCore , egConfirmDialog) {
- // ---------------------
- // Hatch Configs
- $scope.hatchRequired =
- egCore.hatch.getLocalItem('eg.hatch.required');
-
- $scope.updateHatchRequired = function() {
- egCore.hatch.setLocalItem(
- 'eg.hatch.required', $scope.hatchRequired);
- }
-
egCore.hatch.getItem('eg.audio.disable').then(function(val) {
$scope.disable_sound = val;
});
$scope.adv_pane = val;
});
$scope.$watch('adv_pane', function(newVal, oldVal) {
- if (newVal != oldVal) {
+ if (typeof newVal != 'undefined' && newVal != oldVal) {
egCore.hatch.setItem('eg.search.adv_pane', newVal);
}
});
}
$scope.setContext('default');
- $scope.hatchNotConnected = function() {
- return !egCore.hatch.hatchAvailable;
+ $scope.useHatchPrinting = function() {
+ return egCore.hatch.usePrinting();
+ }
+
+ $scope.hatchIsOpen = function() {
+ return egCore.hatch.hatchAvailable;
}
$scope.getPrinterByAttr = function(attr, value) {
}
}])
+.controller('HatchCtrl',
+ ['$scope','egCore',
+function($scope , egCore) {
+ var hatch = egCore.hatch; // convenience
+
+ $scope.hatch_available = hatch.hatchAvailable;
+ $scope.hatch_printing = hatch.usePrinting();
+ $scope.hatch_settings = hatch.useSettings();
+ $scope.hatch_offline = hatch.useOffline();
+
+ // Apply Hatch settings as changes occur in the UI.
+
+ $scope.$watch('hatch_printing', function(newval) {
+ if (typeof newval != 'boolean') return;
+ hatch.setLocalItem('eg.hatch.enable.printing', newval);
+ });
+
+ $scope.$watch('hatch_settings', function(newval) {
+ if (typeof newval != 'boolean') return;
+ hatch.setLocalItem('eg.hatch.enable.settings', newval);
+ });
+
+ $scope.$watch('hatch_offline', function(newval) {
+ if (typeof newval != 'boolean') return;
+ hatch.setLocalItem('eg.hatch.enable.offline', newval);
+ });
+
+}])
+
$scope.checkins = checkinSvc.checkins;
var today = new Date();
$scope.checkinArgs = {backdate : today}
- $scope.using_hatch = egCore.hatch.usingHatch();
+ $scope.using_hatch_printer = egCore.hatch.usePrinting();
$scope.modifiers = {};
$scope.fine_total = 0;
$scope.is_capture = $location.path().match(/capture$/);
);
}
- $scope.using_hatch = egCore.hatch.usingHatch();
+ $scope.using_hatch_printer = egCore.hatch.usePrinting();
egCore.hatch.getItem('circ.checkout.strict_barcode')
.then(function(sb){ $scope.strict_barcode = sb });
*
* Dispatches print and data storage requests to the appropriate handler.
*
- * With each top-level request, if a connection to Hatch is established,
- * the request is relayed. If a connection has not been attempted, an
- * attempt is made then the request is handled. If Hatch is known to be
- * inaccessible, requests are routed to local handlers.
+ * If Hatch is configured to honor the request -- current request types
+ * are 'settings', 'offline', and 'printing' -- the request will be
+ * relayed to the Hatch service. Otherwise, the request is handled
+ * locally.
*
* Most handlers also provide direct remote and local variants to the
* application can decide to which to use as needed.
var service = {};
service.msgId = 1;
service.messages = {};
- service.pending = [];
- service.hatchAvailable = null;
+ service.hatchAvailable = false;
// key/value cache -- avoid unnecessary Hatch extension requests.
// Only affects *RemoteItem calls.
service.keyCache = {};
+ /**
+ * List string prefixes for On-Call storage keys. On-Call keys
+ * are those that can be set/get/remove'd from localStorage when
+ * Hatch is not avaialable, even though Hatch is configured as the
+ * primary storage location for the key in question. On-Call keys
+ * are those that allow the user to login and perform basic admin
+ * tasks (like disabling Hatch) even when Hatch is down.
+ * AKA Browser Staff Run Level 3.
+ * Note that no attempt is made to synchronize data between Hatch
+ * and localStorage for On-Call keys. Only one destation is active
+ * at a time and each maintains its own data separately.
+ */
+ service.onCallPrefixes = ['eg.workstation'];
+
+ // Returns true if the key can be set/get in localStorage even when
+ // Hatch is not available.
+ service.keyIsOnCall = function(key) {
+ var oncall = false;
+ angular.forEach(service.onCallPrefixes, function(pfx) {
+ if (key.match(new RegExp('^' + pfx)))
+ oncall = true;
+ });
+ return oncall;
+ }
+
// write a message to the Hatch port
service.sendToHatch = function(msg) {
var msg2 = {};
$window.postMessage(msg2, $window.location.origin);
}
- // Send the request to Hatch if it's available.
- // Otherwise handle the request locally.
+ // Send request to Hatch or reject if Hatch is unavailable
service.attemptHatchDelivery = function(msg) {
-
msg.msgid = service.msgId++;
msg.deferred = $q.defer();
- if (service.hatchAvailable === false) { // Hatch is closed.
- msg.deferred.reject(msg);
-
- } else if (service.hatchAvailable === true) { // Hatch is open.
+ if (service.hatchAvailable) {
service.messages[msg.msgid] = msg;
service.sendToHatch(msg);
- } else { // Hatch state unknown
- service.messages[msg.msgid] = msg;
- service.pending.push(msg);
- $timeout(service.openHatch);
+ } else {
+ console.error(
+ 'Hatch request attempted but Hatch is not available');
+ msg.deferred.reject(msg);
}
- /*
- console.debug(
- Object.keys(service.messages).length + " pending Hatch requests");
- */
-
return msg.deferred.promise;
}
// the page body to indicate it's available.
if (!$window.document.body.getAttribute('hatch-is-open')) {
- service.hatchWontOpen('Hatch is not available');
+ console.debug("Hatch is not available");
return;
}
});
service.hatchAvailable = true; // public flag
- service.hatchOpened();
- }
-
- service.hatchWontOpen = function(err) {
- console.debug("Hatch connection failed: " + err);
- service.hatchAvailable = false;
- while ( (msg = service.pending.shift()) ) {
- msg.deferred.reject(msg);
- delete service.messages[msg.msgid];
- }
- }
-
- // Returns true if Hatch is required or if we are currently
- // communicating with the Hatch service.
- service.usingHatch = function() {
- return service.hatchAvailable || service.hatchRequired();
- }
-
- // Returns true if this browser (via localStorage) is
- // configured to require Hatch.
- service.hatchRequired = function() {
- return service.getLocalItem('eg.hatch.required');
- }
-
- service.hatchOpened = function() {
- // let others know we're connected
- if (service.onHatchOpen) service.onHatchOpen();
-
- // Deliver any previously queued requests
- while ( (msg = service.pending.shift()) ) {
- service.sendToHatch(msg);
- };
}
service.remotePrint = function(
);
}
+ service.usePrinting = function() {
+ return service.getLocalItem('eg.hatch.enable.printing');
+ }
+
+ service.useSettings = function() {
+ return service.getLocalItem('eg.hatch.enable.settings');
+ }
+
+ service.useOffline = function() {
+ return service.getLocalItem('eg.hatch.enable.offline');
+ }
+
// get the value for a stored item
service.getItem = function(key) {
- return service.getRemoteItem(key)['catch'](
- function(msg) {
- if (service.hatchRequired()) {
- console.error("getRemoteItem() failed for key " + key);
- return null;
- }
- return service.getLocalItem(msg.key);
- }
- );
+
+ if (!service.useSettings())
+ return $q.when(service.getLocalItem(key));
+
+ if (service.hatchAvailable)
+ return service.getRemoteItem(key);
+
+ if (service.keyIsOnCall(key)) {
+ console.warn("Unable to getItem from Hatch: " + key +
+ ". Retrieving item from local storage instead");
+
+ return $q.when(service.getLocalItem(key));
+ }
+
+ console.error("Unable to getItem from Hatch: " + key);
+ return $q.reject();
}
service.getRemoteItem = function(key) {
service.getLocalItem = function(key) {
var val = $window.localStorage.getItem(key);
if (val == null) return;
- return JSON.parse(val);
+ try {
+ return JSON.parse(val);
+ } catch(E) {
+ console.error(
+ "Deleting invalid JSON for localItem: " + key + " => " + val);
+ service.removeLocalItem(key);
+ return null;
+ }
}
service.getLoginSessionItem = function(key) {
* tmp values are removed during logout or browser close.
*/
service.setItem = function(key, value) {
- return service.setRemoteItem(key, value)['catch'](
- function(msg) {
- if (service.hatchRequired()) {
- console.error("setRemoteItem() failed for key " + key);
- return null;
- }
- return service.setLocalItem(msg.key, value);
- }
- );
+ if (service.useSettings())
+ return $q.when(service.setLocalItem(key, value));
+
+ if (service.hatchAvailable)
+ return service.setRemoteItem(key, value);
+
+ if (service.keyIsOnCall(key)) {
+ console.warn("Unable to setItem in Hatch: " +
+ key + ". Setting in local storage instead");
+
+ return $q.when(service.setLocalItem(key, value));
+ }
+
+ console.error("Unable to setItem in Hatch: " + key);
+ return $q.reject();
}
// set the value for a stored or new item
$window.sessionStorage.setItem(key, jsonified);
}
- // assumes the appender and appendee are both strings
- // TODO: support arrays as well
- service.appendLocalItem = function(key, value) {
- var item = service.getLocalItem(key);
- if (item) {
- if (typeof item != 'string') {
- logger.warn("egHatch.appendLocalItem => "
- + "cannot append to a non-string item: " + key);
- return;
- }
- value = item + value; // concatenate our value
- }
- service.setLocalitem(key, value);
- }
-
// remove a stored item
service.removeItem = function(key) {
- return service.removeRemoteItem(key)['catch'](
- function(msg) {
- return service.removeLocalItem(msg.key)
- }
- );
+ if (!service.useSettings())
+ return $q.when(service.removeLocalItem(key));
+
+ if (service.hatchAvailable)
+ return service.removeRemoteItem(key);
+
+ if (service.keyIsOnCall(key)) {
+ console.warn("Unable to removeItem from Hatch: " + key +
+ ". Removing item from local storage instead");
+
+ return $q.when(service.removeLocalItem(key));
+ }
+
+ console.error("Unable to removeItem from Hatch: " + key);
+ return $q.reject();
}
service.removeRemoteItem = function(key) {
// if set, prefix limits the return set to keys starting with 'prefix'
service.getKeys = function(prefix) {
- return service.getRemoteKeys(prefix)['catch'](
- function() {
- if (service.hatchRequired()) {
- console.error("getRemoteKeys() failed");
- return [];
- }
- return service.getLocalKeys(prefix)
- }
- );
+ if (service.useSettings())
+ return service.getRemoteKeys(prefix);
+ return $q.when(service.getLocalKeys(prefix));
}
service.getRemoteKeys = function(prefix) {
service.setLocalItem('eg.hatch.login_keys', keys);
}
+ // The only requirement for opening Hatch is that the DOM be loaded.
+ // Open the connection now so its state will be immediately available.
+ service.openHatch();
+
return service;
}])
service.print_content = function(args) {
service.fleshPrintScope(args.scope);
- var promise = egHatch.hatchAvailable ?
+ var promise = egHatch.usePrinting() ?
service.print_via_hatch(args) :
service.print_via_browser(args);