Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-25 18:50:27 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
ae87e383d2
Commit
d8fd5cb258
24 geänderte Dateien mit 384 neuen und 324 gelöschten Zeilen
|
@ -21,17 +21,6 @@ export default Component.extend({
|
||||||
publicTopicFields: or('createTopic', 'openComposer'),
|
publicTopicFields: or('createTopic', 'openComposer'),
|
||||||
showSkipRedirect: or('createTopic', 'sendMessage'),
|
showSkipRedirect: or('createTopic', 'sendMessage'),
|
||||||
|
|
||||||
@observes('action.type')
|
|
||||||
setupDefaults() {
|
|
||||||
const defaultProperties = schema.action.types[this.action.type];
|
|
||||||
|
|
||||||
Object.keys(defaultProperties).forEach(property => {
|
|
||||||
if (defaultProperties[property]) {
|
|
||||||
this.set(`action.${property}`, defaultProperties[property]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed('wizard.steps')
|
@discourseComputed('wizard.steps')
|
||||||
runAfterContent(steps) {
|
runAfterContent(steps) {
|
||||||
let content = steps.map(function(step) {
|
let content = steps.map(function(step) {
|
||||||
|
|
|
@ -19,17 +19,6 @@ export default Component.extend({
|
||||||
showMinLength: or('isText', 'isTextarea', 'isUrl', 'isComposer'),
|
showMinLength: or('isText', 'isTextarea', 'isUrl', 'isComposer'),
|
||||||
categoryPropertyTypes: selectKitContent(['id', 'slug']),
|
categoryPropertyTypes: selectKitContent(['id', 'slug']),
|
||||||
|
|
||||||
@observes('field.type')
|
|
||||||
setupDefaults() {
|
|
||||||
const defaultProperties = schema.field.types[this.field.type];
|
|
||||||
|
|
||||||
Object.keys(defaultProperties).forEach(property => {
|
|
||||||
if (defaultProperties[property]) {
|
|
||||||
this.set(`field.${property}`, defaultProperties[property]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes('field.type')
|
@observes('field.type')
|
||||||
clearMapped() {
|
clearMapped() {
|
||||||
schema.field.mapped.forEach(property => {
|
schema.field.mapped.forEach(property => {
|
||||||
|
|
|
@ -18,10 +18,10 @@ export default Component.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
applySortable() {
|
applySortable() {
|
||||||
$(this.element).find("ul").sortable({tolerance: 'pointer'}).on('sortupdate', (e, ui) => {
|
$(this.element).find("ul")
|
||||||
const itemId = ui.item.data('id');
|
.sortable({ tolerance: 'pointer' })
|
||||||
const index = ui.item.index();
|
.on('sortupdate', (e, ui) => {
|
||||||
bind(this, this.updateItemOrder(itemId, index));
|
this.updateItemOrder(ui.item.data('id'), ui.item.index());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export default Component.extend({
|
||||||
label = generateName(item.type);
|
label = generateName(item.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
link.label = label;
|
link.label = `${label} (${item.id})`;
|
||||||
|
|
||||||
let classes = 'btn';
|
let classes = 'btn';
|
||||||
if (current && item.id === current.id) {
|
if (current && item.id === current.id) {
|
||||||
|
@ -78,21 +78,29 @@ export default Component.extend({
|
||||||
add() {
|
add() {
|
||||||
const items = this.items;
|
const items = this.items;
|
||||||
const itemType = this.itemType;
|
const itemType = this.itemType;
|
||||||
|
let next = 1;
|
||||||
|
|
||||||
|
if (items.length) {
|
||||||
|
next = Math.max.apply(Math, items.map((i) => (i.id.split('_')[1]))) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
id: `${itemType}_${items.length + 1}`,
|
id: `${itemType}_${next}`,
|
||||||
isNew: true
|
isNew: true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (schema[itemType].objectArrays) {
|
let objectArrays = schema[itemType].objectArrays;
|
||||||
Object.keys(schema[itemType].objectArrays).forEach(objectType => {
|
if (objectArrays) {
|
||||||
|
Object.keys(objectArrays).forEach(objectType => {
|
||||||
params[objectArrays[objectType].property] = A();
|
params[objectArrays[objectType].property] = A();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
params = this.setDefaults(schema[itemType].basic, params);
|
params = this.setDefaults(schema[itemType].basic, params);
|
||||||
if (schema[itemType].types) {
|
|
||||||
params = this.setDefaults(schema[itemType].types[params.type], params);
|
let types = schema[itemType].types;
|
||||||
|
if (types && params.type) {
|
||||||
|
params = this.setDefaults(types[params.type], params);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newItem = EmberObject.create(params);
|
const newItem = EmberObject.create(params);
|
||||||
|
@ -107,8 +115,26 @@ export default Component.extend({
|
||||||
|
|
||||||
remove(itemId) {
|
remove(itemId) {
|
||||||
const items = this.items;
|
const items = this.items;
|
||||||
items.removeObject(items.findBy('id', itemId));
|
let item;
|
||||||
this.set('current', items[items.length - 1]);
|
let index;
|
||||||
|
|
||||||
|
items.forEach((it, ind) => {
|
||||||
|
if (it.id === itemId) {
|
||||||
|
item = it;
|
||||||
|
index = ind;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let nextIndex;
|
||||||
|
if (this.current.id === itemId) {
|
||||||
|
nextIndex = index < (items.length-2) ? (index+1) : (index-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.removeObject(item);
|
||||||
|
|
||||||
|
if (nextIndex) {
|
||||||
|
this.set('current', items[nextIndex]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { gt } from '@ember/object/computed';
|
import { gt } from '@ember/object/computed';
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
|
import { removeMapperClasses } from '../lib/wizard-mapper';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'],
|
classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'],
|
||||||
|
@ -9,5 +10,11 @@ export default Component.extend({
|
||||||
let key = this.connector;
|
let key = this.connector;
|
||||||
let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`;
|
let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`;
|
||||||
return I18n.t(`admin.wizard.${path}`);
|
return I18n.t(`admin.wizard.${path}`);
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
onOpen() {
|
||||||
|
removeMapperClasses(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
|
@ -2,13 +2,13 @@ import { alias, or, gt } from "@ember/object/computed";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
import { default as discourseComputed, observes, on } from "discourse-common/utils/decorators";
|
import { default as discourseComputed, observes, on } from "discourse-common/utils/decorators";
|
||||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||||
import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper';
|
import { defaultSelectionType, selectionTypes, removeMapperClasses } from '../lib/wizard-mapper';
|
||||||
import { snakeCase } from '../lib/wizard';
|
import { snakeCase } from '../lib/wizard';
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { bind } from "@ember/runloop";
|
import { bind } from "@ember/runloop";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: 'mapper-selector',
|
classNameBindings: [':mapper-selector', 'activeType'],
|
||||||
groups: alias('site.groups'),
|
groups: alias('site.groups'),
|
||||||
categories: alias('site.categories'),
|
categories: alias('site.categories'),
|
||||||
showText: computed('activeType', function() { return this.showInput('text') }),
|
showText: computed('activeType', function() { return this.showInput('text') }),
|
||||||
|
@ -30,7 +30,6 @@ export default Component.extend({
|
||||||
userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
|
userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
|
||||||
listEnabled: computed('options.listSelection', 'inputType', function() { return this.optionEnabled('listSelection') }),
|
listEnabled: computed('options.listSelection', 'inputType', function() { return this.optionEnabled('listSelection') }),
|
||||||
hasTypes: gt('selectorTypes.length', 1),
|
hasTypes: gt('selectorTypes.length', 1),
|
||||||
showTypes: false,
|
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
$(document).on("click", bind(this, this.documentClick));
|
$(document).on("click", bind(this, this.documentClick));
|
||||||
|
@ -41,14 +40,16 @@ export default Component.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
documentClick(e) {
|
documentClick(e) {
|
||||||
let $element = $(this.element);
|
if (this._state == "destroying") return;
|
||||||
|
|
||||||
let $target = $(e.target);
|
let $target = $(e.target);
|
||||||
|
|
||||||
if (!$target.hasClass('type-selector-icon') &&
|
if (!$target.parents('.wizard-mapper .input').length) {
|
||||||
$target.closest($element).length < 1 &&
|
this.send('disableActive');
|
||||||
this._state !== "destroying") {
|
}
|
||||||
|
|
||||||
this.set("showTypes", false);
|
if (!$target.parents('.type-selector').length) {
|
||||||
|
this.send('hideTypes');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -147,14 +148,34 @@ export default Component.extend({
|
||||||
return this.activeType === type && this[`${type}Enabled`];
|
return this.activeType === type && this[`${type}Enabled`];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
removeClasses() {
|
||||||
|
removeMapperClasses(this);
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
toggleType(type) {
|
toggleType(type) {
|
||||||
this.set('activeType', type);
|
this.set('activeType', type);
|
||||||
this.set('showTypes', false);
|
this.send('hideTypes');
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleTypes() {
|
// jquery is used here to ensure other selectors and types disable properly
|
||||||
this.toggleProperty('showTypes')
|
|
||||||
|
showTypes() {
|
||||||
|
this.removeClasses();
|
||||||
|
$(this.element).find('.selector-types').addClass('show');
|
||||||
|
},
|
||||||
|
|
||||||
|
hideTypes() {
|
||||||
|
$(this.element).find('.selector-types').removeClass('show');
|
||||||
|
},
|
||||||
|
|
||||||
|
enableActive() {
|
||||||
|
this.removeClasses();
|
||||||
|
$(this.element).addClass('active');
|
||||||
|
},
|
||||||
|
|
||||||
|
disableActive() {
|
||||||
|
$(this.element).removeClass('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -7,6 +7,7 @@ import { dasherize } from "@ember/string";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import { scheduleOnce, later } from "@ember/runloop";
|
import { scheduleOnce, later } from "@ember/runloop";
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
|
import copyText from "discourse/lib/copy-text";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
hasName: notEmpty('model.name'),
|
hasName: notEmpty('model.name'),
|
||||||
|
@ -58,7 +59,7 @@ export default Controller.extend({
|
||||||
let stepFields = s.fields.map((f) => {
|
let stepFields = s.fields.map((f) => {
|
||||||
return EmberObject.create({
|
return EmberObject.create({
|
||||||
id: f.id,
|
id: f.id,
|
||||||
label: `${f.label} (${s.id})`,
|
label: `${f.label} (${s.id}, ${f.id})`,
|
||||||
type: f.type
|
type: f.type
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -95,13 +96,15 @@ export default Controller.extend({
|
||||||
}).catch((result) => {
|
}).catch((result) => {
|
||||||
this.set('saving', false);
|
this.set('saving', false);
|
||||||
|
|
||||||
let error = true;
|
let errorType = 'failed';
|
||||||
|
let errorParams = {};
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
let type = result.error.type;
|
errorType = result.error.type;
|
||||||
let params = result.error.params || {};
|
errorParams = result.error.params;
|
||||||
error = I18n.t(`admin.wizard.error.${type}`, params);
|
|
||||||
}
|
}
|
||||||
this.set('error', error);
|
|
||||||
|
this.set('error', I18n.t(`admin.wizard.error.${errorType}`, errorParams));
|
||||||
|
|
||||||
later(() => this.set('error', null), 10000);
|
later(() => this.set('error', null), 10000);
|
||||||
});
|
});
|
||||||
|
@ -127,6 +130,20 @@ export default Controller.extend({
|
||||||
|
|
||||||
toggleAdvanced() {
|
toggleAdvanced() {
|
||||||
this.toggleProperty('model.showAdvanced');
|
this.toggleProperty('model.showAdvanced');
|
||||||
|
},
|
||||||
|
|
||||||
|
copyUrl() {
|
||||||
|
const $copyRange = $('<p id="copy-range"></p>');
|
||||||
|
$copyRange.html(this.wizardUrl);
|
||||||
|
|
||||||
|
$(document.body).append($copyRange);
|
||||||
|
|
||||||
|
if (copyText(this.wizardUrl, $copyRange[0])) {
|
||||||
|
this.set("copiedUrl", true);
|
||||||
|
later(() => this.set("copiedUrl", false), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$copyRange.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -134,7 +134,8 @@ function actionPatch(json) {
|
||||||
|
|
||||||
function buildProperties(json) {
|
function buildProperties(json) {
|
||||||
let props = {
|
let props = {
|
||||||
steps: A()
|
steps: A(),
|
||||||
|
actions: A()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (present(json)) {
|
if (present(json)) {
|
||||||
|
|
|
@ -22,6 +22,12 @@ function inputTypesContent(options = {}) {
|
||||||
mapInputTypes(selectableInputTypes);
|
mapInputTypes(selectableInputTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeMapperClasses(ctx) {
|
||||||
|
const $mapper = $(ctx.element).parents('.wizard-mapper');
|
||||||
|
$mapper.find('.selector-types').removeClass('show');
|
||||||
|
$mapper.find('.mapper-selector').removeClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
// Connectors
|
// Connectors
|
||||||
|
|
||||||
const connectors = {
|
const connectors = {
|
||||||
|
@ -154,6 +160,7 @@ export {
|
||||||
defaultInputType,
|
defaultInputType,
|
||||||
defaultSelectionType,
|
defaultSelectionType,
|
||||||
defaultConnector,
|
defaultConnector,
|
||||||
|
removeMapperClasses,
|
||||||
connectorContent,
|
connectorContent,
|
||||||
inputTypesContent,
|
inputTypesContent,
|
||||||
selectionTypes,
|
selectionTypes,
|
||||||
|
|
|
@ -126,7 +126,7 @@ const fieldProperties = {
|
||||||
description: null,
|
description: null,
|
||||||
required: null,
|
required: null,
|
||||||
key: null,
|
key: null,
|
||||||
type: 'text'
|
type: null
|
||||||
},
|
},
|
||||||
types: {
|
types: {
|
||||||
text: {
|
text: {
|
||||||
|
@ -138,15 +138,15 @@ const fieldProperties = {
|
||||||
composer: {
|
composer: {
|
||||||
min_length: null
|
min_length: null
|
||||||
},
|
},
|
||||||
|
text_only: {
|
||||||
|
},
|
||||||
number: {
|
number: {
|
||||||
},
|
},
|
||||||
|
checkbox: {
|
||||||
|
},
|
||||||
url: {
|
url: {
|
||||||
min_length: null
|
min_length: null
|
||||||
},
|
},
|
||||||
'text-only': {
|
|
||||||
},
|
|
||||||
'user-selector': {
|
|
||||||
},
|
|
||||||
upload: {
|
upload: {
|
||||||
file_types: '.jpg,.png'
|
file_types: '.jpg,.png'
|
||||||
},
|
},
|
||||||
|
@ -168,6 +168,8 @@ const fieldProperties = {
|
||||||
group: {
|
group: {
|
||||||
prefill: null,
|
prefill: null,
|
||||||
content: null
|
content: null
|
||||||
|
},
|
||||||
|
user_selector: {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mapped: [
|
mapped: [
|
||||||
|
@ -193,7 +195,7 @@ const actionProperties = {
|
||||||
basic: {
|
basic: {
|
||||||
id: null,
|
id: null,
|
||||||
run_after: 'wizard_completion',
|
run_after: 'wizard_completion',
|
||||||
type: 'create_topic'
|
type: null
|
||||||
},
|
},
|
||||||
types: {
|
types: {
|
||||||
create_topic: {
|
create_topic: {
|
||||||
|
|
|
@ -44,7 +44,7 @@ const CustomWizard = EmberObject.extend({
|
||||||
for (let property of listProperties(type, objectType)) {
|
for (let property of listProperties(type, objectType)) {
|
||||||
let value = object.get(property);
|
let value = object.get(property);
|
||||||
|
|
||||||
result = this.validateValue(property, value, type, result);
|
result = this.validateValue(property, value, object, type, result);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
break;
|
break;
|
||||||
|
@ -90,7 +90,7 @@ const CustomWizard = EmberObject.extend({
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
validateValue(property, value, type, result) {
|
validateValue(property, value, object, type, result) {
|
||||||
if (schema[type].required.indexOf(property) > -1 && !value) {
|
if (schema[type].required.indexOf(property) > -1 && !value) {
|
||||||
result.error = {
|
result.error = {
|
||||||
type: 'required',
|
type: 'required',
|
||||||
|
@ -130,6 +130,10 @@ const CustomWizard = EmberObject.extend({
|
||||||
type: inpt.type,
|
type: inpt.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (inpt.connector) {
|
||||||
|
input.connector = inpt.connector;
|
||||||
|
}
|
||||||
|
|
||||||
if (present(inpt.output)) {
|
if (present(inpt.output)) {
|
||||||
input.output = inpt.output;
|
input.output = inpt.output;
|
||||||
input.output_type = snakeCase(inpt.output_type);
|
input.output_type = snakeCase(inpt.output_type);
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
<div class="wizard-url">
|
<div class="wizard-url">
|
||||||
{{#if model.name}}
|
{{#if model.name}}
|
||||||
<a href="{{wizardUrl}}" target="_blank">{{wizardUrl}}</a>
|
<a href="{{wizardUrl}}" target="_blank">{{wizardUrl}}</a>
|
||||||
|
{{#if copiedUrl}}
|
||||||
|
{{d-button class="btn-hover pull-right" icon="copy" label="ip_lookup.copied"}}
|
||||||
|
{{else}}
|
||||||
|
{{d-button action=(action "copyUrl") class="pull-right no-text" icon="copy"}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<div class="wizard-header medium">{{{i18n header}}}</div>
|
<div class="wizard-header medium">{{{i18n header}}}</div>
|
||||||
|
|
||||||
|
<div class="link-list">
|
||||||
{{#if anyLinks}}
|
{{#if anyLinks}}
|
||||||
<ul>
|
|
||||||
{{#each links as |l|}}
|
{{#each links as |l|}}
|
||||||
<li data-id='{{l.id}}'>
|
<div data-id='{{l.id}}'>
|
||||||
{{d-button action="change" actionParam=l.id translatedLabel=l.label class=l.classes}}
|
{{d-button action="change" actionParam=l.id translatedLabel=l.label class=l.classes}}
|
||||||
{{d-button action='remove' actionParam=l.id icon='times' class='remove'}}
|
{{d-button action='remove' actionParam=l.id icon='times' class='remove'}}
|
||||||
</li>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
|
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
{{combo-box
|
{{combo-box
|
||||||
value=connector
|
value=connector
|
||||||
content=connectors
|
content=connectors
|
||||||
onChange=(action (mut connector))}}
|
onChange=(action (mut connector))
|
||||||
|
onOpen=(action "onOpen")}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="connector-single">
|
<span class="connector-single">
|
||||||
{{connectorLabel}}
|
{{connectorLabel}}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<div class="type-selector">
|
<div class="type-selector">
|
||||||
{{#if hasTypes}}
|
{{#if hasTypes}}
|
||||||
<a {{action "toggleTypes"}} class="active">
|
<a {{action "showTypes"}} class="active">
|
||||||
{{activeTypeLabel}}
|
{{activeTypeLabel}}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{{#if showTypes}}
|
|
||||||
<div class="selector-types">
|
<div class="selector-types">
|
||||||
{{#each selectorTypes as |item|}}
|
{{#each selectorTypes as |item|}}
|
||||||
{{wizard-mapper-selector-type
|
{{wizard-mapper-selector-type
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
toggle=(action 'toggleType')}}
|
toggle=(action 'toggleType')}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<span>{{activeTypeLabel}}</span>
|
<span>{{activeTypeLabel}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -24,6 +22,7 @@
|
||||||
{{input
|
{{input
|
||||||
type="text"
|
type="text"
|
||||||
value=value
|
value=value
|
||||||
|
click=(action 'enableActive')
|
||||||
placeholder=(i18n placeholderKey)}}
|
placeholder=(i18n placeholderKey)}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -32,6 +31,8 @@
|
||||||
value=value
|
value=value
|
||||||
content=comboBoxContent
|
content=comboBoxContent
|
||||||
onChange=(action (mut value))
|
onChange=(action (mut value))
|
||||||
|
onOpen=(action "enableActive")
|
||||||
|
onClick=(action 'enableActive')
|
||||||
options=(hash
|
options=(hash
|
||||||
none=placeholderKey
|
none=placeholderKey
|
||||||
)}}
|
)}}
|
||||||
|
@ -42,6 +43,8 @@
|
||||||
content=multiSelectContent
|
content=multiSelectContent
|
||||||
value=value
|
value=value
|
||||||
onChange=(action (mut value))
|
onChange=(action (mut value))
|
||||||
|
onOpen=(action "enableActive")
|
||||||
|
onClose=(action "disableActive")
|
||||||
options=multiSelectOptions}}
|
options=multiSelectOptions}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -55,6 +58,8 @@
|
||||||
{{tag-chooser
|
{{tag-chooser
|
||||||
tags=value
|
tags=value
|
||||||
filterable=true
|
filterable=true
|
||||||
|
onOpen=(action "enableActive")
|
||||||
|
onClose=(action "disableActive")
|
||||||
options=(hash
|
options=(hash
|
||||||
none=placeholderKey
|
none=placeholderKey
|
||||||
)}}
|
)}}
|
||||||
|
@ -65,6 +70,7 @@
|
||||||
includeMessageableGroups='true'
|
includeMessageableGroups='true'
|
||||||
placeholderKey=placeholderKey
|
placeholderKey=placeholderKey
|
||||||
usernames=value
|
usernames=value
|
||||||
autocomplete="discourse"}}
|
autocomplete="discourse"
|
||||||
|
click=(action "enableActive")}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
|
@ -161,7 +161,7 @@ export default {
|
||||||
const fields = {};
|
const fields = {};
|
||||||
|
|
||||||
this.get('fields').forEach(f => {
|
this.get('fields').forEach(f => {
|
||||||
if (f.type !== 'text-only') {
|
if (f.type !== 'text_only') {
|
||||||
fields[f.id] = f.value;
|
fields[f.id] = f.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -218,8 +218,8 @@ export default {
|
||||||
inputComponentName: function() {
|
inputComponentName: function() {
|
||||||
const type = this.get('field.type');
|
const type = this.get('field.type');
|
||||||
const id = this.get('field.id');
|
const id = this.get('field.id');
|
||||||
if (['text-only'].includes(type)) return false;
|
if (['text_only'].includes(type)) return false;
|
||||||
return (type === 'component') ? dasherize(id) : `wizard-field-${type}`;
|
return dasherize((type === 'component') ? id : `wizard-field-${type}`);
|
||||||
}.property('field.type', 'field.id')
|
}.property('field.type', 'field.id')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -230,8 +230,8 @@ export default {
|
||||||
'dropdown',
|
'dropdown',
|
||||||
'tag',
|
'tag',
|
||||||
'image',
|
'image',
|
||||||
'user-selector',
|
'user_selector',
|
||||||
'text-only',
|
'text_only',
|
||||||
'composer',
|
'composer',
|
||||||
'category',
|
'category',
|
||||||
'group'
|
'group'
|
||||||
|
|
|
@ -2,18 +2,21 @@
|
||||||
@import 'wizard-transfer';
|
@import 'wizard-transfer';
|
||||||
@import 'wizard-api';
|
@import 'wizard-api';
|
||||||
|
|
||||||
$setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
|
||||||
|
|
||||||
body.admin-wizard {
|
body.admin-wizard {
|
||||||
|
.admin-container > .row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.boxed.white {
|
.boxed.white {
|
||||||
background-color: initial;
|
background-color: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-list {
|
.wizard-list {
|
||||||
float: left;
|
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
min-width: 250px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
float: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-settings-parent {
|
.wizard-settings-parent {
|
||||||
|
@ -46,7 +49,7 @@ body.admin-wizard {
|
||||||
.wizard-custom-field {
|
.wizard-custom-field {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
background-color: $setting-background;
|
background-color: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +96,11 @@ body.admin-wizard {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
min-height: 31px;
|
min-height: 31px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
width: 350px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,11 +118,18 @@ body.admin-wizard {
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-url {
|
.wizard-url {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
font-size: 1rem;
|
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
background-color: $setting-background;
|
|
||||||
padding: 2px 6px;
|
a {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: $primary-low;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +137,6 @@ body.admin-wizard {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-list + .content {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-wizard.settings {
|
.admin-wizard.settings {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
|
@ -358,16 +366,19 @@ body.admin-wizard {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
ul {
|
.link-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-flow: wrap;
|
||||||
|
|
||||||
li {
|
> div {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
margin-bottom: 7px;
|
align-items: center;
|
||||||
margin-right: 7px;
|
margin-bottom: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,13 +100,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapper-input.assignment,
|
||||||
|
.mapper-input.validation,
|
||||||
|
.mapper-input.association {
|
||||||
|
.mapper-selector {
|
||||||
|
max-width: 250px;
|
||||||
|
min-width: 250px;
|
||||||
|
|
||||||
|
> input, .select-kit, .ac-wrap, .autocomplete.ac-user {
|
||||||
|
width: 250px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapper-input.conditional {
|
||||||
|
.mapper-selector {
|
||||||
|
max-width: 170px;
|
||||||
|
min-width: 170px;
|
||||||
|
|
||||||
|
&:not(.text).active .input {
|
||||||
|
width: 250px;
|
||||||
|
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
position: absolute;
|
||||||
|
z-index: 300;
|
||||||
|
|
||||||
|
.select-kit, .ac-wrap, .autocomplete.ac-user, .select-kit-wrapper {
|
||||||
|
width: 250px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mapper-selector {
|
.mapper-selector {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 150px;
|
position: relative;
|
||||||
min-width: 150px;
|
|
||||||
|
|
||||||
input, .select-kit, .ac-wrap, .autocomplete.ac-user {
|
.input {
|
||||||
width: 150px !important;
|
width: 100%;
|
||||||
|
transition: all 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-selector {
|
.type-selector {
|
||||||
|
@ -139,12 +170,16 @@
|
||||||
.selector-types {
|
.selector-types {
|
||||||
box-shadow: shadow('dropdown');
|
box-shadow: shadow('dropdown');
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
display: none;
|
||||||
background: $secondary;
|
background: $secondary;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
padding: 5px 7px;
|
padding: 5px 7px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid $primary-low;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-list .remove-value-btn {
|
.value-list .remove-value-btn {
|
||||||
|
|
|
@ -95,6 +95,7 @@ en:
|
||||||
list: "Enter item"
|
list: "Enter item"
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
failed: "failed to save wizard"
|
||||||
required: "{{type}} requires {{property}}"
|
required: "{{type}} requires {{property}}"
|
||||||
invalid: "{{property}} is invalid"
|
invalid: "{{property}} is invalid"
|
||||||
dependent: "{{property}} is dependent on {{dependent}}"
|
dependent: "{{property}} is dependent on {{dependent}}"
|
||||||
|
|
|
@ -18,16 +18,17 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
else
|
else
|
||||||
wizard = result[:wizard]
|
wizard = result[:wizard]
|
||||||
existing_wizard = result[:existing_wizard]
|
existing_wizard = result[:existing_wizard]
|
||||||
|
after_time = result[:after_time]
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
||||||
|
|
||||||
if wizard['after_time'] && result[:new_after_time]
|
if after_time[:enabled]
|
||||||
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id'])
|
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id'])
|
||||||
Jobs.enqueue_at(after_time_scheduled, :set_after_time_wizard, wizard_id: wizard['id'])
|
Jobs.enqueue_at(after_time[:scheduled], :set_after_time_wizard, wizard_id: wizard['id'])
|
||||||
end
|
end
|
||||||
|
|
||||||
if existing_wizard && existing_wizard['after_time'] && !wizard['after_time']
|
if existing_wizard && existing_wizard['after_time'] && !after_time[:enabled]
|
||||||
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id'])
|
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id'])
|
||||||
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id'])
|
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id'])
|
||||||
end
|
end
|
||||||
|
@ -181,19 +182,24 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
def validate_after_time(wizard, existing_wizard)
|
def validate_after_time(wizard, existing_wizard)
|
||||||
new = false
|
new = false
|
||||||
error = nil
|
error = nil
|
||||||
|
enabled = false
|
||||||
|
scheduled = nil
|
||||||
|
|
||||||
if wizard["after_time"]
|
if wizard["after_time"]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
if !wizard["after_time_scheduled"] && !existing_wizard["after_time_scheduled"]
|
if !wizard["after_time_scheduled"] && !existing_wizard["after_time_scheduled"]
|
||||||
error = 'after_time_need_time'
|
error = 'after_time_need_time'
|
||||||
else
|
else
|
||||||
after_time_scheduled = Time.parse(wizard["after_time_scheduled"]).utc
|
scheduled = Time.parse(wizard["after_time_scheduled"]).utc
|
||||||
|
new = false
|
||||||
|
|
||||||
new = existing_wizard['after_time_scheduled'] ?
|
if existing_wizard['after_time_scheduled']
|
||||||
after_time_scheduled != Time.parse(existing_wizard['after_time_scheduled']).utc :
|
new = scheduled != Time.parse(existing_wizard['after_time_scheduled']).utc
|
||||||
true
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
error = 'after_time_invalid' if new && after_time_scheduled < Time.now.utc
|
error = 'after_time_invalid' if new && scheduled < Time.now.utc
|
||||||
rescue ArgumentError
|
rescue ArgumentError
|
||||||
error = 'after_time_invalid'
|
error = 'after_time_invalid'
|
||||||
end
|
end
|
||||||
|
@ -203,7 +209,11 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
if error
|
if error
|
||||||
{ error: { type: error } }
|
{ error: { type: error } }
|
||||||
else
|
else
|
||||||
{ new: new }
|
{
|
||||||
|
new: new,
|
||||||
|
scheduled: scheduled,
|
||||||
|
enabled: enabled
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -214,8 +224,8 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
validation = validate_wizard(wizard)
|
validation = validate_wizard(wizard)
|
||||||
return validation if validation[:error]
|
return validation if validation[:error]
|
||||||
|
|
||||||
after_time_validation = validate_after_time(wizard, existing_wizard)
|
after_time = validate_after_time(wizard, existing_wizard)
|
||||||
return after_time_validation if after_time_validation[:error]
|
return after_time if after_time[:error]
|
||||||
|
|
||||||
wizard['steps'].each do |step|
|
wizard['steps'].each do |step|
|
||||||
if step['raw_description']
|
if step['raw_description']
|
||||||
|
@ -223,10 +233,15 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{
|
result = {
|
||||||
wizard: wizard,
|
wizard: wizard,
|
||||||
existing_wizard: existing_wizard,
|
existing_wizard: existing_wizard
|
||||||
new_after_time: after_time_validation[:new]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if after_time[:enabled]
|
||||||
|
result[:after_time] = after_time
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -124,7 +124,7 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
if step_template['fields'] && step_template['fields'].length
|
if step_template['fields'] && step_template['fields'].length
|
||||||
step_template['fields'].each do |field|
|
step_template['fields'].each do |field|
|
||||||
validate_field(field, updater, step_template) if field['type'] != 'text-only'
|
validate_field(field, updater, step_template) if field['type'] != 'text_only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class CustomWizard::Field
|
class CustomWizard::Field
|
||||||
def self.types
|
def self.types
|
||||||
@types ||= ['checkbox', 'composer', 'dropdown', 'tag', 'category', 'group', 'text', 'textarea', 'text-only', 'number', 'upload', 'user-selector', 'url']
|
@types ||= ['text', 'textarea', 'composer', 'text_only', 'number', 'checkbox', 'url', 'upload', 'dropdown', 'tag', 'category', 'group', 'user_selector']
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.require_assets
|
def self.require_assets
|
||||||
|
|
92
spec/components/custom_wizard/action_spec.rb
Normale Datei
92
spec/components/custom_wizard/action_spec.rb
Normale Datei
|
@ -0,0 +1,92 @@
|
||||||
|
describe CustomWizard::Action do
|
||||||
|
let(:create_topic_action) {{"id":"create_topic","type":"create_topic","title":"text","post":"textarea"}}
|
||||||
|
let(:send_message_action) {{"id":"send_message","type":"send_message","title":"text","post":"textarea","username":"angus"}}
|
||||||
|
let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}}
|
||||||
|
let(:open_composer_action) {{"id":"open_composer","type":"open_composer","title":"text","post":"textarea"}}
|
||||||
|
let(:add_to_group_action) {{"id":"add_to_group","type":"add_to_group","group_id":"dropdown_groups"}}
|
||||||
|
|
||||||
|
it 'creates a topic' do
|
||||||
|
template['steps'][0]['fields'] = [text_field, textarea_field]
|
||||||
|
template['steps'][0]["actions"] = [create_topic_action]
|
||||||
|
updater = run_update(template, nil,
|
||||||
|
text: "Topic Title",
|
||||||
|
textarea: "topic body"
|
||||||
|
)
|
||||||
|
topic = Topic.where(title: "Topic Title")
|
||||||
|
|
||||||
|
expect(topic.exists?).to eq(true)
|
||||||
|
expect(Post.where(
|
||||||
|
topic_id: topic.pluck(:id),
|
||||||
|
raw: "topic body"
|
||||||
|
).exists?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sends a message' do
|
||||||
|
fields = [text_field, textarea_field]
|
||||||
|
|
||||||
|
if extra_field
|
||||||
|
fields.push(extra_field)
|
||||||
|
end
|
||||||
|
|
||||||
|
template['steps'][0]['fields'] = fields
|
||||||
|
template['steps'][0]["actions"] = [send_message_action.merge(extra_action_opts)]
|
||||||
|
|
||||||
|
run_update(template, nil,
|
||||||
|
text: "Message Title",
|
||||||
|
textarea: "message body"
|
||||||
|
)
|
||||||
|
|
||||||
|
topic = Topic.where(
|
||||||
|
archetype: Archetype.private_message,
|
||||||
|
title: "Message Title"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(topic.exists?).to eq(true)
|
||||||
|
expect(
|
||||||
|
topic.first.topic_allowed_users.first.user.username
|
||||||
|
).to eq('angus')
|
||||||
|
expect(Post.where(
|
||||||
|
topic_id: topic.pluck(:id),
|
||||||
|
raw: "message body"
|
||||||
|
).exists?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates a profile' do
|
||||||
|
run_update(template, template['steps'][1]['id'], name: "Sally")
|
||||||
|
expect(user.name).to eq('Sally')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'opens a composer' do
|
||||||
|
template['steps'][0]['fields'] = [text_field, textarea_field]
|
||||||
|
template['steps'][0]["actions"] = [open_composer_action]
|
||||||
|
|
||||||
|
updater = run_update(template, nil,
|
||||||
|
text: "Topic Title",
|
||||||
|
textarea: "topic body"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updater.result.blank?).to eq(true)
|
||||||
|
|
||||||
|
updater = run_update(template, template['steps'][1]['id'])
|
||||||
|
|
||||||
|
expect(updater.result[:redirect_on_complete]).to eq(
|
||||||
|
"/new-topic?title=Topic%20Title&body=topic%20body"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds a user to a group' do
|
||||||
|
template['steps'][0]['fields'] = [dropdown_groups_field]
|
||||||
|
template['steps'][0]["actions"] = [add_to_group_action]
|
||||||
|
|
||||||
|
updater = run_update(template, nil, dropdown_groups: group.id)
|
||||||
|
expect(group.users.first.username).to eq('angus')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 're-routes a user' do
|
||||||
|
template['steps'][0]["actions"] = [route_to_action]
|
||||||
|
updater = run_update(template, nil, {})
|
||||||
|
expect(updater.result[:redirect_on_next]).to eq(
|
||||||
|
"https://google.com"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,27 +15,6 @@ describe CustomWizard::Builder do
|
||||||
).read)
|
).read)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:permitted_params) {[{"key":"param_key","value":"submission_param_key"}]}
|
|
||||||
let(:required_data) {[{"key":"nickname","connector":"equals","value":"name"}]}
|
|
||||||
let(:required_data_message) {"Nickname is required to match your name"}
|
|
||||||
|
|
||||||
let(:checkbox_field) {{"id":"checkbox","type":"checkbox","label":"Checkbox"}}
|
|
||||||
let(:composer_field) {{"id": "composer","label":"Composer","type":"composer"}}
|
|
||||||
let(:tag_field) {{"id": "tag","type": "tag","label": "Tag","limit": "2"}}
|
|
||||||
let(:category_field) {{"id": "category","type": "category","limit": "1","label": "Category"}}
|
|
||||||
let(:image_field) {{"id": "image","type": "image","label": "Image"}}
|
|
||||||
let(:text_field) {{"id": "text","type": "text","label": "Text"}}
|
|
||||||
let(:textarea_field) {{"id": "textarea","type": "textarea","label": "Textarea"}}
|
|
||||||
let(:text_only_field) {{"id": "text_only","type": "text-only","label": "Text only"}}
|
|
||||||
let(:upload_field) {{"id": "upload","type": "upload","file_types": ".jpg,.png,.pdf","label": "Upload"}}
|
|
||||||
let(:user_selector_field) {{"id": "user_selector","type": "user-selector","label": "User selector"}}
|
|
||||||
|
|
||||||
let(:create_topic_action) {{"id":"create_topic","type":"create_topic","title":"text","post":"textarea"}}
|
|
||||||
let(:send_message_action) {{"id":"send_message","type":"send_message","title":"text","post":"textarea","username":"angus"}}
|
|
||||||
let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}}
|
|
||||||
let(:open_composer_action) {{"id":"open_composer","type":"open_composer","title":"text","post":"textarea"}}
|
|
||||||
let(:add_to_group_action) {{"id":"add_to_group","type":"add_to_group","group_id":"dropdown_groups"}}
|
|
||||||
|
|
||||||
def build_wizard(t = template, u = user, build_opts = {}, params = {})
|
def build_wizard(t = template, u = user, build_opts = {}, params = {})
|
||||||
CustomWizard::Wizard.add_wizard(t)
|
CustomWizard::Wizard.add_wizard(t)
|
||||||
CustomWizard::Builder.new(u, 'welcome').build(build_opts, params)
|
CustomWizard::Builder.new(u, 'welcome').build(build_opts, params)
|
||||||
|
@ -59,22 +38,6 @@ describe CustomWizard::Builder do
|
||||||
updater
|
updater
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_message(extra_field = nil, extra_action_opts = {})
|
|
||||||
fields = [text_field, textarea_field]
|
|
||||||
|
|
||||||
if extra_field
|
|
||||||
fields.push(extra_field)
|
|
||||||
end
|
|
||||||
|
|
||||||
template['steps'][0]['fields'] = fields
|
|
||||||
template['steps'][0]["actions"] = [send_message_action.merge(extra_action_opts)]
|
|
||||||
|
|
||||||
run_update(template, nil,
|
|
||||||
text: "Message Title",
|
|
||||||
textarea: "message body"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'disabled' do
|
context 'disabled' do
|
||||||
before do
|
before do
|
||||||
SiteSetting.custom_wizard_enabled = false
|
SiteSetting.custom_wizard_enabled = false
|
||||||
|
@ -101,7 +64,7 @@ describe CustomWizard::Builder do
|
||||||
expect(build_wizard.steps.length).to eq(2)
|
expect(build_wizard.steps.length).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns no steps if the multiple submissions are disabled and user has completed it' do
|
it 'returns no steps if multiple submissions are disabled and user has completed' do
|
||||||
history_params = {
|
history_params = {
|
||||||
action: UserHistory.actions[:custom_wizard_step],
|
action: UserHistory.actions[:custom_wizard_step],
|
||||||
acting_user_id: user.id,
|
acting_user_id: user.id,
|
||||||
|
@ -114,12 +77,12 @@ describe CustomWizard::Builder do
|
||||||
expect(build_wizard(template).steps.length).to eq(0)
|
expect(build_wizard(template).steps.length).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns no steps if has min trust and user does not meet it' do
|
it 'returns no steps if user is not permitted' do
|
||||||
template["min_trust"] = 3
|
template["min_trust"] = 3
|
||||||
expect(build_wizard(template).steps.length).to eq(0)
|
expect(build_wizard(template).steps.length).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns steps if it has min trust and user meets it' do
|
it 'returns steps if user is permitted' do
|
||||||
template["min_trust"] = 3
|
template["min_trust"] = 3
|
||||||
expect(build_wizard(template, trusted_user).steps.length).to eq(2)
|
expect(build_wizard(template, trusted_user).steps.length).to eq(2)
|
||||||
end
|
end
|
||||||
|
@ -156,12 +119,6 @@ describe CustomWizard::Builder do
|
||||||
expect(build_wizard(template, user).steps[0].permitted).to eq(false)
|
expect(build_wizard(template, user).steps[0].permitted).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is not permitted if required data is not present" do
|
|
||||||
template['steps'][0]['required_data'] = required_data
|
|
||||||
add_submission_data(nickname: "John")
|
|
||||||
expect(build_wizard(template, user).steps[0].permitted).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'it shows required data message if required data has message' do
|
it 'it shows required data message if required data has message' do
|
||||||
template['steps'][0]['required_data'] = required_data
|
template['steps'][0]['required_data'] = required_data
|
||||||
template['steps'][0]['required_data_message'] = required_data_message
|
template['steps'][0]['required_data_message'] = required_data_message
|
||||||
|
@ -217,152 +174,10 @@ describe CustomWizard::Builder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'actions' do
|
|
||||||
it 'runs actions attached to a step' do
|
it 'runs actions attached to a step' do
|
||||||
run_update(template, template['steps'][1]['id'], name: "Gus")
|
run_update(template, template['steps'][1]['id'], name: "Gus")
|
||||||
expect(user.name).to eq('Gus')
|
expect(user.name).to eq('Gus')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'interpolates user data correctly' do
|
|
||||||
user.name = "Angus"
|
|
||||||
user.save!
|
|
||||||
|
|
||||||
expect(
|
|
||||||
CustomWizard::Builder.fill_placeholders(
|
|
||||||
"My name is u{name}",
|
|
||||||
user,
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
).to eq('My name is Angus')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a topic' do
|
|
||||||
template['steps'][0]['fields'] = [text_field, textarea_field]
|
|
||||||
template['steps'][0]["actions"] = [create_topic_action]
|
|
||||||
updater = run_update(template, nil,
|
|
||||||
text: "Topic Title",
|
|
||||||
textarea: "topic body"
|
|
||||||
)
|
|
||||||
topic = Topic.where(title: "Topic Title")
|
|
||||||
|
|
||||||
expect(topic.exists?).to eq(true)
|
|
||||||
expect(Post.where(
|
|
||||||
topic_id: topic.pluck(:id),
|
|
||||||
raw: "topic body"
|
|
||||||
).exists?).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a topic with a custom title' do
|
|
||||||
user.name = "Angus"
|
|
||||||
user.save!
|
|
||||||
|
|
||||||
template['steps'][0]['fields'] = [text_field, textarea_field]
|
|
||||||
|
|
||||||
create_topic_action['custom_title_enabled'] = true
|
|
||||||
create_topic_action['custom_title'] = "u{name}' Topic Title"
|
|
||||||
template['steps'][0]["actions"] = [create_topic_action]
|
|
||||||
|
|
||||||
run_update(template, nil, textarea: "topic body")
|
|
||||||
|
|
||||||
topic = Topic.where(title: "Angus' Topic Title")
|
|
||||||
|
|
||||||
expect(topic.exists?).to eq(true)
|
|
||||||
expect(Post.where(
|
|
||||||
topic_id: topic.pluck(:id),
|
|
||||||
raw: "topic body"
|
|
||||||
).exists?).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a topic with a custom post' do
|
|
||||||
user.name = "Angus"
|
|
||||||
user.save!
|
|
||||||
|
|
||||||
template['steps'][0]['fields'] = [text_field, textarea_field]
|
|
||||||
|
|
||||||
create_topic_action['post_builder'] = true
|
|
||||||
create_topic_action['post_template'] = "u{name}' w{textarea}"
|
|
||||||
template['steps'][0]["actions"] = [create_topic_action]
|
|
||||||
|
|
||||||
run_update(template, nil,
|
|
||||||
text: "Topic Title",
|
|
||||||
textarea: "topic body"
|
|
||||||
)
|
|
||||||
|
|
||||||
topic = Topic.where(title: "Topic Title")
|
|
||||||
|
|
||||||
expect(topic.exists?).to eq(true)
|
|
||||||
expect(Post.where(
|
|
||||||
topic_id: topic.pluck(:id),
|
|
||||||
raw: "Angus' topic body"
|
|
||||||
).exists?).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sends a message' do
|
|
||||||
send_message
|
|
||||||
|
|
||||||
topic = Topic.where(
|
|
||||||
archetype: Archetype.private_message,
|
|
||||||
title: "Message Title"
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(topic.exists?).to eq(true)
|
|
||||||
expect(
|
|
||||||
topic.first.topic_allowed_users.first.user.username
|
|
||||||
).to eq('angus')
|
|
||||||
expect(Post.where(
|
|
||||||
topic_id: topic.pluck(:id),
|
|
||||||
raw: "message body"
|
|
||||||
).exists?).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'doesnt sent a message if the required data is not present' do
|
|
||||||
send_message(user_selector_field, required: "user_selector")
|
|
||||||
topic = Topic.where(
|
|
||||||
archetype: Archetype.private_message,
|
|
||||||
title: "Message Title"
|
|
||||||
)
|
|
||||||
expect(topic.exists?).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates a profile' do
|
|
||||||
run_update(template, template['steps'][1]['id'], name: "Sally")
|
|
||||||
expect(user.name).to eq('Sally')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'opens a composer' do
|
|
||||||
template['steps'][0]['fields'] = [text_field, textarea_field]
|
|
||||||
template['steps'][0]["actions"] = [open_composer_action]
|
|
||||||
|
|
||||||
updater = run_update(template, nil,
|
|
||||||
text: "Topic Title",
|
|
||||||
textarea: "topic body"
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(updater.result.blank?).to eq(true)
|
|
||||||
|
|
||||||
updater = run_update(template, template['steps'][1]['id'])
|
|
||||||
|
|
||||||
expect(updater.result[:redirect_on_complete]).to eq(
|
|
||||||
"/new-topic?title=Topic%20Title&body=topic%20body"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds a user to a group' do
|
|
||||||
template['steps'][0]['fields'] = [dropdown_groups_field]
|
|
||||||
template['steps'][0]["actions"] = [add_to_group_action]
|
|
||||||
|
|
||||||
updater = run_update(template, nil, dropdown_groups: group.id)
|
|
||||||
expect(group.users.first.username).to eq('angus')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 're-routes a user' do
|
|
||||||
template['steps'][0]["actions"] = [route_to_action]
|
|
||||||
updater = run_update(template, nil, {})
|
|
||||||
expect(updater.result[:redirect_on_next]).to eq(
|
|
||||||
"https://google.com"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
14
spec/components/custom_wizard/mapper_spec.rb
Normale Datei
14
spec/components/custom_wizard/mapper_spec.rb
Normale Datei
|
@ -0,0 +1,14 @@
|
||||||
|
describe CustomWizard::Mapper do
|
||||||
|
|
||||||
|
it 'interpolates user data' do
|
||||||
|
user.name = "Angus"
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
expect(
|
||||||
|
CustomWizard::Builder.fill_placeholders(
|
||||||
|
"My name is u{name}",
|
||||||
|
user,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
).to eq('My name is Angus')
|
||||||
|
end
|
Laden …
In neuem Issue referenzieren