今後のヘッダー変更 - テーマとプラグインの準備

We’ve recently been working on updating Discourse’s header from the legacy ‘widget’ rendering system to modern Glimmer components. This change is now available in Discourse core behind the glimmer header mode site setting.

:timer_clock: Approximate Timeline

(very rough estimates - subject to change in either direction)

Q1 2024:

  • :white_check_mark: core implementation finished & enabled on Meta

  • :white_check_mark: upgrade advice published; console deprecation messages enabled

  • :white_check_mark: work begins to update all official and third-party plugins/themes

Q2 2024:

  • :white_check_mark: start enabling new header implementation by default

  • :white_check_mark: official and third-party themes/plugins are ready for the upgrade

  • :white_check_mark: Deprecation messages start triggering an admin warning banner for any remaining issues

Q3 2024:

  • :white_check_mark: Announcement topic posted for wider visibility: Preparing your community for behind-the-scenes header changes

  • :white_check_mark: w/c 5th August 2024 (v3.4.0.beta1): new header enabled for all sites by default. It will still be possible for admins to switch back to the old header by toggling the ‘glimmer header mode’ site setting.

  • :white_check_mark: w/c 2nd September 2024: final removal of feature flag and legacy code

:eyes: What Does it Mean for Me?

If your plugin or theme uses any ‘widget’ APIs to customize the header, those will need to be updated for compatibility with the new header.

:person_tipping_hand: How Do I Try the New Header?

In the latest version of Discourse, the new header is automatically enabled when all your themes/plugins are compatible.

If your themes/plugins are not compatible, then the legacy header will still be used, and a warning will be printed to the console alongside the existing deprecation messages. A warning banner will also be shown to admins in the UI.

In the unlikely event that this automatic system does not work as expected, you can temporarily override this ‘automatic feature flag’ via the glimmer header mode site setting. If you do that, please let us know the reason in this topic.

:technologist: Do I Need to Update My Plugin/Theme?

To determine whether your customization needs to be updated, check if it uses decorateWidget, changeWidgetSetting, reopenWidget or attachWidgetAction on any of these widgets:

  • header
  • site-header
  • header-contents
  • header-buttons
  • user-status-bubble
  • sidebar-toggle
  • header-icons
  • header-topic-info
  • header-notifications
  • home-logo
  • user-dropdown

or uses one of these plugin API methods:

  • addToHeaderIcons
  • addHeaderPanel

All of these things will now cause deprecation messages to be printed to the console. Deprecation IDs are:

  • discourse.add-header-panel
  • discourse.header-widget-overrides

:warning: If you use more than one theme in your instance, be sure to check all of them.

Admin notice

As of June 20, 2024, we’ve enabled the admin notice for the deprecations above.

If your instance was deployed after this date and your instance’s current plugins, theme, or theme components triggers one of the deprecation warnings, the following message will be displayed only for the admins*:

This message is just to alert the admins that they need to take action soon to modernize the affected customizations: the old customizations will still work until we remove the legacy codebase.

:twisted_rightwards_arrows: What Are the Replacements?

Each theme/plugin is different, but here is some guidance for the most common use cases:

addToHeaderIcons

:information_source: For custom header icons, we recommend removing your code and installing the official Custom Header Links (Icons) Theme Component. If that doesn’t meet your requirements, see below for information for details on the required code changes:

The addToHeaderIcons plugin API has been deprecated in favor of the new headerIcons API. It exists to allow adding, removing, or re-ordering of icons in the header. It requires a Component to be passed.

The component can be passed as so:

Before After
api.addToHeaderIcons(“widget-foo”) api.headerIcons.add(“foo”, FooIcon)
api.decorateWidget(“header-icons:before”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooIcon, { before: “search” })
api.decorateWidget(“header-icons:after”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooComponent, { after: “search” })

This example uses Ember’s Template Tag Format (gjs) to define a component inline and pass it to the headerButtons.add API:

// .../discourse/api-initializers/add-my-button.gjs

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li><DButton class="icon btn-flat" @href="/u" @icon="address-book" /></li>
  </template>);
});

