Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-26 02:50:28 +01:00
Merge branch 'master' into sprockets-fix
Dieser Commit ist enthalten in:
Commit
7b9a54590b
42 geänderte Dateien mit 469 neuen und 178 gelöschten Zeilen
2
.github/workflows/plugin-linting.yml
gevendort
2
.github/workflows/plugin-linting.yml
gevendort
|
@ -6,6 +6,8 @@ on:
|
||||||
- master
|
- master
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
23
.github/workflows/plugin-tests.yml
gevendort
23
.github/workflows/plugin-tests.yml
gevendort
|
@ -6,6 +6,8 @@ on:
|
||||||
- master
|
- master
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -51,23 +53,26 @@ jobs:
|
||||||
repository: discourse/discourse
|
repository: discourse/discourse
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Install plugin
|
- name: Install plugin
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: plugins/${{ github.event.repository.name }}
|
path: plugins/${{ env.REPOSITORY_NAME }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Check spec existence
|
- name: Check spec existence
|
||||||
id: check_spec
|
id: check_spec
|
||||||
uses: andstor/file-existence-action@v1
|
uses: andstor/file-existence-action@v1
|
||||||
with:
|
with:
|
||||||
files: "plugins/${{ github.event.repository.name }}/spec"
|
files: "plugins/${{ env.REPOSITORY_NAME }}/spec"
|
||||||
|
|
||||||
- name: Check qunit existence
|
- name: Check qunit existence
|
||||||
id: check_qunit
|
id: check_qunit
|
||||||
uses: andstor/file-existence-action@v1
|
uses: andstor/file-existence-action@v1
|
||||||
with:
|
with:
|
||||||
files: "plugins/${{ github.event.repository.name }}/test/javascripts"
|
files: "plugins/${{ env.REPOSITORY_NAME }}/test/javascripts"
|
||||||
|
|
||||||
- name: Setup Git
|
- name: Setup Git
|
||||||
run: |
|
run: |
|
||||||
|
@ -100,7 +105,7 @@ jobs:
|
||||||
|
|
||||||
- name: Lint English locale
|
- name: Lint English locale
|
||||||
if: matrix.build_type == 'backend'
|
if: matrix.build_type == 'backend'
|
||||||
run: bundle exec ruby script/i18n_lint.rb "plugins/${{ github.event.repository.name }}/locales/{client,server}.en.yml"
|
run: bundle exec ruby script/i18n_lint.rb "plugins/${{ env.REPOSITORY_NAME }}/locales/{client,server}.en.yml"
|
||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
|
@ -123,15 +128,11 @@ jobs:
|
||||||
bin/rake db:create
|
bin/rake db:create
|
||||||
bin/rake db:migrate
|
bin/rake db:migrate
|
||||||
|
|
||||||
- name: Plugin RSpec
|
- name: Plugin RSpec with Coverage
|
||||||
if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true'
|
if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true'
|
||||||
run: bin/rake plugin:spec[${{ github.event.repository.name }}]
|
run: SIMPLECOV=1 bin/rake plugin:spec[${{ env.REPOSITORY_NAME }}]
|
||||||
|
|
||||||
- name: Plugin QUnit
|
- name: Plugin QUnit
|
||||||
if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true'
|
if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true'
|
||||||
run: bundle exec rake plugin:qunit['${{ github.event.repository.name }}','1200000']
|
run: bundle exec rake plugin:qunit['${{ env.REPOSITORY_NAME }}','1200000']
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
- name: Simplecov Report
|
|
||||||
if: matrix.build_type == 'backend'
|
|
||||||
run: COVERAGE=1 bin/rake plugin:spec[${{ github.event.repository.name }}]
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
import { alias, or } from "@ember/object/computed";
|
import { alias, equal, or } from "@ember/object/computed";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
const generateContent = function (array, type) {
|
const generateContent = function (array, type) {
|
||||||
|
@ -29,6 +29,7 @@ export default Component.extend({
|
||||||
loading: or("saving", "destroying"),
|
loading: or("saving", "destroying"),
|
||||||
destroyDisabled: alias("loading"),
|
destroyDisabled: alias("loading"),
|
||||||
closeDisabled: alias("loading"),
|
closeDisabled: alias("loading"),
|
||||||
|
isExternal: equal("field.id", "external"),
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
||||||
|
@ -61,13 +62,14 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"saving",
|
"saving",
|
||||||
|
"isExternal",
|
||||||
"field.name",
|
"field.name",
|
||||||
"field.klass",
|
"field.klass",
|
||||||
"field.type",
|
"field.type",
|
||||||
"field.serializers"
|
"field.serializers"
|
||||||
)
|
)
|
||||||
saveDisabled(saving) {
|
saveDisabled(saving, isExternal) {
|
||||||
if (saving) {
|
if (saving || isExternal) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,11 @@ export default Component.extend(UndoChanges, {
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("action.type")
|
||||||
|
customFieldsContext(type) {
|
||||||
|
return `action.${type}`;
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed("wizard.steps")
|
@discourseComputed("wizard.steps")
|
||||||
runAfterContent(steps) {
|
runAfterContent(steps) {
|
||||||
let content = steps.map(function (step) {
|
let content = steps.map(function (step) {
|
||||||
|
|
|
@ -6,11 +6,24 @@ import {
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
||||||
import { generateName, snakeCase, userProperties } from "../lib/wizard";
|
import {
|
||||||
|
generateName,
|
||||||
|
sentenceCase,
|
||||||
|
snakeCase,
|
||||||
|
userProperties,
|
||||||
|
} from "../lib/wizard";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { bind, later } from "@ember/runloop";
|
import { bind, later } from "@ember/runloop";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
|
const customFieldActionMap = {
|
||||||
|
topic: ["create_topic", "send_message"],
|
||||||
|
post: ["create_topic", "send_message"],
|
||||||
|
category: ["create_category"],
|
||||||
|
group: ["create_group"],
|
||||||
|
user: ["update_profile"],
|
||||||
|
};
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNameBindings: [":mapper-selector", "activeType"],
|
classNameBindings: [":mapper-selector", "activeType"],
|
||||||
|
|
||||||
|
@ -188,11 +201,19 @@ export default Component.extend({
|
||||||
customFields
|
customFields
|
||||||
) {
|
) {
|
||||||
let content;
|
let content;
|
||||||
|
let context;
|
||||||
|
let contextType;
|
||||||
|
|
||||||
|
if (this.options.context) {
|
||||||
|
let contextAttrs = this.options.context.split(".");
|
||||||
|
context = contextAttrs[0];
|
||||||
|
contextType = contextAttrs[1];
|
||||||
|
}
|
||||||
|
|
||||||
if (activeType === "wizardField") {
|
if (activeType === "wizardField") {
|
||||||
content = wizardFields;
|
content = wizardFields;
|
||||||
|
|
||||||
if (this.options.context === "field") {
|
if (context === "field") {
|
||||||
content = content.filter((field) => field.id !== currentFieldId);
|
content = content.filter((field) => field.id !== currentFieldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +225,7 @@ export default Component.extend({
|
||||||
type: a.type,
|
type: a.type,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (this.options.context === "action") {
|
if (context === "action") {
|
||||||
content = content.filter((a) => a.id !== currentActionId);
|
content = content.filter((a) => a.id !== currentActionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,7 +239,7 @@ export default Component.extend({
|
||||||
.concat(userFields || []);
|
.concat(userFields || []);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.options.context === "action" &&
|
context === "action" &&
|
||||||
this.inputType === "association" &&
|
this.inputType === "association" &&
|
||||||
this.selectorType === "key"
|
this.selectorType === "key"
|
||||||
) {
|
) {
|
||||||
|
@ -234,7 +255,17 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeType === "customField") {
|
if (activeType === "customField") {
|
||||||
content = customFields;
|
content = customFields
|
||||||
|
.filter((f) => {
|
||||||
|
return (
|
||||||
|
f.type !== "json" &&
|
||||||
|
customFieldActionMap[f.klass].includes(contextType)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((f) => ({
|
||||||
|
id: f.name,
|
||||||
|
name: `${sentenceCase(f.klass)} ${f.name} (${f.type})`,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
|
|
|
@ -3,12 +3,12 @@ import CustomWizardCustomField from "../models/custom-wizard-custom-field";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
messageKey: "create",
|
messageKey: "create",
|
||||||
fieldKeys: ["klass", "type", "serializers", "name"],
|
fieldKeys: ["klass", "type", "name", "serializers"],
|
||||||
documentationUrl: "https://thepavilion.io/t/3572",
|
documentationUrl: "https://thepavilion.io/t/3572",
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addField() {
|
addField() {
|
||||||
this.get("customFields").pushObject(
|
this.get("customFields").unshiftObject(
|
||||||
CustomWizardCustomField.create({ edit: true })
|
CustomWizardCustomField.create({ edit: true })
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -120,4 +120,5 @@ export {
|
||||||
listProperties,
|
listProperties,
|
||||||
notificationLevels,
|
notificationLevels,
|
||||||
wizardFieldList,
|
wizardFieldList,
|
||||||
|
sentenceCase,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@ import CustomWizard from "../models/custom-wizard";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { selectKitContent } from "../lib/wizard";
|
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
|
@ -33,9 +32,7 @@ export default DiscourseRoute.extend({
|
||||||
wizardList: parentModel.wizard_list,
|
wizardList: parentModel.wizard_list,
|
||||||
fieldTypes,
|
fieldTypes,
|
||||||
userFields: parentModel.userFields,
|
userFields: parentModel.userFields,
|
||||||
customFields: selectKitContent(
|
customFields: parentModel.custom_fields,
|
||||||
parentModel.custom_fields.map((f) => f.name)
|
|
||||||
),
|
|
||||||
apis: parentModel.apis,
|
apis: parentModel.apis,
|
||||||
themes: parentModel.themes,
|
themes: parentModel.themes,
|
||||||
wizard,
|
wizard,
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
none="admin.wizard.custom_field.type.select"
|
none="admin.wizard.custom_field.type.select"
|
||||||
onChange=(action (mut field.type))}}
|
onChange=(action (mut field.type))}}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="input">
|
||||||
|
{{input
|
||||||
|
value=field.name
|
||||||
|
placeholder=(i18n "admin.wizard.custom_field.name.select")}}
|
||||||
|
</td>
|
||||||
<td class="multi-select">
|
<td class="multi-select">
|
||||||
{{multi-select
|
{{multi-select
|
||||||
value=field.serializers
|
value=field.serializers
|
||||||
|
@ -20,11 +25,6 @@
|
||||||
none="admin.wizard.custom_field.serializers.select"
|
none="admin.wizard.custom_field.serializers.select"
|
||||||
onChange=(action (mut field.serializers))}}
|
onChange=(action (mut field.serializers))}}
|
||||||
</td>
|
</td>
|
||||||
<td class="input">
|
|
||||||
{{input
|
|
||||||
value=field.name
|
|
||||||
placeholder=(i18n "admin.wizard.custom_field.name.select")}}
|
|
||||||
</td>
|
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
{{#if loading}}
|
{{#if loading}}
|
||||||
{{loading-spinner size="small"}}
|
{{loading-spinner size="small"}}
|
||||||
|
@ -51,13 +51,25 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<td><label>{{field.klass}}</label></td>
|
<td><label>{{field.klass}}</label></td>
|
||||||
<td><label>{{field.type}}</label></td>
|
<td><label>{{field.type}}</label></td>
|
||||||
|
<td class="input"><label>{{field.name}}</label></td>
|
||||||
<td class="multi-select">
|
<td class="multi-select">
|
||||||
|
{{#if isExternal}}
|
||||||
|
—
|
||||||
|
{{else}}
|
||||||
{{#each field.serializers as |serializer|}}
|
{{#each field.serializers as |serializer|}}
|
||||||
<label>{{serializer}}</label>
|
<label>{{serializer}}</label>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td class="input"><label>{{field.name}}</label></td>
|
{{#if isExternal}}
|
||||||
|
<td class="external">
|
||||||
|
<label title={{i18n "admin.wizard.custom_field.external.title"}}>
|
||||||
|
{{i18n "admin.wizard.custom_field.external.label"}}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
{{else}}
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
{{d-button action="edit" icon="pencil-alt"}}
|
{{d-button action="edit" icon="pencil-alt"}}
|
||||||
</td>
|
</td>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -738,7 +738,7 @@
|
||||||
wizardActionSelection="value"
|
wizardActionSelection="value"
|
||||||
userFieldSelection="value"
|
userFieldSelection="value"
|
||||||
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
keyPlaceholder="admin.wizard.action.custom_fields.key"
|
||||||
context="action"
|
context=customFieldsContext
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,10 @@ export default Ember.Application.extend({
|
||||||
rootElement: "#custom-wizard-main",
|
rootElement: "#custom-wizard-main",
|
||||||
Resolver: buildResolver("wizard"),
|
Resolver: buildResolver("wizard"),
|
||||||
|
|
||||||
|
customEvents: {
|
||||||
|
paste: "paste",
|
||||||
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
Object.keys(requirejs._eak_seen).forEach((key) => {
|
Object.keys(requirejs._eak_seen).forEach((key) => {
|
||||||
if (/\/pre\-initializers\//.test(key)) {
|
if (/\/pre\-initializers\//.test(key)) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default {
|
||||||
);
|
);
|
||||||
const DEditor = requirejs("discourse/components/d-editor").default;
|
const DEditor = requirejs("discourse/components/d-editor").default;
|
||||||
const { clipboardHelpers } = requirejs("discourse/lib/utilities");
|
const { clipboardHelpers } = requirejs("discourse/lib/utilities");
|
||||||
const { toMarkdown } = requirejs("discourse/lib/to-markdown");
|
const toMarkdown = requirejs("discourse/lib/to-markdown").default;
|
||||||
|
|
||||||
FieldComponent.reopen({
|
FieldComponent.reopen({
|
||||||
classNameBindings: ["field.id"],
|
classNameBindings: ["field.id"],
|
||||||
|
@ -181,7 +181,7 @@ export default {
|
||||||
markdown = pre.match(/\S$/) ? ` ${markdown}` : markdown;
|
markdown = pre.match(/\S$/) ? ` ${markdown}` : markdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.appEvents.trigger("composer:insert-text", {
|
this.appEvents.trigger("wizard-editor:insert-text", {
|
||||||
fieldId: this.fieldId,
|
fieldId: this.fieldId,
|
||||||
text: markdown,
|
text: markdown,
|
||||||
});
|
});
|
||||||
|
|
|
@ -667,6 +667,10 @@
|
||||||
margin-left: 5px !important;
|
margin-left: 5px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td.external {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ en:
|
||||||
edit: "You're editing an action"
|
edit: "You're editing an action"
|
||||||
documentation: "Check out the action documentation"
|
documentation: "Check out the action documentation"
|
||||||
custom_fields:
|
custom_fields:
|
||||||
create: "Create, edit or destroy a custom field record"
|
create: "View, create, edit and destroy custom fields"
|
||||||
saved: "Saved custom field"
|
saved: "Saved custom field"
|
||||||
error: "Failed to save: {{messages}}"
|
error: "Failed to save: {{messages}}"
|
||||||
documentation: Check out the custom field documentation
|
documentation: Check out the custom field documentation
|
||||||
|
@ -322,6 +322,9 @@ en:
|
||||||
custom_field:
|
custom_field:
|
||||||
nav_label: "Custom Fields"
|
nav_label: "Custom Fields"
|
||||||
add: "Add"
|
add: "Add"
|
||||||
|
external:
|
||||||
|
label: "from another plugin"
|
||||||
|
title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here."
|
||||||
name:
|
name:
|
||||||
label: "Name"
|
label: "Name"
|
||||||
select: "underscored_name"
|
select: "underscored_name"
|
||||||
|
|
|
@ -14,7 +14,7 @@ class CustomWizard::AdminController < ::Admin::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_field_list
|
def custom_field_list
|
||||||
serialize_data(CustomWizard::CustomField.list, CustomWizard::CustomFieldSerializer)
|
serialize_data(CustomWizard::CustomField.full_list, CustomWizard::CustomFieldSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_error(message)
|
def render_error(message)
|
||||||
|
|
|
@ -23,12 +23,12 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
if updater.success?
|
if updater.success?
|
||||||
wizard_id = update_params[:wizard_id]
|
wizard_id = update_params[:wizard_id]
|
||||||
builder = CustomWizard::Builder.new(wizard_id, current_user)
|
builder = CustomWizard::Builder.new(wizard_id, current_user)
|
||||||
@wizard = builder.build
|
@wizard = builder.build(force: true)
|
||||||
|
|
||||||
current_step = @wizard.find_step(update[:step_id])
|
current_step = @wizard.find_step(update[:step_id])
|
||||||
current_submission = @wizard.current_submission
|
current_submission = @wizard.current_submission
|
||||||
result = {}
|
result = {}
|
||||||
|
@wizard.filter_conditional_fields
|
||||||
if current_step.conditional_final_step && !current_step.last_step
|
if current_step.conditional_final_step && !current_step.last_step
|
||||||
current_step.force_final = true
|
current_step.force_final = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,7 +61,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
result = success_json
|
result = success_json
|
||||||
user = current_user
|
user = current_user
|
||||||
|
|
||||||
if user
|
if user && wizard.can_access?
|
||||||
submission = wizard.current_submission
|
submission = wizard.current_submission
|
||||||
if submission && submission['redirect_to']
|
if submission && submission['redirect_to']
|
||||||
result.merge!(redirect_to: submission['redirect_to'])
|
result.merge!(redirect_to: submission['redirect_to'])
|
||||||
|
|
6
extensions/custom_field/extension.rb
Normale Datei
6
extensions/custom_field/extension.rb
Normale Datei
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module CustomWizardCustomFieldExtension
|
||||||
|
def custom_field_types
|
||||||
|
@custom_field_types
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,7 +4,8 @@ module ExtraLocalesControllerCustomWizard
|
||||||
super || begin
|
super || begin
|
||||||
return false unless bundle =~ /wizard/ && request.referer =~ /\/w\//
|
return false unless bundle =~ /wizard/ && request.referer =~ /\/w\//
|
||||||
path = URI(request.referer).path
|
path = URI(request.referer).path
|
||||||
wizard_id = path.split('/w/').last
|
wizard_path = path.split('/w/').last
|
||||||
|
wizard_id = wizard_path.split('/').first
|
||||||
CustomWizard::Template.exists?(wizard_id.underscore)
|
CustomWizard::Template.exists?(wizard_id.underscore)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
module Jobs
|
|
||||||
class ClearAfterTimeWizard < ::Jobs::Base
|
|
||||||
sidekiq_options queue: 'critical'
|
|
||||||
|
|
||||||
def execute(args)
|
|
||||||
User.human_users.each do |u|
|
|
||||||
if u.custom_fields['redirect_to_wizard'] == args[:wizard_id]
|
|
||||||
u.custom_fields.delete('redirect_to_wizard')
|
|
||||||
u.save_custom_fields(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -454,35 +454,54 @@ class CustomWizard::Action
|
||||||
data: data,
|
data: data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
registered_fields = CustomWizard::CustomField.full_list
|
||||||
registered_fields = CustomWizard::CustomField.cached_list
|
|
||||||
|
|
||||||
field_map.each do |field|
|
field_map.each do |field|
|
||||||
keyArr = field[:key].split('.')
|
keyArr = field[:key].split('.')
|
||||||
value = field[:value]
|
value = field[:value]
|
||||||
|
|
||||||
if keyArr.length > 1
|
if keyArr.length > 1
|
||||||
klass = keyArr.first
|
klass = keyArr.first.to_sym
|
||||||
name = keyArr.last
|
name = keyArr.second
|
||||||
|
|
||||||
|
if keyArr.length === 3 && name.include?("{}")
|
||||||
|
name = name.gsub("{}", "")
|
||||||
|
json_attr = keyArr.last
|
||||||
|
type = :json
|
||||||
|
end
|
||||||
else
|
else
|
||||||
name = keyArr.first
|
name = keyArr.first
|
||||||
end
|
end
|
||||||
|
|
||||||
registered = registered_fields.select { |f| f[:name] == name }
|
registered = registered_fields.select { |f| f.name == name }.first
|
||||||
if registered.first.present?
|
if registered.present?
|
||||||
klass = registered.first[:klass]
|
klass = registered.klass
|
||||||
|
type = registered.type
|
||||||
end
|
end
|
||||||
|
|
||||||
if klass === 'topic'
|
next if type === :json && json_attr.blank?
|
||||||
|
|
||||||
|
if klass === :topic
|
||||||
params[:topic_opts] ||= {}
|
params[:topic_opts] ||= {}
|
||||||
params[:topic_opts][:custom_fields] ||= {}
|
params[:topic_opts][:custom_fields] ||= {}
|
||||||
|
|
||||||
|
if type === :json
|
||||||
|
params[:topic_opts][:custom_fields][name] ||= {}
|
||||||
|
params[:topic_opts][:custom_fields][name][json_attr] = value
|
||||||
|
else
|
||||||
params[:topic_opts][:custom_fields][name] = value
|
params[:topic_opts][:custom_fields][name] = value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if type === :json
|
||||||
|
params[:custom_fields][name] ||= {}
|
||||||
|
params[:custom_fields][name][json_attr] = value
|
||||||
else
|
else
|
||||||
params[:custom_fields] ||= {}
|
params[:custom_fields] ||= {}
|
||||||
params[:custom_fields][name] = value
|
params[:custom_fields][name] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
def build(build_opts = {}, params = {})
|
def build(build_opts = {}, params = {})
|
||||||
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
|
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
|
||||||
return @wizard if !@wizard.can_access?
|
return @wizard if !@wizard.can_access? && !build_opts[:force]
|
||||||
|
|
||||||
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
|
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
|
||||||
|
|
||||||
|
|
|
@ -66,10 +66,12 @@ class ::CustomWizard::CustomField
|
||||||
value = send(attr)
|
value = send(attr)
|
||||||
i18n_key = "wizard.custom_field.error"
|
i18n_key = "wizard.custom_field.error"
|
||||||
|
|
||||||
if value.blank?
|
if value.blank? && REQUIRED.include?(attr)
|
||||||
if REQUIRED.include?(attr)
|
|
||||||
add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
|
add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
|
||||||
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if attr == 'serializers' && !value.is_a?(Array)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ class ::CustomWizard::CustomField
|
||||||
|
|
||||||
fields.select do |cf|
|
fields.select do |cf|
|
||||||
if attr == :serializers
|
if attr == :serializers
|
||||||
cf[attr].include?(value)
|
cf[attr] && cf[attr].include?(value)
|
||||||
else
|
else
|
||||||
cf[attr] == value
|
cf[attr] == value
|
||||||
end
|
end
|
||||||
|
@ -215,4 +217,32 @@ class ::CustomWizard::CustomField
|
||||||
def self.enabled?
|
def self.enabled?
|
||||||
any?
|
any?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.external_list
|
||||||
|
external = []
|
||||||
|
|
||||||
|
CLASSES.keys.each do |klass|
|
||||||
|
field_types = klass.to_s.classify.constantize.custom_field_types
|
||||||
|
|
||||||
|
if field_types.present?
|
||||||
|
field_types.each do |name, type|
|
||||||
|
unless list.any? { |field| field.name === name }
|
||||||
|
field = new(
|
||||||
|
'external',
|
||||||
|
name: name,
|
||||||
|
klass: klass,
|
||||||
|
type: type
|
||||||
|
)
|
||||||
|
external.push(field)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
external
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.full_list
|
||||||
|
(list + external_list).uniq
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,20 +5,27 @@ class CustomWizard::Mapper
|
||||||
USER_FIELDS = [
|
USER_FIELDS = [
|
||||||
'name',
|
'name',
|
||||||
'username',
|
'username',
|
||||||
'email',
|
|
||||||
'date_of_birth',
|
'date_of_birth',
|
||||||
'title',
|
'title',
|
||||||
'locale',
|
'locale',
|
||||||
'trust_level',
|
'trust_level',
|
||||||
|
'email'
|
||||||
|
]
|
||||||
|
|
||||||
|
USER_OPTION_FIELDS = [
|
||||||
'email_level',
|
'email_level',
|
||||||
'email_messages_level',
|
'email_messages_level',
|
||||||
'email_digests'
|
'email_digests'
|
||||||
]
|
]
|
||||||
|
|
||||||
PROFILE_FIELDS = ['location', 'website', 'bio_raw']
|
PROFILE_FIELDS = [
|
||||||
|
'location',
|
||||||
|
'website',
|
||||||
|
'bio_raw'
|
||||||
|
]
|
||||||
|
|
||||||
def self.user_fields
|
def self.user_fields
|
||||||
USER_FIELDS + PROFILE_FIELDS
|
USER_FIELDS + USER_OPTION_FIELDS + PROFILE_FIELDS
|
||||||
end
|
end
|
||||||
|
|
||||||
OPERATORS = {
|
OPERATORS = {
|
||||||
|
@ -197,11 +204,15 @@ class CustomWizard::Mapper
|
||||||
|
|
||||||
def map_user_field(value)
|
def map_user_field(value)
|
||||||
if value.include?(User::USER_FIELD_PREFIX)
|
if value.include?(User::USER_FIELD_PREFIX)
|
||||||
UserCustomField.where(user_id: user.id, name: value).pluck(:value).first
|
user.custom_fields[value]
|
||||||
elsif PROFILE_FIELDS.include?(value)
|
elsif PROFILE_FIELDS.include?(value)
|
||||||
UserProfile.find_by(user_id: user.id).send(value)
|
user.user_profile.send(value)
|
||||||
elsif USER_FIELDS.include?(value)
|
elsif USER_FIELDS.include?(value)
|
||||||
User.find(user.id).send(value)
|
user.send(value)
|
||||||
|
elsif USER_OPTION_FIELDS.include?(value)
|
||||||
|
user.user_option.send(value)
|
||||||
|
else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,19 +228,11 @@ class CustomWizard::Mapper
|
||||||
return string if string.blank?
|
return string if string.blank?
|
||||||
|
|
||||||
if opts[:user]
|
if opts[:user]
|
||||||
string.gsub!(/u\{(.*?)\}/) do |match|
|
string.gsub!(/u\{(.*?)\}/) { |match| map_user_field($1) || '' }
|
||||||
result = ''
|
|
||||||
result = user.send($1) if USER_FIELDS.include?($1)
|
|
||||||
result = user.user_profile.send($1) if PROFILE_FIELDS.include?($1)
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts[:wizard]
|
if opts[:wizard]
|
||||||
string.gsub!(/w\{(.*?)\}/) do |match|
|
string.gsub!(/w\{(.*?)\}/) { |match| recurse(data, [*$1.split('.')]) || '' }
|
||||||
value = recurse(data, [*$1.split('.')])
|
|
||||||
value.present? ? value : ''
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts[:value]
|
if opts[:value]
|
||||||
|
|
|
@ -49,17 +49,14 @@ class CustomWizard::Template
|
||||||
|
|
||||||
def self.remove(wizard_id)
|
def self.remove(wizard_id)
|
||||||
wizard = CustomWizard::Wizard.create(wizard_id)
|
wizard = CustomWizard::Wizard.create(wizard_id)
|
||||||
|
|
||||||
return false if !wizard
|
return false if !wizard
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
PluginStore.remove(CustomWizard::PLUGIN_NAME, wizard.id)
|
PluginStore.remove(CustomWizard::PLUGIN_NAME, wizard.id)
|
||||||
|
clear_user_wizard_redirect(wizard_id)
|
||||||
|
end
|
||||||
|
|
||||||
if wizard.after_time
|
Jobs.cancel_scheduled_job(:set_after_time_wizard) if wizard.after_time
|
||||||
Jobs.cancel_scheduled_job(:set_after_time_wizard)
|
|
||||||
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
@ -88,6 +85,10 @@ class CustomWizard::Template
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.clear_user_wizard_redirect(wizard_id)
|
||||||
|
UserCustomField.where(name: 'redirect_to_wizard', value: wizard_id).destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def normalize_data
|
def normalize_data
|
||||||
|
@ -132,7 +133,7 @@ class CustomWizard::Template
|
||||||
Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id)
|
Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id)
|
||||||
elsif old_data && old_data[:after_time]
|
elsif old_data && old_data[:after_time]
|
||||||
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)
|
self.class.clear_user_wizard_redirect(wizard_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -247,6 +247,26 @@ class CustomWizard::Wizard
|
||||||
set_submissions(submissions)
|
set_submissions(submissions)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_conditional_fields
|
||||||
|
included_fields = steps.map { |s| s.fields.map { |f| f.id } }.flatten
|
||||||
|
filtered_submision = current_submission&.select do |key, _|
|
||||||
|
key = key.to_s
|
||||||
|
included_fields.include?(key) ||
|
||||||
|
required_fields.include?(key) ||
|
||||||
|
key.include?("action")
|
||||||
|
end
|
||||||
|
|
||||||
|
save_submission(filtered_submision)
|
||||||
|
end
|
||||||
|
|
||||||
|
def required_fields
|
||||||
|
%w{
|
||||||
|
submitted_at
|
||||||
|
route_to
|
||||||
|
saved_param
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def final_cleanup!
|
def final_cleanup!
|
||||||
if id == user.custom_fields['redirect_to_wizard']
|
if id == user.custom_fields['redirect_to_wizard']
|
||||||
user.custom_fields.delete('redirect_to_wizard')
|
user.custom_fields.delete('redirect_to_wizard')
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
"name": "discourse-custom-wizard",
|
"name": "discourse-custom-wizard",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"repository": "git@github.com:paviliondev/discourse-custom-wizard.git",
|
"repository": "git@github.com:paviliondev/discourse-custom-wizard.git",
|
||||||
"author": "Discourse",
|
"author": "Pavilion",
|
||||||
"license": "MIT",
|
"license": "GPL V2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint-config-discourse": "^1.1.8"
|
"eslint-config-discourse": "^1.1.8"
|
||||||
}
|
}
|
||||||
|
|
12
plugin.rb
12
plugin.rb
|
@ -65,7 +65,6 @@ after_initialize do
|
||||||
../controllers/custom_wizard/wizard.rb
|
../controllers/custom_wizard/wizard.rb
|
||||||
../controllers/custom_wizard/steps.rb
|
../controllers/custom_wizard/steps.rb
|
||||||
../controllers/custom_wizard/realtime_validations.rb
|
../controllers/custom_wizard/realtime_validations.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/validators/template.rb
|
../lib/custom_wizard/validators/template.rb
|
||||||
|
@ -109,6 +108,7 @@ after_initialize do
|
||||||
../extensions/users_controller.rb
|
../extensions/users_controller.rb
|
||||||
../extensions/custom_field/preloader.rb
|
../extensions/custom_field/preloader.rb
|
||||||
../extensions/custom_field/serializer.rb
|
../extensions/custom_field/serializer.rb
|
||||||
|
../extensions/custom_field/extension.rb
|
||||||
].each do |path|
|
].each do |path|
|
||||||
load File.expand_path(path, __FILE__)
|
load File.expand_path(path, __FILE__)
|
||||||
end
|
end
|
||||||
|
@ -201,18 +201,18 @@ after_initialize do
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField::CLASSES.keys.each do |klass|
|
CustomWizard::CustomField::CLASSES.keys.each do |klass|
|
||||||
|
class_constant = klass.to_s.classify.constantize
|
||||||
|
|
||||||
add_model_callback(klass, :after_initialize) do
|
add_model_callback(klass, :after_initialize) do
|
||||||
if CustomWizard::CustomField.enabled?
|
if CustomWizard::CustomField.enabled?
|
||||||
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
|
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
|
||||||
klass.to_s
|
class_constant.register_custom_field_type(field[:name], field[:type].to_sym)
|
||||||
.classify
|
|
||||||
.constantize
|
|
||||||
.register_custom_field_type(field[:name], field[:type].to_sym)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
klass.to_s.classify.constantize.singleton_class.prepend CustomWizardCustomFieldPreloader
|
class_constant.singleton_class.prepend CustomWizardCustomFieldPreloader
|
||||||
|
class_constant.singleton_class.prepend CustomWizardCustomFieldExtension
|
||||||
end
|
end
|
||||||
|
|
||||||
CustomWizard::CustomField.serializers.each do |serializer_klass|
|
CustomWizard::CustomField.serializers.each do |serializer_klass|
|
||||||
|
|
|
@ -72,6 +72,42 @@ describe CustomWizard::Action do
|
||||||
raw: "topic body"
|
raw: "topic body"
|
||||||
).exists?).to eq(false)
|
).exists?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "adds custom fields" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
|
wizard.create_updater(wizard.steps.first.id,
|
||||||
|
step_1_field_1: "Topic Title",
|
||||||
|
step_1_field_2: "topic body"
|
||||||
|
).update
|
||||||
|
wizard.create_updater(wizard.steps.second.id, {}).update
|
||||||
|
wizard.create_updater(wizard.steps.last.id,
|
||||||
|
step_3_field_3: category.id
|
||||||
|
).update
|
||||||
|
|
||||||
|
topic = Topic.where(
|
||||||
|
title: "Topic Title",
|
||||||
|
category_id: category.id
|
||||||
|
).first
|
||||||
|
topic_custom_field = TopicCustomField.where(
|
||||||
|
name: "topic_field",
|
||||||
|
value: "Topic custom field value",
|
||||||
|
topic_id: topic.id
|
||||||
|
)
|
||||||
|
topic_json_custom_field = TopicCustomField.where("
|
||||||
|
name = 'topic_json_field' AND
|
||||||
|
(value::json->>'key_1') = 'Key 1 value' AND
|
||||||
|
(value::json->>'key_2') = 'Key 2 value' AND
|
||||||
|
topic_id = #{topic.id}"
|
||||||
|
)
|
||||||
|
post_custom_field = PostCustomField.where(
|
||||||
|
name: "post_field",
|
||||||
|
value: "Post custom field value",
|
||||||
|
post_id: topic.first_post.id
|
||||||
|
)
|
||||||
|
expect(topic_custom_field.exists?).to eq(true)
|
||||||
|
expect(topic_json_custom_field.exists?).to eq(true)
|
||||||
|
expect(post_custom_field.exists?).to eq(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'sending a message' do
|
context 'sending a message' do
|
||||||
|
|
|
@ -49,6 +49,40 @@ describe CustomWizard::CustomField do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "validation" do
|
context "validation" do
|
||||||
|
it "does not save without required attributes" do
|
||||||
|
invalid_field_json = custom_field_json['custom_fields'].first
|
||||||
|
invalid_field_json['klass'] = nil
|
||||||
|
|
||||||
|
custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
|
||||||
|
expect(custom_field.save).to eq(false)
|
||||||
|
expect(custom_field.valid?).to eq(false)
|
||||||
|
expect(custom_field.errors.full_messages.first).to eq(
|
||||||
|
I18n.t("wizard.custom_field.error.required_attribute", attr: "klass")
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
PluginStoreRow.where(
|
||||||
|
plugin_name: CustomWizard::CustomField::NAMESPACE,
|
||||||
|
key: custom_field.name
|
||||||
|
).exists?
|
||||||
|
).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does save without optional attributes" do
|
||||||
|
field_json = custom_field_json['custom_fields'].first
|
||||||
|
field_json['serializers'] = nil
|
||||||
|
|
||||||
|
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||||
|
expect(custom_field.save).to eq(true)
|
||||||
|
expect(custom_field.valid?).to eq(true)
|
||||||
|
expect(
|
||||||
|
PluginStoreRow.where("
|
||||||
|
plugin_name = '#{CustomWizard::CustomField::NAMESPACE}' AND
|
||||||
|
key = '#{custom_field.name}' AND
|
||||||
|
value::jsonb = '#{field_json.except('name').to_json}'::jsonb
|
||||||
|
",).exists?
|
||||||
|
).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
it "does not save with an unsupported class" do
|
it "does not save with an unsupported class" do
|
||||||
invalid_field_json = custom_field_json['custom_fields'].first
|
invalid_field_json = custom_field_json['custom_fields'].first
|
||||||
invalid_field_json['klass'] = 'user'
|
invalid_field_json['klass'] = 'user'
|
||||||
|
@ -178,6 +212,22 @@ describe CustomWizard::CustomField do
|
||||||
it "lists saved custom field records by attribute value" do
|
it "lists saved custom field records by attribute value" do
|
||||||
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
|
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "lists saved custom field records by optional values" do
|
||||||
|
field_json = custom_field_json['custom_fields'].first
|
||||||
|
field_json['serializers'] = nil
|
||||||
|
|
||||||
|
custom_field = CustomWizard::CustomField.new(nil, field_json)
|
||||||
|
expect(CustomWizard::CustomField.list_by(:serializers, ['post']).length).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "lists custom field records added by other plugins " do
|
||||||
|
expect(CustomWizard::CustomField.external_list.length).to eq(11)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "lists all custom field records" do
|
||||||
|
expect(CustomWizard::CustomField.full_list.length).to eq(15)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is enabled if there are custom fields" do
|
it "is enabled if there are custom fields" do
|
||||||
|
|
|
@ -229,7 +229,8 @@ describe CustomWizard::Mapper do
|
||||||
).perform).to eq("value 2")
|
).perform).to eq("value 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "interpolates user fields" do
|
context "interpolates" do
|
||||||
|
it "user fields" do
|
||||||
expect(CustomWizard::Mapper.new(
|
expect(CustomWizard::Mapper.new(
|
||||||
inputs: inputs['interpolate_user_field'],
|
inputs: inputs['interpolate_user_field'],
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -237,21 +238,32 @@ describe CustomWizard::Mapper do
|
||||||
).perform).to eq("Name: Angus")
|
).perform).to eq("Name: Angus")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "interpolates wizard fields" do
|
it "user emails" do
|
||||||
expect(CustomWizard::Mapper.new(
|
expect(CustomWizard::Mapper.new(
|
||||||
inputs: inputs['interpolate_wizard_field'],
|
inputs: inputs['interpolate_user_email'],
|
||||||
data: data,
|
data: data,
|
||||||
user: user1
|
user: user1
|
||||||
).perform).to eq("Input 1: value 1")
|
).perform).to eq("Email: angus@email.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "interpolates date" do
|
it "user options" do
|
||||||
|
user1.user_option.update_columns(email_level: UserOption.email_level_types[:never])
|
||||||
|
|
||||||
|
expect(CustomWizard::Mapper.new(
|
||||||
|
inputs: inputs['interpolate_user_option'],
|
||||||
|
data: data,
|
||||||
|
user: user1
|
||||||
|
).perform).to eq("Email Level: #{UserOption.email_level_types[:never]}")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "date" do
|
||||||
expect(CustomWizard::Mapper.new(
|
expect(CustomWizard::Mapper.new(
|
||||||
inputs: inputs['interpolate_timestamp'],
|
inputs: inputs['interpolate_timestamp'],
|
||||||
data: data,
|
data: data,
|
||||||
user: user1
|
user: user1
|
||||||
).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
|
).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "handles greater than pairs" do
|
it "handles greater than pairs" do
|
||||||
expect(CustomWizard::Mapper.new(
|
expect(CustomWizard::Mapper.new(
|
||||||
|
|
|
@ -41,6 +41,14 @@ describe CustomWizard::Template do
|
||||||
).to eq(nil)
|
).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "removes user wizard redirects if template is removed" do
|
||||||
|
user.custom_fields['redirect_to_wizard'] = 'super_mega_fun_wizard'
|
||||||
|
user.save_custom_fields(true)
|
||||||
|
|
||||||
|
CustomWizard::Template.remove('super_mega_fun_wizard')
|
||||||
|
expect(user.reload.custom_fields['redirect_to_wizard']).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
it "checks for wizard template existence" do
|
it "checks for wizard template existence" do
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Template.exists?('super_mega_fun_wizard')
|
CustomWizard::Template.exists?('super_mega_fun_wizard')
|
||||||
|
|
|
@ -173,6 +173,8 @@ describe CustomWizard::Wizard do
|
||||||
progress_step("step_2", acting_user: trusted_user)
|
progress_step("step_2", acting_user: trusted_user)
|
||||||
progress_step("step_3", acting_user: trusted_user)
|
progress_step("step_3", acting_user: trusted_user)
|
||||||
|
|
||||||
|
@permitted_template["multiple_submissions"] = true
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
|
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
|
||||||
).to eq(true)
|
).to eq(true)
|
||||||
|
|
|
@ -37,6 +37,13 @@ describe ExtraLocalesControllerCustomWizard, type: :request do
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns wizard locales when requested by user in a wizard step" do
|
||||||
|
sign_in(new_user)
|
||||||
|
|
||||||
|
get @locale_url, headers: { 'REFERER' => "/w/super-mega-fun-wizard/steps/step_1" }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
end
|
||||||
|
|
||||||
it "return wizard locales if user cant access wizard" do
|
it "return wizard locales if user cant access wizard" do
|
||||||
template[:permitted] = permitted["permitted"]
|
template[:permitted] = permitted["permitted"]
|
||||||
CustomWizard::Template.save(template.as_json)
|
CustomWizard::Template.save(template.as_json)
|
||||||
|
|
16
spec/fixtures/mapper/inputs.json
gevendort
16
spec/fixtures/mapper/inputs.json
gevendort
|
@ -57,6 +57,22 @@
|
||||||
"output": "Name: u{name}"
|
"output": "Name: u{name}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"interpolate_user_email": [
|
||||||
|
{
|
||||||
|
"type": "assignment",
|
||||||
|
"output_type": "text",
|
||||||
|
"output_connector": "set",
|
||||||
|
"output": "Email: u{email}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpolate_user_option": [
|
||||||
|
{
|
||||||
|
"type": "assignment",
|
||||||
|
"output_type": "text",
|
||||||
|
"output_connector": "set",
|
||||||
|
"output": "Email Level: u{email_level}"
|
||||||
|
}
|
||||||
|
],
|
||||||
"interpolate_wizard_field": [
|
"interpolate_wizard_field": [
|
||||||
{
|
{
|
||||||
"type": "assignment",
|
"type": "assignment",
|
||||||
|
|
31
spec/fixtures/wizard.json
gevendort
31
spec/fixtures/wizard.json
gevendort
|
@ -3,7 +3,6 @@
|
||||||
"name": "Super Mega Fun Wizard",
|
"name": "Super Mega Fun Wizard",
|
||||||
"background": "#333333",
|
"background": "#333333",
|
||||||
"save_submissions": true,
|
"save_submissions": true,
|
||||||
"multiple_submissions": true,
|
|
||||||
"after_signup": false,
|
"after_signup": false,
|
||||||
"prompt_completion": false,
|
"prompt_completion": false,
|
||||||
"theme_id": 2,
|
"theme_id": 2,
|
||||||
|
@ -391,10 +390,34 @@
|
||||||
"pairs": [
|
"pairs": [
|
||||||
{
|
{
|
||||||
"index": 0,
|
"index": 0,
|
||||||
"key": "custom_field_1",
|
"key": "post_field",
|
||||||
"key_type": "text",
|
"key_type": "text",
|
||||||
"value": "title",
|
"value": "Post custom field value",
|
||||||
"value_type": "user_field",
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"key": "topic.topic_field",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Topic custom field value",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"key": "topic.topic_json_field{}.key_1",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Key 1 value",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "association"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"key": "topic.topic_json_field{}.key_2",
|
||||||
|
"key_type": "text",
|
||||||
|
"value": "Key 2 value",
|
||||||
|
"value_type": "text",
|
||||||
"connector": "association"
|
"connector": "association"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative '../plugin_helper'
|
|
||||||
|
|
||||||
describe Jobs::ClearAfterTimeWizard do
|
|
||||||
fab!(:user1) { Fabricate(:user) }
|
|
||||||
fab!(:user2) { Fabricate(:user) }
|
|
||||||
fab!(:user3) { Fabricate(:user) }
|
|
||||||
|
|
||||||
let(:template) {
|
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
|
||||||
).read).with_indifferent_access
|
|
||||||
}
|
|
||||||
|
|
||||||
it "clears wizard redirect for all users " do
|
|
||||||
after_time_template = template.dup
|
|
||||||
after_time_template["after_time"] = true
|
|
||||||
after_time_template["after_time_scheduled"] = (Time.now + 3.hours).iso8601
|
|
||||||
|
|
||||||
CustomWizard::Template.save(after_time_template)
|
|
||||||
|
|
||||||
Jobs::SetAfterTimeWizard.new.execute(wizard_id: 'super_mega_fun_wizard')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
UserCustomField.where(
|
|
||||||
name: 'redirect_to_wizard',
|
|
||||||
value: 'super_mega_fun_wizard'
|
|
||||||
).length
|
|
||||||
).to eq(3)
|
|
||||||
|
|
||||||
described_class.new.execute(wizard_id: 'super_mega_fun_wizard')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
UserCustomField.where("
|
|
||||||
name = 'redirect_to_wizard' AND
|
|
||||||
value = 'super_mega_fun_wizard'
|
|
||||||
").exists?
|
|
||||||
).to eq(false)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,7 +6,7 @@ if ENV['SIMPLECOV']
|
||||||
SimpleCov.start do
|
SimpleCov.start do
|
||||||
root "plugins/discourse-custom-wizard"
|
root "plugins/discourse-custom-wizard"
|
||||||
track_files "plugins/discourse-custom-wizard/**/*.rb"
|
track_files "plugins/discourse-custom-wizard/**/*.rb"
|
||||||
add_filter { |src| src.filename =~ /(\/spec\/|\/db\/|plugin\.rb|api)/ }
|
add_filter { |src| src.filename =~ /(\/spec\/|\/db\/|plugin\.rb|api|gems)/ }
|
||||||
SimpleCov.minimum_coverage 80
|
SimpleCov.minimum_coverage 80
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,9 +17,9 @@ describe CustomWizard::AdminCustomFieldsController do
|
||||||
sign_in(admin_user)
|
sign_in(admin_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the list of custom fields" do
|
it "returns the full list of custom fields" do
|
||||||
get "/admin/wizards/custom-fields.json"
|
get "/admin/wizards/custom-fields.json"
|
||||||
expect(response.parsed_body.length).to eq(4)
|
expect(response.parsed_body.length).to eq(15)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "saves custom fields" do
|
it "saves custom fields" do
|
||||||
|
|
|
@ -43,6 +43,12 @@ describe ApplicationController do
|
||||||
.first['redirect_to']
|
.first['redirect_to']
|
||||||
).to eq("/t/2")
|
).to eq("/t/2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not redirect if wizard does not exist" do
|
||||||
|
CustomWizard::Template.remove('super_mega_fun_wizard')
|
||||||
|
get "/"
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "who is not required to complete wizard" do
|
context "who is not required to complete wizard" do
|
||||||
|
|
|
@ -249,4 +249,33 @@ describe CustomWizard::StepsController do
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(response.parsed_body['final']).to eq(true)
|
expect(response.parsed_body['final']).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "excludes the non-included conditional fields from the submissions" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_2_field_1: "1995-04-23"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will not pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wizard_id = response.parsed_body['wizard']['id']
|
||||||
|
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||||
|
submission = wizard.submissions.last
|
||||||
|
expect(submission.keys).not_to include("step_2_field_1")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,14 @@ describe CustomWizard::WizardController do
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let(:permitted_json) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(
|
CustomWizard::Template.save(
|
||||||
JSON.parse(File.open(
|
JSON.parse(File.open(
|
||||||
|
@ -47,6 +55,14 @@ describe CustomWizard::WizardController do
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'lets user skip if user cant access wizard' do
|
||||||
|
@template["permitted"] = permitted_json["permitted"]
|
||||||
|
CustomWizard::Template.save(@template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/skip.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns a no skip message if user is not allowed to skip' do
|
it 'returns a no skip message if user is not allowed to skip' do
|
||||||
@template['required'] = 'true'
|
@template['required'] = 'true'
|
||||||
CustomWizard::Template.save(@template)
|
CustomWizard::Template.save(@template)
|
||||||
|
|
Laden …
In neuem Issue referenzieren