Для разработчиков
Шаблонизатор
Рабочее место в Интеграме — это 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. Пункт с адресом orders ведёт на /mydb/orders и открывает orders.html.
Само меню — тоже блок: в оболочке стоит <!-- Begin: MyRoleMenu -->, и его строки даёт запрос MyRoleMenu базы.
Синтаксис
Блоки
Блок — кусок 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и найдите неподставленные{...}.