在主题或插件中使用插件输出连接器

Discourse 包含数百个插件出口(Plugin Outlets),可用于向 Discourse UI 注入新内容或替换现有内容。“出口参数”(Outlet arguments)的提供使得可以根据上下文对内容进行自定义。

选择出口

要查找插件出口的名称,请在 Discourse 核心代码中搜索 “<PluginOutlet”,或者使用 plugin outlet locations 主题组件。(例如 topic-above-posts)。

包装器出口

核心代码中的一些出口看起来像 <PluginOutlet @name="foo" />。这些允许你注入新内容。其他出口则会像这样“包装”一个现有的核心实现:

<PluginOutlet @name="foo">
  核心实现
</PluginOutlet>

为这种“包装器”出口定义连接器(connector)将会替换核心实现。只有一个活跃的主题/插件可以为一个包装器插件出口提供连接器。

对于包装器插件出口,你可以使用 {{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 指南

对于我们要假设的“品牌官方话题”连接器,该文件可能如下所示:

<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
Add HTML (Link) Next To Logo
What is the best way to integrate member applications?
Group Semantics
Can I put the search form at the top of our 404 page?
Native theme support
How to show user total post count beside name
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
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 以了解更现代的模式。[deprecation 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 重写。\n\n\n\n const ajax = require('discourse/lib/ajax').ajax;\n const Topic = require('discourse/models/topic').default;\n // 我们正在使用 Discourse 的 ajax 和 Topic 模型\n\n api.registerConnectorClass('above-main-container', 'featured-topics', {\n // above-main-container 是插件出口,\n // featured-topics 是你的自定义组件名称\n\n setupComponent(args, component) {\n\n // 剩余代码如下\n\n我猜我尝试用 api.renderInOutlet 替换 api.registerConnectorClass,但它出错了。我在这方面不是主题编码专家。感谢任何帮助。

您可以在此处看到一个示例:

StatBanner 是在 components 目录中定义的本地类:

在您的情况下,那将是 api.renderInOutlet("above-main-container", YourClass)

我不认为您可以在 HEAD 文件中执行此操作。您应该将代码拆分成多个文件

我鼓励您使用Discourse Theme CLI,因为它将使开发 Theme component 更加容易!

您的 Theme Component 是公开的吗?

3 个赞

我的 .js 文件中有什么方法可以获取当前的 outlet 吗?

我认为那是不可能的。

以前在经典组件中可以检查 parentView

但是该属性已被弃用。

1 个赞

您说的“获取当前插座”是什么意思?您想要插座的名称吗?还是其他什么?

我在我的问题 (这里) 中找到了一个解决方案,它大致是这样的:

  • 为 3 个不同的出口创建 .hbs.js 文件。
  • 在每个 JS 文件中,检查它正在使用的出口是否是设置 banner_location 的值。
  • 如果是,则显示横幅。如果不是,则隐藏横幅。
1 个赞

太棒了!看起来你决定使用 api.renderInOutlet,并为 outlet 名称使用了动态值 :chefs_kiss:

1 个赞