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

.
docs: update docs submodule with forum link normalization guide
.
docs(changelog): add entry for forum normalize-message-links command
.
feat(forum): add command to normalize legacy links in message text
Adds forum:normalize-message-links, a one-time console command that unwraps
the /redirect/ interstitial from internal links in forum posts and rewrites
legacy links to canonical routes:
- ?act=show_post&id=N -> /forum/post/N/
- ?type=topic&id=N[&page=P] -> SEO url via ForumTopicPathService

External links stay wrapped. Link rewriting lives in a pure, tested
ForumMessageLinkNormalizer service.
.
refactor(system): use Cache library for counters
- Replace manual file cache (file_exists/filemtime/json_decode/file_put_contents) with Johncms\Cache remember()
- Merge duplicated counter queries: album/downloads/library/forum pairs now share one cached raw method and a single cache key
- Fix warning on accessing array offset on null when a cache file was empty or corrupted
- Drop unused Tools dependency from Counters constructor
.
fix(system): handle null path from parse_url in UserStat::determinePlace
Protocol-relative request URIs like //x.php are parsed as host without path, making parse_url return null and str_ireplace throw a TypeError.
.
feat(collections): add standalone install script for existing setups
- install/install_collections.php creates the 5 module tables via ModuleInstaller
- idempotent: skips when the collections table already exists
- optional --demo flag installs the sample "Blog" collection
- document the step in the 9.8 upgrade guide and CHANGELOG
.
fix(system): restore HTMLPurifier factory for global \HTMLPurifier service
The plain class registration overrode the factory definition, so the
container returned a default HTMLPurifier without figure/oembed/figcaption
support. Rich content images lost their figure.image wrapper, breaking
in-container image scaling and the magnific popup.

Closes #254
.
fix(collections): build item URL from its own section path in root listing
The root listing shows items from every section, but the detail URL was built
from the current listing path, so a sectioned item shown at the collection root
got /collection/code.html without its section segment and 404'd. PublicItemDTO
now carries sectionId, and the listing resolves each item's section path
(getPathTo, memoized per page) to build /collection/section-path/code.html.
.
feat(collections): auto-generate item slug from name
When the item code is left empty it is generated as a slug of the name
(Str::slug, transliterating Cyrillic) in SaveCollectionItemUseCase and made
unique within the collection/section with a numeric suffix on collision
(base, base-2, ...). An explicit code is still rejected on duplicate. The admin
validation now treats code as optional; the facade addItem generates it too.
.
feat(collections): use CKEditor for item preview text
Preview text becomes rich HTML like detail text: the admin item form renders it
through the CKEditor partial (scripts loaded once, on the preview editor), and
the public output sanitizes it via ItemContentFormatter in both the listing
(ListPublicItemsUseCase) and the detail page, rendering purified HTML instead of
nl2br+escape.