Discourse コードベースの継続的な改善の一環として、レガシーな「ウィジェット」レンダリングシステムの使用を廃止し、Glimmer コンポーネントに置き換えています。
最近、投稿メニューをモダン化し、glimmer_post_menu_mode 設定で Discourse 内で利用可能になりました。
この設定では、以下の 3 つの値を受け付けます。
disabled: レガシーな「ウィジェット」システムを使用auto: 現在のプラグインとテーマの互換性を検出します。互換性がないものが含まれている場合はレガシーシステムを使用し、そうでない場合は新しいメニューを使用します。enabled: 新しいメニューを使用します。互換性がないプラグインやテーマがある場合、サイトが破損する可能性があります。
公式プラグインはすでに新しいメニューと互換性があるように更新されていますが、まだ新しいメニューと互換性がないサードパーティ製プラグイン、テーマ、またはテーマコンポーネントがある場合は、それらのアップグレードが必要です。
ブラウザのコンソールには、互換性の問題の原因を特定する警告が出力されます。
展開スケジュール
これらは変更される可能性がある概算です
2024 年第 4 四半期:
コア実装完了
公式プラグインの更新
Meta での有効化
glimmer_post_menu_modeのデフォルトをautoに設定、コンソールでの非推奨メッセージを有効化
アップグレードに関するアドバイスの公開
2025 年第 1 四半期:
サードパーティ製プラグインとテーマの更新完了
非推奨メッセージの開始により、残存する問題に対して管理者警告バナーが表示される
新しい投稿メニューのデフォルト有効化
2025 年第 2 四半期
4 月 1 日 - 機能フラグ設定とレガシーコードの削除
これは私にとって何を意味しますか?
プラグインやテーマが投稿メニューのカスタマイズに「ウィジェット」API を使用している場合、それらは新しいバージョンとの互換性のために更新する必要があります。
新しい投稿メニューを試すには?
Discourse の最新バージョンでは、互換性のないプラグインやテーマがない場合、新しい投稿メニューが自動的に有効になります。
互換性のない拡張機能がインストールされている場合、管理者として設定を enabled に変更して、新しいメニューを強制的に使用することもできます。ただし、インストールされているカスタマイズによってはサイトが破損する可能性があるため、この設定は注意して使用してください。
稀なケースですが、この自動システムが期待通りに動作しない場合は、上記の設定を使用して一時的にこの「自動機能フラグ」をオーバーライドできます。その必要がある場合は、このトピックでお知らせください。
プラグインやテーマを更新する必要がありますか?
以下のいずれかのカスタマイズを行っている場合、プラグインやテーマの更新が必要です。
-
以下のウィジェットに対して
decorateWidget、changeWidgetSetting、reopenWidget、またはattachWidgetActionを使用する:post-menupost-user-tip-shimsmall-user-list
-
以下の API メソッドのいずれかを使用する:
addPostMenuButtonremovePostMenuButtonreplacePostMenuButton
上記のカスタマイズを実行する拡張機能がある場合、トピックページにアクセスすると、アップグレードが必要なプラグインやコンポーネントを特定する警告がコンソールに出力されます。
非推奨 ID は
discourse.post-menu-widget-overridesです。
インスタンスで複数のテーマを使用している場合は、警告がアクティブなプラグインと現在使用されているテーマおよびテーマコンポーネントに対してのみ出力されるため、すべてのテーマを確認してください。
代替手段は何か?
投稿メニューのカスタマイズ用の新しい API として、値トランスフォーマー post-menu-buttons を導入しました。
この値トランスフォーマーは DAG オブジェクトを提供し、ボタンの追加、置換、削除、または並べ替えを可能にします。また、メニューに関連する投稿、表示されている投稿の状態、アイテムの配置を容易にするためのボタンキーなどのコンテキスト情報も提供します。
DAG API は、.add や .replace のように新しいボタン定義が必要な場合、Ember コンポーネントを受け取ることを想定しています。
各カスタマイズは異なりますが、最も一般的なユースケースに対するガイダンスを以下に示します。
addPostMenuButton
以前:
withPluginApi("1.34.0", (api) => {
api.addPostMenuButton("solved", (attrs) => {
if (attrs.can_accept_answer) {
const isOp = currentUser?.id === attrs.topicCreatedById;
return {
action: "acceptAnswer",
icon: "far-check-square",
className: "unaccepted",
title: "solved.accept_answer",
label: isOp ? "solved.solution" : null,
position: attrs.topic_accepted_answer ? "second-last-hidden" : "first",
};
}
});
});
以後:
以下の例では、Ember の テンプレートタグ形式 (gjs) を使用しています。
// components/solved-accept-answer-button.gjs
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d_button";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default class SolvedAcceptAnswerButton extends Component {
// ボタンが即座に表示されるか、「もっと表示」ボタンの背後に隠されるかを示す
static hidden(args) {
return args.post.topic_accepted_answer;
}
...
<template>
<DButton
class="post-action-menu__solved-unaccepted unaccepted"
...attributes
@action={{this.acceptAnswer}}
@icon="far-check-square"
@label={{if this.showLabel "solved.solution"}}
@title="solved.accept_answer"
/>
</template>
}
// initializer.js
import SolvedAcceptAnswerButton from "../components/solved-accept-answer-button";
...
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({
value: dag,
context: {
post,
firstButtonKey, // 最初のボタンのキー
secondLastHiddenButtonKey, // 2 番目に隠されたボタンのキー
lastHiddenButtonKey, // 最後の隠されたボタンのキー
},
}) => {
dag.add(
"solved",
SolvedAcceptAnswerButton,
post.topic_accepted_answer
? {
before: lastHiddenButtonKey,
after: secondLastHiddenButtonKey,
}
: {
before: [
"assign", // assign プラグインによって追加されたボタン
firstButtonKey,
],
}
);
}
);
});
ボタンのスタイリング
上記の例のように、コンポーネントに
...attributesを含めることをお勧めします。これを
DButtonまたはDMenuコンポーネントの使用と組み合わせることで、ボイラープレートクラスを処理し、投稿メニュー内の他のボタンと同様のフォーマットに従うようにボタンを確保できます。追加のフォーマットは、カスタムクラスを使用して指定できます。
replacePostMenuButton
- 以前:
withPluginApi("1.34.0", (api) => {
api.replacePostMenuButton("like", {
name: "discourse-reactions-actions",
buildAttrs: (widget) => {
return { post: widget.findAncestorModel() };
},
shouldRender: (widget) => {
const post = widget.findAncestorModel();
return post && !post.deleted_at;
},
});
});
- 以後:
import ReactionsActionButton from "../components/discourse-reactions-actions-button";
...
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { buttonKeys } }) => {
// ReactionsActionButton は新しいボタンコンポーネントです
dag.replace(buttonKeys.LIKE, ReactionsActionButton);
}
);
});
removePostMenuButton
- 以前:
withPluginApi("1.34.0", (api) => {
api.removePostMenuButton('like', (attrs, state, siteSettings, settings, currentUser) => {
if (attrs.post_number === 1) {
return true;
}
});
});
- 以後:
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, buttonKeys } }) => {
if (post.post_number === 1) {
dag.delete(buttonKeys.LIKE);
}
}
);
});
他のカスタマイズについてはどうなりますか?
新しく導入した API を使用してカスタマイズを実現できない場合は、新しい開発トピックを作成してご相談ください。
プラグイン/テーマ作成者です。移行期間中に新旧両方の投稿メニューをサポートするようにテーマ/プラグインを更新するにはどうすればよいですか?
プラグインで新旧両方の投稿メニューのバージョンをサポートするために、以下のパターンを使用しています。
function customizePostMenu(api) {
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context }) => {
// 新しい投稿メニューのカスタマイズ
...
}
);
const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";
withSilencedDeprecations(silencedKey, () => customizeWidgetPostMenu(api));
}
function customizeWidgetPostMenu(api) {
// レガシーな「ウィジェット」コードのカスタマイズ
...
}
export default {
name: "my-plugin",
initialize(container) {
withPluginApi("1.34.0", customizePostMenu);
}
};
さらに多くの例
新しい API の使用方法の例については、公式プラグインを確認してください。