Как добавить суперпростое пользовательское поле темы

Я пытаюсь понять, как добавить пользовательское поле к темам, и работаю над очень простым примером. Цель: добавить к каждой создаваемой теме пользовательское поле с именем “sample_field” со значением в виде простой строки.

Я изучил различные примеры, такие как плагин для опросов и плагин “решено”, а также это обсуждение, но эти плагины делают с пользовательскими полями так много всего, что я пока не смог разобраться в базовом коде, который для этого нужен.

Поэтому я еще не добился успеха — в моем файле 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
          });
        }
      })
    })
  }
}

Теперь, как добавить значение пользовательского поля к теме в момент её сохранения?

Я полагаю, что ключевое действие “save” для темы происходит здесь в базе кода:

app/templates/composer.hbs:

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

Как мне выполнить какое-то действие (в данном случае — добавить значение к пользовательскому полю), когда происходит это действие “save”?

Я пробовал создать js-файл в папке initializers, где мог бы сделать что-то вроде:

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

Но мне нужно связать этот “topic.set” с действием “save” в composer.hbs, и я не знаю, как это сделать.


И если есть более простой способ сделать это, я с радостью выслушаю ваши предложения!

Я не знаю идеального примера, но, возможно, в какой-то момент попробую его создать.

Вы можете посмотреть другие плагины в поиске примеров. Прямо сейчас я точно не вспомню, какой именно. Существует репозиторий Discourse с названием вроде «all-the-plugins», я часто использую grep для поиска там нужных вещей.

Уверен, что плагин реакций, который сейчас проходит тестирование здесь, — хороший пример. Посмотрите на баннер и ознакомьтесь с ним.

Да, всегда смотрите на существующие плагины.

Вот пример:

Он использует этот коннектор и компонент:

Спасибо всем — я изучу это. В оригинальном посте я упоминал, что уже просмотрел множество других примеров. Есть ли какие-то мысли по поводу предоставленного мной кода?

Извините. Я не заметил никаких очевидных (для меня) проблем. Хотя я становлюсь лучше в разработке плагинов, я часто чувствую, что знаю ровно столько, чтобы быть опасным.

Возможно, это излишне усложнено, если вы готовы следовать «подходу Discourse», то есть редактировать данные в области мета-данных темы и делать это только позже, когда будет нажата иконка карандаша.

Вам нужно только отрисовать поле ввода, правильно подключенное, и убедиться, что вы сериализовали значение в представление темы.

Код PostRevisor сделает магию обновления пользовательского поля.

Посмотрите на пример плагина Locations. Даже он содержит больше, чем вам нужно.

Убедитесь, что вы также запустили плагин Locations, чтобы увидеть, как это выглядит.

Вот ещё один пример со слегка иной сценарной подачей, но с использованием тех же механизмов и хуков:

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

Спасибо. Я не знаком с PostRevisor. Сейчас разберусь.

Не совсем понимаю, что вы имеете в виду под областью метаданных темы. Это какой-то файл? (С точки зрения интерфейса я знаю, что есть редактор для создания с d-редактором внутри, а затем есть сам просмотр темы. Не уверен, где именно находится область “Мета”.)

Я считаю, что информация о категориях и тегах является метаданными.

Вот текущий рабочий базовый пример создания пользовательского поля для темы и его последующего заполнения. Это поле устанавливается, когда пользователь нажимает определённую кнопку на странице просмотра темы.

Я заметил, что в плагинах, использующих пользовательские поля тем, в файле plugin.rb часто применяются PostRevisor (например, PostRevisor.track_topic_field) и DiscourseEvent.on(:topic_created) do |topic|.... Это могло бы позволить устанавливать пользовательское поле без необходимости нажатия кнопки — например, при создании темы. Однако я пока не разобрался, как это реализовать.

[РЕДАКТИРОВАНИЕ: Если кто-то хочет предложить код для plugin.rb, который добавлял бы значение пользовательского поля к теме непосредственно в момент её создания (вместо отдельной кнопки, требующей нажатия), пожалуйста, поделитесь! :slight_smile: ]

Поэтому вот базовый пример, который работает без всего этого сложного функционала:

plugin.rb

Topic.register_custom_field_type('sample_field', :string)
add_to_class(:topic, :sample_field) { self.custom_fields['sample_field'] } ##возможно, не обязательно. основано на строке 83 плагина discourse-locations plugin.rb
	
add_to_serializer(:topic_view, :sample_field, false) { object.topic.sample_field } ##вероятно, необходимо только если вы хотите отображать результат пользовательского поля пользователю

connector/[add-button].hbs

//пример здесь: коннектор — 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
			   });
			}
	     })
	   })
    }
}

Я только что опубликовал «плагин для обучения», демонстрирующий, как это делается

Спасибо, @angus. Это невероятно полезно и именно то, что нужно, чтобы упростить работу с Discourse. Лично мне бы очень хотелось видеть подобные ресурсы — лаконичные, минималистичные примеры кода по реализации одной функции за раз — для различных задач в Discourse. Например, для настройки пользовательских полей в категориях и группах. Такие материалы экономят массу времени.