Официальный сайт SLAED CMS
Журнал изменений
The browser check of every form before 8.0 ran the panel shell, the site for a guest and a user, the content of the site, the forum with the small modules and the content of the panel on the stand. Each defect it met is fixed in its source function: empty titles and bodies are refused before any write, URL fields no longer start with a bare scheme the browser refuses, rights follow the section they belong to, and the forum counts and titles itself once.
Core changes:
- Panel shell (admin/index.php, admin/modules/admins.php, public/index.php):
- setAdminOption() refuses an unknown editor instead of storing the default in its place
- The administrator form keeps the stored name, as no loop of the form reuses $name, and starts its URL empty
- go=5 lets a delegated administrator refresh getUserSessionAdminInfo of the admin block
- Site and cabinet (modules/account/index.php):
- The private rows and the admin menu of a profile need the right account, not any administrator
- An empty feed address of the cabinet is answered with _RSS_PROBLEM, not with nothing
- A saved profile returns to its form with _SUCCSAVE
- Comments (core/user.php, core/classes/comment.php, modules/node/index.php, docs/NODE.md):
- A moderator is no longer told that his comment waits for a check
- The public form of a material carries no comment mode, so a moderator who sends it gets CommentMode::Open
- Comment::updateBody() refuses an empty body with _CERROR1, as every other edit path does
- Forum (modules/forum/index.php, modules/forum/admin/index.php, modules/forum/admin/info/ru.md):
- The page titles are given to setHead() as title and ctitle and joined once by getHeadTitle()
- A new topic counts topics+1 only, as delete, move and updateForumSync() count it
- The final delete of a topic takes back the comment points of its replies
- The first tab of the section is _HOME on all three tabs; the help names the sync, the counting and the recycle forum
- Categories (admin/modules/categories.php):
- addsave() and save() refuse an empty title with _CERROR before the Node writer or the SQL of the forum
- Node in the panel (modules/node/admin/index.php, modules/node/admin/info/ru.md):
- "Добавить" no longer offers a type of the extension support and its op=add answers 404, as a request is made by a user on the site and the panel keeps it through its card; the help says so
- Users in the panel (modules/account/admin/index.php):
- A new user starts with an empty site, so the field of type url no longer holds the form
- Tests (tests/Unit/AdminGuardTest.php, CommentTrustBoundaryTest.php, CommentStateTest.php, FeedTest.php, NodeServiceTest.php, NodeSupportTest.php, UploadIntegrationTest.php, tests/Support/contract_probe.php):
- Each fix is held by its suite; the contract probe saves an empty body by the moderation path and expects _CERROR1
Benefits:
- A refused input writes nothing and says why, on the screen that took it
- The rights of a delegated administrator follow the section it holds
Technical notes:
- No schema, URL or API change; the stand keeps only the traces of the checks
- RouteParseTest still expects the forum title "Форум Talk - Форум" for forum/talk, while getHeadTitle() now gives "Talk - Форум"; its expectation is the next step of the plan
The picture of a hero that opens a page is its largest paint, and loading="lazy" held it back until the layout was known. A hero with nothing above it now asks for its picture with high priority; a hero under the module output or the band, or hidden on one device, keeps the lazy load, as an eager picture is fetched under display: none too.
Core changes:
- Layout (core/classes/layout.php):
- getPageHtml() hands each section is_first: no section and no module output above it, no band on the page, shown on the phone and the desk
- Hero section (public/templates/lite/sections/hero.html):
- The picture carries fetchpriority="high" with is_first and loading="lazy" without it
- Docs (docs/TEMPLATES.md, docs/VERSIONS.md):
- The data of a fragment names is_first and when it is set
- Tests (tests/Unit/LayoutTest.php):
- onlyTheTopHeroLoadsItsPictureAtOnce renders a hero on top, under the module, under the band and off the phone
Benefits:
- The largest paint of a page opened by a hero is fetched first
Technical notes:
- No schema, URL or API change; a section fragment may read is_first, none has to
The check "Ошибки PHP" of the home lamp summed error_php.log, error_site.log and error_file.log, and the site journal writes every 404 and 403 at the level error, so the bots probing for files kept the lamp red on a site with no fault; its row led to the monitor, whose own lamp has no error check, so the errors seemed gone after a click. Each journal is now its own check under its true name and opens itself, and a miss of a visitor is no failure.
Core changes:
- Error count (core/monitor.php):
- getErrorLogCountHours() skips a line whose http_code is under 500, on the lamp and in the count of the monitor
- getSystemSignals() answers sixteen checks: "Ошибки PHP" counts error_php.log alone, the new "Ошибки сайта" and "Ошибки файлов" count error_site.log and error_file.log, each warning from one a day and failing from a hundred
- The four error checks link to logs&op=logview with their journal instead of the monitor and the database tool
- Database connection (core/classes/pdo.php):
- A refused connection is written by Logger::addSql to error_sql.log instead of the site journal
- The SQL error checks of the lamp and the monitor and the es figure of the update check now count it
- Language (admin/lang/*.php):
- _ADM_SIG_SITE and _ADM_SIG_FILE in the six locales
- Help, docs and demo (admin/info/monitor/ru.md, admin/info/panel/ru.md, docs/ARCHITECTURE.md, docs/TEMPLATES.md, docs/VERSIONS.md, public/demo/admin/admin.js):
- Sixteen checks, the thresholds of each error journal, what is not counted, and the journal each row opens
- The demo lamp lists the two new checks and the new links
- Tests (tests/Unit/CockpitTest.php, tests/Support/cockpit_probe.php, tests/Unit/JournalNameTest.php, tests/Unit/NodeProfileTest.php, tests/Unit/ApiEntryTest.php, tests/Support/route_probe.php):
- CockpitTest holds the sixteen keys and the thresholds of the site and file errors
- JournalNameTest holds that a 500 counts and a 404 or 403 does not
- NodeProfileTest expects the refused connection of the installer in error_sql.log
- The entry run of route_probe counts the refusal of its lost database in error_sql.log and takes it out, so ApiEntryTest holds it written once while the requests still write no other SQL error
Benefits:
- The lamp fails only on a fault, and every row says what broke and leads to its record
Technical notes:
- No schema, URL or API change; the journals and their lines stay as they are
- The SQL error figure an installation reports grows by its refused connections
The high findings of the defence audit and the leaks of a closed forum: the sitemap stylesheet runs in the origin of the site, so its editor is a right over the whole site; a poll answer was printed as markup; the forum search, the teasers of the blocks, the quote of a reply and the favorites listed topics of categories the visitor may not read; the separator of the forum was printed raw and the type defis of getVar() only fed it.
Core changes:
- Sitemap template (modules/sitemap/admin/index.php, modules/sitemap/admin/info/ru.md):
- getSitemapTabs() builds the one tab strip of the module and drops the tab of the template below the super administrator
- xsledit() and xslsave() refuse any other administrator before the file is touched
- The help says who sees the tab and why
- Poll answers (core/system.php, modules/voting/admin/index.php):
- getVotingView() prints an answer escaped in the results and as label_text in the form
- save() keeps the answer as typed, replacing only the column separator and line ends; add() shows it as stored
- Closed forum (core/system.php, core/user.php, modules/search/index.php, modules/forum/index.php, modules/account/index.php):
- getForumCats() returns the categories the visitor may list and read, the recycle one for the moderator only, memoised per request
- getForumTopics(), getSearchForum(), addFavorite(), getFavoriteList() and the favorites of profil() keep to those categories and to published topics for a visitor
- add() of the forum quotes only a post of the topic being answered, and a hidden one only for a moderator
- Separator of the forum (core/security.php, core/helpers.php, modules/forum/admin/index.php, modules/forum/index.php, public/templates/lite/index.php, CONTRIBUTING.md):
- The separator is escaped wherever it is printed
- The type defis of getVar() is gone; configsave() reads the sign as let and stores it urlencoded as before
- Tests (tests/Unit/VotingViewTest.php, tests/Unit/AdminGuardTest.php, tests/Unit/ForumRightTest.php, tests/Support/contract_probe.php):
- VotingViewTest renders a poll with markup in its answers through the probe and checks the save
- AdminGuardTest holds the guard of the sitemap template; ForumRightTest holds the guest set of a closed forum
Benefits:
- A delegated administrator of the sitemap cannot run script in the origin of the site
- What the forum hides from a visitor stays hidden in every list that names its topics
Technical notes:
- No schema, URL or API change; the stored separator keeps its urlencoded form
- getVar() no longer knows the type defis; no caller passes it any more
The content of the block "Администрация" is written in the Markdown editor since the move away from BB, but its own links were read from <a> tags alone, so a link typed in the editor never showed, and the move of the production texts left the old link as plain text. The block now reads both forms, lets a link name the site, its host and its panel file instead of a fixed address, and keeps its links at hand without the floating menu.
Core changes:
- Own links (core/system.php):
- getAdminBlockLinks() reads name of Markdown beside the <a> tag, in the order written
- An image is no link; a hint is optional and falls back to the name
- [homeurl], [host] and [admin] in an address or a hint become the address of the site from its setting, its host and HOME_PATH with the panel file of $afile, so a section link holds on a site whose panel is not admin.php
- Panel layout (core/admin.php):
- The block stands on every page of the panel while the floating menu is off (dock-off), on the home as before
- Help and docs (admin/info/blocks/ru.md, admin/info/panel/ru.md, docs/ARCHITECTURE.md, docs/VERSIONS.md):
- The help of the blocks shows how a link is written and what the three marks become
- The help of the panel and ARCHITECTURE.md say where the block stands without the floating menu
Benefits:
- A link typed in the editor is a link of the block and of the floating menu
- One list of links fits the stand and production alike
Technical notes:
- The content of the block row is unchanged in form; an <a> tag and javascript:OpenWindow are read as before
- No schema, URL or API change
The guard screen of the presentation moved its gate by top every frame and measured the layout inside the frame, which kept the main thread drawing and restyling. The gate now stands on a whole device pixel of its scene and moves by transform, measured only when the scene changes size. The laws that hold the cost of a moving page down, the rig that proves them and the measured pairs are written down with it.
Core changes:
- Guard gate (public/plugins/presentation/presentation.js, public/templates/lite/assets/css/presentation.css):
- measure() reads the run of the gate, its size and the place of its shield once, and again from a ResizeObserver
- patrol() reads no layout per frame and writes --sl-d-gate-y on the gate, not on the scene
- --sl-d-gate-top sets the gate on the first whole device pixel of its scene; --sl-d-gate-y moves it by whole device pixels, rounded where the shield falls, so the parts of the gate move as one
- --sl-d-gate-y is registered by @property without inheritance, so a frame restyles the gate alone
- Rig and contract (tools/frame-cost.mjs, tools/ui-contract.php, tools/ui-audit-baseline.json):
- The pixel check of the rig gains the part development of the presentation
- The contract names --sl-d-gate-top and the new meaning of --sl-d-gate-y
- The audit baseline is regenerated: the tokens --sl-led-ring, --sl-pres-photo-bg, --sl-switch-on-bg and --sl-switch-on-text, and the single-use counts of both themes
- Docs (docs/TEMPLATES.md, docs/PERFORMANCE.md, docs/VERSIONS.md):
- TEMPLATES.md, "The Cost Of A Frame": the picture first, transform and opacity, a thin line keeps its offset, a script moves by whole pixels, no layout inside a frame, a loop sleeps off screen, nothing inherited per move, and what stays by decision
- PERFORMANCE.md, "Frame Cost": the jobs of the rig, its interleaved pairs, its pixel tolerance and the measured pairs of 2026-11-01 and 2026-11-02
- VERSIONS.md records the motion work of these two days
Benefits:
- The guard screen falls from 85 to 60 % of a core, its style time from 256 to 42 ms a second
- The laws and the rig are written once, so a new effect is checked against them instead of by eye
Technical notes:
- No schema, URL or API change; the picture of the gate is held by the pixel check of the rig
The visit counter drawn as a PNG badge and the random banner block belong to the era of link exchanges; the site keeps its statistics in the panel. This change removes both blocks with their pictures, stops a clean install from seeding them, and lets the update switch off a block that still runs one of their files.
Core changes:
- Blocks and the counter picture (blocks/stat.php, blocks/banner_random.php, core/user.php):
- The file blocks stat.php and banner_random.php are removed
- The switch on index.php?stat=1&img=N that drew today's visits onto a PNG is removed from core/user.php
- Theme images (public/templates/lite/images/, public/templates/admin/images/ranks/):
- The folder banners/ of lite with its badges and the random banners is removed
- The leftover folders account/ of lite and ranks/ of admin, which nothing reads, are removed
- Installation and update (storage/update/sql/insert.sql, public/update.php):
- A clean install no longer seeds the blocks "Счетчик посещений" and "Реклама"; "Форум внизу" takes id 6
- update.php switches off an active block that runs stat.php or banner_random.php, as it does with the blocks of the removed modules, and names the files it switched off
- Probe and test (tests/Support/install_probe.php, tests/Unit/UpdateSiteTest.php):
- The upgrade probe seeds a stat.php block and reads the new report line; the test expects it switched off
- Demo and docs (public/demo/assets/sb-data.js, docs/TEMPLATES.md, docs/VERSIONS.md):
- The site builder demo drops the two file blocks from its list and its start layout
- TEMPLATES.md no longer names banners among what the panel reads of the site theme
Benefits:
- Two blocks, a GD drawing path and 48 images less to ship and to keep
- An updated site does not run a block whose file is gone
Technical notes:
- Breaking: an outside page that embeds index.php?stat=1 gets the start page instead of a PNG
- No schema change; the update only sets status = 0 on the matching _blocks rows
The presentation page is the front of the site, and a visual review of it in both colour schemes found words cut at any letter, text past its frame and parts that said nothing; this change settles them, lets the guard and the cache of the request path be switched to show what each one does, and gives the lightbox of the site the walk its arrows already promised. The panel LEDs move their pulse off box-shadow to transform and opacity.
Core changes:
- Presentation data (modules/presentation/index.php, modules/presentation/lang/*.php):
- The hero heading is one constant that marks its accent word as word; the lead text fills four lines
- The control window lists the active modules forum, voting, contact, rss and changelog, five trace rows each
- The guard and the request path carry a Yes/No switch in their head instead of a state pill
- Both cache pools ship whatever the site runs: a miss stores once, then the visits read the stored markup
- The monitoring row of the pulse section is gone with its data, its six constants and the xs ring size
- MySQL and MariaDB share one technology card, JavaScript gets its own; version, query and DNA figures read cleanly
- Constants added: _PRES_HEAD, _PRES_GD_MODEOFF, _PRES_AR_HITS, _PRES_AR_PARSES, _PRES_AR_PARSING, _PRES_TECH_JS
- Removed: _PRES_HEAD_A/B/C, _PRES_PU_MON*, _PRES_PU_CORES, _PRES_PU_UPTIME, _PRES_GD_ON, _PRES_AR_ON, _PRES_AR_OFF
- Presentation templates and styles (public/templates/lite/partials/presentation-*.html, fragments, presentation.css):
- The section head renders the switch of the theme (.sl-radio-switch) when its data carries one
- The house of the guard has taller windows, a raised door lit like the windows, and a straight chimney
- Its smoke reads in the dark scheme; a threat that passes an unguarded gate lights a window red
- The rail fits twelve items from 1440 px, the lead fills its column, the router is a card that holds its words
- Labels that #content broke at any letter wrap between words or hyphenate long words only
- A dusk veil lies over the photos in the dark scheme; the voices show "since" on one line without a repeated note
- Presentation script (public/plugins/presentation/presentation.js):
- setToggle() writes a switch onto its scene; the guard lets threats in, the path swaps its pool and its pace
- The request path counts the visits read from the cache against those parsed again, and names the parser
- setBlockLinks() lights the wire and the page slot of the block under the pointer
- setCount() counts the figures of the facts and the statistics up once; still motion leaves them as written
- Every catch fills the next trap of the quarantine and flashes its count
- Lightbox (public/plugins/system/slaed.js, presentation-archive.html, docs/WINDOW.md):
- The view window walks the image links of the nearest data-sl-shot-group by its arrows and the arrow keys
- A link outside any group shows alone, its arrows hidden; the brand archive is such a group
- Lite theme tokens (public/templates/lite/assets/css/base.css, theme.css, tools/ui-contract.php, tools/ui-contrast.json):
- --sl-success is #248234, the panel green one tone darker to keep 4.5:1 on #f8f9fb; its subtle and muted follow
- New tokens --sl-switch-on-bg, --sl-switch-on-text (mint #72e6b0 by night) and --sl-pres-photo-bg
- The contrast record is the one the screenshot rig measured after the change
- Panel LEDs (public/templates/admin/assets/css/base.css, theme.css, public/demo/admin, tools/frame-cost.mjs):
- A lit LED pulses a still ring disc by transform and opacity; the live icon keeps its shadow as sl-live-pulse
- --sl-led-ring names the width of the ring; the frame rig crops a ring outside its box with a padding
Benefits:
- The front page reads in both schemes and on a phone without a word cut or a part past its frame
- The two switches show what the guard and the cache do instead of stating it
- One green across panel and site, and an LED pulse that costs no repaint
Technical notes:
- No schema, URL or API change; the switches change the scene only, never a setting of the site
- config/layout.php is the layout of the stand and stays out of the commit
The rendered page used to be repaired afterwards by a regex normalizer that could not fix the indentation of nested fragments; now the templates of both themes are canonical HTML and the engine keeps that form on the way out, so the page is correct as rendered, with no pass over it.
Core changes:
- Template engine (core/classes/template.php):
- A line holding only control tags or a note leaves nothing in the output, its indent and line end included
- An insert alone on its line (raw echo, include, freeblock, block) takes the indent of that line on every line
- getPad() keeps pre, textarea, script and a tag whose attribute spans lines byte for byte
- An empty insert leaves no line; getPad() is public static so the core can indent late output
- The line end PHP swallows after a closing tag is given back, except after the tag that ends the file
- The hash of an inline snippet carries the engine time, so a changed engine compiles it again
- Companion asset tags are joined without a second line end
- Page output (core/system.php, core/classes/mail.php):
- getOutputHtml() is removed: the page, the page cache and the queued mail carry what the engine rendered
- The head fragments and the script and stylesheet lists are joined without an extra line end
- getTimedHtml() indents the debug block it substitutes after the render
- Templates (public/templates/admin, public/templates/lite, modules/presentation/sections):
- Every opening tag stands on one line, a conditional attribute carries its own leading space
- Block elements stand on their own lines with a two-space indent; the root of a fragment starts at column 0
- A multi-line block insert stands alone on its line inside its container
- The inline link and span fragments are one line with no space at their edges
- Whitespace between inline items is kept where it renders; parser fragments stay byte-exact for CommonMark
- Tests and tooling (tests/, tools/, composer.json, package.json, docs/):
- TemplateOutputTest holds the three output rules; source and markup assertions accept canonical line breaks
- The clean installation, the package installation and the data update form the group release: composer test:fast leaves it out, composer test:release runs it alone
- A class that memoizes its probe keeps the raw answer before any assertion; NodeRouteTest is cut into NodeRouteListTest, NodeRouteFormTest and NodeRouteSeoTest over the base NodeRouteCase
- The unused code audits leave PHPUnit for tools/unused.php, run by hand
- ui:gates and the commit hook rely on ThemeContractTest for the counts instead of a second full audit
- Presentation (public/plugins/presentation/presentation.js, presentation.css, tools/frame-cost.mjs):
- A part off screen is paused by CSS and its SMIL by script, and the pulse strip reads no layout per frame
- The frame cost rig measures the hero as a page of its own
Benefits:
- The source of a page reads as the templates are written, with no post-processing per request
- A multi-line attribute value and a hidden field reach the browser unchanged
- The fast suite leaves the slow release half out
Technical notes:
- Measured on the stand: getPad() costs about 3.5 ms on the heaviest pages (about 0.4 % of their response), about 1 ms on the news list
- Whitespace inside inline links and spans at their edges is gone; the screenshot rig moved no pixel on the pages it guards
- No schema, URL or API change
The screensaver redrew its layers on every requestAnimationFrame although its state moves in steps of 1/60 s, cleared layers nothing had drawn on, and repainted thousands of dots each frame in the acts that stand still. Each layer of a round stays equal to the original on the frozen clock, so the scene looks as it did and costs less.
Core changes:
- The frame loop (public/templates/admin/assets/js/phoenix.js):
- the frame callback of setPhxPlay() draws after a step, while leaving and after fresh
- setFresh() marks a resize, which empties the canvases, and a scroll, which moves the logo under its glint
- a frame between two steps showed an empty ground on 9 of 28 resizes without it, and on none with it
- The layers (public/templates/admin/assets/js/phoenix.js):
- setRain() and setFrame() clear the rain and the top layer only after a frame that drew on them
- The still acts (public/templates/admin/assets/js/phoenix.js):
- setStill() draws hold, big and small once into a canvas of their own and copies it pixel for pixel
- the key is the act and the layer in pixels and in CSS pixels, since a zoom can keep the first, and setBuild() forgets it
- the canvas stays in software by willReadFrequently: an accelerated source left 8 of 273 layers off the original
Benefits:
- Script of the acts in pairs: hold 563 to 7 ms/s, small 141 to 8, big 587 to 194
- A round on average, pairs: renderer 90 to 80 %, GPU 115 to 107 %, script 430 to 327 ms/s, 77 to 86 frames a second
- The quiet ground alone, CSS animations off: renderer 11 to 7 %, GPU 17 to 9 %, script 15 to 9 ms/s
Technical notes:
- No schema, URL or API change; 273 of 273 layers of one round equal the original, tools/frame-cost.mjs --phoenix
- With the CSS animations on, the idle home keeps renderer 51 % and GPU about 85 %: the animations of the page hold the frames
- The canvas of a still act stays allocated from the first such act on, about 5 MB at 1440x900