Skip to content

Release 0.9.0-rc.1 - #514

Open
fabiodalez-dev wants to merge 625 commits into
mainfrom
release/0.9.0-rc.1
Open

fabiodalez-dev wants to merge 625 commits into
mainfrom
release/0.9.0-rc.1

Conversation

@fabiodalez-dev

Copy link
Copy Markdown
Owner

Release candidate 0.9.0-rc.1: the 0.9.0 stack (#502 → #504 → #505 → #506 → #507 → #508 → #509 → #510 → #511 → #512 → #513) plus the version bump, the CHANGELOG entry and the README lead.

The prerelease tag goes on the head of this PR once every required check is green; the PR is merged with a merge commit afterwards, so the tagged commit stays in main. #503 (CI runners, TinyMCE 8.10.0) carries the same three CI commits as #502 and is part of this tree.

Upgrade path

  • The migration ships as migrate_0.9.0-rc.1.sql: a prerelease sorts before its final, so migrate_0.9.0.sql would be skipped on the upgrade to the RC; this name runs on the RC and again on the later 0.9.0 upgrade, where it is a no-op.
  • tests/migration-0.9.0-rc.1.unit.php runs every section test; scripts/verify-schema.sh now also runs the test of a migrate_X.Y.Z-rc.N.sql at the stable X.Y.Z, and fails when it is missing.
  • mobile-api 1.9.0 requires 0.9.0-rc.1, so it can be activated on the RC.

Settings of an upgraded installation
The migration only inserts missing rows. Stocktake, suppliers, the visitor log, RDA overrides and fines start switched off; the other modules are on but change nothing until configured (the seeded Standard patron type inherits every global setting, the closures calendar is empty, no fine is charged, nobody is blocked, no new e-mail is sent). Historic prestiti.sanzione amounts become ledger debts only after fines and loss penalties are switched on.

Checks run on this head
Local Code Quality mirror on a fresh 0.9.0 database: PHPStan level 5 on the full tree, composer and npm audits, locale and route parity, frontend lint, migration and requires_app guards, schema gate with version.json at 0.9.0-rc.1, 325 of 328 standalone unit tests. The three that do not pass locally fail identically on the unchanged code from a worktree (two maintenance tests hit the main checkout's server; one suite needs to create databases as root) and run in CI.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 654 files, which is 554 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 9a9e0ea and 93c2585.

Files ignored due to path filters: 137
  • package-lock.json is excluded by !**/package-lock.json
  • public/assets/tinymce/README.md is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/bower.json is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/composer.json is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/icons/default/icons.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/icons/default/icons.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/license.md is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/models/dom/model.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/models/dom/model.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/notices.txt is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/package.json is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/accordion/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/accordion/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/advlist/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/advlist/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/anchor/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/anchor/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autolink/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autolink/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autoresize/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autoresize/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autosave/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/autosave/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/charmap/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/charmap/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/code/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/code/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/codesample/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/codesample/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/directionality/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/directionality/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/emoticons/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/emoticons/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/fullscreen/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/fullscreen/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/help/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/help/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/image/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/image/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/importcss/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/importcss/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/insertdatetime/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/insertdatetime/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/link/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/link/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/lists/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/lists/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/media/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/media/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/nonbreaking/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/nonbreaking/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/pagebreak/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/pagebreak/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/preview/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/preview/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/quickbars/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/quickbars/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/save/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/save/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/searchreplace/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/searchreplace/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/table/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/table/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/visualblocks/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/visualblocks/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/visualchars/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/visualchars/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/plugins/wordcount/plugin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/plugins/wordcount/plugin.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/skins/content/dark/content.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/content/dark/content.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/content/dark/content.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.inline.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.inline.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.inline.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.inline.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.inline.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/content.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/skin.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/skin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/skin.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/skin.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide-dark/skin.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.inline.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.inline.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.inline.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.inline.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.inline.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/content.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/skin.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/skin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/skin.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/skin.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/oxide/skin.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.inline.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.inline.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.inline.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.inline.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/content.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/skin.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/skin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/skin.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/skin.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5-dark/skin.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.inline.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.inline.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.inline.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.inline.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.inline.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/content.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/skin.css is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/skin.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/skin.min.css is excluded by !**/*.min.css, !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/skin.min.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/skins/ui/tinymce-5/skin.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/themes/silver/theme.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/themes/silver/theme.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
  • public/assets/tinymce/tinymce.d.ts is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/tinymce.js is excluded by !public/assets/tinymce/**
  • public/assets/tinymce/tinymce.min.js is excluded by !**/*.min.js, !public/assets/tinymce/**
Files selected for processing: 654
  • .github/workflows/ci-browser-security.yml
  • .github/workflows/ci-database-compatibility.yml
  • .github/workflows/ci-deep-regression.yml
  • .github/workflows/ci-e2e.yml
  • .github/workflows/ci-quality.yml
  • .github/workflows/ci-real-upgrade.yml
  • .github/workflows/ci-security-supply-chain.yml
  • .github/workflows/ci-upgrade-smoke.yml
  • .github/workflows/codeql.yml
  • .github/workflows/pullfrog.yml
  • .github/workflows/release.yml
  • .github/workflows/test-migrations.yml
  • .gitignore
  • .gitleaksignore
  • .rsync-filter
  • CHANGELOG.md
  • README.md
  • app/Controllers/Admin/LibraryCalendarController.php
  • app/Controllers/Admin/StatsController.php
  • app/Controllers/AttendanceController.php
  • app/Controllers/AutoriApiController.php
  • app/Controllers/CatalogueCardController.php
  • app/Controllers/CirculationSettingsController.php
  • app/Controllers/CollaneController.php
  • app/Controllers/CopyController.php
  • app/Controllers/CsvImportController.php
  • app/Controllers/DeweyApiController.php
  • app/Controllers/EditoriApiController.php
  • app/Controllers/FrontendController.php
  • app/Controllers/GeneriApiController.php
  • app/Controllers/ImportHistoryController.php
  • app/Controllers/InventoryController.php
  • app/Controllers/LibraryThingImportController.php
  • app/Controllers/LibriApiController.php
  • app/Controllers/LibriController.php
  • app/Controllers/LoanApprovalController.php
  • app/Controllers/MarcExportController.php
  • app/Controllers/MarcImportController.php
  • app/Controllers/PatronCardsController.php
  • app/Controllers/PatronLedgerController.php
  • app/Controllers/PrestitiApiController.php
  • app/Controllers/PrestitiController.php
  • app/Controllers/PublicApiController.php
  • app/Controllers/RegistrationController.php
  • app/Controllers/ReservationManager.php
  • app/Controllers/ReservationsAdminController.php
  • app/Controllers/ReservationsController.php
  • app/Controllers/SearchController.php
  • app/Controllers/SettingsController.php
  • app/Controllers/SuppliersController.php
  • app/Controllers/UserActionsController.php
  • app/Controllers/UserDashboardController.php
  • app/Controllers/UserImportController.php
  • app/Controllers/UsersController.php
  • app/Controllers/UtentiApiController.php
  • app/Middleware/FeatureGateMiddleware.php
  • app/Models/AttendanceRepository.php
  • app/Models/AuthorRepository.php
  • app/Models/BookRepository.php
  • app/Models/CopyRepository.php
  • app/Models/LoanRepository.php
  • app/Models/PublisherRepository.php
  • app/Models/SettingsRepository.php
  • app/Models/SupplierRepository.php
  • app/Routes/web.php
  • app/Services/BulkEnrichmentService.php
  • app/Services/CapacityService.php
  • app/Services/Circulation/LoanPolicy.php
  • app/Services/Circulation/PolicyResolver.php
  • app/Services/Circulation/UserPolicy.php
  • app/Services/CirculationPolicy.php
  • app/Services/FineCalculator.php
  • app/Services/FineParameters.php
  • app/Services/LoanRequestGate.php
  • app/Services/PatronLedger.php
  • app/Services/ReservationReassignmentService.php
  • app/Services/StocktakeService.php
  • app/Services/UnifiedCatalogService.php
  • app/Support/ActivityLog.php
  • app/Support/AuthorNormalizer.php
  • app/Support/CatalogueCard.php
  • app/Support/CataloguingExtras.php
  • app/Support/ConfigStore.php
  • app/Support/ContentSecurityPolicy.php
  • app/Support/ContributorSync.php
  • app/Support/DataIntegrity.php
  • app/Support/ExactSearch.php
  • app/Support/Features.php
  • app/Support/Holidays/ChineseLunarCalendar.php
  • app/Support/Holidays/CustomHolidays.php
  • app/Support/Holidays/HolidayCalculator.php
  • app/Support/Holidays/HolidayDataset.php
  • app/Support/Holidays/HolidayPresets.php
  • app/Support/Holidays/HolidayRules.php
  • app/Support/Holidays/WorldCalendars.php
  • app/Support/Holidays/data/AD.json
  • app/Support/Holidays/data/AE.json
  • app/Support/Holidays/data/AF.json
  • app/Support/Holidays/data/AG.json
  • app/Support/Holidays/data/AI.json
  • app/Support/Holidays/data/AL.json
  • app/Support/Holidays/data/AM.json
  • app/Support/Holidays/data/AO.json
  • app/Support/Holidays/data/AQ.json
  • app/Support/Holidays/data/AR.json
  • app/Support/Holidays/data/AS.json
  • app/Support/Holidays/data/AT.json
  • app/Support/Holidays/data/AU.json
  • app/Support/Holidays/data/AW.json
  • app/Support/Holidays/data/AX.json
  • app/Support/Holidays/data/AZ.json
  • app/Support/Holidays/data/BA.json
  • app/Support/Holidays/data/BB.json
  • app/Support/Holidays/data/BD.json
  • app/Support/Holidays/data/BE.json
  • app/Support/Holidays/data/BF.json
  • app/Support/Holidays/data/BG.json
  • app/Support/Holidays/data/BH.json
  • app/Support/Holidays/data/BI.json
  • app/Support/Holidays/data/BJ.json
  • app/Support/Holidays/data/BL.json
  • app/Support/Holidays/data/BM.json
  • app/Support/Holidays/data/BN.json
  • app/Support/Holidays/data/BO.json
  • app/Support/Holidays/data/BQ.json
  • app/Support/Holidays/data/BR.json
  • app/Support/Holidays/data/BS.json
  • app/Support/Holidays/data/BT.json
  • app/Support/Holidays/data/BV.json
  • app/Support/Holidays/data/BW.json
  • app/Support/Holidays/data/BY.json
  • app/Support/Holidays/data/BZ.json
  • app/Support/Holidays/data/CA.json
  • app/Support/Holidays/data/CC.json
  • app/Support/Holidays/data/CD.json
  • app/Support/Holidays/data/CF.json
  • app/Support/Holidays/data/CG.json
  • app/Support/Holidays/data/CH.json
  • app/Support/Holidays/data/CI.json
  • app/Support/Holidays/data/CK.json
  • app/Support/Holidays/data/CL.json
  • app/Support/Holidays/data/CM.json
  • app/Support/Holidays/data/CN.json
  • app/Support/Holidays/data/CO.json
  • app/Support/Holidays/data/CR.json
  • app/Support/Holidays/data/CU.json
  • app/Support/Holidays/data/CV.json
  • app/Support/Holidays/data/CW.json
  • app/Support/Holidays/data/CX.json
  • app/Support/Holidays/data/CY.json
  • app/Support/Holidays/data/CZ.json
  • app/Support/Holidays/data/DE.json
  • app/Support/Holidays/data/DJ.json
  • app/Support/Holidays/data/DK.json
  • app/Support/Holidays/data/DM.json
  • app/Support/Holidays/data/DO.json
  • app/Support/Holidays/data/DZ.json
  • app/Support/Holidays/data/EC.json
  • app/Support/Holidays/data/EE.json
  • app/Support/Holidays/data/EG.json
  • app/Support/Holidays/data/EH.json
  • app/Support/Holidays/data/ER.json
  • app/Support/Holidays/data/ES.json
  • app/Support/Holidays/data/ET.json
  • app/Support/Holidays/data/FI.json
  • app/Support/Holidays/data/FJ.json
  • app/Support/Holidays/data/FK.json
  • app/Support/Holidays/data/FM.json
  • app/Support/Holidays/data/FO.json
  • app/Support/Holidays/data/FR.json
  • app/Support/Holidays/data/GA.json
  • app/Support/Holidays/data/GB.json
  • app/Support/Holidays/data/GD.json
  • app/Support/Holidays/data/GE.json
  • app/Support/Holidays/data/GF.json
  • app/Support/Holidays/data/GG.json
  • app/Support/Holidays/data/GH.json
  • app/Support/Holidays/data/GI.json
  • app/Support/Holidays/data/GL.json
  • app/Support/Holidays/data/GM.json
  • app/Support/Holidays/data/GN.json
  • app/Support/Holidays/data/GP.json
  • app/Support/Holidays/data/GQ.json
  • app/Support/Holidays/data/GR.json
  • app/Support/Holidays/data/GS.json
  • app/Support/Holidays/data/GT.json
  • app/Support/Holidays/data/GU.json
  • app/Support/Holidays/data/GW.json
  • app/Support/Holidays/data/GY.json
  • app/Support/Holidays/data/HK.json
  • app/Support/Holidays/data/HM.json
  • app/Support/Holidays/data/HN.json
  • app/Support/Holidays/data/HR.json
  • app/Support/Holidays/data/HT.json
  • app/Support/Holidays/data/HU.json
  • app/Support/Holidays/data/ID.json
  • app/Support/Holidays/data/IE.json
  • app/Support/Holidays/data/IL.json
  • app/Support/Holidays/data/IM.json
  • app/Support/Holidays/data/IN.json
  • app/Support/Holidays/data/IO.json
  • app/Support/Holidays/data/IQ.json
  • app/Support/Holidays/data/IR.json
  • app/Support/Holidays/data/IS.json
  • app/Support/Holidays/data/IT.json
  • app/Support/Holidays/data/JE.json
  • app/Support/Holidays/data/JM.json
  • app/Support/Holidays/data/JO.json
  • app/Support/Holidays/data/JP.json
  • app/Support/Holidays/data/KE.json
  • app/Support/Holidays/data/KG.json
  • app/Support/Holidays/data/KH.json
  • app/Support/Holidays/data/KI.json
  • app/Support/Holidays/data/KM.json
  • app/Support/Holidays/data/KN.json
  • app/Support/Holidays/data/KP.json
  • app/Support/Holidays/data/KR.json
  • app/Support/Holidays/data/KW.json
  • app/Support/Holidays/data/KY.json
  • app/Support/Holidays/data/KZ.json
  • app/Support/Holidays/data/LA.json
  • app/Support/Holidays/data/LB.json
  • app/Support/Holidays/data/LC.json
  • app/Support/Holidays/data/LI.json
  • app/Support/Holidays/data/LICENSE-python-holidays
  • app/Support/Holidays/data/LK.json
  • app/Support/Holidays/data/LR.json
  • app/Support/Holidays/data/LS.json
  • app/Support/Holidays/data/LT.json
  • app/Support/Holidays/data/LU.json
  • app/Support/Holidays/data/LV.json
  • app/Support/Holidays/data/LY.json
  • app/Support/Holidays/data/MA.json
  • app/Support/Holidays/data/MC.json
  • app/Support/Holidays/data/MD.json
  • app/Support/Holidays/data/ME.json
  • app/Support/Holidays/data/MF.json
  • app/Support/Holidays/data/MG.json
  • app/Support/Holidays/data/MH.json
  • app/Support/Holidays/data/MK.json
  • app/Support/Holidays/data/ML.json
  • app/Support/Holidays/data/MM.json
  • app/Support/Holidays/data/MN.json
  • app/Support/Holidays/data/MO.json
  • app/Support/Holidays/data/MP.json
  • app/Support/Holidays/data/MQ.json
  • app/Support/Holidays/data/MR.json
  • app/Support/Holidays/data/MS.json
  • app/Support/Holidays/data/MT.json
  • app/Support/Holidays/data/MU.json
  • app/Support/Holidays/data/MV.json
  • app/Support/Holidays/data/MW.json
  • app/Support/Holidays/data/MX.json
  • app/Support/Holidays/data/MY.json
  • app/Support/Holidays/data/MZ.json
  • app/Support/Holidays/data/NA.json
  • app/Support/Holidays/data/NC.json
  • app/Support/Holidays/data/NE.json
  • app/Support/Holidays/data/NF.json
  • app/Support/Holidays/data/NG.json
  • app/Support/Holidays/data/NI.json
  • app/Support/Holidays/data/NL.json
  • app/Support/Holidays/data/NO.json
  • app/Support/Holidays/data/NP.json
  • app/Support/Holidays/data/NR.json
  • app/Support/Holidays/data/NU.json
  • app/Support/Holidays/data/NZ.json
  • app/Support/Holidays/data/OM.json
  • app/Support/Holidays/data/PA.json
  • app/Support/Holidays/data/PE.json
  • app/Support/Holidays/data/PF.json
  • app/Support/Holidays/data/PG.json
  • app/Support/Holidays/data/PH.json
  • app/Support/Holidays/data/PK.json
  • app/Support/Holidays/data/PL.json
  • app/Support/Holidays/data/PM.json
  • app/Support/Holidays/data/PN.json
  • app/Support/Holidays/data/PR.json
  • app/Support/Holidays/data/PS.json
  • app/Support/Holidays/data/PT.json
  • app/Support/Holidays/data/PW.json
  • app/Support/Holidays/data/PY.json
  • app/Support/Holidays/data/QA.json
  • app/Support/Holidays/data/RE.json
  • app/Support/Holidays/data/RO.json
  • app/Support/Holidays/data/RS.json
  • app/Support/Holidays/data/RU.json
  • app/Support/Holidays/data/RW.json
  • app/Support/Holidays/data/SA.json
  • app/Support/Holidays/data/SB.json
  • app/Support/Holidays/data/SC.json
  • app/Support/Holidays/data/SD.json
  • app/Support/Holidays/data/SE.json
  • app/Support/Holidays/data/SG.json
  • app/Support/Holidays/data/SH.json
  • app/Support/Holidays/data/SI.json
  • app/Support/Holidays/data/SJ.json
  • app/Support/Holidays/data/SK.json
  • app/Support/Holidays/data/SL.json
  • app/Support/Holidays/data/SM.json
  • app/Support/Holidays/data/SN.json
  • app/Support/Holidays/data/SO.json
  • app/Support/Holidays/data/SR.json
  • app/Support/Holidays/data/SS.json
  • app/Support/Holidays/data/ST.json
  • app/Support/Holidays/data/SV.json
  • app/Support/Holidays/data/SX.json
  • app/Support/Holidays/data/SY.json
  • app/Support/Holidays/data/SZ.json
  • app/Support/Holidays/data/TC.json
  • app/Support/Holidays/data/TD.json
  • app/Support/Holidays/data/TF.json
  • app/Support/Holidays/data/TG.json
  • app/Support/Holidays/data/TH.json
  • app/Support/Holidays/data/TJ.json
  • app/Support/Holidays/data/TK.json
  • app/Support/Holidays/data/TL.json
  • app/Support/Holidays/data/TM.json
  • app/Support/Holidays/data/TN.json
  • app/Support/Holidays/data/TO.json
  • app/Support/Holidays/data/TR.json
  • app/Support/Holidays/data/TT.json
  • app/Support/Holidays/data/TV.json
  • app/Support/Holidays/data/TW.json
  • app/Support/Holidays/data/TZ.json
  • app/Support/Holidays/data/UA.json
  • app/Support/Holidays/data/UG.json
  • app/Support/Holidays/data/UM.json
  • app/Support/Holidays/data/US.json
  • app/Support/Holidays/data/UY.json
  • app/Support/Holidays/data/UZ.json
  • app/Support/Holidays/data/VA.json
  • app/Support/Holidays/data/VC.json
  • app/Support/Holidays/data/VE.json
  • app/Support/Holidays/data/VG.json
  • app/Support/Holidays/data/VI.json
  • app/Support/Holidays/data/VN.json
  • app/Support/Holidays/data/VU.json
  • app/Support/Holidays/data/WF.json
  • app/Support/Holidays/data/WS.json
  • app/Support/Holidays/data/XK.json
  • app/Support/Holidays/data/YE.json
  • app/Support/Holidays/data/YT.json
  • app/Support/Holidays/data/ZA.json
  • app/Support/Holidays/data/ZM.json
  • app/Support/Holidays/data/ZW.json
  • app/Support/HttpClient.php
  • app/Support/ImportLogger.php
  • app/Support/LibraryCalendar.php
  • app/Support/LoanEligibility.php
  • app/Support/LoanPdfGenerator.php
  • app/Support/LoanWindow.php
  • app/Support/MaintenanceService.php
  • app/Support/Marc/BookMarcBuilder.php
  • app/Support/Marc/ContributorSorter.php
  • app/Support/Marc/Danmarc2LibriMapper.php
  • app/Support/Marc/Danmarc2Parser.php
  • app/Support/Marc/GeneratorStream.php
  • app/Support/Marc/Iso2709Reader.php
  • app/Support/Marc/Iso2709Writer.php
  • app/Support/Marc/Marc21LibriMapper.php
  • app/Support/Marc/Marc8Decoder.php
  • app/Support/Marc/MarcBookData.php
  • app/Support/Marc/MarcBookImporter.php
  • app/Support/Marc/MarcCoverFetcher.php
  • app/Support/Marc/MarcField.php
  • app/Support/Marc/MarcFormat.php
  • app/Support/Marc/MarcLanguage.php
  • app/Support/Marc/MarcLibriMapper.php
  • app/Support/Marc/MarcMediaType.php
  • app/Support/Marc/MarcParseException.php
  • app/Support/Marc/MarcReadError.php
  • app/Support/Marc/MarcRecord.php
  • app/Support/Marc/MarcXmlReader.php
  • app/Support/Marc/MarcXmlWriter.php
  • app/Support/Marc/RelatorRoles.php
  • app/Support/Marc/UnimarcLibriMapper.php
  • app/Support/Marc/UnimarcLibriParser.php
  • app/Support/NotificationService.php
  • app/Support/PatronCardPdf.php
  • app/Support/PatronCardSettings.php
  • app/Support/PatronPhoto.php
  • app/Support/PatronTypeAssignment.php
  • app/Support/PatronTypeImport.php
  • app/Support/PaymentReceiptPdf.php
  • app/Support/Permissions.php
  • app/Support/RdaTypes.php
  • app/Support/RolePresets.php
  • app/Support/SearchIndexBuilder.php
  • app/Support/SettingsMailTemplates.php
  • app/Support/StatisticsSections.php
  • app/Support/StocktakePdfGenerator.php
  • app/Support/Updater.php
  • app/Support/UserImport.php
  • app/Support/mail_templates/da_DK.php
  • app/Support/mail_templates/de_DE.php
  • app/Support/mail_templates/en_US.php
  • app/Support/mail_templates/fr_FR.php
  • app/Views/admin/calendar.php
  • app/Views/admin/csv_import.php
  • app/Views/admin/imports_history.php
  • app/Views/admin/marc_import.php
  • app/Views/admin/plugins.php
  • app/Views/admin/stats.php
  • app/Views/attendance/index.php
  • app/Views/autori/index.php
  • app/Views/dashboard/permissions.php
  • app/Views/editori/index.php
  • app/Views/fornitori/form.php
  • app/Views/fornitori/index.php
  • app/Views/frontend/book-detail.php
  • app/Views/frontend/catalog.php
  • app/Views/frontend/home-sections/hero.php
  • app/Views/frontend/layout.php
  • app/Views/frontend/partials/filters-sidebar.php
  • app/Views/inventario/index.php
  • app/Views/inventario/show.php
  • app/Views/layout.php
  • app/Views/libri/index.php
  • app/Views/libri/partials/book_form.php
  • app/Views/libri/scheda_libro.php
  • app/Views/partials/role-navigation.php
  • app/Views/partials/settings-sections.php
  • app/Views/partials/workspace-header.php
  • app/Views/prenotazioni/crea_prenotazione.php
  • app/Views/prenotazioni/index.php
  • app/Views/prenotazioni/modifica_prenotazione.php
  • app/Views/prestiti/crea_prestito.php
  • app/Views/prestiti/dettagli_prestito.php
  • app/Views/prestiti/index.php
  • app/Views/prestiti/restituito_prestito.php
  • app/Views/settings/cards-tab.php
  • app/Views/settings/circulation-tab.php
  • app/Views/settings/circulation-type-edit.php
  • app/Views/settings/index.php
  • app/Views/settings/loans-tab.php
  • app/Views/settings/modules-tab.php
  • app/Views/settings/partials/circulation-type-fields.php
  • app/Views/settings/partials/feature-switch.php
  • app/Views/user_dashboard/prenotazioni.php
  • app/Views/utenti/_movimenti.php
  • app/Views/utenti/crea_utente.php
  • app/Views/utenti/dettagli_utente.php
  • app/Views/utenti/import.php
  • app/Views/utenti/index.php
  • app/Views/utenti/modifica_utente.php
  • app/Views/utenti/tipologia-lettore.php
  • cron/automatic-notifications.php
  • cron/full-maintenance.php
  • docs/reviews/pr-512-settings-flexibility-2026-10-29.md
  • installer/database/data_da_DK.sql
  • installer/database/data_de_DE.sql
  • installer/database/data_en_US.sql
  • installer/database/data_fr_FR.sql
  • installer/database/data_it_IT.sql
  • installer/database/migrations/migrate_0.9.0-rc.1.sql
  • installer/database/schema.sql
  • locale/da_DK.json
  • locale/de_DE.json
  • locale/en_US.json
  • locale/fr_FR.json
  • locale/it_IT.json
  • package.json
  • public/assets/catalog-pages.css
  • public/assets/main.css
  • public/assets/pinakes-2026.css
  • public/index.php
  • scripts/ci-audit-playwright-results.js
  • scripts/copy-vendor-assets.js
  • scripts/generate-holiday-dataset.py
  • scripts/verify-schema.sh
  • storage/plugins/archives/ArchivesPlugin.php
  • storage/plugins/archives/views/index.php
  • storage/plugins/archives/views/public/index.php
  • storage/plugins/archives/views/search.php
  • storage/plugins/bibframe-linked-data/BibframeLinkedDataPlugin.php
  • storage/plugins/book-club/src/Repo.php
  • storage/plugins/emeroteca/EmerotecaPlugin.php
  • storage/plugins/emeroteca/src/Controllers/PublicController.php
  • storage/plugins/emeroteca/src/Services/ContributionService.php
  • storage/plugins/emeroteca/src/Services/PeriodicalPublisherService.php
  • storage/plugins/mobile-api/MobileApiPlugin.php
  • storage/plugins/mobile-api/plugin.json
  • storage/plugins/mobile-api/src/Controllers/ActionsController.php
  • storage/plugins/mobile-api/src/Controllers/CatalogController.php
  • storage/plugins/mobile-api/src/Controllers/CollectionsController.php
  • storage/plugins/mobile-api/src/Controllers/HealthController.php
  • storage/plugins/mobile-api/src/Controllers/OpenApiController.php
  • storage/plugins/mobile-api/src/Controllers/StaffArchivesController.php
  • storage/plugins/mobile-api/src/Controllers/StaffDesiderataController.php
  • storage/plugins/mobile-api/src/Controllers/StaffEntitiesController.php
  • storage/plugins/mobile-api/src/Controllers/StaffReservationsController.php
  • storage/plugins/mobile-api/src/Support/CirculationOutcome.php
  • storage/plugins/mobile-api/src/Support/StaffCirculationOpenApi.php
  • storage/plugins/mobile-api/src/Support/StaffLoanReader.php
  • storage/plugins/ncip-server/NcipServerPlugin.php
  • storage/plugins/oai-pmh-server/OaiPmhServerPlugin.php
  • storage/plugins/z39-server/Z39ServerPlugin.php
  • storage/plugins/z39-server/classes/CopyCataloging.php
  • storage/plugins/z39-server/classes/Danmarc2Parser.php
  • storage/plugins/z39-server/classes/LocalSruClient.php
  • storage/plugins/z39-server/classes/RelatorRoles.php
  • storage/plugins/z39-server/classes/SruClient.php
  • storage/plugins/z39-server/classes/UNIMARCXMLFormatter.php
  • storage/plugins/z39-server/classes/UnimarcLibriParser.php
  • storage/plugins/z39-server/views/copy-cataloging.php
  • storage/plugins/z39-server/views/reicat/book-fields.php
  • tests/archives-plugin.unit.php
  • tests/attendance-090.spec.js
  • tests/calendar-permission.unit.php
  • tests/catalogue-cards-090.spec.js
  • tests/cataloguing-extras-090.unit.php
  • tests/circulation-card.spec.js
  • tests/circulation-consultation.spec.js
  • tests/circulation-hardening-0780.unit.php
  • tests/circulation-interop.spec.js
  • tests/circulation-patron-types.spec.js
  • tests/circulation-policy-equivalence.unit.php
  • tests/circulation-policy-resolver.unit.php
  • tests/circulation-queue-material-rules.unit.php
  • tests/circulation-reservations.spec.js
  • tests/circulation-rules-matrix.spec.js
  • tests/circulation-settings-access.spec.js
  • tests/closures-calendar-admin.spec.js
  • tests/closures-calendar-circulation.spec.js
  • tests/closures-calendar-loans.spec.js
  • tests/contributor-plugin-compatibility-237.unit.php
  • tests/copy-cataloging-limits.unit.php
  • tests/custom-holidays.unit.php
  • tests/debt-block.spec.js
  • tests/emeroteca-412.unit.php
  • tests/exact-quoted-search-501.unit.php
  • tests/exact-search-apis-501.unit.php
  • tests/exact-search-archives-501.unit.php
  • tests/exact-search-articles-501.unit.php
  • tests/exact-search-backend-501.unit.php
  • tests/exact-search-core-501.unit.php
  • tests/exact-search-dedup-501.unit.php
  • tests/exact-search-fulltext-501.unit.php
  • tests/exact-search-mobile-collections-501.unit.php
  • tests/feature-toggle-catalogue-cards.spec.js
  • tests/feature-toggle-circulation-rules.spec.js
  • tests/feature-toggle-closures-calendar.spec.js
  • tests/feature-toggle-fines.spec.js
  • tests/feature-toggle-marc.spec.js
  • tests/feature-toggle-patron-cards.spec.js
  • tests/feature-toggle-rda-types.spec.js
  • tests/feature-toggle-stocktake.spec.js
  • tests/feature-toggle-suppliers.spec.js
  • tests/feature-toggle-user-import.spec.js
  • tests/feature-toggle-visitor-log.spec.js
  • tests/feature-toggles-followups.spec.js
  • tests/feature-toggles-registry.unit.php
  • tests/feature-toggles-routes.unit.php
  • tests/feature-toggles-services.unit.php
  • tests/feature-toggles-settings.spec.js
  • tests/fine-calculator.unit.php
  • tests/fines-closed-days.spec.js
  • tests/fines-ledger.spec.js
  • tests/fines-patron-types.spec.js
  • tests/fixtures/marc/malformed.mrc
  • tests/fixtures/marc/marc21-collection.xml
  • tests/fixtures/marc/marc21-marc8.mrc
  • tests/fixtures/marc/marc21-utf8.mrc
  • tests/fixtures/marc/oai-golden/minimal.marcxml.xml
  • tests/fixtures/marc/oai-golden/minimal.unimarc.mrc
  • tests/fixtures/marc/oai-golden/minimal.unimarc.xml
  • tests/fixtures/marc/oai-golden/monograph.marcxml.xml
  • tests/fixtures/marc/oai-golden/monograph.unimarc.mrc
  • tests/fixtures/marc/oai-golden/monograph.unimarc.xml
  • tests/fixtures/marc/oai-golden/serial.marcxml.xml
  • tests/fixtures/marc/oai-golden/serial.unimarc.mrc
  • tests/fixtures/marc/oai-golden/serial.unimarc.xml
  • tests/fixtures/marc/unimarc-utf8.mrc
  • tests/fixtures/upgrades/README.md
  • tests/fixtures/upgrades/schema-0.8.9.sql
  • tests/helpers/circulation-fixtures.js
  • tests/helpers/exact-search-501-seed.php
  • tests/helpers/exact-search-501.js
  • tests/helpers/exact-search-501.php
  • tests/helpers/feature-toggles.js
  • tests/helpers/marc-e2e.js
  • tests/helpers/marc-fixtures.php
  • tests/helpers/marc-inspect.php
  • tests/helpers/minor-features-090.js
  • tests/helpers/server-files.js
  • tests/holiday-calculator.unit.php
  • tests/holiday-presets-apply.unit.php
  • tests/holiday-presets-missing.unit.php
  • tests/holidays-preset.spec.js
  • tests/holidays-world.spec.js
  • tests/inventory-stocktake.spec.js
  • tests/issue-237-regression.unit.php
  • tests/issue-501-admin-lists.spec.js
  • tests/issue-501-exact-quoted-search.spec.js
  • tests/issue-501-name-pages.spec.js
  • tests/issue-501-quoted-hint.spec.js
  • tests/issue-501-quoted-new-author.spec.js
  • tests/issue-501-search-fields.spec.js
  • tests/issue-501-search-hint.spec.js
  • tests/issues-301-306.unit.php
  • tests/ledger-permission.unit.php
  • tests/library-calendar-loans.unit.php
  • tests/library-calendar.unit.php
  • tests/loan-auto-approval-301-reservation-path.unit.php
  • tests/loan-coherence-audit.unit.php
  • tests/loan-renewal-window.unit.php
  • tests/loan-window-cap.unit.php
  • tests/loan-window-surfaces.unit.php
  • tests/marc-bulk-export.spec.js
  • tests/marc-bulk-import.spec.js
  • tests/marc-copy-cataloging.spec.js
  • tests/marc-import-robustness.unit.php
  • tests/marc-library.unit.php
  • tests/marc-oai-byte-identity.unit.php
  • tests/migration-0.8.6-rc.1-accounts.unit.php
  • tests/migration-0.9.0-circulation-policy.unit.php
  • tests/migration-0.9.0-closures-calendar.unit.php
  • tests/migration-0.9.0-custom-holidays.unit.php
  • tests/migration-0.9.0-feature-toggles.unit.php
  • tests/migration-0.9.0-fines-ledger.unit.php
  • tests/migration-0.9.0-holiday-presets.unit.php
  • tests/migration-0.9.0-marc.unit.php
  • tests/migration-0.9.0-minor-features.unit.php
  • tests/migration-0.9.0-patron-import.unit.php
  • tests/migration-0.9.0-rc.1.unit.php
  • tests/migration-0.9.0-stocktake.unit.php
  • tests/minor-features-090.unit.php
  • tests/mobile-api-circulation.spec.js
  • tests/mobile-api-circulation.unit.php
  • tests/mobile-api-idempotency.spec.js
  • tests/ncip-circulation-coherence.unit.php
  • tests/patron-cards-import.unit.php
  • tests/patron-cards.spec.js
  • tests/patron-ledger.unit.php
  • tests/patron-photo.spec.js
  • tests/plugin-audit-hardening-regressions.unit.php
  • tests/rda-types-090.spec.js
  • tests/release-0.8.7-changes.unit.php
  • tests/sru-search.unit.php
  • tests/stack-090-integration.spec.js
  • tests/stack-090-review-fixes.spec.js
  • tests/stack-090-review-fixes.unit.php
  • tests/stack-090-settings-flexibility.unit.php
  • tests/stack-090-zero-config.spec.js
  • tests/statistics-090.spec.js
  • tests/stocktake-classification.unit.php
  • tests/stocktake-pdf.unit.php
  • tests/stocktake-permission.unit.php
  • tests/stocktake-snapshot.unit.php
  • tests/suppliers-090.spec.js
  • tests/unimarc-standards.unit.php
  • tests/user-import.spec.js
  • version.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues. There is one small regression in the contributor cache normaliser; see the inline comment.

Reviewed changes

The stacked feature PRs (#502 → #513) were already reviewed on their own PRs. This review covers only what landed after the last reviewed #512 head (029b19a7): 42 files, up to and including the release commit.

  • Release metadata: version.json is now 0.9.0-rc.1. The CHANGELOG heading is the exact ## [0.9.0-rc.1] that create-release.sh needs, and README has ## What's New in v0.9.0-rc.1. Every bundled requires_app is ≤ 0.9.0-rc.1; mobile-api is the only plugin pinned to the RC.
  • Migration renamed for the RC: migrate_0.9.0.sql → migrate_0.9.0-rc.1.sql with identical content. All section tests, seed comments and version_compare guards point at the new name. The dispatcher tests/migration-0.9.0-rc.1.unit.php now skips itself, since it matches its own migration-0.9.0-* glob. scripts/verify-schema.sh also runs the RC migration's test when version.json is the stable X.Y.Z, and fails if that test is missing.
  • Contributor cache lists: AuthorNormalizer::normalizeStored() now splits a ;/| list and normalises each name, so inverted names and per-name quotes come out right. LibraryThing import and BookRepository::updateOptionals() use it for the libri.* cache columns.
  • Vanished holiday ids: HolidayPresets::missingIds() lists the chosen ids that the rule table or dataset no longer knows. The calendar page shows them in a warning box, with locale strings in all five languages, a unit test and an E2E test.

ℹ️ The RC migration will not run again on the upgrade from the RC to 0.9.0

The PR body says migrate_0.9.0-rc.1.sql "runs on the RC and again on the later 0.9.0 upgrade". However, Updater::shouldRunMigration() needs the migration version to be greater than $fromVersion, and isMigrationExecuted() also skips it. So on an RC install that upgrades to 0.9.0, the file never runs again. It does still run once for libraries that go straight from 0.8.x to 0.9.0. This means any schema fix made after the RC has to ship in a new file, such as migrate_0.9.0.sql or migrate_0.9.0-rc.2.sql, not as an edit to this one.

Technical details
# RC migration is not re-run on RC → stable

## Affected sites
- app/Support/Updater.php:3827 — `shouldRunMigration()` uses `version_compare($migrationVersion, $fromVersion, '>')`; `'0.9.0-rc.1' > '0.9.0-rc.1'` is false.
- scripts/verify-schema.sh:55 — comment "That migration still runs on the upgrade to the stable release" is true only for upgrades from < 0.9.0-rc.1.

## Required outcome
- Release notes / PR body describe the actual behaviour; any change to the 0.9.0 schema after this RC ships in a new migration file.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread app/Support/AuthorNormalizer.php Resolved

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The contributor cache fix and the MARC dedup change both look correct.

Reviewed changes

This run covers what changed since the previous Pullfrog review at 4ffc58b8. The fixes were made on the stack branches and merged forward into this branch.

  • Entity-safe list split: AuthorNormalizer::normalizeStored() now looks for the list separator on the decoded value and splits with ContributorSync::splitNames(). As a result, O&#039;Brien, Flann stays one name, and the cache uses the same rule as the entity sync. Two new checks cover a single name with an entity and a list that contains one.
  • MARC title + author dedup: when a record has no ISBN, MarcBookImporter::findExisting() now resolves the first author with AuthorRepository::findByCanonicalName(). That is the same lookup that CsvImportController::getOrCreateAuthor() uses when it links the author. The query then matches la.autore_id together with the accent-exact title. Before, a Haffner record never matched a book filed under Häffner, so re-importing it created a duplicate. New DB checks cover four cases: the fold-to-existing match, a double import that leaves one book, the exact spelling winning once both authors are on file, and accent-distinct titles.

The earlier note on the migration still applies, and the PR body has not changed. An RC install that upgrades to 0.9.0 will not run migrate_0.9.0-rc.1.sql again, so any schema fix after the RC needs a new migration file.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

The close panel claimed that book availability is recalculated whatever the operator picks, but leaving the missing copies as they are touches no copy and recalculates nothing. The hint now says availability changes only when the copies are marked as lost, in every locale.
The settings persistence test now saves a QR code and the A4 sheet format
and asserts both rows in system_settings, plus that a card PDF without
?format uses the stored sheet default. The A4 sheet test reads word
positions with pdftotext -bbox and checks that the back text sits in the
mirrored column of its front.
The 0.9.0 migration entry point only compared the number of SQL sections with the number of section tests, so one extra test could hide a section that has none. It now carries an explicit section-to-test map (stocktake included, no slug derivation) and fails unless every section in the SQL has its mapped test file.
The afterAll cleanup no longer deletes every closure above an id taken at
start, which could also remove rows a concurrent writer stored meanwhile;
every closure the spec creates carries the run tag, so that tag is the only
criterion.
Before importing the Italian holidays the test makes the yearly Ferragosto
closure absent (pre-existing rows are made non-yearly and restored in the
teardown), then asserts the import inserted exactly that holiday instead of
relying on the total count of holidays.
The UNIMARC builder always wrote bibliographic level m in label/07, so a periodical exported as a monograph and its ISSN was lost. A serial now gets level s and its ISSN in field 011, mirroring the MARC 21 leader/07 and 022 logic; monographs are unchanged.
The deleted-book duplicate case soft-deleted the book with a direct UPDATE, so it proved nothing about what the real delete path leaves behind. It now deletes through the admin bulk delete endpoint and asserts the soft delete cleared the identifiers before re-importing.
Instead of accepting any start date after today, the test walks the stored
library-wide closures (one-off and yearly) from tomorrow and asserts the
reservation starts on the first day none of them covers, so a pre-existing
closure can no longer hide a wrong date.
The default-port test only covered MarcCoverFetcher::effectivePort. LocalSruClient::isLocalUrl now has its own checks: the installation's /api/sru is recognised with the HTTPS port implicit or written out, on either the canonical URL or the target, while another port or plain http on 443 is not.
The whole suite was skipped when the bibframe-linked-data plugin was inactive, although only the BIBFRAME checks need it: now only those tests skip, and the form, storage, page and clear-override tests always run. The DVD case also checks the derived video media label, the post-clear BIBFRAME check asserts the derived audio media type, and a new pair of tests saves a content type override and verifies it in the database, the reloaded form, the book page and BIBFRAME.
expectCardSize read only the first /MediaBox, so a multi-card PDF with a wrongly sized later page still passed. It now collects every MediaBox, requires at least one and checks width and height of each against 125 x 75 mm.
The schema.sql check confirmed the presenze table and columns but not the delete rule of fk_presenze_utente. It now also requires ON DELETE SET NULL there, so a fresh install cannot drift from the migration and drop visits when a member is deleted.
The test now dispatches a real POST to /admin/calendar/rotate-link through
the registered routes and middleware, with a valid CSRF token, as a custom
role holding settings.manage and as staff. Both pass the middleware, are
refused by the route and leave the library feed secret unchanged. A failed
check now throws so the role and user fixtures are always removed.
Both modules are off on an upgraded install, so the 0.9.0 write checks
ran against modules that refuse to write. The test now switches them on
for those checks and also asserts the opposite: with the modules off a
save keeps the stored values and an import batch assigns no supplier.
With loss penalties off the backfill never completes and chargeLossPenalty() re-arms the marker, so debtBalanceCents() and debtBalanceSql() kept adding every unconverted prestiti.sanzione: patrons were refused loans for amounts the library chose not to mirror, while the balance shown to staff and patrons stayed under the threshold. legacyPenaltiesPending() now answers false when loss penalties are off, so the debt block reads the ledger alone in PHP and SQL.
importCodes() keeps one transaction for the whole file, so the locking reads record() takes for a single scan held the copie and prestiti locks of every recorded code until the last one: returns waited for the upload, and a desk loan of a copy further down the file could deadlock with it. The upload now reads the committed loan state without locks; a single scan, in its own short transaction, keeps the locking reads that wait for a return in progress.
A cancel whose history entry failed hid the resume panel but left the upload step hidden too (it is rendered hidden while a run is resumable), so the page showed only the warning and no way to load a new file until a reload. The upload step is now shown under the warning.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The stocktake upload no longer holds circulation locks for the whole file, and the user import recovers cleanly after an unlogged cancel.

Reviewed changes

This run covers the two non-merge commits since the previous Pullfrog review at 4411496e.

  • Stocktake upload without circulation locks: when record() runs inside importCodes()'s single transaction, it now reads the loan and the copy state with plain consistent reads. A single scan() still uses the locking reads (loan, then copy). Before, a long upload held share locks on every recorded prestiti/copie row until the last code, so desk loans and returns of those copies waited, and a loan of a later copy could deadlock. The trade-off is deliberate: a copy returned while the upload runs can be recorded as in_prestito. inventario_scansioni has no foreign key to copie, so the scan INSERT takes no parent-row lock either. A new DB test holds a copy FOR UPDATE on a second connection and checks two things: the single scan times out, and the upload records the code.
  • User import after an unlogged cancel: when abandoning an interrupted import returns a history_warning, the page now shows the upload form again under the warning, so a new file can be picked without reloading.

The earlier note on the migration still applies: an RC install that upgrades to 0.9.0 will not run migrate_0.9.0-rc.1.sql again.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. This third hardening pass looks correct. The changed pure unit suites (library-calendar, stack-090-review-fixes, sru-search, unimarc-standards, copy-cataloging-limits, stocktake-classification/-pdf, marc-oai-byte-identity, marc-library) all pass locally, and the locale parity check is clean.

Reviewed changes

This run covers the 37 non-merge commits since c66fc170. They were merged forward through the stack branches.

  • Debt block fails closed: PatronLedger::schemaReadyOrFail()/activeOrFail() now throw when the schema probe fails, instead of reading as "no ledger". The debt block (CirculationPolicy, LoanEligibility::debtBlockSql()) and the reader delete guard use them. While the legacy backfill is still pending, debtBalanceSql() adds unconverted prestiti.sanzione amounts. It uses the same NOT EXISTS penalty:<id> predicate as the backfill, so a converted penalty is counted once.
  • Calendar reads fail closed: loadClosedWeekdays() and branchOfCopy() now throw on a failed read, like loadClosures(). ICS EXDATE now matches on the DTSTART date and time, and trailing junk in a date is refused.
  • Calendar admin: an update re-checks that its closure still exists under the lock. ICS import carries the whole-year check state from one event to the next, so it only rescans from a known open day that the new row closes. Coverage rejections now get their own message.
  • User import: each block resumes at a saved byte offset with fgetcsv, using the same controls as Csv::configureReader(). The per-row seen_* session sets are replaced by exists(). send_invites is now a real boolean. A run closed without its history entry now shows a history_warning instead of claiming it was recorded.
  • Reader delete: the row is deleted first. Its photo file is removed before the commit, and a failed unlink rolls back the delete. Photo writes now unlink a short or failed write.
  • Stocktake: a scan reads the loan, then the copy, with share locks inside its transaction. The CSV report is streamed from a generator, and the PDF re-checks the exact rows it renders (LengthException above MAX_ROWS). Code import reads only the first cell of each line or row.
  • MARC import: in non-skip modes, a record whose identifiers match more than one book is refused. The title+author fallback now requires a principal author. An overwrite clears another work's ISBN form. ISO 2709 detection checks for the terminator at the base address.
  • UNIMARC export: both ISBNs and every language are written. The genre moves from 606 $2 local to 608 $2 local (unimarcGenreField()) in the builder and in the SRU formatter, and the mapper reads it back.
  • Copy cataloguing / SRU: retries carrying the same import_token replay the first answer, and the MARC detail is capped. SSRF-blocked requests are not retried. The retry budget now includes backoff and rate-limit waits. The bare-isbn fallback only runs after an HTTP 400.
  • Smaller guards: bulk card print above MAX_BULK now returns 422. MARC export refuses an ids[] array. The previous MARC preview is kept until the new file is staged. The OAI single download reports a reduced record or cut fields in headers. The book request return date stops at the last loaded availability day.

The earlier note on the migration still applies: an RC install that upgrades to 0.9.0 will not run migrate_0.9.0-rc.1.sql again.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. With loss penalties off, the debt block now uses the same balance that staff and patrons see.

Reviewed changes

This run covers the single non-merge commit since the previous Pullfrog review at df3e9fdc.

  • Legacy penalties when loss penalties are off: PatronLedger::legacyPenaltiesPending() now returns false when loss_penalties is off. As a result, debtBalanceSql() and debtBalanceCents() fall back to balanceSql() and balanceCents(). Before, chargeLossPenalty() re-armed the marker and backfillLegacyPenalties() never ran with the module off, so unconverted prestiti.sanzione amounts kept blocking loans indefinitely, even though no visible balance included them. Those two methods are the only callers. When the module is switched back on, SettingsController re-arms the marker, so the pending amounts count again until the backfill drains. Because loss_penalties is a child of fines, turning fines off has the same effect. A new check in tests/patron-ledger.unit.php covers both the PHP and the SQL paths. It needs a DB, so it did not run here.

The earlier note on the migration still applies: an RC install that upgrades to 0.9.0 will not run migrate_0.9.0-rc.1.sql again.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

0162a39 stopped the longest return date at the last loaded availability day, but the book page loads 180 days and lets a reader start on almost any of them: a start four months ahead could no longer have its return date in the following month. The public availability route now loads occupancy data past the 180 start days by the longest loan plus the closed-day margin (the same horizon the closed days already use), and returns start_until, the last day a request may start on, which the start picker uses as its maximum. The clamp from 0162a39 stays as a safety net.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. A reader who picks a late start date can now get the full loan window again.

Reviewed changes

This run covers the single non-merge commit since the previous Pullfrog review at 96881d09.

  • Availability past the last start day: getBookAvailabilityData() has a new $coverLoanWindow flag. When the flag is set, days now reaches as far as closed_dates already did: 180 start days, plus the longest loan, plus the 31-day closed-day margin. The method also always returns start_until, the last day a request may start on. Only the public /api/books/{id}/availability route sets the flag, so the mobile, staff and calendar callers are unchanged.
  • Start picker cap: book-detail.php now uses start_until as the start picker's maxDate, and falls back to the last loaded day when it is missing. The lastLoaded clamp in loanWindowFrom() stays as a safety net, and it no longer cuts a late start short. tests/loan-window-cap.unit.php checks that the plain call ends at start_until and that the covered call runs past it by at least the longest loan plus 31 days.
  • Side effects on the same route: earliest_available and unavailable_dates now cover the longer window. This only matters when all 180 start days are blocked, and then no start can be picked either way. In the admin loan form (crea_prestito.php), isAvailableNow still means "no blocked day in the loaded window", but the window is now longer. That form takes no maxDate from days.

The earlier note on the migration still applies: an RC install that upgrades to 0.9.0 will not run migrate_0.9.0-rc.1.sql again.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant