library: one address - Add books inside the staff site, plus Find a book
- Apache in the Koha container serves Quick Add at /add/ on the staff site (KOHA_QUICKADD_URL); koha now starts after quickadd so the name resolves - Quick Add: relative API paths so it works at / and /add/; Add books / Find a book tabs; search by title, author or ISBN showing each copy's shelf and status (/api/search); Manage and Public catalogue links - staff home gets a 'Library tools' block (HTML customizations, how Koha 26.05 stores it) linking to Add books - installer, kiosk add-on and READMEs advertise one main address; the public catalogue stays on its own port (Koha gives public and staff sites the same URL shapes, so they cannot share one address) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
This commit is contained in:
+11
-4
@@ -12,12 +12,19 @@ it never overwrites your `.env` (passwords) or your data.
|
|||||||
|
|
||||||
| What | Address | Login |
|
| What | Address | Login |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Catalogue** (public search, no login) | `http://<this machine>:8080` | none |
|
| **Your library**: add books, find a book, people, lending, labels, settings | `http://<this machine>:8081` | `admin` and the generated password |
|
||||||
| **Staff interface** (cataloguing, labels, Z39.50) | `http://<this machine>:8081` | `admin` and the generated password |
|
| **Add books** (scan an ISBN, or use the phone camera) | `http://<this machine>:8081/add/` | same login (asked once more) |
|
||||||
| **Add books** (scan an ISBN) | `http://<this machine>:8082` | same login |
|
| **Public catalogue** (search only, for a kiosk or visitors) | `http://<this machine>:8080` | none |
|
||||||
|
|
||||||
|
For home use you only need the first address: sign in to **Your library**, and use the **Add books**
|
||||||
|
button on its home page. The Add books page has a **Find a book** tab (title, author or ISBN, with the
|
||||||
|
shelf and whether it is on the shelf or checked out) and a **Manage** link back to the staff site. The
|
||||||
|
public catalogue exists for a kiosk screen or visitors and does not need a login. (Koha gives its public and
|
||||||
|
staff sites the same URL shapes, so they cannot share one address; Add books can, and does.)
|
||||||
|
|
||||||
Ports move up to the next free number if one is taken; the final addresses are printed
|
Ports move up to the next free number if one is taken; the final addresses are printed
|
||||||
at the end and by `sudo ./install.sh --status`.
|
at the end and by `sudo ./install.sh --status`. Add books is also reachable directly on its own port
|
||||||
|
(`8082`), which is handy behind a proxy but not needed otherwise.
|
||||||
|
|
||||||
## What it is made of
|
## What it is made of
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
quickadd:
|
||||||
|
condition: service_started
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ:-UTC}
|
TZ: ${TZ:-UTC}
|
||||||
|
KOHA_QUICKADD_URL: http://quickadd:8082
|
||||||
KOHA_DB_HOST: db
|
KOHA_DB_HOST: db
|
||||||
KOHA_DB_PASS: ${DB_PASSWORD}
|
KOHA_DB_PASS: ${DB_PASSWORD}
|
||||||
KOHA_ADMIN_USER: ${ADMIN_USER:-admin}
|
KOHA_ADMIN_USER: ${ADMIN_USER:-admin}
|
||||||
@@ -53,9 +56,6 @@ services:
|
|||||||
image: library-quickadd:local
|
image: library-quickadd:local
|
||||||
container_name: library-quickadd
|
container_name: library-quickadd
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
|
||||||
koha:
|
|
||||||
condition: service_started
|
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ:-UTC}
|
TZ: ${TZ:-UTC}
|
||||||
KOHA_API: http://koha:8081/api/v1
|
KOHA_API: http://koha:8081/api/v1
|
||||||
|
|||||||
+4
-4
@@ -79,10 +79,10 @@ show_status() {
|
|||||||
fi
|
fi
|
||||||
dc ps
|
dc ps
|
||||||
echo
|
echo
|
||||||
echo " Catalogue (public): http://$ip:$OPAC_PORT"
|
echo " Your library: http://$ip:$STAFF_PORT"
|
||||||
echo " Staff interface: http://$ip:$STAFF_PORT"
|
echo " sign in here: add books at /add/ (or the 'Add books' button), find a book, lending, labels, people"
|
||||||
echo " Add books (scan): http://$ip:${QUICKADD_PORT:-8082} (same login)"
|
echo " Public catalogue: http://$ip:$OPAC_PORT (search only - for a kiosk or visitors)"
|
||||||
echo " Staff login: $ADMIN_USER / $ADMIN_PASSWORD (also stored in $HERE/.env)"
|
echo " Login: $ADMIN_USER / $ADMIN_PASSWORD (also stored in $HERE/.env; Add books asks once more with the same login)"
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,19 @@ Koha::CirculationRules->set_rules({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# ---- Staff home page: a "Library tools" block with a link to the Add books page ----
|
||||||
|
# (Koha 26.05 stores these in the HTML customizations tool, not in a preference.)
|
||||||
|
{
|
||||||
|
my $html = '<div class="main-heading"><h3>Library tools</h3></div><ul class="list-unstyled">'
|
||||||
|
. '<li><a class="btn btn-primary" href="/add/">Add books (scan an ISBN)</a></li>'
|
||||||
|
. '<li><a href="/add/#find">Find a book</a></li></ul>';
|
||||||
|
$dbh->do(q{INSERT INTO additional_contents (category, code, location, branchcode, published_on, number)
|
||||||
|
VALUES ('html_customizations', 'library_tools', 'IntranetmainUserblock', NULL, CURDATE(), 1)});
|
||||||
|
my $id = $dbh->last_insert_id( undef, undef, 'additional_contents', 'id' );
|
||||||
|
$dbh->do( q{INSERT INTO additional_contents_localizations (additional_content_id, title, content, lang)
|
||||||
|
VALUES (?, 'Library tools', ?, 'default')}, undef, $id, $html );
|
||||||
|
}
|
||||||
|
|
||||||
# ---- Preferences ----------------------------------------------------------
|
# ---- Preferences ----------------------------------------------------------
|
||||||
my %prefs = (
|
my %prefs = (
|
||||||
autoBarcode => 'OFF', # barcodes are assigned by Quick Add (zero-padded, or none/scanned on request)
|
autoBarcode => 'OFF', # barcodes are assigned by Quick Add (zero-padded, or none/scanned on request)
|
||||||
|
|||||||
@@ -56,6 +56,20 @@ if [ ! -d "/etc/koha/sites/$INST" ]; then
|
|||||||
-e 's|^#\s*Include /etc/koha/apache-shared-intranet-plack.conf| Include /etc/koha/apache-shared-intranet-plack.conf|' "$SITE"
|
-e 's|^#\s*Include /etc/koha/apache-shared-intranet-plack.conf| Include /etc/koha/apache-shared-intranet-plack.conf|' "$SITE"
|
||||||
koha-plack --enable "$INST" >/dev/null 2>&1 || true # koha-create may already have enabled it
|
koha-plack --enable "$INST" >/dev/null 2>&1 || true # koha-create may already have enabled it
|
||||||
|
|
||||||
|
# One address: serve the Add books page (Quick Add) inside the staff site at /add/.
|
||||||
|
# (The staff and public catalogue sites cannot share one address: Koha gives both the
|
||||||
|
# same URL shapes. The staff site is the natural home for a home library.)
|
||||||
|
if [ -n "${KOHA_QUICKADD_URL:-}" ]; then
|
||||||
|
cat > /etc/koha/apache-quickadd.conf << APACHE
|
||||||
|
<IfModule mod_proxy_http.c>
|
||||||
|
RedirectMatch ^/add\$ /add/
|
||||||
|
ProxyPass /add/ ${KOHA_QUICKADD_URL%/}/
|
||||||
|
ProxyPassReverse /add/ ${KOHA_QUICKADD_URL%/}/
|
||||||
|
</IfModule>
|
||||||
|
APACHE
|
||||||
|
sed -i 's|^\(\s*\)Include /etc/koha/apache-shared-intranet.conf|&\n\1Include /etc/koha/apache-quickadd.conf|' "/etc/apache2/sites-available/$INST.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run Apache itself as the instance user instead of mpm_itk's per-vhost
|
# Run Apache itself as the instance user instead of mpm_itk's per-vhost
|
||||||
# uid switching (which needs extra capabilities and is pointless here:
|
# uid switching (which needs extra capabilities and is pointless here:
|
||||||
# Plack does the real work as that user already).
|
# Plack does the real work as that user already).
|
||||||
|
|||||||
@@ -687,6 +687,42 @@ def op_assign(payload):
|
|||||||
"last": fmt_barcode(first + len(items) - 1, s)}
|
"last": fmt_barcode(first + len(items) - 1, s)}
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------- search
|
||||||
|
def item_status(it):
|
||||||
|
if it.get("lost_status"):
|
||||||
|
return "Lost"
|
||||||
|
if it.get("withdrawn"):
|
||||||
|
return "Withdrawn"
|
||||||
|
if it.get("checked_out_date"):
|
||||||
|
return "Checked out"
|
||||||
|
if it.get("damaged_status"):
|
||||||
|
return "Damaged"
|
||||||
|
return "On shelf"
|
||||||
|
|
||||||
|
|
||||||
|
def op_search(term):
|
||||||
|
"""Find books by title, author or ISBN; each result lists its copies with shelf and status."""
|
||||||
|
term = re.sub(r"[%_\\]", "", term or "").strip()[:80]
|
||||||
|
if len(term) < 2:
|
||||||
|
return {"results": []}
|
||||||
|
like = {"-like": "%" + term + "%"}
|
||||||
|
q = {"-or": [{"title": like}, {"author": like}, {"isbn": like}, {"ean": like}]}
|
||||||
|
biblios = koha("GET", "/biblios", query={"q": json.dumps(q), "_per_page": "25", "_order_by": "title"}) or []
|
||||||
|
if not biblios:
|
||||||
|
return {"results": []}
|
||||||
|
ids = [b["biblio_id"] for b in biblios]
|
||||||
|
items = koha("GET", "/items", query={"q": json.dumps({"biblio_id": ids}), "_per_page": "500"}) or []
|
||||||
|
where = {l["value"]: l["label"] for l in get_locations()}
|
||||||
|
by_bib = {}
|
||||||
|
for it in items:
|
||||||
|
by_bib.setdefault(it["biblio_id"], []).append({
|
||||||
|
"barcode": it.get("external_id"), "callnumber": it.get("callnumber") or "",
|
||||||
|
"location": where.get(it.get("location"), ""), "status": item_status(it)})
|
||||||
|
return {"results": [{
|
||||||
|
"biblio_id": b["biblio_id"], "title": (b.get("title") or "").rstrip(" /:"), "author": b.get("author") or "",
|
||||||
|
"year": b.get("copyright_date") or "", "copies": by_bib.get(b["biblio_id"], [])} for b in biblios]}
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ photo scanning
|
# ------------------------------------------------------------ photo scanning
|
||||||
MAX_IMAGE = 12 * 1024 * 1024
|
MAX_IMAGE = 12 * 1024 * 1024
|
||||||
ZBAR_TIMEOUT = 20
|
ZBAR_TIMEOUT = 20
|
||||||
@@ -801,6 +837,13 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
"sources": [s.split(":", 1)[0] if s.startswith("z3950") else s for s in SOURCES]})
|
"sources": [s.split(":", 1)[0] if s.startswith("z3950") else s for s in SOURCES]})
|
||||||
if path == "/api/settings":
|
if path == "/api/settings":
|
||||||
return self._send(200, settings_view())
|
return self._send(200, settings_view())
|
||||||
|
if path == "/api/search":
|
||||||
|
qs = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
|
||||||
|
try:
|
||||||
|
return self._send(200, op_search((qs.get("q") or [""])[0]))
|
||||||
|
except KohaError as e:
|
||||||
|
return self._send(502, {"status": "error", "message": "Koha is not answering yet - try again in a minute."
|
||||||
|
if e.status == 503 else "Search failed: %s" % e.body[:120]})
|
||||||
self._send(404, {"error": "not found"})
|
self._send(404, {"error": "not found"})
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
|
|||||||
@@ -44,6 +44,20 @@
|
|||||||
padding: 10px 16px; background: var(--card); color: var(--ink); font-weight: 600; }
|
padding: 10px 16px; background: var(--card); color: var(--ink); font-weight: 600; }
|
||||||
button.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
|
button.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
|
||||||
button:focus-visible, a:focus-visible, input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
|
button:focus-visible, a:focus-visible, input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
|
||||||
|
nav.tabs { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; }
|
||||||
|
nav.tabs a { color: var(--accent); text-decoration: none; font-weight: 600; padding: 6px 12px; border-radius: 999px; }
|
||||||
|
nav.tabs a.on { background: var(--accent); color: var(--accent-ink); }
|
||||||
|
nav.tabs a.ext::after { content: ' \2197'; font-size: .85em; }
|
||||||
|
.find-form { display: flex; gap: 8px; margin: 10px 0; }
|
||||||
|
.find-form input { flex: 1; font-size: 1.3rem; padding: 12px 14px; border: 2px solid var(--line); border-radius: 12px;
|
||||||
|
background: var(--card); color: var(--ink); min-width: 0; }
|
||||||
|
.hit { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; margin: 10px 0; }
|
||||||
|
.hit .t { font-weight: 700; font-size: 1.05rem; overflow-wrap: anywhere; }
|
||||||
|
.hit a { color: var(--accent); }
|
||||||
|
.copy { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; padding: 6px 0 0; }
|
||||||
|
.chip { border-radius: 999px; padding: 1px 10px; font-size: .88rem; font-weight: 600; border: 1px solid var(--line); }
|
||||||
|
.chip.in { background: var(--ok-bg); border-color: var(--ok); color: var(--ok); }
|
||||||
|
.chip.out { background: var(--warn-bg); border-color: var(--warn); color: var(--warn); }
|
||||||
.cam-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0 0; }
|
.cam-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0 0; }
|
||||||
#status { min-height: 1.5em; margin: 10px 2px; color: var(--muted); }
|
#status { min-height: 1.5em; margin: 10px 2px; color: var(--muted); }
|
||||||
.card { background: var(--card); border: 1px solid var(--line); border-radius: 14px;
|
.card { background: var(--card); border: 1px solid var(--line); border-radius: 14px;
|
||||||
@@ -85,12 +99,26 @@
|
|||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<header>
|
<header>
|
||||||
<h1>Add to library</h1>
|
<h1 id="title">Add to library</h1>
|
||||||
<nav aria-label="Library links">
|
<nav class="tabs" aria-label="Sections">
|
||||||
<a id="lnk-opac" href="#">Catalogue</a><a id="lnk-staff" href="#">Staff</a>
|
<a id="tab-add" href="#add" class="on">Add books</a>
|
||||||
|
<a id="tab-find" href="#find">Find a book</a>
|
||||||
|
<a id="lnk-staff" class="ext" href="#" target="_blank" rel="noopener">Manage</a>
|
||||||
|
<a id="lnk-opac" class="ext" href="#" target="_blank" rel="noopener">Public catalogue</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<section id="view-find" hidden>
|
||||||
|
<form class="find-form" id="findform" autocomplete="off">
|
||||||
|
<label class="sr" for="q">Title, author or ISBN</label>
|
||||||
|
<input id="q" type="search" placeholder="Title, author or ISBN" enterkeyhint="search">
|
||||||
|
<button class="primary" type="submit">Find</button>
|
||||||
|
</form>
|
||||||
|
<div id="found" aria-live="polite"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="view-add">
|
||||||
|
|
||||||
<fieldset class="types" id="types" style="border:0;padding:0" aria-label="What are you adding?"></fieldset>
|
<fieldset class="types" id="types" style="border:0;padding:0" aria-label="What are you adding?"></fieldset>
|
||||||
<div class="shelf" id="shelfbox" hidden>
|
<div class="shelf" id="shelfbox" hidden>
|
||||||
<label for="shelf">Shelf / room</label>
|
<label for="shelf">Shelf / room</label>
|
||||||
@@ -149,6 +177,7 @@
|
|||||||
|
|
||||||
<h2>Earlier</h2>
|
<h2>Earlier</h2>
|
||||||
<ul class="list" id="earlier"></ul>
|
<ul class="list" id="earlier"></ul>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -182,7 +211,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function api(path, body) {
|
async function api(path, body) {
|
||||||
const r = await fetch(path, body ? { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
const r = await fetch(path.replace(/^\//, ''), body ? { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(body) } : {});
|
body: JSON.stringify(body) } : {});
|
||||||
return r.json();
|
return r.json();
|
||||||
}
|
}
|
||||||
@@ -300,7 +329,7 @@
|
|||||||
$('status').textContent = 'Reading the barcode…';
|
$('status').textContent = 'Reading the barcode…';
|
||||||
try {
|
try {
|
||||||
const blob = await shrink(f);
|
const blob = await shrink(f);
|
||||||
const r = await fetch('/api/scan?kind=' + kind, { method: 'POST', headers: { 'Content-Type': 'image/jpeg' }, body: blob });
|
const r = await fetch('api/scan?kind=' + kind, { method: 'POST', headers: { 'Content-Type': 'image/jpeg' }, body: blob });
|
||||||
resolve(r.ok ? (await r.json()).codes : []);
|
resolve(r.ok ? (await r.json()).codes : []);
|
||||||
} catch (e) { resolve([]); }
|
} catch (e) { resolve([]); }
|
||||||
};
|
};
|
||||||
@@ -351,7 +380,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let staffBase = '';
|
let staffBase = '';
|
||||||
const staffRoot = () => staffBase || ('http://' + location.hostname + ':' + staffPort);
|
const staffRoot = () => staffBase || (location.pathname.startsWith('/add/') ? location.origin : 'http://' + location.hostname + ':' + staffPort);
|
||||||
const printLink = (from, to) => staffRoot() +
|
const printLink = (from, to) => staffRoot() +
|
||||||
'/cgi-bin/koha/labels/label-print.pl?from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to);
|
'/cgi-bin/koha/labels/label-print.pl?from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to);
|
||||||
|
|
||||||
@@ -424,6 +453,40 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- under the staff site (/add/) everything is same-origin
|
||||||
|
const embedded = location.pathname.startsWith('/add/');
|
||||||
|
|
||||||
|
// ---- views: Add books / Find a book
|
||||||
|
function showView() {
|
||||||
|
const find = location.hash === '#find';
|
||||||
|
$('view-add').hidden = find; $('view-find').hidden = !find;
|
||||||
|
$('tab-add').classList.toggle('on', !find); $('tab-find').classList.toggle('on', find);
|
||||||
|
$('title').textContent = find ? 'Find a book' : 'Add to library';
|
||||||
|
document.title = find ? 'Find a book' : 'Add to library';
|
||||||
|
if (find) $('q').focus(); else $('code').focus();
|
||||||
|
}
|
||||||
|
window.addEventListener('hashchange', showView);
|
||||||
|
|
||||||
|
async function find(term) {
|
||||||
|
const box = $('found');
|
||||||
|
if (term.trim().length < 2) { box.replaceChildren(el('div', { className: 'small' }, 'Type at least two letters.')); return; }
|
||||||
|
box.replaceChildren(el('div', { className: 'small' }, 'Searching…'));
|
||||||
|
let r;
|
||||||
|
try { r = await api('/api/search?q=' + encodeURIComponent(term)); } catch (e) { r = { status: 'error' }; }
|
||||||
|
if (!r.results) { box.replaceChildren(el('div', { className: 'card warn' }, el('div', { className: 'body' }, r.message || 'Search failed.'))); return; }
|
||||||
|
if (!r.results.length) { box.replaceChildren(el('div', { className: 'small' }, 'No matches for "' + term + '".')); return; }
|
||||||
|
box.replaceChildren(...r.results.map(b => el('div', { className: 'hit' },
|
||||||
|
el('div', { className: 't' }, b.title), el('div', { className: 'meta' }, [b.author, b.year].filter(Boolean).join(' · ')),
|
||||||
|
...(b.copies.length ? b.copies.map(c => el('div', { className: 'copy' },
|
||||||
|
c.barcode ? el('span', { className: 'badge' }, c.barcode) : '',
|
||||||
|
el('span', {}, c.location || 'no shelf set'), c.callnumber ? el('span', { className: 'meta' }, c.callnumber) : '',
|
||||||
|
el('span', { className: 'chip ' + (c.status === 'On shelf' ? 'in' : 'out') }, c.status)))
|
||||||
|
: [el('div', { className: 'small' }, 'No copies recorded.')]),
|
||||||
|
el('div', { className: 'small', style: 'margin-top:6px' },
|
||||||
|
el('a', { href: staffRoot() + '/cgi-bin/koha/catalogue/detail.pl?biblionumber=' + b.biblio_id, target: '_blank', rel: 'noopener' }, 'Open full record')))));
|
||||||
|
}
|
||||||
|
$('findform').onsubmit = ev => { ev.preventDefault(); find($('q').value); };
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const s = await api('/api/state');
|
const s = await api('/api/state');
|
||||||
types = s.types; if (!types[type]) type = 'BOOK'; typeButtons();
|
types = s.types; if (!types[type]) type = 'BOOK'; typeButtons();
|
||||||
@@ -443,7 +506,7 @@
|
|||||||
}
|
}
|
||||||
api('/api/settings').then(applySettings);
|
api('/api/settings').then(applySettings);
|
||||||
$('earlier').replaceChildren(...s.recent.slice(0, 25).map(e => rowFor(e, false)));
|
$('earlier').replaceChildren(...s.recent.slice(0, 25).map(e => rowFor(e, false)));
|
||||||
$('code').focus();
|
showView();
|
||||||
})();
|
})();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -217,10 +217,10 @@ action_library_show_access() {
|
|||||||
ip="localhost"
|
ip="localhost"
|
||||||
echo "(Network access is limited to this kiosk.)"
|
echo "(Network access is limited to this kiosk.)"
|
||||||
fi
|
fi
|
||||||
echo " Catalogue (public search): http://${ip}:$(library_env_get OPAC_PORT)/"
|
echo " Your library (sign in here): http://${ip}:$(library_env_get STAFF_PORT)/"
|
||||||
echo " Staff interface: http://${ip}:$(library_env_get STAFF_PORT)/"
|
echo " Add books: http://${ip}:$(library_env_get STAFF_PORT)/add/"
|
||||||
echo " Add books (scan ISBNs): http://${ip}:$(library_env_get QUICKADD_PORT)/"
|
echo " Public catalogue (kiosk): http://${ip}:$(library_env_get OPAC_PORT)/"
|
||||||
echo " Login (staff and add books): ${user} / ${pass}"
|
echo " Login: ${user} / ${pass} (Add books asks once more with the same login)"
|
||||||
echo
|
echo
|
||||||
echo " Passwords live in $LIBRARY_DIR/.env (root only)."
|
echo " Passwords live in $LIBRARY_DIR/.env (root only)."
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -168,8 +168,9 @@ check ".env BIND_ADDR back to 127.0.0.1" test "$(library_env_get BIND_ADDR
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
echo "== access screen"
|
echo "== access screen"
|
||||||
printf '\n' | action_library_show_access > "$T/access.out" 2>&1
|
printf '\n' | action_library_show_access > "$T/access.out" 2>&1
|
||||||
check "shows the staff login" grep -q 'admin / s3cretPASS' "$T/access.out"
|
check "shows the login" grep -q 'admin / s3cretPASS' "$T/access.out"
|
||||||
check "shows quick-add address" grep -q 'Add books.*:8082' "$T/access.out"
|
check "shows the one library address" grep -q 'sign in here.*:8081/' "$T/access.out"
|
||||||
|
check "shows the add-books path on that address" grep -q ':8081/add/' "$T/access.out"
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
echo "== update"
|
echo "== update"
|
||||||
|
|||||||
Reference in New Issue
Block a user