История коммитов

.
feat(collections): add sitemap provider for public collection pages
Registers a johncms.sitemap_provider that emits URLs for each active
collection root, every reachable active section (paths built by walking the
tree; sections behind an inactive ancestor are skipped) and every published
item (getVisibleForSitemap streams them with a cursor), with lastmod from
updated_at. Items under an inactive section are skipped. Item repository gains
getVisibleForSitemap; DB test covers active-only output and unreachable skips.
.
feat(collections): show child sections in the public listing
Makes the section tree browsable on the public site.

- Section repository gains getActiveChildren(collectionId, parent): active
sections of a level, parent-scoped and sorted.
- CollectionRouterController lists the active child sections of the current
level as links above the items, so the root shows root sections and a section
shows its subsections. The empty-list notice appears only when neither
sections nor items exist.
- Section repository DB tests (active filter, parent scope, ordering).
.
feat(collections): reject reserved codes when saving a collection
Guards the public URL resolver against unreachable collections.

- ReservedCodeChecker (interface + impl) inspects the compiled Symfony
RouteCollection and collects claimed top-level segments: the literal first
segment of every route (placeholder-first paths like /{route} are skipped)
plus a static list of filesystem/entry paths (admin, assets, upload, system,
data, install, vendor, index.php).
- SaveCollectionUseCase checks isReserved() before persisting and throws
CollectionCodeReservedException; the admin controller surfaces it as a form
error. Tests cover the checker over sample routes and the reserved branch.
.
feat(collections): add public URL resolver with listing and detail
First public-output increment of iteration 4.

- Low-priority catch-all route /{route} (priority -1000, custom requirement for
nested paths and the .html suffix), registered outside the admin rights guard.
- CollectionRouterController parses the path: first segment resolves a collection
code (cached), a .html suffix renders the item detail, otherwise the section or
root listing. Breadcrumbs via NavChain, PageMeta and Pagination (per_page from
settings); missing collection/section/item -> pageNotFound.
- CollectionCodeCache (interface + Johncms\Cache impl) caches the active
code->id map so the resolver, which is also the catch-all 404 path, keeps
misses off the database; invalidated on collection save/delete.
- ListPublicItemsUseCase / GetPublicItemUseCase (visible-by-code with publish
window, values by field label); PublicItemDTO/PublicItemDetailDTO; repository
gains getActiveCodeMap and findVisibleByCode. Public listing/detail templates
(escape + nl2br).
- Real routes win by priority; the catch-all only handles otherwise-unmatched
URLs. Use case tests (listing mock, detail DB test).
.
feat(collections): add admin CRUD for items with dynamic field form
Fourth admin increment: items with custom EAV values.

- Nested routes /admin/collections/{collection_id}/items[...].
- CollectionItemsAdminController: paginated list (admin sees inactive too,
optional ?section= filter); create/edit form with base fields plus dynamic
widgets per FieldType (textarea for text/html, number for integer/double,
checkbox for boolean, per-line textarea for multiple, text otherwise);
section picker when has_sections; store with CSRF, validation and code
uniqueness per (collection_id, section_id).
- EAV read/write: form values read via getValuesMap (findWithValues), and
SaveCollectionItemUseCase fully rebuilds values (delete + insert) into the
typed columns, casting per FieldType (boolean always 0/1, empty singles
skipped, dates normalized).
- New ContentCollectionItemValueRepository (deleteByItem/insertMany); item
repository gains findWithValues/create/update/delete; section repository gains
getAllByCollection. Items link added per collection.
- DB test for the EAV write path (typed columns, full replace, duplicates) plus
list/delete use case tests.
.
feat(collections): add admin CRUD for collection sections
Third admin increment: hierarchical sections.

- Nested routes /admin/collections/{collection_id}/sections[...] with level-by-
level tree navigation (?parent=), breadcrumb path via getPathTo, per-level
pagination and child counts (withCount).
- CollectionSectionsAdminController: list, create/edit form (parent fixed on
edit, validated against the collection on create), store (CSRF, code format,
code uniqueness per (collection_id, parent)), delete confirmation. Deleting a
section cascades to child sections and nulls items' section_id.
- List/Save/DeleteCollectionSectionUseCase, CollectionSectionFormDTO/
CollectionSectionListItemDTO, CollectionSectionCodeAlreadyExistsException;
section repository gains findById/create/update/delete and withCount.
- Sections link shown per collection only when has_sections (CollectionListItemDTO
gains hasSections); use case tests.
.
feat(collections): add admin CRUD for collection fields
Second admin increment.

- Nested routes /admin/collections/{collection_id}/fields[...] under the same
rights guard.
- CollectionFieldsAdminController: field list, create/edit form with a FieldType
<select> (localized type labels), store (CSRF, code format/required/type
validation, code uniqueness per (collection_id, code)), delete confirmation.
Ownership check ensures the field belongs to the collection in the URL.
- List/Save/DeleteCollectionFieldUseCase, CollectionFieldFormDTO/
CollectionFieldListItemDTO, CollectionFieldCodeAlreadyExistsException; field
repository gains findById/create/update/delete.
- Fields link added to each collection row; use case tests. Field settings
(select options, relation target) will be edited with the item form.
.
feat(collections): add admin CRUD for collections
First admin increment of the collections module.

- Routes /admin/collections[...] guarded by rights >= 9, wired in the module's
own routes.php.
- CollectionsAdminController: paginated list with success flash, create/edit
form, store (CSRF, code format/required validation, code uniqueness), and
delete confirmation (FK cascade removes fields, sections, items, values).
- List/Save/DeleteCollectionUseCase, CollectionFormDTO/CollectionListItemDTO,
CollectionCodeAlreadyExistsException; repository gains create/update/delete
(update via model save to apply the settings cast).
- Admin templates (escape on output) and a Collections entry in the admin
sidebar menu.
- Exclude Application DTO/Exceptions from service autoload; use case tests.
.
feat(collections): add content collections module with EAV query core
Universal 'sections + items' storage with a single
query API over any collection.

- Schema: 5 tables (collections, collection_fields, collection_sections,
collection_items, collection_item_values) via Installer, plus demo Blog data.
- Domain: Eloquent models, FieldType/FilterOperator/SortDirection enums,
repository interfaces, immutable ContentCollectionItemQuery with typed
FieldFilterDTO/OrderByDTO.
- Infrastructure: repositories + ContentCollectionItemQueryCompiler applying
EAV filters (whereExists) and ordering (correlated sub-select) with
allowlisted base columns and enum-only operators/directions.
- Read model: ContentCollectionItem::getValuesMap() maps field code to cast
value (multiple fields as sort-ordered arrays) from eager-loaded values.
- Register module (composer PSR-4, modules.global, phpunit source).
- Tests run on in-memory SQLite via new Tests\Support\BootsInMemoryDatabase.
.
fix(downloads): avoid undefined sort session keys in category list