تضمين قائمة مواضيع Discourse في موقع آخر

إذا قمت بتحميل أحدث إصدارات من Discourse، فستحصل على إمكانية تضمين قوائم المواضيع في مواقع أخرى عبر بعض أكواد JavaScript و HTML البسيطة.

الحالة الاستخدامية النموذجية لهذا هي مدونة أو موقع آخر يعتمد على المحتوى، حيث ترغب في وجود أداة جانبية على جانب الشاشة تعرض قائمة بالمواضيع. يمكنك التصفية حسب الفئة، أو الوسم، أو أي من خيارات التصفية العامة الأخرى المتاحة.

كيفية تضمين قائمة بالمواضيع

أولاً، يجب تمكين إعداد الموقع embed topics list.

ثم، في ملف HTML الخاص بك، أضف وسم <script> يتضمن كود JavaScript اللازم لتضمين مواضيع Discourse. يمكنك إضافة هذا في أي مكان تضيف فيه الأكواد البرمجية عادةً. على سبيل المثال:

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

استبدل URL بعنوان المنتدى، بما في ذلك المجلد الفرعي إذا كان موجودًا.

بعد ذلك، في وسم <body> في مستند HTML الخاص بك، أضف وسم d-topics-list للإشارة إلى قائمة المواضيع التي ترغب في تضمينها. ستحتاج أيضًا إلى استبدال URL بعنوان URL الأساسي الخاص بك هنا:

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

أي سمات تقدمها (باستثناء discourse-url الذي هو إلزامي) سيتم تحويلها إلى معلمات الاستعلام لبحث المواضيع. لذا، إذا كنت ترغب في البحث عن مواضيع حسب الوسم، يمكنك فعل ذلك كالتالي:

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

إذا كان لمعلمة الاستعلام خط سفلي، فحوّله إلى شرطة. في المثال أعلاه، لاحظت على الأرجح أن per_page أصبحت per-page.

في سياقات نفس الموقع (SameSite) (أي أن الموقع المضمن ومنتدىك يشتركان في نطاق رئيسي)، سيعرف Discourse ما إذا كنت مسجلًا في المنتدى، وسيعرض النتائج وفقًا لذلك. لا تتفاجأ إذا رأيت فئات آمنة وما شابه عند تسجيل الدخول - فالمستخدمون المجهولون لن يتمكنوا من ذلك!

قائمة المعلمات

template: إما complete أو basic (الافتراضي). بينما تكون القائمة الأساسية مجرد قائمة بعناوين المواضيع، فإن النسخة الكاملة تجلب العنوان، واسم المستخدم، ورمز المستخدم، وتاريخ الإنشاء، وصورة مصغرة للموضوع.

per-page: رقم. يتحكم في عدد المواضيع التي سيتم إرجاعها.

category: رقم. يقيّد المواضيع بفئة واحدة. مرّر id الفئة المستهدفة.

allow-create: قيمة منطقية (Boolean). إذا تم تمكينها، ستحتوي الأداة المضمنة على زر “موضوع جديد”.

tags: نص. يقيّد المواضيع بتلك المرتبطة بهذا الوسم.

top_period: أحد القيم التالية: all, yearly, quarterly, monthly, weekly, daily. إذا تم تمكينه، سيعيد “أفضل” مواضيع الفترة المحددة.

أمثلة

لقد أنشأت موقعًا تجريبيًا هنا:

https://embed.eviltrout.com

يجب أن تتمكن من عرض مصدر الصفحة في متصفحك لرؤية الكود، ولكن يمكنك أيضًا العثور على المصدر الكامل على GitHub:

هذه ميزة جديدة تمامًا، لذا فإن أي ملاحظات أو طلبات ستكون موضع تقدير.

تنسيق القائمة

يمكنك استخدام ميزة السمة الحالية لدينا لإضافة أنماط مخصصة لقائمة التضمين.

على سبيل المثال، بشكل افتراضي، تبدو قائمة التضمين التي تستخدم قالب complete كالتالي:

إذا كنت تريد أن تبدو مثل شبكة، على سبيل المثال، يمكنك إضافة SCSS مخصص إلى Theme > Common > Embedded CSS:

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

مما سيجعلها تبدو كالتالي:

95 إعجابًا

Hey folks! We’re looking to have the links open in a new tab (so target="_blank" instead of target="_parent"). Is there an easy way to do this given the iframe?

إعجابَين (2)

I don’t know if it’s what you’re looking for, but something like this should work to open links in new tabs. CORS has to be enabled for the external domain. I tried it this way because I wanted to filter out a pinned topic.

// `fetch` might require a polyfill
const targetEl = document.getElementById("forumTopics");

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

// tack `.json` on to any topic list
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 إعجابات

Thanks! I like this idea, but based on this post What are the risks of enabling Cross-origin resource sharing (DISCOURSE_ENABLE_CORS) I’m thinking that enabling CORS isn’t the best idea.

إعجابَين (2)

