Utiliser les connecteurs de plugins depuis un thème ou un plugin

Discourse comprend des centaines de Plugin Outlets (points d’extension) qui peuvent être utilisés pour injecter du nouveau contenu ou remplacer du contenu existant dans l’interface Discourse. Des « arguments de sortie » sont mis à disposition afin que le contenu puisse être personnalisé en fonction du contexte.

Choisir un point d’extension

Pour trouver le nom d’un point d’extension, recherchez « <PluginOutlet » dans le code source de Discourse, ou utilisez le composant thème Emplacements des points d’extension. (par exemple topic-above-posts).

Points d’extension enveloppeurs

Certains points d’extension du code source ressemblent à <PluginOutlet @name="foo" />. Ceux-ci vous permettent d’injecter du nouveau contenu. D’autres points d’extension « enveloppent » une implémentation existante du code source comme suit :

<PluginOutlet @name="foo">
  implémentation du code source
</PluginOutlet>

Définir un connecteur pour ce type de point d’extension « enveloppeur » remplacera l’implémentation du code source. Un seul thème/plugin actif peut fournir un connecteur pour un point d’extension enveloppeur.

Pour les points d’extension enveloppeurs, vous pouvez rendre l’implémentation originale du code source en utilisant le mot-clé {{yield}}. Cela peut être utile si vous souhaitez uniquement remplacer l’implémentation du code source dans certaines conditions, ou si vous souhaitez l’envelopper dans autre chose.

Définir le connecteur

Une fois que vous avez choisi un point d’extension, choisissez un nom pour votre connecteur. Ce nom doit être unique parmi tous les thèmes/plugins installés sur une communauté donnée. par exemple brand-official-topics

Dans votre thème/plugin, définissez un nouveau connecteur .gjs avec un chemin formaté comme suit :

:art: {theme}/javascripts/discourse/connectors/{nom-du-point-d-extension}/{nom-du-connecteur}.gjs

:electric_plug: {plugin}/assets/javascripts/discourse/connectors/{nom-du-point-d-extension}/{nom-du-connecteur}.gjs

Le contenu de ces fichiers sera rendu en tant que composant Ember. Pour des informations générales sur Ember et le format .gjs, consultez les guides Ember.

Pour notre connecteur hypothétique « sujets officiels de la marque », le fichier pourrait ressembler à ceci :

<template>
  <div class="alert alert-info">
    Ce sujet a été créé par un membre de l’équipe
    <a href="https://discourse.org/team">Discourse</a>
  </div>
</template>

Utiliser les arguments de sortie

Les Plugin Outlets fournissent des informations sur le contexte environnant via @outletArgs. Les arguments transmis à chaque point d’extension varient. Un moyen simple de visualiser les arguments est d’ajouter ceci à votre modèle :

{{log @outletArgs}}

Cela enregistrera les arguments dans la console de développement de votre navigateur. Ils apparaîtront en tant qu’objet Proxy — pour explorer la liste des arguments, développez le [[Target]] du proxy.

Dans notre exemple topic-above-posts, le sujet rendu est disponible sous @outletArgs.model. Nous pouvons donc ajouter le nom d’utilisateur du membre de l’équipe comme suit :

<template>
  <div class="alert alert-info">
    Ce sujet a été créé par
    {{@outletArgs.model.details.created_by.username}}
    (un membre de l’équipe
    <a href="https://discourse.org/team">Discourse</a>)
  </div>
</template>

Ajouter une logique plus complexe

Parfois, un simple modèle ne suffit pas. Pour ajouter une logique JavaScript à votre connecteur, mettez à niveau votre fichier .gjs pour exporter un composant basé sur une classe. Cela fonctionne exactement comme toute autre définition de composant et peut inclure des injections de services.

Dans notre exemple topic-above-posts, nous pourrions vouloir afficher l’utilisateur différemment en fonction du paramètre de site « privilégier le nom d’utilisateur dans l’UX ». Le fichier .gjs pourrait ressembler à ceci :

.../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">
      Ce sujet a été créé par
      {{this.displayName}}
      (un membre de l’équipe
      <a href="https://discourse.org/team">Discourse</a>)
    </div>
  </template>
}

