Журнал изменений
Portable, PHP-level redirects in core/security.php (proxy-aware via X-Forwarded-Proto, GET-only, default off):
- bare /index.php -> / (301) to kill the duplicate homepage URL
- opt-in forcessl (http->https) and forcehost (www->canonical homeurl host)
Admin config SEO tab gains forcessl/forcehost toggles (constants in all 6 languages); getPublicUrl() returns root with trailing slash; help doc admin/info/config/ru.md documents canonical behavior and the new toggles.
Rename the Magistral webfont from Magistral-Book.woff2 to magistral.woff2 and update the @font-face src plus the PERFORMANCE.md caching example to match. Same binary, lowercase/simpler name.
Core changes:
- Font asset (templates/lite/assets/css):
- Rename fonts/Magistral-Book.woff2 to fonts/magistral.woff2
- base.css @font-face src updated to fonts/magistral.woff2
- Docs (docs/PERFORMANCE.md):
- Update font caching-caveat example to magistral.woff2
Technical notes:
- Byte-identical file (26808 bytes); no remaining refs to old name
Move hardcoded & and hand-built HTMX markup out of PHP: URL producers now pass a raw & URL as data, and fragments escape it once at the output boundary via {{ }}. Removes the last inconsistency where URL-bearing HTMX attributes still followed the legacy escape-at-source + markup-in-PHP pattern.
Core changes:
- Link/tab/button fragments (templates/{lite,admin}/fragments):
- tabs-link.html: add hx_get/hx_target/hx_swap block, URL escaped via {{ }}
button.html: add hx-post block (hx_post/hx_include/hx_target/hx_swap/ hx_on_click/hx_on_after), URL escaped; JS validation stays raw passthrough
- link fragment already had is_htmx (hx-get="{{ href }}") — reused as-is
- PHP producers now emit data, not markup:
- core/admin.php: upload file links use is_htmx + hx_target (drops & blob)
- admin/modules/uploads.php: upload tabs pass raw hx_get/hx_target
- core/user.php: comment/PM submit forms pass raw hx_post + hx_* fields
- Docs:
- Remove docs/PROMPT_HREF_AMPERSAND.md (task complete)
Benefits:
- Single escape at the sink; & structurally impossible
- Unified hx_* field vocabulary across link/tabs-link/button fragments
- Removes duplicated URL (href + hand-built hx-get) in upload links
Technical notes:
- getSafe() = htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE, UTF-8)
- Verified live: comment form, admin upload tabs — single &, zero &
- Additive change; legacy link_attr/input_attr passthrough kept for other uses
Track the Magistral-Book.woff2 asset referenced by the base.css @font-face declaration, completing the font migration whose base.css update and legacy WOFF2 removal landed in the previous commit.
URLs are now treated as data: every producer emits a raw & and the template layer escapes exactly once at the output boundary via {{ href }}. This removes the previous mix of producer-side & encoding and raw {{{ }}} output and eliminates latent single/double-escape inconsistencies across the site.
Core changes:
- URL producers to raw & (blocks, modules, admin/modules, core):
- Front-end SEO links built via getSeoUrl() (raw &, SEO-friendly URLs under rewrite)
- Admin afile.php?..., htmx go=...&op=... tails, absolute/redirect URLs to raw &
- getQueryString() always emits raw & (dropped the obsolete $html parameter)
- Template output boundaries (81 flips, lite + admin fragments/partials):
- href/src/action/hx-get/hx-post URL attributes: {{{ x }}} to {{ x }}
- Kept raw ({{{ }}}): _html and _attr blobs, inline-JS onclick (reset_url, reload_query)
- Removed runtime URL pre-encoders exposed by the boundary flip:
$alink session-monitor URL, RSS source href, and files-admin download href no longer wrapped in htmlspecialchars() (the boundary now escapes)
- domain() html_entity_decode()s stored referer URLs before building the link
Benefits:
- Single source of truth for escaping; no more & risk
- Redirects/Location and htmx keep a raw &; HTML output is uniformly &
- Blocks now honor rewrite / SEO-friendly URLs like the modules do
Technical notes:
Left untouched: parser entity decoders, config/* content cache, setup legacy HTML echoes, sitemap XML content encoder
Pre-existing/out-of-scope: _referer stores double-encoded anchor data; config.replace link URLs are stored with a bare &
Verified: php -l (85 files), phpunit 8/8, guest + admin real-HTTP render with zero app-generated &
Remove legacy raster icons from the admin and lite themes in favour of Bootstrap Icons, add the bootstrap-icon fragment, and normalize admin and module handlers, config, and styles across the board.
Add the breadcrumb variant galleries under demo/ and a task prompt for the site-wide URL escaping migration (escape at the HTML output boundary, keep getSeoUrl raw).
Rewrite getTplCategoryTrail as an ordered root-to-current walk, dropping the legacy nested-anchor markup and adding a cycle guard. Rich mode renders text crumbs via the new category-crumb fragment; each crumb takes its category tone (ordern % 6) through the shared sl-cat-tone map and reveals it on hover with a sliding underline and a nudging separator. Plain mode keeps flat links for the head banner. Crumb hrefs are escaped at the template boundary for valid ampersands.
Rework the admin sidebar counter block into a data-driven table with a single row helper, switch to COUNT queries, wire sidebar dials, and bring helper names and comments in line with the project rules.
Core changes:
- Sidebar counters (core/admin.php):
- Replace the repeated per-module blocks with a grouped table and loop
- Add getAdminCountRow() using SELECT COUNT(id) instead of materializing rows
- Emit the unified sl-chip badge for counts
- Remove the dead album block
- Naming and comments (core/admin.php, admin/modules/monitor.php):
- Rename admininfo() to getAdminInfo() and db_version() to getDbVersion()
- Migrate call sites
- Add missing function comments, drop in-body comments and blank lines
- Sidebar dials (admin/index.php):
- Build the dial action list once for both grid cards and list rows
- Docs (docs/PERFORMANCE.md):
- Sync the counter reference to the new function name
Benefits:
- Fewer and lighter count queries per admin render
- Less duplication and no dead code
- Naming and comments compliant with the project rules
Technical notes:
- No storage or template contract changes
- Backward compatibility is preserved
Introduce round dial action buttons on the left module list, a shared chip vocabulary aligned with the lite theme, and a borderless module-head icon. Template and CSS layer only.
Core changes:
- Sidebar dial (templates/admin/fragments/*.html):
- Extract the dial markup into a shared fragments/dial.html
- Reuse it from menu-grid-item and menu-list-item
- Unified chips (templates/admin/assets/css/theme.css, fragments/inline-badge.html):
- Add .sl-chip base with .sl-chip-success and .sl-chip-warn tones
- Add chip_tone support to the inline-badge fragment
- Drop the legacy sidebar count-value pill styling
- Module-head icon (assets/css/base.css, theme.css):
- Add the --sl-size-icon-lg token (28px)
- Render the head icon as a bare 28px glyph without frame or background
Benefits:
- One chip vocabulary shared with the lite theme
- Quicker access to module actions from the sidebar
- Less bespoke CSS
Technical notes:
- No PHP or storage changes
- Backward compatibility is preserved