テーマコンポーネント vs プラグイン:違いは何か

Discourse の 3 つの概念である テーマコンポーネントプラグインpluginAPI の違いについて、どなたか明確にしてください。

特にテーマコンポーネントとプラグインの違いについてです。フォーラムをカスタマイズしたい場合、どちらを構築すべきかどう判断すればよいでしょうか?

入門でこの違いを見逃していたかもしれません。そこには記載されていないようです)

Discourse の初心者ではありませんが、専門家でもありません。

  1. テーマコンポーネントは、HTML、CSS、JavaScript を使用して基本テーマを拡張します。
    基本テーマと呼ばれることに注意してください。通常は単に「テーマ」と呼ばれますが、人々は区別を明確にしないことがあり、文脈から推測する必要があります。テーマおよび/またはテーマコンポーネントは、管理者がサイトを停止させることなくインストールできます。Discourse の顧客であれば、これらを追加することもできます (リスト)。また、Discourse テーマの使い方の初心者ガイドもご覧ください。

  2. プラグインは Ruby を使用し、可能な限りあらゆることを実行できます。Discourse の顧客であれば、有効化できるプラグインのセットは制限されていますが、セルフホストしている場合は好きなだけ追加できます。ただし、カスタムプラグインがアップグレード中にサイトを破損させる投稿を多く目にするため、注意が必要です。これらも有効化時に再起動は不要ですが、最初にインストールする際には再起動が必要かもしれません。詳細は他の人が補足できるでしょう。私のプラグインに関する経験は、管理メニューから有効化するのみです (リスト)。また、Discourse プラグイン作成の初心者ガイド - パート 1もご覧ください。

  3. 私はプラグインを開発したことがないため、おそらく Discourse API Ruby Gem のことを指していると思われます。参照: Use the Discourse API ruby gem

  4. また、API があり、これは Webhook で、通常は curl や他のプログラミング言語と組み合わせて使用されます。これにより Ruby に依存せずに済むため、便利です。

  5. これも試したことはありませんが、PostgreSQL データベースレベルでプログラミングすることも可能です。ただし、非常にスキルが高く、自分の能力に確信がある場合を除き、お勧めしません。

参考になれば幸いです。


編集

Discourse 開発者として本格的に取り組みたい方のためのボーナス情報

参照: Discourse 初心者(私のような者)が何から始めるべきか

@EricGT さんの回答はすでに非常に良く説明されていますが、それに付け加える形で以下に補足します。

  • テーマまたはテーマコンポーネントは、Discourse のフロントエンドである EmberJS アプリのあらゆる部分を修正する方法です。HTML や CSS のカスタマイズという単純なものから、新機能の追加という複雑なものまで多岐にわたります。テーマは、何か問題が発生してもより graceful(優雅)に対処できるため、何かうまくいかなくてもサイト全体がダウンするとは限りません。
  • プラグインは主に Rails のサーバーサイドアプリに影響を与えますが、テーマのすべての機能と EmberJS アプリへの影響も兼ね備えています。ただし、より複雑です。プラグインの失敗は graceful ではない傾向があるため、テーマで実現できる場合はまずそこから始めることをお勧めします。ただし、カスタムルートが必要だったり、データを保存する必要がある場合は、プラグインが必須となります。
  • pluginAPI は、クライアントサイドの API で、テーマやテーマコンポーネントが Discourse クライアントの特定の部分をより容易に修正するために使用できます。

サイトをカスタマイズする際は、まずテーマから始めるのが最善です。以下にいくつかの参考資料をご紹介します。

デザイナー向け Discourse テーマガイド
開発者向け Discourse テーマガイド
Discourse テーマの構築を始めるためのテーマクリエーターとテーマ CLI の使い方に関する初心者向けガイド

ありがとうございます、皆さん。とても参考になりました。私が理解した重要な区別は以下の通りです:

– フロントエンドの表示や挙動のみを変更したい場合は、テーマを作成します。
– バックエンドとの連携が必要な変更を加えたい場合は、プラグインを作成します。

この理解で合っていますか?

具体的な例を挙げて説明します。私が検討しているのは、カテゴリのモデレーターがそのカテゴリ内のトピックをピン留めできるようにする機能です。大まかな流れは以下のようになると思います:

  1. ユーザーがそのカテゴリのモデレーターかどうかを判定する(これはユーザー情報やカテゴリ情報を取得するためにバックエンドへのアクセスが必要)

  2. ユーザーがモデレーターの場合、ピン留めボタンを表示する(これはフロントエンドの処理)

  3. ユーザーがピン留めボタンをクリックした場合、そのトピックを最上部に移動する(Discourse のコード上でどこで処理されるかは確信がありませんが、おそらくフロントエンドとバックエンドの両方に関わる処理かもしれません)

