diff --git a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
index 9ed7762c..0a7803b3 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
@@ -2,16 +2,16 @@ import Controller from "@ember/controller";
import EmberObject from '@ember/object';
import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error';
+import CustomWizardCustomField from "../models/custom-wizard-custom-field";
export default Controller.extend({
fieldKeys: ['klass', 'type', 'serializers', 'name'],
+ documentationUrl: "https://thepavilion.io/t/3572",
actions: {
addField() {
this.get('customFields').pushObject(
- EmberObject.create({
- new: true
- })
+ CustomWizardCustomField.create()
);
},
@@ -21,22 +21,17 @@ export default Controller.extend({
saveFields() {
this.set('saving', true);
- ajax(`/admin/wizards/custom-fields`, {
- type: 'PUT',
- dataType: 'json',
- contentType: 'application/json',
- data: JSON.stringify({
- custom_fields: this.customFields
- })
- }).then(result => {
- if (result.success) {
- this.set('saveIcon', 'check');
- } else {
- this.set('saveIcon', 'times');
- }
- setTimeout(() => this.set('saveIcon', ''), 5000);
- }).finally(() => this.set('saving', false))
- .catch(popupAjaxError);
+ CustomWizardCustomField.saveFields(this.customFields)
+ .then(result => {
+ if (result.success) {
+ this.set('saveIcon', 'check');
+ } else {
+ this.set('saveIcon', 'times');
+ }
+ setTimeout(() => this.set('saveIcon', ''), 5000);
+ }).finally(() => {
+ this.set('saving', false);
+ });
}
}
});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/models/custom-wizard-custom-field.js.es6 b/assets/javascripts/discourse/models/custom-wizard-custom-field.js.es6
new file mode 100644
index 00000000..01b88f44
--- /dev/null
+++ b/assets/javascripts/discourse/models/custom-wizard-custom-field.js.es6
@@ -0,0 +1,29 @@
+import { ajax } from 'discourse/lib/ajax';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import EmberObject from "@ember/object";
+import { isEmpty } from "@ember/utils";
+
+const CustomWizardCustomField = EmberObject.extend({
+ isNew: isEmpty('id')
+});
+
+const basePath = '/admin/wizards/custom-fields';
+
+CustomWizardCustomField.reopenClass({
+ listFields() {
+ return ajax(basePath).catch(popupAjaxError);
+ },
+
+ saveFields(customFields) {
+ return ajax(basePath, {
+ type: 'PUT',
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify({
+ custom_fields: customFields
+ })
+ }).catch(popupAjaxError);
+ }
+});
+
+export default CustomWizardCustomField;
\ No newline at end of file
diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
index c6775477..b4fd1c4e 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
@@ -1,13 +1,14 @@
import DiscourseRoute from "discourse/routes/discourse";
-import { ajax } from 'discourse/lib/ajax';
+import CustomWizardCustomField from "../models/custom-wizard-custom-field";
import { A } from "@ember/array";
export default DiscourseRoute.extend({
model() {
- return ajax('/admin/wizards/custom-fields');
+ return CustomWizardCustomField.listFields();
},
setupController(controller, model) {
- controller.set('customFields', A(model || []));
+ const customFields = A(model || []);
+ controller.set('customFields', customFields);
}
});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs
index a79ff3a9..3e451294 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs
@@ -1,6 +1,6 @@
{{i18n 'admin.wizard.custom_field.nav_label'}}
-
+
{{#if saving}}
{{loading-spinner size="small"}}
@@ -19,6 +19,11 @@
+{{wizard-message
+ key='create'
+ url=documentationUrl
+ component='custom_fields'}}
+
{{#if customFields}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss
index 0b07475d..b92e6e33 100644
--- a/assets/stylesheets/common/wizard-admin.scss
+++ b/assets/stylesheets/common/wizard-admin.scss
@@ -537,6 +537,10 @@
}
.admin-wizards-custom-fields {
+ h3 {
+ margin-bottom: 0;
+ }
+
.select-kit {
width: 200px;
}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 39137f57..906d6f77 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -72,6 +72,9 @@ en:
type: "Select an action type"
edit: "You're editing an action"
documentation: "Check out the action documentation"
+ custom_fields:
+ create: "Create or edit a custom field record"
+ documentation: Check out the custom field documentation
editor:
show: "Show"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 4f9ef820..b06be5f8 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -6,6 +6,18 @@ en:
wizard:
custom_title: "Wizard"
+
+ custom_field:
+ error:
+ required_attribute: "'%{attr}' is a required attribute"
+ unsupported_class: "'%{class}' is not a supported class"
+ unsupported_serializers: "'%{serializers}' are not supported serializers for '%{class}'"
+ unsupported_type: "%{type} is not a supported custom field type"
+ name_invalid: "'%{name}' is not a valid custom field name"
+ name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})"
+ name_already_taken: "'%{value}' is already taken as a custom field name"
+ save_default: "Failed to save custom field '%{name}'"
+
field:
too_short: "%{label} must be at least %{min} characters"
required: "%{label} is required."
diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/controllers/custom_wizard/admin/custom_fields.rb
index 6359d08f..add147da 100644
--- a/controllers/custom_wizard/admin/custom_fields.rb
+++ b/controllers/custom_wizard/admin/custom_fields.rb
@@ -3,33 +3,35 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
render_json_dump(custom_field_list)
end
- def update
- custom_fields = custom_field_params[:custom_fields].map do |data|
- CustomWizard::CustomField.new(data.to_h)
- end
+ def update
+ fields_to_save = []
- custom_fields.each do |custom_field|
- custom_field.validate
+ custom_field_params[:custom_fields].each do |field_param|
+ field_id = nil
+ field_data = {}
- unless custom_field.valid?
- raise Discourse::InvalidParameters,
- custom_field.errors.full_messages.join("\n\n")
+ if saved_field = CustomWizard::CustomField.find(field_param[:name])
+ CustomWizard::CustomField::ATTRS.each do |attr|
+ field_data[attr] = field_param[attr] || saved_field.send(attr)
+ end
+ field_id = saved_field.id
+ end
+
+ fields_to_save.push(CustomWizard::CustomField.new(field_id, field_data))
+ end
+
+ PluginStoreRow.transaction do
+ fields_to_save.each do |field|
+ unless field.save
+ raise ActiveRecord::Rollback.new,
+ field.errors.any? ?
+ field.errors.full_messages.join("\n\n") :
+ I18n.t("wizard.custom_field.error.save_default", name: field.name)
+ end
end
end
-
- all_fields_saved = true
-
- custom_fields.each do |field|
- unless field.save
- all_fields_saved = false
- end
- end
-
- if all_fields_saved
- render json: success_json
- else
- render json: error_json
- end
+
+ render json: success_json
end
private
@@ -37,8 +39,8 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
def custom_field_params
params.permit(
custom_fields: [
- :klass,
:name,
+ :klass,
:type,
serializers: []
]
diff --git a/extensions/extra_locales_controller.rb b/extensions/extra_locales_controller.rb
index 2e3f8a78..31a9f488 100644
--- a/extensions/extra_locales_controller.rb
+++ b/extensions/extra_locales_controller.rb
@@ -7,7 +7,7 @@ module ExtraLocalesControllerCustomWizard
hash = ::ExtraLocalesController.bundle_js_hash(bundle)
immutable_for(1.year) if hash == params[:v]
end
-
+
render plain: ::ExtraLocalesController.bundle_js(bundle), content_type: "application/javascript"
else
super
diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb
index b658a958..afce1c9e 100644
--- a/lib/custom_wizard/custom_field.rb
+++ b/lib/custom_wizard/custom_field.rb
@@ -1,19 +1,40 @@
+# frozen_string_literal: true
+
class ::CustomWizard::CustomField
include HasErrors
include ActiveModel::Serialization
- CLASSES ||= ["topic", "group", "category", "post"]
- SERIALIZERS ||= ["topic_view", "topic_list_item", "post", "basic_category"]
- TYPES ||= ["string", "boolean", "integer", "json"]
- ATTRS ||= ["name", "klass", "type", "serializers"]
- KEY ||= "custom_wizard_custom_fields"
+ attr_reader :id
- def initialize(data)
+ ATTRS ||= ["name", "klass", "type", "serializers"]
+ REQUIRED ||= ["name", "klass"]
+ NAMESPACE ||= "custom_wizard_custom_fields"
+ NAME_MIN_LENGTH ||= 3
+
+ CLASSES ||= {
+ topic: ["topic_view", "topic_list_item"],
+ group: ["basic_group"],
+ category: ["basic_category"],
+ post: ["post"]
+ }
+ TYPES ||= ["string", "boolean", "integer", "json"]
+
+ def self.serializers
+ CLASSES.values.flatten.uniq
+ end
+
+ def initialize(id, data)
+ @id = id
data = data.with_indifferent_access
ATTRS.each do |attr|
- self.class.class_eval { attr_accessor attr }
- send("#{attr}=", data[attr]) if data[attr].present?
+ self.class.class_eval { attr_accessor attr }
+
+ value = data[attr]
+
+ if value.present?
+ send("#{attr}=", value)
+ end
end
end
@@ -22,19 +43,18 @@ class ::CustomWizard::CustomField
if valid?
data = {}
- name = nil
+ key = name
- ATTRS.each do |attr|
- value = send(attr)
-
- if attr == 'name'
- name = value.parameterize(separator: '_')
- else
- data[attr] = value
- end
+ (ATTRS - ['name']).each do |attr|
+ data[attr] = send(attr)
end
- PluginStore.set(KEY, name, data)
+ if self.class.save_to_store(id, key, data)
+ self.class.reset
+ true
+ else
+ false
+ end
else
false
end
@@ -43,50 +63,111 @@ class ::CustomWizard::CustomField
def validate
ATTRS.each do |attr|
value = send(attr)
+ i18n_key = "wizard.custom_field.error"
- if value.blank?
- add_error("Attribute required: #{attr}")
+ if REQUIRED.include?(attr) && value.blank?
+ I18n.t("#{i18n_key}.required_attribute", attr: attr)
next
end
- if attr == 'klass' && CLASSES.exclude?(value)
- add_error("Unsupported class: #{value}")
+ if (attr == 'klass' && CLASSES.keys.exclude?(value.to_sym)) ||
+ (attr == 'serializers' && CLASSES[klass.to_sym].blank?)
+ add_error(I18n.t("#{i18n_key}.unsupported_class", class: value))
+ next
end
- if attr == 'serializers' && value.present? && (SERIALIZERS & value).empty?
- add_error("Unsupported serializer: #{value}")
+ if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0
+ add_error(I18n.t("#{i18n_key}.unsupported_serializers",
+ class: klass,
+ serializers: unsupported.join(", ")
+ ))
end
if attr == 'type' && TYPES.exclude?(value)
- add_error("Unsupported type: #{value}")
+ add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
end
- if attr == 'name' && value.length < 3
- add_error("Field name is too short")
+
+ if attr == 'name'
+ unless value.is_a?(String)
+ add_error(I18n.t("#{i18n_key}.name_invalid", name: value))
+ end
+
+ if value.length < NAME_MIN_LENGTH
+ add_error(I18n.t("#{i18n_key}.name_too_short", name: value, min_length: NAME_MIN_LENGTH))
+ end
+
+ if new? && self.class.exists?(name)
+ add_error(I18n.t("#{i18n_key}.name_already_taken", name: value))
+ end
+
+ begin
+ @name = value.parameterize(separator: '_')
+ rescue
+ add_error(I18n.t("#{i18n_key}.name_invalid", name: value))
+ end
end
end
end
+ def new?
+ id.blank?
+ end
+
def valid?
errors.blank?
end
+ def self.reset
+ @list = nil
+ end
+
def self.list
- PluginStoreRow.where(plugin_name: KEY)
- .map do |record|
- data = JSON.parse(record.value)
- data[:name] = record.key
- self.new(data)
- end
+ @list ||= PluginStoreRow.where(plugin_name: NAMESPACE)
+ .map { |record| create_from_store(record) }
end
def self.list_by(attr, value)
self.list.select do |cf|
- if attr == 'serializers'
+ if attr == :serializers
cf.send(attr).include?(value)
else
cf.send(attr) == value
end
end
end
+
+ def self.exists?(name)
+ PluginStoreRow.where(plugin_name: NAMESPACE, key: name).exists?
+ end
+
+ def self.find(name)
+ records = PluginStoreRow.where(plugin_name: NAMESPACE, key: name)
+
+ if records.exists?
+ create_from_store(records.first)
+ else
+ false
+ end
+ end
+
+ def self.create_from_store(record)
+ data = JSON.parse(record.value)
+ data[:name] = record.key
+ new(record.id, data)
+ end
+
+ def self.save_to_store(id = nil, key, data)
+ if id
+ record = PluginStoreRow.find_by(id: id, plugin_name: NAMESPACE, key: key)
+ return false if !record
+ record.value = data.to_json
+ record.save
+ else
+ record = PluginStoreRow.new(plugin_name: NAMESPACE, key: key)
+ record.type_name = "JSON"
+ record.value = data.to_json
+ record.save
+ end
+ end
end
\ No newline at end of file
diff --git a/plugin.rb b/plugin.rb
index 3392353d..20df5a12 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -167,29 +167,44 @@ after_initialize do
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
end
- CustomWizard::CustomField::CLASSES.each do |klass|
- add_model_callback(klass.to_sym, :after_initialize) do
- CustomWizard::CustomField.list_by('klass', klass).each do |field|
- klass.classify
+ CustomWizard::CustomField::CLASSES.keys.each do |klass|
+ add_model_callback(klass, :after_initialize) do
+ CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
+ klass.to_s
+ .classify
.constantize
.register_custom_field_type(field.name, field.type.to_sym)
end
end
end
- CustomWizard::CustomField::SERIALIZERS.each do |serializer_klass|
- "#{serializer_klass}_serializer".classify.constantize.class_eval do
- CustomWizard::CustomField.list_by('serializers', serializer_klass).each do |field|
- attributes(field.name.to_sym)
- class_eval %{def #{field.name}
- if "#{serializer_klass}" == "topic_view"
- object.topic.custom_fields["#{field.name}"]
+ module CustomWizardCustomFieldSerialization
+ def attributes(*args)
+ hash = super
+ @cw_klass = self.class.name.underscore.gsub("_serializer", "")
+
+ if cw_fields.any?
+ cw_fields.each do |field|
+ if @cw_klass == "topic_view"
+ hash[field.name.to_sym] = object.topic.custom_fields["#{field.name}"]
else
- object.custom_fields["#{field.name}"]
+ hash[field.name.to_sym] = object.custom_fields["#{field.name}"]
end
- end}
+ end
end
+
+ hash
end
+
+ private
+
+ def cw_fields
+ @cw_fields ||= CustomWizard::CustomField.list_by(:serializers, @cw_klass)
+ end
+ end
+
+ CustomWizard::CustomField.serializers.each do |serializer_klass|
+ "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerialization
end
DiscourseEvent.trigger(:custom_wizard_ready)
diff --git a/serializers/custom_wizard/custom_field_serializer.rb b/serializers/custom_wizard/custom_field_serializer.rb
index 14e3fb32..9d684be3 100644
--- a/serializers/custom_wizard/custom_field_serializer.rb
+++ b/serializers/custom_wizard/custom_field_serializer.rb
@@ -1,3 +1,3 @@
class CustomWizard::CustomFieldSerializer < ApplicationSerializer
- attributes :klass, :name, :type, :serializers
+ attributes :id, :klass, :name, :type, :serializers
end
\ No newline at end of file
diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb
new file mode 100644
index 00000000..6d7f8708
--- /dev/null
+++ b/spec/components/custom_wizard/custom_field_spec.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CustomWizard::CustomField do
+
+ let(:custom_field_json) {
+ JSON.parse(File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
+ ).read)
+ }
+
+ it "saves custom field records" do
+ custom_field_json['custom_fields'].each do |field_json|
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ expect(custom_field.save).to eq(true)
+ expect(
+ PluginStoreRow.where("
+ plugin_name = '#{CustomWizard::CustomField::NAMESPACE}' AND
+ key = '#{custom_field.name}' AND
+ value::jsonb = '#{field_json.except('name').to_json}'::jsonb
+ ", ).exists?
+ ).to eq(true)
+ end
+ end
+
+ it "updates existing custom field records" do
+ custom_field_json['custom_fields'].each do |field_json|
+ CustomWizard::CustomField.new(nil, field_json).save
+ end
+
+ updated_field_json = custom_field_json['custom_fields'][0]
+ updated_field_json['serializers'] = ["topic_view"]
+ existing_field = CustomWizard::CustomField.find(updated_field_json["name"])
+ updated_field = CustomWizard::CustomField.new(existing_field.id, updated_field_json)
+
+ expect(updated_field.save).to eq(true)
+ expect(
+ PluginStoreRow.where("
+ plugin_name = '#{CustomWizard::CustomField::NAMESPACE}' AND
+ key = '#{updated_field.name}' AND
+ value::jsonb = '#{updated_field_json.except('name').to_json}'::jsonb
+ ", ).exists?
+ ).to eq(true)
+ end
+
+ context "validation" do
+ it "does not save with an unsupported class" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['klass'] = 'user'
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.unsupported_class", class: "user")
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+
+ it "does not save with an unsupported serializer" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['klass'] = 'category'
+ invalid_field_json['serializers'] = ['category', 'site_category']
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.unsupported_serializers",
+ class: "category",
+ serializers: "category, site_category"
+ )
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+
+ it "does not save with an unsupported type" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['type'] = 'bigint'
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.unsupported_type", type: "bigint")
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+
+ it "does not save with a short field name" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['name'] = 'cf'
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.name_too_short", name: "cf")
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+
+ it "does not save with an existing name if new" do
+ custom_field_json['custom_fields'].each do |field_json|
+ CustomWizard::CustomField.new(nil, field_json).save
+ end
+
+ first_field_json = custom_field_json['custom_fields'][0]
+ custom_field = CustomWizard::CustomField.new(nil, first_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.name_already_taken", name: "topic_field_1")
+ )
+ end
+
+ it "does not save with an invalid name" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['name'] = ["invalid_name"]
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.name_invalid", name: ["invalid_name"])
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+ end
+
+ context "lists" do
+ before do
+ custom_field_json['custom_fields'].each do |field_json|
+ CustomWizard::CustomField.new(nil, field_json).save
+ end
+ end
+
+ it "lists saved custom field records" do
+ expect(CustomWizard::CustomField.list.length).to eq(4)
+ end
+
+ it "lists saved custom field records by attribute value" do
+ expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb
new file mode 100644
index 00000000..de588d95
--- /dev/null
+++ b/spec/extensions/custom_field_extensions_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+describe "custom field extensions" do
+ fab!(:topic) { Fabricate(:topic) }
+ fab!(:post) { Fabricate(:post) }
+ fab!(:category) { Fabricate(:category) }
+ fab!(:group) { Fabricate(:group) }
+ fab!(:user) { Fabricate(:user) }
+
+ let(:custom_field_json) {
+ JSON.parse(File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
+ ).read)
+ }
+
+ before do
+ custom_field_json['custom_fields'].each do |field_json|
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ custom_field.save
+ end
+ end
+
+ context "topic" do
+ it "registers topic custom fields" do
+ topic
+ expect(Topic.get_custom_field_type("topic_field_1")).to eq(:boolean)
+ end
+
+ it "adds topic custom fields to the topic_view serializer" do
+ topic.custom_fields["topic_field_1"] = true
+ topic.save_custom_fields(true)
+
+ serializer = TopicViewSerializer.new(
+ TopicView.new(topic.id, user),
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:topic_field_1]).to eq(true)
+ end
+
+ it "adds topic custom fields to the topic_list_item serializer" do
+ topic.custom_fields["topic_field_1"] = true
+ topic.save_custom_fields(true)
+
+ serializer = TopicListItemSerializer.new(
+ topic,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:topic_field_1]).to eq(true)
+ end
+ end
+
+ context "post" do
+ it "registers post custom fields" do
+ post
+ expect(Post.get_custom_field_type("post_field_1")).to eq(:integer)
+ end
+
+ it "adds post custom fields to the post serializer" do
+ post.custom_fields["post_field_1"] = 7
+ post.save_custom_fields(true)
+
+ serializer = PostSerializer.new(
+ post,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:post_field_1]).to eq(7)
+ end
+ end
+
+ context "category" do
+ it "registers category custom fields" do
+ category
+ expect(Category.get_custom_field_type("category_field_1")).to eq(:json)
+ end
+
+ it "adds category custom fields to the basic category serializer" do
+ category.custom_fields["category_field_1"] = { a: 1, b: 2 }.to_json
+ category.save_custom_fields(true)
+
+ serializer = BasicCategorySerializer.new(
+ category,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json)
+ end
+ end
+
+ context "group" do
+ it "registers group custom fields" do
+ group
+ expect(Group.get_custom_field_type("group_field_1")).to eq(:string)
+ end
+
+ it "adds group custom fields to the basic group serializer" do
+ group.custom_fields["group_field_1"] = "Hello"
+ group.save_custom_fields(true)
+
+ serializer = BasicGroupSerializer.new(
+ group,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:group_field_1]).to eq("Hello")
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/extensions/extra_locales_controller_spec.rb b/spec/extensions/extra_locales_controller_spec.rb
index bac0f2c6..e12b5f04 100644
--- a/spec/extensions/extra_locales_controller_spec.rb
+++ b/spec/extensions/extra_locales_controller_spec.rb
@@ -14,6 +14,9 @@ describe ExtraLocalesControllerCustomWizard, type: :request do
end
it "returns locales when requested by wizard" do
+ @controller.request = ActionController::TestRequest.create(@controller.class)
+ @controller.request.env['HTTP_REFERER'] = "/w/super-mega-fun-wizard"
+
expect(
ExtraLocalesController.url("wizard")
).to eq(
diff --git a/spec/fixtures/custom_field/custom_fields.json b/spec/fixtures/custom_field/custom_fields.json
new file mode 100644
index 00000000..0e7741ce
--- /dev/null
+++ b/spec/fixtures/custom_field/custom_fields.json
@@ -0,0 +1,37 @@
+{
+ "custom_fields": [
+ {
+ "klass": "topic",
+ "name": "topic_field_1",
+ "type": "boolean",
+ "serializers": [
+ "topic_list_item",
+ "topic_view"
+ ]
+ },
+ {
+ "klass": "post",
+ "name": "post_field_1",
+ "type": "integer",
+ "serializers": [
+ "post"
+ ]
+ },
+ {
+ "klass": "category",
+ "name": "category_field_1",
+ "type": "json",
+ "serializers": [
+ "basic_category"
+ ]
+ },
+ {
+ "klass": "group",
+ "name": "group_field_1",
+ "type": "string",
+ "serializers": [
+ "basic_group"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
new file mode 100644
index 00000000..90e7e618
--- /dev/null
+++ b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
@@ -0,0 +1,32 @@
+require 'rails_helper'
+
+describe CustomWizard::AdminCustomFieldsController do
+ fab!(:admin_user) { Fabricate(:user, admin: true) }
+
+ let(:custom_field_json) {
+ JSON.parse(File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
+ ).read)
+ }
+
+ before do
+ custom_field_json['custom_fields'].each do |field_json|
+ CustomWizard::CustomField.new(nil, field_json).save
+ end
+ sign_in(admin_user)
+ end
+
+ it "returns the list of custom fields" do
+ get "/admin/wizards/custom-fields.json"
+ expect(response.parsed_body.length).to eq(4)
+ end
+
+ it "updates the list of custom fields" do
+ custom_field_json['custom_fields'][0]['type'] = 'string'
+ put "/admin/wizards/custom-fields.json", params: custom_field_json
+ expect(response.status).to eq(200)
+ expect(
+ CustomWizard::CustomField.find('topic_field_1').type
+ ).to eq('string')
+ end
+end
\ No newline at end of file
diff --git a/spec/serializers/custom_wizard/custom_field_serializer_spec.rb b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb
new file mode 100644
index 00000000..525b6fd8
--- /dev/null
+++ b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CustomWizard::CustomFieldSerializer do
+ fab!(:user) { Fabricate(:user) }
+
+ let(:custom_field_json) {
+ JSON.parse(File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
+ ).read)
+ }
+
+ it 'should return custom field attributes' do
+ custom_field_json['custom_fields'].each do |field_json|
+ CustomWizard::CustomField.new(nil, field_json).save
+ end
+
+ json = CustomWizard::CustomFieldSerializer.new(
+ CustomWizard::CustomField.find("topic_field_1"),
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+ expect(json[:name]).to eq("topic_field_1")
+ expect(json[:klass]).to eq("topic")
+ expect(json[:type]).to eq("boolean")
+ expect(json[:serializers]).to match_array(["topic_list_item","topic_view"])
+ end
+end
\ No newline at end of file