Журнал изменений

Официальный сайт SLAED CMS

Журнал изменений

Фильтр и поиск

Всего: 1053 Доступных коммитов | Отфильтровано: 1053 Коммиты | Страница: 1 / 106
Сегодня (27.07.2026)
Fix: sync the editor into its textarea and plan the comment and mail rework
Автор: Eduard Laas | Дата: 15:48 27.07.2026

Posting a comment from the frontend was impossible: the editor only copied its content into the textarea on a native form submit, which htmx never triggers, so an empty text reached the server. Investigating it produced measurements that justify two follow-up plans.

Core changes:

  1. Editor value synchronisation (plugins/editors/toastui/driver.php):
  2. Bind the copy to editor events instead of the form submit event

    • change and blur keep the textarea current at all times
    • the submit listener stays for the plain, non-htmx path
  3. Frontend forms post the written text again; the admin panel is unaffected because it always submitted natively

  4. Comment subsystem plan (docs/COMMENTS-REDESIGN-2026.md):
  5. Record the measured state: a 252-line render function, a 51 KB response for a single add, filesort on the list query, no index behind the flood check, duplicated validation and an unreachable admin branch

  6. Describe the target: one Comments class owning SQL, validation, permissions and state changes, additive columns only, threads, soft delete, idempotent writes and per-fragment responses

  7. Split the work into five independently shippable stages with explicit verification, including migration and HTTP route checks

  8. Mail queue plan (docs/MAIL-QUEUE-2026.md):
  9. Record the measured state: 26.6 s spent inside one comment request, a single send point that discards the result of mail(), a private newsletter queue built on a comma separated column, and a throughput of four messages per hour

  10. Describe the target: one queue table, one MailQueue class behind the existing addMail() signature, an atomic claim, backoff and retry, and a scheduler drain

  11. Keep all 26 existing call sites untouched

Benefits:

  • Comments can be posted again from every frontend module
  • The two reworks start from measured facts instead of assumptions
  • The mail queue removes the blocking send for every feature, not only comments

Technical notes:

  • The editor change is client side only; no PHP behaviour changes
  • Both documents are plans; no schema or runtime change is included here
  • The mail queue is a prerequisite for stage 3 of the comment plan
Fix: input filter defects, captcha regions and honest filter tests
Автор: Eduard Laas | Дата: 14:24 27.07.2026

Rewriting the input filter tests against the shipped functions instead of local replicas exposed a fatal in filterFields() and two silent behaviour drifts. The captcha helper also poisoned every cacheable build unconditionally, even when captcha was switched off and returned an empty string.

Core changes:

  1. Scalar input reaching filterFields() (core/security.php):
  2. Branch on is_array() instead of the truthiness helper isArray()

    • A non-empty string passed the old check and hit implode(), raising a
TypeError that killed the request
  • getVar(..., 'field') feeds exactly that, so account, help, order, forum,
