Ранее на Хабре были опубликованы статьи Создание модулей с учётом новой структуры Joomla 4, Создание плагинов с учётом новой структуры Joomla 4, где освещались нюансы по апгрейду расширений до архитектуры Joomla 4 / Joomla 5. А также статья Создание плагина кнопки редактора в Joomla 4, в которой в том числе затрагиваются вопросы распределения функционала между фронтом и бэком, приводится практический пример работы с ajax нативными для Joomla средствами. Однако, расширение типа template, пожалуй, создаётся для сайтов на Joomla чаще, чем другие типы расширений.

Оглавление статьи

Вступление

Нужно признать, что Joomla предоставляет довольно большой функционал из коробки, путей решения одной и той же задачи может быть несколько и бывает сложно назвать какой-либо из них однозначно правильным. Разные разработчики являются приверженцами разных подходов, что порой вызывает жаркие споры между ними.

С Joomla работают как начинающие веб-мастера, так и профессиональные команды. В руках разработчика шаблона для Joomla может оказаться как только FTP, Notepad++ и шаблон-билдер, так и и профессиональные среды разработки (PHP Storm / VS Code). Существуют команды, где Joomla обитает в условиях CI/DI, Git, тестовых стендов и прочим. А бывают и условия, когда изменения вносятся прямо на прод (production, боевой сайт) и он падает прямо на глазах у посетителей. Поднимается и снова падает, и снова поднимается... Соответственно конечный результат работы вебмастера, разработчика может быть совершенно разный и мы понимаем, что не инструмент красит человека, а умелые руки - инструмент.

Определенную нишу занимают шаблоны-билдеры, а-ля Helix Ultimate (+ SP Page Builder) от JoomShaper, YooTheme PRO, Astroid (Templazza), T4 (JoomlaArt), Gantry и другие. Работа с ними несколько отличается от классических шаблонов, так как предполагает создание своих элементов для билдеров и в целом содержит немалую долю работы с интерфейсом. Этот подход имеет свою аудиторию и право на жизнь. Однако, приобретая в скорости разработки шаблона нередко приходится жертвовать качеством HTML-кода. В нём могут появляться лишние div-обёртки и мусорный код, которых можно было бы избежать. В данной статье шаблоны-билдеры если и будут затрагиваться, то косвенно.

Процесс создания шаблона состоит из дизайна, верстки и "натяжки" на CMS. Дизайном обычно занимается дизайнер, вёрсткой может заниматься отдельный верстальщик (порой за штатом). При этом особенности CMS могут не учитываться и тому, кто будет натягивать шаблон на Joomla, придётся что-то переделывать как минимум в вёрстке. Данный текст может помочь как дизайнерам, так и остальным участникам процесса избежать подобных ситуаций.

В данной статье я постараюсь сместить акцент с вектора "как создать шаблон для Joomla 4 и старше" на "какие возможности даёт Joomla 4 при разработке шаблона и для уникализации страниц". Использовать или нет и как использовать определённые возможности Joomla для создания шаблона сайта - решать уже Вам.

Данная статья может пригодиться не только тем, кто будет создавать шаблон с нуля, но и тем, кто работает с готовым шаблоном и нуждается в его изменении. Далеко не у всех клиентов есть бюджеты на хорошего дизайнера и отдельного верстальщика, поэтому случаев, когда нужно "скачать, установить и изменить под себя" довольно много. На заметку - каталог шаблонов Joomla.

В тексте статьи будут кратко описываться и базовые понятия Joomla. Они будут не интересны опытным разработчикам, но полезны тем, кто впервые сталкивается с подобной задачей.

Обзор литературы

Из статей-мануалов на Хабре можно выделить серию статей Создаём шаблон Joomla по стандартам (часть 1) 2008 года - ещё для Joomla 1.5. Статью Как сверстать шаблон для Joomla 2015 года, где на скриншотах видна Joomla 3.4.1. А также статья Дизайн шаблона Joomla для front-end разработчика совершенно незнакомого с CMS 2014 года, которая показалась мне самой лаконичной и дельной из всех представленных на Хабре.

К сожалению, официальная документация Joomla по созданию шаблона на момент написания статьи приводит примеры для Joomla 1.5-2.5 и большая её часть на данный момент является уже устаревшей.

Новый портал документации Joomla пока ещё на стадии наполнения. На момент написания этой статьи о шаблонах там заполнена лишь некоторая информация.

Подавляющую часть сил активная часть сообщества бросает на работу с кодом ядра, поэтому актуальная документация - это больная мозоль. Постараемся от неё избавиться в этой статье.

Из современных недавних текстов, посвящённых уже Joomla 4 можно отметить статью Developing a Joomla Template" Кевина Олсона (Kevin Olson). Нужно отдать автору должное, периодически он обновляет содержимое статьи. На том же сайте в категории статей Joomla Templates есть ещё несколько полезных текстов о разработке шаблонов Joomla.

Также в интернете существует довольно много статей и видео, посвящённых созданию шаблона для предыдущих версий Joomla. Многие принципы, описанные в них, остались и действует. Но знание деталей и нюансов как старых, так и новых не менее важно.

Определение логической структуры страниц

Для каждой страницы сайта важна верно сконструированная в нескольких плоскостях логика: маркетинг, UI/UX, дизайн, верстка, взаимодействие с бэком.

Маркетинг

Здесь мы подразумеваем то, что страница в целом должна обеспечить пользователю максимально быстрое и удобное продвижение по пути к совершению целевого действия (продвижение по customer jorney). Для разных типов страниц это разные целевые действия: для одних это может быть переход на другую страницу, для других - нажатие кнопки, обращение в живой чат, заполнение формы, долгое время нахождения на странице, просмотр попапа (всплывающего окна) и т.д. Распределением по страницам целевых действий занимается не верстальщик или вебмастер, но он должен понимать что должна страница сайта предоставить пользователю.

UI/UX и дизайн

Я позволю себе объединить их в один раздел эти специфичные, хоть и во многом близкие по целям, области. Грамотный UI/UX с помощью дизайна является той логикой и в чём-то механикой продвижения посетителя сайта к целевому действию. Именно UI/UX специалисты в купе с дизайнерами помогают нам быстро ориентироваться на странице, находить нужные кнопки и радоваться работе с приятным интерфейсом.

Верстка

Казалось бы зачем всё вышесказанное знать верстальщику? Причем тут код? Но знание того, что должно, например, отобразиться на сайте быстрее и начать работать быстрее (в случае активных элементов) позволит выстроить правильный порядок загрузки скриптов. Или не помещать в карусель изображений на первый экран 100500 картинок, так как пользователи пролистают от силы 2-3 и уйдут вниз по странице или вообще на другую страницу. Те же знания о UI/UX и важных целевых действиях позволят правильно расставить атрибуты loading="lazy" изображениям (а не всем подряд) и т.д. и т.п.

Семантическая верстка

На эту тему написаны тонны текстов. Joomla начиная с версии 3.0 имеет возможность использовать HTML5 теги для модулей - уже больше 10 лет. Но насколько редко этим функционалом пользуются! А ведь она нужна для передачи смысла поисковикам.

У вас контентный сайт, в статье покупают ссылки и размещают рекламные блоки. Для тех, кто покупает ссылки хорошо, чтобы они были максимально "нативно" внедрены в текст, в то время как для Вашего сайта важно не засорять тексты сильно, иначе поисковики вычленят купленные ссылки, могут пессимизировать сайта и он потеряет трафик, а вы - прибыль от рекламы. Таким образом, рекламную вставку внутри текста статьи Вы будете оформлять не тегом <div> или <section>, а тегом <aside>, семантическая роль которого в отделимом и не имеющем прямого отношения к основному тексту смысле. А если модуль с тегами статей в правой или левой колонке окажется в теге <aside>, то его семантическая полезность для страницы может быть утрачена, так как поисковик будет считать его содержимое не относящимся к основному смыслу страницы.

Таких деталей много: теги <strong> и <b>, теги <section> и <article>, обернуть вывод компонента в <main> (коих, кстати, может быть несколько на странице) и т.д. Семантическая вёрстка - это про умение грамотно понимать и выражать мысли, расставлять правильные акценты. У Joomla для этого есть все возможности.

Взаимодействие с backend сайта

Здесь хочется сказать о том, что в Joomla существуют типовые для неё способы решения определённых задач. Например, одна из страниц Вашего макета сайта - какой-нибудь условный калькулятор. И вы хотите спрятать бизнес-логику этого калькулятора за бэкендом. Чаще всего незнакомые с Joomla разработчики начинают помещать вне Joomla API свои файлы-обработчики и обращаться к ним по ajax. Или точно такая же история может быть с формами обратной связи (на Хабре писал уже статью на эту тему).

В Joomla уже много лет существует ajax-интерфейс для получения данных и если расширение (компонент, модуль, плагин, шаблон) его поддерживает (читаем официальную документацию), то пользоваться надо именно им.

Так, в примере с калькулятором, обработчик формы должен быть оформлен в виде плагина группы ajax. Но, если этот же плагин выполняет ещё какую-то работу, то группу для плагина можно выбрать и любую другую. Благо через ajax-интерфейс можно обратиться к любому плагину (лишь бы сам плагин поддерживал работу с ajax-интерфейсом). Также начиная с Joomla 4 в ядре есть REST API. При необходимости можно использовать и его.

Это вроде бы мелочи, о которых думают в последний момент или не думают вообще. Но при оценке стоимости и сроков проекта влияние подобных мелочей весьма ощутимо.

Определение типа расширения Joomla для каждого элемента страницы

При составлении плана статьи я хотел назвать этот раздел "определение сущностей Joomla для элементов страницы", но в рамках Joomla термин "сущности" пока что не используется, а если и будет использоваться, то в другом ракурсе. Поэтому "сущности" стали "типами расширений", но смысловой оттенок я хотел бы взять именно от "сущностей" - когда мы смотрим на дизайн, мы должны представлять каким типом расширения будет выводиться (какой "сущностью" будет являться) тот или иной блок страницы.

Какие типы расширений Joomla существуют?

В Joomla существует 7 типов расширений (подробнее Типы расширений Joomla), из которых возможность вывода HTML имеют 4: компонент, модуль, плагин и шаблон.

Компонент (Component) - это самый сложный тип расширения в Joomla. Как правило он даёт возможность CRUD операций (создание, чтение, изменение и удаление данных в базе данных). Примеры компонентов: блог, каталог продукции, доска объявлений, социальная сеть, интернет-магазин, гостевая книга и т.д. В комплекте с компонентом часто идут плагины и модули, иногда библиотеки. В таком случае компонент и сопутствующие расширения собираются в пакет. Ядро Joomla содержит встроенные компоненты (например, для блога, управления пользователями и их правами доступа, контакты и т.д.) и может расширяться компонентами сторонних разработчиков (интернет-магазины, форумы, e-mail маркетинг и т.д.).

Модуль (Module) - расширение, которое чаще всего выводит данные из какого-либо компонента или собственных настроек, либо позволяет добавлять данные в какой-либо компонент. Пример модулей: слайдшоу изображений, карусель товаров, новые статьи, форма подписки на email-рассылку и т.д. Иногда функции модулей и компонентов могут пересекаться. Например, вывести интерактивную карту с гео-метками филиалов и контактов к ним можно с помощью как специализированного компонента, так и с помощью модуля; форму авторизации можно отобразить как с помощью компонента (отдельный пункт меню и страница на сайте), либо модулем (отобразит форму входа на всех страницах согласно настройкам модуля). Выбор конкретного способа зависит от решаемых задач.

Модули выводятся в позициях шаблона, которые задаются в файле index.php шаблона или же в том месте, где их вызывает шорт-код {loadposition ...} или {loadmoduleid 123}. Также есть продвинутый вариант рендера модулей программным методом с помощью класса ModuleHelper.

Плагин (Plugin). В терминологии Joomla "плагин" - это расширение, которое предоставляет функции, которые связаны с событиями (Event Dispatching). Когда происходит вызов конкретного события, то происходит последовательное выполнение всех функций подключаемых плагинов, связанных с этим событием. События могут вызывать как ядро Joomla, так и компоненты, да и в принципе любые расширения Joomla.

Общая информация о принципе действия Joomla

Полезно будет посмотреть статью Общая информация о принципе действия Joomla. Она описывает общий принцип действия Joomla, начиная с загрузки файла index.php и заканчивая отображением страницы в обозревателе. В статье рассматривается Joomla 3, однако большая часть верна и для Joomla 4.

