Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-15 14:22:53 +01:00
merged master and resolved conflicts
Dieser Commit ist enthalten in:
Commit
5e0ffd49db
86 geänderte Dateien mit 1297 neuen und 537 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:
|
||||||
|
|
24
.github/workflows/plugin-tests.yml
gevendort
24
.github/workflows/plugin-tests.yml
gevendort
|
@ -6,6 +6,8 @@ on:
|
||||||
- master
|
- master
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 */12 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -51,23 +53,27 @@ jobs:
|
||||||
repository: discourse/discourse
|
repository: discourse/discourse
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Fetch Repo Name
|
||||||
|
id: repo-name
|
||||||
|
run: echo "::set-output name=value::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')"
|
||||||
|
|
||||||
- name: Install plugin
|
- name: Install plugin
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: plugins/${{ github.event.repository.name }}
|
path: plugins/${{ steps.repo-name.outputs.value }}
|
||||||
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/${{ steps.repo-name.outputs.value }}/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/${{ steps.repo-name.outputs.value }}/test/javascripts"
|
||||||
|
|
||||||
- name: Setup Git
|
- name: Setup Git
|
||||||
run: |
|
run: |
|
||||||
|
@ -100,7 +106,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/${{ steps.repo-name.outputs.value }}/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 +129,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[${{ steps.repo-name.outputs.value }}]
|
||||||
|
|
||||||
- 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['${{ steps.repo-name.outputs.value }}','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) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default Component.extend(UndoChanges, {
|
||||||
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
|
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
|
||||||
showLimit: or("isCategory", "isTag"),
|
showLimit: or("isCategory", "isTag"),
|
||||||
isTextType: or("isText", "isTextarea", "isComposer"),
|
isTextType: or("isText", "isTextarea", "isComposer"),
|
||||||
|
isComposerPreview: equal("field.type", "composer_preview"),
|
||||||
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
||||||
showAdvanced: alias("field.type"),
|
showAdvanced: alias("field.type"),
|
||||||
messageUrl: "https://thepavilion.io/t/2809",
|
messageUrl: "https://thepavilion.io/t/2809",
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import CustomWizard from "../models/custom-wizard";
|
import CustomWizard from "../models/custom-wizard";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
|
const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
return CustomWizard.submissions(params.wizardId);
|
return CustomWizard.submissions(params.wizardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
if (model.submissions) {
|
if (model && model.submissions) {
|
||||||
let fields = [];
|
let fields = ["username"];
|
||||||
model.submissions.forEach((s) => {
|
model.submissions.forEach((s) => {
|
||||||
Object.keys(s).forEach((k) => {
|
Object.keys(s.fields).forEach((k) => {
|
||||||
if (fields.indexOf(k) < 0) {
|
if (!excludedMetaFields.includes(k) && fields.indexOf(k) < 0) {
|
||||||
fields.push(k);
|
fields.push(k);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -19,9 +21,13 @@ export default DiscourseRoute.extend({
|
||||||
|
|
||||||
let submissions = [];
|
let submissions = [];
|
||||||
model.submissions.forEach((s) => {
|
model.submissions.forEach((s) => {
|
||||||
let submission = {};
|
let submission = {
|
||||||
fields.forEach((f) => {
|
username: s.username,
|
||||||
submission[f] = s[f];
|
};
|
||||||
|
Object.keys(s.fields).forEach((f) => {
|
||||||
|
if (fields.includes(f)) {
|
||||||
|
submission[f] = s.fields[f];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
submissions.push(submission);
|
submissions.push(submission);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
{{d-button
|
{{d-button
|
||||||
label="refresh"
|
label="refresh"
|
||||||
icon="refresh"
|
icon="sync"
|
||||||
action="refresh"
|
action="refresh"
|
||||||
class="refresh"}}
|
class="refresh"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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="multi-select">
|
|
||||||
{{#each field.serializers as |serializer|}}
|
|
||||||
<label>{{serializer}}</label>
|
|
||||||
{{/each}}
|
|
||||||
</td>
|
|
||||||
<td class="input"><label>{{field.name}}</label></td>
|
<td class="input"><label>{{field.name}}</label></td>
|
||||||
<td class="actions">
|
<td class="multi-select">
|
||||||
{{d-button action="edit" icon="pencil-alt"}}
|
{{#if isExternal}}
|
||||||
|
—
|
||||||
|
{{else}}
|
||||||
|
{{#each field.serializers as |serializer|}}
|
||||||
|
<label>{{serializer}}</label>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
</td>
|
</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">
|
||||||
|
{{d-button action="edit" icon="pencil-alt"}}
|
||||||
|
</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>
|
||||||
|
|
|
@ -127,6 +127,31 @@
|
||||||
checked=field.char_counter}}
|
checked=field.char_counter}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.field.field_placeholder"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{textarea
|
||||||
|
name="field_placeholder"
|
||||||
|
class="medium"
|
||||||
|
value=field.placeholder}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isComposerPreview}}
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.field.preview_template"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{textarea name="preview-template" value=field.preview_template}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isUpload}}
|
{{#if isUpload}}
|
||||||
|
|
|
@ -1,43 +1,4 @@
|
||||||
//= require discourse/app/lib/autocomplete
|
//= require_tree_discourse discourse/app/lib
|
||||||
//= require discourse/app/lib/utilities
|
|
||||||
//= require discourse/app/lib/offset-calculator
|
|
||||||
//= require discourse/app/lib/lock-on
|
|
||||||
//= require discourse/app/lib/text-direction
|
|
||||||
//= require discourse/app/lib/to-markdown
|
|
||||||
//= require discourse/app/lib/load-script
|
|
||||||
//= require discourse/app/lib/url
|
|
||||||
//= require discourse/app/lib/ajax
|
|
||||||
//= require discourse/app/lib/ajax-error
|
|
||||||
//= require discourse/app/lib/page-visible
|
|
||||||
//= require discourse/app/lib/logout
|
|
||||||
//= require discourse/app/lib/render-tag
|
|
||||||
//= require discourse/app/lib/notification-levels
|
|
||||||
//= require discourse/app/lib/computed
|
|
||||||
//= require discourse/app/lib/user-search
|
|
||||||
//= require discourse/app/lib/text
|
|
||||||
//= require discourse/app/lib/formatter
|
|
||||||
//= require discourse/app/lib/quote
|
|
||||||
//= require discourse/app/lib/link-mentions
|
|
||||||
//= require discourse/app/lib/link-hashtags
|
|
||||||
//= require discourse/app/lib/category-hashtags
|
|
||||||
//= require discourse/app/lib/tag-hashtags
|
|
||||||
//= require discourse/app/lib/uploads
|
|
||||||
//= require discourse/app/lib/category-tag-search
|
|
||||||
//= require discourse/app/lib/intercept-click
|
|
||||||
//= require discourse/app/lib/show-modal
|
|
||||||
//= require discourse/app/lib/key-value-store
|
|
||||||
//= require discourse/app/lib/settings
|
|
||||||
//= require discourse/app/lib/user-presence
|
|
||||||
//= require discourse/app/lib/hash
|
|
||||||
//= require discourse/app/lib/bookmark
|
|
||||||
//= require discourse/app/lib/put-cursor-at-end
|
|
||||||
//= require discourse/app/lib/safari-hacks
|
|
||||||
//= require discourse/app/lib/preload-store
|
|
||||||
//= require discourse/app/lib/topic-fancy-title
|
|
||||||
//= require discourse/app/lib/cookie
|
|
||||||
//= require discourse/app/lib/public-js-versions
|
|
||||||
//= require discourse/app/lib/load-oneboxes
|
|
||||||
//= require discourse/app/lib/highlight-syntax
|
|
||||||
|
|
||||||
//= require discourse/app/mixins/singleton
|
//= require discourse/app/mixins/singleton
|
||||||
//= require discourse/app/mixins/upload
|
//= require discourse/app/mixins/upload
|
||||||
|
@ -46,35 +7,7 @@
|
||||||
|
|
||||||
//= require message-bus
|
//= require message-bus
|
||||||
|
|
||||||
//= require discourse/app/models/login-method
|
//= require_tree_discourse discourse/app/models
|
||||||
//= require discourse/app/models/permission-type
|
|
||||||
//= require discourse/app/models/archetype
|
|
||||||
//= require discourse/app/models/rest
|
|
||||||
//= require discourse/app/models/site
|
|
||||||
//= require discourse/app/models/category
|
|
||||||
//= require discourse/app/models/session
|
|
||||||
//= require discourse/app/models/post-action-type
|
|
||||||
//= require discourse/app/models/trust-level
|
|
||||||
//= require discourse/app/models/store
|
|
||||||
//= require discourse/app/models/result-set
|
|
||||||
//= require discourse/app/models/bookmark
|
|
||||||
//= require discourse/app/models/user
|
|
||||||
//= require discourse/app/models/user-stream
|
|
||||||
//= require discourse/app/models/user-action
|
|
||||||
//= require discourse/app/models/user-action-group
|
|
||||||
//= require discourse/app/models/user-posts-stream
|
|
||||||
//= require discourse/app/models/badge
|
|
||||||
//= require discourse/app/models/badge-grouping
|
|
||||||
//= require discourse/app/models/user-badge
|
|
||||||
//= require discourse/app/models/topic
|
|
||||||
//= require discourse/app/models/action-summary
|
|
||||||
//= require discourse/app/models/user-action-stat
|
|
||||||
//= require discourse/app/models/user-drafts-stream
|
|
||||||
//= require discourse/app/models/user-draft
|
|
||||||
//= require discourse/app/models/composer
|
|
||||||
//= require discourse/app/models/draft
|
|
||||||
//= require discourse/app/models/group
|
|
||||||
//= require discourse/app/models/group-history
|
|
||||||
|
|
||||||
//= require discourse/app/helpers/category-link
|
//= require discourse/app/helpers/category-link
|
||||||
//= require discourse/app/helpers/user-avatar
|
//= require discourse/app/helpers/user-avatar
|
||||||
|
|
|
@ -1,3 +1,42 @@
|
||||||
import DateInput from "discourse/components/date-input";
|
import DateInput from "discourse/components/date-input";
|
||||||
|
import loadScript from "discourse/lib/load-script";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import I18n from "I18n";
|
||||||
|
/* global Pikaday:true */
|
||||||
|
|
||||||
export default DateInput.extend();
|
export default DateInput.extend({
|
||||||
|
useNativePicker: false,
|
||||||
|
|
||||||
|
@discourseComputed()
|
||||||
|
placeholder() {
|
||||||
|
return this.format;
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadPikadayPicker(container) {
|
||||||
|
return loadScript("/javascripts/pikaday.js").then(() => {
|
||||||
|
let defaultOptions = {
|
||||||
|
field: this.element.querySelector(".date-picker"),
|
||||||
|
container: container || this.element.querySelector(".picker-container"),
|
||||||
|
bound: container === null,
|
||||||
|
format: this.format,
|
||||||
|
firstDay: 1,
|
||||||
|
i18n: {
|
||||||
|
previousMonth: I18n.t("dates.previous_month"),
|
||||||
|
nextMonth: I18n.t("dates.next_month"),
|
||||||
|
months: moment.months(),
|
||||||
|
weekdays: moment.weekdays(),
|
||||||
|
weekdaysShort: moment.weekdaysShort(),
|
||||||
|
},
|
||||||
|
onSelect: (date) => this._handleSelection(date),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.relativeDate) {
|
||||||
|
defaultOptions = Object.assign({}, defaultOptions, {
|
||||||
|
minDate: moment(this.relativeDate).toDate(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pikaday(Object.assign({}, defaultOptions, this._opts()));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
||||||
|
import { schedule } from "@ember/runloop";
|
||||||
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { on } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
@on("init")
|
||||||
|
updatePreview() {
|
||||||
|
if (this.isDestroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule("afterRender", () => {
|
||||||
|
if (this._state !== "inDOM" || !this.element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $preview = $(this.element);
|
||||||
|
|
||||||
|
if ($preview.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.previewUpdated($preview);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
previewUpdated($preview) {
|
||||||
|
// Paint oneboxes
|
||||||
|
const paintFunc = () => {
|
||||||
|
loadOneboxes(
|
||||||
|
$preview[0],
|
||||||
|
ajax,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
this.siteSettings.max_oneboxes_per_post,
|
||||||
|
true // refresh on every load
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
discourseDebounce(this, paintFunc, 450);
|
||||||
|
|
||||||
|
// Short upload urls need resolution
|
||||||
|
resolveAllShortUrls(ajax, this.siteSettings, $preview[0]);
|
||||||
|
},
|
||||||
|
});
|
|
@ -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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* unit: custom_wizard:templates_and_builder
|
* unit: custom_wizard:templates_and_builder
|
||||||
|
@ -192,7 +192,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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,9 @@ export default {
|
||||||
const setDefaultOwner = requirejs("discourse-common/lib/get-owner")
|
const setDefaultOwner = requirejs("discourse-common/lib/get-owner")
|
||||||
.setDefaultOwner;
|
.setDefaultOwner;
|
||||||
const messageBus = requirejs("message-bus-client").default;
|
const messageBus = requirejs("message-bus-client").default;
|
||||||
|
const getToken = requirejs("wizard/lib/ajax").getToken;
|
||||||
|
const setEnvironment = requirejs("discourse-common/config/environment")
|
||||||
|
.setEnvironment;
|
||||||
const container = app.__container__;
|
const container = app.__container__;
|
||||||
Discourse.Model = EmberObject.extend();
|
Discourse.Model = EmberObject.extend();
|
||||||
Discourse.__container__ = container;
|
Discourse.__container__ = container;
|
||||||
|
@ -89,6 +91,7 @@ export default {
|
||||||
const session = container.lookup("session:main");
|
const session = container.lookup("session:main");
|
||||||
const setupData = document.getElementById("data-discourse-setup").dataset;
|
const setupData = document.getElementById("data-discourse-setup").dataset;
|
||||||
session.set("highlightJsPath", setupData.highlightJsPath);
|
session.set("highlightJsPath", setupData.highlightJsPath);
|
||||||
|
setEnvironment(setupData.environment);
|
||||||
|
|
||||||
Router.reopen({
|
Router.reopen({
|
||||||
rootURL: getUrl("/w/"),
|
rootURL: getUrl("/w/"),
|
||||||
|
@ -107,5 +110,9 @@ export default {
|
||||||
},
|
},
|
||||||
model() {},
|
model() {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$.ajaxPrefilter(function (_, __, jqXHR) {
|
||||||
|
jqXHR.setRequestHeader("X-CSRF-Token", getToken());
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import loadScript from "./load-script";
|
import loadScript from "./load-script";
|
||||||
import { default as PrettyText } from "pretty-text/pretty-text";
|
import { default as PrettyText, buildOptions } from "pretty-text/pretty-text";
|
||||||
import Handlebars from "handlebars";
|
import Handlebars from "handlebars";
|
||||||
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
|
|
||||||
export function cook(text, options) {
|
export function cook(text, options) {
|
||||||
|
if (!options) {
|
||||||
|
options = buildOptions({
|
||||||
|
getURL: getURL,
|
||||||
|
siteSettings: getOwner(this).lookup("site-settings:main"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return new Handlebars.SafeString(new PrettyText(options).cook(text));
|
return new Handlebars.SafeString(new PrettyText(options).cook(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
const getThemeId = () => {
|
const getThemeId = () => {
|
||||||
let themeId = parseInt($("meta[name=discourse_theme_ids]")[0].content, 10);
|
let themeId = parseInt(
|
||||||
|
document.querySelector("meta[name=discourse_theme_id]").content,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
if (!isNaN(themeId)) {
|
if (!isNaN(themeId)) {
|
||||||
return themeId.toString();
|
return themeId.toString();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{d-editor
|
{{d-editor
|
||||||
tabindex=field.tabindex
|
tabindex=field.tabindex
|
||||||
value=composer.reply
|
value=composer.reply
|
||||||
placeholder=replyPlaceholder
|
placeholderTranslated=replyPlaceholder
|
||||||
previewUpdated=(action "previewUpdated")
|
previewUpdated=(action "previewUpdated")
|
||||||
markdownOptions=markdownOptions
|
markdownOptions=markdownOptions
|
||||||
extraButtons=(action "extraButtons")
|
extraButtons=(action "extraButtons")
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="wizard-composer-preview d-editor-preview-wrapper">
|
||||||
|
<div class="d-editor-preview">
|
||||||
|
{{html-safe field.preview_template}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -2,4 +2,5 @@
|
||||||
date=date
|
date=date
|
||||||
onChange=(action "onChange")
|
onChange=(action "onChange")
|
||||||
tabindex=field.tabindex
|
tabindex=field.tabindex
|
||||||
|
format=field.format
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-message {
|
.wizard-message {
|
||||||
background-color: $primary-low;
|
background-color: var(--primary-low);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a + a {
|
a + a {
|
||||||
border-left: 1px solid $primary-medium;
|
border-left: 1px solid var(--primary-medium);
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
|
|
||||||
.wizard-settings-parent {
|
.wizard-settings-parent {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-settings-group {
|
.wizard-settings-group {
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
|
|
||||||
.wizard-custom-field {
|
.wizard-custom-field {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
background-color: dark-light-diff($primary, $secondary, 96%, -65%);
|
background-color: var(--primary-very-low);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
a {
|
a {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
background-color: $primary-low;
|
background-color: var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -256,6 +256,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.medium {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
|
@ -263,7 +267,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
input[disabled] {
|
input[disabled] {
|
||||||
background-color: $primary-low;
|
background-color: var(--primary-low);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,8 +438,8 @@
|
||||||
display: none;
|
display: none;
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: $secondary;
|
background-color: var(--secondary);
|
||||||
border: 1px solid $primary-medium;
|
border: 1px solid var(--primary-medium);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
&.force-preview {
|
&.force-preview {
|
||||||
|
@ -454,17 +458,17 @@
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $primary;
|
color: var(--primary);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $secondary;
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-editor-gutter-popover {
|
.wizard-editor-gutter-popover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: $secondary;
|
background-color: var(--secondary);
|
||||||
box-shadow: shadow("card");
|
box-shadow: shadow("card");
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
top: 40px;
|
top: 40px;
|
||||||
|
@ -502,7 +506,7 @@
|
||||||
width: 100px;
|
width: 100px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
background-color: $primary-low;
|
background-color: var(--primary-low);
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
@ -587,8 +591,8 @@
|
||||||
.add-mapper-input .btn,
|
.add-mapper-input .btn,
|
||||||
.btn-after-time,
|
.btn-after-time,
|
||||||
.wizard-editor-gutter .btn {
|
.wizard-editor-gutter .btn {
|
||||||
background-color: $secondary;
|
background-color: var(--secondary);
|
||||||
border: 1px solid $primary-medium;
|
border: 1px solid var(--primary-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-wizards-custom-fields {
|
.admin-wizards-custom-fields {
|
||||||
|
@ -667,6 +671,10 @@
|
||||||
margin-left: 5px !important;
|
margin-left: 5px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td.external {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
.wizard-api-authentication {
|
.wizard-api-authentication {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: $primary-very-low;
|
background-color: var(--primary-very-low);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
border-left: 1px solid $primary;
|
border-left: 1px solid var(--primary);
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-api-endpoints {
|
.wizard-api-endpoints {
|
||||||
background-color: $primary-very-low;
|
background-color: var(--primary-very-low);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
|
|
||||||
#import-button:enabled,
|
#import-button:enabled,
|
||||||
#export-button:enabled {
|
#export-button:enabled {
|
||||||
background-color: $tertiary;
|
background-color: var(--tertiary);
|
||||||
color: $secondary;
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#destroy-button:enabled {
|
#destroy-button:enabled {
|
||||||
background-color: $danger;
|
background-color: var(--danger);
|
||||||
color: $secondary;
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,13 +32,13 @@
|
||||||
|
|
||||||
.filename {
|
.filename {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border: 1px solid $primary;
|
border: 1px solid var(--primary);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $primary;
|
color: var(--primary);
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
width: min-content;
|
width: min-content;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border: 2px solid $primary-low;
|
border: 2px solid var(--primary-low);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 25px 7px 7px 7px;
|
padding: 25px 7px 7px 7px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
background: rgba($secondary, 0.5);
|
background: rgba(var(--secondary-rgb), 0.5);
|
||||||
border: 2px solid $primary-low;
|
border: 2px solid var(--primary-low);
|
||||||
|
|
||||||
.d-icon {
|
.d-icon {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
input[disabled] {
|
input[disabled] {
|
||||||
background-color: $primary-low;
|
background-color: var(--primary-low);
|
||||||
border-color: #ddd;
|
border-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,10 +62,10 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
background: $secondary;
|
background: var(--secondary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
border: 2px solid $primary-low;
|
border: 2px solid var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.association,
|
&.association,
|
||||||
|
@ -89,8 +89,8 @@
|
||||||
|
|
||||||
&.single {
|
&.single {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
background: $secondary;
|
background: var(--secondary);
|
||||||
border: 1px solid $primary-medium;
|
border: 1px solid var(--primary-medium);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-selector a {
|
.type-selector a {
|
||||||
color: $primary;
|
color: var(--primary);
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -150,11 +150,11 @@
|
||||||
box-shadow: shadow("dropdown");
|
box-shadow: shadow("dropdown");
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
background: $secondary;
|
background: var(--secondary);
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
padding: 5px 7px;
|
padding: 5px 7px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-list .remove-value-btn {
|
.value-list .remove-value-btn {
|
||||||
|
@ -162,7 +162,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
.d-icon {
|
.d-icon {
|
||||||
color: $primary;
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
.extra-info-wrapper & {
|
.extra-info-wrapper & {
|
||||||
color: $header-primary;
|
color: var(--header_primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
.extra-info-wrapper & {
|
.extra-info-wrapper & {
|
||||||
color: $header-primary;
|
color: var(--header_primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,11 +199,11 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba($primary, 0.8);
|
background-color: rgba(var(--primary-rgb), 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-composer-hyperlink-contents {
|
.wizard-composer-hyperlink-contents {
|
||||||
background-color: $secondary;
|
background-color: var(--secondary);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
@ -227,7 +227,7 @@
|
||||||
bottom: 1px;
|
bottom: 1px;
|
||||||
right: 1px;
|
right: 1px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: $secondary;
|
background-color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
|
@ -239,31 +239,43 @@
|
||||||
|
|
||||||
// Markdown table styles for wizard composer preview
|
// Markdown table styles for wizard composer preview
|
||||||
|
|
||||||
.cooked table,
|
.cooked,
|
||||||
.d-editor-preview table {
|
.d-editor-preview {
|
||||||
border-collapse: collapse;
|
a.mention {
|
||||||
|
display: inline-block; // https://bugzilla.mozilla.org/show_bug.cgi?id=1656119
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.93em;
|
||||||
|
color: var(--primary-high-or-secondary-low);
|
||||||
|
padding: 0 4px 1px;
|
||||||
|
background: var(--primary-low);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
tr {
|
table {
|
||||||
border-bottom: 1px solid var(--primary-low);
|
border-collapse: collapse;
|
||||||
&.highlighted {
|
|
||||||
animation: background-fade-highlight 2.5s ease-out;
|
tr {
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
&.highlighted {
|
||||||
|
animation: background-fade-highlight 2.5s ease-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
th {
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tbody {
|
tbody {
|
||||||
border-top: 3px solid var(--primary-low);
|
border-top: 3px solid var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 3px 3px 3px 0.5em;
|
padding: 3px 3px 3px 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,4 +162,15 @@
|
||||||
.text-field input {
|
.text-field input {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-field,
|
||||||
|
.textarea-field,
|
||||||
|
.composer-field {
|
||||||
|
input[type="text"],
|
||||||
|
textarea {
|
||||||
|
&:focus::placeholder {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@import "common/foundation/variables";
|
@import "common/foundation/variables";
|
||||||
@import "common/base/code_highlighting";
|
@import "common/base/code_highlighting";
|
||||||
@import "common/base/modal";
|
@import "common/base/modal";
|
||||||
|
@import "common/base/onebox";
|
||||||
@import "common/components/buttons";
|
@import "common/components/buttons";
|
||||||
@import "common/d-editor";
|
@import "common/d-editor";
|
||||||
@import "desktop/modal";
|
@import "desktop/modal";
|
||||||
|
|
|
@ -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
|
||||||
|
@ -173,7 +173,9 @@ en:
|
||||||
max_length_placeholder: "Maximum length in characters"
|
max_length_placeholder: "Maximum length in characters"
|
||||||
char_counter: "Character Counter"
|
char_counter: "Character Counter"
|
||||||
char_counter_placeholder: "Display Character Counter"
|
char_counter_placeholder: "Display Character Counter"
|
||||||
|
field_placeholder: "Field Placeholder"
|
||||||
file_types: "File Types"
|
file_types: "File Types"
|
||||||
|
preview_template: "Preview Template"
|
||||||
limit: "Limit"
|
limit: "Limit"
|
||||||
property: "Property"
|
property: "Property"
|
||||||
prefill: "Prefill"
|
prefill: "Prefill"
|
||||||
|
@ -200,6 +202,7 @@ en:
|
||||||
text: "Text"
|
text: "Text"
|
||||||
textarea: Textarea
|
textarea: Textarea
|
||||||
composer: Composer
|
composer: Composer
|
||||||
|
composer_preview: Composer Preview
|
||||||
text_only: Text Only
|
text_only: Text Only
|
||||||
number: Number
|
number: Number
|
||||||
checkbox: Checkbox
|
checkbox: Checkbox
|
||||||
|
@ -322,6 +325,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"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
en:
|
en:
|
||||||
admin:
|
admin:
|
||||||
wizard:
|
wizard:
|
||||||
submissions:
|
submission:
|
||||||
no_user: "deleted (id: %{id})"
|
no_user: "deleted (user_id: %{user_id})"
|
||||||
|
|
||||||
wizard:
|
wizard:
|
||||||
custom_title: "Wizard"
|
custom_title: "Wizard"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -13,34 +13,20 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
|
||||||
def show
|
def show
|
||||||
render_json_dump(
|
render_json_dump(
|
||||||
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
|
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
|
||||||
submissions: build_submissions.as_json
|
submissions: ActiveModel::ArraySerializer.new(ordered_submissions, each_serializer: CustomWizard::SubmissionSerializer)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def download
|
def download
|
||||||
send_data build_submissions.to_json,
|
send_data ordered_submissions.to_json,
|
||||||
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
|
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
|
||||||
content_type: "application/json",
|
content_type: "application/json",
|
||||||
disposition: "attachment"
|
disposition: "attachment"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
protected
|
||||||
|
|
||||||
def build_submissions
|
def ordered_submissions
|
||||||
PluginStoreRow.where(plugin_name: "#{@wizard.id}_submissions")
|
CustomWizard::Submission.list(@wizard, order_by: 'id')
|
||||||
.order('id DESC')
|
|
||||||
.map do |row|
|
|
||||||
value = ::JSON.parse(row.value)
|
|
||||||
|
|
||||||
if user = User.find_by(id: row.key)
|
|
||||||
username = user.username
|
|
||||||
else
|
|
||||||
username = I18n.t('admin.wizard.submissions.no_user', id: row.key)
|
|
||||||
end
|
|
||||||
|
|
||||||
value.map do |v|
|
|
||||||
{ username: username }.merge!(v.except("redirect_to"))
|
|
||||||
end
|
|
||||||
end.flatten
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
|
|
||||||
update[:fields] = {}
|
update[:fields] = {}
|
||||||
if params[:fields]
|
if params[:fields]
|
||||||
field_ids = @step_template['fields'].map { |f| f['id'] }
|
field_ids = @builder.wizard.field_ids
|
||||||
params[:fields].each do |k, v|
|
params[:fields].each do |k, v|
|
||||||
update[:fields][k] = v if field_ids.include? k
|
update[:fields][k] = v if field_ids.include? k
|
||||||
end
|
end
|
||||||
|
@ -23,7 +23,7 @@ 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
|
||||||
|
@ -36,15 +36,19 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
if current_step.final?
|
if current_step.final?
|
||||||
builder.template.actions.each do |action_template|
|
builder.template.actions.each do |action_template|
|
||||||
if action_template['run_after'] === 'wizard_completion'
|
if action_template['run_after'] === 'wizard_completion'
|
||||||
CustomWizard::Action.new(
|
action_result = CustomWizard::Action.new(
|
||||||
action: action_template,
|
action: action_template,
|
||||||
wizard: @wizard,
|
wizard: @wizard,
|
||||||
data: current_submission
|
submission: current_submission
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
if action_result.success?
|
||||||
|
current_submission = action_result.submission
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@wizard.save_submission(current_submission)
|
current_submission.save
|
||||||
|
|
||||||
if redirect = get_redirect
|
if redirect = get_redirect
|
||||||
updater.result[:redirect_on_complete] = redirect
|
updater.result[:redirect_on_complete] = redirect
|
||||||
|
@ -54,6 +58,8 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
|
|
||||||
result[:final] = true
|
result[:final] = true
|
||||||
else
|
else
|
||||||
|
current_submission.save
|
||||||
|
|
||||||
result[:final] = false
|
result[:final] = false
|
||||||
result[:next_step_id] = current_step.next.id
|
result[:next_step_id] = current_step.next.id
|
||||||
end
|
end
|
||||||
|
@ -101,9 +107,9 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
def get_redirect
|
def get_redirect
|
||||||
return @result[:redirect_on_next] if @result[:redirect_on_next].present?
|
return @result[:redirect_on_next] if @result[:redirect_on_next].present?
|
||||||
|
|
||||||
current_submission = @wizard.current_submission
|
submission = @wizard.current_submission
|
||||||
return nil unless current_submission.present?
|
return nil unless submission.present?
|
||||||
## route_to set by actions, redirect_on_complete set by actions, redirect_to set at wizard entry
|
## route_to set by actions, redirect_on_complete set by actions, redirect_to set at wizard entry
|
||||||
current_submission[:route_to] || current_submission[:redirect_on_complete] || current_submission[:redirect_to]
|
submission.route_to || submission.redirect_on_complete || submission.redirect_to
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
|
|
||||||
before_action :ensure_plugin_enabled
|
before_action :ensure_plugin_enabled
|
||||||
helper_method :wizard_page_title
|
helper_method :wizard_page_title
|
||||||
helper_method :wizard_theme_ids
|
helper_method :wizard_theme_id
|
||||||
helper_method :wizard_theme_lookup
|
helper_method :wizard_theme_lookup
|
||||||
helper_method :wizard_theme_translations_lookup
|
helper_method :wizard_theme_translations_lookup
|
||||||
|
|
||||||
|
@ -20,16 +20,16 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title')
|
wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title')
|
||||||
end
|
end
|
||||||
|
|
||||||
def wizard_theme_ids
|
def wizard_theme_id
|
||||||
wizard ? [wizard.theme_id] : nil
|
wizard ? wizard.theme_id : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def wizard_theme_lookup(name)
|
def wizard_theme_lookup(name)
|
||||||
Theme.lookup_field(wizard_theme_ids, mobile_view? ? :mobile : :desktop, name)
|
Theme.lookup_field(wizard_theme_id, mobile_view? ? :mobile : :desktop, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def wizard_theme_translations_lookup
|
def wizard_theme_translations_lookup
|
||||||
Theme.lookup_field(wizard_theme_ids, :translations, I18n.locale)
|
Theme.lookup_field(wizard_theme_id, :translations, I18n.locale)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -61,10 +61,11 @@ 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']
|
|
||||||
result.merge!(redirect_to: submission['redirect_to'])
|
if submission.present? && submission.redirect_to
|
||||||
|
result.merge!(redirect_to: submission.redirect_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
wizard.final_cleanup!
|
wizard.final_cleanup!
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"result": {
|
"result": {
|
||||||
"line": 90.52
|
"line": 91.83
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
||||||
|
|
|
@ -5,7 +5,7 @@ module InvitesControllerCustomWizard
|
||||||
wizard_id = @user.custom_fields['redirect_to_wizard']
|
wizard_id = @user.custom_fields['redirect_to_wizard']
|
||||||
|
|
||||||
if wizard_id && url != '/'
|
if wizard_id && url != '/'
|
||||||
CustomWizard::Wizard.set_submission_redirect(@user, wizard_id, url)
|
CustomWizard::Wizard.set_wizard_redirect(@user, wizard_id, url)
|
||||||
url = "/w/#{wizard_id.dasherize}"
|
url = "/w/#{wizard_id.dasherize}"
|
||||||
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
|
|
|
@ -9,7 +9,7 @@ module Jobs
|
||||||
user_ids = []
|
user_ids = []
|
||||||
|
|
||||||
User.human_users.each do |user|
|
User.human_users.each do |user|
|
||||||
if CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
|
if CustomWizard::Wizard.set_user_redirect(wizard.id, user)
|
||||||
user_ids.push(user.id)
|
user_ids.push(user.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class CustomWizard::Action
|
class CustomWizard::Action
|
||||||
attr_accessor :data,
|
attr_accessor :submission,
|
||||||
:action,
|
:action,
|
||||||
:user,
|
:user,
|
||||||
:guardian,
|
:guardian,
|
||||||
|
@ -11,7 +11,7 @@ class CustomWizard::Action
|
||||||
@action = opts[:action]
|
@action = opts[:action]
|
||||||
@user = @wizard.user
|
@user = @wizard.user
|
||||||
@guardian = Guardian.new(@user)
|
@guardian = Guardian.new(@user)
|
||||||
@data = opts[:data]
|
@submission = opts[:submission]
|
||||||
@log = []
|
@log = []
|
||||||
@result = CustomWizard::ActionResult.new
|
@result = CustomWizard::ActionResult.new
|
||||||
end
|
end
|
||||||
|
@ -26,14 +26,21 @@ class CustomWizard::Action
|
||||||
end
|
end
|
||||||
|
|
||||||
if @result.success? && @result.output.present?
|
if @result.success? && @result.output.present?
|
||||||
data[action['id']] = @result.output
|
@submission.fields[action['id']] = @result.output
|
||||||
end
|
end
|
||||||
|
|
||||||
save_log
|
save_log
|
||||||
|
|
||||||
|
@result.submission = @submission
|
||||||
|
@result
|
||||||
|
end
|
||||||
|
|
||||||
|
def mapper_data
|
||||||
|
@mapper_data ||= @submission&.fields_and_meta || {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def mapper
|
def mapper
|
||||||
@mapper ||= CustomWizard::Mapper.new(user: user, data: data)
|
@mapper ||= CustomWizard::Mapper.new(user: user, data: mapper_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_topic
|
def create_topic
|
||||||
|
@ -47,7 +54,7 @@ class CustomWizard::Action
|
||||||
messages = creator.errors.full_messages.join(" ")
|
messages = creator.errors.full_messages.join(" ")
|
||||||
log_error("failed to create", messages)
|
log_error("failed to create", messages)
|
||||||
elsif action['skip_redirect'].blank?
|
elsif action['skip_redirect'].blank?
|
||||||
data['redirect_on_complete'] = post.topic.url
|
@submission.redirect_on_complete = post.topic.url
|
||||||
end
|
end
|
||||||
|
|
||||||
if creator.errors.blank?
|
if creator.errors.blank?
|
||||||
|
@ -65,7 +72,7 @@ class CustomWizard::Action
|
||||||
if action['required'].present?
|
if action['required'].present?
|
||||||
required = CustomWizard::Mapper.new(
|
required = CustomWizard::Mapper.new(
|
||||||
inputs: action['required'],
|
inputs: action['required'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -79,7 +86,7 @@ class CustomWizard::Action
|
||||||
|
|
||||||
targets = CustomWizard::Mapper.new(
|
targets = CustomWizard::Mapper.new(
|
||||||
inputs: action['recipient'],
|
inputs: action['recipient'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user,
|
user: user,
|
||||||
multiple: true
|
multiple: true
|
||||||
).perform
|
).perform
|
||||||
|
@ -115,7 +122,7 @@ class CustomWizard::Action
|
||||||
messages = creator.errors.full_messages.join(" ")
|
messages = creator.errors.full_messages.join(" ")
|
||||||
log_error("failed to create message", messages)
|
log_error("failed to create message", messages)
|
||||||
elsif action['skip_redirect'].blank?
|
elsif action['skip_redirect'].blank?
|
||||||
data['redirect_on_complete'] = post.topic.url
|
@submission.redirect_on_complete = post.topic.url
|
||||||
end
|
end
|
||||||
|
|
||||||
if creator.errors.blank?
|
if creator.errors.blank?
|
||||||
|
@ -178,7 +185,7 @@ class CustomWizard::Action
|
||||||
def watch_categories
|
def watch_categories
|
||||||
watched_categories = CustomWizard::Mapper.new(
|
watched_categories = CustomWizard::Mapper.new(
|
||||||
inputs: action['categories'],
|
inputs: action['categories'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -193,7 +200,7 @@ class CustomWizard::Action
|
||||||
|
|
||||||
mute_remainder = CustomWizard::Mapper.new(
|
mute_remainder = CustomWizard::Mapper.new(
|
||||||
inputs: action['mute_remainder'],
|
inputs: action['mute_remainder'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -202,7 +209,7 @@ class CustomWizard::Action
|
||||||
if action['usernames']
|
if action['usernames']
|
||||||
mapped_users = CustomWizard::Mapper.new(
|
mapped_users = CustomWizard::Mapper.new(
|
||||||
inputs: action['usernames'],
|
inputs: action['usernames'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -284,7 +291,7 @@ class CustomWizard::Action
|
||||||
end
|
end
|
||||||
|
|
||||||
route_to = Discourse.base_uri + url
|
route_to = Discourse.base_uri + url
|
||||||
@result.output = data['route_to'] = route_to
|
@result.output = @submission.route_to = route_to
|
||||||
|
|
||||||
log_success("route: #{route_to}")
|
log_success("route: #{route_to}")
|
||||||
else
|
else
|
||||||
|
@ -295,7 +302,7 @@ class CustomWizard::Action
|
||||||
def add_to_group
|
def add_to_group
|
||||||
group_map = CustomWizard::Mapper.new(
|
group_map = CustomWizard::Mapper.new(
|
||||||
inputs: action['group'],
|
inputs: action['group'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user,
|
user: user,
|
||||||
opts: {
|
opts: {
|
||||||
multiple: true
|
multiple: true
|
||||||
|
@ -345,18 +352,18 @@ class CustomWizard::Action
|
||||||
else
|
else
|
||||||
url = CustomWizard::Mapper.new(
|
url = CustomWizard::Mapper.new(
|
||||||
inputs: url_input,
|
inputs: url_input,
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
if action['code']
|
if action['code']
|
||||||
data[action['code']] = SecureRandom.hex(8)
|
@submission.fields[action['code']] = SecureRandom.hex(8)
|
||||||
url += "&#{action['code']}=#{data[action['code']]}"
|
url += "&#{action['code']}=#{@submission.fields[action['code']]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
route_to = UrlHelper.encode(url)
|
route_to = UrlHelper.encode(url)
|
||||||
data['route_to'] = route_to
|
@submission.route_to = route_to
|
||||||
|
|
||||||
log_info("route: #{route_to}")
|
log_info("route: #{route_to}")
|
||||||
end
|
end
|
||||||
|
@ -416,7 +423,7 @@ class CustomWizard::Action
|
||||||
def action_category
|
def action_category
|
||||||
output = CustomWizard::Mapper.new(
|
output = CustomWizard::Mapper.new(
|
||||||
inputs: action['category'],
|
inputs: action['category'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -434,7 +441,7 @@ class CustomWizard::Action
|
||||||
def action_tags
|
def action_tags
|
||||||
output = CustomWizard::Mapper.new(
|
output = CustomWizard::Mapper.new(
|
||||||
inputs: action['tags'],
|
inputs: action['tags'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user,
|
user: user,
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -451,35 +458,54 @@ class CustomWizard::Action
|
||||||
if (custom_fields = action['custom_fields']).present?
|
if (custom_fields = action['custom_fields']).present?
|
||||||
field_map = CustomWizard::Mapper.new(
|
field_map = CustomWizard::Mapper.new(
|
||||||
inputs: custom_fields,
|
inputs: custom_fields,
|
||||||
data: data,
|
data: mapper_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] ||= {}
|
||||||
params[:topic_opts][:custom_fields][name] = value
|
|
||||||
|
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
|
||||||
|
end
|
||||||
else
|
else
|
||||||
params[:custom_fields] ||= {}
|
if type === :json
|
||||||
params[:custom_fields][name] = value
|
params[:custom_fields][name] ||= {}
|
||||||
|
params[:custom_fields][name][json_attr] = value
|
||||||
|
else
|
||||||
|
params[:custom_fields] ||= {}
|
||||||
|
params[:custom_fields][name] = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -494,7 +520,7 @@ class CustomWizard::Action
|
||||||
|
|
||||||
params[:title] = CustomWizard::Mapper.new(
|
params[:title] = CustomWizard::Mapper.new(
|
||||||
inputs: action['title'],
|
inputs: action['title'],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -506,7 +532,7 @@ class CustomWizard::Action
|
||||||
wizard: true,
|
wizard: true,
|
||||||
template: true
|
template: true
|
||||||
) :
|
) :
|
||||||
data[action['post']]
|
@submission.fields[action['post']]
|
||||||
|
|
||||||
params[:import_mode] = ActiveRecord::Type::Boolean.new.cast(action['suppress_notifications'])
|
params[:import_mode] = ActiveRecord::Type::Boolean.new.cast(action['suppress_notifications'])
|
||||||
|
|
||||||
|
@ -529,7 +555,7 @@ class CustomWizard::Action
|
||||||
unless action[field].nil? || action[field] == ""
|
unless action[field].nil? || action[field] == ""
|
||||||
params[field.to_sym] = CustomWizard::Mapper.new(
|
params[field.to_sym] = CustomWizard::Mapper.new(
|
||||||
inputs: action[field],
|
inputs: action[field],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
end
|
end
|
||||||
|
@ -568,7 +594,7 @@ class CustomWizard::Action
|
||||||
if input.present?
|
if input.present?
|
||||||
value = CustomWizard::Mapper.new(
|
value = CustomWizard::Mapper.new(
|
||||||
inputs: input,
|
inputs: input,
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
@ -598,7 +624,7 @@ class CustomWizard::Action
|
||||||
if action[attr].present?
|
if action[attr].present?
|
||||||
value = CustomWizard::Mapper.new(
|
value = CustomWizard::Mapper.new(
|
||||||
inputs: action[attr],
|
inputs: action[attr],
|
||||||
data: data,
|
data: mapper_data,
|
||||||
user: user
|
user: user
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class CustomWizard::ActionResult
|
class CustomWizard::ActionResult
|
||||||
attr_accessor :success, :handler, :output
|
attr_accessor :success, :handler, :output, :submission
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@success = false
|
@success = false
|
||||||
|
|
|
@ -24,13 +24,13 @@ class CustomWizard::Builder
|
||||||
def mapper
|
def mapper
|
||||||
CustomWizard::Mapper.new(
|
CustomWizard::Mapper.new(
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @wizard.current_submission
|
data: @wizard.current_submission&.fields_and_meta
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -47,9 +47,8 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
step.on_update do |updater|
|
step.on_update do |updater|
|
||||||
@updater = updater
|
@updater = updater
|
||||||
@submission = (@wizard.current_submission || {})
|
@submission = @wizard.current_submission
|
||||||
.merge(@updater.submission)
|
@submission.fields.merge!(@updater.submission)
|
||||||
.with_indifferent_access
|
|
||||||
|
|
||||||
@updater.validate
|
@updater.validate
|
||||||
next if @updater.errors.any?
|
next if @updater.errors.any?
|
||||||
|
@ -60,11 +59,11 @@ class CustomWizard::Builder
|
||||||
run_step_actions
|
run_step_actions
|
||||||
|
|
||||||
if @updater.errors.empty?
|
if @updater.errors.empty?
|
||||||
if route_to = @submission['route_to']
|
route_to = @submission.route_to
|
||||||
@submission.delete('route_to')
|
@submission.route_to = nil
|
||||||
end
|
@submission.save
|
||||||
|
|
||||||
@wizard.save_submission(@submission)
|
@wizard.update!
|
||||||
@updater.result[:redirect_on_next] = route_to if route_to
|
@updater.result[:redirect_on_next] = route_to if route_to
|
||||||
|
|
||||||
true
|
true
|
||||||
|
@ -75,7 +74,7 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@wizard.update_step_order!
|
@wizard.update!
|
||||||
@wizard
|
@wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,8 +99,8 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
params[:value] = prefill_field(field_template, step_template)
|
params[:value] = prefill_field(field_template, step_template)
|
||||||
|
|
||||||
if !build_opts[:reset] && (submission = @wizard.current_submission)
|
if !build_opts[:reset] && (submission = @wizard.current_submission).present?
|
||||||
params[:value] = submission[field_template['id']] if submission[field_template['id']]
|
params[:value] = submission.fields[field_template['id']] if submission.fields[field_template['id']]
|
||||||
end
|
end
|
||||||
|
|
||||||
if field_template['type'] === 'group' && params[:value].present?
|
if field_template['type'] === 'group' && params[:value].present?
|
||||||
|
@ -144,7 +143,7 @@ class CustomWizard::Builder
|
||||||
content = CustomWizard::Mapper.new(
|
content = CustomWizard::Mapper.new(
|
||||||
inputs: content_inputs,
|
inputs: content_inputs,
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @wizard.current_submission,
|
data: @wizard.current_submission&.fields_and_meta,
|
||||||
opts: {
|
opts: {
|
||||||
with_type: true
|
with_type: true
|
||||||
}
|
}
|
||||||
|
@ -179,7 +178,7 @@ class CustomWizard::Builder
|
||||||
index = CustomWizard::Mapper.new(
|
index = CustomWizard::Mapper.new(
|
||||||
inputs: field_template['index'],
|
inputs: field_template['index'],
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @wizard.current_submission
|
data: @wizard.current_submission&.fields_and_meta
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
params[:index] = index.to_i unless index.nil?
|
params[:index] = index.to_i unless index.nil?
|
||||||
|
@ -195,6 +194,28 @@ class CustomWizard::Builder
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if field_template['preview_template'].present?
|
||||||
|
preview_template = mapper.interpolate(
|
||||||
|
field_template['preview_template'],
|
||||||
|
user: true,
|
||||||
|
value: true,
|
||||||
|
wizard: true,
|
||||||
|
template: true
|
||||||
|
)
|
||||||
|
|
||||||
|
params[:preview_template] = PrettyText.cook(preview_template)
|
||||||
|
end
|
||||||
|
|
||||||
|
if field_template['placeholder'].present?
|
||||||
|
params[:placeholder] = mapper.interpolate(
|
||||||
|
field_template['placeholder'],
|
||||||
|
user: true,
|
||||||
|
value: true,
|
||||||
|
wizard: true,
|
||||||
|
template: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
field = step.add_field(params)
|
field = step.add_field(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -203,7 +224,7 @@ class CustomWizard::Builder
|
||||||
CustomWizard::Mapper.new(
|
CustomWizard::Mapper.new(
|
||||||
inputs: prefill,
|
inputs: prefill,
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @wizard.current_submission
|
data: @wizard.current_submission&.fields_and_meta
|
||||||
).perform
|
).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -213,7 +234,7 @@ class CustomWizard::Builder
|
||||||
result = CustomWizard::Mapper.new(
|
result = CustomWizard::Mapper.new(
|
||||||
inputs: template['condition'],
|
inputs: template['condition'],
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @wizard.current_submission,
|
data: @wizard.current_submission&.fields_and_meta,
|
||||||
opts: {
|
opts: {
|
||||||
multiple: true
|
multiple: true
|
||||||
}
|
}
|
||||||
|
@ -283,19 +304,20 @@ class CustomWizard::Builder
|
||||||
permitted_data = {}
|
permitted_data = {}
|
||||||
submission_key = nil
|
submission_key = nil
|
||||||
params_key = nil
|
params_key = nil
|
||||||
submission = @wizard.current_submission || {}
|
submission = @wizard.current_submission
|
||||||
|
|
||||||
permitted_params.each do |pp|
|
permitted_params.each do |pp|
|
||||||
pair = pp['pairs'].first
|
pair = pp['pairs'].first
|
||||||
params_key = pair['key'].to_sym
|
params_key = pair['key'].to_sym
|
||||||
submission_key = pair['value'].to_sym
|
submission_key = pair['value'].to_sym
|
||||||
|
|
||||||
if submission_key && params_key
|
if submission_key && params_key && params[params_key].present?
|
||||||
submission[submission_key] = params[params_key]
|
submission.permitted_param_keys << submission_key.to_s
|
||||||
|
submission.fields[submission_key] = params[params_key]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@wizard.save_submission(submission)
|
submission.save
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_required_data(step, step_template)
|
def ensure_required_data(step, step_template)
|
||||||
|
@ -304,13 +326,13 @@ class CustomWizard::Builder
|
||||||
pair['key'].present? && pair['value'].present?
|
pair['key'].present? && pair['value'].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
if pairs.any? && !@wizard.current_submission
|
if pairs.any? && !@wizard.current_submission.present?
|
||||||
step.permitted = false
|
step.permitted = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
pairs.each do |pair|
|
pairs.each do |pair|
|
||||||
pair['key'] = @wizard.current_submission[pair['key']]
|
pair['key'] = @wizard.current_submission.fields[pair['key']]
|
||||||
end
|
end
|
||||||
|
|
||||||
if !mapper.validate_pairs(pairs)
|
if !mapper.validate_pairs(pairs)
|
||||||
|
@ -334,11 +356,15 @@ class CustomWizard::Builder
|
||||||
if @template.actions.present?
|
if @template.actions.present?
|
||||||
@template.actions.each do |action_template|
|
@template.actions.each do |action_template|
|
||||||
if action_template['run_after'] === updater.step.id
|
if action_template['run_after'] === updater.step.id
|
||||||
CustomWizard::Action.new(
|
result = CustomWizard::Action.new(
|
||||||
action: action_template,
|
action: action_template,
|
||||||
wizard: @wizard,
|
wizard: @wizard,
|
||||||
data: @submission
|
submission: @submission
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
|
if result.success?
|
||||||
|
@submission = result.submission
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
lib/custom_wizard/exceptions/exceptions.rb
Normale Datei
5
lib/custom_wizard/exceptions/exceptions.rb
Normale Datei
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module CustomWizard
|
||||||
|
class SprocketsFileNotFound < StandardError; end
|
||||||
|
class SprocketsEmptyPath < StandardError; end
|
||||||
|
end
|
|
@ -45,6 +45,8 @@ class CustomWizard::Field
|
||||||
content: [:serializable, :permitted, :mapped],
|
content: [:serializable, :permitted, :mapped],
|
||||||
prefill: [:permitted, :mapped],
|
prefill: [:permitted, :mapped],
|
||||||
condition: [:permitted, :mapped],
|
condition: [:permitted, :mapped],
|
||||||
|
preview_template: [:serializable, :permitted, :mapped],
|
||||||
|
placeholder: [:serializable, :permitted, :mapped],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,20 +110,26 @@ class CustomWizard::Field
|
||||||
max_length: nil,
|
max_length: nil,
|
||||||
prefill: nil,
|
prefill: nil,
|
||||||
char_counter: nil,
|
char_counter: nil,
|
||||||
validations: nil
|
validations: nil,
|
||||||
|
placeholder: nil
|
||||||
},
|
},
|
||||||
textarea: {
|
textarea: {
|
||||||
min_length: nil,
|
min_length: nil,
|
||||||
max_length: nil,
|
max_length: nil,
|
||||||
prefill: nil,
|
prefill: nil,
|
||||||
char_counter: nil
|
char_counter: nil,
|
||||||
|
placeholder: nil
|
||||||
},
|
},
|
||||||
composer: {
|
composer: {
|
||||||
min_length: nil,
|
min_length: nil,
|
||||||
max_length: nil,
|
max_length: nil,
|
||||||
char_counter: nil
|
char_counter: nil,
|
||||||
|
placeholder: nil
|
||||||
},
|
},
|
||||||
text_only: {},
|
text_only: {},
|
||||||
|
composer_preview: {
|
||||||
|
preview_template: nil,
|
||||||
|
},
|
||||||
date: {
|
date: {
|
||||||
format: "YYYY-MM-DD"
|
format: "YYYY-MM-DD"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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]
|
||||||
|
|
118
lib/custom_wizard/submission.rb
Normale Datei
118
lib/custom_wizard/submission.rb
Normale Datei
|
@ -0,0 +1,118 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class CustomWizard::Submission
|
||||||
|
include ActiveModel::SerializerSupport
|
||||||
|
|
||||||
|
KEY ||= "submissions"
|
||||||
|
META ||= %w(submitted_at route_to redirect_on_complete redirect_to)
|
||||||
|
|
||||||
|
attr_reader :id,
|
||||||
|
:user,
|
||||||
|
:user_id,
|
||||||
|
:wizard
|
||||||
|
|
||||||
|
attr_accessor :fields,
|
||||||
|
:permitted_param_keys
|
||||||
|
|
||||||
|
META.each do |attr|
|
||||||
|
class_eval { attr_accessor attr }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(wizard, data = {}, user_id = nil)
|
||||||
|
@wizard = wizard
|
||||||
|
@user_id = user_id
|
||||||
|
|
||||||
|
if user_id
|
||||||
|
@user = User.find_by(id: user_id)
|
||||||
|
else
|
||||||
|
@user = wizard.user
|
||||||
|
end
|
||||||
|
|
||||||
|
data = (data || {}).with_indifferent_access
|
||||||
|
@id = data['id'] || SecureRandom.hex(12)
|
||||||
|
non_field_keys = META + ['id']
|
||||||
|
@fields = data.except(*non_field_keys) || {}
|
||||||
|
|
||||||
|
META.each do |attr|
|
||||||
|
send("#{attr}=", data[attr]) if data[attr]
|
||||||
|
end
|
||||||
|
|
||||||
|
@permitted_param_keys = data['permitted_param_keys'] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
def save
|
||||||
|
return nil unless wizard.save_submissions
|
||||||
|
validate
|
||||||
|
|
||||||
|
submission_list = self.class.list(wizard, user_id: user.id)
|
||||||
|
submissions = submission_list.select { |submission| submission.id != self.id }
|
||||||
|
submissions.push(self)
|
||||||
|
|
||||||
|
submission_data = submissions.map { |submission| data_to_save(submission) }
|
||||||
|
PluginStore.set("#{wizard.id}_#{KEY}", user.id, submission_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate
|
||||||
|
self.fields = fields.select { |key, value| validate_field_key(key) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_field_key(key)
|
||||||
|
wizard.field_ids.include?(key) ||
|
||||||
|
wizard.action_ids.include?(key) ||
|
||||||
|
permitted_param_keys.include?(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fields_and_meta
|
||||||
|
result = fields
|
||||||
|
|
||||||
|
META.each do |attr|
|
||||||
|
if value = self.send(attr)
|
||||||
|
result[attr] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def present?
|
||||||
|
fields_and_meta.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def data_to_save(submission)
|
||||||
|
data = {
|
||||||
|
id: submission.id
|
||||||
|
}
|
||||||
|
|
||||||
|
data.merge!(submission.fields_and_meta)
|
||||||
|
|
||||||
|
if submission.permitted_param_keys.present?
|
||||||
|
data[:permitted_param_keys] = submission.permitted_param_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get(wizard, user_id)
|
||||||
|
data = PluginStore.get("#{wizard.id}_#{KEY}", user_id).first
|
||||||
|
new(wizard, data, user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.list(wizard, user_id: nil, order_by: nil)
|
||||||
|
params = { plugin_name: "#{wizard.id}_#{KEY}" }
|
||||||
|
params[:key] = user_id if user_id.present?
|
||||||
|
|
||||||
|
query = PluginStoreRow.where(params)
|
||||||
|
query = query.order("#{order_by} DESC") if order_by.present?
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
query.each do |record|
|
||||||
|
if (submission_data = ::JSON.parse(record.value)).any?
|
||||||
|
submission_data.each do |data|
|
||||||
|
result.push(new(wizard, data, record.key))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,18 +49,15 @@ 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)
|
||||||
if wizard.after_time
|
|
||||||
Jobs.cancel_scheduled_job(:set_after_time_wizard)
|
|
||||||
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Jobs.cancel_scheduled_job(:set_after_time_wizard) if wizard.after_time
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -32,11 +32,11 @@ class ::CustomWizard::UpdateValidator
|
||||||
@updater.errors.add(field_id, I18n.t('wizard.field.required', label: label))
|
@updater.errors.add(field_id, I18n.t('wizard.field.required', label: label))
|
||||||
end
|
end
|
||||||
|
|
||||||
if min_length && value.is_a?(String) && value.strip.length < min_length.to_i
|
if min_length.present? && value.is_a?(String) && value.strip.length < min_length.to_i
|
||||||
@updater.errors.add(field_id, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
|
@updater.errors.add(field_id, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
|
||||||
end
|
end
|
||||||
|
|
||||||
if max_length && value.is_a?(String) && value.strip.length > max_length.to_i
|
if max_length.present? && value.is_a?(String) && value.strip.length > max_length.to_i
|
||||||
@updater.errors.add(field_id, I18n.t('wizard.field.too_long', label: label, max: max_length.to_i))
|
@updater.errors.add(field_id, I18n.t('wizard.field.too_long', label: label, max: max_length.to_i))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class ::CustomWizard::UpdateValidator
|
||||||
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_file', label: label, types: file_types))
|
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_file', label: label, types: file_types))
|
||||||
end
|
end
|
||||||
|
|
||||||
if ['date', 'date_time'].include?(type) && value.present? && !validate_date(value)
|
if ['date', 'date_time'].include?(type) && value.present? && !validate_date(value, format)
|
||||||
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_date'))
|
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_date'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,13 +88,8 @@ class ::CustomWizard::UpdateValidator
|
||||||
.include?(File.extname(value['original_filename'])[1..-1])
|
.include?(File.extname(value['original_filename'])[1..-1])
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_date(value)
|
def validate_date(value, format)
|
||||||
begin
|
v8.eval("moment('#{value}', '#{format}', true).isValid()")
|
||||||
Date.parse(value)
|
|
||||||
true
|
|
||||||
rescue ArgumentError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_time(value)
|
def validate_time(value)
|
||||||
|
@ -126,4 +121,12 @@ class ::CustomWizard::UpdateValidator
|
||||||
def standardise_boolean(value)
|
def standardise_boolean(value)
|
||||||
ActiveRecord::Type::Boolean.new.cast(value)
|
ActiveRecord::Type::Boolean.new.cast(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def v8
|
||||||
|
return @ctx if @ctx
|
||||||
|
|
||||||
|
@ctx = PrettyText.v8
|
||||||
|
PrettyText.ctx_load(@ctx, "#{Rails.root}/vendor/assets/javascripts/moment.js")
|
||||||
|
@ctx
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,10 +34,15 @@ class CustomWizard::Wizard
|
||||||
:needs_groups,
|
:needs_groups,
|
||||||
:steps,
|
:steps,
|
||||||
:step_ids,
|
:step_ids,
|
||||||
|
:field_ids,
|
||||||
:first_step,
|
:first_step,
|
||||||
:start,
|
:start,
|
||||||
:actions,
|
:actions,
|
||||||
:user
|
:action_ids,
|
||||||
|
:user,
|
||||||
|
:submissions
|
||||||
|
|
||||||
|
attr_reader :all_step_ids
|
||||||
|
|
||||||
def initialize(attrs = {}, user = nil)
|
def initialize(attrs = {}, user = nil)
|
||||||
@user = user
|
@user = user
|
||||||
|
@ -66,11 +71,22 @@ class CustomWizard::Wizard
|
||||||
|
|
||||||
@first_step = nil
|
@first_step = nil
|
||||||
@steps = []
|
@steps = []
|
||||||
|
|
||||||
if attrs['steps'].present?
|
if attrs['steps'].present?
|
||||||
@step_ids = attrs['steps'].map { |s| s['id'] }
|
@step_ids = @all_step_ids = attrs['steps'].map { |s| s['id'] }
|
||||||
|
|
||||||
|
@field_ids = []
|
||||||
|
attrs['steps'].each do |step|
|
||||||
|
if step['fields'].present?
|
||||||
|
step['fields'].each do |field|
|
||||||
|
@field_ids << field['id']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@actions = []
|
@actions = attrs['actions'] || []
|
||||||
|
@action_ids = @actions.map { |a| a['id'] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_bool(val)
|
def cast_bool(val)
|
||||||
|
@ -91,7 +107,19 @@ class CustomWizard::Wizard
|
||||||
step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
|
step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_step_order!
|
def update!
|
||||||
|
update_step_order
|
||||||
|
update_step_ids
|
||||||
|
update_field_ids
|
||||||
|
update_action_ids
|
||||||
|
|
||||||
|
@submissions = nil
|
||||||
|
@current_submission = nil
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_step_order
|
||||||
steps.sort_by!(&:index)
|
steps.sort_by!(&:index)
|
||||||
|
|
||||||
steps.each_with_index do |step, index|
|
steps.each_with_index do |step, index|
|
||||||
|
@ -110,7 +138,7 @@ class CustomWizard::Wizard
|
||||||
step.conditional_final_step = true
|
step.conditional_final_step = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if index === (step_ids.length - 1)
|
if index === (all_step_ids.length - 1)
|
||||||
step.last_step = true
|
step.last_step = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -125,7 +153,7 @@ class CustomWizard::Wizard
|
||||||
acting_user_id: user.id,
|
acting_user_id: user.id,
|
||||||
action: ::UserHistory.actions[:custom_wizard_step],
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
context: id,
|
context: id,
|
||||||
subject: step_ids
|
subject: all_step_ids
|
||||||
).order("created_at").last
|
).order("created_at").last
|
||||||
|
|
||||||
last_completed_step.subject
|
last_completed_step.subject
|
||||||
|
@ -229,30 +257,41 @@ class CustomWizard::Wizard
|
||||||
@groups ||= ::Site.new(Guardian.new(user)).groups
|
@groups ||= ::Site.new(Guardian.new(user)).groups
|
||||||
end
|
end
|
||||||
|
|
||||||
def submissions
|
def update_step_ids
|
||||||
return nil unless user.present?
|
@step_ids = steps.map(&:id)
|
||||||
@submissions ||= Array.wrap(PluginStore.get("#{id}_submissions", user.id))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_submission
|
def update_field_ids
|
||||||
if submissions.present? && submissions.last.present? && !submissions.last.key?("submitted_at")
|
@field_ids = steps.map { |step| step.fields.map { |field| field.id } }.flatten
|
||||||
submissions.last.with_indifferent_access
|
end
|
||||||
else
|
|
||||||
nil
|
def update_action_ids
|
||||||
|
@action_ids = []
|
||||||
|
|
||||||
|
@actions.each do |action|
|
||||||
|
if action['run_after'].blank? ||
|
||||||
|
action['run_after'] === 'wizard_completion' ||
|
||||||
|
step_ids.include?(action['run_after'])
|
||||||
|
|
||||||
|
@action_ids << action['id']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_submissions(submissions)
|
def submissions
|
||||||
PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
|
return nil unless user.present?
|
||||||
@submissions = nil
|
@submissions ||= CustomWizard::Submission.list(self, user_id: user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def save_submission(submission)
|
def current_submission
|
||||||
return nil unless save_submissions
|
@current_submission ||= begin
|
||||||
|
if submissions.present?
|
||||||
submissions.pop(1) if unfinished?
|
unsubmitted = submissions.select { |submission| !submission.submitted_at }
|
||||||
submissions.push(submission)
|
unsubmitted.present? ? unsubmitted.first : CustomWizard::Submission.new(self)
|
||||||
set_submissions(submissions)
|
else
|
||||||
|
CustomWizard::Submission.new(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def final_cleanup!
|
def final_cleanup!
|
||||||
|
@ -261,18 +300,12 @@ class CustomWizard::Wizard
|
||||||
user.save_custom_fields(true)
|
user.save_custom_fields(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if submission = current_submission
|
if current_submission.present?
|
||||||
submission['submitted_at'] = Time.now.iso8601
|
current_submission.submitted_at = Time.now.iso8601
|
||||||
save_submission(submission)
|
current_submission.save
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def self.submissions(wizard_id, user)
|
update!
|
||||||
new({ id: wizard_id }, user).submissions
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.set_submissions(wizard_id, user, submissions)
|
|
||||||
new({ id: wizard_id }, user).set_submissions(submissions)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create(wizard_id, user = nil)
|
def self.create(wizard_id, user = nil)
|
||||||
|
@ -327,11 +360,7 @@ class CustomWizard::Wizard
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_submission_redirect(user, wizard_id, url)
|
def self.set_user_redirect(wizard_id, user)
|
||||||
set_submissions(wizard_id, user, [{ redirect_to: url }])
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.set_wizard_redirect(wizard_id, user)
|
|
||||||
wizard = self.create(wizard_id, user)
|
wizard = self.create(wizard_id, user)
|
||||||
|
|
||||||
if wizard.permitted?
|
if wizard.permitted?
|
||||||
|
@ -341,4 +370,16 @@ class CustomWizard::Wizard
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.set_wizard_redirect(user, wizard_id, url)
|
||||||
|
wizard = self.create(wizard_id, user)
|
||||||
|
|
||||||
|
if wizard.permitted?
|
||||||
|
submission = wizard.current_submission
|
||||||
|
submission.redirect_to = url
|
||||||
|
submission.save
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
39
plugin.rb
39
plugin.rb
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
# name: discourse-custom-wizard
|
# name: discourse-custom-wizard
|
||||||
# about: Create custom wizards
|
# about: Create custom wizards
|
||||||
# version: 0.7.0
|
# version: 0.8.0
|
||||||
# authors: Angus McLeod
|
# authors: Angus McLeod
|
||||||
# url: https://github.com/paviliondev/discourse-custom-wizard
|
# url: https://github.com/paviliondev/discourse-custom-wizard
|
||||||
# contact emails: angus@thepavilion.io
|
# contact emails: angus@thepavilion.io
|
||||||
|
@ -42,6 +42,22 @@ if respond_to?(:register_svg_icon)
|
||||||
register_svg_icon "save"
|
register_svg_icon "save"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ::Sprockets::DirectiveProcessor
|
||||||
|
def process_require_tree_discourse_directive(path = ".")
|
||||||
|
raise CustomWizard::SprocketsEmptyPath, "path cannot be empty" if path == "."
|
||||||
|
|
||||||
|
discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
|
||||||
|
path = File.expand_path(path, discourse_asset_path)
|
||||||
|
stat = @environment.stat(path)
|
||||||
|
|
||||||
|
if stat && stat.directory?
|
||||||
|
require_paths(*@environment.stat_sorted_tree_with_dependencies(path))
|
||||||
|
else
|
||||||
|
raise CustomWizard::SprocketsFileNotFound, "#{path} not found in discourse core"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
%w[
|
%w[
|
||||||
../lib/custom_wizard/engine.rb
|
../lib/custom_wizard/engine.rb
|
||||||
|
@ -56,7 +72,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
|
||||||
|
@ -74,6 +89,7 @@ after_initialize do
|
||||||
../lib/custom_wizard/log.rb
|
../lib/custom_wizard/log.rb
|
||||||
../lib/custom_wizard/step_updater.rb
|
../lib/custom_wizard/step_updater.rb
|
||||||
../lib/custom_wizard/step.rb
|
../lib/custom_wizard/step.rb
|
||||||
|
../lib/custom_wizard/submission.rb
|
||||||
../lib/custom_wizard/template.rb
|
../lib/custom_wizard/template.rb
|
||||||
../lib/custom_wizard/wizard.rb
|
../lib/custom_wizard/wizard.rb
|
||||||
../lib/custom_wizard/api/api.rb
|
../lib/custom_wizard/api/api.rb
|
||||||
|
@ -81,6 +97,7 @@ after_initialize do
|
||||||
../lib/custom_wizard/api/endpoint.rb
|
../lib/custom_wizard/api/endpoint.rb
|
||||||
../lib/custom_wizard/api/log_entry.rb
|
../lib/custom_wizard/api/log_entry.rb
|
||||||
../lib/custom_wizard/liquid_extensions/first_non_empty.rb
|
../lib/custom_wizard/liquid_extensions/first_non_empty.rb
|
||||||
|
../lib/custom_wizard/exceptions/exceptions.rb
|
||||||
../serializers/custom_wizard/api/authorization_serializer.rb
|
../serializers/custom_wizard/api/authorization_serializer.rb
|
||||||
../serializers/custom_wizard/api/basic_endpoint_serializer.rb
|
../serializers/custom_wizard/api/basic_endpoint_serializer.rb
|
||||||
../serializers/custom_wizard/api/endpoint_serializer.rb
|
../serializers/custom_wizard/api/endpoint_serializer.rb
|
||||||
|
@ -93,12 +110,14 @@ after_initialize do
|
||||||
../serializers/custom_wizard/wizard_step_serializer.rb
|
../serializers/custom_wizard/wizard_step_serializer.rb
|
||||||
../serializers/custom_wizard/wizard_serializer.rb
|
../serializers/custom_wizard/wizard_serializer.rb
|
||||||
../serializers/custom_wizard/log_serializer.rb
|
../serializers/custom_wizard/log_serializer.rb
|
||||||
|
../serializers/custom_wizard/submission_serializer.rb
|
||||||
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
|
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
|
||||||
../extensions/extra_locales_controller.rb
|
../extensions/extra_locales_controller.rb
|
||||||
../extensions/invites_controller.rb
|
../extensions/invites_controller.rb
|
||||||
../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
|
||||||
|
@ -117,7 +136,7 @@ after_initialize do
|
||||||
|
|
||||||
if !wizard.completed?
|
if !wizard.completed?
|
||||||
custom_redirect = true
|
custom_redirect = true
|
||||||
CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
|
CustomWizard::Wizard.set_user_redirect(wizard.id, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -138,7 +157,7 @@ after_initialize do
|
||||||
|
|
||||||
on(:user_approved) do |user|
|
on(:user_approved) do |user|
|
||||||
if wizard = CustomWizard::Wizard.after_signup(user)
|
if wizard = CustomWizard::Wizard.after_signup(user)
|
||||||
CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
|
CustomWizard::Wizard.set_user_redirect(wizard.id, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -149,7 +168,7 @@ after_initialize do
|
||||||
|
|
||||||
if request.format === 'text/html' && !@excluded_routes.any? { |str| /#{str}/ =~ url } && wizard_id
|
if request.format === 'text/html' && !@excluded_routes.any? { |str| /#{str}/ =~ url } && wizard_id
|
||||||
if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
|
if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
|
||||||
CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
|
CustomWizard::Wizard.set_wizard_redirect(current_user, wizard_id, request.referer)
|
||||||
end
|
end
|
||||||
if CustomWizard::Template.exists?(wizard_id)
|
if CustomWizard::Template.exists?(wizard_id)
|
||||||
redirect_to "/w/#{wizard_id.dasherize}"
|
redirect_to "/w/#{wizard_id.dasherize}"
|
||||||
|
@ -191,18 +210,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|
|
||||||
|
|
16
serializers/custom_wizard/submission_serializer.rb
Normale Datei
16
serializers/custom_wizard/submission_serializer.rb
Normale Datei
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class CustomWizard::SubmissionSerializer < ApplicationSerializer
|
||||||
|
attributes :id,
|
||||||
|
:username,
|
||||||
|
:fields,
|
||||||
|
:submitted_at,
|
||||||
|
:route_to,
|
||||||
|
:redirect_on_complete,
|
||||||
|
:redirect_to
|
||||||
|
|
||||||
|
def username
|
||||||
|
object.user.present? ?
|
||||||
|
object.user.username :
|
||||||
|
I18n.t('admin.wizard.submission.no_user', user_id: object.user_id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -55,6 +55,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def placeholder
|
def placeholder
|
||||||
|
return object.placeholder if object.placeholder.present?
|
||||||
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,4 +101,8 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
|
||||||
def char_counter
|
def char_counter
|
||||||
object.char_counter
|
object.char_counter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preview_template
|
||||||
|
object.preview_template
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,11 +8,11 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
|
||||||
:completed,
|
:completed,
|
||||||
:required,
|
:required,
|
||||||
:permitted,
|
:permitted,
|
||||||
:uncategorized_category_id
|
:uncategorized_category_id,
|
||||||
|
:categories
|
||||||
|
|
||||||
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
|
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
|
||||||
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
|
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
|
||||||
has_many :categories, serializer: ::BasicCategorySerializer, embed: :objects
|
|
||||||
has_many :groups, serializer: ::BasicGroupSerializer, embed: :objects
|
has_many :groups, serializer: ::BasicGroupSerializer, embed: :objects
|
||||||
|
|
||||||
def completed
|
def completed
|
||||||
|
@ -56,4 +56,8 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
|
||||||
def include_uncategorized_category_id?
|
def include_uncategorized_category_id?
|
||||||
object.needs_categories
|
object.needs_categories
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def categories
|
||||||
|
object.categories.map { |c| c.to_h }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
||||||
|
@ -146,7 +182,7 @@ describe CustomWizard::Action do
|
||||||
updater = wizard.create_updater(wizard.steps[1].id, {})
|
updater = wizard.create_updater(wizard.steps[1].id, {})
|
||||||
updater.update
|
updater.update
|
||||||
|
|
||||||
category = Category.find_by(id: wizard.current_submission['action_8'])
|
category = Category.find_by(id: wizard.current_submission.fields['action_8'])
|
||||||
|
|
||||||
expect(updater.result[:redirect_on_next]).to eq(
|
expect(updater.result[:redirect_on_next]).to eq(
|
||||||
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1"
|
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1"
|
||||||
|
@ -158,11 +194,10 @@ describe CustomWizard::Action do
|
||||||
open_composer['post_template'] = "Body & more body & more body".dup
|
open_composer['post_template'] = "Body & more body & more body".dup
|
||||||
|
|
||||||
wizard = CustomWizard::Wizard.new(@template, user)
|
wizard = CustomWizard::Wizard.new(@template, user)
|
||||||
|
|
||||||
action = CustomWizard::Action.new(
|
action = CustomWizard::Action.new(
|
||||||
wizard: wizard,
|
wizard: wizard,
|
||||||
action: open_composer,
|
action: open_composer,
|
||||||
data: {}
|
submission: wizard.current_submission
|
||||||
)
|
)
|
||||||
action.perform
|
action.perform
|
||||||
|
|
||||||
|
@ -179,20 +214,20 @@ describe CustomWizard::Action do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||||
expect(Category.where(id: wizard.current_submission['action_8']).exists?).to eq(true)
|
expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a group' do
|
it 'creates a group' do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||||
expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
|
expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds a user to a group' do
|
it 'adds a user to a group' do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
step_id = wizard.steps[0].id
|
step_id = wizard.steps[0].id
|
||||||
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
|
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
|
||||||
group = Group.find_by(name: wizard.current_submission['action_9'])
|
group = Group.find_by(name: wizard.current_submission.fields['action_9'])
|
||||||
expect(group.users.first.username).to eq('angus')
|
expect(group.users.first.username).to eq('angus')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -201,7 +236,7 @@ describe CustomWizard::Action do
|
||||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||||
wizard.create_updater(wizard.steps[1].id, {}).update
|
wizard.create_updater(wizard.steps[1].id, {}).update
|
||||||
expect(CategoryUser.where(
|
expect(CategoryUser.where(
|
||||||
category_id: wizard.current_submission['action_8'],
|
category_id: wizard.current_submission.fields['action_8'],
|
||||||
user_id: user.id
|
user_id: user.id
|
||||||
).first.notification_level).to eq(2)
|
).first.notification_level).to eq(2)
|
||||||
expect(CategoryUser.where(
|
expect(CategoryUser.where(
|
||||||
|
|
|
@ -189,7 +189,10 @@ describe CustomWizard::Builder do
|
||||||
context "user has partially completed" do
|
context "user has partially completed" do
|
||||||
before do
|
before do
|
||||||
wizard = CustomWizard::Wizard.new(@template, user)
|
wizard = CustomWizard::Wizard.new(@template, user)
|
||||||
wizard.set_submissions(step_1_field_1: 'I am a user submission')
|
data = {
|
||||||
|
step_1_field_1: 'I am a user submission'
|
||||||
|
}
|
||||||
|
CustomWizard::Submission.new(wizard, data).save
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns saved submissions' do
|
it 'returns saved submissions' do
|
||||||
|
@ -253,9 +256,9 @@ describe CustomWizard::Builder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is permitted if required data is present' do
|
it 'is permitted if required data is present' do
|
||||||
CustomWizard::Wizard.set_submissions('super_mega_fun_wizard', user,
|
wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user)
|
||||||
required_data: "required_value"
|
CustomWizard::Submission.new(wizard, step_1_field_1: "required").save
|
||||||
)
|
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Builder.new(@template[:id], user).build
|
CustomWizard::Builder.new(@template[:id], user).build
|
||||||
.steps.first
|
.steps.first
|
||||||
|
@ -274,7 +277,7 @@ describe CustomWizard::Builder do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build({},
|
wizard = CustomWizard::Builder.new(@template[:id], user).build({},
|
||||||
param: 'param_value'
|
param: 'param_value'
|
||||||
)
|
)
|
||||||
expect(wizard.current_submission['saved_param']).to eq('param_value')
|
expect(wizard.current_submission.fields['saved_param']).to eq('param_value')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -336,31 +339,27 @@ describe CustomWizard::Builder do
|
||||||
|
|
||||||
context 'on update' do
|
context 'on update' do
|
||||||
def perform_update(step_id, submission)
|
def perform_update(step_id, submission)
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
updater = @wizard.create_updater(step_id, submission)
|
||||||
updater = wizard.create_updater(step_id, submission)
|
|
||||||
updater.update
|
updater.update
|
||||||
updater
|
updater
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'saves submissions' do
|
it 'saves submissions' do
|
||||||
|
@wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
perform_update('step_1', step_1_field_1: 'Text input')
|
perform_update('step_1', step_1_field_1: 'Text input')
|
||||||
expect(
|
expect(@wizard.current_submission.fields['step_1_field_1']).to eq('Text input')
|
||||||
CustomWizard::Wizard.submissions(@template[:id], user)
|
|
||||||
.first['step_1_field_1']
|
|
||||||
).to eq('Text input')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'save submissions disabled' do
|
context 'save submissions disabled' do
|
||||||
before do
|
before do
|
||||||
@template[:save_submissions] = false
|
@template[:save_submissions] = false
|
||||||
CustomWizard::Template.save(@template.as_json)
|
CustomWizard::Template.save(@template.as_json)
|
||||||
|
@wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not save submissions" do
|
it "does not save submissions" do
|
||||||
perform_update('step_1', step_1_field_1: 'Text input')
|
perform_update('step_1', step_1_field_1: 'Text input')
|
||||||
expect(
|
expect(@wizard.current_submission.present?).to eq(false)
|
||||||
CustomWizard::Wizard.submissions(@template[:id], user).first
|
|
||||||
).to eq(nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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,28 +229,40 @@ describe CustomWizard::Mapper do
|
||||||
).perform).to eq("value 2")
|
).perform).to eq("value 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "interpolates user fields" do
|
context "interpolates" do
|
||||||
expect(CustomWizard::Mapper.new(
|
it "user fields" do
|
||||||
inputs: inputs['interpolate_user_field'],
|
expect(CustomWizard::Mapper.new(
|
||||||
data: data,
|
inputs: inputs['interpolate_user_field'],
|
||||||
user: user1
|
data: data,
|
||||||
).perform).to eq("Name: Angus")
|
user: user1
|
||||||
end
|
).perform).to eq("Name: Angus")
|
||||||
|
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
|
||||||
expect(CustomWizard::Mapper.new(
|
user1.user_option.update_columns(email_level: UserOption.email_level_types[:never])
|
||||||
inputs: inputs['interpolate_timestamp'],
|
|
||||||
data: data,
|
expect(CustomWizard::Mapper.new(
|
||||||
user: user1
|
inputs: inputs['interpolate_user_option'],
|
||||||
).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
|
data: data,
|
||||||
|
user: user1
|
||||||
|
).perform).to eq("Email Level: #{UserOption.email_level_types[:never]}")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "date" do
|
||||||
|
expect(CustomWizard::Mapper.new(
|
||||||
|
inputs: inputs['interpolate_timestamp'],
|
||||||
|
data: data,
|
||||||
|
user: user1
|
||||||
|
).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
|
||||||
|
|
43
spec/components/custom_wizard/submission_spec.rb
Normale Datei
43
spec/components/custom_wizard/submission_spec.rb
Normale Datei
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require_relative '../../plugin_helper'
|
||||||
|
|
||||||
|
describe CustomWizard::Submission do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
|
let(:template_json) {
|
||||||
|
JSON.parse(File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||||
|
).read)
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
CustomWizard::Template.save(template_json, skip_jobs: true)
|
||||||
|
|
||||||
|
template_json_2 = template_json.dup
|
||||||
|
template_json_2["id"] = "super_mega_fun_wizard_2"
|
||||||
|
CustomWizard::Template.save(template_json_2, skip_jobs: true)
|
||||||
|
|
||||||
|
@wizard = CustomWizard::Wizard.create(template_json["id"], user)
|
||||||
|
@wizard2 = CustomWizard::Wizard.create(template_json["id"], user2)
|
||||||
|
@wizard3 = CustomWizard::Wizard.create(template_json_2["id"], user)
|
||||||
|
|
||||||
|
described_class.new(@wizard, step_1_field_1: "I am a user submission").save
|
||||||
|
described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save
|
||||||
|
described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save
|
||||||
|
end
|
||||||
|
|
||||||
|
it "saves a user's submission" do
|
||||||
|
expect(
|
||||||
|
described_class.get(@wizard, user.id).fields["step_1_field_1"]
|
||||||
|
).to eq("I am a user submission")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "list submissions by wizard" do
|
||||||
|
expect(described_class.list(@wizard).size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "list submissions by wizard and user" do
|
||||||
|
expect(described_class.list(@wizard, user_id: user.id).size).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
|
@ -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')
|
||||||
|
|
|
@ -132,4 +132,44 @@ describe CustomWizard::UpdateValidator do
|
||||||
updater.errors.messages[:step_2_field_6].first
|
updater.errors.messages[:step_2_field_6].first
|
||||||
).to eq(nil)
|
).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'validates date fields' do
|
||||||
|
@template[:steps][1][:fields][0][:format] = "DD-MM-YYYY"
|
||||||
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
|
updater = perform_validation('step_2', step_2_field_1: '13-11-2021')
|
||||||
|
expect(
|
||||||
|
updater.errors.messages[:step_2_field_1].first
|
||||||
|
).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesn\'t validate date field if the format is not respected' do
|
||||||
|
@template[:steps][1][:fields][0][:format] = "MM-DD-YYYY"
|
||||||
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
|
updater = perform_validation('step_2', step_2_field_1: '13-11-2021')
|
||||||
|
expect(
|
||||||
|
updater.errors.messages[:step_2_field_1].first
|
||||||
|
).to eq(I18n.t('wizard.field.invalid_date'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'validates date time fields' do
|
||||||
|
@template[:steps][1][:fields][2][:format] = "DD-MM-YYYY HH:mm:ss"
|
||||||
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
|
updater = perform_validation('step_2', step_2_field_3: '13-11-2021 09:15:00')
|
||||||
|
expect(
|
||||||
|
updater.errors.messages[:step_2_field_3].first
|
||||||
|
).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesn\'t validate date time field if the format is not respected' do
|
||||||
|
@template[:steps][1][:fields][2][:format] = "MM-DD-YYYY HH:mm:ss"
|
||||||
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
|
updater = perform_validation('step_2', step_2_field_3: '13-11-2021 09:15')
|
||||||
|
expect(
|
||||||
|
updater.errors.messages[:step_2_field_3].first
|
||||||
|
).to eq(I18n.t('wizard.field.invalid_date'))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe CustomWizard::Wizard do
|
||||||
template_json['steps'].each do |step_template|
|
template_json['steps'].each do |step_template|
|
||||||
@wizard.append_step(step_template['id'])
|
@wizard.append_step(step_template['id'])
|
||||||
end
|
end
|
||||||
@wizard.update_step_order!
|
@wizard.update!
|
||||||
end
|
end
|
||||||
|
|
||||||
def progress_step(step_id, acting_user: user, wizard: @wizard)
|
def progress_step(step_id, acting_user: user, wizard: @wizard)
|
||||||
|
@ -44,7 +44,7 @@ describe CustomWizard::Wizard do
|
||||||
context: wizard.id,
|
context: wizard.id,
|
||||||
subject: step_id
|
subject: step_id
|
||||||
)
|
)
|
||||||
@wizard.update_step_order!
|
@wizard.update!
|
||||||
end
|
end
|
||||||
|
|
||||||
it "appends steps" do
|
it "appends steps" do
|
||||||
|
@ -72,7 +72,7 @@ describe CustomWizard::Wizard do
|
||||||
expect(@wizard.steps.first.index).to eq(2)
|
expect(@wizard.steps.first.index).to eq(2)
|
||||||
expect(@wizard.steps.last.index).to eq(0)
|
expect(@wizard.steps.last.index).to eq(0)
|
||||||
|
|
||||||
@wizard.update_step_order!
|
@wizard.update!
|
||||||
|
|
||||||
expect(@wizard.steps.first.id).to eq("step_3")
|
expect(@wizard.steps.first.id).to eq("step_3")
|
||||||
expect(@wizard.steps.last.id).to eq("step_1")
|
expect(@wizard.steps.last.id).to eq("step_1")
|
||||||
|
@ -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)
|
||||||
|
@ -197,19 +199,13 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "lists the site categories" do
|
it "lists the site categories" do
|
||||||
|
Site.clear_cache
|
||||||
expect(@wizard.categories.length).to eq(1)
|
expect(@wizard.categories.length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "submissions" do
|
context "submissions" do
|
||||||
before do
|
before do
|
||||||
@wizard.set_submissions(step_1_field_1: 'I am a user submission')
|
CustomWizard::Submission.new(@wizard, step_1_field_1: "I am a user submission").save
|
||||||
end
|
|
||||||
|
|
||||||
it "sets the user's submission" do
|
|
||||||
expect(
|
|
||||||
PluginStore.get("#{template_json['id']}_submissions", user.id)
|
|
||||||
.first['step_1_field_1']
|
|
||||||
).to eq('I am a user submission')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "lists the user's submissions" do
|
it "lists the user's submissions" do
|
||||||
|
@ -217,20 +213,10 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the user's current submission" do
|
it "returns the user's current submission" do
|
||||||
expect(@wizard.current_submission['step_1_field_1']).to eq('I am a user submission')
|
expect(@wizard.current_submission.fields["step_1_field_1"]).to eq("I am a user submission")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "provides class methods to set and list submissions" do
|
|
||||||
CustomWizard::Wizard.set_submissions(template_json['id'], user,
|
|
||||||
step_1_field_1: 'I am a user submission'
|
|
||||||
)
|
|
||||||
expect(
|
|
||||||
CustomWizard::Wizard.submissions(template_json['id'], user)
|
|
||||||
.first['step_1_field_1']
|
|
||||||
).to eq('I am a user submission')
|
|
||||||
end
|
|
||||||
|
|
||||||
context "class methods" do
|
context "class methods" do
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
||||||
|
@ -271,7 +257,7 @@ describe CustomWizard::Wizard do
|
||||||
|
|
||||||
it "sets wizard redirects if user is permitted" do
|
it "sets wizard redirects if user is permitted" do
|
||||||
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
||||||
CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', trusted_user)
|
CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user)
|
||||||
expect(
|
expect(
|
||||||
trusted_user.custom_fields['redirect_to_wizard']
|
trusted_user.custom_fields['redirect_to_wizard']
|
||||||
).to eq("super_mega_fun_wizard")
|
).to eq("super_mega_fun_wizard")
|
||||||
|
@ -279,7 +265,7 @@ describe CustomWizard::Wizard do
|
||||||
|
|
||||||
it "does not set a wizard redirect if user is not permitted" do
|
it "does not set a wizard redirect if user is not permitted" do
|
||||||
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
||||||
CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', user)
|
CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user)
|
||||||
expect(
|
expect(
|
||||||
trusted_user.custom_fields['redirect_to_wizard']
|
trusted_user.custom_fields['redirect_to_wizard']
|
||||||
).to eq(nil)
|
).to eq(nil)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
55
spec/extensions/sprockets_directive_spec.rb
Normale Datei
55
spec/extensions/sprockets_directive_spec.rb
Normale Datei
|
@ -0,0 +1,55 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative '../plugin_helper'
|
||||||
|
|
||||||
|
describe "Sprockets: require_tree_discourse directive" do
|
||||||
|
let(:discourse_asset_path) {
|
||||||
|
"#{Rails.root}/app/assets/javascripts/"
|
||||||
|
}
|
||||||
|
let(:fixture_asset_path) {
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
|
||||||
|
}
|
||||||
|
let(:test_file_contents) {
|
||||||
|
"console.log('hello')"
|
||||||
|
}
|
||||||
|
let(:resolved_file_contents) {
|
||||||
|
File.read(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/resolved_js_file_contents.txt"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
@env ||= Sprockets::Environment.new
|
||||||
|
discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
|
||||||
|
fixture_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
|
||||||
|
@env.append_path(discourse_asset_path)
|
||||||
|
@env.append_path(fixture_asset_path)
|
||||||
|
@env.cache = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_tmp_folder_and_run(path, file_contents, &block)
|
||||||
|
dir = File.dirname(path)
|
||||||
|
unless File.directory?(dir)
|
||||||
|
FileUtils.mkdir_p(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
File.new(path, 'w')
|
||||||
|
File.write(path, file_contents)
|
||||||
|
yield block if block_given?
|
||||||
|
FileUtils.rm_r(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "includes assets from the discourse core" do
|
||||||
|
create_tmp_folder_and_run("#{discourse_asset_path}/sptest/test.js", test_file_contents) do
|
||||||
|
expect(@env.find_asset("require_tree_discourse_test.js").to_s).to eq(resolved_file_contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "throws ArgumentError if path is empty" do
|
||||||
|
expect { @env.find_asset("require_tree_discourse_empty.js") }.to raise_error(CustomWizard::SprocketsEmptyPath).with_message("path cannot be empty")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "throws ArgumentError if path is non non-existent" do
|
||||||
|
expect { @env.find_asset("require_tree_discourse_non_existant.js") }.to raise_error(CustomWizard::SprocketsFileNotFound)
|
||||||
|
end
|
||||||
|
end
|
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",
|
||||||
|
|
1
spec/fixtures/sprockets/require_tree_discourse_empty.js
gevendort
Normale Datei
1
spec/fixtures/sprockets/require_tree_discourse_empty.js
gevendort
Normale Datei
|
@ -0,0 +1 @@
|
||||||
|
//= require_tree_discourse
|
1
spec/fixtures/sprockets/require_tree_discourse_non_existant.js
gevendort
Normale Datei
1
spec/fixtures/sprockets/require_tree_discourse_non_existant.js
gevendort
Normale Datei
|
@ -0,0 +1 @@
|
||||||
|
//= require_tree_discourse dummy_path
|
1
spec/fixtures/sprockets/require_tree_discourse_test.js
gevendort
Normale Datei
1
spec/fixtures/sprockets/require_tree_discourse_test.js
gevendort
Normale Datei
|
@ -0,0 +1 @@
|
||||||
|
//= require_tree_discourse sptest
|
3
spec/fixtures/sprockets/resolved_js_file_contents.txt
gevendort
Normale Datei
3
spec/fixtures/sprockets/resolved_js_file_contents.txt
gevendort
Normale Datei
|
@ -0,0 +1,3 @@
|
||||||
|
eval("define(\"sptest/test\", [], function () {\n \"use strict\";\n\n console.log('hello');\n});" + "\n//# sourceURL=sptest/test");
|
||||||
|
;
|
||||||
|
eval("" + "\n//# sourceURL=require_tree_discourse_test");
|
4
spec/fixtures/step/required_data.json
gevendort
4
spec/fixtures/step/required_data.json
gevendort
|
@ -6,9 +6,9 @@
|
||||||
"pairs": [
|
"pairs": [
|
||||||
{
|
{
|
||||||
"index": 0,
|
"index": 0,
|
||||||
"key": "required_data",
|
"key": "step_1_field_1",
|
||||||
"key_type": "text",
|
"key_type": "text",
|
||||||
"value": "required_value",
|
"value": "required",
|
||||||
"value_type": "text",
|
"value_type": "text",
|
||||||
"connector": "equal"
|
"connector": "equal"
|
||||||
}
|
}
|
||||||
|
|
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
|
||||||
|
|
|
@ -5,6 +5,7 @@ describe CustomWizard::AdminSubmissionsController do
|
||||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||||
fab!(:user1) { Fabricate(:user) }
|
fab!(:user1) { Fabricate(:user) }
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
fab!(:user3) { Fabricate(:user) }
|
||||||
|
|
||||||
let(:template) {
|
let(:template) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(File.open(
|
||||||
|
@ -12,35 +13,40 @@ describe CustomWizard::AdminSubmissionsController do
|
||||||
).read)
|
).read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let(:template_2) {
|
||||||
|
temp = template.dup
|
||||||
|
temp["id"] = "super_mega_fun_wizard_2"
|
||||||
|
temp
|
||||||
|
}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(template, skip_jobs: true)
|
CustomWizard::Template.save(template, skip_jobs: true)
|
||||||
CustomWizard::Wizard.set_submissions(template['id'], user1,
|
CustomWizard::Template.save(template_2, skip_jobs: true)
|
||||||
step_1_field_1: "I am a user1's submission"
|
|
||||||
)
|
wizard1 = CustomWizard::Wizard.create(template["id"], user1)
|
||||||
CustomWizard::Wizard.set_submissions(template['id'], user2,
|
wizard2 = CustomWizard::Wizard.create(template["id"], user2)
|
||||||
step_1_field_1: "I am a user2's submission"
|
wizard3 = CustomWizard::Wizard.create(template_2["id"], user3)
|
||||||
)
|
|
||||||
|
CustomWizard::Submission.new(wizard1, step_1_field_1: "I am a user1's submission").save
|
||||||
|
CustomWizard::Submission.new(wizard2, step_1_field_1: "I am a user2's submission").save
|
||||||
|
CustomWizard::Submission.new(wizard3, step_1_field_1: "I am a user3's submission").save
|
||||||
|
|
||||||
sign_in(admin_user)
|
sign_in(admin_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a basic list of wizards" do
|
it "returns a list of wizards" do
|
||||||
get "/admin/wizards/submissions.json"
|
get "/admin/wizards/submissions.json"
|
||||||
expect(response.parsed_body.length).to eq(1)
|
expect(response.parsed_body.length).to eq(2)
|
||||||
expect(response.parsed_body.first['id']).to eq(template['id'])
|
expect(response.parsed_body.first['id']).to eq(template['id'])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the all user's submissions for a wizard" do
|
it "returns users' submissions for a wizard" do
|
||||||
get "/admin/wizards/submissions/#{template['id']}.json"
|
get "/admin/wizards/submissions/#{template['id']}.json"
|
||||||
expect(response.parsed_body['submissions'].length).to eq(2)
|
expect(response.parsed_body['submissions'].length).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the all user's submissions for a wizard" do
|
it "downloads submissions" do
|
||||||
get "/admin/wizards/submissions/#{template['id']}.json"
|
get "/admin/wizards/submissions/#{template_2['id']}/download"
|
||||||
expect(response.parsed_body['submissions'].length).to eq(2)
|
expect(response.parsed_body.length).to eq(1)
|
||||||
end
|
|
||||||
|
|
||||||
it "downloads all user submissions" do
|
|
||||||
get "/admin/wizards/submissions/#{template['id']}/download"
|
|
||||||
expect(response.parsed_body.length).to eq(2)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,10 +39,16 @@ describe ApplicationController do
|
||||||
it "saves original destination of user" do
|
it "saves original destination of user" do
|
||||||
get '/', headers: { 'REFERER' => "/t/2" }
|
get '/', headers: { 'REFERER' => "/t/2" }
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Wizard.submissions(@template['id'], user)
|
CustomWizard::Wizard.create(@template['id'], user).submissions
|
||||||
.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
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe CustomWizard::StepsController do
|
||||||
|
|
||||||
wizard_id = response.parsed_body['wizard']['id']
|
wizard_id = response.parsed_body['wizard']['id']
|
||||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||||
expect(wizard.submissions.last['step_1_field_1']).to eq("Text input")
|
expect(wizard.current_submission.fields['step_1_field_1']).to eq("Text input")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "raises an error" do
|
context "raises an error" do
|
||||||
|
@ -175,8 +175,11 @@ describe CustomWizard::StepsController do
|
||||||
|
|
||||||
wizard_id = response.parsed_body['wizard']['id']
|
wizard_id = response.parsed_body['wizard']['id']
|
||||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||||
group_name = wizard.submissions.last['action_9']
|
|
||||||
|
group_name = wizard.submissions.first.fields['action_9']
|
||||||
group = Group.find_by(name: group_name)
|
group = Group.find_by(name: group_name)
|
||||||
|
|
||||||
|
expect(group.present?).to eq(true)
|
||||||
expect(group.full_name).to eq("My cool group")
|
expect(group.full_name).to eq("My cool group")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -249,4 +252,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.current_submission
|
||||||
|
expect(submission.fields.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)
|
||||||
|
@ -55,9 +71,8 @@ describe CustomWizard::WizardController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'skip response contains a redirect_to if in users submissions' do
|
it 'skip response contains a redirect_to if in users submissions' do
|
||||||
CustomWizard::Wizard.set_submissions(@template['id'], user,
|
@wizard = CustomWizard::Wizard.create(@template["id"], user)
|
||||||
redirect_to: '/t/2'
|
CustomWizard::Submission.new(@wizard, redirect_to: "/t/2").save
|
||||||
)
|
|
||||||
put '/w/super-mega-fun-wizard/skip.json'
|
put '/w/super-mega-fun-wizard/skip.json'
|
||||||
expect(response.parsed_body['redirect_to']).to eq('/t/2')
|
expect(response.parsed_body['redirect_to']).to eq('/t/2')
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
<%= discourse_stylesheet_link_tag :wizard, theme_id: nil %>
|
<%= discourse_stylesheet_link_tag :wizard, theme_id: nil %>
|
||||||
<%= discourse_stylesheet_link_tag :wizard_custom %>
|
<%= discourse_stylesheet_link_tag :wizard_custom %>
|
||||||
<%- if wizard_theme_ids.present? %>
|
<%- if wizard_theme_id.present? %>
|
||||||
<%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme), theme_ids: wizard_theme_ids %>
|
<%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme), theme_id: wizard_theme_id %>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
|
||||||
<%= preload_script "locales/#{I18n.locale}" %>
|
<%= preload_script "locales/#{I18n.locale}" %>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
<%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
|
<%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
|
||||||
|
|
||||||
<meta name="discourse_theme_ids" content="<%= wizard_theme_ids&.join(",") %>">
|
<meta name="discourse_theme_id" content="<%= wizard_theme_id %>">
|
||||||
<meta name="discourse-base-uri" content="<%= Discourse.base_path %>">
|
<meta name="discourse-base-uri" content="<%= Discourse.base_path %>">
|
||||||
|
|
||||||
<%= render partial: "layouts/head" %>
|
<%= render partial: "layouts/head" %>
|
||||||
|
|
Laden …
In neuem Issue referenzieren