Verwendung von Plugin-Outlet-Connectors aus einem Theme oder Plugin

Discourse enthält Hunderte von Plugin-Ausgängen (Plugin Outlets), die verwendet werden können, um neue Inhalte einzufügen oder vorhandene Inhalte in der Discourse-Benutzeroberfläche zu ersetzen. „Outlet-Argumente“ werden bereitgestellt, sodass Inhalte basierend auf dem Kontext angepasst werden können.

Einen Ausgang auswählen

Um den Namen eines Plugin-Ausgangs zu finden, suchen Sie im Discourse-Core nach “<PluginOutlet” oder verwenden Sie das Theme-Component Plugin-Ausgangsstandorte. (z. B. topic-above-posts).

Wrapper-Ausgänge

Einige Ausgänge im Core sehen wie <PluginOutlet @name="foo" /> aus. Diese ermöglichen es Ihnen, neue Inhalte einzufügen. Andere Ausgänge „umhüllen“ eine vorhandene Core-Implementierung, wie hier gezeigt:

<PluginOutlet @name="foo">
  Core-Implementierung
</PluginOutlet>

Das Definieren eines Connectors für diese Art von „Wrapper“-Ausgang ersetzt die Core-Implementierung. Nur ein aktives Theme/Plugin kann einen Connector für einen Wrapper-Plugin-Ausgang bereitstellen.

Für Wrapper-Plugin-Ausgänge können Sie die ursprüngliche Core-Implementierung mit dem Schlüsselwort {{yield}} rendern. Dies kann hilfreich sein, wenn Sie die Core-Implementierung nur unter bestimmten Bedingungen ersetzen möchten oder wenn Sie sie in etwas einbetten möchten.

Den Connector definieren

Sobald Sie einen Ausgang gewählt haben, entscheiden Sie sich für einen Namen für Ihren Connector. Dieser muss in allen auf einer bestimmten Community installierten Themes/Plugins eindeutig sein, z. B. brand-official-topics.

Definieren Sie in Ihrem Theme/Plugin einen neuen .gjs-Connector mit einem Pfad, der wie folgt formatiert ist:

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

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

Der Inhalt dieser Dateien wird als Ember-Komponente gerendert. Für allgemeine Informationen zu Ember und dem .gjs-Format schauen Sie sich die Ember-Anleitungen an.

Für unseren hypothetischen „brand official topics“-Connector könnte die Datei so aussehen:

<template>
  <div class="alert alert-info">
    Dieses Thema wurde von einem Mitglied des
    <a href="https://discourse.org/team">Discourse-Teams</a>
    erstellt
  </div>
</template>

Verwendung von Ausgangsargumenten

Plugin-Ausgänge liefern Informationen über den umgebenden Kontext über @outletArgs. Die an jeden Ausgang übergebenen Argumente variieren. Eine einfache Möglichkeit, die Argumente anzuzeigen, besteht darin, dies zu Ihrem Template hinzuzufügen:

{{log @outletArgs}}

Dies protokolliert die Argumente in der Entwicklerkonsole Ihres Browsers. Sie erscheinen als Proxy-Objekt – um die Liste der Argumente zu erkunden, erweitern Sie das [[Target]] des Proxys.

In unserem topic-above-posts-Beispiel ist das gerenderte Thema unter @outletArgs.model verfügbar. Wir können also den Benutzernamen des Teammitglieds wie folgt hinzufügen:

<template>
  <div class="alert alert-info">
    Dieses Thema wurde von
    {{@outletArgs.model.details.created_by.username}}
    (einem Mitglied des
    <a href="https://discourse.org/team">Discourse-Teams</a>)
    erstellt
  </div>
</template>

Hinzufügen komplexerer Logik

Manchmal reicht ein einfaches Template nicht aus. Um Javascript-Logik zu Ihrem Connector hinzuzufügen, aktualisieren Sie Ihre .gjs-Datei, um eine klassenbasierte Komponente zu exportieren. Dies funktioniert genau wie jede andere Komponenten-Definition und kann Service-Injektionen enthalten.

In unserem topic-above-posts-Beispiel möchten wir den Benutzer möglicherweise basierend auf der Site-Einstellung „Priorität des Benutzernamens in UX“ unterschiedlich rendern. Die .gjs-Datei könnte so aussehen:

.../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">
      Dieses Thema wurde von
      {{this.displayName}}
      (einem Mitglied des
      <a href="https://discourse.org/team">Discourse-Teams</a>)
      erstellt
    </div>
  </template>
}

