Intégrer un widget dans le texte d'un sujet

Bonjour !! :slight_smile:

J’espère que cela intéressera d’autres personnes : j’essaie de créer un sujet de type « flux de réseaux sociaux », dans lequel je souhaite intégrer les widgets de différentes plateformes afin que les utilisateurs (et les administrateurs !) puissent les consulter rapidement sur une seule page. Cela permet de rester dans le forum, évitant la surcharge liée au passage constant entre autant de plateformes sociales pour de simples mises à jour (d’autant qu’elles partagent souvent le même contenu), augmentant ainsi la motivation à utiliser le forum comme hub pour le développement de la communauté.

J’ai trouvé un générateur de widgets sympathique appelé Woxo, qui est suffisamment propre et simple pour cet usage. Le problème maintenant est que je n’arrive pas à comprendre comment intégrer le widget dans le sujet. J’essaie de voir s’il existe une solution de contournement avec des iframes ou autre chose, mais je voulais d’abord demander si cela est possible du tout.

Voici le code que Woxo me fournit pour le flux Instagram :

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

Ce que j’ai essayé jusqu’à présent :

  • Placer le <script> dans la section <body>, <footer> et <header> (je l’ai laissé dans l’en-tête)
  • S’assurer que l’URL d’où provient le script est dans la liste blanche (https://cdn2.woxo.tech/ dans ce cas)
  • Ajouter defer ne change rien (je le conserve au cas où)

Si j’inspecte la page, le script apparaît bien en bas de la section body (à l’intérieur), et puisque la source est dans la liste blanche, il devrait avoir un effet. J’ai vérifié si cela pouvait venir de mon navigateur, mais si j’exécute le HTML ici W3Schools Tryit Editor, cela fonctionne parfaitement.

J’ai réduit l’erreur à une fonction spécifique à l’intérieur du script JS. L’appel suivant renvoie une valeur nulle. C’est la seule erreur d’exécution :

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

Ce div est écrit dans le sujet (la partie <div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>). Il reste sous forme de code HTML pur, il devrait donc être lisible. Pour une raison quelconque, le script échoue à le localiser.

Avec l’attribut defer et placé dans le pied de page, le script ne génère aucune erreur (le fait qu’il génère une erreur auparavant prouve que l’URL du fichier JS est bien dans la liste blanche), alors maintenant je suis à l’aveugle quant à la raison pour laquelle il ne fait rien.

Toute contribution sera plus qu’appréciée, merci d’avance pour votre temps ! :slight_smile:
Lisandro

EDIT : Finalement, j’ai dû renoncer. Puisque seuls les iframes sont pris en charge, je cherche actuellement un bon service web capable d’en fournir un. La plupart des services gratuits sont trop limités, et les versions payantes coûtent plus du double du service d’hébergement du forum. Désolé de me plaindre, j’ai dû crier ici car je n’arrive pas simplement à insérer le code HTML gratuit :cry:

2 « J'aime »

Discourse est une application monopage. Le problème que vous rencontrez survient parce que le script que vous utilisez n’en est pas conscient. Lorsque vous visitez la page d’accueil ou n’importe quelle autre page dans Discourse, vous obtenez quelque chose comme ceci.

<html>
  <head>
    contenu de l'en-tête incluant votre script
  </head>
  <body>
    <section id="main">
      contenu de la page
    </section>
  </body>
</html>

Lorsque vous naviguez vers une autre page, la seule chose qui est rechargée est le contenu à l’intérieur

<section id="main">

Ainsi, le DOM a changé et votre script personnalisé ne se déclenche pas à nouveau. Si vous essayez de visiter directement la page du sujet, vous verrez qu’elle se charge correctement.

.

La question est donc maintenant de savoir comment le faire fonctionner avec Discourse.

Le plugin-api dispose d’une méthode que vous pouvez utiliser pour « décorer » les messages.

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

Vous pouvez l’utiliser pour déclencher des scripts tiers lorsqu’un message est rendu.

Voici le code dont vous auriez besoin. Ajoutez ceci à l’onglet common > header de votre thème.

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

// créer un décorateur de message
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>

Vous devrez ensuite ajouter quelques domaines pour CSP. Ajoutez ceci à votre

content_security_policy_script_src

paramètre du site

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

enfin, vous devrez ajouter un peu de CSS pour l’aperçu statique du compositeur

Cela va dans l’onglet common > CSS de votre thème.

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

Ensuite, vous pouvez simplement ajouter

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

à n’importe quel message, et les widgets seront rendus et pleinement fonctionnels.

Si vous examinez le JavaScript, vous remarquerez qu’il possède deux options tout en haut.

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

Modifiez WOXO_SCRIPT_SRC pour mettre la source que Woxo vous fournit. Elle devrait être la même pour tous les intégrations que vous créez.

Modifiez PREVIEW_ICON pour mettre le nom de l’icône que vous souhaitez utiliser dans l’aperçu du compositeur. L’exécution de ce code dans le compositeur est un peu coûteuse, c’est pourquoi le compositeur propose un aperçu statique qui ressemble à ceci.

L’icône que vous choisissez s’affichera au centre.

Voici une version commentée du code si vous souhaitez suivre ce qui se passe

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

// nous utilisons la bibliothèque de chargement de scripts de Discourse pour garantir que les scripts sont chargés
// correctement. Ne vous inquiétez pas, elle est assez intelligente pour ne pas dupliquer le script
// s'il est déjà chargé
const loadScript = require("discourse/lib/load-script").default;

// nous chargeons la fonction HTML d'icône de Discourse pour obtenir le SVG de l'icône
// que nous voulons utiliser dans l'aperçu statique du compositeur
const { iconHTML } = require("discourse-common/lib/icon-library");

// configuration de l'icône d'aperçu du compositeur
const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
  class: "woxo-preview-icon"
});

