diff --git a/circ/circulation-home.pl b/circ/circulation-home.pl index d4f5a2c069..d10829f6d3 100755 --- a/circ/circulation-home.pl +++ b/circ/circulation-home.pl @@ -39,6 +39,7 @@ $template->param( fast_cataloging => 1 ) if (defined $fa); # Checking if the transfer page needs to be displayed $template->param( display_transfer => 1 ) if ( ($flags->{'superlibrarian'} == 1) || (C4::Context->preference("IndependentBranches") == 0) ); +$template->{'VARS'}->{'AllowOfflineCirculation'} = C4::Context->preference('AllowOfflineCirculation'); output_html_with_http_headers $query, $cookie, $template->output; diff --git a/circ/offline-mf.pl b/circ/offline-mf.pl new file mode 100755 index 0000000000..010c86a0f8 --- /dev/null +++ b/circ/offline-mf.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA +# + +use Modern::Perl; +use CGI; +use C4::Auth; + +my $query = new CGI; +my ($template, $loggedinuser, $cookie, $flags) += get_template_and_user({template_name => "circ/offline-mf.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => {circulate => "circulate_remaining_permissions"}, + }); + +$template->{'VARS'}->{'cookie'} = $cookie; +print $query->header(-type => 'text/cache-manifest', cookie => $cookie); +print $template->output; diff --git a/circ/offline.pl b/circ/offline.pl new file mode 100755 index 0000000000..b33dc5e453 --- /dev/null +++ b/circ/offline.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA +# + +use Modern::Perl; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Context; + +my $query = new CGI; +my ($template, $loggedinuser, $cookie, $flags) += get_template_and_user({template_name => "circ/offline.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => {circulate => "circulate_remaining_permissions"}, + }); + +$template->{'VARS'}->{'AllowOfflineCirculation'} = C4::Context->preference('AllowOfflineCirculation'); +$template->{'VARS'}->{'maxoutstanding'} = C4::Context->preference('maxoutstanding') || 0; +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 4b0f364ede..71513ce875 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -17,6 +17,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('AllowItemsOnHoldCheckout','0','','Do not generate RESERVE_WAITING and RESERVED warning when checking out items reserved to someone else. This allows self checkouts for those items.','YesNo'), ('AllowMultipleCovers','0','1','Allow multiple cover images to be attached to each bibliographic record.','YesNo'), ('AllowNotForLoanOverride','0','','If ON, Koha will allow the librarian to loan a not for loan item.','YesNo'), +('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo'), ('AllowOnShelfHolds','0','','Allow hold requests to be placed on items that are not on loan','YesNo'), ('AllowPKIAuth','None','None|Common Name|emailAddress','Use the field from a client-side SSL certificate to look a user in the Koha database','Choice'), ('AllowPurchaseSuggestionBranchChoice','0','1','Allow user to choose branch when making a purchase suggestion','YesNo'), diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index abd7c8e314..7dbbad4bd9 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -7185,6 +7185,13 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.13.00.XXX"; +if ( CheckVersion($DBversion) ) { + $dbh->do("INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo')"); + print "Upgrade to $DBversion done (Bug 10240: Add syspref AllowOfflineCirculation)\n"; + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.indexeddb.js b/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.indexeddb.js new file mode 100644 index 0000000000..e75a2a5875 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.indexeddb.js @@ -0,0 +1,517 @@ +(function($, undefined) { + var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; + var IDBCursor = window.IDBCursor || window.webkitIDBCursor; + IDBCursor.PREV = IDBCursor.PREV || "prev"; + IDBCursor.NEXT = IDBCursor.NEXT || "next"; + + /** + * Best to use the constant IDBTransaction since older version support numeric types while the latest spec + * supports strings + */ + var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; + + function getDefaultTransaction(mode) { + var result = null; + switch (mode) { + case 0: + case 1: + case "readwrite": + case "readonly": + result = mode; + break; + default: + result = IDBTransaction.READ_WRITE || "readwrite"; + } + return result; + } + + $.extend({ + /** + * The IndexedDB object used to open databases + * @param {Object} dbName - name of the database + * @param {Object} config - version, onupgradeneeded, onversionchange, schema + */ + "indexedDB": function(dbName, config) { + if (config) { + // Parse the config argument + if (typeof config === "number") config = { + "version": config + }; + + var version = config.version; + if (config.schema && !version) { + var max = -1; + for (key in config.schema) { + max = max > key ? max : key; + } + version = config.version || max; + } + } + + + var wrap = { + "request": function(req, args) { + return $.Deferred(function(dfd) { + try { + var idbRequest = typeof req === "function" ? req(args) : req; + idbRequest.onsuccess = function(e) { + + dfd.resolveWith(idbRequest, [idbRequest.result, e]); + }; + idbRequest.onerror = function(e) { + + dfd.rejectWith(idbRequest, [idbRequest.error, e]); + }; + if (typeof idbRequest.onblocked !== "undefined" && idbRequest.onblocked === null) { + idbRequest.onblocked = function(e) { + + var res; + try { + res = idbRequest.result; + } catch (e) { + res = null; // Required for Older Chrome versions, accessing result causes error + } + dfd.notifyWith(idbRequest, [res, e]); + }; + } + if (typeof idbRequest.onupgradeneeded !== "undefined" && idbRequest.onupgradeneeded === null) { + idbRequest.onupgradeneeded = function(e) { + + dfd.notifyWith(idbRequest, [idbRequest.result, e]); + }; + } + } catch (e) { + e.name = "exception"; + dfd.rejectWith(idbRequest, ["exception", e]); + } + }); + }, + // Wraps the IDBTransaction to return promises, and other dependent methods + "transaction": function(idbTransaction) { + return { + "objectStore": function(storeName) { + try { + return wrap.objectStore(idbTransaction.objectStore(storeName)); + } catch (e) { + idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); + return wrap.objectStore(null); + } + }, + "createObjectStore": function(storeName, storeParams) { + try { + return wrap.objectStore(idbTransaction.db.createObjectStore(storeName, storeParams)); + } catch (e) { + idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); + } + }, + "deleteObjectStore": function(storeName) { + try { + idbTransaction.db.deleteObjectStore(storeName); + } catch (e) { + idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); + } + }, + "abort": function() { + idbTransaction.abort(); + } + }; + }, + "objectStore": function(idbObjectStore) { + var result = {}; + // Define CRUD operations + var crudOps = ["add", "put", "get", "delete", "clear", "count"]; + for (var i = 0; i < crudOps.length; i++) { + result[crudOps[i]] = (function(op) { + return function() { + return wrap.request(function(args) { + return idbObjectStore[op].apply(idbObjectStore, args); + }, arguments); + }; + })(crudOps[i]); + } + + result.each = function(callback, range, direction) { + return wrap.cursor(function() { + if (direction) { + return idbObjectStore.openCursor(wrap.range(range), direction); + } else { + return idbObjectStore.openCursor(wrap.range(range)); + } + }, callback); + }; + + result.index = function(name) { + return wrap.index(function() { + return idbObjectStore.index(name); + }); + }; + + result.createIndex = function(prop, options, indexName) { + if (arguments.length === 2 && typeof options === "string") { + indexName = arguments[1]; + options = null; + } + if (!indexName) { + indexName = prop; + } + return wrap.index(function() { + return idbObjectStore.createIndex(indexName, prop, options); + }); + }; + + result.deleteIndex = function(indexName) { + return idbObjectStore.deleteIndex(indexName); + }; + + return result; + }, + + "range": function(r) { + if ($.isArray(r)) { + if (r.length === 1) { + return IDBKeyRange.only(r[0]); + } else { + return IDBKeyRange.bound(r[0], r[1], (typeof r[2] === 'undefined') ? true : r[2], (typeof r[3] === 'undefined') ? true : r[3]); + } + } else if (typeof r === "undefined") { + return null; + } else { + return r; + } + }, + + "cursor": function(idbCursor, callback) { + return $.Deferred(function(dfd) { + try { + + var cursorReq = typeof idbCursor === "function" ? idbCursor() : idbCursor; + cursorReq.onsuccess = function(e) { + + if (!cursorReq.result) { + dfd.resolveWith(cursorReq, [null, e]); + return; + } + var elem = { + // Delete, update do not move + "delete": function() { + return wrap.request(function() { + return cursorReq.result["delete"](); + }); + }, + "update": function(data) { + return wrap.request(function() { + return cursorReq.result["update"](data); + }); + }, + "next": function(key) { + this.data = key; + }, + "key": cursorReq.result.key, + "value": cursorReq.result.value + }; + + dfd.notifyWith(cursorReq, [elem, e]); + var result = callback.apply(cursorReq, [elem]); + + try { + if (result === false) { + dfd.resolveWith(cursorReq, [null, e]); + } else if (typeof result === "number") { + cursorReq.result["advance"].apply(cursorReq.result, [result]); + } else { + if (elem.data) cursorReq.result["continue"].apply(cursorReq.result, [elem.data]); + else cursorReq.result["continue"](); + } + } catch (e) { + + dfd.rejectWith(cursorReq, [cursorReq.result, e]); + } + }; + cursorReq.onerror = function(e) { + + dfd.rejectWith(cursorReq, [cursorReq.result, e]); + }; + } catch (e) { + + e.type = "exception"; + dfd.rejectWith(cursorReq, [null, e]); + } + }); + }, + + "index": function(index) { + try { + var idbIndex = (typeof index === "function" ? index() : index); + } catch (e) { + idbIndex = null; + } + + return { + "each": function(callback, range, direction) { + return wrap.cursor(function() { + if (direction) { + return idbIndex.openCursor(wrap.range(range), direction); + } else { + return idbIndex.openCursor(wrap.range(range)); + } + + }, callback); + }, + "eachKey": function(callback, range, direction) { + return wrap.cursor(function() { + if (direction) { + return idbIndex.openKeyCursor(wrap.range(range), direction); + } else { + return idbIndex.openKeyCursor(wrap.range(range)); + } + }, callback); + }, + "get": function(key) { + if (typeof idbIndex.get === "function") { + return wrap.request(idbIndex.get(key)); + } else { + return idbIndex.openCursor(wrap.range(key)); + } + }, + "count": function() { + if (typeof idbIndex.count === "function") { + return wrap.request(idbIndex.count()); + } else { + throw "Count not implemented for cursors"; + } + }, + "getKey": function(key) { + if (typeof idbIndex.getKey === "function") { + return wrap.request(idbIndex.getKey(key)); + } else { + return idbIndex.openKeyCursor(wrap.range(key)); + } + } + }; + } + }; + + + // Start with opening the database + var dbPromise = wrap.request(function() { + + return version ? indexedDB.open(dbName, parseInt(version)) : indexedDB.open(dbName); + }); + dbPromise.then(function(db, e) { + + db.onversionchange = function() { + // Try to automatically close the database if there is a version change request + if (!(config && config.onversionchange && config.onversionchange() !== false)) { + db.close(); + } + }; + }, function(error, e) { + + // Nothing much to do if an error occurs + }, function(db, e) { + if (e && e.type === "upgradeneeded") { + if (config && config.schema) { + // Assuming that version is always an integer + + for (var i = e.oldVersion + 1; i <= e.newVersion; i++) { + typeof config.schema[i] === "function" && config.schema[i].call(this, wrap.transaction(this.transaction)); + } + } + if (config && typeof config.upgrade === "function") { + config.upgrade.call(this, wrap.transaction(this.transaction)); + } + } + }); + + return $.extend(dbPromise, { + "cmp": function(key1, key2) { + return indexedDB.cmp(key1, key2); + }, + "deleteDatabase": function() { + // Kinda looks ugly coz DB is opened before it needs to be deleted. + // Blame it on the API + return $.Deferred(function(dfd) { + dbPromise.then(function(db, e) { + db.close(); + wrap.request(function() { + return indexedDB.deleteDatabase(dbName); + }).then(function(result, e) { + dfd.resolveWith(this, [result, e]); + }, function(error, e) { + dfd.rejectWith(this, [error, e]); + }, function(db, e) { + dfd.notifyWith(this, [db, e]); + }); + }, function(error, e) { + dfd.rejectWith(this, [error, e]); + }, function(db, e) { + dfd.notifyWith(this, [db, e]); + }); + }); + }, + "transaction": function(storeNames, mode) { + !$.isArray(storeNames) && (storeNames = [storeNames]); + mode = getDefaultTransaction(mode); + return $.Deferred(function(dfd) { + dbPromise.then(function(db, e) { + var idbTransaction; + try { + + idbTransaction = db.transaction(storeNames, mode); + + idbTransaction.onabort = idbTransaction.onerror = function(e) { + dfd.rejectWith(idbTransaction, [e]); + }; + idbTransaction.oncomplete = function(e) { + dfd.resolveWith(idbTransaction, [e]); + }; + } catch (e) { + + e.type = "exception"; + dfd.rejectWith(this, [e]); + return; + } + try { + dfd.notifyWith(idbTransaction, [wrap.transaction(idbTransaction)]); + } catch (e) { + e.type = "exception"; + dfd.rejectWith(this, [e]); + } + }, function(err, e) { + dfd.rejectWith(this, [e, err]); + }, function(res, e) { + + //dfd.notifyWith(this, ["", e]); + }); + + }); + }, + "objectStore": function(storeName, mode) { + var me = this, + result = {}; + + function op(callback) { + return $.Deferred(function(dfd) { + function onTransactionProgress(trans, callback) { + try { + + callback(trans.objectStore(storeName)).then(function(result, e) { + dfd.resolveWith(this, [result, e]); + }, function(err, e) { + dfd.rejectWith(this, [err, e]); + }); + } catch (e) { + + e.name = "exception"; + dfd.rejectWith(trans, [e, e]); + } + } + me.transaction(storeName, getDefaultTransaction(mode)).then(function() { + + // Nothing to do when transaction is complete + }, function(err, e) { + // If transaction fails, CrudOp fails + if (err.code === err.NOT_FOUND_ERR && (mode === true || typeof mode === "object")) { + + var db = this.result; + db.close(); + dbPromise = wrap.request(function() { + + return indexedDB.open(dbName, (parseInt(db.version, 10) || 1) + 1); + }); + dbPromise.then(function(db, e) { + + db.onversionchange = function() { + // Try to automatically close the database if there is a version change request + if (!(config && config.onversionchange && config.onversionchange() !== false)) { + db.close(); + } + }; + me.transaction(storeName, getDefaultTransaction(mode)).then(function() { + + // Nothing much to do + }, function(err, e) { + dfd.rejectWith(this, [err, e]); + }, function(trans, e) { + + onTransactionProgress(trans, callback); + }); + }, function(err, e) { + dfd.rejectWith(this, [err, e]); + }, function(db, e) { + if (e.type === "upgradeneeded") { + try { + + db.createObjectStore(storeName, mode === true ? { + "autoIncrement": true + } : mode); + + } catch (ex) { + + dfd.rejectWith(this, [ex, e]); + } + } + }); + } else { + dfd.rejectWith(this, [err, e]); + } + }, function(trans) { + + onTransactionProgress(trans, callback); + }); + }); + } + + function crudOp(opName, args) { + return op(function(wrappedObjectStore) { + return wrappedObjectStore[opName].apply(wrappedObjectStore, args); + }); + } + + function indexOp(opName, indexName, args) { + return op(function(wrappedObjectStore) { + var index = wrappedObjectStore.index(indexName); + return index[opName].apply(index[opName], args); + }); + } + + var crud = ["add", "delete", "get", "put", "clear", "count", "each"]; + for (var i = 0; i < crud.length; i++) { + result[crud[i]] = (function(op) { + return function() { + return crudOp(op, arguments); + }; + })(crud[i]); + } + + result.index = function(indexName) { + return { + "each": function(callback, range, direction) { + return indexOp("each", indexName, [callback, range, direction]); + }, + "eachKey": function(callback, range, direction) { + return indexOp("eachKey", indexName, [callback, range, direction]); + }, + "get": function(key) { + return indexOp("get", indexName, [key]); + }, + "count": function() { + return indexOp("count", indexName, []); + }, + "getKey": function(key) { + return indexOp("getKey", indexName, [key]); + } + }; + }; + + return result; + } + }); + } + }); + + $.indexedDB.IDBCursor = IDBCursor; + $.indexedDB.IDBTransaction = IDBTransaction; + $.idb = $.indexedDB; +})(jQuery); \ No newline at end of file diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css index 308e3a02b2..e262249c9b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -2659,3 +2659,27 @@ span.browse-button { float: right; padding-right: 1em; } + +.loading-overlay { + background-color: #FFF; + cursor: wait; + height: 100%; + left: 0; + opacity: .7; + position: fixed; + top: 0; + width: 100%; + z-index: 1000; +} +.loading-overlay div { + background : transparent url(../../img/loading.gif) top left no-repeat; + font-size : 175%; + font-weight: bold; + height: 2em; + left: 50%; + margin: -1em 0 0 -2.5em; + padding-left : 50px; + position: absolute; + top: 50%; + width: 15em; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/offlinecirc.js b/koha-tmpl/intranet-tmpl/prog/en/js/offlinecirc.js new file mode 100644 index 0000000000..4645d17535 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/js/offlinecirc.js @@ -0,0 +1,109 @@ +/* Copyright 2013 C & P Bibliography Services + * + * This file is part of Koha. + * + * Koha is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * Koha is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Koha; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +(function( kohadb, $, undefined ) { + kohadb.settings = kohadb.settings || {}; + kohadb.initialize = function (callback) { + $.indexedDB("koha", { + "version": 1, + "schema": { + "1": function(versionTransaction){ + var patrons = versionTransaction.createObjectStore("patrons", { + "keyPath": "cardnumber" + }); + var items = versionTransaction.createObjectStore("items", { + "keyPath": "barcode" + }); + var issues = versionTransaction.createObjectStore("issues", { + "keyPath": "barcode" + }); + issues.createIndex("cardnumber", { "multiEntry": true }); + var transactions = versionTransaction.createObjectStore("transactions", { + "keyPath": "timestamp" + }); + var settings = versionTransaction.createObjectStore("offline_settings", { + "keyPath": "key" + }); + }, + } + }).done(function(){ + if (typeof callback === 'function') { + callback(); + kohadb.loadSetting('userid'); + kohadb.loadSetting('branchcode'); + } + }); + }; + kohadb.loadSetting = function (key, callback) { + $.indexedDB("koha").transaction(["offline_settings"]).then(function(){ + }, function(err, e){ + }, function(transaction){ + var settings = transaction.objectStore("offline_settings"); + settings.get(key).done(function (item, error) { + if (typeof item !== 'undefined') { + kohadb.settings[key] = item.value; + } + if (typeof callback === 'function') { + callback(key, kohadb.settings[key]); + } + }); + }); + }; + kohadb.saveSetting = function (key, value) { + $.indexedDB("koha").transaction(["offline_settings"]).then(function(){ + }, function(err, e){ + }, function(transaction){ + var settings = transaction.objectStore("offline_settings"); + settings.put({ "key" : key, "value" : value }); + kohadb.settings[key] = value; + }); + }; + kohadb.recordTransaction = function (newtrans, callback) { + $.indexedDB("koha").transaction(["transactions"]).then(function(){ + callback(newtrans); + }, function(err, e){ + }, function(dbtransaction) { + var transactions = dbtransaction.objectStore("transactions"); + transactions.put(newtrans); + }); + }; +}( window.kohadb = window.bndb || {}, jQuery )); + +if ( !Date.prototype.toMySQLString ) { + ( function() { + + function pad(number) { + var r = String(number); + if ( r.length === 1 ) { + r = '0' + r; + } + return r; + } + + Date.prototype.toMySQLString = function() { + return this.getFullYear() + + '-' + pad( this.getMonth() + 1 ) + + '-' + pad( this.getDate() ) + + ' ' + pad( this.getHours() ) + + ':' + pad( this.getMinutes() ) + + ':' + pad( this.getSeconds() ) + + '.' + String( (this.getMilliseconds()/1000).toFixed(3) ).slice( 2, 5 ); + }; + + }() ); +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index 005c0e549a..a8f4e43ad7 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -129,6 +129,12 @@ Circulation: - The following fields should be excluded from the patron checkout history CSV or iso2709 export - pref: ExportRemoveFields - (separate fields with space, e.g. 100a 200b 300c) + - + - pref: AllowOfflineCirculation + choices: + yes: Enable + no: "Do not enable" + - "offline circulation on regular circulation computers. (NOTE: This system preference does not affect the Firefox plugin or the desktop application)" Checkout Policy: - diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt index af533f49b3..72abd1fd8f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt @@ -53,12 +53,15 @@
Offline circulation
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline-mf.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline-mf.tt new file mode 100644 index 0000000000..93a0259ccc --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline-mf.tt @@ -0,0 +1,43 @@ +CACHE MANIFEST +# [% cookie %] + +# Explicitly cached 'master entries'. +CACHE: +/cgi-bin/koha/circ/offline.pl +/intranet-tmpl/lib/bootstrap/bootstrap.min.css +/intranet-tmpl/lib/bootstrap/bootstrap.min.js +/intranet-tmpl/lib/jquery/jquery-ui.css +/intranet-tmpl/lib/jquery/jquery-ui.js +/intranet-tmpl/lib/jquery/jquery.js +/intranet-tmpl/lib/jquery/plugins/jquery.cookie.min.js +/intranet-tmpl/lib/jquery/plugins/jquery.highlight-3.js +/intranet-tmpl/lib/jquery/plugins/jquery.hotkeys.min.js +/intranet-tmpl/lib/jquery/plugins/jquery.indexeddb.js +/intranet-tmpl/lib/jquery/plugins/jquery.validate.min.js +/intranet-tmpl/prog/en/css/print.css +/intranet-tmpl/prog/en/css/staff-global.css +/intranet-tmpl/prog/en/js/basket.js +/intranet-tmpl/prog/en/js/offlinecirc.js +/intranet-tmpl/prog/en/js/staff-global.js +/intranet-tmpl/prog/en/lib/jquery/plugins/jquery-ui-timepicker-addon.js +/intranet-tmpl/prog/en/lib/yui/button/button-min.js +/intranet-tmpl/prog/en/lib/yui/container/container_core-min.js +/intranet-tmpl/prog/en/lib/yui/menu/menu-min.js +/intranet-tmpl/prog/en/lib/yui/reset-fonts-grids.css +/intranet-tmpl/prog/en/lib/yui/skin.css +/intranet-tmpl/prog/en/lib/yui/utilities/utilities.js +/intranet-tmpl/prog/img/cart-small.gif +/intranet-tmpl/prog/img/glyphicons-halflings-koha.png +/intranet-tmpl/prog/img/koha-logo-medium.gif +/intranet-tmpl/prog/img/loading.gif +/intranet-tmpl/prog/sound/beep.ogg +/intranet-tmpl/prog/sound/critical.ogg + +# Resources that require the user to be online. +NETWORK: +* + +# static.html will be served if main.py is inaccessible +# offline.jpg will be served in place of all images in images/large/ +# offline.html will be served in place of all other .html files +FALLBACK: diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline.tt new file mode 100644 index 0000000000..772d9db3bc --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline.tt @@ -0,0 +1,690 @@ + +[% IF (AllowOfflineCirculation) %] +[% SET manifestattr = 'manifest="/cgi-bin/koha/circ/offline-mf.pl"' %] +[% END %] +[% IF ( bidi ) %][% ELSE %][% END %] + +Koha › Circulation +[% INCLUDE 'doc-head-close.inc' %] + + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'circ-search.inc' %] + + + + +
+ +
+
+ + + +
+
+ [% UNLESS (AllowOfflineCirculation) %] +
+

Warning: Offline Circulation has been disabled. You may continue and record transactions, but patron and item information will not be available.

+
+ [% END %] + +
+
+

Offline circulation

+ + +
+

Note: You must be online to use these options.

+ +
+
+
+ + + + + + +
+ +