Or for a dropdown, you could use <DMenu instead of <DButton:

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";
import DMenu from "float-kit/components/d-menu";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li>
      <DMenu class="icon btn-flat" @icon="address-book">
        <DButton @translatedLabel="User 1" @href="/u/user1" />
        <DButton @translatedLabel="User 2" @href="/u/user2" />
        <DButton @translatedLabel="User 3" @href="/u/user3" />
      </DMenu>
    </li>
  </template>);
});

Example upgrade commits:

decorateWidget("header-buttons:*")

:information_source: For custom header links, we recommend removing your code and installing the official Custom Header Links Theme Component. If that doesn’t meet your requirements, see below for information for details on the required code changes:

The header-buttons widget has been deprecated and we have introduced a headerButtons plugin API. It exists to allow adding, removing, or re-ordering of buttons in the header. It requires a Component to be passed.

Before After
api.decorateWidget(“header-buttons:before”) api.headerButtons(“button-name”, ButtonComponent, { before: “auth” })
api.decorateWidget(“header-buttons:after”) api.headerButtons(“button-name”, ButtonComponent, { after: “auth” })

changeWidgetSetting(...) for the header widgets

:information_source: The most common uses of changeWidgetSetting can be achieved using these theme components:

If these don’t fit your use-case, read on…

Some customizations on the header widgets were using the changeWidgetSetting API.

Although, there is no direct replacement for customizations like the one above, due to how the Glimmer components fields work, we introduced a new plugin API on Discourse 3.3.0.beta3 to handle some of these cases.

registerValueTransformer can be used to override values that were tagged in the source code as overridable, this is a similar approach to how plugin outlets work.

We already added two transformers for the use cases we found to be common in our source code base:

  • home-logo-href: can be used to override the URL in the home logo anchor. See the section home-logo below for examples.

  • header-notifications-avatar-size: can be used to change the size of the image fetched to the user avatar in the header. Example:

The code below:

api.changeWidgetSetting(
  "header-notifications",
  "avatarSize",
  settings.header_avatars_size
);

Would be converted to:

api.registerValueTransformer(
  "header-notifications-avatar-size",
  () => settings.header_avatars_size
);

These transformers need to be added to the Discourse source code. If you need a different one, please let us know posting your use case below.

More details about the new value transformer APIs can be found here.

home-logo

We have introduced a home-logo plugin outlet in replacement of home-logo:before or home-logo:after widget decorations. You can utilize the automatic __before and __after naming in your connector file to specify where your custom content should be placed.

More details on before/after connector file naming can be found here.

Before After
api.decorateWidget(“home-logo:before”) Move content to /connectors/home-logo__before
api.decorateWidget(“header-buttons:after”) Move content to /connectors/home-logo__after)

Altering the home-logo anchor URL:

A very common need is altering the URL the home-logo links to. We introduced the home-logo-href value transformer to address this. Examples:

  • to change the link to a static URL

    api.registerValueTransformer("home-logo-href", () => "https://example.com");
    
  • to return a dynamic URL based on the current user

    api.registerValueTransformer("home-logo-href", () => {
      const currentUser = api.getCurrentUser();
      return `https://example.com/${currentUser.username}`;
    });
    
  • to return a URL based on a theme-component setting

    api.registerValueTransformer("home-logo-href", () => {
      return settings.example_logo_url_setting;
    });
    

:sos: What about other customizations?

If your customization cannot be achieved using CSS, PluginOutlets, or the new APIs we’ve introduced, please let us know by creating a new Dev topic to discuss.

:sparkles: How do I update a theme/plugin to support both old and new header?

All the new APIs and plugin outlets listed in this document are supported on both the new and the old header. So you only need to make one update to your theme/plugin now, and users will be ready for the switch.

「いいね!」 31

しかし、FooIcon をどのように定義すればよいのでしょうか?

プラグインで /assets/javascripts/discourse/components/server-link.js を作成しようとしました(他のコンポーネントを hbs ファイルで使用しているように)。

import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";

