Commit
13304f5b6d
104 geänderte Dateien mit 2464 neuen und 1379 gelöschten Zeilen
|
@ -1,13 +1,29 @@
|
|||
import Component from "@ember/component";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { alias, equal, or } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
|
||||
const generateContent = function (array, type) {
|
||||
return array.map((key) => ({
|
||||
id: key,
|
||||
name: I18n.t(`admin.wizard.custom_field.${type}.${key}`),
|
||||
}));
|
||||
const klasses = ["topic", "post", "group", "category"];
|
||||
const types = ["string", "boolean", "integer", "json"];
|
||||
const proTypes = {
|
||||
klass: ["group", "category"],
|
||||
type: ["json"],
|
||||
};
|
||||
|
||||
const generateContent = function (array, type, proSubscribed = false) {
|
||||
return array.reduce((result, key) => {
|
||||
let proArr = proTypes[type];
|
||||
let pro = proArr && proArr.includes(key);
|
||||
if (!pro || proSubscribed) {
|
||||
result.push({
|
||||
id: key,
|
||||
name: I18n.t(`admin.wizard.custom_field.${type}.${key}`),
|
||||
pro,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default Component.extend({
|
||||
|
@ -16,14 +32,12 @@ export default Component.extend({
|
|||
postSerializers: ["post"],
|
||||
groupSerializers: ["basic_group"],
|
||||
categorySerializers: ["basic_category"],
|
||||
klassContent: generateContent(
|
||||
["topic", "post", "group", "category"],
|
||||
"klass"
|
||||
),
|
||||
typeContent: generateContent(
|
||||
["string", "boolean", "integer", "json"],
|
||||
"type"
|
||||
),
|
||||
klassContent: computed("proSubscribed", function () {
|
||||
return generateContent(klasses, "klass", this.proSubscribed);
|
||||
}),
|
||||
typeContent: computed("proSubscribed", function () {
|
||||
return generateContent(types, "type", this.proSubscribed);
|
||||
}),
|
||||
showInputs: or("field.new", "field.edit"),
|
||||
classNames: ["custom-field-input"],
|
||||
loading: or("saving", "destroying"),
|
||||
|
@ -40,7 +54,7 @@ export default Component.extend({
|
|||
const serializers = this.get(`${klass}Serializers`);
|
||||
|
||||
if (serializers) {
|
||||
return generateContent(serializers, "serializers");
|
||||
return generateContent(serializers, "serializers", this.proSubscribed);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: "wizard-advanced-toggle",
|
||||
|
||||
@discourseComputed("showAdvanced")
|
||||
toggleClass(showAdvanced) {
|
||||
let classes = "btn";
|
||||
if (showAdvanced) {
|
||||
classes += " btn-primary";
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleAdvanced() {
|
||||
this.toggleProperty("showAdvanced");
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { and, empty, equal, or } from "@ember/object/computed";
|
||||
import wizardSchema 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 { computed } from "@ember/object";
|
||||
import wizardSchema from "../lib/wizard-schema";
|
||||
import UndoChanges from "../mixins/undo-changes";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
|
@ -25,8 +25,6 @@ export default Component.extend(UndoChanges, {
|
|||
createGroup: equal("action.type", "create_group"),
|
||||
apiEmpty: empty("action.api"),
|
||||
groupPropertyTypes: selectKitContent(["id", "name"]),
|
||||
hasAdvanced: or("hasCustomFields", "routeTo"),
|
||||
showAdvanced: and("hasAdvanced", "action.type"),
|
||||
hasCustomFields: or(
|
||||
"basicTopicFields",
|
||||
"updateProfile",
|
||||
|
@ -36,12 +34,6 @@ export default Component.extend(UndoChanges, {
|
|||
basicTopicFields: or("createTopic", "sendMessage", "openComposer"),
|
||||
publicTopicFields: or("createTopic", "openComposer"),
|
||||
showPostAdvanced: or("createTopic", "sendMessage"),
|
||||
actionTypes: Object.keys(wizardSchema.action.types).map((type) => {
|
||||
return {
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.action.${type}.label`),
|
||||
};
|
||||
}),
|
||||
availableNotificationLevels: notificationLevels.map((type) => {
|
||||
return {
|
||||
id: type,
|
||||
|
@ -101,4 +93,19 @@ export default Component.extend(UndoChanges, {
|
|||
}
|
||||
return apis.find((a) => a.name === api).endpoints;
|
||||
},
|
||||
|
||||
@discourseComputed("proSubscribed")
|
||||
actionTypes(proSubscribed) {
|
||||
return Object.keys(wizardSchema.action.types).reduce((result, type) => {
|
||||
let pro = wizardSchema.action.proTypes.includes(type);
|
||||
if (proSubscribed || !pro) {
|
||||
result.push({
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.action.${type}.label`),
|
||||
pro,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { alias, equal, or } from "@ember/object/computed";
|
||||
import { equal, or } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import { selectKitContent } from "../lib/wizard";
|
||||
import UndoChanges from "../mixins/undo-changes";
|
||||
|
@ -27,7 +27,6 @@ export default Component.extend(UndoChanges, {
|
|||
isTextType: or("isText", "isTextarea", "isComposer"),
|
||||
isComposerPreview: equal("field.type", "composer_preview"),
|
||||
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
||||
showAdvanced: alias("field.type"),
|
||||
messageUrl: "https://thepavilion.io/t/2809",
|
||||
|
||||
@discourseComputed("field.type")
|
||||
|
|
|
@ -6,6 +6,7 @@ import I18n from "I18n";
|
|||
const icons = {
|
||||
error: "times-circle",
|
||||
success: "check-circle",
|
||||
warn: "exclamation-circle",
|
||||
info: "info-circle",
|
||||
};
|
||||
|
||||
|
|
18
assets/javascripts/discourse/components/wizard-pro-selector.js.es6
Normale Datei
18
assets/javascripts/discourse/components/wizard-pro-selector.js.es6
Normale Datei
|
@ -0,0 +1,18 @@
|
|||
import SingleSelectComponent from "select-kit/components/single-select";
|
||||
|
||||
export default SingleSelectComponent.extend({
|
||||
classNames: ["combo-box", "wizard-pro-selector"],
|
||||
|
||||
selectKitOptions: {
|
||||
autoFilterable: false,
|
||||
filterable: false,
|
||||
showFullTitle: true,
|
||||
headerComponent: "wizard-pro-selector/wizard-pro-selector-header",
|
||||
caretUpIcon: "caret-up",
|
||||
caretDownIcon: "caret-down",
|
||||
},
|
||||
|
||||
modifyComponentForRow() {
|
||||
return "wizard-pro-selector/wizard-pro-selector-row";
|
||||
},
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import SingleSelectHeaderComponent from "select-kit/components/select-kit/single-select-header";
|
||||
import { computed } from "@ember/object";
|
||||
import { reads } from "@ember/object/computed";
|
||||
|
||||
export default SingleSelectHeaderComponent.extend({
|
||||
classNames: ["combo-box-header", "wizard-pro-selector-header"],
|
||||
caretUpIcon: reads("selectKit.options.caretUpIcon"),
|
||||
caretDownIcon: reads("selectKit.options.caretDownIcon"),
|
||||
caretIcon: computed(
|
||||
"selectKit.isExpanded",
|
||||
"caretUpIcon",
|
||||
"caretDownIcon",
|
||||
function () {
|
||||
return this.selectKit.isExpanded ? this.caretUpIcon : this.caretDownIcon;
|
||||
}
|
||||
),
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend();
|
|
@ -0,0 +1,53 @@
|
|||
import Component from "@ember/component";
|
||||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [
|
||||
":custom-wizard-pro-subscription",
|
||||
"subscription.active:active:inactive",
|
||||
],
|
||||
subscribed: notEmpty("subscription"),
|
||||
|
||||
@discourseComputed("subscription.type")
|
||||
title(type) {
|
||||
return type
|
||||
? I18n.t(`admin.wizard.pro.subscription.title.${type}`)
|
||||
: I18n.t("admin.wizard.pro.not_subscribed");
|
||||
},
|
||||
|
||||
@discourseComputed("subscription.active")
|
||||
stateClass(active) {
|
||||
return active ? "active" : "inactive";
|
||||
},
|
||||
|
||||
@discourseComputed("stateClass")
|
||||
stateLabel(stateClass) {
|
||||
return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
update() {
|
||||
this.set("updating", true);
|
||||
CustomWizardPro.update_subscription()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
this.setProperties({
|
||||
updateIcon: "check",
|
||||
subscription: result.subscription,
|
||||
});
|
||||
} else {
|
||||
this.set("updateIcon", "times");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("updating", false);
|
||||
setTimeout(() => {
|
||||
this.set("updateIcon", null);
|
||||
}, 7000);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -6,7 +6,8 @@ import discourseComputed from "discourse-common/utils/decorators";
|
|||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["realtime-validations"],
|
||||
classNames: ["realtime-validations", "setting", "full", "pro"],
|
||||
|
||||
@discourseComputed
|
||||
timeUnits() {
|
||||
return ["days", "weeks", "months", "years"].map((unit) => {
|
||||
|
|
62
assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6
Normale Datei
62
assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6
Normale Datei
|
@ -0,0 +1,62 @@
|
|||
import Controller from "@ember/controller";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import { alias } from "@ember/object/computed";
|
||||
|
||||
export default Controller.extend({
|
||||
messageUrl: "https://thepavilion.io/t/3652",
|
||||
messageType: "info",
|
||||
messageKey: null,
|
||||
showSubscription: alias("model.authentication.active"),
|
||||
|
||||
setup() {
|
||||
const authentication = this.get("model.authentication");
|
||||
const subscription = this.get("model.subscription");
|
||||
const subscribed = subscription && subscription.active;
|
||||
const authenticated = authentication && authentication.active;
|
||||
|
||||
if (!subscribed) {
|
||||
this.set("messageKey", authenticated ? "not_subscribed" : "authorize");
|
||||
} else {
|
||||
this.set(
|
||||
"messageKey",
|
||||
!authenticated
|
||||
? "subscription_expiring"
|
||||
: subscribed
|
||||
? "subscription_active"
|
||||
: "subscription_inactive"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("model.server")
|
||||
messageOpts(server) {
|
||||
return { server };
|
||||
},
|
||||
|
||||
actions: {
|
||||
unauthorize() {
|
||||
this.set("unauthorizing", true);
|
||||
|
||||
CustomWizardPro.unauthorize()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
this.setProperties({
|
||||
messageKey: "unauthorized",
|
||||
messageType: "warn",
|
||||
"model.authentication": null,
|
||||
"model.subscription": null,
|
||||
});
|
||||
} else {
|
||||
this.setProperties({
|
||||
messageKey: "unauthorize_failed",
|
||||
messageType: "error",
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("unauthorizing", false);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -77,7 +77,11 @@ export default Controller.extend({
|
|||
wizard
|
||||
.save(opts)
|
||||
.then((result) => {
|
||||
this.send("afterSave", result.wizard_id);
|
||||
if (result.wizard_id) {
|
||||
this.send("afterSave", result.wizard_id);
|
||||
} else if (result.errors) {
|
||||
this.set("error", result.errors.join(", "));
|
||||
}
|
||||
})
|
||||
.catch((result) => {
|
||||
let errorType = "failed";
|
||||
|
@ -114,10 +118,6 @@ export default Controller.extend({
|
|||
controller.setup();
|
||||
},
|
||||
|
||||
toggleAdvanced() {
|
||||
this.toggleProperty("wizard.showAdvanced");
|
||||
},
|
||||
|
||||
copyUrl() {
|
||||
const $copyRange = $('<p id="copy-range"></p>');
|
||||
$copyRange.html(this.wizardUrl);
|
||||
|
|
|
@ -43,12 +43,20 @@ export default {
|
|||
}
|
||||
);
|
||||
|
||||
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
|
||||
this.route("adminWizardsLogs", {
|
||||
path: "/logs",
|
||||
resetNamespace: true,
|
||||
});
|
||||
|
||||
this.route("adminWizardsManager", {
|
||||
path: "/manager",
|
||||
resetNamespace: true,
|
||||
});
|
||||
|
||||
this.route("adminWizardsPro", {
|
||||
path: "/pro",
|
||||
resetNamespace: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
|
|
@ -97,11 +97,6 @@ function buildObjectArray(json, type) {
|
|||
if (present(json)) {
|
||||
json.forEach((objJson, objectIndex) => {
|
||||
let object = buildObject(objJson, type, objectIndex);
|
||||
|
||||
if (hasAdvancedProperties(object, type)) {
|
||||
object.set("showAdvanced", true);
|
||||
}
|
||||
|
||||
array.pushObject(object);
|
||||
});
|
||||
}
|
||||
|
@ -112,21 +107,11 @@ function buildObjectArray(json, type) {
|
|||
function buildBasicProperties(json, type, props, objectIndex = null) {
|
||||
listProperties(type).forEach((p) => {
|
||||
props[p] = buildProperty(json, p, type, objectIndex);
|
||||
|
||||
if (hasAdvancedProperties(json, type)) {
|
||||
props.showAdvanced = true;
|
||||
}
|
||||
});
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
function hasAdvancedProperties(object, type) {
|
||||
return Object.keys(object).some((p) => {
|
||||
return wizardSchema[type].advanced.indexOf(p) > -1 && present(object[p]);
|
||||
});
|
||||
}
|
||||
|
||||
/// to be removed: necessary due to action array being moved from step to wizard
|
||||
function actionPatch(json) {
|
||||
let actions = json.actions || [];
|
||||
|
|
|
@ -18,7 +18,6 @@ const wizard = {
|
|||
permitted: null,
|
||||
},
|
||||
mapped: ["permitted"],
|
||||
advanced: ["restart_on_revisit"],
|
||||
required: ["id"],
|
||||
dependent: {
|
||||
after_time: "after_time_scheduled",
|
||||
|
@ -50,7 +49,6 @@ const step = {
|
|||
force_final: false,
|
||||
},
|
||||
mapped: ["required_data", "permitted_params", "condition", "index"],
|
||||
advanced: ["required_data", "permitted_params", "condition", "index"],
|
||||
required: ["id"],
|
||||
dependent: {},
|
||||
objectArrays: {
|
||||
|
@ -68,6 +66,7 @@ const field = {
|
|||
label: null,
|
||||
image: null,
|
||||
description: null,
|
||||
property: null,
|
||||
required: null,
|
||||
key: null,
|
||||
type: null,
|
||||
|
@ -75,7 +74,6 @@ const field = {
|
|||
},
|
||||
types: {},
|
||||
mapped: ["prefill", "content", "condition", "index"],
|
||||
advanced: ["property", "key", "condition", "index"],
|
||||
required: ["id", "type"],
|
||||
dependent: {},
|
||||
objectArrays: {},
|
||||
|
@ -196,14 +194,14 @@ const action = {
|
|||
"visibility_level",
|
||||
"members_visibility_level",
|
||||
],
|
||||
advanced: [
|
||||
"code",
|
||||
"custom_fields",
|
||||
"skip_redirect",
|
||||
"suppress_notifications",
|
||||
"required",
|
||||
],
|
||||
required: ["id", "type"],
|
||||
proTypes: [
|
||||
"send_message",
|
||||
"add_to_group",
|
||||
"create_category",
|
||||
"create_group",
|
||||
"send_to_api",
|
||||
],
|
||||
dependent: {},
|
||||
objectArrays: {},
|
||||
};
|
||||
|
|
33
assets/javascripts/discourse/models/custom-wizard-pro.js.es6
Normale Datei
33
assets/javascripts/discourse/models/custom-wizard-pro.js.es6
Normale Datei
|
@ -0,0 +1,33 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
const CustomWizardPro = EmberObject.extend();
|
||||
|
||||
const basePath = "/admin/wizards/pro";
|
||||
|
||||
CustomWizardPro.reopenClass({
|
||||
status() {
|
||||
return ajax(basePath, {
|
||||
type: "GET",
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
authorize() {
|
||||
window.location.href = `${basePath}/authorize`;
|
||||
},
|
||||
|
||||
unauthorize() {
|
||||
return ajax(`${basePath}/authorize`, {
|
||||
type: "DELETE",
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
update_subscription() {
|
||||
return ajax(`${basePath}/subscription`, {
|
||||
type: "POST",
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
|
||||
export default CustomWizardPro;
|
|
@ -8,7 +8,12 @@ export default DiscourseRoute.extend({
|
|||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
const customFields = A(model || []);
|
||||
controller.set("customFields", customFields);
|
||||
const customFields = A(model.custom_fields || []);
|
||||
const proSubscribed = model.pro_subscribed;
|
||||
|
||||
controller.setProperties({
|
||||
customFields,
|
||||
proSubscribed,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
19
assets/javascripts/discourse/routes/admin-wizards-pro.js.es6
Normale Datei
19
assets/javascripts/discourse/routes/admin-wizards-pro.js.es6
Normale Datei
|
@ -0,0 +1,19 @@
|
|||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return CustomWizardPro.status();
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set("model", model);
|
||||
controller.setup();
|
||||
},
|
||||
|
||||
actions: {
|
||||
authorize() {
|
||||
CustomWizardPro.authorize();
|
||||
},
|
||||
},
|
||||
});
|
|
@ -39,6 +39,7 @@ export default DiscourseRoute.extend({
|
|||
currentStep: wizard.steps[0],
|
||||
currentAction: wizard.actions[0],
|
||||
creating: model.create,
|
||||
proSubscribed: parentModel.pro_subscribed,
|
||||
};
|
||||
|
||||
controller.setProperties(props);
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
{{custom-field-input
|
||||
field=field
|
||||
removeField=(action "removeField")
|
||||
saveField=(action "saveField")}}
|
||||
saveField=(action "saveField")
|
||||
proSubscribed=proSubscribed}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
34
assets/javascripts/discourse/templates/admin-wizards-pro.hbs
Normale Datei
34
assets/javascripts/discourse/templates/admin-wizards-pro.hbs
Normale Datei
|
@ -0,0 +1,34 @@
|
|||
<div class="admin-wizard-controls">
|
||||
<h3>{{i18n "admin.wizard.pro.title"}}</h3>
|
||||
|
||||
<div class="buttons">
|
||||
{{#if model.authentication.active}}
|
||||
{{conditional-loading-spinner size="small" condition=unauthorizing}}
|
||||
<a {{action "unauthorize"}} title={{i18n "admin.wizard.pro.unauthorize"}} role="button">
|
||||
{{i18n "admin.wizard.pro.unauthorize"}}
|
||||
</a>
|
||||
<label title={{i18n "admin.wizard.pro.authorized"}}>
|
||||
{{i18n "admin.wizard.pro.authorized"}}
|
||||
</label>
|
||||
{{else}}
|
||||
{{d-button
|
||||
icon="id-card"
|
||||
label="admin.wizard.pro.authorize"
|
||||
title="admin.wizard.pro.authorize"
|
||||
action=(route-action "authorize")}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{wizard-message
|
||||
key=messageKey
|
||||
url=messageUrl
|
||||
type=messageType
|
||||
opts=messageOpts
|
||||
component="pro"}}
|
||||
|
||||
<div class="admin-wizard-container">
|
||||
{{#if showSubscription}}
|
||||
{{wizard-pro-subscription subscription=model.subscription}}
|
||||
{{/if}}
|
||||
</div>
|
|
@ -126,33 +126,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{wizard-advanced-toggle showAdvanced=wizard.showAdvanced}}
|
||||
|
||||
{{#if wizard.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
<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.save_submissions"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<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>
|
||||
|
||||
{{wizard-links
|
||||
|
@ -166,7 +158,8 @@
|
|||
wizard=wizard
|
||||
currentField=currentField
|
||||
wizardFields=wizardFields
|
||||
fieldTypes=fieldTypes}}
|
||||
fieldTypes=fieldTypes
|
||||
proSubscribed=proSubscribed}}
|
||||
{{/if}}
|
||||
|
||||
{{wizard-links
|
||||
|
@ -182,7 +175,8 @@
|
|||
wizard=wizard
|
||||
apis=apis
|
||||
removeAction="removeAction"
|
||||
wizardFields=wizardFields}}
|
||||
wizardFields=wizardFields
|
||||
proSubscribed=proSubscribed}}
|
||||
{{/each}}
|
||||
|
||||
<div class="admin-wizard-buttons">
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
{{/if}}
|
||||
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
|
||||
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
|
||||
{{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}}
|
||||
|
||||
<div class="admin-actions">
|
||||
<a target="_blank" class="btn btn-pavilion-pro" rel="noreferrer noopener" href="https://thepavilion.io/w/support" title={{i18n "admin.wizard.pro_support_button.title"}}>{{d-icon "far-life-ring"}}{{i18n "admin.wizard.pro_support_button.label"}}</a>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{{#if showInputs}}
|
||||
<td>
|
||||
{{combo-box
|
||||
{{wizard-pro-selector
|
||||
value=field.klass
|
||||
content=klassContent
|
||||
none="admin.wizard.custom_field.klass.select"
|
||||
onChange=(action (mut field.klass))}}
|
||||
</td>
|
||||
<td>
|
||||
{{combo-box
|
||||
{{wizard-pro-selector
|
||||
value=field.type
|
||||
content=typeContent
|
||||
none="admin.wizard.custom_field.type.select"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{{d-button
|
||||
action="toggleAdvanced"
|
||||
label="admin.wizard.advanced"
|
||||
class=toggleClass}}
|
|
@ -12,13 +12,14 @@
|
|||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
{{wizard-pro-selector
|
||||
value=action.type
|
||||
content=actionTypes
|
||||
onChange=(action "changeType")
|
||||
options=(hash
|
||||
none="admin.wizard.select_type"
|
||||
)}}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -714,99 +715,90 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showAdvanced}}
|
||||
{{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
|
||||
|
||||
{{#if action.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
{{#if hasCustomFields}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.custom_fields.label"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=action.custom_fields
|
||||
property="custom_fields"
|
||||
onUpdate=(action "mappedFieldUpdated")
|
||||
options=(hash
|
||||
inputTypes="association"
|
||||
customFieldSelection="key"
|
||||
wizardFieldSelection="value"
|
||||
wizardActionSelection="value"
|
||||
userFieldSelection="value"
|
||||
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
||||
context=customFieldsContext
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if sendMessage}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.required"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=action.required
|
||||
property="required"
|
||||
onUpdate=(action "mappedFieldUpdated")
|
||||
options=(hash
|
||||
textSelection="value"
|
||||
wizardFieldSelection=true
|
||||
userFieldSelection=true
|
||||
groupSelection=true
|
||||
context="action"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showPostAdvanced}}
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.skip_redirect.label"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=action.skip_redirect}}
|
||||
|
||||
<span>
|
||||
{{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.suppress_notifications.label"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=action.suppress_notifications}}
|
||||
|
||||
<span>
|
||||
{{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if routeTo}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.route_to.code"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input value=action.code}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if hasCustomFields}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.custom_fields.label"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=action.custom_fields
|
||||
property="custom_fields"
|
||||
onUpdate=(action "mappedFieldUpdated")
|
||||
options=(hash
|
||||
inputTypes="association"
|
||||
customFieldSelection="key"
|
||||
wizardFieldSelection="value"
|
||||
wizardActionSelection="value"
|
||||
userFieldSelection="value"
|
||||
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
||||
context=customFieldsContext
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if sendMessage}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.required"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=action.required
|
||||
property="required"
|
||||
onUpdate=(action "mappedFieldUpdated")
|
||||
options=(hash
|
||||
textSelection="value"
|
||||
wizardFieldSelection=true
|
||||
userFieldSelection=true
|
||||
groupSelection=true
|
||||
context="action"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showPostAdvanced}}
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.skip_redirect.label"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=action.skip_redirect}}
|
||||
|
||||
<span>
|
||||
{{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.suppress_notifications.label"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input type="checkbox" checked=action.suppress_notifications}}
|
||||
|
||||
<span>
|
||||
{{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if routeTo}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.action.route_to.code"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{input value=action.code}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -207,70 +207,66 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showAdvanced}}
|
||||
{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
|
||||
{{#if proSubscribed}}
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
{{#if field.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.condition
|
||||
options=fieldConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
</div>
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.condition
|
||||
options=fieldConditionOptions}}
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.index
|
||||
options=fieldIndexOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if isCategory}}
|
||||
<div class="setting pro">
|
||||
<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 full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.index
|
||||
options=fieldIndexOptions}}
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.property
|
||||
content=categoryPropertyTypes
|
||||
onChange=(action (mut field.property))
|
||||
options=(hash
|
||||
none="admin.wizard.selector.placeholder.property"
|
||||
)}}
|
||||
</div>
|
||||
|
||||
{{#if isCategory}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.property"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.property
|
||||
content=categoryPropertyTypes
|
||||
onChange=(action (mut field.property))
|
||||
options=(hash
|
||||
none="admin.wizard.selector.placeholder.property"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
</div>
|
||||
<div class="setting-value medium">
|
||||
{{input
|
||||
name="key"
|
||||
value=field.key
|
||||
class="medium"
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if validations}}
|
||||
{{wizard-realtime-validations field=field validations=validations}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input
|
||||
name="key"
|
||||
value=field.key
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if validations}}
|
||||
{{wizard-realtime-validations field=field validations=validations}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -33,85 +33,84 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
|
||||
|
||||
{{#if step.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.condition
|
||||
options=stepConditionOptions}}
|
||||
</div>
|
||||
{{#if proSubscribed}}
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label"></div>
|
||||
<div class="setting-value force-final">
|
||||
<h4>{{i18n "admin.wizard.step.force_final.label"}}</h4>
|
||||
{{input type="checkbox" checked=step.force_final}}
|
||||
<span>{{i18n "admin.wizard.step.force_final.description"}}</span>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.condition
|
||||
options=stepConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.required_data
|
||||
options=(hash
|
||||
inputTypes="validation"
|
||||
inputConnector="and"
|
||||
wizardFieldSelection="value"
|
||||
userFieldSelection="value"
|
||||
keyPlaceholder="admin.wizard.submission_key"
|
||||
context="step"
|
||||
)}}
|
||||
{{#if step.required_data}}
|
||||
<div class="required-data-message">
|
||||
<div class="label">
|
||||
{{i18n "admin.wizard.step.required_data.not_permitted_message"}}
|
||||
</div>
|
||||
{{input value=step.required_data_message}}
|
||||
<div class="setting full">
|
||||
<div class="setting-label"></div>
|
||||
<div class="setting-value force-final">
|
||||
<h4>{{i18n "admin.wizard.step.force_final.label"}}</h4>
|
||||
{{input type="checkbox" checked=step.force_final}}
|
||||
<span>{{i18n "admin.wizard.step.force_final.description"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<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
|
||||
inputs=step.required_data
|
||||
options=(hash
|
||||
inputTypes="validation"
|
||||
inputConnector="and"
|
||||
wizardFieldSelection="value"
|
||||
userFieldSelection="value"
|
||||
keyPlaceholder="admin.wizard.submission_key"
|
||||
context="step"
|
||||
)}}
|
||||
{{#if step.required_data}}
|
||||
<div class="required-data-message">
|
||||
<div class="label">
|
||||
{{i18n "admin.wizard.step.required_data.not_permitted_message"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{input value=step.required_data_message}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.step.permitted_params.label"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.permitted_params
|
||||
options=(hash
|
||||
pairConnector="set"
|
||||
inputTypes="association"
|
||||
keyPlaceholder="admin.wizard.param_key"
|
||||
valuePlaceholder="admin.wizard.submission_key"
|
||||
context="step"
|
||||
)}}
|
||||
</div>
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.step.permitted_params.label"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.permitted_params
|
||||
options=(hash
|
||||
pairConnector="set"
|
||||
inputTypes="association"
|
||||
keyPlaceholder="admin.wizard.param_key"
|
||||
valuePlaceholder="admin.wizard.submission_key"
|
||||
context="step"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input
|
||||
name="key"
|
||||
value=step.key
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
<div class="setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input
|
||||
name="key"
|
||||
value=step.key
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -129,5 +128,6 @@
|
|||
currentFieldId=currentField.id
|
||||
fieldTypes=fieldTypes
|
||||
removeField="removeField"
|
||||
wizardFields=wizardFields}}
|
||||
wizardFields=wizardFields
|
||||
proSubscribed=proSubscribed}}
|
||||
{{/each}}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="select-kit-header-wrapper">
|
||||
|
||||
{{component selectKit.options.selectedNameComponent
|
||||
tabindex=tabindex
|
||||
item=selectedContent
|
||||
selectKit=selectKit
|
||||
shouldDisplayClearableButton=shouldDisplayClearableButton
|
||||
}}
|
||||
|
||||
{{#if selectedContent.pro}}
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
{{/if}}
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
{{#if icons}}
|
||||
<div class="icons">
|
||||
<span class="selection-indicator"></span>
|
||||
{{#each icons as |icon|}}
|
||||
{{d-icon icon translatedtitle=(dasherize title)}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="texts">
|
||||
<span class="name">{{html-safe label}}</span>
|
||||
{{#if item.pro}}
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -0,0 +1,31 @@
|
|||
<div class="title-container">
|
||||
<h3 class="subscription-title">{{title}}</h3>
|
||||
|
||||
<div class="buttons">
|
||||
<span>
|
||||
{{#if updating}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else if updateIcon}}
|
||||
{{d-icon updateIcon}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{d-button
|
||||
icon="sync"
|
||||
action=(action "update")
|
||||
disabled=updating
|
||||
title="admin.wizard.pro.subscription.update"
|
||||
label="admin.wizard.pro.subscription.update"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if subscribed}}
|
||||
<div class="detail-container">
|
||||
<div class="subscription-state {{stateClass}}" title={{stateLabel}}>{{stateLabel}}</div>
|
||||
|
||||
{{#if subscription.updated_at}}
|
||||
<div class="subscription-updated-at" title={{subscription.updated_at}}>
|
||||
{{i18n "admin.wizard.pro.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,50 +1,54 @@
|
|||
<h3>{{i18n "admin.wizard.field.validations.header"}}</h3>
|
||||
|
||||
<ul>
|
||||
{{#each-in field.validations as |type props|}}
|
||||
<li>
|
||||
<span class="setting-title">
|
||||
<h4>{{i18n (concat "admin.wizard.field.validations." type)}}</h4>
|
||||
{{input type="checkbox" checked=props.status}}
|
||||
{{i18n "admin.wizard.field.validations.enabled"}}
|
||||
</span>
|
||||
<div class="validation-container">
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.categories"}}</label>
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.header"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value full">
|
||||
<ul>
|
||||
{{#each-in field.validations as |type props|}}
|
||||
<li>
|
||||
<span class="setting-title">
|
||||
<h4>{{i18n (concat "admin.wizard.field.validations." type)}}</h4>
|
||||
{{input type="checkbox" checked=props.status}}
|
||||
{{i18n "admin.wizard.field.validations.enabled"}}
|
||||
</span>
|
||||
<div class="validation-container">
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.categories"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{category-selector
|
||||
categories=(get this (concat "validationBuffer." type ".categories"))
|
||||
onChange=(action "updateValidationCategories" type props)
|
||||
class="wizard"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{category-selector
|
||||
categories=(get this (concat "validationBuffer." type ".categories"))
|
||||
onChange=(action "updateValidationCategories" type props)
|
||||
class="wizard"}}
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.max_topic_age"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="number" class="time-n-value" value=props.time_n_value}}
|
||||
{{combo-box
|
||||
value=(readonly props.time_unit)
|
||||
content=timeUnits
|
||||
class="time-unit-selector"
|
||||
onChange=(action (mut props.time_unit))}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.position"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{radio-button name=(concat type field.id) value="above" selection=props.position}}
|
||||
<span>{{i18n "admin.wizard.field.validations.above"}}</span>
|
||||
{{radio-button name=(concat type field.id) value="below" selection=props.position}}
|
||||
<span>{{i18n "admin.wizard.field.validations.below"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.max_topic_age"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="number" class="time-n-value" value=props.time_n_value}}
|
||||
{{combo-box
|
||||
value=(readonly props.time_unit)
|
||||
content=timeUnits
|
||||
class="time-unit-selector"
|
||||
onChange=(action (mut props.time_unit))}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.position"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{radio-button name=(concat type field.id) value="above" selection=props.position}}
|
||||
{{i18n "admin.wizard.field.validations.above"}}
|
||||
{{radio-button name=(concat type field.id) value="below" selection=props.position}}
|
||||
{{i18n "admin.wizard.field.validations.below"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& + .wizard-message + div {
|
||||
margin-top: 20px;
|
||||
|
@ -294,7 +294,7 @@
|
|||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
span {
|
||||
> span {
|
||||
font-size: 0.929em;
|
||||
}
|
||||
|
||||
|
@ -423,6 +423,17 @@
|
|||
.setting-gutter {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&.pro {
|
||||
.setting-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings {
|
||||
|
@ -729,27 +740,59 @@
|
|||
}
|
||||
}
|
||||
|
||||
.realtime-validations > ul {
|
||||
.admin-wizard-container.settings .realtime-validations .setting-value > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> li {
|
||||
background-color: var(--primary-low);
|
||||
padding: 1em;
|
||||
margin: 0 0 1em 0;
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
.setting-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
h4 {
|
||||
margin: 0 15px 0 0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.input .select-kit,
|
||||
> .select-kit {
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
> span {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.validation-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em 0;
|
||||
|
||||
.validation-section {
|
||||
width: 250px;
|
||||
min-width: 250px;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,6 +810,82 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pro-label {
|
||||
color: var(--tertiary);
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.admin-wizards-pro {
|
||||
.admin-wizard-controls {
|
||||
h3,
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 0.4em 0.5em;
|
||||
margin-left: 0.75em;
|
||||
background-color: var(--success);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.loading-container {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-wizard-pro-subscription {
|
||||
.title-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.buttons > span {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1em;
|
||||
background-color: var(--primary-very-low);
|
||||
|
||||
.subscription-state {
|
||||
padding: 0.25em 0.5em;
|
||||
margin-right: 0.75em;
|
||||
|
||||
&.active {
|
||||
background-color: var(--success);
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-pro-selector.select-kit.single-select {
|
||||
.select-kit-row .texts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pro-label {
|
||||
margin-left: 0.75em;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.btn-pavilion-pro {
|
||||
background: var(--pavilion-primary);
|
||||
color: var(--pavilion-secondary);
|
||||
|
|
|
@ -58,6 +58,7 @@ en:
|
|||
select_type: "Select a type"
|
||||
condition: "Condition"
|
||||
index: "Index"
|
||||
|
||||
pro_support_button:
|
||||
title: "Request Pro Support"
|
||||
label: "Pro Support"
|
||||
|
@ -89,11 +90,19 @@ en:
|
|||
no_file: Please choose a file to import
|
||||
file_size_error: The file size must be 512kb or less
|
||||
file_format_error: The file must be a .json file
|
||||
server_error: "Error: {{message}}"
|
||||
importing: Importing wizards...
|
||||
destroying: Destroying wizards...
|
||||
import_complete: Import complete
|
||||
destroy_complete: Destruction complete
|
||||
pro:
|
||||
documentation: Check out the PRO documentation
|
||||
authorize: "Authorize this forum to use your PRO subscription plan on %{server}."
|
||||
not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}."
|
||||
subscription_expiring: "Your subscription is active, but will expire in the next 48 hours."
|
||||
subscription_active: "Your subscription is active."
|
||||
subscription_inactive: "Your subscription is inactive on this forum. Read more in <a href='https://thepavilion.io/t/3652'>the documentation</a>."
|
||||
unauthorized: "You're unauthorized. If you have a subscription, it will become inactive in the next 48 hours."
|
||||
unauthorize_failed: Failed to unauthorize.
|
||||
submissions:
|
||||
select: "Select a wizard to see its submissions"
|
||||
viewing: "You're viewing the submissions of the %{wizardName}. Click 'Download' on the right to download them."
|
||||
|
@ -102,7 +111,6 @@ en:
|
|||
viewing: "View recent logs for wizards on the forum"
|
||||
documentation: "Check out the logs documentation"
|
||||
|
||||
|
||||
editor:
|
||||
show: "Show"
|
||||
hide: "Hide"
|
||||
|
@ -182,9 +190,9 @@ en:
|
|||
min_length_placeholder: "Minimum length in characters"
|
||||
max_length: "Max Length"
|
||||
max_length_placeholder: "Maximum length in characters"
|
||||
char_counter: "Character Counter"
|
||||
char_counter: "Counter"
|
||||
char_counter_placeholder: "Display Character Counter"
|
||||
field_placeholder: "Field Placeholder"
|
||||
field_placeholder: "Placeholder"
|
||||
file_types: "File Types"
|
||||
preview_template: "Preview Template"
|
||||
limit: "Limit"
|
||||
|
@ -195,7 +203,7 @@ en:
|
|||
label: "Format"
|
||||
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
|
||||
validations:
|
||||
header: "Realtime Validations"
|
||||
header: "Validations"
|
||||
enabled: "Enabled"
|
||||
similar_topics: "Similar Topics"
|
||||
position: "Position"
|
||||
|
@ -443,6 +451,24 @@ en:
|
|||
destroy: Destroy
|
||||
destroyed: destroyed
|
||||
|
||||
pro:
|
||||
nav_label: PRO
|
||||
label: PRO
|
||||
title: Custom Wizard PRO
|
||||
authorize: Authorize
|
||||
authorized: Authorized
|
||||
unauthorize: cancel
|
||||
not_subscribed: You're not currently subscribed
|
||||
subscription:
|
||||
title:
|
||||
community: Community Subscription
|
||||
business: Business Subscription
|
||||
status:
|
||||
active: Active
|
||||
inactive: Inactive
|
||||
update: Update
|
||||
last_updated: Last updated
|
||||
|
||||
wizard_js:
|
||||
group:
|
||||
select: "Select a group"
|
||||
|
|
|
@ -17,6 +17,7 @@ en:
|
|||
name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})"
|
||||
name_already_taken: "'%{name}' is already taken as a custom field name"
|
||||
save_default: "Failed to save custom field '%{name}'"
|
||||
pro_type: "%{type} custom fields require PRO Subscription"
|
||||
|
||||
field:
|
||||
too_short: "%{label} must be at least %{min} characters"
|
||||
|
@ -49,6 +50,7 @@ en:
|
|||
required: "%{property} is required"
|
||||
conflict: "Wizard with id '%{wizard_id}' already exists"
|
||||
after_time: "After time setting is invalid"
|
||||
pro: "%{type} %{property} is PRO only"
|
||||
|
||||
site_settings:
|
||||
custom_wizard_enabled: "Enable custom wizards."
|
||||
|
|
|
@ -43,5 +43,11 @@ Discourse::Application.routes.append do
|
|||
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
||||
post 'admin/wizards/manager/import' => 'admin_manager#import'
|
||||
delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy'
|
||||
|
||||
get 'admin/wizards/pro' => 'admin_pro#index'
|
||||
get 'admin/wizards/pro/authorize' => 'admin_pro#authorize'
|
||||
get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback'
|
||||
delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication'
|
||||
post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
||||
def index
|
||||
render_json_dump(custom_field_list)
|
||||
render_json_dump(
|
||||
custom_fields: custom_field_list,
|
||||
pro_subscribed: CustomWizard::Pro.subscribed?
|
||||
)
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
48
controllers/custom_wizard/admin/pro.rb
Normale Datei
48
controllers/custom_wizard/admin/pro.rb
Normale Datei
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::AdminProController < CustomWizard::AdminController
|
||||
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
|
||||
|
||||
def index
|
||||
render_serialized(pro, CustomWizard::ProSerializer, root: false)
|
||||
end
|
||||
|
||||
def authorize
|
||||
request_id = SecureRandom.hex(32)
|
||||
cookies[:user_api_request_id] = request_id
|
||||
redirect_to pro.authentication_url(current_user.id, request_id).to_s
|
||||
end
|
||||
|
||||
def authorize_callback
|
||||
payload = params[:payload]
|
||||
request_id = cookies[:user_api_request_id]
|
||||
|
||||
pro.authentication_response(request_id, payload)
|
||||
pro.update_subscription
|
||||
|
||||
redirect_to '/admin/wizards/pro'
|
||||
end
|
||||
|
||||
def destroy_authentication
|
||||
if pro.destroy_authentication
|
||||
render json: success_json
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
|
||||
def update_subscription
|
||||
if pro.update_subscription
|
||||
subscription = CustomWizard::ProSubscriptionSerializer.new(pro.subscription, root: false)
|
||||
render json: success_json.merge(subscription: subscription)
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def pro
|
||||
@pro ||= CustomWizard::Pro.new
|
||||
end
|
||||
end
|
|
@ -10,7 +10,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
|||
),
|
||||
field_types: CustomWizard::Field.types,
|
||||
realtime_validations: CustomWizard::RealtimeValidation.types,
|
||||
custom_fields: custom_field_list
|
||||
custom_fields: custom_field_list,
|
||||
pro_subscribed: CustomWizard::Pro.subscribed?
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
layout 'wizard'
|
||||
|
||||
before_action :ensure_plugin_enabled
|
||||
before_action :update_pro_subscription, only: [:index]
|
||||
helper_method :wizard_page_title
|
||||
helper_method :wizard_theme_id
|
||||
helper_method :wizard_theme_lookup
|
||||
|
@ -82,4 +83,8 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
redirect_to path("/")
|
||||
end
|
||||
end
|
||||
|
||||
def update_pro_subscription
|
||||
CustomWizard::Pro.update_subscription
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"result": {
|
||||
"line": 91.83
|
||||
"line": 91.96
|
||||
}
|
||||
}
|
||||
|
|
9
jobs/scheduled/update_pro_subscription.rb
Normale Datei
9
jobs/scheduled/update_pro_subscription.rb
Normale Datei
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled
|
||||
every 1.hour
|
||||
|
||||
def execute(args = {})
|
||||
CustomWizard::Pro.update_subscription
|
||||
end
|
||||
end
|
|
@ -749,4 +749,8 @@ class CustomWizard::Action
|
|||
@log.join('; ')
|
||||
)
|
||||
end
|
||||
|
||||
def pro_actions
|
||||
%w[send_message watch_categories send_to_api create_group create_category]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,13 +21,6 @@ class CustomWizard::Builder
|
|||
@sorted_handlers.sort_by! { |h| -h[:priority] }
|
||||
end
|
||||
|
||||
def mapper
|
||||
CustomWizard::Mapper.new(
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission&.fields_and_meta
|
||||
)
|
||||
end
|
||||
|
||||
def build(build_opts = {}, params = {})
|
||||
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
|
||||
return @wizard if !@wizard.can_access? && !build_opts[:force]
|
||||
|
@ -79,6 +72,32 @@ class CustomWizard::Builder
|
|||
@wizard
|
||||
end
|
||||
|
||||
def check_condition(template)
|
||||
if template['condition'].present?
|
||||
result = CustomWizard::Mapper.new(
|
||||
inputs: template['condition'],
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission&.fields_and_meta,
|
||||
opts: {
|
||||
multiple: true
|
||||
}
|
||||
).perform
|
||||
|
||||
result.any?
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mapper
|
||||
CustomWizard::Mapper.new(
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission&.fields_and_meta
|
||||
)
|
||||
end
|
||||
|
||||
def append_field(step, step_template, field_template, build_opts)
|
||||
params = {
|
||||
id: field_template['id'],
|
||||
|
@ -222,23 +241,6 @@ class CustomWizard::Builder
|
|||
end
|
||||
end
|
||||
|
||||
def check_condition(template)
|
||||
if template['condition'].present?
|
||||
result = CustomWizard::Mapper.new(
|
||||
inputs: template['condition'],
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission&.fields_and_meta,
|
||||
opts: {
|
||||
multiple: true
|
||||
}
|
||||
).perform
|
||||
|
||||
result.any?
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def check_if_permitted(step, step_template)
|
||||
step.permitted = true
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@ class ::CustomWizard::CustomField
|
|||
category: ["basic_category"],
|
||||
post: ["post"]
|
||||
}
|
||||
PRO_CLASSES ||= ['category', 'group']
|
||||
TYPES ||= ["string", "boolean", "integer", "json"]
|
||||
PRO_TYPES ||= ["json"]
|
||||
LIST_CACHE_KEY ||= 'custom_field_list'
|
||||
|
||||
def self.serializers
|
||||
|
@ -37,6 +39,8 @@ class ::CustomWizard::CustomField
|
|||
send("#{attr}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def save
|
||||
|
@ -81,6 +85,10 @@ class ::CustomWizard::CustomField
|
|||
next
|
||||
end
|
||||
|
||||
if attr == 'klass' && PRO_CLASSES.include?(value) && !@pro.subscribed?
|
||||
add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0
|
||||
add_error(I18n.t("#{i18n_key}.unsupported_serializers",
|
||||
class: klass,
|
||||
|
@ -92,6 +100,10 @@ class ::CustomWizard::CustomField
|
|||
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'type' && PRO_TYPES.include?(value) && !@pro.subscribed?
|
||||
add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'name'
|
||||
unless value.is_a?(String)
|
||||
add_error(I18n.t("#{i18n_key}.name_invalid", name: value))
|
||||
|
|
|
@ -47,6 +47,7 @@ class CustomWizard::Mapper
|
|||
@data = params[:data] || {}
|
||||
@user = params[:user]
|
||||
@opts = params[:opts] || {}
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def perform
|
||||
|
@ -251,7 +252,7 @@ class CustomWizard::Mapper
|
|||
end
|
||||
end
|
||||
|
||||
if opts[:template]
|
||||
if opts[:template] && @pro.subscribed?
|
||||
template = Liquid::Template.parse(string)
|
||||
string = template.render(data)
|
||||
end
|
||||
|
|
186
lib/custom_wizard/pro.rb
Normale Datei
186
lib/custom_wizard/pro.rb
Normale Datei
|
@ -0,0 +1,186 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::Pro
|
||||
include ActiveModel::Serialization
|
||||
|
||||
attr_accessor :authentication,
|
||||
:subscription
|
||||
|
||||
def initialize
|
||||
@authentication = CustomWizard::ProAuthentication.new(get_authentication)
|
||||
@subscription = CustomWizard::ProSubscription.new(get_subscription)
|
||||
end
|
||||
|
||||
def authorized?
|
||||
@authentication.active?
|
||||
end
|
||||
|
||||
def subscribed?
|
||||
@subscription.active?
|
||||
end
|
||||
|
||||
def server
|
||||
"test.thepavilion.io"
|
||||
end
|
||||
|
||||
def subscription_type
|
||||
"stripe"
|
||||
end
|
||||
|
||||
def client_name
|
||||
"custom-wizard"
|
||||
end
|
||||
|
||||
def scope
|
||||
"discourse-subscription-server:user_subscription"
|
||||
end
|
||||
|
||||
def update_subscription
|
||||
if @authentication.active?
|
||||
response = Excon.get(
|
||||
"https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}",
|
||||
headers: {
|
||||
"User-Api-Key" => @authentication.api_key
|
||||
}
|
||||
)
|
||||
|
||||
if response.status == 200
|
||||
begin
|
||||
data = JSON.parse(response.body).deep_symbolize_keys
|
||||
rescue JSON::ParserError
|
||||
return false
|
||||
end
|
||||
|
||||
return false unless data && data.is_a?(Hash)
|
||||
subscriptions = data[:subscriptions]
|
||||
|
||||
if subscriptions.present? && type = subscriptions.first[:price_nickname]
|
||||
@subscription = set_subscription(type)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
destroy_subscription
|
||||
false
|
||||
end
|
||||
|
||||
def destroy_subscription
|
||||
if remove_subscription
|
||||
@subscription = CustomWizard::ProSubscription.new(get_subscription)
|
||||
!@subscription.active?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def authentication_url(user_id, request_id)
|
||||
keys = @authentication.generate_keys(user_id, request_id)
|
||||
params = {
|
||||
public_key: keys.public_key,
|
||||
nonce: keys.nonce,
|
||||
client_id: @authentication.client_id,
|
||||
auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback",
|
||||
application_name: SiteSetting.title,
|
||||
scopes: scope
|
||||
}
|
||||
|
||||
uri = URI.parse("https://#{server}/user-api-key/new")
|
||||
uri.query = URI.encode_www_form(params)
|
||||
uri.to_s
|
||||
end
|
||||
|
||||
def authentication_response(request_id, payload)
|
||||
data = @authentication.decrypt_payload(request_id, payload)
|
||||
return false unless data.is_a?(Hash) && data[:key] && data[:user_id]
|
||||
|
||||
api_key = data[:key]
|
||||
user_id = data[:user_id]
|
||||
user = User.find(user_id)
|
||||
|
||||
if user&.admin
|
||||
@authentication = set_authentication(api_key, user.id)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_authentication
|
||||
if remove_authentication
|
||||
@authentication = CustomWizard::ProAuthentication.new(get_authentication)
|
||||
!@authentication.active?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.subscribed?
|
||||
self.new.subscribed?
|
||||
end
|
||||
|
||||
def self.authorized?
|
||||
self.new.authorized?
|
||||
end
|
||||
|
||||
def self.update_subscription
|
||||
self.new.update_subscription
|
||||
end
|
||||
|
||||
def self.namespace
|
||||
"custom_wizard_pro"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription_db_key
|
||||
"subscription"
|
||||
end
|
||||
|
||||
def authentication_db_key
|
||||
"authentication"
|
||||
end
|
||||
|
||||
def get_subscription
|
||||
raw = PluginStore.get(self.class.namespace, subscription_db_key)
|
||||
|
||||
if raw.present?
|
||||
OpenStruct.new(
|
||||
type: raw['type'],
|
||||
updated_at: raw['updated_at']
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_subscription
|
||||
PluginStore.remove(self.class.namespace, subscription_db_key)
|
||||
end
|
||||
|
||||
def set_subscription(type)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, subscription_db_key, type: type, updated_at: Time.now)
|
||||
CustomWizard::ProSubscription.new(get_subscription)
|
||||
end
|
||||
|
||||
def get_authentication
|
||||
raw = PluginStore.get(self.class.namespace, authentication_db_key)
|
||||
OpenStruct.new(
|
||||
key: raw && raw['key'],
|
||||
auth_by: raw && raw['auth_by'],
|
||||
auth_at: raw && raw['auth_at']
|
||||
)
|
||||
end
|
||||
|
||||
def set_authentication(key, user_id)
|
||||
PluginStore.set(self.class.namespace, authentication_db_key,
|
||||
key: key,
|
||||
auth_by: user_id,
|
||||
auth_at: Time.now
|
||||
)
|
||||
CustomWizard::ProAuthentication.new(get_authentication)
|
||||
end
|
||||
|
||||
def remove_authentication
|
||||
PluginStore.remove(self.class.namespace, authentication_db_key)
|
||||
get_authentication
|
||||
end
|
||||
end
|
95
lib/custom_wizard/pro/authentication.rb
Normale Datei
95
lib/custom_wizard/pro/authentication.rb
Normale Datei
|
@ -0,0 +1,95 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProAuthentication
|
||||
include ActiveModel::Serialization
|
||||
|
||||
attr_reader :client_id,
|
||||
:auth_by,
|
||||
:auth_at,
|
||||
:api_key
|
||||
|
||||
def initialize(auth)
|
||||
if auth
|
||||
@api_key = auth.key
|
||||
@auth_at = auth.auth_at
|
||||
@auth_by = auth.auth_by
|
||||
end
|
||||
|
||||
@client_id = get_client_id || set_client_id
|
||||
end
|
||||
|
||||
def active?
|
||||
@api_key.present?
|
||||
end
|
||||
|
||||
def generate_keys(user_id, request_id)
|
||||
rsa = OpenSSL::PKey::RSA.generate(2048)
|
||||
nonce = SecureRandom.hex(32)
|
||||
set_keys(request_id, user_id, rsa, nonce)
|
||||
|
||||
OpenStruct.new(nonce: nonce, public_key: rsa.public_key)
|
||||
end
|
||||
|
||||
def decrypt_payload(request_id, payload)
|
||||
keys = get_keys(request_id)
|
||||
|
||||
return false unless keys.present? && keys.pem
|
||||
delete_keys(request_id)
|
||||
|
||||
rsa = OpenSSL::PKey::RSA.new(keys.pem)
|
||||
decrypted_payload = rsa.private_decrypt(Base64.decode64(payload))
|
||||
|
||||
return false unless decrypted_payload.present?
|
||||
|
||||
begin
|
||||
data = JSON.parse(decrypted_payload).symbolize_keys
|
||||
rescue JSON::ParserError
|
||||
return false
|
||||
end
|
||||
|
||||
return false unless data[:nonce] == keys.nonce
|
||||
data[:user_id] = keys.user_id
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def get_keys(request_id)
|
||||
raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}")
|
||||
OpenStruct.new(
|
||||
user_id: raw && raw['user_id'],
|
||||
pem: raw && raw['pem'],
|
||||
nonce: raw && raw['nonce']
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def keys_db_key
|
||||
"keys"
|
||||
end
|
||||
|
||||
def client_id_db_key
|
||||
"client_id"
|
||||
end
|
||||
|
||||
def set_keys(request_id, user_id, rsa, nonce)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}",
|
||||
user_id: user_id,
|
||||
pem: rsa.export,
|
||||
nonce: nonce
|
||||
)
|
||||
end
|
||||
|
||||
def delete_keys(request_id)
|
||||
PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}")
|
||||
end
|
||||
|
||||
def get_client_id
|
||||
PluginStore.get(CustomWizard::Pro.namespace, client_id_db_key)
|
||||
end
|
||||
|
||||
def set_client_id
|
||||
client_id = SecureRandom.hex(32)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, client_id_db_key, client_id)
|
||||
client_id
|
||||
end
|
||||
end
|
22
lib/custom_wizard/pro/subscription.rb
Normale Datei
22
lib/custom_wizard/pro/subscription.rb
Normale Datei
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSubscription
|
||||
include ActiveModel::Serialization
|
||||
|
||||
attr_reader :type,
|
||||
:updated_at
|
||||
|
||||
def initialize(subscription)
|
||||
if subscription
|
||||
@type = subscription.type
|
||||
@updated_at = subscription.updated_at
|
||||
end
|
||||
end
|
||||
|
||||
def types
|
||||
%w(community business)
|
||||
end
|
||||
|
||||
def active?
|
||||
types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
|
||||
end
|
||||
end
|
|
@ -6,6 +6,7 @@ class CustomWizard::TemplateValidator
|
|||
def initialize(data, opts = {})
|
||||
@data = data
|
||||
@opts = opts
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def perform
|
||||
|
@ -14,12 +15,15 @@ class CustomWizard::TemplateValidator
|
|||
check_id(data, :wizard)
|
||||
check_required(data, :wizard)
|
||||
validate_after_time
|
||||
validate_pro(data, :wizard)
|
||||
|
||||
data[:steps].each do |step|
|
||||
check_required(step, :step)
|
||||
validate_pro(step, :step)
|
||||
|
||||
if data[:fields].present?
|
||||
data[:fields].each do |field|
|
||||
if step[:fields].present?
|
||||
step[:fields].each do |field|
|
||||
validate_pro(field, :field)
|
||||
check_required(field, :field)
|
||||
end
|
||||
end
|
||||
|
@ -27,6 +31,7 @@ class CustomWizard::TemplateValidator
|
|||
|
||||
if data[:actions].present?
|
||||
data[:actions].each do |action|
|
||||
validate_pro(action, :action)
|
||||
check_required(action, :action)
|
||||
end
|
||||
end
|
||||
|
@ -47,16 +52,53 @@ class CustomWizard::TemplateValidator
|
|||
}
|
||||
end
|
||||
|
||||
def self.pro
|
||||
{
|
||||
wizard: {},
|
||||
step: {
|
||||
condition: 'present',
|
||||
index: 'conditional'
|
||||
},
|
||||
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)
|
||||
CustomWizard::TemplateValidator.required[type].each do |property|
|
||||
self.class.required[type].each do |property|
|
||||
if object[property].blank?
|
||||
errors.add :base, I18n.t("wizard.validation.required", property: property)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_pro(object, type)
|
||||
self.class.pro[type].each do |property, pro_type|
|
||||
is_pro = object[property.to_s].present? && (
|
||||
pro_type === 'present' ||
|
||||
(pro_type === 'conditional' && object[property.to_s].is_a?(Hash)) ||
|
||||
(pro_type.is_a?(Array) && pro_type.include?(object[property.to_s]))
|
||||
)
|
||||
|
||||
if is_pro && !@pro.subscribed?
|
||||
errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_id(object, type)
|
||||
if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id])
|
||||
errors.add :base, I18n.t("wizard.validation.conflict", wizard_id: object[:id])
|
||||
|
|
|
@ -310,10 +310,10 @@ class CustomWizard::Wizard
|
|||
end
|
||||
end
|
||||
|
||||
def self.list(user, template_opts: {}, not_completed: false)
|
||||
def self.list(user, template_opts = {}, not_completed = false)
|
||||
return [] unless user
|
||||
|
||||
CustomWizard::Template.list(template_opts).reduce([]) do |result, template|
|
||||
CustomWizard::Template.list(**template_opts).reduce([]) do |result, template|
|
||||
wizard = new(template, user)
|
||||
result.push(wizard) if wizard.can_access? && (
|
||||
!not_completed || !wizard.completed?
|
||||
|
@ -325,7 +325,7 @@ class CustomWizard::Wizard
|
|||
def self.after_signup(user)
|
||||
wizards = list(
|
||||
user,
|
||||
template_opts: {
|
||||
{
|
||||
setting: 'after_signup',
|
||||
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
|
||||
}
|
||||
|
@ -336,11 +336,11 @@ class CustomWizard::Wizard
|
|||
def self.prompt_completion(user)
|
||||
wizards = list(
|
||||
user,
|
||||
template_opts: {
|
||||
{
|
||||
setting: 'prompt_completion',
|
||||
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
|
||||
},
|
||||
not_completed: true
|
||||
true
|
||||
)
|
||||
if wizards.any?
|
||||
wizards.map do |w|
|
||||
|
|
12
plugin.rb
12
plugin.rb
|
@ -71,11 +71,13 @@ after_initialize do
|
|||
../controllers/custom_wizard/admin/logs.rb
|
||||
../controllers/custom_wizard/admin/manager.rb
|
||||
../controllers/custom_wizard/admin/custom_fields.rb
|
||||
../controllers/custom_wizard/admin/pro.rb
|
||||
../controllers/custom_wizard/wizard.rb
|
||||
../controllers/custom_wizard/steps.rb
|
||||
../controllers/custom_wizard/realtime_validations.rb
|
||||
../jobs/refresh_api_access_token.rb
|
||||
../jobs/set_after_time_wizard.rb
|
||||
../jobs/regular/refresh_api_access_token.rb
|
||||
../jobs/regular/set_after_time_wizard.rb
|
||||
../jobs/scheduled/update_pro_subscription.rb
|
||||
../lib/custom_wizard/validators/template.rb
|
||||
../lib/custom_wizard/validators/update.rb
|
||||
../lib/custom_wizard/action_result.rb
|
||||
|
@ -94,6 +96,9 @@ after_initialize do
|
|||
../lib/custom_wizard/submission.rb
|
||||
../lib/custom_wizard/template.rb
|
||||
../lib/custom_wizard/wizard.rb
|
||||
../lib/custom_wizard/pro.rb
|
||||
../lib/custom_wizard/pro/subscription.rb
|
||||
../lib/custom_wizard/pro/authentication.rb
|
||||
../lib/custom_wizard/api/api.rb
|
||||
../lib/custom_wizard/api/authorization.rb
|
||||
../lib/custom_wizard/api/endpoint.rb
|
||||
|
@ -114,6 +119,9 @@ after_initialize do
|
|||
../serializers/custom_wizard/log_serializer.rb
|
||||
../serializers/custom_wizard/submission_serializer.rb
|
||||
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
|
||||
../serializers/custom_wizard/pro/authentication_serializer.rb
|
||||
../serializers/custom_wizard/pro/subscription_serializer.rb
|
||||
../serializers/custom_wizard/pro_serializer.rb
|
||||
../extensions/extra_locales_controller.rb
|
||||
../extensions/invites_controller.rb
|
||||
../extensions/users_controller.rb
|
||||
|
|
11
serializers/custom_wizard/pro/authentication_serializer.rb
Normale Datei
11
serializers/custom_wizard/pro/authentication_serializer.rb
Normale Datei
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer
|
||||
attributes :active,
|
||||
:client_id,
|
||||
:auth_by,
|
||||
:auth_at
|
||||
|
||||
def active
|
||||
object.active?
|
||||
end
|
||||
end
|
10
serializers/custom_wizard/pro/subscription_serializer.rb
Normale Datei
10
serializers/custom_wizard/pro/subscription_serializer.rb
Normale Datei
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer
|
||||
attributes :type,
|
||||
:active,
|
||||
:updated_at
|
||||
|
||||
def active
|
||||
object.active?
|
||||
end
|
||||
end
|
6
serializers/custom_wizard/pro_serializer.rb
Normale Datei
6
serializers/custom_wizard/pro_serializer.rb
Normale Datei
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSerializer < ApplicationSerializer
|
||||
attributes :server
|
||||
has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects
|
||||
has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects
|
||||
end
|
|
@ -6,26 +6,22 @@ describe CustomWizard::Action do
|
|||
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
|
||||
fab!(:group) { Fabricate(:group) }
|
||||
|
||||
let(:wizard_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||
let(:open_composer) { get_wizard_fixture("actions/open_composer") }
|
||||
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
||||
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(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") }
|
||||
|
||||
let(:open_composer) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
def update_template(template)
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
before do
|
||||
Group.refresh_automatic_group!(:trust_level_2)
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
update_template(wizard_template)
|
||||
end
|
||||
|
||||
context 'creating a topic' do
|
||||
|
@ -110,54 +106,6 @@ describe CustomWizard::Action do
|
|||
end
|
||||
end
|
||||
|
||||
context 'sending a message' do
|
||||
it 'works' do
|
||||
User.create(username: 'angus1', email: "angus1@email.com")
|
||||
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, {}).update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
topic = Topic.where(
|
||||
archetype: Archetype.private_message,
|
||||
title: "Message title"
|
||||
)
|
||||
|
||||
post = Post.where(
|
||||
topic_id: topic.pluck(:id),
|
||||
raw: "I will interpolate some wizard fields"
|
||||
)
|
||||
|
||||
expect(topic.exists?).to eq(true)
|
||||
expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
|
||||
expect(post.exists?).to eq(true)
|
||||
end
|
||||
|
||||
it 'allows using multiple PM targets' do
|
||||
User.create(username: 'angus1', email: "angus1@email.com")
|
||||
User.create(username: 'faiz', email: "faiz@email.com")
|
||||
Group.create(name: "cool_group")
|
||||
Group.create(name: 'cool_group_1')
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, {}).update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
topic = Topic.where(
|
||||
archetype: Archetype.private_message,
|
||||
title: "Multiple Recipients title"
|
||||
)
|
||||
|
||||
post = Post.where(
|
||||
topic_id: topic.pluck(:id),
|
||||
raw: "I will interpolate some wizard fields"
|
||||
)
|
||||
expect(topic.exists?).to eq(true)
|
||||
expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz')
|
||||
expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
|
||||
expect(post.exists?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates a profile' do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
upload = Upload.create!(
|
||||
|
@ -182,10 +130,8 @@ describe CustomWizard::Action do
|
|||
updater = wizard.create_updater(wizard.steps[1].id, {})
|
||||
updater.update
|
||||
|
||||
category = Category.find_by(id: wizard.current_submission.fields['action_8'])
|
||||
|
||||
expect(updater.result[:redirect_on_next]).to eq(
|
||||
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1"
|
||||
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&tags=tag1"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -210,35 +156,11 @@ describe CustomWizard::Action do
|
|||
end
|
||||
end
|
||||
|
||||
it 'creates a category' 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(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
|
||||
end
|
||||
|
||||
it 'creates a group' do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||
expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
|
||||
end
|
||||
|
||||
it 'adds a user to a group' do
|
||||
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 '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: wizard.current_submission.fields['action_8'],
|
||||
user_id: user.id
|
||||
).first.notification_level).to eq(2)
|
||||
|
||||
expect(CategoryUser.where(
|
||||
category_id: category.id,
|
||||
user_id: user.id
|
||||
|
@ -251,4 +173,97 @@ describe CustomWizard::Action do
|
|||
updater.update
|
||||
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
|
||||
end
|
||||
|
||||
context "pro actions" do
|
||||
before do
|
||||
enable_pro
|
||||
end
|
||||
|
||||
it '#send_message' do
|
||||
wizard_template['actions'] << send_message
|
||||
update_template(wizard_template)
|
||||
|
||||
User.create(username: 'angus1', email: "angus1@email.com")
|
||||
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, {}).update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
topic = Topic.where(
|
||||
archetype: Archetype.private_message,
|
||||
title: "Message title"
|
||||
)
|
||||
|
||||
post = Post.where(
|
||||
topic_id: topic.pluck(:id),
|
||||
raw: "I will interpolate some wizard fields"
|
||||
)
|
||||
|
||||
expect(topic.exists?).to eq(true)
|
||||
expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
|
||||
expect(post.exists?).to eq(true)
|
||||
end
|
||||
|
||||
it '#send_message allows using multiple targets' do
|
||||
wizard_template['actions'] << send_message_multi
|
||||
update_template(wizard_template)
|
||||
|
||||
User.create(username: 'angus1', email: "angus1@email.com")
|
||||
User.create(username: 'faiz', email: "faiz@email.com")
|
||||
Group.create(name: "cool_group")
|
||||
Group.create(name: 'cool_group_1')
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
wizard.create_updater(wizard.steps[0].id, {}).update
|
||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||
|
||||
topic = Topic.where(
|
||||
archetype: Archetype.private_message,
|
||||
title: "Multiple Recipients title"
|
||||
)
|
||||
|
||||
post = Post.where(
|
||||
topic_id: topic.pluck(:id),
|
||||
raw: "I will interpolate some wizard fields"
|
||||
)
|
||||
|
||||
expect(topic.exists?).to eq(true)
|
||||
expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz')
|
||||
expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
|
||||
expect(post.exists?).to eq(true)
|
||||
end
|
||||
|
||||
it '#create_category' do
|
||||
wizard_template['actions'] << create_category
|
||||
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(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
|
||||
end
|
||||
|
||||
it '#create_group' do
|
||||
wizard_template['actions'] << create_group
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,45 +15,15 @@ describe CustomWizard::Builder do
|
|||
fab!(:category2) { Fabricate(:category, name: 'cat2') }
|
||||
fab!(:group) { Fabricate(:group) }
|
||||
|
||||
let(:required_data_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_param_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:user_condition_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||
let(:required_data_json) { get_wizard_fixture("step/required_data") }
|
||||
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") }
|
||||
|
||||
before do
|
||||
Group.refresh_automatic_group!(:trust_level_3)
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
|
@ -283,6 +253,7 @@ describe CustomWizard::Builder do
|
|||
|
||||
context "with condition" do
|
||||
before do
|
||||
enable_pro
|
||||
@template[:steps][0][:condition] = user_condition_json['condition']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
@ -321,6 +292,7 @@ describe CustomWizard::Builder do
|
|||
|
||||
context "with condition" do
|
||||
before do
|
||||
enable_pro
|
||||
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
|
|
@ -3,12 +3,8 @@
|
|||
require_relative '../../plugin_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)
|
||||
}
|
||||
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
|
||||
let(:custom_field_pro_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
|
||||
|
||||
before do
|
||||
CustomWizard::CustomField.invalidate_cache
|
||||
|
@ -104,8 +100,8 @@ describe CustomWizard::CustomField do
|
|||
|
||||
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']
|
||||
invalid_field_json['klass'] = 'post'
|
||||
invalid_field_json['serializers'] = ['post', 'post_revision']
|
||||
|
||||
custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
|
||||
|
||||
|
@ -113,8 +109,8 @@ describe CustomWizard::CustomField do
|
|||
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"
|
||||
class: "post",
|
||||
serializers: "post_revision"
|
||||
)
|
||||
)
|
||||
expect(
|
||||
|
@ -196,6 +192,50 @@ describe CustomWizard::CustomField do
|
|||
).exists?
|
||||
).to eq(false)
|
||||
end
|
||||
|
||||
it "does not save pro field types without a pro subscription" do
|
||||
pro_field_json = custom_field_pro_json['custom_fields'].first
|
||||
custom_field = CustomWizard::CustomField.new(nil, pro_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.pro_type", type: "json")
|
||||
)
|
||||
end
|
||||
|
||||
it "does not save pro field classes without a pro subscription" do
|
||||
pro_field_json = custom_field_pro_json['custom_fields'].second
|
||||
custom_field = CustomWizard::CustomField.new(nil, pro_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.pro_type", type: "category")
|
||||
)
|
||||
end
|
||||
|
||||
context "with a pro subscription" do
|
||||
before do
|
||||
enable_pro
|
||||
end
|
||||
|
||||
it "saves pro field types" do
|
||||
pro_field_json = custom_field_pro_json['custom_fields'].first
|
||||
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
|
||||
|
||||
expect(custom_field.save).to eq(true)
|
||||
expect(custom_field.valid?).to eq(true)
|
||||
end
|
||||
|
||||
it "saves pro field classes" do
|
||||
pro_field_json = custom_field_pro_json['custom_fields'].second
|
||||
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
|
||||
|
||||
expect(custom_field.save).to eq(true)
|
||||
expect(custom_field.valid?).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "lists" do
|
||||
|
@ -205,15 +245,15 @@ describe CustomWizard::CustomField do
|
|||
end
|
||||
end
|
||||
|
||||
it "lists saved custom field records" do
|
||||
expect(CustomWizard::CustomField.list.length).to eq(4)
|
||||
it "saved custom field records" do
|
||||
expect(CustomWizard::CustomField.list.length).to eq(2)
|
||||
end
|
||||
|
||||
it "lists saved custom field records by attribute value" do
|
||||
it "saved custom field records by attribute value" do
|
||||
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
|
||||
end
|
||||
|
||||
it "lists saved custom field records by optional values" do
|
||||
it "saved custom field records by optional values" do
|
||||
field_json = custom_field_json['custom_fields'].first
|
||||
field_json['serializers'] = nil
|
||||
|
||||
|
@ -221,12 +261,12 @@ describe CustomWizard::CustomField do
|
|||
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)
|
||||
it "custom field records added by other plugins " do
|
||||
expect(CustomWizard::CustomField.external_list.length).to be > 10
|
||||
end
|
||||
|
||||
it "lists all custom field records" do
|
||||
expect(CustomWizard::CustomField.full_list.length).to eq(15)
|
||||
it "all custom field records" do
|
||||
expect(CustomWizard::CustomField.full_list.length).to be > 12
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Field do
|
||||
let(:field_hash) do
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
||||
).read).with_indifferent_access
|
||||
end
|
||||
let(:field_hash) { get_wizard_fixture("field/field") }
|
||||
|
||||
before do
|
||||
CustomWizard::Field.register(
|
||||
|
|
|
@ -31,16 +31,8 @@ describe CustomWizard::Mapper do
|
|||
]
|
||||
)
|
||||
}
|
||||
let(:inputs) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/inputs.json"
|
||||
).read)
|
||||
}
|
||||
let(:data) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/data.json"
|
||||
).read)
|
||||
}
|
||||
let(:inputs) { get_wizard_fixture("mapper/inputs") }
|
||||
let(:data) { get_wizard_fixture("mapper/data") }
|
||||
let(:template_params) {
|
||||
{
|
||||
"step_1_field_1" => "Hello"
|
||||
|
@ -352,7 +344,7 @@ describe CustomWizard::Mapper do
|
|||
expect(result).to eq(template_params["step_1_field_1"])
|
||||
end
|
||||
|
||||
it "treats replaced values as string literals" do
|
||||
it "requires a pro subscription" do
|
||||
template = '{{ "w{step_1_field_1}" | size }}'
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
|
@ -362,60 +354,17 @@ describe CustomWizard::Mapper do
|
|||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params["step_1_field_1"].size.to_s)
|
||||
expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}")
|
||||
end
|
||||
|
||||
it "allows the wizard values to be used inside conditionals" do
|
||||
template = <<-LIQUID
|
||||
{%- if "w{step_1_field_1}" contains "ello" -%}
|
||||
Correct
|
||||
{%- else -%}
|
||||
Incorrect
|
||||
{%-endif-%}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq("Correct")
|
||||
end
|
||||
context "with a pro subscription" do
|
||||
before do
|
||||
enable_pro
|
||||
end
|
||||
|
||||
it "can access data passed to render method as variable" do
|
||||
template = "{{step_1_field_1.size}}"
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params["step_1_field_1"].size.to_s)
|
||||
end
|
||||
|
||||
it "doesn't parse the template when template param is false" do
|
||||
template = <<-LIQUID.strip
|
||||
{{ "w{step_1_field_1}" | size}}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: false,
|
||||
)
|
||||
expect(result).to eq(template)
|
||||
end
|
||||
|
||||
context "custom filter: 'first_non_empty'" do
|
||||
it "gives first non empty element from list" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{{ entry }}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_non_empty, user1)
|
||||
it "treats replaced values as string literals" do
|
||||
template = '{{ "w{step_1_field_1}" | size }}'
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
|
@ -423,15 +372,18 @@ describe CustomWizard::Mapper do
|
|||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params_non_empty["step_1_field_3"])
|
||||
expect(result).to eq(template_params["step_1_field_1"].size.to_s)
|
||||
end
|
||||
|
||||
it "gives first non empty element from list when multiple non empty values present" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{{ entry }}
|
||||
it "allows the wizard values to be used inside conditionals" do
|
||||
template = <<-LIQUID
|
||||
{%- if "w{step_1_field_1}" contains "ello" -%}
|
||||
Correct
|
||||
{%- else -%}
|
||||
Incorrect
|
||||
{%-endif-%}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_multiple_non_empty, user1)
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
|
@ -439,25 +391,84 @@ describe CustomWizard::Mapper do
|
|||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"])
|
||||
expect(result).to eq("Correct")
|
||||
end
|
||||
|
||||
it "gives empty if all elements are empty" do
|
||||
it "can access data passed to render method as variable" do
|
||||
template = "{{step_1_field_1.size}}"
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params["step_1_field_1"].size.to_s)
|
||||
end
|
||||
|
||||
it "doesn't parse the template when template param is false" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{%- if entry -%}
|
||||
{{ "w{step_1_field_1}" | size}}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: false,
|
||||
)
|
||||
expect(result).to eq(template)
|
||||
end
|
||||
|
||||
context "custom filter: 'first_non_empty'" do
|
||||
it "gives first non empty element from list" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{{ entry }}
|
||||
{%- endif -%}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_empty, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq("")
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_non_empty, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params_non_empty["step_1_field_3"])
|
||||
end
|
||||
|
||||
it "gives first non empty element from list when multiple non empty values present" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{{ entry }}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_multiple_non_empty, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"])
|
||||
end
|
||||
|
||||
it "gives empty if all elements are empty" do
|
||||
template = <<-LIQUID.strip
|
||||
{%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
|
||||
{%- if entry -%}
|
||||
{{ entry }}
|
||||
{%- endif -%}
|
||||
LIQUID
|
||||
mapper = create_template_mapper(template_params_empty, user1)
|
||||
result = mapper.interpolate(
|
||||
template.dup,
|
||||
template: true,
|
||||
user: true,
|
||||
wizard: true,
|
||||
value: true
|
||||
)
|
||||
expect(result).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
125
spec/components/custom_wizard/pro_spec.rb
Normale Datei
125
spec/components/custom_wizard/pro_spec.rb
Normale Datei
|
@ -0,0 +1,125 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Pro do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
it "initializes pro authentication and subscription" do
|
||||
pro = described_class.new
|
||||
expect(pro.authentication.class).to eq(CustomWizard::ProAuthentication)
|
||||
expect(pro.subscription.class).to eq(CustomWizard::ProSubscription)
|
||||
end
|
||||
|
||||
it "returns authorized and subscribed states" do
|
||||
pro = described_class.new
|
||||
expect(pro.authorized?).to eq(false)
|
||||
expect(pro.subscribed?).to eq(false)
|
||||
end
|
||||
|
||||
context "subscription" do
|
||||
before do
|
||||
@pro = described_class.new
|
||||
end
|
||||
|
||||
it "updates valid subscriptions" do
|
||||
stub_subscription_request(200, valid_subscription)
|
||||
expect(@pro.update_subscription).to eq(true)
|
||||
expect(@pro.subscribed?).to eq(true)
|
||||
end
|
||||
|
||||
it "handles invalid subscriptions" do
|
||||
stub_subscription_request(200, invalid_subscription)
|
||||
expect(@pro.update_subscription).to eq(false)
|
||||
expect(@pro.subscribed?).to eq(false)
|
||||
end
|
||||
|
||||
it "handles subscription http errors" do
|
||||
stub_subscription_request(404, {})
|
||||
expect(@pro.update_subscription).to eq(false)
|
||||
expect(@pro.subscribed?).to eq(false)
|
||||
end
|
||||
|
||||
it "destroys subscriptions" do
|
||||
stub_subscription_request(200, valid_subscription)
|
||||
expect(@pro.update_subscription).to eq(true)
|
||||
expect(@pro.destroy_subscription).to eq(true)
|
||||
expect(@pro.subscribed?).to eq(false)
|
||||
end
|
||||
|
||||
it "has class aliases" do
|
||||
authenticate_pro
|
||||
stub_subscription_request(200, valid_subscription)
|
||||
expect(described_class.update_subscription).to eq(true)
|
||||
expect(described_class.subscribed?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "authentication" do
|
||||
before do
|
||||
@pro = described_class.new
|
||||
user.update!(admin: true)
|
||||
end
|
||||
|
||||
it "generates a valid authentication request url" do
|
||||
request_id = SecureRandom.hex(32)
|
||||
uri = URI(@pro.authentication_url(user.id, request_id))
|
||||
expect(uri.host).to eq(@pro.server)
|
||||
|
||||
parsed_query = Rack::Utils.parse_query uri.query
|
||||
expect(parsed_query['public_key'].present?).to eq(true)
|
||||
expect(parsed_query['nonce'].present?).to eq(true)
|
||||
expect(parsed_query['client_id'].present?).to eq(true)
|
||||
expect(parsed_query['auth_redirect'].present?).to eq(true)
|
||||
expect(parsed_query['application_name']).to eq(SiteSetting.title)
|
||||
expect(parsed_query['scopes']).to eq(@pro.scope)
|
||||
end
|
||||
|
||||
def generate_payload(request_id, user_id)
|
||||
uri = URI(@pro.authentication_url(user_id, request_id))
|
||||
keys = @pro.authentication.get_keys(request_id)
|
||||
raw_payload = {
|
||||
key: "12345",
|
||||
nonce: keys.nonce,
|
||||
push: false,
|
||||
api: UserApiKeysController::AUTH_API_VERSION
|
||||
}.to_json
|
||||
public_key = OpenSSL::PKey::RSA.new(keys.pem)
|
||||
Base64.encode64(public_key.public_encrypt(raw_payload))
|
||||
end
|
||||
|
||||
it "handles authentication response if request and response is valid" do
|
||||
request_id = SecureRandom.hex(32)
|
||||
payload = generate_payload(request_id, user.id)
|
||||
|
||||
expect(@pro.authentication_response(request_id, payload)).to eq(true)
|
||||
expect(@pro.authorized?).to eq(true)
|
||||
end
|
||||
|
||||
it "discards authentication response if user who made request as not an admin" do
|
||||
user.update!(admin: false)
|
||||
|
||||
request_id = SecureRandom.hex(32)
|
||||
payload = generate_payload(request_id, user.id)
|
||||
|
||||
expect(@pro.authentication_response(request_id, payload)).to eq(false)
|
||||
expect(@pro.authorized?).to eq(false)
|
||||
end
|
||||
|
||||
it "discards authentication response if request_id is invalid" do
|
||||
payload = generate_payload(SecureRandom.hex(32), user.id)
|
||||
|
||||
expect(@pro.authentication_response(SecureRandom.hex(32), payload)).to eq(false)
|
||||
expect(@pro.authorized?).to eq(false)
|
||||
end
|
||||
|
||||
it "destroys authentication" do
|
||||
request_id = SecureRandom.hex(32)
|
||||
payload = generate_payload(request_id, user.id)
|
||||
@pro.authentication_response(request_id, payload)
|
||||
|
||||
expect(@pro.destroy_authentication).to eq(true)
|
||||
expect(@pro.authorized?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,21 +2,8 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Step do
|
||||
let(:step_hash) do
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json"
|
||||
).read
|
||||
).with_indifferent_access
|
||||
end
|
||||
|
||||
let(:field_hash) do
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
||||
).read
|
||||
).with_indifferent_access
|
||||
end
|
||||
let(:step_hash) { get_wizard_fixture("step/step") }
|
||||
let(:field_hash) { get_wizard_fixture("field/field") }
|
||||
|
||||
before do
|
||||
@step = CustomWizard::Step.new(step_hash[:id])
|
||||
|
|
|
@ -4,12 +4,7 @@ require_relative '../../plugin_helper'
|
|||
describe CustomWizard::Submission do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:user2) { Fabricate(:user) }
|
||||
|
||||
let(:template_json) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
let(:template_json) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(template_json, skip_jobs: true)
|
||||
|
|
|
@ -3,17 +3,8 @@ require_relative '../../plugin_helper'
|
|||
|
||||
describe CustomWizard::Template do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
let(:template_json) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
let(:permitted_json) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read)
|
||||
}
|
||||
let(:template_json) { get_wizard_fixture("wizard") }
|
||||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(template_json, skip_jobs: true)
|
||||
|
|
|
@ -3,12 +3,9 @@ require_relative '../../plugin_helper'
|
|||
|
||||
describe CustomWizard::TemplateValidator do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read).with_indifferent_access
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
||||
let(:user_condition) { get_wizard_fixture("condition/user_condition") }
|
||||
|
||||
it "validates valid templates" do
|
||||
expect(
|
||||
|
@ -45,4 +42,52 @@ describe CustomWizard::TemplateValidator do
|
|||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(false)
|
||||
end
|
||||
|
||||
it "invalidates pro step attributes without a pro subscription" do
|
||||
template[:steps][0][:condition] = user_condition['condition']
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(false)
|
||||
end
|
||||
|
||||
it "invalidates pro field attributes without a pro subscription" do
|
||||
template[:steps][0][:fields][0][:condition] = user_condition['condition']
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(false)
|
||||
end
|
||||
|
||||
it "invalidates pro actions without a pro subscription" do
|
||||
template[:actions] << create_category
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(false)
|
||||
end
|
||||
|
||||
context "with pro subscription" do
|
||||
before do
|
||||
enable_pro
|
||||
end
|
||||
|
||||
it "validates pro step attributes" do
|
||||
template[:steps][0][:condition] = user_condition['condition']
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(true)
|
||||
end
|
||||
|
||||
it "validates pro field attributes" do
|
||||
template[:steps][0][:fields][0][:condition] = user_condition['condition']
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(true)
|
||||
end
|
||||
|
||||
it "validates pro actions" do
|
||||
template[:actions] << create_category
|
||||
expect(
|
||||
CustomWizard::TemplateValidator.new(template).perform
|
||||
).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,12 +3,7 @@ require_relative '../../plugin_helper'
|
|||
|
||||
describe CustomWizard::UpdateValidator do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read).with_indifferent_access
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
|
|
|
@ -6,22 +6,8 @@ describe CustomWizard::Wizard do
|
|||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:trusted_user) { Fabricate(:user, trust_level: TrustLevel[3]) }
|
||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||
|
||||
let(:template_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
let(:template_json) { get_wizard_fixture("wizard") }
|
||||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
before do
|
||||
Group.refresh_automatic_group!(:trust_level_3)
|
||||
|
|
|
@ -9,11 +9,8 @@ describe "custom field extensions" do
|
|||
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)
|
||||
}
|
||||
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
|
||||
let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
|
||||
|
||||
before do
|
||||
custom_field_json['custom_fields'].each do |field_json|
|
||||
|
@ -75,43 +72,54 @@ describe "custom field extensions" do
|
|||
end
|
||||
end
|
||||
|
||||
context "category" do
|
||||
it "registers category custom fields" do
|
||||
category
|
||||
expect(Category.get_custom_field_type("category_field_1")).to eq(:json)
|
||||
context "pro custom fields" do
|
||||
before do
|
||||
enable_pro
|
||||
|
||||
pro_custom_field_json['custom_fields'].each do |field_json|
|
||||
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||
custom_field.save
|
||||
end
|
||||
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)
|
||||
context "category" do
|
||||
it "registers" do
|
||||
category
|
||||
expect(Category.get_custom_field_type("category_field_1")).to eq(:json)
|
||||
end
|
||||
|
||||
serializer = BasicCategorySerializer.new(
|
||||
category,
|
||||
scope: Guardian.new(user),
|
||||
root: false
|
||||
).as_json
|
||||
it "adds 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)
|
||||
|
||||
expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json)
|
||||
end
|
||||
end
|
||||
serializer = BasicCategorySerializer.new(
|
||||
category,
|
||||
scope: Guardian.new(user),
|
||||
root: false
|
||||
).as_json
|
||||
|
||||
context "group" do
|
||||
it "registers group custom fields" do
|
||||
group
|
||||
expect(Group.get_custom_field_type("group_field_1")).to eq(:string)
|
||||
expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json)
|
||||
end
|
||||
end
|
||||
|
||||
it "adds group custom fields to the basic group serializer" do
|
||||
group.custom_fields["group_field_1"] = "Hello"
|
||||
group.save_custom_fields(true)
|
||||
context "group" do
|
||||
it "registers" do
|
||||
group
|
||||
expect(Group.get_custom_field_type("group_field_1")).to eq(:string)
|
||||
end
|
||||
|
||||
serializer = BasicGroupSerializer.new(
|
||||
group,
|
||||
scope: Guardian.new(user),
|
||||
root: false
|
||||
).as_json
|
||||
it "adds custom fields to the basic group serializer" do
|
||||
group.custom_fields["group_field_1"] = "Hello"
|
||||
group.save_custom_fields(true)
|
||||
|
||||
expect(serializer[:group_field_1]).to eq("Hello")
|
||||
serializer = BasicGroupSerializer.new(
|
||||
group,
|
||||
scope: Guardian.new(user),
|
||||
root: false
|
||||
).as_json
|
||||
|
||||
expect(serializer[:group_field_1]).to eq("Hello")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,18 +4,8 @@ require_relative '../plugin_helper'
|
|||
describe ExtraLocalesControllerCustomWizard, type: :request do
|
||||
let(:new_user) { Fabricate(:user, trust_level: TrustLevel[0]) }
|
||||
let(:staff_user) { Fabricate(:moderator) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
|
||||
let(:permitted) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read)
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
let(:permitted) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
|
|
|
@ -4,12 +4,7 @@ require_relative '../plugin_helper'
|
|||
describe InvitesControllerCustomWizard, type: :request do
|
||||
fab!(:topic) { Fabricate(:topic) }
|
||||
let(:invite) { Invite.generate(topic.user, email: "angus@mcleod.org", topic: topic) }
|
||||
|
||||
let(:template) do
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
end
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
@controller = InvitesController.new
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
require_relative '../plugin_helper'
|
||||
|
||||
describe CustomWizardUsersController, type: :request do
|
||||
let(:template) do
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
end
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
@controller = UsersController.new
|
||||
|
|
13
spec/fixtures/actions/add_to_group.json
gevendort
Normale Datei
13
spec/fixtures/actions/add_to_group.json
gevendort
Normale Datei
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "action_6",
|
||||
"run_after": "step_1",
|
||||
"type": "add_to_group",
|
||||
"group": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_9",
|
||||
"output_type": "wizard_action",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
}
|
51
spec/fixtures/actions/create_category.json
gevendort
Normale Datei
51
spec/fixtures/actions/create_category.json
gevendort
Normale Datei
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"id": "action_8",
|
||||
"run_after": "step_1",
|
||||
"type": "create_category",
|
||||
"custom_fields": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "category_custom_field",
|
||||
"key_type": "text",
|
||||
"value": "CC Val",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"slug": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "action_9",
|
||||
"key_type": "wizard_action",
|
||||
"value": "2",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
104
spec/fixtures/actions/create_group.json
gevendort
Normale Datei
104
spec/fixtures/actions/create_group.json
gevendort
Normale Datei
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"id": "action_9",
|
||||
"run_after": "step_1",
|
||||
"type": "create_group",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "New Group Member",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"custom_fields": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "group_custom_field",
|
||||
"key_type": "text",
|
||||
"value": "step_3_field_1",
|
||||
"value_type": "wizard_field",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"full_name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"usernames": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"owner_usernames": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus"
|
||||
]
|
||||
}
|
||||
],
|
||||
"grant_trust_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "3",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"mentionable_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "1",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"messageable_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "2",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"visibility_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "3",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"members_visibility_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "99",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
}
|
25
spec/fixtures/actions/send_message.json
gevendort
Normale Datei
25
spec/fixtures/actions/send_message.json
gevendort
Normale Datei
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"id": "action_2",
|
||||
"run_after": "step_2",
|
||||
"type": "send_message",
|
||||
"post_builder": true,
|
||||
"post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "Message title",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"recipient": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
28
spec/fixtures/actions/send_message_multi.json
gevendort
Normale Datei
28
spec/fixtures/actions/send_message_multi.json
gevendort
Normale Datei
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"id": "action_11",
|
||||
"run_after": "step_2",
|
||||
"type": "send_message",
|
||||
"post_builder": true,
|
||||
"post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "Multiple Recipients title",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"recipient": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1",
|
||||
"faiz",
|
||||
"cool_group",
|
||||
"cool_group_1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
16
spec/fixtures/custom_field/custom_fields.json
gevendort
16
spec/fixtures/custom_field/custom_fields.json
gevendort
|
@ -16,22 +16,6 @@
|
|||
"serializers": [
|
||||
"post"
|
||||
]
|
||||
},
|
||||
{
|
||||
"klass": "category",
|
||||
"name": "category_field_1",
|
||||
"type": "json",
|
||||
"serializers": [
|
||||
"basic_category"
|
||||
]
|
||||
},
|
||||
{
|
||||
"klass": "group",
|
||||
"name": "group_field_1",
|
||||
"type": "string",
|
||||
"serializers": [
|
||||
"basic_group"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
28
spec/fixtures/custom_field/pro_custom_fields.json
gevendort
Normale Datei
28
spec/fixtures/custom_field/pro_custom_fields.json
gevendort
Normale Datei
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"custom_fields": [
|
||||
{
|
||||
"klass": "topic",
|
||||
"name": "topic_field_2",
|
||||
"type": "json",
|
||||
"serializers": [
|
||||
"topic_view"
|
||||
]
|
||||
},
|
||||
{
|
||||
"klass": "category",
|
||||
"name": "category_field_1",
|
||||
"type": "json",
|
||||
"serializers": [
|
||||
"basic_category"
|
||||
]
|
||||
},
|
||||
{
|
||||
"klass": "group",
|
||||
"name": "group_field_1",
|
||||
"type": "string",
|
||||
"serializers": [
|
||||
"basic_group"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
285
spec/fixtures/wizard.json
gevendort
285
spec/fixtures/wizard.json
gevendort
|
@ -163,197 +163,6 @@
|
|||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"id": "action_9",
|
||||
"run_after": "step_1",
|
||||
"type": "create_group",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "New Group Member",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"custom_fields": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "group_custom_field",
|
||||
"key_type": "text",
|
||||
"value": "step_3_field_1",
|
||||
"value_type": "wizard_field",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"full_name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"usernames": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"owner_usernames": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus"
|
||||
]
|
||||
}
|
||||
],
|
||||
"grant_trust_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "3",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"mentionable_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "1",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"messageable_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "2",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"visibility_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "3",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"members_visibility_level": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "99",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_6",
|
||||
"run_after": "step_1",
|
||||
"type": "add_to_group",
|
||||
"group": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_9",
|
||||
"output_type": "wizard_action",
|
||||
"output_connector": "set"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_8",
|
||||
"run_after": "step_1",
|
||||
"type": "create_category",
|
||||
"custom_fields": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "category_custom_field",
|
||||
"key_type": "text",
|
||||
"value": "CC Val",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"slug": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "step_1_field_1",
|
||||
"output_type": "wizard_field",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"type": "association",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "action_9",
|
||||
"key_type": "wizard_action",
|
||||
"value": "2",
|
||||
"value_type": "text",
|
||||
"connector": "association"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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_1",
|
||||
"run_after": "step_3",
|
||||
|
@ -442,6 +251,29 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
|
@ -462,59 +294,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_2",
|
||||
"run_after": "step_2",
|
||||
"type": "send_message",
|
||||
"post_builder": true,
|
||||
"post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "Message title",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"recipient": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_11",
|
||||
"run_after": "step_2",
|
||||
"type": "send_message",
|
||||
"post_builder": true,
|
||||
"post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
|
||||
"title": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "Multiple Recipients title",
|
||||
"output_type": "text",
|
||||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"recipient": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output_type": "user",
|
||||
"output_connector": "set",
|
||||
"output": [
|
||||
"angus1",
|
||||
"faiz",
|
||||
"cool_group",
|
||||
"cool_group_1"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action_3",
|
||||
"run_after": "step_2",
|
||||
|
@ -529,24 +308,6 @@
|
|||
"output_connector": "set"
|
||||
}
|
||||
],
|
||||
"category": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"output": "action_8",
|
||||
"output_type": "wizard_action",
|
||||
"output_connector": "set",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "step_2_field_5",
|
||||
"key_type": "wizard_field",
|
||||
"value": "true",
|
||||
"value_type": "text",
|
||||
"connector": "is"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"type": "assignment",
|
||||
|
|
|
@ -7,11 +7,7 @@ describe Jobs::SetAfterTimeWizard do
|
|||
fab!(:user2) { Fabricate(:user) }
|
||||
fab!(:user3) { Fabricate(:user) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read).with_indifferent_access
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
it "sets wizard redirect for all users " do
|
||||
after_time_template = template.dup
|
||||
|
|
11
spec/jobs/update_pro_subscription_spec.rb
Normale Datei
11
spec/jobs/update_pro_subscription_spec.rb
Normale Datei
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../plugin_helper'
|
||||
|
||||
describe CustomWizard::UpdateProSubscription do
|
||||
it "updates the pro subscription" do
|
||||
stub_subscription_request(200, valid_subscription)
|
||||
described_class.new.execute
|
||||
expect(CustomWizard::Pro.subscribed?).to eq(true)
|
||||
end
|
||||
end
|
|
@ -15,3 +15,44 @@ require 'oj'
|
|||
Oj.default_options = Oj.default_options.merge(cache_str: -1)
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
def get_wizard_fixture(path)
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/#{path}.json"
|
||||
).read
|
||||
).with_indifferent_access
|
||||
end
|
||||
|
||||
def authenticate_pro
|
||||
CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true)
|
||||
end
|
||||
|
||||
def enable_pro
|
||||
CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true)
|
||||
end
|
||||
|
||||
def disable_pro
|
||||
CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(false)
|
||||
end
|
||||
|
||||
def valid_subscription
|
||||
{
|
||||
product_id: "prod_CBTNpi3fqWWkq0",
|
||||
price_id: "price_id",
|
||||
price_nickname: "business"
|
||||
}
|
||||
end
|
||||
|
||||
def invalid_subscription
|
||||
{
|
||||
product_id: "prod_CBTNpi3fqWWkq0",
|
||||
price_id: "price_id"
|
||||
}
|
||||
end
|
||||
|
||||
def stub_subscription_request(status, subscription)
|
||||
authenticate_pro
|
||||
pro = CustomWizard::Pro.new
|
||||
stub_request(:get, "https://#{pro.server}/subscription-server/user-subscriptions/#{pro.subscription_type}/#{pro.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json)
|
||||
end
|
||||
|
|
|
@ -3,12 +3,7 @@ require_relative '../../../plugin_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)
|
||||
}
|
||||
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
|
||||
|
||||
before do
|
||||
custom_field_json['custom_fields'].each do |field_json|
|
||||
|
@ -19,7 +14,7 @@ describe CustomWizard::AdminCustomFieldsController do
|
|||
|
||||
it "returns the full list of custom fields" do
|
||||
get "/admin/wizards/custom-fields.json"
|
||||
expect(response.parsed_body.length).to eq(15)
|
||||
expect(response.parsed_body["custom_fields"].length).to be > 12
|
||||
end
|
||||
|
||||
it "saves custom fields" do
|
||||
|
|
|
@ -3,12 +3,7 @@ require_relative '../../../plugin_helper'
|
|||
|
||||
describe CustomWizard::AdminManagerController do
|
||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
sign_in(admin_user)
|
||||
|
|
71
spec/requests/custom_wizard/admin/pro_controller_spec.rb
Normale Datei
71
spec/requests/custom_wizard/admin/pro_controller_spec.rb
Normale Datei
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
require_relative '../../../plugin_helper'
|
||||
|
||||
describe CustomWizard::AdminProController do
|
||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||
|
||||
def generate_payload(request_id, user_id)
|
||||
uri = URI(@pro.authentication_url(user_id, request_id))
|
||||
keys = @pro.authentication.get_keys(request_id)
|
||||
raw_payload = {
|
||||
key: "12345",
|
||||
nonce: keys.nonce,
|
||||
push: false,
|
||||
api: UserApiKeysController::AUTH_API_VERSION
|
||||
}.to_json
|
||||
public_key = OpenSSL::PKey::RSA.new(keys.pem)
|
||||
Base64.encode64(public_key.public_encrypt(raw_payload))
|
||||
end
|
||||
|
||||
before do
|
||||
@pro = CustomWizard::Pro.new
|
||||
sign_in(admin_user)
|
||||
end
|
||||
|
||||
it "#index" do
|
||||
get "/admin/wizards/pro.json"
|
||||
expect(response.parsed_body['server']).to eq(@pro.server)
|
||||
expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::ProAuthenticationSerializer.new(@pro.authentication, root: false).as_json)
|
||||
expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::ProSubscriptionSerializer.new(@pro.subscription, root: false).as_json)
|
||||
end
|
||||
|
||||
it "#authorize" do
|
||||
get "/admin/wizards/pro/authorize"
|
||||
expect(response.status).to eq(302)
|
||||
expect(cookies[:user_api_request_id].present?).to eq(true)
|
||||
end
|
||||
|
||||
it "#destroy_authentication" do
|
||||
request_id = SecureRandom.hex(32)
|
||||
payload = generate_payload(request_id, admin_user.id)
|
||||
@pro.authentication_response(request_id, payload)
|
||||
|
||||
delete "/admin/wizards/pro/authorize.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(CustomWizard::Pro.authorized?).to eq(false)
|
||||
end
|
||||
|
||||
context "subscription" do
|
||||
before do
|
||||
stub_subscription_request(200, valid_subscription)
|
||||
end
|
||||
|
||||
it "handles authentication response and the updates subscription" do
|
||||
request_id = cookies[:user_api_request_id] = SecureRandom.hex(32)
|
||||
payload = generate_payload(request_id, admin_user.id)
|
||||
get "/admin/wizards/pro/authorize/callback", params: { payload: payload }
|
||||
|
||||
expect(response).to redirect_to("/admin/wizards/pro")
|
||||
expect(CustomWizard::Pro.subscribed?).to eq(true)
|
||||
end
|
||||
|
||||
it "updates the subscription" do
|
||||
authenticate_pro
|
||||
post "/admin/wizards/pro/subscription.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(CustomWizard::Pro.subscribed?).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,12 +7,7 @@ describe CustomWizard::AdminSubmissionsController do
|
|||
fab!(:user2) { Fabricate(:user) }
|
||||
fab!(:user3) { Fabricate(:user) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
let(:template_2) {
|
||||
temp = template.dup
|
||||
temp["id"] = "super_mega_fun_wizard_2"
|
||||
|
|
|
@ -5,12 +5,7 @@ describe CustomWizard::AdminWizardController do
|
|||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||
fab!(:user1) { Fabricate(:user) }
|
||||
fab!(:user2) { Fabricate(:user) }
|
||||
|
||||
let(:template) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
|
|
|
@ -2,21 +2,11 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe ApplicationController do
|
||||
fab!(:user) {
|
||||
Fabricate(
|
||||
:user,
|
||||
username: 'angus',
|
||||
email: "angus@email.com",
|
||||
trust_level: TrustLevel[3]
|
||||
)
|
||||
}
|
||||
fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
|
||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
|
|
|
@ -8,12 +8,8 @@ describe "custom field extensions" do
|
|||
let!(:category) { Fabricate(:category) }
|
||||
let!(:user) { Fabricate(:user) }
|
||||
let!(:group) { Fabricate(:group, users: [user]) }
|
||||
|
||||
let(:custom_field_json) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
|
||||
).read)
|
||||
}
|
||||
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
|
||||
let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
|
||||
|
||||
before do
|
||||
custom_field_json['custom_fields'].each do |field_json|
|
||||
|
@ -32,27 +28,6 @@ describe "custom field extensions" do
|
|||
expect(response.parsed_body["topic_field_1"]).to eq(true)
|
||||
end
|
||||
|
||||
it "adds category custom fields to the show categories response" do
|
||||
category.custom_fields["category_field_1"] = { a: 1, b: 2 }
|
||||
category.save_custom_fields(true)
|
||||
|
||||
get "/c/#{category.id}/show.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
|
||||
end
|
||||
|
||||
it "adds group custom fields to the show group response" do
|
||||
group.custom_fields["group_field_1"] = "Group cf entry"
|
||||
group.save_custom_fields(true)
|
||||
|
||||
sign_in(user)
|
||||
get "/groups/#{group.name}.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry")
|
||||
end
|
||||
|
||||
it "adds post custom fields to the show post response" do
|
||||
post.custom_fields["post_field_1"] = 7
|
||||
post.save_custom_fields(true)
|
||||
|
@ -63,32 +38,64 @@ describe "custom field extensions" do
|
|||
expect(response.parsed_body['post_field_1']).to eq(7)
|
||||
end
|
||||
|
||||
context "preloaded" do
|
||||
it "preloads category custom fields on site categories" do
|
||||
Site.preloaded_category_custom_fields << "other_field"
|
||||
context "with a pro subscription" do
|
||||
before do
|
||||
enable_pro
|
||||
|
||||
pro_custom_field_json['custom_fields'].each do |field_json|
|
||||
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||
custom_field.save
|
||||
end
|
||||
end
|
||||
|
||||
it "adds category custom fields to the show categories response" do
|
||||
category.custom_fields["category_field_1"] = { a: 1, b: 2 }
|
||||
category.save_custom_fields(true)
|
||||
|
||||
get "/site.json"
|
||||
expect(response.status).to eq(200)
|
||||
get "/c/#{category.id}/show.json"
|
||||
|
||||
site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first
|
||||
expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
|
||||
end
|
||||
|
||||
it "preloads group custom fields on group index" do
|
||||
Group.preloaded_custom_field_names << "other_field"
|
||||
|
||||
group = Fabricate(:group)
|
||||
it "adds group custom fields to the show group response" do
|
||||
group.custom_fields["group_field_1"] = "Group cf entry"
|
||||
group.save_custom_fields(true)
|
||||
|
||||
get "/groups.json"
|
||||
expect(response.status).to eq(200)
|
||||
sign_in(user)
|
||||
get "/groups/#{group.name}.json"
|
||||
|
||||
group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first
|
||||
expect(group['group_field_1']).to eq("Group cf entry")
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry")
|
||||
end
|
||||
|
||||
context "preloaded" do
|
||||
it "preloads category custom fields on site categories" do
|
||||
Site.preloaded_category_custom_fields << "other_field"
|
||||
|
||||
category.custom_fields["category_field_1"] = { a: 1, b: 2 }
|
||||
category.save_custom_fields(true)
|
||||
|
||||
get "/site.json"
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first
|
||||
expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
|
||||
end
|
||||
|
||||
it "preloads group custom fields on group index" do
|
||||
Group.preloaded_custom_field_names << "other_field"
|
||||
|
||||
group = Fabricate(:group)
|
||||
group.custom_fields["group_field_1"] = "Group cf entry"
|
||||
group.save_custom_fields(true)
|
||||
|
||||
get "/groups.json"
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first
|
||||
expect(group['group_field_1']).to eq("Group cf entry")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,55 +2,12 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::StepsController do
|
||||
fab!(:user) {
|
||||
Fabricate(
|
||||
:user,
|
||||
username: 'angus',
|
||||
email: "angus@email.com",
|
||||
trust_level: TrustLevel[3]
|
||||
)
|
||||
}
|
||||
|
||||
fab!(:user2) {
|
||||
Fabricate(
|
||||
:user,
|
||||
username: 'bob',
|
||||
email: "bob@email.com",
|
||||
trust_level: TrustLevel[2]
|
||||
)
|
||||
}
|
||||
|
||||
let(:wizard_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:wizard_field_condition_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/wizard_field_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:user_condition_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
|
||||
fab!(:user2) { Fabricate(:user, username: 'bob', email: "bob@email.com", trust_level: TrustLevel[2]) }
|
||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||
let(:wizard_field_condition_template) { get_wizard_fixture("condition/wizard_field_condition") }
|
||||
let(:user_condition_template) { get_wizard_fixture("condition/user_condition") }
|
||||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
|
@ -90,17 +47,6 @@ describe CustomWizard::StepsController do
|
|||
put '/w/super-mega-fun-wizard/steps/step_10.json'
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "when user cant see the step due to conditions" do
|
||||
sign_in(user2)
|
||||
|
||||
new_wizard_template = wizard_template.dup
|
||||
new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
it "works if the step has no fields" do
|
||||
|
@ -123,64 +69,40 @@ describe CustomWizard::StepsController do
|
|||
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||
end
|
||||
|
||||
it "returns an updated wizard when condition doesnt pass" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition wont pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_3")
|
||||
end
|
||||
|
||||
it "runs completion actions if user has completed wizard" do
|
||||
new_template = wizard_template.dup
|
||||
|
||||
## route_to action
|
||||
new_template['actions'].last['run_after'] = 'wizard_completion'
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition wont pass"
|
||||
}
|
||||
}
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com")
|
||||
end
|
||||
|
||||
it "saves results of completion actions if user has completed wizard" do
|
||||
new_template = wizard_template.dup
|
||||
|
||||
## Create group action
|
||||
new_template['actions'].first['run_after'] = 'wizard_completion'
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "My cool group"
|
||||
step_1_field_1: "Topic title",
|
||||
step_1_field_2: "Topic post"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
wizard_id = response.parsed_body['wizard']['id']
|
||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||
|
||||
group_name = wizard.submissions.first.fields['action_9']
|
||||
group = Group.find_by(name: group_name)
|
||||
|
||||
expect(group.present?).to eq(true)
|
||||
expect(group.full_name).to eq("My cool group")
|
||||
topic_id = wizard.submissions.first.fields[new_template['actions'].first['id']]
|
||||
topic = Topic.find(topic_id)
|
||||
expect(topic.present?).to eq(true)
|
||||
end
|
||||
|
||||
it "returns a final step without conditions" do
|
||||
|
@ -197,88 +119,119 @@ describe CustomWizard::StepsController do
|
|||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
it "returns the correct final step when the conditional final step and last step are the same" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
context "pro" do
|
||||
before do
|
||||
enable_pro
|
||||
end
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
it "raises an error when user cant see the step due to conditions" do
|
||||
sign_in(user2)
|
||||
|
||||
new_wizard_template = wizard_template.dup
|
||||
new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it "returns an updated wizard when condition doesnt pass" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition wont pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_3")
|
||||
end
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
it "returns the correct final step when the conditional final step and last step are the same" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
it "returns the correct final step when the conditional final step and last step are different" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
it "returns the correct final step when the conditional final step is determined in the same action" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
it "returns the correct final step when the conditional final step and last step are different" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
it "excludes the non-included conditional fields from the submissions" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
it "returns the correct final step when the conditional final step is determined in the same action" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
|
||||
fields: {
|
||||
step_2_field_1: "1995-04-23"
|
||||
it "excludes the non-included conditional fields from the submissions" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
|
||||
fields: {
|
||||
step_2_field_1: "1995-04-23"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wizard_id = response.parsed_body['wizard']['id']
|
||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||
submission = wizard.current_submission
|
||||
expect(submission.fields.keys).not_to include("step_2_field_1")
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
}
|
||||
}
|
||||
|
||||
wizard_id = response.parsed_body['wizard']['id']
|
||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||
submission = wizard.current_submission
|
||||
expect(submission.fields.keys).not_to include("step_2_field_1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,29 +2,12 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::WizardController do
|
||||
fab!(:user) {
|
||||
Fabricate(
|
||||
:user,
|
||||
username: 'angus',
|
||||
email: "angus@email.com",
|
||||
trust_level: TrustLevel[3]
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
|
||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find("super_mega_fun_wizard")
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
|
@ -4,13 +4,10 @@ require_relative '../../plugin_helper'
|
|||
|
||||
describe CustomWizard::BasicWizardSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
it 'should return basic wizard attributes' do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
json = CustomWizard::BasicWizardSerializer.new(
|
||||
CustomWizard::Builder.new("super_mega_fun_wizard", user).build,
|
||||
scope: Guardian.new(user)
|
||||
|
|
|
@ -4,12 +4,7 @@ require_relative '../../plugin_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)
|
||||
}
|
||||
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
|
||||
|
||||
it 'should return custom field attributes' do
|
||||
custom_field_json['custom_fields'].each do |field_json|
|
||||
|
|
17
spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb
Normale Datei
17
spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb
Normale Datei
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../../plugin_helper'
|
||||
|
||||
describe CustomWizard::ProAuthenticationSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
it 'should return pro authentication attributes' do
|
||||
auth = CustomWizard::ProAuthentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id))
|
||||
serialized = described_class.new(auth, root: false).as_json
|
||||
|
||||
expect(serialized[:active]).to eq(true)
|
||||
expect(serialized[:client_id]).to eq(auth.client_id)
|
||||
expect(serialized[:auth_by]).to eq(auth.auth_by)
|
||||
expect(serialized[:auth_at]).to eq(auth.auth_at)
|
||||
end
|
||||
end
|
14
spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb
Normale Datei
14
spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb
Normale Datei
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../../plugin_helper'
|
||||
|
||||
describe CustomWizard::ProSubscriptionSerializer do
|
||||
it 'should return pro subscription attributes' do
|
||||
sub = CustomWizard::ProSubscription.new(OpenStruct.new(type: 'community', updated_at: Time.now))
|
||||
serialized = described_class.new(sub, root: false).as_json
|
||||
|
||||
expect(serialized[:active]).to eq(true)
|
||||
expect(serialized[:type]).to eq('community')
|
||||
expect(serialized[:updated_at]).to eq(sub.updated_at)
|
||||
end
|
||||
end
|
14
spec/serializers/custom_wizard/pro_serializer_spec.rb
Normale Datei
14
spec/serializers/custom_wizard/pro_serializer_spec.rb
Normale Datei
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::ProSerializer do
|
||||
it 'should return pro attributes' do
|
||||
pro = CustomWizard::Pro.new
|
||||
serialized = described_class.new(pro, root: false)
|
||||
|
||||
expect(serialized.server).to eq(pro.server)
|
||||
expect(serialized.authentication.class).to eq(CustomWizard::ProAuthentication)
|
||||
expect(serialized.subscription.class).to eq(CustomWizard::ProSubscription)
|
||||
end
|
||||
end
|
|
@ -4,13 +4,10 @@ require_relative '../../plugin_helper'
|
|||
|
||||
describe CustomWizard::FieldSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
||||
end
|
||||
|
||||
|
|
|
@ -5,19 +5,11 @@ require_relative '../../plugin_helper'
|
|||
describe CustomWizard::WizardSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:category) { Fabricate(:category) }
|
||||
|
||||
let(:similar_topics_validation) {
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/validation/similar_topics.json"
|
||||
).read)
|
||||
}
|
||||
let(:template) { get_wizard_fixture("wizard") }
|
||||
let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") }
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
|
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden …
In neuem Issue referenzieren