Joomla 6: возможности, ключи и приёмынаверх ↑
Оглавление

Редакция 1.1 · 13 октября 2026 года

Joomla 6: возможности, ключи и приёмы

Справочный доклад по устройству ядра, API, командной строке, шаблонам, безопасности и практике сайтостроения

Доклад собирает в одном месте то, что нужно знать о Joomla 6 разработчику сайтов и редакции: версии и сроки поддержки, устройство ядра, подключение скриптов и стилей, шаблоны и переопределения, поля и метки, API и командную строку, ключи конфигурации и доступа, приёмы и кейсы. Состояние описано на 13 октября 2026 года, в день выхода стабильной Joomla 6.2.0; проверки сделаны на 6.1.4 и 6.2.0. Каждое утверждение помечено способом проверки — запуском на стенде, чтением исходников, официальным документом или практикой автора, — чтобы читатель видел, на чём оно держится.

Как читать доклад

Ключ в трёх значениях. В заглавии слово «ключ» берётся в трёх значениях, и доклад держит их раздельно. Во-первых, ключ есть параметр, который открывает возможность: ключ конфигурации, имя события, имя ассета. Во-вторых, ключ есть токен доступа: секрет сайта, токен программного интерфейса, токен защиты форм. В-третьих, ключ есть приём — короткое знание, которое отпирает целую задачу и которое обычно не записано в документации. Первое значение раскрывают главы об устройстве ядра и приложение А, второе — главы об API и безопасности, третье — каталоги приёмов и граблей.

Как проверялось. Сказанное в докладе проверено на деле или опирается на названный источник. Полные пакеты Joomla 6.1.4 (выпуск от 29 сентября 2026 года), 6.2.0-rc2 (кандидат в релизы от 6 октября 2026 года) и 6.2.0 (стабильный выпуск от 13 октября 2026 года) установлены через консольный установщик на PHP 8.3.6, MariaDB 10.11 и Apache 2.4 [1, 2, 3]. На этой установке запускались запросы к API, консольные команды, собственные плагины и сценарии; исходники ядра читались там, где документации не хватало. Сведения о выпусках и сроках поддержки взяты из официальных объявлений и дорожной карты проекта.

Знаки проверки. Рядом с утверждениями стоят шесть знаков, и по ним видно, насколько можно доверять сказанному.

Знак Что он значит
стенд воспроизведено запуском на стенде 6.1.4 (и, где сказано, на 6.2.0)
исходники прочитано в исходниках пакета, но отдельным запуском не проверялось
документация сказано в официальном документе проекта: объявлении, дорожной карте, руководстве
практика наблюдение автора по рабочим проектам; в этой проверке не воспроизводилось
сторонний сообщено сторонним источником; официального подтверждения нет
исследование взято из прежнего исследования автора «Joomla 6 для новостной редакции», где факт снабжён номером источника; рядом дана ссылка на нужную главу

Связь с исследованием «Joomla 6 для новостной редакции». Доклад дополняет исследование автора, опубликованное 8 октября 2026 года. Исследование состоит из трёх частей: доклада на 16 глав, интерактивного лонгрида и разбора редакционного конвейера в 36 снимках настоящей Joomla 6.1.4 [4]. Там подробно разобрано то, что здесь только названо: редакционный контур ядра, конвейер новости, таксономия, нагрузка, видимость, живые порталы, русскоязычная среда и положение в РК. Где доклад опирается на исследование или отсылает к нему, стоит знак исследование и ссылка на нужную главу; тексты, схемы и снимки исследования распространяются по лицензии CC BY 4.0 [4].

О терминах. В докладе принято то, что устоялось в русском языке разработчиков, и международная практика: «ассет», «плагин», «фронтенд», «бэкенд», «браузер», «кэш», «токен» не заменены придуманными переводами. Названия разделов админки даны по русской локализации Joomla и её глоссарию [5] сторонний: «Планировщик задач», «Обучающие туры», «Многоязычные связи», «Рабочий процесс», «этап» и «переход» (stage, transition). Один из образцов обращения с терминами — портал jpath.ru: русское название стоит первым, исходное слово — в скобках при первом упоминании, как в заголовках «Диспетчеризация (Dispatching)» и «Разбор (парсинг) URI» [6] сторонний. Имена классов, методов и файлов набраны моноширинным шрифтом, имена событий — курсивом; понятие, которое читатель мог не встречать, объясняется в той же фразе или в заметке «О слове». Спорные решения названы прямо. Портал пишет «ID» там, где можно написать «номер» или «идентификатор»; в докладе принято «ID», как в списках админки. Портал называет браузер «обозревателем»; в докладе остаётся «браузер». Вместо «произвольных полей» принято «настраиваемые поля» (custom fields); в самой админке раздел называется просто «Поля». Слово tag локализация переводит как «тег», а доклад следует исследованию автора и пишет «метка». Полный перечень терминов с исходными словами и местами, где они объяснены, дан в приложении В.

Заметки «О слове». Специальные слова в докладе объяснены там, где они впервые встречаются: откуда слово пришло, как сложилась его форма и что оно значит в работе. Такие заметки опираются на общеизвестные сведения по истории терминов и знаками проверки не помечены.

Границы. Не проверялись поведение под большой нагрузкой, совместимость сторонних расширений с Joomla 6, работа на PostgreSQL и на Windows-хостинге. Где доклад говорит о таких вещах, это оговорено.

1 Joomla 6 в одной картине

1.1 Имя, версии и кодовые названия

О слове «Joomla». Имя восходит к слову суахили jumla — «все вместе, как целое»; проект принял его в 2005 году. Отсюда же обычай давать выпускам кодовые названия на суахили: kuimarisha — «укреплять», kutegemea — «полагаться», nyota — «звезда», mwezi — «луна» или «месяц». У обычая есть практический смысл: кодовое название входит в длинную строку версии и указывает, о какой линии выпусков идёт речь.

Joomla 6.0 («Kuimarisha») вышла 14 октября 2025 года вместе с 5.4 («Kutegemea») [7]. Версия 6.1 («Nyota») вышла 14 апреля 2026 года [8] сторонний, а 6.1.4 от 29 сентября 2026 года стала последним исправляющим выпуском этой ветки к дню выхода 6.2 исходники. Версия 6.2 («Mwezi») вышла 13 октября 2026 года [9] документация; кодовое название и дату выпуска хранит файл версии пакета исходники. Исправляющие выпуски ветки 6.1 — с 6.1.1 по 6.1.4 — вышли 26 мая, 7 июля, 18 августа и 29 сентября 2026 года, а первый кандидат в релизы 6.2 — 29 сентября исследование (глава 3 «Релизы и дорожная карта» [10]).

1.2 Версии и сроки поддержки

Дорожная карта проекта различает два срока: до какой даты ветка получает обычные исправления ошибок и до какой — только исправления безопасности [11] документация.

Ветка Выпуск серии Исправления ошибок до Только безопасность до
4.x 17.08.2021 15.10.2024 14.10.2025
5.x 17.10.2023 13.10.2026 12.10.2027
6.x 14.10.2025 17.10.2028 16.10.2029
Сроки поддержки веток Joomla 4, 5 и 6Для каждой ветки показан период обычных исправлений и период исправлений только безопасности; пунктирная линия отмечает 12 октября 2026 года.2021202220232024202520262027202820294.x15.10.202414.10.20255.x13.10.202612.10.20276.x17.10.202816.10.202913.10.2026обычные исправлениятолько исправления безопасности
Рис. 1. Сроки поддержки ветвей Joomla по дорожной карте проекта; пунктиром отмечена дата доклада.

Из таблицы следуют три практических вывода. Поддержка безопасности ветки 4.x закончилась 14 октября 2025 года: сайты на ней нужно переводить, а не ждать. Ветка 5.x вышла из режима обычных исправлений 13 октября 2026 года: в этот день выпущена 5.4.10, последняя в таком режиме [11] документация исходники; год после этого она получает только исправления безопасности. Для новых проектов разумна ветка 6.x: сроки её поддержки самые длинные.

Joomla 6.2.0 вышла 13 октября 2026 года в 16:00 UTC: так показывают тег в репозитории и объявление проекта [9] документация исходники. Выпуск уложился в плановую дату, как и предыдущий: 6.1 была назначена на «около 14 апреля 2026 года» [12] документация и вышла 14 апреля [8] сторонний, а 6.2 назначена на 13 октября и вышла в этот день; первый кандидат появился 29 сентября, rc2 — 6 октября [13] документация. Время тоже привычное: 6.0.0, 6.1.0, 6.1.2, 6.1.3, 6.1.4 и 6.2.0 опубликованы между 15:57 и 16:02 UTC по времени публикации на GitHub исходники. От rc2 до стабильного выпуска изменились четыре файла: файл версии, список пакетов Composer, манифест пакета и файл метаданных FIDO для WebAuthn стенд; поэтому проверки, сделанные на 6.2.0-rc2, остаются в силе для 6.2.0 стенд. Выпуски идут по шестимесячному ритму — октябрь и апрель. Из объявления о 6.1 видно, что последней минорной версией ряда проект считает 6.4: автоматические обновления не пересекают границу мажорной версии, ни «с 5.4 на 6.x», ни «с 6.4 на 7.x» [14] документация.

1.3 Требования

Минимальная версия PHP — 8.3.0: проверка стоит в самом пакете, в файле платформенных требований Composer исходники. Исследование автора сверило требования с руководством разработчика на 8 октября 2026 года исследование (глава 4 «Технический фундамент» [10]). PHP: минимум 8.3.0, рекомендуется 8.4. MySQL: минимум 8.0.13, рекомендуется 8.4. MariaDB: минимум 10.4, поддерживается 10.6, рекомендуется 12.0. PostgreSQL: минимум 12, поддерживается 14, рекомендуется 17.6. Apache: 2.4. Nginx: поддерживается 1.26, рекомендуется 1.29. Те же минимумы для MySQL и MariaDB называет и сторонний обзор [15] сторонний. Перед развёртыванием таблицу стоит сверить с руководством заново [7].

Установщик 6.1.4 знает ключ --public-folder, а в консоли есть команда site:create-public-folder: ядро умеет работать из отдельной публичной папки, оставляя код и конфигурацию вне корня сайта стенд.

1.4 Что внутри пакета

Установка 6.1.4 содержит 251 расширение, и тот же набор стоит в 6.2.0 стенд.

Тип Число
плагины 156
модули 49
компоненты 36
шаблоны 3 (Atum, Cassiopeia, Cassiopeia Extended)
языки 3
библиотеки 2
файлы и пакеты по 1

Самые многочисленные группы плагинов: system — 24, webservices и fields — по 18, content — 10, schemaorg и task — по 9, editors-xtd и quickicon — по 8 стенд. Под ядром лежат 89 пакетов Composer. Основу составляют пакеты Joomla Framework версии 4 (application, di, event, database, router, session, console и другие), консоль Symfony 7.4.7, почтовая библиотека PHPMailer 6.12.0, библиотека web-auth/webauthn-lib 4.5.2 для беспарольного входа и пакет tobscure/json-api, на котором строится вывод API исходники.

1.5 Переход с 5.4 на 6.x

Проект называет переход с 5.4 на 6.0 обновлением, а не миграцией, и поддерживает его через плагин «Behaviour — Backward Compatibility 6» [7] документация. По руководству проекта, в 5.4 этот плагин установлен, включён и ничего не делает; в новой установке 6.x он установлен, но выключен; как плагин группы behaviour, он загружается раньше остальных [16] документация. На стенде 6.1.4 плагин compat6 действительно выключен стенд.

Плагин даёт три вещи, и это видно по его языковым строкам: псевдонимы классов, которые переименовали или перенесли в пространства имён; сами удалённые устаревшие классы; пустые заглушки удалённых ассетов, чтобы старый код не падал с исключением исходники. В его карте классов лежит, например, псевдоним JFactory на Joomla\CMS\Factory исходники. Значит, расширение, которое всё ещё зовёт JFactory, при выключенном плагине работать не будет.

Руководство даёт и порядок действий: старый плагин совместимости Joomla 5 перед обновлением отключают, ошибки разбирают ещё на 5.4, а повторяют обновление, только когда сайт без него работает [16] документация. Смысл порядка простой: если сайт упадёт на 5.4, плагин легко включить обратно, а если он упадёт посреди обновления, пути назад нет. Не стоит и заводить собственный плагин в группе behaviour: группа может исчезнуть [16] документация.

2 Что нового в 6.0, 6.1 и 6.2

Официальные списки новинок лежат не только на сайте проекта: они встроены в сами пакеты как обучающие туры «Что нового» (раздел «Обучающие туры» в админке). Ниже сведено то, что сказано в объявлениях и в этих турах; сверх списка отмечено то, что нашлось в коде.

2.1 Версия 6.0

Основу выпуска составляют две перемены: минимальный PHP поднят до 8.3, а из кода удалено то, что было помечено устаревшим; сайтам, которым нужно время, предусмотрен плагин совместимости. Из новшеств, названных в объявлении [7] документация:

Новшество Что это даёт на практике
автоматические обновления ядра сайт обновляется сам в пределах мажорной ветки; защищённая доставка через TUF существует с 5.1
дочерний шаблон Cassiopeia в ядре стоит готовый пример дочернего шаблона с настройкой цветов и шрифтов без собственного CSS
расширенное версионирование в историю версий попадают значения настраиваемых полей; обучающий тур добавляет метки и категории
поля «notes» и «number» подсказка между полями и числовое поле с форматом денег
поля «date» и «datetime-local» нативные браузерные поля ввода даты и времени
TinyMCE 8.0.1 редактор обновлён с шестой версии сразу на восьмую
кэш языковых файлов слой кэширования у LanguageHelper::parseIniFile ускоряет загрузку строк
плавные переходы CSS-переходы в Cassiopeia и в админской Atum

Обучающий тур 6.0 в пакете называет ещё несколько мелочей: письмо при одобрении регистрации администратором, свои классы в выпадающем списке диалога изображений TinyMCE, пакетное снятие метки, общий вид менеджера статей для избранных исходники.

О слове «TUF». The Update Framework — «каркас обновлений»: набор правил, по которым клиент проверяет, что полученное обновление подлинно, свежо и не подменено, даже если взломан сам сервер раздачи. Идея возникла около 2009 года в Нью-Йоркском университете и затем стала отраслевым стандартом. В Joomla она работает так: список файлов обновления подписан несколькими ключами, и сайт сверяет подписи, прежде чем принять пакет. Поэтому подмена файла на зеркале не проходит.

2.2 Версия 6.1

Выпуск 6.1 принёс четыре заметных новшества и ряд мелких [14] документация:

Новшество Что это даёт на практике
капча на доказательстве работы защита форм без внешнего сервиса и без аккаунта; движок называют Altcha [17] сторонний
визуальный редактор workflow этапы и переходы редакционного процесса рисуются диаграммой
медиаполя: звук, видео, документы настраиваемое поле выводит HTML5-аудио, HTML5-видео или ссылку на документ
версии и связи модулей у модуля есть история правок; экземпляры модулей связываются между языками

Из мелких: кнопка abbr в TinyMCE и подсказки о состоянии языковых переопределений исходники. В пакете 6.1.4 из капч есть единственная — powcaptcha; плагинов капч внешних сервисов в составе нет стенд. Объявление называет её возвращением защиты от спама.

2.3 Версия 6.2

Состав 6.2 известен по заметкам о выпуске [9] документация, по пакету 6.2.0 и по встроенному обучающему туру исходники:

Новшество Что это даёт на практике
выбор файлов в редакторе выборщик теперь открывает штатный Медиа-менеджер, а кнопка «Read More» вставляет разрыв статьи
раздельные классы у вводного и полного изображения статьи отдельные классы для img и для окружающего figure
многоуровневые меню Cassiopeia выпадающие и сворачиваемые меню получили больше одного вложенного уровня
ссылки в mod_login ссылки «Забыли пароль» и «Забыли имя» скрываются параметром, без переопределения
флаг безопасности обновления разработчик помечает релиз расширения как обновление безопасности со степенью от «нет» до «критической», и оно подсвечивается в списке обновлений и на значке панели управления
запасные языки в настройках компонента «Языки» есть раздел «Language Fallback»: каждая строка сопоставляет установленный язык с запасным, и непереведённые строки берутся из него исходники; в объявлении проекта эта возможность не названа документация
изображения подкатегорий параметр «Subcategories Image» выводит изображения подкатегорий исходники документация
выбор позиции модуля в расширенном выборе результаты поиска показывают название группы, и нужную позицию модуля найти проще документация
выбор ссылок единое окно выбора ссылки для кнопок «Материал», «Контакт» и «Пункт меню» в редакторе документация
фильтр текста редактор TinyMCE теперь следует настройкам фильтра текста Joomla, а не собственным допустимым элементам документация
категории с отложенными материалами категория больше не выглядит пустой, если её материалы назначены к публикации в будущем документация
справка админки страница справки переработана под шаблон Atum, навигация внутри неё улучшена документация
установка веб-установщик точнее сообщает о сбоях документация
почта через OAuth 2.0 в настройках почты появился способ проверки подлинности «OAuth 2.0» (в первую очередь для Microsoft 365 и Google Workspace документация) с полями адреса авторизации, клиента, секрета, адреса токена и области и кнопками получения и проверки токена; в коде ему служат MailOAuth2TokenProvider и поле Oauth2TokenField исходники
сборка ресурсов ядра сборка JavaScript и CSS разбита на сборщики по расширениям: npm run builders-list показывает их список, npm run build -- -n com_content собирает один, npm run watch -- --all следит за всеми; исходники перенесены из build/media_source/ в media_source/ [18] документация

Сверх списка в коде 6.2.0 нашлись перечисляемый тип поля формы EnumField, класс MailController в компоненте «Конфигурация», на который перенесена отправка пробного письма, и новые плагины TinyMCE для выбора ссылок и файлов исходники. Английских языковых строк в 6.2.0 на 372 больше, чем в 6.1.4: 13 504 против 13 132 исходники. В заметках для разработчиков к 6.2 новых объявлений об устаревании нет [18] документация, а в коде их тринадцать; они перечислены в таблице устаревшего в главе об устройстве ядра исходники.

Исследование автора, опираясь на объявление о релиз-кандидате, называет ещё две вещи: новую систему сборки и тестовую библиотеку PHPUnit 12 исследование (глава 3 «Релизы и дорожная карта» [10]). Запасные языки важнее всего для казахстанских сайтов: запасным для kk-KZ назначают ru-RU, и непереведённые казахские строки интерфейса показываются по-русски, а не по-английски исследование (глава 13 (русскоязычная среда и положение в РК) [10]).

Стороннее издание сообщало, что экспериментальную связку Joomla с серверами MCP — протоколом подключения моделей ИИ к инструментам — не включат ни в один выпуск ветки 6.x [8] сторонний; официального подтверждения этому доклад не нашёл.

3 Устройство ядра

3.1 Слои