この場合、バックエンドとのやり取りが必要になるため(おそらく 1 で、もしかすると 3 でも)、プラグインを使用する必要があります。この理解で合っていますか?

表面的な部分かもしれませんが、権限やセキュリティに関わるためバックエンドの関与が必要です。フロントエンドに誰がどのような権限を持っているかを判断させることはできません。

つまり、「このユーザーはこのカテゴリのモデレーターか」をプログラム的に判断する際に、フロントエンドとバックエンドの複数のファイルが関与するということでしょうか? Hmm…

テーマ内であれば、ユーザーがモデレーターかどうかを確認でき、Discourse API がフロントエンドから呼び出せるエンドポイントを公開していれば(テーマは JavaScript を使用できるため)、バックエンド呼び出しも可能です。そのため、[1] についてはプラグインは不要なはずです。プラグインが必要になるのは、バックエンドの動作を変更したり、API を公開したりする場合のみです。

ただし、[3] についてはおそらくプラグインが必要です。@merefield さんがおっしゃったように、これは権限とセキュリティに関わる問題だからです(もしバックエンドがモデレーターによるトピックのピン留めをブロックしている場合、それを許可するように変更する必要があります)。

前述の通り、その検証(ユーザーがモデレーターかどうかを確認する)だけなら、おそらくプラグインは不要です。しかし、モデレーターにカテゴリをピン留めさせるというアクションを行うためには、おそらくプラグインが必要になるでしょう。もし Discourse に「すべてのモデレーターがトピックをピン留めできるようにする」オプションがある場合(あるかどうかはわかりませんが)、プラグインは不要です(ただし、ピン留めボタンがモデレーターに表示されていない場合を除きます。その場合、テーマを使用してモデレーターにボタンを表示し、クリック時に JavaScript でエンドポイントを呼び出す必要があるかもしれません)。

テーマとプラグインの違い、また私が挙げた具体的な例についても、とても参考になりました。ありがとうございます。

これまでの私のアプローチは、Discourse のコードの詳細(このオブジェクトはどこで定義されているか、どのテンプレートが制御されているか、このアクションを処理するロジックはどこにあるかなど)を一つずつ確認するものでした。しかし、それは非常に時間がかかる作業でした。

より効率的な方法として、API に焦点を当てるべきだと考えています。そうすれば、成熟した Discourse コードの詳細をすべて確認する必要もなくなりますし、プラグインではなくテーマの構築、あるいは「カスタマイズ」ダッシュボードへの直接の変更入力に集中することもできます。

要するに、Discourse のコードベース全体を理解しようとするよりも、API の仕組みを理解する方がずっと現実的だと感じています。

私が挙げた例で考えると:カテゴリのモデレーターであるユーザーが、カテゴリページでトピックをピン留めできるようにする。

これをプラグインなしで実現できるでしょうか?以下のように考えてみました。

1. ユーザーがカテゴリのモデレーターかどうか?

現在、API を見ても、ユーザーがカテゴリのモデレーターかどうかを示すものが何も見つかりません。カテゴリを取得する GET リクエストに含まれているはずだと思われますが、そのような呼び出しは見当たりません。あるいはユーザーを取得する GET リクエストにあるかもしれませんが、そこにはユーザーがモデレーターを務めるカテゴリの一覧もありません。

これらを追加することは可能でしょうか?

あるいは、ユーザーまたはカテゴリにカスタムフィールドを作成してモデレーターであることを識別し、カテゴリページが読み込まれた際にそのカスタムフィールドを API で呼び出す方法もあるかもしれません。

2. ユーザーがモデレーターの場合、ピン留めボタンを表示する。

(1) が解決できれば、フロントエンドの JavaScript と CSS を使って、ユーザーがモデレーターである場合にのみこのボタンを表示すればよいと考えられます。

3. ユーザー(モデレーター)がボタンをクリックし、トピックをピン留めする。

API 上、トピックには「pinned」という属性(ブール値)があるようです。これはカテゴリ内でのピン留め状態に対応していると思われます(各トピックは通常 1 つのカテゴリに属しているため)。

したがって、モデレーターが「ピン留め」ボタンを押した際に、トピックの「pinned」ステータスを True に更新すればよいでしょう。もしそれが機能しない場合、カスタムフィールドを使う方法もあります(ただし、トピックにカスタムフィールドを追加する方法は現時点では見つかっていません)。


このように、あるいはこれに似た方法で、API を活用すれば、プラグインを使って実装する場合に必要となる Discourse コードベースの膨大なファイル確認を避けつつ、このタスクを達成できそうです。

