Что нового для разработчиков в XenForo 2.3?

HYS Как и обещали, на этой неделе мы быстро рассмотрим некоторые изменения в XenForo 2.3, ориентированные на разработчиков.

Если вас интересует определенная тема больше других, кликните по одной из ссылок ниже:
Хотя у нас еще есть немного материала для показа, следующие несколько недель будут сосредоточены на подготовке XenForo 2.3 к установке здесь, и между тем и выпуском публичной бета-версии могут появиться дополнительные сообщения "Вы видели?". До тех пор спасибо, что присоединились к нам в этом путешествии.
 

JavaScript​

В нашем втором сообщении HYS мы объявили о нашем решении отказаться от jQuery, и некоторые из вас уже выпускают обновления для своих дополнений, что приятно видеть. Следующее служит неисчерпывающим справочником для любых конкретных изменений в фреймворке, которые могут повлиять на то, как вы пишете код JavaScript в своих дополнениях в дальнейшем.

XF.extendObject

Это новый метод, который заменяет стандартный метод jQuery $.extend(). Он работает практически точно так же, включая возможность делать "глубокое" клонирование.

XF.createElementFromString

jQuery поддерживал создание нового элемента с различными свойствами и атрибутами полностью из строки, например:
JavaScript:
const $input = $('<input type="text" readonly class="input" />')

Мы хотели иметь что-то похожее, поэтому мы добавили метод, который работает аналогично:
JavaScript:
const input = XF.createElementFromString('<input type="text" readonly class="input" />')

У нас есть совершенно новая концепция, которая называется XF.createElement. О ней вы можете прочитать в последующем сообщении.

Управление событиями​

Некоторые вещи в управлении событиями в jQuery довольно крутые, поэтому мы постарались воспроизвести их насколько это возможно. Особенно стоит отметить, что мы поддерживаем именованные события аналогично jQuery, вместе с эквивалентными методами jQuery, названными XF.on(), XF.off(), XF.trigger() и XF.customEvent(). Для обработки делегированных событий у нас есть новый метод XF.onDelegated. Если ранее вы использовали метод jQuery one, чтобы слушатель события удалялся после его первого срабатывания, теперь вы можете просто передать { once: true } в вызовы XF.on().

Изменения в XF.ajax

Хотя использование XF.ajax() в основном не изменилось, очевидно, что мы больше не используем под капотом $.ajax() от jQuery, который является оболочкой для XMLHttpRequest. Мы решили отказаться от XMLHttpRequest в пользу более современного Fetch API.

XF.ajax теперь возвращает Promise, что похоже на то, что возвращал jQuery, хотя имена методов промиса немного отличаются. Которые были упомянуты в оригинальном сообщении "Вы видели".

Другое заметное изменение касается того, как прерываются AJAX-запросы, если это необходимо. Ранее объект, возвращаемый jQuery, имел метод abort, который можно было вызвать. Fetch API имеет другой способ достижения этого, который немного более сложен, поэтому мы создали новый метод XF.ajaxAbortable, который делает это немного проще, но стоит отметить, что ваше существующее использование XF.ajax, где может потребоваться прерывание вызова, нужно будет изменить.

Вот пример использования из form.js:
JavaScript:
const {
    ajax,
    abortController,
} = XF.ajaxAbortable('post', this.options.descUrl, { id: value }, this.onLoad.bind(this))

if (abortController)
{
    this.abortController = abortController
}

// ... elsewhere in the code

if (this.abortController)
{
    this.abortController.abort()
    this.abortController = null
}

XF.proxy

Метод XF.proxy обычно используется, когда вы хотите изменить контекст переменной this при вызове другой функции. Например, если вы передаете функцию в качестве обратного вызова при прослушивании события загрузки изображения load (или подобного), this в этом обратном вызове обычно будет ссылкой на само изображение. Обычно это не желательно, поэтому XF.proxy помогает нам поддерживать постоянство контекста this.

Хотя, конечно, XF.proxy по-прежнему существует и остается неизменным, пожалуйста, рассматривайте это как устаревшее и помеченное для удаления в будущем.

Вместо этого мы теперь рекомендуем и используем собственный подход JavaScript для этого. Это выглядит так:
JavaScript:
XF.on(form, 'reset', this.formReset.bind(this))

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

JavaScript анимации и CSS-переходы​

В jQuery есть ряд функций анимации, которые нам показались стоящими сохранения, поэтому мы переписали их. Новое пространство имен для этих методов - XF.Animate, которое включает различные подходы для скольжения/затухания контента.

