Widget in Text eines Themas einbetten

Hallo!! :slight_smile:

Ich hoffe, das interessiert auch andere: Ich versuche, ein Thema im Stil eines „Social-Media-Feeds" zu erstellen, in das ich Widgets verschiedener Plattformen einbetten möchte. So können die Nutzer (und auch die Admins!) alle auf einen Blick auf einer einzigen Seite sehen, bleiben im Forum und müssen nicht ständig zwischen so vielen Social-Media-Plattformen hin- und herwechseln, nur um kurze Updates zu erhalten (zumal diese oft denselben Inhalt teilen). Das soll die Motivation steigern, das Forum als zentrale Drehscheibe für die Entwicklung der Community zu nutzen.

Ein netter Widget-Generator, den ich gefunden habe, ist Woxo – er ist sauber und einfach genug für diesen Zweck. Das Problem ist nun, dass ich nicht herausbekomme, wie man das Widget in das Thema einbettet. Ich versuche gerade, ob es einen Workaround mit iframes oder Ähnlichem gibt, wollte aber erst einmal fragen, ob das überhaupt möglich ist.

Hier ist der Code, den ich von Woxo für den Instagram-Feed erhalte:

<div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>
        
<script 
  src="https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619" 
  async data-usrc>
</script>

Meine bisherigen Versuche:

  • Das <script>-Tag im <body>, <footer> und <header> platziert (ich habe es im Header gelassen)
  • Sicherstellen, dass die URL, von der das Skript kommt, freigegeben ist (in diesem Fall https://cdn2.woxo.tech/)
  • Das Hinzufügen von defer hat nicht geholfen (ich behalte es trotzdem vor)

Wenn ich die Seite inspiziere, erscheint das Skript am Ende des <body>-Bereichs (im Inneren), und da die Quelle freigegeben ist, sollte es wirken. Ich habe geprüft, ob es vielleicht an meinem Browser liegt, aber wenn ich den HTML-Code hier ausführe W3Schools Tryit Editor, funktioniert er einwandfrei.

Ich habe den Fehler auf eine bestimmte Funktion im JS-Skript eingegrenzt. Der folgende Aufruf liefert einen null-Wert zurück. Das ist der einzige Laufzeitfehler:

e=document.querySelector("div[data-mc-src]")
e is null

Dieses div steht im Thema (der Teil <div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>). Es bleibt als reiner HTML-Code erhalten und sollte also lesbar sein. Aus irgendeinem Grund findet das Skript es jedoch nicht.

Mit dem Attribut defer und im Footer platziert, wirft das Skript keinen Fehler mehr (die Tatsache, dass es vorher einen Fehler warf, beweist, dass die URL der JS-Datei tatsächlich freigegeben ist). Jetzt bin ich jedoch ratlos, warum es nichts bewirkt.

Jeder Hinweis wäre mehr als willkommen, danke im Voraus für eure Zeit! :slight_smile:
Lisandro

EDIT: Schließlich musste ich es aufgeben. Da nur iframes unterstützt werden, suche ich derzeit nach einem guten Webdienst, der eines bereitstellen kann. Die meisten kostenlosen Dienste sind zu eingeschränkt, und die kostenpflichtigen Versionen kosten mehr als das Doppelte des Hosting-Services für das Forum. Entschuldigt das Jammern, ich musste hier laut schreien, weil ich einfach den kostenlosen HTML-Code nicht einfügen konnte :cry:

2 „Gefällt mir“

Discourse ist eine Single-Page-Anwendung. Das Problem, auf das du stößt, tritt auf, weil das von dir verwendete Skript dies nicht berücksichtigt. Wenn du die Startseite oder eine beliebige andere Seite in Discourse besuchst, erhältst du etwas wie dies:

<html>
  <head>
    Head-Inhalte einschließlich deines Skripts
  </head>
  <body>
    <section id="main">
      Seiteninhalt
    </section>
  </body>
</html>

Wenn du zu einer anderen Seite navigierst, wird nur der Inhalt innerhalb von

<section id="main">

neu geladen.

Das DOM hat sich also geändert, und dein benutzerdefiniertes Skript wird nicht erneut ausgeführt. Wenn du versuchst, die Themen-Seite direkt aufzurufen, wirst du sehen, dass sie ordnungsgemäß lädt.

.

Die Frage ist nun also, wie man dies mit Discourse zum Laufen bringt.

Die Plugin-API bietet eine Methode, mit der du Beiträge „dekoriert" kannst.

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L282-L318

Damit kannst du Skripte von Drittanbietern ausführen, wenn ein Beitrag gerendert wird.

Hier ist der Code, den du benötigst. Füge dies im Reiter common > header deines Themes hinzu.

<script type="text/discourse-plugin" version="0.8">
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

const loadScript = require("discourse/lib/load-script").default;
const { iconHTML } = require("discourse-common/lib/icon-library");

const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
  class: "woxo-preview-icon"
});

const previewMarkup = () => {
  const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;
  return markup;
};

