Для разработчиков

Шаблонизатор

Рабочее место в Интеграме — это HTML-файл базы. Ядро берёт его, находит в нём блоки <!-- Begin: Имя -->…<!-- End: Имя -->, наполняет каждый блок строками запроса с тем же именем и подставляет значения вместо {плейсхолдеров}. Логики в шаблоне нет: данные готовит запрос, шаблон их только раскладывает.

Примеры даны для https://ideav.ru и базы mydb. Загрузка файлов — в разделе «Файлы и рабочие места» справочника API, команды запросов — там же.

Как это устроено

Путь от адреса до страницы

Адрес /mydb/orders ядро читает как базу mydb и рабочее место orders. Дальше — четыре шага:

  • Загружается оболочка main.html: шапка, меню роли, подключение скриптов.
  • Маркер <!-- File: a --> в оболочке подключает файл рабочего места orders.html. Если такого файла нет, подставляется info.html.
  • Файл разбирается на дерево блоков. Каждый блок с именем запроса получает строки этого запроса.
  • Блоки повторяются по строкам, значения встают на места плейсхолдеров, готовый HTML уходит в браузер.

PHP внутри шаблона не выполняется, кэша шаблонов нет: файл читается с диска на каждый запрос, поэтому загруженная правка видна сразу.

Где лежат шаблоны

Ядро ищет файл сначала в папке базы templates/custom/mydb/, потом в общей templates/. Файл базы перекрывает общий с тем же именем — так у базы появляется своя оболочка main.html.

В папку базы файлы попадают через файловый менеджер dir_admin с флагом templates=1. Скрипты и стили кладутся отдельно, с флагом download=1, и раздаются по адресу https://ideav.ru/download/mydb/.