export default Component.extend({
// 何かを入れる必要がありますか?
});

そして、assets/javascripts/discourse/templates/components/server-link.hbs
this is a link
(この「Hello, world」が機能すれば、リンクにできると思います)

上記の例には const IconWithDropdown = ... がありますが、それはどこに配置すればよいのでしょうか? 初期化子(api.decorateWidget があった場所)に入れようとしましたが、私や Ember にとっては有効な JavaScript ではなさそうです。

以前は headerlinks 配列があり、次のようにしていました。

        headerLinks.push(
          h(
            `li.headerLink.no-servers`,
            h("a", anchorAttributes, I18n.t("pfaffmanager.no_servers_title"))
          )
        );

リンクを追加するために。もし

      api.headerIcons.add("foo", ServerLink, { before: "search" });

が機能すれば、その配列を構築したループにそれを配置できると思います。

「いいね!」 1

OMG。Glimmerコンポーネントはassets/javascripts/discourse/componentに、Embrerコンポーネントはassets/javascripts/discourse/componentsに入るということですか?!?!

これでserver-link.gjsができました。

import Component from "@ember/component";
export default class ServerLink extends Component {
  // URLに必要な引数
  url = null;
  // リンクテキストのオプション引数
  text = 'asdf';
  click() {
    console.log('ServerLink clicked!',this);

  }
  // コンポーネントのテンプレート
  <template>
    {{log "my template" this}}
    LINK!
    <a href={{this.url}}>{{this.text}}</a>
  </template>
}

そして、イニシャライザーにこれがあります。

      api.headerIcons.add("foo", ServerLink, { param: "url, yo", before: "search" });

これでヘッダーに何か表示されました。

しかし、ServerLinkにどうやって値を渡せばいいのでしょうか?異なるURLと異なるクリック用テキストで数回呼び出す必要があります。コンポーネント内の{}にあるものが表示されません。

また、<template>の前にJavaScriptを入れたくないですよね。私のconsole.log("")は解析されません!

以下も試しました。

      const x = new ServerLink({
        url: "mylink",
        text: "my-text",
        name: 'Bob',
        message: 'Generated from JavaScript',
      });

そして、ServerLinkの代わりにxを渡しましたが、うまくいきませんでした。

「いいね!」 2

ヘッダーに異なるアイコン/テキスト/URLを持つ複数のボタンが必要ですか、それとも同じボタンですが、コンテキストに応じてテキスト/URLを変更したいということですか?

はい、クラス内です。そこで変数、関数、またはテンプレートを宣言します!

はい。リンクはユーザーごとに異なります。古いコードは servers の配列を通じてリンクし、それらをこの配列に追加していました。

            headerLinks.push(
              h(
                `li.headerLink${deviceClass}${newClass}`,
                h("a", anchorAttributes, linkText)
              )
            );

そして、これを実行しました。

      // api.decorateWidget("header-buttons:before", (helper) => {
      //   return helper.h("ul.pfaffmanager-header-links", headerLinks);
      // });

これにより、ヘッダーに最大3つのリンクが追加され、それぞれが個別のサーバーURLにリンクされていました。

なるほど。そういうことだったんですね。

いいえ、心配しないでください。規約はまだ/components/です :sweat_smile:

(技術的には、GJSコンポーネントはどのようにでも定義して渡すことができるため、好きなディレクトリ名を選ぶことができます。しかし、私たちは/components/にこだわっています。)

はい、それはもっともな質問です。ヘッダーにアイコンを追加する方法について、ゼロからドキュメントを作成します。そうすれば、より良い参照点が得られます。

それまでの間、discourse-icon-header-linksの更新を参考にするとよいでしょう。GJSを使用するクールな点は、コンポーネントをどこにでも定義でき、ローカルスコープの変数にアクセスできることです。

したがって、イニシャライザーを.gjsにリネームすると、次のようなことができます。

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, <li><DButton @translatedLabel={{server.name}} @icon="server" /></li>);
});

または、同じファイル内のより早い段階でコンポーネントを定義し、次のように使用することもできます。

