Custom Wizard Plugin

(Angus McLeod) #227

@codechef Great! If you could make a PR to the Custom Wizard repo, I’ll merge it in.

(Danny Goodall) #228

The removal of app/views/common/_special_font_face.html.erb as part of the FA work seems to have caused an error in the plugin

I’m getting an error in v2.2.0.beta4 +353 with the latest version of the plugin. It looks related to the Font Awesome changes made by @pmusaraj.

To reproduce…

Running the above Discourse version, create a new wizard with anything in it and follow the link to the wizard /w/.... It will fail to show anything on the screen (blank browser page) and will produce the following error in the log.

ActionView::Template::Error (Missing partial common/_special_font_face with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :mpeg, :m

This produces the long backtrace below, but the interesting part is here:

/var/www/discourse/plugins/discourse-custom-wizard/views/layouts/wizard.html.erb:19:in _plugins_discourse_custom_wizard_views_layouts_wizard_html_erb___2578403230507367641_69897385317380'

Full backtrace and env details


ActionView::Template::Error (Missing partial common/_special_font_face with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip], :variants=>, :handlers=>[:raw, :erb, :html, :builder, :ruby]}. Searched in: * “/var/www/discourse/plugins/discourse-custom-wizard/views” * “/var/www/discourse/app/views” * “/var/www/discourse/plugins/docker_manager/app/views” ) /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/path_set.rb:48:in `find’


/var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/path_set.rb:48:in find' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/lookup_context.rb:116:infind’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/partial_renderer.rb:423:in find_template' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/partial_renderer.rb:418:infind_partial’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/partial_renderer.rb:299:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/renderer.rb:49:inrender_partial’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/renderer.rb:23:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/helpers/rendering_helper.rb:34:inrender’ /var/www/discourse/plugins/discourse-custom-wizard/views/layouts/wizard.html.erb:19:in _plugins_discourse_custom_wizard_views_layouts_wizard_html_erb___2578403230507367641_69897385317380' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/template.rb:159:inblock in render’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:170:in instrument' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/template.rb:354:ininstrument_render_template’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/template.rb:157:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-mini-profiler-1.0.0/lib/mini_profiler/profiling_methods.rb:78:inblock in profile_method’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/template_renderer.rb:66:in render_with_layout' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/template_renderer.rb:52:inrender_template’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/template_renderer.rb:16:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/renderer.rb:44:inrender_template’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/renderer/renderer.rb:25:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/rendering.rb:103:in_render_template’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/streaming.rb:219:in _render_template' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/rendering.rb:84:inrender_to_body’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rendering.rb:52:in render_to_body' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/renderers.rb:142:inrender_to_body’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/rendering.rb:25:in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rendering.rb:36:inrender’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:46:in block (2 levels) in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/core_ext/benchmark.rb:14:inblock in ms’ /usr/local/lib/ruby/2.5.0/benchmark.rb:308:in realtime' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/core_ext/benchmark.rb:14:inms’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:46:in block in render' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:87:incleanup_view_runtime’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.0/lib/active_record/railties/controller_runtime.rb:31:in cleanup_view_runtime' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:45:inrender’ /var/www/discourse/app/controllers/application_controller.rb:117:in block in &lt;class:ApplicationController&gt;' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/rescuable.rb:114:ininstance_exec’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/rescuable.rb:114:in block in handler_for_rescue' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/rescuable.rb:92:inrescue_with_handler’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/rescuable.rb:165:in rescue_with_handler' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rescue.rb:25:inrescue in process_action’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rescue.rb:21:in process_action' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:34:inblock in process_action’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:168:in block in instrument' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications/instrumenter.rb:23:ininstrument’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:168:in instrument' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:32:inprocess_action’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/params_wrapper.rb:256:in process_action' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.0/lib/active_record/railties/controller_runtime.rb:24:inprocess_action’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/base.rb:134:in process' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/rendering.rb:32:inprocess’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-mini-profiler-1.0.0/lib/mini_profiler/profiling_methods.rb:78:in block in profile_method' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal.rb:191:indispatch’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal.rb:252:in dispatch' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:52:indispatch’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:34:in serve' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:52:inblock in serve’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:in each' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:inserve’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:840:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:524:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/railtie.rb:190:in public_send' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/railtie.rb:190:inmethod_missing’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/mapper.rb:19:in block in &lt;class:Constraints&gt;' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/mapper.rb:48:inserve’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:52:in block in serve' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:ineach’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:in serve' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:840:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-protection-2.0.3/lib/rack/protection/frame_options.rb:31:in call' /var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:24:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/tempfile_reaper.rb:15:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/conditional_get.rb:25:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/head.rb:12:in call' /var/www/discourse/lib/content_security_policy.rb:14:incall’ /var/www/discourse/lib/middleware/anonymous_cache.rb:216:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/session/abstract/id.rb:232:incontext’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/session/abstract/id.rb:226:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/cookies.rb:670:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/callbacks.rb:28:in block in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/callbacks.rb:98:inrun_callbacks’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/callbacks.rb:26:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/debug_exceptions.rb:61:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/show_exceptions.rb:33:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/logster-1.3.1/lib/logster/middleware/reporter.rb:31:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:38:in call_app' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:28:incall’ /var/www/discourse/config/initializers/100-quiet_logger.rb:16:in call' /var/www/discourse/config/initializers/100-silence_logger.rb:29:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/remote_ip.rb:81:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/request_id.rb:27:incall’ /var/www/discourse/lib/middleware/enforce_hostname.rb:17:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/method_override.rb:22:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/executor.rb:14:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/sendfile.rb:111:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-mini-profiler-1.0.0/lib/mini_profiler/profiler.rb:174:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/message_bus-2.1.6/lib/message_bus/rack/middleware.rb:63:incall’ /var/www/discourse/lib/middleware/request_tracker.rb:180:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:524:incall’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/railtie.rb:190:in public_send' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/railtie.rb:190:inmethod_missing’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:68:in block in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:53:ineach’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:53:in call' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.0/lib/unicorn/http_server.rb:606:inprocess_client’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.0/lib/unicorn/http_server.rb:701:in worker_loop' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.0/lib/unicorn/http_server.rb:549:inspawn_missing_workers’ /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.0/lib/unicorn/http_server.rb:142:in start' /var/www/discourse/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.0/bin/unicorn:126:in<top (required)>’ /var/www/discourse/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in load' /var/www/discourse/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in<main>’


hostname xxxxxxxxx-app
process_id 462
application_version bdb1268528eec66a450b6e52184a5854ccd6c804
REQUEST_URI /w/show-abcc-record-report
HTTP_USER_AGENT Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
username DanAdmin

The file that is referenced from wizard.html.erb:19 is here.

That file in the discourse repository was apparently removed as part of the merging of the Font Awesome branch a day or so ago.

Sorry @angus, but I just don’t have enough experience with the frontend to look at this. I hope that this gives you enough info to go on - or to advise if I’ve got a dodgy environment.

(Danny Goodall) #229

I’ll share a workaround that is working for me in case anyone else’s Wizard has just stopped working.

I simply deleted the offending line that is trying to render the now missing partial. Be warned, I’m sure this will have consequences so test and expect it to break, but for my specific 5-6 wizards, it’s now working again instead of presenting users with a blank screen.

Here’s my fork of @angus’ repository

And here’s the commit

The only things that I’ve noticed aren’t rendering properly are the carat on the dropdown UI component and the site logo in the corner of the wizard.


Thinking about trying to fix this, I seem to remember @angus mentioning that the Wizard was a stand-alone Ember app.

If that is the case, then I’m wondering how much of the Discourse FA 5 eco-system it needs and whether it might just be possible to pull the old special_font_face.html.erb partial into the plugin’s code base and use that?

But looking at the partial, it references some assets too, so probably need to move some of the things deleted in this commit into the Wizard Plugin codebase.

If I get some time I’ll try that.

Any other pointers gratefully received as not only am I flying blind, I can’t actually fly this type of aircraft!

(Angus McLeod) #230

@bletch Thanks! Fixed.

I also picked @codechef’s useful IE fixes into master. Thanks!

(Anton) #231

@angus, I love the plugin’s idea, but haven’t been watching it for a while. I now skimmed the topic and found out that issues are still being reported, so I assume the plugin is not yet stable? Or am I mistaken? may you please recommend if it’s a good idea to use it in production or not yet? I have a few cases for it, but I’m afraid it will break something.

(Angus McLeod) #232

Most issues occur due to changes in Discourse. For example, the most recent one that @bletch reported. This is a risk with any non-core plugin if you’re running the latest version of Discourse on your site. This plugin has been used consistently in a variety of sites for some time. It’s really up to you.

(codechef) #233

Thanks @angus Sorry, I was behind on setting up that PR - holidays and travel :turkey:

(Alexis Duran) #234

I am still reading the thread as I do not want to bother if is not necessary, nevertheless, I do not see information about people displaying the custom fields within the profile info, so I am wondering Is possible to get the custom fields that we asked on the wizards we create and then show them in the profile page?

(Angus McLeod) #235

Yes, you can setup an action to update a User Field, which will display in a user’s profile.

(Alexis Duran) #236

Thanks very much for taking the time to answer it, I have tested and play around a bit and works perfectly (which is obvious cause a lot of people use it haha). But thanks, I am looking forward now to add custom validations for specific fields. I may be ending up by creating a plugin, since I think that is actually something not supported.

Besides, may be interesting to add new input types to the set of types you have defined on the wizard. (Let’s say I want google places powered input instead the one you currently define). Anyway, I keep digging any other useful info would be welcome too, but you already did a lot. Thanks and Happy Christmas!

(Danny Goodall) #237

I’ve done some custom validation with the Wizard Plugin - albeit in the plugin side in Ruby.

It’s a little clunky as it works from a text field and only validates on the Wizard submit but, as I mentioned above, I needed validation for dates and it sort of works.

I’m on mobile at the moment but happy to share some stuff I learned about how to send back field and step level error messages to the user - when I’m online tomorrow.

(Alexis Duran) #238

Hey! thanks for the feedback let me know if you have some link or any source you can point out that can help me whenever I start to make something complex with this wizard plugin.


(Danny Goodall) #239

A few things I’ve learned that might be useful for others

I use the Custom Wizard Plugin as the UI for my Ruby code for a project I’ve written for the school I work at. It’s a little clunky but as I don’t have the time at the moment to dig in to ember and the front-end, it’s saved my bacon. I hope the information below might be useful to others.


So all the code below is running in a plugin with just Ruby-side code.

Following @angus’ examples above, I’ve implemented step handlers by creating a block using the following construct.

CustomWizard::Builder.add_step_handler('step_name') do | builder |

The code in the block is executed following the submission, in the wizard, of the step id of step_name.

The variable builder that is passed through to the step handler is an instance of the CustomWizard::Builder class and contains all of the information about the wizard, steps and fields - including:

builder.wizard - instance of CustomWizard::Wizard
builder.updater - instance of CustomerWizard::StepUpdater
builder.submissions array of fields and data from previous steps

Getting the Value of a Field in a Step Handler

In the step handler block example above, you are passed builder. To get hold of the field values for the wizard, we access the fields property of the embedded updater object instance.

So to cast the fields for the wizard to a hash and access the field we need, we do some like this.

CustomWizard::Builder.add_step_handler('step_name') do | builder |
  h = builder.updater.fields.to_h
  field_i_need = h[:field_i_need]

Reporting Errors Back to the User

So one of the things I needed to do was to post an error/information message back to the user at the end of the step handler block.

To do this we use the builder instance again and add errors to the updater instance like so.

CustomWizard::Builder.add_step_handler('step_name') do | builder |
  builder.updater.errors.add("step_name", "This is the message to display")

Note that "step_name" must be set to the id of the step for the message to display.

I’ve used this approach to implement step-wide validation. i.e. I want to perform some logic on more than one field on that step and, if needed, I send a message back to the wizard.

Following the end of the block, if errors have been added then the message is displayed and (I believe) the wizard will be stopped from advancing to the next step, or ending if it was on the last step.

Redirect the User to a URL at the end of the Step

This one took me a while to figure out.

In my plugin, I wanted to create a topic and then redirect the user to that topic. The issue (I eventually figured out) was that my code in the plugin isn’t actually in a Rails request so none of the redirect stuff I found in the Discourse source worked.

Luckily, @angus’ Wizard has received a Rails request and he had already built in the ability to redirect the user at the end of a step.

It looks like this:

CustomWizard::Builder.add_step_handler('step_name') do | builder |
  builder.updater.result = { redirect_to: "http://url-to-redirect-to" }

Again, we use the builder object that is passed to our step handler block and set the result hash.

At the end of the block, the wizard plugin will then re-direct the user to the specified url-to-redirect-to.

Field Level Validation

I needed to support dates in my wizard and would love to see a date / time field added to the wizard at some point (together with calendar lookup like the events plugin). But for the time being, I have had to use text fields and then in my plugin attempt to cast the text to a DateTime. If the casting fails I assume that the date entered by the user is not a valid date and display a message.

The basic structure of the field validator looks like this.

CustomWizard::Builder.add_field_validator('text') do |field, updater, step_template|

The first thing we see is that we’ve registered a field validator for all ‘text’ fields. This means that whenever a wizard contains a text field, our code will be run. I would have preferred to register a validator for the field id, but instead, we have to check for the id of the field(s) that we want to validate inside the block.

field is a hash of {id: label: type:} (and others I believe)
updater is an instance of CustomWizard::StepUpdater
step_template is a hash of {id: 'step_id', title: 'Title of Step', fields: {'field' : value}}

So this is my date validation code.

As our code will be executed every time a text field is validated, I need to only run the validation code on the field ids of the date fields that are to be validated - and by extension ignore the other text fields.

CustomWizard::Builder.add_field_validator('text') do |field, updater, step_template|
  field_id = field['id']
  value = updater.fields[field_id]
  min_length = field['min_length'] # Example to show some of the other fields properties
  case field_id
  when 'lrl_date_start', 'lrl_date_end', 'pcl_date_start', 'pcl_date_end', 'abcc_date_start', 'abcc_date_end', 'eod_date_start', 'eod_date_end'
    # Validate as date
      casted = value.empty? ? '' : DateTime.parse(value)
    rescue ArgumentError
      casted = nil
    updater.errors.add(field_id.to_s, "I don't recognise that date") if casted.nil?

(Joshua Kogan) #240

I setup the wizard just like your example (name, bio), but the background won’t show up. I also used a pattern library image url. I also tried adding it to my staff topic, but url didn’t take from there either. Any recommendations?


I had to remove and reinstall this plugin (and all my others) due to an upgrade recently, and since then my custom wizards haven’t worked. The wizard attempts to load the file /custom-wizard-start.js, which 404s, here’s an example of this. What could be the issue here?

(Angus McLeod) #242

Sorry you’ve had an issue! This should fix it:

(Angus McLeod) #243

Hey @Joshua_Kogan, sorry for the slow reply. Did you wrap the url in url(" ")? That field is mapped to the background css attribute of the body.

I just changed the background image of the welcome wizard on my sandbox as an example:


Thanks, that fixed that issue, but now this happens on my wizards:

Edit: This is happening because of this:

.custom-wizard .wizard-step-form {
    max-height: 500px;

I’ve confirmed that this isn’t code in my custom theme.

(Danny Goodall) #245

This looks like expected behaviour to me - if you’ve added all of those fields into a single wizard step.

My understanding (educated guess) is that if you run out of space on one wizard ‘screenful’ then you add multiple steps to accommodate the additional fields.

The wizard metaphor is widely understood by users and (no data for this) I’d think they’d expect to advance through a series of non-scrolling screens by pressing Next.

What is confusing me is that you seem to be suggesting that this wizard has worked before and that it is now failing. Is that the case?


Previously this didn’t happen, as the max-height was added in the ‘various fixes’ commit that fixed the JS loading issue I had earlier. Other than the overflow (which I’ve fixed in my theme by just setting max-height: none) the wizard works fine.