Joomla не есть единый монолит. Внизу лежит Joomla Framework — набор независимых PHP-пакетов (application, di, event, database, router, session, console, input, registry, uri и другие); в 6.1.4 они подключены в четвёртой версии исходники. Framework развивается как отдельный проект со своим сайтом [7] документация, так что его пакеты можно брать и без CMS. По сведениям исследования автора, у Framework около тридцати двух активных пакетов под именами joomla/*, скачанных вместе более 14,6 млн раз; серия 4.x легла в основу CMS 6.0, а серия 3.x — в основу CMS 5 исследование (глава 4 «Технический фундамент» [10]). Выше стоит сама CMS: каталог libraries/src с фабрикой Factory, слоем MVC, формами, ассетами и типизированными событиями. Над CMS поднимаются четыре приложения — сайт, административная часть, API и консоль, — а на самом верху находятся расширения: компоненты, модули, плагины, шаблоны, библиотеки, пакеты, файлы и языки. Классы libraries/src портал jpath.ru делит на основные (Application, Database, Document, Factory, Form, Input, Language, Router, Session, Uri, User и другие), служебные (Authentication, Categories, Date, Image, Mail, Pagination, Toolbar и другие) и вспомогательные — классы вида …Helper [19] сторонний.

Слои Joomla 6Расширения и шаблоныcomponent · module · plugin · template · library · package · file · languageПриложенияsite · administrator · api · consoleCMS: libraries/srcFactory · MVC · формы · ассеты · события · маршрутизация · база данныхJoomla Framework 4.x и внешние пакетыapplication · di · event · database · router · session · console · input · uriи ещё Symfony Console 7.4 · PHPMailer 6.12 · webauthn-lib 4.5 · json-apiконтейнер зависимостей · карта неймспейсов · события
Рис. 2. Слои Joomla 6.1.4: снизу вверх — Framework, ядро CMS, четыре приложения и расширения.

3.2 Четыре входа в систему

У Joomla 6 четыре точки входа, и у каждой своё приложение — класс, который принимает запрос, прогоняет его по конвейеру и возвращает ответ исходники.

Вход Файл Приложение Для чего
сайт index.php SiteApplication публичная часть
админка administrator/index.php AdministratorApplication управление сайтом
API api/index.php ApiApplication Web Services API
консоль cli/joomla.php ConsoleApplication команды и планировщик

Все четыре входа загружают общий каркас includes/framework.php, поднимают контейнер зависимостей и берут из него своё приложение. В файлах api/includes/app.php и cli/joomla.php одинаково стоит набор из пяти алиасов сессии, который заменяет веб-сессию на консольную: session, JSession, Joomla\CMS\Session\Session, Joomla\Session\Session и Joomla\Session\SessionInterface — все на session.cli исходники. Без этих алиасов приложение вне веб-контекста падает при первом обращении к сессии; рабочий гайд проекта называет их обязательными [20] практика.

Кроме четырёх приложений, в libraries/src/Application лежит класс DaemonApplication: он превращает консольное приложение в службу (демона), а события DaemonForkEvent и DaemonReceiveSignalEvent описывают его работу; своего входа в корне сайта у него нет исходники. Портал jpath.ru называет его пятым типом приложений [6] сторонний.

3.3 Цикл выполнения веб-приложения

Портал jpath.ru разбирает выполнение веб-приложения на этапы и называет их по-русски, с исходным словом в скобках [6] сторонний. Ниже эти названия сверены с классами событий 6.1.4 исходники. Статья описывает сайт Joomla 3 и помечена порталом словом «Устарело», поэтому порядок читают как схему, а не как описание кода шестой ветки.

Этап События и их классы Что происходит (по статье)
Подготовка (Bootstrapping)  — проверка версии PHP, подключение defines.php, framework.php и файла конфигурации, создание приложения
Инициализация (Initialisation) onAfterInitialise — AfterInitialiseEvent создаются пользователь, сессия и язык; подключаются системные плагины
Маршрутизация и авторизация (Routing & Authorisation) onAfterRoute — AfterRouteEvent разбор адреса; проверка права на пункт меню
Диспетчеризация (Dispatching) onAfterDispatch — AfterDispatchEvent выполняется компонент, готовится документ
Рендеринг (Rendering) onBeforeRender, onBeforeCompileHead, onAfterRender — BeforeRenderEvent, BeforeCompileHeadEvent, AfterRenderEvent буферы документа выводятся в плейсхолдеры jdoc:include шаблона
Вывод (Response) onAfterCompress, onBeforeRespond, onAfterRespond — AfterCompressEvent, BeforeRespondEvent, AfterRespondEvent сжатие, заголовки и тело ответа

Плагины из этого доклада стоят на двух этапах: подключение ассетов — на рендеринге, в событии onBeforeCompileHead, а проба адреса и редирект — на маршрутизации, в событии onAfterRoute. Событие onBeforeRespond в статье не названо; класс BeforeRespondEvent в 6.1.4 есть.

3.4 Контейнер зависимостей и сервис-провайдеры

О слове «внедрение зависимостей». Термин предложил Мартин Фаулер в 2004 году, разбирая, как объекты получают то, от чего зависят. Раньше объект сам создавал нужное ему — базу, почту, журнал — и потому был намертво связан с ними; при внедрении зависимостей он получает готовое снаружи. Контейнер есть место, где записаны способы создать такие вещи; слово «сервис» в его языке означает любой подобный объект, а «провайдер» — класс, который способы записывает. Практическая польза проста: чтобы подменить базу данных в тесте, достаточно заменить одну запись в контейнере.

Контейнер зависимостей (DI-контейнер, DIC) доступен через Factory::getContainer(). Базу данных, пользователей, почту, маршрутизатор берут из него по имени интерфейса стенд:

<?php
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseInterface;

$db = Factory::getContainer()->get(DatabaseInterface::class);

Глобальный контейнер собирается в Factory::createContainer() из списка сервис-провайдеров. В статье портала jpath.ru 2022 года их девятнадцать [21] сторонний; в 6.1.4 их двадцать три: к прежним добавились провайдеры CaptchaRegistry, EditorsRegistry, Input и Mailer исходники. Та же статья напоминает, что контейнер не заменяет Factory прямо: в контроллерах вместо Factory::getDocument() правильнее $this->app->getDocument(), и тогда класс можно проверять подменой приложения [21] сторонний. У каждого компонента свой контейнер, из которого берут фабрики моделей: вызов Factory::getApplication()->bootComponent('com_content')->getMVCFactory()->createModel('Articles', 'Site') работает и на сайте, и в админке, и в API [21] сторонний; этот приём применён в главе о содержимом при записи материала из кода.

Расширения современного вида приносят файл services/provider.php, который возвращает анонимный класс с интерфейсом ServiceProviderInterface и записывает в контейнер способ создать плагин, модуль или компонент. Образцом служат плагины ядра: из 159 их провайдеров все 159 пользуются set(), а 153 — ещё и lazy(), так что плагин создаётся при первом обращении стенд; у system/skipto настройки берутся из PluginHelper::getPlugin(), а у task/globalcheckin дополнительно подаётся база данных через setDatabase() исходники. Статья портала для Joomla 4 показывает метод share() [21] сторонний; в 6.1.4 он существует рядом с set(), lazy(), protect(), alias() и extend() стенд, но в провайдерах плагинов ядра принята связка set() и lazy(). Рабочие провайдеры для трёх своих плагинов приведены ниже, в главах о командной строке и о приёмах кода.

3.5 Карта неймспейсов

О слове «неймспейс». Английское namespace — «пространство имён»: приём, при котором два класса с одним названием не сталкиваются, потому что лежат в разных «пространствах». В PHP оно записывается через обратную косую черту: Joomla\Component\Content\Administrator\Table\ArticleTable. Четвёртая по счёту рекомендация группы PHP-FIG, PSR-4, добавила к приёму договорённость об автозагрузке: начало имени соответствует каталогу, остальное — пути к файлу. Joomla следует этой договорённости, поэтому по имени класса всегда ясно, где искать файл.

Какой префикс какому каталогу соответствует, Joomla знает из файла administrator/cache/autoload_psr4.php: в нём, например, Joomla\Component\Content\Administrator\ сопоставлен administrator/components/com_content/src стенд. Файл строит класс JNamespacePsr4Map, а вызывает его метод приложения createExtensionNamespaceMap() исходники. Веб-приложения и консоль делают это сами при запуске; в собственном сценарии, который лежит вне этой цепочки, вызов нужен вручную.

Автозагрузчик запоминает промах. Если в сценарии до загрузки карты спросить class_exists() о классе компонента, автозагрузчик запишет, что такого класса нет, и не найдёт его даже после загрузки карты. Порядок такой: сначала createExtensionNamespaceMap(), потом любые проверки классов стенд.

Рабочая заготовка автономного сценария, проверенная на 6.1.4, — она повторяет cli/joomla.php, но вместо запуска команд оставляет приложение в распоряжении сценария стенд:

<?php
define('_JEXEC', 1);
define('JPATH_BASE', dirname(__DIR__));            // сценарий лежит в cli/
require_once JPATH_BASE . '/includes/defines.php';
require_once JPATH_BASE . '/includes/framework.php';

use Joomla\CMS\Factory;

$container = Factory::getContainer();
$container->alias('session', 'session.cli')
    ->alias('JSession', 'session.cli')
    ->alias(\Joomla\CMS\Session\Session::class, 'session.cli')
    ->alias(\Joomla\Session\Session::class, 'session.cli')
    ->alias(\Joomla\Session\SessionInterface::class, 'session.cli');

$app = $container->get(\Joomla\Console\Application::class);
Factory::$application = $app;
$app->createExtensionNamespaceMap();   // до любых class_exists() по классам расширений

Две подробности пригодятся при записи данных. Модель материала в автономном сценарии и в консольной команде требует константу JPATH_COMPONENT: определяет её только ComponentHelper::renderComponent() при веб-вызове компонента исходники, и без неё загрузка формы падает с «Undefined constant …_COMPONENT» стенд. Статья портала jpath.ru 2024 года считает, что в Joomla 4 и выше константу задавать не нужно [22] сторонний; в консольной команде 6.1.4 это не подтвердилось стенд. А метки при сохранении через модель попадают в базу, только если до сохранения подключены плагины группы behaviour — об этом в главе о содержимом.

3.6 События и плагины

Событие есть именованный момент работы приложения («после маршрутизации», «перед сборкой заголовка страницы», «перед сохранением материала»), а плагин — подписчик, который откликается на него. В 6.1.4 события описаны типизированными классами: в каталоге libraries/src/Event лежат 216 файлов, сгруппированных по областям — Application, Content, User, Model, Table, Workflow, WebAsset, Finder и так далее исходники. Подписчик объявляет интерфейс SubscriberInterface и возвращает из getSubscribedEvents() карту «событие → метод»; метод получает объект своего типа события, например BeforeCompileHeadEvent или AfterRouteEvent стенд.

Старый способ — публичные методы с именами вида onContentPrepare — пока поддержан: базовый класс плагина допускает слушателей старого образца (legacy listeners), но для каждого такого плагина пишет предупреждение об устаревании «The plugin should implement SubscriberInterface» исходники. Консольное приложение при запуске подключает плагины трёх групп — behaviour, system и console, — а приложение API подключает группу webservices исходники. Из этого следует практическое правило: команда командной строки живёт в плагине группы console, маршрут API — в плагине группы webservices.

Плагины сведены в 24 группы (число плагинов в скобках): system (24), webservices (18), fields (18), content (10), schemaorg (9), task (9), editors-xtd (8), quickicon (8), privacy (6), installer (5), finder (5), multifactorauth (5), user (5), extension (4), authentication (3), behaviour (3), editors (3), media-action (3), workflow (3), api-authentication (2), sampledata (2), actionlog, captcha и filesystem (по одному) стенд.

3.7 Что объявлено устаревшим

Часть привычных вызовов в 6.1.4 и 6.2.0 ещё работает, но помечена к удалению в 7.0 или в 8.0. Новый код на них писать не стоит.

Устаревший вызов Чем заменить Основание
Factory::getUser() $app->getIdentity() для текущего пользователя; UserFactoryInterface::loadUserById() для любого @deprecated 4.3, удаление в 7.0 исходники
Factory::getDbo() Factory::getContainer()->get(DatabaseInterface::class) @deprecated 4.3, удаление в 7.0 исходники
Factory::getDocument(), getSession(), getLanguage(), getConfig(), getCache(), getMailer() те же сведения из приложения или контейнера @deprecated 4.3 и 4.4, удаление в 7.0 исходники
$db->getQuery(true) $db->createQuery() параметр $new объявлен устаревшим исходники
$app->triggerEvent() (в рецептах для Joomla 4 вместо JEventDispatcher) диспетчер событий приложения с объектом события @deprecated 4.0, удаление в 7.0 исходники
класс Crypt и его методы timingSafeCompare(), safeStrlen(), safeSubstr(); класс SodiumCipher замены в описаниях не названы @deprecated 6.2.0, удаление в 8.0 исходники
MailTemplate::getTemplate(), createTemplate(), updateTemplate(), deleteTemplate() без замены @deprecated 6.2.0, удаление в 8.0 исходники
ApplicationController::sendtestmail() в компоненте «Конфигурация» MailController, новый в 6.2.0 @deprecated 6.2 исходники
поле text в ответе API для статей читать introtext и fulltext @deprecated 6.2.0, удаление в 8.0, без замены исходники
Document::addStyleSheet(), addScript(), addStyleDeclaration(), addScriptDeclaration() Web Asset Manager @deprecated 4.3, удаление в 7.0 исходники
классы вида JFactory, JLoader::register(…) классы с пространствами имён псевдонимы живут в плагине compat6 исходники

Не всё из старого устарело: методы addScriptOptions(), addCustomTag() и addHeadLink() у документа не помечены, а помощник HTMLHelper::_('bootstrap.tab', …) и родственные ему (modal, collapse, dropdown, offcanvas, popover, toast и другие) в 6.1.4 на месте исходники.

4 Web Asset Manager: как подключать скрипты и стили

О слове «ассет». Английское asset — «имущество, достояние»; оно пришло из старофранцузского asez («довольно, достаточно»), а тот восходит к латинскому ad satis — «до достаточности». Сперва так называли имущество, достаточное, чтобы покрыть долг. В вёрстке и в играх ассетами стали называть всё, чем пользуется программа, кроме кода: картинки, шрифты, скрипты, стили. В Joomla ассет — это запись о скрипте или стиле вместе с его зависимостями, а не сам файл. Поэтому «подключить ассет» значит сообщить менеджеру, что запись нужна странице; файл он выведет сам, в нужном порядке и один раз.

До Joomla 4 простого способа указать, что один скрипт нуждается в другом, не было; Web Assets ввели понятие ресурса и менеджер, который разрешает зависимости [23] документация. Менеджер — класс Joomla\CMS\WebAsset\WebAssetManager; получить его можно у документа стенд:

<?php
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

В шаблоне та же сущность доступна как $this->getWebAssetManager().

Оговорка об источнике. Основа главы — статья портала jpath.ru 2021 года; сам портал помечает её, как и соседние статьи своего раздела, словом «Устарело» [24]. Поэтому всё взятое оттуда сверено здесь с реестрами и кодом 6.1.4: одни положения подтвердились, у других нашлись расхождения, и они названы по месту.

4.1 Два состояния ассета

У ассета два состояния: зарегистрированный и используемый [23] документация. Зарегистрированный ассет только занесён в реестр: менеджер знает, что такой есть, но на страницу не выводит. Используемый ассет менеджер при сборке страницы выводит вместе с его зависимостями. Обратиться к незарегистрированному ассету нельзя: это вызывает исключение UnknownAssetException. Всё, что описано в joomla.asset.json, находится в первом состоянии, пока код не попросит его.

4.2 Где живут реестры

Описания ассетов лежат в файлах joomla.asset.json. В 6.1.4 их 38: общий реестр сторонних библиотек media/vendor/joomla.asset.json, системный media/system/joomla.asset.json, а ещё у ряда компонентов и плагинов и у обоих шаблонов стенд. Системный реестр содержит 65 записей (57 скриптов и 8 стилей), реестр библиотек — 57 (38 скриптов, 12 стилей и 7 пресетов), у Cassiopeia — 12, у админской Atum — 15 исходники. Руководство 2021 года перечисляло ещё и media/legacy/joomla.asset.json [23]; в пакете 6.1.4 такого файла нет исходники. Позднее определение перекрывает раннее по имени ассета: этим пользуются, чтобы заменить адрес скрипта, не трогая код расширения [23] документация.

Имена, которые стоит знать (проверены по реестрам 6.1.4) исходники:

Группа Имена
ядро страницы core, keepalive, messages, form.validate, showon, searchtools, multiselect, table.columns, list-view, joomla.dialog
Bootstrap bootstrap.alert, .button, .carousel, .collapse, .dropdown, .modal, .offcanvas, .popover, .scrollspy, .tab, .toast
jQuery jquery, jquery-noconflict, jquery-migrate
библиотеки choicesjs, awesomplete, cropperjs, dragula, qrcode, hotkeysjs, metismenujs, minicolors, altcha, sa11y, skipto
веб-компоненты webcomponent.joomla-alert, webcomponent.joomla-tab, webcomponent.field-fancy-select и родственные
шаблон template.cassiopeia, template.active, template.user (скрипт и стиль)

В примере из руководства 2021 года стоит имя fields.validate; в системном реестре 6.1.4 такого имени нет, а проверка форм зовётся form.validate исходники. Старые примеры стоит сверять с реестром, прежде чем копировать.

4.3 Методы менеджера

Методы строятся по образцу «действие + тип», и этот образец виден в коде: имя метода, начинающееся с use, disable, register или add, разбирается магическим вызовом и пересылается типовому методу исходники. Из этого выводится весь набор.

Задача Вызов
включить зарегистрированный useScript('имя'), useStyle('имя'), usePreset('имя')
зарегистрировать registerScript(), registerStyle(), registerPreset()
зарегистрировать и включить registerAndUseScript(), registerAndUseStyle()
инлайн-код addInlineScript(), addInlineStyle()
отключить disableScript(), disableStyle(), disablePreset()
спросить assetExists(), isAssetActive(), getAssetState()

Подпись регистрации одна для скриптов и стилей: имя, адрес, параметры (['version' => 'auto']), атрибуты тега (['defer' => true]), зависимости (['core']) [23] документация. Инлайн-код можно поставить до или после зависимости параметром ['position' => 'before'] или 'after', и менеджер выведет его в нужном месте относительно файла [23] документация. Если отключить ассет, от которого зависят другие, менеджер включит его снова: зависимость сильнее запрета [23] документация.

Пресет — особый ассет, который включает сразу несколько других; в списке тип указывается после решётки: core#script, foobar#style [23] документация. Веб-компоненты регистрируются с флагом webcomponent и грузятся не обычным скриптом, а асинхронным загрузчиком; имя для них принято начинать со слова webcomponent. [23] документация. Наконец, для особых нужд можно подставить собственный класс элемента: он решает, например, какой файл выдать для текущего языка, и через WebAssetAttachBehaviorInterface добавляет параметры скрипта в момент подключения — до события onBeforeCompileHead [23] документация.

4.4 Короткий путь «расширение/файл»

Приём. Адрес ассета можно записать коротко — расширение/файл, — и менеджер сам подставит папку css или js: адрес plg_system_wmlab/wmlab.css превращается в /media/plg_system_wmlab/css/wmlab.css. Если по привычке написать папку самому — plg_system_wmlab/css/wmlab.css, — менеджер ищет css/css/…, ничего не находит и молча не выводит тег. Ошибки при этом нет стенд.

Причина такого поведения — раскладка файлов, которой требует помощник HTMLHelper: /media/расширение/тип/файл, где тип — css, js или images [25] сторонний. Запись com_foo/bar.css при опции relative разворачивается в /media/com_foo/css/bar.css, а тот же файл можно переопределить в шаблоне по адресу /templates/{шаблон}/css/com_foo/bar.css [25] сторонний; на стенде 6.1.4 сработал другой адрес — /media/templates/site/{шаблон}/css/com_foo/bar.css стенд. В коде 6.1.4 поиск идёт сперва по самому шаблону, затем по родителю дочернего шаблона и лишь потом по каталогу media исходники. Менеджер ассетов пользуется тем же помощником, поэтому короткий адрес у него тот же.

Проверено на плагине группы system, который подписан на событие «перед сборкой заголовка» стенд:

<?php
public function attachAssets(BeforeCompileHeadEvent $event): void
{
    $app = $this->getApplication();

    if (!$app->isClient('site')) {
        return;
    }

    $wa = $app->getDocument()->getWebAssetManager();

    $wa->registerAndUseStyle('wmlab.style', 'plg_system_wmlab/wmlab.css', ['version' => 'auto']);
    $wa->registerAndUseScript('wmlab.script', 'plg_system_wmlab/wmlab.js', ['version' => 'auto'], ['defer' => true], ['core']);
    $wa->addInlineScript('document.documentElement.dataset.wmlab = "1";', ['position' => 'after'], [], ['wmlab.script']);
}

На главной странице чистой 6.1.4 получились три строки в <head> стенд:

<link href="/media/plg_system_wmlab/css/wmlab.css?5c832e" rel="stylesheet">
<script src="/media/plg_system_wmlab/js/wmlab.js?5c832e" defer></script>
<script>document.documentElement.dataset.wmlab = "1";</script>

Суффикс ?5c832e — не версия файла, а общая версия медиафайлов сайта: тот же суффикс у всех ассетов ядра на той же странице. Параметр version => auto велит менеджеру взять именно её. Рабочий гайд проекта выводит отсюда порядок сброса кэшей: пересохранить общие настройки — это меняет версию медиафайлов — и лишь потом чистить остальное [20] практика.

О режиме отладки статья 2021 года пишет, что помощник подставляет к имени файла суффикс -uncompressed [25] сторонний. В коде 6.1.4 такое имя встречается только в описании параметров: помощник подбирает пару имя.js и имя.min.js, и выбор между ними зависит от режима отладки, а при включённом detectDebug предпочитаются несжатые файлы исходники. Свои скрипты и стили поэтому отдают в двух видах — app.js и app.min.js.

4.5 Что Cassiopeia грузит по умолчанию

На главной странице чистой установки 6.1.4 шаблон Cassiopeia выводит стили joomla-fontawesome (ленивой загрузкой), template, colors_standard, mod-menu, joomla-alert и скрипты core, template, menu, keepalive, passwordview, messages — большей частью модулями ES. Ни jQuery, ни Bootstrap JS в списке нет стенд. Нужный компонент подключают явно, одним из двух путей: ассетом ($wa->useScript('bootstrap.tab')) или помощником HTMLHelper::_('bootstrap.tab', '.selector', []), который в 6.1.4 по-прежнему существует исходники. Первый путь предпочтителен: он прозрачен для менеджера и для проверки зависимостей.

Файлы media/templates/site/cassiopeia/css/user.css и js/user.js в чистой установке не созданы, но ассеты template.user для них заведены в реестре шаблона: положив файл, его подключают без единой строки кода исходники. Этот приём описан ещё в 2022 году для Joomla 4.1 [26] сторонний.

5 Шаблоны, дочерние шаблоны и переопределения

5.1 Два шаблона и их позиции

В Joomla 6 два основных шаблона: Cassiopeia для сайта и Atum для админки; рядом в пакете лежит третий — Cassiopeia Extended, дочерний стенд. Шаблон отвечает за каркас страницы и за позиции — именованные места, куда администратор ставит модули. В Cassiopeia 6.1.4 их шестнадцать исходники:

Область Позиции
шапка topbar, below-top, menu, search, banner, breadcrumbs
верх страницы top-a, top-b, main-top
колонки sidebar-left, sidebar-right
низ страницы main-bottom, bottom-a, bottom-b, footer
отладка debug

Раскладку Cassiopeia строит CSS Grid: в template.css 6.1.4 класс .site-grid встречается восемь раз исходники. Практический вывод сделал ещё автор статьи 2022 года: перестановка позиций на десктопе — несколько строк grid-template-areas в user.css, без правки PHP [26] сторонний. Оговорка: в той статье области сетки названы коротко (side-l, bot-a), а позиции модулей — полными именами (sidebar-left, bottom-a); сверять нужно по template.css своей версии.

5.2 Дочерние шаблоны

Дочерний шаблон наследует всё от родителя и меняет только то, что объявлено в нём самом. Механизм появился в Joomla 4.1, и вместе с ним пути ресурсов шаблона переехали из templates/cassiopeia/ в media/templates/site/cassiopeia/ [26] сторонний; файл user.css, созданный в старом месте, при обновлении был перенесён автоматически. В манифесте родителя Cassiopeia стоит <inheritable>1</inheritable> — разрешение быть родителем исходники. Дочерний шаблон создаётся кнопкой в разделе шаблонов сайта [26] сторонний, после чего его назначают шаблоном по умолчанию.

С версии 6.0 в ядре есть готовый образец — Cassiopeia Extended [7] документация. Он устроен показательно исходники:

  • в манифесте указан <parent>cassiopeia</parent> и <inheritable>0</inheritable>: потомок сам родителем быть не может;
  • в каталоге шаблона лежат только component.php, error.php, index.php, offline.php, html/ и templateDetails.xml, а стили и изображения вынесены в media/templates/site/cassiopeia_extended/;
  • манифест объявляет свои параметры: на вкладке «Дополнительно» — brand, logoFile, siteTitle, siteDescription, fluidContainer, stickyHeader, backTop; на вкладке цветов — двенадцать полей от headerbg до footercolor; на вкладке шрифтов — выбор схемы и системные шрифты для текста и заголовков.

Из этого образца вытекает общее правило: параметры шаблона заводятся полями в templateDetails.xml дочернего шаблона, читаются в его index.php через $this->params->get('имя') и выводятся там, где нужно; так делает и статья 2022 года для собственных скриптов в <head> [26] сторонний.

Дочерний шаблон решает лишь половину задачи — отличает облик сайта от родительского; вторую половину делают стили. Стиль (template style) есть шаблон плюс набор его параметров, и назначается он пункту меню. Так разделу «Экономика» можно дать один вид, спецпроекту — шапку без колонок, архиву — компактный; переключается это одним полем «Стиль шаблона», без условий в коде исследование (часть 5 «Дочерние шаблоны» [27]). Там же, на настоящей установке 6.1.4, показано, как кнопкой создаётся дочерний шаблон и как рядом с двумя штатными стилями заводится третий, с другими цветами и шрифтами; показано и то, как интерактивный лонгрид встраивается в материал и открывается на своём стиле.

5.3 Переопределения

О слове «переопределение». Английское override значит «перекрыть, отменить решение вышестоящего»; в языках программирования так называют подмену поведения без правки оригинала. В Joomla переопределение — это файл-копия макета вывода (layout) в каталоге html/ шаблона, который система берёт вместо ядерного. Оригинал остаётся нетронутым, поэтому обновление ядра не стирает вашу правку; цена этой свободы — обязанность следить, не изменился ли оригинал.

Переопределение создают в разделе шаблонов сайта на вкладке «Создание переопределений»: выбирают, например, mod_custom, и в templates/<шаблон>/html/mod_custom/default.php появляется копия макета [26] сторонний. Если файл переименовать и положить рядом, он станет альтернативным макетом — его можно выбрать в параметрах модуля. Так один модуль получает несколько видов, которые выбирают в его параметрах. Отдельный слой — стили модуля: в Cassiopeia это card и noCard, а ядро добавляет html5, none, outline и table стенд: два первых лежат в templates/cassiopeia/html/layouts/chromes/, остальные — в общем каталоге layouts/chromes/.

Чтобы не пропустить изменение оригинала после обновления, в ядре есть пара плагинов: installer/override и quickicon/overridecheck; второй, по описанию, проверяет переопределения при обновлении и выводит уведомление на панели управления, но только если включён первый стенд. Рядом в базе есть таблица #__template_overrides стенд. Рабочее правило проектов автора строже: переопределение сверяют с ядерным макетом построчно, а штатные параметры (лимит знаков вступления, число столбцов) должны продолжать работать, а не подменяться настройкой, живущей только в шаблоне практика.

За идеями удобно ходить в коллекцию из 57 переопределений для модулей mod_articles_news, mod_articles_category, mod_articles_popular, mod_articles_latest и mod_stats: она открыта и показывает, как далеко можно уйти от стандартной разметки без единой правки ядра [28] сторонний.

5.4 Cassiopeia без PHP

Многое делается одним user.css. Переменные цвета объявлены в template.css 6.1.4 как --cassiopeia-color-primary, --cassiopeia-color-hover и --cassiopeia-color-brand исходники; статья 2022 года показывает, как переопределить их глобально и для отдельной страницы через CSS-класс страницы, который задаётся в параметрах пункта меню [26] сторонний. Классы для блога категории, названные в той же статье, в 6.1.4 на месте: boxed, image-left, image-right, image-bottom, image-alternate, columns-2…4 и masonry-2…4, а для обтекания изображений — float-start и float-end исходники. В 6.2 у вводного и полного изображения появятся отдельные поля классов для самого img и для окружающего figure исходники.

Ещё два приёма из той же статьи, сохранившие силу. Подвал сайта показывает две языковые константы, MOD_FOOTER_LINE1 и MOD_FOOTER_LINE2, и обе переопределяются в «Переопределении констант» без правки модуля [26] сторонний. Логотип в одном ряду с меню получают отдельным модулем «HTML-код» в позиции menu [26] сторонний.

Особняком стоят шаблонные фреймворки сторонних студий. Статья 2022 года о T4 Framework 2.1.3 описывала поддержку разметки Open Graph для статьи, категории и пункта меню и страницу автора с блоком в статье [29] сторонний; на 12 октября 2026 года на сайте студии есть отдельная страница о подготовке к Joomla 6. Совместимость любого стороннего шаблона и расширения с шестой веткой проверяют по версии именно вашего экземпляра, а не по историческим заметкам. Сводку по конструкторам и шаблонным фреймворкам на 8 октября 2026 года даёт исследование автора исследование (глава 14 «Шаблоны и конструкторы» [10]). YOOtheme Pro 5 совместим с Joomla 6; Astroid 3.3.0 — свободный фреймворк с несколькими макетами; Gantry 5.6.0 — первый выпуск новой команды Tiger12 после закрытия RocketTheme 30 июня 2025 года; Helix Ultimate на Bootstrap 5 популярен в странах СНГ; SP Page Builder 6.0.0 берёт динамическое содержимое из материалов. Для портала с десятками модулей на главной исследование советует собственный лёгкий шаблон на UIkit 3 с переопределениями макетов ядра, а не конструктор страниц: накладные расходы конструкторов на такой странице заметны исследование.

5.5 Свой макет модуля с ассетами

Статья 2022 года предлагала вставлять CSS и JS в переопределённый макет модуля старым способом — методами документа addStyleSheet() и addScriptDeclaration() [26] сторонний. В 6.1.4 они всё ещё есть, но помечены к удалению в 7.0, поэтому тот же приём записывают через менеджер ассетов. Образец ниже на стенде не запускался и приведён как схема:

<?php
// templates/<шаблон>/html/mod_custom/wm_reveal.php — альтернативный макет модуля
defined('_JEXEC') or die;

use Joomla\CMS\Factory;

/** @var \stdClass $module */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