この考え方で合っていますでしょうか?**

見つけましたか:Discourse API のリバースエンジニアリング方法

以前も見たことがありますが、今、詳しく確認しています。ご指摘ありがとうございます。

以下の点について整理しようとしています:

– 特定のカテゴリのモデレーターに関する情報を API のどこから取得できるか(カテゴリやユーザーに関する返却情報には表示されていないようですが、どこかにはあるはずです)

– API を使ってエントリーに新しいフィールドを追加できるか。例えば、トピックにカスタムフィールドを追加するために API を使えるか?(一般的にトピックにはカスタムフィールドは付いていないと思いますが)

データベースを確認することをお勧めします。

データベースには posts テーブルがあり、そこにフィールドを追加することも可能ですが、そうすると公式のサポート範囲外となり、サポートが受けられなくなります。

ありがとうございます、とても参考になりました。

どのデータベースのことでしょうか?


確認ですが、私の考えでは、プラグインが必要になる可能性のある処理の一部を API を使って行うことです。例えば、私のサイト自体が API を呼び出して、特定のユーザー(または場合によってはグループ)がカテゴリのモデレーターかどうかを判定します。この呼び出しは、カスタマイズパネル、またはテーマやプラグインにハードコードされます。

このような呼び出しを行うには、認証が必要であり、そのためには管理パネルから API キーを作成する必要があります。管理パネルでの API 作成プロセスでは、「説明」と「ユーザーレベル」の入力が求められますが、これがこの場合にどう適用されるかよくわかりません。私の場合、アプリ自身が API 呼び出しを行うものであり、特定のユーザーのために実行されるものではありません。そのため、私の理解が間違っている可能性もあります。

このような API 呼び出しに適した「ユーザーレベル」は何か、あるいはそこに入力すべき内容は何かご存知でしょうか?

Discourseを標準通りにインストールすると、Dockerコンテナ内で動作します。データの永続化はPostgreSQLデータベースを使用して実現されます。

参照: Data Explorerプラグイン

管理者権限をお持ちの場合は、バックアップのいずれかを取得できます。これはSQL形式で圧縮されたtar.gzファイルです。このSQLを使用して、データを別のPostgreSQLデータベースに再読み込みし、さらに多くの操作を行うことができます。

テーマやプラグインを作成したことはなく、Ruby からの API 利用や、他のプログラミング言語での API 利用経験もありません。ただし、バックアップにアクセスできたのは、本番サイトでの管理者権限を持っているためです。また、Prolog でプログラミングしており、Prolog を使ってデータにアクセスし、DCGを用いて投稿を解析しています。BNF をご存知であれば、DCG はそれほど遠くはありませんが、より高度な部分では、構文の統一と後方連鎖を理解する必要があります。

ありがとうございます。この件については別途対応いたします。

いいえ、各アクションへのアクセス権限はサーバー側で強制されるため、プラグインなしではできません。

カテゴリ固有のモデレーターがトピックをピン留めできるようにするためには、lib/guardian/ および lib/guardian.rb 内の関数をオーバーライドする必要があります。その後、プラグイン内でテーマの JavaScript と同様のメカニズムを使用して UI を変更し、適切な場合に「トピックをピン留め」オプションが表示されるようにします。

ああ、なるほど。つまり、その用途で API を使うのはうまくいかないんですね(あなたの返信のおかげで、多くの時間を節約できそうです)。

通常のピン留め動作とは少し異なる方法で試してみようと思います。特定のユーザーにカテゴリに対する「所有権」権限を与え、それによってカテゴリ内の特定のトピックを強調表示できるようにするのです。

JSON API を使う場合のアプローチは以下の通りです。カスタマイズダッシュボードから、関連するユーザーにカスタムフィールド(例:category-name: owner)などを付与します。そして、カテゴリページが読み込まれた際に API を呼び出してユーザーを評価します。もしそのユーザーに該当のカスタムフィールドがあれば、「強調表示」ボタンを表示します。その後、ユーザーがトピックの「強調表示」ボタンを押すと、そのトピックをカテゴリの強調表示グループ(これもカテゴリ用のカスタムフィールド)に割り当てます。

これは大まかなスケッチなので、各ステップを確認する必要はありません(コーディング時に調整が必要になるかもしれません)。さて、今の質問ですが、JSON API をこのように使うこと、特に Discourse アプリ内でカスタムフィールドの作成と取得を行うことは可能でしょうか?

@JQ331

しかし、現時点での私の質問は、Discourse アプリ内でカスタムフィールドの作成や取得を行うために、このように JSON API を利用することは可能でしょうか?

