Incrustar una lista de temas de Discourse en otro sitio

Si descargue las últimas versiones de Discourse, obtendrá la capacidad de incrustar listas de temas en otros sitios mediante un sencillo Javascript y HTML.

El caso de uso típico para esto es un blog u otro sitio impulsado por contenido, donde desea un widget en el lateral de la pantalla que liste los temas. Puede filtrar por categoría, etiqueta o cualquiera de las otras opciones de filtrado públicas disponibles.

Cómo incrustar una lista de temas

Primero, debe habilitar la configuración del sitio embed topics list (incrustar lista de temas).

Luego, en su HTML, agregue una etiqueta <script> que incluya el Javascript necesario para incrustar los temas de Discourse. Puede agregar esto donde normalmente agrega scripts. Por ejemplo:

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

Reemplace URL con la dirección del foro, incluida la subcarpeta si existe.

Después de eso, en el <body> de su documento HTML, agregue una etiqueta d-topics-list para indicar la lista de temas que desea incrustar. También deberá reemplazar URL con su URL base aquí:

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

Cualquier atributo que proporcione (excepto discourse-url, que es obligatorio) se convertirá en los parámetros de consulta para la búsqueda de temas. Por lo tanto, si desea buscar temas por etiqueta, podría hacer lo siguiente:

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

Si un parámetro de consulta tiene guiones bajos, conviértalo a guiones. En el ejemplo anterior, probablemente notó que per_page se convirtió en per-page.

En contextos SameSite (es decir, el sitio de incrustación y su foro comparten un dominio principal), Discourse sabrá si ha iniciado sesión en el foro y mostrará los resultados en consecuencia. No se sorprenda si ve categorías seguras y similares cuando ha iniciado sesión; ¡los usuarios anónimos no podrán verlas!

Lista de parámetros

template: complete o basic (predeterminado). Mientras que basic es solo una lista de títulos de temas, complete incluye título, nombre de usuario, avatar del usuario, fecha de creación y miniatura del tema.

per-page: Número. Controla cuántos temas devolver.

category: Número. Restringe los temas a una sola categoría. Pase el id de la categoría objetivo.

allow-create: Booleano. Si está habilitado, la incrustación tendrá un botón “Nuevo tema”.

tags: Cadena. Restringe los temas a aquellos asociados con esta etiqueta.

top_period: Uno de all, yearly, quarterly, monthly, weekly, daily. Si está habilitado, devolverá los temas “Top” del período.

Ejemplos

He creado un sitio de ejemplo aquí:

https://embed.eviltrout.com

Debería poder ver el código fuente en su navegador para ver el código, pero también todo el código fuente está en GitHub:

Esta es una función completamente nueva, por lo que cualquier comentario o solicitud sería muy apreciado.

Estilizando la lista

Puede utilizar nuestra función de temas existente para agregar estilos personalizados para la lista incrustada.

Por ejemplo, de forma predeterminada, nuestra lista incrustada que usa la plantilla complete se ve así:

Si desea que se vea, por ejemplo, como una cuadrícula, puede agregar SCSS personalizado a Tema > Común > CSS Incrustado:

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

Lo que hará que se vea así:

95 Me gusta

¡Hola a todos! Estamos buscando que los enlaces se abran en una nueva pestaña (es decir, target=“_blank” en lugar de target=“_parent”). ¿Hay alguna forma sencilla de hacerlo considerando el iframe?

2 Me gusta

No sé si es lo que buscas, pero algo así debería funcionar para abrir los enlaces en nuevas pestañas. Debes habilitar CORS para el dominio externo. Lo intenté de esta manera porque quería filtrar un tema fijado.

// `fetch` podría requerir un 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>`;
}

// añade `.json` a cualquier lista de temas
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 Me gusta

¡Gracias! Me gusta esta idea, pero basándome en este post What are the risks of enabling Cross-origin resource sharing (DISCOURSE_ENABLE_CORS) creo que habilitar CORS no es la mejor opción.

2 Me gusta

Solo debes habilitar CORS para sitios en los que confíes. Habilitar CORS para cualquier origen anula su propósito.

6 Me gusta

Solo habilité CORS para otros sitios que controlo. El objetivo era cargar una lista de temas del foro en nuestros otros sitios con código frontend. Ese método probablemente no funcionaría para permitir que sitios aleatorios incrusten las publicaciones.

Edición: si copias y pegas ese código, ten cuidado con .slice(1, 6) porque elimina uno de los temas. (Creo que era el tema fijado que quería eliminar.)

3 Me gusta

Genial, entonces esto es algo que podemos probar. Por ahora no tengo ningún sitio más que localhost (durante las pruebas), por eso dudaba en agregarlo. Pero si puedo encontrar a alguien que tenga un lugar, podemos intentarlo. ¡Gracias por tu ayuda!

4 Me gusta

Hola @j127

Actualmente estamos enfrentando el mismo problema y queremos abrir los enlaces en una nueva pestaña.
Solo una pregunta rápida.

¿Dónde exactamente colocas el código que proporcionaste?

@eviltrout ¿Tienes alguna idea de por qué, en mi caso, los estilos CSS no aparecen?

2 Me gusta

Ese código es solo un ejemplo rápido de la idea general. No creo que fetch funcione en todos los navegadores. Puedo reescribirlo en ES5 si lo deseas.

