Test plan (ktd)
0) Update the database (ktd, ignore if sandbox):
$ updatedatabase
1) Enable 'RealTimeHoldsQueue' and 'DisplayAddHoldGroups' system preferences
2) restart_all # This is required or the worker won't pick up the new sys pref values
3) Place at least 2 holds for a patron on different items/biblios, without grouping them:
<staff_url>/cgi-bin/koha/members/moremember.pl?borrowernumber=19
4) Build the holds queue once to get a baseline:
$ perl misc/cronjobs/holds/build_holds_queue.pl --force
5) Visit the holds queue and confirm the holds appear without a group indicator:
<staff_url>/cgi-bin/koha/circ/view_holdsqueue.pl?branchlimit=&itemtypeslimit=&run_report=1
6) Create a hold group for those holds via the patron page
<staff_url>/cgi-bin/koha/circ/circulation.pl?borrowernumber=<patron_borrowernumber>
7) Without running build_holds_queue.pl, refresh the holds queue, confirm the 'part of a hold group' indicator now appears
8) Ungroup the holds, refresh the holds queue, confirm the 'part of a hold group' is no longer showing
9) Run the unit tests:
$ prove t/db_dependent/Reserves/HoldGroup.t
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Test plan:
0) Update the database (ktd, ignore if sandbox):
$ updatedatabase
1) Enable DisplayAddHoldGroups sys prefs
2) Go to 'Henry' and click 'Search to hold':
<staff_url>/cgi-bin/koha/members/moremember.pl?borrowernumber=19
3) Search for 'test' and click the top 'Select all' link
4) Click the downward arrow in the 'Place hold' button and pick 'Place hold for Henry'
5) Set a pickup location for each hold and tick the 'Treat as hold group' checkbox
6) Click 'Place holds'.
7) Run build_holds_queue.pl:
perl misc/cronjobs/holds/build_holds_queue.pl --force
8) Visit the Holds queue page:
<staff_url>/cgi-bin/koha/circ/view_holdsqueue.pl?branchlimit=&itemtypeslimit=&run_report=1
9) Confirm the 'part of a hold group' bit shows and display the modal like 'Holds to pull'
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch removes an "onload" event attribute from the label batch edit
template. An "onload" event attribute on the body tag can be easily
moved to the $(document).ready block.
To test, apply the patch and go to Cataloging -> Label creator -> Label
batches -> New label batch. As soon as the page loads the "Add by
barcodes" textarea should get focus.
Sponsored-by: Athens County Public Libraries
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch removes an "onmousedown" event attribute from the include
file used by item types and authorized values administration in icon
selection. The event handling is copied to each page's JS block.
To test, apply the patch and go to Administraion -> Item types.
In the "Choose an icon" section, click the "Remote image" tab. Click
the text input field. The corresponding radio button should be
checked.
Perform the same check on Administration -> Authorized values.
Sponsored-by: Athens County Public Libraries
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
In the loop to check fields, we enter a conditional when the element is null - but then we try to adjust the element.
This patch removes the condition and prevents accessing NULL fields
To test:
1 - Go to Administration->MARC Bibliographic framework
2 - Under Default click Action->MARC Structure
3 - Search for 020
4 - Click Actions->Edit subfields
5 - Click on tab 'a'
6 - Check important, save
7 - Cataloging->New from Z39
8 - Search for ISBN: 9798225048921 (or any record you want)
9 - Ensure the result has more than one ISBN and click Actions->Import
10 - Delete one of the ISBN fields
11 - Click 'Save'
12 - Check the console (F12)
Uncaught TypeError: Cannot read properties of null (reading 'nodeName')
13 - Apply patch
14 - Repeat 8-10
15 - Click save (and clear any other fields that need edited)
16 - Confirm record can be saved
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Instead of translating ", by" we add more flexibility for languages that
need to invert author and title for instance.
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This fixes a translation issue in the purchase suggestions
table in the staff interface - a ", by" is added between the
title and the author in the suggestion column, the "by" is
not translated when the language for the staff interface
is a language other than English.
Test plan:
1. Add another language to Koha:
./misc/translator/translate install fr-CA
2. Enable the new language in StaffInterfaceLanguages:
2.1. Go to Administration > System preferences
2.2. Search for StaffInterfaceLanguages
2.3. Check the new language
2.4. Click Save all I18N/L10N preferences
3. Create a new purchase suggestion:
3.1. Go to More > Suggestions
3.2. Click New purchase suggestion
3.3. Fill out the title AND the author at least
3.4. Click Submit your suggestion
4. View the suggestion in the table
--> It says title, by author
5. Switch language
--> The ", by" is still in English
6. Apply patch.
7. Run these commands to update the translation:
7.1 ./misc/translator/translate update fr-CA
7.2 ./misc/translator/translate install fr-CA
8. Reload page.
9. Notice that ", by" is now translated.
--> in this instance, you will see ", par"
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
MySQL 8.4 requires foreign keys to reference PRIMARY KEY or UNIQUE keys.
Since authorised_values.authorised_value is only unique in combination
with category (composite UNIQUE key av_uniq), the FK constraint is not
compatible with MySQL 8.4+.
Data integrity for illrequests.status_alias is maintained at the application layer:
- Koha::ILL::Request->status_alias() validates values against
authorised_values (category='ILL_STATUS_ALIAS') before storing
- All ILL request operations in Koha use this object layer
- CASCADE behaviors (ON DELETE SET NULL, ON UPDATE CASCADE) are already
handled by the application layer when modifying/deleting authorised values.
Test Plan, using KTD:
1. Prepare KTD for MySQL 8.4
- In koha-testing-docker, edit 'compose/db-mysql.yml'
- Comment out the line 5:
`# command: --default-authentication-plugin=mysql_native_password`
Note: This is required because MySQL 8.4 doesn't support the native password plugin.
2. Start a new instance with MySQL 8.4
- `DB_IMAGE=mysql:8.4 ktd up`
- Observe that it fails to start with the errors such as 'DBD::mysql::st execute failed'
3. Apply the patch
- `git bz apply 39107`
4. Start the instance again
- `DB_IMAGE=mysql:8.4 ktd up`
- Observe that it starts successfully
5. Update the database (Optional, only for existing instances)
- `perl installer/data/mysql/updatedatabase.pl`
6. Access Koha Staff and OPAC interfaces and verify that everything works as expected
7. Sign off
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The 'ModBiblio on invalid record' subtest asserted the action log info
column contained 'biblionumber', but Koha::Biblio::_unblessed_for_log
restricts the logged hash to frameworkcode, opac_suppressed, and serial.
The biblionumber lives in action_logs.object, not in the info JSON.
Update the regex to match the new "biblio { ... }" prefix format set by
C4::Log::logaction so the assertion confirms biblio data is logged
without depending on a field that is intentionally excluded.
Test plan:
1. prove t/db_dependent/Biblio.t
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Replace the empty =head3 stub with a proper synopsis explaining the URL,
the supported filtering / ordering / embed surface, and the permission
requirement. Aligns the controller with the documentation density of
other Koha REST controllers and removes the "Missing POD" QA noise.
Test plan:
1. perl -MKoha::REST::V1::ActionLogs -e '' (still loads)
2. perl -c Koha/REST/V1/ActionLogs.pm (still parses)
3. perldoc Koha/REST/V1/ActionLogs.pm (renders the new POD)
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The catalog detail page links to the modification log with
modules=CATALOGUING&object_type=biblio&object=<biblionumber>. The old
server-rendered viewer matched both biblio-level entries (object =
biblionumber, info LIKE 'biblio%') and item-level entries (object IN
(itemnumbers of this biblio), info LIKE 'item%'). Converting to the
AJAX DataTable kept that expansion only in the CSV export branch, so
the on-screen view silently dropped item-level history.
Recreate the expansion in the AJAX path:
* tools/viewlog.pl: when (single CATALOGUING module + object_type
=biblio + non-empty object) collect the biblio's itemnumbers and
expose them, along with object_type, to the template.
* viewlog.tt: include object_type and biblio_itemnumbers in the
search_params JSON the inline script builds for the JS.
* viewlog.js: when those params are present, build the same -or
clause the old SQL produced and skip the regular per-object filter.
The info substring filter is also suppressed in that branch (the
old code did the same: it dropped %info% in favour of the -or).
Test plan:
1. Enable CataloguingLog.
2. Edit the biblio and a couple of its items.
3. From the biblio detail page, open "Modification log".
4. Verify the table shows both the biblio MODIFY rows and the item
MODIFY rows for that biblio (and only those).
5. From Tools -> Log viewer, plain Cataloguing filter still returns
the unfiltered log set as before.
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
CIRCULATION rows whose info payload is a JSON object can carry `forced`
and `confirmations` arrays listing the override codes a librarian
acknowledged when checking out, renewing or otherwise circulating an
item (e.g. DEBT, AGE_RESTRICTION, BIBLIO_ALREADY_ISSUED). The old
server-rendered template ran these through the translate_log_override
SWITCH block in action-logs.inc; in the new AJAX-driven viewer the JS
default branch was just escapeHtml()-ing the raw JSON, leaving the
codes unlocalised and unreadable.
Port the override -> human-readable label map into str/viewlog.inc as
override_labels, mirroring the module/action/interface label maps
already there. Add a renderCircInfo() helper that recognises the JSON
shape and emits the same div.forced / div.confirmed blocks the old
template did, so the existing CSS continues to apply.
renderInfo() now falls through CIRCULATION -> renderCircInfo, the
generic record-info renderer (CATALOGUING / AUTHORITIES from bug 42032)
and finally escapeHtml(), so non-JSON payloads (item barcodes,
ID-only info, ...) keep displaying as before.
Test plan:
1. Enable IssueLog.
2. Trigger an override checkout (e.g. patron with debt, item not for
loan) and confirm.
3. Tools -> Log viewer, filter by module=Circulation.
4. The override row's Info column should show one or more localised
"Patron had debt", "Item was not for loan", ... lines instead of
a raw JSON blob.
5. Plain ISSUE / RETURN rows (no overrides) keep showing the raw
itemnumber / barcode payload as before.
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
QA review pointed out three regressions and a label gap in the new
DataTable-driven log viewer:
1. The Librarian column showed a bare borrowernumber linked to the
patron page; the old server-side template showed the librarian's
full name with the borrowernumber in parentheses.
2. The Object column for MEMBERS / CIRCULATION / FINES / APIKEYS rows
showed only the borrowernumber; the old template showed the
patron's full name and used "API keys for <name> (<id>)" wording.
3. Several user-visible strings in renderObject() were hard-coded
English ("Bibliographic record ", "Subscription ", "Authority ",
"Basket ") and would never have been picked up by the translator.
4. The form's Actions list still includes RESTORE but the new
str/viewlog.inc action_labels map missed the corresponding entry,
so the rendered Action cell fell through to escapeHtml("RESTORE")
instead of the localised "Restore".
This patch:
* Asks the API to embed both `librarian` and `patron` and uses those
objects when rendering the Librarian and Object columns. When the
embed is absent (e.g. because the relation does not match for a
non-MEMBERS row) the renderers fall back to the bare id, preserving
backwards compatibility.
* Wraps the four hardcoded strings in __() with %s placeholders so
the translator extracts them. The Basket markup is also assembled
via __("Basket %s").format(...) rather than string concatenation
so that translators can re-order the link text.
* Adds RESTORE: _("Restore") to str/viewlog.inc.
Test plan:
1. Tools -> Log viewer with various modules selected.
2. Verify the Librarian column shows "Firstname Surname (id)" and
links to the moremember page.
3. Filter to MEMBERS / CIRCULATION / FINES; verify the Object column
shows the patron's full name, and the API keys row uses the
"API keys for <name> (<id>)" wording.
4. Filter to CATALOGUING / SERIAL / AUTHORITIES / ACQUISITIONS; verify
"Bibliographic record N", "Subscription N", "Authority N" and
"Basket N" still link correctly.
5. Restore a deleted biblio (CataloguingLog must be on); verify the
Action column shows "Restore" rather than "RESTORE".
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Build on the new ActionLog DBIC relations to expose them as opt-in
embeds, and shore up gaps in the test coverage that QA flagged.
Swagger
* paths/action_logs.yaml declares an x-koha-embed header parameter
constrained to the supported relations (librarian, patron).
* definitions/action_log.yaml adds the corresponding optional
`librarian` and `patron` object fields.
Tests
* Convert the single 24-test subtest into focused subtests:
- authorization (403 vs 200)
- simple typed filters
- q-DSL: multi-value module, info LIKE, combined filter
- embeds: librarian and patron, with and without x-koha-embed
- pagination + X-Total-Count and order_by
* Mock AuthSuccessLog/AuthFailureLog/BorrowersLog to 0 to remove the
flakiness where the API GET itself appended an AUTH log row between
delete and assertion.
* Build the test librarian with both `tools` and `borrowers` flags so
embedded patron fields are not redacted by Koha::Patron->is_accessible.
* Use a small build_fixtures() helper to avoid copy-pasting setup
across subtests.
Test plan:
1. yarn build && restart_all (refresh swagger bundle)
2. prove t/db_dependent/api/v1/action_logs.t (all subtests green)
3. curl -H 'x-koha-embed: librarian,patron' \
/api/v1/action_logs?module=MEMBERS -- patron and librarian
objects appear in the response.
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
QA review noted that the new /api/v1/action_logs endpoint exposed only
column data, so the log viewer ended up showing bare borrowernumbers in
the Librarian and Object columns where the old server-side rendering
showed firstname/surname (borrowernumber). Add the DBIC plumbing needed
to support x-koha-embed for those relationships.
Two belongs_to relationships are declared below the auto-generated
marker on Koha::Schema::Result::ActionLog:
* librarian -> borrowers.borrowernumber via action_logs.user
* patron -> borrowers.borrowernumber via action_logs.object
The patron relation is intentionally polymorphic; the action_logs.object
column holds different identifiers per module (biblionumber for
CATALOGUING, subscriptionid for SERIAL, ...) so callers must filter by
module before trusting the joined patron - this is documented in both
schema and Koha::ActionLog comments.
Koha::ActionLog gains explicit librarian() and patron() accessor methods
following the Koha::Hold pattern, returning Koha::Patron objects (or
undef when the relation does not match). The accessors deliberately
avoid `use Koha::Patrons` at compile time: Koha::ActionLog is loaded
very early via C4::Log and Koha::Patron is not yet defined at that
point - the accessors reach for it lazily at call time.
koha_object_class / koha_objects_class overrides are also added to bring
the result class in line with the rest of the codebase.
Test plan:
1. prove t/db_dependent/api/v1/action_logs.t (still green)
2. perl -MKoha::ActionLog -e 'print "ok"' (no circular import)
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The script viewlog.pl it's very slow because it retrieves all action logs at the same time.
It should use KohaTables with pagination/number max of returning entries like other Koha tables (API + AJAX wrapper).
Test Plan:
1) Apply this patch
2) Rebuild swagger file
3) Restart all the things!
4) Browse to /cgi-bin/koha/tools/viewlog.pl
5) Test functionality, functionality should be no different!
* Limit by librarian
* Limit by modules
* Limit by actions
* Object search string
* Info search string
* Limit by interface
* Limit by from date
* Limit by to date
* Limit by from and to date
* Test "Compare" checkboxes ( only two should be selectable at once )
* Test column sorting
* Test sharable link
* Test output to a file
* Test columns show/hide
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Authority MODIFY action log diffs were emitting bogus entries for every
field with a default ' ' indicator on every save, producing output like:
_marc.fields.1.040.ind1 ' ' ' '
_marc.fields.1.040.ind2 ' ' ' '
_marc.fields.2.150.ind1 ' ' ' '
_marc.fields.3.667.ind1 ' ' ' '
...
even when only a single subfield was edited.
Root cause: Struct::Diff treats a utf8-flagged ' ' as different from a
non-utf8 ' ' SV, even though Perl's eq returns true. ModAuthority was
sourcing the two sides of the diff from different parse paths:
- $oldrecord = GetAuthority($authid)
-> MARC::Record->new_from_xml(...) -> indicators arrive utf8-flagged
- $record (the "after" side) was the in-memory MARC built by the
editor from CGI parameters -> indicators are bare byte strings
The XML round-trip through AddAuthority normalises the stored copy, but
the in-memory $record we passed to logaction had not been re-parsed, so
every default indicator looked like a change to Struct::Diff.
Fix: after AddAuthority commits the new state, re-read it via
$new_authority->record and pass that to _authority_log_data. Both sides
of the diff now traverse the same new_from_xml path, utf8 flags match,
and Struct::Diff falls silent on unchanged indicators.
This is the symmetric counterpart of how biblios already work in
C4::Biblio: ModBiblio sources both $original_marc and $updated_marc_log
through Koha::Biblio::Metadata::record_strip_nonxml (which also calls
new_from_xml), so biblios don't hit this.
The deeper asymmetry between in-memory editor MARC and stored MARC
(default indicators only get normalised on the XML round-trip) is left
for a separate bug; this patch keeps the action log diff clean without
touching the storage path.
Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Edit any authority via the staff UI making a
single subfield change. Open Tools -> Log viewer for that authority
and verify the diff column lists only the changed subfield (plus the
005 timestamp), with no rows for ind1/ind2 of untouched fields.
3. Re-run the same edit a second time changing nothing else; the diff
should again be limited to 005 + the touched subfield.
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Mirrors bug 42188's biblio log-viewer rendering. Generalise the
biblio-only renderCatalogInfo() helper in viewlog.js to also match
the "authority <JSON>" prefix introduced by the previous follow-up,
and rename it to renderRecordInfo() to reflect its wider scope. The
existing fmtJsonValue() helper already handles MiJ's nested arrays
of subfield objects, so no rendering changes are needed beyond the
regex and name.
Test plan:
1. Enable AuthoritiesLog.
2. Create, edit, and delete an authority via the staff UI.
3. Go to Tools -> Log viewer, filter by module=Authorities.
4. Verify:
- ADD row's info cell shows just "authority".
- MODIFY and DELETE rows' info cells render as a nested list
(auth_header columns plus _marc leader + fields array).
- Diff column still renders the before/after struct-diff table.
5. Re-check a biblio (CataloguingLog) entry: still renders as before.
6. Re-check a MEMBERS entry: untouched.
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Mirrors bug 42188's biblio info-column convention. Generalise the
CATALOGUING-only info-column rewrite branch in C4::Log::logaction to
cover AUTHORITIES as well, using a module-aware prefix map so each
module gets its own prefix ("biblio" / "authority"). For AUTHORITIES
this means MODIFY now stores "authority {pre-change JSON}" and DELETE
stores "authority {final-state JSON}", while ADD stays bare
"authority" (the call site passes the string directly and ADD is
guarded anyway). The biblio behaviour is preserved bit-for-bit.
Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Create / modify / delete an authority via
the staff UI, then check action_logs:
- ADD row's info column reads just "authority"
- MODIFY row's info column reads "authority { ... pre-change JSON ... }"
- DELETE row's info column reads "authority { ... final-state JSON ... }"
3. prove t/db_dependent/Koha/Biblio.t
4. prove t/db_dependent/Log.t
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Mirrors the bug 42188 main commit for biblios. Replace the custom
_marc_record_to_diffable helper with MARC::Record::MiJ's to_mij_structure
so AUTHORITIES action log diffs use the same MARC-in-JSON shape as
CATALOGUING diffs. Add Koha::Authority::_unblessed_for_log to expose
only the auth_header columns that exist independently of the MARC
record (authtypecode, origincode, linkid), avoiding duplication of
MARC-derived data (heading, authtrees), raw marcxml, and system
timestamps. The explicit whitelist also removes the need for the
post-hoc empty-field stripping added in a previous follow-up.
Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Create, edit, and delete an authority via
the staff UI, then inspect action_logs.diff in the DB:
- _marc contains MiJ shape { leader: "...", fields: [ { tag: {...} }, ... ] }
- Changes to heading fields produce diffs on
_marc.fields[N].TAG.subfields[0].a
- The diff no longer carries a redundant top-level heading entry
(it's MARC-derived and now only appears in _marc).
3. prove t/db_dependent/Log.t
4. prove t/db_dependent/Koha/Authority.t
5. prove t/db_dependent/Koha/Biblio.t
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The AUTHORITIES action log entries for authority operations previously
stored minimal data: ADD and DELETE logged just the string "authority",
while MODIFY logged a raw MARC-formatted dump of the pre-change record
as a plain text string ("authority BEFORE=>..."). None of these populated
the diff column.
This patch updates AddAuthority, ModAuthority and DelAuthority in
C4/AuthoritiesMarc.pm to follow the same pattern introduced for biblios
in bug 40135: passing combined hashrefs to logaction so that the diff
column is automatically populated with Struct::Diff JSON.
The combined hashref contains the auth_header table columns (authtypecode,
heading, origincode, etc. — excluding the raw marcxml blob) plus a _marc
key built by the new _marc_record_to_diffable helper, which converts a
MARC::Record into a tag => array-of-formatted-strings structure readable
by the existing diff viewer.
A new _authority_log_data helper packages the authority unblessed data
and MARC record into the combined hashref, keeping the three call sites
concise.
For ADD, the logaction is now emitted after the authority record has been
fully stored (after _after_authority_action_hooks), so the diff captures
the complete saved state. For MODIFY, the before-state is captured from
Koha::Authorities before calling AddAuthority, and the after-state is
read back once AddAuthority has committed the new data. For DELETE, the
MARC is captured before the authority is removed.
The diff column for MODIFY will now show both authority table column
changes (e.g. heading) and MARC field-level changes (e.g. _marc.151.0)
in the existing diff viewer table in the log viewer.
Tests are added to verify that ADD, MODIFY, and DELETE all populate the
diff column and that _marc content is captured correctly.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The CATALOGUING info column for MODIFY/DELETE logs stores "biblio "
followed by a pretty-printed JSON dump of the biblio hashref, which
includes a MARC-in-JSON _marc structure. Raw, this is noisy and hard
for librarians to read in the log viewer.
Extract the nested-object formatter already used by the struct-diff
renderer into a shared fmtJsonValue() helper, and use it from a new
renderCatalogInfo() that runs over each div.loginfo on page load. If
the cell text matches "biblio {JSON}" it is replaced with the pretty
rendering; otherwise the cell is left untouched so non-catalog info
payloads (and the bare "biblio" logged on ADD) are unaffected.
Falling back silently on a parse failure means the old non-JSON
Dumper-format item payloads continue to display as-is.
Test plan:
1. Enable CataloguingLog.
2. Create, edit, and delete a biblio record via the staff UI.
3. Go to Tools -> Log viewer, filter by module=Cataloguing.
4. Verify:
- ADD row's info cell shows just "biblio".
- MODIFY and DELETE rows' info cells render as a nested list
(leader, fields array of tag/indicator/subfield objects, plus
frameworkcode/opac_suppressed/serial).
- Diff column still renders the before/after struct-diff table.
5. Check another module's log entries (e.g. MEMBERS) are untouched.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The info column for CATALOGUING action logs is, by convention, the
record state before modification -- MODIFY writes the pre-change JSON
payload, DELETE writes the state at time of deletion, but ADD has no
pre-change state. Writing the new record state into info on ADD
duplicates the state that the subsequent MODIFY (if any) captures as
its "before" snapshot, and conflicts with that convention.
Skip the JSON payload on ADD so info degrades to the bare "biblio"
prefix. The diff column already captures the full creation (every
field "added" against an empty hashref), so no information is lost.
Test plan:
1. prove t/db_dependent/Koha/Biblio.t
2. Observe that the "CataloguingLog MARC-in-JSON diff tests" subtest
now also asserts:
- ADD info == "biblio"
- MODIFY info matches /^biblio \{/
- DELETE info matches /^biblio \{/
3. Create / modify / delete a record via the staff UI with
CataloguingLog on, then check action_logs: the ADD row's info
column should read just "biblio".
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Replace Dumper with to_json for the biblio info column in logaction,
fixing UTF-8 encoding and extending coverage to ADD and DELETE actions
which previously logged only the literal string "biblio".
All three actions (ADD, MODIFY, DELETE) now log "biblio " followed by
a pretty-printed JSON representation of the relevant state: pre-change
for MODIFY, new state for ADD, and deleted state for DELETE.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Update the existing _unblessed_for_log() subtest to reflect the new
behaviour: MARC-derived columns (title, author, copyrightdate, etc.)
are now excluded, while frameworkcode is always included even when it
is an empty string (the Default framework).
Add a new subtest 'CataloguingLog MARC-in-JSON diff tests' covering:
- ADD: diff _marc key is MARC-in-JSON (has leader + fields array),
indicators and subfield values are correct, and MARC-derived
biblio columns are absent from the diff.
- MODIFY: diff _marc key present, MARC-derived columns absent.
- DELETE: diff _marc key present with correct MARC-in-JSON structure.
Use from_json() instead of decode_json() when reading diffs back from
the database, as the DBD layer returns decoded Perl Unicode strings
which decode_json (which expects UTF-8 bytes) would reject for any
non-ASCII content.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Replace the custom _marc_record_to_diffable helper with MARC::Record::MiJ's
to_mij_structure method, which produces standard MARC-in-JSON. This fixes
three problems with the old approach:
- Dollar signs used as subfield delimiters in the formatted string were
ambiguous when subfield content itself contained " $x " sequences.
- Grouping fields by tag into a hash lost inter-field ordering, making
lossless reconstruction of the MARC record impossible.
- Non-ASCII characters were subject to Perl string context encoding
issues; JSON serialisation of the MiJ structure handles UTF-8 cleanly.
_unblessed_for_log in Koha::Biblio is updated to return only the three
biblio columns that are genuinely independent of the MARC record
(frameworkcode, opac_suppressed, serial). The remaining columns
(author, title, subtitle, notes, etc.) are denormalised copies of MARC
data already captured in the _marc key, so including them produced
redundant and noisy diffs.
The fmt() helper in viewlog.js is updated to render JavaScript arrays
as ordered lists rather than objects with numeric keys, improving the
display of MARC subfield arrays in the diff table.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch removes the newline to <br/> (nl2br) handling in the .pl script,
and instead moves it after the Template::Toolkit "html" filter. In this way,
we get the security of the "html" filter while also converting new lines into
safe <br/> elements. We also fix a little style problem. Hurray!
Test plan:
0. Apply the patch and koha-plack --restart kohadev
1. Ensure the RoutingSerials system preference is enabled
2. Create a "Subscription" for a known biblionumber (e.g. 29) with whatever details
3. On the subscription-detail.pl page for that subscription, click on "Create routing list"
4. Add whatever recipients you want and put the following into Notes:
Testing out notes
1-26-26 more notes
2-5-26 = more notes, 3 copies
<p>Awesome... still safe</p>
5. Click "Save"
6. Observe that your notes are output exactly the same way you put them in. The newlines
appear to be newlines to the human eye, while that pesky <p></p> markup is escaped.
7. Observe that the notes are also neatly lined up with the rest of the routing list
display for an attractive user experience
8. Celebrate again. Hurray!
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch makes sure we inject the __sort form of local-number, rather
than the field directly - this ensurs searching works even if mappings mayu
be different and local-number itself is not numeric
To test:
1 - Edit mappings.yaml and set local-number from 'number' to 'string'
NOTE: There are two copies of local-number - one in auth and one in biblio - the latter is the important one here
2 - perl misc/search_tools/rebuild_elasticsearch.pl -r -v
3 - Confirm in staff interface Admin -> Search Engine Configuration that local-number is now a string
4 - Search and get an error
Error: Unable to perform your search. Please try again.
In the logs:
[illegal_argument_exception] Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [local-number] in order to load field data by uninverting the inverted index. Note that this can use significant memory.
5 - Apply patch, restart all
6 - Search again
7 - Success!
Signed-off-by: Bernard Scaife <bernard.scaife@openfifth.co.uk>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
I don't know if this is appropriate for master as it requires Koha objects,
but this is helpful for upgrading sites to avoid any problems from old mappings
for local-number
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This fixes the spelling of "marc" in two places:
- an error message from running the database audit
- the description for the marc_modification_templates permission
Test plan:
1. The easiest way to test this change is to check the diff for the
patch and note that "marc" is changed to "MARC" in two places.
2. Database audit output (More > About Koha > Database audit):
- Check the diff (otherwise would need to alter the database
structure somehow so that the audit would generate the error
message).
3. Check the description for the marc_modification_templates permission:
- Go to any patron record
- Select More > Set permissions
- In the filter, enter: marc modification
- Note that the description is:
Manage marc modification templates (marc_modification_templates)
4. Apply the patch.
5. Repeat step 3 and note that the description is now:
Manage MARC modification templates (marc_modification_templates)
Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
As per https://software.indexdata.com/zebra/doc/querymodel-rpn.html#querymodel-bib1-completeness:
"[...] Complete field (3) triggers search and scan in index type="p"."
The UNIMARC Zebra index definitions for authorities *do* include
a 'p' (short for 'phrase') type index for authtype -- however, the
MARC21 Zebra index definitions don't and this is causing these two
tests to fail:
t/db_dependent/Biblio.t
t/db_dependent/FrameworkPlugin.t
This patch fixes that.
Test plan for KTD:
1) Apply the patch, then:
k$ sudo cp etc/zebradb/marc_defs/marc21/authorities/authority-koha-indexdefs.xml /etc/koha/zebradb/marc_defs/marc21/authorities/
k$ sudo cp etc/zebradb/marc_defs/marc21/authorities/authority-zebra-indexdefs.xsl /etc/koha/zebradb/marc_defs/marc21/authorities/
k$ koha-rebuild-zebra --authorities --full --force --verbose kohadev
2) Run these tests -- they should both complete without warnings/errors:
k$ prove -v t/db_dependent/Biblio.t
k$ prove -v t/db_dependent/FrameworkPlugin.t
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Remove unused patron attribute values sent to the template
Send preferred name
Use SQL to do date comparison to see if an item is overdue
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
We use SQL here to get all the needed info - the query says it is slow, but isn't so bad.
Worse is fetching the patron for each borrower with overdues (and more than once if they have several)
It appears we started grabbing the patron so we could use patron title, but we can just pass the info already fetched.
I additionally fetch the patron category types and update patron-title to allow for hide_patron_infos_if_needed to work
without a patron object - we can just use the branchcode as ultimately that's all we need
To test:
1 - Add many items to your catalog (can use my handy repo randitems.pl)
https://github.com/kidclamp/handy-koha-script
2 - Add many issues (can use randissues.pl from above)
3 - I tested with about 1000
4 - Browse to Circulation->Overdues
5 - Note load time
6 - Apply patch
7 - Restart all
8 - Note load time is faster
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>