テーマやプラグインからプラグインのアウトレットコネクタを使用する

Discourseには数百のPlugin Outletが含まれており、これらを使用してDiscourseのUIに新しいコンテンツを注入したり、既存のコンテンツを置き換えたりできます。コンテンツをコンテキストに基づいてカスタマイズできるように、「Outlet引数」が利用可能になっています。

Outletの選択

Plugin Outletの名前を見つけるには、Discourseのコアで"<PluginOutlet"を検索するか、plugin outlet locationsテーマコンポーネントを使用してください。(例:topic-above-posts)。

ラッパーOutlet

コア内の一部のOutletは<PluginOutlet @name="foo" />のように見えます。これらは新しいコンテンツの注入を可能にします。他のOutletは、次のように既存のコア実装を「ラップ(囲む)」します。

<PluginOutlet @name="foo">
  core implementation
</PluginOutlet>
```\n
この種の「ラッパー」Outletに対するコネクタを定義すると、コア実装が置き換えられます。ラッパーPlugin Outletに対してコネクタを提供できるのは、アクティブなテーマ/プラグイン1つのみです。

ラッパーPlugin Outletでは、`{{yield}}`キーワードを使用して、元のコア実装をレンダリングできます。これは、コア実装を特定の条件下でのみ置き換えたい場合や、何かでラップしたい場合に役立ちます。

# コネクタの定義

Outletを選んだら、コネクタの名前を決定します。これは、特定のコミュニティにインストールされているすべてのテーマ/プラグインの中で一意である必要があります。例:`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ガイド](https://guides.emberjs.com/release/components/)を確認してください。

我々の仮想的な「brand official topics」コネクタの場合、ファイルは次のようになるかもしれません。

```gjs
<template>
  <div class="alert alert-info">
    This topic was created by a member of the
    <a href="https://discourse.org/team">Discourse Team</a>
  </div>
</template>

Outlet引数の使用

Plugin Outletは、@outletArgsを通じて、周囲のコンテキストに関する情報を提供します。各Outletに渡される引数は異なります。引数を表示する簡単な方法は、テンプレートにこれを追加することです:

{{log @outletArgs}}

これにより、引数がブラウザの開発者コンソールにログ出力されます。Proxyオブジェクトとして表示されます - 引数のリストを調べるには、プロキシの[[Target]]を展開してください。

我々のtopic-above-postsの例では、レンダリングされたトピックは@outletArgs.modelの下で利用可能です。したがって、チームメンバーのユーザー名を次のように追加できます:

<template>
  <div class="alert alert-info">
    This topic was created by
    {{@outletArgs.model.details.created_by.username}}
    (a member of the
    <a href="https://discourse.org/team">Discourse Team</a>)
  </div>
</template>

より複雑なロジックの追加

場合によっては、単純なテンプレートでは不十分です。コネクタにJavaScriptロジックを追加するには、.gjsファイルをクラスベースのコンポーネントのエクスポートにアップグレードします。これは他のコンポーネント定義と同様に機能し、サービスの注入を含めることができます。

我々のtopic-above-postsの例では、「prioritize username in 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 topic was created by
      {{this.displayName}}
      (a member of the
      <a href="https://discourse.org/team">Discourse Team</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";
  }
  // ... (any other logic)

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

これにより、トピックの最初の投稿がチームメンバーによって作成された場合のみ、コネクタがレンダリングされるようになります。

shouldRenderはGlimmerのオートトラッキングコンテキストで評価されます。参照されているプロパティ(例:outletArgs)への将来の変更は、関数が再評価される原因となります。

新しいOutletの導入

まだ存在しないOutletが必要な場合は、プルリクエストを作成するか、#devでトピックを開いてください。


このドキュメントはバージョン管理されています - 変更の提案は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 を使用して書き直す方法がわかりません。

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Discourse の ajax と Topic モデルを使用しています

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container はプラグインのアウトレットです
    // featured-topics はカスタムコンポーネント名です

    setupComponent(args, component) {

   // コードの残りは続きます

api.registerConnectorClassapi.renderInOutlet に置き換えようとしましたが、うまくいきませんでした。テーマのコーディングはあまり得意ではありません。助けていただけると幸いです。

こちらで例を確認できます。

StatBannercomponents ディレクトリで定義されたネイティブクラスです。

この場合、api.renderInOutlet("above-main-container", YourClass) となります。

HEAD ファイルではできないと思います。コードを複数のファイルに分割する必要があります。

開発を容易にするために、Discourse Theme CLI の使用をお勧めします。これにより、Theme コンポーネントの開発がはるかに簡単になります。

Theme コンポーネントは公開されていますか?

「いいね!」 3

.js ファイルで現在のコンセントを取得する方法はありますか?

それは不可能だと思います。

以前は、クラシックコンポーネントで parentView を検査することができました。

しかし、そのプロパティは非推奨になりました。

「いいね!」 1

「現在のコンセントを取得する」とはどういう意味ですか?コンセントの名前が必要ですか?それとも何か他のものですか?

問題の解決策を見つけました(こちら)、それは次のようなものでした。

  • 3つの異なるアウトレット用の .hbs ファイルと .js ファイルを作成します。
  • 各 JS ファイルで、使用しているアウトレットが設定 banner_location の値であるかどうかを確認します。
  • もしそうなら、バナーを表示します。そうでなければ、バナーを非表示にします。
「いいね!」 1

クール!ダイナミックなアウトレット名を持つ api.renderInOutlet を使用することにしたようですね :chefs_kiss:

「いいね!」 1