Вот пример, где мы плавно уменьшаем видимость существующего контейнера для его скрытия, заменяем его содержимое, а затем плавно увеличиваем видимость обратно, чтобы показать новый контент:
JavaScript:
XF.Animate.fadeUp(containerEl, {
    speed: XF.config.speed.fast,
    complete ()
    {
        containerEl.innerHTML = html.innerHTML
        XF.Animate.fadeDown(containerEl)
    },
})

Возможно, вы также знакомы с нашими настраиваемыми методами addClassTransitioned и removeClassTransitioned. Ранее эти методы были добавлены как расширения jQuery. Теперь они перенесены в новое пространство имен XF.Transition и требуют передачи элемента в качестве первого аргумента.
JavaScript:
XF.Transition.addClassTransitioned(this.errorElement, 'is-active')

Изменения в библиотеках сторонних производителей​

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

Select2​

К сожалению, Select2 все еще написан с зависимостью от jQuery, поэтому начиная с XenForo 2.3 он больше не будет включен. Мы используем Select2 только для нашей системы "ввода токенов", которая используется в качестве ввода тегов и для выбора нескольких пользователей (например, в беседах). Чтобы сохранить эту функциональность, теперь мы включаем библиотеку под названием .

Она практически имеет ту же функциональность, к которой вы привыкли, но, о, посмотрите, аватары для ввода нескольких пользователей:
1700245718433.png

Генерация QR-кодов​

Мы включаем библиотеку для генерации QR-кодов в основном для облегчения настройки TOTP как метода двухфакторной аутентификации. Предыдущая версия этой библиотеки зависела от jQuery, но новые версии были переписаны без каких-либо специфических зависимостей. Если вы используете QR-коды в любом из ваших дополнений, это просто то, о чем вы захотите знать. Конкретную версию этой библиотеки, которую мы теперь используем, можно найти .

Рейтинг​

Рейтинги, как вы можете видеть как в XenForo Media Gallery, так и в XenForo Resource Manager, ранее зависели от сторонней библиотеки. Прямая замена действительно не существовала, поэтому мы просто сами перевели её на JavaScript. Теперь это новый класс под названием XF.BarRating, который вы можете найти в rating.js.
 

SwiftMailer на Symfony Mail​

Объявление о прекращении поддержки SwiftMailer было , и мы быстро реализовали Symfony Mail в качестве замены... еще в 2021 году. Хотя мы могли бы внедрить это в течение жизненного цикла XenForo 2.2, есть несколько небольших изменений, нарушающих обратную совместимость.

Любые классы-расширения для XF\Mail\Mail и XF\Mail\Mailer вероятно будут затронуты и потребуют изменений для работы с XenForo 2.3. Это в основном затронет любые методы, которые в настоящее время принимают объекты с классом, имеющим префикс Swift_, и использование этих объектов, которые могут иметь другой API.

В целом, Symfony Mail по сути является полной переработкой SwiftMailer, поэтому процесс перевода кода на использование Symfony Mail не должен вызвать слишком много проблем.
 

Doctrine Cache на Symfony Cache​

Doctrine Cache также был признан устаревшим и заменен компонентом Symfony Cache. Хотя это изменение схоже по масштабам с Symfony Mailer, оно может иметь большее влияние на сайты, использующие дополнения, затрагивающие кеширование в той или иной форме.

С учетом этого мы предоставили слой совместимости, чтобы снизить вероятность серьезных проблем. При получении адаптера кеша из контейнера сервисов, он оборачивается объектом с тем же полным квалифицированным именем класса (FQCN) и интерфейсом, что и у провайдера кеша Doctrine. В большинстве случаев это должно позволить дополнениям продолжать работать без изменений. Мы используем обертку Doctrine Cache в \XF\CssRenderer, чтобы сохранить обратную совместимость. Другие классы, которые не используют нашу систему расширений, были обновлены для прямого использования адаптера Symfony Cache.

Чтобы облегчить переход на адаптер Symfony Cache, вы можете получить его, используя новый третий логический аргумент метода \XF\App::cache:

PHP:
$cache = \XF::app()->cache('context', true, false);

// установка элемента кеша
$item = $cache->getItem($id);
$item->set($data);
$item->expiresAfter($ttl);
$cache->save($item);

// получение элемента кеша
$data = $cache->getItem($id);

Вы можете ознакомиться с документацией по для получения дополнительной информации.
 

Обновленный Emogrifier​