$wa->addInlineStyle('.wm-reveal{opacity:0;transform:translateY(8px);transition:opacity .4s,transform .4s}.wm-reveal.is-in{opacity:1;transform:none}');
$wa->addInlineScript('document.addEventListener("DOMContentLoaded",()=>{const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add("is-in");io.unobserve(e.target)}}),{threshold:.5});document.querySelectorAll(".wm-reveal").forEach(el=>io.observe(el))});');

echo '<div class="wm-reveal">' . $module->content . '</div>';

6 Содержимое: категории, метки, поля, версии

6.1 Категории и метки как рубрики

Категории Joomla образуют вложенное дерево, и в таблице хранятся границы ветвей lft и rgt; после вставки через код дерево пересчитывают методом rebuild(), а #__assets и lft/rgt руками не правят — иначе ломаются права на весь сайт [20] практика. Метки тоже иерархичны: при создании через API у метки есть parent_id, и корню соответствует единица стенд. Для новостного сайта это даёт гибкое дерево рубрик: например, метка «экономика» с вложенными «нефть» и «металлы».

Исследование автора формулирует для этого правило двух осей исследование (глава 8 «Таксономия: костяк и дерево смыслов» [10]). Категории — немногочисленный устойчивый костяк отделов: у материала она одна, и она задаёт адрес, права, процесс публикации и макет. Метки — растущая семантика: у материала их сколько угодно, они образуют дерево и общие для всех типов содержимого. Страница метки — готовая посадочная страница со своим макетом, метаданными, изображениями и уровнем доступа. Пункт меню «Материалы по метке» умеет включать потомков и выбирать режим совпадения — «все метки» или «любая из меток». Право создавать метки отбирают у авторов и оставляют редакторам отделов, иначе метки множатся дублями и опечатками. Границы названы там же: у самого материала метки выводятся плоско, а у метки нет собственного процесса публикации — только статус и версия исследование.

О слове «таксономия». Греческое taxis — «порядок» и nomos — «закон». Швейцарский ботаник Огюстен Пирам Декандоль ввёл слово в 1813 году для классификации растений; затем оно перешло в библиотечное дело и в информационную архитектуру. В Joomla таксономия двухосная: категории задают структуру и адреса, метки — смысловую сеть поверх любого содержимого, и пересечение осей — «регион» и «тема» — собирается без единой строки кода.

О словах «алиас» и «слаг». Латинское alias значит «иначе, в другое время»; так называют второе имя вещи. В Joomla алиас — безопасное для адреса имя материала или пункта меню. Слово slug пришло из типографии: так называли строку набора, а в газетной редакции — короткое рабочее имя заметки; в веб-разработку его перенёс фреймворк Django. В Joomla «слагом» зовётся строка id:alias, на которой строятся ссылки: index.php?option=com_content&view=article&id=6:proba&catid=8 стенд. Модель списка материалов сайта в 6.1.4 слаг не отдаёт: на стенде материалы пришли без поля slug, и строку собирают из ID и алиаса; об этом же говорит и рабочий гайд проекта [20] стенд.

Ключ конфигурации unicodeslugs по умолчанию выключен стенд; пока он выключен, алиасы строятся латиницей, и русский заголовок получает транслитерированный адрес. Включённый ключ оставляет кириллицу в адресе: так устроено порождение алиаса в ArticleModel::save() исходники.

Связь материала с метками хранится в таблице #__contentitem_tag_map, а работают метки и история версий через два плагина группы behaviour — taggable и versionable стенд. Отсюда ловушка, которую рабочий гайд описывает так: метки, сохранённые из кода, молча не записываются, если до сохранения не подключены плагины поведения [20]. На стенде это воспроизводится точно стенд: статья, сохранённая через модель без подключения, получила ноль строк в карте меток; та же статья после PluginHelper::importPlugin('behaviour') получила три.

<?php
use Joomla\CMS\Plugin\PluginHelper;

define('JPATH_COMPONENT', JPATH_ADMINISTRATOR . '/components/com_content');  // нужна модели в автономном сценарии

PluginHelper::importPlugin('behaviour');    // до сохранения, иначе метки теряются

$model = $app->bootComponent('com_content')->getMVCFactory()
    ->createModel('Article', 'Administrator', ['ignore_request' => true]);

$ok = $model->save([
    'id' => 0, 'title' => 'Заголовок', 'alias' => 'zagolovok', 'articletext' => '<p>Текст</p>',
    'catid' => 8, 'state' => 1, 'access' => 1, 'language' => '*',
    'tags' => [2, 3, 4],                     // идентификаторы существующих меток
]);
$id = $ok ? (int) $model->getState('article.id') : 0;

Метки не вставляют прямым INSERT в #__contentitem_tag_map: не построится запись служебной таблицы UCM, и метка не будет работать полноценно [20] практика.

6.2 Материал из кода: форма, проверка, поля

Статья портала jpath.ru в редакции 31 июля 2024 года даёт полный порядок создания материала с настраиваемыми полями из консольного расширения [22] сторонний. Порядок такой: взять модель через фабрику компонента; получить форму материала и проверить данные; сымитировать задачу сохранения — $app->getInput()->set('task', 'save'); сохранить; затем записать значения полей моделью Field компонента com_fields, передав ID поля, ID материала и значение. Для списков и флажков со многими значениями значение передают массивом. Порядок воспроизведён на 6.1.4 консольной командой, и в нём нашлись три поправки стенд:

<?php
use Joomla\CMS\Factory;
use Joomla\CMS\User\UserFactoryInterface;

// внутри команды: в консоли константу никто не определяет
if (!\defined('JPATH_COMPONENT')) {
    \define('JPATH_COMPONENT', JPATH_ADMINISTRATOR . '/components/com_content');
}

$app   = Factory::getApplication();
$admin = Factory::getContainer()->get(UserFactoryInterface::class)->loadUserByUsername('admin');
$app->loadIdentity($admin);

$model   = $app->bootComponent('com_content')->getMVCFactory()->createModel('Article', 'Administrator', ['ignore_request' => true]);
$article = ['id' => 0, 'title' => 'Заголовок', 'alias' => '', 'introtext' => '<p>Текст</p>',
            'catid' => 8, 'state' => 0, 'language' => '*', 'access' => 1];

$form = $model->getForm($article, false);
if (!$form || !$model->validate($form, $article)) {
    // ошибка: $model->getError()
}

$app->getInput()->set('task', 'save');          // без этого пустой алиас не будет порождён
$model->save($article);
$id = (int) $model->getState('article.id');

// поля: canEditFieldValue() смотрит на Factory::getUser(), а не на loadIdentity()
Factory::getSession()->set('user', $admin);
$fields = $app->bootComponent('com_fields')->getMVCFactory()->createModel('Field', 'Administrator', ['ignore_request' => true]);
$ok = $fields->setFieldValue(2, $id, 'Интерфакс');   // номер поля, номер материала, значение

Первая поправка — константа JPATH_COMPONENT. Статья считает, что в Joomla 4 и выше её задавать не нужно; в консольной команде 6.1.4 без неё форма не грузится, потому что константу определяет только ComponentHelper::renderComponent() при веб-вызове компонента стенд исходники. Вторая касается алиаса. Имитация задачи нужна ради него: когда алиас пуст, а задача — save, модель строит его из заголовка, и при включённом ключе unicodeslugs оставляет кириллицу, а при выключенном транслитерирует исходники. Повтор того же заголовка в той же категории модель встречает ошибкой COM_CONTENT_ERROR_UNIQUE_ALIAS стенд. На стенде с одним английским языком заголовок «Статья из консольной команды» получил алиас stata-iz-konsolnoj-komandy: итог транслитерации зависит от языкового пакета сайта стенд.

Третья поправка самая коварная. Метод setFieldValue() возвращает false молча, без исключения и без сообщения, если пользователь не прошёл проверку права core.edit.value; а проверка в 6.1.4 идёт через устаревший Factory::getUser(), который в консоли не видит пользователя, загруженного loadIdentity(), даже когда это суперпользователь. На стенде помогла запись пользователя в сессию, как в примере: после неё проверка прошла, метод вернул true, и значение легло в таблицу #__fields_values стенд. Поэтому возвращаемое значение метода проверяют всегда.

Поля материала сверены с таблицей. В 6.1.4 в таблице #__content тридцать столбцов: id, asset_id, title, alias, introtext, fulltext, state, catid, created, created_by, created_by_alias, modified, modified_by, checked_out, checked_out_time, publish_up, publish_down, images, urls, attribs, version, ordering, metakey, metadesc, access, hits, metadata, featured, language, note стенд. Перечень, который статья даёт для массива сохранения, с ними совпадает; виртуальное поле articletext столбцом не служит.

Поле Что хранит
state 0 — не опубликован, 1 — опубликован, 2 — в архиве, -2 — в корзине стенд
articletext общий текст; модель делит его на introtext и fulltext по разрыву <hr id="system-readmore"> сторонний
images JSON с ключами image_intro, float_intro, image_intro_alt, image_intro_caption и теми же четырьмя для полного текста сторонний
urls JSON: urla, urlatext, targeta и такие же тройки для b и c; цель — 0 в том же окне, 1 в новом, 2 модально сторонний
metadata JSON с ключами robots, author, rights сторонний
created, modified, publish_up, publish_down дата и время вида ГГГГ-ММ-ДД ЧЧ:ММ:СС; без значения берётся момент создания сторонний
created_by, created_by_alias автор по ID пользователя и подменная подпись сторонний

6.3 Настраиваемые поля

В пакете 6.1.4 восемнадцать типов настраиваемых полей (custom fields): calendar, checkboxes, color, editor, imagelist, integer, list, media, note, number, radio, sql, subform, text, textarea, url, user, usergrouplist стенд. В 6.0 появились note и number, в 6.1 поле media научили выводить звук, видео и документы [7, 14] документация. Поле привязано к контексту — строке вида com_content.article, com_users.user, com_contact.contact: одинаковое устройство полей для статей, категорий, контактов и пользователей видно по маршрутам API, где у каждого контекста свой путь /v1/fields/… стенд.

Описания и подсказки полей — данные сайта, а не шаблона: они живут в таблицах #__fields и #__fields_values, и править их файлами шаблона нельзя, только SQL, сидером или админкой [20] практика.

Читать поля в макете. Фрагмент для Joomla 3 в авторской подборке [30] сторонний брал поля автора материала через JLoader::register('FieldsHelper', …) и JFactory::getUser(), а нужное поле выбирал по номеру в массиве. В Joomla 6 тот же приём записывается классами с пространствами имён и выбором по имени поля стенд:

<?php
// templates/<шаблон>/html/com_content/article/default.php
use Joomla\CMS\Factory;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;

$author = Factory::getContainer()->get(UserFactoryInterface::class)
    ->loadUserById((int) $this->item->created_by);

$bio = null;
foreach (FieldsHelper::getFields('com_users.user', $author, true) as $field) {
    if ($field->name === 'bio') {
        $bio = $field->value;           // значение уже подготовлено к выводу
    }
}

Третий параметр true просит подготовить значение к выводу; вернётся массив объектов с полями name, title, value стенд. Выбор по имени надёжнее выбора по номеру: порядок полей в админке меняют.

Поля в API. Значения настраиваемых полей читаются и пишутся обычными атрибутами с именем поля: PATCH с телом {"istochnik": "Reuters"} записал значение, а в ответе на GET поле пришло как istochnik — и в карточке, и в списке стенд. Создание самого поля через API требует двух вещей: явного context и непустых params; без context запись создаётся в базе с пустым контекстом, вызов отвечает 404, а «призрачное» поле остаётся и должно быть удалено вручную стенд.

Современный выбор из списка. В админских формах многострочный выбор рисуют атрибутом layout="joomla.form.field.list-fancy-select": макет есть в ядре, а за ним стоят ассеты choicesjs и webcomponent.field-fancy-select исходники. Пример для поля в XML формы:

<field name="topics" type="list" multiple="true"
       layout="joomla.form.field.list-fancy-select" label="Рубрики">
    <option value="economy">Экономика</option>
    <option value="energy">Энергетика</option>
</field>

6.4 Версии, рабочий процесс и связи

Версионирование, которое раньше охватывало только таблицу материала, в 6.0 расширено на значения настраиваемых полей, а по обучающему туру — на метки и категории; в 6.1 ту же возможность получили модули, и их экземпляры теперь можно связывать между языками [7, 14] документация. Рабочий процесс (workflow) — этапы и переходы публикации — давно есть в ядре: три плагина группы workflow называются featuring, publishing и notification стенд. В 6.1 процесс получил визуальный редактор: включают рабочие процессы в менеджере статей и ищут кнопку с диаграммой исходники. Редакционный конвейер на этих средствах собран и снят в исследовании автора (глава 7 «Конвейер новости» [10]; часть 2 «Процесс «Новости»: стадии, переходы, права» [27]) исследование. Четыре роли — репортёр, редактор отдела, корректор и главный редактор для материалов особой важности — заводятся четырьмя группами, детьми группы Registered; отделы — категориями; процесс «Новости» состоит из пяти этапов (черновик, на редактуре, на корректуре, на утверждении, опубликовано) и девяти переходов. У каждого перехода четыре вкладки: откуда и куда, действия, письма и права доступа; право «выполнить переход» раздаётся группам и решает, кто вправе опубликовать материал, а кто обязан отправить его главному редактору. Включают процессы одной настройкой компонента «Материалы» на вкладке «Интеграция»; выключить их можно в любой момент без потери содержания, потому что состояния публикации и этапы хранятся порознь исследование.

Три границы того же разбора стоит запомнить. Переходы не ветвятся по значению поля, поэтому «особая важность» — это выбор редактора или отдельная категория, а не флажок. Комментариев на полях у ядра нет, и замечания корректора живут в заметке к версии. Процесс закрепляется за материалом при первом сохранении, а сменить его потом может только суперпользователь пакетной операцией «Установить этап», поэтому категорию важного материала задают сразу исследование.

6.5 Похожие материалы

В ядре есть два готовых модуля — mod_related_items и mod_tags_similar («Похожие теги» в русском интерфейсе), который собирает блок «читать по теме» с настраиваемой близостью совпадения меток стенд исследование; прежде чем писать собственный запрос, их стоит проверить на своих данных. Рецепт из авторской подборки — «10 похожих публикаций» для Joomla 4 [31] сторонний — исходит из общих меток; его же в Joomla 6 записывают конструктором запросов (query builder) createQuery() с привязкой параметров вместо склейки строк стенд:

<?php
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\ParameterType;

$db  = Factory::getContainer()->get(DatabaseInterface::class);
$cur = (int) $article->id;                       // текущий материал

$query = $db->createQuery()
    ->select([$db->quoteName('c.id'), $db->quoteName('c.title'),
              'COUNT(' . $db->quoteName('m2.tag_id') . ') AS ' . $db->quoteName('shared')])
    ->from($db->quoteName('#__contentitem_tag_map', 'm1'))
    ->join('INNER', $db->quoteName('#__contentitem_tag_map', 'm2')
        . ' ON ' . $db->quoteName('m2.tag_id') . ' = ' . $db->quoteName('m1.tag_id')
        . ' AND ' . $db->quoteName('m2.type_alias') . ' = ' . $db->quoteName('m1.type_alias'))
    ->join('INNER', $db->quoteName('#__content', 'c')
        . ' ON ' . $db->quoteName('c.id') . ' = ' . $db->quoteName('m2.content_item_id'))
    ->where($db->quoteName('m1.content_item_id') . ' = :cur')
    ->where($db->quoteName('m1.type_alias') . ' = ' . $db->quote('com_content.article'))
    ->where($db->quoteName('m2.content_item_id') . ' <> :cur2')
    ->where($db->quoteName('c.state') . ' = 1')
    ->group([$db->quoteName('c.id'), $db->quoteName('c.title'), $db->quoteName('c.created')])
    ->order([$db->quoteName('shared') . ' DESC', $db->quoteName('c.created') . ' DESC'])
    ->bind(':cur', $cur, ParameterType::INTEGER)
    ->bind(':cur2', $cur, ParameterType::INTEGER);

