diff --git a/CHANGELOG.md b/CHANGELOG.md index db6ede3..a7bcccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # (MODX)EvolutionCMS.snippets.ddMenuBuilder changelog +## Version 2.1.1 (2020-04-12) +* \+ README: Improvements. +* \+ README_ru. + + ## Version 2.1 (2020-03-07) * \+ Snippet: All templates has the following placeholders: * \+ `[+totalAllChildren+]` — total number of displayed children at all levels. @@ -135,4 +140,5 @@ * \+ The first release. + \ No newline at end of file diff --git a/README.md b/README.md index 7a44261..b2205e8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # (MODX)EvolutionCMS.snippets.ddMenuBuilder - Simple and flexible template-driven menu builder. Initially inspired by combination of the Wayfinder and Ditto advantages with significant code simplification. ## Requires + * PHP >= 5.6 * [(MODX)EvolutionCMS](https://github.com/evolution-cms/evolution) >= 1.1 * [(MODX)EvolutionCMS.libraries.ddTools](https://code.divandesign.biz/modx/ddtools) >= 0.24.1 @@ -20,7 +20,7 @@ Initially inspired by combination of the Wayfinder and Ditto advantages with sig #### 1. Elements → Snippets: Create a new snippet with the following data 1. Snippet name: `ddMenuBuilder`. -2. Description: `2.1 Simple and flexible template-driven menu builder.`. +2. Description: `2.1.1 Simple and flexible template-driven menu builder.`. 3. Category: `Core → Navigation`. 4. Parse DocBlock: `no`. 5. Snippet code (php): Insert content of the `ddMenuBuilder_snippet.php` file from the archive. @@ -32,4 +32,269 @@ Initially inspired by combination of the Wayfinder and Ditto advantages with sig 2. Extract the archive to the folder (except `ddMenuBuilder_snippet.php`). -## [Home page →](http://code.divandesign.biz/modx/ddmenubuilder) \ No newline at end of file +### Parameters description + + +#### Data provider parameters + +Providers get documents data to output. + +* `provider` + * Desctription: Name of the provider that will be used to fetch documents. + * Valid values: + * `'parent'` + * `'select'` + * Default value: `'parent'` + +* `providerParams` + * Desctription: Parameters to be passed to the provider. + * Valid values: + * `stringJsonObject` — as [JSON](https://en.wikipedia.org/wiki/JSON) + * `stringQueryFormated` — as [Query string](https://en.wikipedia.org/wiki/Query_string) + * Default value: — + + +##### Providers → Parent (``&provider=`parent` ``) + +Select children documents from required parent(s). + +* `providerParams->parentIds` + * Desctription: Parent IDs — the starting points for the menu. Specify '0' to start from the site root. + * Valid values: + * `array` + * `stringCommaSepareted` + * Default value: `'0'` + +* `providerParams->parentIds[i]` + * Desctription: Parent ID. + * Valid values: `integerDocumentID` + * **Required** + +* `providerParams->depth` + * Desctription: The depth of documents to build the menu. + * Valid values: `integer` + * Default value: `1` + + +##### Providers → Select (``&provider=`select` ``) + +Just output selected documents. + +* `providerParams->ids` + * Desctription: Document IDs. + * Valid values: + * `array` + * `stringCommaSepareted` + * **Required** + +* `providerParams->ids[i]` + * Desctription: Document IDs. + * Valid values: `integerDocumentID` + * **Required** + + +#### General parameters + +* `sortDir` + * Desctription: The sorting direction (by `menuindex` field). + * Valid values: + * `'ASC'` + * `'DESC'` + * Default value: `'ASC'` + +* `showPublishedOnly` + * Desctription: Show only published documents. + * Valid values: + * `0` + * `1` + * Default value: `1` + +* `showInMenuOnly` + * Desctription: Show only documents visible in the menu. + * Valid values: + * `0` + * `1` + * Default value: `1` + + +#### Template parameters + +* `templates` + * Desctription: Templates. + Placeholders available in all templates: + * `[+id+]` + * `[+menutitle+]` — will be equal to `[+pagetitle+]` if empty. + * `[+pagetitle+]` + * `[+published+]` + * `[+isfolder+]` + * `[+totalAllChildren+]` + * `[+totalThisLevelChildren+]` + * `[+level+]` + * Valid values: + * `stringJsonObject` — as [JSON](https://en.wikipedia.org/wiki/JSON) + * `stringQueryFormated` — as [Query string](https://en.wikipedia.org/wiki/Query_string) + * Default value: — + + +##### Item templates + +* `templates->item` + * Desctription: The menu item template. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemHere` + * Desctription: The menu item template for the current document. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemActive` + * Desctription: The menu item template for a document which is one of the parents to the current document when the current document doesn't displayed in the menu (e. g. excluded by the `depth` parameter). + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->itemHere` + +* `templates->itemUnpub` + * Desctription: The menu item template for unpublished document (when `showPublishedOnly` == `0`). + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->item` + +* `templates->itemUnpubActive` + * Desctription: The menu item template for unpublished document which is one of the parents to the current document when the current document doesn't displayed in the menu (e. g. excluded by the `depth` parameter). + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->itemActive` + + +##### Parent item templates + +* `templates->itemParent` + * Desctription: The menu item template for documents which has a children displayed in menu. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemParentHere` + * Desctription: The menu item template for the current document when it has children displayed in menu. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemParentActive` + * Desctription: The menu item template for a document which has the current document as one of the children. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->itemParentHere` + +* `templates->itemParentUnpub` + * Desctription: The menu item template for unpublished documents which has a children displayed in menu. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->itemParent` + +* `templates->itemParentUnpubActive` + * Desctription: The menu item template for an unpublished document which has the current document as one of the children. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: = `templates->itemParentActive` + + +##### Outer template + +* `templates->outer` + * Desctription: Wrapper template. + Available placeholders: + * `[+children+]` — Generated HTML with all items. + * Valid values: + * `stringChunkName` + * `string` — use inline templates starting with `@CODE:` + * Default value: `'@CODE:'` + +* `placeholders` + * Desctription: + Additional data has to be passed into the `templates->outer`. + Arrays are supported too: `some[a]=one&some[b]=two` => `[+some.a+]`, `[+some.b+]`; `some[]=one&some[]=two` => `[+some.0+]`, `[some.1]`. + * Valid values: + * `stringJsonObject` — as [JSON](https://en.wikipedia.org/wiki/JSON) + * `stringQueryFormated` — as [Query string](https://en.wikipedia.org/wiki/Query_string) + * Default value: — + + +### Examples + + +#### Providers → Parent + +```html +[[ddMenuBuilder? + &provider=`parent` + &providerParams=`{ + "parentId": 1, + "depth": 2 + }` +]] +``` + + +#### Providers → Select + +```html +[[ddMenuBuilder? + &provider=`select` + &providerParams=`{ + "ids": [ + 1, + 2, + 3 + ] + }` +]] +``` + + +#### Pass additional data into outer chunk (the `placeholders` parameter) + +```html +[[ddMenuBuilder? + &templates=` + "outer": "@CODE:[+somePlaceholder2+]" + ` + &placeholders=`{ + "class": "someClass", + "somePlaceholder2": "

    Some value for placeholder.

    " + }` +]] +``` + + +#### Using Query string instead of JSON + +JSON syntax is more clear than Query string, but sometimes it's not convenient. For example, if you want to pass JSON string as string. + +```html +[[ddMenuBuilder? + &provider=`parent` + &providerParams=`parentId=1&depth=2` + &templates=`outer=general_nav` + &placeholders=`pladeholder1={"someName": "someValue"}&pladeholder2={"name": "John"}` +]] +``` + + +## [Home page →](http://code.divandesign.biz/modx/ddmenubuilder) + + + \ No newline at end of file diff --git a/README_ru.md b/README_ru.md new file mode 100644 index 0000000..6ade9d1 --- /dev/null +++ b/README_ru.md @@ -0,0 +1,300 @@ +# (MODX)EvolutionCMS.snippets.ddMenuBuilder + +Простой и гибкий сборщик меню, построенный на шаблонах. +Иначальная идея появилась, как комбинация преимуществ Wayfinder и Ditto с существенным упрощением кода. + + +## Requires + +* PHP >= 5.6 +* [(MODX)EvolutionCMS](https://github.com/evolution-cms/evolution) >= 1.1 +* [(MODX)EvolutionCMS.libraries.ddTools](https://code.divandesign.biz/modx/ddtools) >= 0.24.1 + + +## Документация + + +### Установка + + +#### 1. Элементы → Сниппеты: Создать новый сниппет со следующими параметрами: + +1. Название сниппета: `ddMenuBuilder`. +2. Описание: `2.1.1 Simple and flexible template-driven menu builder.`. +3. Категория: `Core → Navigation`. +4. Анализировать DocBlock: `no`. +5. Код сниппета (php): Вставьте содержимое файла `ddMenuBuilder_snippet.php` из архива. + + +#### 2. Элементы → Управление файлами: + +1. Создайте папку `assets/snippets/ddMenuBuilder/`. +2. Извлеките содержимое архива в неё (за исключением файла `ddMenuBuilder_snippet.php`). + + +### Описание параметров + + +#### Параметры провайдера данных + +Провайдеры получают данные документов для вывода. + +* `provider` + * Описание: Название провайдера, используемого для получения документов. + * Допустимые значения: + * `'parent'` + * `'select'` + * Значение по умолчанию: `'parent'` + +* `providerParams` + * Описание: Параметры провайдера. + * Допустимые значения: + * `stringJsonObject` — as [JSON](https://en.wikipedia.org/wiki/JSON) + * `stringQueryFormated` — as [Query string](https://en.wikipedia.org/wiki/Query_string) + * Значение по умолчанию: — + + +##### Провайдеры → Parent (``&provider=`parent` ``) + +Находит необходимые дочерние документы заданных родителей. + +* `providerParams->parentIds` + * Описание: ID документов-родителей, в которых надо искать документы для вывода. + * Допустимые значения: + * `array` + * `stringCommaSepareted` + * Значение по умолчанию: `'0'` + +* `providerParams->parentIds[i]` + * Описание: ID документа-родителя. + * Допустимые значения: `integerDocumentID` + * **Required** + +* `providerParams->depth` + * Описание: Глубина поиска дочерних документов. + * Допустимые значения: `integer` + * Значение по умолчанию: `1` + + +##### Провайдеры → Select (``&provider=`select` ``) + +Просто выводит заданные документы. + +* `providerParams->ids` + * Описание: ID документов для вывода. + * Допустимые значения: + * `array` + * `stringCommaSepareted` + * **Required** + +* `providerParams->ids[i]` + * Описание: ID документа. + * Допустимые значения: `integerDocumentID` + * **Required** + + +#### Общие параметры + +* `sortDir` + * Описание: Направление сортировки (по полю `menuindex`). + * Допустимые значения: + * `'ASC'` + * `'DESC'` + * Значение по умолчанию: `'ASC'` + +* `showPublishedOnly` + * Описание: Выводить только опубликованные документы? + * Допустимые значения: + * `0` + * `1` + * Значение по умолчанию: `1` + +* `showInMenuOnly` + * Описание: Выводить только документы с галочкой «отображать в меню»? + * Допустимые значения: + * `0` + * `1` + * Значение по умолчанию: `1` + + +#### Шаблоны + +* `templates` + * Описание: Шаблоны. + Плейсхолдеры, доступные во всех шаблонах: + * `[+id+]` + * `[+menutitle+]` — если поле документа не заполнено, в плейсхолдер посдтавится значение `[+pagetitle+]`. + * `[+pagetitle+]` + * `[+published+]` + * `[+isfolder+]` + * `[+totalAllChildren+]` + * `[+totalThisLevelChildren+]` + * `[+level+]` + * Допустимые значения: + * `stringJsonObject` — as [JSON](https://en.wikipedia.org/wiki/JSON) + * `stringQueryFormated` — as [Query string](https://en.wikipedia.org/wiki/Query_string) + * Значение по умолчанию: — + + +##### Шаблоны пунктов меню + +* `templates->item` + * Описание: Шаблон пункта меню. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemHere` + * Описание: Шаблон пункта меню текущего документа (когда пользователь на этой странице). + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemActive` + * Описание: Шаблон пункта меню документа, когда текущий документ (на котором находится пользователь) является дочерним к нему, но не отображается в меню (например, из-за параметра `depth`). + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->itemHere` + +* `templates->itemUnpub` + * Описание: Шаблон пункта меню неопубликованного документа (когда `showPublishedOnly` == `0`). + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->item` + +* `templates->itemUnpubActive` + * Описание: Шаблон неопубликованного пункта меню документа, когда текущий документ (на котором находится пользователь) является дочерним к нему, но не отображается в меню (например, из-за параметра `depth`). + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->itemActive` + + +##### Шаблоны пунктов-родителей (содержащих вложенное меню) + +* `templates->itemParent` + * Описание: Шаблон пункта меню, содержащего дочернее меню. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemParentHere` + * Описание: Шаблон пункта меню текущего документа (когда пользователь на этой странице), содержащего дочернее меню. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: `'@CODE:
  • [+menutitle+]
  • '` + +* `templates->itemParentActive` + * Описание: Шаблон пункта меню документа, когда один из дочерних пунктов является текущим. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->itemParentHere` + +* `templates->itemParentUnpub` + * Описание: Шаблон пункта меню неопубликованного документа, содержащего дочернее меню. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->itemParent` + +* `templates->itemParentUnpubActive` + * Описание: Шаблон пункта меню неопубликованного документа, содержащего дочернее меню, когда один из дочерних пунктов является текущим. . + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: = `templates->itemParentActive` + + +##### Шаблон внешней обертки меню + +* `templates->outer` + * Описание: Шаблон внешней обертки меню. + Доступные плейсхолдеры: + * `[+children+]` — Сгенерированный код всех пунктов меню. + * Допустимые значения: + * `stringChunkName` + * `string` — вместо чанка можно сразу передавать код, используя префикс `@CODE:` + * Значение по умолчанию: `'@CODE:'` + +* `placeholders` + * Описание: + Дополнительные данные, которые необходимо передать в `templates->outer`. + Массивы также поддерживаются: `some[a]=one&some[b]=two` => `[+some.a+]`, `[+some.b+]`; `some[]=one&some[]=two` => `[+some.0+]`, `[some.1]`. + * Допустимые значения: + * `stringJsonObject` — в виде [JSON](https://en.wikipedia.org/wiki/JSON) объекта + * `stringQueryFormated` — в виде [Query string](https://en.wikipedia.org/wiki/Query_string) + * Значение по умолчанию: — + + +### Примеры + + +#### Провайдеры → Parent + +```html +[[ddMenuBuilder? + &provider=`parent` + &providerParams=`{ + "parentId": 1, + "depth": 2 + }` +]] +``` + + +#### Провайдеры → Select + +```html +[[ddMenuBuilder? + &provider=`select` + &providerParams=`{ + "ids": [ + 1, + 2, + 3 + ] + }` +]] +``` + + +#### Передача дополнительных данных в шаблон внешней обёртки (параметр `placeholders`) + +```html +[[ddMenuBuilder? + &templates=` + "outer": "@CODE:[+somePlaceholder2+]" + ` + &placeholders=`{ + "class": "someClass", + "somePlaceholder2": "

    Some value for placeholder.

    " + }` +]] +``` + + +#### Использование Query string вместо JSON + +Синтаксис JSON более нагляден, чем Query string, но иногда не очень удобен. Например, когда вы хотите передать JSON строку, как строку, чтобы она не парсилась, как объект. + +```html +[[ddMenuBuilder? + &provider=`parent` + &providerParams=`parentId=1&depth=2` + &templates=`outer=general_nav` + &placeholders=`pladeholder1={"someName": "someValue"}&pladeholder2={"name": "John"}` +]] +``` + + +## [Home page →](http://code.divandesign.biz/modx/ddmenubuilder) + + + \ No newline at end of file diff --git a/composer.json b/composer.json index a7d0aaf..cdd008f 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "dd/evolutioncms-snippets-ddmenubuilder", "type": "modxevo-snippet", - "version": "2.1", + "version": "2.1.1", "description": "Simple and flexible template-driven menu builder. Initially inspired by combination of the Wayfinder and Ditto advantages with significant code simplification.", "keywords": [ "modx", diff --git a/ddMenuBuilder_snippet.php b/ddMenuBuilder_snippet.php index 45350b7..9d3d6dc 100644 --- a/ddMenuBuilder_snippet.php +++ b/ddMenuBuilder_snippet.php @@ -1,52 +1,10 @@ - * @param $providerParams['parentIds'] {array|stringCommaSepareted} — Parent IDs — the starting points for the menu. Specify '0' to start from the site root. Default: '0'. - * @param $providerParams['parentIds'][i] {integerDocumentID} — Parent ID. @required - * @param $providerParams['depth'] {integer} — The depth of documents to build the menu. Default: 1. - * @example &providerParams=`{"parentId": 1, "depth": 2}`. - * @example &providerParams=`parentId=1&depth=2`. - * When $provider == 'select' => - * @param $providerParams['ids'] {array|stringCommaSepareted} — Document IDs. @required - * @param $providerParams['ids'][i] {integerDocumentID} — Document ID. @required - * @example &providerParams=`{"ids": [1, 2, 3]}`. - * @example &providerParams=`ids=1,2,3`. - * - * General parameters: - * @param $sortDir {'ASC'|'DESC'} — The sorting direction (by “menuindex” field). Default: 'ASC'. - * @param $showPublishedOnly {0|1} — Show only published documents. Default: 1. - * @param $showInMenuOnly {0|1} — Show only documents visible in the menu. Default: 1. - * - * Template parameters: - * @param $templates {stirngJsonObject|stringQueryFormated} — Templates. All templates can be set as chunk name or code via “@CODE:” prefix. Placeholders available in all templates: [+id+], [+menutitle+] (will be equal to [+pagetitle+] if empty), [+pagetitle+], [+published+], [+isfolder+], [+totalAllChildren+], [+totalThisLevelChildren+], [+level+]. - * @param $templates['item'] {stringChunkName|string} — The menu item template. Default: '
  • [+menutitle+]
  • '. - * @param $templates['itemHere'] {stringChunkName|string} — The menu item template for the current document. Default: '
  • [+menutitle+]
  • '. - * @param $templates['itemActive'] {stringChunkName|string} — The menu item template for a document which is one of the parents to the current document when the current document doesn't displayed in the menu (e. g. excluded by the “depth” parameter). Default: $templates['itemHere']. - * - * @param $templates['itemUnpub'] {stringChunkName|string} — The menu item template for unpublished document. Default: $templates['item']. - * @param $templates['itemUnpubActive'] {stringChunkName|string} — The menu item template for unpublished document which is one of the parents to the current document when the current document doesn't displayed in the menu (e. g. excluded by the “depth” parameter). Default: $templates['itemActive']. - * - * @param $templates['itemParent'] {stringChunkName|string} — The menu item template for documents which has a children displayed in menu. Default: '
  • [+menutitle+]
  • ';. - * @param $templates['itemParentHere'] {stringChunkName|string} — The menu item template for the current document when it has children displayed in menu. Default: '
  • [+menutitle+]
  • '. - * @param $templates['itemParentActive'] {stringChunkName|string} — The menu item template for a document which has the current document as one of the children. Default: $templates['itemParentHere']. - * - * @param $templates['itemParentUnpub'] {stringChunkName|string} — The menu item template for unpublished documents which has a children displayed in menu. Default: $templates['itemParent']. - * @param $templates['itemParentUnpubActive'] {stringChunkName|string} — The menu item template for an unpublished document which has the current document as one of the children. Default: $templates['itemParentActive']. - * - * @param $templates['outer'] {stringChunkName|string} — Wrapper template. Available placeholders: [+children+]. Default: ''. - * - * @param $placeholders {stirngJsonObject|stringQueryFormated} — Additional data as query string has to be passed into “templates['outer']”. The parameter must be set as JSON (https://en.wikipedia.org/wiki/JSON) or Query string (https://en.wikipedia.org/wiki/Query_string). Default: —. - * @example &placeholders=`{"pladeholder1": "value1", "pagetitle", "My awesome pagetitle!"}`. - * @example &placeholders=`pladeholder1=value1&pagetitle=My awesome pagetitle!`. - * * @link http://code.divandesign.biz/modx/ddmenubuilder * * @copyright 2009–2020 DivanDesign {@link http://www.DivanDesign.biz } @@ -59,7 +17,7 @@ ); //Prepare template params -$templates = ddTools::encodedStringToArray($templates); +$templates = \ddTools::encodedStringToArray($templates); $templates['outer'] = isset($templates['outer']) ? @@ -87,7 +45,7 @@ $ddMenuBuilder = new ddMenuBuilder($ddMenuBuilder_params); //Prepare provider params -$providerParams = ddTools::encodedStringToArray($providerParams); +$providerParams = \ddTools::encodedStringToArray($providerParams); //Генерируем меню $result = $ddMenuBuilder->generate($ddMenuBuilder->prepareProviderParams([ @@ -102,7 +60,7 @@ //Данные, которые необоходимо передать в шаблон if (!empty($placeholders)){ - $placeholders = ddTools::encodedStringToArray($placeholders); + $placeholders = \ddTools::encodedStringToArray($placeholders); }else{ $placeholders = []; } @@ -111,7 +69,7 @@ $placeholders['totalAllChildren'] = $result->totalAll; $placeholders['totalThisLevelChildren'] = $result->totalThisLevel; -return ddTools::parseText([ +return \ddTools::parseText([ 'text' => $templates['outer'], 'data' => $placeholders ]);