Мы обновили включенную библиотеку Emogrifier. Это библиотека, которая конвертирует CSS классы, используемые в шаблонах электронной почты, во встроенные стили, чтобы улучшить совместимость отображения почты в различных почтовых клиентах.

Это привело к незначительному изменению в классе XF\Mail\Styler. Если вы добавили расширение класса здесь, которое расширяет метод __construct конкретно, вам потребуется внести изменения, так как он больше не получает свойство inliner:

PHP:
public function __construct(CssRenderer $renderer)
 

Событие предзагрузки реестра​

Иногда определенные данные, хранящиеся в реестре, необходимы для большинства или потенциально всех запросов. Когда XenForo не настроена на использование кэширования, загрузка данных из реестра приводит к запросу к базе данных. В ядре мы можем предварительно загружать общие данные одним запросом при инициализации фреймворка, но эта функциональность не была доступна для дополнений, потому что данные дополнений, сами по себе, хранятся в реестре. Замкнутый круг! (Или кэш-22...?)

Мы ввели кодовое событие app_preload_extra в XF 2.3, позволяющее дополнениям предварительно загружать данные из реестра. Это означает, что все дополнения могут загружать данные из реестра вместе одним запросом, вместо того чтобы запрашивать данные индивидуально. Объект \XF\App также передается, так что ключи могут быть ограничены определенным приложением:

PHP:
public static function appPreloadExtra(\XF\App $app, array &$keys): void
{
    if ($app instanceof \XF\Pub\App)
    {
        $keys[] = 'myRegistryKey';
    }
}
 

PHP методы конвертации IP​

В предыдущих версиях мы использовали собственные методы для конвертации IP-адресов между текстовым и бинарным форматами. В XF 2.3 мы объявили устаревшими методы \XF\Util\Ip::convertIpStringToBinary и \XF\Util\Ip::convertIpBinaryToString в пользу новых методов \XF\Util\Ip::stringToBinary и \XF\Util\Ip::binaryToString. Новые методы используют встроенные функции PHP inet_pton и inet_ntop, которые более надежны и не приводят к потенциально небезопасным неоднозначностям.

Если вы используете устаревшие методы в дополнении, новые методы в большинстве случаев должны функционировать как замена без изменений. Однако есть несколько оговорок, которые следует учитывать. Новые методы не будут пытаться обрабатывать ситуации, когда IP-адрес уже был конвертирован в целевой формат, и будут вызывать исключение, если им передан неверный IP-адрес по умолчанию. Вы можете передать false в качестве последнего аргумента, чтобы вместо исключения возвращалось значение false. Метод \XF\Util\Ip::binaryToString также сохраняет поддержку расширения адресов IPv6, где это желательно.
 

Поддержка строк классов​

Начиная с XF 2.0, мы широко использовали короткие имена классов для расширения имен классов и ассоциирования связанных объектов, таких как сущности и поисковики. Несмотря на удобство, многие чрезвычайно полезные современные инструменты не понимают эту конвенцию, что требует обходных путей, таких как генерация расширенных метаданных для PhpStorm или пользовательские расширения для инструментов статического анализа.

В XF 2.3 теперь поддерживается использование полностью квалифицированных имен классов везде, где поддерживаются короткие имена. Это включает получение сущностей, поисковиков, репозиториев и других объектов, а также определение метаданных, таких как связи сущностей и поведения. При использовании полностью квалифицированных имен классов среды разработки лучше справляются с рефакторингом, таким как переименование классов, а статические анализаторы лучше способны определять, какие объекты возвращаются из методов контейнера и фабрики.

PHP:
use XF\Entity\User;

// ...

$user = \XF::app()->find(User::class, 1);

// ...

$structure->relations = [
    'User' => [
        'entity' => User::class,
        'type' => self::TO_ONE,
        'conditions' => 'user_id',
        'primary' => true,
    ],
];
 

Обновления тегов <time> - короткий формат даты​


Во время разработки того, что теперь стало стилем XenForo 3, мы внесли некоторые улучшения в HTML-вывод тегов <time> (которые отображаются при использовании тега xf:date в синтаксисе шаблонов XenForo).

Хотя эта функция активно не используется в версии 2.3 и не изменяет внешний вид вывода шаблона, мы решили не отказываться от этих изменений, оставив их в этой версии.

Так что же это на самом деле делает?

В XenForo 2.2 мы выводили теги времени следующим образом:
HTML:
<time datetime="2023-11-15T09:49:23+0000" data-time="1700041763"
   data-date-string="Nov 15, 2023" data-time-string="9:49 AM"
   title="Nov 15, 2023 at 9:49 AM">51 mins</time>