// Erstelle einen Beitrags-Dekorator
api.decorateCookedElement(
  post => {
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    if (woxoWidgets.length) {
      woxoWidgets.forEach(woxoWidget => {
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        loadScript(WOXO_SCRIPT_SRC).then(() => {
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";
          window.MC.Loader.init();
        });
      });
    }
  },
  { id: "render-woxo-widgets" }
);
</script>

Anschließend musst du ein paar Domains für CSP hinzufügen. Füge diese zu deiner

content_security_policy_script_src

Site-Einstellung hinzu

https://*.woxo.tech/
https://us-central1-core-period-259421.cloudfunctions.net/availableComponentTracks

Schließlich musst du ein wenig CSS für die statische Composer-Vorschau hinzufügen.

Dies geht im Reiter common > CSS deines Themes.

.woxo-preview {
  height: 400px;
  width: 100%;
  background: var(--primary-low);
  display: flex;
  align-items: center;
  justify-content: center;
  .woxo-preview-icon {
    font-size: var(--font-up-4);
    color: var(--primary-high);
  }
}

Dann kannst du einfach

<div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>

zu einem beliebigen Beitrag hinzufügen, und die Widgets werden gerendert und sind voll funktionsfähig.

Wenn du dir das JavaScript ansiehst, wirst du feststellen, dass es ganz oben zwei Optionen hat.

const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

Ändere WOXO_SCRIPT_SRC in die Quelle, die dir Woxo gibt. Diese sollte für alle von dir erstellten Embeds gleich sein.

Ändere PREVIEW_ICON in den Namen des Icons, das du in der Composer-Vorschau verwenden möchtest. Die Ausführung dieses Codes im Composer ist etwas aufwendig, daher verfügt der Composer über eine statische Vorschau, die so aussieht:

Das von dir gewählte Icon wird in der Mitte angezeigt.

Hier ist eine kommentierte Version des Codes, falls du Schritt für Schritt mitgehen möchtest:

kommentierter Code
<script type="text/discourse-plugin" version="0.8">
// Optionen
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

// Wir verwenden die Discourse Load-Script-Bibliothek, um sicherzustellen, dass Skripte ordnungsgemäß geladen werden.
// Keine Sorge, dies ist intelligent genug, um das Skript nicht zu duplizieren, falls es bereits geladen ist.
const loadScript = require("discourse/lib/load-script").default;

// Wir laden die Discourse Icon-HTML-Funktion, um das SVG für das Icon zu erhalten,
// das wir in der statischen Composer-Vorschau verwenden möchten.
const { iconHTML } = require("discourse-common/lib/icon-library");

// Richte das Composer-Vorschau-Icon ein
const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
  class: "woxo-preview-icon"
});

// Erstelle eine Hilfsfunktion für die Composer-Vorschau-Markup
const previewMarkup = () => {
  const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;

  return markup;
};

// Erstelle einen Beitrags-Dekorator
api.decorateCookedElement(
  post => {
    // Enthält dieser Beitrag Woxo-Widgets?
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    // Ja, also machen wir etwas.
    if (woxoWidgets.length) {
      // Für jedes Woxo-Widget
      woxoWidgets.forEach(woxoWidget => {
        // Wenn es sich um ein Composer-Widget handelt, ersetze es durch eine statische Vorschau und
        // beende frühzeitig
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        // Wenn es sich nicht um den Composer handelt, lade das Woxo-Skript.
        loadScript(WOXO_SCRIPT_SRC).then(() => {
          // Das Woxo-Skript ist sehr seltsam. Es funktioniert nicht, es sei denn, das Skript-Tag
          // hat ein leeres data-usrc-Attribut. Also fügen wir es hinzu.
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";

          // Alles ist bereit, rufen wir die init-Methode im Woxo-Skript auf.
          window.MC.Loader.init();
        });
      });
    }
  },
  // Füge eine ID zum Dekorator hinzu, um Speicherlecks zu vermeiden
  { id: "render-woxo-widgets" }
);

</script>
3 „Gefällt mir“

Erstens: O-M-G, vielen, vielen DANK :exploding_head:
Ich bin erstaunt über das Ausmaß der Reaktion. Mein einziger Trost angesichts dieser enormen Arbeit ist die Gewissheit, dass dies vielen von großem Nutzen sein wird. Dies eröffnet viele neue Möglichkeiten, alles zum Wohle der Entwicklung eines Forums als Zentrum für eine Community.

Zweitens: Es tut mir so leid, dass ich so spät antworte, obwohl Sie so schnell geantwortet haben. Zum einen konnte ich nicht früher an meinen Computer, und zum anderen wollte ich erst antworten, nachdem ich den Code, den Sie so freundlich Zeile für Zeile kommentiert haben, tatsächlich durchgearbeitet hatte (:flushed:, mein Gott, vielen Dank). Natürlich hat alles perfekt funktioniert, der gesamte Feed lädt so schnell… Ich bin Ihnen etwas schuldig :pray:

Nochmals vielen Dank, Johan. Ich hoffe sehr, mit meiner eigenen Erfahrung einen Beitrag leisten zu können :pray:

PS: Ich behalte das Herz für die statische Vorschau definitiv bei.

1 „Gefällt mir“

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.