Команда загрузки с токеном, кукой и _xsrf — в справочнике API, раздел «Файлы и рабочие места» (/api.html#files).

Синтаксис

Блоки

Блок — кусок HTML между двумя комментариями. Блоки вкладываются друг в друга на любую глубину.

<!-- Begin: Orders -->
<tr><td>{num}</td><td>{client}</td></tr>
<!-- End: Orders -->
  • Разделители строгие: <!-- с пробелом в начале и  --> с пробелом в конце. Регистр слов Begin/End не важен.
  • Имя — латиница, кириллица, цифры, _ и пробел. Дефис, точка и буква «ё» в имени не распознаются.
  • End закрывает последний открытый Begin с тем же именем. Перепутанная вложенность — ошибка «Invalid blocks nesting».
  • Имя, начинающееся с _, — локальный блок без запроса: кусок разметки, который выводится или прячется по правилам ниже.
  • Остальные HTML-комментарии остаются в странице как есть.

Плейсхолдеры

Плейсхолдер — имя в фигурных скобках. Ядро подставляет значение во все его вхождения в блоке.

ЗаписьЧто подставляется
{колонка}значение колонки текущей строки запроса этого блока
{_parent_.колонка}колонка текущей строки ближайшего внешнего блока, у которого она есть
{_global_.имя}переменная сессии и окружения, список — ниже; неизвестное имя даёт пустую строку
{_request_.имя}параметр адреса или формы (POST перекрывает GET)

Колонка ищется по заголовку колонки запроса. Подставленные значения экранируются: текст из базы не превращается в разметку и не порождает новых плейсхолдеров.

Переменные _global_

ПеременнаяЗначение
{_global_.z}имя базы
{_global_.user}, {_global_.user_id}логин и id пользователя
{_global_.role}, {_global_.role_id}роль и её id
{_global_.action}, {_global_.id}рабочее место и id объекта из адреса /mydb/{action}/{id}
{_global_.token}, {_global_.xsrf}токен сессии и XSRF — для запросов из JavaScript страницы
{_global_.uri}адрес страницы, экранированный
{_global_.version}версия ядра
{_global_.today}, now, yesterday, tomorrow, week_ago, month_ago, month_plusдаты в формате дд.мм.гггг с учётом часового пояса

Вставка файлов

<!-- File: header --> вставляет файл header.html с тем же порядком поиска: сначала папка базы, потом общая. Вставленный файл разбирается целиком — в нём могут быть свои блоки и вставки.

Имя a особое: <!-- File: a --> вставляет файл текущего рабочего места. Так устроена оболочка main.html.

Повторение по строкам

  • Блок с колонками выводится столько раз, сколько строк вернул его запрос.
  • Блок без колонок — только с _global_, _request_ или вовсе без плейсхолдеров — выводится один раз.
  • Вложенный блок разбирается заново на каждой строке внешнего: так строится «заказ → его позиции».

Откуда берутся данные

Блок = запрос

Блок получает строки запроса (отчёта) базы, имя которого совпадает с именем блока. Если имя запроса не подходит под правила имени блока, блок можно назвать числовым id запроса.

  • Колонки блока — заголовки колонок запроса; скрытые колонки в шаблон не попадают.
  • Строку данных собирает запрос: вычисления, фильтры, сортировку и права роли считает движок запросов, шаблон только выводит готовое.
  • Блоки ядра (&main, меню, формы объектов) берут данные из самого ядра, а не из запросов.

Фильтры из адреса

Параметры адреса фильтруют запросы блоков, как фильтры отчёта: FR_колонка и TO_колонка (пробелы в имени — _), а также ORDER, SELECT, TOTALS, WHERE.

https://ideav.ru/mydb/orders?FR_client=Иванов

Фильтры адреса действуют на ВСЕ блоки-запросы страницы, у которых есть колонка с таким именем. Если два блока используют колонку client, фильтр сузит оба.

Запрос внутри запроса

В фильтре запроса можно сослаться на внешний блок: _parent_.колонка — значение колонки текущей строки внешнего блока. Ещё доступны _request_.имя, _global_.имя и встроенные [USER_ID], [TODAY].

<!-- Begin: Orders -->
<h3>Заказ {num} — {client}</h3>
<ul>
  <!-- Begin: Order items -->
  <li>{product}: {qty} шт.</li>
  <!-- End: Order items -->
</ul>
<!-- End: Orders -->

Запрос Order items фильтруется по _parent_.num, и у каждого заказа выводятся его позиции.

Вложенный запрос выполняется заново на каждую строку внешнего блока. Сто заказов — сто запросов позиций: для длинных списков лучше один плоский запрос.

JSON-режим и JavaScript

Любой параметр, начинающийся с JSON, переключает ответ: страница считается как обычно, но вместо HTML уходят данные блоков — для каждого блока массивы значений его плейсхолдеров.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/orders?JSON=1"

Интерактив рабочего места — таблицы, формы, сохранение — пишется на JavaScript поверх API: оболочка кладёт в страницу {_global_.xsrf} и {_global_.token}, скрипт читает отчёты (report/Имя?JSON_KV) и пишет записи командами _m_*.

Правила вывода и подводные камни

Условий нет — блок прячется сам

if/else в шаблонизаторе нет. Условность получается из двух правил:

  • Нет строк — нет блока. Блок с колонками, чей запрос ничего не вернул, не выводится вовсе.
  • Неподставленный плейсхолдер — строка выброшена. Если в строке блока остался {...} без значения, она пропадает.

Отсюда приём «показать, если передан параметр»: локальный блок с {_request_.имя} выводится, только когда параметр есть в адресе.

<!-- Begin: _Msg -->
<div class="alert">Сохранено: {_request_.saved}</div>
<!-- End: _Msg -->

{_global_.имя} блок НЕ прячет: неизвестная глобальная переменная подставляется пустой строкой. Чтобы спрятать блок при пустом запросе, нужна колонка, например <!--{num}-->.

Фигурные скобки в JS и CSS

Ядро считает плейсхолдером любые фигурные скобки, внутри которых только буквы, цифры, пробелы и знаки . _ - & — в том числе в <script> и <style>. {id}, { id } или { return } в JS оно примет за колонку, не найдёт — и выбросит строку блока или весь блок.

  • Безопасно то, где внутри скобок есть другой знак: { color: red; }, { a: 1 }. Тело функции начинайте с новой строки после {.
  • Большие скрипты и стили выносите в файлы download=1 и подключайте ссылкой: туда шаблонизатор не заглядывает.

Регистр и имена

  • Латинские имена блоков и колонок сравниваются без учёта регистра.
  • Кириллические — пишите в том же регистре, что заголовок колонки запроса: регистр кириллицы ядро не выравнивает.
  • Буква «ё» в имени блока или плейсхолдера не распознаётся — заменяйте на «е».
  • Опечатка в имени колонки не даёт ошибки: блок молча пустеет. Ищите её с параметром ?debug=1 — он оставляет неподставленные {...} на странице.

Экранирование

  • Значения колонок экранируются, переводы строк становятся <br/>.
  • Колонки базового типа HTML вставляются как есть — это разметка, которой доверяет автор базы.
  • Значение колонки внутри JS-строки в одинарных кавычках ('{name}') может её сломать: на PHP до 8.1 апостроф в данных не экранируется. Берите двойные кавычки или читайте данные через JSON.
  • {_request_.имя} вставляется без экранирования. Не выводите параметры адреса в разметку напрямую — передавайте их в запрос фильтром, где ядро их экранирует.

Шаблон новее ядра

Если шаблон использует переменную, которой ещё нет в ядре установки, плейсхолдер остаётся неподставленным — и блок исчезает вместе с вложенными. Обновляйте ядро раньше шаблонов.

На практике

Рабочее место за три шага

  • Сделайте в базе запрос Orders с колонками num, client, sum.
  • Загрузите файл orders.html через dir_admin с флагом templates=1.
  • Добавьте роли пункт меню с адресом orders.

orders.html

<h1>Заказы — {_global_.user}</h1>
<table>
  <!-- Begin: Orders -->
  <tr><td>{num}</td><td>{client}</td><td>{sum}</td></tr>
  <!-- End: Orders -->
</table>

Страница /mydb/orders покажет таблицу по строкам запроса внутри оболочки базы; /mydb/orders?FR_client=Иванов — только заказы Иванова.

Если блок пустой

  • Имя блока совпадает с именем запроса символ в символ, без «ё», дефиса и точки?
  • Запрос возвращает строки под этой ролью и с этими фильтрами адреса?
  • Все {колонки} блока есть среди заголовков запроса и не скрыты?
  • Нет ли в блоке {слова} из JS или CSS без пробелов?
  • Begin и End закрыты в правильном порядке?
  • Откройте страницу с ?debug=1 и найдите неподставленные {...}.