Сломался шаблон моего "component". Как это исправить?

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

Компонент не хранится в git-репозитории, он настроен напрямую через «Администрирование» → «Внешний вид» → «Темы и компоненты» → вкладка «Компоненты»:

Пользовательский код

Пользовательские секции:

  • Общие: CSS, Подвал, JS

Загрузки

Дополнительные файлы

Экспортируйте тему, чтобы просмотреть эти файлы.
  • javascripts/discourse/api-initializers/theme-initializer.gjs

Содержимое этих «секций»

как видно при нажатии «Редактировать код»; путь к соответствующим файлам в экспорте указан в скобках

CSS (common/common.scss)
.sponsors {
    .inner {
        display: flex;
        align-items: center;
        justify-content: space-around;
        margin-bottom: 10px;
    }

    .heading {
        font-size: 1.75em;
    }

    .sponsor-image {
        max-height: 55px;
    }

    .geoaargau {
        max-height: 45px;
    }
}
Подвал (common/footer.html)
  {{#if displaySponsors}}
    <div class="sponsors-wrapper wrap">
      <div class="inner">
        <h3 class="heading">Sponsoren</h3>
        <a href="http://www.asseco-berit.ch/"><img src="{{theme-setting "theme_uploads.asseco_berit"}}" alt="Asseco Berit" class="sponsor-image asseco-berit"></a>
        <a href="http://www.geoaargau.ch/"><img src="{{theme-setting "theme_uploads.geoaargau"}}" alt="GEOAargau" class="sponsor-image geoaargau"></a>
      </div>
    </div>
  {{/if}}
JS (javascripts/discourse/api-initializers/theme-initializer.gjs)
import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  // Это выходной интерфейс плагина, за которым следует пользовательское имя компонента
  api.registerConnectorClass("above-footer", "sponsors", {

    setupComponent(args, component) {
        
     var topMenuRoutes = 
        component.siteSettings.top_menu.split('|')
        .map(function(route) {return '/' + route});
      
     var homeRoute = topMenuRoutes[0];
     
     api.onPageChange((url) => {

        if (url === "/" || url === homeRoute ){ 
          document.querySelector("html").classList.add("sponsors"); 
          component.set("displaySponsors", true); 
        } else {
          document.querySelector("html").classList.remove("sponsors"); 
          component.set("displaySponsors", false); 
        }
      });
    }
    
  });
});

В экспорте также содержится:
about.json (466 Bytes)
что, полагаю, генерируется на лету при экспорте, поэтому я не считаю его релевантным.

Релевантная часть фактического DOM с этой ошибкой:

<div class="custom-footer-content">
    {{#if displaySponsors}}
    <div class="sponsors-wrapper wrap">
      <div class="inner">
        <h3 class="heading">Sponsoren</h3>
        <a href="http://www.asseco-berit.ch/"><img src="{{theme-setting " theme_uploads.asseco_berit"}}"="" alt="Asseco Berit" class="sponsor-image asseco-berit"></a>
        <a href="http://www.geoaargau.ch/"><img src="{{theme-setting " theme_uploads.geoaargau"}}"="" alt="GEOAargau" class="sponsor-image geoaargau"></a>
      </div>
    </div>
  {{/if}}
</div>

здесь видно, что директива управления {{#if ...}}...{{/if}} и плейсхолдеры {{theme-setting "..."}} были использованы как обычный HTML, а не выполнены/оценены/интерполированы.

Как и почему это изменилось, и как мне это исправить?

Возможно, это связано с Upcoming Header Changes - Preparing Themes and Plugins? (Хотя я не вижу упоминания изменений синтаксиса шаблонов там.)

1 лайк

(Под «как и почему» я имею в виду: что именно изменилось на уровне системы, и пропустил ли я какие-либо уведомления об этом? Я достаточно уверен, что причина такого изменения поведения попала в этот экземпляр через автоматические обновления Discourse, но я хотел бы знать корневую причину.)

Есть ли какие-либо ошибки или предупреждения в консоли, которые могли бы предоставить дополнительную информацию?

1 лайк

В консоли браузера? Возможно — я не уверен, что там считается «нормальным», а что может указывать на проблему. Можно ли просто вставить её содержимое сюда или это может привести к утечке конфиденциальной информации?

Да, консоль браузера — её можно вставить :slight_smile: Часто, если что-то устарело, консоль выдаст полезную информацию.

1 лайк
Полное содержимое консоли браузера при загрузке https://www.geowebforum.ch/c/feedback/2
ℹ️ Discourse v2026.3.0-latest — https://github.com/discourse/discourse/commits/38ad2acd2c — Ember v6.6.0 chunk.f47b6dc1cc59c827db42.d41d8cd9.js:275:35231
[PLUGIN discourse-weekly-newsletter] Чтобы избежать ошибок в тестах, добавьте ключ `pluginId` в вызов `modifyClass`. Это обеспечит применение модификации только один раз. chunk.f47b6dc1cc59c827db42.d41d8cd9.js:209:136918
УВЕДОМЛЕНИЕ О УСТАРЕВАНИИ: Определение классов-коннекторов через registerConnectorClass устарело. См. https://meta.discourse.org/t/32727 для более современных подходов. [id устаревания: discourse.register-connector-class-legacy] chunk.f47b6dc1cc59c827db42.d41d8cd9.js:134:74065
Ошибка при парсинге значения для «-webkit-text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:1:800
Ошибка при парсинге значения для «-moz-text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:1:827
Неизвестное свойство «text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:1:849
Неизвестный псевдокласс или псевдоэлемент «-moz-focus-inner». Набор правил проигнорирован из-за некорректного селектора. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:1:1615
Ожидалось «none», URL или функция фильтра, но найдено «alpha(». Ошибка при парсинге значения для «filter». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:622
Набор правил проигнорирован из-за некорректного селектора. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:36117
Ошибка при парсинге значения для «justify-content». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:181379
Ошибка при парсинге значения для «text-wrap». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:205516
Ошибка при парсинге значения для «image-rendering». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:264428
Ошибка при парсинге значения для «-webkit-text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:268491
Ошибка при парсинге значения для «-moz-text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:268518
Неизвестное свойство «text-size-adjust». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:268540
Ошибка при парсинге значения для «font-size». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:298136
Ожидалось «none», URL или функция фильтра, но найдено «alpha(». Ошибка при парсинге значения для «filter». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:447258
Ожидалось «none», URL или функция фильтра, но найдено «alpha(». Ошибка при парсинге значения для «filter». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:4:447681
Ожидалось «none», URL или функция фильтра, но найдено «alpha(». Ошибка при парсинге значения для «filter». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:6:33799
Ошибка при парсинге значения для «text-wrap». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:6:105390
Ошибка при парсинге значения для «text-wrap». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:6:105471
Ошибка при парсинге значения для «padding». Объявление отброшено. common_6dfc7bba61f3a7b80a86f119f30594abc84aaa6d.css:6:257744

Кажется, мне нужно посмотреть Using Plugin Outlet Connectors from a Theme or Plugin

Итак, я думаю, что registerConnectorClass с setupComponent устарел, судя по вашему логу ошибок:

DEPRECATION NOTICE: Defining connector classes via registerConnectorClass is deprecated. See https://meta.discourse.org/t/32727 for more modern patterns. [deprecation id: discourse.register-connector-class-legacy] chunk.f47b6dc1cc59c827db42.d41d8cd9.js:134:74065

Также я не уверен, что мы всё ещё допускаем использование «сырого» HTML, как в вашем компоненте подвала.

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

Что касается вашего другого вопроса:

Это связано с ongoing работой по модернизации Discourse и переходу к новым современным стандартам с использованием компонентов Glimmer.

3 лайка

Если не «сырой» HTML-шаблон, то какой контент ожидается на странице https://www.geowebforum.ch/admin/customize/themes/3/common/footer/edit? Markdown, как в контенте, предоставленном пользователями?

Можете ли вы указать какие-либо руководства о том, как это сделать? Я немного растерян, так как документация по Discourse, похоже, разбросана по всему форуму meta.

Для этого я могу просто распаковать «экспорт» и закоммитить его в репозиторий Git?

Разве мой компонент уже не является компонентом Glimmer, если в экспорте есть файл *.gjs? Или это на самом деле часть проблемы — текущий Discourse ошибочно принимает мой компонент за компонент Glimmer (и поэтому экспортирует его с таким именем файла), хотя с точки зрения кода он ещё не является таковым?

(Пожалуйста, простите мою неосведомлённость. Я не веб-разработчик и почти ничего не знаю об Ember и Glimmer.)

1 лайк

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

Да, файл .gjs действительно указывал на компонент Glimmer, но используемый в нём метод registerConnectorClass больше не работает.

Это вполне понятно. Суть того, что говорится в Using Plugin Outlet Connectors from a Theme or Plugin, заключается в следующем: чтобы отобразить информацию в определённом месте страницы с помощью метода registerConnectorClass, как это делал ваш файл footer.html, теперь используется специальный элемент, называемый «выходом плагина» (plugin outlet). Для футера хорошо подойдёт выход above-footer.

Хорошей идеей будет прочитать Beginner's guide to using Discourse Themes и Developing Discourse Themes & Theme Components.

Также может помочь изучение структуры простого компонента темы, например: GitHub - discourse/discourse-minimal-footer · GitHub.

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

Если вы совсем не хотите возиться с этим, последний вариант — создать тему в канале Marketplace для поиска платной помощи.

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

2 лайка

Вы имеете в виду @discobot?

Нет, это ask.discourse.com.

2 лайка

С помощью указаний здесь, ответов на дополнительный вопрос и некоторой помощи от бота ask.discourse.com мне удалось заставить это работать:

Спасибо всем, особенно @chapoi!

(Приветствуются отзывы о GEOWebforum / Discourse theme component geowebforum-sponsors · GitLab, если у кого-то есть желание. :slightly_smiling_face:)

2 лайка

Пожалуйста, рад, что всё получилось.

Примечание: Если какой-то ответ вам помог, будет приятнее отметить его как решение, а не свой собственный :wink:

1 лайк

Примечание: Если какой-то ответ вам помог, вежливее пометить его как решение, а не свой собственный :wink:

Нет ни одного ответа, который я мог бы выделить как единственное решение, поэтому я счёл более полезным[1] создать новый ответ со ссылкой на рабочий результат и пометить его как «решение». :slight_smile: Я вовсе не собираюсь тем самым лишать заслуг все ответы здесь, которые действительно приближали меня к решению или давали необходимый контекст для понимания проблемы. :sweat_smile:


  1. для справки на будущее, на случай если другие столкнутся с похожими проблемами и найдут эту тему ↩︎

1 лайк

Я так и не подумал! Я понимаю, что вы имеете в виду; я просто считаю, что в целом это более правильный подход, и стараюсь применять его на этом форуме.

Всё в порядке :hugs:

1 лайк