0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 17:30:29 +01:00

Add enabled setting && tests && file restructure

Dieser Commit ist enthalten in:
Angus McLeod 2019-12-05 17:48:32 +11:00
Ursprung bbfc5abae1
Commit f1fdc37a21
31 geänderte Dateien mit 597 neuen und 510 gelöschten Zeilen

Datei anzeigen

@ -3,7 +3,11 @@ import DiscourseURL from 'discourse/lib/url';
export default { export default {
name: 'custom-wizard-edits', 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 => { withPluginApi('0.8.12', api => {
api.modifyClass('component:global-notice', { api.modifyClass('component:global-notice', {
buildBuffer(buffer) { buildBuffer(buffer) {

Datei anzeigen

@ -6,8 +6,9 @@ export default {
initialize: function (container) { initialize: function (container) {
const messageBus = container.lookup('message-bus:main'); const messageBus = container.lookup('message-bus:main');
const siteSettings = container.lookup('site-settings:main');
if (!messageBus) { return; } if (!siteSettings.custom_wizard_enabled || !messageBus) return;
messageBus.subscribe("/redirect_to_wizard", function (wizardId) { messageBus.subscribe("/redirect_to_wizard", function (wizardId) {
const wizardUrl = window.location.origin + '/w/' + wizardId; const wizardUrl = window.location.origin + '/w/' + wizardId;

Datei anzeigen

@ -21,6 +21,7 @@ en:
no_valid_wizards: "File doesn't contain any valid wizards" no_valid_wizards: "File doesn't contain any valid wizards"
site_settings: site_settings:
custom_wizard_enabled: "Enable custom wizards."
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." 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_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" wizard_national_flags_label_cue: "The characters that must appear in the dropdown field label for it to show a flag"

36
config/routes.rb Normale Datei
Datei anzeigen

@ -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

Datei anzeigen

@ -1,4 +1,5 @@
plugins: plugins:
custom_wizard_enabled: true
wizard_redirect_exclude_paths: wizard_redirect_exclude_paths:
client: true client: true
type: list type: list

Datei anzeigen

@ -77,7 +77,14 @@ class CustomWizard::Builder
end end
def build(build_opts = {}, params = {}) def build(build_opts = {}, params = {})
unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted?
return @wizard if !SiteSetting.custom_wizard_enabled ||
(!@wizard.multiple_submissions &&
@wizard.completed? &&
!@wizard.user.admin) ||
!@steps ||
!@wizard.permitted?
reset_submissions if build_opts[:reset] reset_submissions if build_opts[:reset]
@steps.each do |step_template| @steps.each do |step_template|
@ -187,7 +194,6 @@ class CustomWizard::Builder
end end
end end
end end
end
@wizard @wizard
end end

6
lib/custom_wizard/engine.rb Normale Datei
Datei anzeigen

@ -0,0 +1,6 @@
module ::CustomWizard
class Engine < ::Rails::Engine
engine_name 'custom_wizard'
isolate_namespace CustomWizard
end
end

Datei anzeigen

@ -3,6 +3,8 @@ require_dependency 'wizard/field'
require_dependency 'wizard/step_updater' require_dependency 'wizard/step_updater'
require_dependency 'wizard/builder' require_dependency 'wizard/builder'
UserHistory.actions[:custom_wizard_step] = 1000
class CustomWizard::Wizard class CustomWizard::Wizard
attr_reader :steps, :user attr_reader :steps, :user

Datei anzeigen

@ -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

314
plugin.rb
Datei anzeigen

@ -8,11 +8,12 @@ register_asset 'stylesheets/wizard_custom_admin.scss'
register_asset 'lib/jquery.timepicker.min.js' register_asset 'lib/jquery.timepicker.min.js'
register_asset 'lib/jquery.timepicker.scss' register_asset 'lib/jquery.timepicker.scss'
enabled_site_setting :custom_wizard_enabled
config = Rails.application.config 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', 'javascripts')
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard') config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard')
if Rails.env.production? if Rails.env.production?
config.assets.precompile += %w{ config.assets.precompile += %w{
wizard-custom-guest.js wizard-custom-guest.js
@ -35,79 +36,260 @@ if respond_to?(:register_svg_icon)
end end
after_initialize do 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
module ::CustomWizard ::Wizard.class_eval do
class Engine < ::Rails::Engine def self.user_requires_completion?(user)
engine_name 'custom_wizard' wizard_result = self.new(user).requires_completion?
isolate_namespace CustomWizard 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
end end
CustomWizard::Engine.routes.draw do !!custom_redirect
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
end end
load File.expand_path('../jobs/clear_after_time_wizard.rb', __FILE__) ::Wizard::Field.class_eval do
load File.expand_path('../jobs/set_after_time_wizard.rb', __FILE__) attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property
load File.expand_path('../lib/builder.rb', __FILE__) attr_accessor :dropdown_none
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__)
load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__) def initialize(attrs)
load File.expand_path('../lib/api/api.rb', __FILE__) @attrs = attrs || {}
load File.expand_path('../lib/api/authorization.rb', __FILE__) @id = attrs[:id]
load File.expand_path('../lib/api/endpoint.rb', __FILE__) @type = attrs[:type]
load File.expand_path('../lib/api/log_entry.rb', __FILE__) @required = !!attrs[:required]
load File.expand_path('../controllers/api.rb', __FILE__) @description = attrs[:description]
load File.expand_path('../serializers/api/api_serializer.rb', __FILE__) @image = attrs[:image]
load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__) @key = attrs[:key]
load File.expand_path('../serializers/api/basic_api_serializer.rb', __FILE__) @min_length = attrs[:min_length]
load File.expand_path('../serializers/api/endpoint_serializer.rb', __FILE__) @value = attrs[:value]
load File.expand_path('../serializers/api/basic_endpoint_serializer.rb', __FILE__) @choices = []
load File.expand_path('../serializers/api/log_serializer.rb', __FILE__) @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
::UsersController.class_eval do ::UsersController.class_eval do
def wizard_path def wizard_path

Datei anzeigen

@ -3,27 +3,49 @@
require 'rails_helper' require 'rails_helper'
describe CustomWizard::Builder do describe CustomWizard::Builder do
it "returns a wizard when enabled" do fab!(:user) { Fabricate(:user) }
## implement enabled site setting first
before do
@template = File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read
end end
it "returns nothing when disabled" do it "returns no steps when disabled" do
## implement enabled site setting first 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
context 'enabled' do
before do
SiteSetting.custom_wizard_enabled = true
end
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 end
it 'returns a wizard with prefilled data if user has partially completed' do it 'returns a wizard with prefilled data if user has partially completed' do
end end
it 'returns a wiard with no prefilled data if options include reset' do it 'returns a wizard with no prefilled data if options include reset' do
end
it 'returns nothing if the multiple submissions are disabled and user has completed' do
end
it 'returns nothing if the user is not permitted to see it' do
end end
@ -156,4 +178,5 @@ describe CustomWizard::Builder do
end end
end end
end end
end
end end

52
spec/fixtures/wizard.json gevendort Normale Datei
Datei anzeigen

@ -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": "<p>Hey there, thanks for signing up.</p>\n<p>Were Pavilion, an international freelancer cooperative that specialises in online communities.</p>\n<p>This site is our own community, where we work with our clients, users of our open source work and our broader community.</p>"
},
{
"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": "<p>Wed like to know a little more about you. Add a your name and your website below. This will update your user profile.</p>"
}
]
}