Incorporando uma lista de tópicos do Discourse em outro site

Se você baixar as últimas versões do Discourse, terá a capacidade de incorporar listas de tópicos em outros sites por meio de algum JavaScript e HTML simples.

O caso de uso típico para isso é um blog ou outro site orientado a conteúdo, onde você deseja um widget na lateral da tela que liste tópicos. Você pode filtrar por categoria, tag ou qualquer outra opção de filtro pública disponível.

Como incorporar uma lista de tópicos

Primeiro, você deve ativar a configuração do site embed topics list (incorporar lista de tópicos).

Em seguida, em seu HTML, adicione uma tag <script> que inclua o JavaScript necessário para incorporar os tópicos do Discourse. Você pode adicioná-la onde normalmente adiciona scripts. Por exemplo:

<script src="http://URL/javascripts/embed-topics.js"></script>

Substitua URL pelo endereço do fórum, incluindo a subpasta, se existir.

Depois disso, no <body> do seu documento HTML, adicione uma tag d-topics-list para indicar a lista de tópicos que você deseja incorporar. Você também precisará substituir URL pela sua URL base aqui:

<d-topics-list discourse-url="URL" category="1234" per-page="5"></d-topics-list>

Qualquer atributo que você fornecer (exceto discourse-url, que é obrigatório) será convertido em parâmetros de consulta para a pesquisa de tópicos. Então, se você quiser pesquisar tópicos por tag, pode fazer o seguinte:

<d-topics-list discourse-url="URL" tags="cool"></d-topics-list>

Se um parâmetro de consulta tiver underlines, converta-os para hífens. No exemplo acima, você provavelmente notou que per_page se tornou per-page.

Em contextos SameSite (ou seja, o site de incorporação e seu fórum compartilham um domínio principal), o Discourse saberá se você está logado no fórum e exibirá os resultados de acordo. Não se surpreenda se vir categorias seguras e afins quando estiver logado — usuários anônimos não poderão ver!

Lista de parâmetros

template: Pode ser complete ou basic (padrão). Enquanto o basic é apenas uma lista de títulos de tópicos, o complete traz título, nome do usuário, avatar do usuário, data de criação e miniatura do tópico.

per-page: Número. Controla quantos tópicos retornar.

category: Número. Restringe os tópicos a uma única categoria. Passe o id da categoria de destino.

allow-create: Booleano. Se habilitado, a incorporação terá um botão “Novo Tópico”.

tags: String. Restringe os tópicos aos associados a esta tag.

top_period: Um de all, yearly, quarterly, monthly, weekly, daily. Se habilitado, retornará os tópicos “Top” do período.

Exemplos

Criei um site de exemplo aqui:

https://embed.eviltrout.com

Você deve ser capaz de visualizar o código-fonte no seu navegador para ver o código, mas também todo o código-fonte está no GitHub:

Esta é uma funcionalidade totalmente nova, então qualquer feedback ou solicitação será muito apreciada.

Estilizando a lista

Você pode usar nosso recurso de tema existente para adicionar estilos personalizados para a lista incorporada.

Por exemplo, por padrão, nossa lista incorporada usando o modelo complete aparece assim:

Se você quiser que ela se pareça, por exemplo, com uma grade, pode adicionar SCSS personalizado em Tema > Comum > CSS Incorporado:

.topics-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  
  .topic-list-item { 
    .main-link {
      border: 1px dotted gray;
      padding: 0;
    }
  
    .topic-column-wrapper {
      flex-direction: column-reverse;
      
      .topic-column.details-column {
        width: 100%;
      }
        
      .topic-column.featured-image-column .topic-featured-image img {
        max-width: initial;
        max-height: initial;
        width: 100%;
      }
    }
  }
}

O que fará com que ela se pareça com isto:

95 curtidas

E aí, pessoal! Estamos procurando fazer com que os links abram em uma nova aba (ou seja, target=“_blank” em vez de target=“_parent”). Existe uma maneira fácil de fazer isso considerando o iframe?

2 curtidas

Não sei se é isso que você está procurando, mas algo assim deve funcionar para abrir links em novas abas. O CORS precisa estar habilitado para o domínio externo. Tentei dessa forma porque queria filtrar um tópico fixado.

