Incorpora commenti da Discourse nella tua applicazione a pagina singola

Quando si tenta di implementare [1] nella propria Single Page Application (SPA) che non fornisce pagine renderizzate lato server, si finirà per incontrare problemi, vedere: [2]

Quindi, dopo un po’ di sperimentazione, vorrei presentare il seguente approccio. L’esempio è in Vue.js, tuttavia, può essere facilmente adattato ad altri framework/librerie.

Nota: Utilizzerò il termine articoli del blog dove dovrebbe essere incorporata una sezione di commenti di Discourse. Ma ovviamente questo può significare anche singole pagine del tuo sito.

1. I problemi in [1]

1.1. javascripts/embed.js non può funzionare con contenuti renderizzati lato client

Lo snippet <script>...</script> che ti viene detto in [1] di inserire nel tuo HTML, quindi, non farà parte dell’implementazione che stiamo affrontando qui. Utilizzeremo alcune parti di javascripts/embed.js fornite dalla tua istanza di Discourse come funzioni all’interno della nostra SPA.

1.2. Discourse non può fare lo scraping di contenuti renderizzati lato client

Discourse crea automaticamente argomenti per ogni articolo del blog e tenta di accedere all’URL originale (di un articolo del blog) per determinare il titolo e il contenuto. Questo fallisce con una SPA, perché Discourse otterrà la parte non-javascript di essa, ad esempio Siamo spiacenti ma questo sito non funziona correttamente senza JavaScript abilitato. Abilitalo per continuare.

Utilizzeremo il plugin RSS Polling per fornire i dati necessari e creare gli argomenti per noi.

2. L’implementazione

2.1 RSS Polling e il feed RSS/Atom

Crea un endpoint sul tuo sito che fornisca un feed RSS o Atom per il plugin RSS Polling. Questo endpoint può essere solo un file statico formattato XML o una funzione lato server che fornisce il contenuto formattato XML, esempio:

URL: https://mysite.com/blog.atom

Contenuto:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Articoli del Blog del Mio Sito</title>
  <link href="https://mysite.com/blog/"/>
  <updated>2022-07-03T09:02:48.721Z</updated>
  <id>urn:uuid:790c1857-b968-49cc-9fbd-bf7afe3552c2</id>

  <entry>
    <title>Un Articolo sulla Tecnologia</title>
    <author>
      <name>Il Tuo Nome Qui</name>
    </author>
    <link href="https://mysite.com/blog/un-articolo-sulla-tecnologia"/>
    <id>urn:uuid:f6cc13e4-d2eb-4385-af28-c867a94f48dc</id>
    <published>2022-07-03T00:00:00Z</published>
    <updated>2022-07-03T00:00:00Z</updated>
    <summary>Discutiamo un po' di tecnologia in questo articolo.</summary>
  </entry>

</feed>

Installa il plugin RSS Polling per Discourse come descritto in [3] (Discourse ospitato) o [4] (ospitato autonomamente)

Impostazioni consigliate per RSS Polling in Admin → Impostazioni → Plugin:

Chiave Valore
rss polling enabled true
rss polling frequency 10 (cioè 10 minuti)

Aggiungi un nuovo feed nella configurazione del plugin RSS Polling in Admin → Plugin → RSS Polling

Configuralo come descritto in [3]:

Chiave Valore
URL https://mysite.com/blog.atom
Category Filter <questo è opzionale>
Author <definisci un autore degli argomenti generati automaticamente>
Category <definisci la/le categoria/e dove verranno pubblicati gli argomenti generati automaticamente>
Tags <questo è opzionale>

2.2 Configurazione del Router SPA

Discourse utilizza l’ultima parte del percorso dell’URL come identificatore per un singolo articolo del blog.

Esempi:

https://mysite.com/blog/un-articolo-sulla-tecnologia
https://mysite.com/blog/un-altro-articolo-sui-gatti

Configura il tuo router SPA di conseguenza, in modo che i singoli articoli del blog corrispondano a URL individuali.

Inoltre: Discourse fornirà un link all’articolo del blog individuale, quindi è una buona UX che quando cliccato, il tuo sito mostri l’articolo effettivo.

