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>
Fixes regression in which default expiry date is ignored due to regression introduced by fix for bug 34483. If default expiry date is completed in patron import tool, the value is ignored and expiry date unaltered. This patch fixes this error.
Test plan:
1. User should exist and have expiry date in future (say 2099-12-31)
2. Save above as file
3. Tools > Import users
4. Choose file saved in 2
5. Field to use for record matching: cardnumber
6. In default values set expiration date to a date in past (say 2025-10-19) using date picker
7. Select overwrite the existing one with this
8. Leave renew existing patrons as "use the expiration date in the file if present"
9. Run
10. Observe user expiry date is still 2099-12-31
11. Apply patch / restart_all
12. Repeat 4 - 9 above
13. Observe user expiry date is now 2025-10-19
Signed-off-by: Eric Phetteplace <ephetteplace@cca.edu>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
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>
The "Cite" option in the OPAC is missing the subtitle (245) in MARC21.
To recreate:
1. Add a record or find a record with a subtitle (in KTD, biblionumber 58 has a 245 http://localhost:8081/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=58
2. In the OPAC, go to the record
http://localhost:8080/cgi-bin/koha/opac-detail.pl?biblionumber=58
3. Click "Cite"
--> The subtitle is not in any of the citations
4. Apply the patch
5. Repeat step 2 and 3
--> The subtitle is now shown
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Test plan:
Read the patch.
Search for both parameters to verify that they are not used.
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Previously, t/AuthoritiesMarc_MARC21.t would fail if
you tried to run it from within a UNIMARC instance.
This patch fixes that.
Test plan:
1) Inside a UNIMARC instance, run the test and
notice the subtest failure:
$ prove -v t/AuthoritiesMarc_MARC21.t
2) Apply this patch.
3) Re-run the test and notice that it now completes
without any failures.
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>
UNIMARC instances include the following two authority types
by default (among others): Corporate Body Name (code: 'CO')
and Collective Title (code: 'CO_UNI_TI').
The problem is that when searching for Corporate Body Name
authorities (in the OPAC or in the Staff interface), the
results will include Collective Title authorities as well.
It has to do with Corporate Body Name authorities using
an authtypecode (CO) that happens to be the prefix of the
authtypecode (CO_UNI_TI) used by Collective Titles.
NOTE: Elasticsearch is not affected by this.
The fix is to additionally pass the completeness attribute
to the RPN query sent to Zebra so that it will try to match
on what is literally found in the entire field's phrase
index (i.e. the value of UNIMARC 152$b), instead of performing
a partial match.
More information can be found at the official documentation:
https://software.indexdata.com/zebra/doc/querymodel-rpn.html#querymodel-bib1-completeness
Test plan (tailored for KTD):
0) Launch a UNIMARC Koha instance using Zebra:
ktd --marcflavour unimarc --search-engine zebra up
1) Import the Sample MARC record attached to this Bug,
containing a single Collective Title authority
(adding one manually would take a long time due to
the many mandatory subfields).
2) Perform a generic authority search (without entering
anything in the search box) for all 'Corporate Body
Name' authorities. Note the number of results returned
(83), and also notice that in page 4 of the results you
get a stray 'Collective Title' authority ("Selected
works", the one you imported in Step 1).
3) Apply this patch (and `restart_all`).
4) Repeat the same search as in Step 2. This time, the
number of results returned should be one less than
before (82), and you should only see 'Corporate Body
Name' authorities in the results.
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>
Bug 34438 added lang in opac registration/modification form.
But it is linked to primary contact method hidden or not.
Looks like this is not on purpose.
This patch removes this link.
Test plan:
1) Verify system preference
'PatronSelfModificationBorrowerUnwantedField' is empty
2) Go to OPAC user account > Personal details
3) You see 'Preferred language for notices'
4) In system preference 'PatronSelfModificationBorrowerUnwantedField'
check 'primary_contact_method'
5) Go to OPAC user account > Personal details
=> Without patch lang is also hidden
=> With patch lang is not hidden
Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
See comment 4 for a detail explanation of the problem.
Test plan:
hit /cgi-bin/koha/installer/install.pl
login with the DB credentials
upgrade
=> Without this patch you get
```
ISBN_RANGE_MESSAGE is set to [] but that file does not exist!
Trying to use the default locations
```
=> With this patch applied: "Everything went okay. Update done."
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch removes an ISBN_RANGE_MESSAGE env variable that is
accidentally set by Business::ISBN::Data.
Test plan:
- git checkout v25.11.03-2
- reset_all
- git checkout -b 42374 origin/main
- Apply the patch
- restart_all
- Go to staff interface (e.g. http://localhost:8081/)
- Log into web installer
- Click "Update your database"
- Note it works
- Repeat the process but instead of using the web installer
use updatedatabase.pl directly (say through koha-upgrade-schema)
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch splits delete_reports into delete_own_reports and delete_all_reports
While delete_all_reports supercedes delete_own_reports it seems to make more sense than
delete_own_reports and delete_others_reports.
With delete_all_reports we can have a user who can delete all, without specific permission to delete their own
The other way we would have delete others, and you could not delete your own, which seems more odd
To test:
1 - Have 2 users - 1 with delete_reports, 1 without
2 - Apply patches, update database
3 - Confirm user with permission now has 'delete_all_reports' and other user still has no reports deletion permission
4 - Give the second user 'delete_own_reports' permission
5 - Create 4 reports as each user
6 - Confirm view/edit pages show the delete button for each user, and confirm they both work
7 - You shoudl have 2 reports left for each user
8 - Confirm each user can delete their own report from the saved SQL list:
http://localhost:8081/cgi-bin/koha/reports/guided_reports.pl?op=list
9 - There should be 1 report from each
10 - Give the second user 'delete_all_reports' permission in one tab and reload the saved reports in another tab
11 - Select both reports in the second tab
12 - Remove 'delete_all_reports' from the second user, leaving 'delete_own_reports' in the first tab
13 - Click 'Delete selected' in the reports tab
14 - Confirm you get 'You do not have permission to continue'
15 - Hit back, deselect other users report, confirm you can delete your own with 'Delete selected'
16 - Sign off
Signed-off-by: Judy Poyer <jpoyer@aacpl.net>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Now that we can explicitly list reports with any one of the permissions we need
to adjust some displays to ensure we can get to the list view.
Additionally I change 'Use saved' to 'View saved' since we may not be able to run.
I also fix the action buttons to show 'View' instead of 'Run' if user cannot execute.
I adjust reports home to check if the user can use report plugins before showing that link as well
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Test plan:
0. Apply the patch and restart_all
1. Using a user with only "create_reports", "execute_reports", or "delete_reports",
try to perform operations not covered by that permission.
That is, if you have "create_reports", try to run, export, and delete reports.
If you have "execute_reports", try to create, modify, and delete reports
If you have "delete_reports", try to create, modify, and run reports
2. Note that a user with each of these permissions can list and view SQL.
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This bug refactors the preview sql buttons to pass the permissions granted for each report to
the javascript that clones and updates the modal.
That allows us to check for the appropriate permissions when preparing the buttons for the modal
and to remove any that are not allowed.
This allows for different permissions per report for future devs
(see bugs 42406, 16631)
To test:
1. Create some reports
2. Create a user with only execute_reports permissions
3. Login as that user and go to Reports > use saved
4. In the actions column click the ^ and select 'Preview sql'
5. Notice delete button renders and works to delete
6. Apply patch and restart_all
7. Repeat step 4, now only the Run report button renders and works
8. Add permissions for create_reports
9. Repeat step for, now Edit, Duplicate, Schedule and run buttons render and work
10. Add permissions for delete_reports
11. Repeat step 4, confirm the delete button renders and works
12. Confirm the preview sql button works to preview different reports
We also fix some error in the script, several wrong things there:
* delete was not replaced with cud-delete
* missing op for the creation of new reports
* update and run requires two permssions
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Scott Barter <sbarter@aacpl.net>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch makes it so the values copied come from the merge results
which fixes the case of copying empty values or - accurately.
To test:
1. Have a patron with some empty fields and some fields with '-'
2. Follow the original test plan and try to copy those fields
3. confirm empty fields and '-' now copy as expected
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
When merging two patrons this patch adds a new checkbox to
'Copy values from the record to be deleted'
To test:
- Apply patches and restart_all
1. From select two patrons and click 'Merge selected patrons'
2. Select one of the records
3. Check the new box to 'Copy values from the record to be deleted'
4. A diff of the two patrons is shown with checkboxes for each field
5. Confirm checking and un-checking the boxes updates the diff as expected
6. Confirm changing the patron selection updates the diff as expected.
7. Confirm un-checking and re-checking the Copy values from the record to be deleted
resets the check boxes for the fields to copy
8. Select some fields to copy values and click merge patrons
9. Confirm the values were copied as expected
Sponsored-by: CLAMS
Signed-off-by: Lari Taskula <lari.taskula@hypernova.fi>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The cash register transaction history page (pos/register.pl) allows
access with either cashup OR anonymous_refund permission, but the
API endpoints for fetching cashups only checked for cashup permission.
This caused a 403 error when users with only anonymous_refund permission
tried to view the transaction history page, as the cashups table failed
to load.
This patch updates the API permissions for:
- GET /cash_registers/{id}/cashups
- GET /cashups/{id}
to accept either cashup or anonymous_refund permission, matching the
page access logic.
Test plan:
1. Create a staff user with only cash_management > anonymous_refund
permission (not cashup)
2. Navigate to Point of Sale > Transaction history for any cash register
3. Verify the cashups table loads successfully (previously returned 403)
4. Run: prove t/db_dependent/api/v1/cashups.t
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Rename /libraries/{id}/holidays to /libraries/{id}/closed_dates
for consistency with existing sub-resource naming (desks,
cash_registers).
Efficiency: instead of calling $calendar->is_holiday() per day
(which re-checks all three data sources each time), access the
pre-loaded data structures directly: weekly_closed_days array,
day_month_closed_days hash, and _holidays hash. The logic properly
handles exceptions (dates marked open despite a weekly/annual
closure).
Additional fixes:
- Add 365-day max range guard
- Add missing 401/403 responses in swagger spec
- Add request_id_header parameter
- Test exception handling (open on normally-closed day)
- Remove fragile 'empty defaults' assertion
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
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>
- Add list_holidays controller to Koha::REST::V1::Libraries
- Returns closed days for a library within a configurable date range
- Uses Koha::Calendar to determine holidays
- Defaults to today + 3 months if no range specified
- Add OpenAPI spec for the new endpoint
- Register route in swagger.yaml
- Add unit tests in t/db_dependent/api/v1/libraries.t
To test:
1. Apply patch
2. prove t/db_dependent/api/v1/libraries.t
3. Verify all tests pass, including the new list_holidays subtest
which covers:
- Holidays returned within a specified date range
- Empty list when no holidays exist in range
- 400 response when 'to' date precedes 'from' date
- 404 response for non-existent library
- Default range (no params) returns successfully
Sponsored-by: Büchereizentrale Schleswig-Holstein <https://www.bz-sh.de/>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
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>
When a duplicate record is suspected during cataloguing, query the
AdditionalContents record_display category for the
StaffDuplicateCheckPage location. If configured, process the content
as a Template Toolkit template with the duplicate biblio object and
display the rendered result in the duplicate warning.
The TT context variable is 'biblio' (a Koha::Biblio object), so
administrators can use expressions like:
[% biblio.author %] ([% biblio.copyrightdate %])
Content is scrubbed using the record_display scrubber for XSS safety.
Signed-off-by: Jeanne Mauriello <jeanne.mauriello@mainlib.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Add a new 'StaffDuplicateCheckPage' location to the record_display
category in the Additional Contents admin UI (Tools > Additional
Contents). This allows administrators to configure custom bibliographic
information to display when a duplicate record is suspected during
cataloguing.
This uses the existing AdditionalContents infrastructure (bug 39860)
rather than a system preference, providing per-branch and multilingual
support.
Signed-off-by: Jeanne Mauriello <jeanne.mauriello@mainlib.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch updates the diff for ADD/CREATE and DELETE logactions to
exclude undefined fields from the diff.
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Update suggestion action logging to populate the diff column using the
correct before/after semantics:
- CREATE: empty before-state ({}), new suggestion data as after-state
- MODIFY: pre-change unblessed data as before, post-store object as after
- DELETE: log before the SQL DELETE so the object can still be fetched;
pass the suggestion as both $infos (info column) and $original (diff
before-state), logaction sets after-state to {}
Also add tests verifying the diff column is populated for all three actions.
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 atomicupdate converts AllowItemsOnHoldCheckoutSCO and
AllowItemsOnHoldCheckoutSIP from YesNo to Choice (0|1|2), where
value 1 now means "Allow pending holds only" and value 2 means
"Allow pending and waiting holds".
For AllowItemsOnHoldCheckoutSCO, pre-upgrade value 1 meant "allow
all on-hold checkouts (pending AND waiting)" at SCO. Post-upgrade,
sco-main.pl reads value 1 as "pending only", silently downgrading
sites that previously allowed waiting-hold checkouts. Remap SCO
value 1 -> 2 at upgrade time to preserve prior behavior.
For AllowItemsOnHoldCheckoutSIP, the SIP checkout code was already
gated on >= 1 for pending and >= 2 for waiting, so value 1 meant
"pending only" in practice even under the old YesNo type. No value
migration is needed -- leaving it at 1 preserves current behavior.
The value remap lives inside the branch that only runs when the
type is not already Choice, so re-running the atomicupdate is safe
and will not double-remap.
Test plan:
1) On a pre-upgrade DB, set both prefs to YesNo/1:
UPDATE systempreferences SET type='YesNo', options=NULL,
value='1'
WHERE variable IN ('AllowItemsOnHoldCheckoutSCO',
'AllowItemsOnHoldCheckoutSIP');
2) Run installer/data/mysql/updatedatabase.pl.
3) Verify:
SELECT variable, type, options, value FROM systempreferences
WHERE variable IN ('AllowItemsOnHoldCheckoutSCO',
'AllowItemsOnHoldCheckoutSIP');
SCO -> type=Choice, options=0|1|2, value=2.
SIP -> type=Choice, options=0|1|2, value=1.
4) Re-run updatedatabase.pl; confirm both prefs report
"already migrated, skipping" and values are unchanged.
5) Spot-check other starting values: SCO=0 stays 0, SCO=2
stays 2, SIP=0 stays 0.
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Introduce a new AllowHoldCheckoutOverride system preference (default 1
to preserve existing behaviour) that lets libraries prevent staff from
overriding held-item blockers at the circulation desk.
When AllowHoldCheckoutOverride is set to 0, CanBookBeIssued still
returns the hold-related keys (RESERVED, RESERVE_WAITING, TRANSFERRED
and PROCESSING) in the confirmation hash, but circ/circulation.pl
promotes them to the impossible hash before rendering the template.
This means no override button appears, and checkout of the held item
is refused outright with a clear message explaining which patron the
hold is for.
Matching entries have been added to the IMPOSSIBLE block of the
circulation template so the blocked-checkout message is meaningful.
This sits alongside the existing Allow*Override family
(AllowTooManyOverride, AllowNotForLoanOverride, AllowHoldPolicyOverride
and AllowRenewalOnHoldOverride) and uses the same staff-only semantics.
Test plan:
1. Set AllowHoldCheckoutOverride=1 (default). Place a waiting hold
for patron A on an item. At /cgi-bin/koha/circ/circulation.pl
attempt to check the item out to patron B: a confirmation dialog
appears with "Check out anyway" override buttons.
2. Set AllowHoldCheckoutOverride=0. Repeat: checkout is refused with
a message stating the item has been waiting for patron A. No
override button is offered.
3. Repeat with pending holds (reserves.found IS NULL), items in
transit and items being processed: each is refused when the
preference is 0 and offered as an overridable confirmation when
the preference is 1.
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
The OPAC REST self-checkout path (guarded by OpacTrustedCheckout) is
functionally SCO-over-API, so it should honour the same hold policy
as the SCO web interface. Previously _check_availability unconditionally
promoted RESERVED, RESERVE_WAITING, TRANSFERRED and PROCESSING from
the confirmation hash to the blockers hash for any public caller. This
meant the OPAC REST endpoint always refused held items regardless of
the librarian's AllowItemsOnHoldCheckoutSCO configuration.
With this patch the promotion is gated on AllowItemsOnHoldCheckoutSCO:
* 0 - Promote all four hold confirmations to blockers (previous
behaviour for pending holds)
* 1 - Promote only waiting/transit/processing; silently allow
checkout of items with a pending hold
* 2 - Promote none; silently allow checkout of items with any hold
Remaining hold confirmations are stripped from the response so the
caller does not have to resolve a confirmation token for a policy the
admin has already granted.
Test plan:
1. prove t/db_dependent/api/v1/checkouts.t
2. Enable OpacTrustedCheckout. POST an item with a pending hold for
another patron to /api/v1/public/patrons/X/checkouts:
- AllowItemsOnHoldCheckoutSCO=0: 403 Checkout not authorized
- AllowItemsOnHoldCheckoutSCO=1: 201 Created
3. Place a waiting hold for another patron on an item. Repeat:
- AllowItemsOnHoldCheckoutSCO=1: 403
- AllowItemsOnHoldCheckoutSCO=2: 201
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This patch extends the SIP checkout handler to honour the new
three-valued AllowItemsOnHoldCheckoutSIP preference:
* 0 - Don't allow checkout of any held item
* 1 - Allow checkout when the hold is still pending (not attached)
* 2 - Also allow checkout when the hold is waiting, in transit or
being processed for another patron
Previously the preference was a simple YesNo toggle that, when
enabled, allowed the checkout regardless of whether the hold was
pending or already attached to an item on the hold shelf. Libraries
could not block the more disruptive case (checking out a waiting
hold destined for another patron) without also blocking pending
holds.
The existing post-confirmation filter is reworked into a single
branch per confirmation type that consults the preference value.
Test plan:
1. prove t/db_dependent/SIP/Transaction.t
2. With AllowItemsOnHoldCheckoutSIP=0, place a pending hold for
patron A on an item, attempt SIP checkout to patron B: refused.
3. Repeat with the hold marked waiting: refused.
4. Set AllowItemsOnHoldCheckoutSIP=1, pending hold: allowed;
waiting hold: refused.
5. Set AllowItemsOnHoldCheckoutSIP=2, waiting hold: allowed.
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
AllowItemsOnHoldCheckoutSCO was previously passed through to
CanBookBeIssued as the ignore_reserves flag, which skipped the entire
holds branch in C4::Circulation and silently allowed checkout of
items already waiting on the hold shelf (reserves.found = 'W') or in
transit/processing for another patron's hold.
With the pref now a 3-value Choice:
0 = don't allow checkout of any held items
1 = allow checkout of items with a pending hold only
2 = allow checkout of items with any hold state
sco-main.pl now always calls CanBookBeIssued with ignore_reserves=0
and filters the needsconfirmation keys according to the pref value,
mirroring the pattern SIP already uses. RESERVE_WAITING, TRANSFERRED
and PROCESSING are added to the error-matching list and to the SCO
template so patrons see a specific "on the hold shelf" / "in transit"
/ "being processed" message instead of falling through to the generic
"please see a member of the library staff" line.
Test plan:
- Enable WebBasedSelfCheck; log in to /cgi-bin/koha/sco/sco-main.pl.
- Create an item with a pending hold for another patron
(reserves.found IS NULL).
- With AllowItemsOnHoldCheckoutSCO = 0, scan the barcode: checkout is
refused with "This item is on hold for another patron".
- Switch to 1 ("Allow pending holds only"): checkout succeeds.
- Set the hold to waiting (reserves.found = 'W') and try again with
the pref still at 1: checkout is refused with "This item is waiting
on the hold shelf for another patron".
- Switch to 2 ("Allow pending and waiting holds"): checkout succeeds.
- Repeat with found = 'T' and 'P' at pref = 1 -> refused with the
appropriate transit/processing message; at pref = 2 -> allowed.
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP become
Choice (0|1|2) so libraries can distinguish pending from waiting holds:
0 = Don't allow (unchanged)
1 = Allow pending holds only (new semantics; previously "Allow")
2 = Allow pending and waiting holds (preserves old permissive behaviour)
A new AllowHoldCheckoutOverride YesNo preference (default 1) follows
the existing Allow*Override family and lets libraries block the
"check out anyway" override in the staff interface when an item is on
hold for another patron.
Note: installations that previously had either Allow*CheckoutSCO/SIP
set to 1 will migrate to "Allow pending only". Libraries that relied
on the previous behaviour (silently allowing checkout of items already
waiting on the hold shelf) need to manually switch to 2.
Test plan:
- Apply the patch and restart Koha.
- Run the web installer / updatedatabase.pl.
- Confirm no errors and the log mentions the three sysprefs.
- Run it a second time; confirm the "already migrated" / "already
exists" skip messages appear.
- In Administration > System preferences, search for
AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP and
confirm the dropdown now has three options.
- Confirm AllowHoldCheckoutOverride appears in the same section with
the Allow/Don't allow choices and defaults to Allow.
Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>