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

.
feat(collections): add public flag to gate URL access
Separate 'reachable by URL' from 'active': a new collections.public boolean
(default 1). A private collection stays fully manageable in admin and readable
via the API/facade, but mints no front URL and is excluded from the sitemap.

URL resolver and sitemap now go through getPublicCodeMap() (active AND public,
renamed from getActiveCodeMap); the code cache stores the filtered map. Admin
form gains a Public checkbox. Reserved-code validation stays flag-independent
so a private code can go public without a segment clash.
.
feat(collections): public CollectionsApi facade for third-party modules
Add CollectionsApi (CollectionsApiInterface), a stable DI-public facade over
repositories and item use cases. Read:
findCollection/getItems/countItems/getItem/getItemByCode. Write: addItem
(returns new id), updateItem, deleteItem, reusing code-uniqueness checks and
EAV value rebuild.

SaveCollectionItemUseCase::execute now returns the item id (int) instead of
bool; the admin controller derives the create/update flash from $id.
.
feat(collections): rich detail text with CKEditor and output sanitization
Completes iteration 4 (public output).

- The item admin form edits detail_text with CKEditor via a module ckeditor
partial (the admin theme ships CkeditorInputComponent.vue; system::app/ckeditor
only exists in the default theme, so collections carries its own).
- Content is stored raw; GetPublicItemUseCase sanitizes detail_text on output
through ItemContentFormatter (injects \HTMLPurifier) and the detail template
renders it as-is. preview_text stays plain (nl2br + escape).
- ItemContentFormatter test (real HTMLPurifier strips script/on* handlers, keeps
safe tags); GetPublicItemUseCase test updated with a passthrough purifier.
.
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.