Видов плагинов существует огромное количество. Они могут добавлять команды в CLI интерфейс, отправлять данные заказа в CRM и службу доставки, добавлять поля в интерфейс админки, обрабатывать и проверять данные в процессе их изменения и многое другое. Вызовы плагинов происходит в разные моменты жизненного цикла приложения Joomla.

Из их многообразия нас больше всего интересуют плагины группы content. Плагины этой группы чаще всего обрабатывают HTML код и заменяют в нём шорткоды вида {START_TAG}некий код{/END_TAG} или {ANY_TAG param=1 param2=2} на нужные Вам данные. Например, известный плагин All Videos заменяет теги {youtube}код_видео{/youtube} на код проигрывателя Youtube-видео, а {MP4}путь_к_файлу{/MP4} - на HTML5 тег <video> для указанного файла. Но таким образом могут вставляться и модули (шорт-код {loadposition position_name} или {loadmoduleid id}) и данные из других компонентов, если для них существуют контент-плагины (фотогалереи, товары, карусели и т.д.). С HTML-кодом часто работают плагины группы system на событие onAfterRender. В этом случае обработка HTML-кода происходит с помощью регулярных выражений PHP.

Шаблон (Template). Это тип расширения, в котором описывается HTML разметка для HTML-вывода сайта. В терминологии WordPress шаблон Joomla - это тема. У шаблона также могут быть свои параметры, логика вывода позиций для модулей и т.д. В шаблоне (но не только в нём) подключаются CSS, JS, иконки, шрифты и прочие Web Assets.

В файлах шаблона иногда хардкодятся костыли.

В целом, для того, чтобы правильно определить типы расширений Joomla для проекта нужно знать как минимум расширения ядра CMS и их возможности. В идеале на сайте должен быть минимум сторонних расширений, чтобы не зависеть от работы внешних разработчиков. У них может быть свой road map, который не пересекается с Вашим или впоследствии разработчик может забросить своё детище и Вам придётся искать альтернативы.

Собственно определение типов расширений Joomla при создании шаблона

Как я уже говорил, в Joomla одна и та же в целом задача может быть решена разными способами. Здесь нужно иметь ещё и некое видение наперёд: как будет развиваться проект, в какую сторону он будет развиваться и постараться предусмотреть наиболее вероятные пути его движения. В зависимости от этого Вы будете выбирать правильные расширения для сайта и с ними уже работать.

Так, каталог продукции можно сделать как на стандартных материалах Joomla (статья), так и на компоненте интернет-магазина, который будет работать в режиме каталога. Если Вы поговорили с клиентом и выяснили, что каталог останется каталогом, то можно использовать стандартные материалы. Из плюсов - Вы не зависите от сторонних расширений. А если же проект может "взлететь" до приёма оплаты, личного кабинета, интеграции с CRM, службами доставки и прочим, то лучше сразу использовать компонент интернет-магазина. Таким образом Вам легче будет работать с ним потом.

Перейдём, наконец, к практике.

Если б Хабр был сделан на Joomla

Возьмём для примера страницу просмотра статьи на Хабре и посмотрим как она устроена.

Условные обозначения:

  • T - template - в принципе это вся страница целиком. Но специально выделены те элементы, которые обычно выносятся в настройки шаблона (или порой хардкодятся).

  • C - component - область вывода компонента. Центральный, основной контент страницы.

  • M - module - "обвязка" основного контента из модулей с самым разным функционалом.

  • P - plugin - плагины. В данном примере действия плагинов похожи на модули, но плагины внедряются в то, что выводит компонент и добавляют в него свой HTML-вывод. Поскольку на уровне кода это может быть решено разными способами, то в этих случаях я указал знак вопроса: P ? M.

А что если Хабр сделан на Joomla?..
А что если Хабр сделан на Joomla?..

Несмотря на кажущуюся простоту страница просмотра статьи на Хабре имеет сложную структуру (и это только одна страница!). На ней более 10 разных типов модулей. На странице используются минимум 7 (скорее всего 8-10) позиций для модулей. Частично задействованы плагины. Но это могут быть тоже модули )). Всё зависит от конкретных расширений, которые Вы используете на сайте.

Сущность шаблона Joomla. Отличия шаблонов Joomla 4 и Joomla 5 от шаблонов Joomla 3

Кратко, описания изменений для опытных Joomla-разработчиков.

  1. Папка media для шаблона. Начиная с Joomla 4.1 все медиа-файлы (CSS, JS, шрифты, иконки и картинки и прочее) переехали в папку media/templates/site/template_name. Если шаблон пишете для админки Joomla, то всё помещаем в media/templates/administrator/template_name.

  2. Joomla 4 внедрила концепцию Web Assets. Управление JavaScript и CSS в Joomla значительно упростилось, благодаря классу WebAssetManager. Есть замечательная статья Как правильно подключать JavaScript и CSS в Joomla 4, в которой подробно и с примерами кода рассказывается об этой концепции и её применении. 
    Например, было: иконочный шрифт могут использовать 2 разных модуля. CSS обычно подключается в шаблоне и он грузится везде, даже там, где не надо. Если же подключать CSS в одном модуле, а в другом нет - на странице стиль есть ровно до тех пор, пока опубликован модуль с этим подключением.
    Стало: теперь в макетах расширений мы просто пишем $wa->useStyle('my.style'); и за необходимостью подключения нужного ассета (в данном случае CSS с иконочным шрифтом) следит Web Asset Manager. Если мы снимем один модуль с публикации, то нужный ассет подключит другой модуль. Также поддерживаются зависимости. Например, если какой-то js-скрипт требует jQuery (от чего сейчас избавляются, но всё же), то достаточно при подключении ассета своего скрипта указать в зависимостях jQuery и он подключится автоматически, даже если в других местах Вы его выключали. Для инлайн стилей есть возможность выстраивать порядок включения.

  3. Файл joomla.asset.json. Для того, чтобы можно было работать с WebAssetManager нужно создать файл joomla.asset.json и описать js, css шаблона в нём. Файл должен находиться в templates/template_name.

  4. Дочерние шаблоны. Начиная с Joomla 4.3 есть возможность создавать дочерние шаблоны. Для того в XML-манифесте должен быть элемент <inheritable>1</inheritable>. Файлы дочернего шаблона переопределяют файлы родительского. То есть мы можем скопировать из родительского шаблона лишь некоторые файлы, которые мы хотим изменить, а в остальном будут использоваться файлы родительского шаблона.

Теперь опишем более подробно процесс работы с шаблоном.

Файловая структура шаблона Joomla 4 и Joomla 5

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

Файловая структура шаблона Joomla 4 и Joomla 5
Файловая структура шаблона Joomla 4 и Joomla 5

Минимум для создания шаблона нужно по-прежнему 2 файла:

  • templateDetails.xml - XML-манифест расширения. В нём содержится информация об авторе, дате создания, сервере обновлений (если есть), раздел с настройками и т.д.

  • index.php - основной файл, в котором содержится HTML-разметка шаблона.

Но мы устанавливаем Joomla 4 или Joomla 5 не для того, чтобы работать как 20 лет назад. Поэтому структура современного шаблона более сложная.

Файл templateDetails.xml

Содержимое этого файла во-многом аналогично XML-манифестам прочих расширений Joomla.

<?xml version="1.0" encoding="utf-8"?>
<extension type="template" client="site" method="upgrade">
	<name>webtolk</name>
	<version>1.0.0</version>
	<creationDate>April 2023</creationDate>
	<author>Sergey Tolkachyov</author>
	<authorEmail>info@web-tolk.ru</authorEmail>
	<copyright>(C) 2023 Sergey Tolkachyov.</copyright>
	<description>TPL_WEBTOLK_DESC</description>
	<inheritable>1</inheritable>
	<files>
		<filename>component.php</filename>
		<filename>error.php</filename>
		<filename>index.php</filename>
		<filename>joomla.asset.json</filename>
		<filename>offline.php</filename>
		<filename>templateDetails.xml</filename>
		<folder>html</folder>
		<folder>subform</folder>
	</files>
	<media destination="templates/site/webtolk" folder="media">
		<folder>js</folder>
		<folder>css</folder>
		<folder>fonts</folder>
		<folder>scss</folder>
		<folder>images</folder>
	</media>
	<positions>
		<position>topbar</position>
		<position>below-top</position>
		<position>menu</position>
		<position>search</position>
		<position>banner</position>
		<position>top-a</position>
		<position>top-b</position>
		<position>main-top</position>
		<position>main-bottom</position>
		<position>breadcrumbs</position>
		<position>sidebar-left</position>
		<position>sidebar-right</position>
		<position>bottom-a</position>
		<position>bottom-b</position>
		<position>footer</position>
		<position>debug</position>
	</positions>
	<languages folder="language">
		<language tag="en-GB">en-GB/tpl_webtolk.ini</language>
		<language tag="en-GB">en-GB/tpl_webtolk.sys.ini</language>
		<language tag="ru-RU">ru-RU/tpl_webtolk.ini</language>
		<language tag="ru-RU">ru-RU/tpl_webtolk.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<!-- тут параметры шаблона -->
			</fieldset>
		</fields>
	</config>
</extension>

Я опущу интуитивно понятные элементы и опишу только самые необходимые.

Элемент <name> - это системное имя шаблона, под которым Joomla будет знать этот шаблон. Также это имя папки шаблона, в которую он будет установлен в папке templates. Если в имени содержатся пробелы и другие символы - для системных нужд они будут удалены. В админке же Вы будете видеть красивое имя вида "My Digital Agency - Cool Template name".

Элемент <inheritable> нужен для возможности создания дочерних шаблонов. Чуть выше описано как это работает.

Элемент <description> - краткое описание расширения. Тут можно писать сразу текст или использовать языковую константу из языковых файлов шаблона. Аналогичный подход и к параметрам шаблона.

Элемент <files>. Здесь мы указываем список копируемых файлов и папок шаблона. Если физически файл будет установочном пакете, но не описан в XML-манифесте - он не будет скопирован. Если файл будет описан, но физически его не будет в установочном пакете - будет ошибка установки шаблона.

Элемент <media>. О нём кратко сказано выше (о концепции Web Assets). Он сообщает какие папки нужно поместить в папку media сайта. Атрибут destination="templates/site/webtolk" указывает путь от папки media, атрибут folder="media" говорит из какой папки в установочном архиве нужно брать файлы и папки для копирования.

Элемент <positions> - это список позиций для модулей, который Ваш шаблон поддерживает. Эти позиции оказываются в выпадающем списке в настройках модулей.

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

Но никто не мешает Вам в этом поле ввести вручную имя позиции, не описанной в XML-манифесте и в index.php (или шорткодом lв материалах или программно где угодно) вызвать эту позицию и модули волшебным образом появятся там, где нужно. Это очень помогает, когда нужно вставить условную форму захвата контакта или какой-нибудь другой модуль прямо в середину статьи. Придумываем несуществующую доселе позицию, пишем её в настройках модуля (нажимаем Enter) и знаем, что эта позиция используется только в одном конкретном месте. Важно понимать, что позиции для модулей легко добавляются на лету. Не нужно каждый раз редактировать XML-манифест шаблона. Хотя это правильный путь, но так редко делают. Обычно в XML-манифесте описаны часто используемые, универсальные позиции.

Элемент <languages> содержит описания языковых файлов для шаблона. Если пользователи админки из разных стран или Вы планируете распространять свой Joomla шаблон в разных странах, то полезно сделать файлы локализации. Если же не планируете, то можно на первое время захардкодить описания, текст прямо в XML.

Но:

  1. Нужно всё равно сделать языковые константы, чтоб был порядок на сайте.

  2. В языковые константы можно помещать HTML, предварительно экранировав кавычки атрибутов. Тогда Вы можете делать информативные описания со ссылками, картинками и т..д. Можно хоть видео с Youtube вставлять.

Секция <config> нужна для шаблонов, у которых есть настраиваемые параметры. Подробнее об этом ниже.

Файл index.php шаблона Joomla 4

Здесь находится HTML-каркас вёрстки Joomla сайта. Базовой для создания шаблона вещью является конструкция <jdoc:include type="тут тип"/>. Самый простой index.php может выглядеть следующим образом:

<?php
// No direct access to this file
defined('_JEXEC') or die;
?>

<!DOCTYPE html>
<html>
<head>
  <!-- В Joomla 3 был только type="head". 
  В Joomla 4 появились три типа, вместо одного type="head":
  type="metas", type="styles", type="scripts"
  Теперь можно быстро перенести скрипты и стили в подвал сайта, 
  без всяких плагинов (если это нужно) -->
	<jdoc:include type="metas"/>
	<jdoc:include type="styles"/>
	<jdoc:include type="scripts"/>
