如何添加一个非常基础的主题自定义字段

我正在尝试了解如何为话题添加自定义字段,并正在通过一个非常基础的示例进行实践。目标:为每个创建的话题添加一个名为 “sample_field” 的自定义字段,其值为一个简单的字符串。

我已经查阅了各种示例,例如 poll 插件solved 插件 以及 这篇讨论,但这些插件对自定义字段的操作过于复杂,我尚未理清所需的基础代码。

所以我还没完全搞定——我的 plugin.rb 文件似乎缺少某些内容(我认为),而且我还没弄清楚如何在话题创建时将字符串值绑定到自定义字段上。

我需要做什么才能让这生效?

任何帮助都将不胜感激。谢谢!

以下是我目前的代码:

plugin.rb:
// 创建自定义字段:

  after_initialize do
     Topic.register_custom_field_type('sample_field', :string)
     add_to_serializer(:topic_view, :custom_fields) { object.custom_fields } // 如果我想在客户端显示自定义字段
  end

assets/javascripts/initializers/topic-custom-field.js.es6:
// 初始化自定义字段对象,并使其能够发送到服务器:

import { withPluginApi } from 'discourse/lib/plugin-api';

export default {
  name: 'topic-custom-field',
  initialize() {
    withPluginApi('0.8.31', api => {
      api.modifyClass('model:topic', {
        custom_fields: {},
        asJSON() {
          return Object.assign(this._super(), {
            custom_fields: this.custom_fields
          });
        }
      })
    })
  }
}

那么,如何在话题保存时为自定义字段添加值呢?

我认为话题的“保存”关键操作在代码库中发生在这里:

app/templates/composer.hbs:

  <div class="save-or-cancel">
            {{#unless model.viewFullscreen}}
              {{composer-save-button action=(action "save")
                                     icon=saveIcon
                                     label=saveLabel
                                     disableSubmit=disableSubmit}}
...

当该“保存”操作发生时,我该如何执行某些操作(在本例中,为自定义字段添加一个值)?

我尝试在 initializers 下创建一个 js 文件,在其中我可以执行类似以下的操作:

api.modifyClass('component:composer-save-button', {
   actions: {
       topic.set('custom_fields.sample_field', 'here's a value for the sample_field')
   }
}

但我需要将那个 “topic.set” 与 composer.hbs 中的 “save” 操作关联起来,而我不知道该如何实现。


如果有更简单的方法,我也很乐意听取建议!

2 个赞

我目前不知道有没有完美的示例,但也许会在某个时候尝试开发一个。

你可以查看其他一些插件来寻找示例,我一时想不起具体是哪一个。有一个名为“all-the-plugins”之类的 Discourse 仓库,我经常用它来 grep 查找相关内容。

我敢打赌,目前正在测试的 reactions 插件就是一个很好的例子。请查看横幅并参考一下。

2 个赞

是的,请务必查看现有的插件。

这是一个示例:

它依赖于以下连接器和组件:

2 个赞

谢谢大家——我会看看这些。我在原帖中提到,我已经查看了很多其他示例。对于我提供的代码,大家有什么看法吗?

2 个赞

抱歉。我没发现任何(在我看来)明显的问题。虽然我越来越擅长插件开发,但我常觉得自己懂得刚好够危险。

2 个赞

如果你能接受“Discourse 的做法”,即:在主题元数据区域编辑数据,并且仅在点击铅笔图标时才进行更新,那么当前的方案可能过于复杂了。

你只需要渲染一个正确连接的输入框,并确保将该值序列化到主题视图中即可。

PostRevisor 代码会自动完成更新自定义字段的魔法操作。

参考 Locations 插件的示例。甚至那个示例都比你实际需要的更复杂。

记得同时运行 Locations 插件,看看效果如何。

2 个赞

这是另一个示例,输入场景略有不同,但依然利用了相同的机制和钩子:

https://github.com/paviliondev/discourse-topic-previews/blob/master/assets/javascripts/discourse/connectors/edit-topic/select-thumbnail-connector.hbs

4 个赞

谢谢。我不熟悉 PostRevisor,正在查看中。

不太清楚您所说的“主题元数据区域”具体指什么。您是指某个文件吗?(就用户界面而言,我知道有用于创建的编辑器,其中包含 d-editor,此外还有主题视图本身。不确定其中的“元数据”区域在哪里。)

2 个赞

我认为分类和标签信息属于元数据。

2 个赞

这是一个当前可用的创建主题自定义字段并设置它的基本示例。当用户在主题查看页面点击特定按钮时,会设置该字段。

我注意到,在那些包含主题自定义字段的插件中,plugin.rb 中有一个常见用法:使用 PostRevisor(例如 PostRevisor.track_topic_field)以及 DiscourseEvent.on(:topic_created) do |topic|...。这些方法可能允许在无需点击按钮的情况下设置自定义字段——例如在主题创建时直接设置。不过我目前尚未完全弄清楚如何实现这一点。

[编辑:如果有人愿意提供 plugin.rb 中的代码示例,以便在创建主题时直接添加自定义字段值(而不是依赖需要点击的独立按钮),请随时提出!:]

因此,下面是一个无需那些高级功能即可运行的基本示例:

plugin.rb

Topic.register_custom_field_type('sample_field', :string)
add_to_class(:topic, :sample_field) { self.custom_fields['sample_field'] } ## 可能并非必需,参考 discourse-locations 插件 plugin.rb 第 83 行
	
add_to_serializer(:topic_view, :sample_field, false) { object.topic.sample_field } ## 可能仅在需要向用户展示自定义字段结果时才必需

connector/[add-button].hbs

// 示例:此处 connector 为 topic-above-post-stream,它通过插件出口传递一个模型(topic)

<button {{action "setThatTopic" model}}>点击此处</button>

connector/[add-button].js.es6

import Topic from 'discourse/models/topic';  // 可能并非必需

export default {			
    actions: {
       setThatTopic(model){
           model.set('custom_fields.sample_field', 'newValue123')
           console.log('测试自定义字段是否存在 = ' + JSON.stringify(model.custom_fields))
        }
     }
}

assets/javascripts/initializers/topic-custom-field.js.es6

// 这可能并非必需。但其思路是初始化 custom_fields 对象,使其能够发送至服务器:

import { withPluginApi } from 'discourse/lib/plugin-api';

export default {
    name: 'topic-custom-field',
     initialize() {
	   withPluginApi('0.8.31', api => {
		 api.modifyClass('model:topic', {
			custom_fields: {},
			asJSON() {
			    return Object.assign(this._super(), {
					custom_fields: this.custom_fields
			   });
			}
	     })
	   })
    }
}
6 个赞

我刚刚发布了一个“教育插件”,演示了如何实现这一功能。

9 个赞

感谢 @angus。这非常有帮助,正是让使用 Discourse 进行编码变得更简单的那类内容。我个人非常希望看到更多此类资源——针对 Discourse 的各种任务,提供直击要点、精简实用的代码示例,一次实现一个功能。例如,为分类和群组添加自定义字段等。这类资源能极大节省时间。

3 个赞