Использование коннекторов Plugin Outlet из темы или плагина

Discourse включает сотни плагинов-выходов (Plugin Outlets), которые можно использовать для вставки нового контента или замены существующего контента в интерфейсе Discourse. Аргументы выхода (Outlet arguments) становятся доступными для настройки контента в зависимости от контекста.

Выбор выхода

Чтобы найти имя плагина-выхода, выполните поиск по ядру Discourse по строке “<PluginOutlet” или используйте тему-компонент расположения плагинов-выходов (например, topic-above-posts).

Выходы-обертки

Некоторые выходы в ядре выглядят как <PluginOutlet @name="foo" />. Они позволяют вставлять новый контент. Другие выходы «оборачивают» существующую реализацию ядра следующим образом:

<PluginOutlet @name="foo">
  реализация ядра
</PluginOutlet>

Определение коннектора для такого типа «оберточного» выхода заменит реализацию ядра. Только одна активная тема/плагин может предоставлять коннектор для оберточного плагина-выхода.

Для оберточных плагинов-выходов вы можете отрендерить исходную реализацию ядра, используя ключевое слово {{yield}}. Это может быть полезно, если вы хотите заменить реализацию ядра только при определенных условиях или если хотите обернуть её во что-то еще.

Определение коннектора

После выбора выхода определите имя для вашего коннектора. Оно должно быть уникальным среди всех установленных тем/плагинов в данном сообществе. Например: brand-official-topics.

В вашей теме/плагине определите новый коннектор .gjs с путем в формате:

:art: {theme}/javascripts/discourse/connectors/{outlet-name}/{connector-name}.gjs

:electric_plug: {plugin}/assets/javascripts/discourse/connectors/{outlet-name}/{connector-name}.gjs

Содержимое этих файлов будет рендериться как компонент Ember. Общую информацию об Ember и формате .gjs можно найти в руководствах Ember.

Для нашего гипотетического коннектора “brand official topics” файл может выглядеть так:

<template>
  <div class="alert alert-info">
    Эта тема была создана участником
    <a href="https://discourse.org/team">команды Discourse</a>
  </div>
</template>

Использование аргументов выхода

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

{{log @outletArgs}}

Это выведет аргументы в консоль разработчика вашего браузера. Они появятся как объект Proxy — чтобы просмотреть список аргументов, раскройте [[Target]] прокси.

В нашем примере topic-above-posts рендеримая тема доступна в @outletArgs.model. Таким образом, мы можем добавить имя пользователя участника команды следующим образом:

<template>
  <div class="alert alert-info">
    Эта тема была создана
    {{@outletArgs.model.details.created_by.username}}
    (участником
    <a href="https://discourse.org/team">команды Discourse</a>)
  </div>
</template>

Добавление более сложной логики

Иногда простого шаблона недостаточно. Чтобы добавить логику JavaScript в ваш коннектор, обновите ваш файл .gjs, экспортировав компонент на основе класса. Он работает так же, как и любое другое определение компонента, и может включать внедрение сервисов.

В нашем примере topic-above-posts мы можем захотеть отображать пользователя по-разному в зависимости от настройки сайта «приоритет имени пользователя в UX». Файл .gjs может выглядеть примерно так:

.../connectors/topic-above-posts/brand-official-topic.gjs:

import Component from "@glimmer/component";
import { service } from "@ember/service";

export default class BrandOfficialTopics extends Component {
  @service siteSettings;

  get displayName() {
    const user = this.args.outletArgs.model.details.created_by;
    if (this.siteSettings.prioritize_username_in_ux) {
      return user.username;
    } else {
      return user.name;
    }
  }

  <template>
    <div class="alert alert-info">
      Эта тема была создана
      {{this.displayName}}
      (участником
      <a href="https://discourse.org/team">команды Discourse</a>)
    </div>
  </template>
}

Условный рендеринг

