- {{d-button action="change" actionParam=l.id translatedLabel=l.label class=l.classes}}
- {{d-button action="remove" actionParam=l.id icon="times" class="remove"}}
+ {{#each links as |link|}}
+
+ {{d-button action="change" actionParam=link.id translatedLabel=link.label class=link.classes}}
+ {{#unless link.first}}
+ {{d-button action="back" actionParam=link icon="arrow-left" class="back"}}
+ {{/unless}}
+ {{#unless link.last}}
+ {{d-button action="forward" actionParam=link icon="arrow-right" class="forward"}}
+ {{/unless}}
+ {{d-button action="remove" actionParam=link.id icon="times" class="remove"}}
{{/each}}
{{/if}}
diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js
index 8b30ad94..d0fef99a 100644
--- a/assets/javascripts/wizard-custom.js
+++ b/assets/javascripts/wizard-custom.js
@@ -2,6 +2,7 @@
//= require discourse/app/mixins/singleton
//= require discourse/app/mixins/upload
+//= require discourse/app/mixins/composer-upload
//= require discourse/app/adapters/rest
diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb
index e0cf669d..7125fc8a 100644
--- a/controllers/custom_wizard/wizard.rb
+++ b/controllers/custom_wizard/wizard.rb
@@ -68,7 +68,8 @@ class CustomWizard::WizardController < ::ApplicationController
result.merge!(redirect_to: submission.redirect_to)
end
- wizard.final_cleanup!
+ submission.remove if submission.present?
+ wizard.reset
end
render json: result
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index 1b5770d7..6a36af41 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -392,7 +392,7 @@ class CustomWizard::Action
user_ids.each { |user_id| group.group_users.build(user_id: user_id) }
end
- GroupActionLogger.new(user, group, skip_guardian: true).log_change_group_settings
+ GroupActionLogger.new(user, group).log_change_group_settings
log_success("Group created", group.name)
result.output = group.name
diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb
index 51ca4183..aede16a2 100644
--- a/lib/custom_wizard/builder.rb
+++ b/lib/custom_wizard/builder.rb
@@ -75,6 +75,7 @@ class CustomWizard::Builder
end
@wizard.update!
+ CustomWizard::Submission.cleanup_incomplete_submissions(@wizard)
@wizard
end
diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb
index 618a9a67..050ec000 100644
--- a/lib/custom_wizard/submission.rb
+++ b/lib/custom_wizard/submission.rb
@@ -4,7 +4,7 @@ class CustomWizard::Submission
PAGE_LIMIT = 50
KEY ||= "submissions"
- META ||= %w(submitted_at route_to redirect_on_complete redirect_to)
+ META ||= %w(updated_at submitted_at route_to redirect_on_complete redirect_to)
attr_reader :id,
:user,
@@ -45,7 +45,8 @@ class CustomWizard::Submission
validate
submission_list = self.class.list(wizard, user_id: user.id)
- submissions = submission_list.submissions.select { |submission| submission.id != self.id }
+ submissions = submission_list.select { |submission| submission.id != self.id }
+ self.updated_at = Time.now.iso8601
submissions.push(self)
submission_data = submissions.map { |submission| data_to_save(submission) }
@@ -97,7 +98,41 @@ class CustomWizard::Submission
new(wizard, data, user_id)
end
- def self.list(wizard, user_id: nil, page: nil)
+ def remove
+ if present?
+ user_id = @user.id
+ wizard_id = @wizard.id
+ submission_id = @id
+ data = PluginStore.get("#{wizard_id}_#{KEY}", user_id)
+ data.delete_if { |sub| sub["id"] == submission_id }
+ PluginStore.set("#{wizard_id}_#{KEY}", user_id, data)
+ end
+ end
+
+ def self.cleanup_incomplete_submissions(wizard)
+ user_id = wizard.user.id
+ all_submissions = list(wizard, user_id: user_id)
+ sorted_submissions = all_submissions.sort_by do |submission|
+ zero_epoch_time = DateTime.strptime("0", '%s')
+ [
+ submission.submitted_at ? Time.iso8601(submission.submitted_at) : zero_epoch_time,
+ submission.updated_at ? Time.iso8601(submission.updated_at) : zero_epoch_time
+ ]
+ end.reverse
+
+ has_incomplete = false
+ valid_submissions = sorted_submissions.select do |submission|
+ to_be_included = submission.submitted_at || !has_incomplete
+ has_incomplete = true if !submission.submitted_at
+
+ to_be_included
+ end
+
+ valid_data = valid_submissions.map { |submission| submission.data_to_save(submission) }
+ PluginStore.set("#{wizard.id}_#{KEY}", user_id, valid_data)
+ 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?
diff --git a/lib/custom_wizard/validators/update.rb b/lib/custom_wizard/validators/update.rb
index c722a763..2301760f 100644
--- a/lib/custom_wizard/validators/update.rb
+++ b/lib/custom_wizard/validators/update.rb
@@ -32,12 +32,14 @@ class ::CustomWizard::UpdateValidator
@updater.errors.add(field_id, I18n.t('wizard.field.required', label: label))
end
- 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))
- end
+ if value.is_a?(String) && (stripped_length = value.strip.length) > 0
+ if min_length.present? && stripped_length < min_length.to_i
+ @updater.errors.add(field_id, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
+ end
- 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))
+ if max_length.present? && stripped_length > max_length.to_i
+ @updater.errors.add(field_id, I18n.t('wizard.field.too_long', label: label, max: max_length.to_i))
+ end
end
if is_url_type(field) && value.present? && !check_if_url(value)
diff --git a/plugin.rb b/plugin.rb
index 615ad81e..e5402542 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# name: discourse-custom-wizard
# about: Create custom wizards
-# version: 0.8.0
+# version: 0.8.1
# authors: Angus McLeod
# url: https://github.com/paviliondev/discourse-custom-wizard
# contact emails: angus@thepavilion.io
@@ -34,6 +34,7 @@ if respond_to?(:register_svg_icon)
register_svg_icon "chevron-left"
register_svg_icon "save"
register_svg_icon "far-life-ring"
+ register_svg_icon "arrow-right"
end
class ::Sprockets::DirectiveProcessor
diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb
index fd7e8984..a9caaa0a 100644
--- a/spec/components/custom_wizard/submission_spec.rb
+++ b/spec/components/custom_wizard/submission_spec.rb
@@ -47,4 +47,50 @@ describe CustomWizard::Submission do
it "paginates submission lists" do
expect(described_class.list(@wizard, page: 1).submissions.size).to eq((@count + 1) - CustomWizard::Submission::PAGE_LIMIT)
end
+
+ context "#cleanup_incomplete_submissions" do
+ it "cleans up redundant incomplete submissions on each build" do
+ freeze_time Time.now + 1
+ described_class.new(@wizard, step_1_field_1: "I am the second submission").save
+ builder = CustomWizard::Builder.new(@wizard.id, @wizard.user)
+ builder.build
+ sub_list = described_class.list(@wizard, user_id: @wizard.user.id)
+
+ expect(sub_list.length).to eq(1)
+ expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the second submission")
+ end
+
+ it "handles submissions without 'updated_at' field correctly" do
+ described_class.new(@wizard, step_1_field_1: "I am the second submission").save
+ described_class.new(@wizard, step_1_field_1: "I am the third submission").save
+ sub_data = PluginStore.get("#{@wizard.id}_submissions", @wizard.user.id)
+ sub_data.each do |sub|
+ sub['updated_at'] = nil
+ end
+ PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, sub_data)
+ builder = CustomWizard::Builder.new(@wizard.id, @wizard.user)
+ builder.build
+ sub_list = described_class.list(@wizard, user_id: @wizard.user.id)
+
+ expect(sub_list.length).to eq(1)
+ expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the third submission")
+ end
+
+ it "handles submissions with and without 'updated_at' field correctly" do
+ freeze_time Time.now + 1
+ described_class.new(@wizard, step_1_field_1: "I am the second submission").save
+ freeze_time Time.now + 2
+ described_class.new(@wizard, step_1_field_1: "I am the third submission").save
+ sub_data = PluginStore.get("#{@wizard.id}_submissions", @wizard.user.id)
+ sub_data[0]['updated_at'] = nil
+ PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, sub_data)
+
+ builder = CustomWizard::Builder.new(@wizard.id, @wizard.user)
+ builder.build
+ sub_list = described_class.list(@wizard, user_id: @wizard.user.id)
+
+ expect(sub_list.length).to eq(1)
+ expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the third submission")
+ end
+ end
end
diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb
index e7658d8c..e976e1ff 100644
--- a/spec/components/custom_wizard/update_validator_spec.rb
+++ b/spec/components/custom_wizard/update_validator_spec.rb
@@ -97,6 +97,31 @@ describe CustomWizard::UpdateValidator do
).to eq(nil)
end
+ it "applies min length only if the input is non-empty" do
+ min_length = 3
+
+ @template[:steps][0][:fields][0][:min_length] = min_length
+
+ CustomWizard::Template.save(@template)
+
+ updater = perform_validation('step_1', step_1_field_1: '')
+ expect(
+ updater.errors.messages[:step_1_field_1].first
+ ).to eq(nil)
+ end
+
+ it "applies max length only if the input is non-empty" do
+ max_length = 100
+
+ @template[:steps][0][:fields][0][:max_length] = max_length
+
+ CustomWizard::Template.save(@template)
+ updater = perform_validation('step_1', step_1_field_1: "")
+ expect(
+ updater.errors.messages[:step_1_field_1].first
+ ).to eq(nil)
+ end
+
it 'standardises boolean entries' do
updater = perform_validation('step_2', step_2_field_5: 'false')
expect(updater.submission['step_2_field_5']).to eq(false)
diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb
index 93f33a81..9e4bbbbe 100644
--- a/spec/plugin_helper.rb
+++ b/spec/plugin_helper.rb
@@ -11,4 +11,7 @@ if ENV['SIMPLECOV']
end
end
+require 'oj'
+Oj.default_options = Oj.default_options.merge(cache_str: -1)
+
require 'rails_helper'
diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb
index f2000bda..87ff7efe 100644
--- a/spec/requests/custom_wizard/wizard_controller_spec.rb
+++ b/spec/requests/custom_wizard/wizard_controller_spec.rb
@@ -50,30 +50,55 @@ describe CustomWizard::WizardController do
expect(response.parsed_body["error"]).to eq("We couldn't find a wizard at that address.")
end
- it 'skips a wizard if user is allowed to skip' do
- put '/w/super-mega-fun-wizard/skip.json'
- expect(response.status).to eq(200)
- end
+ context 'when user skips the wizard' do
- it 'lets user skip if user cant access wizard' do
- @template["permitted"] = permitted_json["permitted"]
- CustomWizard::Template.save(@template, skip_jobs: true)
+ it 'skips a wizard if user is allowed to skip' do
+ put '/w/super-mega-fun-wizard/skip.json'
+ expect(response.status).to eq(200)
+ end
- put '/w/super-mega-fun-wizard/skip.json'
- expect(response.status).to eq(200)
- end
+ it 'lets user skip if user cant access wizard' do
+ @template["permitted"] = permitted_json["permitted"]
+ CustomWizard::Template.save(@template, skip_jobs: true)
- it 'returns a no skip message if user is not allowed to skip' do
- @template['required'] = 'true'
- CustomWizard::Template.save(@template)
- put '/w/super-mega-fun-wizard/skip.json'
- expect(response.parsed_body['error']).to eq("Wizard can't be skipped")
- end
+ put '/w/super-mega-fun-wizard/skip.json'
+ expect(response.status).to eq(200)
+ end
- it 'skip response contains a redirect_to if in users submissions' do
- @wizard = CustomWizard::Wizard.create(@template["id"], user)
- CustomWizard::Submission.new(@wizard, redirect_to: "/t/2").save
- put '/w/super-mega-fun-wizard/skip.json'
- expect(response.parsed_body['redirect_to']).to eq('/t/2')
+ it 'returns a no skip message if user is not allowed to skip' do
+ @template['required'] = 'true'
+ CustomWizard::Template.save(@template)
+ put '/w/super-mega-fun-wizard/skip.json'
+ expect(response.parsed_body['error']).to eq("Wizard can't be skipped")
+ end
+
+ it 'skip response contains a redirect_to if in users submissions' do
+ @wizard = CustomWizard::Wizard.create(@template["id"], user)
+ CustomWizard::Submission.new(@wizard, redirect_to: "/t/2").save
+ put '/w/super-mega-fun-wizard/skip.json'
+ expect(response.parsed_body['redirect_to']).to eq('/t/2')
+ end
+
+ it "deletes the submission if user has filled up some data" do
+ @wizard = CustomWizard::Wizard.create(@template["id"], user)
+ CustomWizard::Submission.new(@wizard, step_1_field_1: "Hello World").save
+ current_submission = @wizard.current_submission
+ put '/w/super-mega-fun-wizard/skip.json'
+ list = CustomWizard::Submission.list(@wizard)
+
+ expect(list.any? { |submission| submission.id == current_submission.id }).to eq(false)
+ end
+
+ it "starts from the first step if user visits after skipping the wizard" do
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Text input"
+ }
+ }
+ put '/w/super-mega-fun-wizard/skip.json'
+ get '/w/super-mega-fun-wizard.json'
+
+ expect(response.parsed_body["start"]).to eq('step_1')
+ end
end
end