Einbetten Sie Kommentare aus Discourse in Ihre Single Page App

Wenn Sie versuchen, [1] in Ihrer Single Page Application (SPA) zu implementieren, die keine serverseitig gerenderten Seiten bereitstellt, werden Sie letztendlich auf Probleme stoßen, siehe: [2]

Nach einigem Tüfteln möchte ich den folgenden Ansatz vorstellen. Das Beispiel ist in Vue.js, kann aber leicht an andere Frameworks/Bibliotheken angepasst werden.

Hinweis: Ich werde den Begriff Blogbeiträge verwenden, wo ein Discourse-Kommentarbereich eingebettet werden soll. Dies kann aber natürlich auch einzelne Seiten Ihrer Website bedeuten.

1. Die Probleme in [1]

1.1. javascripts/embed.js kann nicht mit clientseitig gerendertem Inhalt arbeiten

Der <script>...</script>-Snippet, den Sie in [1] zum Einfügen in Ihr HTML erhalten, wird daher nicht Teil der Implementierung sein, die wir hier verfolgen. Wir werden einige Teile von javascripts/embed.js, die von Ihrer Discourse-Instanz bereitgestellt werden, als Funktionen innerhalb unserer SPA verwenden.

1.2. Discourse kann clientseitig gerenderten Inhalt nicht scrapen

Discourse erstellt automatisch Themen für jeden Blogbeitrag und versucht, auf die ursprüngliche URL (eines Blogbeitrags) zuzugreifen, um Titel und Inhalt zu ermitteln. Dies schlägt bei einer SPA fehl, da Discourse den Nicht-JavaScript-Teil davon erhält, z. B. Wir entschuldigen uns, aber diese Seite funktioniert ohne aktivierten JavaScript nicht richtig. Bitte aktivieren Sie ihn, um fortzufahren.

Wir werden das RSS Polling Plugin verwenden, um die notwendigen Daten bereitzustellen und die Themen für uns zu erstellen.

2. Die Implementierung

2.1 RSS Polling und der RSS/Atom-Feed

Erstellen Sie einen Endpunkt auf Ihrer Website, der einen RSS- oder Atom-Feed für das RSS Polling Plugin bereitstellt. Dieser Endpunkt kann entweder nur eine statische XML-formatierte Datei oder eine serverseitige Funktion sein, die den XML-formatierten Inhalt bereitstellt, Beispiel:

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

Inhalt:

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

  <title>My Site Blog Posts</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>An Article about Technology</title>
    <author>
      <name>Your Name Here</name>
    </author>
    <link href="https://mysite.com/blog/an-article-about-technology"/>
    <id>urn:uuid:f6cc13e4-d2eb-4385-af28-c867a94f48dc</id>
    <published>2022-07-03T00:00:00Z</published>
    <updated>2022-07-03T00:00:00Z</updated>
    <summary>Let's discuss some technology in this article.</summary>
  </entry>

</feed>

Installieren Sie das RSS Polling Plugin für Discourse gemäß [3] (Discourse gehostet) oder [4] (selbst gehostet)

Empfohlene Einstellungen für RSS Polling in Admin → Einstellungen → Plugins:

Schlüssel Wert
rss polling enabled true
rss polling frequency 10 (d. h. 10 Minuten)

Fügen Sie einen neuen Feed in der RSS Polling Plugin-Konfiguration unter Admin → Plugins → RSS Polling hinzu.

Konfigurieren Sie ihn gemäß [3]:

Schlüssel Wert
URL https://mysite.com/blog.atom
Category Filter <dies ist optional>
Author <definieren Sie einen Autor der automatisch generierten Themen>
Category <definieren Sie die Kategorie(n), in der/denen die automatisch generierten Themen gepostet werden>
Tags <dies ist optional>

2.2 SPA Router-Konfiguration

Discourse verwendet den letzten Teil des URL-Pfads als Identifikator für einen einzelnen Blogbeitrag.

Beispiele:

https://mysite.com/blog/an-article-about-technology
https://mysite.com/blog/another-article-about-cats

Konfigurieren Sie Ihren SPA-Router entsprechend, sodass einzelne Blogbeiträge einzelnen URLs entsprechen.

Außerdem: Discourse stellt einen Link zurück zum einzelnen Blogbeitrag bereit, daher ist es eine gute UX, wenn Ihre Website beim Klicken den tatsächlichen Artikel anzeigt.

2.3 Die Artikel-Komponente

Wie bereits erwähnt, können wir nicht den Ansatz mit dem <script></script> aus [1] verwenden. Daher implementieren wir den iframe und einige Funktionen aus javascripts/embed.js in unserer Komponente:

Article.vue

Dinge, die Sie bearbeiten sollten:

Element Beschreibung
#YOUR-DISCOURSE-URL# die URL Ihrer Discourse-Instanz, z. B. discourse.mysite.com)
#YOUR-SITE-URL# die URL Ihrer Website, z. B. mysite.com möglicherweise auch %2Fblog%2F, wenn der Pfad Ihrer Blogbeiträge nicht /blog/ ist
<template>
  <div id="article">

    <!-- Ihr formatierter Artikel hier -->

    <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,     // der Slug des Blogbeitrags, z. B. "an-article-about-technology", während die Route "https://mysite.com/blog/an-article-about-technology" ist
    iframeHeight: 0 // Discourse wird uns die genaue iframe-Höhe mitteilen (siehe: receiveMessage Methode)
  }),

  methods: {
    // iframe-Kommunikation
    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) {
          // iframe-Offset finden
          const destY = this.findPosY(this.$refs["discourse-embed-frame"]) + event.data.top;
          window.scrollTo(0, destY);
        }
      }
    },

    // Danke 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 Discourse Embed-Konfiguration

Nun, da das RSS/Atom-Feed-Polling eingerichtet ist und die Implementierung auf Ihrer Website vorhanden ist, können wir endlich die Einbettung auf der Discourse-Instanz konfigurieren.

Gehen Sie zu Admin → Anpassen → Einbetten und fügen Sie einen Host hinzu:

Schlüssel Wert
Allowed Hosts Ihre Website-Basis-URL, z. B. “mysite.com
Class Name optionaler Klassenname für Styling
Path Allowlist z. B. “/blog/.*”
Post to Category dieselbe Kategorie wie in RSS Polling Category konfiguriert

3. Abschließende Bemerkungen

Das RSS/Atom-Feed-Polling sowie Discourse selbst erstellen ein neues Thema, wenn es für den einzelnen Blogbeitrag nicht existiert. Stellen Sie sicher, dass das RSS/Atom-Feed-Polling zuerst erfolgt (d. h. warten Sie, bis das Thema erstellt ist, bevor Sie den Blogbeitrag auf Ihrer Website besuchen).

Grund: Discourse kann den Titel und die Zusammenfassung nicht scrapen, daher wäre das Thema mysite.com mit der Zusammenfassung Wir entschuldigen uns, aber diese Seite funktioniert ohne JavaScript nicht. :wink:

Wenn Discourse aus irgendeinem Grund zuerst kam, können Sie das Thema einfach löschen und warten, bis der RSS/Atom-Feed aktiv wird.

Viele Grüße
– MK2k

2 „Gefällt mir“