Если вы хотите, чтобы ваш контент рендерился только при определенных условиях, часто достаточно обернуть ваш шаблон в блок Handlebars {{#if}}. Если этого недостаточно, вы можете использовать хук shouldRender, чтобы контролировать, будет ли ваш шаблон коннектора рендериться вообще.

Сначала убедитесь, что у вас есть коннектор .gjs на основе класса, как описано выше. Затем добавьте статическую функцию static shouldRender(). Расширяя наш пример:

import Component from "@glimmer/component";

export default class BrandOfficialTopics extends Component {
  static shouldRender(outletArgs, helper) {
    const firstPost = outletArgs.model.postStream.posts[0];
    return firstPost.primary_group_name === "team";
  }
  // ... (любая другая логика)

  <template>{{! ... }}</template>
}

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

shouldRender оценивается в контексте автотрекинга Glimmer. Будущие изменения любых ссылаемых свойств (например, outletArgs) вызовут повторную оценку функции.

Создание новых выходов

Если вам нужен выход, которого еще не существует, пожалуйста, создайте pull request или откройте тему в канале Development.


Этот документ находится под версионным контролем — предложите изменения на GitHub.

39 лайков
Using discourse's plugin outlets
What is the best way to integrate member applications?
Add HTML (Link) Next To Logo
Group Semantics
Can I put the search form at the top of our 404 page?
How to show user total post count beside name
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Native theme support
Feedback on "on-discourse" javascript for setting up custom JS for each page?
Topic-timeline api.decorateWidget call has stopped working
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Developing Discourse Themes & Theme Components
How to add btn before "sign in"
Minimizing Maintenance on Theme Customizations
How to add custom fields to models
Tags at the top of the topic list in a Category
I want to insert images (banner) between the topic answers. How do I start?
How to add a link shortcut to the area under the title
Baidu Search
Add Banner/HTML (Widget) before reply button
Upcoming Header Changes - Preparing Themes and Plugins
Upgrading Discourse to Ember 4
Converting modals from legacy controllers to new DModal component API
Need help integrating code wrote on Edittext to the Discourse
Settings not appearing
Add link to external SSO profile to profile page
How to add a custom button in user profile card?
(not recommended) Overriding Discourse templates from a Theme or Plugin
How to override the site-header.hbs file from custom theme?
Upcoming topic-list changes - how to prepare themes and plugins
Working with .erb templates in a plugin
How to Integrate a Custom Plugin in discourse UI
Templating of my "component" broke. How do I fix it?
Templating of my "component" broke. How do I fix it?
Modernizing inline script tags for templates & JS API
Custom Components -- add button or text at any plugin outlet
(not recommended) Overriding Discourse templates from a Theme or Plugin
Adding "latest topics" header in the main interface
Display Tags inline with thread title, instead of being on the bottom line
How to add a custom button in user profile card?
Discourse view file update does not reflect in browser
Discourse view file update does not reflect in browser
Add likes and views to search display
Using template hbs to add HTML content to a plugin outlet
Adding a billing section in the member section
Adding a billing section in the member section
How to modify the header HTML, but still remaining the default founctions
Removing support for "template overrides" and mobile-specific templates
How can i add image in login and register box
Most “traditional” or classic forum Category listing
Newbie help accessing code
How to add custom html next to logo using discourse plugin methods
Using the DModal API to render Modal windows (aka popups/dialogs) in Discourse
Air Theme
How to create a plugin with backend API calls to populate composer while drafting?
How to add a custom url text link on the login page
Add Text In Header Beside Logo
Split up theme Javascript into multiple files

Это уже устарело, верно?
Уведомление об устаревании: Определение классов коннекторов через registerConnectorClass устарело. См. https://meta.discourse.org/t/32727 для более современных паттернов. [id устаревания: discourse.register-connector-class-legacy]

Верно; вы можете вместо этого использовать api.renderInOutlet. :slight_smile:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L982-L1008

1 лайк

Кажется, это немного сложнее, чем просто это :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

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

1 лайк

Спасибо! (Извините за беспорядок в коде, я просто хотел, чтобы он работал, когда я в последний раз к нему прикасался :sweat_smile:)

1 лайк

Я немного запутался. Пришло уведомление о моем компоненте, используемом в файле HEAD темы. Не уверен, как переписать это с помощью api.renderInOutlet.

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Мы используем ajax и модель Topic из Discourse

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container — это плагин-аутлет,
    // featured-topics — имя вашего пользовательского компонента

    setupComponent(args, component) {

   // остальной код следует далее

Пытался заменить api.registerConnectorClass на api.renderInOutlet, но это сломало код. Я не очень разбираюсь в написании тем. Спасибо за любую помощь.

Вы можете посмотреть пример здесь:

StatBanner — это нативный класс, определенный в директории components:

В вашем случае это будет api.renderInOutlet("above-main-container", YourClass).

Я не думаю, что это можно сделать в файле HEAD. Вам следует разделить свой код на несколько файлов.

Я рекомендую использовать Discourse Theme CLI, так как разработка компонента темы станет намного проще!

Ваш компонент темы является общедоступным?

3 лайка

Есть ли способ получить текущий outlet в моём .js-файле?

Я не думаю, что это возможно.

Раньше в классических компонентах можно было проверять parentView.

Но это свойство устарело.

Get glimmer component access to stuff from the parent - #5 by david

1 лайк

Что вы имеете в виду под «получить текущий розетку»? Вы хотите название розетки? Или что-то другое?

Я нашел решение своей проблемы (здесь), но оно было примерно таким:

  • Создайте файлы .hbs и .js для трёх разных outlets.
  • В каждом JS-файле проверьте, совпадает ли используемый outlet со значением настройки banner_location.
  • Если совпадает, покажите баннер. Если нет — скройте его.
1 лайк

Круто! Похоже, вы выбрали использование api.renderInOutlet с динамическим значением для имени выхода :chefs_kiss:

1 лайк