</head>

<body>
<nav>
<jdoc:include type="modules" name="menu"/>
</nav>
<main>
<jdoc:include type="component"/>
</main>
</body>
</html>

Обратите внимание, что здесь совсем нет PHP-кода, но данный шаблон уже будет работать. defined('_JEXEC') or die не в счёт, так как это стандартная проверка безопасности Joomla, не позволяющая обращаться к файлу "снаружи", минуя Joomla API. Эта строка должна быть абсолютно в любом PHP-файле в Joomla, в самом начале. Если в файле есть секция подключения namespaces - use \Joomla\CMS\Factory например - строка defined... должна идти сразу после неё.

<jdoc:include type="modules" name="menu"/> - выведет в данном месте HTML, который отдают модули в позиции menu. Также может быть ещё 3-й параметр style, который означает выбранный chrome (обёртку) для модуля - <jdoc:include type="modules" name="menu" style="xhtml"/>. Подробнее об этом будет сказано ниже.

Также спецификация HTML5 требует, чтобы у тега <html> были указаны параметры языка и направления чтения. Joomla предоставляет эти данные для шаблона. Тег <html> будет выглядеть следующим образом:

<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">

Условия для вывода позиций внутри шаблона

При работе с сайтом, чтобы узнать какие модульные позиции существуют в шаблоне нужно в Система - Шаблоны - кнопка "Настройки" включить параметр "Просмотр позиций модулей". Не забудьте его выключить после окончания работ с шаблоном сайта.

параметр "Просмотр позиций модулей" в настройках менеджера модулей Joomla 4
параметр "Просмотр позиций модулей" в настройках менеджера модулей Joomla 4

После этого добавьте в адресной строке параметр &tp=1 и Вы увидите все модульные позиции Вашего шаблона. Чтобы вернуть всё обратно - уберите этот параметр из адресной строки. Если ничего не поменялось, то сначала установите параметр равным 0 - &tp=0, а потом уберите.

Проверка наличия модулей и их содержимого - countModules('position-name')

На практике таким простым шаблоном, как в примере выше, никто не пользуется. Часто вывод позиций содержит разного рода обёртки в вёрстке. Так, типовую 3-хколоночную сетку - правая, левая колонки и центральный основной текст - Вы задавать будете именно в index.php шаблона. Однако, если модули сайта на каких-то страницах не отображаются в данной позиции, то шорт-код <jdoc .../> ничего не выведет, а в HTML-коде останется лишняя разметка и мы увидим левую/правую колонку, но пустую. Дабы этого избежать в шаблон внедряется проверка на наличие модулей в данной позиции:

<?php if ($this->countModules('topbar')) : ?>
	<div class="container-fluid topbar">
		<jdoc:include type="modules" name="topbar"/>
	</div>
<?php endif; ?>

Метод $this->countModules('topbar') проверяет есть ли опубликованные модули в данной позиции и возвращает количество опубликованных модулей в позиции. В PHP при нестрогом сравнении int 0 будет логически равно false. Таким образом, если опубликованных модулей в позиции нет, то лишняя разметка выводиться не будет.

Но, бывают случаи, когда модуль опубликован на всех страницах. И на каких-то страницах ему есть что показать, а на каких-то нет. Такое поведение бывает у модулей фильтров товаров в каталогах, у модулей просмотренных товаров, когда ещё ни один товар не был просмотрен и т.д. Тогда получается, что модуль в позиции есть, в шаблоне будет показываться его обёртка из вёрстки, но контента в нём по факту нет. Для таких случаев в Joomla 4 внедрён второй параметр для метода countModules, который называется withContentOnly. Он принимает на вход true или false, по умолчанию - false. Если этот параметр равен true, то Joomla выполняет предварительный рендер модулей и смотрит присутствует ли результирующий HTML-вывод модулей - их контент. Если нет, то позиция выводиться не будет. Этот параметр нужно использовать там, где нужно. Например, шапка сайта - с логотипом, меню, контактами, кнопками "заказать звонок" на сайте присутствует всегда. Также и подвал сайта с логотипом, частью меню, контактами и другой полезной информацией - тоже присутствует всегда, на всех страницах сайта. Поэтому для этих позиций нет смысла делать пререндер модулей. В то время как для боковых колонок, позиций над и под выводом компонента и другими это может иметь смысл.

<?php 
// Параметр true выполнит пре-рендер содержимого модулей и если модуль есть, но 
// контента нет - данный блок разметки выводиться не будет.
if ($this->countModules('topbar', true)) : ?>
	<div class="container-fluid topbar">
		<jdoc:include type="modules" name="topbar"/>
	</div>
<?php endif; ?>

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

<?php 
// Если есть модули и их содержимое в позиции topbar -
// выведем модули в позиции bottom-bar
if ($this->countModules('topbar', true)) : ?>
	<div class="container-fluid bottom-bar">
		<jdoc:include type="modules" name="bottom-bar"/>
	</div>
<?php endif; ?>

Показ позиций только на определённых страницах

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

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

Настройки привязки модулей к пунктам меню в Joomla 4
Настройки привязки модулей к пунктам меню в Joomla 4

В этих случаях нам нужно работать с параметрами URL Joomla. Все URL в Joomla (после включения SEF) выглядят человеко-понятно (ЧПУ) - то есть site.ru/category-alias/article-name. Однако, "под капотом" нам доступны традиционные URL с GET-параметрами. И опираясь на их значения мы можем выстраивать логику показа позиций. Выключите SEF на своём Joomla-сайте, чтобы увидеть URL вида index.php?option=com_... и Вы увидите что-то вроде этого: index.php?option=com_content&view=article&id=2 для компонента материалов. Или index.php?option=com_jshopping&controller=product&task=view&category_id=3&product_id=2 для компонента интернет-магазина JoomShopping.

Параметр option равен системному имени компонента. com_content для материалов и категорий Joomla, com_contact для компонента контактов, com_jshopping для интернет-магазина JoomShopping, com_virtuemart для интернет-магазина Virtuemart и т.д. У любого компонента Joomla есть свой уникальный option-параметр в URL.

Дальше у разных компонентов могут быть разные параметры, однако нужные можно подсмотреть, побродив по сайту с выключенным SEF URL.

Чаще всего встречаются следующие параметры URL:

  • option - системное имя компонента

  • controller - может быть разным для категории и товара, например. controller=category, controller=product

  • view - вид. Может быть разным для категории и материала или контакта. view=category, view=article, view=contact.

  • layout - определяет параметры вида. Например layout=edit.

  • task - ещё один параметр, который может использоваться в компонентах Joomla.

Чтобы получить значения этих параметров в шаблоне используется метод getInput() объекта приложения Joomla:

<?php
use Joomla\CMS\Factory;

defined('_JEXEC') or die;

$app = Factory::getApplication(); 
// Получаем любые параметры из Input
// Это могут быть данные из $_GET или $_POST
// или из $_SERVER
$option     = $app->getInput()->getCmd('option', '');
$controller = $app->getInput()->getCmd('controller', '');
$view       = $app->getInput()->getCmd('view', '');
$layout     = $app->getInput()->getCmd('layout', '');
$task       = $app->getInput()->getCmd('task', '');
$itemid     = $app->getInput()->getCmd('Itemid', '');

В методе getCmd('parameter_name','default_value') parameter_name - имя параметра запроса (в том числе GET-запроса). default_value - значение по умолчанию, если запрашиваемый параметр отсутствует или будет пустым. Метод $app->getInput() возвращает объект Input, который раньше в Joomla 2.5 и Joomla 3.x назывался JInput. Официальная документация.

Так, мы можем показывать модульную позицию topbar только в виде категории материалов при условии, что в ней опубликованы модули и модулям есть что сказать миру:

<?php 
use Joomla\CMS\Factory;

defined('_JEXEC') or die;

$app = Factory::getApplication(); 
// Получаем любые параметры из Input

$option     = $app->getInput()->getCmd('option', '');
$view       = $app->getInput()->getCmd('view', '');

// Если есть модули и их содержимое в позиции topbar выведем позицию
if ($this->countModules('topbar', true) && $option == 'com_content' && $view == 'category') : ?>
	<div class="container-fluid topbar-bar">
		<jdoc:include type="modules" name="topbar-bar"/>
	</div>
<?php endif; ?>

Проверять на $option лучше всегда, так как у Вас на сайте могут использоваться одновременно несколько компонентов, которые могут иметь в своём url параметр $view. И Ваши модули могут вылезти там, где не нужно. Ещё пример - выводим модульную позицию только в карточке товара компонента интернет-магазина JoomShopping.

<?php 
use Joomla\CMS\Factory;

defined('_JEXEC') or die;

$app = Factory::getApplication(); 
// Получаем любые параметры из Input

// тут будет 'com_jshopping'
$option     = $app->getInput()->getCmd('option', ''); 
// тут будет 'product'
$controller       = $app->getInput()->getCmd('product', '');
// тут будет 'view' (т.е. просмотр), а может быть 'edit' или что-то ещё
$task       = $app->getInput()->getCmd('task', '');

// Если есть модули и их содержимое в позиции topbar выведем позицию
if ($this->countModules('topbar', true) 
    && $option == 'com_jshopping' 
    && $controller == 'product' 
    && $task == 'view') : ?>
	<div class="container-fluid topbar-bar">
		<jdoc:include type="modules" name="topbar-bar"/>
	</div>
<?php endif; ?>

Логику отображения позиций можно использовать любую: по сочетанию условий, отрицанию условия и т.д. Документация PHP: операторы сравнения.

Не показывать вывод компонента на главной странице

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

Вариант 1 - используется компонент-билдер страниц. Ничего менять не нужно.

Вариант 2 - собрать нужные блоки для главной страницы из модулей. Тогда вывод компонента может только мешать, так как Joomla должна знать к каком компоненту принадлежит тот или иной пункт меню. Второй вариант, в свою очередь, может иметь 2 пути решения задачи:

  • отключать вывод компонента на главной странице

  • использовать компонент "пустой страницы", главная задача которого... ничего не показывать.

Чтобы отключить вывод компонента на главной странице в Joomla нам пригодится класс для работы с Uri - Joomla\CMS\Uri\Uri.. В нём есть 2 метода, возвращающих текущий страницу и base uri. Мы сравниваем и если они не равны - можно выводить данные компонента. Почему не равны? Потому, что когда мы находимся на любой другой странице, кроме главной, метод Uri::current() будет возвращать нам url вида https://my-sute.ru/category-name, а метод Uri::base() - только https://my-sute.ru. Если же они оказываются одинаковыми, то это признак нахождения на главной странице.

<?php

use Joomla\CMS\Uri\Uri;

defined('_JEXEC') or die;

if(Uri::current() !== Uri::base()) :?>
<!-- Этот блок кода не будет показываться на главной странице сайта Joomla -->
  <main>
      <jdoc:include type="component"/>
  </main>
<?php endif; 

Способ уникализации страниц с помощью CSS и pageClass

В Joomla в настройках пунктов меню есть параметр CSS-класс страницы.

Вывод этого параметра зависит от конкретного шаблона. Но, в стандартном шаблоне Cassiopeia этот параметр назначается тегу <body> страницы.

<?php 
// Это фрагмент кода из файла index.php шаблона Cassiopeia

// Параметр CSS-класс страницы из настроек пункта меню
$pageclass = $menu !== null ? $menu->getParams()->get('pageclass_sfx', '') : '';

?>
<!-- И далее ниже по коду -->
<body class="site <?php echo $option
    . ' ' . $wrapper
    . ' view-' . $view
    . ($layout ? ' layout-' . $layout : ' no-layout')
    . ($task ? ' task-' . $task : ' no-task')
    . ($itemid ? ' itemid-' . $itemid : '')
    . ($pageclass ? ' ' . $pageclass : '')
    . $hasClass
    . ($this->direction == 'rtl' ? ' rtl' : '');
?>">

Поскольку CSS-класс будет назначен всей странице, это означает, что в своих CSS правилах мы можем переиначить полностью весь дизайн только опираясь на наличие или отсутствие этого CSS класса у <body>.

/* Просто цвет ссылки - синий */
a { color: var(--bs-blue); }

/* А здесь - <body class="dark"> - цвет ссылки красный*/
body.dark a { color: var(--bs-danger); }

Таким образом можно создавать уникальные дизайны для отдельных страниц сайта, создавать темы (тёмная / светлая / рыжая / и т.д.) и "включать" их просто указанием нужного класса в админке Joomla.