2.3 Il Componente Articolo

Come detto prima, non possiamo usare l’approccio con lo <script></script> di [1]. Quindi implementiamo l’iframe e alcune funzioni da javascripts/embed.js nel nostro componente:

Article.vue

Cose da modificare:

Elemento Descrizione
#YOUR-DISCOURSE-URL# l’URL della tua istanza di Discourse, ad es. discourse.mysite.com)
#YOUR-SITE-URL# l’URL del tuo sito, ad es. mysite.com eventualmente anche %2Fblog%2F se il percorso dei tuoi articoli non è /blog/
<template>
  <div id="article">

    <!-- il tuo articolo formattato qui -->

    <iframe
      v-if="slug"
      v-bind:src="`https://#YOUR-DISCOURSE-URL#/embed/comments?embed_url=https%3A%2F%2F#YOUR-SITE-URL#%2Fblog%2F${slug}%2F`"
      id="discourse-embed-frame"
      width="100%"
      v-bind:height="`${iframeHeight}px`"
      frameborder="0"
      scrolling="no"
      referrerpolicy="no-referrer-when-downgrade"
    />
  </div>
</template>

<script>
export default {
  data: () => ({
    slug: null,     // lo slug dell'articolo del blog, ad es. "un-articolo-sulla-tecnologia" mentre il percorso è "https://mysite.com/blog/un-articolo-sulla-tecnologia"
    iframeHeight: 0 // Discourse ci dirà l'altezza esatta dell'iframe (vedi: metodo receiveMessage)
  }),

  methods: {
    // comunicazione iframe
    receiveMessage(event) {
      if (!event) {
        return;
      }
      if (!(event.origin || "").includes("#YOUR-DISCOURSE-URL#")) {
        return;
      }

      if (event.data) {
        if (event.data.type === "discourse-resize" && event.data.height) {
          this.iframeHeight = +event.data.height;
        }

        if (event.data.type === "discourse-scroll" && event.data.top) {
          // trova l'offset dell'iframe
          const destY = this.findPosY(this.$refs["discourse-embed-frame"]) + event.data.top;
          window.scrollTo(0, destY);
        }
      }
    },

    // Grazie a http://amendsoft-javascript.blogspot.ca/2010/04/find-x-and-y-coordinate-of-html-control.html
    findPosY(obj) {
      var top = 0;
      if (obj.offsetParent) {
        while (1) {
          top += obj.offsetTop;
          if (!obj.offsetParent) break;
          obj = obj.offsetParent;
        }
      } else if (obj.y) {
        top += obj.y;
      }
      return top;
    }
  },

  async created() {
    this.slug = this.$router.currentRoute.path.split("/")[2];
  },

  mounted() {
    window.addEventListener("message", this.receiveMessage);
  },
  beforeDestroy() {
    window.removeEventListener("message", this.receiveMessage);
  }
}
</script>

2.4 Configurazione Embed Discourse

Ora con il polling del feed RSS/Atom attivo e l’implementazione sul tuo sito, possiamo finalmente configurare l’embedding sull’istanza di Discourse.

Vai su Admin → Personalizza → Embedding e aggiungi un host:

Chiave Valore
Allowed Hosts il tuo URL base del sito> es. “mysite.com
Class Name nome classe opzionale per lo styling
Path Allowlist es. “/blog/.*”
Post to Category la stessa categoria configurata in RSS Polling Category

3. Note Finali

Il polling del feed RSS/Atom e Discourse stesso creeranno un nuovo argomento se non esiste per il singolo articolo del blog. Assicurati che il polling del feed RSS/Atom avvenga per primo (cioè, attendi che l’argomento venga creato prima di visitare l’articolo del blog sul tuo sito).

Motivo: Discourse non può fare lo scraping del titolo e del riassunto, quindi l’argomento sarebbe mysite.com con il riassunto Siamo spiacenti, ma questo sito non funziona senza javascript. :wink:

Se per qualche motivo Discourse venisse prima, puoi semplicemente eliminare l’argomento e attendere che il feed RSS/Atom venga elaborato.

Saluti
– MK2k

2 Mi Piace