¿Tu sitio es WordPress o algún tipo de WordPress headless?

4 Me gusta

Parece funcionar en todos los navegadores modernos, y Discourse ya no admite IE11.

4 Me gusta

Mi sitio es simplemente WordPress normal, no headless.

1 me gusta

¿Puedo usar esto para incrustar una lista de temas de Discourse en otra instancia de Discourse? ¿O hay una forma más inteligente de hacerlo?

Mi caso de uso es tener un “puente” entre dos instancias como medida temporal hasta que puedan fusionarse en el futuro. Sería ideal que esto fuera automático y dinámico.

2 Me gusta

Si no te importa IE, ese fragmento debería funcionar. (CORS debe estar habilitado para el dominio externo en la configuración de Discourse.)

Para probarlo, abre cualquier página en este foro y pega el siguiente fragmento en la consola del navegador. He cambiado el elemento objetivo a document.body, por lo que reemplazará toda la página con la lista de temas del foro. Para un sitio real, simplemente cambia el elemento objetivo a document.getElementById("#someId") y luego coloca un div con ese ID en algún lugar de la página. El script lo rellenará con la lista de temas.

// `fetch` podría requerir un polyfill a menos que no te importe IE
// const targetEl = document.getElementById("forumTopics");

// esto reemplaza el cuerpo de la página, solo como ejemplo
const targetEl = document.body;

// pon aquí la URL de tu foro para probarlo en tu propio foro
const baseForumURL = `https://meta.discourse.org`;

// cuántos temas quieres 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>`;
}

// añade `.json` al final de cualquier lista de temas
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);
    });

Ejemplo: si el div objetivo en algún lugar de tu sitio WP se ve así

<div id="forumTopics">
    <!-- las publicaciones del foro aparecerán aquí -->
</div>

entonces el JavaScript podría apuntar a ese ID así:

// busca esta línea en el ejemplo original que publiqué
const targetEl = document.getElementById("forumTopics");
4 Me gusta

¿Con eso te refieres al código en el primer mensaje o al fragmento del mensaje 50? Y no, no me importa IE. No hay necesidad de adaptarse a ese dinosaurio :).
En mi caso, el CSS no se carga para mi fragmento en ningún navegador.

Entonces, esto es JS y tengo que poner la etiqueta <script> alrededor al implementarlo en WordPress, ¿correcto?

2 Me gusta

Ha pasado un tiempo desde que usé WordPress. ¿Es algo que estás agregando al HTML de un tema? Si es así, envuelve mi código en una etiqueta script y coloca el <div> donde quieras que aparezcan los publicaciones.

Creo que puedes insertar el código de abajo directamente en el HTML de una plantilla de WordPress en cualquier lugar donde quieras que aparezcan las publicaciones. Asegúrate de actualizar la línea que contiene la URL del foro. Si no funciona, avísame. (No ha sido probado).

Haz clic aquí para ver el código
<div id="forumTopics"></div>
<script>
// coloca la URL de tu foro aquí para probarlo en tu propio foro
const baseForumURL = `https://forum.ejemplo.com`;

// cuántos temas quieres mostrar
const numTopics = 6;

const targetEl = document.getElementById("forumTopics");
// Si las publicaciones no se cargan por alguna razón, podrías mostrar un enlace al foro
targetEl.innerHTML = `<a class="link-to-discourse" href="${baseForumURL}/">Ver las últimas publicaciones del foro</a>`;

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

// agrega `.json` al final de cualquier lista de temas
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 Me gusta

Gracias, aprecio el fragmento completo.

Tras la prueba inicial, solo se muestra un enlace. Pero creo que CORS está desactivado, así que le pediré a mi proveedor de alojamiento que lo habilite.
Mientras tanto, ¿cómo puedo hacer que el script muestre solo publicaciones con una etiqueta específica y de una categoría concreta?

2 Me gusta

No estoy seguro, pero creo que el formato es
https://forum.example.com/tags/c/<nombre_de_categoria>/<nombre_de_etiqueta>

Ejemplo: la categoría es “soporte” y la etiqueta es “css”:
https://meta.discourse.org/tags/c/support/css

Para convertirlo a JSON, agrega .json al final:
https://meta.discourse.org/tags/c/support/css.json

Así que en la función fetch, en lugar de /latest.json, usa algo como /tags/c/support/css.json.

(No probado.)

3 Me gusta

¿Y si quiero incrustar 5 temas en la propia instancia de Discourse?

Creo un tema. Lo convierto en un wiki. Lo publico como una página. ¿Cómo puedo incrustar los cinco temas más recientes de una categoría o etiqueta específica, o utilizar la plantilla que admite esta función?

Gracias.

1 me gusta

¿Cuál es el parámetro de consulta para los temas principales? Intenté order="top", pero no devuelve los temas principales de la misma manera que el foro según un período determinado. ¿Cómo puedo replicar el resultado del filtro del foro? ¿Dónde puedo consultar los valores de atributo que aceptan las opciones de filtro de Discourse? ¡Gracias!

2 Me gusta

¿Es posible cambiar el estilo de fuente del contenido incrustado? Probé selectores CSS como d-topics-list iframe html .topics-list .topic-list-item sin éxito. ¡Agradezco de antemano cualquier orientación!

2 Me gusta