$db->setQuery($query, 0, 10);                    // не более десяти
$items = $db->loadObjectList();

На стенде для материала с метками 2, 3 и 4 запрос вернул две статьи с двумя общими метками каждая, упорядочив их по числу общих меток и по дате стенд. Ссылку к найденному материалу строит RouteHelper::getArticleRoute($id . ':' . $alias, $catid, $language): она даёт строку вида index.php?option=com_content&view=article&id=6:proba&catid=8; её пропускают через Route::_() стенд. Для боевого сайта к запросу добавляют условия по access, language, publish_up и publish_down; здесь они опущены для краткости.

6.6 Счётчики у меток и категорий

Две практические задачи портала jpath.ru — показать число материалов рядом с пунктом меню категории и рядом с меткой — решены там для Joomla 3 переопределением макетов [32] [33] сторонний. В 6.1.4 первая решается так же, а вторая требует поправки.

Пункт меню категории. Макет, который выводит пункт типа «компонент», называется modules/mod_menu/tmpl/default_component.php и в 6.1.4 существует исходники; его копию кладут в templates/<шаблон>/html/mod_menu/. Число берут у объекта категорий с включённым подсчётом стенд:

<?php
use Joomla\CMS\Categories\Categories;

$numItems = Categories::getInstance('Content', ['countItems' => 1])->get((int) $item->query['id'])->numitems;

На стенде для категории с ID 8 результат — пять; прямой подсчёт опубликованных материалов в ней дал то же число стенд. Подсчёт делает запрос к базе, поэтому его применяют к небольшому числу пунктов [32] сторонний.

Метка. Макет меток — layouts/joomla/content/tags.php исходники; его переопределяют в templates/<шаблон>/html/layouts/joomla/content/. Рецепт 2021 года вызывает TagsHelper::getTagItemsQuery($tag->tag_id, 1) статически, считая единицу ID типа содержимого [33] сторонний. В 6.1.4 статический вызов падает с ошибкой «Non-static method … cannot be called statically»: метод стал нестатическим. На экземпляре он работает, и тип по-прежнему задаётся ID — 1 или [1]; описание метода говорит об алиасах, но вызов с алиасом падает с «A key is required» стенд. К тому же рецепт делает по запросу на каждую метку. Вместо этого за один запрос получают числа для всех меток сразу стенд:

<?php
$query = $db->createQuery()
    ->select([$db->quoteName('m.tag_id'), 'COUNT(*) AS ' . $db->quoteName('n')])
    ->from($db->quoteName('#__contentitem_tag_map', 'm'))
    ->join('INNER', $db->quoteName('#__content', 'c') . ' ON ' . $db->quoteName('c.id') . ' = ' . $db->quoteName('m.content_item_id'))
    ->where($db->quoteName('m.type_alias') . ' = ' . $db->quote('com_content.article'))
    ->where($db->quoteName('c.state') . ' = 1')
    ->group($db->quoteName('m.tag_id'));

$counts = [];
foreach ($db->setQuery($query)->loadObjectList() as $row) {
    $counts[(int) $row->tag_id] = (int) $row->n;       // номер метки => число материалов
}

На стенде этот запрос вернул для меток 2, 3 и 4 числа 2, 3 и 2 стенд.

7 Web Services API

7.1 Что это и где живёт

Web Services API (веб-службы, REST-интерфейс) — отдельная точка входа api/index.php, через которую программа читает и меняет содержимое сайта без админки. Слово «API» без уточнения означает в докладе именно его; программный интерфейс самого ядра на PHP портал jpath.ru зовёт «Joomla API» и делит на API CMS, API компонентов ядра и API сторонних библиотек [19] сторонний. Адреса имеют вид /api/index.php/v1/<область>/<ресурс>. На стенде 6.1.4 ядро регистрирует 236 маршрутов: 101 для GET, 48 для POST, 45 для PATCH и 42 для DELETE стенд. В 6.2.0 тот же подсчёт даёт столько же маршрутов стенд. Маршруты приносят плагины группы webservices; в чистой установке их 18, и все включены стенд. Области охвата: материалы и категории, метки, настраиваемые поля и их группы, меню и их пункты, модули, стили шаблонов, пользователи с группами и уровнями доступа, языки и переопределения, конфигурация сайта и компонентов, расширения и плагины, обновление ядра, медиафайлы, баннеры, контакты, ленты, редиректы, сообщения и запросы о персональных данных; подробная карта дана в приложении Б.

О словах «REST» и «JSON: API». Аббревиатуру REST — «передача состояния представления» — ввёл Рой Филдинг в докторской диссертации 2000 года. Так он назвал набор ограничений, которым подчиняется устройство сети Веб; позднее его именем стали называть программные интерфейсы, где ресурс имеет адрес, а действие выражено методом протокола — читать, создать, изменить, удалить. JSON: API — соглашение, которое идёт на шаг дальше и фиксирует форму самого ответа: объект с полями data, links, meta, а внутри ресурса — type, id, attributes и relationships. Его медиатип записывается как application/vnd.api+json; приставка vnd. обозначает дерево изготовителя в реестре медиатипов. Joomla использует его для ответов о ресурсах; служебные ответы могут быть устроены иначе — отказ удалить опубликованную запись, например, вернул объект с полями success, messages и data стенд.

Типичный ответ списка выглядит так стенд:

{
  "links": {"self": ".../v1/content/articles?page[limit]=2", "next": "...", "last": "..."},
  "data": [
    {"type": "articles", "id": "2",
     "attributes": {"title": "Вторая статья", "alias": "stat-2", "state": 1, "language": "*", "tags": {"2": "экономика", "3": "нефть"}},
     "relationships": {"category": {"data": {"type": "categories", "id": "8"}}}}
  ],
  "meta": {"total-pages": 2}
}

7.2 Ключ доступа: токен

О словах «токен» и «HMAC». Английское token восходит к староанглийскому tācen — «знак, примета»; в сетевой безопасности так называют «жетон»: предъявив его, объект доказывает право на доступ. HMAC — «код проверки подлинности сообщения на основе хеша» — описан в RFC 2104 в 1997 году: от сообщения и секретного ключа берётся хеш такого вида, что подделать его, не зная ключа, нельзя. Joomla вычисляет HMAC от заготовки токена с секретом сайта, поэтому токен нельзя выдумать, а при смене секрета он перестаёт действовать.

Для входа через API служит группа плагинов api-authentication. В ней два плагина: token включён, basic по умолчанию выключен стенд. Токен выдаёт плагин пользователя user/token: он добавляет в профиль пользователя поля токена — включение и само значение исходники. Алгоритм прочитан в исходниках исходники:

  1. При создании токена в таблице #__user_profiles для пользователя сохраняется зерно — 32 случайных байта в кодировке base64 — под ключом joomlatoken.token, а признак включения — под ключом joomlatoken.enabled.
  2. Выдаваемый токен — base64 от строки алгоритм:ID_пользователя:HMAC, где HMAC вычислен от зерна секретом сайта (ключ secret в configuration.php); допустимые алгоритмы — sha256 и sha512.
  3. Токен передают либо в заголовке Authorization: Bearer <токен> — слово Bearer с заглавной буквы и пробел после него обязательны, — либо в заголовке X-Joomla-Token.
  4. Плагин раскодирует токен, пересчитывает HMAC по зерну пользователя и сравнивает за постоянное время, чтобы по длительности нельзя было угадать совпадение; затем проверяет, что токен включён и что пользователь состоит в разрешённой группе. По умолчанию разрешена группа 8 — в стандартной установке это Super Users.

Практические следствия исходники. Запрос выполняется от имени владельца токена, и права определяются его группами. Токен привязан к секрету сайта: если сменить secret, все выданные токены перестанут действовать — это следует из алгоритма. Токен надо хранить как пароль.

Путь запроса через Web Services APIКлиентAuthorization: Bearer … или X-Joomla-Tokenapi/index.php → ApiApplicationплагины webservices регистрируют 236 маршрутовПлагин api-authentication/tokenbase64 → алгоритм : ID : HMAC; зерно из профиляПроверки токенатокен включён · группа разрешена (по умолчанию 8)ApiRouterпуть v1/… и метод → контроллер.действиеКонтроллер и модель компонентазапрос идёт с правами владельца токенаОтвет в формате JSON:APIlinks · data · meta; коды 200, 204, 4xxнет или неверный токен401 Forbiddenмаршрута нет404 Resource not found
Рис. 3. Путь запроса через Web Services API: от заголовка с токеном до ответа в формате JSON:API.

Как ведёт себя проверка, показали пробные запросы стенд: запрос без токена, с неверным токеном и с написанным строчными bearer получает 401 и тело {"errors":[{"title":"Forbidden"}]}; заголовок X-Joomla-Token с тем же токеном даёт 200.

7.3 Первые запросы

Чтение списка с постраничным выводом стенд:

TOKEN='…'     # токен из профиля пользователя
curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.api+json" \
  "https://example.kz/api/index.php/v1/content/articles?page[limit]=2&page[offset]=0"

Создание материала. Текст передаётся в поле articletext, метки — списком идентификаторов стенд:

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"title":"Первая статья","alias":"pervaya","articletext":"<p>Текст</p>",
       "catid":8,"language":"*","state":1,"access":1,"tags":[2,3]}' \
  "https://example.kz/api/index.php/v1/content/articles"

Успешное создание отвечает 200, а не 201. Правка частичная: PATCH с одним полем меняет только его. Удаление двухступенчатое: опубликованный материал нельзя удалить сразу, ответ 409 с пояснением «must be trashed before it can be deleted»; сначала PATCH со state равным -2, затем DELETE, который отвечает 204 стенд. Через тот же токен работает и мобильная подача репортёра: создание материала — запрос POST /api/index.php/v1/content/articles; в исследовании автора этот путь описан как часть редакционного конвейера исследование (глава 7 «Конвейер новости» [10]).

7.4 Что умеют списки

Параметр Что делает Проверено
page[limit], page[offset] постраничный вывод; в meta приходит total-pages, в links — next и last стенд
filter[search] поиск; на стенде нашёл статью по слову из заголовка стенд
filter[category], filter[tag] отбор по категории и метке стенд
filter[state], filter[language] отбор по состоянию и по языку стенд
list[ordering], list[direction] сортировка, например title и DESC стенд
fields[articles]=id,title разрежённый набор полей не сократил выдачу стенд

Последняя строка — не ошибка таблицы: параметр fields[articles] в 6.1.4 список материалов не сокращает, приходят все атрибуты. Лишний вес ответа приходится резать на стороне клиента.

7.5 Ловушки API

Метка без description не создаётся. POST /v1/tags без parent_id отвечает 400 Invalid field: Parent, а без description — 400 с текстом базы «Field 'description' doesn't have a default value». Второе зависит от строгого режима базы: на нестрогом сервере вызов мог бы пройти стенд.

Поле без context оставляет «призрак». При создании настраиваемого поля нужно явно указать context (например, com_content.article) и непустые params; без context запись попадает в базу с пустым контекстом, вызов отвечает 404, а «призрачное» поле потом приходится удалять руками стенд.

Встроенный сервер PHP для API не годится. Запущенный через php -S, API на всех маршрутах отвечает 404 Resource not found, потому что приложение не может вывести базовый путь из переменных такого сервера. Под Apache те же запросы идут без единой правки стенд.

Две оговорки о заголовке авторизации исходники: плагин ищет токен в HTTP_AUTHORIZATION, а при пустом значении — в REDIRECT_HTTP_AUTHORIZATION и, под mod_php, в apache_request_headers(). Стандартный файл htaccess.txt содержит правило, которое передаёт заголовок Authorization в PHP (E=HTTP_AUTHORIZATION) исходники; если сервер всё же убирает заголовок, остаётся запасной X-Joomla-Token.

7.6 Своя точка API

Новый маршрут добавляет плагин группы webservices: он подписывается на событие onBeforeApiRoute и просит у маршрутизатора набор CRUD-маршрутов. Подпись метода прочитана в ядре: createCRUDRoutes($baseName, $controller, $defaults = [], $publicGets = false); она создаёт чтение списка и записи, а также создание, правку и удаление, и четвёртый параметр открывает чтение без токена исходники.

О слове «CRUD». Четыре буквы — Create, Read, Update, Delete: создать, прочесть, изменить, удалить. Перечень четырёх действий над записью утвердился в языке баз данных в 1980-е годы, а вместе с ним и сокращение. В Joomla это не отвлечённое слово, а название метода: createCRUDRoutes порождает сразу все маршруты записи по одному базовому адресу.

Схема повторяет плагин webservices/tags из ядра; на стенде эта часть не запускалась исходники:

<?php
// plugins/webservices/zametki/src/Extension/Zametki.php
public static function getSubscribedEvents(): array
{
    return ['onBeforeApiRoute' => 'onBeforeApiRoute'];
}

public function onBeforeApiRoute(BeforeApiRouteEvent $event): void
{
    $router = $event->getRouter();
    $router->createCRUDRoutes('v1/zametki', 'zametki', ['component' => 'com_zametki']);
}

Контроллер, наследующий ApiController, и представление в формате JSON: API лежат в каталоге api/components/com_zametki/; руководство по этому вопросу есть на русском среди переводов документации [34] сторонний.

7.7 Кросс-доменные запросы (CORS)

О слове «CORS». Cross-Origin Resource Sharing — «совместное использование ресурсов между источниками». Браузер считает «источником» сочетание схемы, хоста и порта и по умолчанию не даёт странице одного источника читать ответы другого: страницу чужого сайта нельзя превратить в посредника для запросов к вашему API. Правила смягчения записаны в заголовках ответа, которые сервер ставит сам; ранее они жили в отдельном отчёте консорциума W3C, а ныне — в стандарте Fetch.

Для API в configuration.php предусмотрены ключи cors, cors_allow_origin, cors_allow_methods и cors_allow_headers; по умолчанию cors выключен стенд. Приложение API умеет отвечать на предварительный запрос браузера (метод OPTIONS): за это отвечает метод handlePreflight() в ApiApplication исходники. Включать CORS стоит только вместе с перечнем доверенных источников, а не со звёздочкой.

7.8 Безопасность API

Все 18 плагинов webservices включены по умолчанию стенд. На сайте, где API не нужен, их отключают, а где нужен — выдают токен отдельному интеграционному пользователю с минимальными правами, а не владельцу сайта практика. Токен передают только по HTTPS и ограничивают адрес и роль пользователя; при подозрении на утечку токен отключают в профиле или меняют secret.

8 Командная строка и планировщик задач

8.1 Команды ядра

Консольное приложение запускается файлом cli/joomla.php; в 6.1.4 и в 6.2.0 набор команд одинаков и содержит 36 именованных команд стенд.

Область Команды Для чего
кэш и настройки cache:clean, config:get, config:set очистка кэша, чтение и запись ключей конфигурации
обновление ядра core:update, core:update:check (псевдоним core:check-updates), core:update:channel, core:autoupdate:register, core:autoupdate:unregister обновить, проверить, выбрать канал, включить или выключить автообновление
база данных database:export, database:import выгрузка и загрузка
расширения extension:install, extension:list, extension:enable, extension:disable, extension:remove, extension:discover, extension:discover:list, extension:discover:install установка и управление
поиск finder:index перестроить индекс Умного поиска (Smart Search)
обслуживание maintenance:database, session:gc, session:metadata:gc, update:extensions:check, update:joomla:remove-old-files проверка структуры, уборка сессий, обновления расширений, удаление старых файлов
планировщик scheduler:list, scheduler:run, scheduler:state задачи по расписанию
сайт site:down, site:up, site:create-public-folder офлайн-режим, публичная папка
пользователи user:add, user:addtogroup, user:removefromgroup, user:delete, user:list, user:reset-password управление учётными записями

Одна из команд требует осторожности. core:autoupdate:register регистрирует сайт в службе автоматических обновлений проекта; по тексту установщика, при регистрации проекту передаются адрес сайта, версия PHP, тип и версия базы данных, версия CMS и сведения об операционной системе исходники. Установщик Joomla предлагает автообновления при установке и позволяет отказаться от них исходники.

8.2 Планировщик

О слове «cron». Название демона расписаний Unix образовано, как принято считать, от греческого chronos — «время». Файл расписания называется crontab, а строка расписания из пяти полей — минуты, часы, день месяца, месяц, день недели — стала языком, на котором описывают повторяющиеся действия и далеко за пределами Unix.

Планировщик Joomla — компонент com_scheduler с таблицей #__scheduler_tasks; в ней, среди прочего, есть признак cli_exclusive — задача только для командной строки стенд. Исполнителями служат плагины группы task: в пакете их девять — checkfiles, deleteactionlogs, globalcheckin, privacyconsent, requests, rotatelogs, sessiongc, sitestatus, updatenotification стенд. Системный плагин schedulerunner отвечает за ленивый запуск (при заходах на сайт), вебкрон и запуск по кнопке исходники; для надёжного расписания используют команду стенд:

*/5 * * * * /usr/bin/php /var/www/site/cli/joomla.php scheduler:run --all >/dev/null 2>&1

В чистой установке заведены три задачи: «Rotate Logs» (следующий запуск через месяц), «Session GC» и «Update Notification» стенд. Ключ --id у scheduler:run — ID задачи в таблице, а не порядковый номер в списке: на стенде команда --id=1 запустила не мою задачу, а «Rotate Logs», потому что ID с 1 по 3 заняты задачами ядра. Журнал исполнения пишется в administrator/logs/joomla_scheduler.php стенд. Редакции полезны две задачи по расписанию: ночная переиндексация Smart Search командой finder:index и сборка файла карты сайта. У Smart Search по умолчанию пакет индексации равен тридцати единицам, а подсказки отключают, если на сайте есть закрытые материалы исследование (глава 10 «Высокая нагрузка на практике» [10]).

8.3 Своя команда: плагин группы console

Команда живёт в плагине группы console: приложение подключает эту группу при запуске и объявляет событие application.before_execute, в ответ на которое плагин добавляет команды исходники. Заготовка из трёх файлов проверена на 6.1.4 стенд. Манифест:

<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="console" method="upgrade">
    <name>plg_console_wmlab</name>
    <version>1.0.0</version>
    <description>Пример команды командной строки</description>
    <namespace path="src">Lab\Plugin\Console\Wmlab</namespace>
    <files>
        <folder plugin="wmlab">services</folder>
        <folder>src</folder>
    </files>
</extension>

Провайдер сервисов (services/provider.php). Он создаёт плагин, подаёт ему диспетчер событий и приложение; запись устроена по образцу плагинов ядра — set() с lazy(), и плагин создаётся при первом обращении стенд:

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Lab\Plugin\Console\Wmlab\Extension\Wmlab;

return new class () implements ServiceProviderInterface {
    public function register(Container $container): void
    {
        $container->set(
            PluginInterface::class,
            $container->lazy(Wmlab::class, function (Container $container) {
                $plugin = new Wmlab(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('console', 'wmlab')
                );
                $plugin->setApplication(Factory::getApplication());

                return $plugin;
            })
        );
    }
};

Плагин (src/Extension/Wmlab.php) подписывается на событие и добавляет команду:

<?php
namespace Lab\Plugin\Console\Wmlab\Extension;

defined('_JEXEC') or die;

use Joomla\Application\ApplicationEvents;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Lab\Plugin\Console\Wmlab\Command\HelloCommand;

final class Wmlab extends CMSPlugin implements SubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [ApplicationEvents::BEFORE_EXECUTE => 'registerCommands'];
    }

    public function registerCommands(): void
    {
        $this->getApplication()->addCommand(new HelloCommand());
    }
}

Сама команда (src/Command/HelloCommand.php) в стиле команд ядра: имя задаётся свойством $defaultName, работа — в doExecute():

<?php
namespace Lab\Plugin\Console\Wmlab\Command;

defined('_JEXEC') or die;

use Joomla\CMS\Version;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

final class HelloCommand extends AbstractCommand
{
    protected static $defaultName = 'wmlab:hello';

    protected function doExecute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->success(sprintf('Привет, %s! Joomla %s', $input->getOption('name'), (new Version())->getShortVersion()));

        return 0;
    }

    protected function configure(): void
    {
        $this->setDescription('Пример команды из плагина группы console');
        $this->addOption('name', null, InputOption::VALUE_OPTIONAL, 'Имя', 'мир');
    }
}

Пакет собирают в архив, ставят командой extension:install --path=… и включают плагин; после этого команда видна в списке своей области имён и работает стенд:

$ php cli/joomla.php wmlab:hello --name=Бейбит
 [OK] Привет, Бейбит! Joomla 6.1.4

8.4 Своя задача планировщика

Плагин группы task делает три вещи: объявляет тип задачи, исполняет её и пишет в журнал. Всё остальное берёт на себя типаж TaskPluginTrait. Провайдер повторяет предыдущий с заменой группы и класса, а плагин выглядит так стенд:

<?php
namespace Lab\Plugin\Task\Wmlab\Extension;

defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent;
use Joomla\Component\Scheduler\Administrator\Task\Status as TaskStatus;
use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait;
use Joomla\Event\SubscriberInterface;

final class Wmlab extends CMSPlugin implements SubscriberInterface
{
    use TaskPluginTrait;

    protected const TASKS_MAP = [
        'plg_task_wmlab_hello' => [
            'langConstPrefix' => 'PLG_TASK_WMLAB_HELLO',   // PLG_TASK_WMLAB_HELLO_TITLE и _DESC в языковом файле
            'method'          => 'hello',
        ],
    ];

