Theme-Component v Plugin: Qual a diferença

Alguém pode esclarecer a diferença entre esses três conceitos do Discourse: theme-component, plugin e pluginAPI?

Especialmente entre theme-component e plugin. Se eu quiser personalizar meu fórum, como sei qual deles devo construir?

(desculpe se perdi essa distinção no intro, mas não a estou vendo lá)

3 curtidas

Não sou iniciante no Discourse, mas também não sou um especialista.

  1. O componente de tema usa HTML, CSS e JavaScript para aprimorar um tema base.
    Note que falo em tema base, pois geralmente é chamado apenas de “tema” e, às vezes, as pessoas não fazem essa distinção, sendo necessário inferir. Um tema e/ou componente de tema pode ser instalado por um administrador sem precisar tirar o site do ar. Se você for um cliente do Discourse, também pode adicionar esses componentes. (lista) Veja também: Guia para iniciantes sobre o uso de temas no Discourse

  2. Um plugin usa Ruby e pode fazer praticamente tudo que é possível. Se você for um cliente do Discourse, terá um conjunto limitado de plugins que podem ser ativados. No entanto, se estiver hospedando seu próprio servidor, poderá adicionar quantos quiser, mas fique avisado: vejo muitos posts relatando que plugins personalizados quebram o site durante atualizações. Esses também não exigem reinicialização ao serem ativados; suspeito que uma reinicialização possa ser necessária na primeira instalação. Outros podem complementar, pois minha única experiência com plugins foi ativá-los pelos menus de administração. (lista) Veja também: Guia para iniciantes sobre a criação de plugins no Discourse - Parte 1

  3. Como não desenvolvi um plugin, meu palpite é que você esteja se referindo ao Discourse API Ruby Gem. Veja: Use the Discourse API ruby gem

  4. Existe também a API, que utiliza webhooks e é normalmente usada com o curl ou outra linguagem de programação. Isso é bom porque te liberta da necessidade de usar Ruby.

  5. Embora eu também não tenha mexido com isso, você poderia programar no nível do banco de dados PostgreSQL, mas não recomendo, a menos que seja muito habilidoso e tenha muita confiança nas suas capacidades.

Espero ter ajudado.


EDIT

Bônus, se quiser se dedicar totalmente como desenvolvedor do Discourse

Veja: Como começar a criar coisas para o Discourse se você é iniciante (como eu)

7 curtidas

Para complementar a resposta de @EricGT, que já faz um ótimo trabalho explicando:

  • Um tema ou componente de tema é essencialmente uma maneira de modificar qualquer parte do aplicativo front-end EmberJS do Discourse. Isso pode ser tão simples quanto personalizar HTML ou CSS, ou tão complexo quanto adicionar novas funcionalidades. Temas são muito mais elegantes em caso de falha, o que significa que todo o seu site não necessariamente ficará fora do ar se algo não funcionar.
  • Um plugin afeta principalmente o aplicativo Rails no lado do servidor, mas também inclui todo o poder de um tema e a capacidade de modificar o aplicativo EmberJS, embora seja muito mais complexo. Falhas em plugins tendem a ser menos elegantes, então, se você puder construir algo em um tema, comece por aí. No entanto, um plugin é necessário se você precisar de uma rota personalizada ou de armazenar dados.
  • A pluginAPI é uma API no lado do cliente que temas ou componentes de tema podem usar para modificar mais facilmente partes específicas do cliente do Discourse.

O melhor lugar para começar a personalizar seu site é com um tema. Aqui estão alguns recursos:

Guia do Designer para Temas do Discourse
Guia do Desenvolvedor para Temas do Discourse
Guia para Iniciantes sobre o uso do Theme Creator e Theme CLI para começar a criar um tema do Discourse

10 curtidas

Obrigado, pessoal. Isso é útil. Acho que a distinção chave que estou percebendo é:
–se você quiser mudar algo que só envolve o front-end, crie um tema.
–se você quiser mudar algo que requer interação com o back-end, crie um plugin.

Faz sentido?

Aqui está um exemplo concreto que tenho em mente e estou tentando resolver: quero permitir que qualquer moderador de categoria fixe tópicos nessa categoria. De forma geral, acho que os passos são:

  1. Verificar se o usuário é moderador da categoria (isso requer acessar o back-end para obter informações sobre o usuário e a categoria)

  2. Se o usuário for moderador, mostrar o botão de fixar (isso é front-end)

  3. Se o usuário clicar no botão de fixar, levar o tópico para o topo (não tenho certeza de onde isso ocorre no código do Discourse — talvez seja tanto no front-end quanto no back-end?)

