Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-26 02:50:28 +01:00
add permitted setting
Dieser Commit ist enthalten in:
Ursprung
b212eaa2f3
Commit
ee61c1deb3
28 geänderte Dateien mit 589 neuen und 524 gelöschten Zeilen
|
@ -46,11 +46,11 @@ export default Ember.Component.extend({
|
||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
|
||||||
canFilter: or('isCategory', 'isTag', 'isGroup'),
|
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
|
||||||
|
|
||||||
@computed('field.type')
|
@computed('field.type')
|
||||||
filterOptions(fieldType) {
|
contentOptions(fieldType) {
|
||||||
if (!this.canFilter) return {};
|
if (!this.contentEnabled) return {};
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
hasOutput: true,
|
hasOutput: true,
|
||||||
|
|
|
@ -23,9 +23,14 @@ export default Ember.Component.extend({
|
||||||
if (!this.type) this.set('type', defaultInputType(this.options));
|
if (!this.type) this.set('type', defaultInputType(this.options));
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed('options.allowedInputs')
|
||||||
inputTypes() {
|
allowedInputs(option) {
|
||||||
return ['conditional', 'assignment'].map((type) => {
|
return option || 'conditional,assignment';
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed('allowedInputs')
|
||||||
|
inputTypes(allowedInputs) {
|
||||||
|
return allowedInputs.split(',').map((type) => {
|
||||||
return {
|
return {
|
||||||
id: type,
|
id: type,
|
||||||
name: I18n.t(`admin.wizard.input.${type}.prefix`)
|
name: I18n.t(`admin.wizard.input.${type}.prefix`)
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||||
import { on } from 'discourse-common/utils/decorators';
|
import { on } from 'discourse-common/utils/decorators';
|
||||||
import { newInput } from '../lib/custom-wizard';
|
import { newInput } from '../lib/custom-wizard';
|
||||||
|
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'field-mapper',
|
classNames: 'field-mapper',
|
||||||
|
|
||||||
|
@discourseComputed('inputs.[]', 'options.singular')
|
||||||
|
canAdd(inputs, singular) {
|
||||||
|
return !singular || !inputs || inputs.length < 1;
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
add() {
|
add() {
|
||||||
if (!this.get('inputs')) this.set('inputs', Ember.A());
|
if (!this.get('inputs')) this.set('inputs', Ember.A());
|
||||||
|
|
|
@ -10,16 +10,15 @@ function generateName(id) {
|
||||||
|
|
||||||
const profileFields = [
|
const profileFields = [
|
||||||
'name',
|
'name',
|
||||||
'user_avatar',
|
'username',
|
||||||
|
'email',
|
||||||
'date_of_birth',
|
'date_of_birth',
|
||||||
'title',
|
'title',
|
||||||
'locale',
|
'locale',
|
||||||
'location',
|
'location',
|
||||||
'website',
|
'website',
|
||||||
'bio_raw',
|
'bio_raw',
|
||||||
'profile_background',
|
'trust_level'
|
||||||
'card_background',
|
|
||||||
'theme_id'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const connectors = [
|
const connectors = [
|
||||||
|
@ -69,7 +68,10 @@ const inputTypes = [
|
||||||
]
|
]
|
||||||
|
|
||||||
function defaultInputType(options = {}) {
|
function defaultInputType(options = {}) {
|
||||||
return options.hasOutput ? 'conditional' : 'pair';
|
if (!options.hasOutput) return 'pair';
|
||||||
|
const allowedInputs = options.allowedInputs;
|
||||||
|
if (!allowedInputs) return 'conditional';
|
||||||
|
return allowedInputs.split(',')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultSelectionType(inputType, options = {}) {
|
function defaultSelectionType(inputType, options = {}) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ const wizardProperties = [
|
||||||
'required',
|
'required',
|
||||||
'prompt_completion',
|
'prompt_completion',
|
||||||
'restart_on_revisit',
|
'restart_on_revisit',
|
||||||
'min_trust',
|
'theme_id',
|
||||||
'theme_id'
|
'permitted'
|
||||||
];
|
];
|
||||||
|
|
||||||
const CustomWizard = EmberObject.extend({
|
const CustomWizard = EmberObject.extend({
|
||||||
|
@ -267,7 +267,7 @@ CustomWizard.reopenClass({
|
||||||
props['required'] = false;
|
props['required'] = false;
|
||||||
props['prompt_completion'] = false;
|
props['prompt_completion'] = false;
|
||||||
props['restart_on_revisit'] = false;
|
props['restart_on_revisit'] = false;
|
||||||
props['min_trust'] = 0;
|
props['permitted'] = null;
|
||||||
props['steps'] = Ember.A();
|
props['steps'] = Ember.A();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,7 @@
|
||||||
placeholderKey="admin.wizard.name_placeholder"}}
|
placeholderKey="admin.wizard.name_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="wizard-header medium">
|
|
||||||
{{i18n 'admin.wizard.label'}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="wizard-settings">
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.background'}}</label>
|
<label>{{i18n 'admin.wizard.background'}}</label>
|
||||||
|
@ -40,7 +34,13 @@
|
||||||
placeholderKey="admin.wizard.background_placeholder"}}
|
placeholderKey="admin.wizard.background_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-header medium">
|
||||||
|
{{i18n 'admin.wizard.label'}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-settings">
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.save_submissions'}}</label>
|
<label>{{i18n 'admin.wizard.save_submissions'}}</label>
|
||||||
|
@ -106,16 +106,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.min_trust'}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-value">
|
|
||||||
<span>{{i18n 'admin.wizard.min_trust_label'}}</span>
|
|
||||||
{{input type='number' value=model.min_trust class='input-small'}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.theme_id'}}</label>
|
<label>{{i18n 'admin.wizard.theme_id'}}</label>
|
||||||
|
@ -144,17 +134,17 @@
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.group'}}</label>
|
<label>{{i18n 'admin.wizard.permitted'}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{wizard-field-mapper
|
{{wizard-field-mapper
|
||||||
inputs=model.group
|
inputs=model.permitted
|
||||||
options=(hash
|
options=(hash
|
||||||
hasOutput=true
|
hasOutput=true
|
||||||
enableConnectors=true
|
groupSelection='output'
|
||||||
userFieldSelection='key,value'
|
|
||||||
groupSelection=true
|
|
||||||
textDisabled='output'
|
textDisabled='output'
|
||||||
|
allowedInputs='assignment'
|
||||||
|
singular=true
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -170,17 +170,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if canFilter}}
|
{{#if contentEnabled}}
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.field.filter'}}</label>
|
<label>{{i18n 'admin.wizard.field.content'}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{wizard-field-mapper
|
{{wizard-field-mapper
|
||||||
inputs=field.filters
|
inputs=field.content
|
||||||
wizardFields=wizardFields
|
wizardFields=wizardFields
|
||||||
options=filterOptions}}
|
options=contentOptions}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
remove=(action 'remove')}}
|
remove=(action 'remove')}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if canAdd}}
|
||||||
<div class="add-custom-input">
|
<div class="add-custom-input">
|
||||||
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
|
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
|
@ -30,6 +30,7 @@
|
||||||
//= require discourse/lib/show-modal
|
//= require discourse/lib/show-modal
|
||||||
//= require discourse/lib/key-value-store
|
//= require discourse/lib/key-value-store
|
||||||
//= require discourse/lib/settings
|
//= require discourse/lib/settings
|
||||||
|
//= require discourse/lib/user-presence
|
||||||
|
|
||||||
//= require discourse/mixins/singleton
|
//= require discourse/mixins/singleton
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
export default Ember.Component.extend({
|
|
||||||
didInsertElement() {
|
|
||||||
console.log(this.field)
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -3,10 +3,15 @@ import { computed } from "@ember/object";
|
||||||
import { makeArray } from "discourse-common/lib/helpers";
|
import { makeArray } from "discourse-common/lib/helpers";
|
||||||
|
|
||||||
export default ComboBox.extend({
|
export default ComboBox.extend({
|
||||||
content: computed("groups.[]", "whitelist.[]", function() {
|
content: computed("groups.[]", "field.content.[]", function() {
|
||||||
const whitelist = makeArray(this.whitelist);
|
const whitelist = makeArray(this.field.content);
|
||||||
return this.groups.filter(group => {
|
return this.groups.filter(group => {
|
||||||
return !whitelist.length || whitelist.indexOf(group.id) > -1;
|
return !whitelist.length || whitelist.indexOf(group.id) > -1;
|
||||||
|
}).map(g => {
|
||||||
|
return {
|
||||||
|
id: g.id,
|
||||||
|
name: g.name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -14,7 +14,6 @@ export default Ember.Route.extend({
|
||||||
if (model) {
|
if (model) {
|
||||||
const completed = model.get('completed');
|
const completed = model.get('completed');
|
||||||
const permitted = model.get('permitted');
|
const permitted = model.get('permitted');
|
||||||
const minTrust = model.get('min_trust');
|
|
||||||
const wizardId = model.get('id');
|
const wizardId = model.get('id');
|
||||||
const user = model.get('user');
|
const user = model.get('user');
|
||||||
const name = model.get('name');
|
const name = model.get('name');
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{wizard-category-selector
|
{{wizard-category-selector
|
||||||
categories=categories
|
categories=categories
|
||||||
whitelist=field.filter
|
whitelist=field.content
|
||||||
maximum=field.limit
|
maximum=field.limit
|
||||||
onChange=(action (mut categories))}}
|
onChange=(action (mut categories))}}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{wizard-group-selector
|
{{wizard-group-selector
|
||||||
groups=wizard.groups
|
groups=wizard.groups
|
||||||
whitelist=field.filter
|
field=field
|
||||||
|
whitelist=field.content
|
||||||
value=field.value
|
value=field.value
|
||||||
onChange=(action (mut field.value))
|
onChange=(action (mut field.value))
|
||||||
options=(hash
|
options=(hash
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{{wizard-no-access text=(i18n 'wizard.requires_login' name=name) wizardId=wizardId}}
|
{{wizard-no-access text=(i18n 'wizard.requires_login' name=name) wizardId=wizardId}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if notPermitted}}
|
{{#if notPermitted}}
|
||||||
{{wizard-no-access text=(i18n 'wizard.not_permitted' name=name level=minTrust) wizardId=wizardId}}
|
{{wizard-no-access text=(i18n 'wizard.not_permitted' name=name) wizardId=wizardId}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if completed}}
|
{{#if completed}}
|
||||||
{{wizard-no-access text=(i18n 'wizard.completed' name=name) wizardId=wizardId}}
|
{{wizard-no-access text=(i18n 'wizard.completed' name=name) wizardId=wizardId}}
|
||||||
|
|
|
@ -61,10 +61,6 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-basic-details {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-list + .content {
|
.content-list + .content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@ en:
|
||||||
prompt_completion_label: "Prompt user to complete wizard."
|
prompt_completion_label: "Prompt user to complete wizard."
|
||||||
restart_on_revisit: "Restart"
|
restart_on_revisit: "Restart"
|
||||||
restart_on_revisit_label: "Restart the the wizard whenever the user revisits, regardless of prior progress."
|
restart_on_revisit_label: "Restart the the wizard whenever the user revisits, regardless of prior progress."
|
||||||
min_trust: "Trust"
|
|
||||||
min_trust_label: "Trust level required to access wizard."
|
|
||||||
theme_id: "Theme"
|
theme_id: "Theme"
|
||||||
no_theme: "Select a Theme (optional)"
|
no_theme: "Select a Theme (optional)"
|
||||||
save: "Save Changes"
|
save: "Save Changes"
|
||||||
|
@ -61,6 +59,7 @@ en:
|
||||||
submission_key: 'submission key'
|
submission_key: 'submission key'
|
||||||
param_key: 'param'
|
param_key: 'param'
|
||||||
group: "Group"
|
group: "Group"
|
||||||
|
permitted: "Permitted"
|
||||||
|
|
||||||
editor:
|
editor:
|
||||||
show: "Show"
|
show: "Show"
|
||||||
|
@ -125,7 +124,7 @@ en:
|
||||||
limit: "Limit"
|
limit: "Limit"
|
||||||
property: "Property"
|
property: "Property"
|
||||||
prefill: "Prefill"
|
prefill: "Prefill"
|
||||||
filter: "Content"
|
content: "Content"
|
||||||
|
|
||||||
action:
|
action:
|
||||||
header: "Actions"
|
header: "Actions"
|
||||||
|
@ -306,12 +305,12 @@ en:
|
||||||
|
|
||||||
wizard:
|
wizard:
|
||||||
completed: "You have completed the {{name}} wizard."
|
completed: "You have completed the {{name}} wizard."
|
||||||
not_permitted: "You need to be trust level {{level}} or higher to access the {{name}} wizard."
|
not_permitted: "You are not permitted to access the {{name}} wizard."
|
||||||
none: "There is no wizard here."
|
none: "There is no wizard here."
|
||||||
return_to_site: "Return to {{siteName}}"
|
return_to_site: "Return to {{siteName}}"
|
||||||
requires_login: "You need to be logged in to access the {{name}} wizard."
|
requires_login: "You need to be logged in to access the {{name}} wizard."
|
||||||
reset: "Reset this wizard."
|
reset: "Reset this wizard."
|
||||||
step_not_permitted: "You're not allowed to view this step."
|
step_not_permitted: "You're not permitted to view this step."
|
||||||
|
|
||||||
wizard_composer:
|
wizard_composer:
|
||||||
show_preview: "Preview Post"
|
show_preview: "Preview Post"
|
||||||
|
|
|
@ -33,8 +33,6 @@ fr:
|
||||||
required_label: "Les utilisatrices doivent compléter l'assistant."
|
required_label: "Les utilisatrices doivent compléter l'assistant."
|
||||||
prompt_completion: "Invitation"
|
prompt_completion: "Invitation"
|
||||||
prompt_completion_label: "Inviter l'utilisatrice à compléter l'assistant."
|
prompt_completion_label: "Inviter l'utilisatrice à compléter l'assistant."
|
||||||
min_trust: "Confiance"
|
|
||||||
min_trust_label: "Niveau de confiance requis pour accéder à l'assistant."
|
|
||||||
theme_id: "Thème"
|
theme_id: "Thème"
|
||||||
no_theme: "Choisir un thème (optionnel)"
|
no_theme: "Choisir un thème (optionnel)"
|
||||||
save: "Sauvegarder"
|
save: "Sauvegarder"
|
||||||
|
|
|
@ -14,11 +14,8 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
params.require(:wizard)
|
params.require(:wizard)
|
||||||
|
|
||||||
wizard = ::JSON.parse(params[:wizard])
|
wizard = ::JSON.parse(params[:wizard])
|
||||||
|
|
||||||
existing = PluginStore.get('custom_wizard', wizard['id']) || {}
|
existing = PluginStore.get('custom_wizard', wizard['id']) || {}
|
||||||
|
|
||||||
new_time = false
|
new_time = false
|
||||||
|
|
||||||
error = nil
|
error = nil
|
||||||
|
|
||||||
if wizard["id"].blank?
|
if wizard["id"].blank?
|
||||||
|
|
289
lib/custom_wizard/actions.rb
Normale Datei
289
lib/custom_wizard/actions.rb
Normale Datei
|
@ -0,0 +1,289 @@
|
||||||
|
class CustomWizard::Action
|
||||||
|
attr_accessor :data,
|
||||||
|
:action,
|
||||||
|
:user,
|
||||||
|
:updater
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
@action = params[:action]
|
||||||
|
@user = params[:user]
|
||||||
|
@data = params[:data]
|
||||||
|
@updater = params[:updater]
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
ActiveRecord::Base.transaction { self.send(action['type'].to_sym) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def mapper
|
||||||
|
@mapper ||= CustomWizard::Mapper.new(user: user, data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_topic
|
||||||
|
if action['custom_title_enabled']
|
||||||
|
title = mapper.interpolate(action['custom_title'])
|
||||||
|
else
|
||||||
|
title = data[action['title']]
|
||||||
|
end
|
||||||
|
|
||||||
|
if action['post_builder']
|
||||||
|
post = mapper.interpolate(action['post_template'])
|
||||||
|
else
|
||||||
|
post = data[action['post']]
|
||||||
|
end
|
||||||
|
|
||||||
|
if title
|
||||||
|
params = {
|
||||||
|
title: title,
|
||||||
|
raw: post,
|
||||||
|
skip_validations: true
|
||||||
|
}
|
||||||
|
|
||||||
|
params[:category] = action_category_id(action, data)
|
||||||
|
tags = action_tags(action, data)
|
||||||
|
params[:tags] = tags
|
||||||
|
|
||||||
|
topic_custom_fields = {}
|
||||||
|
|
||||||
|
if action['add_fields']
|
||||||
|
action['add_fields'].each do |field|
|
||||||
|
value = field['value_custom'].present? ? field['value_custom'] : data[field['value']]
|
||||||
|
key = field['key']
|
||||||
|
|
||||||
|
if key && (value.present? || value === false)
|
||||||
|
if key.include?('custom_fields')
|
||||||
|
keyArr = key.split('.')
|
||||||
|
|
||||||
|
if keyArr.length === 3
|
||||||
|
custom_key = keyArr.last
|
||||||
|
type = keyArr.first
|
||||||
|
|
||||||
|
if type === 'topic'
|
||||||
|
topic_custom_fields[custom_key] = value
|
||||||
|
elsif type === 'post'
|
||||||
|
params[:custom_fields] ||= {}
|
||||||
|
params[:custom_fields][custom_key.to_sym] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
value = [*value] + tags if key === 'tags'
|
||||||
|
params[key.to_sym] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
creator = PostCreator.new(user, params)
|
||||||
|
post = creator.create
|
||||||
|
|
||||||
|
if creator.errors.present?
|
||||||
|
updater.errors.add(:create_topic, creator.errors.full_messages.join(" "))
|
||||||
|
else
|
||||||
|
if topic_custom_fields.present?
|
||||||
|
topic_custom_fields.each do |k, v|
|
||||||
|
post.topic.custom_fields[k] = v
|
||||||
|
end
|
||||||
|
post.topic.save_custom_fields(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless action['skip_redirect']
|
||||||
|
data['redirect_on_complete'] = post.topic.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_message
|
||||||
|
if action['required'].present? && data[action['required']].blank?
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if action['custom_title_enabled']
|
||||||
|
title = mapper.interpolate(action['custom_title'])
|
||||||
|
else
|
||||||
|
title = data[action['title']]
|
||||||
|
end
|
||||||
|
|
||||||
|
if action['post_builder']
|
||||||
|
post = mapper.interpolate(action['post_template'])
|
||||||
|
else
|
||||||
|
post = data[action['post']]
|
||||||
|
end
|
||||||
|
|
||||||
|
if title && post
|
||||||
|
creator = PostCreator.new(user,
|
||||||
|
title: title,
|
||||||
|
raw: post,
|
||||||
|
archetype: Archetype.private_message,
|
||||||
|
target_usernames: action['username']
|
||||||
|
)
|
||||||
|
|
||||||
|
post = creator.create
|
||||||
|
|
||||||
|
if creator.errors.present?
|
||||||
|
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
||||||
|
else
|
||||||
|
unless action['skip_redirect']
|
||||||
|
data['redirect_on_complete'] = post.topic.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_profile
|
||||||
|
return unless action['profile_updates'].length
|
||||||
|
|
||||||
|
attributes = {}
|
||||||
|
custom_fields = {}
|
||||||
|
|
||||||
|
action['profile_updates'].each do |pu|
|
||||||
|
value = pu['value']
|
||||||
|
key = pu['key']
|
||||||
|
|
||||||
|
return if data[key].blank?
|
||||||
|
|
||||||
|
if user_field || custom_field
|
||||||
|
custom_fields[user_field || custom_field] = data[key]
|
||||||
|
else
|
||||||
|
updater_key = value
|
||||||
|
if ['profile_background', 'card_background'].include?(value)
|
||||||
|
updater_key = "#{value}_upload_url"
|
||||||
|
end
|
||||||
|
attributes[updater_key.to_sym] = data[key] if updater_key
|
||||||
|
end
|
||||||
|
|
||||||
|
if ['user_avatar'].include?(value)
|
||||||
|
this_upload_id = data[key][:id]
|
||||||
|
user.create_user_avatar unless user.user_avatar
|
||||||
|
user.user_avatar.custom_upload_id = this_upload_id
|
||||||
|
user.uploaded_avatar_id = this_upload_id
|
||||||
|
user.save!
|
||||||
|
user.user_avatar.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if custom_fields.present?
|
||||||
|
attributes[:custom_fields] = custom_fields
|
||||||
|
end
|
||||||
|
|
||||||
|
if attributes.present?
|
||||||
|
user_updater = UserUpdater.new(user, user)
|
||||||
|
user_updater.update(attributes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_to_api
|
||||||
|
api_body = nil
|
||||||
|
|
||||||
|
if action['api_body'] != ""
|
||||||
|
begin
|
||||||
|
api_body_parsed = JSON.parse(action['api_body'])
|
||||||
|
rescue JSON::ParserError
|
||||||
|
raise Discourse::InvalidParameters, "Invalid API body definition: #{action['api_body']} for #{action['title']}"
|
||||||
|
end
|
||||||
|
api_body = JSON.parse(mapper.interpolate(JSON.generate(api_body_parsed)))
|
||||||
|
end
|
||||||
|
|
||||||
|
result = CustomWizard::Api::Endpoint.request(user, action['api'], action['api_endpoint'], api_body)
|
||||||
|
|
||||||
|
if error = result['error'] || (result[0] && result[0]['error'])
|
||||||
|
error = error['message'] || error
|
||||||
|
updater.errors.add(:send_to_api, error)
|
||||||
|
else
|
||||||
|
## add validation callback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def open_composer
|
||||||
|
if action['custom_title_enabled']
|
||||||
|
title = mapper.interpolate(action['custom_title'])
|
||||||
|
else
|
||||||
|
title = data[action['title']]
|
||||||
|
end
|
||||||
|
|
||||||
|
url = "/new-topic?title=#{title}"
|
||||||
|
|
||||||
|
if action['post_builder']
|
||||||
|
post = mapper.interpolate(action['post_template'])
|
||||||
|
else
|
||||||
|
post = data[action['post']]
|
||||||
|
end
|
||||||
|
|
||||||
|
url += "&body=#{post}"
|
||||||
|
|
||||||
|
if category_id = action_category_id(action, data)
|
||||||
|
if category = Category.find(category_id)
|
||||||
|
url += "&category=#{category.full_slug('/')}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if tags = action_tags(action, data)
|
||||||
|
url += "&tags=#{tags.join(',')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
data['redirect_on_complete'] = Discourse.base_uri + URI.encode(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_to_group
|
||||||
|
groups = CustomWizard::Mapper.new(
|
||||||
|
inputs: action['inputs'],
|
||||||
|
data: data,
|
||||||
|
user: user,
|
||||||
|
opts: {
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
).output
|
||||||
|
|
||||||
|
groups = groups.flatten.reduce([]) do |result, g|
|
||||||
|
begin
|
||||||
|
result.push(Integer(g))
|
||||||
|
rescue ArgumentError
|
||||||
|
group = Group.find_by(name: g)
|
||||||
|
result.push(group.id) if group
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
if groups.present?
|
||||||
|
groups.each do |group_id|
|
||||||
|
group = Group.find(group_id) if group_id
|
||||||
|
group.add(user) if group
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def route_to
|
||||||
|
url = mapper.interpolate(action['url'])
|
||||||
|
if action['code']
|
||||||
|
data[action['code']] = SecureRandom.hex(8)
|
||||||
|
url += "&#{action['code']}=#{data[action['code']]}"
|
||||||
|
end
|
||||||
|
data['route_to'] = URI.encode(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_category_id
|
||||||
|
if action['custom_category_enabled']
|
||||||
|
if action['custom_category_wizard_field']
|
||||||
|
data[action['category_id']]
|
||||||
|
elsif action['custom_category_user_field_key']
|
||||||
|
if action['custom_category_user_field_key'].include?('custom_fields')
|
||||||
|
field = action['custom_category_user_field_key'].split('.').last
|
||||||
|
user.custom_fields[field]
|
||||||
|
else
|
||||||
|
user.send(action['custom_category_user_field_key'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
action['category_id']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_tags
|
||||||
|
if action['custom_tag_enabled']
|
||||||
|
data[action['custom_tag_field']]
|
||||||
|
else
|
||||||
|
action['tags']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,4 @@
|
||||||
TagStruct = Struct.new(:id, :name)
|
|
||||||
|
|
||||||
class CustomWizard::Builder
|
class CustomWizard::Builder
|
||||||
|
|
||||||
attr_accessor :wizard, :updater, :submissions
|
attr_accessor :wizard, :updater, :submissions
|
||||||
|
|
||||||
def initialize(user=nil, wizard_id)
|
def initialize(user=nil, wizard_id)
|
||||||
|
@ -10,10 +7,7 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
@steps = data['steps']
|
@steps = data['steps']
|
||||||
@wizard = CustomWizard::Wizard.new(user, data)
|
@wizard = CustomWizard::Wizard.new(user, data)
|
||||||
|
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)) if user
|
||||||
if user
|
|
||||||
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sorted_handlers
|
def self.sorted_handlers
|
||||||
|
@ -42,47 +36,6 @@ class CustomWizard::Builder
|
||||||
@sorted_field_validators.sort_by! { |h| -h[:priority] }
|
@sorted_field_validators.sort_by! { |h| -h[:priority] }
|
||||||
end
|
end
|
||||||
|
|
||||||
USER_FIELDS = ['name', 'username', 'email', 'date_of_birth', 'title', 'locale']
|
|
||||||
PROFILE_FIELDS = ['location', 'website', 'bio_raw', 'profile_background', 'card_background']
|
|
||||||
OPERATORS = {
|
|
||||||
'eq': '==',
|
|
||||||
'gt': '>',
|
|
||||||
'lt': '<',
|
|
||||||
'gte': '>=',
|
|
||||||
'lte': '<='
|
|
||||||
}
|
|
||||||
|
|
||||||
def self.fill_placeholders(string, user, data)
|
|
||||||
result = string.gsub(/u\{(.*?)\}/) do |match|
|
|
||||||
result = ''
|
|
||||||
result = user.send($1) if USER_FIELDS.include?($1)
|
|
||||||
result = user.user_profile.send($1) if PROFILE_FIELDS.include?($1)
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
result = result.gsub(/w\{(.*?)\}/) { |match| recurse(data, [*$1.split('.')]) }
|
|
||||||
|
|
||||||
result.gsub(/v\{(.*?)\}/) do |match|
|
|
||||||
attrs = $1.split(':')
|
|
||||||
key = attrs.first
|
|
||||||
format = attrs.length > 1 ? attrs.last : nil
|
|
||||||
v = nil
|
|
||||||
|
|
||||||
if key == 'time'
|
|
||||||
time_format = format.present? ? format : "%B %-d, %Y"
|
|
||||||
v = Time.now.strftime(time_format)
|
|
||||||
end
|
|
||||||
|
|
||||||
v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.recurse(data, keys)
|
|
||||||
k = keys.shift
|
|
||||||
result = data[k]
|
|
||||||
keys.empty? ? result : self.recurse(result, keys)
|
|
||||||
end
|
|
||||||
|
|
||||||
def build(build_opts = {}, params = {})
|
def build(build_opts = {}, params = {})
|
||||||
|
|
||||||
return @wizard if !SiteSetting.custom_wizard_enabled ||
|
return @wizard if !SiteSetting.custom_wizard_enabled ||
|
||||||
|
@ -135,13 +88,24 @@ class CustomWizard::Builder
|
||||||
step.permitted = false
|
step.permitted = false
|
||||||
else
|
else
|
||||||
required_data.each do |required|
|
required_data.each do |required|
|
||||||
pairs = required['pairs'].map { |p| p['value'] = @submissions.last[p['value']] }
|
pairs = required['pairs'].map do |p|
|
||||||
step.permitted = false unless validate_pairs(pairs)
|
p['key'] = @submissions.last[p['key']]
|
||||||
|
end
|
||||||
|
|
||||||
|
unless CustomWizard::Mapper.new(
|
||||||
|
user: @wizard.user,
|
||||||
|
data: @submissions.last
|
||||||
|
).validate_pairs(pairs)
|
||||||
|
step.permitted = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if !step.permitted
|
if !step.permitted
|
||||||
step.permitted_message = step_template['required_data_message'] if step_template['required_data_message']
|
if step_template['required_data_message']
|
||||||
|
step.permitted_message = step_template['required_data_message']
|
||||||
|
end
|
||||||
|
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -183,7 +147,12 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
if step_template['actions'] && step_template['actions'].length && data
|
if step_template['actions'] && step_template['actions'].length && data
|
||||||
step_template['actions'].each do |action|
|
step_template['actions'].each do |action|
|
||||||
self.send(action['type'].to_sym, user, action, data)
|
CustomWizard::Action.new(
|
||||||
|
action: action,
|
||||||
|
user: user,
|
||||||
|
data: data,
|
||||||
|
updater: updater
|
||||||
|
).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -238,6 +207,10 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
params[:value] = prefill_field(field_template, step_template) || params[:value]
|
params[:value] = prefill_field(field_template, step_template) || params[:value]
|
||||||
|
|
||||||
|
if field_template['type'] === 'group'
|
||||||
|
params[:value] = params[:value].first
|
||||||
|
end
|
||||||
|
|
||||||
if field_template['type'] === 'checkbox'
|
if field_template['type'] === 'checkbox'
|
||||||
params[:value] = standardise_boolean(params[:value])
|
params[:value] = standardise_boolean(params[:value])
|
||||||
end
|
end
|
||||||
|
@ -262,8 +235,12 @@ class CustomWizard::Builder
|
||||||
@wizard.needs_groups = true
|
@wizard.needs_groups = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if (prefill = field_template['filters']).present?
|
if (content = field_template['content']).present?
|
||||||
params[:filter] = get_output(field_template['filters'])
|
params[:content] = CustomWizard::Mapper.new(
|
||||||
|
inputs: content,
|
||||||
|
user: @wizard.user,
|
||||||
|
data: @submissions.last
|
||||||
|
).output
|
||||||
end
|
end
|
||||||
|
|
||||||
field = step.add_field(params)
|
field = step.add_field(params)
|
||||||
|
@ -275,76 +252,11 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
def prefill_field(field_template, step_template)
|
def prefill_field(field_template, step_template)
|
||||||
if (prefill = field_template['prefill']).present?
|
if (prefill = field_template['prefill']).present?
|
||||||
get_output(prefill)
|
CustomWizard::Mapper.new(
|
||||||
end
|
inputs: prefill,
|
||||||
end
|
user: @wizard.user,
|
||||||
|
data: @submissions.last
|
||||||
def get_output(inputs, opts = {})
|
).output
|
||||||
output = opts[:multiple] ? [] : nil
|
|
||||||
|
|
||||||
inputs.each do |input|
|
|
||||||
if input['type'] === 'conditional' && validate_pairs(input['pairs'])
|
|
||||||
if opts[:multiple]
|
|
||||||
output.push(get_field(input['output'], input['output_type'], opts))
|
|
||||||
else
|
|
||||||
output = get_field(input['output'], input['output_type'], opts)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if input['type'] === 'assignment'
|
|
||||||
value = get_field(input['output'], input['output_type'], opts)
|
|
||||||
|
|
||||||
if opts[:multiple]
|
|
||||||
output.push(value)
|
|
||||||
else
|
|
||||||
output = value
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_pairs(pairs)
|
|
||||||
failed = false
|
|
||||||
|
|
||||||
pairs.each do |pair|
|
|
||||||
key = get_field(pair['key'], pair['key_type'])
|
|
||||||
value = get_field(pair['value'], pair['value_type'])
|
|
||||||
failed = true unless key.public_send(get_operator(pair['connector']), value)
|
|
||||||
end
|
|
||||||
|
|
||||||
!failed
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_operator(connector)
|
|
||||||
OPERATORS[connector] || '=='
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_field(value, type, opts = {})
|
|
||||||
method = "get_#{type}_field"
|
|
||||||
|
|
||||||
if self.respond_to?(method)
|
|
||||||
self.send(method, value, opts)
|
|
||||||
else
|
|
||||||
value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_wizard_field(value, opts = {})
|
|
||||||
data = opts[:data] || @submissions.last
|
|
||||||
data && !data.key?("submitted_at") && data[value]
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_user_field(value, opts = {})
|
|
||||||
if value.include?('user_field_')
|
|
||||||
UserCustomField.where(user_id: @wizard.user.id, name: value).pluck(:value).first
|
|
||||||
elsif UserProfile.column_names.include? value
|
|
||||||
UserProfile.find_by(user_id: @wizard.user.id).send(value)
|
|
||||||
elsif User.column_names.include? value
|
|
||||||
User.find(@wizard.user.id).send(value)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -382,7 +294,10 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
|
|
||||||
if min_length && value.is_a?(String) && value.strip.length < min_length.to_i
|
if min_length && value.is_a?(String) && value.strip.length < min_length.to_i
|
||||||
updater.errors.add(field['id'].to_s, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
|
updater.errors.add(
|
||||||
|
field['id'].to_s,
|
||||||
|
I18n.t('wizard.field.too_short', label: label, min: min_length.to_i)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
## ensure all checkboxes are booleans
|
## ensure all checkboxes are booleans
|
||||||
|
@ -405,245 +320,6 @@ class CustomWizard::Builder
|
||||||
ActiveRecord::Type::Boolean.new.cast(value)
|
ActiveRecord::Type::Boolean.new.cast(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_topic(user, action, data)
|
|
||||||
if action['custom_title_enabled']
|
|
||||||
title = CustomWizard::Builder.fill_placeholders(action['custom_title'], user, data)
|
|
||||||
else
|
|
||||||
title = data[action['title']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if action['post_builder']
|
|
||||||
post = CustomWizard::Builder.fill_placeholders(action['post_template'], user, data)
|
|
||||||
else
|
|
||||||
post = data[action['post']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if title
|
|
||||||
params = {
|
|
||||||
title: title,
|
|
||||||
raw: post,
|
|
||||||
skip_validations: true
|
|
||||||
}
|
|
||||||
|
|
||||||
params[:category] = action_category_id(action, data)
|
|
||||||
|
|
||||||
tags = action_tags(action, data)
|
|
||||||
|
|
||||||
params[:tags] = tags
|
|
||||||
|
|
||||||
topic_custom_fields = {}
|
|
||||||
|
|
||||||
if action['add_fields']
|
|
||||||
action['add_fields'].each do |field|
|
|
||||||
value = field['value_custom'].present? ? field['value_custom'] : data[field['value']]
|
|
||||||
key = field['key']
|
|
||||||
|
|
||||||
if key && (value.present? || value === false)
|
|
||||||
if key.include?('custom_fields')
|
|
||||||
keyArr = key.split('.')
|
|
||||||
|
|
||||||
if keyArr.length === 3
|
|
||||||
custom_key = keyArr.last
|
|
||||||
type = keyArr.first
|
|
||||||
|
|
||||||
if type === 'topic'
|
|
||||||
topic_custom_fields[custom_key] = value
|
|
||||||
elsif type === 'post'
|
|
||||||
params[:custom_fields] ||= {}
|
|
||||||
params[:custom_fields][custom_key.to_sym] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
value = [*value] + tags if key === 'tags'
|
|
||||||
params[key.to_sym] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
creator = PostCreator.new(user, params)
|
|
||||||
post = creator.create
|
|
||||||
|
|
||||||
if creator.errors.present?
|
|
||||||
updater.errors.add(:create_topic, creator.errors.full_messages.join(" "))
|
|
||||||
else
|
|
||||||
if topic_custom_fields.present?
|
|
||||||
topic_custom_fields.each do |k, v|
|
|
||||||
post.topic.custom_fields[k] = v
|
|
||||||
end
|
|
||||||
post.topic.save_custom_fields(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless action['skip_redirect']
|
|
||||||
data['redirect_on_complete'] = post.topic.url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_message(user, action, data)
|
|
||||||
|
|
||||||
if action['required'].present? && data[action['required']].blank?
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if action['custom_title_enabled']
|
|
||||||
title = CustomWizard::Builder.fill_placeholders(action['custom_title'], user, data)
|
|
||||||
else
|
|
||||||
title = data[action['title']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if action['post_builder']
|
|
||||||
post = CustomWizard::Builder.fill_placeholders(action['post_template'], user, data)
|
|
||||||
else
|
|
||||||
post = data[action['post']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if title && post
|
|
||||||
creator = PostCreator.new(user,
|
|
||||||
title: title,
|
|
||||||
raw: post,
|
|
||||||
archetype: Archetype.private_message,
|
|
||||||
target_usernames: action['username']
|
|
||||||
)
|
|
||||||
|
|
||||||
post = creator.create
|
|
||||||
|
|
||||||
if creator.errors.present?
|
|
||||||
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
|
||||||
else
|
|
||||||
unless action['skip_redirect']
|
|
||||||
data['redirect_on_complete'] = post.topic.url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_profile(user, action, data)
|
|
||||||
return unless action['profile_updates'].length
|
|
||||||
|
|
||||||
attributes = {}
|
|
||||||
custom_fields = {}
|
|
||||||
|
|
||||||
action['profile_updates'].each do |pu|
|
|
||||||
value = pu['value']
|
|
||||||
key = pu['key']
|
|
||||||
|
|
||||||
return if data[key].blank?
|
|
||||||
|
|
||||||
if user_field || custom_field
|
|
||||||
custom_fields[user_field || custom_field] = data[key]
|
|
||||||
else
|
|
||||||
updater_key = value
|
|
||||||
if ['profile_background', 'card_background'].include?(value)
|
|
||||||
updater_key = "#{value}_upload_url"
|
|
||||||
end
|
|
||||||
attributes[updater_key.to_sym] = data[key] if updater_key
|
|
||||||
end
|
|
||||||
|
|
||||||
if ['user_avatar'].include?(value)
|
|
||||||
this_upload_id = data[key][:id]
|
|
||||||
user.create_user_avatar unless user.user_avatar
|
|
||||||
user.user_avatar.custom_upload_id = this_upload_id
|
|
||||||
user.uploaded_avatar_id = this_upload_id
|
|
||||||
user.save!
|
|
||||||
user.user_avatar.save!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if custom_fields.present?
|
|
||||||
attributes[:custom_fields] = custom_fields
|
|
||||||
end
|
|
||||||
|
|
||||||
if attributes.present?
|
|
||||||
user_updater = UserUpdater.new(user, user)
|
|
||||||
user_updater.update(attributes)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_to_api(user, action, data)
|
|
||||||
api_body = nil
|
|
||||||
|
|
||||||
if action['api_body'] != ""
|
|
||||||
begin
|
|
||||||
api_body_parsed = JSON.parse(action['api_body'])
|
|
||||||
rescue JSON::ParserError
|
|
||||||
raise Discourse::InvalidParameters, "Invalid API body definition: #{action['api_body']} for #{action['title']}"
|
|
||||||
end
|
|
||||||
api_body = JSON.parse(CustomWizard::Builder.fill_placeholders(JSON.generate(api_body_parsed), user, data))
|
|
||||||
end
|
|
||||||
|
|
||||||
result = CustomWizard::Api::Endpoint.request(user, action['api'], action['api_endpoint'], api_body)
|
|
||||||
|
|
||||||
if error = result['error'] || (result[0] && result[0]['error'])
|
|
||||||
error = error['message'] || error
|
|
||||||
updater.errors.add(:send_to_api, error)
|
|
||||||
else
|
|
||||||
## add validation callback
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def open_composer(user, action, data)
|
|
||||||
if action['custom_title_enabled']
|
|
||||||
title = CustomWizard::Builder.fill_placeholders(action['custom_title'], user, data)
|
|
||||||
else
|
|
||||||
title = data[action['title']]
|
|
||||||
end
|
|
||||||
|
|
||||||
url = "/new-topic?title=#{title}"
|
|
||||||
|
|
||||||
if action['post_builder']
|
|
||||||
post = CustomWizard::Builder.fill_placeholders(action['post_template'], user, data)
|
|
||||||
else
|
|
||||||
post = data[action['post']]
|
|
||||||
end
|
|
||||||
|
|
||||||
url += "&body=#{post}"
|
|
||||||
|
|
||||||
if category_id = action_category_id(action, data)
|
|
||||||
if category = Category.find(category_id)
|
|
||||||
url += "&category=#{category.full_slug('/')}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if tags = action_tags(action, data)
|
|
||||||
url += "&tags=#{tags.join(',')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
data['redirect_on_complete'] = Discourse.base_uri + URI.encode(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_to_group(user, action, data)
|
|
||||||
groups = get_output(action['inputs'], multiple: true, data: data)
|
|
||||||
|
|
||||||
groups = groups.flatten.reduce([]) do |result, g|
|
|
||||||
begin
|
|
||||||
result.push(Integer(g))
|
|
||||||
rescue ArgumentError
|
|
||||||
group = Group.find_by(name: g)
|
|
||||||
result.push(group.id) if group
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
if groups.present?
|
|
||||||
groups.each do |group_id|
|
|
||||||
group = Group.find(group_id) if group_id
|
|
||||||
group.add(user) if group
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def route_to(user, action, data)
|
|
||||||
url = CustomWizard::Builder.fill_placeholders(action['url'], user, data)
|
|
||||||
if action['code']
|
|
||||||
data[action['code']] = SecureRandom.hex(8)
|
|
||||||
url += "&#{action['code']}=#{data[action['code']]}"
|
|
||||||
end
|
|
||||||
data['route_to'] = URI.encode(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_submissions(data, final_step)
|
def save_submissions(data, final_step)
|
||||||
if final_step
|
if final_step
|
||||||
data['submitted_at'] = Time.now.iso8601
|
data['submitted_at'] = Time.now.iso8601
|
||||||
|
@ -661,29 +337,4 @@ class CustomWizard::Builder
|
||||||
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
|
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
|
||||||
@wizard.reset
|
@wizard.reset
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_category_id(action, data)
|
|
||||||
if action['custom_category_enabled']
|
|
||||||
if action['custom_category_wizard_field']
|
|
||||||
data[action['category_id']]
|
|
||||||
elsif action['custom_category_user_field_key']
|
|
||||||
if action['custom_category_user_field_key'].include?('custom_fields')
|
|
||||||
field = action['custom_category_user_field_key'].split('.').last
|
|
||||||
user.custom_fields[field]
|
|
||||||
else
|
|
||||||
user.send(action['custom_category_user_field_key'])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
action['category_id']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_tags(action, data)
|
|
||||||
if action['custom_tag_enabled']
|
|
||||||
data[action['custom_tag_field']]
|
|
||||||
else
|
|
||||||
action['tags']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
114
lib/custom_wizard/mapper.rb
Normale Datei
114
lib/custom_wizard/mapper.rb
Normale Datei
|
@ -0,0 +1,114 @@
|
||||||
|
class CustomWizard::Mapper
|
||||||
|
attr_accessor :inputs, :data, :user
|
||||||
|
|
||||||
|
USER_FIELDS = ['name', 'username', 'email', 'date_of_birth', 'title', 'locale', 'trust_level']
|
||||||
|
PROFILE_FIELDS = ['location', 'website', 'bio_raw']
|
||||||
|
OPERATORS = { 'eq': '==', 'gt': '>', 'lt': '<', 'gte': '>=', 'lte': '<=' }
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
@inputs = params[:inputs] || {}
|
||||||
|
@data = params[:data] || {}
|
||||||
|
@user = params[:user]
|
||||||
|
@opts = params[:opts] || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def output
|
||||||
|
multiple = @opts[:multiple]
|
||||||
|
output = multiple ? [] : nil
|
||||||
|
|
||||||
|
inputs.each do |input|
|
||||||
|
if input['type'] === 'conditional' && validate_pairs(input['pairs'])
|
||||||
|
if multiple
|
||||||
|
output.push(map_field(input['output'], input['output_type']))
|
||||||
|
else
|
||||||
|
output = map_field(input['output'], input['output_type'])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if input['type'] === 'assignment'
|
||||||
|
value = map_field(input['output'], input['output_type'])
|
||||||
|
|
||||||
|
if @opts[:multiple]
|
||||||
|
output.push(value)
|
||||||
|
else
|
||||||
|
output = value
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_pairs(pairs)
|
||||||
|
failed = false
|
||||||
|
|
||||||
|
pairs.each do |pair|
|
||||||
|
key = map_field(pair['key'], pair['key_type'])
|
||||||
|
value = map_field(pair['value'], pair['value_type'])
|
||||||
|
failed = true unless key.public_send(operator(pair['connector']), value)
|
||||||
|
end
|
||||||
|
|
||||||
|
!failed
|
||||||
|
end
|
||||||
|
|
||||||
|
def operator(connector)
|
||||||
|
OPERATORS[connector] || '=='
|
||||||
|
end
|
||||||
|
|
||||||
|
def map_field(value, type)
|
||||||
|
method = "#{type}_field"
|
||||||
|
|
||||||
|
if self.respond_to?(method)
|
||||||
|
self.send(method, value)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wizard_field(value)
|
||||||
|
data && !data.key?("submitted_at") && data[value]
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_field(value)
|
||||||
|
if value.include?('user_field_')
|
||||||
|
UserCustomField.where(user_id: user.id, name: value).pluck(:value).first
|
||||||
|
elsif PROFILE_FIELDS.include?(value)
|
||||||
|
UserProfile.find_by(user_id: user.id).send(value)
|
||||||
|
elsif USER_FIELDS.include?(value)
|
||||||
|
User.find(user.id).send(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def interpolate(string)
|
||||||
|
result = string.gsub(/u\{(.*?)\}/) do |match|
|
||||||
|
result = ''
|
||||||
|
result = user.send($1) if USER_FIELDS.include?($1)
|
||||||
|
result = user.user_profile.send($1) if PROFILE_FIELDS.include?($1)
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result.gsub(/w\{(.*?)\}/) { |match| recurse(data, [*$1.split('.')]) }
|
||||||
|
|
||||||
|
result.gsub(/v\{(.*?)\}/) do |match|
|
||||||
|
attrs = $1.split(':')
|
||||||
|
key = attrs.first
|
||||||
|
format = attrs.length > 1 ? attrs.last : nil
|
||||||
|
val = nil
|
||||||
|
|
||||||
|
if key == 'time'
|
||||||
|
time_format = format.present? ? format : "%B %-d, %Y"
|
||||||
|
val = Time.now.strftime(time_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def recurse(data, keys)
|
||||||
|
k = keys.shift
|
||||||
|
result = data[k]
|
||||||
|
keys.empty? ? result : self.recurse(result, keys)
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,12 +8,12 @@ class CustomWizard::Template
|
||||||
:multiple_submissions,
|
:multiple_submissions,
|
||||||
:prompt_completion,
|
:prompt_completion,
|
||||||
:restart_on_revisit,
|
:restart_on_revisit,
|
||||||
:min_trust,
|
|
||||||
:after_signup,
|
:after_signup,
|
||||||
:after_time,
|
:after_time,
|
||||||
:after_time_scheduled,
|
:after_time_scheduled,
|
||||||
:required,
|
:required,
|
||||||
:theme_id
|
:theme_id,
|
||||||
|
:permitted
|
||||||
|
|
||||||
def initialize(data)
|
def initialize(data)
|
||||||
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
||||||
|
@ -28,12 +28,12 @@ class CustomWizard::Template
|
||||||
@multiple_submissions = data['multiple_submissions'] || false
|
@multiple_submissions = data['multiple_submissions'] || false
|
||||||
@prompt_completion = data['prompt_completion'] || false
|
@prompt_completion = data['prompt_completion'] || false
|
||||||
@restart_on_revisit = data['restart_on_revisit'] || false
|
@restart_on_revisit = data['restart_on_revisit'] || false
|
||||||
@min_trust = data['min_trust'] || 0
|
|
||||||
@after_signup = data['after_signup']
|
@after_signup = data['after_signup']
|
||||||
@after_time = data['after_time']
|
@after_time = data['after_time']
|
||||||
@after_time_scheduled = data['after_time_scheduled']
|
@after_time_scheduled = data['after_time_scheduled']
|
||||||
@required = data['required'] || false
|
@required = data['required'] || false
|
||||||
@theme_id = data['theme_id']
|
@theme_id = data['theme_id']
|
||||||
|
@permitted = data['permitted'] || nil
|
||||||
|
|
||||||
if data['theme']
|
if data['theme']
|
||||||
theme = Theme.find_by(name: data['theme'])
|
theme = Theme.find_by(name: data['theme'])
|
||||||
|
|
|
@ -14,13 +14,13 @@ class CustomWizard::Wizard
|
||||||
:background,
|
:background,
|
||||||
:save_submissions,
|
:save_submissions,
|
||||||
:multiple_submissions,
|
:multiple_submissions,
|
||||||
:min_trust,
|
|
||||||
:after_time,
|
:after_time,
|
||||||
:after_time_scheduled,
|
:after_time_scheduled,
|
||||||
:after_signup,
|
:after_signup,
|
||||||
:required,
|
:required,
|
||||||
:prompt_completion,
|
:prompt_completion,
|
||||||
:restart_on_revisit,
|
:restart_on_revisit,
|
||||||
|
:permitted,
|
||||||
:needs_categories,
|
:needs_categories,
|
||||||
:needs_groups
|
:needs_groups
|
||||||
|
|
||||||
|
@ -127,7 +127,10 @@ class CustomWizard::Wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted?
|
def permitted?
|
||||||
user && (user.staff? || user.trust_level.to_i >= min_trust.to_i)
|
return false unless user
|
||||||
|
return true if user.admin? || permitted.blank?
|
||||||
|
group_ids = permitted.first['output']
|
||||||
|
return GroupUser.exists?(group_id: group_ids, user_id: user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset
|
def reset
|
||||||
|
@ -147,21 +150,36 @@ class CustomWizard::Wizard
|
||||||
@groups ||= ::Site.new(Guardian.new(@user)).groups
|
@groups ||= ::Site.new(Guardian.new(@user)).groups
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.after_signup
|
def self.templates(filter = nil)
|
||||||
rows = PluginStoreRow.where(plugin_name: 'custom_wizard')
|
rows = [*PluginStoreRow.where(plugin_name: 'custom_wizard')]
|
||||||
wizards = [*rows].select { |r| r.value['after_signup'] }
|
rows = rows.select { |r| r.value[filter] } if filter
|
||||||
if wizards.any?
|
rows
|
||||||
wizards.first.key
|
end
|
||||||
|
|
||||||
|
def self.after_signup(user)
|
||||||
|
if (temps = templates('after_signup')).any?
|
||||||
|
wizard = nil
|
||||||
|
|
||||||
|
temps
|
||||||
|
.sort_by { |t| template.value['permitted'].present? }
|
||||||
|
.each do |template|
|
||||||
|
wizard = CustomWizard::Wizard.new(user, template)
|
||||||
|
|
||||||
|
if wizard.permitted?
|
||||||
|
wizard = wizard
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wizard
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.prompt_completion(user)
|
def self.prompt_completion(user)
|
||||||
rows = PluginStoreRow.where(plugin_name: 'custom_wizard')
|
if (temps = templates('prompt_completion')).any?
|
||||||
wizards = [*rows].select { |r| r.value['prompt_completion'] }
|
temps.reduce([]) do |result, w|
|
||||||
if wizards.any?
|
|
||||||
wizards.reduce([]) do |result, w|
|
|
||||||
data = ::JSON.parse(w.value)
|
data = ::JSON.parse(w.value)
|
||||||
id = data['id']
|
id = data['id']
|
||||||
name = data['name']
|
name = data['name']
|
||||||
|
@ -175,10 +193,8 @@ class CustomWizard::Wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.restart_on_revisit
|
def self.restart_on_revisit
|
||||||
rows = PluginStoreRow.where(plugin_name: 'custom_wizard')
|
if (temps = templates('restart_on_revisit')).any?
|
||||||
wizards = [*rows].select { |r| r.value['restart_on_revisit'] }
|
temps.first.key
|
||||||
if wizards.any?
|
|
||||||
wizards.first.key
|
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ module CustomWizardFieldExtension
|
||||||
:file_types,
|
:file_types,
|
||||||
:limit,
|
:limit,
|
||||||
:property,
|
:property,
|
||||||
:filter
|
:content
|
||||||
|
|
||||||
attr_accessor :dropdown_none
|
attr_accessor :dropdown_none
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ module CustomWizardFieldExtension
|
||||||
@file_types = attrs[:file_types]
|
@file_types = attrs[:file_types]
|
||||||
@limit = attrs[:limit]
|
@limit = attrs[:limit]
|
||||||
@property = attrs[:property]
|
@property = attrs[:property]
|
||||||
@filter = attrs[:filter]
|
@content = attrs[:content]
|
||||||
end
|
end
|
||||||
|
|
||||||
def label
|
def label
|
||||||
|
|
82
plugin.rb
82
plugin.rb
|
@ -37,42 +37,44 @@ if respond_to?(:register_svg_icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
[
|
%w[
|
||||||
'../lib/custom_wizard/engine.rb',
|
../lib/custom_wizard/engine.rb
|
||||||
'../config/routes.rb',
|
../config/routes.rb
|
||||||
'../controllers/custom_wizard/wizard.rb',
|
../controllers/custom_wizard/wizard.rb
|
||||||
'../controllers/custom_wizard/steps.rb',
|
../controllers/custom_wizard/steps.rb
|
||||||
'../controllers/custom_wizard/admin.rb',
|
../controllers/custom_wizard/admin.rb
|
||||||
'../controllers/custom_wizard/transfer.rb',
|
../controllers/custom_wizard/transfer.rb
|
||||||
'../controllers/custom_wizard/api.rb',
|
../controllers/custom_wizard/api.rb
|
||||||
'../controllers/application_controller.rb',
|
../controllers/application_controller.rb
|
||||||
'../controllers/extra_locales_controller.rb',
|
../controllers/extra_locales_controller.rb
|
||||||
'../controllers/invites_controller.rb',
|
../controllers/invites_controller.rb
|
||||||
'../jobs/clear_after_time_wizard.rb',
|
../jobs/clear_after_time_wizard.rb
|
||||||
'../jobs/refresh_api_access_token.rb',
|
../jobs/refresh_api_access_token.rb
|
||||||
'../jobs/set_after_time_wizard.rb',
|
../jobs/set_after_time_wizard.rb
|
||||||
'../lib/custom_wizard/builder.rb',
|
../lib/custom_wizard/actions.rb
|
||||||
'../lib/custom_wizard/field.rb',
|
../lib/custom_wizard/builder.rb
|
||||||
'../lib/custom_wizard/step_updater.rb',
|
../lib/custom_wizard/field.rb
|
||||||
'../lib/custom_wizard/template.rb',
|
../lib/custom_wizard/mapper.rb
|
||||||
'../lib/custom_wizard/wizard.rb',
|
../lib/custom_wizard/step_updater.rb
|
||||||
'../lib/custom_wizard/api/api.rb',
|
../lib/custom_wizard/template.rb
|
||||||
'../lib/custom_wizard/api/authorization.rb',
|
../lib/custom_wizard/wizard.rb
|
||||||
'../lib/custom_wizard/api/endpoint.rb',
|
../lib/custom_wizard/api/api.rb
|
||||||
'../lib/custom_wizard/api/log_entry.rb',
|
../lib/custom_wizard/api/authorization.rb
|
||||||
'../lib/wizard/choice.rb',
|
../lib/custom_wizard/api/endpoint.rb
|
||||||
'../lib/wizard/field.rb',
|
../lib/custom_wizard/api/log_entry.rb
|
||||||
'../lib/wizard/step.rb',
|
../lib/wizard/choice.rb
|
||||||
'../serializers/custom_wizard/api/authorization_serializer.rb',
|
../lib/wizard/field.rb
|
||||||
'../serializers/custom_wizard/api/basic_endpoint_serializer.rb',
|
../lib/wizard/step.rb
|
||||||
'../serializers/custom_wizard/api/endpoint_serializer.rb',
|
../serializers/custom_wizard/api/authorization_serializer.rb
|
||||||
'../serializers/custom_wizard/api/log_serializer.rb',
|
../serializers/custom_wizard/api/basic_endpoint_serializer.rb
|
||||||
'../serializers/custom_wizard/api_serializer.rb',
|
../serializers/custom_wizard/api/endpoint_serializer.rb
|
||||||
'../serializers/custom_wizard/basic_api_serializer.rb',
|
../serializers/custom_wizard/api/log_serializer.rb
|
||||||
'../serializers/custom_wizard/wizard_field_serializer.rb',
|
../serializers/custom_wizard/api_serializer.rb
|
||||||
'../serializers/custom_wizard/wizard_step_serializer.rb',
|
../serializers/custom_wizard/basic_api_serializer.rb
|
||||||
'../serializers/custom_wizard/wizard_serializer.rb',
|
../serializers/custom_wizard/wizard_field_serializer.rb
|
||||||
'../serializers/site_serializer.rb'
|
../serializers/custom_wizard/wizard_step_serializer.rb
|
||||||
|
../serializers/custom_wizard/wizard_serializer.rb
|
||||||
|
../serializers/site_serializer.rb
|
||||||
].each do |path|
|
].each do |path|
|
||||||
load File.expand_path(path, __FILE__)
|
load File.expand_path(path, __FILE__)
|
||||||
end
|
end
|
||||||
|
@ -85,13 +87,11 @@ after_initialize do
|
||||||
|
|
||||||
if user &&
|
if user &&
|
||||||
user.first_seen_at.blank? &&
|
user.first_seen_at.blank? &&
|
||||||
wizard_id = CustomWizard::Wizard.after_signup
|
wizard = CustomWizard::Wizard.after_signup(user)
|
||||||
|
|
||||||
wizard = CustomWizard::Wizard.create(user, wizard_id)
|
if !wizard.completed?
|
||||||
|
|
||||||
if !wizard.completed? && wizard.permitted?
|
|
||||||
custom_redirect = true
|
custom_redirect = true
|
||||||
CustomWizard::Wizard.set_wizard_redirect(user, wizard_id)
|
CustomWizard::Wizard.set_wizard_redirect(user, wizard.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ class CustomWizardFieldSerializer < ::WizardFieldSerializer
|
||||||
:file_types,
|
:file_types,
|
||||||
:limit,
|
:limit,
|
||||||
:property,
|
:property,
|
||||||
:filter
|
:content
|
||||||
|
|
||||||
has_many :choices, serializer: WizardFieldChoiceSerializer, embed: :objects
|
has_many :choices, serializer: WizardFieldChoiceSerializer, embed: :objects
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class CustomWizardFieldSerializer < ::WizardFieldSerializer
|
||||||
object.property
|
object.property
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter
|
def content
|
||||||
object.filter
|
object.content
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -7,7 +7,6 @@ class CustomWizardSerializer < ::WizardSerializer
|
||||||
:background,
|
:background,
|
||||||
:completed,
|
:completed,
|
||||||
:required,
|
:required,
|
||||||
:min_trust,
|
|
||||||
:permitted,
|
:permitted,
|
||||||
:uncategorized_category_id
|
:uncategorized_category_id
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren