Como adicionar um campo personalizado de tópico super básico

Estou tentando entender como adicionar um campo personalizado aos tópicos, trabalhando em um exemplo muito básico. Objetivo: Adicionar um campo personalizado chamado “sample_field” a cada tópico criado, com um valor simples do tipo string.

Revisei vários exemplos, como o plugin poll e o plugin solved e esta discussão, mas esses plugins fazem muito mais com seus campos personalizados, e ainda não consegui entender o código básico necessário.

Então, ainda não cheguei lá — meu arquivo plugin.rb está faltando algo (acho que sim), e ainda não descobri como vincular o valor da string ao custom_field no momento em que o tópico é criado.

O que preciso para fazer isso funcionar?

Qualquer ajuda é apreciada. Obrigado!

Aqui está o que tenho:

plugin.rb:
//criar o campo personalizado:

  after_initialize do
     Topic.register_custom_field_type('sample_field', :string)
     add_to_serializer(:topic_view, :custom_fields) { object.custom_fields } //se eu quiser mostrar o campo personalizado no lado do cliente
  end

assets/javascripts/initializers/topic-custom-field.js.es6:
//inicializar o objeto custom_fields e permitir que seja enviado para o servidor:

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
          });
        }
      })
    })
  }
}

Então, como adicionar o valor do campo personalizado ao tópico no momento em que ele é salvo?

Acredito que a ação chave “save” para um tópico ocorre aqui na base de código:

app/templates/composer.hbs:

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

Como faço algo (neste caso, adicionar um valor ao campo personalizado) quando essa ação “save” ocorre?

Tentei criar um arquivo JS em initializers, onde poderia fazer algo como:

api.modifyClass('component:composer-save-button', {
   actions: {
       topic.set('custom_fields.sample_field', 'aqui está um valor para o sample_field')
   }
}

Mas preciso conseguir vincular esse “topic.set” à ação “save” em composer.hbs, e não sei como fazer isso.


E se houver uma maneira mais simples de fazer isso, ficarei feliz em ouvir!

2 curtidas

Não tenho conhecimento de um exemplo perfeito, mas talvez tente desenvolver um em algum momento.

Você pode dar uma olhada em alguns outros plugins para buscar exemplos. Não tenho certeza de qual seria de cabeça. Existe um repositório do Discourse chamado algo como all-the-plugins, e eu costumo fazer grep nele para procurar coisas.

Aposto que o plugin de reações que está em teste aqui agora é um bom exemplo. Veja o banner e dê uma olhada nele.

2 curtidas

Sim, sempre consulte plugins existentes.

Aqui está um exemplo:

Que depende deste conector e componente:

2 curtidas

Obrigado a todos — vou dar uma olhada nisso. Mencionei na postagem original que já tinha revisado vários outros exemplos. Alguma opinião sobre o código que forneci?

2 curtidas

Desculpe. Não vi nenhum problema óbvio (para mim). Embora esteja ficando melhor no desenvolvimento de plugins, muitas vezes sinto que sei apenas o suficiente para ser perigoso.

2 curtidas

Potencialmente muito complicado se você puder se ater ao “jeito Discourse de fazer as coisas”, ou seja: editar os dados na área de Metadados do Tópico e apenas mais tarde, quando o ícone de lápis for clicado.

Você só precisa renderizar uma caixa de entrada corretamente conectada e garantir que o valor tenha sido serializado para a Visualização do Tópico.

O código do PostRevisor fará a mágica de atualizar o campo personalizado.

Observe o exemplo do plugin Locations. Até mesmo esse é mais do que você precisa.

Certifique-se de executar o plugin Locations também para ver como isso fica.

2 curtidas

Aqui está outro exemplo, com um cenário de entrada ligeiramente diferente, mas ainda aproveitando os mesmos mecanismos e ganchos:

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

4 curtidas

Obrigado. Não estou familiarizado com PostRevisor. Estou investigando isso agora.

Não tenho certeza do que você quer dizer com área de Metadados do Tópico. É um arquivo que você tem em mente? (Em termos de interface, sei que há o compositor para criação, com o editor d dentro dele, e depois há a própria visualização do tópico. Não tenho certeza de onde está a área de “Metadados” nisso.)

2 curtidas

Eu considero as informações de Categoria e Tag como metadados.

2 curtidas

Aqui está um exemplo básico funcional de criação de um campo personalizado de tópico e, em seguida, sua definição. Isso define o campo quando o usuário clica em um botão específico na página de visualização do tópico.

Notei que nos plugins que possuem campos personalizados de tópico, há um uso comum em plugin.rb de PostRevisor (como PostRevisor.track_topic_field) e DiscourseEvent.on(:topic_created) do |topic|.... Esses podem permitir definir o campo personalizado sem precisar clicar em um botão — como definir o campo personalizado quando o tópico é criado. Mas ainda não consegui resolver isso.

[EDIT: Se alguém quiser sugerir código para plugin.rb que adicione o valor do campo personalizado a um tópico no momento da criação do tópico (em vez de ter um botão separado que precise ser clicado), por favor, faça! :slight_smile: ]

Então, aqui está um exemplo básico que funciona sem essas coisas sofisticadas:

plugin.rb

Topic.register_custom_field_type('sample_field', :string)
add_to_class(:topic, :sample_field) { self.custom_fields['sample_field'] } ##talvez não seja necessário. baseado na linha 83 do plugin.rb do plugin discourse-locations
	
add_to_serializer(:topic_view, :sample_field, false) { object.topic.sample_field } ##provavelmente necessário apenas se quiser mostrar o resultado do campo personalizado ao usuário

connector/[add-button].hbs

//exemplo aqui: o conector é topic-above-post-stream, que passa um modelo (tópico) no outlet do plugin

<button {{action "setThatTopic" model}}>Clique Aqui</button>

connector/[add-button].js.es6

import Topic from 'discourse/models/topic';  //pode não ser necessário

export default {			
    actions: {
       setThatTopic(model){
           model.set('custom_fields.sample_field', 'newValue123')
           console.log('testando se o campo personalizado está presente = ' + JSON.stringify(model.custom_fields))
        }
     }
}

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

//isso pode não ser necessário. Mas a ideia é inicializar o objeto de campos personalizados e fazer com que ele possa ser enviado ao servidor:

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 curtidas

Acabei de publicar um “plugin de educação” demonstrando como isso é feito

9 curtidas

Obrigado, @angus. Isso é extremamente útil e exatamente o tipo de coisa que facilita a codificação com o Discourse. Pessoalmente, adoraria ver esse tipo de recurso — exemplos de código diretos e essenciais para implementar uma funcionalidade de cada vez — para diversas tarefas com o Discourse. Por exemplo, campos personalizados para categorias e grupos também. Esse tipo de recurso é uma grande economia de tempo.

3 curtidas