class ServerButton extends Component {
  get icon(){
    // アイコンを決定するロジック
  }
  <li><DButton @translatedLabel={{@server.name}} @icon={{this.icon}} /></li>
}

...

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, <ServerButton @server={{server}} />);
});

または、サーバーのリストが実行時に変更される可能性のあるTrackedArrayである場合に便利な、テンプレート内にイテレーションを移動することもできます。

api.headerIcons.add("server-buttons", {{#each servers as |server|}}
  <ServerButton @server={{server}} />
{{/each}});
「いいね!」 7

より詳細なサンプルコードを作成している間

ああ。やったー。components で試したのですが、うまくいかなかったと思ったのです。

ありがとうございます!これらのうちの1つを機能させることができると思います。ヘッダーリンクへのリンクは大きな助けになります。私がコードを書いたとき、それを理解するためにそのコンポーネント自体を見たのに十分な判断力があったと確信しています。

「いいね!」 4

希望の兆しが見えてきた!

@david@Arkshine さん、やりました!

ななな、なんだって?ただ名前を変えるだけ?それはクレイジーだ。でも、確かに。やってみたら、今では

          servers.filter(Boolean).map((server) => {
            const linkHref = `/pfaffmanager/servers/${server.id}`;
            const linkTitle = `click to configure server ${server.id}`;
            let host = String(server.hostname);
            const linkText = host.replace(
              /www.|community.|forums?.|talk.|discourse./,
              ""
            );
            const serverLink = <template>
              <li class="headerLink">
                <a class="btn-flat" href={{linkHref}} title={{linkTitle}}>
                  {{host}}
                </a>
              </li>
            </template>;
            const beforeIcon = ["chat", "search", "hamburger", "user-menu"];
            api.headerIcons.add(host, serverLink, { before: beforeIcon });
          });

そして、以前と同じように動作しました。それが私が望んでいたことです!

ええ、それはとてもクールで、私が望むものにさらに近いですが、それらのことはあまり頻繁には変更されないので、今日はこれで終わりにします。ホスト名を変更した場合は、リンクを変更するためにページをリロードする必要があります。

上記を更新する際に、私の余分なゴミは削除されると思いますが(そうでなければ、ドキュメントトピックであまりおしゃべりしなかったかもしれません…)。

「いいね!」 5

OP を完全に機能する gjs の例で更新し、Ember のアップストリーム ドキュメントへのリンクを含めました。 @pfaffman さん、どのように見えますか? 追加する価値があると思われるものは他にありますか?

「いいね!」 1

動作する例があるので、以前より良くなりました。しかし、EmberコンポーネントとGlimmerコンポーネントがあるという理解で合っていますか?もしそうなら、Glimmerコンポーネントが必要であると述べるべきだと思います。

また、それらがどのように機能するかについてのGlimmerのドキュメントへのリンクも追加した方が良いかもしれません。

例のようにインラインコンポーネントを使用することもでき、同じファイル内の変数に割り当てるか、コンポーネントディレクトリに配置してインクルードする別のファイルに配置する別のタイプもあるようです。このトピックには情報が多すぎるかもしれませんが、それに関する専用のトピックがあれば嬉しいです。

「いいね!」 2

これらは完全に相互に交換可能です。クラシックなエンバーコンポーネントまたはグリマーコンポーネントを使用できます。どちらの場合も、古い形式の .js/.hbs または新しい形式の .gjs を使用して作成することを選択できます。

エンバーのドキュメントへのリンクをいくつか含めるようにします :+1:

「いいね!」 4

:mega: 本日、この変更をマージしました。これにより、互換性のあるテーマ/プラグインを使用しているサイトでは、新しいヘッダー実装が自動的に有効になります。

テーマ/プラグインが互換性がない場合、レガシーヘッダーが引き続き使用され、既存の非推奨メッセージと共にコンソールに警告が表示されます。近い将来、このコンソール警告はUIの警告バナーにアップグレードされます。

この自動変更により問題が発生する可能性は低いですが、万が一問題が発生した場合は、glimmer header mode サイト設定を通じて、この「自動フィーチャーフラグ」を一時的に無効にすることができます。その場合は、このトピックで理由をお知らせください。

「いいね!」 3

変更するつもりはありませんでしたが、非推奨の通知がそうではないことを示しています。

したがって、選択肢があり、現状維持を続ける簡単な方法があるのでしょうか?

または

古いヘッダーを維持することを選択した場合、何が欠けていますか?新しいヘッダーの意味がわかりません。グループ設定があり、異なるグループへのカスタムテーラリングは興味深いですが、何がカスタム可能ですか?

今日見つけたのはこれです。

私はこれらの変更に関してグルやウィズではありません。時間がかかり、ユーザーがここで簡単に理解/知っているように見えるテクニックを学ぶことを本当に望むほど頻繁には行いません。

そもそもそれらを行うことをいくらか不本意に思っていますが、プリンがなくなった老人のように叫ぶ前に、何が、なぜ、どこに向かっているのかを知りたいです。

私はこれを生業としていますが、それでもこのJavaScriptは簡単ではないと感じています。

私は老人で、あなたの痛みがわかります。

残念ながら、これは進歩です。このEmberのアップグレードは多くのものを壊しましたが、まだ終わっていません。

そのカスタマイズをしたとき、あなたは「それを求めた」のです。過去5年間で新しい電話やラップトップを手に入れたのではないでしょうか。

もし私があなただったら(そしてあなたが私のような人で、フルタイムのDiscourseの仕事をしていないとしたら)、#marketplaceに投稿するでしょう。もし私が私だったら、300ドル未満では応答しない可能性が高いですが、誰かが100ドルか200ドルで応答する可能性は十分にあります。それが再び壊れるのは、さらに5年以上先のことだと思います。

ハンバーガーテーマセレクターは削除して、サイドバーを使用できると思います。

「いいね!」 3

丁寧で正直な返信、ありがとうございます。しかし、あまり作業するものがありません。おそらく、もっと何か出てくるでしょう(そう願っています)。

そもそも、ここで扱っているのがJavaだとは知りませんでした :man_shrugging:

あなたのプリン(分け前)を誰かに奪われたくないですからね :face_with_hand_over_mouth:

ええ、しかし、目指すべき目標は何なのでしょうか?このソフトウェアはあまりにも多くのことに手を出しているので、誰が何を見ているのか疑問に思います。

これは単にEmberのアップグレードから必要とされたのでしょうか?

Emberがなぜ行われたのかもわかりませんが、もしそれが機能しているのであれば、なぜ修正する必要があるのでしょうか?すべてが将来につながる長い深い説明があるはずですが、共有される真のビジョンはないのでしょうか?

私は非常に古いソフトウェアを使用している他のフォーラムを訪れます。個人的には、Discourseはそれらのどれよりもはるかに優れていると思いますが、彼らは比較において苦しんでいるようには見えません。彼らは同じ成長の問題を抱えています。ほとんどは、私の意見では、個性対ソフトウェアであり、プリンを失った古参が多すぎます。将来、それらのフォーラムを文字通り時代遅れにするIoTはありますか?それらはまったく機能しなくなり、Discourseはそれを認識して準備しているのでしょうか?

あなたが提供する正直さのさらに良い例ですね :grin: その通りです。そして、私はもっと学びたかった、もっと野心的だった、より価値があると感じていました。それ以来、打ちのめされ、轢かれ、死んだように放置されてきました。

よし、その賭けに乗った。あなたはすでに負けたので、これで私を助けてくれるだろう。友達になろう。

そうしたら、おそらくずっと前に辞めていただろう。打ちのめされ、轢かれ、死んだように放置されたというのは、少し婉曲的な表現でした。私だけがハンドルを握っています。他の誰かは、ハイパードライブを修理しようとしているどこかのパネルにいるのでしょう。私は他の人とあまりコミュニケーションをとらないので、わかりません。資金はありません。私たち(FULL30)はソーシャルメディアとDiscourseからもプラットフォームから追放されました。Discourseが自発的にどれだけの有料顧客を切り捨てたのか、またはその信念が攻撃的だと見なされたためにDiscourseが公にお金をかけて反対した顧客がどれだけいたのか疑問に思いますか?

しかし、真実を語る間、私は気分を害しません。生きて、そして生きさせよ。未来が来ることはわかっています。なぜ私がまだここにいて、まだ試みているのかはわかりませんが、試みています。だから、AAのように、今日だけ試み続けます :hugs:

しかし、私がそれを採用したときは、すべてが流行っていました :expressionless:

サイドバー(ここでは)はハンバーガーメニューで閉じることができます。機能に大きな違いはありません。ナビゲーションウィンドウを開閉しますが、私のものは簡単に保存できませんか?

はい、カスタマイズしたコードを整理して、物事をうまく機能させるために誰かに支払いたいと思っています。喜んで支払います。他人を雇用すること、富を分かち合うことを楽しんでいます。成長したら慈善家になりたいですが、今日は慈善家が必要です :innocent: そして、再び、他人が提供できるどんな助けにも感謝します。

もう一つの方法は、コミュニティに助けを求めることです。カスタマイズしていた作業を中止し、コードを共有する新しいトピックを開始して助けを求めてください。最近、そのような件で多くの助けを得ました。

「いいね!」 2

残念ながら、そうではありません。「古いヘッダー」に固執する機能は、移行期間中のあくまで一時的なものです。すぐに、新しいヘッダーのみが唯一の選択肢となります。

はい!Dev での質問にはいつでも喜んでお手伝いします。さらに、コードとソリューションを公開することで、他の人にとって役立つリソースが作成されます。

「いいね!」 4

ふぅ、私のコミュニティは他の問題にもっと精通しています。

確かにここで共有することはできますが、それは逆効果になります。どのようなコーダーが私たちを助けることに興味を持つでしょうか?

皮肉なことに、コーディングは将来の銃器になる可能性があり、さらに多くの死と破壊を引き起こす可能性さえあります。話が逸れました。

なるほど、それは私にとってどういう意味ですか?ユーザーグループを作成し、公開して、ログインしていない状態にするのでしょうか?

これらのグループ設定は、実際の異なるグループ(狩猟グループや釣りグループなど)ではなく、信頼レベルに基づいていると認識していますか?

まず、目標を理解し、私の努力が最も効果を発揮する場所、つまり時間とフォーラムのカスタム感を節約できる場所を理解する必要があります。


誰かのスレッドを脱線させたくありません。これが独自のスレッドと見なされるべきであれば、私はそれで構いません。

しかし、人々が不快に感じるものを削除する必要がある場合、真にまとまりのある関係がどのようにして生まれるのでしょうか?

他者を理解するには忍耐が必要です。削除されたリンクは、投稿中は表示されませんでしたが、ロゴが見つからないことを示していました。これも議論すべきヘッダーの問題ですか?

それは私のフォーラムでの投稿で、80歳を過ぎたと思われる男性によって書かれたものでした。確かに尋ねることはできますが、彼は私と話すことを拒否します。彼を叱責、禁止、または避けるべきですか?

いいえ、なぜなら、より良い方法があるからです。しかし、それは他者とその考えを受け入れることを意味します。私は悪い地域で良い人々を見つけ、悪く見える良い人々、そしてその逆も見つけます。

まさに、エラーを見つけたばかりで、それに対処したいのですが、将来が進んでいるという以外に根本原因を理解していません。新しいヘッダーが必要だ、それはいいとして、どこに焦点を当てるべきか、単純な調整か、全面的なコース修正か?

これらの3つの領域のみを修正する必要がある議論をしていますか?

コンポーネントの使用が混在しています。最初は何も使用していませんでしたが、それらが有益であると学びました。コンポーネントを完全に導入したことはなく、さまざまなものが混在しています。

参考までに、コンポーネントなしの私のテーマは次のとおりです。
discourse-full30-ii.zip (10.1 KB)
それらも投稿できます。モダールの一部は、最近すでに機能していません。