Usando conectores de saída de plugins de um tema ou plugin

O Discourse inclui centenas de Plugin Outlets que podem ser usados para injetar novo conteúdo ou substituir conteúdo existente na interface do usuário do Discourse. ‘Argumentos de outlet’ são disponibilizados para que o conteúdo possa ser personalizado com base no contexto.

Escolhendo um outlet

Para encontrar o nome de um plugin outlet, pesquise no núcleo do Discourse por “<PluginOutlet”, ou use o componente de tema plugin outlet locations. (por exemplo, topic-above-posts).

Outlets wrapper

Alguns outlets no núcleo se parecem com <PluginOutlet @name="foo" />. Eles permitem que você injete novo conteúdo. Outros outlets ‘envolverão’ uma implementação existente do núcleo, assim:

<PluginOutlet @name="foo">
  implementação do núcleo
</PluginOutlet>

Definir um conector para esse tipo de outlet ‘wrapper’ substituirá a implementação do núcleo. Apenas um tema/plugin ativo pode contribuir com um conector para um plugin outlet wrapper.

Para plugin outlets wrapper, você pode renderizar a implementação original do núcleo usando a palavra-chave {{yield}}. Isso pode ser útil se você quiser substituir a implementação do núcleo apenas sob certas condições, ou se desejar envolvê-la em algo.

Definindo o conector

Depois de escolher um outlet, decida um nome para seu conector. Este precisa ser único em todos os temas/plugins instalados em uma determinada comunidade. Por exemplo: brand-official-topics

Em seu tema/plugin, defina um novo conector .gjs com um caminho formatado assim:

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

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

O conteúdo desses arquivos será renderizado como um Componente Ember. Para informações gerais sobre Ember e o formato .gjs, consulte os guias do Ember.

Para nosso conector hipotético “brand official topics”, o arquivo pode parecer assim:

<template>
  <div class="alert alert-info">
    Este tópico foi criado por um membro da
    <a href="https://discourse.org/team">Equipe Discourse</a>
  </div>
</template>

Usando argumentos de outlet

Plugin Outlets fornecem informações sobre o contexto circundante via @outletArgs. Os argumentos passados para cada outlet variam. Uma maneira fácil de visualizar os argumentos é adicionar isso ao seu template:

{{log @outletArgs}}

Isso registrará os argumentos no console de desenvolvedor do seu navegador. Eles aparecerão como um objeto Proxy — para explorar a lista de argumentos, expanda o [[Target]] do proxy.

Em nosso exemplo topic-above-posts, o tópico renderizado está disponível em @outletArgs.model. Então podemos adicionar o nome de usuário do membro da equipe assim:

<template>
  <div class="alert alert-info">
    Este tópico foi criado por
    {{@outletArgs.model.details.created_by.username}}
    (um membro da
    <a href="https://discourse.org/team">Equipe Discourse</a>)
  </div>
</template>

Adicionando lógica mais complexa

Às vezes, um template simples não é suficiente. Para adicionar lógica JavaScript ao seu conector, atualize seu arquivo .gjs para exportar um componente baseado em classe. Isso funciona da mesma forma que qualquer outra definição de componente e pode incluir injeção de serviços.

Em nosso exemplo topic-above-posts, podemos querer renderizar o usuário de forma diferente com base na configuração do site ‘prioritize username in ux’. O arquivo .gjs pode se parecer com algo assim:

.../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">
      Este tópico foi criado por
      {{this.displayName}}
      (um membro da
      <a href="https://discourse.org/team">Equipe Discourse</a>)
    </div>
  </template>
}

Renderização condicional

Se você quiser que seu conteúdo seja renderizado apenas sob certas condições, muitas vezes basta envolver seu template em um bloco {{#if}} do Handlebars. Se isso não for suficiente, você pode querer usar o hook shouldRender para controlar se o template do seu conector será renderizado ou não.

Primeiro, certifique-se de ter um conector .gjs baseado em classe como descrito acima. Em seguida, adicione uma função static shouldRender(). Estendendo nosso exemplo:

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";
  }
  // ... (qualquer outra lógica)

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

Agora o conector será renderizado apenas quando a primeira postagem do tópico tiver sido criada por um membro da equipe.

shouldRender é avaliado em um contexto de autotracking do Glimmer. Alterações futuras em qualquer propriedade referenciada (por exemplo, outletArgs) farão com que a função seja reavaliada.

Introduzindo novos outlets

Se você precisar de um outlet que ainda não existe, sinta-se à vontade para fazer um pull request ou abrir um tópico em Development.


Este documento está sob controle de versão — sugira alterações no GitHub.

39 curtidas
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

Isso agora está descontinuado, certo?
Aviso de descontinuação: A definição de classes de conector via registerConnectorClass está descontinuada. Veja https://meta.discourse.org/t/32727 para padrões mais modernos. [deprecation id: discourse.register-connector-class-legacy]

Correto; você pode usar api.renderInOutlet em vez disso. :slight_smile:

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

1 curtida

Acho que é um pouco mais complicado do que isso :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

Não se preocupe; verei o que posso fazer para te ajudar mais tarde (preciso dormir agora). :smile:

1 curtida

Obrigado! (Desculpe pela bagunça no código, eu só queria que funcionasse da última vez que mexi nele :sweat_smile:)

1 curtida

Estou um pouco sobrecarregado aqui, recebi um aviso para o meu componente usado no arquivo HEAD do meu tema. Não tenho certeza de como reescrever com api.renderInOutlet.

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Estamos usando ajax e o modelo Topic do Discourse

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container é o plugin outlet,
    // featured-topics é o nome do seu componente personalizado

    setupComponent(args, component) {

   // o resto do código continua

Acho que tentei substituir api.registerConnectorClass por api.renderInOutlet, mas deu errado. Eu realmente não sou um especialista em codificação de temas aqui. Agradeço qualquer ajuda.

Você pode ver um exemplo aqui:

StatBanner é uma classe nativa definida no diretório components:

No seu caso, seria api.renderInOutlet("above-main-container", YourClass)

Não acho que você possa fazer isso no arquivo HEAD. Você deve dividir seu código em vários arquivos.

Eu encorajo você a usar o Discourse Theme CLI, pois será muito mais fácil desenvolver um componente de tema!

Seu Componente de Tema é público?

3 curtidas

Existe uma maneira de obter o outlet atual no meu arquivo .js?

Não acho que isso seja possível.

Costumava ser possível inspecionar parentView em Componentes Clássicos.

Mas essa propriedade foi descontinuada.

1 curtida

O que você quer dizer com “obter a saída atual”? Você quer o nome da saída? Ou outra coisa?

Encontrei uma solução para o meu problema (aqui), mas foi algo como isto:

  • Crie arquivos .hbs e .js para 3 outlets diferentes.
  • Em cada arquivo JS, verifique se o outlet que está usando é o valor da configuração banner_location.
  • Se for, mostre o banner. Se não for, oculte o banner.
1 curtida

Legal! Parece que você decidiu usar api.renderInOutlet, com um valor dinâmico para o nome do outlet :chefs_kiss:

1 curtida