Neste caso, como preciso interagir com o back-end (provavelmente no passo 1, e talvez também no 3?), eu teria que usar um plugin. Isso está correto?

2 curtidas

A parte superficial pode ser essa, mas envolverá o back-end, pois está relacionada a permissões e segurança. Você não pode deixar o front-end decidir quais privilégios alguém possui.

1 curtida

Você quer dizer que responder programaticamente “Este usuário é moderador desta categoria” envolverá múltiplos arquivos entre frontend e backend? Hmm…

1 curtida

Em um tema, você deve ser capaz de saber se um usuário é moderador e também fazer chamadas ao backend, se a API do Discourse expuser um endpoint que possa ser chamado pelo front-end (afinal, o tema pode usar JavaScript). Portanto, você provavelmente não precisará de um plugin para [1]. Você só precisaria se precisar alterar o comportamento do backend ou expor uma API.

Mas você provavelmente precisará para [3], porque, como @merefield disse, isso está relacionado a permissões e segurança (se o backend bloquear um moderador de fixar um tópico, você teria que alterar para começar a permitir).

Como disse acima, provavelmente não será necessário um plugin apenas para fazer essa validação (saber se o usuário é moderador ou não), mas provavelmente será necessário devido à ação de permitir que ele fixe a categoria. Se o Discourse tiver uma opção para permitir que qualquer moderador fixe um tópico (não sei se tem), então você não precisaria de um plugin (mas provavelmente também não precisaria de um tema, a menos que o botão de fixar não seja exibido para moderadores, e você use um tema apenas para mostrá-lo aos moderadores e chamar o endpoint em JavaScript quando ele clicar no botão de fixar).

2 curtidas

Isso é muito útil em relação a temas vs. plugins e também sobre o exemplo específico que mencionei. Obrigado.

Até agora, minha abordagem para introduzir alterações tem sido analisar os detalhes do código do Discourse (onde esse objeto é definido, o que controla esse template, onde está a lógica que trata dessa ação, etc.). Mas isso tem sido um processo lento.

Estou pensando que provavelmente uma rota mais eficiente seria focar na API. Assim, não preciso analisar todos os detalhes do código maduro do Discourse e posso me concentrar em construir um tema em vez de um plugin — ou talvez apenas fazer alterações no painel de “personalização”.

Basicamente, entender como a API funciona parece muito mais viável do que entender a base de código do Discourse.

Para manter o exemplo que mencionei: se o usuário for moderador de uma categoria, permitir que esse usuário fixe tópicos na página da categoria.

Seria possível fazer isso sem um plugin? Deixe-me ver se consigo esboçar:

1. O usuário é moderador de uma categoria?

Atualmente, não vejo nada na API que indique se um usuário é moderador de uma categoria. Esperaria encontrar isso em uma chamada GET para recuperar uma categoria, mas não vejo tal chamada lá. Ou talvez na chamada GET para recuperar um usuário, mas lá não vejo uma lista das categorias das quais o usuário é moderador.

Seria possível adicionar essas informações?

Ou, alternativamente, talvez eu pudesse criar um campo personalizado no usuário ou na categoria para identificar que ele é moderador e, em seguida, fazer uma chamada de API para esse campo personalizado quando a página da categoria for carregada.

2. Se o usuário for moderador, mostrar o botão de fixar.

Se eu puder responder à pergunta (1), então assumo que posso simplesmente adicionar esse botão com JavaScript e CSS no front-end, exibindo-o apenas se o usuário for moderador.

3. O usuário (que é moderador) clica no botão, e isso fixa o tópico.

Na API, os tópicos parecem ter uma característica “pinned” (um booleano). Assumo que isso corresponda a saber se estão fixados em sua categoria, já que parece que cada tópico tem apenas uma categoria.

Então, aqui, quando o moderador clicar no botão “fixar”, provavelmente poderei atualizar o status “pinned” do tópico para True. Se isso não funcionar, campos personalizados também poderiam ser uma solução aqui (embora eu não esteja vendo como adicionar campos personalizados a um tópico).


Com isso, ou algo semelhante, parece que eu poderia realizar essa tarefa usando a API, o que, se eu fizesse com um plugin, exigiria muito tempo analisando os arquivos da base de código do Discourse.

Isso faz sentido?

1 curtida

Você já encontrou: Como fazer engenharia reversa na API do Discourse

1 curtida

Já vi isso antes, mas estou dando uma olhada mais de perto agora. Obrigado pela lembrança.