Затем это обновляется с помощью JavaScript, чтобы поддерживать время относительно настоящего.

Для нового стиля мы хотим иметь возможность выводить короткую дату, когда место ограничено, так что 51 минута назад будет отображаться как 51м. Для этого мы включаем новый атрибут data-short, содержащий формат короткой даты/времени.
HTML:
<!-- 2m (2 minutes ago) -->
<time datetime="2023-11-15T10:43:14+0000" data-timestamp="1700044994"
    data-date="Nov 15, 2023" data-time="10:43 AM"
    data-short="2m" title="Nov 15, 2023 at 10:43 AM">2 minutes ago</time>
Тот же JavaScript, который обновляет основной вывод, также обновляет атрибут data-short, указывая текущий короткий формат.

Другие примеры:
HTML:
<!-- 13h (13 hours ago) -->
<time datetime="2023-11-14T21:00:23+0000" data-timestamp="1699995623"
    data-date="Nov 14, 2023" data-time="9:00 PM"
    data-short="13h" title="Nov 14, 2023 at 9:00 PM">Yesterday at 9:00 PM</time>

<!-- 2d (2 days ago) -->
<time datetime="2023-11-12T22:46:15+0000" data-timestamp="1699829175"
    data-date="Nov 12, 2023" data-time="10:46 PM"
    data-short="2d" title="Nov 12, 2023 at 10:46 PM">Sunday at 10:46 PM</time>

<!-- Sep 14 (September 14 this year) -->
<time datetime="2023-09-14T14:50:24+0100" data-timestamp="1694699424"
    data-date="Sep 14, 2023" data-time="2:50 PM"
    data-short="Sep 14" title="Sep 14, 2023 at 2:50 PM">Sep 14, 2023</time>

<!-- Nov '18 (November 12 2018) -->
<time datetime="2018-11-12T15:26:22+0000" data-timestamp="1542036382"
    data-date="Nov 12, 2018" data-time="3:26 PM"
    data-short="Nov '18" title="Nov 12, 2018 at 3:26 PM">Nov 12, 2018</time>

Естественно, эти короткие форматы дат интегрируются с системой языка и фраз, так что если 2m не означает 2 минуты на вашем языке, вы можете свободно редактировать вывод так же, как и для длинного формата даты.

По умолчанию, при отображении даты из предыдущего года, короткий формат исключает день месяца (Ноя '22 M 'y, а не Ноя 15, '22 M j, 'y), но если вы хотите включить его, вы можете сделать это с помощью пользовательского формата даты, например, M j 'y.

1700245762284.png

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

Названия шаблонов в HTML-выводе​

Просмотр страницы с HTML-выводом XenForo и попытки понять, как она была построена, когда она может содержать фрагменты многих различных шаблонов, может быть сложной задачей.

Или, по крайней мере, было.

Ранее мы выводили атрибут data-template в теге <body>, чтобы помочь разработчикам и дизайнерам определить основной шаблон контента в использовании, но теперь мы значительно расширили эту систему.

Теперь, включив опцию Внедрить имена шаблонов в HTML в разделе настроек Внешний вид, ваш HTML-вывод будет включать атрибуты data-template-name, которые облегчают идентификацию шаблона, ответственного за конкретную часть страницы, которую вы изучаете.

1700245782462.png

Эти атрибуты показываются только администраторам.

HTML:
<html data-template-name="PAGE_CONTAINER" id="XF" lang="en-US" dir="LTR" data-xf="2.3" data-app="public" ...>
HTML:
<div data-template-name="thread_view" class="block block--messages" ...>

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

На практике атрибуты data-template-name добавляются к самому внешнему отрендеренному HTML-тегу в каждом шаблоне. Есть несколько случаев, когда в шаблоне есть контент до HTML-тега, в таких случаях рендерер выводит этот контент перед помеченным тегом, но все равно будет намного проще определить ответственный шаблон, чем раньше.

Значение атрибута data-template-name не ограничивается только именами шаблонов. В случаях, когда вывод происходит из шаблона макроса, значение атрибута будет включать как имя шаблона, так и имя макроса.

HTML:
<article data-template-name="post_macros::post" class="message message--post" ...>

1700245793623.png

Обратите внимание, что при включении этих тегов data-template-name они выводятся только для администраторов, поэтому вы не должны полагаться на них в качестве селекторов CSS, в отличие от оригинального селектора body[data-template], который безопасно использовать.

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

Метод nth в AbstractCollection​