// `fetch` pode exigir um polyfill
const targetEl = document.getElementById("forumTopics");

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</a></li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// adicione `.json` a qualquer lista de tópicos
fetch("https://forum.example.com/latest.json")
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(1, 6)
            .map((t) => [
                t.title,
                `https://forum.example.com/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });
3 curtidas

Obrigado! Gosto dessa ideia, mas com base neste post What are the risks of enabling Cross-origin resource sharing (DISCOURSE_ENABLE_CORS), estou pensando que habilitar o CORS não é a melhor opção.

2 curtidas

Você só deve habilitar o CORS para sites em que confia. Habilitar o CORS para todas as fontes anula o propósito dele.

6 curtidas

Ativei CORS apenas para outros sites que eu controlo. O objetivo era carregar uma lista de tópicos do fórum em nossos outros sites usando código frontend. Esse método provavelmente não funcionaria para permitir que sites aleatórios incorporassem as postagens.

Edição: se você copiar e colar esse código, fique atento ao .slice(1, 6), pois ele remove um dos tópicos. (Acho que era o tópico fixado que eu queria remover.)

3 curtidas

Legal, então isso é algo que podemos tentar. No momento, não tenho nenhum site além do localhost (durante os testes), por isso hesitei em adicioná-lo. Mas se eu conseguir encontrar alguém que tenha um local, podemos tentar. Obrigado pela ajuda!

4 curtidas

Olá @j127

Estamos enfrentando o mesmo problema atualmente e queremos abrir os links em uma nova aba.
Apenas uma pergunta rápida.

Onde exatamente você coloca o código que forneceu?

@eviltrout, alguma ideia do motivo pelo qual, no meu caso, os estilos CSS não estão aparecendo?

2 curtidas

Esse código é apenas um exemplo rápido da ideia geral. Acho que fetch não funciona em todos os navegadores. Posso reescrevê-lo em ES5 se quiser.

Seu site é WordPress ou algum tipo de WordPress headless?

4 curtidas

Parece funcionar em todos os navegadores modernos, e o Discourse não suporta mais o IE11.

4 curtidas

Meu site é apenas um WordPress comum, não headless.

1 curtida

Posso usar isso para incorporar uma lista de tópicos do Discourse em outra instância do Discourse? Ou existe uma maneira mais inteligente de fazer isso?

Meu caso de uso é ter uma ‘ponte’ entre duas instâncias como medida temporária até que elas possam ser mescladas no futuro. Seria ótimo ter isso automatizado e dinâmico.

2 curtidas

Se você não se importa com o IE, então esse trecho deve funcionar. (O CORS precisa estar habilitado para o domínio externo nas configurações do Discourse.)

Para testar, abra qualquer página neste fórum e cole o trecho abaixo no console do navegador. Alterei o elemento de destino para document.body, então ele substituirá toda a página pela lista de tópicos do fórum. Para um site real, basta alterar o elemento de destino para document.getElementById("#someId") e depois colocar um div com esse ID em algum lugar da página. O script o preencherá com a lista de tópicos.

// `fetch` pode exigir um polyfill, a menos que você não se importe com o IE
// const targetEl = document.getElementById("forumTopics");

// isso substitui o corpo da página, apenas como exemplo
const targetEl = document.body;

// coloque a URL do seu fórum aqui para testar no seu próprio fórum
const baseForumURL = `https://meta.discourse.org`;

// quantos tópicos você deseja mostrar
const numTopics = 6;

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// adicione `.json` a qualquer lista de tópicos
fetch(`${baseForumURL}/latest.json`)
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(0, numTopics)
            .map((t) => [
                t.title,
                `${baseForumURL}/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });

Exemplo: se o div de destino em algum lugar do seu site WP for assim

<div id="forumTopics">
    <!-- as postagens do fórum aparecerão aqui -->
</div>

então o JavaScript pode mirar nesse ID assim:

// procure esta linha no exemplo original que postei
const targetEl = document.getElementById("forumTopics");
4 curtidas

Você está se referindo ao código do primeiro post ou ao trecho do post 50? E não, não me importo com o IE. Não há necessidade de atender aquele dinossauro :).
No meu caso, o CSS não está sendo carregado para meu trecho em nenhum navegador.

Então isso é JS e preciso colocar a tag <script> ao redor dele ao implementá-lo no WordPress, correto?

2 curtidas

Já faz um tempo que não uso o WordPress. É algo que você está adicionando ao HTML de um tema? Se sim, envolva meu código em uma tag script e coloque o <div> onde quiser que as postagens apareçam.

Acho que você pode inserir o código abaixo diretamente no HTML de um modelo do WordPress, no local onde deseja que as postagens apareçam. Certifique-se de atualizar a linha que contém a URL do fórum. Se não funcionar, me avise. (Não foi testado.)

Clique aqui para ver o código
<div id="forumTopics"></div>
<script>
// insira a URL do seu fórum aqui para testá-lo no seu próprio fórum
const baseForumURL = `https://forum.exemplo.com`;

// quantos tópicos você deseja mostrar
const numTopics = 6;

const targetEl = document.getElementById("forumTopics");
// Se as postagens não carregarem por algum motivo, você pode mostrar um link para o fórum
targetEl.innerHTML = `<a class="link-to-discourse" href="${baseForumURL}/">Ver as últimas postagens do fórum</a>`;

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</a></li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// adicione `.json` ao final de qualquer lista de tópicos
fetch(`${baseForumURL}/latest.json`)
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(0, numTopics)
            .map((t) => [
                t.title,
                `${baseForumURL}/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });
</script>
3 curtidas

Obrigado, agradeço o trecho completo.

Após o teste inicial, apenas um link é exibido. Mas acho que o CORS está desabilitado, então vou pedir ao meu provedor de hospedagem para ativá-lo.
Enquanto isso, como posso fazer com que o script exiba apenas posts com uma tag específica e de uma categoria específica?

2 curtidas

Não tenho certeza, mas acho que o formato é
https://forum.example.com/tags/c/<nome_da_categoria>/<nome_da_tag>

Exemplo: a categoria é “suporte” e a tag é “css”:
https://meta.discourse.org/tags/c/support/css

Para convertê-lo em JSON, adicione .json no final:
https://meta.discourse.org/tags/c/support/css.json

Então, na função fetch, em vez de /latest.json, use algo como /tags/c/support/css.json.

(Não testado.)

3 curtidas

E se eu quiser incorporar 5 tópicos na própria instância do Discourse?

Crio um tópico, defino como wiki e o publico como uma página. Como posso incorporar os cinco tópicos mais recentes de uma categoria ou tag específica, ou usar o modelo que esse recurso suporta?

Obrigado.

1 curtida

Qual é o parâmetro de consulta para os tópicos principais? Tentei order="top", mas não está retornando os tópicos principais da mesma forma que o fórum, com base em um período específico. Como posso replicar o resultado do filtro do fórum? Onde posso consultar os valores de atributo que as opções de filtro do Discourse aceitam? Obrigado!

2 curtidas

É possível alterar o estilo da fonte do conteúdo incorporado? Tive seletores CSS como d-topics-list iframe html .topics-list .topic-list-item sem sucesso. Agradeço desde já pela orientação!

2 curtidas