Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 01:10:28 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
92e61f3f51
Commit
d128565979
21 geänderte Dateien mit 161 neuen und 113 gelöschten Zeilen
|
@ -1,8 +1,15 @@
|
|||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { wizardFieldList } from '../lib/wizard';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: 'wizard-custom-step',
|
||||
|
||||
@discourseComputed('wizard.steps', 'step.id')
|
||||
descriptionWizardFields(steps, stepId) {
|
||||
return wizardFieldList(steps, { upTo: stepId });
|
||||
},
|
||||
|
||||
actions: {
|
||||
bannerUploadDone(upload) {
|
||||
this.set("step.banner", upload.url);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { default as discourseComputed, on } from 'discourse-common/utils/decorators';
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import { userProperties } from '../lib/wizard';
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
|
@ -8,9 +9,11 @@ export default Component.extend({
|
|||
barEnabled: true,
|
||||
previewEnabled: true,
|
||||
fieldsEnabled: true,
|
||||
hasWizardFields: notEmpty('wizardFieldList'),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (!this.barEnabled) {
|
||||
scheduleOnce('afterRender', () => {
|
||||
$(this.element).find('.d-editor-button-bar').addClass('hidden');
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{{#each site.complete_custom_wizard as |wizard|}}
|
||||
<div class='row'>
|
||||
<div class='alert alert-info alert-wizard'>
|
||||
<a href="{{wizard.url}}">{{i18n 'wizard.complete_custom' name=wizard.name}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
shouldRender(_, ctx) {
|
||||
return ctx.siteSettings.custom_wizard_enabled &&
|
||||
ctx.site.complete_custom_wizard;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
|
||||
import { notEmpty, alias } from "@ember/object/computed";
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { generateId } from '../lib/wizard';
|
||||
import { generateId, wizardFieldList } from '../lib/wizard';
|
||||
import { buildProperties } from '../lib/wizard-json';
|
||||
import { dasherize } from "@ember/string";
|
||||
import EmberObject from "@ember/object";
|
||||
|
@ -47,29 +47,11 @@ export default Controller.extend({
|
|||
|
||||
@discourseComputed('currentStep.id', 'wizard.save_submissions', 'wizard.steps.@each.fields[]')
|
||||
wizardFields(currentStepId, saveSubmissions) {
|
||||
const allSteps = this.get('wizard.steps');
|
||||
let steps = allSteps;
|
||||
let fields = [];
|
||||
|
||||
let steps = this.wizard.steps;
|
||||
if (!saveSubmissions) {
|
||||
steps = [allSteps.findBy('id', currentStepId)];
|
||||
steps = [steps.findBy('id', currentStepId)];
|
||||
}
|
||||
|
||||
steps.forEach((s) => {
|
||||
if (s.fields && s.fields.length > 0) {
|
||||
let stepFields = s.fields.map((f) => {
|
||||
return EmberObject.create({
|
||||
id: f.id,
|
||||
label: `${f.label} (${s.id}, ${f.id})`,
|
||||
type: f.type
|
||||
});
|
||||
});
|
||||
|
||||
fields.push(...stepFields);
|
||||
}
|
||||
});
|
||||
|
||||
return fields;
|
||||
return wizardFieldList(steps);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -7,25 +7,6 @@ export default {
|
|||
const siteSettings = container.lookup('site-settings:main');
|
||||
|
||||
if (!siteSettings.custom_wizard_enabled) return;
|
||||
|
||||
withPluginApi('0.8.12', api => {
|
||||
api.modifyClass('component:global-notice', {
|
||||
buildBuffer(buffer) {
|
||||
this._super(...arguments);
|
||||
const wizards = this.site.get('complete_custom_wizard');
|
||||
if (wizards) {
|
||||
wizards.forEach((w) => {
|
||||
const text = I18n.t('wizard.complete_custom', {
|
||||
wizard_url: w.url,
|
||||
wizard_name: w.name,
|
||||
site_name: this.siteSettings.title
|
||||
});
|
||||
buffer.push(`<div class='row'><div class='alert alert-info alert-wizard'>${text}</div></div>`);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const existing = DiscourseURL.routeTo;
|
||||
DiscourseURL.routeTo = function(path, opts) {
|
||||
|
|
|
@ -137,7 +137,7 @@ function buildProperties(json) {
|
|||
steps: A(),
|
||||
actions: A()
|
||||
};
|
||||
|
||||
|
||||
if (present(json)) {
|
||||
props.existingId = true;
|
||||
props = buildBasicProperties(json, 'wizard', props);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import EmberObject from "@ember/object";
|
||||
|
||||
function selectKitContent(content) {
|
||||
return content.map(i => ({id: i, name: i}));
|
||||
}
|
||||
|
@ -177,9 +179,8 @@ const fieldProperties = {
|
|||
'content'
|
||||
],
|
||||
advanced: [
|
||||
'prefill',
|
||||
'content',
|
||||
'property'
|
||||
'property',
|
||||
'key'
|
||||
],
|
||||
required: [
|
||||
'id',
|
||||
|
@ -290,6 +291,33 @@ function listProperties(type, objectType = null) {
|
|||
return properties;
|
||||
}
|
||||
|
||||
function wizardFieldList(steps = [], opts = {}) {
|
||||
let upToIndex = null;
|
||||
|
||||
if (opts.upTo) {
|
||||
upToIndex = steps.map((s) => (s.id)).indexOf(opts.upTo);
|
||||
}
|
||||
|
||||
return steps.reduce((result, step, index) => {
|
||||
let fields = step.fields;
|
||||
|
||||
if (fields && fields.length > 0) {
|
||||
|
||||
if (upToIndex === null || index < upToIndex) {
|
||||
result.push(...fields.map((field) => {
|
||||
return EmberObject.create({
|
||||
id: field.id,
|
||||
label: `${field.label} (${step.id}, ${field.id})`,
|
||||
type: field.type
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export {
|
||||
selectKitContent,
|
||||
generateName,
|
||||
|
@ -298,5 +326,6 @@ export {
|
|||
snakeCase,
|
||||
schema,
|
||||
userProperties,
|
||||
listProperties
|
||||
listProperties,
|
||||
wizardFieldList
|
||||
};
|
|
@ -21,7 +21,7 @@ export default DiscourseRoute.extend({
|
|||
setupController(controller, model) {
|
||||
const parentModel = this.modelFor('adminWizardsWizard');
|
||||
const wizard = CustomWizard.create((!model || model.create) ? {} : model);
|
||||
|
||||
|
||||
controller.setProperties({
|
||||
wizardList: parentModel.wizard_list,
|
||||
fieldTypes: selectKitContent(parentModel.field_types),
|
||||
|
|
|
@ -97,6 +97,34 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showPrefill}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n 'admin.wizard.field.prefill'}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.prefill
|
||||
options=prefillOptions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showContent}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n 'admin.wizard.field.content'}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.content
|
||||
options=contentOptions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
|
||||
|
||||
{{#if field.showAdvanced}}
|
||||
|
@ -120,34 +148,6 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showPrefill}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n 'admin.wizard.field.prefill'}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.prefill
|
||||
options=prefillOptions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showContent}}
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n 'admin.wizard.field.content'}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.content
|
||||
options=contentOptions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n 'admin.wizard.translation'}}</label>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<div class="setting-value">
|
||||
{{wizard-text-editor
|
||||
value=step.raw_description
|
||||
fieldsEnabled=false}}
|
||||
wizardFields=descriptionWizardFields}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -21,10 +21,13 @@
|
|||
{{i18n 'admin.wizard.action.post_builder.user_fields'}}
|
||||
{{userFieldList}}
|
||||
</label>
|
||||
<label>
|
||||
{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}
|
||||
{{wizardFieldList}}
|
||||
</label>
|
||||
|
||||
{{#if hasWizardFields}}
|
||||
<label>
|
||||
{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}
|
||||
{{wizardFieldList}}
|
||||
</label>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -89,7 +89,13 @@ export default {
|
|||
|
||||
animateInvalidFields() {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
$('.invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit').wiggle(2, 100);
|
||||
let query = '.invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit';
|
||||
|
||||
$([document.documentElement, document.body]).animate({
|
||||
scrollTop: $(query).offset().top - 200
|
||||
}, 400, function() {
|
||||
$(query).wiggle(2, 100);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -185,6 +191,7 @@ export default {
|
|||
if (wizardErrors.length) {
|
||||
this.handleWizardError(wizardErrors.join('\n'));
|
||||
}
|
||||
this.animateInvalidFields();
|
||||
throw response;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
{{wizard-no-access text=(i18n 'wizard.none') wizardId=wizardId}}
|
||||
{{else}}
|
||||
{{#if requiresLogin}}
|
||||
{{wizard-no-access text=(i18n 'wizard.requires_login' name=name) wizardId=wizardId}}
|
||||
{{wizard-no-access text=(i18n 'wizard.requires_login') wizardId=wizardId}}
|
||||
{{else}}
|
||||
{{#if notPermitted}}
|
||||
{{wizard-no-access text=(i18n 'wizard.not_permitted' name=name) wizardId=wizardId}}
|
||||
{{wizard-no-access text=(i18n 'wizard.not_permitted') wizardId=wizardId}}
|
||||
{{else}}
|
||||
{{#if completed}}
|
||||
{{wizard-no-access text=(i18n 'wizard.completed' name=name) wizardId=wizardId}}
|
||||
{{wizard-no-access text=(i18n 'wizard.completed') wizardId=wizardId}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
en:
|
||||
js:
|
||||
wizard:
|
||||
complete_custom: "Welcome to %{site_name}! Get started with <a href='%{wizard_url}' data-auto-route='true'>the %{wizard_name} wizard</a> ✨"
|
||||
complete_custom: "Complete the {{name}}"
|
||||
|
||||
admin_js:
|
||||
admin:
|
||||
|
@ -337,11 +337,11 @@ en:
|
|||
other: "Select at least {{count}} items."
|
||||
|
||||
wizard:
|
||||
completed: "You have completed the {{name}} wizard."
|
||||
not_permitted: "You are not permitted to access the {{name}} wizard."
|
||||
completed: "You have completed this wizard."
|
||||
not_permitted: "You are not permitted to access this wizard."
|
||||
none: "There is no wizard here."
|
||||
return_to_site: "Return to {{siteName}}"
|
||||
requires_login: "You need to be logged in to access the {{name}} wizard."
|
||||
requires_login: "You need to be logged in to access this wizard."
|
||||
reset: "Reset this wizard."
|
||||
step_not_permitted: "You're not permitted to view this step."
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
module Jobs
|
||||
class SetAfterTimeWizard < ::Jobs::Base
|
||||
def execute(args)
|
||||
if CustomWizard::Wizard.find(args[:wizard_id])
|
||||
user_ids = []
|
||||
if SiteSetting.custom_wizard_enabled
|
||||
wizard = CustomWizard::Wizard.find(args[:wizard_id])
|
||||
|
||||
if wizard && wizard.after_time
|
||||
user_ids = []
|
||||
|
||||
User.human_users.each do |user|
|
||||
if CustomWizard::Wizard.set_wizard_redirect(args[:wizard_id], user)
|
||||
user_ids.push(user.id)
|
||||
User.human_users.each do |user|
|
||||
if CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
|
||||
user_ids.push(user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MessageBus.publish "/redirect_to_wizard", args[:wizard_id], user_ids: user_ids
|
||||
MessageBus.publish "/redirect_to_wizard", wizard.id, user_ids: user_ids
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,6 +36,13 @@ class CustomWizard::Builder
|
|||
sorted_field_validators << { priority: priority, type: type, block: block }
|
||||
@sorted_field_validators.sort_by! { |h| -h[:priority] }
|
||||
end
|
||||
|
||||
def mapper
|
||||
CustomWizard::Mapper.new(
|
||||
user: @wizard.user,
|
||||
data: @submissions.last
|
||||
)
|
||||
end
|
||||
|
||||
def build(build_opts = {}, params = {})
|
||||
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
|
||||
|
@ -46,8 +53,12 @@ class CustomWizard::Builder
|
|||
@steps.each do |step_template|
|
||||
@wizard.append_step(step_template['id']) do |step|
|
||||
step.title = step_template['title'] if step_template['title']
|
||||
step.description = step_template['description'] if step_template['description']
|
||||
step.banner = step_template['banner'] if step_template['banner']
|
||||
|
||||
if step_template['description']
|
||||
step.description = mapper.interpolate(step_template['description'])
|
||||
end
|
||||
|
||||
step.key = step_template['key'] if step_template['key']
|
||||
step.permitted = true
|
||||
|
||||
|
@ -87,10 +98,7 @@ class CustomWizard::Builder
|
|||
p['key'] = @submissions.last[p['key']]
|
||||
end
|
||||
|
||||
unless CustomWizard::Mapper.new(
|
||||
user: @wizard.user,
|
||||
data: @submissions.last
|
||||
).validate_pairs(pairs)
|
||||
unless mapper.validate_pairs(pairs)
|
||||
step.permitted = false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,17 +13,23 @@ class CustomWizard::StepUpdater
|
|||
end
|
||||
|
||||
def update
|
||||
return false if !SiteSetting.custom_wizard_enabled
|
||||
|
||||
@step.updater.call(self) if @step.present? && @step.updater.present?
|
||||
byebug
|
||||
|
||||
if success?
|
||||
if SiteSetting.custom_wizard_enabled &&
|
||||
@step.present? &&
|
||||
@step.updater.present? &&
|
||||
success?
|
||||
|
||||
@step.updater.call(self)
|
||||
|
||||
UserHistory.create(
|
||||
action: UserHistory.actions[:custom_wizard_step],
|
||||
acting_user_id: @current_user.id,
|
||||
context: @wizard.id,
|
||||
subject: @step.id
|
||||
)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -107,11 +107,14 @@ class CustomWizard::Validator
|
|||
wizard = CustomWizard::Wizard.create(@params[:id]) if !@opts[:create]
|
||||
current_time = wizard.present? ? wizard.after_time_scheduled : nil
|
||||
new_time = @params[:after_time_scheduled]
|
||||
|
||||
begin
|
||||
active_time = Time.parse(new_time.present? ? new_time : current_time).utc
|
||||
rescue ArgumentError
|
||||
invalid_time = true
|
||||
end
|
||||
|
||||
if (new_time.blank? && current_time.blank?) ||
|
||||
(new_time !~ /^([01]?[0-9]|2[0-3])\:[0-5][0-9]$/) ||
|
||||
(Time.parse(new_time).utc < Time.now.utc)
|
||||
|
||||
if invalid_time || active_time.blank? || active_time < Time.now.utc
|
||||
@error = { type: 'after_time' }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -172,9 +172,7 @@ class CustomWizard::Wizard
|
|||
|
||||
def can_access?
|
||||
return true if user.admin
|
||||
return false if multiple_submissions && completed?
|
||||
return false if !permitted?
|
||||
return true
|
||||
return permitted? && (multiple_submissions || !completed?)
|
||||
end
|
||||
|
||||
def reset
|
||||
|
@ -230,7 +228,11 @@ class CustomWizard::Wizard
|
|||
if (records = filter_records('prompt_completion')).any?
|
||||
records.reduce([]) do |result, record|
|
||||
wizard = CustomWizard::Wizard.new(::JSON.parse(record.value), user)
|
||||
result.push(id: wizard.id, name: wizard.name) if !wizard.completed?
|
||||
|
||||
if wizard.permitted? && !wizard.completed?
|
||||
result.push(id: wizard.id, name: wizard.name)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
else
|
||||
|
|
|
@ -15,7 +15,7 @@ module CustomWizardSiteSerializerExtension
|
|||
|
||||
def complete_custom_wizard
|
||||
if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user)
|
||||
requires_completion.map {|w| {name: w[:name], url: "/w/#{w[:id]}"}}
|
||||
requires_completion.map {|w| { name: w[:name], url: "/w/#{w[:id]}"} }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Laden …
In neuem Issue referenzieren