    protected $autoloadLanguage = true;

    public static function getSubscribedEvents(): array
    {
        return [
            'onTaskOptionsList' => 'advertiseRoutines',
            'onExecuteTask'     => 'standardRoutineHandler',
        ];
    }

    private function hello(ExecuteTaskEvent $event): int
    {
        $this->logTask('wmlab hello task started');
        file_put_contents(JPATH_ROOT . '/tmp/wmlab-task.txt', date('c') . "\n", FILE_APPEND);

        return TaskStatus::OK;
    }
}

Языковой файл plg_task_wmlab.ini обязан содержать PLG_TASK_WMLAB_HELLO_TITLE и PLG_TASK_WMLAB_HELLO_DESC: по ним тип задачи показан в админке. Метод задачи возвращает код состояния — TaskStatus::OK или другой — и именно он попадает в счётчики. Результат на стенде стенд: задача отработала за доли секунды, в таблице стали times_executed = 1 и last_exit_code = 0, следующий запуск назначен через пять минут, а в журнал легли три строки: «Running task#04…», «Task> wmlab hello task started» и «Successfully finished task#04…». Строку задачи на стенде завели вручную записью в таблицу; в работе её создают в админке через форму «Новая задача».

9 Приёмы кода для расширений

9.1 Анатомия расширения

О слове «манифест». Латинское manifestus значит «явный, очевидный»; через итальянское manifesto слово попало в мореходное дело, где так называли перечень груза, поданный таможне. Манифест расширения Joomla делает то же самое: это XML-файл в корне пакета, который объявляет, что внутри — какие папки, какие файлы, какой язык, какие настройки, — и без которого установщик не знает, что с пакетом делать.

Современное расширение — каталог с манифестом, папкой services/ и папкой src/, где лежат классы с пространством имён. Манифест системного плагина, проверенный на 6.1.4, показывает все основные узлы стенд:

<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="system" method="upgrade">
    <name>plg_system_wmlab</name>
    <version>1.0.0</version>
    <description>Пример: ассеты, редирект и адрес</description>
    <namespace path="src">Lab\Plugin\System\Wmlab</namespace>
    <files>
        <folder plugin="wmlab">services</folder>
        <folder>src</folder>
    </files>
    <media destination="plg_system_wmlab" folder="media">
        <folder>css</folder>
        <folder>js</folder>
    </media>
</extension>

Узел <namespace path="src"> связывает префикс с папкой и попадает в карту неймспейсов при установке; plugin="wmlab" у папки services называет файл плагина; <media> копирует ресурсы в media/<назначение>/, откуда их потом берёт менеджер ассетов. Установка из командной строки — php cli/joomla.php extension:install --path=пакет.zip; плагин по умолчанию отключён и включается в админке или командой extension:enable стенд.

9.2 Системный плагин: события, ассеты, редирект

Провайдер сервисов системного плагина повторяет провайдер консольного из предыдущей главы, с заменой группы и класса стенд. Сам плагин подписывается на два события — сборку заголовка страницы и конец маршрутизации — и показывает приёмы, которые чаще всего нужны стенд:

<?php
namespace Lab\Plugin\System\Wmlab\Extension;

defined('_JEXEC') or die;

use Joomla\CMS\Event\Application\AfterRouteEvent;
use Joomla\CMS\Event\Application\BeforeCompileHeadEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Uri\Uri;
use Joomla\Event\SubscriberInterface;

final class Wmlab extends CMSPlugin implements SubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            'onBeforeCompileHead' => 'attachAssets',
            'onAfterRoute'        => 'probe',
        ];
    }

    public function attachAssets(BeforeCompileHeadEvent $event): void
    {
        // см. главу о Web Asset Manager
    }

    public function probe(AfterRouteEvent $event): void
    {
        $app  = $this->getApplication();
        $mode = $app->getInput()->getCmd('wmlab');

        if (!$app->isClient('site') || $mode === '') {
            return;
        }

        if ($mode === 'redirect') {
            $app->enqueueMessage('Сообщение после редиректа', 'message');
            $app->redirect(Uri::root() . 'index.php?wmlab=url', 303);
        }

        if ($mode === 'url') {
            $app->enqueueMessage('URL: ' . Uri::getInstance()->toString(), 'notice');
        }
    }
}

9.3 Редирект и адрес текущей страницы

Рецепт из авторской подборки — сообщение посетителю и редирект в Joomla 4 — в 6.1.4 остаётся верным [31] сторонний. Сигнатура метода в ядре — redirect($url, $status = 303): второй параметр есть код состояния HTTP исходники. Привычка из Joomla 3 — передавать вторым аргументом текст сообщения — теперь ломает запрос: redirect($url, 'Сообщение', 'message') на стенде дала 500 с текстом «You have not supplied a valid HTTP status code» стенд. Правильнее ставить сообщение в очередь отдельным вызовом, а в redirect передавать адрес и код стенд:

<?php
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;

$app = Factory::getApplication();
$app->enqueueMessage('Текст, который увидит посетитель после перехода', 'message');
$app->redirect(Route::_('index.php?option=com_content&view=featured'), 303);

На стенде проверка шла с полным адресом Uri::root() . 'index.php?wmlab=url': ответ пришёл как 303 See other с заголовком Location, а на следующей странице сообщение показано стенд; для адресов с человекочитаемыми ссылками (SEF) внутренний адрес пропускают через Route::_(). Типы сообщений — message, notice, warning, error.

Адрес текущей страницы получают так стенд:

<?php
use Joomla\CMS\Uri\Uri;

$current = Uri::getInstance()->toString();    // например, http://сайт/index.php?wmlab=url
$root    = Uri::root();                       // корень сайта

9.4 База данных

Работа с базой в Joomla 6 строится на трёх правилах, и каждое подтверждено на стенде стенд. Соединение берут из контейнера по интерфейсу DatabaseInterface, а не через устаревшее Factory::getDbo(). Запрос строят вызовом $db->createQuery(); значения подставляют параметрами ->bind(':имя', $значение, ParameterType::INTEGER), а не склейкой строк; имена таблиц и столбцов оборачивают в quoteName(), префикс таблиц записывают как #__. Результат запроса читают loadResult(), loadObjectList() и их родственниками; ограничение по числу строк — вторым и третьим параметрами setQuery($query, $offset, $limit).

Для записи в таблицы компонентов гайд проекта советует поднимать классы таблиц прямо, а не через устаревшие фабрики: например, \Joomla\Component\Content\Administrator\Table\ArticleTable [20] практика; существование класса в 6.1.4 подтверждено стенд. Правила от того же гайда: после вставки категории вызывать rebuild() дерева; #__assets и столбцы lft/rgt руками не править; точечные правки на живом сайте делать SQL или админкой, а перед пакетными UPDATE снимать дамп затронутых таблиц [20] практика.

9.5 Формы

Форма пункта меню — ловушка, которая обошлась проекту дважды: форма вычищает параметры, не объявленные в XML макета. Поэтому XML каждого собственного макета должен быть полным: человеческое название (прямой строкой или константой с парой строк в .ini и .sys.ini), блок <fields name="request"> с оригинальными полями и все используемые параметры как поля формы; названия типов в окне выбора берутся из .sys.ini [20] практика. Для языковых файлов порядок загрузки таков: глобальная копия из language/xx-XX/ побеждает копию в каталоге шаблона; копию шаблона держат байт-в-байт равной глобальной, а для каждого языка заводят пару .ini и .sys.ini [20] практика.

9.6 Скрипты вне веб-контекста

Всё нужное для автономного сценария уже собрано в главе об устройстве ядра и в главе о содержимом. Остаётся напомнить три бытовых ловушки хостинга, которые, по гайду проекта, стоили часов работы [20] практика. Первая: open_basedir на панели HestiaCP режет доступ к /tmp, и путь для временных файлов задают свой. Вторая: при отладке веб-сценария меняют имя файла (seed2.php, seed3.php), иначе OPcache продолжает выдавать старый байткод. Третья: сессию в веб-режиме стартуют после присвоения Factory::$application, но до первого вывода, иначе придёт «headers already sent».

10 Ключи и безопасность

10.1 Ключи конфигурации

Главный файл настроек — configuration.php в корне сайта; его путь задаёт константа JPATH_CONFIGURATION исходники. В 6.1.4 в нём 67 ключей, и тот же набор стоит в 6.2.0 стенд. Читать и менять их можно и из консоли: php cli/joomla.php config:get sef вернёт таблицу из имени и значения, а config:set запишет новое значение стенд. Полный перечень с умолчаниями чистой установки дан в приложении А; здесь — ключи, от которых зависит безопасность.

Ключ В чистой установке Что от него зависит
secret случайная строка подпись токенов API; хранить как пароль, при смене все токены перестают действовать
password, smtppass, dbsslkey пароли и ключи доступ к базе и почте; файл configuration.php не должен читаться посторонними
force_ssl 0 принудительный HTTPS (все значения: выкл., только админка, весь сайт)
error_reporting default уровень сообщений об ошибках; в рабочем режиме — не maximum и не development
debug, debug_lang false отладка; в рабочем режиме выключена
cors, cors_allow_* false доступ к API с других источников
lifetime 15 время жизни сессии, минут
captcha '0' какая капча включена по умолчанию
session_handler database где хранятся сессии

10.2 Три вида «токена»

В Joomla слово «токен» встречается в трёх разных вещах, и путать их опасно. Первый — токен API, описанный в главе об API. Второй — токен формы: каждая страница содержит в параметрах скрипта значение csrf.token, и формы обязаны вернуть его при отправке; он живёт столько же, сколько сессия стенд. Третий — ключ доступа к обновлениям платных расширений (download key); за напоминание о нём отвечает плагин quickicon/downloadkey исходники, а к безопасности входа он отношения не имеет.

О слове «CSRF». Cross-Site Request Forgery — «подделка межсайтового запроса». Суть нападения: вы вошли на свой сайт, а на чужой странице скрыта форма, которая от вашего имени отправляет запрос на удаление статьи; браузер честно приложит ваши куки. Защита — токен формы: секретное значение, которое известно странице вашего сайта и неизвестно постороннему сайту, потому что тот не может прочитать вашу страницу. Нет токена в запросе — запрос отвергают.

10.3 Вход: многофакторная аутентификация и ключи доступа

В 6.1.4 пять методов второго фактора — плагины группы multifactorauth: email, fixed, totp, webauthn и yubikey; плагины входа — cookie, joomla, ldap; отдельный системный плагин webauthn обслуживает беспарольный вход стенд. Основа последнего — библиотека web-auth/webauthn-lib версии 4.5.2 исходники. Руководства по WebAuthn и по многофакторной аутентификации для Joomla 4 переведены на русский и собраны на «Лунной Базе» [34] сторонний.

О словах «TOTP» и «WebAuthn». TOTP — «одноразовый пароль по времени», стандарт RFC 6238 2011 года: приложение-аутентификатор и сервер знают общий секрет и каждые тридцать секунд независимо считают из него шестизначное число; совпало — вы это вы. WebAuthn — «веб-аутентификация», программный интерфейс браузера, принятый W3C как рекомендация в 2019 году в составе семейства FIDO2: вместо пароля сайт хранит открытый ключ, а закрытый остаётся в устройстве или в связке ключей пользователя. Отсюда выражение «ключ доступа» (passkey) — так называют такую пару. Подделать её фишингом нельзя: ключ привязан к адресу сайта.

10.4 Капча на доказательстве работы

О словах «CAPTCHA» и «доказательство работы». CAPTCHA — «полностью автоматизированный публичный тест Тьюринга для отличения компьютеров от людей»; сокращение закрепили исследователи университета Карнеги — Меллона в начале 2000-х. Доказательство работы (proof of work) — другая идея, из начала 1990-х. Тот, кто шлёт запрос, тратит небольшую вычислительную работу: для одного запроса она ничтожна, а для миллиона рассылок разорительна; на ней держится и Hashcash Адама Бэка 1997 года. Joomla 6.1 перенесла эту мысль в формы: браузер посетителя незаметно решает задачу и прикладывает результат.

В пакете 6.1.4 капча одна — плагин powcaptcha с классами Extension\POWCaptcha и Provider\POWCaptchaProvider стенд; движок называют Altcha, а ассет altcha стоит в реестре библиотек [17] сторонний. Включают в два шага: разрешают плагин в списке плагинов и выбирают его капчей по умолчанию в общих настройках, то есть записывают его имя в ключ captcha исходники; в чистой установке там '0' — капчи нет стенд.

10.5 Заголовки ответа

Системный плагин httpheaders включён по умолчанию стенд. Уже на главной странице чистой установки и в ответах API видны три заголовка: X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin и Cross-Origin-Opener-Policy: same-origin стенд. Настройки плагина охватывают X-Frame-Options, Referrer-Policy и COOP, любые дополнительные заголовки с выбором стороны (сайт или админка), HSTS (срок, поддомены, предзагрузка) и политику безопасности содержимого: включение, область применения, режим «только отчёты», одноразовые метки nonce, хеши скриптов и стилей, strict-dynamic, frame-ancestors 'self' и собственные директивы исходники.

О слове «nonce». Это сокращение английского number used once — «число, использованное один раз»: в Joomla его создают на каждый запрос и проставляют и в заголовок политики, и в тег <script nonce="…">. Браузер выполняет только скрипты с метками, которые назвал сервер, а злоумышленник, вставивший свой скрипт в страницу, метку угадать не может — на следующий запрос она будет другой.

10.6 Обновления: TUF и автоматический режим

Защищённая доставка обновлений через TUF существует с 5.1, автоматическое обновление ядра — с 5.4 и 6.0 [7] документация. Автообновление действует только внутри мажорной ветки: переход «с 5.4 на 6.x» и «с 6.4 на 7.x» выполняют вручную в админке или внешним средством [14] документация. В консоли за это отвечают core:update:channel и пара core:autoupdate:register/unregister; про передаваемые при регистрации сведения сказано в главе о командной строке. В 6.2 к этому добавлен флаг безопасности у обновлений расширений: разработчик указывает важность от «нет» до «критической», и такие обновления подсвечиваются в списке и на значке панели управления исходники. По дорожной карте проекта поддержка безопасности ветки 4.x закончилась 14 октября 2025 года [11] документация; сайты на ней остались без исправлений безопасности.

10.7 Кто отвечает за безопасность ядра

За уязвимости ядра отвечает постоянная команда JSST — Joomla Security Strike Team: она принимает сообщения об уязвимостях, присваивает им идентификаторы CVE и выпускает исправления в плановых релизах. Отдельная политика проекта требует помечать код, написанный с помощью моделей ИИ, и запрещает слепую сдачу машинного кода без понимания; нарушители теряют доступ к репозиторию исследование (глава 2 «Что такое Joomla сегодня» [10]). Для редакции это значит, что ядро, на котором она строит сайт, проверяется людьми.

10.8 Лог действий и персональные данные

Для лога действий в ядре есть плагин actionlog и задача deleteactionlogs, для работы с персональными данными — одноимённый компонент, шесть плагинов группы privacy, плагин и задача privacyconsent стенд; запросы о данных и согласия доступны и через API по адресам /v1/privacy/requests и /v1/privacy/consents стенд.

10.9 Перечень для закалки

Короткий перечень для сайта, выходящего в работу; пункты, которые подтверждены стендом, помечены.

Пункт Основание
debug и error_reporting в рабочем режиме — по умолчанию умолчания чистой установки: false и default стенд
каталог installation удалён (консольный установщик удаляет его сам) вывод установщика: «Deleting /installation folder…OK» стенд
включена многофакторная аутентификация для администраторов пять методов в ядре стенд
на формах стоит капча на доказательстве работы плагин powcaptcha стенд
заголовки безопасности настроены под сайт, а CSP начат с режима «только отчёты» настройки плагина httpheaders исходники
отключены ненужные плагины webservices; токены выданы отдельным пользователям с минимальными правами все 18 включены по умолчанию стенд
перед обновлением снята выгрузка базы командой database:export команда есть в ядре стенд
сайт обновляется на копии, а потом на рабочем месте практика проекта практика

11 Производительность и масштаб

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

11.1 Кэш

О слове «кэш». Французское cache значит «тайник, укромное место» — от глагола cacher, «прятать». В вычислительном смысле слово закрепилось в 1960-е годы: так назвали малую быструю память, где процессор прячет копии недавно нужных данных, чтобы не ходить за ними в большую медленную. Всякий кэш держится на одном компромиссе: что-то можно взять быстро, но оно может устареть. Поэтому у каждого кэша есть срок годности.

Кэш Joomla настраивается ключами caching (по умолчанию 0 — выключено), cache_handler (по умолчанию file) и cachetime (по умолчанию 15 минут) стенд. Хранилищ в ядре четыре: файловое, APCu, Memcached и Redis исходники. Страничный кэш реализует системный плагин cache стенд.

11.2 Слои кэша на новостном сайте

Исследование автора описывает кэш Joomla как трёхслойный исследование (глава 10 «Высокая нагрузка на практике» [10]). Системный плагин «Кэширование страниц» хранит страницу целиком; консервативный режим кэширует вывод компонентов и тех модулей, которым кэширование разрешено; прогрессивный кэширует все модули и годится только сайтам без личных блоков. Хранилище — файлы, Redis или Memcached; в ядре 6.1.4 есть и APCu исходники. Ловушка для новостников: правка материала не сбрасывает кэш страниц, где он показан, — сброс делают вручную или через внешний слой исследование. Измерения журнала Joomla Community Magazine за апрель 2025 года, на которые опирается исследование, показали: сервер LiteSpeed снижает время до первого байта с 336 до 224 мс, а Redis в консервативном режиме ускоряет начало отрисовки с 0,9 до 0,6 с исследование. Цифры чужие и относятся к одному сайту, так что свои измерения они не заменяют.

О слове «CDN». Content Delivery Network — «сеть доставки содержимого»: распределённые серверы, которые отдают копии страниц и файлов ближе к читателю и принимают всплески трафика вместо сайта. По исследованию автора, найденные им новостные порталы на Joomla живут именно за такими сетями: Cloudflare у хорватских порталов и у Dnews, Akamai у CNN Greece и Newsbomb исследование. Обычная архитектура большого портала такова: сеть доставки принимает удар трафика, Redis держит объектный кэш, а база отвечает индексами.

11.3 Сессии

Сессии хранятся в базе (session_handler = database), живут lifetime = 15 минут, общей сессии между сайтами нет (shared_session = false), а метаданные сессий включены (session_metadata = true) стенд. Мусор убирают задача «Session GC» в планировщике и команды session:gc и session:metadata:gc стенд.

11.4 База данных

В таблице материалов #__content на стенде заведены индексы по первичному ключу и по столбцам access, checked_out, state, catid, created_by, language, alias, а также составной по featured и catid; в таблице меню — уникальный индекс по четвёрке client_id, parent_id, alias, language стенд. Составного индекса по state, catid и срокам публикации среди них нет. Добавлять его стоит только после измерения: сравнивают план запроса командой EXPLAIN до и после, на реальных данных сайта практика. Обсуждение #45737 в репозитории ядра, разобранное в исследовании автора, описывает сайт на Joomla 3 со 150 тыс. материалов в 1 705 категориях. Составной индекс по (catid, state) сократил там время главной страницы с 20 до 1 секунды, а замена LEFT JOIN на STRAIGHT_JOIN в выборке — время выбора двухсот материалов с 94 до 5 секунд исследование (глава 10 «Высокая нагрузка на практике» [10]). Предупреждение из того же обсуждения: индексные эксперименты, помогавшие большим сайтам, вредили обычным; поэтому измеряют до и после. Индекс поиска (таблицы #__finder_*) можно перестроить командой finder:index, и в дамп он не нужен [20] практика.

11.5 Поиск на большом сайте

Smart Search — поисковый движок на чистом PHP; для очень больших сайтов руководство советует выносить поиск в Solr. Индекс растёт с каждым материалом, а сохранение материала во время полной переиндексации может испортить индекс, поэтому полную переиндексацию ставят на ночь, в планировщик исследование (глава 10 «Высокая нагрузка на практике» [10]).

11.6 Страница и ассеты

Шаблон Cassiopeia выводит стили joomla-fontawesome ленивой загрузкой, а большинство скриптов — модулями ES; ни jQuery, ни Bootstrap JS по умолчанию не грузятся стенд. В Joomla 6 добавлен слой кэширования для разбора языковых файлов [7] документация. Адреса ассетов содержат общую версию медиафайлов, и браузер получает свежий файл после её смены стенд. Из этого складывается простой порядок экономии: не включать лишнего, подключать ассеты через менеджер, а не вставлять <script> в макеты, и измерять до и после каждой правки.

12 Языки, адреса и структурированные данные

12.1 Адреса: SEF

Ключи sef (включён), sef_rewrite (выключен), sef_suffix (выключен) и unicodeslugs (выключен) определяют вид адресов стенд. Для адресов без index.php в пути нужны sef_rewrite и переписывание адресов веб-сервером; в пакете лежит заготовка htaccess.txt, которую для Apache копируют в .htaccess исходники. Основа маршрутизации — пункты меню. Ключ пункта в таблице — четвёрка client_id, parent_id, alias, language; поля menutype в нём нет стенд. Из этого рабочий гайд выводит два правила: одинаковые алиасы в разных меню конфликтуют, а пункт-псевдоним на другой пункт-псевдоним не маршрутизируется и даёт ?Itemid=N вместо человекочитаемой ссылки; реестр статей поэтому ведут по ключу «категория/алиас», потому что тёзки в разных категориях законны [20] практика.

12.2 Несколько языков

За многоязычность отвечают плагины languagefilter и languagecode, модуль mod_languages и компонент «Многоязычные связи», который связывает материалы между языками стенд. В 6.1 связи получили и модули [14] документация, а языковые переопределения — подсказку о состоянии и быстрое создание недостающих исходники. Загрузку языковых строк в 6.0 ускорил кэширующий слой [7] документация.

