Commits vergleichen
6 Commits
main
...
update_sub
Autor | SHA1 | Datum | |
---|---|---|---|
|
16eb609b55 | ||
|
a81b4f8eb5 | ||
|
7c84deda18 | ||
|
d351577ea3 | ||
|
5a347b656a | ||
|
7de00ca040 |
36 geänderte Dateien mit 530 neuen und 594 gelöschten Zeilen
2
.github/workflows/plugin-tests.yml
gevendort
2
.github/workflows/plugin-tests.yml
gevendort
|
@ -52,7 +52,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: discourse/discourse
|
||||
ref: "${{ (github.base_ref || github.ref) }}"
|
||||
ref: "tests-passed"
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Fetch Repo Name
|
||||
|
|
|
@ -2,29 +2,7 @@ import Component from "@ember/component";
|
|||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { alias, equal, or } from "@ember/object/computed";
|
||||
import I18n from "I18n";
|
||||
|
||||
import wizardSchema, {
|
||||
requiringAdditionalSubscription,
|
||||
subscriptionLevel,
|
||||
} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
|
||||
|
||||
const generateContent = function (kategory, subscription) {
|
||||
let unsubscribedCustomFields = requiringAdditionalSubscription(
|
||||
subscription,
|
||||
"custom_fields",
|
||||
kategory
|
||||
);
|
||||
return wizardSchema.custom_field[kategory].reduce((result, item) => {
|
||||
let disabled = unsubscribedCustomFields.includes(item);
|
||||
result.push({
|
||||
id: item,
|
||||
name: I18n.t(`admin.wizard.custom_field.${kategory}.${item}`),
|
||||
subscription: subscriptionLevel(item, "custom_fields", kategory),
|
||||
disabled,
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
};
|
||||
import { buildSubscriptionContent } from "../lib/wizard";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "tr",
|
||||
|
@ -60,12 +38,12 @@ export default Component.extend({
|
|||
|
||||
@discourseComputed("subscription")
|
||||
customFieldTypes(subscription) {
|
||||
return generateContent("type", subscription);
|
||||
return buildSubscriptionContent("custom_fields", "types", subscription);
|
||||
},
|
||||
|
||||
@discourseComputed("subscription")
|
||||
customFieldKlasses(subscription) {
|
||||
return generateContent("klass", subscription);
|
||||
return buildSubscriptionContent("custom_fields", "klass", subscription);
|
||||
},
|
||||
|
||||
@observes("field.klass")
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import wizardSchema, {
|
||||
requiringAdditionalSubscription,
|
||||
subscriptionLevel,
|
||||
} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
|
||||
import { empty, equal, or } from "@ember/object/computed";
|
||||
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
||||
import {
|
||||
buildSubscriptionContent,
|
||||
notificationLevels,
|
||||
selectKitContent,
|
||||
} from "../lib/wizard";
|
||||
import { computed } from "@ember/object";
|
||||
import UndoChanges from "../mixins/undo-changes";
|
||||
import Component from "@ember/component";
|
||||
|
@ -99,20 +99,6 @@ export default Component.extend(UndoChanges, {
|
|||
|
||||
@discourseComputed("subscription")
|
||||
actionTypes(subscription) {
|
||||
let unsubscribedActions = requiringAdditionalSubscription(
|
||||
subscription,
|
||||
"actions",
|
||||
""
|
||||
);
|
||||
return Object.keys(wizardSchema.action.types).reduce((result, type) => {
|
||||
let disabled = unsubscribedActions.includes(type);
|
||||
result.push({
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.action.${type}.label`),
|
||||
subscription: subscriptionLevel(type, "actions", ""),
|
||||
disabled,
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
return buildSubscriptionContent("action", "type", subscription);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ export default {
|
|||
this.set("customWizardCriticalNotices", criticalNotices);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
api.modifyClass("component:d-navigation", {
|
||||
|
|
|
@ -71,7 +71,7 @@ const field = {
|
|||
type: null,
|
||||
condition: null,
|
||||
},
|
||||
types: {},
|
||||
type: {},
|
||||
mapped: ["prefill", "content", "condition", "index"],
|
||||
required: ["id", "type"],
|
||||
dependent: {},
|
||||
|
@ -84,7 +84,7 @@ const action = {
|
|||
run_after: "wizard_completion",
|
||||
type: null,
|
||||
},
|
||||
types: {
|
||||
type: {
|
||||
create_topic: {
|
||||
title: null,
|
||||
post: null,
|
||||
|
@ -203,107 +203,46 @@ const custom_field = {
|
|||
type: ["string", "boolean", "integer", "json"],
|
||||
};
|
||||
|
||||
const subscription_levels = {
|
||||
standard: {
|
||||
actions: ["send_message", "add_to_group", "watch_categories"],
|
||||
custom_fields: {
|
||||
klass: [],
|
||||
type: ["json"],
|
||||
},
|
||||
},
|
||||
|
||||
business: {
|
||||
actions: ["create_category", "create_group", "send_to_api"],
|
||||
custom_fields: {
|
||||
klass: ["group", "category"],
|
||||
type: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wizardSchema = {
|
||||
wizard,
|
||||
step,
|
||||
field,
|
||||
custom_field,
|
||||
action,
|
||||
subscription_levels,
|
||||
};
|
||||
|
||||
export function requiringAdditionalSubscription(
|
||||
currentSubscription,
|
||||
category,
|
||||
subCategory
|
||||
export function hasRequiredSubscription(
|
||||
currentSubscriptionType,
|
||||
featureSubscriptionType
|
||||
) {
|
||||
switch (category) {
|
||||
case "actions":
|
||||
switch (currentSubscription) {
|
||||
case "business":
|
||||
return [];
|
||||
case "standard":
|
||||
return subscription_levels["business"][category];
|
||||
default:
|
||||
return subscription_levels["standard"][category].concat(
|
||||
subscription_levels["business"][category]
|
||||
);
|
||||
}
|
||||
case "custom_fields":
|
||||
switch (currentSubscription) {
|
||||
case "business":
|
||||
return [];
|
||||
case "standard":
|
||||
return subscription_levels["business"][category][subCategory];
|
||||
default:
|
||||
return subscription_levels["standard"][category][subCategory].concat(
|
||||
subscription_levels["business"][category][subCategory]
|
||||
);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
const types = wizardSchema.subscription.types;
|
||||
return (
|
||||
types.indexOf(currentSubscriptionType) >=
|
||||
types.indexOf(featureSubscriptionType)
|
||||
);
|
||||
}
|
||||
|
||||
export function subscriptionType(klass, attribute, value) {
|
||||
let attributes = wizardSchema.subscription.features[klass];
|
||||
|
||||
if (!attributes || !attributes[attribute] || !attributes[attribute][value]) {
|
||||
return wizardSchema.subscription.types[0];
|
||||
} else {
|
||||
return attributes[attribute][value];
|
||||
}
|
||||
}
|
||||
|
||||
export function subscriptionLevel(type, category, subCategory) {
|
||||
switch (category) {
|
||||
case "actions":
|
||||
if (subscription_levels["business"].actions.includes(type)) {
|
||||
return "business";
|
||||
} else {
|
||||
if (subscription_levels["standard"].actions.includes(type)) {
|
||||
return "standard";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
case "custom_fields":
|
||||
if (
|
||||
subscription_levels["business"].custom_fields[subCategory].includes(
|
||||
type
|
||||
)
|
||||
) {
|
||||
return "business";
|
||||
} else {
|
||||
if (
|
||||
subscription_levels["standard"].custom_fields[subCategory].includes(
|
||||
type
|
||||
)
|
||||
) {
|
||||
return "standard";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
export function buildSchema(model) {
|
||||
wizardSchema.subscription = {};
|
||||
|
||||
export function buildFieldTypes(types) {
|
||||
wizardSchema.field.types = types;
|
||||
}
|
||||
let features = model.subscription_features;
|
||||
features["field"]["types"] = features["field"]["type"];
|
||||
features["action"]["types"] = features["action"]["type"];
|
||||
|
||||
export function buildFieldValidations(validations) {
|
||||
wizardSchema.field.validations = validations;
|
||||
wizardSchema.subscription.features = features;
|
||||
wizardSchema.subscription.types = model.subscription_types;
|
||||
wizardSchema.field.types = model.field_types;
|
||||
wizardSchema.field.validations = model.realtime_validations;
|
||||
}
|
||||
|
||||
const siteSettings = getOwner(this).lookup("site-settings:main");
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import wizardSchema from "./wizard-schema";
|
||||
import wizardSchema, {
|
||||
hasRequiredSubscription,
|
||||
subscriptionType,
|
||||
} from "./wizard-schema";
|
||||
import I18n from "I18n";
|
||||
|
||||
function selectKitContent(content) {
|
||||
return content.map((i) => ({ id: i, name: i }));
|
||||
|
@ -110,6 +114,25 @@ function wizardFieldList(steps = [], opts = {}) {
|
|||
}, []);
|
||||
}
|
||||
|
||||
function buildSubscriptionContent(klass, attribute, currentSubscription) {
|
||||
let attributes = wizardSchema[klass];
|
||||
let values = attributes[attribute];
|
||||
|
||||
if (typeof values === "object") {
|
||||
values = Object.keys(values);
|
||||
}
|
||||
|
||||
return values.map((value) => {
|
||||
let type = subscriptionType(klass, attribute, value);
|
||||
return {
|
||||
id: value,
|
||||
name: I18n.t(`admin.wizard.${klass}.${attribute}.${value}.label`),
|
||||
subscription: type,
|
||||
disabled: hasRequiredSubscription(currentSubscription, type),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
selectKitContent,
|
||||
generateName,
|
||||
|
@ -121,4 +144,5 @@ export {
|
|||
notificationLevels,
|
||||
wizardFieldList,
|
||||
sentenceCase,
|
||||
buildSubscriptionContent,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { buildFieldTypes, buildFieldValidations } from "../lib/wizard-schema";
|
||||
import { buildSchema } from "../lib/wizard-schema";
|
||||
import EmberObject, { set } from "@ember/object";
|
||||
import { A } from "@ember/array";
|
||||
import { all } from "rsvp";
|
||||
|
@ -11,8 +11,7 @@ export default DiscourseRoute.extend({
|
|||
},
|
||||
|
||||
afterModel(model) {
|
||||
buildFieldTypes(model.field_types);
|
||||
buildFieldValidations(model.realtime_validations);
|
||||
buildSchema(model);
|
||||
|
||||
return all([
|
||||
this._getThemes(model),
|
||||
|
|
|
@ -53,16 +53,6 @@
|
|||
</div>
|
||||
|
||||
<div class="wizard-settings">
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.required"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=wizard.required}}
|
||||
<span>{{i18n "admin.wizard.required_label"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.after_signup"}}</label>
|
||||
|
@ -83,6 +73,26 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.save_submissions"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=wizard.save_submissions}}
|
||||
<span>{{i18n "admin.wizard.save_submissions_label"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.restart_on_revisit"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=wizard.restart_on_revisit}}
|
||||
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.prompt_completion"}}</label>
|
||||
|
@ -93,7 +103,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full-inline">
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.after_time"}}</label>
|
||||
</div>
|
||||
|
@ -108,42 +118,32 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.permitted"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=wizard.permitted
|
||||
options=(hash
|
||||
context="wizard"
|
||||
inputTypes="assignment,validation"
|
||||
groupSelection="output"
|
||||
userFieldSelection="key"
|
||||
textSelection="value"
|
||||
inputConnector="and"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#subscription-container subscribed=subscribed}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.save_submissions"}}</label>
|
||||
<label>{{i18n "admin.wizard.required"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=wizard.save_submissions}}
|
||||
<span>{{i18n "admin.wizard.save_submissions_label"}}</span>
|
||||
{{input type="checkbox" checked=wizard.required}}
|
||||
<span>{{i18n "admin.wizard.required_label"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.restart_on_revisit"}}</label>
|
||||
<label>{{i18n "admin.wizard.permitted"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=wizard.restart_on_revisit}}
|
||||
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
|
||||
{{wizard-mapper
|
||||
inputs=wizard.permitted
|
||||
options=(hash
|
||||
context="wizard"
|
||||
inputTypes="assignment,validation"
|
||||
groupSelection="output"
|
||||
userFieldSelection="key"
|
||||
textSelection="value"
|
||||
inputConnector="and"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/subscription-container}}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.required"}}</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="setting-value">
|
||||
<span>{{i18n "admin.wizard.field.required_label"}}</span>
|
||||
{{input type="checkbox" checked=field.required}}
|
||||
|
@ -54,7 +54,7 @@
|
|||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.type"}}</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.type
|
||||
|
@ -220,10 +220,10 @@
|
|||
options=fieldConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>>
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
|
@ -234,12 +234,11 @@
|
|||
</div>
|
||||
|
||||
{{#if isCategory}}
|
||||
<div class="setting pro">
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.property"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.property
|
||||
|
|
|
@ -56,10 +56,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
|
|
|
@ -920,6 +920,7 @@ $error: #ef1700;
|
|||
|
||||
&:not(.subscribed) .subscription-settings {
|
||||
filter: blur(1px);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ en:
|
|||
value: "Value"
|
||||
profile: "profile"
|
||||
type: "Type"
|
||||
none: "Make a selection"
|
||||
none: "Make a selection"
|
||||
submission_key: 'submission key'
|
||||
param_key: 'param'
|
||||
group: "Group"
|
||||
|
@ -126,9 +126,9 @@ en:
|
|||
hide: "Hide"
|
||||
preview: "{{action}} Preview"
|
||||
popover: "{{action}} Fields"
|
||||
|
||||
|
||||
input:
|
||||
conditional:
|
||||
conditional:
|
||||
name: 'if'
|
||||
output: 'then'
|
||||
assignment:
|
||||
|
@ -137,7 +137,7 @@ en:
|
|||
name: 'map'
|
||||
validation:
|
||||
name: 'ensure'
|
||||
|
||||
|
||||
selector:
|
||||
label:
|
||||
text: "text"
|
||||
|
@ -175,7 +175,7 @@ en:
|
|||
dependent: "{{property}} is dependent on {{dependent}}"
|
||||
conflict: "{{type}} with {{property}} '{{value}}' already exists"
|
||||
after_time: "After time invalid"
|
||||
|
||||
|
||||
step:
|
||||
header: "Steps"
|
||||
title: "Title"
|
||||
|
@ -189,7 +189,7 @@ en:
|
|||
force_final:
|
||||
label: "Conditional Final Step"
|
||||
description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step."
|
||||
|
||||
|
||||
field:
|
||||
header: "Fields"
|
||||
label: "Label"
|
||||
|
@ -211,7 +211,7 @@ en:
|
|||
property: "Property"
|
||||
prefill: "Prefill"
|
||||
content: "Content"
|
||||
date_time_format:
|
||||
date_time_format:
|
||||
label: "Format"
|
||||
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
|
||||
validations:
|
||||
|
@ -228,7 +228,7 @@ en:
|
|||
weeks: "Weeks"
|
||||
months: "Months"
|
||||
years: "Years"
|
||||
|
||||
|
||||
type:
|
||||
text: "Text"
|
||||
textarea: Textarea
|
||||
|
@ -247,7 +247,7 @@ en:
|
|||
date: Date
|
||||
time: Time
|
||||
date_time: Date & Time
|
||||
|
||||
|
||||
connector:
|
||||
and: "and"
|
||||
or: "or"
|
||||
|
@ -261,7 +261,7 @@ en:
|
|||
regex: '=~'
|
||||
association: '→'
|
||||
is: 'is'
|
||||
|
||||
|
||||
action:
|
||||
header: "Actions"
|
||||
include: "Include Fields"
|
||||
|
@ -269,8 +269,7 @@ en:
|
|||
post: "Post"
|
||||
topic_attr: "Topic Attribute"
|
||||
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
|
||||
|
||||
run_after:
|
||||
run_after:
|
||||
label: "Run After"
|
||||
wizard_completion: "Wizard Completion"
|
||||
custom_fields:
|
||||
|
@ -282,81 +281,83 @@ en:
|
|||
suppress_notifications:
|
||||
label: "Suppress Notifications"
|
||||
description: "Suppress normal notifications triggered by post creation"
|
||||
send_message:
|
||||
label: "Send Message"
|
||||
recipient: "Recipient"
|
||||
create_topic:
|
||||
label: "Create Topic"
|
||||
category: "Category"
|
||||
tags: "Tags"
|
||||
visible: "Visible"
|
||||
open_composer:
|
||||
label: "Open Composer"
|
||||
update_profile:
|
||||
label: "Update Profile"
|
||||
setting: "Fields"
|
||||
key: "field"
|
||||
watch_categories:
|
||||
label: "Watch Categories"
|
||||
categories: "Categories"
|
||||
mute_remainder: "Mute Remainder"
|
||||
notification_level:
|
||||
label: "Notification Level"
|
||||
regular: "Normal"
|
||||
watching: "Watching"
|
||||
tracking: "Tracking"
|
||||
watching_first_post: "Watching First Post"
|
||||
muted: "Muted"
|
||||
select_a_notification_level: "Select level"
|
||||
wizard_user: "Wizard User"
|
||||
usernames: "Users"
|
||||
post_builder:
|
||||
checkbox: "Post Builder"
|
||||
label: "Builder"
|
||||
user_properties: "User Properties"
|
||||
wizard_fields: "Wizard Fields"
|
||||
wizard_actions: "Wizard Actions"
|
||||
placeholder: "Insert wizard fields using the field_id in w{}. Insert user properties using property in u{}."
|
||||
add_to_group:
|
||||
label: "Add to Group"
|
||||
route_to:
|
||||
label: "Route To"
|
||||
url: "Url"
|
||||
code: "Code"
|
||||
send_to_api:
|
||||
label: "Send to API"
|
||||
api: "API"
|
||||
endpoint: "Endpoint"
|
||||
select_an_api: "Select an API"
|
||||
select_an_endpoint: "Select an endpoint"
|
||||
body: "Body"
|
||||
body_placeholder: "JSON"
|
||||
create_category:
|
||||
label: "Create Category"
|
||||
name: Name
|
||||
slug: Slug
|
||||
color: Color
|
||||
text_color: Text color
|
||||
parent_category: Parent Category
|
||||
permissions: Permissions
|
||||
create_group:
|
||||
label: Create Group
|
||||
name: Name
|
||||
full_name: Full Name
|
||||
title: Title
|
||||
bio_raw: About
|
||||
owner_usernames: Owners
|
||||
usernames: Members
|
||||
grant_trust_level: Automatic Trust Level
|
||||
mentionable_level: Mentionable Level
|
||||
messageable_level: Messageable Level
|
||||
visibility_level: Visibility Level
|
||||
members_visibility_level: Members Visibility Level
|
||||
|
||||
|
||||
type:
|
||||
send_message:
|
||||
label: "Send Message"
|
||||
recipient: "Recipient"
|
||||
create_topic:
|
||||
label: "Create Topic"
|
||||
category: "Category"
|
||||
tags: "Tags"
|
||||
visible: "Visible"
|
||||
open_composer:
|
||||
label: "Open Composer"
|
||||
update_profile:
|
||||
label: "Update Profile"
|
||||
setting: "Fields"
|
||||
key: "field"
|
||||
watch_categories:
|
||||
label: "Watch Categories"
|
||||
categories: "Categories"
|
||||
mute_remainder: "Mute Remainder"
|
||||
notification_level:
|
||||
label: "Notification Level"
|
||||
regular: "Normal"
|
||||
watching: "Watching"
|
||||
tracking: "Tracking"
|
||||
watching_first_post: "Watching First Post"
|
||||
muted: "Muted"
|
||||
select_a_notification_level: "Select level"
|
||||
wizard_user: "Wizard User"
|
||||
usernames: "Users"
|
||||
post_builder:
|
||||
checkbox: "Post Builder"
|
||||
label: "Builder"
|
||||
user_properties: "User Properties"
|
||||
wizard_fields: "Wizard Fields"
|
||||
wizard_actions: "Wizard Actions"
|
||||
placeholder: "Insert wizard fields using the field_id in w{}. Insert user properties using property in u{}."
|
||||
add_to_group:
|
||||
label: "Add to Group"
|
||||
route_to:
|
||||
label: "Route To"
|
||||
url: "Url"
|
||||
code: "Code"
|
||||
send_to_api:
|
||||
label: "Send to API"
|
||||
api: "API"
|
||||
endpoint: "Endpoint"
|
||||
select_an_api: "Select an API"
|
||||
select_an_endpoint: "Select an endpoint"
|
||||
body: "Body"
|
||||
body_placeholder: "JSON"
|
||||
create_category:
|
||||
label: "Create Category"
|
||||
name: Name
|
||||
slug: Slug
|
||||
color: Color
|
||||
text_color: Text color
|
||||
parent_category: Parent Category
|
||||
permissions: Permissions
|
||||
create_group:
|
||||
label: Create Group
|
||||
name: Name
|
||||
full_name: Full Name
|
||||
title: Title
|
||||
bio_raw: About
|
||||
owner_usernames: Owners
|
||||
usernames: Members
|
||||
grant_trust_level: Automatic Trust Level
|
||||
mentionable_level: Mentionable Level
|
||||
messageable_level: Messageable Level
|
||||
visibility_level: Visibility Level
|
||||
members_visibility_level: Members Visibility Level
|
||||
|
||||
custom_field:
|
||||
nav_label: "Custom Fields"
|
||||
add: "Add"
|
||||
external:
|
||||
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:
|
||||
|
@ -385,7 +386,7 @@ en:
|
|||
basic_category: "Category"
|
||||
basic_group: "Group"
|
||||
post: "Post"
|
||||
|
||||
|
||||
submissions:
|
||||
nav_label: "Submissions"
|
||||
title: "{{name}} Submissions"
|
||||
|
@ -467,7 +468,7 @@ en:
|
|||
|
||||
subscription_container:
|
||||
title: Subscriber Features
|
||||
subscribed:
|
||||
subscribed:
|
||||
label: Subscribed
|
||||
title: You're subscribed and can use these features
|
||||
not_subscribed:
|
||||
|
@ -635,7 +636,7 @@ en:
|
|||
yourself_confirm:
|
||||
title: "Did you forget to add recipients?"
|
||||
body: "Right now this message is only being sent to yourself!"
|
||||
|
||||
|
||||
realtime_validations:
|
||||
similar_topics:
|
||||
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"
|
||||
|
|
|
@ -6,7 +6,7 @@ en:
|
|||
|
||||
wizard:
|
||||
custom_title: "Wizard"
|
||||
|
||||
|
||||
custom_field:
|
||||
error:
|
||||
required_attribute: "'%{attr}' is a required attribute"
|
||||
|
@ -18,7 +18,7 @@ en:
|
|||
name_already_taken: "'%{name}' is already taken as a custom field name"
|
||||
save_default: "Failed to save custom field '%{name}'"
|
||||
subscription_type: "%{type} custom fields require a subscription"
|
||||
|
||||
|
||||
field:
|
||||
too_short: "%{label} must be at least %{min} characters"
|
||||
too_long: "%{label} must not be more than %{max} characters"
|
||||
|
@ -27,30 +27,30 @@ en:
|
|||
invalid_file: "%{label} must be a %{types}"
|
||||
invalid_date: "Invalid date"
|
||||
invalid_time: "Invalid time"
|
||||
|
||||
|
||||
none: "We couldn't find a wizard at that address."
|
||||
no_skip: "Wizard can't be skipped"
|
||||
export:
|
||||
error:
|
||||
select_one: "Please select at least one valid wizard"
|
||||
invalid_wizards: "No valid wizards selected"
|
||||
|
||||
|
||||
import:
|
||||
error:
|
||||
no_file: "No file selected"
|
||||
file_large: "File too large"
|
||||
invalid_json: "File is not a valid json file"
|
||||
|
||||
|
||||
destroy:
|
||||
error:
|
||||
no_template: No template found
|
||||
default: Error destroying wizard
|
||||
|
||||
|
||||
validation:
|
||||
required: "%{property} is required"
|
||||
required: "%{attribute} is required"
|
||||
conflict: "Wizard with id '%{wizard_id}' already exists"
|
||||
after_time: "After time setting is invalid"
|
||||
subscription: "%{type} %{property} is subscription only"
|
||||
subscription: "%{class} %{attribute} is subscription only"
|
||||
|
||||
notice:
|
||||
connection_error: "Failed to connect to http://%{domain}"
|
||||
|
@ -62,7 +62,7 @@ en:
|
|||
title: Unable to connect to the Custom Wizard Plugin status server
|
||||
message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. If this issue persists contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
|
||||
subscription_message:
|
||||
connection_error:
|
||||
connection_error:
|
||||
title: Unable to connect to the Custom Wizard Plugin subscription server
|
||||
message: If this issue persists contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
|||
realtime_validations: CustomWizard::RealtimeValidation.types,
|
||||
custom_fields: custom_field_list,
|
||||
subscribed: CustomWizard::Subscription.subscribed?,
|
||||
subscription: CustomWizard::Subscription.type
|
||||
subscription: CustomWizard::Subscription.type,
|
||||
subscription_features: CustomWizard::Subscription::Subscription::FEATURES,
|
||||
subscription_types: CustomWizard::Subscription::Subscription.types
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ class ::CustomWizard::CustomField
|
|||
next
|
||||
end
|
||||
|
||||
if attr == 'klass' && @subscription.requires_additional_subscription("custom_fields", "klass").include?(value)
|
||||
if attr == 'klass' && !@subscription.can_use_feature?("custom_field", "klass", value)
|
||||
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
|
||||
end
|
||||
|
||||
|
@ -99,7 +99,7 @@ class ::CustomWizard::CustomField
|
|||
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'type' && @subscription.requires_additional_subscription("custom_fields", "type").include?(value)
|
||||
if attr == 'type' && !@subscription.can_use_feature?("custom_field", "type", value)
|
||||
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
|
||||
end
|
||||
|
||||
|
|
|
@ -6,23 +6,6 @@ class CustomWizard::Subscription
|
|||
attr_accessor :authentication,
|
||||
:subscription
|
||||
|
||||
SUBSCRIPTION_LEVELS = {
|
||||
standard: {
|
||||
actions: ["send_message", "add_to_group", "watch_categories"],
|
||||
custom_fields: {
|
||||
klass: [],
|
||||
type: ["json"],
|
||||
},
|
||||
},
|
||||
business: {
|
||||
actions: ["create_category", "create_group", "send_to_api"],
|
||||
custom_fields: {
|
||||
klass: ["group", "category"],
|
||||
type: [],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def initialize
|
||||
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
|
||||
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
|
||||
|
@ -36,6 +19,11 @@ class CustomWizard::Subscription
|
|||
@subscription.active?
|
||||
end
|
||||
|
||||
def can_use_feature?(klass, attribute, value)
|
||||
t = @subscription.determine_feature_subscription_type(klass, attribute, value)
|
||||
!t || @subscription.has_required_type?(t)
|
||||
end
|
||||
|
||||
def server
|
||||
"test.thepavilion.io"
|
||||
end
|
||||
|
@ -56,31 +44,6 @@ class CustomWizard::Subscription
|
|||
"discourse-subscription-server:user_subscription"
|
||||
end
|
||||
|
||||
def requires_additional_subscription(kategory, sub_kategory)
|
||||
case kategory
|
||||
when "actions"
|
||||
case self.type
|
||||
when "business"
|
||||
[]
|
||||
when "standard"
|
||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
||||
else
|
||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
||||
end
|
||||
when "custom_fields"
|
||||
case self.type
|
||||
when "business"
|
||||
[]
|
||||
when "standard"
|
||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
||||
else
|
||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @authentication.active?
|
||||
response = Excon.get(
|
||||
|
@ -169,10 +132,6 @@ class CustomWizard::Subscription
|
|||
self.new.type
|
||||
end
|
||||
|
||||
def self.requires_additional_subscription(kategory, sub_kategory)
|
||||
self.new.requires_additional_subscription(kategory, sub_kategory)
|
||||
end
|
||||
|
||||
def self.authorized?
|
||||
self.new.authorized?
|
||||
end
|
||||
|
|
|
@ -5,6 +5,54 @@ class CustomWizard::Subscription::Subscription
|
|||
attr_reader :type,
|
||||
:updated_at
|
||||
|
||||
STANDARD ||= "standard"
|
||||
BUSINESS ||= "business"
|
||||
FEATURES ||= {
|
||||
wizard: {
|
||||
permitted: STANDARD
|
||||
},
|
||||
step: {
|
||||
index: STANDARD,
|
||||
condition: STANDARD,
|
||||
required_data: BUSINESS,
|
||||
permitted_params: BUSINESS
|
||||
},
|
||||
field: {
|
||||
index: STANDARD,
|
||||
condition: STANDARD,
|
||||
prefill: STANDARD,
|
||||
content: STANDARD,
|
||||
validations: STANDARD,
|
||||
type: {
|
||||
tag: STANDARD,
|
||||
category: STANDARD,
|
||||
group: STANDARD,
|
||||
composer: STANDARD,
|
||||
composer_preview: STANDARD
|
||||
}
|
||||
},
|
||||
action: {
|
||||
type: {
|
||||
send_message: STANDARD,
|
||||
watch_categories: STANDARD,
|
||||
add_to_group: STANDARD,
|
||||
send_to_api: BUSINESS,
|
||||
create_category: BUSINESS,
|
||||
create_group: BUSINESS
|
||||
}
|
||||
},
|
||||
custom_field: {
|
||||
klass: {
|
||||
group: BUSINESS,
|
||||
category: BUSINESS
|
||||
},
|
||||
type: {
|
||||
json: STANDARD
|
||||
}
|
||||
},
|
||||
api: {}
|
||||
}
|
||||
|
||||
def initialize(subscription)
|
||||
if subscription
|
||||
@type = subscription.type
|
||||
|
@ -12,11 +60,34 @@ class CustomWizard::Subscription::Subscription
|
|||
end
|
||||
end
|
||||
|
||||
def types
|
||||
%w(none standard business)
|
||||
def active?
|
||||
self.class.types.include?(type) && updated_recently
|
||||
end
|
||||
|
||||
def active?
|
||||
types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
|
||||
def updated_recently
|
||||
updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
|
||||
end
|
||||
|
||||
def has_required_type?(t)
|
||||
t && type && type_index(type) >= type_index(t)
|
||||
end
|
||||
|
||||
def type_index(t)
|
||||
self.class.types.index(t)
|
||||
end
|
||||
|
||||
def determine_feature_subscription_type(klass, attribute, value)
|
||||
return BUSINESS if klass.to_sym === :api
|
||||
type = FEATURES.dig(*[klass.to_sym, attribute.to_sym])
|
||||
|
||||
if type.is_a?(Hash) && value.present?
|
||||
type = type[value.to_sym]
|
||||
else
|
||||
type
|
||||
end
|
||||
end
|
||||
|
||||
def self.types
|
||||
[STANDARD, BUSINESS]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,34 +13,26 @@ class CustomWizard::TemplateValidator
|
|||
data = @data
|
||||
|
||||
check_id(data, :wizard)
|
||||
check_required(data, :wizard)
|
||||
validate_after_time
|
||||
validate_subscription(data, :wizard)
|
||||
validate_class(data, :wizard)
|
||||
|
||||
data[:steps].each do |step|
|
||||
check_required(step, :step)
|
||||
validate_subscription(step, :step)
|
||||
validate_class(step, :step)
|
||||
|
||||
if step[:fields].present?
|
||||
step[:fields].each do |field|
|
||||
validate_subscription(field, :field)
|
||||
check_required(field, :field)
|
||||
validate_class(field, :field)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if data[:actions].present?
|
||||
data[:actions].each do |action|
|
||||
validate_subscription(action, :action)
|
||||
check_required(action, :action)
|
||||
validate_class(action, :action)
|
||||
end
|
||||
end
|
||||
|
||||
if errors.any?
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
!errors.any?
|
||||
end
|
||||
|
||||
def self.required
|
||||
|
@ -52,56 +44,25 @@ class CustomWizard::TemplateValidator
|
|||
}
|
||||
end
|
||||
|
||||
def self.subscription
|
||||
{
|
||||
wizard: {
|
||||
save_submissions: 'false',
|
||||
restart_on_revisit: 'true',
|
||||
},
|
||||
step: {
|
||||
condition: 'present',
|
||||
index: 'conditional',
|
||||
required_data: 'present',
|
||||
permitted_params: 'present'
|
||||
},
|
||||
field: {
|
||||
condition: 'present',
|
||||
index: 'conditional'
|
||||
},
|
||||
action: {
|
||||
type: %w[
|
||||
send_message
|
||||
add_to_group
|
||||
create_category
|
||||
create_group
|
||||
send_to_api
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_required(object, type)
|
||||
self.class.required[type].each do |property|
|
||||
if object[property].blank?
|
||||
errors.add :base, I18n.t("wizard.validation.required", property: property)
|
||||
def validate_class(object, klass)
|
||||
check_required(object, klass)
|
||||
validate_subscription(object, klass)
|
||||
end
|
||||
|
||||
def check_required(object, klass)
|
||||
self.class.required[klass].each do |attribute|
|
||||
if object[attribute].blank?
|
||||
errors.add :base, I18n.t("wizard.validation.required", attribute: attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_subscription(object, type)
|
||||
self.class.subscription[type].each do |property, subscription_type|
|
||||
val = object[property.to_s]
|
||||
is_subscription = (val != nil) && (
|
||||
subscription_type === 'present' && val.present? ||
|
||||
(['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) ||
|
||||
(subscription_type === 'conditional' && val.is_a?(Hash)) ||
|
||||
(subscription_type.is_a?(Array) && subscription_type.include?(val))
|
||||
)
|
||||
|
||||
if is_subscription && !@subscription.subscribed?
|
||||
errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property)
|
||||
def validate_subscription(object, klass)
|
||||
object.keys.each do |attribute|
|
||||
if !@subscription.can_use_feature?(klass, attribute, object[attribute])
|
||||
errors.add :base, I18n.t("wizard.validation.subscription", class: klass.to_s, attribute: attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
# name: discourse-custom-wizard
|
||||
# about: Create custom wizards for topic creation, onboarding, user surveys and much more.
|
||||
# version: 1.16.3
|
||||
# version: 1.16.4
|
||||
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George
|
||||
# contact_emails: support@thepavilion.io
|
||||
# url: https://github.com/paviliondev/discourse-custom-wizard
|
||||
|
|
|
@ -12,6 +12,7 @@ describe CustomWizard::Action do
|
|||
let(:create_group) { get_wizard_fixture("actions/create_group") }
|
||||
let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
|
||||
let(:send_message) { get_wizard_fixture("actions/send_message") }
|
||||
let(:watch_categories) { get_wizard_fixture("actions/watch_categories") }
|
||||
let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") }
|
||||
let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") }
|
||||
let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body") }
|
||||
|
@ -159,17 +160,6 @@ describe CustomWizard::Action do
|
|||
end
|
||||
end
|
||||
|
||||
it 'watches categories' do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
expect(CategoryUser.where(
|
||||
category_id: category.id,
|
||||
user_id: user.id
|
||||
).first.notification_level).to eq(0)
|
||||
end
|
||||
|
||||
it 're-routes a user' do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
updater = wizard.create_updater(wizard.steps.last.id, {})
|
||||
|
@ -177,7 +167,7 @@ describe CustomWizard::Action do
|
|||
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
|
||||
end
|
||||
|
||||
context "subscription actions" do
|
||||
context "standard subscription actions" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
end
|
||||
|
@ -235,6 +225,38 @@ describe CustomWizard::Action do
|
|||
expect(post.exists?).to eq(true)
|
||||
end
|
||||
|
||||
it '#add_to_group' do
|
||||
add_to_group["group"][0]["output"] = group.name
|
||||
wizard_template['actions'] << add_to_group
|
||||
update_template(wizard_template)
|
||||
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
step_id = wizard.steps[0].id
|
||||
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
|
||||
|
||||
expect(group.users.first.username).to eq('angus')
|
||||
end
|
||||
|
||||
it '#watch_categories' do
|
||||
wizard_template['actions'] << watch_categories
|
||||
update_template(wizard_template)
|
||||
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
expect(CategoryUser.where(
|
||||
category_id: category.id,
|
||||
user_id: user.id
|
||||
).first.notification_level).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "business subscription actions" do
|
||||
before do
|
||||
enable_subscription("business")
|
||||
end
|
||||
|
||||
it '#create_category' do
|
||||
wizard_template['actions'] << create_category
|
||||
update_template(wizard_template)
|
||||
|
@ -256,19 +278,6 @@ describe CustomWizard::Action do
|
|||
expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
|
||||
end
|
||||
|
||||
it '#add_to_group' do
|
||||
wizard_template['actions'] << create_group
|
||||
wizard_template['actions'] << add_to_group
|
||||
update_template(wizard_template)
|
||||
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
step_id = wizard.steps[0].id
|
||||
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
|
||||
group = Group.find_by(name: wizard.current_submission.fields['action_9'])
|
||||
|
||||
expect(group.users.first.username).to eq('angus')
|
||||
end
|
||||
|
||||
it '#send_to_api successful' do
|
||||
stub_request(:put, "https://myexternalapi.com/update").
|
||||
with(
|
||||
|
|
|
@ -20,6 +20,7 @@ describe CustomWizard::Builder do
|
|||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
let(:permitted_param_json) { get_wizard_fixture("step/permitted_params") }
|
||||
let(:user_condition_json) { get_wizard_fixture("condition/user_condition") }
|
||||
let(:prefill_json) { get_wizard_fixture("field/prefill") }
|
||||
|
||||
let(:boolean_field_condition_json) {
|
||||
JSON.parse(
|
||||
|
@ -35,6 +36,12 @@ describe CustomWizard::Builder do
|
|||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
def perform_update(step_id, submission)
|
||||
updater = @wizard.create_updater(step_id, submission)
|
||||
updater.update
|
||||
updater
|
||||
end
|
||||
|
||||
context 'disabled' do
|
||||
before do
|
||||
SiteSetting.custom_wizard_enabled = false
|
||||
|
@ -101,6 +108,7 @@ describe CustomWizard::Builder do
|
|||
|
||||
context "with restricted permissions" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
@template[:permitted] = permitted_json["permitted"]
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
@ -155,13 +163,21 @@ describe CustomWizard::Builder do
|
|||
end
|
||||
end
|
||||
|
||||
it 'returns prefilled data' do
|
||||
expect(
|
||||
CustomWizard::Builder.new(@template[:id], user).build
|
||||
.steps.first
|
||||
.fields.first
|
||||
.value
|
||||
).to eq('I am prefilled')
|
||||
context "prefilled data" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
@template[:steps][0][:fields][0][:prefill] = prefill_json["prefill"]
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
||||
it "works" do
|
||||
expect(
|
||||
CustomWizard::Builder.new(@template[:id], user).build
|
||||
.steps.first
|
||||
.fields.first
|
||||
.value
|
||||
).to eq('I am prefilled')
|
||||
end
|
||||
end
|
||||
|
||||
context "user has partially completed" do
|
||||
|
@ -190,12 +206,14 @@ describe CustomWizard::Builder do
|
|||
end
|
||||
|
||||
it 'does not return saved submissions' do
|
||||
@wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
perform_update('step_1', step_1_field_1: 'Text input')
|
||||
expect(
|
||||
CustomWizard::Builder.new(@template[:id], user).build
|
||||
.steps.first
|
||||
.fields.first
|
||||
.value
|
||||
).to eq('I am prefilled')
|
||||
).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -213,7 +231,7 @@ describe CustomWizard::Builder do
|
|||
|
||||
context 'with required data' do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
enable_subscription("business")
|
||||
@template[:steps][0][:required_data] = required_data_json['required_data']
|
||||
@template[:steps][0][:required_data_message] = required_data_json['required_data_message']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
|
@ -249,7 +267,7 @@ describe CustomWizard::Builder do
|
|||
|
||||
context "with permitted params" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
enable_subscription("business")
|
||||
@template[:steps][0][:permitted_params] = permitted_param_json['permitted_params']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
@ -298,14 +316,14 @@ describe CustomWizard::Builder do
|
|||
.build
|
||||
.steps.first
|
||||
.fields.length
|
||||
).to eq(4)
|
||||
).to eq(3)
|
||||
end
|
||||
|
||||
context "with condition" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
|
||||
@template[:steps][2][:fields][5][:condition] = boolean_field_condition_json['condition']
|
||||
@template[:steps][2][:fields][2][:condition] = boolean_field_condition_json['condition']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
||||
|
@ -326,18 +344,12 @@ describe CustomWizard::Builder do
|
|||
|
||||
builder = CustomWizard::Builder.new(@template[:id], user)
|
||||
wizard = builder.build
|
||||
expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][5]['id'])
|
||||
expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][2]['id'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on update' do
|
||||
def perform_update(step_id, submission)
|
||||
updater = @wizard.create_updater(step_id, submission)
|
||||
updater.update
|
||||
updater
|
||||
end
|
||||
|
||||
it 'saves submissions' do
|
||||
@wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
perform_update('step_1', step_1_field_1: 'Text input')
|
||||
|
|
|
@ -48,6 +48,8 @@ describe CustomWizard::Template do
|
|||
|
||||
context "wizard template list" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
|
||||
template_json_2 = template_json.dup
|
||||
template_json_2["id"] = 'super_mega_fun_wizard_2'
|
||||
template_json_2["permitted"] = permitted_json['permitted']
|
||||
|
|
|
@ -6,6 +6,7 @@ describe CustomWizard::TemplateValidator do
|
|||
let(:template) { get_wizard_fixture("wizard") }
|
||||
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
||||
let(:user_condition) { get_wizard_fixture("condition/user_condition") }
|
||||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
it "validates valid templates" do
|
||||
expect(
|
||||
|
@ -45,7 +46,7 @@ describe CustomWizard::TemplateValidator do
|
|||
|
||||
context "without subscription" do
|
||||
it "invalidates subscription wizard attributes" do
|
||||
template[:save_submissions] = false
|
||||
template[:permitted] = permitted_json["permitted"]
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(false)
|
||||
|
@ -73,13 +74,13 @@ describe CustomWizard::TemplateValidator do
|
|||
end
|
||||
end
|
||||
|
||||
context "with subscription" do
|
||||
context "with standard subscription" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
end
|
||||
|
||||
it "validates wizard attributes" do
|
||||
template[:save_submissions] = false
|
||||
template[:permitted] = permitted_json["permitted"]
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(true)
|
||||
|
@ -98,6 +99,12 @@ describe CustomWizard::TemplateValidator do
|
|||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "with business subscription" do
|
||||
before do
|
||||
enable_subscription("business")
|
||||
end
|
||||
|
||||
it "validates actions" do
|
||||
template[:actions] << create_category
|
||||
|
|
|
@ -22,7 +22,6 @@ describe CustomWizard::UpdateValidator do
|
|||
|
||||
@template[:steps][0][:fields][0][:min_length] = min_length
|
||||
@template[:steps][0][:fields][1][:min_length] = min_length
|
||||
@template[:steps][0][:fields][2][:min_length] = min_length
|
||||
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
|
@ -35,11 +34,6 @@ describe CustomWizard::UpdateValidator do
|
|||
expect(
|
||||
updater.errors.messages[:step_1_field_2].first
|
||||
).to eq(I18n.t('wizard.field.too_short', label: 'Textarea', min: min_length))
|
||||
|
||||
updater = perform_validation('step_1', step_1_field_3: 'Te')
|
||||
expect(
|
||||
updater.errors.messages[:step_1_field_3].first
|
||||
).to eq(I18n.t('wizard.field.too_short', label: 'Composer', min: min_length))
|
||||
end
|
||||
|
||||
it 'prevents submission if the length is over the max length' do
|
||||
|
@ -47,7 +41,6 @@ describe CustomWizard::UpdateValidator do
|
|||
|
||||
@template[:steps][0][:fields][0][:max_length] = max_length
|
||||
@template[:steps][0][:fields][1][:max_length] = max_length
|
||||
@template[:steps][0][:fields][2][:max_length] = max_length
|
||||
|
||||
CustomWizard::Template.save(@template)
|
||||
long_string = "Our Competitive Capability solution offers platforms a suite of wholesale offerings. In the future, will you be able to effectively revolutionize synergies in your business? In the emerging market space, industry is ethically investing its mission critical executive searches. Key players will take ownership of their capabilities by iteratively right-sizing world-class visibilities. "
|
||||
|
@ -60,11 +53,6 @@ describe CustomWizard::UpdateValidator do
|
|||
expect(
|
||||
updater.errors.messages[:step_1_field_2].first
|
||||
).to eq(I18n.t('wizard.field.too_long', label: 'Textarea', max: max_length))
|
||||
|
||||
updater = perform_validation('step_1', step_1_field_3: long_string)
|
||||
expect(
|
||||
updater.errors.messages[:step_1_field_3].first
|
||||
).to eq(I18n.t('wizard.field.too_long', label: 'Composer', max: max_length))
|
||||
end
|
||||
|
||||
it "allows submission if the length is under or equal to the max length" do
|
||||
|
@ -72,7 +60,6 @@ describe CustomWizard::UpdateValidator do
|
|||
|
||||
@template[:steps][0][:fields][0][:max_length] = max_length
|
||||
@template[:steps][0][:fields][1][:max_length] = max_length
|
||||
@template[:steps][0][:fields][2][:max_length] = max_length
|
||||
|
||||
CustomWizard::Template.save(@template)
|
||||
hundred_chars_string = "This is a line, exactly hundred characters long and not more even a single character more than that."
|
||||
|
@ -85,11 +72,6 @@ describe CustomWizard::UpdateValidator do
|
|||
expect(
|
||||
updater.errors.messages[:step_1_field_2].first
|
||||
).to eq(nil)
|
||||
|
||||
updater = perform_validation('step_1', step_1_field_3: hundred_chars_string)
|
||||
expect(
|
||||
updater.errors.messages[:step_1_field_3].first
|
||||
).to eq(nil)
|
||||
end
|
||||
|
||||
it "applies min length only if the input is non-empty" do
|
||||
|
|
|
@ -14,6 +14,7 @@ describe CustomWizard::Wizard do
|
|||
@permitted_template = template_json.dup
|
||||
@permitted_template["permitted"] = permitted_json["permitted"]
|
||||
@wizard = CustomWizard::Wizard.new(template_json, user)
|
||||
enable_subscription("standard")
|
||||
end
|
||||
|
||||
def append_steps
|
||||
|
|
6
spec/fixtures/actions/add_to_group.json
gevendort
6
spec/fixtures/actions/add_to_group.json
gevendort
|
@ -5,9 +5,9 @@
|
|||
"group": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_9",
|
||||
"output_type": "wizard_action",
|
||||
"output": "",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
23
spec/fixtures/actions/watch_categories.json
gevendort
Normale Datei
23
spec/fixtures/actions/watch_categories.json
gevendort
Normale Datei
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "action_5",
|
||||
"run_after": "step_1",
|
||||
"type": "watch_categories",
|
||||
"notification_level": "tracking",
|
||||
"wizard_user": true,
|
||||
"categories": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_8",
|
||||
"output_type": "wizard_action",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"mute_remainder": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "true",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
}
|
33
spec/fixtures/field/content.json
gevendort
Normale Datei
33
spec/fixtures/field/content.json
gevendort
Normale Datei
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"content": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "choice1",
|
||||
"key_type": "text",
|
||||
"value": "Choice 1",
|
||||
"value_type": "text",
|
||||
"connector": "equal"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"key": "choice2",
|
||||
"key_type": "text",
|
||||
"value": "Choice 2",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"key": "choice3",
|
||||
"key_type": "text",
|
||||
"value": "Choice 3",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
10
spec/fixtures/field/prefill.json
gevendort
Normale Datei
10
spec/fixtures/field/prefill.json
gevendort
Normale Datei
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"prefill": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "I am prefilled",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
}
|
90
spec/fixtures/wizard.json
gevendort
90
spec/fixtures/wizard.json
gevendort
|
@ -17,15 +17,7 @@
|
|||
"label": "Text",
|
||||
"description": "Text field description.",
|
||||
"type": "text",
|
||||
"min_length": "3",
|
||||
"prefill": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "I am prefilled",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
"min_length": "3"
|
||||
},
|
||||
{
|
||||
"id": "step_1_field_2",
|
||||
|
@ -33,11 +25,6 @@
|
|||
"type": "textarea",
|
||||
"min_length": ""
|
||||
},
|
||||
{
|
||||
"id": "step_1_field_3",
|
||||
"label": "Composer",
|
||||
"type": "composer"
|
||||
},
|
||||
{
|
||||
"id": "step_1_field_4",
|
||||
"label": "I'm only text",
|
||||
|
@ -102,55 +89,7 @@
|
|||
{
|
||||
"id": "step_3_field_1",
|
||||
"label": "Custom Dropdown",
|
||||
"type": "dropdown",
|
||||
"content": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "choice1",
|
||||
"key_type": "text",
|
||||
"value": "Choice 1",
|
||||
"value_type": "text",
|
||||
"connector": "equal"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"key": "choice2",
|
||||
"key_type": "text",
|
||||
"value": "Choice 2",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"key": "choice3",
|
||||
"key_type": "text",
|
||||
"value": "Choice 3",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "step_3_field_2",
|
||||
"label": "Tag",
|
||||
"type": "tag"
|
||||
},
|
||||
{
|
||||
"id": "step_3_field_3",
|
||||
"label": "Category",
|
||||
"type": "category",
|
||||
"limit": 1,
|
||||
"property": "id"
|
||||
},
|
||||
{
|
||||
"id": "step_3_field_4",
|
||||
"label": "Group",
|
||||
"type": "group"
|
||||
"type": "dropdown"
|
||||
},
|
||||
{
|
||||
"id": "step_3_field_5",
|
||||
|
@ -257,29 +196,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_5",
|
||||
"run_after": "step_1",
|
||||
"type": "watch_categories",
|
||||
"notification_level": "tracking",
|
||||
"wizard_user": true,
|
||||
"categories": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_8",
|
||||
"output_type": "wizard_action",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"mute_remainder": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "true",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_4",
|
||||
"run_after": "step_2",
|
||||
|
@ -337,4 +253,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ def enable_subscription(type)
|
|||
# CustomWizard::Subscription.new
|
||||
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true)
|
||||
CustomWizard::Subscription.any_instance.stubs(:type).returns(type)
|
||||
CustomWizard::Subscription::Subscription.any_instance.stubs(:type).returns(type)
|
||||
end
|
||||
|
||||
def disable_subscription
|
||||
|
|
|
@ -8,6 +8,7 @@ describe CustomWizard::AdminWizardController do
|
|||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
|
||||
template_2 = template.dup
|
||||
|
|
|
@ -35,6 +35,8 @@ describe CustomWizard::StepsController do
|
|||
end
|
||||
|
||||
it "when the user cant access the wizard" do
|
||||
enable_subscription("standard")
|
||||
|
||||
new_template = wizard_template.dup
|
||||
new_template["permitted"] = permitted_json["permitted"]
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
|
|
@ -18,10 +18,10 @@ describe CustomWizard::FieldSerializer do
|
|||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
expect(json_array.size).to eq(4)
|
||||
expect(json_array.size).to eq(3)
|
||||
expect(json_array[0][:label]).to eq("<p>Text</p>")
|
||||
expect(json_array[0][:description]).to eq("Text field description.")
|
||||
expect(json_array[3][:index]).to eq(3)
|
||||
expect(json_array[2][:index]).to eq(2)
|
||||
end
|
||||
|
||||
it "should return optional field attributes" do
|
||||
|
|
|
@ -42,55 +42,73 @@ describe CustomWizard::WizardSerializer do
|
|||
).to eq(BasicUserSerializer.new(user, root: false).as_json)
|
||||
end
|
||||
|
||||
it "should not return categories if there are no category fields" do
|
||||
@template[:steps][2][:fields].delete_at(2)
|
||||
CustomWizard::Template.save(@template)
|
||||
context "with subscription" do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
end
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json[:wizard][:categories].present?).to eq(false)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
|
||||
end
|
||||
it "should not return categories if there are no category fields" do
|
||||
@template[:steps][2][:fields].delete_at(2)
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
it "should return categories if there is a category selector field" do
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json[:wizard][:categories].present?).to eq(true)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
|
||||
end
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
it "should return categories if there is a similar topics validation scoped to category(s)" do
|
||||
@template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations]
|
||||
CustomWizard::Template.save(@template)
|
||||
expect(json[:wizard][:categories].present?).to eq(false)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
|
||||
end
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json[:wizard][:categories].present?).to eq(true)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
|
||||
end
|
||||
it "should return categories if there is a category selector field" do
|
||||
@template[:steps][0][:fields] << { "id": "step_1_field_5", "label": "Category", "type": "category" }.as_json
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
it 'should return groups if there is a group selector field' do
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json[:wizard][:groups].length).to eq(8)
|
||||
end
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
it 'should not return groups if there is not a group selector field' do
|
||||
@template[:steps][2][:fields].delete_at(3)
|
||||
CustomWizard::Template.save(@template)
|
||||
expect(json[:wizard][:categories].present?).to eq(true)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
|
||||
end
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json[:wizard][:groups].present?).to eq(false)
|
||||
it "should return categories if there is a similar topics validation scoped to category(s)" do
|
||||
@template[:steps][0][:fields][0][:validations] = similar_topics_validation
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
expect(json[:wizard][:categories].present?).to eq(true)
|
||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
|
||||
end
|
||||
|
||||
it 'should return groups if there is a group selector field' do
|
||||
@template[:steps][0][:fields] << { "id": "step_1_field_5", "label": "Group", "type": "group" }.as_json
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
expect(json[:wizard][:groups].present?).to eq(true)
|
||||
expect(json[:wizard][:groups].length).to eq(8)
|
||||
end
|
||||
|
||||
it 'should not return groups if there is not a group selector field' do
|
||||
@template[:steps][2][:fields].delete_at(3)
|
||||
CustomWizard::Template.save(@template)
|
||||
|
||||
json = CustomWizard::WizardSerializer.new(
|
||||
CustomWizard::Builder.new(@template[:id], user).build,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
|
||||
expect(json[:wizard][:groups].present?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,12 +29,12 @@ describe CustomWizard::StepSerializer do
|
|||
each_serializer: described_class,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array[0][:fields].length).to eq(4)
|
||||
expect(json_array[0][:fields].length).to eq(3)
|
||||
end
|
||||
|
||||
context 'with required data' do
|
||||
before do
|
||||
enable_subscription("standard")
|
||||
enable_subscription("business")
|
||||
wizard_template['steps'][0]['required_data'] = required_data_json['required_data']
|
||||
wizard_template['steps'][0]['required_data_message'] = required_data_json['required_data_message']
|
||||
CustomWizard::Template.save(wizard_template)
|
||||
|
|
Laden …
In neuem Issue referenzieren