You are only supposed to enable CORS for sites you trust. Enabling CORS for every source is defeating the purpose of it.

6 إعجابات

I only enabled CORS for other sites I control. The goal was to load a list of forum topics in our other sites with frontend code. That method probably wouldn’t work for letting random sites embed the posts.

Edit: if you copy/paste that code watch out for .slice(1, 6) because it removes one of the topics. (I think that was the pinned topic that I wanted to remove.)

3 إعجابات

Cool, this is something we can try then - I don’t have a site other than localhost for now (during testing) which is why I was hesitant to add it. But if I can find someone that has a place, we can give it a go. Thanks for your help!

4 إعجابات

Hey @j127

we are currently facing the same issue and want to open links in new tab.
Just quick question.

Where exactly do you put the code that you provided

@eviltrout Any idea why in my case the CSS styles are not appearing?

إعجابَين (2)

That code is just a quick example of the general idea. I don’t think fetch works in all browsers. I can rewrite it in ES5 if you want.

Is your site WordPress or some kind of headless WordPress?

4 إعجابات

It appears to work in all modern browsers, and Discourse doesn’t support IE11 any more…

4 إعجابات

My site is just regular wordpress, not headless.

إعجاب واحد (1)

Can I use this to embed a list of Discourse Topics in another Discourse instance? Or is there a smarter way to do that?

My use case is having a ‘bridge’ between two instances as a temporary measure until they can be merged in future. It would be nice to have this automated and dynamic.

إعجابَين (2)

If you don’t care about IE, then that snippet should work. (CORS has to be enabled for the external domain in the Discourse settings.)

To test it, open any page on this forum and paste the snippet below in the browser console. I changed the target element to document.body, so it will replace the entire page with the list for forum topics. For a real site, just change the target element to document.getElementById("#someId") and then put a div with that ID on the page somewhere. The script will fill it with the list of topics.

// `fetch` might require a polyfill unless you don't care about IE
// const targetEl = document.getElementById("forumTopics");

// this replaces the body of the page, just as an example
const targetEl = document.body;

// put your forum's URL here to test it on your own forum
const baseForumURL = `https://meta.discourse.org`;

// how many topics you want to show
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>`;
}

// tack `.json` on to any topic list
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);
    });

Example: if the target div somewhere on your WP site looks like this

<div id="forumTopics">
    <!-- the forum posts will appear here -->
</div>

then the JavaScript could target that ID like this:

// look for this line in the original example I posted
const targetEl = document.getElementById("forumTopics");
4 إعجابات

By that you mean the code in the first post or the snippet in post 50? And no, I don’t care about IE. No need to cater to that dinosaur :).
In my case the CSS is not loaded for my snippet in any browser.

So this is JS and I have to put <script> tag around it when implementing it to wordpress, correct?

إعجابَين (2)

It has been a while since I used WordPress. Is it something that you’re adding to a theme’s HTML? If yes, then wrap my code in a script tag and put the <div> wherever you want the posts to appear.

I think you could drop the code below right into the HTML of a WordPress template wherever you want the posts to appear. Be sure to update the line that has the URL of the forum. If it doesn’t work, let me know. (It’s untested.)

Click here for the code
<div id="forumTopics"></div>
<script>
// put your forum's URL here to test it on your own forum
const baseForumURL = `https://forum.example.com`;

// how many topics you want to show
const numTopics = 6;

const targetEl = document.getElementById("forumTopics");
// If the posts don't load for some reason, you could show a link to the forum
targetEl.innerHTML = `<a class="link-to-discourse" href="${baseForumURL}/">View the latest forum posts</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>`;
}

// tack `.json` on to any topic list
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 إعجابات

Thx, appreciate the full snippet

After initial test, only a link is displayed. But I think CORS is disabled, so gonna have my host enable that.
Meanwhile, how can I make the script only display post with a specific tag and from a specific category?

إعجابَين (2)

I’m not sure, but I think the format is
https://forum.example.com/tags/c/<catgory_name>/<tag_name>

Example: the category is “support” and the tag is “css”:
https://meta.discourse.org/tags/c/support/css

To turn it into JSON, add .json on the end:
https://meta.discourse.org/tags/c/support/css.json

So in the fetch function, instead of /latest.json, use something like /tags/c/support/css.json.

(Untested.)

3 إعجابات

And if I want to embed 5 topics in the Discourse-instance itself?

I make a topic. Make it a wiki. I publish it as a page. How do I embed the five latest topics from a specific category/tag or use the template this feature supports?

Thanks.

إعجاب واحد (1)

What is the query parameter for top topics? I tried order="top" but it is not returning the top topics the same way the forum does based on a particular period. How can I replicate the forum filter result? Where can I look up the attribute values Discourse’s filter options take? Thank you!

إعجابَين (2)

Is it possible to change the font style of the embedded content? I tried CSS selectors like d-topics-list iframe html .topics-list .topic-list-item without success. Appreciate the pointer in advance!

إعجابَين (2)