Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 09:20:29 +01:00
Add custom field spec and improve custom field structure
Dieser Commit ist enthalten in:
Ursprung
1f1f2c5726
Commit
3da4d546b2
18 geänderte Dateien mit 639 neuen und 96 gelöschten Zeilen
|
@ -2,16 +2,16 @@ import Controller from "@ember/controller";
|
||||||
import EmberObject from '@ember/object';
|
import EmberObject from '@ember/object';
|
||||||
import { ajax } from 'discourse/lib/ajax';
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
import CustomWizardCustomField from "../models/custom-wizard-custom-field";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
fieldKeys: ['klass', 'type', 'serializers', 'name'],
|
fieldKeys: ['klass', 'type', 'serializers', 'name'],
|
||||||
|
documentationUrl: "https://thepavilion.io/t/3572",
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addField() {
|
addField() {
|
||||||
this.get('customFields').pushObject(
|
this.get('customFields').pushObject(
|
||||||
EmberObject.create({
|
CustomWizardCustomField.create()
|
||||||
new: true
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -21,22 +21,17 @@ export default Controller.extend({
|
||||||
|
|
||||||
saveFields() {
|
saveFields() {
|
||||||
this.set('saving', true);
|
this.set('saving', true);
|
||||||
ajax(`/admin/wizards/custom-fields`, {
|
CustomWizardCustomField.saveFields(this.customFields)
|
||||||
type: 'PUT',
|
.then(result => {
|
||||||
dataType: 'json',
|
if (result.success) {
|
||||||
contentType: 'application/json',
|
this.set('saveIcon', 'check');
|
||||||
data: JSON.stringify({
|
} else {
|
||||||
custom_fields: this.customFields
|
this.set('saveIcon', 'times');
|
||||||
})
|
}
|
||||||
}).then(result => {
|
setTimeout(() => this.set('saveIcon', ''), 5000);
|
||||||
if (result.success) {
|
}).finally(() => {
|
||||||
this.set('saveIcon', 'check');
|
this.set('saving', false);
|
||||||
} else {
|
});
|
||||||
this.set('saveIcon', 'times');
|
|
||||||
}
|
|
||||||
setTimeout(() => this.set('saveIcon', ''), 5000);
|
|
||||||
}).finally(() => this.set('saving', false))
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -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;
|
|
@ -1,13 +1,14 @@
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
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";
|
import { A } from "@ember/array";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model() {
|
model() {
|
||||||
return ajax('/admin/wizards/custom-fields');
|
return CustomWizardCustomField.listFields();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
controller.set('customFields', A(model || []));
|
const customFields = A(model || []);
|
||||||
|
controller.set('customFields', customFields);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -19,6 +19,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{wizard-message
|
||||||
|
key='create'
|
||||||
|
url=documentationUrl
|
||||||
|
component='custom_fields'}}
|
||||||
|
|
||||||
<div class="admin-wizard-container">
|
<div class="admin-wizard-container">
|
||||||
{{#if customFields}}
|
{{#if customFields}}
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -537,6 +537,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-wizards-custom-fields {
|
.admin-wizards-custom-fields {
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.select-kit {
|
.select-kit {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,9 @@ en:
|
||||||
type: "Select an action type"
|
type: "Select an action type"
|
||||||
edit: "You're editing an action"
|
edit: "You're editing an action"
|
||||||
documentation: "Check out the action documentation"
|
documentation: "Check out the action documentation"
|
||||||
|
custom_fields:
|
||||||
|
create: "Create or edit a custom field record"
|
||||||
|
documentation: Check out the custom field documentation
|
||||||
|
|
||||||
editor:
|
editor:
|
||||||
show: "Show"
|
show: "Show"
|
||||||
|
|
|
@ -6,6 +6,18 @@ en:
|
||||||
|
|
||||||
wizard:
|
wizard:
|
||||||
custom_title: "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:
|
field:
|
||||||
too_short: "%{label} must be at least %{min} characters"
|
too_short: "%{label} must be at least %{min} characters"
|
||||||
required: "%{label} is required."
|
required: "%{label} is required."
|
||||||
|
|
|
@ -4,32 +4,34 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
custom_fields = custom_field_params[:custom_fields].map do |data|
|
fields_to_save = []
|
||||||
CustomWizard::CustomField.new(data.to_h)
|
|
||||||
|
custom_field_params[:custom_fields].each do |field_param|
|
||||||
|
field_id = nil
|
||||||
|
field_data = {}
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
custom_fields.each do |custom_field|
|
PluginStoreRow.transaction do
|
||||||
custom_field.validate
|
fields_to_save.each do |field|
|
||||||
|
unless field.save
|
||||||
unless custom_field.valid?
|
raise ActiveRecord::Rollback.new,
|
||||||
raise Discourse::InvalidParameters,
|
field.errors.any? ?
|
||||||
custom_field.errors.full_messages.join("\n\n")
|
field.errors.full_messages.join("\n\n") :
|
||||||
|
I18n.t("wizard.custom_field.error.save_default", name: field.name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
all_fields_saved = true
|
render json: success_json
|
||||||
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -37,8 +39,8 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
||||||
def custom_field_params
|
def custom_field_params
|
||||||
params.permit(
|
params.permit(
|
||||||
custom_fields: [
|
custom_fields: [
|
||||||
:klass,
|
|
||||||
:name,
|
:name,
|
||||||
|
:klass,
|
||||||
:type,
|
:type,
|
||||||
serializers: []
|
serializers: []
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,19 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ::CustomWizard::CustomField
|
class ::CustomWizard::CustomField
|
||||||
include HasErrors
|
include HasErrors
|
||||||
include ActiveModel::Serialization
|
include ActiveModel::Serialization
|
||||||
|
|
||||||
CLASSES ||= ["topic", "group", "category", "post"]
|
attr_reader :id
|
||||||
SERIALIZERS ||= ["topic_view", "topic_list_item", "post", "basic_category"]
|
|
||||||
TYPES ||= ["string", "boolean", "integer", "json"]
|
|
||||||
ATTRS ||= ["name", "klass", "type", "serializers"]
|
|
||||||
KEY ||= "custom_wizard_custom_fields"
|
|
||||||
|
|
||||||
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
|
data = data.with_indifferent_access
|
||||||
|
|
||||||
ATTRS.each do |attr|
|
ATTRS.each do |attr|
|
||||||
self.class.class_eval { attr_accessor attr }
|
self.class.class_eval { attr_accessor attr }
|
||||||
send("#{attr}=", data[attr]) if data[attr].present?
|
|
||||||
|
value = data[attr]
|
||||||
|
|
||||||
|
if value.present?
|
||||||
|
send("#{attr}=", value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,19 +43,18 @@ class ::CustomWizard::CustomField
|
||||||
|
|
||||||
if valid?
|
if valid?
|
||||||
data = {}
|
data = {}
|
||||||
name = nil
|
key = name
|
||||||
|
|
||||||
ATTRS.each do |attr|
|
(ATTRS - ['name']).each do |attr|
|
||||||
value = send(attr)
|
data[attr] = send(attr)
|
||||||
|
|
||||||
if attr == 'name'
|
|
||||||
name = value.parameterize(separator: '_')
|
|
||||||
else
|
|
||||||
data[attr] = value
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
PluginStore.set(KEY, name, data)
|
if self.class.save_to_store(id, key, data)
|
||||||
|
self.class.reset
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -43,50 +63,111 @@ class ::CustomWizard::CustomField
|
||||||
def validate
|
def validate
|
||||||
ATTRS.each do |attr|
|
ATTRS.each do |attr|
|
||||||
value = send(attr)
|
value = send(attr)
|
||||||
|
i18n_key = "wizard.custom_field.error"
|
||||||
|
|
||||||
if value.blank?
|
if REQUIRED.include?(attr) && value.blank?
|
||||||
add_error("Attribute required: #{attr}")
|
I18n.t("#{i18n_key}.required_attribute", attr: attr)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
if attr == 'klass' && CLASSES.exclude?(value)
|
if (attr == 'klass' && CLASSES.keys.exclude?(value.to_sym)) ||
|
||||||
add_error("Unsupported class: #{value}")
|
(attr == 'serializers' && CLASSES[klass.to_sym].blank?)
|
||||||
|
add_error(I18n.t("#{i18n_key}.unsupported_class", class: value))
|
||||||
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
if attr == 'serializers' && value.present? && (SERIALIZERS & value).empty?
|
if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0
|
||||||
add_error("Unsupported serializer: #{value}")
|
add_error(I18n.t("#{i18n_key}.unsupported_serializers",
|
||||||
|
class: klass,
|
||||||
|
serializers: unsupported.join(", ")
|
||||||
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
if attr == 'type' && TYPES.exclude?(value)
|
if attr == 'type' && TYPES.exclude?(value)
|
||||||
add_error("Unsupported type: #{value}")
|
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new?
|
||||||
|
id.blank?
|
||||||
|
end
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
errors.blank?
|
errors.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.reset
|
||||||
|
@list = nil
|
||||||
|
end
|
||||||
|
|
||||||
def self.list
|
def self.list
|
||||||
PluginStoreRow.where(plugin_name: KEY)
|
@list ||= PluginStoreRow.where(plugin_name: NAMESPACE)
|
||||||
.map do |record|
|
.map { |record| create_from_store(record) }
|
||||||
data = JSON.parse(record.value)
|
|
||||||
data[:name] = record.key
|
|
||||||
self.new(data)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.list_by(attr, value)
|
def self.list_by(attr, value)
|
||||||
self.list.select do |cf|
|
self.list.select do |cf|
|
||||||
if attr == 'serializers'
|
if attr == :serializers
|
||||||
cf.send(attr).include?(value)
|
cf.send(attr).include?(value)
|
||||||
else
|
else
|
||||||
cf.send(attr) == value
|
cf.send(attr) == value
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
41
plugin.rb
41
plugin.rb
|
@ -167,29 +167,44 @@ after_initialize do
|
||||||
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
|
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField::CLASSES.each do |klass|
|
CustomWizard::CustomField::CLASSES.keys.each do |klass|
|
||||||
add_model_callback(klass.to_sym, :after_initialize) do
|
add_model_callback(klass, :after_initialize) do
|
||||||
CustomWizard::CustomField.list_by('klass', klass).each do |field|
|
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
|
||||||
klass.classify
|
klass.to_s
|
||||||
|
.classify
|
||||||
.constantize
|
.constantize
|
||||||
.register_custom_field_type(field.name, field.type.to_sym)
|
.register_custom_field_type(field.name, field.type.to_sym)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField::SERIALIZERS.each do |serializer_klass|
|
module CustomWizardCustomFieldSerialization
|
||||||
"#{serializer_klass}_serializer".classify.constantize.class_eval do
|
def attributes(*args)
|
||||||
CustomWizard::CustomField.list_by('serializers', serializer_klass).each do |field|
|
hash = super
|
||||||
attributes(field.name.to_sym)
|
@cw_klass = self.class.name.underscore.gsub("_serializer", "")
|
||||||
class_eval %{def #{field.name}
|
|
||||||
if "#{serializer_klass}" == "topic_view"
|
if cw_fields.any?
|
||||||
object.topic.custom_fields["#{field.name}"]
|
cw_fields.each do |field|
|
||||||
|
if @cw_klass == "topic_view"
|
||||||
|
hash[field.name.to_sym] = object.topic.custom_fields["#{field.name}"]
|
||||||
else
|
else
|
||||||
object.custom_fields["#{field.name}"]
|
hash[field.name.to_sym] = object.custom_fields["#{field.name}"]
|
||||||
end
|
end
|
||||||
end}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
hash
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
DiscourseEvent.trigger(:custom_wizard_ready)
|
DiscourseEvent.trigger(:custom_wizard_ready)
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
class CustomWizard::CustomFieldSerializer < ApplicationSerializer
|
class CustomWizard::CustomFieldSerializer < ApplicationSerializer
|
||||||
attributes :klass, :name, :type, :serializers
|
attributes :id, :klass, :name, :type, :serializers
|
||||||
end
|
end
|
178
spec/components/custom_wizard/custom_field_spec.rb
Normale Datei
178
spec/components/custom_wizard/custom_field_spec.rb
Normale Datei
|
@ -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
|
117
spec/extensions/custom_field_extensions_spec.rb
Normale Datei
117
spec/extensions/custom_field_extensions_spec.rb
Normale Datei
|
@ -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
|
|
@ -14,6 +14,9 @@ describe ExtraLocalesControllerCustomWizard, type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns locales when requested by wizard" do
|
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(
|
expect(
|
||||||
ExtraLocalesController.url("wizard")
|
ExtraLocalesController.url("wizard")
|
||||||
).to eq(
|
).to eq(
|
||||||
|
|
37
spec/fixtures/custom_field/custom_fields.json
gevendort
Normale Datei
37
spec/fixtures/custom_field/custom_fields.json
gevendort
Normale Datei
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
32
spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
Normale Datei
32
spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
Normale Datei
|
@ -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
|
29
spec/serializers/custom_wizard/custom_field_serializer_spec.rb
Normale Datei
29
spec/serializers/custom_wizard/custom_field_serializer_spec.rb
Normale Datei
|
@ -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
|
Laden …
In neuem Issue referenzieren