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

O Discourse inclui centenas de Plugin Outlets (Pontos de Saída de Plugin) que podem ser usados para injetar novo conteúdo ou substituir conteúdo existente na interface do usuário do Discourse. ‘Argumentos do 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 localizações de plugin outlet. (ex: topic-above-posts).

Wrapper outlets

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

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

Definir um conector para este 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 você gostaria de envolvê-la em algo.

Definindo o template

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. ex: brand-official-topics

No seu tema/plugin, defina um novo template handlebars com um caminho formatado assim:

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

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

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

Para nosso hipotético conector “brand official topics”, o template pode se parecer com

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

Alguns plugin outlets envolverão automaticamente seu conteúdo em um elemento HTML. O tipo de elemento é definido por @connectorTagName no <PluginOutlet>.

Usando argumentos do outlet

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

{{log @outletArgs}}

Isso registrará os argumentos no console do 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 sob @outletArgs.model. Então podemos adicionar o nome de usuário do membro da equipe assim:

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

Adicionando lógica mais complexa

Às vezes, um template handlebars simples não é suficiente. Para adicionar lógica Javascript ao seu conector, você pode definir um arquivo Javascript adjacente ao seu template handlebars. Este arquivo deve exportar uma definição de componente. Isso funciona exatamente como qualquer outra definição de componente, e pode incluir injeções de serviço.

Definir um componente assim removerá o elemento wrapper automático connectorTagName, então você pode querer reintroduzir um elemento do mesmo tipo no seu arquivo hbs.

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’. Uma definição de componente para isso pode se parecer com isto:

.../connectors/topic-above-posts/brand-official-topic.js:

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;
    }
  }
}

Podemos então atualizar o template para referenciar o novo getter:

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

Renderização condicional

Se você deseja que seu conteúdo seja renderizado apenas sob certas condições, geralmente é suficiente envolver seu template com 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 é renderizado.

Primeiro, certifique-se de ter uma definição de conector .js como descrito acima. Em seguida, adicione uma função estática static shouldRender(). Estendendo nosso exemplo:

import Component from "@glimmer/component";
import { getOwner } from "discourse-common/lib/get-owner";

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)
}

Agora o conector só será renderizado quando o primeiro post do tópico foi criado por um membro da equipe.

shouldRender é avaliado em um contexto de autotracking Glimmer. Mudanças em quaisquer propriedades referenciadas (ex: 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 Dev.


Este documento é controlado por 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
Native theme support
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
How to add a custom button in user profile card?
How to add a custom button in user profile card?
Customizing the topic list
(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
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
Display Tags inline with thread title, instead of being on the bottom line
Add link to external SSO profile to profile page
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