Для двуязычных русско-казахских сайтов автор держится трёх правил. Имена людей на казахской версии пишут казахским написанием (с буквами ә, і, ұ и отчествами на -ұлы и -қызы), на русской — русским. Казахские переводы ориентируют на лексику советского периода, а не на новейшие неологизмы, а привычные международные слова вроде «алиас» оставляют как есть. «Пошта» остаётся «пошта»: это верная казахская форма, а не новообразование практика.

12.3 Русскоязычная среда и положение в РК

О слове «экосистема». Термин создал английский эколог Артур Тенсли в 1935 году: от греческих oikos — «дом, обиталище» и systema — «составленное целое». Он искал одно слово для сообщества живых существ вместе с неживой средой, от которой они зависят, — леса, пруда, луга. В 1993 году американский исследователь Джеймс Мур перенёс слово в деловой язык: «деловая экосистема» у него — сеть участников, которые зависят друг от друга и вместе создают то, чего не создал бы ни один из них. Отсюда мерка для разговора о Joomla: экосистема существует там, где разработчики расширений, переводчики, студии, хостеры и читатели поддерживают друг друга — одни пишут, другие переводят, третьи платят, четвёртые находят ошибки. Одиночки, даже сильные, экосистемы не образуют: уйдёт один — и на его месте пусто.

Русскоязычную среду Joomla в этом смысле можно назвать экосистемой исследование (глава 13 (русскоязычная среда и положение в РК) [10]). По числу сайтов русский язык стоит на третьем месте в мире после английского и немецкого: на нём написано 12,3% сайтов на Joomla. Русская локализация ведётся на GitHub командой JPathRu; пакет для 6.0.1 вышел 25 ноября 2025 года. Портал joomlaportal.ru сам работает на Joomla 6.1.4 и публикует новости о выпусках в день выхода. Рядом работают профессиональный чат и студии со своими расширениями — WebTolk, Joomline, RadicalMart, NorrNext. Звенья поддерживают друг друга: переводчики дают интерфейс, студии — расширения под новую ветку, портал и каналы — новости и ответы.

В РК картина иная. По оценке автора, экосистемы там пока нет: есть отдельные разработчики-одиночки, но нет среды, в которой переводы, расширения и обмен опытом поддерживают друг друга практика. Это видно по фактам, которые исследование автора зафиксировало на 8 октября 2026 года исследование. Казахский пакет kk-KZ последний раз выходил для Joomla 4.4.0 в ноябре 2023 года, а для пятой и шестой веток его нет. Kaspi Pay даёт официальный эквайринг через REST API, но готовых плагинов для магазинов на Joomla в открытых источниках не нашлось; официальный модуль Freedom Pay для JoomShopping проверялся на старых версиях магазина, и совместимость с Joomla 6 не подтверждена.

Смягчение даёт само ядро: с версии 6.2 запасной язык задаётся по строкам, и русский можно назначить запасным для казахского, так что непереведённые строки интерфейса покажутся по-русски, а не по-английски исследование. Содержание при этом переводят руками: связи лишь соединяют переводы материалов, категорий, меню и, с 6.1, модулей исследование. А пробел в платёжных системах исследование записывает и как свободную нишу: кто первым напишет плагин Kaspi Pay или Freedom Pay под JoomShopping 5 либо RadicalMart, получит преимущество исследование (глава 13 (русскоязычная среда и положение в РК) [10]).

12.4 Структурированные данные

О словах «schema.org» и «JSON-LD». Проект schema.org основан в 2011 году поисковыми компаниями Google, Microsoft и Yahoo, к которым затем присоединился «Яндекс»: они договорились об общем словаре для описания страниц — «статья», «организация», «событие», «рецепт». JSON-LD — способ записать такое описание обычным JSON внутри страницы; консорциум W3C принял его как рекомендацию в 2014 году. Поисковые и ответные системы читают эту разметку, не разбирая вёрстку, поэтому она служит для них ключом к смыслу страницы.

В Joomla 6 структурированными данными занимается системный плагин schemaorg и девять плагинов типов: article, blogposting, book, custom, event, jobposting, organization, person, recipe стенд. Уже на главной странице чистой установки есть блок JSON-LD с BreadcrumbList и идентификатором вида …/#/schema/BreadcrumbList/17 стенд. Метаразметки Open Graph на главной чистой установки нет: проверка нашла ноль тегов og: стенд; её добавляют шаблонным фреймворком, как это сделал T4 Framework в 2022 году [29] сторонний, или собственным плагином. Поиск по сайту — Smart Search с пятью плагинами индексации (categories, contacts, content, newsfeeds, tags), индекс перестраивает finder:index стенд.

Границу штатной разметки исследование автора нашло по исходникам ветки 6.1-dev: плагин article выводит жёстко заданный тип Article, а не NewsArticle; полей keywords и articleSection в его форме нет, и из материала плагин сам ничего не подставляет, лишь форматирует даты и картинку исследование (глава 11 «Видимость: поиск, схемы, карты сайта» [10]). Редакции, которая выпускает десятки новостей в день, остаётся выбор между готовым расширением с автозаполнением и собственным плагином типа NewsArticle, берущим keywords из меток, а articleSection из категории: архитектура плагинов типов такому плагину не мешает исследование.

Карт сайта ядро не строит. Исследование разбирает JL Sitemap — компонент, который заранее собирает карту в готовый файл sitemap.xml, так что обращение робота не нагружает базу. Версия 2.2.0 от 25 мая 2026 года пишет файлы атомарно: новая карта заменяет старую, только когда собрана целиком. Для новостной карты Google News названы OSMap, SchuWeb Sitemap и JSitemap Pro; плагин WT IndexNow извещает Яндекс и Bing о новых адресах; Quantum Manager 3.3.0 заменяет штатный Медиа-менеджер и совместим с Joomla 6 без плагина совместимости исследование (глава 11 «Видимость: поиск, схемы, карты сайта» [10]).

О слове «IndexNow». Протокол 2021 года, предложенный Microsoft и «Яндексом»: сайт сам извещает поисковые системы о новых и изменённых адресах, вместо того чтобы ждать обхода роботом. Слово составное: index — «указатель, перечень» и now — «сейчас»; смысл протокола в одном требовании — внести адрес в индекс немедленно, и для новостей это минуты вместо часов.

Для ответных систем на основе моделей ИИ автор в проектах добавляет к сборке сайта файлы llms.txt и llms.json и разметку Open Graph практика. Влияние таких мер на выдачу в этой проверке не измерялось, и доклад не обещает его.

13 Каталог приёмов

Приёмы собраны по областям. Каждый короток и снабжён знаком проверки; где нужна подробность, она дана в своей главе.

13.1 Консоль

  1. Чистый сайт одной командой. php installation/joomla.php install со всеми ключами — --site-name, --admin-user, --admin-username, --admin-password, --admin-email, --db-type, --db-host, --db-user, --db-pass, --db-name, --db-prefix, --no-interaction; так на стенде установлены обе версии стенд.
  2. Публичная папка. Ключ установщика --public-folder и команда site:create-public-folder выносят публичную часть из корня с кодом стенд.
  3. Список команд своей области. php cli/joomla.php list wmlab показывает только команды области wmlab; версия — php cli/joomla.php --version стенд.
  4. Ключ конфигурации из консоли. config:get sef печатает таблицу «имя — значение»; у команды есть параметр --group стенд.
  5. Все назревшие задачи. scheduler:run --all; задачу по ID — scheduler:run --id=N, и ID берут из scheduler:list стенд.
  6. Закрыть сайт на время работ. site:down и site:up стенд.
  7. Выгрузка и загрузка базы. database:export и database:import стенд.
  8. Перестроить поиск. finder:index стенд.

13.2 Ассеты

  1. Короткий адрес. расширение/файл — папку css или js Joomla подставит сама; лишняя папка в адресе — молчаливый отказ стенд.
  2. Инлайн после файла. addInlineScript($код, ['position' => 'after'], [], ['имя.ассета']) выводит код сразу за нужным скриптом стенд.
  3. Подмена адреса чужого скрипта. Зарегистрировать ассет с тем же именем и другим адресом, например с CDN: позднее определение перекрывает раннее [23] документация.
  4. Отключить ненужное. disableScript('jquery-noconflict'); но если от ассета зависят другие, он включится снова [23] документация.
  5. Свежий файл после правки. Пересохранить общие настройки: меняется общая версия медиафайлов, и браузеры берут новые адреса [20] практика.
  6. Bootstrap точечно. useScript('bootstrap.tab') вместо подключения всего пакета; по умолчанию Bootstrap JS не грузится стенд.

13.3 API

  1. Заголовок на запас. Если сервер глотает Authorization, токен передают в X-Joomla-Token стенд.
  2. Постраничный вывод. page[limit] и page[offset], число страниц — в meta.total-pages стенд.
  3. Метки через API. В теле нужны parent_id (корень — 1) и description (хотя бы пустая строка) стенд.
  4. Значения полей по имени. PATCH {"имя_поля": "значение"} стенд.
  5. Удалить материал. Сначала state: -2, затем DELETE стенд.

13.4 Данные и поля

  1. Метки из кода. До сохранения — PluginHelper::importPlugin('behaviour') стенд.
  2. Модель в автономном сценарии. Задать JPATH_COMPONENT стенд.
  3. Слаг вручную. Строку id:alias собирают сами, если модель списка её не отдаёт стенд практика.
  4. Похожие материалы. Сначала проверить mod_tags_similar, потом писать запрос по общим меткам стенд.
  5. Поля автора в статье. FieldsHelper::getFields('com_users.user', $user, true) и выбор по имени стенд.
  6. Современный выбор из списка. layout="joomla.form.field.list-fancy-select" исходники.
  7. Значение поля из консоли. Перед setFieldValue() положить пользователя в сессию — Factory::getSession()->set('user', $user) — и проверить возвращаемое значение стенд.
  8. Алиас из заголовка. Модель строит его только при пустом алиасе и задаче save исходники; на русских заголовках сверяют транслитерацию с языковым пакетом сайта стенд.
  9. Счётчик у меток одним запросом. Группировка по tag_id по таблице карты меток вместо запроса на каждую метку стенд.

13.5 Шаблон

  1. Свои стили и скрипты без дочернего шаблона. media/templates/site/cassiopeia/css/user.css и js/user.js подключаются ассетами template.user исходники.
  2. Цвет для одной страницы. CSS-класс страницы в параметрах пункта меню плюс переопределение --cassiopeia-color-primary под этим классом [26] сторонний.
  3. Раскладка блога. Классы boxed, image-left, image-right, image-alternate, columns-2…4, masonry-2…4 в параметрах пункта меню «Блог категории» исходники.
  4. Перестановка позиций. grid-template-areas для .site-grid в user.css [26] сторонний.
  5. Подвал без «сделано на Joomla». Переопределить константы MOD_FOOTER_LINE1 и MOD_FOOTER_LINE2 [26] сторонний.
  6. Альтернативный макет модуля. Копия default.php под другим именем в html/<модуль>/ [26] сторонний.
  7. Вид модуля. Шесть стилей — card, noCard, html5, none, outline, table — лежат в каталогах chromes стенд; по статье 2022 года outline выводит имя позиции и стиля для отладки [26] сторонний.

13.6 Обновление и совместимость

  1. Прогон на копии с выключенным compat6. Если шаблон и расширения работают при выключенном плагине совместимости, сайт готов к будущему без него документация практика.
  2. Перед ядром — расширения. update:extensions:check стенд.
  3. Снимок базы. database:export стенд.
  4. Сверка переопределений. Значок «Joomla! Overrides Update Notification» на панели управления при включённом плагине installer/override стенд.

13.7 Редакция и нагрузка

Приёмы из исследования автора (глава 7 «Конвейер новости» [10]; глава 10 «Высокая нагрузка на практике» [10]; часть 2 «Процесс «Новости»: стадии, переходы, права» [27]).

  1. Свои группы вместо штатных. Штатные Author, Editor и Publisher не трогают, а заводят четыре своих группы детьми Registered: так права не наследуются от чужих ролей исследование.
  2. Процессы включаются одной настройкой. Материалы → Настройки → вкладка «Интеграция»; по умолчанию они выключены исследование.
  3. Категория — сразу и правильно. Процесс закрепляется за материалом при первом сохранении; сменить его потом может только суперпользователь пакетной операцией «Сменить этап» исследование.
  4. Черновик на глазах у автора. Чтобы репортёр видел на сайте собственный неопубликованный материал, его группу включают в уровень доступа Special или в свой уровень «Редакция» исследование.
  5. Метки — только редакторам отделов. Право создавать метки отбирают у авторов исследование.
  6. Своё меню админки на роль. Меню типа «администратор», связанный модуль в позиции menu и пункт «Контейнер меню компонентов»; штатный контейнер остаётся частью меню — исследование называет это «неизбежным дублированием» исследование.
  7. Очередь корректора — один фильтр. Колонка «Этап» в списке материалов и фильтр по этапу «На корректуре» исследование.
  8. Ночная переиндексация. finder:index по расписанию исследование.
  9. Составной индекс на большом архиве. (catid, state) — после измерения на копии исследование.
  10. Страничный кэш сам не сбрасывается. После правки материала нужен ручной сброс или внешний слой исследование.

14 Кейсы

14.1 Кейс 1. Рабочий гайд проекта TAMGARD/wmarka

Первый кейс — свод знаний, собранный в работе над сайтом TAMGARD и шаблоном wmarka [20] практика. Гайд написан на стенде с Joomla 6.1.2, PHP 8.4 и панелью HestiaCP; на стенде 53 материала, 12 категорий, 12 настраиваемых полей с 144 значениями. Для доклада он ценен тем, что фиксирует находки, которых нет в документации, и каждая либо подтверждена проверкой на 6.1.4, либо остаётся наблюдением автора.

Находка гайда Что проверено на 6.1.4
алиасы сессии, включая SessionInterface, обязательны вне веба исходники — набор из пяти алиасов стоит в api/includes/app.php и cli/joomla.php
карта неймспейсов нужна, иначе классы компонентов не находятся стенд — и порядок вызовов важен
метки без плагина behaviour молча не сохраняются стенд — ноль строк против трёх
ключ пункта меню — (client_id, parent_id, alias, language), без menutype стенд — уникальный индекс таблицы
форма пункта меню вычищает параметры, не объявленные в XML макета практика
глобальная языковая копия побеждает копию в шаблоне практика
алиас на алиас не маршрутизируется практика
модель списка материалов не отдаёт слаг стенд — материалы пришли без поля slug
scroll-padding-top и scroll-margin-top суммируются практика
порядок сброса кэшей при «не применилось» практика, кроме общей версии медиафайлов стенд

Архитектура сидера. Центральная часть гайда — установщик контента для чистых стендов, и его четыре принципа применимы к любому проекту. Первый — идемпотентность: повторный прогон не плодит дубли, потому что записи сверяются по ключу «категория/алиас» для статей и по четвёрке (client, parent, alias, lang) для меню. Второй — строгий порядок: категории, поля (с описаниями), статьи (с метками), контакт, меню (включая скрытые дочерние маршруты и страницы рубрик), модули, привязки, стиль шаблона. Третий — два режима запуска: из консоли (php cli/seed-tamgard.php) и из веба (ключ, run, файл-флаг .done и force), второй спасает на хостинге без SSH. Четвёртый — данные отдельно от кода: источником правды служит articles.json, а сидер лишь применяет его.

О слове «идемпотентность». Термин ввёл американский математик Бенджамин Пирс в 1870 году: от латинских idem — «то же» и potens — «сильный, имеющий власть». Идемпотентна операция, которая при повторе даёт тот же результат, что и после первого раза: дважды нажать «включить» — всё равно что нажать один раз. В программах это ценится как страховка от сбоев: если прогон прервался на середине, его просто запускают снова.

Железное правило эксплуатации. На живом сайте с ручными правками сидер не перезапускают: он двигает пункты меню методом setLocation в положение «последний потомок» и откатывает контент. Сидер — установщик чистых стендов; всё точечное делают SQL или админкой, а перед выходом в работу из веб-корня удаляют cli/seed-*.php и cli/tamgard-seed/ [20] практика.

Выгрузка данных. Полный дамп базы в JSON — каждая таблица массивом строк — читается скриптом без разбора диалекта SQL, поэтому гайд берёт его для аналитических выгрузок. Две подробности из гайда. При чистке HTML сначала выбрасывают атрибуты в тегах (uk-*, data-*) и только потом снимают сами теги; в обратном порядке содержимое атрибутов, как uk-scrollspy="target: > div; cls: uk-animation-fade", протаскивается в текст выгрузки. Поля images, attribs, params и metadata — JSON-строки внутри строки; их разбирают, а не режут регулярным выражением [20] практика.

14.2 Кейс 2. Перенос прототипа на штатные средства Joomla

Второй кейс — документ о том, что вернуть на штатные средства при переносе страничного прототипа в шаблон Joomla [35] практика. Прототип работал в среде, которая перестраивала разметку и откатывала изменения узлов, описанных в шаблоне; в Joomla разметка статична, и три обходных приёма снимаются.

Что было в прототипе Что стало в Joomla Объём
отбор материалов логикой: состояние рубрики и порядка, вычисления, кнопки с обработчиками компонент UIkit uk-filter: обёртка uk-filter="target: .tm-feed; animation: fade", семь uk-filter-control с селекторами вида [data-tag='risk'] и две сортировки uk-filter-control="sort: data-date; order: desc" и order: asc; кнопки снова <a href="#"> 7 управляющих элементов рубрик, 2 сортировки, 18 ячеек с data-tag и data-date, около 12 строк логики убрано
знаки как встроенный <use> со спрайтом в каждом документе глифы и лок-апы — <img src="assets/icons.svg#tm-mark" width="44" height="44" uk-svg>; интерфейсный набор одного кегля — скриптовый UIkit.icon.add и вызов uk-icon="icon: tm-…" 111 узлов <use> в 17 шаблонах страниц и в деке, 8 символов в icons.svg, 19 записей интерфейсного набора
uk-lightbox проверить заново на статической разметке: в прототипе компонент инициализировался, но панель не создавалась не применялся

Два правила из этого документа стоят отдельного упоминания. Первое касается обводки знаков: non-scaling-stroke применяют только к знакам одного кегля — к интерфейсному набору в 22 пикселя; знаки, живущие в нескольких размерах, имеют пропорциональную обводку в долях сетки, и при переносе это решение сохраняется. Второе — три предохранителя, написанные после того, как механические замены шесть раз задели то, что не следовало:

  • замена по стилевому файлу должна обходить блок :root: глобальная замена литералов однажды переписала сами токены палитры самоссылками вида --blue: var(--blue) и обрушила цвет на всех страницах;
  • перед заведением токена проверяют, свободно ли его имя и в теме, и в страничных стилях: имя --on-blue-dim уже занимала голубая метка, и новая роль её перекрыла;
  • сверяют разметку листов деки и её печатной версии по закрытому списку допустимых различий.

Там же стоит урок о счёте: окна текста вместо разбора дерева по стеку тегов заставляли счёт нарушений врать — прямых потомков, которые не ячейки, выходило 107 вместо 40, карточек без тела 64 вместо 5, алиасов 343 вместо 5 [35] практика.

14.3 Кейс 3. bestnews.kz: сайт на собственных расширениях

Третий кейс — спортивный новостной сайт bestnews.kz, созданный автором с самого начала проекта на собственном шаблоне wmarka и UIkit 3 практика. Сторонних расширений на нём почти нет: это JUImage для адаптивных изображений и Quantum Manager; остальное — собственные плагины и макеты. Галерея и универсальная видеогалерея работают как плагины поверх настраиваемых полей материала, а главная страница почти целиком собрана из собственных переопределений mod_custom. Для перехода на Joomla 6 это выгодное положение: чужого кода, который нужно проверять, мало. Шаблон wmarka и его дочерние шаблоны доводят до работы на Joomla 6 с выключенным плагином совместимости, проверяя на копии сайта практика.

Отдельный узел кейса — центр матчей КХЛ на двух пользовательских HTML-модулях. Шесть PHP-файлов (настройки, команды, помощник, карточка матча и две раскладки) берут данные из открытых спортивных источников и держат их в кэше по схеме «отдать старое, пока обновляется новое» с ручным обновлением по токену и диагностической точкой. Из багов центра вытекают уроки, годные далеко за пределами хоккея практика:

  • читать таблицу по подписям столбцов, а не по их номеру: цифры очков принимались за голы, когда порядок столбцов отличался от ожидаемого;
  • сопоставлять метки точно, а не подстрокой: короткое имя столбца совпадало с другим, более длинным;
  • вести цикл по источнику, пока не собраны и сыгранные, и предстоящие матчи: иначе вкладка результатов теряла сыгранные;
  • ограничивать разбор якорем заголовка лиги, чтобы фарм-клубы из соседнего турнира не просачивались в результаты;
  • брать за образец реальную разметку источника, а не предположение о ней: проверка на настоящей странице была решающим приёмом при каждой поломке.

О выражении «stale-while-revalidate». По-русски «отдавать несвежее, пока проверяется свежее». Выражение закреплено в RFC 5861 (2010): сервер или кэш может ответить посетителю копией, срок годности которой истёк, и одновременно в фоне запросить новую для следующего посетителя. Посетитель не ждёт, а данные устаревают не дольше одного обновления. Для спортивных таблиц, где задержка в минуту допустима, а задержка страницы — нет, это самая подходящая схема.

Правило сайта, которое пригодится любому, кто режет изображения через JUImage: каждая нарезка должна ложиться в папку img/ в корне сайта, а не в cache/; часть переопределений mod_custom это нарушала, и после правки правило стало проверяемым практика.

14.4 Кейс 4. Рецепты 2021–2022 годов и их судьба на 6.1.4

