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

In a Discourse instance I maintain, I have a component (inherited from the previous maintainer of the instance) to display the logos of the sponsors of that instance in the footer. The component used to work fine until recently, but now is rendered wrongly, displaying the template control directives literally (and putting the template value placeholders literally into the HTML, so that the paths to the images aren’t resolved):

The component isn’t maintained in a git repo, it’s configured directly under “Admin” → “Appearance” → “Themes & components” → tab “Components”:

Custom Code

Custom sections:

  • Common: CSS, Footer, JS

Uploads

Extra files

Export theme to view these files.
  • javascripts/discourse/api-initializers/theme-initializer.gjs

content of those “sections”

as seen when clicking “Edit Code”; path to corresponding files in the export given in parentheses

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;
    }
}
Footer (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) => {
  // This is the plugin outlet, followed by a custom name for the component
  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); 
        }
      });
    }
    
  });
});

The export additionally contains:
about.json (466 Bytes)
which I guess is generated on-the-fly when exporting, so I don’t assume it’s relavant.

The relevant part of the effective DOM with this breakage is:

<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>

so we can see that the control directive {{#if ...}}...{{/if}} and the placeholders {{theme-setting "..."}} were used as literal HTML rather than executed / evaluated / interpolated.

How and why did that change and how should I fix it?

Is it maybe related to Upcoming Header Changes - Preparing Themes and Plugins? (Though I don’t see changes to the templating syntax mentioned there.)

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?

No, it’s 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 лайк