Нюанс: в выводе некоторых компонентов Joomla параметр $pageclass может выводиться не только у <body> (за это отвечает файл шаблона), но и в области вывода компонента. Так происходит, например, в материалах Joomla (com_content) при просмотре полного текста материала.

Поэтому при построении путей селекторов в CSS нужно быть внимательным. Либо сделать переопределение макета в шаблоне и убрать ненужный код.

Файл component.php

Если к любому url в Joomla добавить параметр tmpl=component, то в ответ Вы получите содержимое вывода только компонента, без окружающих его модулей (без меню, шапки, подвала, боковых колонок и т.д.). Это часто используют в случаях, если нужно показать что-то во всплывающем окне, например, текст политики обработки персональных данных в форме обратной связи. В таких случаях ссылка около чекбокса вызывает модальное окно, а в модальном окне находится <iframe>, в атрибуте src которого ссылка на материал с параметром tmpl=component. Тогда пользователи Вашего сайта смогут ознакомиться с текстом без перехода на другую страницу. HTML-макет такого вывода данных находится в файле component.php.

Его содержимое, как правило, очень простое

<?php

// содержимое файла templates/system/component.php

defined('_JEXEC') or die;

/** @var Joomla\CMS\Document\HtmlDocument $this */

// Styles
$this->getWebAssetManager()->registerAndUseStyle('template.system.general', 'media/system/css/system-site-general.css');

?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
    <jdoc:include type="metas" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <jdoc:include type="styles" />
    <jdoc:include type="scripts" />
</head>
<body class="contentpane">
    <jdoc:include type="message" />
    <jdoc:include type="component" />
</body>
</html>

Если в Вашем шаблоне отсутствует файл component.php, то используется стандартный файл templates/system/component.php.

Для Ваших собственных нужд Вы можете сделать разные шаблоны вывода и реализовывать в них свою специфичную логику. В url запроса тогда Вы будете указывать параметр tmpl=component2 и для вывода данных использоваться будет файл templates/ваш_шаблон/component2.php. Иногда такие запросы используют для получения данных по ajax, хотя отдавать готовый HTML по ajax - не самая лучшая практика.

Файл error.php

Этот файл отвечает за вывод страницы 404. Кроме ошибки 404, в Joomla 4 этот же файл отвечает за вывод сообщения об ошибке 403. Дополнительную информацию можно также подчерпнуть на новом портале документации Joomla.

Если в шаблоне нет файла error.php, то используется системный файл из templates/system/error.php. Вы можете скопировать в свой шаблон системный файл и перенести туда часть вёрстки из основного шаблона или оформить по-своему полностью.

Главное отличие Joomla 4 (и Joomla 5) от Joomla 3 это то, что на странице ошибки теперь можно отображать модули (всё-таки посмотрите ссылку на документацию чуть выше, там как раз про это тоже). Этого очень не хватало в предыдущих версиях Joomla и веб-мастера, выполняя задания СЕО-специалистов и интернет-маркетологов изголялись как могли. Вплоть до обработки 404 в htaccess (вроде ничего страшного), где сервер перенаправлял на сохранённую прямо из браузера HTML-страницу сайта (а вот это уже страшно...).

Теперь Вы можете спокойно повторить на странице 404 и шапку, и подвал сайта, и меню и почти всё, что угодно. Это очень круто.

Но, есть нюанс. Нюанс заключается в том, что страница 404 является объектом не Joomla\CMS\Document\HtmlDocument, а Joomla\CMS\Document\ErrorDocument. Это означает, что, например, некоторые плагины на этой странице работать не будут. И если Вы используете, например, плагин для замены шорт-кодов (для контактов, названия компании, логотипа, ссылок на соц.сети и т.д.), то на странице 404 будут отображаться именно шорт-коды, а не контакты. Будьте внимательны и осторожны ))

Обычно при создании своего шаблона страницу для отображения ошибок копируют из стандартного (Cassipeia для Joomla 4+) и изменяют уже её. в ней содержится типовое сообщение об ошибке в духе "Устаревшая закладка/избранное" , "Кэш поисковой системы ссылается на устаревшую страницу сайта" и т.д. На самом деле эти сообщения уже малополезны для пользователей и поэтому надписи заменяют на фотографию "Админ в ужасе". Но для админов полезным будет код ошибки, её подробное описание и callstack.

В стандартном шаблоне Cassiopeia участок кода, отвечающий за вывод этих данных вкылядит так:

<?php //оставлю это здесь, а то подсветка синтаксиса Хабра хромает :( ?>
<blockquote>
    <span class="badge bg-secondary"><?php echo $this->error->getCode(); ?></span> <?php echo htmlspecialchars($this->error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>
</blockquote>
<?php if ($this->debug) : ?>
    <div>
        <?php echo $this->renderBacktrace(); ?>
        <?php // Check if there are more Exceptions and render their data as well ?>
        <?php if ($this->error->getPrevious()) : ?>
            <?php $loop = true; ?>
            <?php // Reference $this->_error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?>
            <?php // Make the first assignment to setError() outside the loop so the loop does not skip Exceptions ?>
            <?php $this->setError($this->_error->getPrevious()); ?>
            <?php while ($loop === true) : ?>
                <p><strong><?php echo Text::_('JERROR_LAYOUT_PREVIOUS_ERROR'); ?></strong></p>
                <p><?php echo htmlspecialchars($this->_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?></p>
                <?php echo $this->renderBacktrace(); ?>
                <?php $loop = $this->setError($this->_error->getPrevious()); ?>
            <?php endwhile; ?>
            <?php // Reset the main error object to the base error ?>
            <?php $this->setError($this->error); ?>
        <?php endif; ?>
    </div>
<?php endif; ?>

Обратите внимание, что стак вызовов показывается только в случае, если в общих настройках сайта включён параметр "отладка системы" if ($this->debug).

Файл fatal.php

Не встречал, чтобы этот файл фигурировал в шаблонах, но он есть в системном шаблоне - templates/system/fatal.php.

Согласно примечанию, этот файл используется для отображения ошибок только в самых критических ситуациях. И даже в нём у нас есть возможность сделать свой дизайн. для этого нужно создать файл fatal-error.custom.php и поместить его не в папке со своим шаблоном, а в папке templates/system.

Файл offilne.php

Этот файл отображает заглушку "сайт выключен и находится на техническом обслуживании". Выключить сайт можно в Система - Общие настройки - "Сайт выключен".

Если файла offline.php нет в шаблоне, то будет использоваться системный файл из templates/system/offline.php. Тип документа здесь Joomla\CMS\Document\HtmlDocument, поэтому и модули и контент-плагины здесь работают. Как правило, эта страница отображает текст из общих настроек сайта и картинку на фон (или у заголовка) из настроек шаблона. Здесь же можно поместить какой-нибудь счётчик обратного отсчета а-ля Coming soon.

Стандартная страница offline обычно показывает форму входа на сайт. На моей практике, если такой страницей и пользуются, то довольно редко. Форму входа в таких случаях убирают.

Вариант оформления offline страницы шаблона Joomla 4+
Вариант оформления offline страницы шаблона Joomla 4+

Файл joomla.asset.json шаблона Joomla

Этот файл описывает необходимые для работы шаблона веб ассеты. Ещё раз адресую читателя к статье Как правильно подключать JavaScript и CSS в Joomla 4. В ней подробно раскрывается концепция Web Asset в Joomla 4 и Joomla 5, даны примеры кода и паттерны применения. От себя добавлю следующее:

  • в шаблоне мы описываем те веб-ассеты, которые нужны для данного конкретного шаблона.

  • веб-ассеты для расширений (модулей, плагинов, макетов модулей и плагинов, переопределений компонентов), которые используются на сайте, я предпочитаю оформлять отдельными плагинами. Об этом статья Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина. Такие ассеты легко обновлять (всякие скрипты каруселей, лайтбоксов, masonry и т.д.) и легко использовать от проекта к проекту. Установил плагин, включил и всё работает. Также перед созданием своего собственного веб-ассета стоит поискать, может кто-то уже оформил популярный скрипт карусели в веб-ассет и тогда можно использовать уже готовое решение.

  • мы помним, что в шаблоне можно переопределить системные веб-ассеты, а плагином можно переопределить любой веб-ассет, так как веб-ассет из плагина добавляется позже, чем системные и шаблона. Это вопрос логичного распределения ресурсов.

  • Также очень часто используется метод registerAndUseStyle() и registerAndUseSript(), которые по синтаксису похожи на метод HTMLHelper::script() (ex. JHtml). Эти методы позволяют регистрировать и использовать ассеты на лету, но нужно помнить о том, в какой момент жизненного цикла приложения добавляется ассет и не пытаться использовать его раньше его добавления.

На примере стандартного шаблона Joomla - Cassiopeia - можно увидеть, что в отдельные веб ассеты выделены CSS общего характера (template.css), которые используются всегда. А веб-ассеты частных случаев (страница редактирования материала с загруженным редактором, страница offline, дополнительные стили для страниц сайта, доступных авторизованным пользователям) - выделены в отдельные веб-ассеты, которые подключаются только при необходимости.

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

<?php

// Фрагмент кода в index.php шаблона

use Joomla\CMS\Factory;

defined('_JEXEC') or die;

$app = Factory::getApplication();
// Получаем код языка вида ru-RU, en-GB
$lang_tag 	= $app->getLanguage()->getTag();
// добавляем к нему префикс, для удобства чтения и именования файлов
$lang_tag = '-'.$lang_tag; 
// Подключаем web asset
$wa->useStyle('template.webtolk.font.lang'.$lang_tag);

Фрагмент joomla.asset.json

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "webtolk",
  "version": "1.0.0",
  "description": "This file contains details of the assets used by webtolk site template for Joomla 4.",
  "license": "GPL-2.0-or-later",
  "assets": [
    {
      "name": "template.webtolk.offline",
      "description": "The css file to be used when the site is offline and offline.php is being used.",
      "type": "style",
      "uri": "offline.css"
    },
    {
      "name": "template.webtolk.font.lang-ru-RU",
      "type": "style",
      "uri": "font-lang-ru-RU.css"
    },
    {
      "name": "template.webtolk.font.lang-en-GB",
      "type": "style",
      "uri": "font-lang-en-GB.css"
    },
    {
      "name": "template.webtolk.font",
      "type": "style",
      "uri": "font-lang.css"
    }
  ]
}

В css-файлах для css-переменной уже присваивалась разный font-family. Данный пример из файла font-lang-ru-RU.css:

@font-face {
	font-family: "RockStar-regular";
	src: url("../fonts/RS-regular.otf");
	font-weight: normal;
	font-style: normal;
	font-display: swap;
}
:root {
	--bs-font-sans-serif: "RockStar-regular";
}

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

Дочерние шаблоны (дочерние темы) Joomla 4

В Joomla не принят термин "темы", однако я поместил его в заголовок для разработчиков, которые не имеют опыта работы с Joomla. Поскольку дочерние шаблоны - тема для отдельной большой статьи, я помещу сюда ссылку на отдельную большую статью: Joomla Community Magazine - Sweet child o' mine... A deep dive into Joomla Child Templates (статья на английском языке, официальный журнал международного Joomla-сообщества).

Кратко:

  • дочерние шаблоны - продолжение развития механизма переопределений в Joomla 4

  • дочерние шаблоны появились в Joomla 4.1

  • в дочерние шаблоны помещаются те файлы родительского шаблона, которые нужно редактировать

  • остальные файлы используются из родительского шаблона

  • в XML-манифесте дочернего шаблона должен быть быть элемент <inheritable>1</inheritable> (писал об этом выше)

  • создать дочерний шаблон можно в Система - Шаблоны сайта - [конкретный шаблон] - кнопка "создать дочерний шаблон"

  • Подробнее можно прочесть (и посмотреть видео) в статье Новое в Joomla 4.1

Видео о дочерних шаблонах (английский язык):

Сценарий применения дочерних шаблонов Joomla

Если у Вас на сайте установлен шаблон, который периодически получает обновления (не важно Ваш собственный или студийный) - Вам нужно иметь возможность не потерять изменения, которые Вы делаете в файлах. Для этого Вы создаёте дочерний шаблон и помещаете в него файлы, которые хотите изменить. При обновлении родительского шаблона Вы не потеряете изменения и при этом получите исправления ошибок, новый функционал, который можно спокойно проанализировать и перенести в свой дочерний шаблон.

Это задача разделения общего и частного. Обычно разработчики для себя создают некий шаблон-заготовку, который затем поддерживают: обновляют версии Bootstrap, UIkit или других использованных ассетов. В частном случае конкретного сайта всегда есть изменения, которые нужны только для конкретного сайта. Такие изменения лучше всего поместить в дочерний шаблон.

Шаблоны с опциями (добавить настройки для своего шаблона Joomla)

В Joomla под капотом очень удобный конструктор форм. Есть много стандартных типов полей Joomla (официальная документация), а если их не хватает - всегда можно создать свой тип поля и использовать его. Кроме полей типа текст. число, список, textarea есть удобное поле типа subform (документация). Это поле, содержащее в себе повторяемую форму из нескольких простых полей. Таким образом можно в админке быстро "накликать" список повторяемых сущностей с разными параметрами.

Какие опции следует выносить в настройки шаблона?

Если кратко, то те, которые имеют отношение ко всему шаблону. Это своего рода глобальные настройки внешнего вида Вашего сайта. Это могут быть настройки логотипа, шрифтов, цветовых схем, фавиконок, коды верификации поисковиков, соц.сетей, сервисов, возможность вставки кастомного кода из админки (после открывающего <body>, перед закрывающим </body> и т.д.).

Например, на всём сайте нужен логотип.

  • Его могут захардкодить прямо в index.php шаблона

  • Его могут вставить в модуль типа HTML-код

  • Можно сделать параметр шаблона и указывать картинку именно в нём.

Создание интерфейса настроек шаблона Joomla в XML-манифесте

В XML-манифесте шаблона есть секция <config>. В ней описываются поля для создания интерфейса в админке Joomla.

<?xml version="1.0" encoding="utf-8"?>
<extension type="template" client="site" method="upgrade">
<!-- тут мы пропускаем всё то, что писали раньше и переходим
сразу к нужной секции config -->
    <config>
        <fields name="params">
          <fieldset name="basic">
                <field
                        name="logo_file"
                        type="media"
                        types="images"
                        label="TPL_WEBTOLK_BRAND_IMG"
                        showon="brand:img_file"
                />
            </fieldset>
		</fields>
	</config>
</extension>

Почему такая структура в XML? Зачем <config>, затем <fields>, ещё и <fieldset> потом?

Элемент <config> - говорит, что это настройки шаблона.

Элемент <fields> влияет на рендер HTML-формы в админке. Их может быть несколько вложенных друг в друга. В примере мы видим <fields name="params">. Это означает, что все имена полей в настройках шаблона получат префикс jform[params]. А в базе данных есть колонка с именем params, куда сохраняется json с параметрами шаблона. Чудесным образом всё состыкуется :) Так, если имя поля для логотипа - logo_file, то в HTML-коде админки имя поля будет jform[params][logo_file], а в index.php Вашего шаблона будете получать данные этого поля следующим образом:

<?php
// Это файл index.php Вашего шаблона
// Здесь будет находиться путь к картинке вида images/my_picture.webp
echo $this->params->get('logo_file');

Если Вам нужна более сложная вложенность имён в структуре данных - внедряем в XML-формы ещё один <fields name="level2">. И все поля внутри <fields name="level2"> получат ещё один уровень вложенности по структуре данных.

<fields name="canvases">
  <fields name="front">
      <fieldset name="canvas_front">
          <field type="media"
                 name="canvas_image_front"
                 label="COM_WTPRODUCTBUILDER_PRODUCT_IMAGE_FRONT"
                 types="images"
                 preview="true"
                 parentclass="col-12 col-md-6 col-lg-4"/>
      </fieldset>
  </fields>
</fields>

Это пример не из шаблона, но суть он передаёт. Данные поля canvas_image_front будут доступны в виде:

<?php 
$canvases = $this->params->get('canvases');

А $canvases уже будет многомерным массивом с элементом front, в котором находится элемент canvas_image_front

Элемент <fieldset> формирует табы в панели администратора. Атрибут name должен быть уникальным. Атрибут label - это название таба. Атрибут description - отображаемое описание для вкладки.

Как Вы успели заметить в примерах XML полей есть любопытные атрибуты - showon и parentClass (class, кстати, тоже можно использовать).

Атрибут showon позволяет скрывать или показывать поле в админке в зависимости от значения другого поля (официальная документация). Удобная вещь, когда параметров много и не хочется работать со страницей админки современной CMS как с многосекционным однофайловым конфигом FIDONet-софта.

Атрибут parentClass позволяет задать css-класс родительскому элементу поля - это div-обёртка, в которой находится само поле и его label - название. Задать можно любой класс из шаблона админки Joomla (а это Bootstrap 5 + кастомные стили).

Например, parentClass="stack" поместит название поле над полем. Так экономится место на экране. Админка Joomla использует css grid на 6 колонок. Можно удобно группировать поля рядом: parentclass="stack span-3" - изменит размер поля, а parentclass="stack span-3-inline" позволит поместить поле рядом с другим полем. Подробнее смотрите css админки шаблона Joomla - media/templates/administrator/atum/css/template.css.