Четвёртый кейс — проверка присланной автором подборки рецептов для Joomla 3 и 4, в том числе статей портала jpath.ru: насколько они живы в Joomla 6. Оговорка: все четыре статьи раздела «Практические решения» помечены на самом портале словом «Устарело» [36]. Результат сводит таблица стенд.

Рецепт Эпоха Судьба на 6.1.4
подключение через WebAssetManager: registerAndUseStyle, registerAndUseScript, addInlineScript Joomla 4 работает стенд; нужно помнить о коротком адресе
$doc->addStyleSheet(), addScript(), addScriptDeclaration() Joomla 3 методы есть, но объявлены устаревшими с 4.3 и будут удалены в 7.0 исходники
HTMLHelper::_('bootstrap.tab', '.selector', []) Joomla 4 помощник на месте исходники; предпочтителен ассет bootstrap.tab
$app->redirect($link, $msg, 'message') Joomla 3 даёт ошибку 500; правильно enqueueMessage и redirect($url, 303) стенд
Uri::getInstance()->toString() Joomla 4 работает стенд
layout="joomla.form.field.list-fancy-select" Joomla 4 макет на месте исходники
JFactory::getUser() и JLoader::register('FieldsHelper', …) Joomla 3 псевдоним JFactory живёт в плагине compat6 исходники; полем занимается класс Joomla\Component\Fields\Administrator\Helper\FieldsHelper стенд
useScript('fields.validate') Joomla 4.0 такого имени в системном реестре нет; есть form.validate исходники
user.css в media/templates/site/cassiopeia/css/ Joomla 4.1 верно исходники
классы блога и обтекания Cassiopeia Joomla 4 на месте исходники
T4 Framework 2.1.3 Joomla 4.1 исторический выпуск; совместимость проверяют по версии своего экземпляра сторонний
Categories::getInstance('Content', ['countItems' => 1])->get($id)->numitems Joomla 3–4 работает: пять из пяти стенд
TagsHelper::getTagItemsQuery($id, 1) как статический вызов Joomla 3 ошибка «Non-static method …»; на экземпляре работает, тип по-прежнему задаётся ID стенд
define('JPATH_COMPONENT', …) «не нужна в Joomla 4 и выше» Joomla 4 в консольной команде 6.1.4 нужна стенд
HTMLHelper::_('stylesheet', 'com_foo/bar.css', ['relative' => true]) Joomla 3–4 помощник на месте; тот же короткий адрес принимает менеджер ассетов исходники
суффикс -uncompressed для отладки Joomla 3 в коде 6.1.4 не используется; пара имя.js и имя.min.js исходники
Factory::getApplication()->triggerEvent() вместо JEventDispatcher Joomla 4 метод есть, но объявлен устаревшим с 4.0 исходники

14.5 Кейс 5. Редакционный конвейер на чистой Joomla 6.1.4

Пятый кейс — разбор редакционного процесса с четырьмя ролями, собранный и снятый на чистой Joomla 6.1.4 без единого стороннего расширения [27] исследование. Установка шла из пакета с GitHub через консольный установщик, как и стенд этого доклада; русский пакет — ru-RU 6.1.4.1 команды JPathRu. В разборе 36 снимков в пяти частях: основа (группы, уровень доступа «Редакция», включение процессов); процесс «Новости» (этапы, переходы, письма, права); админка глазами каждой из четырёх ролей; те же права в лицевых формах; дочерние шаблоны и стили (часть 1 «Основа: кто есть кто» [27]; часть 3 «Глазами каждой роли» [27]; часть 4 «Лицевая сторона» [27]).

Из наблюдений разбора читателю этого доклада пригодятся три. Админка не «урезается» отдельным режимом: шаблон Atum сам читает права группы, и у репортёра в меню остаётся один раздел, у корректора два, у главного редактора всё редакционное, кроме «Системы». Один и тот же этап даёт разным ролям разные кнопки: на корректуре редактору доступны «Опубликовать» и «На утверждение главреду», а корректору только «Вернуть редактору». И одно расхождение с документацией: руководство, написанное для ветки 4.x, помещает поле этапа лицевой формы на вкладку «Публикация», а в 6.1.4 оно стоит на вкладке «Параметры» исследование. Чего разбор не делал, он называет прямо: собственное меню админки на роль не настраивалось.

14.6 Кейс 6. Живые порталы на Joomla

Шестой кейс — перечень новостных порталов, которые на 8 октября 2026 года, по карточкам сервиса W3Techs, работают на Joomla [10] исследование. Версию определяют по тегу generator, который легко скрыть, поэтому перечень нельзя считать полным.

Сайт Страна и ранг Что видно на карточке
Jutarnji.hr Хорватия, первая тысяча мира Joomla 3.9.8, Cloudflare, HTTP/3, JSON-LD
Slobodnadalmacija.hr Хорватия, первые 10 тыс. Joomla, Cloudflare, HTTP/3
CNN.gr Греция, первые 10 тыс. Joomla 3.10.12, Nginx, Akamai, JSON-LD
Newsbomb.gr Греция, первые 10 тыс. Joomla, Nginx, Akamai
Dnews.gr Греция, первые 10 тыс. «Joomla 3.15.0» (такого выпуска не было), за Cloudflare
Primocanale.it Италия, первые 100 тыс. Joomla 3.10.12 на IIS 10 под Windows

Jutarnji list — второй по посещаемости новостной сайт Хорватии, а Slobodna Dalmacija — вторая газета того же издательского дома; два главных портала одной медиагруппы на одной платформе убеждают сильнее любых рекламных страниц. У таблицы два урока для этого доклада. Все найденные крупные порталы работают ещё на ветке 3.x — это предупреждение редакциям, которые не закладывают обновления в бюджет. И большинство из них стоит за сетями доставки содержимого. В исследовании есть и казахстанский штрих: 4 июля 2011 года Tengrinews сообщал, что государственная компания KazContent строит сайты региональных печатных изданий на Joomla — десять готовы, шестнадцать в работе, редакции платят лишь за домен исследование (глава 12 «Живые порталы на Joomla» [10]).

14.7 Методы jpath.ru: что изменилось и что правильнее

Таблица собрана по статьям портала jpath.ru, которые вошли в доклад, и сверена с 6.1.4. В правом столбце названо то, что правильнее сегодня; это не утверждение, что иначе нельзя: часть старых способов работает и теперь.

Метод в статье Что изменилось в 6.1.4 Что правильнее сегодня
HTMLHelper::_('stylesheet', …) и HTMLHelper::_('script', …) [25] объявлены устаревшими с 5.3.0, удаление в 7.0; вызов HTMLHelper::_('image', …) не тронут исходники подключать через менеджер ассетов: тот же короткий адрес расширение/файл принимает registerAndUseStyle(); изображения пока можно выводить помощником
переопределение файла ресурса в шаблоне по адресу /templates/{шаблон}/css/com_foo/bar.css [25] файл по этому адресу на стенде не подхватился; подхватился файл по адресу /media/templates/site/{шаблон}/css/com_foo/bar.css, в том числе для короткого адреса менеджера ассетов стенд класть переопределение в media/templates/site/{шаблон}/css/ и js/ под именем расширения; полный адрес вида media/… этим путём не переопределяется стенд
суффикс -uncompressed в режиме отладки [25] в коде 6.1.4 его нет; помощник подбирает пару имя.js и имя.min.js исходники отдавать app.js и app.min.js, а выбор между ними оставить режиму отладки
Factory::getApplication()->input [37] в веб-приложении обращение к свойству даёт уведомление об устаревании с 6.0 исходники $app->getInput(); в контроллере компонента $this->input, как пишет и сама статья
$app->triggerEvent() для вызова плагинов [37] объявлен устаревшим с 4.0, удаление в 7.0 исходники диспетчер событий приложения, $app->getDispatcher(), и типизированное событие исходники
$container->share($key, …, true) [21] метод существует рядом с set(), lazy(), protect() стенд в провайдерах плагинов ядра принята связка set() и lazy(): так сделаны 153 провайдера из 159 стенд
список материалов через createModel('Articles', 'Site', ['ignore_request' => true]) [19] в консоли без состояния params модель падает с «Call to a member function get() on null»; с ним возвращает материалы, но без поля slug стенд задать setState('params', ComponentHelper::getParams('com_content')) и собрать slug из ID и алиаса
ContentHelperRoute::getArticleRoute() [19] класс переехал в Joomla\Component\Content\Site\Helper\RouteHelper стенд вызывать его с собранным slug, результат пропускать через Route::_()
Categories::getInstance('Content', ['countItems' => 1]) [32] работает: пять из пяти стенд естественнее брать категории у самого компонента: bootComponent('com_content')->getCategory(['countItems' => true]); он тоже вернул пять стенд
TagsHelper::getTagItemsQuery($tagId, 1) [33] метод нестатический; на экземпляре работает, тип задаётся ID, алиас вызов роняет стенд для одной метки — экземпляр с ID типа; для всех меток сразу — один запрос с группировкой стенд
define('JPATH_COMPONENT', …) «не нужна в Joomla 4 и выше» [22] в консольной команде 6.1.4 нужна стенд определить до создания модели
$app->input->set('task', 'save') ради алиаса [22] работает; про само свойство input см. выше стенд рабочий путь; предсказуемее задать алиас самому: итог не зависит ни от языкового пакета, ни от скрытой задачи
setFieldValue() в консоли [22] молча возвращает false, пока пользователя нет в сессии стенд положить пользователя в сессию и проверять возвращаемое значение стенд
список псевдонимов классов в /libraries/classmap.php [19] файла нет; псевдонимы лежат в карте плагина compat6 исходники искать их в plugins/behaviour/compat6/src/classmap/, а новый код писать на классах с пространствами имён
каталоги /libraries/cms, joomla, legacy, fof, idna_convert, phputf8 [19] в 6.1.4 остались src, vendor, php-encryption и phpass стенд искать классы CMS в libraries/src, сторонние пакеты — в libraries/vendor
«Daemon приложение» [6] класс DaemonApplication и его события на месте исходники использовать как есть; в докладе он назван пятым типом приложений

15 Каталог граблей

В таблице собраны ловушки Joomla 6, которые стоили часов; часть взята из рабочего гайда проекта, часть найдена в этой проверке.

Область Грабля Что делать Знак
маршруты тёзки алиасов в разных меню конфликтуют: ключ пункта не включает меню называть пункты уникально в пределах родителя стенд
маршруты пункт-псевдоним на пункт-псевдоним не маршрутизируется указывать псевдоним на конечный пункт практика
материалы если вступления скрыты глобально, introtext дублируется в начало текста при экспорте учитывать, иначе текст задваивается практика
материалы описания и подсказки полей — данные сайта, а не шаблона заводить SQL или сидером практика
вёрстка scroll-padding-top и scroll-margin-top суммируются оставить один источник практика
вёрстка autofocus и любой фокус при загрузке прокручивает страницу в модальных окнах ставить фокус по событию shown практика
модули секционные модули с обёрткой ломают секции UIkit стиль blank и редактор «None» практика
модули незакрытый тег в модуле роняет следующие секции в столбик проверять разметку практика
кэши «не применилось» порядок проверки: пересохранить общие настройки; жёсткое обновление страницы; очистка кэша; удаление нарезок JUImage; прямое открытие favicon; новое имя файла при OPcache практика
сценарии class_exists() до карты неймспейсов сначала карта, потом проверки стенд
сценарии модель материала без JPATH_COMPONENT падает определить константу стенд
сценарии метки без плагина behaviour теряются подключить группу до сохранения стенд
сценарии open_basedir режет /tmp на HestiaCP задать свой путь практика
API php -S отвечает 404 на всех маршрутах использовать Apache или другой полноценный сервер стенд
API метка без description — 400 передавать пустую строку стенд
API поле без context оставляет «призрак» указывать контекст и непустые params стенд
API bearer строчными не принимается писать Bearer стенд
API fields[...] не сокращает список резать на клиенте стенд
ассеты лишняя папка в коротком адресе: молчаливый отказ писать расширение/файл стенд
ассеты обращение к незарегистрированному ассету — исключение регистрировать или использовать верное имя документация
планировщик --id — ID в таблице, а не номер в списке брать из scheduler:list стенд
планировщик тип задачи не виден без языковых констант …_TITLE и …_DESC в .ini исходники
совместимость JFactory и старые классы только через compat6 переписать на пространства имён исходники
совместимость compat6 в новой установке выключен не рассчитывать на него стенд
сидер повторный запуск на живом сайте двигает меню и откатывает контент запускать только на чистом стенде практика
процессы поле этапа в лицевой форме: руководство для 4.x помещает его на вкладку «Публикация», в 6.1.4 оно на вкладке «Параметры» искать на «Параметрах» исследование
процессы процесс закрепляется за материалом при первом сохранении выбирать категорию сразу; позже этап меняет суперпользователь пакетной операцией исследование
права автор не видит на сайте собственный неопубликованный черновик включить его группу в уровень Special или в свой уровень доступа исследование
кэш правка материала не сбрасывает страничный кэш ручной сброс или внешний слой исследование
разметка штатный schemaorg/article выводит только Article, без keywords и articleSection расширение с автозаполнением или собственный плагин типа исследование
поиск сохранение материала во время полной переиндексации может испортить индекс переиндексировать ночью, по расписанию исследование
консоль статья jpath.ru считает константу JPATH_COMPONENT ненужной, а в 6.1.4 без неё форма материала в команде не грузится определить константу стенд
поля setFieldValue() молча возвращает false в консоли: право проверяется через Factory::getUser() положить пользователя в сессию и проверять результат стенд
материалы повтор заголовка в категории даёт COM_CONTENT_ERROR_UNIQUE_ALIAS задавать алиас самому или ловить ошибку стенд
алиасы транслитерация зависит от языкового пакета: на стенде с одним английским «Статья» стала stata проверять алиасы на своём наборе языков или задавать вручную стенд
метки TagsHelper::getTagItemsQuery() стал нестатическим, а описание говорит об алиасах типов, хотя вызов с алиасом падает вызывать на экземпляре с ID типа или считать одним запросом стенд
события $app->triggerEvent() объявлен устаревшим с 4.0 диспетчер событий приложения исходники
API поле text в ответе API для статей объявлено устаревшим с 6.2.0, удаление в 8.0 читать introtext и fulltext исходники

16 Обновление и переезд: чек-лист

Порядок ниже годится и для перехода с 5.4 на 6.x, и для обычных обновлений внутри ветки 6.x. Он составлен из руководства проекта, объявлений о выпусках и рабочего гайда проекта автора; знаки показывают, что из этого подтверждено проверкой.

Перед обновлением

  • Прочесть заметки об обновлении между нужными версиями в руководстве разработчика: для перехода на шестую ветку они лежат в разделе об обновлениях 5.4 → 6.0, а для обычных выпусков — в разделе вида «от какой версии — к какой» [7] [20] документация.
  • На 5.4 выключить старый плагин «Behaviour — Backward Compatibility» и разобрать ошибки, пока сайт ещё на 5.4 [16] документация.
  • Убедиться в PHP 8.3 и выше; версии базы сверить с руководством по требованиям исходники.
  • Развернуть копию сайта и прогнать обновление на ней, а не на рабочем месте практика.
  • Снять выгрузку базы командой database:export и копию файлов стенд.
  • Проверить, готовы ли расширения: update:extensions:check показывает ожидающие обновления, а совместимость каждого расширения с шестой веткой уточняют у разработчика стенд.

После обновления

  • Проверить, что compat6 выключен, а сайт работает без него; если плагин нужен для старого расширения, включать его как временную меру с датой отказа документация практика.
  • Открыть формы пунктов меню всех собственных макетов: не вычистились ли параметры, не объявленные в XML макета практика.
  • Проверить пары языковых файлов .ini и .sys.ini по каждому языку: не потерялись ли константы практика.
  • Сверить переопределения: значок «Joomla! Overrides Update Notification» на панели управления при включённом плагине installer/override стенд.
  • Перестроить индекс поиска командой finder:index и проверить работу фильтров стенд практика.
  • Прогнать автотест сборки user.css: нет ли дублей селекторов, цела ли структура практика.
  • Сидер запускать на чистом стенде, но не на живом сайте практика.
  • Сбросить кэши в известном порядке и проверить общую версию медиафайлов практика.

Если расширение пришло с ветки 3.x. Среди сайтов на Joomla 51,9% работают ещё на ветке 3.x исследование (глава 5 «Рынок без прикрас» [10]), а значит, немало расширений придётся вести через несколько главных версий. Портал jpath.ru собрал, что убрали уже в Joomla 4 и что поэтому вычищают первым делом [37] сторонний.

Что убрали Чем заменить
JError, JException обычное исключение Exception и сообщение через приложение
JEventDispatcher PluginHelper::importPlugin() и диспетчер событий приложения; метод $app->triggerEvent() из того же рецепта в 6.1.4 объявлен устаревшим исходники
JFactory::getXml(), JXMLElement обычный SimpleXMLElement
вызовы JHtml вроде behavior.formvalidation HTMLHelper или, лучше, ассет form.validate исходники
JRequest класс Input, то есть $app->getInput() стенд
isAdmin(), isSite() $app->isClient('administrator') и $app->isClient('site') стенд
Mootools обычный JavaScript
Bootstrap 2 в админке Bootstrap 5; публичная часть к фреймворку больше не привязана

17 Дорожная карта и поддержка

Последнее событие — выход Joomla 6.2.0 13 октября 2026 года [9] документация. Дальше идут исправляющие выпуски 6.2.x: 6.2.1 — 24 ноября 2026 года, 6.2.2 — 5 января 2027 года, 6.2.3 — 16 февраля 2027 года и 6.2.4 — 30 марта 2027 года; даты взяты из дорожной карты в редакции от 2 октября, до выхода 6.2.0 [11] документация. Следующий минорный выпуск, 6.3, по шестимесячному ритму придётся на весну 2027 года практика. С 16 по 18 октября в Потсдаме под Берлином проходит Всемирная конференция Joomla [9] документация. Плановая дата Joomla 7.0 — 12 октября 2027 года: она стоит в файле Version.php ветки 7.0-dev, ровно по двухлетнему ритму мажорных версий исследование (глава 3 «Релизы и дорожная карта» [10]). Из объявления о 6.1 видно, что последней минорной версией ряда проект считает 6.4; отсюда можно ждать 6.3 весной и 6.4 осенью 2027 года, но этих дат проект не объявлял — это расчёт по ритму практика; руководство разработчика уже содержит страницы «С 6.2 на 6.3» и «С 6.4 на 7.0» [18] документация. Поддержка ветки 6.x по дорожной карте идёт до 17 октября 2028 года для исправлений ошибок и до 16 октября 2029 года для исправлений безопасности [11] документация.

Риски исследование называет без смягчений исследование (глава 5 «Рынок без прикрас» [10]; глава 15 «Конкуренты и риски» [10]). Доля Joomla среди систем управления содержимым упала с 9,3% в 2014 году до 1,6% на 6 октября 2026 года; среди сайтов на Joomla 51,9% работают ещё на третьей ветке и лишь 9,2% на шестой. Для планирования отсюда два вывода: обновления закладывают в бюджет как постоянную статью, а совместимость сторонних расширений с шестой веткой проверяют до начала проекта, а не после.

Что делать, зависит от того, где сайт находится сейчас.

Где сайт Что делать Основание
ветка 4.x переходить: поддержка безопасности закончилась 14.10.2025; путь — через 5.x по руководству о переходе с 4.4 на 5.x [11, 7] документация
ветка 5.x до 5.4 обновиться до 5.4 — «мосту» к шестой ветке; безопасность 5.x поддерживается до 12.10.2027 [11, 7] документация
5.4 готовить переход на 6.x по чек-листу выше; обновление считается обновлением, а не миграцией [7] документация
6.0 и 6.1 держаться на последнем патче; на 6.2 переходить, когда расширения подтвердят совместимость: стабильная 6.2.0 вышла 13 октября 2026 года практика
новый проект начинать сразу с ветки 6.x и писать код без устаревших вызовов [11] документация

Последний совет касается дисциплины кода, а не версий: всё, что в 6.1.4 помечено устаревшим «с удалением в 7.0», имеет смысл заменять заранее. Тогда выход седьмой ветки не потребует срочной переделки, а плагин совместимости останется страховкой на случай чужого кода, а не опорой для собственного.

Приложение А. Ключи конфигурации

Перечень охватывает 67 ключей configuration.php; значения чистой установки 6.1.4 даны там, где они не секретны и не зависят от сервера стенд. Набор ключей в 6.2.0 тот же стенд.

Сайт и режим закрытия

Ключ Умолчание Назначение
sitename — название сайта
offline false сайт закрыт для посетителей
offline_message — текст сообщения при закрытии
display_offline_message 1 как показывать сообщение о закрытии
offline_image пусто изображение на странице закрытого сайта
editor tinymce редактор по умолчанию (tinymce, codemirror, none)
captcha 0 капча по умолчанию (0 — нет)
list_limit 20 число строк в списках админки
access 1 уровень доступа по умолчанию
frontediting 1 правка модулей и пунктов меню прямо на сайте
offset UTC часовой пояс сайта
helpurl https://help.joomla.org/proxy?keyref=Help{major}{minor}:{keyref}&lang={langcode} адрес справки
gzip false сжатие ответов
live_site пусто принудительный адрес сайта (обычно пусто)

Безопасность и отладка

Ключ Умолчание Назначение
secret — случайная строка сайта; подпись токенов API
force_ssl 0 принудительный HTTPS: 0 — нет, 1 — админка, 2 — весь сайт
error_reporting default уровень сообщений об ошибках: default, none, simple, maximum, development
debug false отладка системы
debug_lang false отладка языковых строк
debug_lang_const true показывать константы в языковой отладке

База данных

Ключ Умолчание Назначение
dbtype mysqli драйвер базы
host — сервер базы
user — пользователь базы
password — пароль базы
db — имя базы
dbprefix — префикс таблиц
dbencryption 0 шифрование соединения: 0 — нет, 1 — односторонее, 2 — двустороннее
dbsslverifyservercert пусто проверка сертификата сервера базы
dbsslkey — файл ключа для защищённого соединения
dbsslcert — файл сертификата для защищённого соединения
dbsslca — файл удостоверяющего центра
dbsslcipher — набор шифров