Estou tentando organizar:
–onde na API eu obteria a informação sobre quem é o moderador de uma determinada categoria (não estou vendo isso nas informações retornadas sobre categorias ou usuários—obviamente, tem que estar em algum lugar)

–é possível usar a API para adicionar novos campos a uma entrada? Por exemplo, poderia usar a API para adicionar um campo personalizado a um tópico? (Não acho que tópicos geralmente venham com campos personalizados)

1 curtida

Eu procuraria no banco de dados.

O banco de dados possui uma tabela posts e você poderia adicionar um campo lá, mas, nesse caso, você sairia do ambiente oficial e não receberia suporte.

1 curtida

Obrigado — tudo isso é muito útil.

A qual banco de dados você se refere?


Para confirmar, minha ideia aqui seria usar a API para fazer parte desse trabalho que, de outra forma, exigiria um plugin. Por exemplo, meu próprio site faria uma chamada à API para determinar se um usuário (ou grupo, conforme o caso) é moderador de uma categoria. A chamada estaria codificada diretamente no painel de personalização, ou em um tema ou plugin.

Para fazer esse tipo de chamada, precisarei estar autenticado, o que, acredito, exige a criação de uma chave de API no painel de administração. O processo de criação de API no painel de administração requer uma “Descrição” e um “Nível de Usuário” — não tenho certeza de como isso se aplica aqui. No meu caso, quero que meu aplicativo faça a chamada à API. Ela não está sendo feita em nome de um usuário específico. Então, posso estar entendendo errado.

Você sabe qual é o “Nível de Usuário” apropriado para esse tipo de chamada à API ou o que devo inserir nesse campo?

1 curtida

Quando o Discourse é instalado conforme o padrão, ele é executado em um container Docker. A persistência de dados é feita por meio de um banco de dados PostgreSQL.

Veja: Plugin Data Explorer

Se você tiver privilégios de administrador, pode baixar um dos backups, que é um arquivo SQL comprimido em um tar.gz. Você pode usar o SQL para recarregar os dados em outro banco de dados PostgreSQL e fazer ainda mais.

2 curtidas

Nunca criei um tema, um plugin ou utilizei a API a partir do Ruby, nem com qualquer outra linguagem de programação. Tenho acesso de administrador em um site de produção, foi assim que acessei o backup. Também programo em Prolog, que é como estou acessando os dados e usando-os para analisar as postagens com DCGs. Se você conhece BNF, as DCGs não são muito diferentes, mas é preciso entender a unificação sintática e o encadeamento reverso para as partes mais sofisticadas.

1 curtida

Obrigado. Vou tratar desse item separadamente.

1 curtida

Não, pois os direitos de acesso para cada ação são aplicados pelo servidor.

Você deve verificar a sobrescrita das funções em lib/guardian/ e lib/guardian.rb para permitir que moderadores específicos de categoria fixem tópicos e, em seguida, use os mesmos mecanismos do JS do Tema (exceto no plugin) para alterar a interface do usuário, de modo que a opção “fixar tópico” apareça quando apropriado.

1 curtida

Ahh, isso faz sentido. Então, parece que usar a API para isso não funcionaria (sua resposta pode me poupar muito tempo).

Eu poderia tentar fazer isso de um jeito um pouco diferente do comportamento normal de fixação. Em vez disso, eu daria a certos usuários direitos de “propriedade” sobre uma categoria, o que permitiria que eles destacassem certos tópicos naquela categoria.

A forma como eu faria isso com a API JSON seria: a partir do meu painel de personalização, atribuir aos usuários relevantes um campo personalizado (como category-name: owner) ou algo assim. E então, quando a página da categoria carregar, chamar a API para avaliar o usuário; se eles tiverem esse campo personalizado, mostrar o botão “destacar”, e então, se clicarem no botão “destacar” para um tópico, atribuir esse tópico ao grupo de destaques da categoria (também um campo personalizado para essa categoria).

Este é um esboço grosseiro — não há necessidade de confirmar cada etapa detalhadamente (quando eu codificar, talvez precise ajustar de qualquer forma). Mas minha pergunta por enquanto: essa maneira de usar a API JSON — especialmente para criar e recuperar campos personalizados dentro do meu aplicativo Discourse — funciona?

1 curtida

Prezado @JQ331,

Mas minha pergunta por enquanto: esse modo de usar a API JSON — especialmente para criar e recuperar campos personalizados dentro do meu aplicativo Discourse — funciona?

Houve excelentes respostas às suas perguntas sobre as diferenças entre um componente de tema, um plugin e a API do Discourse, e duvido que eu possa agregar muito mais valor; mas aqui está outra forma de ver isso, que você pode ou não achar útil:

Tanto os componentes de tema quanto os plugins usam ganchos de template (ganchos de plugin) para executar código no ciclo de vida do Ember.js (o que é bom aprender, aliás).

Além disso, a API do Discourse também está disponível tanto para componentes de tema quanto para plugins.

A API expõe basicamente um subconjunto, mas não tudo, dos dados do banco de dados PostgreSQL subjacente.

Ao desenvolver funcionalidades, seria uma boa ideia começar pela API e verificar se os dados de que você precisa estão disponíveis nela.

Se houver algum dado necessário que não esteja na API, você precisará examinar o banco de dados PostgreSQL para ver se esses dados existem nele.

Se os dados adicionais de que você precisa existirem no banco de dados, então você precisará expor esses dados, e, em geral, isso significa adicionar dados ao serializador de dados do Discourse e estender a API.

O serializador de dados é simplesmente o processo de criar o objeto JSON que é exposto pela API. Isso pode ser estendido para adicionar mais objetos.

Para examinar o banco de dados PostgreSQL, imagino que existam várias formas de fazer isso (lendo o código no GitHub, por exemplo), mas eu faço isso acessando diretamente o banco de dados, analisando as tabelas, estudando a estrutura dessas tabelas e verificando quais campos estão em cada tabela (usando SQL básico).

Portanto, para resumir (e manter isso breve), precisamos ter tanto a API quanto as tabelas do banco de dados como referências. Em geral, quando você deseja “estender a API” adicionando dados que não são fornecidos pela API nativamente (OOTB), criamos um plugin para isso; e então esses dados serão expostos junto com a API (estendida), permitindo que sejam utilizados tanto em componentes de tema quanto em plugins.

Espero que essa perspectiva tenha sido útil ou tenha ajudado de alguma forma.

6 curtidas

Explicação excepcional. Muito obrigado por essa resposta. Isso destaca algo que eu não havia percebido antes:

A partir dessas respostas, estou concluindo que (como você diz) interagir com a API JSON pode ser um bom ponto de partida em muitos casos, o que poderia evitar a necessidade de criar um novo tema ou plugin. No entanto, existem alguns tipos de dados que não são expostos pela API. Para acessar e trabalhar com esses tipos de dados, você precisaria usar o serializador de dados do Discourse para expor essas informações; e para fazer essa serialização, é necessário usar um plugin.

Parece que um bom exemplo de dados não disponíveis através da API são os proprietários de um grupo. Digo isso porque (em relação ao acesso aos proprietários de grupo):

Um ponto de confusão: na API do Discourse, quando você obtém um grupo específico, uma das características retornadas é listada como "is_group_owner": true, então não tenho certeza do que isso significa…

Mas parece que, para obter o proprietário do grupo, eu precisaria serializar a característica do proprietário do grupo.


Existem bons exemplos de uso do serializador do Discourse? Já vi este, mas, dada sua importância, um tutorial com alguns exemplos seria extremamente útil.

O exemplo mais próximo que tenho é:

Isso é útil, mas não está totalmente correto (pelo menos me dá erros dizendo “plugin inválido”). Não tenho certeza de como ajustá-lo para que, na página de índice de grupos, eu possa acessar os proprietários de cada grupo.

3 curtidas

Não tenho certeza de como você tentou usar o exemplo do plugin, mas consegui executá-lo com sucesso em uma instância de desenvolvimento ao usar a estrutura de arquivos e o código que publiquei em seu outro tópico.

is_group_owner é usado no contexto do usuário atual visualizando os grupos.

Você pode obter as informações do proprietário por meio de uma chamada AJAX, mas, até onde sei, isso precisaria ser feito para cada grupo individual na lista, o que potencialmente resultaria em muitas solicitações. Acredito que, no geral, seria desafiador fazer isso funcionar usando esse método. De qualquer forma, se quiser experimentar, pode tentar o seguinte trecho de código de conceito de prova em um tema. Basta substituir GROUP_NAME pelo nome de um dos seus grupos. (EDIT: Aqui também está um componente de tema com um exemplo de como uma chamada AJAX pode ser utilizada: discourse-featured-topics/common/head_tag.html at master · awesomerobot/discourse-featured-topics · GitHub)

<script type="text/discourse-plugin" version="0.8.40">
  const { ajax } = require("discourse/lib/ajax");
  ajax(`/groups/GROUP_NAME/members.json`).then(response => {
    console.log(response.owners.map(owner => owner.username))
  });
</script>

Dito tudo isso, usar um plugin seria definitivamente a solução mais fácil e limpa.

1 curtida