(不推荐)从主题或插件覆盖 Discourse 模板

理想情况下,当通过主题/插件自定义 Discourse 时,您应该使用 CSS、JavaScript 插件 API插件插口 (plugin outlets)。如果这些方法都不适用于您的情况,请随时向 Discourse 核心提交 PR 或在 Meta 上开启一个 Dev 主题。我们总是乐于讨论添加新的插口/API 以简化自定义。

如果您已尝试所有其他选项,则可能需要诉诸模板覆盖。此技术允许您从主题/插件覆盖任何 Ember 组件或路由的整个模板。

:rotating_light: 这不是推荐的自定义 Discourse 的方法。 Discourse 核心的日常更改最终与您的模板覆盖冲突,可能在渲染论坛时导致灾难性错误。

如果您决定采用此方法,请确保您有足够的自动化测试和质量保证流程来检测回归。如果您分发带有模板覆盖的主题/插件,请确保论坛管理员了解您的主题/插件带来的稳定性风险。

:rotating_light: :rotating_light: :rotating_light: 2023 年 10 月更新:对于新功能,Discourse 越来越多地转向使用使用 Ember 的 .gjs 文件格式编写的组件。这些组件的模板是内联定义的,无法被主题/插件覆盖。

展望未来,所有模板自定义都应使用 插件插口 (Plugin Outlets) 完成。

我明白这在不久的将来会中断,仍然向我展示文档

覆盖组件模板

要覆盖 Ember 组件模板(即 Discourse 核心中 components/* 下的任何内容),您应该在主题/插件中创建一个同名的 .hbs 文件。例如,要覆盖 Discourse 核心中 badge-button 组件的模板,您需要在主题/插件中此位置创建一个模板文件:

:art: {theme}/javascripts/discourse/templates/components/badge-button.hbs

:electric_plug: {plugin}/assets/javascripts/discourse/templates/components/badge-button.hbs

覆盖必须始终嵌套在 /templates 目录内,即使核心组件具有“共置”模板也是如此。

覆盖路由模板

覆盖路由模板(即 templates/* 下所有非组件模板)的​​工作方式与组件相同。在主题/插件中创建一个同名的模板。例如,要覆盖核心中的 discovery.hbs,您可以创建一个如下文件:

:art: {theme}/javascripts/discourse/templates/discovery.hbs

:electric_plug: {plugin}/assets/javascripts/discourse/templates/discovery.hbs

覆盖“原始”模板 (.hbr)

Discourse 的“原始”模板系统很快将被常规 Ember 组件取代。但在那之前,覆盖原始模板的工作方式与 Ember 模板相同。例如,要覆盖核心中的 topic-list-item.hbr,您可以创建一个文件,如下所示:

:art: {theme}/javascripts/discourse/templates/list/topic-list-item.hbr

:electric_plug: {plugin}/assets/javascripts/discourse/templates/list/topic-list-item.hbr

多个主题/插件之间的交互

如果多个已安装的主题/插件覆盖了相同的模板,则“获胜者”是在此列表中排名最低的那个:

  1. 主题覆盖(主题 ‘id’ 越高者获胜)
  2. 插件覆盖(字母顺序靠后的插件名称获胜)
  3. 核心

此优先级也意味着您可以从主题覆盖插件模板。技术上您也可以从其他主题覆盖主题模板,以及从其他插件覆盖插件模板,但由于依赖于插件名称和主题 ID,其行为可能会令人惊讶。

这是如何工作的?

Discourse 在 DiscourseTemplateMap 类中组装和确定模板的优先级。对于共置的组件模板,该信息在 应用初始化期间 用于替换核心模板关联。对于所有其他模板,解析器在 运行时 使用该映射来获取正确的模板。


本文档是版本控制的 - 在 github 上建议更改。

17 个赞

移动模板呢?重写核心模板的目录结构是什么?

它应该完全一样——你匹配核心模板的名称。因此,如果它有 /mobile,请将其包含在你的覆盖中。

我尝试重写 mobile login.hbs 模板,但它不起作用 Imgur: The magic of the Internet

据我所见,您的截图中未显示完整路径。请在此处将其粘贴为文本。

themeroot/javascripts/mobile/modal/login.hbs

您的路径中缺少 discourse/templates

因此,在您的情况下,它将是 {theme}/javascripts/discourse/templates/mobile/modal/login.hbs

2 个赞

这种情况现在还存在吗?

我有点难过能够覆盖大量代码的功能被移除了。

用 Glimmer 替换定制的 Widget 系统在某种程度上是有意义的,但 Widget 系统让我们能够深入到多个层级来挂钩现有代码,通过巧妙地定位小的代码块来降低很多破坏性更改的风险,使我们能够:

  • 添加功能
  • 不干扰其他任何东西。

例如,我刚刚不得不从 Discourse Journal 中删除两个重要的功能,它们是基于对 Widget 的细粒度覆盖来实现的,因为在 Glimmer 中重新创建它们的唯一方法是通过一对模板覆盖(包括尝试更改 .gjs 文件),而这似乎不再受支持。

即使支持,我们也只能覆盖比 Widget 框架更大的代码块,这会增加核心更改与覆盖冲突的风险。

这对平台的扩展性不利。

有什么办法可以解决这个问题吗?

7 个赞

是的,我明白你的意思——小部件可扩展性 API 有一些很好的地方。

但另一方面,我们很难修改核心中的任何基于小部件的用户界面,因为我们不知道人们可能会引入哪些随机方法/装饰。这就是为什么小部件自定义似乎相对稳定——我们太害怕触碰核心实现。

我们未来的解决方案是Wrapper Plugin Outlets。这些允许主题和插件使用自己的实现可选地覆盖非常小的模板块。

例如,看看 Chat 如何有条件地覆盖 home-logo自定义组件。这适用于现有的基于小部件的标题,以及新的基于 Glimmer 的标题(即将推出!:tm:

我们通常乐于接受在各种地方添加新的 wrapper outlets 的 PR。如果您不确定某个特定用例,请随时使用 Dev 主题发布详细信息!

10 个赞

好的,这听起来是个可行的方案,谢谢你。

我需要消化一下它的影响,并据此调整策略。

感谢你的回复!

6 个赞