Rendu conditionnel

Si vous souhaitez que votre contenu ne soit rendu que dans certaines conditions, il suffit souvent d’envelopper votre modèle avec un bloc {{#if}} Handlebars. Si cela ne suffit pas, vous pouvez utiliser le hook shouldRender pour contrôler si le modèle de votre connecteur est rendu du tout.

Tout d’abord, assurez-vous d’avoir un connecteur .gjs basé sur une classe comme décrit ci-dessus. Ensuite, ajoutez une fonction static shouldRender(). En prolongeant notre exemple :

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";
  }
  // ... (toute autre logique)

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

Maintenant, le connecteur ne sera rendu que lorsque le premier message du sujet a été créé par un membre de l’équipe.

shouldRender est évalué dans un contexte de suivi automatique Glimmer. Les changements futurs de toutes les propriétés référencées (par exemple outletArgs) entraîneront la réévaluation de la fonction.

Introduire de nouveaux points d’extension

Si vous avez besoin d’un point d’extension qui n’existe pas encore, n’hésitez pas à faire une demande de tirage (pull request) ou à ouvrir un sujet dans Development.


Ce document est sous contrôle de version - suggérez des modifications sur github.

39 « J'aime »
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

Ceci est maintenant déprécié, n’est-ce pas ?
Avis de dépréciation : La définition des classes de connecteur via registerConnectorClass est dépréciée. Voir https://meta.discourse.org/t/32727 pour des modèles plus modernes. [deprecation id: discourse.register-connector-class-legacy]

C’est exact ; vous pouvez utiliser api.renderInOutlet à la place. :slight_smile:

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

1 « J'aime »

Je pense que c’est un peu plus compliqué que ça :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

Ne t’inquiète pas ; je verrai ce que je peux faire pour t’aider plus tard (je dois dormir maintenant). :smile:

1 « J'aime »

Merci ! (Désolé pour le code qui est un désordre, je voulais juste qu’il fonctionne la dernière fois que j’y ai touché :sweat_smile:)

1 « J'aime »

Je suis un peu dépassé, j’ai reçu une notification concernant mon composant utilisé dans le fichier HEAD de mon thème. Je ne suis pas sûr de la manière de réécrire avec api.renderInOutlet.

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Nous utilisons ajax et le modèle Topic de Discourse

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container est la sortie du plugin,
    // featured-topics est le nom de votre composant personnalisé

    setupComponent(args, component) {

   // le reste du code suit

J’imagine que j’ai essayé de remplacer api.registerConnectorClass par api.renderInOutlet, mais cela a échoué. Je ne suis pas vraiment un expert en codage de thèmes ici. Merci pour toute aide.

Vous pouvez voir un exemple ici :

StatBanner est une classe native définie dans le répertoire components :

Dans votre cas, ce serait api.renderInOutlet("above-main-container", YourClass)

Je ne pense pas que vous puissiez le faire dans le fichier HEAD. Vous devriez diviser votre code en plusieurs fichiers.

Je vous encourage à utiliser Discourse Theme CLI car il sera beaucoup plus facile de développer un composant de thème !

Votre composant de thème est-il public ?

3 « J'aime »

Existe-t-il un moyen d’obtenir la sortie actuelle dans mon fichier .js ?

Je ne pense pas que ce soit possible.

Il était possible d’inspecter parentView dans les composants classiques.

Mais cette propriété a été dépréciée.

1 « J'aime »

Que voulez-vous dire par « obtenir le point de vente actuel » ? Vous voulez le nom du point de vente ? Ou autre chose ?

J’ai trouvé une solution à mon problème (ici), mais c’était quelque chose comme ceci :

  • Créer des fichiers .hbs et .js pour 3 sorties différentes.
  • Dans chaque fichier JS, vérifier si la sortie utilisée correspond à la valeur du paramètre banner_location.
  • Si c’est le cas, afficher la bannière. Sinon, masquer la bannière.
1 « J'aime »

Génial ! Il semble que vous ayez décidé d’utiliser api.renderInOutlet, avec une valeur dynamique pour le nom de la sortie :chefs_kiss:

1 « J'aime »