news and pages could fatal on a scalar field value
  • Filter a scalar through the text filter instead of returning an empty string
  • Captcha as a dynamic region (core/system.php, core/user.php, modules/*):
  • Accept register, comment and contact next to login in checkDynamicMark()
  • Read the captcha through getPageCaptcha() on 17 frontend call sites

    • getCaptcha() marks the build as uncacheable before it even asks whether
captcha is active, so a form dropped the page cache in every configuration
  • The admin login keeps the direct call: the admin area is never cached
  1. Comment length guard (core/user.php):
  2. Compare the longest word instead of the last one

    • The loop overwrote its own result, so only the final word was measured
  3. Count characters with mb_strlen() so cyrillic is not charged twice
  4. Filter tests without replicas (tests/Unit/InputFilterTest.php, tests/Support/contract_probe.php):

  5. Drive the real functions through a new filters probe scenario
  6. Correct three assertions that described the replicas, not the code

    • filterVar() returns an empty array, not an empty string, for a bad list
    • filterHtml() loses a lone backslash to stripslashes before encoding
    • filterHtml() returns an empty string, never null
  7. Extend the marker contract test with the widened captcha whitelist
  8. Naming and memory (core/security.php, core/classes/parser.php, admin/index.php):

  9. Rename the global filterUrl() to filterWebUrl()

    • It normalises a submitted address and did not relate to the parser link
policy Parser::filterUrl(), which refuses dangerous schemes
  • Build the getVar() filter table once per request instead of per call
  • Hash the image memo key so an inline data URI is not held twice

Benefits:

  • A scalar field value no longer fatals the request
  • Pages carrying a form stay cacheable
  • The filter tests fail when the filters change, which they did not before

Technical notes:

  • No schema, route or storage changes
  • checkDynamicMark() still rejects adminlogin, empty and unknown parameters
  • InputFilterTest shrank from 31 replica-based cases to 8 contract cases
Docs: allow single-letter counters and refresh the test inventory
Автор: Eduard Laas | Дата: 12:08 27.07.2026

The variable naming rule demanded at least two characters, which the code base contradicted in about eighty places, including the filter table in getVar(). A rule that is never followed stops guiding anything, so the exception is written down instead.

Core changes:

  1. Naming rule (CONTRIBUTING.md):
  2. Allow a single letter where the name carries no domain meaning

    • Loop counters and parameters of single-expression closures
    • Any variable holding a domain value keeps the 2-8 character rule
  3. Test inventory (docs/TESTS.md):
  4. List the unit tests that were added since the file was last updated
  5. Note that the contract tests drive production code through the CLI probe
  6. Completed plans (docs/FRONTEND-DIAL.md, docs/PROFILE-BACKLOG.md):
  7. Remove the plan documents whose work is finished

Benefits:

  • The written rules match the code that reviewers actually read
  • Newcomers find the current test layout instead of an outdated list

Technical notes:

  • Documentation only, no runtime change
Fix: admin forms read their array fields through the correct getVar keys
Автор: Eduard Laas | Дата: 12:08 27.07.2026

Five admin handlers addressed a name[] form field with a scalar key or passed an array default into a scalar filter. The result ranged from a log warning to settings that were silently discarded on every save.

Core changes:

  1. Language editor (admin/modules/lang.php):
  2. Read constants and translations by index through nested keys

    • Form fields carry an explicit index and a hidden row count
    • The saved file no longer depends on browser field ordering
  3. Stop overwriting the posted translations with the file contents

    • The loop variable holding the file body was shadowing the input
  4. Add getLangConstants() and use it in both the editor and the save handler

    • It understands single and double quoted values, so an apostrophe in a
translation survives instead of truncating the parsed value
  • Escaping on write covers the backslash as well, so a trailing backslash
can no longer break the generated file
  • Remove dead merge and unset code left from an earlier revision
  • RSS feed list (modules/rss/admin/index.php):
  • Read the 50 rows as untyped arrays so their indexes stay aligned

    • The previous scalar key produced false and wiped every feed to zeros
  • Strip the pipe separator and line breaks from the stored values
  • Drop the required attribute from the hidden feed rows

    • With 49 empty required rows the browser refused to submit the form at all
  • Comments, sitemap and account (admin/modules/, modules//admin):
  • Address the checkbox and select arrays as id[], mod[] and warn[]

    • Single-row approve and delete no longer log an array conversion
    • The sitemap module selection is stored instead of falling back to zero
    • User warnings survive a redisplay of the account form after an error

Benefits:

  • Language and feed editing actually persist what the admin entered
  • Admin actions stop writing warnings into the PHP log
  • Existing values keep their meaning: nothing is rewritten on read

Technical notes:

  • No schema or config format changes
  • The language editor normalises quoting on first save; values stay identical
  • Verified live in the admin UI with backups restored afterwards
Feature: nested form keys in getVar() with a contract test suite
Автор: Eduard Laas | Дата: 12:08 27.07.2026

Form fields like lng[ru][] could not be read at all: the key syntax stopped at one level, and a mismatched key silently produced false from filter_input(), which then reached the scalar filters. Several admin forms were broken by exactly that, so the helper is extended instead of worked around per module.

Core changes:

  1. Key syntax (core/security.php):
  2. Parse a key as a path so leading segments walk nested form fields

    • lng[ru][] returns the whole branch, lng[ru][_A] one element
    • deep[a][b][c] and row[2][id] work the same way
    • The existing forms key, key[] and key[n] behave exactly as before
  3. Walk the path once before the branches instead of per branch
  4. Missing and mismatched values (core/security.php):
  5. Treat an array reached through a scalar key as missing

    • A wrong form name now yields the default instead of false
  6. Return an array default before any scalar filter runs

    • filterNum() no longer receives an array and logs a conversion warning
  7. Contract tests (tests/Unit/InputVarContractTest.php):
  8. Exercise the real helper through the CLI probe, not a replica

    • Typed array keys still drop empty values and reindex
    • Untyped array keys return the payload untouched for index alignment
    • Nested paths are covered for post, get and req, including a missing branch

Benefits:

  • Forms with nested fields are readable without bypassing input filtering
  • A form and handler name mismatch fails visibly instead of silently
  • The helper keeps a single source of truth for input access

Technical notes:

  • Purely additive for existing callers: the typed array branch is unchanged
  • The scalar branch reads filter_input(), which has no payload in CLI, so it is verified through real HTTP rather than the probe

Fix: restore comment submission on the frontend
Автор: Eduard Laas | Дата: 12:07 27.07.2026

Posting a comment failed for every visitor: the htmx button carried no CSRF token, so the ajax dispatcher answered with a token mismatch, and the handler read its identifiers from POST while the form sends them in the query string.

Core changes:

  1. Comment form (core/user.php):
  2. Append getPageToken() to the addComment request URL

    • getPageToken() is used instead of getSiteToken() so a cacheable build
receives a signed dynamic-region marker rather than a frozen token
  1. Comment handler (core/user.php):
  2. Read id, cid and mod through getVar('req', ...) like updatePost() does

    • The htmx button sends them as query parameters, not as form fields
  3. Guard the flood check against a first-time visitor

    • strtotime() no longer receives null when the IP has no previous comment

Benefits:

  • Guests and members can post comments again
  • The deprecation notice disappears from the PHP log on every first comment

Technical notes:

  • No schema or route changes; only input reading and the request URL differ
  • Verified through a real guest submission followed by admin moderation
Fix: whitelist inline data URIs in the renderer and refuse them in links
Автор: Eduard Laas | Дата: 12:06 27.07.2026

The parser passed any data: URI through untouched, so a guest without upload rights had an unlimited byte channel into the database and a clickable data:text/html href in every comment. Enforcement now happens at render time, which also covers rows that are already stored.

Core changes:

  1. Image sources (core/classes/parser.php):
  2. Accept a data: source only as a whitelisted base64 raster image

    • Allowed types are png, jpeg, jpg, gif and webp, matched case-insensitively
    • The decoded payload must not exceed Parser::EMBEDMAX (65536 bytes)
    • Anything else returns null and renders the existing image placeholder
  3. Cap the encoded length before the regex or base64_decode() runs

    • A multi-megabyte payload is refused without allocating a decoded buffer
  4. Force the derived file name to 'image' for data: sources

    • A rejected payload no longer leaks into the alt and title attributes
  5. Cover all three ingestion paths in both safe and unsafe mode

    • Markdown images, BB [img] and persisted raw <img src> repairs
  6. Link contexts (core/classes/parser.php):
  7. Refuse data: in filterUrl() regardless of the safe flag

    • Untrusted guest content is rendered unsafe, where the policy was skipped
    • Trusted content keeps ftp:, tel: and other hrefs as before
  8. Hand data: image sources straight to the whitelist

    • Safe mode previously rewrote every legitimate embed to src="#"
  9. Shared limit and meta images (plugins/editors/toastui/driver.php, core/system.php):

  10. Publish Parser::EMBEDMAX to the editor as its client-side embedmax

    • The client limit is UX only and can no longer drift from the server
  11. Drop data: sources in getImgText() so og:image points at a real resource

Benefits:

  • Upload permissions can no longer be bypassed through an inline embed
  • Oversized rows, heavy pages and bloated backups are prevented at the source
  • A data: URI can never appear in an href, only as escaped text

Technical notes:

  • Existing rows are covered because the rule runs at render time
  • Legitimate embeds above 64 KiB stop rendering and show the placeholder
  • ParserFixturesTest covers both modes, hostile MIME tricks, whitespace variants, the exact size limit and a multi-megabyte payload

Fix: fail-safe cache, log and counter writes with cookie-free public responses
Автор: Eduard Laas | Дата: 12:06 27.07.2026

Partial writes were reported as success, so a truncated cache body or a torn counter record could survive as valid data. Public responses could also carry a Set-Cookie header into a shared proxy, letting one visitor's state reach the next one.

Core changes:

  1. Atomic cache body write (core/classes/cache.php):
  2. Treat a short write as a failure instead of trusting a non-false return

    • The temp file is removed without the error-suppression operator
    • Only a complete body ever reaches the target path through rename()
  3. Drop every pending Set-Cookie when emitting Cache-Control: public

    • A shared proxy or CDN can no longer store a visitor-bound cookie
    • Sessions and CSRF are unaffected: token and captcha pages are no-store
  4. Append safety for counters and logs (core/system.php):
  5. Rework addFile() append mode around fopen/flock/fwrite

    • The write position is recorded before writing
    • An incomplete append is rolled back with ftruncate()
    • A short write is reported as a failure to the caller
  6. Contract test coverage (tests/Support/contract_probe.php, tests/Unit):
  7. Extend the CLI probe with a scratch COUNTER_DIR and new scenarios

    • stathit drives the real updateStatsTrack() per process
    • appendfail proves a blocked append is reported, not silently lost
    • geoip measures the streaming reader against the real corpus
  8. Add StatsContractTest and GeoipReaderTest, extend PageCacheContractTest
  9. Add the getvar scenario used by the input contract tests that follow

Benefits:

  • A truncated write can no longer be served as a valid cached page
  • Statistics stay exact under parallel hits instead of losing records
  • Public caching becomes safe for shared proxies

Technical notes:

  • No storage format changes; counters and cache files keep their layout
  • Backward compatible: only failure paths and response headers changed
  • Documentation updated in docs/PERFORMANCE.md; the remediation plan is done and its document removed

Эта неделя (24.07.2026)
Style: shorten new statistics and cache-contract comments to line limit
Автор: Eduard Laas | Дата: 12:55 24.07.2026

Trims the function comments added by the performance remediation work to the 180-character line limit while keeping their meaning; no code or behavior changes.

Core changes:

  1. Comment wording (core/system.php, core/classes/cache.php):
  2. updateStatsCookie, updateStatsTrack, marker contract, and page-cache contract comments rewritten concisely within one line each

  3. Test probe (tests/Support/contract_probe.php):
  4. header comment shortened to the same limit

Technical notes:

  • legacy over-length lines elsewhere are untouched, out of scope
Fix: bounded page-cache identity and fail-safe exact statistics
Автор: Eduard Laas | Дата: 12:46 24.07.2026

Closes the correctness and availability gaps from the 2026 performance remediation plan: the public page cache can no longer be amplified by arbitrary query input or foreign hosts, and the exact visitor statistics survive IO failures without losing or double-counting entities.

Core changes:

  1. Bounded page-cache identity (core/system.php, core/classes/cache.php):
  2. Cache::getQueryVars() validates the request query against a per-route key/value regex contract, dropping tracking keys centrally

    • unknown, duplicate, or malformed keys render live and create no entry
    • replaces the now-dead Cache::filterCacheUrl()
  3. getCacheRouteVars() adds the canonical homeurl host gate and feeds getPageHash(), which builds the pc2-versioned identity from validated values so num=1, encodings, and foreign Host values cannot fork entries

  4. Dynamic-region marker contract (core/system.php):
  5. checkDynamicMark() allowlists token scopes ajax/account/scheduler, captcha action login, and positive voting IDs

  6. invalid emitters poison the build, log, and fall back to live rendering
  7. the contract is revalidated at serve time so forged markers stay inert
  8. Fail-safe exact statistics (core/system.php):
  9. v2 stats cookie carries only session metrics and country cache; no client state can suppress an exact count

  10. ips.log/user.log sets are the source of truth with hosts/users counters derived from set size, so any single write failure self-heals

  11. statistic.log is replaced atomically via temp file and rename under a stable statistic.lock; partial writes can never trigger a day reset

  12. rollover verifies full day lines, rolls back short appends, aborts on any archive/rename/unlink failure before destroying state, and names archives from the data date

  13. checkUniqueIp()/check_user() consolidated into updateStatsTrack()
  14. Regression coverage (tests/):
  15. PageCacheContractTest exercises the production functions directly and through contract_probe.php, which boots the real core per scenario with LOGS_DIR redirected to scratch so failure paths stay assertable

Benefits:

  • guest requests cannot grow the cache key space or host namespaces
  • exact unique hosts/users stay correct across injected IO failures
  • rotation failures preserve data and retry instead of deleting sources

Technical notes:

  • pc2 identity version makes pre-contract cache files unreachable; normal GC removes them without a purge

  • v1 stats cookies are discarded by design (disposable analytics state)
  • OPcache/cron deployment (Batch 4) remains a manual environment step; docs/PERFORMANCE-REMEDIATION-2026.md stays OPEN with recorded results

Всего: 1053 на 106 страницах по 10 на каждой странице

1 2 3 4 5 6 7 8 9 10 106
Хотите опробовать SLAED CMS в действии?
Идеи и предложения
Обратная связь
Подтверждение

Поделиться
QR-код