// création d'une fonction utilitaire pour le markup de l'aperçu du compositeur
const previewMarkup = () => {
  const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;

  return markup;
};

// création d'un décorateur de message
api.decorateCookedElement(
  post => {
    // ce message contient-il des widgets Woxo ?
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    // Oui, alors faisons quelques travaux.
    if (woxoWidgets.length) {
      // pour chaque widget Woxo
      woxoWidgets.forEach(woxoWidget => {
        // s'il s'agit d'un widget du compositeur, remplacez-le par un aperçu statique et
        // quittez immédiatement
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        // s'il n'est pas dans le compositeur, chargez le script Woxo.
        loadScript(WOXO_SCRIPT_SRC).then(() => {
          // Le script Woxo est très étrange. Il ne fonctionnera pas à moins que la balise
          // script ait un attribut data-usrc vide. Ajoutons-le donc
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";

          // tout est prêt, appelons la méthode init dans le script Woxo
          window.MC.Loader.init();
        });
      });
    }
  },
  // ajoutez un ID au décorateur pour éviter les fuites de mémoire
  { id: "render-woxo-widgets" }
);

</script>
3 « J'aime »

D’abord : O-M-G MERCI BEAUCOUP :exploding_head:
Je suis stupéfait par la qualité de la réponse. Ma seule consolation en voyant autant de travail est que c’est certain que cela sera d’une grande aide pour beaucoup. Cela ouvre beaucoup de nouvelles possibilités, toutes au profit du développement d’un forum en tant que hub pour une communauté.

Deuxièmement : Je suis tellement désolé d’avoir répondu si tard alors que vous avez répondu si rapidement. Je n’ai pas pu accéder à mon ordinateur plus tôt, et deuxièmement, je ne voulais répondre qu’une fois avoir effectivement parcouru le code que vous avez si gentiment commenté (ligne par ligne :flushed:, mon Dieu, merci infiniment). Bien sûr, tout a fonctionné parfaitement, le flux entier se charge si rapidement… Je suis en dette :pray:

Merci encore Johan, j’espère vraiment pouvoir contribuer de mon côté avec ma propre expérience :pray:

PS : Je garde absolument le cœur pour l’aperçu statique.

1 « J'aime »

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