Плагин шорткодов для Joomla

от автора

Приветствую, коллеги!

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

И, так как в Joomla шорткоды широко не применяются, на всякий случай проясню что это такое.

Шорткод (англ. shortcode — короткий код) — это удобный способ добавить в текст статьи динамический контент или сложные HTML-элементы, не захламляя редактор громоздким кодом. Вместо того чтобы вставлять, скажем, полноценный <iframe> с кучей параметров, вы используете короткий и понятный тег.

Классический пример — вставка видео из YouTube в WordPress. Традиционный код выглядит примерно так:

<iframe    src="https://www.youtube.com/embed/kBddBRQ-xic"    width="560"    height="315"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    title="YouTube video player"    referrerpolicy="strict-origin-when-cross-origin"    frameborder="0"    allowfullscreen></iframe>

С шорткодом всё это превращается в одну строку:

[youtube https://www.youtube.com/watch?v=dQw4w9WgXcQ width="560" height="315"]

В Joomla тоже есть несколько встроенных шорткодов, например:

  • {loadmoduleid 123}: загружает модуль по ID;

  • {loadposition sidebar}: выводит модули из заданной позиции;

  • {field 5}: вставляет значение произвольного поля материала.

Однако WordPress, в отличие от Joomla, позволяет легко добавлять собственные шорткоды при помощи функции add_shortcode(), которую можно прописать в functions.php текущей темы или в MU-плагине.

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

add_shortcode('current_year', fn () => date('Y'));

После этого в любом посте будет работать конструкция [current_year], заменяясь на текущий год.

В Joomla для создания собственного шорткода вам придётся:

  1. Создать полноценный плагин группы content с XML-файлом манифеста.

  2. Установить его и активировать (можно через php cli/joomla extension:discovery).

  3. Подписаться на событие onContentPrepare.

  4. Написать регулярное выражение для поиска вашего тега.

  5. Обработать текст статьи при помощи preg_replace_callback().

  6. И, возможно, что-то ещё…

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

Среди расширений есть, например, JShortcodes с drag-and-drop интерфейсом или Easy Shortcodes на базе INI-файлов. Первый, на мой взгляд, перегружен сотнями виджетов и платный, второй — использует ini-файлы там, где естественнее писать на PHP.

Мне больше всего нравится Snippets от Regular Labs, но в нём также нет поддержки PHP.

Shortcoder использует другой подход. Никакого сложного визуального интерфейса: все шорткоды описываются прямо в PHP-файлах или задаются как вызываемые объекты (обычно это функции). Плагин автоматически подхватывает файлы из папки /shortcodes и превращает их имена в одноимённые теги.

Вот как можно просто создать шорткод для текущего года.

Для этого мы создаём файл shortcodes/current_year.php с содержимым:

<?php \defined('_JEXEC') or die;// Весь вывод файла попадает в {current_year}echo date('Y');

Теперь в любой статье можно написать {current_year} — и увидеть актуальный год.

Вообще говоря, такой шорткод лучше реализовать в виде замыкания. Для этого используется специальный файл shortcodes.php в директории shortcodes:

<?php // shortcodes/shortcodes.php\defined('_JEXEC') or die;// Файл должен возвращать массив с шорткодамиreturn [    // {current_year}    'current_year' => fn () => date('Y'),    // {hello "John Doe"}    'hello' => fn (array $attributes) =>        sprintf('Hello, %s!', $attributes[0] ?? 'World'),];

А вот пример более сложного шорткода {details} для вставки спойлера. Здесь используется именованный атрибут summary и специальная переменная $content для получения содержимого внутри тега:

<?php // shortcodes/details.php\defined('_JEXEC') or die;$summary = $attributes['summary'] ?? 'Click to see more';?><details>    <summary><?php echo htmlspecialchars($summary); ?></summary>    <div>        <?php echo $content; ?>    </div></details>

Использование в редакторе:

{details summary="Важная информация"}Здесь скрытый текст, который увидит пользователь после клика.{/details}

Надеюсь, я смог передать основную идею плагина.

Установка

Системные требования:

  • Joomla 4 и выше.

  • PHP 7.4 и выше.

Установка стандартная — скачиваете zip-файл с последней версией с GitHub Releases и устанавливаете через панель администратора: «Система» → «Установка» → «Расширения».

Можно использовать CLI:

php cli/joomla.php extension:install https://github.com/PopArtDesign/joomla-shortcoder/archive/refs/tags/v1.0.0.zip

После установки активируйте плагин «Content — Shortcoder» в менеджере плагинов.

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

mkdir shortcodes && touch shortcodes/shortcodes.php

Синтаксис шорткодов

Шорткоды добавляются в текст при помощи тегов, заключённых в фигурные скобки: {foo}, {bar}, {baz} и т.д. В качестве имени тега может использоваться любая строка, содержащая буквы латинского алфавита, цифры, символы тире и нижнего подчёркивания.

Шорткоды бывают двух типов:

  • самозакрывающиеся: {hello "John Doe"}

  • с закрывающим тегом: {repeat 10}Repeat Me{/repeat}

Последние используются для захвата внутреннего содержимого, которое будет доступно в переменной $content:

<?php // shortcodes/shortcodes.php\defined('_JEXEC') or die;return [    // {repeat 5}Hello, World!{/repeat}    'repeat' => fn (array $attributes, string $content) =>        str_repeat($content, (int) ($attributes[0] ?? 1))];

Атрибуты

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

Атрибуты также бывают двух типов:

  • позиционные — перечисляются через пробел и доступны по индексу в массиве: $attributes[0], $attributes[1] и т.д.

    Пример: {hello John Doe}:

    • $attributes[0] = 'John'

    • $attributes[1] = 'Doe'

  • именованные — записываются в формате ключ="значение". Внутри обработчика шорткода они доступны по имени ключа: $attributes['key'].

    Пример: {hello first="John" last="Doe"}:

    • $attributes['first'] = 'John'

    • $attributes['last'] = 'Doe'

Все атрибуты передаются в обработчик в виде единого массива $attributes.

Также существует специальный атрибут _ (нижнее подчёркивание), который содержит только позиционные атрибуты в виде индексированного массива:

<?php // shortcodes/shortcodes.php\defined('_JEXEC') or die;return [    // {sum 1 2 3 4}    'sum' => fn (array $attributes) => array_sum($attributes['_']),];

Значения атрибутов можно заключать в “двойные” или ‘одинарные’ кавычки. Если значение не содержит пробелов и специальных символов, кавычки можно опустить.

  • {button url="https://example.com" label="Click me"}

  • {button url=https://example.com label=Click} — валидно, но нужно помнить: значения без кавычек обрезаются первым же пробелом.

Переменная $item

В специальную переменную $item передаётся текущий объект публикации: статья, категория или другой элемент, который обрабатывается плагином. Это позволяет шорткоду взаимодействовать с контекстом, например, получать значения пользовательских полей (custom fields).

Вот пример шорткода, который выводит значение произвольного поля по его имени, а не по ID (в отличие от встроенного {field}):

<?php // shortcodes/shortcodes.php\defined('_JEXEC') or die;return [    // Пример: {jcfield price raw="false"}    'jcfield' => function (array $attributes, string $content = '', $item = null) {        $name = $attributes['name'] ?? $attributes[0] ?? '';        // Вообще говоря, свойство $item->jcfields может отсутствовать,        // и лучше делать загрузку с помощью FieldsHelper        if (!$name || !$item || empty($item->jcfields ?? null)) {            return '';        }        // Выводить "сырое" значение?        $raw = ($attributes['raw'] ?? 'true') === 'true';        foreach ($item->jcfields as $field) {            if ($field->name === $name) {                return $raw ? $field->rawvalue : $field->value;            }        }        return '';    },];

Вложенность

Шорткоды могут вкладываться друг в друга, но есть одно ограничение: не поддерживаются вложенные одноимённые теги.

✅ Работает:

{outer}    {inner}Текст{/inner}{/outer}

❌ Не работает:

{details}    {details}Вложенный спойлер{/details}{/details}

Это связано с ограничениями парсера, использующего регулярные выражения. Если кратко, регулярные выражения (конечные автоматы) «не умеют считать» и могут найти либо самый первый закрывающий тег, либо самый последний (жадный вариант). Если выбрать первый вариант — теряется поддержка вложенности, если второй — поддержка соседних тегов. Первый вариант мне показался более предпочтительным.

GitHub Gist

Напоследок давайте сделаем ещё один пример шорткода {gist} для вставки содержимого GitHub Gist:

<?php // shortcodes/gist.php\defined('_JEXEC') or die;// Получение URL для вставкиif (!$url = $attributes[0] ?? '') {    // URL не был передан: {gist}, можно ничего не выводить    return;}if (strpos($url, 'https://gist.github.com') !== 0) {    // Был указан некорректный адрес: {gist https://gitflic.ru}    return;}$scriptUrl = rtrim($url, '/') . '.js';// Имя файла: {gist url file="somefile.php"}if ($file = $attributes['file'] ?? '') {    $scriptUrl .= '?file=' . urlencode($file);}?><script src="<?php echo htmlspecialchars($scriptUrl); ?>"></script>

Использование:

{gist https://gist.github.com/voronkovich/d35cdcdf6eb09e986ab9b16f91a5b2e8}{gist https://gist.github.com/voronkovich/d35cdcdf6eb09e986ab9b16f91a5b2e8 file=somefile.php}

Более подробную документацию по работе с плагином вы можете прочитать в репозитории проекта.

Благодарю за внимание!

ссылка на оригинал статьи https://habr.com/ru/articles/1030674/