Большая часть настроек шаблона - это радиокнопки (выключатели), списки (списки файлов, шрифтов, CSS, простые текстовые поля или textarea.

Пример, как добавить коды верификации поисковиков, соц.сетей и сервисов в свой шаблон.

В XML-манифесте нужно добавить поле типа subform

<!-- В label должна быть языковая константа, но пишу сразу по-русски, 
чтоб было понятнее -->
<field name="verification_codes"
       label="Коды верификации"
       type="subform"
       layout="joomla.form.field.subform.repeatable-table"
       formsource="templates/webtolk/subform/search_consoles_meta.xml"
       multiple="true"
       buttons="add,remove"
/>

Поле этого типа загружает из файла повторяемую форму с собственно опциями. В index.php в $this->params->get('verification_codes') потом получим массив со значениями для рендера.

<?xml version="1.0" encoding="UTF-8"?>
<form>
	<fieldset name="search_engine_types">
		<field name="search_engine" type="list" label="Сервис для верификации">
			<option value="yandex-verification">Yandex</option>
			<option value="google-site-verification">Google</option>
			<option value="mailru-domain">Mail.ru</option>
			<option value="msvalidate.01">Bing</option>
			<option value="baidu-site-verification">Baidu</option>
			<option value="cmsmagazine">CMS Magazine</option>
			<option value="p:domain_verify">Pinterest</option>
		</field>
		<field name="search_engine_verification_code" type="text" label="Код верификации"/>
	</fieldset>
</form>

В настройках шаблона у нас появится сабформа для настроек с выпадающим списком поисковиков и сервисов и текстовым полем для кода верификации.

Далее, в index.php шаблона обрабатываем полученные из настроек данные и добавляем в <head> страницы.

<?php
// это фрагмент файла index.php Вашего шаблона
/**
 * Search engines webmaster's consoles verifictation codes
 */

// Проверяем, а есть ли вообще эти коды?
if ($this->params->get('verification_codes') && !empty($this->params->get('verification_codes')))
{
    // Проходим их все
	foreach ($this->params->get('verification_codes') as $search_engine)
	{
        // Проверяем, а не не забыли ли указать собственно код верификации?
		if (!empty($search_engine->search_engine_verification_code))
		{
          // Вроде всё на месте. Добавляем мета-тег
			$this->addCustomTag('<meta name="'.$search_engine->search_engine.'" content="'.$search_engine->search_engine_verification_code.'"/>');
		}
	}
}

У каждого Joomla-специалиста обычно кристаллизуется свой набор опций для шаблона, которые постепенно добавляются в разных проектах. При проектировании шаблона - повторю эту мысль - важно разумно распределить ресурсы между общим и частным. Надеюсь, данная статья поможет в этом.

Шаблоны и стили шаблона Joomla

Нередко для разных страниц сайта бывают необходимы вариации внешнего вида. В Joomla с давних пор существует функционал стилей шаблона.

Стили шаблона в Joomla

В разрабатываемом нами шаблоне теперь есть свои настройки: цветовые схемы, изображение логотипа, фавиконки, фиксированный или "резиновый" контейнер для сайта и т.д. Разные комбинации этих настроек можно сохранять в качестве своеобразных пресетов - "стилей" - и применять эти стили к определенным пунктам меню. Пунктам меню, в свою очередь, можно назначить отображение определенным группам пользователей, языкам сайта и т.д.

Этот функционал появился ещё линейке 2.5, если не раньше. Небольшое видео демонстрирует этот функционал на примере Joomla 3.9. (видео демо взято из более ранней статьи).

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

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

Сущности шаблонов и стилей в Joomla позволяют хоть для каждого пункта меню назначить полностью уникальный дизайн. Сделать это можно как абсолютно разными шаблонами (5 пунктов меню = 5 разных шаблонов ????), так и разными стилями одного шаблона или дочерними шаблонами. Хотя на практике обычно это не требуется.

Переопределения в шаблоне Joomla

Архитектура Joomla в целом соответствует паттерну MVC - Model-View-Controller. Таким образом логика и обработка данных отделяется от вывода данных, что облегчает работу по изменению отдельных частей движка. Но, в Joomla сделана специфическая реализация этого паттерна, что делает её чем-то особенным. В частности внедрён дополнительный слой - Templates - который позволяет делать переопределения. Подробнее можно прочесть в книге греческого разработчика Николаса Дионисопулоса Joomla Extensions Development.

Переопределения помещаются в папке html шаблона. При рендере страницы Joomla смотрит в папку html текущего шаблона и, если видит переопределение, то использует его. Подробнее о переопределениях типов расширений Joomla будет сказано в посвящённых им разделах.

Сущность модуля. Дизайн для модулей на Joomla-сайте

У каждого модуля есть макеты вывода, в которых содержится его HTML-вёрстка. Лежат эти макеты в modules/<имя_модуля>/tmpl. Макеты модулей панели администратора находятся в administrator/modules/<имя_модуля>/tmpl. Обычно макет вывода по-умолчанию называется default.php.

В настройках каждого модуля есть опция "макет", где можно выбрать другой макет для модуля (если разработчик расширения предоставляет альтернативные макеты). Эта опция находится в табе "дополнительные параметры".

Очень часто (практически всегда) возникает необходимость изменить вёрстку стандартного или стороннего модуля и самый верный путь - переопределить макет вывода модуля. Сделать это можно двумя способами:

  • переопределить макет в папке tmpl модуля

  • переопределить макет в папке html шаблона

Оба способа абсолютно равнозначны и являются правильными в равной степени. Разница только в удобстве переиспользования кода между проектами. Я обычно те макеты модулей, которые используются только в одном проекте помещаю в папку tmpl модуля. Те, которые можно использовать на разных проектах в рамках данного шаблона - в папку html шаблона. Постепенно в папке html будут копиться созданные переопределения и функциональность шаблона будет возрастать.

Макет модуля Joomla. Переопределения

  1. Идём в папку с модулем modules/<имя_модуля>/tmpl

    1. Копируем и переименовываем файл default.php в той же папке. При переименовании лучше не использовать символ подчеркивания _. Он зарезервирован и используется при подключении subLayouts, если они используются.

    2. ЛИБО копируем файл макета в templates/<ваш_шаблон>/html/<имя_модуля>. В этом случае файл можно НЕ переименовывать и тогда он будет заменять собой default.php из папки tmpl модуля. Либо всё-таки переименовать, и тогда он будет виден отдельным макетом.

  2. После этого созданный макет появится в выпадающем списке поля "макет" в настройках модуля. Выбираем его и сохраняем модуль.

  3. Творим в своём файле макета всё, что душе угодно.

Пример макета модуля, расположенного в папке html шаблона
Пример макета модуля, расположенного в папке html шаблона

При создании макета модуля Joomla порой нужны некоторые классы (например объект приложения, Input и т.д.). В Joomla 4 они уже присутствуют в макете модуля. Подробнее в статье Создание модулей с учётом новой структуры Joomla 4.

  • $module - объект модуля. Оттуда Вы можете взять id модуля ($module->id), заголовок модуля, его позицию и т.д.

  • $app - объект приложения. Это значит, что Вам не нужно самостоятельно вызывать Joomla\CMS\Factory::getApplication(). Он уже есть для Вашего удобства.

  • $input - также в макете модуля теперь сразу доступен объект Input (через него мы получаем GET, POST параметры, SERVER и т.д.), который раньше приходилось вызывать самостоятельно.

  • $params - параметры модуля. Получаем их как раньше: $params->get('param_name' , 'default_value_if_value_is_empty'). Эти параметры мы собираем с помощью различных типов полей Joomla в xml-манифесте модуля.

  • $template - параметры настроек стиля текущего шаблона.

Переопределение макета модуля меню в Joomla

В плане работы с этим модулем в Joomla больших изменений не было, поэтому опытным разработчикам можно пропустить этот раздел. Единственное заметная разница между Joomla 3 и Joomla 4 - это то, что параметры пункта меню стали приватными и получить к ним доступ просто по $item->params уже нельзя. В Joomla 4 и Joomla 5 это вызовет ошибку, поэтому нужно получить параметры каждого пункта меню в отдельную переменную:

<?php 
// Фрагмент переопределённого файла modules/mod_menu/tmpl/default.php
?>
<ul<?php echo $id; ?> class="nav d-flex flex-lg-row <?php echo $class_sfx; ?>">
<?php foreach ($list as $i => &$item)
{
    // Получение параметров пункта меню.
    // Далее по коду все парамтеры берём из $itemParams
    $itemParams = $item->getParams();
	$class      = 'nav-item item-' . $item->id;

	if ($item->id == $default_id)
	{
		$class .= ' default';
	}

	if ($item->id == $active_id || ($item->type === 'alias' && $itemParams->get('aliasoptions') == $active_id))
	{
		$class .= ' current';
	}
  
...

Один из самых часто переопределяемых модулей - меню. Макетов для модуля меню нередко нужно несколько. Идём в папку modules/mod_menu/tmpl, копируем и переименовываем 5 файлов:

  • default.php - основной файл макета, где формируется сам список <ul> <li>.

  • default_component.php - вывод ссылки на какой-либо компонент.

  • default_heading.php - вывод пункта меню типа "заголовок"

  • default_separator.php - вывод пункта меню типа "разделитель"

  • default_url.php - вывод пункта меню типа "внешняя ссылка"

Меняем default в названиях файлов на что-то своё. Если переопределение модуля создается под проект, то я пишу имя домена / проекта: myproject_default.php, myproject_component.php и т.д.

Изменение вызова подмакетов модуля меню в Joomla 4 и Joomla 5
Изменение вызова подмакетов модуля меню в Joomla 4 и Joomla 5

Не забываем изменить и вызов подмакетов для рендера самой ссылки.

Далее в файлах внедряем свою вёрстку. CSS-классы, которые назначаются всем пунктам меню данного макета хардкодим прямо в макете. В конце конкатенируем стили из настроек пункта меню.

При использовании CSS-классов помним, что в CSS-свойствах тоже работают переопределения (но уже не Joomla ????). Если 2 разных CSS-класса, назначенных селектору, имеют одинаковое свойство (color, background-color и т.д.), то приоритет получит то, которое в CSS-файле указано позже, так как оно перезапишет ранее указанное значение. При этом порядок указания классов в самом селекторе не важен.

Например, класс .btn-danger указан в 3358-й строке, а класс .btn-success - в 3253-й Вашего template.css. Нередко при работе с javascript, чтобы не писать лишний код, просто добавляешь CSS-класс к селектору в надежде, что он перезапишет указанные до него CSS-правила.

<!-- Просто красная кнопка -->
<button type="button" class="btn btn-danger">Нажать!</button>

<!-- Должна быть просто зелёная кнопка, но она останется красной, 
так как .btn-danger в CSS правилах описан позже, чем btn-success -->
<button type="button" class="btn btn-danger btn-success">Нажал</button>

<!-- Порядок указания классов роли не играет -->
<button type="button" class="btn btn-success btn-danger">Нажал</button>

<!-- Поэтому в данном конкретном случае нужно удалять класс .btn-danger
и потом уже добавлять .btn-success, либо писать свои кастомные классы и делать toggle -->
<button type="button" class="btn btn-success">Нажал</button>

Примерно так строится работа со всеми модулями в Joomla.

Лайфхак, который можно использовать, когда необходимо выполнить произвольный PHP-код в Joomla - это сделать свой макет для модуля HTML-код (mod_custom). Удалить в нём всё, что предлагает Joomla и поместить свой код. В панели администратора же создать модуль, выбрать для него свой макет и... оно работает, но ещё дополнено функциональностью Joomla (привязка к пунктам меню, уровням доступа и языкам).

Суффикс класса модуля (CSS-класс модуля)

Эта функция в Joomla существует ещё с версий 1.5 (если не раньше). Каждому модулю можно назначить суффикс для CSS-класса, который потом описать в CSS шаблона. Предполагалось, что все модули имеют CSS-класс moduletable, а суффикс класса конкатенирует к нему то, что указано в настройках модуля. И тогда получается уникальный CSS-класс для каждого модуля: .moduletable_red, .moduletable_green и т.д.

Сейчас этот параметр называется просто "CSS-класс модуля", однако его поведение по-прежнему во многих расширениях как у суффикса. Речь идёт о пробеле между классом moduletable и тем, что Вы введёте в этом поле. Поскольку в PHP-коде moduletable часто конкатенируется сразу, то 2 класса могут слиться в один несуществующий. Поэтому у опытных Joomla-разработчиков есть привычка начинать значение этого поля с пробела.

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

<?php 
// @var $params \Joomla\Registry\Registry - объект Registry с настройками модуля. 
// Значения получаем через метод get()
echo $params->get('moduleclass_sfx', 'тут значение по умолчанию, если в настройках модуля пусто');

Типичный пример кода макета модуля с использованием CSS-класса модуля:

<?php 
// Файл из modules/<имя модуля>/tmpl/your-template.php
// Вообще, это должно делаться в диспетчере модуля Dispatcher.php,
// Но можно присвоить и здесь.
$moduleclass_sfx = $params->get('');
?>

<ul class="nav mod_wt_quick_links my-2 py-1 flex-nowrap overflow-auto flex-lg-wrap <?php echo $moduleclass_sfx; ?> ">
<?php foreach ($list as $item) : ?>
    <li>
        <?php if($item->use_link == 1 && !empty($item->url)):?>
            <a href="<?php echo $item->url; ?>" class="btn btn-sm text-nowrap">
        <?php endif;?>
            <?php echo $item->link_text; ?>
        <?php if($item->use_link == 1 && !empty($item->url)):?>
            </a>
        <?php endif;?>
    </li>
    <?php endforeach; ?>
</ul>

Chrome модуля (стиль модуля) Joomla

Chromes модулей - это дополнительные обёртки для HTML-вывода модулей. Они позволяют быстро переключать как визуальную, так и семантическую составляющую вывода модуля.

В панели администратора они находятся в настройках каждого модуля, таб "Дополнительные параметры", в самом низу.

Chrome модуля (стиль модуля) Joomla
Chrome модуля (стиль модуля) Joomla

В Joomla 4 по умолчанию предустановлено 4 стиля модуля

Стиль модуля HTML5 в Joomla 4

Пожалуй, самый полезный полезный стиль вывода модуля. Он позволяет:

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

    • выводить заголовок модуля (например, теги <section>, <article> по спецификации HTML5 обязаны иметь вложенный заголовок). Заголовок модуля - это то, что видно снаружи сайта, а примечание в духе "контакты и соцсети в подвале" мы пишем в поле комментария к модулю

  • И вообще использовать все настройки в табе "Дополнительные параметры".

Нюансы использования: CSS-стиль модуля (суффикс класса модуля) присваивается как в обёртке (chrome), так и в макете. Поэтому если используете этот параметр, то в файле макета CSS-класс модуля лучше убрать, чтобы не было неожиданных артефактов в вёрстке.

Стиль модуля none

Ещё один очень нужный стиль вывода модуля. Он просто выводит то, что рендерит модуль в файле макета. Это порой просто необходимо. Но, заголовки, их стили, CSS-класса модуля тогда забираем напрямую из параметров модуля и обрабатываем сами в макете.

Стиль модуля outline в Joomla 4

Используется когда Вы захотели посмотреть все доступные позиции для модулей с помощью GET-параметра ?tp=1. На боевых сайтах этот стиль не применяется.

Стиль модуля table в Joomla 4

Этот стиль выводит заголовок и содержимое модуля в виде таблицы: заголовок в одной ячейке, содержимое - в другой. За всю свою практику я никогда этим стилем не пользовался и мне кажется, что он остался в Joomla со времен табличной вёрстки.

Применение стилей модуля в Joomla

В Joomla 2.5 и 3.x эти стили находились в файле modules.php в templates/system/html. В Joomla 4 стили разделили по файлам и перенесли в layouts/chromes.

В Joomla 2.5-3.x если разработчик хотел создать свои собственные стили-обёртки, то в папке templates/<имя_шаблона>/html создавался файл modules.php, в котором стили создавались в виде методов класса.

Сейчас для создания своего стиля достаточно положить файл в папку с шаблоном templates/<имя_шаблона>/html/layouts/chromes. Оформлять файл в виде класса или метода класса не нужно. Образец можно посмотреть в стандартном шаблоне Cassiopeia.

Ниже пример кода стиля модуля из стандартного шаблона Cassiopeia - стиль card.

<?php

/**
 * @package     Joomla.Site
 * @subpackage  Templates.cassiopeia
 *
 * @copyright   (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Utilities\ArrayHelper;

$module  = $displayData['module'];
$params  = $displayData['params'];
$attribs = $displayData['attribs'];

if ($module->content === null || $module->content === '') {
    return;
}

$moduleTag              = $params->get('module_tag', 'div');
$moduleAttribs          = [];
$moduleAttribs['class'] = $module->position . ' card ' . htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_QUOTES, 'UTF-8');
$headerTag              = htmlspecialchars($params->get('header_tag', 'h3'), ENT_QUOTES, 'UTF-8');
$headerClass            = htmlspecialchars($params->get('header_class', ''), ENT_QUOTES, 'UTF-8');
$headerAttribs          = [];
$headerAttribs['class'] = $headerClass;

// Only output a header class if it is not card-title
if ($headerClass !== 'card-title') :
    $headerAttribs['class'] = 'card-header ' . $headerClass;
endif;

// Only add aria if the moduleTag is not a div
if ($moduleTag !== 'div') {
    if ($module->showtitle) :
        $moduleAttribs['aria-labelledby'] = 'mod-' . $module->id;
        $headerAttribs['id']              = 'mod-' . $module->id;
    else :
        $moduleAttribs['aria-label'] = $module->title;
    endif;
}

$header = '<' . $headerTag . ' ' . ArrayHelper::toString($headerAttribs) . '>' . $module->title . '</' . $headerTag . '>';
?>
<<?php echo $moduleTag; ?> <?php echo ArrayHelper::toString($moduleAttribs); ?>>
    <?php if ($module->showtitle && $headerClass !== 'card-title') : ?>
        <?php echo $header; ?>
    <?php endif; ?>
    <div class="card-body">
        <?php if ($module->showtitle && $headerClass === 'card-title') : ?>
            <?php echo $header; ?>
        <?php endif; ?>
        <?php echo $module->content; ?>
    </div>
</<?php echo $moduleTag; ?>>

В нём видно, что содержимое модуля echo $module->content; оборачивается в Bootstrap 5 компонент card. А также видно, что разработчиками заложено любопытное поведение стиля в плане рендера заголовка. Если заголовку в настройках модуля присвоен CSS-класс card-title, то заголовок выводится внутри <div class="card-body">, если же нет, то заголовок выводится внутри тега (это может быть <section>, <div>, <article>, <address> и т.д.) с классом card, но за пределами div.card-body. При этом заголовку автоматически добавляется CSS-класс card-header.

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

Главная особенность стилей модулей это то, что мы можем указывать их при указании позиций в index.php шаблона.

<jdoc:include type="modules" name="menu" style="html5"/>

Таким образом, если в настройках модуля не указан стиль вывода модуля, всем модулям в позиции menu будет назначен стиль html5.


Как это и, главное, зачем это применять? Кроме удобства работы с семантической вёрсткой есть ещё один важный момент: это забота о тех, кто с сайтом будет работать после разработчика. Ведь во-многом именно из их пользовательского опыта складывается отношение как к разработчику, так и к Joomla в целом.

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


Способы вставки модулей Joomla

Выводимую модулями информацию удобно использовать в самых разных местах сайта. Поэтому в Joomla существует функционал, позволяющий вставлять модули почти куда угодно. Мы помним о том, что модуль - это расширение, предназначенное для вывода информации на нескольких или всех страницах сайта сразу. Но, если у Вас небольшой сайт, иногда быстрее и удобнее оказывается создать модуль и затем вставить его в середину материала или в описание товара.

Способ 1. Контент плагин {loadposition } и {loadmoduleid }

Плагины группы контент чаще всего обрабатывают страницу на предмет наличия шорт-кодов и заменяют их на нужное содержимое. Обычно плагинами этой группы обрабатывается вывод компонента (текст материала, описание товара и т.д.) и некоторые модули (модуль типа HTML-код, если включено в настройках, и т.д.)

Мы просто указываем {loadposition menu} и в материале будут выведены модули из позиции menu, если настройки модулей позволяют отображаться на данной странице.

Шорт-код {loadmoduleid <число>} позволяет отобразить один модуль по его id.

Используя эту возможность, можно, например, собрать своё мега-меню на расширениях ядра (модуле типа HTML-код и меню) и не зависеть от сторонних расширений.

Способ 2. Использование уникальных имён модульных позиций только для одной страницы

Этот способ является продолжением первого способа.

Задача: нам нужно вывести экземпляр конкретного модуля только на одной конкретной странице на всём сайте, где-нибудь в середине текста. И на этот материал или товар нет пункта меню, чтобы мы могли привязать модуль к нему.

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

Мы вводим это название в поле выбора позиции и нажимаем клавишу enter. Затем в нужном нам особенном месте мы вставляем знакомый нам шорт-код {loadposition unique-position-name}. Поскольку эта позиция будет встречаться только в нужном нам материале или товаре, то и модуль будет выводиться лишь там и нигде больше.

3 способ. Вставляем модули в коде с помощью ModuleHelper

Раньше класс ModuleHelper назывался JModuleHelper. В плане работы с ним он не сильно изменился. Примеры из старых статей при небольшой корректуре будут работать и на Joomla 4 и Joomla 5.

<?php

use Joomla\CMS\Helper\ModuleHelper;
defined('_JEXEC') or die();

/**
 * Модуль с id 136
 */

// Получаем экземлпяр модуля по Id.
$module = ModuleHelper::getModuleById('136');

// Если модуль выводит данные из параметров модуля, на этом этапе 
// можно их модифицировать и передать в рендер уже изменённые данные.
// С точки зрения архитектуры такие операции не должны выполняться 
// в шаблоне. Но иногда по-другому делать нецелесообразно или слишком трудоёмко.
// Выводим нужный модуль
echo ModuleHelper::renderModule($module);

// Либо получаем все модули в позиции
$modules = ModuleHelper::getModules('position_name');
// Циклом обрабатываем все полученные модули
foreach ($modules as $module) {
   // Можем внедрить дополнительные проверки
   if ($module->module != 'mod_login'){
     // Делаем рендер модуля
        echo ModuleHelper::renderModule($module, ['id' => 'section-box']);
    }
}

Методы класса ModuleHelper можно посмотреть в файле libraries/src/Helper/ModuleHelper.php, там же видны входные параметры методов и их типы.

Сущность плагина Joomla в аспекте вёрстки шаблона

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

Пример жизненного цикла приложения Joomla 3 крупным планом. На схеме указана малая часть системных событий.
Пример жизненного цикла приложения Joomla 3 крупным планом. На схеме указана малая часть системных событий.

На приведённой схеме (правда, для Joomla 3), взятой из статьи Общая информация о принципе действия Joomla проекта JPath.ru (благодаря этому проекту мы имеем Joomla на русском языке) показаны лишь некоторые системные события. Однако, их гораздо больше. Также у каждого компонента есть свои триггеры (события) для плагинов, специфичные только для этого компонента.

Некоторые плагины могут добавлять к материалам в блоге кнопки "поделиться в соц.сетях", другие - разметку OpenGraph, микроразметку Schema.org (если Вы не внедряете её прямо в вёрстку), кнопки лайков вместо рейтинга статьи, время прочтения статьи и т.д. Или же с помощью плагинов можно вставлять данные из других компонентов: например в блог вставить ссылку на товар, но не текстом, а шорт-кодом. И тогда плагин вставит не только ссылку на товар, но и покажет цену, картинку товара, кнопку "купить" и т.д.

Как правило это плагины группы content, так как они обрабатывают основное содержимое компонента на наличие шорт-кодов. Но контент-плагины могут работать и по-другому: многие компоненты в объектах своих сущностей (материалы, контакты, товары, категории и т.д.) предусматривают "места" для вывода работы плагинов.

Ниже пример из макета вывода материала Joomla (com_content). Среди событий для контент-плагинов есть событие onContentAfterTitle. На этом событии во View компонента происходит вызов всех плагинов этой группы и присваивание результата элементу объекта сущности:

<?php 
// фрагмент файла components/com_content/src/View/Article/HtmlView.php

$item->event                    = new \stdClass();
$results                        = Factory::getApplication()->triggerEvent('onContentAfterTitle', ['com_content.article', &$item, &$item->params, $offset]);
$item->event->afterDisplayTitle = trim(implode("\n", $results));

Далее собственно вывод в шаблоне происходит следующим образом.

Фрагмент макета вывода материала Joomla 4.
Фрагмент макета вывода материала Joomla 4.

Выше по тексту был скриншот страницы просмотра статьи Хабра. Блок информации об авторе в Joomla скорее всего выводился бы плагином и добавлялся бы на событие onContentAfterDisplay. В шаблоне был бы вывод echo $item->event->afterDisplayContent. Некоторые студии "вшивают" этот функционал прямо в шаблоны.

Рекламный блок и блок комментариев, скорее всего, выводились бы тоже плагинами на то же событие. Почему это скорее всего не модули? Потому что ещё ниже мы видим статус публикации, который в рамках Joomla чаще всего является признаком вывода компонента. Какого-то отдельного расширения для показа статуса, например, материала (опубликован / не опубликован) нет. А значит, вывод компонента идёт ровно до этого места на странице.

Фрагмент страница просмотра статьи на Хабре
Фрагмент страница просмотра статьи на Хабре

Или, допустим, Вы хотите добавить дополнительные кнопки или любую другую информацию рядом с кнопкой "купить" в компоненте интернет-магазина JoomShopping. В таком случае грамотнее всего сделать простой плагин на событие onBeforeDisplayProduct или onBeforeDisplayProductView. В файле components/com_jshopping/Controller/ProductController.php в методе display() происходит вызов обоих этих триггеров в разное время. Соответственно, в плагине Вам будут доступны разные параметры и данные для работы плагина.

Позиции товара в шаблоне JoomShopping для добавления данных с помощью плагина
Позиции товара в шаблоне JoomShopping для добавления данных с помощью плагина

В шаблоне JoomShopping же Вы будете выводить эти позиции как echo $this->_tmp_product_html_buttons. Некоторые позиции находятся в $this->product. Как правило разработчики уже ориентируются на месте в этих позициях.

Макеты вывода плагинов Joomla. Переопределение

К сожалению, далеко не все разработчики делают подключаемые макеты в плагинах. Но, если они есть, то они находятся в plugins/<plugin_group>/<plugin_name>/tmpl. Соответственно, в настройках плагина должен быть выпадающий список макетов, по аналогии с модулем, если предполагается возможность выбора макета вручную. Либо внести изменения можно с помощью переопределения макета вывода плагина.

В макеты плагинов приходят только те данные, которые в них передают разработчики. Так, как в модулях передаются дополнительно $app, объект Input - в плагинах такого нет. Если эти данные необходимы - получаем их сами из DI-контейнера или объекта приложения.

Макеты плагинов можно точно также переопределять в папке html шаблона. Для этого создайте в папке templates/<template_name>/html папку вида "plg_<plugin_group>_<plugin_name>" - например, plg_content_vote (штатный плагин рейтинга материалов Joomla). И скопируйте в неё файлы из папки tmpl плагина. Например, из plugins/content/vote/tmpl в templates/<template_name>/html/plg_content_vote . Если имена файлов останутся те же, то Joomla заменит стандартный HTML-вывод Вашим.

Также плагины могут обрабатывать готовый HTML-код перед отдачей его в браузер на системное событие onAfterRender(). Тут могут работать либо с помощью регулярных выражений, либо с помощью DOMDocument. Это своеобразный скальпель в руках хирурга и изначально планировать его использовать при создании шаблона не нужно. Но знать, что оно существует - полезно.

Сущность компонента Joomla в ракурсе создания шаблона сайта

Компонент это всегда самая важная часть страницы сайта на Joomla, ради этой информации посетитель приходит на сайт. Любой пункт меню (за небольшим исключением) - это всегда ссылка на какой-либо компонент.

Макеты вывода компонента Joomla (шаблоны компонента) - канонический и не канонический способ реализации

Для Joomla существует огромное количество расширений. За Joomla закрепилась слава CMS не самой простой для понимания и в чём-то это больше плюс, чем минус, потому что любой человек должен понимать ЧТО он делает, КАК и ЗАЧЕМ. Для работы с Joomla обычно требуется более высокий порог вхождения, чем для многих других способов создания сайтов.

В Joomla существует канонический способ размещения файлов шаблона вывода компонента и не канонический. К не каноническим относится любой другой, отличающийся от канонического.

Канонический способ

Канонический способ можно увидеть в компонентах ядра Joomla: com_content, com_contact, com_users и т.д.

Как Вы успели заметить - все файлы макетов вывода находятся в папках tmpl. Мы переходим в components/<component_name>/tmpl и увидим папки с названиями, повторяющими View компонента - категориИ, категориЯ, материал и т.д.

В каждой папке находятся файлы макетов вывода. Как правило это комплект из xml-файла и php-файла с одинаковым названием. Именно в php файлах находится вёрстка того или иного макета вывода компонента. Тема переопределений макетов Joomla подробно разобрана в статье Рецепт приготовления современного сайта-каталога на пользовательских полях Joomla.

Чтобы сделать переопределение макета компонента нужно в папке templates/<template_name>/html создать папку с названием компонента. Например, templates/<template_name>/html/com_content. В неё Вы копируете папки с макетами компонента. Из compontents/com_content/tmpl в templates/<template_name>/html/com_content as. Предположим, если нужно сделать переопределение только макета блога, то копируем только compontents/com_content/tmpl/category/blog.php (и подмакеты blog_item и т.д.) и compontents/com_content/tmpl/category/blog.xml в папку templates/<template_name>/html/com_content/category. И далее в php файлах меняем вёрстку по своему усмотрению.

Кроме компонентов ядра используют канонический способ работы с макетами вывода (из тех, что мне довелось видеть): интернет-магазин Phoca Cart, интернет-магазин Radical Mart, компонент комментариев для материалов Joomla 4 Akeeba Engage, DJ-Catalog2 и другие.

Где находились макеты вывода компонента в Joomla 3?

В Joomla 3 макеты вывода компонента находили в папке components/<component_name>/views/<view_name>/tmpl. Например, для компонента материалов вид списка категорий находился в components/com_content/views/categories/tmpl. Некоторые компоненты ещё не полностью адаптированы для Joomla 4 / Joomla 5. Разработчики только "завели" их, но новую архитектуру не внедрили. Для компонентов с большой историей и большой аудиторией это требует много ресурсов. Порой обновление компонентов занимает от полугода до года. Поэтому старая архитектура Joomla 3 ещё может встречаться.

Неканонический подход

Здесь может быть просто море самых различных вариантов. Начиная от того, что может просто отличаться название папки с расположением макетов вывода компонента и кончая тем, что в компоненте собственная система шаблонирования, которая может поддерживать (а может и нет) переопределения макетов Joomla.

Приведу примеры.

Макеты вывода (шаблон) Virteumart

Популярный компонент интернет-магазина Virtuemart, имеет огромную историю и ведет своё начало ещё с до-Joomla эпохи. Соответственно, в нём довольно много legacy-кода, который сохраняется для того, чтобы не нарушать обратную совместимость. В нём макеты вывода хранятся в components/com_virtuemart/sublayouts. Однако, Virtuemart поддерживает переопределения макетов Joomla и полностью свой макет Вы можете создать с помощью переопределений. Хорошая серия статей по созданию шаблонов для Virtuemart на сайте Виталия Wedal.

Макеты вывода (шаблон) JoomShopping

Ещё один популярный компонент интернет-магазина - JoomShopping. Он также существует довольно давно и в нём используется не канонический подход к работе с шаблонами. Папка tmpl в нём присутствует, но в ней самих макетов нет. Они находятся в components/com_jshopping/templates/default.

JoomShopping поддерживает переопределения макетов Joomla, однако можно просто скопировать папку default, переименовать её и поместить рядом. Тогда в настройках компонента (Компоненты - JoomShopping - Настройка - Основное - Шаблон) выпадающий список дополнится Вашим шаблоном и Вы сможете использовать его. В папке components/com_jshopping/css скопируйте и переименуйте default.css.

Путь к настройке выбора шаблона JoomShopping 5
Путь к настройке выбора шаблона JoomShopping 5

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

Разумным советом здесь будет внимательно относится к вёрстке шаблона в плане изменения / удаления стандартных CSS-классов. В некоторых случаях javascript-логика привязана именно к классам, а не к id или data-атрибутам. Ещё одним неудобным нюансом является то, что в некоторых случаях JoomShopping отдаёт по ajax уже готовый HTML, а не данные в чистом виде и приходится изощряться для того, чтоб их изменить.

Сущность лейаутов (Joomla Layouts). Переопределение

Лейауты - это фрагменты интерфейса шаблона Joomla, причем не только наружной части сайта, но и панели администратора. В них программным способом передаются данные в виде массива $displayData. Внутри же лейаута данные доступны уже в виде его составляющих.

При создании расширения разработчики чаще всего руководствуются следующей логикой: если данный элемент интерфейса нужен только в одном месте сайта - это макет. Если элемент интерфейса можно переиспользовать в разных частях сайта (расширения) - это layout.

Таким образом в layouts оказываются такие элементы как кнопка, пагинация, заголовок материала (и не только материала, но и контакта, пользователя и т.д.), картинка в теге figure с figcaption, поле (панели администратора), боковое меню (панели администратора) и так далее. Это некие примитивы, которые чаще всего не обладают сложной логикой. Так, layout пагинации может использоваться в любом списке сущностей Joomla как внутри, так и снаружи сайта - в разных компонентах. Тулбар с кнопками "создать", "сохранить", "сохранить и закрыть", "удалить" и другие - используются в разных компонентах. Но выглядят они везде одинаково потому, что они эти элементы реализованы в виде layouts.

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

<?php

return (new FileLayout(
            'regularlabs.form.field.downloadkey',
            JPATH_SITE . '/libraries/regularlabs/layouts'
        ))->render(
            [
                'id'        => $this->id,
                'extension' => strtolower($this->get('extension', 'all')),
                'use_modal' => $this->get('use-modal', true),
            ]
        );

//
// ИЛИ такой вариант
//

// Instantiate a new FileLayout instance and render the layout
$layout = new FileLayout('joomla.quickicons.icon');

return $layout->render($button);

// ИЛИ такой вариант

echo LayoutHelper::render('joomla.icon.iconclass', $displayData);

В виде строки с точками вида joomla.icon.iconclass указан путь в папке layouts. Так, joomla.icon.iconclass говорит Joomla использовать файл layouts/joomla/icon/iconclass.php.

Пример файла лейаута в Joomla 4
Пример файла лейаута в Joomla 4

А для стандартной пагинации будет использоваться лейаут из файла layouts/joomla/pagination/link.php:

<?php 

echo LayoutHelper::render('joomla.pagination.link', $pages['start']);

?>
Лейауты пагинации в Joomla 4
Лейауты пагинации в Joomla 4

Лейауты можно переопределить и вся Joomla будет использовать Вашу вёрстку (если Ваш шаблон назначен для данной страницы). Для этого лейауты нужно скопировать в папку html шаблона: templates/<template_name>/html/layouts. И далее соблюсти путь папки layouts в Вашем шаблоне.

Так, для переопределения пагинации нужно скопировать файлы из layouts/joomla/pagination в templates/<template_name>/html/layouts/joomla/pagination и внедрить в них свою вёрстку. Если Вам нужно переопределить лейаут компонента, то в папку templates/<template_name>/html/layouts копируем лейауты компонента и изменяем их.

При обновлении Joomla и/или компонентов Ваши изменения не затрутся. Но следить за актуальностью кода в Ваших переопределениях Вам нужно будет самостоятельно. Joomla сможет подсказать Вам об изменениях и помочь сравнить файлы.

Уведомление в панели администратора Joomla 4 об обновлениях переопределений.
Уведомление в панели администратора Joomla 4 об обновлениях переопределений.

По сути, Вы можете переопределить в свой шаблон почти всю Joomla и у Вас будет уникальный и неповторимый сайт без потери функциональности при обновлении.

Скорость загрузки сайта и 100 зелёных попугаев

Работа над скоростью загрузки сайта важна, хоть и не является самоцелью. Наверняка, когда Вы изучали стандартный шаблон Cassoipeia Вы обратили внимание на минифицированные и сжатые js и css файлы.

Минифицированные ресурсы в Joomla 4
Минифицированные ресурсы в Joomla 4

Прежде всего хочу сразу адресовать читателя к серии статей в Joomla Community Magazine - Joomla Performance Tuning. В частности к статье Joomla Performance Tuning II: Basic Settings.

В ней рассказывается какие режимы кэширования в Joomla 4 использовать, о сжатии HTML на лету и т.д. Настоятельной рекомендацией данной статьи является НЕ использовать сжатие CSS и JS на лету, что предлагают делать многие шаблоны и/или плагины. Правильно настроенный сервер сделает это гораздо быстрее (читаем статью по ссылке...).

А также Joomla 4 позволяет Вам сразу поставлять в шаблоне заранее сжатые веб ассеты (js и css), что сэкономит и ресурсы сервера. Это работает следующим образом.

Представим, что у Вас есть js-файл media/com_example/js/something.min.js. Сожмите этот файл с помощью GZip в media/com_example/js/something.min.js.gz. Когда бразуер запросит файл media/com_example/js/something.min.js , веб-сервер проверит HTTP-заголовок Accepts  - поддерживаются ли для данного запроса сжатые GZip-ресурсы. Если поддерживаются, то сервер отдаст сразу сжатый файл media/com_example/js/something.min.js.gz вместо обычного несжатого  media/com_example/js/something.min.js .

Для этого не забудьте переименовать файл htaccess.txt в .htaccess. Если же Вы используете в работе свой собственный htaccess-файл, то добавьте в него следующий код

## These directives are only enabled if the Apache mod_headers module is enabled.
## This section will check if a .gz file exists and if so will stream it
##     directly or fallback to gzip any asset on the fly
## If your site starts to look strange after enabling this, and you see
##     ERR_CONTENT_DECODING_FAILED in your browser console network tab,
##     then your server is already gzipping css and js files and you don't need this
##     block enabled in your .htaccess
<IfModule mod_headers.c>
        # Serve gzip compressed CSS files if they exist
        # and the client accepts gzip.
        RewriteCond "%{HTTP:Accept-encoding}" "gzip"
        RewriteCond "%{REQUEST_FILENAME}\.gz" -s
        RewriteRule "^(.*)\.css" "$1\.css\.gz" [QSA]

        # Serve gzip compressed JS files if they exist
        # and the client accepts gzip.
        RewriteCond "%{HTTP:Accept-encoding}" "gzip"
        RewriteCond "%{REQUEST_FILENAME}\.gz" -s
        RewriteRule "^(.*)\.js" "$1\.js\.gz" [QSA]

        # Serve correct content types, and prevent mod_deflate double gzip.
        RewriteRule "\.css\.gz$" "-" [T=text/css,E=no-gzip:1]
        RewriteRule "\.js\.gz$" "-" [T=text/javascript,E=no-gzip:1]

        <FilesMatch "(\.js\.gz|\.css\.gz)$">
                # Serve correct encoding type.
                Header append Content-Encoding gzip

                # Force proxies to cache gzipped &
                # non-gzipped css/js files separately.
                Header append Vary Accept-Encoding
	</FilesMatch>
</IfModule>

В целом советую ознакомиться со статьями из этой серии, они Вам помогут.

Общие рекомендации

Хотя это скорее не рекомендации, а моя личная практика, которая не претендует на абсолют. Как я писал в начале статьи - в Joomla существует несколько путей решения одной и той же задачи и нельзя какой-то из них однозначно назвать правильным.

В моей практике сложилось так, что файл index.php шаблона - это сетка и логика вывода позиций. В большинстве случаев каждая позиция для модулей обёрнута в условия вывода на php и простой Bootstrap-контейнер

<?php 
// фрагмент файла index.php шаблона. 
// Тут сократили 250 строк получения параметров и переменных для построения условий
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
	<jdoc:include type="metas"/>
	<jdoc:include type="styles"/>
	<jdoc:include type="scripts"/>
</head>

<body class="<?php echo($pageclass ? ' ' . $pageclass : '');
echo $hasClass; ?>">

<?php
//  add custom code form template settings
if(!empty($this->params->get('custom_code_after_body'))){
	echo $this->params->get('custom_code_after_body');
}
?>

<?php if ($this->countModules('topbar', true)) : ?>
	<div class="container topbar">
		<jdoc:include type="modules" name="topbar" style="none"/>
	</div>
<?php endif; ?>

<div class="bg-white sticky-top shadow-sm">
	<header class="container">
		<div class="row py-3">
			<?php if ($this->params->get('brand', 1)) : ?>
				<div class="col-6 col-lg-1 order-lg-1">
					<a class="brand-logo" href="<?php echo $this->baseurl; ?>/">
						<?php echo $logo; ?>
					</a>
				</div>
			<?php endif; ?>
			<?php if ($this->countModules('menu', true)) : ?>
				<jdoc:include type="modules" name="menu"/>
			<?php endif; ?>
		</div>
	</header>
</div>
                  
<?php if ($this->countModules('video-bg-header', true)) : ?>
	<jdoc:include type="modules" name="video-bg-header" style="none"/>
<?php endif; ?>


<?php if ($this->countModules('banner', true)) : ?>
	<div class="container">
		<jdoc:include type="modules" name="banner" style="none"/>
	</div>
<?php endif; ?>

<!-- и так далее -->

Всё остальное я помещаю в макеты модулей или в модули типа HTML-код, использую CSS-классы модуля (суффиксы класса модуля). За редким исключением модальных окон или offcanvas.

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

Заключение

Кастомизация панели администратора Joomla

В этой статье я описывал возможности, которые даёт Joomla для создания шаблона сайта. Обычно все понимают под этим шаблон фронтенда - дизайн публичной части. Однако те же методы и подходы можно применять и для панели администратора. У шаблонов панели администратора есть свои параметры (идём Система - блок Шаблоны - Стили):

В Joomla 4 и Joomla 5 шаблон панели управления по умолчанию называется Atum. В нём можно поиграть с цветами панели админстратора, указать свои логотипы и тем самым брендировать админку сайта для клиента.

Существуют модули панели управления. У них много своих интересных параметров. Вы можете даже стандартными средствами оформить админку под себя. Также существуют и альтернативные шаблоны админок для Joomla 4 и 5.

Позволю себе не останавливаться на этом подробно, поскольку данная тема уже будет затрагивать больше частные случаи, чем общие.

Собственно заключение

Данная статья создавалась в течение примерно 3 недель большей частью на детских площадках и в машине, за что им большое спасибо ))

