Официальный сайт SLAED CMS
Журнал изменений
The Node plan kept the configuration of the stand out of its commits. On request of the project owner it now ships as it runs on the stand: the Node types with their settings, the field definitions, the rating and upload rules of these types and the protective files of their upload directories.
Core changes:
- Node types (config/node.php):
- The types of the stand with their full settings replace the empty shipped list
- Fields, ratings and uploads (config/fields.php, config/ratings.php, config/uploads.php):
- The field definitions of the stand replace the demo definitions
- Rating rules node.<name> and upload rules of the stand types are added
- Upload directories (uploads/content, uploads/docs, uploads/jokes, uploads/media):
- .htaccess and index.html guard the directories of the stand types
Benefits:
- The repository reflects the configuration the stand runs with
Technical notes:
- config/security.php is not part of this commit; the shipped secret stays empty
A clean installation reads config/node.php from the release: types listed there have no rows in _node_types until the installer or the panel creates them, and NodeProfileTest checks a clean installation against this configuration
The second audit of stages S00-S19.8 split its findings into eight fix windows. This commit carries all of them: every finding was re-checked in code before its fix, the forks the contracts left open were decided by the user and written into the owning documents, and the plan of docs/node is complete.
Core changes:
- Public form security - S20.1 (modules/node/index.php, core/classes/node/query.php, core/classes/node/service.php):
A guest passes the comment captcha on every POST before any file is read; uploads of the public form need checkEditorUploadAccess() and respect the role max and maxfiles, inactive and link roles take no file
A new per-IP window limits.send (60 seconds) answers NodeException::LIMITED with 429, the moderator of the type is exempt; a closed category answers 404 through NodeQuery::checkNodeCategory()
- Search titles of Node are decoded once and escaped once
- Installer and 6.3 update - S20.2 (setup/index.php, core/classes/pdo.php, setup/lang, UPGRADING.md):
The form never prints the database password, an empty field keeps the stored one, a refusal keeps config/setup.unlock
- The table prefix must match [A-Za-z0-9_]{1,32} and the admin file name goes through filterVar()
setUpdateModules() keeps the switches a 6.2 site stored in _modules over the shipped config/modules.php; checkUpdateBase() refuses a clean install over existing tables of the prefix and an update without _users and _admins
- Integrity of comments, points and categories - S20.3 (core/classes/comment.php, core/classes/node/service.php, admin/modules/categories.php):
- deleteNode() refuses with STORAGE when Point cannot confirm the compensation of an award
A premoderated comment of a Node material locks the material; NodeService::checkTypeRegistry() guards the category screen, which now answers _ERROR_UP instead of a raw SQL failure
- Document tree - S20.4 (modules/node/index.php, templates/lite, setup/sql):
A type with features.tree shows the tree of its documents with the current node, previous and next links and a paged remainder, read in batches of NodeQuery::TREEPART
- _nodes gets the index tree (tid, status, id) in table.sql and table_update6_3.sql
- The moderate action and small rendering fixes - S20.5 (core/classes/node/service.php, core/classes/comment.php, core/classes/node/view.php):
Approving a material, a comment or an asset report writes a Point event moderate with the aid of the moderator; comment event scopes live in one place
NodeView renders only card for a NodeTarget, the unused fragment node/search.html is removed, the sitemap reads Node in batches of limits.syncbatch, related cards no longer count views
- Display modes - S20.6 (modules/node/index.php, templates/lite, tools/ui-contract.php):
- docs, faq, files and media get their own card and view templates, media also its list
Cards and og:image prefer the poster role, the list card carries download, every view gets download_label and hits_label
- Code rules and consolidation - S20.7 (core/system.php, modules/node, core/classes, admin/modules, setup/index.php, tools/node-profile.php):
getNodeReader(), getNodeWriter() and addNodeMail() live in the core next to getNodeHandler(); support and admin mail go through addNodeMail()
render_blocks() becomes setBlockView(int $bid); arrow functions and parameters of the plan code are typed, redundant casts are removed
- Plan documents - S20.8 (docs/node):
Status lines, the test structure of 13 and 02, the NodeContext and target signatures of 05, the cover label of 06 and the module tree of 04 follow the code
Benefits:
- Every finding of the second audit is fixed or recorded with its reason in docs/node/PROGRESS.md
- Promised but missing features of the plan - the document tree, moderate and display modes - are shipped
Technical notes:
- Schema: KEY tree (tid, status, id) on _nodes in table.sql and table_update6_3.sql
- Config: config/node.php gets limits.send = 60; NodeException gains LIMITED = 6
API: render_blocks() is replaced by setBlockView(int $bid); getConst() resolves only names with a leading underscore; checkUpdateBase() takes bool $fresh; NodeService::checkTypeRegistry() is new; getNodeReader(), getNodeWriter() and addNodeMail() are core functions
Breaking: the installer refuses a prefix outside [A-Za-z0-9_]{1,32}, so a 6.2 site with such a prefix cannot pass the update; the theme fragment fragments/node/search.html is gone; NodeView accepts only card for targets
The audit of stages S00-S18 split its findings into eight fix windows. This commit carries all of them: every finding was re-checked in code before its fix, the decisions the contracts left open were taken by the user and written into the owning documents, and the plan of docs/node ends here.
Core changes:
- Input and output security - S19.1 (setup/index.php, core/classes/field.php, core/classes/node/*.php):
An installed site keeps setup.php shut: any op without the owner key config/setup.unlock shows only the lock screen, a clean run removes the key, and the key is ignored by git
Node titles in search are escaped, fields are saved only from a full POST, a Node delete checks its type, favorites no longer farm points, and Node categories are written through NodeService::addNodeCategory()
- The 6.3 update - S19.2 (setup/index.php, setup/sql/table_update6_3.sql, UPGRADING.md):
A 6.2 configuration is converted inside the update branch, the old files are kept as snapshots until a clean finish, the site is closed by removing config/local.php, and a DDL error stops the branch
- Newsletter recipients move into the mail queue, the MariaDB floor is checked in preflight
- Data integrity - S19.3 (core/classes/node/service.php, core/classes/point.php, core/classes/comment.php):
update6_3 raises AUTO_INCREMENT of _nodes above every id of the nine removed tables, so old addresses cannot land on new materials
Leftover comments and favorites of removed modules are listed and removed through op=remains; deleteNode() removes the comments of a material inside its transaction
- Cache, locks and response codes - S19.4 (core/classes/cache.php, core/classes/comment.php, core/classes/filemanager.php):
Comment writes take the write guard before BEGIN and bump the epoch after COMMIT; account, shop and voting owners follow the same protocol
- The configuration is built from its sources while an unfinished journal exists
- Rendering - S19.5 (core/classes/parser.php, core/classes/node/view.php, setup/sql/table.sql):
Trusted rendering runs only inside tags: filterDoc()/filterContent() take bool $trust, Node views and Node search render with safe and trust
- The first publication of a comment is stored in _comment.shown; plain text drops script and style
- Performance and Feed - S19.6 (core/classes/node/query.php, core/classes/feed.php, tools/node-profile.php):
- Ordered reads split into pinned and unpinned branches over the new _nodes indexes title, updated and expires
- The point journal counts at most 5000 rows, the account and RSS feed page is cached for 900 seconds
- Lost functions of the nine removed modules - S19.7 (core/classes/node/query.php, core/user.php, templates/lite):
NodeQuery::getNodeAuthorStat() feeds the author profile per type, NodeQuery::getNodeCategoryCount() the category screen; theme links to old ids are gone; maildrain runs at priority 8 and update6_3 repairs it
- Code rules - S19.8 (admin/index.php, core/.php, core/classes/node/.php, modules/*, tests/):
The starter news of a clean installation reads _NODE_START_TITLE, _NODE_START_INTRO and _NODE_START_BODY of admin/lang in six languages
Lines over 180 characters and comments inside function bodies added by the plan are gone; 97 redundant (string)/(bool) casts over PDO rows of NOT NULL columns are removed
getUserInfo() returns array, the directory constants of core/system.php respect earlier definitions, filterFields() and four unused core functions are removed, StatsContractTest follows the site time zone, and the presentation demo query no longer names the comment table
Benefits:
- Every audit finding of S00-S18 is fixed or recorded with its reason in docs/node/PROGRESS.md
- The full phpunit suite runs without failures, including the long-standing CommentIsolationTest
Technical notes:
- Schema: _comment.shown, the _nodes indexes title, updated and expires; both in table.sql and update6_3
API: NodeExtension::updateNodeAction() takes int $uid; Parser filterDoc()/filterContent() take bool $trust; NodeQuery gains getNodeAuthorStat() and getNodeCategoryCount(); getUserInfo(): array; filterText() accepts only a string; getTplRefreshTimeSelect(), getAsyncPager(), getTranslit(), url_types() and filterFields() are removed
- Breaking for callers of the removed functions and of filterText() with an array; no caller remains in the tree
Verification: php -l, php-cs-fixer check, phpstan, full phpunit 1501 tests with 0 failures, npm run ui:gates 234 tests
The audit of stages S00-S18 read the implementation in seven slices against the Node contracts and the project rules. Its findings enter the plan as stage S19, split into eight windows S19.1-S19.8, so each fix follows the same one-window-one-stage protocol and closes with its own verification.
Core changes:
- Roadmap cards (docs/node/14-roadmap.md):
An S19 introduction: where the findings come from, lines pinned to 70224f65, each finding re-checked in code before a fix, and two decisions asked of the user before code
Eight cards with dependency, reading list, findings with file:line and function, files and acceptance
- S19.1 input and output security: Node title XSS in search, field save over GET or a truncated POST,
Node delete without the type check, favorite point farming, Node categories outside the service
- S19.2 the 6.3 update: the site stays open because config/local.php outlives close, DDL errors do not stop
the branch, newsletter recipients, the MariaDB 10.5.2 floor, the 6.2 Pro configuration format
- S19.3 data integrity, S19.4 cache and locks, S19.5 rendering, S19.6 read performance, S19.7 leftovers
of the nine removed modules, S19.8 code rules
- Progress (docs/node/PROGRESS.md):
- Eight not-started rows in the stage table; the protocol ends the plan after S19.8
The handoff block names S19.1 as the next window, records the check results at audit time and drops the stale note that S17-S18 were uncommitted
Benefits:
- Every audit finding has an owner window, a verification and a place in the plan instead of the chat
- Open product decisions are marked as such and cannot be settled silently by an executor
Technical notes:
- Documentation only; no code, schema or configuration changes
Stages S17 and S18 close the Node plan of docs/node: the ten shipped profiles become the only source of the starting types, a clean installation creates and switches them on with the first administrator, and the release passes its final acceptance - a real 6.2 site updated over HTTP, MySQL 8.0, and a profile of 100000 materials.
Core changes:
- Profiles and installation (modules/node/profiles/, setup/index.php, admin/index.php, modules/node/admin/index.php):
Ten profiles in the export format: the nine replacements and docs, canonical settings and fields
- empty uploads and rating, so a created type takes the upload rule and a new rating rule of the site
setup new leaves the mark node => new; addNodeProfiles() imports and switches on every profile in the context of the first main administrator, writes the welcome news and removes the mark
- a profile that fails is named on the next page and logged with its step; the others are created
- The type screen offers the profiles, and a type made from a profile keeps its extension settings
- Twelve NODE* labels of roles and fields in six languages, _NODE_SETUP and _NODE_PROFILE
- The 6.3 update (setup/index.php):
config/modules.php is reconciled the way the modules screen does it: records of modules gone from the tree are dropped, node gets the record of a clean installation, the stale img key is no longer written
The upload rules of the nine removed modules leave config/uploads.php unless a Node type of that name exists; they refused a Node type of the same name with the code name
- config/db.php is read only when it exists, since the release ships none; saving creates it
- Reads and MySQL 8 (core/classes/node/query.php, modules/node/admin/index.php, seven files with rank):
NodeQuery::setNodeSets(false) switches off the extra fields and the category, relation and resource sets of a list; the administrative list of materials uses it and costs three statements
- The columns _users.rank and _groups.rank are quoted in 13 statements; rank is reserved on MySQL 8.0.2+
- setComShow() reads the access of a guest without a warning
- Tools and tests (tools/node-profile.php, tests/Support/install_probe.php, tests/Unit/NodeProfileTest.php):
tools/node-profile.php builds a disposable database of 100000 materials in the ten types, measures every route budget, p50 and p95 and the plans of the main statements, and fails on an overrun or a full scan
install_probe installs the tracked release without config/db.php over two php -S servers; its update mode loads a dump of a real site with the configuration of a revision and walks the update to the end
- SLAED_PROBE_DB=host|user|password moves the probe to another server; NodeProfileTest passes on MySQL 8.0
- seo-audit, ui-shots and upload-route-check know the Node routes of the stand
Benefits:
- The release installs with its content sections ready and updates a 6.2 site without guessing any data
- The route budgets of docs/node/11 hold on 100000 materials with index plans, the slowest p95 is 51 ms
- The same release runs on MariaDB 10.2.1+ and MySQL 8.0.16+
Technical notes:
The update creates no Node type and touches no table of the removed modules; a type name stays refused while categories or user files of the old module exist (NOD-200)
- The download and the report of a resource cost four statements: the writer authorizes the resource again
- UPGRADING.md describes the 6.2 to 6.3 update, Node and the nginx rule; docs/VERSIONS.md has the release entry
- Breaking: an updated site loses the config/modules.php records and the upload rules of the nine removed modules
Stages S14 to S16 of docs/node connect the registered Node types to the rest of the system: comments and the private requests of the support extension, the external materials of the sync extension with their scheduler job, and every remaining shared owner - the rating of node.<name>, favorites, polls, search, RSS, the sitemap, the file block and the figures of the showcase. The shared code names no type: each integration is switched on by the settings of the type.
Core changes:
- Comments and support (core/classes/comment.php, core/classes/node/ext/support.php, modules/node/):
Comment resolves a Node target through NodeQuery and keeps comnum, the extension reaction and the points of it
- the lists outside the target page leave out comments of materials the viewer may not read
NodeSupport is the ticket system: owner scope, waiting side, priority, assignment, mail through the queue
- the owner closes and reopens, the moderator works the card and the queue in the administration
- External materials (core/classes/node/ext/sync.php, core/classes/feed.php, config/scheduler.php, setup/index.php):
NodeSync keeps one source per material and writes the body only on a changed answer, under a version condition
- the nodesync job checks due sources, a failure waits with a growing delay
- Feed::getFeedUrl() is public so the stored address is exactly the one Feed requests
- Rating and favorites (core/system.php, core/user.php, core/classes/node/service.php, admin/modules/ratings.php):
The scope node.<name> is resolved through Node; the new NodeService::getLockedTarget() locks the type and the material before anything is read, so two concurrent votes never end in a storage failure
- the extension may refuse a vote and follows a stored one in the same transaction; Point takes no part
A favorite of a material is one transaction with the rights, the feature and the extension of its type
- the lists read the titles through getNodeTitleMap() and hide what the viewer may not read
- deleting a material deletes its favorites
- Polls, search, RSS and sitemap (modules/voting, modules/search, modules/rss, modules/sitemap, core/system.php):
- Deleting a poll clears the links of its materials through NodeService::deleteNodePoll() under the named poll lock
Search, RSS and the sitemap read the types whose own integration is on; the search and sitemap screens toggle it inside the type through updateNodeTypePart()
The sitemap is written as it is produced, a new file after 50000 URLs, Node materials in cursor batches of 500 read as a guest, addresses escaped for XML; the HTML map shows a type with its open categories
- Blocks and figures (blocks/node.php, admin/modules/blocks.php, setup/sql/, modules/presentation, admin/modules/monitor.php):
One file block node.php takes type, mode and limit from the new column _blocks.param of its instance
- getNodeBlockParam() checks it on save and on output; an invalid instance stays off
The showcase, the monitor tiles and the header marquee of the lite theme find their type by view.mode through getNodeModeType()
Benefits:
- Every shared subsystem reaches Node through one reader and one writer, with the rights and the extension of the type
- Votes, favorites and poll links are single transactions that roll back as a whole
- The sitemap no longer holds the whole site in memory
Technical notes:
BREAKING: _blocks gains param VARCHAR(255) NOT NULL DEFAULT ''; setup/sql/insert.sql and table_update6_3.sql carry it, INSERT INTO _blocks without a column list needs the new value
- BREAKING: render_blocks() takes a last parameter $param; getSearchList() in modules/search takes the total count
New tests: NodeSupportTest, NodeSyncTest and NodeIntegTest drive real HTTP on disposable MariaDB databases; the full suite keeps the one earlier failure of CommentIsolationTest on a demo string of modules/presentation
- config/fields.php and config/security.php of the stand are not part of this commit
Stages S07 to S13 of the Node plan land together: the shared Feed transport, the eight Node tables, NodeQuery and NodeService, controlled file delivery with a bounded page cache, and the one module that serves every registered type publicly and in the panel. Content types are now data in config/node.php instead of nine hardcoded modules.
Core changes:
- Feed and RSS (core/classes/feed.php, modules/rss, modules/account, config/rss.php):
One Feed class for RSS/Atom with conditional requests, transport limits and guarded external addresses
- the rss module and account op=rss read through Feed; config/rss.php carries bytes, redirects and timeout
- Node schema and models (setup/sql/table.sql, setup/sql/table_update6_3.sql, core/classes/node/*):
- Eight Node tables, _admins.modules as TEXT, the 6.3 update batch and the nodepublish scheduler job
- Closed class map in load.php: types, materials, targets, assets, relations, inputs, context, status matrix, exceptions
- NodeQuery and NodeService (core/classes/node/query.php, service.php, config/node.php):
- One settings validator filterNodeSettings(), lists and counts with equal predicates, targets, tree, sitemap, deadline
- Type writes (add, update, status, delete, import) with a Node-proof in setConfigRestore()
Material writes with versions, state moves, relations, resources, points and the nodepublish job
- shared screens fields, uploads, ratings, admins and categories write Node parts through the service
- Files and page cache (core/system.php, core/classes/cache.php, filemanager.php, upload.php, parser.php):
- getFileStream() with mime, inline, one Range, If-Range, HEAD, 304/416 and streaming
- Cache::setPageUntil() with until in the sidecar; guard files index.html and .htaccess for every type directory
- NodeService::getNodeFile() for saved attachments and previews; Parser links [attach] of a material to its route
- HTTP and view (index.php, admin/index.php, modules/node/, core/classes/node/view.php, templates, plugins):
- Public routes list, view, add, asset, attach, report with a closed method map, 404/405/409/422/429/503 answers
- Admin screens for the queue, materials with 409 keep/save, states, reports, types, clone, export, import, limits, help
- NodeView prepares one data contract for list/view/card/block/search; the theme owns partials and eight fragments
- Registry-aware getModuleName(), getModuleNavi(), getTplModuleSelect(), blocks/modules.php, letter()
- Canon fragments/repeat.html in both themes with setRepeatRows() in slaed.js; faqtitle slot of the lite theme
Benefits:
- New content types need configuration only, no new module code
- Every write goes through one validator and one writer with version checks and transactions
- Guest lists are served from the page cache without a Node query and expire at the next scheduled change
Technical notes:
- Schema: eight new node* tables and _admins.modules TEXT, applied by table_update6_3.sql
New tests: NodeModelTest, NodeQueryTest, NodeConfigTest, NodeServiceTest, NodeRouteTest, FeedTest, FileStreamTest with disposable MariaDB probes; RatingTest page-cache cases are no longer skipped
Breaking: the type directory uploads/<type> must be refused by the web server (nginx rule in docs/node/09) before a type can be enabled; a moderator right is now node-<type>
Stage S06 of the Node plan. The positional extra fields of account, forum and order give way to one shared, stateless Field class: a closed registry of ten types, an atomic check of definitions, one normalization behind the check and the filter, and values stored as one canonical JSON object per row. The 6.3 data update converts the old definitions and every stored value without guessing, and the running system knows the new format only.
Core changes:
- Shared class (core/classes/field.php, core/system.php):
final class Field with six public methods, no constructor and no state
- getFieldTypeList, filterFieldList, checkFieldValues, filterFieldValues, getFieldForm, getFieldView
- a refused definition set throws InvalidArgumentException with the path of its first error
- six machine codes for a refused value: required, type, format, choice, min, max
- hard ceilings no option raises: 256 definitions, 256 options, 64 choices, 1 MiB of JSON
- getFieldView() calls the parser in its safe mode, because values are stored as raw text
- one global instance $fld
- Owners and helpers (core/helpers.php, modules/account, modules/forum, modules/order, core/security.php):
getFieldRules() reads the definitions and getFieldsPost() writes the values, both behind the update.fields mark
- without the mark no form shows a field and a save hands the stored text back untouched
- a set that fails the shared check keeps the stored values as well
- posted texts lose [usehtml] and [usephp] at storage, the tag being the capability itself
- a switched off field keeps its value through a save, a stored name without a definition is dropped
- getFieldsInRows, getTplFieldsIn, getTplAddFieldRows and getTplViewFieldRows rebuilt on the class
- the hand-made form builder of the account administration and the field filter of getVar() are gone
- the forum prints the rendered field rows as they are and no longer hands them to a trusted parse
- Field manager (admin/modules/fields.php, admin/lang, lang):
- structured definitions per area, select options as a table, stored names, option keys and types locked
- a refused set comes back with the path of its first error and the posted text, the file stays untouched
- six FIELDS messages in six site locales, twelve FIELDS captions and a new _FIELDINFO in six admin locales
- Data update (setup/index.php, setup/sql/table.sql, setup/sql/table_update6_3.sql):
setUpdateFields() with getUpdateRules() and getUpdateValue() runs after the ratings unit
- a preflight of every definition and every row before the first write, reported by table, id and reason
- keys field1..N and option1..N by original position, the full and the short layout, one confirmed result
- a 0 in a select, date or datetime position is the placeholder of an empty choice and becomes absence
- batches of 500 by cursor, each row equal to its source or its target, resumable from its manifest
users.field, forum.field and order.info are MEDIUMTEXT in the fresh schema and in the reconciling MODIFY statements, which used to set them back to TEXT on every run
- the installer writer keeps native types on request, as the definitions need bool, int and null
- Tests (tests/Unit, tests/Support):
FieldTest, FieldViewTest and UpdateFieldsTest; update_probe.php gets the fields unit, contract_probe.php the fieldpost scenario; EditorRoomTest drops order.info
Benefits:
- one path for form, normalization, validation and view, shared by every present and future owner of fields
- stored values survive a disabled field, a refused set and a missing update mark
- the update never guesses: ambiguous data stops it with a report instead of a wrong meaning
Technical notes:
BREAKING: config/fields.php holds named definition arrays, and users.field, forum.field and order.info hold JSON; a 6.2 site reaches this format only through the update6_3 branch of setup
- form controls are named field[<name>] instead of the positional field[]
- config/fields.php ships the former demo definitions in the new format
- no runtime reader of the positional format remains
Stage S05 of docs/node connects the Rating class to the three remaining targets - accounts, forum topics and products - and closes the acceptance of stage S04. A vote is now a POST body checked field by field, stored with its voter and a delivery key, never rewarded with points, and only the main administrator can annul it; the old shared _rating table is left to the polls. The 6.3 data update keeps every existing sum and count as a starting balance, restores the real last participation of every voter and publishes the rules in their four-key form.
Core changes:
- Rating service and vote handler (core/system.php, index.php, core/helpers.php):
getRatingService() builds the class once per request: rules behind the update.ratings mark, the trusted actor of both sessions, and a closed map of targets with read and write adapters
- the read adapter repeats the visitor rights, locks the owner row first and throws on a failed statement
- the write adapter refuses a value an INT UNSIGNED column cannot hold
getRatingView() accepts POST only, the token from the body and five fields checked by regular expressions
- 405 with Allow, 403, 422, 404, 409, 429 with Retry-After and 500; nothing is read from the address
- the handler guards itself in index.php, so the method is refused before the token is asked
getRatingAsync() reads the four-key rule, is live only behind the mark and answers a stored vote with the inner block alone
- Widget and refusal toast (templates/lite/fragments/rating-*.html, plugins/system/slaed.js, theme.css):
- the fragments send mod, id, typ and token through hx-vals and carry no voting address with parameters
slaed.js mints a 32-hex delivery key per intended click and keeps it on the control while the outcome is unknown, so a repeat after a lost connection or a 5xx never stores a second vote
- a refusal keeps the widget and is told by the warning variant of the toast (.sl-toast-warn)
- Administration (admin/modules/ratings.php, modules/account/admin/index.php):
- the settings form saves four keys per scope, zero days meaning no waiting, and refuses a fraction of a day
- op=votes pages the journal by cursor with filters by scope and target, op=annul annuls one vote with a reason
- the mass reset of account votes is removed: it bypassed the class and broke the aggregate invariant
- Ratings unit of the 6.3 data update (setup/index.php, setup/sql/table_update6_3.sql, config/ratings.php):
setUpdateRatings() runs a preflight that writes nothing and names broken aggregates, times, guest addresses and rules by table and id, then takes hashed snapshots and a manifest, applies batches of 500 by cursor, compares targets, terms, owner aggregates and poll rows, publishes the rules and leaves the update.ratings mark
- a repeat never takes a current aggregate for a starting one; polls and other events in _rating are left alone
- the three rating tables are created by the update file, the InnoDB preflight covers them and _products
the installer invalidates OPcache after writing a configuration file, so the second unit of one request reads the mark the first one just wrote
- Stage S04 acceptance fixes (core/classes/point.php, modules/auto_links, modules/forum, modules/shop/admin):
- an empty points scope is not journalled; the auto_links scope is auto-links, which the class grammar accepts
- a forum reply removed through the recycle bin is compensated; the shop client form no longer overwrites uid
- the update6_3 branch closes the site itself after the preflight
- Languages, tests and plan (lang/.php, admin/lang/.php, tests/, docs/node/, tools/ui-audit-baseline.json):
- six RATINGS* refusal texts and nine admin constants in all six locales
UpdateRatingsTest and UpdatePointsTest drive the lifted installer units on a one-off schema and a scratch site through tests/Support/update_probe.php; RatingOwnersTest and PointOwnersTest guard the wiring; PointTest covers the general reset
- docs/node records the decisions, the implementation notes of S04 and S05 and the handoff to stage S06
Benefits:
- every vote has a voter, a value and a history, and an annulment removes exactly what was stored
- a retried request cannot vote twice, and guests and accounts keep independent intervals
- the data update is resumable and refuses to guess: broken data stops it before the first write
Technical notes:
BREAKING: index.php?go=1&op=getRatingView no longer accepts GET or query parameters; config/ratings.php rules are arrays of active, period, detail and guests instead of "period|active|detail" strings
- ratings stay closed for writing until the 6.3 data update leaves update.ratings in config/update.php
- a 6.2 Pro rules file in the $confra variable format is not parsed; the preflight stops on it without writing
- config/security.php is left out of the commit: its only change is the secret generated on the stand
The implementation of docs/node starts: the Point and Rating classes, the shared configuration pipeline with file locks, the early removal of the nine modules Node replaces, and the connection of Point to every remaining owner of a rewarded event. Points are now named actions written to a journal next to the fast balance, the positional list and its numeric helpers are gone together with their callers, and the 6.3 data update keeps the starting balances. Stage S04 is connected and the stand is updated, but its acceptance is not finished; docs/node/PROGRESS.md lists the exact remainder. The admin dashboard demos travel in the same commit.
Core changes:
- Point class and its connection (core/classes/point.php, config/points.php, core/system.php, core/classes/comment.php):
Point writes the _points journal and _users.points in one unit, with fifteen closed actions, a strict string configuration, per-action period and limit, compensation by rid and administrator adjust
- The core builds $pnt before Comment and keeps it closed until config/update.php carries the points mark
- Comment awards the first visible state, moves no points on hide and compensates the award on removal
updatePoints() and addPointsAction() are removed; the page view award and the rating award are removed without a replacement
- Owners of rewarded events (core/user.php, modules/forum, contact, recommend, auto_links, order, shop, account):
forum topics and posts, private messages, favorites, feedback, recommendations, link visits, polls, logins and registrations call Point with a server-made source
an order is rewarded when the administrator confirms it and compensated when the confirmation is taken back or the order is removed; _order gains the uid column, shop rewards the activation of a _clients row
- Administration (admin/modules/groups.php, modules/account/admin/index.php, modules/users/index.php):
the rules screen edits points, period and limit of fourteen actions and the one points.active switch, and shows the journal with independent filters by user, action and day, fifty rows a page
- the user form shows the balance read-only and changes it by a signed difference with a mandatory reason
- the general reset debits balances through adjust events in batches of 500 with a resumable operation id
- the public rules page and the points column follow points.active; refusals of the user form are shown again
- Rating class and the page cache protocol (core/classes/rating.php, core/classes/cache.php, core/system.php):
- Rating with three tables, a trusted target adapter, nonce, cooldown and annulment; not connected yet
- write guards, generation and markers in checkPageCache(), getPageHash() and setFoot()
- Configuration and file locks (core/system.php, core/classes/filemanager.php, admin/modules/config.php):
setConfigFile() takes a string or a Closure, answers bool, journals every save and can be restored from admin.php?name=config&op=restore; getPathLock() and deletePathLock() are re-entrant within a request
- Removal of nine legacy modules (modules/, blocks/, config/, setup/sql/):
news, pages, faq, help, jokes, content, links, files and media leave the tree with their configs, ten blocks, seed rows, nine tables and their update statements; the start page is presentation
- shared helpers keep only the remaining modules, setContentActive() is removed
- Setup and schema (setup/index.php, setup/sql/table.sql, setup/sql/table_update6_3.sql, .gitignore):
the update6_3 branch gains a preflight (closed site, server version, InnoDB), the points unit with a hashed snapshot of balances, a manifest and the config/update.php mark; a fresh install writes all three marks
- _points with its named constraints and _order.uid are declared for a fresh install and for the upgrade
- config/update.php is ignored: a shipped mark would open a subsystem that was never updated
- Languages, help and tests (lang/.php, admin/lang/.php, admin/info, tests/):
seventeen POINTS* labels replace ninety positional _POINTSnn and _DESCnn constants, nine admin constants and eight CONFIG* constants are added in all six locales; the points help pages describe the current screens
PointTest, RatingTest and ConfigFileTest run in isolated processes on one-off schemas and scratch directories; comment, schema and structure tests follow the new contracts
- Plan and demos (docs/node/, docs/, demo/):
docs/node carries the audited plan, decisions NOD-224 to NOD-235, the implementation notes of every stage and the hand-over block the next window starts from
- demo/ gains the admin dashboard studies adm-01 to adm-07 with their own stylesheet and script
Benefits:
- every change of a balance is a journal row with its action, scope, source, administrator and reason
- one event is rewarded once for the life of its target, and removals take back exactly what was awarded
- a configuration save is one journaled, restorable unit instead of an unchecked file write
Technical notes:
BREAKING: modules news, pages, faq, help, jokes, content, links, files and media are removed; their tables leave table.sql, site data and uploads of an existing installation are not touched
BREAKING: users.point and users.points leave config/users.php, points.active replaces them; constants _POINTS01-_POINTS45 and _DESC01-_DESC45 are removed; Comment::__construct() takes Point as its third argument
- BREAKING: points stay closed for writing until setup has run the update6_3 branch on a closed site
Not verified yet: the general reset, auto_links over HTTP, the remaining owners over HTTP and the resume branches of the points unit; one test outside these stages still fails on a demo SQL string in modules/presentation