テーマコンポーネント、プラグイン、Discourse API の違いに関する質問には、素晴らしい回答が寄せられています。私がさらに価値を追加できる余地は少ないかもしれませんが、参考になるかどうかは別として、別の視点から説明させていただきます。

テーマコンポーネントもプラグインも、Ember.js のライフサイクル内でコードを評価するためにテンプレートフック(プラグインフック)を使用します(これは学ぶ価値があります、余談ですが)。

さらに、Discourse API はテーマコンポーネントとプラグインの両方から利用可能です。

API は基本的に、基盤となる PostgreSQL データベースのデータの一部(すべてではありません)を公開します。

機能開発を行う際は、まず API を確認し、必要なデータが API から取得可能かどうかを判断することをお勧めします。

もし API にない必要なデータがある場合は、PostgreSQL データベースを確認して、そのデータが DB に存在するか調べる必要があります。

必要な追加データが DB に存在する場合は、そのデータを公開する必要があります。一般的には、Discourse のデータシリアライザーにデータを追加し、API を拡張することで実現します。

データシリアライザーとは、API によって公開される JSON オブジェクトを作成するプロセスのことです。これにより、さらに多くのオブジェクトを追加して拡張することが可能です。

PostgreSQL データベースの確認方法については、GitHub のコードを読むなど様々な方法があると思いますが、私は直接 DB にログインし、DB 内のテーブルを確認して、テーブルの構造や各テーブルに含まれるフィールドを基本 SQL を使って調査しています。

つまり、要約すると(簡潔にまとめます)、API と DB テーブルの両方を参照資料として持つ必要があります。一般的に、API で標準提供されていないデータを追加して「API を拡張」したい場合は、そのためのプラグインを作成します。そうすることで、そのデータは拡張された API とともに公開され、テーマコンポーネントとプラグインの両方でそのデータを利用できるようになります。

この視点が、少しでもお役に立てれば幸いです。

素晴らしい説明です。この回答を本当にありがとうございます。これまで気づいていなかった点が浮き彫りになりました。

これらの回答から、(あなたが言及しているように)JSON API とのやり取りは多くの場合、新しいテーマやプラグインを作成する必要を回避するための良い出発点になると考えられます。ただし、API によって公開されていないデータの種類もいくつかあります。そのようなデータタイプにアクセスして処理を行うには、Discourse のデータシリアライザーを使用してデータを公開する必要があります。そのシリアライズを行うには、プラグインを使用する必要があります。

API 経由で利用できないデータの好例として、グループのオーナーが挙げられます。これは(グループオーナーへのアクセスに関して)以下の通りです。

一つの混乱点として、Discourse API で特定のグループを取得すると、返される特性の一つに "is_group_owner": true という項目がリストされています。これが何を意味するのかよくわかりません…

しかし、グループオーナーを取得するには、グループオーナーの特性をシリアライズする必要があるようです。


Discourse シリアライザーの優れた使用例はありますか?こちら を見たことはありますが、その重要性を考えると、いくつかの例を交えたハウツーガイドが非常に役立つでしょう。

私が持っている最も近い例は以下の通りです。

これは役立ちますが、完全に正しいわけではありません(少なくとも「無効なプラグイン」というエラーが出ます)。これを調整して、グループ一覧ページで各グループのオーナーにアクセスできるようにする方法がわかりません。

プラグインの例の使い方がどうだったのかはわかりませんが、私が別のトピックで投稿したファイル構造とコードを使用すれば、開発インスタンスでは正常に動作していました。

is_group_owner は、現在のユーザーがグループを表示するコンテキストで使用されます。

Ajax 呼び出しを通じて所有者情報を取得することは可能ですが、私の知る限り、リスト内の各グループ個別に行う必要があり、結果として多数のリクエストが発生する可能性があります。この方法で動作させるのは全体的に難しいと思います。いずれにせよ、実験したい場合は、テーマ内で以下のプロトタイプコードスニペットを試すことができます。GROUP_NAME をご自身のグループ名のいずれかに置き換えてください。(編集:Ajax 呼び出しの活用方法の例を示すテーマコンポーネントもこちらにあります: https://github.com/awesomerobot/discourse-featured-topics/blob/master/common/head_tag.html)

<script type="text/discourse-plugin" version="0.8.40">
  const { ajax } = require("discourse/lib/ajax");
  ajax(`/groups/GROUP_NAME/members.json`).then(response => {
    console.log(response.owners.map(owner => owner.username))
  });
</script>

これらを踏まえると、プラグインを使用することが間違いなく最も簡単でクリーンな解決策です。