From f1fdc37a215c603a8e22a188df37e5beec75507b Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 5 Dec 2019 17:48:32 +1100 Subject: [PATCH] Add enabled setting && tests && file restructure --- .../initializers/custom-wizard-edits.js.es6 | 6 +- .../custom-wizard-redirect.js.es6 | 5 +- config/locales/server.en.yml | 1 + config/routes.rb | 36 ++ config/settings.yml | 1 + controllers/{ => custom_wizard}/admin.rb | 0 controllers/{ => custom_wizard}/api.rb | 0 controllers/{ => custom_wizard}/steps.rb | 0 controllers/{ => custom_wizard}/transfer.rb | 0 controllers/{ => custom_wizard}/wizard.rb | 0 lib/{ => custom_wizard}/api/api.rb | 0 lib/{ => custom_wizard}/api/authorization.rb | 0 lib/{ => custom_wizard}/api/endpoint.rb | 0 lib/{ => custom_wizard}/api/log_entry.rb | 0 lib/{ => custom_wizard}/builder.rb | 184 ++++++----- lib/custom_wizard/engine.rb | 6 + lib/{ => custom_wizard}/field.rb | 0 lib/{ => custom_wizard}/flags.rb | 0 lib/{ => custom_wizard}/step_updater.rb | 0 lib/{ => custom_wizard}/template.rb | 0 lib/{ => custom_wizard}/wizard.rb | 2 + lib/wizard_edits.rb | 227 ------------- plugin.rb | 310 ++++++++++++++---- .../api/authorization_serializer.rb | 0 .../api/basic_endpoint_serializer.rb | 0 .../api/endpoint_serializer.rb | 0 .../{ => custom_wizard}/api/log_serializer.rb | 0 .../{api => custom_wizard}/api_serializer.rb | 0 .../basic_api_serializer.rb | 0 spec/components/custom_wizard/builder_spec.rb | 277 +++++++++------- spec/fixtures/wizard.json | 52 +++ 31 files changed, 597 insertions(+), 510 deletions(-) create mode 100644 config/routes.rb rename controllers/{ => custom_wizard}/admin.rb (100%) rename controllers/{ => custom_wizard}/api.rb (100%) rename controllers/{ => custom_wizard}/steps.rb (100%) rename controllers/{ => custom_wizard}/transfer.rb (100%) rename controllers/{ => custom_wizard}/wizard.rb (100%) rename lib/{ => custom_wizard}/api/api.rb (100%) rename lib/{ => custom_wizard}/api/authorization.rb (100%) rename lib/{ => custom_wizard}/api/endpoint.rb (100%) rename lib/{ => custom_wizard}/api/log_entry.rb (100%) rename lib/{ => custom_wizard}/builder.rb (80%) create mode 100644 lib/custom_wizard/engine.rb rename lib/{ => custom_wizard}/field.rb (100%) rename lib/{ => custom_wizard}/flags.rb (100%) rename lib/{ => custom_wizard}/step_updater.rb (100%) rename lib/{ => custom_wizard}/template.rb (100%) rename lib/{ => custom_wizard}/wizard.rb (99%) delete mode 100644 lib/wizard_edits.rb rename serializers/{ => custom_wizard}/api/authorization_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/basic_endpoint_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/endpoint_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/log_serializer.rb (100%) rename serializers/{api => custom_wizard}/api_serializer.rb (100%) rename serializers/{api => custom_wizard}/basic_api_serializer.rb (100%) create mode 100644 spec/fixtures/wizard.json diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 172f0f34..1e4436cb 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -3,7 +3,11 @@ import DiscourseURL from 'discourse/lib/url'; export default { name: 'custom-wizard-edits', - initialize() { + initialize(container) { + 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) { diff --git a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 index 4f60b57a..32827b9b 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 @@ -6,8 +6,9 @@ export default { initialize: function (container) { const messageBus = container.lookup('message-bus:main'); - - if (!messageBus) { return; } + const siteSettings = container.lookup('site-settings:main'); + + if (!siteSettings.custom_wizard_enabled || !messageBus) return; messageBus.subscribe("/redirect_to_wizard", function (wizardId) { const wizardUrl = window.location.origin + '/w/' + wizardId; diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index dd36b72f..e8e4a7c1 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -21,6 +21,7 @@ en: no_valid_wizards: "File doesn't contain any valid wizards" site_settings: + custom_wizard_enabled: "Enable custom wizards." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview" wizard_national_flags_label_cue: "The characters that must appear in the dropdown field label for it to show a flag" \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 00000000..764c4e42 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,36 @@ +CustomWizard::Engine.routes.draw do + get ':wizard_id' => 'wizard#index' + put ':wizard_id/skip' => 'wizard#skip' + get ':wizard_id/steps' => 'wizard#index' + get ':wizard_id/steps/:step_id' => 'wizard#index' + put ':wizard_id/steps/:step_id' => 'steps#update' +end + +Discourse::Application.routes.append do + mount ::CustomWizard::Engine, at: 'w' + post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" + + scope module: 'custom_wizard', constraints: AdminConstraint.new do + get 'admin/wizards' => 'admin#index' + get 'admin/wizards/field-types' => 'admin#field_types' + get 'admin/wizards/custom' => 'admin#index' + get 'admin/wizards/custom/new' => 'admin#index' + get 'admin/wizards/custom/all' => 'admin#custom_wizards' + get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' + put 'admin/wizards/custom/save' => 'admin#save' + delete 'admin/wizards/custom/remove' => 'admin#remove' + get 'admin/wizards/submissions' => 'admin#index' + get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' + get 'admin/wizards/apis' => 'api#list' + get 'admin/wizards/apis/new' => 'api#index' + get 'admin/wizards/apis/:name' => 'api#find' + put 'admin/wizards/apis/:name' => 'api#save' + delete 'admin/wizards/apis/:name' => 'api#remove' + delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' + get 'admin/wizards/apis/:name/redirect' => 'api#redirect' + get 'admin/wizards/apis/:name/authorize' => 'api#authorize' + get 'admin/wizards/transfer' => 'transfer#index' + get 'admin/wizards/transfer/export' => 'transfer#export' + post 'admin/wizards/transfer/import' => 'transfer#import' + end +end \ No newline at end of file diff --git a/config/settings.yml b/config/settings.yml index e99f28bd..7c51b15f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,4 +1,5 @@ plugins: + custom_wizard_enabled: true wizard_redirect_exclude_paths: client: true type: list diff --git a/controllers/admin.rb b/controllers/custom_wizard/admin.rb similarity index 100% rename from controllers/admin.rb rename to controllers/custom_wizard/admin.rb diff --git a/controllers/api.rb b/controllers/custom_wizard/api.rb similarity index 100% rename from controllers/api.rb rename to controllers/custom_wizard/api.rb diff --git a/controllers/steps.rb b/controllers/custom_wizard/steps.rb similarity index 100% rename from controllers/steps.rb rename to controllers/custom_wizard/steps.rb diff --git a/controllers/transfer.rb b/controllers/custom_wizard/transfer.rb similarity index 100% rename from controllers/transfer.rb rename to controllers/custom_wizard/transfer.rb diff --git a/controllers/wizard.rb b/controllers/custom_wizard/wizard.rb similarity index 100% rename from controllers/wizard.rb rename to controllers/custom_wizard/wizard.rb diff --git a/lib/api/api.rb b/lib/custom_wizard/api/api.rb similarity index 100% rename from lib/api/api.rb rename to lib/custom_wizard/api/api.rb diff --git a/lib/api/authorization.rb b/lib/custom_wizard/api/authorization.rb similarity index 100% rename from lib/api/authorization.rb rename to lib/custom_wizard/api/authorization.rb diff --git a/lib/api/endpoint.rb b/lib/custom_wizard/api/endpoint.rb similarity index 100% rename from lib/api/endpoint.rb rename to lib/custom_wizard/api/endpoint.rb diff --git a/lib/api/log_entry.rb b/lib/custom_wizard/api/log_entry.rb similarity index 100% rename from lib/api/log_entry.rb rename to lib/custom_wizard/api/log_entry.rb diff --git a/lib/builder.rb b/lib/custom_wizard/builder.rb similarity index 80% rename from lib/builder.rb rename to lib/custom_wizard/builder.rb index b51b6e00..abdc4cd1 100644 --- a/lib/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -77,112 +77,118 @@ class CustomWizard::Builder end def build(build_opts = {}, params = {}) - unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted? - reset_submissions if build_opts[:reset] + + return @wizard if !SiteSetting.custom_wizard_enabled || + (!@wizard.multiple_submissions && + @wizard.completed? && + !@wizard.user.admin) || + !@steps || + !@wizard.permitted? + + reset_submissions if build_opts[:reset] - @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'] - step.key = step_template['key'] if step_template['key'] - step.permitted = true + @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'] + step.key = step_template['key'] if step_template['key'] + step.permitted = true - if permitted_params = step_template['permitted_params'] - permitted_data = {} + if permitted_params = step_template['permitted_params'] + permitted_data = {} - permitted_params.each do |param| - key = param['key'].to_sym - permitted_data[key] = params[key] if params[key] - end - - if permitted_data.present? - current_data = @submissions.last || {} - save_submissions(current_data.merge(permitted_data), false) - end + permitted_params.each do |param| + key = param['key'].to_sym + permitted_data[key] = params[key] if params[key] end - if required_data = step_template['required_data'] - if !@submissions.last && required_data.present? - step.permitted = false - next - end + if permitted_data.present? + current_data = @submissions.last || {} + save_submissions(current_data.merge(permitted_data), false) + end + end - required_data.each do |rd| - if rd['connector'] === 'equals' - step.permitted = @submissions.last[rd['key']] == @submissions.last[rd['value']] - end - end - - if !step.permitted - step.permitted_message = step_template['required_data_message'] if step_template['required_data_message'] - next - end + if required_data = step_template['required_data'] + if !@submissions.last && required_data.present? + step.permitted = false + next end + required_data.each do |rd| + if rd['connector'] === 'equals' + step.permitted = @submissions.last[rd['key']] == @submissions.last[rd['value']] + end + end + + if !step.permitted + step.permitted_message = step_template['required_data_message'] if step_template['required_data_message'] + next + end + end + + if step_template['fields'] && step_template['fields'].length + step_template['fields'].each do |field_template| + append_field(step, step_template, field_template, build_opts) + end + end + + step.on_update do |updater| + @updater = updater + user = @wizard.user + if step_template['fields'] && step_template['fields'].length - step_template['fields'].each do |field_template| - append_field(step, step_template, field_template, build_opts) + step_template['fields'].each do |field| + validate_field(field, updater, step_template) if field['type'] != 'text-only' + end + end + + next if updater.errors.any? + + CustomWizard::Builder.step_handlers.each do |handler| + if handler[:wizard_id] == @wizard.id + handler[:block].call(self) end end - step.on_update do |updater| - @updater = updater - user = @wizard.user - - if step_template['fields'] && step_template['fields'].length - step_template['fields'].each do |field| - validate_field(field, updater, step_template) if field['type'] != 'text-only' - end + next if updater.errors.any? + + data = updater.fields.to_h + + ## if the wizard has data from the previous steps make that accessible to the actions. + if @submissions && @submissions.last && !@submissions.last.key?("submitted_at") + submission = @submissions.last + data = submission.merge(data) + end + + if step_template['actions'] && step_template['actions'].length && data + step_template['actions'].each do |action| + self.send(action['type'].to_sym, user, action, data) end - - next if updater.errors.any? + end - CustomWizard::Builder.step_handlers.each do |handler| - if handler[:wizard_id] == @wizard.id - handler[:block].call(self) - end - end + final_step = updater.step.next.nil? + + if route_to = data['route_to'] + data.delete('route_to') + end - next if updater.errors.any? + if @wizard.save_submissions && updater.errors.empty? + save_submissions(data, final_step) + elsif final_step + PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id) + end - data = updater.fields.to_h + if final_step && @wizard.id === @wizard.user.custom_fields['redirect_to_wizard'] + @wizard.user.custom_fields.delete('redirect_to_wizard'); + @wizard.user.save_custom_fields(true) + end - ## if the wizard has data from the previous steps make that accessible to the actions. - if @submissions && @submissions.last && !@submissions.last.key?("submitted_at") - submission = @submissions.last - data = submission.merge(data) - end - - if step_template['actions'] && step_template['actions'].length && data - step_template['actions'].each do |action| - self.send(action['type'].to_sym, user, action, data) - end - end - - final_step = updater.step.next.nil? - - if route_to = data['route_to'] - data.delete('route_to') - end - - if @wizard.save_submissions && updater.errors.empty? - save_submissions(data, final_step) - elsif final_step - PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id) - end - - if final_step && @wizard.id === @wizard.user.custom_fields['redirect_to_wizard'] - @wizard.user.custom_fields.delete('redirect_to_wizard'); - @wizard.user.save_custom_fields(true) - end - - if updater.errors.empty? - if final_step - updater.result[:redirect_on_complete] = route_to || data['redirect_on_complete'] - elsif route_to - updater.result[:redirect_on_next] = route_to - end + if updater.errors.empty? + if final_step + updater.result[:redirect_on_complete] = route_to || data['redirect_on_complete'] + elsif route_to + updater.result[:redirect_on_next] = route_to end end end diff --git a/lib/custom_wizard/engine.rb b/lib/custom_wizard/engine.rb new file mode 100644 index 00000000..80681bee --- /dev/null +++ b/lib/custom_wizard/engine.rb @@ -0,0 +1,6 @@ +module ::CustomWizard + class Engine < ::Rails::Engine + engine_name 'custom_wizard' + isolate_namespace CustomWizard + end +end \ No newline at end of file diff --git a/lib/field.rb b/lib/custom_wizard/field.rb similarity index 100% rename from lib/field.rb rename to lib/custom_wizard/field.rb diff --git a/lib/flags.rb b/lib/custom_wizard/flags.rb similarity index 100% rename from lib/flags.rb rename to lib/custom_wizard/flags.rb diff --git a/lib/step_updater.rb b/lib/custom_wizard/step_updater.rb similarity index 100% rename from lib/step_updater.rb rename to lib/custom_wizard/step_updater.rb diff --git a/lib/template.rb b/lib/custom_wizard/template.rb similarity index 100% rename from lib/template.rb rename to lib/custom_wizard/template.rb diff --git a/lib/wizard.rb b/lib/custom_wizard/wizard.rb similarity index 99% rename from lib/wizard.rb rename to lib/custom_wizard/wizard.rb index 8fe8a690..b8d0f7af 100644 --- a/lib/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -3,6 +3,8 @@ require_dependency 'wizard/field' require_dependency 'wizard/step_updater' require_dependency 'wizard/builder' +UserHistory.actions[:custom_wizard_step] = 1000 + class CustomWizard::Wizard attr_reader :steps, :user diff --git a/lib/wizard_edits.rb b/lib/wizard_edits.rb deleted file mode 100644 index 9c834808..00000000 --- a/lib/wizard_edits.rb +++ /dev/null @@ -1,227 +0,0 @@ -require_dependency 'wizard' -require_dependency 'wizard/field' -require_dependency 'wizard/step' - -::Wizard.class_eval do - def self.user_requires_completion?(user) - wizard_result = self.new(user).requires_completion? - return wizard_result if wizard_result - - custom_redirect = false - - if user && user.first_seen_at.blank? && wizard_id = CustomWizard::Wizard.after_signup - wizard = CustomWizard::Wizard.create(user, wizard_id) - - if !wizard.completed? && wizard.permitted? - custom_redirect = true - CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) - end - end - - !!custom_redirect - end -end - -::Wizard::Field.class_eval do - attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property - attr_accessor :dropdown_none - - def initialize(attrs) - @attrs = attrs || {} - @id = attrs[:id] - @type = attrs[:type] - @required = !!attrs[:required] - @description = attrs[:description] - @image = attrs[:image] - @key = attrs[:key] - @min_length = attrs[:min_length] - @value = attrs[:value] - @choices = [] - @dropdown_none = attrs[:dropdown_none] - @file_types = attrs[:file_types] - @limit = attrs[:limit] - @property = attrs[:property] - end - - def label - @label ||= PrettyText.cook(@attrs[:label]) - end -end - -::Wizard::Choice.class_eval do - def initialize(id, opts) - @id = id - @opts = opts - @data = opts[:data] - @extra_label = opts[:extra_label] - @icon = opts[:icon] - end - - def label - @label ||= PrettyText.cook(@opts[:label]) - end -end - -class ::Wizard::Step - attr_accessor :title, :description, :key, :permitted, :permitted_message -end - -::WizardSerializer.class_eval do - attributes :id, - :name, - :background, - :completed, - :required, - :min_trust, - :permitted, - :user, - :categories, - :uncategorized_category_id - - def id - object.id - end - - def include_id? - object.respond_to?(:id) - end - - def name - object.name - end - - def include_name? - object.respond_to?(:name) - end - - def background - object.background - end - - def include_background? - object.respond_to?(:background) - end - - def completed - object.completed? - end - - def include_completed? - object.completed? && - (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && - !scope.is_admin? - end - - def min_trust - object.min_trust - end - - def include_min_trust? - object.respond_to?(:min_trust) - end - - def permitted - object.permitted? - end - - def include_permitted? - object.respond_to?(:permitted?) - end - - def include_start? - object.start && include_steps? - end - - def include_steps? - !include_completed? - end - - def required - object.required - end - - def include_required? - object.respond_to?(:required) - end - - def user - object.user - end - - def categories - begin - site = ::Site.new(scope) - ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) - rescue => e - puts "HERE IS THE ERROR: #{e.inspect}" - end - end - - def uncategorized_category_id - SiteSetting.uncategorized_category_id - end -end - -::WizardStepSerializer.class_eval do - attributes :permitted, :permitted_message - - def title - return PrettyText.cook(object.title) if object.title - PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) - end - - def description - return object.description if object.description - PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)) - end - - def permitted - object.permitted - end - - def permitted_message - object.permitted_message - end -end - -::WizardFieldSerializer.class_eval do - attributes :dropdown_none, :image, :file_types, :limit, :property - - def label - return object.label if object.label.present? - I18n.t("#{object.key || i18n_key}.label", default: '') - end - - def description - return object.description if object.description.present? - I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url) - end - - def image - object.image - end - - def include_image? - object.image.present? - end - - def placeholder - I18n.t("#{object.key || i18n_key}.placeholder", default: '') - end - - def dropdown_none - object.dropdown_none - end - - def file_types - object.file_types - end - - def limit - object.limit - end - - def property - object.property - end -end diff --git a/plugin.rb b/plugin.rb index 659720b5..a02013e0 100644 --- a/plugin.rb +++ b/plugin.rb @@ -8,11 +8,12 @@ register_asset 'stylesheets/wizard_custom_admin.scss' register_asset 'lib/jquery.timepicker.min.js' register_asset 'lib/jquery.timepicker.scss' +enabled_site_setting :custom_wizard_enabled + config = Rails.application.config config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts') config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard') - if Rails.env.production? config.assets.precompile += %w{ wizard-custom-guest.js @@ -35,79 +36,260 @@ if respond_to?(:register_svg_icon) end after_initialize do - UserHistory.actions[:custom_wizard_step] = 1000 + [ + '../lib/custom_wizard/engine.rb', + '../config/routes.rb', + '../controllers/custom_wizard/wizard.rb', + '../controllers/custom_wizard/steps.rb', + '../controllers/custom_wizard/admin.rb', + '../controllers/custom_wizard/transfer.rb', + '../controllers/custom_wizard/api.rb', + '../jobs/clear_after_time_wizard.rb', + '../jobs/refresh_api_access_token.rb', + '../jobs/set_after_time_wizard.rb', + '../lib/custom_wizard/builder.rb', + '../lib/custom_wizard/field.rb', + '../lib/custom_wizard/flags.rb', + '../lib/custom_wizard/step_updater.rb', + '../lib/custom_wizard/template.rb', + '../lib/custom_wizard/wizard.rb', + '../lib/custom_wizard/api/api.rb', + '../lib/custom_wizard/api/authorization.rb', + '../lib/custom_wizard/api/endpoint.rb', + '../lib/custom_wizard/api/log_entry.rb', + '../serializers/custom_wizard/api_serializer.rb', + '../serializers/custom_wizard/basic_api_serializer.rb', + '../serializers/custom_wizard/api/authorization_serializer.rb', + '../serializers/custom_wizard/api/basic_endpoint_serializer.rb', + '../serializers/custom_wizard/api/endpoint_serializer.rb', + '../serializers/custom_wizard/api/log_serializer.rb' + ].each do |path| + load File.expand_path(path, __FILE__) + end + + ::Wizard.class_eval do + def self.user_requires_completion?(user) + wizard_result = self.new(user).requires_completion? + return wizard_result if wizard_result - module ::CustomWizard - class Engine < ::Rails::Engine - engine_name 'custom_wizard' - isolate_namespace CustomWizard + custom_redirect = false + + if user && user.first_seen_at.blank? && wizard_id = CustomWizard::Wizard.after_signup + wizard = CustomWizard::Wizard.create(user, wizard_id) + + if !wizard.completed? && wizard.permitted? + custom_redirect = true + CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) + end + end + + !!custom_redirect end end - CustomWizard::Engine.routes.draw do - get ':wizard_id' => 'wizard#index' - put ':wizard_id/skip' => 'wizard#skip' - get ':wizard_id/steps' => 'wizard#index' - get ':wizard_id/steps/:step_id' => 'wizard#index' - put ':wizard_id/steps/:step_id' => 'steps#update' - end + ::Wizard::Field.class_eval do + attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property + attr_accessor :dropdown_none - Discourse::Application.routes.append do - mount ::CustomWizard::Engine, at: 'w' - post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" + def initialize(attrs) + @attrs = attrs || {} + @id = attrs[:id] + @type = attrs[:type] + @required = !!attrs[:required] + @description = attrs[:description] + @image = attrs[:image] + @key = attrs[:key] + @min_length = attrs[:min_length] + @value = attrs[:value] + @choices = [] + @dropdown_none = attrs[:dropdown_none] + @file_types = attrs[:file_types] + @limit = attrs[:limit] + @property = attrs[:property] + end - scope module: 'custom_wizard', constraints: AdminConstraint.new do - get 'admin/wizards' => 'admin#index' - get 'admin/wizards/field-types' => 'admin#field_types' - get 'admin/wizards/custom' => 'admin#index' - get 'admin/wizards/custom/new' => 'admin#index' - get 'admin/wizards/custom/all' => 'admin#custom_wizards' - get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' - put 'admin/wizards/custom/save' => 'admin#save' - delete 'admin/wizards/custom/remove' => 'admin#remove' - get 'admin/wizards/submissions' => 'admin#index' - get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' - get 'admin/wizards/apis' => 'api#list' - get 'admin/wizards/apis/new' => 'api#index' - get 'admin/wizards/apis/:name' => 'api#find' - put 'admin/wizards/apis/:name' => 'api#save' - delete 'admin/wizards/apis/:name' => 'api#remove' - delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' - get 'admin/wizards/apis/:name/redirect' => 'api#redirect' - get 'admin/wizards/apis/:name/authorize' => 'api#authorize' - get 'admin/wizards/transfer' => 'transfer#index' - get 'admin/wizards/transfer/export' => 'transfer#export' - post 'admin/wizards/transfer/import' => 'transfer#import' + def label + @label ||= PrettyText.cook(@attrs[:label]) end end - load File.expand_path('../jobs/clear_after_time_wizard.rb', __FILE__) - load File.expand_path('../jobs/set_after_time_wizard.rb', __FILE__) - load File.expand_path('../lib/builder.rb', __FILE__) - load File.expand_path('../lib/field.rb', __FILE__) - load File.expand_path('../lib/flags.rb', __FILE__) - load File.expand_path('../lib/step_updater.rb', __FILE__) - load File.expand_path('../lib/template.rb', __FILE__) - load File.expand_path('../lib/wizard.rb', __FILE__) - load File.expand_path('../lib/wizard_edits.rb', __FILE__) - load File.expand_path('../controllers/wizard.rb', __FILE__) - load File.expand_path('../controllers/steps.rb', __FILE__) - load File.expand_path('../controllers/admin.rb', __FILE__) - #transfer code - load File.expand_path('../controllers/transfer.rb', __FILE__) + ::Wizard::Choice.class_eval do + def initialize(id, opts) + @id = id + @opts = opts + @data = opts[:data] + @extra_label = opts[:extra_label] + @icon = opts[:icon] + end - load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__) - load File.expand_path('../lib/api/api.rb', __FILE__) - load File.expand_path('../lib/api/authorization.rb', __FILE__) - load File.expand_path('../lib/api/endpoint.rb', __FILE__) - load File.expand_path('../lib/api/log_entry.rb', __FILE__) - load File.expand_path('../controllers/api.rb', __FILE__) - load File.expand_path('../serializers/api/api_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/basic_api_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/endpoint_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/basic_endpoint_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/log_serializer.rb', __FILE__) + def label + @label ||= PrettyText.cook(@opts[:label]) + end + end + + class ::Wizard::Step + attr_accessor :title, :description, :key, :permitted, :permitted_message + end + + ::WizardSerializer.class_eval do + attributes :id, + :name, + :background, + :completed, + :required, + :min_trust, + :permitted, + :user, + :categories, + :uncategorized_category_id + + def id + object.id + end + + def include_id? + object.respond_to?(:id) + end + + def name + object.name + end + + def include_name? + object.respond_to?(:name) + end + + def background + object.background + end + + def include_background? + object.respond_to?(:background) + end + + def completed + object.completed? + end + + def include_completed? + object.completed? && + (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && + !scope.is_admin? + end + + def min_trust + object.min_trust + end + + def include_min_trust? + object.respond_to?(:min_trust) + end + + def permitted + object.permitted? + end + + def include_permitted? + object.respond_to?(:permitted?) + end + + def include_start? + object.start && include_steps? + end + + def include_steps? + !include_completed? + end + + def required + object.required + end + + def include_required? + object.respond_to?(:required) + end + + def user + object.user + end + + def categories + begin + site = ::Site.new(scope) + ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) + rescue => e + puts "HERE IS THE ERROR: #{e.inspect}" + end + end + + def uncategorized_category_id + SiteSetting.uncategorized_category_id + end + end + + ::WizardStepSerializer.class_eval do + attributes :permitted, :permitted_message + + def title + return PrettyText.cook(object.title) if object.title + PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) + end + + def description + return object.description if object.description + PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)) + end + + def permitted + object.permitted + end + + def permitted_message + object.permitted_message + end + end + + ::WizardFieldSerializer.class_eval do + attributes :dropdown_none, :image, :file_types, :limit, :property + + def label + return object.label if object.label.present? + I18n.t("#{object.key || i18n_key}.label", default: '') + end + + def description + return object.description if object.description.present? + I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url) + end + + def image + object.image + end + + def include_image? + object.image.present? + end + + def placeholder + I18n.t("#{object.key || i18n_key}.placeholder", default: '') + end + + def dropdown_none + object.dropdown_none + end + + def file_types + object.file_types + end + + def limit + object.limit + end + + def property + object.property + end + end ::UsersController.class_eval do def wizard_path diff --git a/serializers/api/authorization_serializer.rb b/serializers/custom_wizard/api/authorization_serializer.rb similarity index 100% rename from serializers/api/authorization_serializer.rb rename to serializers/custom_wizard/api/authorization_serializer.rb diff --git a/serializers/api/basic_endpoint_serializer.rb b/serializers/custom_wizard/api/basic_endpoint_serializer.rb similarity index 100% rename from serializers/api/basic_endpoint_serializer.rb rename to serializers/custom_wizard/api/basic_endpoint_serializer.rb diff --git a/serializers/api/endpoint_serializer.rb b/serializers/custom_wizard/api/endpoint_serializer.rb similarity index 100% rename from serializers/api/endpoint_serializer.rb rename to serializers/custom_wizard/api/endpoint_serializer.rb diff --git a/serializers/api/log_serializer.rb b/serializers/custom_wizard/api/log_serializer.rb similarity index 100% rename from serializers/api/log_serializer.rb rename to serializers/custom_wizard/api/log_serializer.rb diff --git a/serializers/api/api_serializer.rb b/serializers/custom_wizard/api_serializer.rb similarity index 100% rename from serializers/api/api_serializer.rb rename to serializers/custom_wizard/api_serializer.rb diff --git a/serializers/api/basic_api_serializer.rb b/serializers/custom_wizard/basic_api_serializer.rb similarity index 100% rename from serializers/api/basic_api_serializer.rb rename to serializers/custom_wizard/basic_api_serializer.rb diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index ba781cb9..5d0a8c73 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -3,156 +3,179 @@ require 'rails_helper' describe CustomWizard::Builder do - it "returns a wizard when enabled" do - ## implement enabled site setting first + fab!(:user) { Fabricate(:user) } + + before do + @template = File.open( + "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" + ).read end - it "returns nothing when disabled" do - ## implement enabled site setting first + it "returns no steps when disabled" do + CustomWizard::Wizard.add_wizard(@template) + SiteSetting.custom_wizard_enabled = false + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(0) + expect(wizard.name).to eq('Welcome') end - it 'returns a wizard with prefilled data if user has partially completed' do - - end - - it 'returns a wiard with no prefilled data if options include reset' do - - end - - it 'returns nothing if the multiple submissions are disabled and user has completed' do + context 'enabled' do + before do + SiteSetting.custom_wizard_enabled = true + end - end - - it 'returns nothing if the user is not permitted to see it' do - - end - - context 'building steps' do - it 'returns step data correctly' do + it "returns steps when enabled" do + CustomWizard::Wizard.add_wizard(@template) + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(2) + end + + it 'returns no steps if the multiple submissions are disabled and user has completed' do + @template['multiple_submissions'] = false + CustomWizard::Wizard.add_wizard(@template) + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(0) + end + + it 'returns nothing if the user is not permitted to see it' do + + end + + it 'returns a wizard with prefilled data if user has partially completed' do + + end + + it 'returns a wizard with no prefilled data if options include reset' do + + end + + context 'building steps' do + it 'returns step data correctly' do + + end - end - - it 'saves permitted params' do - - end - - it 'ensures required data is present' do - - end - end - - context 'building fields' do - it 'returns field data correctly' do - - end - - it 'returns checkbox fields correctly' do - - end - - it 'returns upload fields correctly' do - - end - - it 'returns category fields correctly' do - - end - - it 'returns tag fields correctly' do - - end - - it 'returns custom dropdown fields correctly' do - - end - - it 'returns translated dropdown fields correctly' do - - end - - it 'returns preset dropdown fields correctly' do - - end - - it 'applies preset dropdown filters correctly' do - - end - - it 'prefils profile data correctly' do - - end - end - - context 'on update' do - context 'validation' do - it 'applies min length correctly' do + it 'saves permitted params' do end - it 'standardises boolean entries' do + it 'ensures required data is present' do + + end + end + + context 'building fields' do + it 'returns field data correctly' do end - it 'requires required fields' do - ## this may require additional work? + it 'returns checkbox fields correctly' do + end - context 'submisisons' do - it 'saves submissions' do + it 'returns upload fields correctly' do + + end + + it 'returns category fields correctly' do + + end + + it 'returns tag fields correctly' do + + end + + it 'returns custom dropdown fields correctly' do + + end + + it 'returns translated dropdown fields correctly' do + + end + + it 'returns preset dropdown fields correctly' do + + end + + it 'applies preset dropdown filters correctly' do + + end + + it 'prefils profile data correctly' do + + end + end + + context 'on update' do + context 'validation' do + it 'applies min length correctly' do end - it "doesn't save submissions if disabled" do + it 'standardises boolean entries' do + + end + + it 'requires required fields' do + ## this may require additional work? + end + + context 'submisisons' do + it 'saves submissions' do + + end + + it "doesn't save submissions if disabled" do + + end + end + end + + context 'custom_step_handlers' do + it 'runs custom step handlers' do end end - end - - context 'custom_step_handlers' do - it 'runs custom step handlers' do - end - end - - context 'actions' do - it 'runs all actions attached to a step' do + context 'actions' do + it 'runs all actions attached to a step' do + + end - end - - it 'interpolates wizard and user data correctly' do - - end - - it 'creates a topic' do - - end - - it 'sends a message' do - - end - - it 'doesnt sent a message if the required data is not present' do - - end - - it 'updates a profile' do - - end - - it 'calls an api' do - - end - - it 'opens a composer' do - - end - - it 'adds a user to a group' do - - end - - it 're-routes a user' do + it 'interpolates wizard and user data correctly' do + end + + it 'creates a topic' do + + end + + it 'sends a message' do + + end + + it 'doesnt sent a message if the required data is not present' do + + end + + it 'updates a profile' do + + end + + it 'calls an api' do + + end + + it 'opens a composer' do + + end + + it 'adds a user to a group' do + + end + + it 're-routes a user' do + + end end end end diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json new file mode 100644 index 00000000..e384427b --- /dev/null +++ b/spec/fixtures/wizard.json @@ -0,0 +1,52 @@ +{ + "id": "welcome", + "name": "Welcome", + "background": "#006da3", + "save_submissions": true, + "multiple_submissions": true, + "after_signup": true, + "theme_id": 4, + "steps": [ + { + "id": "step_1", + "title": "Welcome to Pavilion", + "raw_description": "Hey there, thanks for signing up.\n\nWe're Pavilion, an international freelancer cooperative that specialises in online communities.\n\nThis site is our own community, where we work with our clients, users of our open source work and our broader community.\n\n", + "description": "

Hey there, thanks for signing up.

\n

We’re Pavilion, an international freelancer cooperative that specialises in online communities.

\n

This site is our own community, where we work with our clients, users of our open source work and our broader community.

" + }, + { + "id": "step_2", + "title": "Tell us about you", + "raw_description": "We'd like to know a little more about you. Add a your name and your website below. This will update your user profile.", + "fields": [ + { + "id": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "id": "field_3", + "label": "Website", + "type": "text" + } + ], + "actions": [ + { + "id": "update_profile", + "type": "update_profile", + "profile_updates": [ + { + "key": "name", + "value": "name" + }, + { + "key": "field_3", + "value": "website" + } + ] + } + ], + "description": "

We’d like to know a little more about you. Add a your name and your website below. This will update your user profile.

" + } + ] +} \ No newline at end of file