Также я признателен участникам Joomla-сообщества за некоторые советы по ходу создания статьи.

Я буду признателен за ценные рекомендации и дополнения, которыми можно улучшить статью. Также зоркий читатель может обнаружить опечатки и косноязычие. Буду рад исправить и их.

Полезные ресурсы

Ресурсы Joomla сообщества

Joomla в Telegram

В хедере статьи использован фрагмент изображения от marymarkevich на Freepik.

Комментарии (5)


  1. Sulpher
    09.10.2023 06:45
    +1

    Ого, какая объемная и подробная статья. Автору пора писать книгу :-)

    В Joomla 5 обещали вынести все переопределения в child шаблоне. Так можно переопределить Uikit через Cassiopeia.
    Вот официальный анонс, в котором упомянули о такой фиче:

    The long-term goal is that the core layouts and Cassiopeia will be decoupled (self-contained and no longer reliant on each other).

    Decoupling would allow us to plan to have a “bootstrap template” as a baseline where all bootstrap-dependent code is included as an override. Everything that is template-specific in any way is moved from the core layouts to Cassiopeia. Cassiopeia either then becomes a child template of this new “bootstrap 5” template or stays as a standalone with all the overrides included. This makes it possible to build additional parent templates based on other frameworks such as Tailwind or UIKit.

    https://www.joomla.org/announcements/release-news/5868-joomla-5-panta-rhei-the-follow-up.html

    Вот это будет просто здорово. Сразу просится Uikit. Особенно актуально для Интернет-магазинов, когда все будет на RadicalMart, а шаблон на Cassiopeia + Uikit.


  1. vadya567
    09.10.2023 06:45
    +1

    Спасибо! Очень подробная статья, текста на методичку хватит)


    1. sergeytolkachyov Автор
      09.10.2023 06:45

      Спасибо)


  1. zikkuratvk
    09.10.2023 06:45
    +1

    Честно не смог все прочитать в один присест. Снимаю шляпу за труд.


    1. sergeytolkachyov Автор
      09.10.2023 06:45

      Спасибо)