Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 09:20:29 +01:00
Add custom field improvements (#115)
* Add custom field improvements This PR does a few things to improve our support of custom fields 1. Adds custom fields added by other plugins to the list in admin/wizards/custom-fields and the custom field list in the mapper selector 2. Adds support for json custom fields in the wizard actions * Make eslint happy * Make prettier happy * Make rubocop happy * Make ember template lint happy * Don't assume we have the context in the selector * Ensure custom fields don't require optional attributes (with tests)
Dieser Commit ist enthalten in:
Ursprung
f1f13065c8
Commit
af3e61fe75
19 geänderte Dateien mit 276 neuen und 55 gelöschten Zeilen
|
@ -1,6 +1,6 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
import { alias, or } from "@ember/object/computed";
|
import { alias, equal, or } from "@ember/object/computed";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
const generateContent = function (array, type) {
|
const generateContent = function (array, type) {
|
||||||
|
@ -29,6 +29,7 @@ export default Component.extend({
|
||||||
loading: or("saving", "destroying"),
|
loading: or("saving", "destroying"),
|
||||||
destroyDisabled: alias("loading"),
|
destroyDisabled: alias("loading"),
|
||||||
closeDisabled: alias("loading"),
|
closeDisabled: alias("loading"),
|
||||||
|
isExternal: equal("field.id", "external"),
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
||||||
|
@ -61,13 +62,14 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"saving",
|
"saving",
|
||||||
|
"isExternal",
|
||||||
"field.name",
|
"field.name",
|
||||||
"field.klass",
|
"field.klass",
|
||||||
"field.type",
|
"field.type",
|
||||||
"field.serializers"
|
"field.serializers"
|
||||||
)
|
)
|
||||||
saveDisabled(saving) {
|
saveDisabled(saving, isExternal) {
|
||||||
if (saving) {
|
if (saving || isExternal) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,11 @@ export default Component.extend(UndoChanges, {
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("action.type")
|
||||||
|
customFieldsContext(type) {
|
||||||
|
return `action.${type}`;
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed("wizard.steps")
|
@discourseComputed("wizard.steps")
|
||||||
runAfterContent(steps) {
|
runAfterContent(steps) {
|
||||||
let content = steps.map(function (step) {
|
let content = steps.map(function (step) {
|
||||||
|
|
|
@ -6,11 +6,24 @@ import {
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
||||||
import { generateName, snakeCase, userProperties } from "../lib/wizard";
|
import {
|
||||||
|
generateName,
|
||||||
|
sentenceCase,
|
||||||
|
snakeCase,
|
||||||
|
userProperties,
|
||||||
|
} from "../lib/wizard";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { bind, later } from "@ember/runloop";
|
import { bind, later } from "@ember/runloop";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
|
const customFieldActionMap = {
|
||||||
|
topic: ["create_topic", "send_message"],
|
||||||
|
post: ["create_topic", "send_message"],
|
||||||
|
category: ["create_category"],
|
||||||
|
group: ["create_group"],
|
||||||
|
user: ["update_profile"],
|
||||||
|
};
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNameBindings: [":mapper-selector", "activeType"],
|
classNameBindings: [":mapper-selector", "activeType"],
|
||||||
|
|
||||||
|
@ -188,11 +201,19 @@ export default Component.extend({
|
||||||
customFields
|
customFields
|
||||||
) {
|
) {
|
||||||
let content;
|
let content;
|
||||||
|
let context;
|
||||||
|
let contextType;
|
||||||
|
|
||||||
|
if (this.options.context) {
|
||||||
|
let contextAttrs = this.options.context.split(".");
|
||||||
|
context = contextAttrs[0];
|
||||||
|
contextType = contextAttrs[1];
|
||||||
|
}
|
||||||
|
|
||||||
if (activeType === "wizardField") {
|
if (activeType === "wizardField") {
|
||||||
content = wizardFields;
|
content = wizardFields;
|
||||||
|
|
||||||
if (this.options.context === "field") {
|
if (context === "field") {
|
||||||
content = content.filter((field) => field.id !== currentFieldId);
|
content = content.filter((field) => field.id !== currentFieldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +225,7 @@ export default Component.extend({
|
||||||
type: a.type,
|
type: a.type,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (this.options.context === "action") {
|
if (context === "action") {
|
||||||
content = content.filter((a) => a.id !== currentActionId);
|
content = content.filter((a) => a.id !== currentActionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,7 +239,7 @@ export default Component.extend({
|
||||||
.concat(userFields || []);
|
.concat(userFields || []);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.options.context === "action" &&
|
context === "action" &&
|
||||||
this.inputType === "association" &&
|
this.inputType === "association" &&
|
||||||
this.selectorType === "key"
|
this.selectorType === "key"
|
||||||
) {
|
) {
|
||||||
|
@ -234,7 +255,17 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeType === "customField") {
|
if (activeType === "customField") {
|
||||||
content = customFields;
|
content = customFields
|
||||||
|
.filter((f) => {
|
||||||
|
return (
|
||||||
|
f.type !== "json" &&
|
||||||
|
customFieldActionMap[f.klass].includes(contextType)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((f) => ({
|
||||||
|
id: f.name,
|
||||||
|
name: `${sentenceCase(f.klass)} ${f.name} (${f.type})`,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
|
|
|
@ -3,12 +3,12 @@ import CustomWizardCustomField from "../models/custom-wizard-custom-field";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
messageKey: "create",
|
messageKey: "create",
|
||||||
fieldKeys: ["klass", "type", "serializers", "name"],
|
fieldKeys: ["klass", "type", "name", "serializers"],
|
||||||
documentationUrl: "https://thepavilion.io/t/3572",
|
documentationUrl: "https://thepavilion.io/t/3572",
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addField() {
|
addField() {
|
||||||
this.get("customFields").pushObject(
|
this.get("customFields").unshiftObject(
|
||||||
CustomWizardCustomField.create({ edit: true })
|
CustomWizardCustomField.create({ edit: true })
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -120,4 +120,5 @@ export {
|
||||||
listProperties,
|
listProperties,
|
||||||
notificationLevels,
|
notificationLevels,
|
||||||
wizardFieldList,
|
wizardFieldList,
|
||||||
|
sentenceCase,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@ import CustomWizard from "../models/custom-wizard";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { selectKitContent } from "../lib/wizard";
|
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
|
@ -33,9 +32,7 @@ export default DiscourseRoute.extend({
|
||||||
wizardList: parentModel.wizard_list,
|
wizardList: parentModel.wizard_list,
|
||||||
fieldTypes,
|
fieldTypes,
|
||||||
userFields: parentModel.userFields,
|
userFields: parentModel.userFields,
|
||||||
customFields: selectKitContent(
|
customFields: parentModel.custom_fields,
|
||||||
parentModel.custom_fields.map((f) => f.name)
|
|
||||||
),
|
|
||||||
apis: parentModel.apis,
|
apis: parentModel.apis,
|
||||||
themes: parentModel.themes,
|
themes: parentModel.themes,
|
||||||
wizard,
|
wizard,
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
none="admin.wizard.custom_field.type.select"
|
none="admin.wizard.custom_field.type.select"
|
||||||
onChange=(action (mut field.type))}}
|
onChange=(action (mut field.type))}}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="input">
|
||||||
|
{{input
|
||||||
|
value=field.name
|
||||||
|
placeholder=(i18n "admin.wizard.custom_field.name.select")}}
|
||||||
|
</td>
|
||||||
<td class="multi-select">
|
<td class="multi-select">
|
||||||
{{multi-select
|
{{multi-select
|
||||||
value=field.serializers
|
value=field.serializers
|
||||||
|
@ -20,11 +25,6 @@
|
||||||
none="admin.wizard.custom_field.serializers.select"
|
none="admin.wizard.custom_field.serializers.select"
|
||||||
onChange=(action (mut field.serializers))}}
|
onChange=(action (mut field.serializers))}}
|
||||||
</td>
|
</td>
|
||||||
<td class="input">
|
|
||||||
{{input
|
|
||||||
value=field.name
|
|
||||||
placeholder=(i18n "admin.wizard.custom_field.name.select")}}
|
|
||||||
</td>
|
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
{{#if loading}}
|
{{#if loading}}
|
||||||
{{loading-spinner size="small"}}
|
{{loading-spinner size="small"}}
|
||||||
|
@ -51,13 +51,25 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<td><label>{{field.klass}}</label></td>
|
<td><label>{{field.klass}}</label></td>
|
||||||
<td><label>{{field.type}}</label></td>
|
<td><label>{{field.type}}</label></td>
|
||||||
<td class="multi-select">
|
|
||||||
{{#each field.serializers as |serializer|}}
|
|
||||||
<label>{{serializer}}</label>
|
|
||||||
{{/each}}
|
|
||||||
</td>
|
|
||||||
<td class="input"><label>{{field.name}}</label></td>
|
<td class="input"><label>{{field.name}}</label></td>
|
||||||
<td class="actions">
|
<td class="multi-select">
|
||||||
{{d-button action="edit" icon="pencil-alt"}}
|
{{#if isExternal}}
|
||||||
|
—
|
||||||
|
{{else}}
|
||||||
|
{{#each field.serializers as |serializer|}}
|
||||||
|
<label>{{serializer}}</label>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
|
{{#if isExternal}}
|
||||||
|
<td class="external">
|
||||||
|
<label title={{i18n "admin.wizard.custom_field.external.title"}}>
|
||||||
|
{{i18n "admin.wizard.custom_field.external.label"}}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
{{else}}
|
||||||
|
<td class="actions">
|
||||||
|
{{d-button action="edit" icon="pencil-alt"}}
|
||||||
|
</td>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -738,7 +738,7 @@
|
||||||
wizardActionSelection="value"
|
wizardActionSelection="value"
|
||||||
userFieldSelection="value"
|
userFieldSelection="value"
|
||||||
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
||||||
context="action"
|
context=customFieldsContext
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -667,6 +667,10 @@
|
||||||
margin-left: 5px !important;
|
margin-left: 5px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td.external {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ en:
|
||||||
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:
|
custom_fields:
|
||||||
create: "Create, edit or destroy a custom field record"
|
create: "View, create, edit and destroy custom fields"
|
||||||
saved: "Saved custom field"
|
saved: "Saved custom field"
|
||||||
error: "Failed to save: {{messages}}"
|
error: "Failed to save: {{messages}}"
|
||||||
documentation: Check out the custom field documentation
|
documentation: Check out the custom field documentation
|
||||||
|
@ -322,6 +322,9 @@ en:
|
||||||
custom_field:
|
custom_field:
|
||||||
nav_label: "Custom Fields"
|
nav_label: "Custom Fields"
|
||||||
add: "Add"
|
add: "Add"
|
||||||
|
external:
|
||||||
|
label: "from another plugin"
|
||||||
|
title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here."
|
||||||
name:
|
name:
|
||||||
label: "Name"
|
label: "Name"
|
||||||
select: "underscored_name"
|
select: "underscored_name"
|
||||||
|
|
|
@ -14,7 +14,7 @@ class CustomWizard::AdminController < ::Admin::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_field_list
|
def custom_field_list
|
||||||
serialize_data(CustomWizard::CustomField.list, CustomWizard::CustomFieldSerializer)
|
serialize_data(CustomWizard::CustomField.full_list, CustomWizard::CustomFieldSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_error(message)
|
def render_error(message)
|
||||||
|
|
6
extensions/custom_field/extension.rb
Normale Datei
6
extensions/custom_field/extension.rb
Normale Datei
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module CustomWizardCustomFieldExtension
|
||||||
|
def custom_field_types
|
||||||
|
@custom_field_types
|
||||||
|
end
|
||||||
|
end
|
|
@ -454,32 +454,51 @@ class CustomWizard::Action
|
||||||
data: data,
|
data: data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
registered_fields = CustomWizard::CustomField.full_list
|
||||||
registered_fields = CustomWizard::CustomField.cached_list
|
|
||||||
|
|
||||||
field_map.each do |field|
|
field_map.each do |field|
|
||||||
keyArr = field[:key].split('.')
|
keyArr = field[:key].split('.')
|
||||||
value = field[:value]
|
value = field[:value]
|
||||||
|
|
||||||
if keyArr.length > 1
|
if keyArr.length > 1
|
||||||
klass = keyArr.first
|
klass = keyArr.first.to_sym
|
||||||
name = keyArr.last
|
name = keyArr.second
|
||||||
|
|
||||||
|
if keyArr.length === 3 && name.include?("{}")
|
||||||
|
name = name.gsub("{}", "")
|
||||||
|
json_attr = keyArr.last
|
||||||
|
type = :json
|
||||||
|
end
|
||||||
else
|
else
|
||||||
name = keyArr.first
|
name = keyArr.first
|
||||||
end
|
end
|
||||||
|
|
||||||
registered = registered_fields.select { |f| f[:name] == name }
|
registered = registered_fields.select { |f| f.name == name }.first
|
||||||
if registered.first.present?
|
if registered.present?
|
||||||
klass = registered.first[:klass]
|
klass = registered.klass
|
||||||
|
type = registered.type
|
||||||
end
|
end
|
||||||
|
|
||||||
if klass === 'topic'
|
next if type === :json && json_attr.blank?
|
||||||
|
|
||||||
|
if klass === :topic
|
||||||
params[:topic_opts] ||= {}
|
params[:topic_opts] ||= {}
|
||||||
params[:topic_opts][:custom_fields] ||= {}
|
params[:topic_opts][:custom_fields] ||= {}
|
||||||
params[:topic_opts][:custom_fields][name] = value
|
|
||||||
|
if type === :json
|
||||||
|
params[:topic_opts][:custom_fields][name] ||= {}
|
||||||
|
params[:topic_opts][:custom_fields][name][json_attr] = value
|
||||||
|
else
|
||||||
|
params[:topic_opts][:custom_fields][name] = value
|
||||||
|
end
|
||||||
else
|
else
|
||||||
params[:custom_fields] ||= {}
|
if type === :json
|
||||||
params[:custom_fields][name] = value
|
params[:custom_fields][name] ||= {}
|
||||||
|
params[:custom_fields][name][json_attr] = value
|
||||||
|
else
|
||||||
|
params[:custom_fields] ||= {}
|
||||||
|
params[:custom_fields][name] = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,10 +66,12 @@ class ::CustomWizard::CustomField
|
||||||
value = send(attr)
|
value = send(attr)
|
||||||
i18n_key = "wizard.custom_field.error"
|
i18n_key = "wizard.custom_field.error"
|
||||||
|
|
||||||
if value.blank?
|
if value.blank? && REQUIRED.include?(attr)
|
||||||
if REQUIRED.include?(attr)
|
add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
|
||||||
add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if attr == 'serializers' && !value.is_a?(Array)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ class ::CustomWizard::CustomField
|
||||||
|
|
||||||
fields.select do |cf|
|
fields.select do |cf|
|
||||||
if attr == :serializers
|
if attr == :serializers
|
||||||
cf[attr].include?(value)
|
cf[attr] && cf[attr].include?(value)
|
||||||
else
|
else
|
||||||
cf[attr] == value
|
cf[attr] == value
|
||||||
end
|
end
|
||||||
|
@ -215,4 +217,32 @@ class ::CustomWizard::CustomField
|
||||||
def self.enabled?
|
def self.enabled?
|
||||||
any?
|
any?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.external_list
|
||||||
|
external = []
|
||||||
|
|
||||||
|
CLASSES.keys.each do |klass|
|
||||||
|
field_types = klass.to_s.classify.constantize.custom_field_types
|
||||||
|
|
||||||
|
if field_types.present?
|
||||||
|
field_types.each do |name, type|
|
||||||
|
unless list.any? { |field| field.name === name }
|
||||||
|
field = new(
|
||||||
|
'external',
|
||||||
|
name: name,
|
||||||
|
klass: klass,
|
||||||
|
type: type
|
||||||
|
)
|
||||||
|
external.push(field)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
external
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.full_list
|
||||||
|
(list + external_list).uniq
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
11
plugin.rb
11
plugin.rb
|
@ -91,6 +91,7 @@ after_initialize do
|
||||||
../extensions/users_controller.rb
|
../extensions/users_controller.rb
|
||||||
../extensions/custom_field/preloader.rb
|
../extensions/custom_field/preloader.rb
|
||||||
../extensions/custom_field/serializer.rb
|
../extensions/custom_field/serializer.rb
|
||||||
|
../extensions/custom_field/extension.rb
|
||||||
].each do |path|
|
].each do |path|
|
||||||
load File.expand_path(path, __FILE__)
|
load File.expand_path(path, __FILE__)
|
||||||
end
|
end
|
||||||
|
@ -183,18 +184,18 @@ after_initialize do
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField::CLASSES.keys.each do |klass|
|
CustomWizard::CustomField::CLASSES.keys.each do |klass|
|
||||||
|
class_constant = klass.to_s.classify.constantize
|
||||||
|
|
||||||
add_model_callback(klass, :after_initialize) do
|
add_model_callback(klass, :after_initialize) do
|
||||||
if CustomWizard::CustomField.enabled?
|
if CustomWizard::CustomField.enabled?
|
||||||
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
|
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
|
||||||
klass.to_s
|
class_constant.register_custom_field_type(field[:name], field[:type].to_sym)
|
||||||
.classify
|
|
||||||
.constantize
|
|
||||||
.register_custom_field_type(field[:name], field[:type].to_sym)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
klass.to_s.classify.constantize.singleton_class.prepend CustomWizardCustomFieldPreloader
|
class_constant.singleton_class.prepend CustomWizardCustomFieldPreloader
|
||||||
|
class_constant.singleton_class.prepend CustomWizardCustomFieldExtension
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField.serializers.each do |serializer_klass|
|
CustomWizard::CustomField.serializers.each do |serializer_klass|
|
||||||
|
|
|
@ -72,6 +72,42 @@ describe CustomWizard::Action do
|
||||||
raw: "topic body"
|
raw: "topic body"
|
||||||
).exists?).to eq(false)
|
).exists?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "adds custom fields" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
|
wizard.create_updater(wizard.steps.first.id,
|
||||||
|
step_1_field_1: "Topic Title",
|
||||||
|
step_1_field_2: "topic body"
|
||||||
|
).update
|
||||||
|
wizard.create_updater(wizard.steps.second.id, {}).update
|
||||||
|
wizard.create_updater(wizard.steps.last.id,
|
||||||
|
step_3_field_3: category.id
|
||||||
|
).update
|
||||||
|
|
||||||
|
topic = Topic.where(
|
||||||
|
title: "Topic Title",
|
||||||
|
category_id: category.id
|
||||||
|
).first
|
||||||
|
topic_custom_field = TopicCustomField.where(
|
||||||
|
name: "topic_field",
|
||||||
|
value: "Topic custom field value",
|
||||||
|
topic_id: topic.id
|
||||||
|
)
|
||||||
|
topic_json_custom_field = TopicCustomField.where("
|
||||||
|
name = 'topic_json_field' AND
|
||||||
|
(value::json->>'key_1') = 'Key 1 value' AND
|
||||||
|
(value::json->>'key_2') = 'Key 2 value' AND
|
||||||
|
topic_id = #{topic.id}"
|
||||||
|
)
|
||||||
|
post_custom_field = PostCustomField.where(
|
||||||
|
name: "post_field",
|
||||||
|
value: "Post custom field value",
|
||||||
|
post_id: topic.first_post.id
|
||||||
|
)
|
||||||
|
expect(topic_custom_field.exists?).to eq(true)
|
||||||
|
expect(topic_json_custom_field.exists?).to eq(true)
|
||||||
|
expect(post_custom_field.exists?).to eq(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'sending a message' do
|
context 'sending a message' do
|
||||||
|
|
|
@ -49,6 +49,40 @@ describe CustomWizard::CustomField do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "validation" do
|
context "validation" do
|
||||||
|
it "does not save without required attributes" do
|
||||||
|
invalid_field_json = custom_field_json['custom_fields'].first
|
||||||
|
invalid_field_json['klass'] = nil
|
||||||
|
|
||||||
|
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.required_attribute", attr: "klass")
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
PluginStoreRow.where(
|
||||||
|
plugin_name: CustomWizard::CustomField::NAMESPACE,
|
||||||
|
key: custom_field.name
|
||||||
|
).exists?
|
||||||
|
).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does save without optional attributes" do
|
||||||
|
field_json = custom_field_json['custom_fields'].first
|
||||||
|
field_json['serializers'] = nil
|
||||||
|
|
||||||
|
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||||
|
expect(custom_field.save).to eq(true)
|
||||||
|
expect(custom_field.valid?).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
|
||||||
|
|
||||||
it "does not save with an unsupported class" do
|
it "does not save with an unsupported class" do
|
||||||
invalid_field_json = custom_field_json['custom_fields'].first
|
invalid_field_json = custom_field_json['custom_fields'].first
|
||||||
invalid_field_json['klass'] = 'user'
|
invalid_field_json['klass'] = 'user'
|
||||||
|
@ -178,6 +212,22 @@ describe CustomWizard::CustomField do
|
||||||
it "lists saved custom field records by attribute value" do
|
it "lists saved custom field records by attribute value" do
|
||||||
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
|
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "lists saved custom field records by optional values" do
|
||||||
|
field_json = custom_field_json['custom_fields'].first
|
||||||
|
field_json['serializers'] = nil
|
||||||
|
|
||||||
|
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||||
|
expect(CustomWizard::CustomField.list_by(:serializers, ['post']).length).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "lists custom field records added by other plugins " do
|
||||||
|
expect(CustomWizard::CustomField.external_list.length).to eq(11)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "lists all custom field records" do
|
||||||
|
expect(CustomWizard::CustomField.full_list.length).to eq(15)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is enabled if there are custom fields" do
|
it "is enabled if there are custom fields" do
|
||||||
|
|
30
spec/fixtures/wizard.json
gevendort
30
spec/fixtures/wizard.json
gevendort
|
@ -391,10 +391,34 @@
|
||||||
"pairs": [
|
"pairs": [
|
||||||
{
|
{
|
||||||
"index": 0,
|
"index": 0,
|
||||||
"key": "custom_field_1",
|
"key": "post_field",
|
||||||
"key_type": "text",
|
"key_type": "text",
|
||||||
"value": "title",
|
"value": "Post custom field value",
|
||||||
"value_type": "user_field",
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"key": "topic.topic_field",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Topic custom field value",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"key": "topic.topic_json_field{}.key_1",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Key 1 value",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"key": "topic.topic_json_field{}.key_2",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Key 2 value",
|
||||||
|
"value_type": "text",
|
||||||
"connector": "association"
|
"connector": "association"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,9 +17,9 @@ describe CustomWizard::AdminCustomFieldsController do
|
||||||
sign_in(admin_user)
|
sign_in(admin_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the list of custom fields" do
|
it "returns the full list of custom fields" do
|
||||||
get "/admin/wizards/custom-fields.json"
|
get "/admin/wizards/custom-fields.json"
|
||||||
expect(response.parsed_body.length).to eq(4)
|
expect(response.parsed_body.length).to eq(15)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "saves custom fields" do
|
it "saves custom fields" do
|
||||||
|
|
Laden …
In neuem Issue referenzieren