Кросс-доменные запросы

Ключ Умолчание Назначение
cors false включить заголовки CORS
cors_allow_origin * разрешённые источники
cors_allow_methods пусто разрешённые методы
cors_allow_headers Content-Type,X-Joomla-Token,Authorization разрешённые заголовки

Почта

Ключ Умолчание Назначение
mailonline true отправка почты включена
mailer mail способ отправки: mail, sendmail, smtp
mailfrom — адрес отправителя
fromname — имя отправителя
sendmail — путь к sendmail
smtpauth false проверка подлинности на SMTP
smtpuser — пользователь SMTP
smtppass — пароль SMTP
smtphost — сервер SMTP
smtpsecure none защита соединения SMTP
smtpport 25 порт SMTP

Кэш и сессии

Ключ Умолчание Назначение
caching 0 режим кэша (0 — выключен)
cache_handler file хранилище кэша
cachetime 15 срок жизни кэша, минут
cache_platformprefix false отдельные кэши по платформам
lifetime 15 время жизни сессии, минут
session_handler database хранилище сессий
shared_session false общая сессия сайта и админки
session_metadata true метаданные сессий

Адреса и метаданные

Ключ Умолчание Назначение
sef true человекочитаемые адреса
sef_rewrite false переписывание адресов (без index.php)
sef_suffix false суффикс .html в адресах
unicodeslugs false оставлять кириллицу в алиасах
MetaDesc пусто описание сайта по умолчанию
MetaAuthor true автор в метатегах
MetaVersion false показывать версию в метатеге generator
robots пусто правило robots по умолчанию
feed_limit 10 число записей в ленте
feed_email none как показывать почту автора в ленте

Пути

Ключ Умолчание Назначение
log_path — каталог журналов
tmp_path — каталог временных файлов

Приложение Б. Карта API

Карта собрана со стенда 6.1.4: 236 маршрутов, 111 различных путей стенд. На 6.2.0 тот же подсчёт даёт столько же маршрутов стенд. :id — числовой ID, :имя — строка, * — любой путь. Методы: Г — GET, С — POST, П — PATCH, У — DELETE.

Баннеры

Путь Методы
/api/index.php/v1/banners Г С
/api/index.php/v1/banners/:id Г П У
/api/index.php/v1/banners/:id/contenthistory Г У
/api/index.php/v1/banners/:id/contenthistory/keep П
/api/index.php/v1/banners/categories Г С
/api/index.php/v1/banners/categories/:id Г П У
/api/index.php/v1/banners/clients Г С
/api/index.php/v1/banners/clients/:id Г П У

Контакты

Путь Методы
/api/index.php/v1/contacts Г С
/api/index.php/v1/contacts/:id Г П У
/api/index.php/v1/contacts/:id/contenthistory Г У
/api/index.php/v1/contacts/:id/contenthistory/keep П
/api/index.php/v1/contacts/categories Г С
/api/index.php/v1/contacts/categories/:id Г П У
/api/index.php/v1/contacts/form/:id С

Конфигурация

Путь Методы
/api/index.php/v1/config/(([A-Za-z0-9_]+)) Г П
/api/index.php/v1/config/application Г П

Ленты новостей

Путь Методы
/api/index.php/v1/newsfeeds/categories Г С
/api/index.php/v1/newsfeeds/categories/:id Г П У
/api/index.php/v1/newsfeeds/feeds Г С
/api/index.php/v1/newsfeeds/feeds/:id Г П У

Материалы и категории

Путь Методы
/api/index.php/v1/content/articles Г С
/api/index.php/v1/content/articles/:id Г П У
/api/index.php/v1/content/articles/:id/contenthistory Г У
/api/index.php/v1/content/articles/:id/contenthistory/keep П
/api/index.php/v1/content/categories Г С
/api/index.php/v1/content/categories/:id Г П У

Медиафайлы

Путь Методы
/api/index.php/v1/media/adapters Г
/api/index.php/v1/media/adapters/:имя Г
/api/index.php/v1/media/files Г С
/api/index.php/v1/media/files/* Г П У
/api/index.php/v1/media/files/*/ Г

Меню

Путь Методы
/api/index.php/v1/menus/administrator Г С
/api/index.php/v1/menus/administrator/:id Г П У
/api/index.php/v1/menus/administrator/items Г С
/api/index.php/v1/menus/administrator/items/:id Г П У
/api/index.php/v1/menus/administrator/items/types Г
/api/index.php/v1/menus/site Г С
/api/index.php/v1/menus/site/:id Г П У
/api/index.php/v1/menus/site/items Г С
/api/index.php/v1/menus/site/items/:id Г П У
/api/index.php/v1/menus/site/items/types Г

Метки

Путь Методы
/api/index.php/v1/tags Г С
/api/index.php/v1/tags/:id Г П У

Модули

Путь Методы
/api/index.php/v1/modules/administrator Г С
/api/index.php/v1/modules/administrator/:id Г П У
/api/index.php/v1/modules/site Г С
/api/index.php/v1/modules/site/:id Г П У
/api/index.php/v1/modules/types/administrator Г
/api/index.php/v1/modules/types/site Г

Обновление ядра

Путь Методы
/api/index.php/v1/joomlaupdate/finalizeUpdate С
/api/index.php/v1/joomlaupdate/getUpdate Г
/api/index.php/v1/joomlaupdate/healthcheck Г
/api/index.php/v1/joomlaupdate/notificationFailed С
/api/index.php/v1/joomlaupdate/notificationSuccess С
/api/index.php/v1/joomlaupdate/prepareUpdate С

Персональные данные

Путь Методы
/api/index.php/v1/privacy/consents Г
/api/index.php/v1/privacy/consents/:id Г
/api/index.php/v1/privacy/requests Г С
/api/index.php/v1/privacy/requests/:id Г
/api/index.php/v1/privacy/requests/export/:id Г

Плагины

Путь Методы
/api/index.php/v1/plugins Г
/api/index.php/v1/plugins/:id Г П

Пользователи

Путь Методы
/api/index.php/v1/users Г С
/api/index.php/v1/users/:id Г П У
/api/index.php/v1/users/groups Г С
/api/index.php/v1/users/groups/:id Г П У
/api/index.php/v1/users/levels Г С
/api/index.php/v1/users/levels/:id Г П У

Произвольные поля

Путь Методы
/api/index.php/v1/fields/contacts/categories Г С
/api/index.php/v1/fields/contacts/categories/:id Г П У
/api/index.php/v1/fields/contacts/contact Г С
/api/index.php/v1/fields/contacts/contact/:id Г П У
/api/index.php/v1/fields/contacts/mail Г С
/api/index.php/v1/fields/contacts/mail/:id Г П У
/api/index.php/v1/fields/content/articles Г С
/api/index.php/v1/fields/content/articles/:id Г П У
/api/index.php/v1/fields/content/categories Г С
/api/index.php/v1/fields/content/categories/:id Г П У
/api/index.php/v1/fields/groups/contacts/categories Г С
/api/index.php/v1/fields/groups/contacts/categories/:id Г П У
/api/index.php/v1/fields/groups/contacts/contact Г С
/api/index.php/v1/fields/groups/contacts/contact/:id Г П У
/api/index.php/v1/fields/groups/contacts/mail Г С
/api/index.php/v1/fields/groups/contacts/mail/:id Г П У
/api/index.php/v1/fields/groups/content/articles Г С
/api/index.php/v1/fields/groups/content/articles/:id Г П У
/api/index.php/v1/fields/groups/content/categories Г С
/api/index.php/v1/fields/groups/content/categories/:id Г П У
/api/index.php/v1/fields/groups/users Г С
/api/index.php/v1/fields/groups/users/:id Г П У
/api/index.php/v1/fields/users Г С
/api/index.php/v1/fields/users/:id Г П У

Расширения

Путь Методы
/api/index.php/v1/extensions Г

Редиректы

Путь Методы
/api/index.php/v1/redirects Г С
/api/index.php/v1/redirects/:id Г П У

Сообщения

Путь Методы
/api/index.php/v1/messages Г С
/api/index.php/v1/messages/:id Г П У

Шаблоны

Путь Методы
/api/index.php/v1/templates/styles/administrator Г С
/api/index.php/v1/templates/styles/administrator/:id Г П У
/api/index.php/v1/templates/styles/site Г С
/api/index.php/v1/templates/styles/site/:id Г П У

Языки

Путь Методы
/api/index.php/v1/languages Г С
/api/index.php/v1/languages/content Г С
/api/index.php/v1/languages/content/:id Г П У
/api/index.php/v1/languages/overrides/administrator/en\-GB Г С
/api/index.php/v1/languages/overrides/administrator/en\-GB/(([A-Z0-9_]+)) Г П У
/api/index.php/v1/languages/overrides/search С
/api/index.php/v1/languages/overrides/search/cache/refresh С
/api/index.php/v1/languages/overrides/site/en\-GB Г С
/api/index.php/v1/languages/overrides/site/en\-GB/(([A-Z0-9_]+)) Г П У

Приложение В. Словарь терминов

Русское название — как принято в докладе; исходное слово — как оно стоит в коде или в английской документации; ссылка ведёт к месту, где термин объяснён. Порядок записи — по ходу доклада.

Термин в докладе Исходное слово Где объяснено
Joomla (имя) jumla (суахили) Имя, версии и кодовые названия
дорожная карта roadmap Версии и сроки поддержки
слой совместимости (плагин compat6) backward compatibility plugin Переход с 5.4 на 6.x
TUF The Update Framework Версия 6.0
Framework Joomla Framework Слои
приложение; точка входа application; entry point Четыре входа в систему
публичная и административная части site, administrator Четыре входа в систему
цикл выполнения execution cycle Цикл выполнения веб-приложения
DI-контейнер (контейнер зависимостей) dependency injection container, DIC Контейнер зависимостей
сервис-провайдер (провайдер) service provider Контейнер зависимостей
пространство имён; автозагрузка namespace; autoloading Карта неймспейсов
событие; плагин; подписчик event; plugin; subscriber События и плагины
ассет (ресурс) asset Web Asset Manager
менеджер ассетов; пресет Web Asset Manager; preset Методы менеджера
реестр ассетов asset registry Где живут реестры
позиция модуля module position Два шаблона и их позиции
дочерний шаблон; стиль шаблона child template; template style Дочерние шаблоны
переопределение; макет вывода; стиль модуля override; layout; module chrome Переопределения
таксономия taxonomy Категории и метки как рубрики
метка; алиас; слаг tag; alias; slug Категории и метки как рубрики
настраиваемое поле; контекст поля custom field; field context Настраиваемые поля
рабочий процесс; этап; переход; связь workflow; stage; transition; association Версии, рабочий процесс и связи
обучающий тур guided tour Что нового в 6.0, 6.1 и 6.2
лог действий actions log Лог действий и персональные данные
конструктор запросов; класс таблицы query builder; table class База данных
манифест manifest Анатомия расширения
Web Services API (веб-службы) Web Services API, REST Что это и где живёт
токен; HMAC token; keyed-hash message authentication code Ключ доступа: токен
CRUD create, read, update, delete Своя точка API
CORS cross-origin resource sharing Кросс-доменные запросы
командная строка command line, CLI Командная строка и планировщик
планировщик задач; cron scheduler; cron Планировщик
ключ конфигурации configuration key Ключи конфигурации
CSRF cross-site request forgery Три вида «токена»
TOTP; WebAuthn time-based one-time password; Web Authentication Вход: многофакторная аутентификация
капча; доказательство работы CAPTCHA; proof of work Капча на доказательстве работы
nonce number used once Заголовки ответа
кэш cache Слои кэша на новостном сайте
CDN content delivery network Слои кэша на новостном сайте
экосистема ecosystem Русскоязычная среда и положение в РК
schema.org; JSON-LD schema.org; JSON-LD Структурированные данные
IndexNow IndexNow Структурированные данные
идемпотентность idempotence Кейс 1
stale-while-revalidate stale-while-revalidate Кейс 3

Список использованных источников

  1. Joomla! CMS 6.1.4 : полный пакет установки // GitHub : сайт. – URL: https://github.com/joomla/joomla-cms/releases/tag/6.1.4 (дата обращения: 11.10.2026). – Текст : электронный.
  2. Joomla! CMS 6.2.0-rc2 : полный пакет установки (кандидат в релизы) // GitHub : сайт. – URL: https://github.com/joomla/joomla-cms/releases/tag/6.2.0-rc2 (дата обращения: 11.10.2026). – Текст : электронный.
  3. Joomla! CMS 6.2.0 : полный пакет установки (стабильный выпуск) // GitHub : сайт. – URL: https://github.com/joomla/joomla-cms/releases/tag/6.2.0 (дата обращения: 13.10.2026). – Текст : электронный.
  4. Саханов, Б. Joomla 6 для новостной редакции : исследование, лонгрид, конвейер в скриншотах (редакция 1.1) // Webmarka : сайт. – 2026, 8 окт. – URL: https://webmarka.kz/research/joomla-6-newsroom/ (дата обращения: 12.10.2026). – Текст : электронный.
  5. Русская локализация Joomla (ru-RU) : файлы и глоссарий // GitHub : сайт. – URL: https://github.com/JPathRu/localisation (дата обращения: 13.10.2026). – Текст : электронный.
  6. Рекун, Д. Общая информация о принципе действия Joomla // Joomla для профессионалов : сайт. – 2022, 21 марта. – URL: https://jpath.ru/docs/basics/joomla-internals/obshchaya-informatsiya-o-printsipe-dejstviya-joomla (дата обращения: 12.10.2026). – Текст : электронный.
  7. Joomla 6.0 and Joomla 5.4 are here! // Joomla! : сайт. – 2025, 14 окт. – URL: https://www.joomla.org/announcements/release-news/joomla-6-0-and-joomla-5-4-are-here.html (дата обращения: 12.10.2026). – Текст : электронный.
  8. Joomla 6.1.0 Released – What’s New // mySites.guru : сайт. – 2026, 14 апр. – URL: https://mysites.guru/blog/joomla-6-1-release-whats-new/ (дата обращения: 12.10.2026). – Текст : электронный.
  9. Joomla 6.2 is here! // Joomla! : сайт. – 2026, 13 окт. – URL: https://www.joomla.org/announcements/release-news/joomla-6-2-is-here.html (дата обращения: 13.10.2026). – Текст : электронный.
  10. Саханов, Б. Joomla 6 для новостной редакции : исследование-доклад (редакция 1.1) // Webmarka : сайт. – 2026, 8 окт. – URL: https://webmarka.kz/research/joomla-6-newsroom/doklad/joomla-6-news-platform-research.html (дата обращения: 12.10.2026). – Текст : электронный.
  11. Project Roadmap // Joomla! Developer Network : сайт. – URL: https://developer.joomla.org/roadmap (дата обращения: 13.10.2026). – Текст : электронный.
  12. Joomla 6.1 Release Candidate – Test the final package // Joomla! : сайт. – 2026. – URL: https://www.joomla.org/announcements/release-news/5945-joomla-6-1-release-candidate-test-the-final-package.html (дата обращения: 12.10.2026). – Текст : электронный.
  13. Joomla 6.2 Release Candidate – Test the final package // Joomla! : сайт. – 2026, 29 сент. – URL: https://www.joomla.org/announcements/release-news/joomla-6-2-release-candidate-test-the-final-package.html (дата обращения: 12.10.2026). – Текст : электронный.
  14. Joomla 6.1 is here! // Joomla! : сайт. – 2026. – URL: https://www.joomla.org/announcements/release-news/5950-joomla-6-1-is-here.html (дата обращения: 12.10.2026). – Текст : электронный.
  15. Joomla 6 Technical Requirements (2026) // mySites.guru : сайт. – URL: https://mysites.guru/blog/joomla-6-technical-requirements/ (дата обращения: 12.10.2026). – Текст : электронный.
  16. Compatibility Plugins // Joomla! Manual : сайт. – URL: https://manual.joomla.org/updates/54-60/compat-plugin/ (дата обращения: 12.10.2026). – Текст : электронный.
  17. How to Enable POW Captcha in Joomla 6.1 // mySites.guru : сайт. – 2026, 15 апр. – URL: https://mysites.guru/blog/how-to-enable-pow-captcha-in-joomla-6-1.pdf (дата обращения: 12.10.2026). – Текст : электронный.
  18. Joomla 6.1 to 6.2 Upgrade Notes // Joomla! Programmers Documentation : сайт. – URL: https://manual.joomla.org/updates/61-62/ (дата обращения: 13.10.2026). – Текст : электронный.
  19. Рекун, Д. Обзор Joomla API // Joomla для профессионалов : сайт. – 2021, 14 июня. – URL: https://jpath.ru/docs/api/obzor-joomla-api (дата обращения: 12.10.2026). – Текст : электронный.
  20. Joomla 6 — рабочий гайд проекта TAMGARD/wmarka : рабочий документ. – 2026. – Текст : непосредственный (рукопись).
  21. Толкачёв, С. Dependency Injection Containers // Joomla для профессионалов : сайт. – 2022, 14 нояб. – URL: https://jpath.ru/docs/basics/dependency-injection-containers (дата обращения: 12.10.2026). – Текст : электронный.
  22. Рекун, Д. Как программно создать материал с настраиваемыми полями на PHP // Joomla для профессионалов : сайт. – 2024, 31 июля. – URL: https://jpath.ru/practice/kak-programmno-sozdat-material-s-nastraivaemymi-polyami-na-php (дата обращения: 12.10.2026). – Текст : электронный.
  23. Рекун, Д. Как правильно подключать JavaScript и CSS в Joomla 4 // Joomla для профессионалов : сайт. – 2021, 12 окт. – URL: https://jpath.ru/docs/output/js-css/kak-pravilno-podklyuchat-javascript-i-css-v-joomla-4 (дата обращения: 12.10.2026). – Текст : электронный.
  24. JS и CSS : раздел документации // Joomla для профессионалов : сайт. – URL: https://jpath.ru/docs/output/js-css (дата обращения: 12.10.2026). – Текст : электронный.
  25. Рекун, Д. Подключение и размещение CSS стилей, JavaScript и изображений в Joomla // Joomla для профессионалов : сайт. – 2021, 20 дек. – URL: https://jpath.ru/docs/output/html-helpers/podklyuchenie-i-razmeshchenie-fajlov-css-javascript-i-izobrazhenij-v-joomla (дата обращения: 12.10.2026). – Текст : электронный.
  26. Толкачёв, С. Joomla 4 — шаблон Cassiopeia — советы и хитрости : перевод статьи М. Дешевра (Marc Dechèvre) // Хабр : сайт. – 2022, 25 янв. – URL: https://habr.com/ru/articles/647399/ (дата обращения: 12.10.2026). – Текст : электронный.
  27. Саханов, Б. Конвейер новости в скриншотах : Joomla 6.1.4 // Webmarka : сайт. – 2026, 8 окт. – URL: https://webmarka.kz/research/joomla-6-newsroom/konveyer-v-skrinshotakh/ (дата обращения: 12.10.2026). – Текст : электронный.
  28. Joomla! overrides // web-eau.net : сайт. – URL: https://www.web-eau.net/developpement/joomla-overrides (дата обращения: 12.10.2026). – Текст : электронный.
  29. Dinh, H. T4 Framework Updated for Joomla 4.1.0, Open Graph support, improved author feature and more // JoomlArt : сайт. – URL: https://www.joomlart.com/blog/t4-framework/t4-framework-updated-for-joomla-4-1-0-open-graph-support-improved-author-feature-and-mo (дата обращения: 12.10.2026). – Текст : электронный.
  30. Список пользователей сайта и их материалы (Joomla 3) // blog.pbelkov.net : сайт. – URL: https://blog.pbelkov.net/category/joomla/hacks/1386-userlist-and-articles (фрагменты кода цитируются по авторскому архиву). – Текст : электронный.
  31. Отображение 10 похожих публикаций в Joomla // d-nik.site : сайт. – URL: https://d-nik.site/ru/joomla-4/joomla-voprosy-i-otvety/kod-otobrazheniya-10-pokhozhikh-publikatsij-v-materiale-joomla (фрагменты кода цитируются по авторскому архиву). – Текст : электронный.
  32. Рекун, Д. Вывод количества материалов рядом с пунктом меню категории // Joomla для профессионалов : сайт. – 2021, 14 июня. – URL: https://jpath.ru/practice/vyvod-kolichestva-materialov-ryadom-s-punktom-menyu-kategorii (дата обращения: 12.10.2026). – Текст : электронный.
  33. Рекун, Д. Как сделать вывод количества материалов рядом с меткой // Joomla для профессионалов : сайт. – 2021, 23 сент. – URL: https://jpath.ru/practice/kak-sdelat-vyvod-kolichestva-materialov-ryadom-s-metkoj (дата обращения: 12.10.2026). – Текст : электронный.
  34. Joomla 4 (Джумла 4) — бесплатный конструктор сайтов // Лунная База : сайт. – URL: https://mb4.ru/cms/joomla4.html (дата обращения: 12.10.2026). – Текст : электронный.
  35. Перенос в Joomla 6 — что вернуть на штатные средства : рабочий документ проекта TAMGARD/wmarka. – 2026. – Текст : непосредственный (рукопись).
  36. Практические решения : раздел документации // Joomla для профессионалов : сайт. – URL: https://jpath.ru/practice (дата обращения: 12.10.2026). – Текст : электронный.
  37. Рекун, Д. Подготовка расширения к переходу на Joomla 4 // Joomla для профессионалов : сайт. – 2022, 10 янв. – URL: https://jpath.ru/practice/podgotovka-rasshireniya-k-perekhodu-na-joomla-4 (дата обращения: 12.10.2026). – Текст : электронный.
← webmarka.kz: все материалы исследования