diff --git a/assets/javascripts/discourse/components/wizard-custom-step.js.es6 b/assets/javascripts/discourse/components/wizard-custom-step.js.es6 index ff3d4ee6..a18743dd 100644 --- a/assets/javascripts/discourse/components/wizard-custom-step.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-step.js.es6 @@ -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); diff --git a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 b/assets/javascripts/discourse/components/wizard-text-editor.js.es6 index 5c95f179..4cbb7efb 100644 --- a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 +++ b/assets/javascripts/discourse/components/wizard-text-editor.js.es6 @@ -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'); diff --git a/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs new file mode 100644 index 00000000..503ee113 --- /dev/null +++ b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs @@ -0,0 +1,7 @@ +{{#each site.complete_custom_wizard as |wizard|}} +
+
+ {{i18n 'wizard.complete_custom' name=wizard.name}} +
+
+{{/each}} \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6 b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6 new file mode 100644 index 00000000..8d097475 --- /dev/null +++ b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6 @@ -0,0 +1,6 @@ +export default { + shouldRender(_, ctx) { + return ctx.siteSettings.custom_wizard_enabled && + ctx.site.complete_custom_wizard; + } +} \ No newline at end of file diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 index e2aaec10..5f10283e 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -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: { diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 1e4436cb..cd2d9558 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -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(`
${text}
`); - }); - } - } - }); - }); const existing = DiscourseURL.routeTo; DiscourseURL.routeTo = function(path, opts) { diff --git a/assets/javascripts/discourse/lib/wizard-json.js.es6 b/assets/javascripts/discourse/lib/wizard-json.js.es6 index 3ef519b1..670fcee7 100644 --- a/assets/javascripts/discourse/lib/wizard-json.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-json.js.es6 @@ -137,7 +137,7 @@ function buildProperties(json) { steps: A(), actions: A() }; - + if (present(json)) { props.existingId = true; props = buildBasicProperties(json, 'wizard', props); diff --git a/assets/javascripts/discourse/lib/wizard.js.es6 b/assets/javascripts/discourse/lib/wizard.js.es6 index 2f252fbe..188e6ed4 100644 --- a/assets/javascripts/discourse/lib/wizard.js.es6 +++ b/assets/javascripts/discourse/lib/wizard.js.es6 @@ -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 }; \ No newline at end of file diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index 1384a252..d2e7ff2f 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -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), diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index b7711339..23a745b3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -97,6 +97,34 @@ {{/if}} +{{#if showPrefill}} +
+
+ +
+ +
+ {{wizard-mapper + inputs=field.prefill + options=prefillOptions}} +
+
+{{/if}} + +{{#if showContent}} +
+
+ +
+ +
+ {{wizard-mapper + inputs=field.content + options=contentOptions}} +
+
+{{/if}} + {{wizard-advanced-toggle showAdvanced=field.showAdvanced}} {{#if field.showAdvanced}} @@ -120,34 +148,6 @@ {{/if}} - {{#if showPrefill}} -
-
- -
- -
- {{wizard-mapper - inputs=field.prefill - options=prefillOptions}} -
-
- {{/if}} - - {{#if showContent}} -
-
- -
- -
- {{wizard-mapper - inputs=field.content - options=contentOptions}} -
-
- {{/if}} -
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index c2f0b073..6c4b359a 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -30,7 +30,7 @@
{{wizard-text-editor value=step.raw_description - fieldsEnabled=false}} + wizardFields=descriptionWizardFields}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs index b306aee6..5638ac70 100644 --- a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs @@ -21,10 +21,13 @@ {{i18n 'admin.wizard.action.post_builder.user_fields'}} {{userFieldList}} - + + {{#if hasWizardFields}} + + {{/if}}
{{/if}} {{/if}} diff --git a/assets/javascripts/wizard/initializers/custom.js.es6 b/assets/javascripts/wizard/initializers/custom.js.es6 index d879e14e..d74e25b1 100644 --- a/assets/javascripts/wizard/initializers/custom.js.es6 +++ b/assets/javascripts/wizard/initializers/custom.js.es6 @@ -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; } diff --git a/assets/javascripts/wizard/templates/custom.index.hbs b/assets/javascripts/wizard/templates/custom.index.hbs index c8e8966c..6040d9b5 100644 --- a/assets/javascripts/wizard/templates/custom.index.hbs +++ b/assets/javascripts/wizard/templates/custom.index.hbs @@ -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}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4ce6d571..463ef8c6 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1,7 +1,7 @@ en: js: wizard: - complete_custom: "Welcome to %{site_name}! Get started with the %{wizard_name} wizard ✨" + 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." diff --git a/jobs/set_after_time_wizard.rb b/jobs/set_after_time_wizard.rb index e94d2d92..7aa528ba 100644 --- a/jobs/set_after_time_wizard.rb +++ b/jobs/set_after_time_wizard.rb @@ -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 diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index b8f9b7dd..fba34f48 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -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 diff --git a/lib/custom_wizard/step_updater.rb b/lib/custom_wizard/step_updater.rb index aa6523d5..caeb37ef 100644 --- a/lib/custom_wizard/step_updater.rb +++ b/lib/custom_wizard/step_updater.rb @@ -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 diff --git a/lib/custom_wizard/validator.rb b/lib/custom_wizard/validator.rb index b14fe22f..5b377584 100644 --- a/lib/custom_wizard/validator.rb +++ b/lib/custom_wizard/validator.rb @@ -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 diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index 9d2383cf..71371554 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -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 diff --git a/serializers/site_serializer.rb b/serializers/site_serializer.rb index f899ae6e..8a25e032 100644 --- a/serializers/site_serializer.rb +++ b/serializers/site_serializer.rb @@ -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