Repository navigation
docs(extending-modx): match plugins, snippets, REST, and lexicons to 3.x - #676
Ibochkarev wants to merge 5 commits into
Conversation
|
Where does this concept of manually including |
|
You are right, and the page no longer tells the reader to call the connector. Where the concept comes from. How a Component does it. The page declares the topics and the manager loads them:
Both load the topic and put it into The page now says that a Component does not call the connector itself — the manager does. The Russian page follows. |
Jako
left a comment
There was a problem hiding this comment.
I have added a comment about loading the lexicons with the lang.js.php
| ``` | ||
|
|
||
| A page declares the topics it needs, and the manager loads them into `MODx.lang`. A controller names them in `getLanguageTopics()`: | ||
| A page declares the topics it needs, and the manager loads them into `MODx.lang`. To add the topics of an own component, name them in `getLanguageTopics()` of a controller of the component — it extends the list of the lexicons loaded for the page: |
There was a problem hiding this comment.
To add multiple lexicons, the parameter of getLanguageTopics can contain an array of topics instead of a string.
There was a problem hiding this comment.
Fixed — the example returns a list now, and the page says that the method returns the list of the topics of the page:
public function getLanguageTopics()
{
return ['mycomponent:default', 'mycomponent:settings'];
}The code behind it: modManagerController::prepareLanguage() takes the return value of getLanguageTopics(), loads every topic of the list with modLexicon->load() and hands the list over to the header of the page through _lang_topics. The core controllers use the list the same way — for example return ['user','access','policy','context','setting']; in the controller of a user group.
One detail worth knowing, since the wording can be read both ways: every element of the list has to be a string with one topic. A nested array would not work — modLexicon::load() skips everything that is not a string, and implode(',', $languageTopics) would flatten it with a notice.
The Russian page follows.
… lexicon coordinates The review of modxorg#662 left four remarks on the page, all of them about the code. **The syntax of the tag.** The example of a placeholder was written as `[[!%userinput=`amazing`]]`, which is not a valid tag: the parser splits the tag on the first `?`, so without it the value stays part of the name. The old example of the page had the same problem in the namespace property, where the name was written as `namespace\_name` — the backslash is not an escape in the syntax of a tag. The example is now `[[!%key? &userinput=`amazing`]]`. **The optional properties.** The `topic` and the `language` can be left out — the topic falls back to `default`, the language to the one of the request. The `namespace` falls back to `core` as well, so it has to be given for a lexicon outside the core namespace; without it the tag looks in the core only. **The topics of the JavaScript connector.** The page states that `connectors/lang.js.php` takes a comma-separated `topic` parameter. The connector reads `$_REQUEST['topic']`, splits it by commas and loads every coordinate with `modLexicon::load()`; the templates of the core ask for the topics the same way (`manager/templates/default/header.tpl` requests `?ctx=mgr&topic=topmenu,file,resource,…`). The page now says so. **The lexicons of the settings.** The label of a setting is the key `setting_<key>`, and its description is `setting_<key>_desc`. The core keeps its labels in the `setting` topic, a Component outside the core namespace keeps them in the `default` topic — that is the topic the manager writes a translated label into (`modSystemSetting::updateTranslation()` picks the topic by the namespace of the setting). The Settings grid loads both topics of the namespace, so a key with the `setting_` prefix is found in either of them. The example of `modLexicon::load()` is rewritten as well: every argument is a coordinate, there is no separate parameter for the namespace. The `description` of the frontmatter of both locales is quoted, and the stray quote at its end is gone.
… call the connector The page told the reader to load extra lexicon topics in the browser by calling `connectors/lang.js.php` with a `topic` parameter. That is how the core does it, but it is not how a Component does it: the connector is the manager's own mechanism, and the manager calls it for the topics of the page. A page declares the topics it needs and the manager loads them: - a controller names them in `getLanguageTopics()` (`core/src/Revolution/modManagerController.php:533`, the core controllers override it); - a page that needs a topic beyond those of its controller adds it with `addLexiconTopic()` (`modManagerController.php:1027`). Both put the topic into `_lang_topics`, which the header template hands over to the connector (`manager/templates/default/header.tpl:23`), and the response replaces `MODx.lang`. The Russian page follows.
…ts own topics The page told that a controller names its topics in `getLanguageTopics()`, but did not say that this is the way a Component adds the topics of its own namespace, and it did not say why the connector is not the way to add topics while the page runs. It is now stated in both places: - to add the topics of an own component, name them in `getLanguageTopics()` of a controller of the component — it extends the list of the lexicons loaded for the page; - the connector does not extend the lexicon of a page that is already running. It answers with the topics of its own request — `core:default` and the ones asked for — and the response replaces `MODx.lang`, so the topics have to be declared before the page is rendered. The Russian page follows.
…s() takes a list The method returns the list of the topics of the page, so a component names all of its topics at once. The example now returns two topics and the page says so. The code behind it: `modManagerController::prepareLanguage()` takes the return value of `getLanguageTopics()`, loads every topic of the list with `modLexicon::load()` and hands the list over to the header of the page through `_lang_topics`. The core controllers use the list the same way — `return ['user','access','policy','context','setting'];`. The Russian page follows.
94705f1 to
2523405
Compare
Samples for mail, snippets, REST, and user models described classes and paths the core does not use.
Follow-up to #662, plus a fact check of the developer pages against MODX 3.x commit
313aabf8478ac2770c1090f42acdf34022291712(22 October 2026).Summary
English and Russian pages now describe the same runtime.
Lexicons
The placeholder example is
[[!%key? &userinput=amazing]]. Without the?the parser keeps the value inside the entry name.topicdefaultlanguagecultureKey), not the column defaultennamespacecore. A lexicon outside the core namespace has to set itA coordinate that includes a namespace is loaded once. A bare topic name is loaded again on the next call.
A lexicon row's
languagecolumn defaults toen. The create processor does not setcreatedon.editedonis set when the entry is created in the manager and updated when the row is written.getLanguageTopics()returns the topics for the page. The controller loadsaction, then that list, thentrash. The base method returns an empty array.addLexiconTopic()adds one more topic after that.connectors/lang.js.phpreadstopic, splits on commas, and loads each coordinate withmodLexicon::load(). The response replacesMODx.lang.A System Setting label is
setting_<key>, and the description issetting_<key>_desc. The Settings grid loads both topics of the setting's namespace.Plugins, snippets, and the manager
The
OnPageNotFoundsample only redirects.$modx->resourceis not loaded on that event. Mail is created withgetService('mail', \MODX\Revolution\Mail\modPHPMailer::class). The constants are onMODX\Revolution\Mail\modMail.Inside a snippet,
$thisis the snippet object and$modxis in scope. A core snippet is written tocache/includes/elements/modx/revolution/modsnippet/{id}.include.cache.php. The directory follows the class name.A manager controller class is not the file name. Namespace
quipand actionhomeloadQuiphomeManagerController. Actionsome/actionloadsQuipsomeActionManagerController.There is no per-component
connector.php. A component processor is a subclass ofMODX\Revolution\Processors\Processor, orrunProcessor()is givenprocessors_path. Thenamespacequery parameter selects a manager page.An empty
session_cookie_pathis replaced withbase_url, then withMODX_BASE_URLifbase_urlis empty too.In the Russian manager the site menu is «Содержимое». The Russian section index no longer links three items at the section root.
REST
A missing verb method returns status
405and the messageUnsupported HTTP method get. If the method runs and never callssuccess(),failure(), orcollection(), the service sends the controller response as it stands. There is no second405.trimParametersdoes not trim the parsed body or the query string. Trim strings in the controller. See modxcms/revolution#17114Parsing and custom users
OnParseDocumentfires on every parser pass. A plugin changes the content by writing$modx->documentOutput. A change to thecontentargument is discarded. See modxcms/revolution#16992An extended user is registered with a
MODX\Revolution\modExtensionPackagerow. Leavepathempty so MODX uses the namespace directory and appendsmodel/.table_prefixon this page isext_. The system settingextension_packagesis still read and marked deprecated. In that JSON the path already ends inmodel/.Existing users keep
class_keyMODX\Revolution\modUseruntil it is changed. The phone relation on the sample schema ismany, so the sample callsgetMany('Phones').Affected versions
3.x
Not checked
getLoader(), schemaextends, hydration byclass_key, andgetOption()on a plugin object were not checked.custom-users/,creating-components/,core-model/, and the individual system-event pages were not part of this pass. The system-event index still shows the labelOnBeforeResourceGroupRemove.