В ответ на недавнее предложение мы реализовали возможность легко возвращать nth элемент из AbstractCollection.

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

Например, если вы делаете запрос на коллекцию постов и хотите получить доступ к третьему посту из возвращенной коллекции, вы можете просто вызвать ->nth(3).

PHP:
$posts = $finder->fetch();
$third = $posts->nth(3);
$fifteenth = $posts->nth(15);
 

Умная очистка кэша Javascript​

Разве не раздражает, когда при разработке javascript приходится каждый раз делать полную перезагрузку страницы после внесения изменений в код?

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

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

Ведь мелочи имеют значение...
 

XF.createElement

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

Рассмотрим следующий Javascript код:
JavaScript:
const el = document.createElement('input')
el.type = 'text'
el.name = 'title'
el.value = 'Hello, world'
parentEl.appendChild(el)
Это действительно много использований el, верно?

Теперь с версией 2.3 и XF.createElement(), мы можем сделать следующее:
JavaScript:
const el = XF.createElement('input', {
    type: 'text',
    name: 'title',
    value: 'Hello, world'
}, parentEl)
Здесь, в одном объявлении, мы создаем элемент ввода, присваиваем ему ряд свойств и добавляем его к parentEl. Нам даже не нужно присваивать возвращаемое значение переменной, если это не требуется в последующем коде.

Как объект свойств, так и узел для добавления элемента являются необязательными параметрами, хотя если вы не собираетесь добавлять элемент в DOM, было бы бессмысленно не присвоить возвращаемое значение переменной 🤔

Объект свойств также поддерживает один уровень вложенности, так что вы также можете сделать что-то вроде следующего:
JavaScript:
const el = XF.createElement('span', {
    className: 'spanny-spanny-moo-moo',
    title: 'This is a span',
    dataset: {
        foo: 1,
        bar: 2
    },
    style: {
        color: 'red',
        fontWeight: 'bold'
    }
}, parentEl)
Также есть специальный случай использования свойства attributes, значения которого будут установлены с использованием el.setAttribute(name, value), если вам нужно его использовать.
 

Изменения синтаксиса макросов шаблонов​

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

Вот в основном свернутое представление шаблона PAGE_CONTAINER XenForo 2.2 в панели Structure PhpStorm и панели Outline Visual Studio Code:

1700245823029.png 1700245831989.png

Довольно бесполезно, правда?

Причина этого в том, что эти древовидные представления игнорируют атрибут name на тегах в своем представлении сводки. Не будем говорить о том, насколько это глупо, особенно когда атрибут name имеет критическое значение для элементов, таких как <input>, но это так, как есть.

1700245863868.png

Мы не можем ничего сделать с <input>, <select> или <textarea>, но мы можем что-то сделать с xf:macro, чтобы он имел более полезное представление в представлении структуры.

<xf:macro name="m" /> ➡ <xf:macro id="m" />​


Это довольно простое изменение, и оно включает в себя замену атрибута name на атрибут id. Это может вызвать жалобы вашей IDE на использование одного и того же атрибута id на нескольких элементах, но это можно игнорировать, так как эти атрибуты не будут выводиться в конечном отрендеренном HTML, где это имеет значение.

Давайте заменим в шаблоне PAGE_CONTAINER использование <xf:macro name...> на <xf:macro id...> и посмотрим, как изменится представление структуры.

1700245874076.png 1700245882989.png

Разве это не лучше?
Атрибут name все еще доступен, но объявлен устаревшим, поэтому вам следует обновить свои шаблоны.

<xf:macro template="t" name="m"> ➡ <xf:macro id="t::m" />​


Мы также объявили устаревшим синтаксис <xf:macro template="template-name" name="macro-name" /> в пользу формата id="template-name::macro-name", который также помогает увидеть разумное представление структуры.

1700245912880.png
 

Прямые сообщения​

Мы уже упоминали об этом ранее, но приняли решение переименовать "Переписки" в "Прямые сообщения". Эта терминология более знакома большинству пользователей интернета и имеет более удачное сокращение "DM" (Direct Message). На протяжении многих лет люди часто называли их "PM" (Personal Message - личные сообщения) или "PC" (Personal Conversations - личные беседы), что может сбивать с толку тех, кто не знаком с программным обеспечением.

В XF 2.3 это изменение только визуальное. Все ссылки в коде, названия классов и шаблонов остались прежними. Появились новые канонические пути direct-messages и direct-messages/replies, но все существующие пути для conversations и conversations/messages будут корректно перенаправляться на новые пути. Изменены только фразы.

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

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