Bedingtes Rendern

Wenn Sie möchten, dass Ihre Inhalte nur unter bestimmten Bedingungen gerendert werden, reicht es oft aus, Ihr Template mit einem Handlebars-{{#if}}-Block zu umschließen. Wenn das nicht ausreicht, können Sie den shouldRender-Hook verwenden, um zu steuern, ob Ihr Connector-Template überhaupt gerendert wird.

Stellen Sie zunächst sicher, dass Sie einen klassenbasierten .gjs-Connector haben, wie oben beschrieben. Fügen Sie dann eine static shouldRender()-Funktion hinzu. Erweiterung unseres Beispiels:

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";
  }
  // ... (andere Logik)

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

Jetzt wird der Connector nur gerendert, wenn der erste Beitrag des Themas von einem Teammitglied erstellt wurde.

shouldRender wird in einem Glimmer-Autotracking-Kontext ausgewertet. Zukünftige Änderungen an beliebigen referenzierten Eigenschaften (z. B. outletArgs) führen dazu, dass die Funktion erneut ausgewertet wird.

Einführung neuer Ausgänge

Wenn Sie einen Ausgang benötigen, der noch nicht existiert, können Sie gerne einen Pull-Request erstellen oder ein Thema in Development eröffnen.


Dieses Dokument ist versioniert – schlagen Sie Änderungen auf GitHub vor.

39 „Gefällt mir“
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

Dies ist jetzt veraltet, richtig?
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]

Das ist richtig; Sie können stattdessen api.renderInOutlet verwenden. :slight_smile:

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

1 „Gefällt mir“

Ich glaube, es ist ein bisschen komplizierter als das :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

Keine Sorge; ich werde sehen, was ich später für Sie tun kann (ich muss jetzt schlafen). :smile:

1 „Gefällt mir“

Danke! (Entschuldigung, dass der Code ein Chaos ist, ich wollte nur, dass er funktioniert, als ich ihn das letzte Mal angefasst habe :sweat_smile:)

1 „Gefällt mir“

Ich stecke hier etwas fest. Ich habe eine Benachrichtigung für meine Komponente erhalten, die in der HEAD-Datei meines Themes verwendet wird. Ich bin mir nicht sicher, wie ich sie mit api.renderInOutlet neu schreiben soll.

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Wir verwenden ajax und das Topic-Modell von Discourse

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container ist der Plugin-Outlet,
    // featured-topics ist der Name Ihrer benutzerdefinierten Komponente

    setupComponent(args, component) {

   // der Rest des Codes folgt

Ich glaube, ich habe versucht, api.registerConnectorClass durch api.renderInOutlet zu ersetzen, aber es hat nicht funktioniert. Ich bin hier kein Experte für Theme-Coding. Danke für jede Hilfe.

Sie können hier ein Beispiel sehen:

StatBanner ist eine native Klasse, die im Verzeichnis components definiert ist:

In Ihrem Fall wäre das api.renderInOutlet("above-main-container", YourClass)

Ich glaube nicht, dass Sie das in der HEAD-Datei tun können. Sie sollten Ihren Code in mehrere Dateien aufteilen.

Ich ermutige Sie, Discourse Theme CLI zu verwenden, da die Entwicklung einer Theme-Komponente damit viel einfacher ist!

Ist Ihre Theme-Komponente öffentlich?

3 „Gefällt mir“

Gibt es eine Möglichkeit, den aktuellen Auslass in meiner .js-Datei zu erhalten?

Ich glaube nicht, dass das möglich ist.

Früher war es möglich, parentView in Classic Components zu inspizieren.

Aber diese Eigenschaft wurde als veraltet markiert.

1 „Gefällt mir“

Was meinen Sie mit „den aktuellen Auslass erhalten“? Meinen Sie den Namen des Auslasses? Oder etwas anderes?

Ich habe eine Lösung für mein Problem gefunden (hier), aber sie sah ungefähr so aus:

  • Erstellen Sie .hbs- und .js-Dateien für 3 verschiedene Auslässe.
  • Überprüfen Sie in jeder JS-Datei, ob der verwendete Auslass dem Wert der Einstellung banner_location entspricht.
  • Wenn ja, zeigen Sie das Banner an. Wenn nicht, blenden Sie das Banner aus.
1 „Gefällt mir“

Cool! Es sieht so aus, als ob Sie sich für die Verwendung von api.renderInOutlet mit einem dynamischen Wert für den Outlet-Namen entschieden haben :chefs_kiss:

1 „Gefällt mir“