diff --git a/.github/workflows/plugin-linting.yml b/.github/workflows/plugin-linting.yml
index a915ea7f..acb85230 100644
--- a/.github/workflows/plugin-linting.yml
+++ b/.github/workflows/plugin-linting.yml
@@ -3,54 +3,52 @@ name: Linting
on:
push:
branches:
- - master
- main
- stable
pull_request:
- schedule:
- - cron: '0 0 * * *'
+
+concurrency:
+ group: plugin-linting-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
+ cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Node.js
- uses: actions/setup-node@v1
+ uses: actions/setup-node@v3
with:
- node-version: 14
+ node-version: 16
+ cache: yarn
+
+ - name: Yarn install
+ run: yarn install
- name: Set up ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
-
- - name: Setup bundler
- run: gem install bundler -v 2.1.4 --no-doc
-
- - name: Setup gems
- run: bundle install --jobs 4
-
- - name: Yarn install
- run: yarn install --dev
+ bundler-cache: true
- name: ESLint
+ if: ${{ always() }}
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
- name: Prettier
+ if: ${{ always() }}
+ shell: bash
run: |
yarn prettier -v
- if [ -d "assets" ]; then \
- yarn prettier --list-different "assets/**/*.{scss,js,es6}" ; \
+ if [ 0 -lt $(find assets -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
+ yarn prettier --list-different "assets/**/*.{scss,js,es6}"
fi
- if [ -d "test" ]; then \
- yarn prettier --list-different "test/**/*.{js,es6}" ; \
+ if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
+ yarn prettier --list-different "test/**/*.{js,es6}"
fi
- - name: Ember template lint
- run: yarn ember-template-lint assets/javascripts
-
- name: Rubocop
- run: bundle exec rubocop .
\ No newline at end of file
+ if: ${{ always() }}
+ run: bundle exec rubocop .
diff --git a/.github/workflows/plugin-metadata.yml b/.github/workflows/plugin-metadata.yml
index c5e3caff..42069470 100644
--- a/.github/workflows/plugin-metadata.yml
+++ b/.github/workflows/plugin-metadata.yml
@@ -36,9 +36,9 @@ jobs:
uses: actions/github-script@v5
with:
script: |
- const semver = require('semver');
- const { head_version, base_version } = process.env;
+ const semver = require('semver');
+ const { head_version, base_version } = process.env;
- if (semver.lte(head_version, base_version)) {
- core.setFailed("Head version is equal to or lower than base version.");
- }
+ if (semver.lte(head_version, base_version)) {
+ core.setFailed("Head version is equal to or lower than base version.");
+ }
diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml
index aec4fa6c..f58f1b64 100644
--- a/.github/workflows/plugin-tests.yml
+++ b/.github/workflows/plugin-tests.yml
@@ -3,24 +3,25 @@ name: Plugin Tests
on:
push:
branches:
- - stable
- - master
- main
+ - stable
pull_request:
- schedule:
- - cron: '0 */12 * * *'
+
+concurrency:
+ group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
+ cancel-in-progress: true
jobs:
build:
name: ${{ matrix.build_type }}
runs-on: ubuntu-latest
- timeout-minutes: 60
+ container: discourse/discourse_test:slim${{ startsWith(matrix.build_type, 'frontend') && '-browsers' || '' }}
+ timeout-minutes: 30
env:
DISCOURSE_HOSTNAME: www.example.com
RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072
RAILS_ENV: test
- PGHOST: localhost
PGUSER: discourse
PGPASSWORD: discourse
@@ -29,121 +30,107 @@ jobs:
matrix:
build_type: ["backend", "frontend"]
- ruby: ["2.7"]
- postgres: ["12"]
- redis: ["6.x"]
-
- services:
- postgres:
- image: postgres:${{ matrix.postgres }}
- ports:
- - 5432:5432
- env:
- POSTGRES_USER: discourse
- POSTGRES_PASSWORD: discourse
- options: >-
- --mount type=tmpfs,destination=/var/lib/postgresql/data
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
steps:
- - uses: haya14busa/action-cond@v1
- id: discourse_branch
- with:
- cond: ${{ github.base_ref == 'stable' }}
- if_true: "stable"
- if_false: "tests-passed"
-
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
repository: discourse/discourse
- ref: ${{ steps.discourse_branch.outputs.value }}
fetch-depth: 1
- - name: Fetch Repo Name
- id: repo-name
- run: echo "::set-output name=value::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')"
-
- name: Install plugin
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
- path: plugins/${{ steps.repo-name.outputs.value }}
- ref: "${{ github.base_ref }}"
+ path: plugins/${{ github.event.repository.name }}
fetch-depth: 1
- - name: Check spec existence
- id: check_spec
- uses: andstor/file-existence-action@v1
- with:
- files: "plugins/${{ steps.repo-name.outputs.value }}/spec"
-
- - name: Check qunit existence
- id: check_qunit
- uses: andstor/file-existence-action@v1
- with:
- files: "plugins/${{ steps.repo-name.outputs.value }}/test/javascripts"
-
- name: Setup Git
run: |
git config --global user.email "ci@ci.invalid"
git config --global user.name "Discourse CI"
- - name: Setup packages
+ - name: Start redis
run: |
- sudo apt-get update
- sudo apt-get -yqq install postgresql-client libpq-dev gifsicle jpegoptim optipng jhead
- wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-pngquant | sudo sh
+ redis-server /etc/redis/redis.conf &
- - name: Update imagemagick
- if: matrix.build_type == 'backend'
+ - name: Start Postgres
run: |
- wget https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-imagemagick
- chmod +x install-imagemagick
- sudo ./install-imagemagick
+ chown -R postgres /var/run/postgresql
+ sudo -E -u postgres script/start_test_db.rb
+ sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';"
- - name: Setup redis
- uses: shogo82148/actions-setup-redis@v1
+ - name: Bundler cache
+ uses: actions/cache@v3
with:
- redis-version: ${{ matrix.redis }}
+ path: vendor/bundle
+ key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-gem-
- - name: Setup ruby
- uses: ruby/setup-ruby@v1
- with:
- ruby-version: ${{ matrix.ruby }}
- bundler-cache: true
+ - name: Setup gems
+ run: |
+ gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock)
+ bundle config --local path vendor/bundle
+ bundle config --local deployment true
+ bundle config --local without development
+ bundle install --jobs 4
+ bundle clean
- name: Lint English locale
if: matrix.build_type == 'backend'
- run: bundle exec ruby script/i18n_lint.rb "plugins/${{ steps.repo-name.outputs.value }}/locales/{client,server}.en.yml"
+ run: bundle exec ruby script/i18n_lint.rb "plugins/${{ github.event.repository.name }}/locales/{client,server}.en.yml"
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
- key: ${{ runner.os }}-${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
- ${{ runner.os }}-${{ matrix.os }}-yarn-
+ ${{ runner.os }}-yarn-
- name: Yarn install
- run: yarn install --dev
+ run: yarn install
- - name: Migrate database
+ - name: Fetch app state cache
+ uses: actions/cache@v3
+ id: app-cache
+ with:
+ path: tmp/app-cache
+ key: >-
+ ${{ hashFiles('.github/workflows/tests.yml') }}-
+ ${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}-
+
+ - name: Restore database from cache
+ if: steps.app-cache.outputs.cache-hit == 'true'
+ run: psql -f tmp/app-cache/cache.sql postgres
+
+ - name: Restore uploads from cache
+ if: steps.app-cache.outputs.cache-hit == 'true'
+ run: rm -rf public/uploads && cp -r tmp/app-cache/uploads public/uploads
+
+ - name: Create and migrate database
+ if: steps.app-cache.outputs.cache-hit != 'true'
run: |
bin/rake db:create
bin/rake db:migrate
- - name: Plugin RSpec with Coverage
- if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true'
- run: SIMPLECOV=1 bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}]
+ - name: Dump database for cache
+ if: steps.app-cache.outputs.cache-hit != 'true'
+ run: mkdir -p tmp/app-cache && pg_dumpall > tmp/app-cache/cache.sql
+
+ - name: Dump uploads for cache
+ if: steps.app-cache.outputs.cache-hit != 'true'
+ run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads
+
+ - name: Plugin RSpec
+ if: matrix.build_type == 'backend'
+ run: bin/rake plugin:spec[${{ github.event.repository.name }}]
- name: Plugin QUnit
- if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true'
- run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000']
- timeout-minutes: 30
+ if: matrix.build_type == 'frontend'
+ run: QUNIT_EMBER_CLI=1 bundle exec rake plugin:qunit['${{ github.event.repository.name }}','1200000']
+ timeout-minutes: 10
diff --git a/.gitignore b/.gitignore
index 11ce0a3c..3da9ad01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
coverage/*
!coverage/.last_run.json
-gems/
+gems/*
.bundle/
auto_generated
.DS_Store
node_modules/
+vendor/*
diff --git a/.rubocop.yml b/.rubocop.yml
index d46296cf..69fcfc56 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,2 +1,8 @@
inherit_gem:
rubocop-discourse: default.yml
+
+RSpec/ContextWording:
+ Enabled: false
+
+RSpec/DescribeClass:
+ Enabled: false
diff --git a/.simplecov b/.simplecov
new file mode 100644
index 00000000..c7b6143b
--- /dev/null
+++ b/.simplecov
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+plugin = "discourse-custom-wizard"
+
+SimpleCov.configure do
+ track_files "plugins/#{plugin}/**/*.rb"
+ add_filter { |src| !(src.filename =~ /(\/#{plugin}\/app\/|\/#{plugin}\/lib\/)/) }
+end
diff --git a/Gemfile.lock b/Gemfile.lock
index 2416ce66..5ab57bb6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,31 +2,32 @@ GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
- parallel (1.20.1)
- parser (3.0.1.0)
+ json (2.6.2)
+ parallel (1.22.1)
+ parser (3.1.2.1)
ast (~> 2.4.1)
- rainbow (3.0.0)
- regexp_parser (2.1.1)
+ rainbow (3.1.1)
+ regexp_parser (2.6.0)
rexml (3.2.5)
- rubocop (1.12.1)
+ rubocop (1.36.0)
+ json (~> 2.3)
parallel (~> 1.10)
- parser (>= 3.0.0.0)
+ parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
- rexml
- rubocop-ast (>= 1.2.0, < 2.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.20.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.4.1)
- parser (>= 2.7.1.5)
- rubocop-discourse (2.4.1)
+ rubocop-ast (1.22.0)
+ parser (>= 3.1.1.0)
+ rubocop-discourse (3.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
- rubocop-rspec (2.2.0)
- rubocop (~> 1.0)
- rubocop-ast (>= 1.1.0)
+ rubocop-rspec (2.13.2)
+ rubocop (~> 1.33)
ruby-progressbar (1.11.0)
- unicode-display_width (2.0.0)
+ unicode-display_width (2.3.0)
PLATFORMS
ruby
diff --git a/README.md b/README.md
index 602709f5..0190f16e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,25 @@
-# discourse-custom-wizard
+# Discourse Custom Wizard Plugin
-See further: https://thepavilion.io/c/knowledge/discourse/custom-wizard/118
+The Custom Wizard Plugin lets you make forms for your Discourse forum. Better user onboarding, structured posting, data enrichment, automated actions and much more for your community.
+
+
+
+## Install
+
+If you're not sure how to install a plugin in Discourse, please follow the [plugin installation guide](https://meta.discourse.org/t/install-a-plugin/19157) or contact your Discourse hosting provider.
+
+## Documentation
+
+[Read the full documentation here](https://discourse.pluginmanager.org/c/discourse-custom-wizard/documentation), or go directly to the relevant section
+
+- [Wizard Administration](https://discourse.pluginmanager.org/t/wizard-administration)
+- [Wizard Settings](https://discourse.pluginmanager.org/t/wizard-settings)
+- [Step Settings](https://discourse.pluginmanager.org/t/step-settings)
+- [Field Settings](https://discourse.pluginmanager.org/t/field-settings)
+- [Conditional Settings](https://discourse.pluginmanager.org/t/conditional-settings)
+- [Field Interpolation](https://discourse.pluginmanager.org/t/field-interpolation)
+- [Wizard Examples and Templates](https://discourse.pluginmanager.org/t/wizard-examples-and-templates)
+
+## Support
+
+- [Report a bug](https://discourse.pluginmanager.org/w/bug-report)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..c799fa5a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,7 @@
+# Security Policy
+
+The security of Discourse plugins are premised on the security of [Discourse](https://github.com/discourse/discourse). Please first consider whether a security issue is best reported and handled by the Discourse team. You can view the Discourse security policy [here](https://github.com/discourse/discourse/security/policy).
+
+## Reporting a Vulnerability
+
+If you find a security vulnerability that is specific to this plugin, please report it to development@pavilion.tech. Security issues always take precedence over all other work. All commits specific to security are prefixed with SECURITY.
diff --git a/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb
similarity index 65%
rename from controllers/custom_wizard/admin/admin.rb
rename to app/controllers/custom_wizard/admin/admin.rb
index c99954d6..867be56c 100644
--- a/controllers/custom_wizard/admin/admin.rb
+++ b/app/controllers/custom_wizard/admin/admin.rb
@@ -3,6 +3,13 @@ class CustomWizard::AdminController < ::Admin::AdminController
before_action :ensure_admin
def index
+ subcription = CustomWizard::Subscription.new
+ render_json_dump(
+ subscribed: subcription.subscribed?,
+ subscription_type: subcription.type,
+ subscription_attributes: CustomWizard::Subscription.attributes,
+ subscription_client_installed: subcription.client_installed?
+ )
end
private
diff --git a/controllers/custom_wizard/admin/api.rb b/app/controllers/custom_wizard/admin/api.rb
similarity index 93%
rename from controllers/custom_wizard/admin/api.rb
rename to app/controllers/custom_wizard/admin/api.rb
index a913e9ca..e4ac31e9 100644
--- a/controllers/custom_wizard/admin/api.rb
+++ b/app/controllers/custom_wizard/admin/api.rb
@@ -20,6 +20,10 @@ class CustomWizard::AdminApiController < CustomWizard::AdminController
raise Discourse::InvalidParameters, "An API with that name already exists: '#{current.title || current.name}'"
end
+ unless subscription.includes?(:api, :all)
+ raise Discourse::InvalidParameters, "Your subscription doesn't include API features."
+ end
+
PluginStoreRow.transaction do
CustomWizard::Api.set(api_params[:name], title: api_params[:title])
@@ -130,4 +134,8 @@ class CustomWizard::AdminApiController < CustomWizard::AdminController
@auth_data ||= auth_data
end
+
+ def subscription
+ @subscription ||= CustomWizard::Subscription.new
+ end
end
diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/app/controllers/custom_wizard/admin/custom_fields.rb
similarity index 95%
rename from controllers/custom_wizard/admin/custom_fields.rb
rename to app/controllers/custom_wizard/admin/custom_fields.rb
index c52759c9..111e9faf 100644
--- a/controllers/custom_wizard/admin/custom_fields.rb
+++ b/app/controllers/custom_wizard/admin/custom_fields.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
def index
- render_json_dump(custom_field_list)
+ render_json_dump(
+ custom_fields: custom_field_list
+ )
end
def update
diff --git a/app/controllers/custom_wizard/admin/logs.rb b/app/controllers/custom_wizard/admin/logs.rb
new file mode 100644
index 00000000..7ca37bb2
--- /dev/null
+++ b/app/controllers/custom_wizard/admin/logs.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+class CustomWizard::AdminLogsController < CustomWizard::AdminController
+ before_action :find_wizard, except: [:index]
+
+ def index
+ render json: ActiveModel::ArraySerializer.new(
+ CustomWizard::Wizard.list(current_user),
+ each_serializer: CustomWizard::BasicWizardSerializer
+ )
+ end
+
+ def show
+ render_json_dump(
+ wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
+ logs: ActiveModel::ArraySerializer.new(
+ log_list.logs,
+ each_serializer: CustomWizard::LogSerializer
+ ),
+ total: log_list.total
+ )
+ end
+
+ protected
+
+ def log_list
+ @log_list ||= begin
+ list = CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i, params[:wizard_id])
+
+ if list.logs.any? && (usernames = list.logs.map(&:username)).present?
+ user_map = User.where(username: usernames)
+ .reduce({}) do |result, user|
+ result[user.username] = user
+ result
+ end
+
+ list.logs.each do |log_item|
+ log_item.user = user_map[log_item.username]
+ end
+ end
+
+ list
+ end
+ end
+end
diff --git a/controllers/custom_wizard/admin/manager.rb b/app/controllers/custom_wizard/admin/manager.rb
similarity index 100%
rename from controllers/custom_wizard/admin/manager.rb
rename to app/controllers/custom_wizard/admin/manager.rb
diff --git a/controllers/custom_wizard/admin/submissions.rb b/app/controllers/custom_wizard/admin/submissions.rb
similarity index 68%
rename from controllers/custom_wizard/admin/submissions.rb
rename to app/controllers/custom_wizard/admin/submissions.rb
index 4cb2a0e4..c3bf809f 100644
--- a/controllers/custom_wizard/admin/submissions.rb
+++ b/app/controllers/custom_wizard/admin/submissions.rb
@@ -13,12 +13,16 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
def show
render_json_dump(
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
- submissions: ActiveModel::ArraySerializer.new(ordered_submissions, each_serializer: CustomWizard::SubmissionSerializer)
+ submissions: ActiveModel::ArraySerializer.new(
+ submission_list.submissions,
+ each_serializer: CustomWizard::SubmissionSerializer
+ ),
+ total: submission_list.total
)
end
def download
- send_data ordered_submissions.to_json,
+ send_data submission_list.submissions.to_json,
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
content_type: "application/json",
disposition: "attachment"
@@ -26,7 +30,7 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
protected
- def ordered_submissions
- CustomWizard::Submission.list(@wizard, order_by: 'id')
+ def submission_list
+ CustomWizard::Submission.list(@wizard, page: params[:page].to_i)
end
end
diff --git a/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb
similarity index 95%
rename from controllers/custom_wizard/admin/wizard.rb
rename to app/controllers/custom_wizard/admin/wizard.rb
index 0a59e02b..08e7b6d0 100644
--- a/controllers/custom_wizard/admin/wizard.rb
+++ b/app/controllers/custom_wizard/admin/wizard.rb
@@ -88,6 +88,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:title,
:key,
:banner,
+ :banner_upload_id,
:raw_description,
:required_data_message,
:force_final,
@@ -99,6 +100,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:index,
:label,
:image,
+ :image_upload_id,
:description,
:required,
:key,
@@ -112,6 +114,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:property,
:preview_template,
:placeholder,
+ :can_create_tag,
prefill: mapped_params,
content: mapped_params,
condition: mapped_params,
@@ -161,7 +164,9 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
mentionable_level: mapped_params,
messageable_level: mapped_params,
visibility_level: mapped_params,
- members_visibility_level: mapped_params
+ members_visibility_level: mapped_params,
+ add_event: mapped_params,
+ add_location: mapped_params
]
)
end
diff --git a/controllers/custom_wizard/realtime_validations.rb b/app/controllers/custom_wizard/realtime_validations.rb
similarity index 100%
rename from controllers/custom_wizard/realtime_validations.rb
rename to app/controllers/custom_wizard/realtime_validations.rb
diff --git a/controllers/custom_wizard/steps.rb b/app/controllers/custom_wizard/steps.rb
similarity index 100%
rename from controllers/custom_wizard/steps.rb
rename to app/controllers/custom_wizard/steps.rb
diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb
new file mode 100644
index 00000000..7aafdd3b
--- /dev/null
+++ b/app/controllers/custom_wizard/wizard.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+class CustomWizard::WizardController < ::ApplicationController
+ before_action :ensure_plugin_enabled
+ before_action :ensure_logged_in, only: [:skip]
+
+ def show
+ if wizard.present?
+ render json: CustomWizard::WizardSerializer.new(wizard, scope: guardian, root: false).as_json, status: 200
+ else
+ render json: { error: I18n.t('wizard.none') }
+ end
+ end
+
+ def skip
+ params.require(:wizard_id)
+
+ if wizard.required && !wizard.completed? && wizard.permitted?
+ return render json: { error: I18n.t('wizard.no_skip') }
+ end
+
+ result = { success: 'OK' }
+
+ if current_user && wizard.can_access?
+ if redirect_to = wizard.current_submission&.redirect_to
+ result.merge!(redirect_to: redirect_to)
+ end
+
+ wizard.cleanup_on_skip!
+ end
+
+ render json: result
+ end
+
+ protected
+
+ def wizard
+ @wizard ||= begin
+ builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user)
+ return nil unless builder.present?
+ opts = {}
+ opts[:reset] = params[:reset]
+ builder.build(opts, params)
+ end
+ end
+
+ private
+
+ def ensure_plugin_enabled
+ unless SiteSetting.custom_wizard_enabled
+ redirect_to path("/")
+ end
+ end
+end
diff --git a/jobs/refresh_api_access_token.rb b/app/jobs/regular/refresh_api_access_token.rb
similarity index 100%
rename from jobs/refresh_api_access_token.rb
rename to app/jobs/regular/refresh_api_access_token.rb
diff --git a/jobs/set_after_time_wizard.rb b/app/jobs/regular/set_after_time_wizard.rb
similarity index 100%
rename from jobs/set_after_time_wizard.rb
rename to app/jobs/regular/set_after_time_wizard.rb
diff --git a/serializers/custom_wizard/api/authorization_serializer.rb b/app/serializers/custom_wizard/api/authorization_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/api/authorization_serializer.rb
rename to app/serializers/custom_wizard/api/authorization_serializer.rb
diff --git a/serializers/custom_wizard/api/basic_endpoint_serializer.rb b/app/serializers/custom_wizard/api/basic_endpoint_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/api/basic_endpoint_serializer.rb
rename to app/serializers/custom_wizard/api/basic_endpoint_serializer.rb
diff --git a/serializers/custom_wizard/api/endpoint_serializer.rb b/app/serializers/custom_wizard/api/endpoint_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/api/endpoint_serializer.rb
rename to app/serializers/custom_wizard/api/endpoint_serializer.rb
diff --git a/serializers/custom_wizard/api/log_serializer.rb b/app/serializers/custom_wizard/api/log_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/api/log_serializer.rb
rename to app/serializers/custom_wizard/api/log_serializer.rb
diff --git a/serializers/custom_wizard/api_serializer.rb b/app/serializers/custom_wizard/api_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/api_serializer.rb
rename to app/serializers/custom_wizard/api_serializer.rb
diff --git a/serializers/custom_wizard/basic_api_serializer.rb b/app/serializers/custom_wizard/basic_api_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/basic_api_serializer.rb
rename to app/serializers/custom_wizard/basic_api_serializer.rb
diff --git a/serializers/custom_wizard/basic_wizard_serializer.rb b/app/serializers/custom_wizard/basic_wizard_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/basic_wizard_serializer.rb
rename to app/serializers/custom_wizard/basic_wizard_serializer.rb
diff --git a/serializers/custom_wizard/custom_field_serializer.rb b/app/serializers/custom_wizard/custom_field_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/custom_field_serializer.rb
rename to app/serializers/custom_wizard/custom_field_serializer.rb
diff --git a/app/serializers/custom_wizard/log_serializer.rb b/app/serializers/custom_wizard/log_serializer.rb
new file mode 100644
index 00000000..56c5fd8f
--- /dev/null
+++ b/app/serializers/custom_wizard/log_serializer.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class CustomWizard::LogSerializer < ApplicationSerializer
+ attributes :date,
+ :action,
+ :username,
+ :message
+
+ has_one :user, serializer: ::BasicUserSerializer, embed: :objects
+end
diff --git a/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb b/app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
similarity index 100%
rename from serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
rename to app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
diff --git a/app/serializers/custom_wizard/submission_serializer.rb b/app/serializers/custom_wizard/submission_serializer.rb
new file mode 100644
index 00000000..732d6743
--- /dev/null
+++ b/app/serializers/custom_wizard/submission_serializer.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+class CustomWizard::SubmissionSerializer < ApplicationSerializer
+ attributes :id,
+ :fields,
+ :submitted_at
+
+ has_one :user, serializer: ::BasicUserSerializer, embed: :objects
+
+ def include_user?
+ object.user.present?
+ end
+
+ def fields
+ @fields ||= begin
+ result = {}
+
+ object.wizard.template['steps'].each do |step|
+ step['fields'].each do |field|
+ if value = object.fields[field['id']]
+ result[field['id']] = {
+ value: value,
+ type: field['type'],
+ label: field['label']
+ }
+ end
+ end
+ end
+
+ result
+ end
+ end
+end
diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/app/serializers/custom_wizard/wizard_field_serializer.rb
similarity index 77%
rename from serializers/custom_wizard/wizard_field_serializer.rb
rename to app/serializers/custom_wizard/wizard_field_serializer.rb
index 70784f7f..56e86cc8 100644
--- a/serializers/custom_wizard/wizard_field_serializer.rb
+++ b/app/serializers/custom_wizard/wizard_field_serializer.rb
@@ -17,6 +17,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
:property,
:content,
:tag_groups,
+ :can_create_tag,
:validations,
:max_length,
:char_counter,
@@ -42,13 +43,8 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
object.value
end
- def i18n_key
- @i18n_key ||= "wizard.step.#{object.step.id}.fields.#{object.id}".underscore
- end
-
def label
- return object.label if object.label.present?
- I18n.t("#{object.key || i18n_key}.label", default: '')
+ I18n.t("#{i18n_key}.label", default: object.label, base_url: Discourse.base_url)
end
def include_label?
@@ -56,14 +52,21 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
end
def description
- return object.description if object.description.present?
- I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)
+ I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url)
end
def include_description?
description.present?
end
+ def placeholder
+ I18n.t("#{i18n_key}.placeholder", default: object.placeholder)
+ end
+
+ def include_placeholder?
+ placeholder.present?
+ end
+
def image
object.image
end
@@ -72,15 +75,6 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
object.image.present?
end
- def placeholder
- return object.placeholder if object.placeholder.present?
- I18n.t("#{object.key || i18n_key}.placeholder", default: '')
- end
-
- def include_placeholder?
- placeholder.present?
- end
-
def file_types
object.file_types
end
@@ -105,6 +99,10 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
object.tag_groups
end
+ def can_create_tag
+ object.can_create_tag
+ end
+
def validations
validations = {}
object.validations&.each do |type, props|
@@ -127,4 +125,14 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
def preview_template
object.preview_template
end
+
+ protected
+
+ def i18n_key
+ @i18n_key ||= "#{object.step.wizard.id}.#{object.step.id}.#{object.id}".underscore
+ end
+
+ def subscribed?
+ @subscribed ||= CustomWizard::Subscription.subscribed?
+ end
end
diff --git a/serializers/custom_wizard/wizard_serializer.rb b/app/serializers/custom_wizard/wizard_serializer.rb
similarity index 61%
rename from serializers/custom_wizard/wizard_serializer.rb
rename to app/serializers/custom_wizard/wizard_serializer.rb
index 708b962d..9741d7af 100644
--- a/serializers/custom_wizard/wizard_serializer.rb
+++ b/app/serializers/custom_wizard/wizard_serializer.rb
@@ -9,13 +9,10 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
:completed,
:required,
:permitted,
- :uncategorized_category_id,
- :categories,
:resume_on_revisit
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
- has_many :groups, serializer: ::BasicGroupSerializer, embed: :objects
def completed
object.completed?
@@ -46,30 +43,4 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
def include_steps?
!include_completed?
end
-
- def include_categories?
- object.needs_categories
- end
-
- def include_groups?
- object.needs_groups
- end
-
- def uncategorized_category_id
- SiteSetting.uncategorized_category_id
- end
-
- def include_uncategorized_category_id?
- object.needs_categories
- end
-
- def categories
- object.categories.map do |category|
- if category.respond_to?(:to_h)
- category.to_h
- else
- ::BasicCategorySerializer.new(category).as_json
- end
- end
- end
end
diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/app/serializers/custom_wizard/wizard_step_serializer.rb
similarity index 76%
rename from serializers/custom_wizard/wizard_step_serializer.rb
rename to app/serializers/custom_wizard/wizard_step_serializer.rb
index 85f527bb..a2a314a4 100644
--- a/serializers/custom_wizard/wizard_step_serializer.rb
+++ b/app/serializers/custom_wizard/wizard_step_serializer.rb
@@ -39,13 +39,8 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer
object.previous.present?
end
- def i18n_key
- @i18n_key ||= "wizard.step.#{object.id}".underscore
- end
-
def title
- return PrettyText.cook(object.title) if object.title
- PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
+ I18n.t("#{i18n_key}.title", default: object.title, base_url: Discourse.base_url)
end
def include_title?
@@ -53,8 +48,7 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer
end
def description
- return object.description if object.description
- PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
+ I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url)
end
def include_description?
@@ -80,4 +74,10 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer
def final
object.final?
end
+
+ protected
+
+ def i18n_key
+ @i18n_key ||= "#{object.wizard.id}.#{object.id}".underscore
+ end
end
diff --git a/app/views/layouts/qunit.html.erb b/app/views/layouts/qunit.html.erb
new file mode 100644
index 00000000..c2f5fb5e
--- /dev/null
+++ b/app/views/layouts/qunit.html.erb
@@ -0,0 +1,28 @@
+
+
+
+ Custom Wizard QUnit Test Runner
+ <%= discourse_stylesheet_link_tag(:test_helper, theme_id: nil) %>
+ <%= discourse_stylesheet_link_tag :wizard, theme_id: nil %>
+ <%= discourse_stylesheet_link_tag :wizard_custom %>
+ <%= preload_script "locales/en" %>
+ <%= preload_script "ember_jquery" %>
+ <%= preload_script "wizard-vendor" %>
+ <%= preload_script "wizard-custom" %>
+ <%= preload_script "wizard-raw-templates" %>
+ <%= preload_script "wizard-plugin" %>
+ <%= preload_script "pretty-text-bundle" %>
+ <%= preload_script "wizard-qunit" %>
+ <%= csrf_meta_tags %>
+
+
+
+ <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
+
+
+
+
+
+
+
+
diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6
index e49c6f1d..5d2d6c3b 100644
--- a/assets/javascripts/discourse/components/custom-field-input.js.es6
+++ b/assets/javascripts/discourse/components/custom-field-input.js.es6
@@ -3,27 +3,12 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { alias, equal, or } from "@ember/object/computed";
import I18n from "I18n";
-const generateContent = function (array, type) {
- return array.map((key) => ({
- id: key,
- name: I18n.t(`admin.wizard.custom_field.${type}.${key}`),
- }));
-};
-
export default Component.extend({
tagName: "tr",
topicSerializers: ["topic_view", "topic_list_item"],
postSerializers: ["post"],
groupSerializers: ["basic_group"],
categorySerializers: ["basic_category"],
- klassContent: generateContent(
- ["topic", "post", "group", "category"],
- "klass"
- ),
- typeContent: generateContent(
- ["string", "boolean", "integer", "json"],
- "type"
- ),
showInputs: or("field.new", "field.edit"),
classNames: ["custom-field-input"],
loading: or("saving", "destroying"),
@@ -40,9 +25,13 @@ export default Component.extend({
const serializers = this.get(`${klass}Serializers`);
if (serializers) {
- return generateContent(serializers, "serializers");
- } else {
- return [];
+ return serializers.reduce((result, key) => {
+ result.push({
+ id: key,
+ name: I18n.t(`admin.wizard.custom_field.serializers.${key}`),
+ });
+ return result;
+ }, []);
}
},
diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/discourse/components/custom-user-selector.js.es6
similarity index 91%
rename from assets/javascripts/wizard/components/custom-user-selector.js.es6
rename to assets/javascripts/discourse/components/custom-user-selector.js.es6
index b2f08ede..3bb1fb3d 100644
--- a/assets/javascripts/wizard/components/custom-user-selector.js.es6
+++ b/assets/javascripts/discourse/components/custom-user-selector.js.es6
@@ -3,10 +3,11 @@ import {
observes,
} from "discourse-common/utils/decorators";
import { renderAvatar } from "discourse/helpers/user-avatar";
-import userSearch from "../lib/user-search";
-import WizardI18n from "../lib/wizard-i18n";
+import userSearch from "discourse/lib/user-search";
+import I18n from "I18n";
import Handlebars from "handlebars";
import { isEmpty } from "@ember/utils";
+import TextField from "@ember/component/text-field";
const template = function (params) {
const options = params.options;
@@ -31,7 +32,7 @@ const template = function (params) {
return new Handlebars.SafeString(html).string;
};
-export default Ember.TextField.extend({
+export default TextField.extend({
attributeBindings: ["autofocus", "maxLength"],
autocorrect: false,
autocapitalize: false,
@@ -40,7 +41,7 @@ export default Ember.TextField.extend({
@computed("placeholderKey")
placeholder(placeholderKey) {
- return placeholderKey ? WizardI18n(placeholderKey) : "";
+ return placeholderKey ? I18n.t(placeholderKey) : "";
},
@observes("usernames")
@@ -55,7 +56,6 @@ export default Ember.TextField.extend({
let self = this,
selected = [],
groups = [],
- currentUser = this.currentUser,
includeMentionableGroups =
this.get("includeMentionableGroups") === "true",
includeMessageableGroups =
@@ -66,13 +66,8 @@ export default Ember.TextField.extend({
function excludedUsernames() {
// hack works around some issues with allowAny eventing
const usernames = self.get("single") ? [] : selected;
-
- if (currentUser && self.get("excludeCurrentUser")) {
- return usernames.concat([currentUser.get("username")]);
- }
return usernames;
}
-
$(this.element)
.val(this.get("usernames"))
.autocomplete({
@@ -84,7 +79,6 @@ export default Ember.TextField.extend({
dataSource(term) {
const termRegex = /[^a-zA-Z0-9_\-\.@\+]/;
-
let results = userSearch({
term: term.replace(termRegex, ""),
topicId: self.get("topicId"),
diff --git a/assets/javascripts/wizard/components/wizard-category-selector.js.es6 b/assets/javascripts/discourse/components/custom-wizard-category-selector.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-category-selector.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-category-selector.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
similarity index 81%
rename from assets/javascripts/wizard/components/wizard-composer-editor.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
index faada1f4..5e2ef424 100644
--- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
@@ -1,5 +1,6 @@
import ComposerEditor from "discourse/components/composer-editor";
import {
+ bind,
default as discourseComputed,
on,
} from "discourse-common/utils/decorators";
@@ -8,10 +9,12 @@ import { scheduleOnce } from "@ember/runloop";
import { caretPosition, inCodeBlock } from "discourse/lib/utilities";
import highlightSyntax from "discourse/lib/highlight-syntax";
import { alias } from "@ember/object/computed";
-import Site from "../models/site";
+import Site from "discourse/models/site";
import { uploadIcon } from "discourse/lib/uploads";
import { dasherize } from "@ember/string";
+const IMAGE_MARKDOWN_REGEX = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
+
export default ComposerEditor.extend({
classNameBindings: ["fieldClass"],
allowUpload: true,
@@ -24,8 +27,8 @@ export default ComposerEditor.extend({
lastValidatedAt: "lastValidatedAt",
popupMenuOptions: [],
draftStatus: "null",
- replyPlaceholder: alias("field.placeholder"),
- uploadingFieldId: null,
+ replyPlaceholder: alias("field.translatedPlaceholder"),
+ wizardEventFieldId: null,
@on("didInsertElement")
_composerEditorInit() {
@@ -34,7 +37,7 @@ export default ComposerEditor.extend({
if (this.siteSettings.enable_mentions) {
$input.autocomplete({
template: findRawTemplate("user-selector-autocomplete"),
- dataSource: (term) => this.userSearchTerm.call(this, term),
+ dataSource: (term) => this._userSearchTerm.call(this, term),
key: "@",
transformComplete: (v) => v.username || v.name,
afterComplete: (value) => {
@@ -76,7 +79,6 @@ export default ComposerEditor.extend({
const wizardEventNames = ["insert-text", "replace-text"];
const eventPrefix = this.eventPrefix;
- const session = this.get("session");
this.appEvents.reopen({
trigger(name, ...args) {
let eventParts = name.split(":");
@@ -87,26 +89,8 @@ export default ComposerEditor.extend({
currentEventPrefix !== "wizard-editor" &&
wizardEventNames.some((wen) => wen === currentEventName)
) {
- let wizardName = name.replace(eventPrefix, "wizard-editor");
- if (currentEventName === "insert-text") {
- args = {
- text: args[0],
- };
- }
- if (currentEventName === "replace-text") {
- args = {
- oldVal: args[0],
- newVal: args[1],
- };
- }
- let wizardArgs = Object.assign(
- {},
- {
- fieldId: session.get("uploadingFieldId"),
- },
- args
- );
- return this._super(wizardName, wizardArgs);
+ let wizardEventName = name.replace(eventPrefix, "wizard-editor");
+ return this._super(wizardEventName, ...args);
} else {
return this._super(name, ...args);
}
@@ -138,11 +122,34 @@ export default ComposerEditor.extend({
}
},
+ @bind
+ _handleImageDeleteButtonClick(event) {
+ if (!event.target.classList.contains("delete-image-button")) {
+ return;
+ }
+
+ const index = parseInt(
+ event.target.closest(".button-wrapper").dataset.imageIndex,
+ 10
+ );
+ const matchingPlaceholder = this.get("composer.reply").match(
+ IMAGE_MARKDOWN_REGEX
+ );
+
+ this.session.set("wizardEventFieldId", this.field.id);
+ this.appEvents.trigger(
+ "composer:replace-text",
+ matchingPlaceholder[index],
+ "",
+ { regex: IMAGE_MARKDOWN_REGEX, index }
+ );
+ },
+
actions: {
extraButtons(toolbar) {
const component = this;
- if (this.allowUpload && this.uploadIcon && !this.site.mobileView) {
+ if (this.allowUpload && this.uploadIcon) {
toolbar.addButton({
id: "upload",
group: "insertions",
@@ -187,14 +194,14 @@ export default ComposerEditor.extend({
}
},
- previewUpdated($preview) {
- highlightSyntax($preview[0], this.siteSettings, this.session);
+ previewUpdated(preview) {
+ highlightSyntax(preview, this.siteSettings, this.session);
if (this.siteSettings.mentionables_enabled) {
const { linkSeenMentionableItems } = requirejs(
"discourse/plugins/discourse-mentionables/discourse/lib/mentionable-items-preview-styling"
);
- linkSeenMentionableItems($preview, this.siteSettings);
+ linkSeenMentionableItems(preview, this.siteSettings);
}
this._super(...arguments);
},
@@ -213,7 +220,7 @@ export default ComposerEditor.extend({
},
showUploadModal() {
- this.session.set("uploadingFieldId", this.field.id);
+ this.session.set("wizardEventFieldId", this.field.id);
document.getElementById(this.fileUploadElementId).click();
},
},
diff --git a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-hyperlink.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-composer-hyperlink.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-date-input.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-date-input.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-date-time-input.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-category.js.es6
similarity index 92%
rename from assets/javascripts/wizard/components/wizard-field-category.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-category.js.es6
index a7452214..65e4a1c7 100644
--- a/assets/javascripts/wizard/components/wizard-field-category.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-category.js.es6
@@ -1,7 +1,8 @@
import { observes } from "discourse-common/utils/decorators";
import Category from "discourse/models/category";
+import Component from "@ember/component";
-export default Ember.Component.extend({
+export default Component.extend({
didInsertElement() {
const property = this.field.property || "id";
const value = this.field.value;
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-checkbox.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-checkbox.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-checkbox.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-composer-preview.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-composer-preview.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-composer.js.es6
similarity index 92%
rename from assets/javascripts/wizard/components/wizard-field-composer.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-composer.js.es6
index 8b9ecb82..1a25344c 100644
--- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-composer.js.es6
@@ -3,8 +3,9 @@ import {
observes,
} from "discourse-common/utils/decorators";
import EmberObject from "@ember/object";
+import Component from "@ember/component";
-export default Ember.Component.extend({
+export default Component.extend({
showPreview: false,
classNameBindings: [
":wizard-field-composer",
diff --git a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-date-time.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-field-date-time.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-date-time.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-field-date.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-date.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-field-date.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-date.js.es6
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6
new file mode 100644
index 00000000..659b8f29
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6
@@ -0,0 +1,13 @@
+import Component from "@ember/component";
+
+export default Component.extend({
+ keyPress(e) {
+ e.stopPropagation();
+ },
+
+ actions: {
+ onChangeValue(value) {
+ this.set("field.value", value);
+ },
+ },
+});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-group.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-group.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-group.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-number.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-number.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-number.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-tag.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-tag.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-tag.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-text.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-text.js.es6
new file mode 100644
index 00000000..3e49cb35
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-text.js.es6
@@ -0,0 +1,7 @@
+import Component from "@ember/component";
+
+export default Component.extend({
+ keyPress(e) {
+ e.stopPropagation();
+ },
+});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6
new file mode 100644
index 00000000..3e49cb35
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6
@@ -0,0 +1,7 @@
+import Component from "@ember/component";
+
+export default Component.extend({
+ keyPress(e) {
+ e.stopPropagation();
+ },
+});
diff --git a/assets/javascripts/wizard/components/wizard-field-time.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-field-time.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-time.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
similarity index 88%
rename from assets/javascripts/wizard/components/wizard-field-upload.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
index 876cdf0e..eb5d318b 100644
--- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
@@ -9,6 +9,9 @@ export default Component.extend(UppyUploadMixin, {
type: computed(function () {
return `wizard_${this.field.id}`;
}),
+ id: computed(function () {
+ return `wizard_field_upload_${this.field.id}`;
+ }),
isImage: computed("field.value.extension", function () {
return (
this.field.value &&
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-url.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-url.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-url.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6
new file mode 100644
index 00000000..87d5ddb0
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6
@@ -0,0 +1,3 @@
+import Component from "@ember/component";
+
+export default Component.extend({});
diff --git a/assets/javascripts/discourse/components/custom-wizard-field.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field.js.es6
new file mode 100644
index 00000000..d706ecd8
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-field.js.es6
@@ -0,0 +1,41 @@
+import Component from "@ember/component";
+import { dasherize } from "@ember/string";
+import discourseComputed from "discourse-common/utils/decorators";
+import { cookAsync } from "discourse/lib/text";
+
+export default Component.extend({
+ classNameBindings: [
+ ":wizard-field",
+ "typeClasses",
+ "field.invalid",
+ "field.id",
+ ],
+
+ didReceiveAttrs() {
+ this._super(...arguments);
+
+ cookAsync(this.field.translatedDescription).then((cookedDescription) => {
+ this.set("cookedDescription", cookedDescription);
+ });
+ },
+
+ @discourseComputed("field.type", "field.id")
+ typeClasses: (type, id) =>
+ `${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`,
+
+ @discourseComputed("field.id")
+ fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`,
+
+ @discourseComputed("field.type", "field.id")
+ inputComponentName(type, id) {
+ if (["text_only"].includes(type)) {
+ return false;
+ }
+ return dasherize(type === "component" ? id : `custom-wizard-field-${type}`);
+ },
+
+ @discourseComputed("field.type")
+ textType(fieldType) {
+ return ["text", "textarea"].includes(fieldType);
+ },
+});
diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/discourse/components/custom-wizard-group-selector.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-group-selector.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-group-selector.js.es6
diff --git a/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6
new file mode 100644
index 00000000..b3b2e26c
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6
@@ -0,0 +1,29 @@
+import CustomWizard from "../models/custom-wizard";
+import discourseComputed from "discourse-common/utils/decorators";
+import Component from "@ember/component";
+import { dasherize } from "@ember/string";
+import getURL from "discourse-common/lib/get-url";
+
+export default Component.extend({
+ classNameBindings: [":wizard-no-access", "reasonClass"],
+
+ @discourseComputed("reason")
+ reasonClass(reason) {
+ return dasherize(reason);
+ },
+
+ @discourseComputed
+ siteName() {
+ return this.siteSettings.title || "";
+ },
+
+ actions: {
+ skip() {
+ if (this.currentUser) {
+ CustomWizard.skip(this.get("wizardId"));
+ } else {
+ window.location = getURL("/");
+ }
+ },
+ },
+});
diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/discourse/components/custom-wizard-similar-topics.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-similar-topics.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-similar-topics.js.es6
diff --git a/assets/javascripts/discourse/components/custom-wizard-step-form.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step-form.js.es6
new file mode 100644
index 00000000..73406b4f
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-step-form.js.es6
@@ -0,0 +1,9 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+
+export default Component.extend({
+ classNameBindings: [":wizard-step-form", "customStepClass"],
+
+ @discourseComputed("step.id")
+ customStepClass: (stepId) => `wizard-step-${stepId}`,
+});
diff --git a/assets/javascripts/discourse/components/custom-wizard-step.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step.js.es6
new file mode 100644
index 00000000..b98db1ab
--- /dev/null
+++ b/assets/javascripts/discourse/components/custom-wizard-step.js.es6
@@ -0,0 +1,233 @@
+import discourseComputed, { observes } from "discourse-common/utils/decorators";
+import Component from "@ember/component";
+import I18n from "I18n";
+import getUrl from "discourse-common/lib/get-url";
+import { htmlSafe } from "@ember/template";
+import { schedule } from "@ember/runloop";
+import { cookAsync } from "discourse/lib/text";
+import CustomWizard, {
+ updateCachedWizard,
+} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard";
+import { alias, not } from "@ember/object/computed";
+
+const alreadyWarned = {};
+
+export default Component.extend({
+ classNameBindings: [":wizard-step", "step.id"],
+ saving: null,
+
+ init() {
+ this._super(...arguments);
+ this.set("stylingDropdown", {});
+ },
+
+ didReceiveAttrs() {
+ this._super(...arguments);
+
+ cookAsync(this.step.translatedTitle).then((cookedTitle) => {
+ this.set("cookedTitle", cookedTitle);
+ });
+ cookAsync(this.step.translatedDescription).then((cookedDescription) => {
+ this.set("cookedDescription", cookedDescription);
+ });
+ },
+
+ didInsertElement() {
+ this._super(...arguments);
+ this.autoFocus();
+ },
+
+ @discourseComputed("step.index", "wizard.required")
+ showQuitButton: (index, required) => index === 0 && !required,
+
+ showNextButton: not("step.final"),
+ showDoneButton: alias("step.final"),
+
+ @discourseComputed(
+ "step.index",
+ "step.displayIndex",
+ "wizard.totalSteps",
+ "wizard.completed"
+ )
+ showFinishButton: (index, displayIndex, total, completed) => {
+ return index !== 0 && displayIndex !== total && completed;
+ },
+
+ @discourseComputed("step.index")
+ showBackButton: (index) => index > 0,
+
+ @discourseComputed("step.banner")
+ bannerImage(src) {
+ if (!src) {
+ return;
+ }
+ return getUrl(src);
+ },
+
+ @discourseComputed("step.id")
+ bannerAndDescriptionClass(id) {
+ return `wizard-banner-and-description wizard-banner-and-description-${id}`;
+ },
+
+ @discourseComputed("step.fields.[]")
+ primaryButtonIndex(fields) {
+ return fields.length + 1;
+ },
+
+ @discourseComputed("step.fields.[]")
+ secondaryButtonIndex(fields) {
+ return fields.length + 2;
+ },
+
+ @observes("step.id")
+ _stepChanged() {
+ this.set("saving", false);
+ this.autoFocus();
+ },
+
+ @observes("step.message")
+ _handleMessage: function () {
+ const message = this.get("step.message");
+ this.showMessage(message);
+ },
+
+ @discourseComputed("step.index", "wizard.totalSteps")
+ barStyle(displayIndex, totalSteps) {
+ let ratio = parseFloat(displayIndex) / parseFloat(totalSteps - 1);
+ if (ratio < 0) {
+ ratio = 0;
+ }
+ if (ratio > 1) {
+ ratio = 1;
+ }
+
+ return htmlSafe(`width: ${ratio * 200}px`);
+ },
+
+ @discourseComputed("step.fields")
+ includeSidebar(fields) {
+ return !!fields.findBy("show_in_sidebar");
+ },
+
+ autoFocus() {
+ schedule("afterRender", () => {
+ const $invalid = $(
+ ".wizard-field.invalid:nth-of-type(1) .wizard-focusable"
+ );
+
+ if ($invalid.length) {
+ return $invalid.focus();
+ }
+
+ $(".wizard-focusable:first").focus();
+ });
+ },
+
+ animateInvalidFields() {
+ schedule("afterRender", () => {
+ let $element = $(
+ ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit"
+ );
+
+ if ($element.length) {
+ $([document.documentElement, document.body]).animate(
+ {
+ scrollTop: $element.offset().top - 200,
+ },
+ 400
+ );
+ }
+ });
+ },
+
+ advance() {
+ this.set("saving", true);
+ this.get("step")
+ .save()
+ .then((response) => {
+ updateCachedWizard(CustomWizard.build(response["wizard"]));
+
+ if (response["final"]) {
+ CustomWizard.finished(response);
+ } else {
+ this.goNext(response);
+ }
+ })
+ .catch(() => this.animateInvalidFields())
+ .finally(() => this.set("saving", false));
+ },
+
+ actions: {
+ quit() {
+ this.get("wizard").skip();
+ },
+
+ done() {
+ this.send("nextStep");
+ },
+
+ showMessage(message) {
+ this.sendAction(message);
+ },
+
+ stylingDropdownChanged(id, value) {
+ this.set("stylingDropdown", { id, value });
+ },
+
+ exitEarly() {
+ const step = this.step;
+ step.validate();
+
+ if (step.get("valid")) {
+ this.set("saving", true);
+
+ step
+ .save()
+ .then(() => this.send("quit"))
+ .finally(() => this.set("saving", false));
+ } else {
+ this.autoFocus();
+ }
+ },
+
+ backStep() {
+ if (this.saving) {
+ return;
+ }
+
+ this.goBack();
+ },
+
+ nextStep() {
+ if (this.saving) {
+ return;
+ }
+
+ const step = this.step;
+ const result = step.validate();
+
+ if (result.warnings.length) {
+ const unwarned = result.warnings.filter((w) => !alreadyWarned[w]);
+ if (unwarned.length) {
+ unwarned.forEach((w) => (alreadyWarned[w] = true));
+ return window.bootbox.confirm(
+ unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"),
+ I18n.t("no_value"),
+ I18n.t("yes_value"),
+ (confirmed) => {
+ if (confirmed) {
+ this.advance();
+ }
+ }
+ );
+ }
+ }
+
+ if (step.get("valid")) {
+ this.advance();
+ } else {
+ this.autoFocus();
+ }
+ },
+ },
+});
diff --git a/assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 b/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-tag-chooser.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-tag-chooser.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-tag-selector.js.es6 b/assets/javascripts/discourse/components/custom-wizard-tag-selector.js.es6
similarity index 100%
rename from assets/javascripts/wizard/components/wizard-tag-selector.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-tag-selector.js.es6
diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/discourse/components/custom-wizard-text-field.js.es6
similarity index 71%
rename from assets/javascripts/wizard/components/wizard-text-field.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-text-field.js.es6
index c522eb2c..e8c22e93 100644
--- a/assets/javascripts/wizard/components/wizard-text-field.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-text-field.js.es6
@@ -1,10 +1,9 @@
-/* eslint no-undef: 0*/
-
import computed from "discourse-common/utils/decorators";
import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction";
-import WizardI18n from "../lib/wizard-i18n";
+import I18n from "I18n";
+import TextField from "@ember/component/text-field";
-export default Ember.TextField.extend({
+export default TextField.extend({
attributeBindings: [
"autocorrect",
"autocapitalize",
@@ -15,7 +14,7 @@ export default Ember.TextField.extend({
@computed
dir() {
- if (Wizard.SiteSettings.support_mixed_text_direction) {
+ if (this.siteSettings.support_mixed_text_direction) {
let val = this.value;
if (val) {
return isRTL(val) ? "rtl" : "ltr";
@@ -26,7 +25,7 @@ export default Ember.TextField.extend({
},
keyUp() {
- if (Wizard.SiteSettings.support_mixed_text_direction) {
+ if (this.siteSettings.support_mixed_text_direction) {
let val = this.value;
if (isRTL(val)) {
this.set("dir", "rtl");
@@ -40,6 +39,6 @@ export default Ember.TextField.extend({
@computed("placeholderKey")
placeholder(placeholderKey) {
- return placeholderKey ? WizardI18n(placeholderKey) : "";
+ return placeholderKey ? I18n.t(placeholderKey) : "";
},
});
diff --git a/assets/javascripts/wizard/components/wizard-time-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-time-input.js.es6
similarity index 61%
rename from assets/javascripts/wizard/components/wizard-time-input.js.es6
rename to assets/javascripts/discourse/components/custom-wizard-time-input.js.es6
index 0bca244d..c09ed110 100644
--- a/assets/javascripts/wizard/components/wizard-time-input.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-time-input.js.es6
@@ -1,3 +1,3 @@
import TimeInput from "discourse/components/time-input";
-export default TimeInput.extend();
+export default TimeInput.extend({});
diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/discourse/components/field-validators.js.es6
similarity index 99%
rename from assets/javascripts/wizard/components/field-validators.js.es6
rename to assets/javascripts/discourse/components/field-validators.js.es6
index a315020d..8b9b39da 100644
--- a/assets/javascripts/wizard/components/field-validators.js.es6
+++ b/assets/javascripts/discourse/components/field-validators.js.es6
@@ -1,4 +1,5 @@
import Component from "@ember/component";
+
export default Component.extend({
actions: {
perform() {
diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/discourse/components/similar-topics-validator.js.es6
similarity index 97%
rename from assets/javascripts/wizard/components/similar-topics-validator.js.es6
rename to assets/javascripts/discourse/components/similar-topics-validator.js.es6
index 98ea9270..634af983 100644
--- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6
+++ b/assets/javascripts/discourse/components/similar-topics-validator.js.es6
@@ -1,4 +1,4 @@
-import WizardFieldValidator from "../../wizard/components/validator";
+import WizardFieldValidator from "discourse/plugins/discourse-custom-wizard/discourse/components/validator";
import { deepMerge } from "discourse-common/lib/object";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { cancel, later } from "@ember/runloop";
diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/discourse/components/validator.js.es6
similarity index 84%
rename from assets/javascripts/wizard/components/validator.js.es6
rename to assets/javascripts/discourse/components/validator.js.es6
index 6227cc64..3c19cc3d 100644
--- a/assets/javascripts/wizard/components/validator.js.es6
+++ b/assets/javascripts/discourse/components/validator.js.es6
@@ -1,7 +1,6 @@
import Component from "@ember/component";
import { equal } from "@ember/object/computed";
-import { ajax } from "discourse/lib/ajax";
-import { getToken } from "wizard/lib/ajax";
+import { ajax, getToken } from "discourse/lib/ajax";
export default Component.extend({
classNames: ["validator"],
@@ -10,7 +9,6 @@ export default Component.extend({
invalidMessageKey: null,
isValid: null,
isInvalid: equal("isValid", false),
- layoutName: "components/validator", // useful for sharing the template with extending components
init() {
this._super(...arguments);
diff --git a/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 b/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6
deleted file mode 100644
index c6e1fd9c..00000000
--- a/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6
+++ /dev/null
@@ -1,21 +0,0 @@
-import { default as discourseComputed } from "discourse-common/utils/decorators";
-import Component from "@ember/component";
-
-export default Component.extend({
- classNames: "wizard-advanced-toggle",
-
- @discourseComputed("showAdvanced")
- toggleClass(showAdvanced) {
- let classes = "btn";
- if (showAdvanced) {
- classes += " btn-primary";
- }
- return classes;
- },
-
- actions: {
- toggleAdvanced() {
- this.toggleProperty("showAdvanced");
- },
- },
-});
diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
index feb83754..81a12530 100644
--- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
@@ -1,8 +1,7 @@
import { default as discourseComputed } from "discourse-common/utils/decorators";
-import { and, empty, equal, or } from "@ember/object/computed";
+import { empty, equal, or } from "@ember/object/computed";
import { notificationLevels, selectKitContent } from "../lib/wizard";
import { computed } from "@ember/object";
-import wizardSchema from "../lib/wizard-schema";
import UndoChanges from "../mixins/undo-changes";
import Component from "@ember/component";
import I18n from "I18n";
@@ -16,6 +15,7 @@ export default Component.extend(UndoChanges, {
createTopic: equal("action.type", "create_topic"),
updateProfile: equal("action.type", "update_profile"),
watchCategories: equal("action.type", "watch_categories"),
+ watchTags: equal("action.type", "watch_tags"),
sendMessage: equal("action.type", "send_message"),
openComposer: equal("action.type", "open_composer"),
sendToApi: equal("action.type", "send_to_api"),
@@ -25,8 +25,6 @@ export default Component.extend(UndoChanges, {
createGroup: equal("action.type", "create_group"),
apiEmpty: empty("action.api"),
groupPropertyTypes: selectKitContent(["id", "name"]),
- hasAdvanced: or("hasCustomFields", "routeTo"),
- showAdvanced: and("hasAdvanced", "action.type"),
hasCustomFields: or(
"basicTopicFields",
"updateProfile",
@@ -36,22 +34,14 @@ export default Component.extend(UndoChanges, {
basicTopicFields: or("createTopic", "sendMessage", "openComposer"),
publicTopicFields: or("createTopic", "openComposer"),
showPostAdvanced: or("createTopic", "sendMessage"),
- actionTypes: Object.keys(wizardSchema.action.types).map((type) => {
- return {
- id: type,
- name: I18n.t(`admin.wizard.action.${type}.label`),
- };
- }),
availableNotificationLevels: notificationLevels.map((type) => {
return {
id: type,
- name: I18n.t(
- `admin.wizard.action.watch_categories.notification_level.${type}`
- ),
+ name: I18n.t(`admin.wizard.action.watch_x.notification_level.${type}`),
};
}),
- messageUrl: "https://thepavilion.io/t/2810",
+ messageUrl: "https://discourse.pluginmanager.org/t/action-settings",
@discourseComputed("action.type")
messageKey(type) {
@@ -101,4 +91,14 @@ export default Component.extend(UndoChanges, {
}
return apis.find((a) => a.name === api).endpoints;
},
+
+ @discourseComputed("fieldTypes")
+ hasEventField(fieldTypes) {
+ return fieldTypes.map((ft) => ft.id).includes("event");
+ },
+
+ @discourseComputed("fieldTypes")
+ hasLocationField(fieldTypes) {
+ return fieldTypes.map((ft) => ft.id).includes("location");
+ },
});
diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
index 8efb7f0c..ed66012a 100644
--- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
@@ -1,5 +1,5 @@
import { default as discourseComputed } from "discourse-common/utils/decorators";
-import { alias, equal, or } from "@ember/object/computed";
+import { equal, or } from "@ember/object/computed";
import { computed } from "@ember/object";
import { selectKitContent } from "../lib/wizard";
import UndoChanges from "../mixins/undo-changes";
@@ -27,8 +27,7 @@ export default Component.extend(UndoChanges, {
isTextType: or("isText", "isTextarea", "isComposer"),
isComposerPreview: equal("field.type", "composer_preview"),
categoryPropertyTypes: selectKitContent(["id", "slug"]),
- showAdvanced: alias("field.type"),
- messageUrl: "https://thepavilion.io/t/2809",
+ messageUrl: "https://discourse.pluginmanager.org/t/field-settings",
@discourseComputed("field.type")
validations(type) {
@@ -144,11 +143,17 @@ export default Component.extend(UndoChanges, {
actions: {
imageUploadDone(upload) {
- this.set("field.image", upload.url);
+ this.setProperties({
+ "field.image": upload.url,
+ "field.image_upload_id": upload.id,
+ });
},
imageUploadDeleted() {
- this.set("field.image", null);
+ this.setProperties({
+ "field.image": null,
+ "field.image_upload_id": null,
+ });
},
},
});
diff --git a/assets/javascripts/discourse/components/wizard-custom-step.js.es6 b/assets/javascripts/discourse/components/wizard-custom-step.js.es6
index 2a07dd65..7605d8a4 100644
--- a/assets/javascripts/discourse/components/wizard-custom-step.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-step.js.es6
@@ -24,11 +24,17 @@ export default Component.extend({
actions: {
bannerUploadDone(upload) {
- this.set("step.banner", upload.url);
+ this.setProperties({
+ "step.banner": upload.url,
+ "step.banner_upload_id": upload.id,
+ });
},
bannerUploadDeleted() {
- this.set("step.banner", null);
+ this.setProperties({
+ "step.banner": null,
+ "step.banner_upload_id": null,
+ });
},
},
});
diff --git a/assets/javascripts/discourse/components/wizard-links.js.es6 b/assets/javascripts/discourse/components/wizard-links.js.es6
index 62c45911..6d02987a 100644
--- a/assets/javascripts/discourse/components/wizard-links.js.es6
+++ b/assets/javascripts/discourse/components/wizard-links.js.es6
@@ -71,6 +71,17 @@ export default Component.extend({
});
},
+ getNextIndex() {
+ const items = this.items;
+ if (!items || items.length === 0) {
+ return 0;
+ }
+ const numbers = items
+ .map((i) => Number(i.id.split("_").pop()))
+ .sort((a, b) => a - b);
+ return numbers[numbers.length - 1];
+ },
+
actions: {
add() {
const items = this.get("items");
@@ -78,16 +89,9 @@ export default Component.extend({
let params = setWizardDefaults({}, itemType);
params.isNew = true;
+ params.index = this.getNextIndex();
- let index = 0;
- if (items.length) {
- let last_item = items[items.length - 1];
- index = Number(last_item.id.split("_").pop());
- }
-
- params.index = index;
-
- let id = `${itemType}_${index + 1}`;
+ let id = `${itemType}_${params.index + 1}`;
if (itemType === "field") {
id = `${this.parentId}_${id}`;
}
diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
index 8a2ae9ce..a257ed12 100644
--- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
+++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
@@ -377,7 +377,7 @@ export default Component.extend({
this.changeValue(event.target.value);
},
- changeUserValue(previousValue, value) {
+ changeUserValue(value) {
this.changeValue(value);
},
},
diff --git a/assets/javascripts/discourse/components/wizard-message.js.es6 b/assets/javascripts/discourse/components/wizard-message.js.es6
index b273e78b..686a7254 100644
--- a/assets/javascripts/discourse/components/wizard-message.js.es6
+++ b/assets/javascripts/discourse/components/wizard-message.js.es6
@@ -6,6 +6,7 @@ import I18n from "I18n";
const icons = {
error: "times-circle",
success: "check-circle",
+ warn: "exclamation-circle",
info: "info-circle",
};
diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6
index 8332b86e..b1d8a0f5 100644
--- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6
+++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6
@@ -6,7 +6,8 @@ import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Component.extend({
- classNames: ["realtime-validations"],
+ classNames: ["realtime-validations", "setting", "full", "subscription"],
+
@discourseComputed
timeUnits() {
return ["days", "weeks", "months", "years"].map((unit) => {
diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6
new file mode 100644
index 00000000..301c618e
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6
@@ -0,0 +1,30 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+import Subscription from "../mixins/subscription";
+import DiscourseURL from "discourse/lib/url";
+import I18n from "I18n";
+
+export default Component.extend(Subscription, {
+ tagName: "a",
+ classNameBindings: [":wizard-subscription-badge", "subscriptionType"],
+ attributeBindings: ["title"],
+
+ @discourseComputed("subscriptionType")
+ i18nKey(type) {
+ return `admin.wizard.subscription.type.${type ? type : "none"}`;
+ },
+
+ @discourseComputed("i18nKey")
+ title(i18nKey) {
+ return I18n.t(`${i18nKey}.title`);
+ },
+
+ @discourseComputed("i18nKey")
+ label(i18nKey) {
+ return I18n.t(`${i18nKey}.label`);
+ },
+
+ click() {
+ DiscourseURL.routeTo(this.subscriptionLink);
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6
new file mode 100644
index 00000000..5cc6b17c
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6
@@ -0,0 +1,26 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+import Subscription from "../mixins/subscription";
+
+export default Component.extend(Subscription, {
+ classNameBindings: [":wizard-subscription-container", "subscribed"],
+
+ @discourseComputed("subscribed")
+ subscribedIcon(subscribed) {
+ return subscribed ? "check" : "dash";
+ },
+
+ @discourseComputed("subscribed")
+ subscribedLabel(subscribed) {
+ return `admin.wizard.subscription.${
+ subscribed ? "subscribed" : "not_subscribed"
+ }.label`;
+ },
+
+ @discourseComputed("subscribed")
+ subscribedTitle(subscribed) {
+ return `admin.wizard.subscription.${
+ subscribed ? "subscribed" : "not_subscribed"
+ }.title`;
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6
new file mode 100644
index 00000000..f483fbe8
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6
@@ -0,0 +1,36 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+import Subscription from "../mixins/subscription";
+import I18n from "I18n";
+
+export default Component.extend(Subscription, {
+ tagName: "a",
+ classNameBindings: [":btn", ":btn-pavilion-support", "subscriptionType"],
+ attributeBindings: ["title"],
+
+ @discourseComputed("subscribed")
+ i18nKey(subscribed) {
+ return `admin.wizard.subscription.cta.${
+ subscribed ? "subscribed" : "none"
+ }`;
+ },
+
+ @discourseComputed("subscribed")
+ icon(subscribed) {
+ return subscribed ? "far-life-ring" : "external-link-alt";
+ },
+
+ @discourseComputed("i18nKey")
+ title(i18nKey) {
+ return I18n.t(`${i18nKey}.title`);
+ },
+
+ @discourseComputed("i18nKey")
+ label(i18nKey) {
+ return I18n.t(`${i18nKey}.label`);
+ },
+
+ click() {
+ window.open(this.subscriptionCtaLink, "_blank").focus();
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6
new file mode 100644
index 00000000..53f7d19c
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6
@@ -0,0 +1,96 @@
+import SingleSelectComponent from "select-kit/components/single-select";
+import Subscription from "../mixins/subscription";
+import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
+import discourseComputed from "discourse-common/utils/decorators";
+import I18n from "I18n";
+
+const nameKey = function (feature, attribute, value) {
+ if (feature === "action") {
+ return `admin.wizard.action.${value}.label`;
+ } else {
+ return `admin.wizard.${feature}.${attribute}.${value}`;
+ }
+};
+
+export default SingleSelectComponent.extend(Subscription, {
+ classNames: ["combo-box", "wizard-subscription-selector"],
+
+ selectKitOptions: {
+ autoFilterable: false,
+ filterable: false,
+ showFullTitle: true,
+ headerComponent:
+ "wizard-subscription-selector/wizard-subscription-selector-header",
+ caretUpIcon: "caret-up",
+ caretDownIcon: "caret-down",
+ },
+
+ allowedSubscriptionTypes(feature, attribute, value) {
+ let attributes = this.subscriptionAttributes[feature];
+ if (!attributes || !attributes[attribute]) {
+ return ["none"];
+ }
+ let allowedTypes = [];
+ Object.keys(attributes[attribute]).forEach((subscriptionType) => {
+ let values = attributes[attribute][subscriptionType];
+ if (values[0] === "*" || values.includes(value)) {
+ allowedTypes.push(subscriptionType);
+ }
+ });
+ return allowedTypes;
+ },
+
+ @discourseComputed("feature", "attribute")
+ content(feature, attribute) {
+ return wizardSchema[feature][attribute]
+ .map((value) => {
+ let allowedSubscriptionTypes = this.allowedSubscriptionTypes(
+ feature,
+ attribute,
+ value
+ );
+
+ let subscriptionRequired =
+ allowedSubscriptionTypes.length &&
+ !allowedSubscriptionTypes.includes("none");
+
+ let attrs = {
+ id: value,
+ name: I18n.t(nameKey(feature, attribute, value)),
+ subscriptionRequired,
+ };
+
+ if (subscriptionRequired) {
+ let subscribed = allowedSubscriptionTypes.includes(
+ this.subscriptionType
+ );
+ let selectorKey = subscribed ? "subscribed" : "not_subscribed";
+ let selectorLabel = `admin.wizard.subscription.${selectorKey}.selector`;
+
+ attrs.disabled = !subscribed;
+ attrs.selectorLabel = selectorLabel;
+ }
+
+ return attrs;
+ })
+ .sort(function (a, b) {
+ if (a.subscriptionType && !b.subscriptionType) {
+ return 1;
+ }
+ if (!a.subscriptionType && b.subscriptionType) {
+ return -1;
+ }
+ if (a.subscriptionType === b.subscriptionType) {
+ return a.subscriptionType
+ ? a.subscriptionType.localeCompare(b.subscriptionType)
+ : 0;
+ } else {
+ return a.subscriptionType === "standard" ? -1 : 0;
+ }
+ });
+ },
+
+ modifyComponentForRow() {
+ return "wizard-subscription-selector/wizard-subscription-selector-row";
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6
new file mode 100644
index 00000000..74f29f08
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6
@@ -0,0 +1,17 @@
+import SingleSelectHeaderComponent from "select-kit/components/select-kit/single-select-header";
+import { computed } from "@ember/object";
+import { reads } from "@ember/object/computed";
+
+export default SingleSelectHeaderComponent.extend({
+ classNames: ["combo-box-header", "wizard-subscription-selector-header"],
+ caretUpIcon: reads("selectKit.options.caretUpIcon"),
+ caretDownIcon: reads("selectKit.options.caretDownIcon"),
+ caretIcon: computed(
+ "selectKit.isExpanded",
+ "caretUpIcon",
+ "caretDownIcon",
+ function () {
+ return this.selectKit.isExpanded ? this.caretUpIcon : this.caretDownIcon;
+ }
+ ),
+});
diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6
new file mode 100644
index 00000000..1d43047a
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6
@@ -0,0 +1,20 @@
+import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
+import { default as discourseComputed } from "discourse-common/utils/decorators";
+
+export default SelectKitRowComponent.extend({
+ classNameBindings: ["isDisabled:disabled"],
+
+ @discourseComputed("item")
+ isDisabled() {
+ return this.item.disabled;
+ },
+
+ click(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ if (!this.item.disabled) {
+ this.selectKit.select(this.rowValue, this.item);
+ }
+ return false;
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-table-field.js.es6 b/assets/javascripts/discourse/components/wizard-table-field.js.es6
new file mode 100644
index 00000000..93859f1f
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-table-field.js.es6
@@ -0,0 +1,139 @@
+import Component from "@ember/component";
+import { action } from "@ember/object";
+import { equal, notEmpty } from "@ember/object/computed";
+import discourseComputed from "discourse-common/utils/decorators";
+import I18n from "I18n";
+
+export default Component.extend({
+ classNameBindings: ["value.type"],
+ isText: equal("value.type", "text"),
+ isComposer: equal("value.type", "composer"),
+ isDate: equal("value.type", "date"),
+ isTime: equal("value.type", "time"),
+ isDateTime: equal("value.type", "date_time"),
+ isNumber: equal("value.type", "number"),
+ isCheckbox: equal("value.type", "checkbox"),
+ isUrl: equal("value.type", "url"),
+ isUpload: equal("value.type", "upload"),
+ isDropdown: equal("value.type", "dropdown"),
+ isTag: equal("value.type", "tag"),
+ isCategory: equal("value.type", "category"),
+ isGroup: equal("value.type", "group"),
+ isUserSelector: equal("value.type", "user_selector"),
+ isSubmittedAt: equal("field", "submitted_at"),
+ isComposerPreview: equal("value.type", "composer_preview"),
+ textState: "text-collapsed",
+ toggleText: I18n.t("admin.wizard.expand_text"),
+
+ @discourseComputed("value", "isUser", "isSubmittedAt")
+ hasValue(value, isUser, isSubmittedAt) {
+ if (isUser || isSubmittedAt) {
+ return value;
+ }
+ return value && value.value;
+ },
+
+ @discourseComputed("field", "value.type")
+ isUser(field, type) {
+ return field === "username" || field === "user" || type === "user";
+ },
+
+ @discourseComputed("value.type")
+ isLongtext(type) {
+ return type === "textarea" || type === "long_text";
+ },
+
+ @discourseComputed("value")
+ checkboxValue(value) {
+ const isCheckbox = this.get("isCheckbox");
+ if (isCheckbox) {
+ if (value.value.includes("true")) {
+ return true;
+ } else if (value.value.includes("false")) {
+ return false;
+ }
+ }
+ },
+
+ @action
+ expandText() {
+ const state = this.get("textState");
+
+ if (state === "text-collapsed") {
+ this.set("textState", "text-expanded");
+ this.set("toggleText", I18n.t("admin.wizard.collapse_text"));
+ } else if (state === "text-expanded") {
+ this.set("textState", "text-collapsed");
+ this.set("toggleText", I18n.t("admin.wizard.expand_text"));
+ }
+ },
+
+ @discourseComputed("value")
+ file(value) {
+ const isUpload = this.get("isUpload");
+ if (isUpload) {
+ return value.value;
+ }
+ },
+
+ @discourseComputed("value")
+ submittedUsers(value) {
+ const isUserSelector = this.get("isUserSelector");
+ const users = [];
+
+ if (isUserSelector) {
+ const userData = value.value;
+ const usernames = [];
+
+ if (userData.indexOf(",")) {
+ usernames.push(...userData.split(","));
+
+ usernames.forEach((u) => {
+ const user = {
+ username: u,
+ url: `/u/${u}`,
+ };
+ users.push(user);
+ });
+ }
+ }
+ return users;
+ },
+
+ @discourseComputed("isUser", "field", "value")
+ username(isUser, field, value) {
+ if (isUser) {
+ return value.username;
+ }
+ if (field === "username") {
+ return value.value;
+ }
+ return null;
+ },
+
+ showUsername: notEmpty("username"),
+
+ @discourseComputed("username")
+ userProfileUrl(username) {
+ if (username) {
+ return `/u/${username}`;
+ }
+ return "/";
+ },
+
+ @discourseComputed("value")
+ categoryUrl(value) {
+ const isCategory = this.get("isCategory");
+ if (isCategory) {
+ return `/c/${value.value}`;
+ }
+ },
+
+ @discourseComputed("value")
+ groupUrl(value) {
+ const isGroup = this.get("isGroup");
+ if (isGroup) {
+ return `/g/${value.value}`;
+ }
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 b/assets/javascripts/discourse/components/wizard-text-editor.js.es6
index 88d7200c..b6d07cef 100644
--- a/assets/javascripts/discourse/components/wizard-text-editor.js.es6
+++ b/assets/javascripts/discourse/components/wizard-text-editor.js.es6
@@ -5,11 +5,7 @@ import { scheduleOnce } from "@ember/runloop";
import Component from "@ember/component";
import I18n from "I18n";
-const excludedUserProperties = [
- "avatar",
- "profile_background",
- "card_background",
-];
+const excludedUserProperties = ["profile_background", "card_background"];
export default Component.extend({
classNames: "wizard-text-editor",
@@ -52,12 +48,12 @@ export default Component.extend({
@discourseComputed("wizardFields")
wizardFieldList(wizardFields) {
- return wizardFields.map((f) => ` w{${f.id}}`);
+ return (wizardFields || []).map((f) => ` w{${f.id}}`);
},
@discourseComputed("wizardActions")
wizardActionList(wizardActions) {
- return wizardActions.map((a) => ` w{${a.id}}`);
+ return (wizardActions || []).map((a) => ` w{${a.id}}`);
},
actions: {
diff --git a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
index f76722fc..f893d4ac 100644
--- a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
+++ b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
@@ -1,3 +1,7 @@
{{#if currentUser.admin}}
{{nav-item route="adminWizards" label="admin.wizard.nav_label"}}
+
+ {{#if wizardErrorNotice}}
+ {{d-icon "exclaimation-circle"}}
+ {{/if}}
{{/if}}
diff --git a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs
index 4b5d673d..5ce96f2f 100644
--- a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs
+++ b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs
@@ -6,11 +6,11 @@
{{combo-box
- value=wizardListVal
- content=wizardList
- onChange=(action "changeWizard")
- options=(hash
- none="admin.wizard.select"
- )}}
+ value=wizardListVal
+ content=wizardList
+ onChange=(action "changeWizard")
+ options=(hash
+ none="admin.wizard.select"
+ )}}
diff --git a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.js.es6 b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.js.es6
index 16352f95..7004c317 100644
--- a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.js.es6
+++ b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.js.es6
@@ -1,9 +1,9 @@
-import CustomWizard from "../../models/custom-wizard";
+import CustomWizardAdmin from "../../models/custom-wizard-admin";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default {
setupComponent(attrs, component) {
- CustomWizard.all()
+ CustomWizardAdmin.all()
.then((result) => {
component.set("wizardList", result);
})
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
index 5dba2d7f..b89cc447 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
@@ -11,7 +11,7 @@ export default Controller.extend({
queryParams: ["refresh_list"],
loadingSubscriptions: false,
notAuthorized: not("api.authorized"),
- endpointMethods: selectKitContent(["GET", "PUT", "POST", "PATCH", "DELETE"]),
+ endpointMethods: selectKitContent(["PUT", "POST", "PATCH", "DELETE"]),
showRemove: not("isNew"),
showRedirectUri: and("threeLeggedOauth", "api.name"),
responseIcon: null,
@@ -88,6 +88,11 @@ export default Controller.extend({
twoLeggedOauth: equal("api.authType", "oauth_2"),
threeLeggedOauth: equal("api.authType", "oauth_3"),
+ @discourseComputed("api.isNew")
+ nameClass(isNew) {
+ return isNew ? "new" : "saved";
+ },
+
actions: {
addParam() {
this.get("api.authParams").pushObject({});
@@ -149,7 +154,6 @@ export default Controller.extend({
const api = this.get("api");
const name = api.name;
const authType = api.authType;
- let refreshList = false; // eslint-disable-line
let error;
if (!name || !authType) {
@@ -164,11 +168,6 @@ export default Controller.extend({
data["title"] = api.title;
}
- const originalTitle = this.get("api.originalTitle");
- if (api.get("isNew") || (originalTitle && api.title !== originalTitle)) {
- refreshList = true;
- }
-
if (api.get("isNew")) {
data["new"] = true;
}
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6
new file mode 100644
index 00000000..4754c577
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6
@@ -0,0 +1,14 @@
+import Controller from "@ember/controller";
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+
+export default Controller.extend(ModalFunctionality, {
+ actions: {
+ save() {
+ this.send("closeModal");
+ },
+
+ resetToDefault() {
+ this.get("model.reset")();
+ },
+ },
+});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
index 404c6afd..5d47d8d7 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
@@ -4,7 +4,7 @@ import CustomWizardCustomField from "../models/custom-wizard-custom-field";
export default Controller.extend({
messageKey: "create",
fieldKeys: ["klass", "type", "name", "serializers"],
- documentationUrl: "https://thepavilion.io/t/3572",
+ documentationUrl: "https://discourse.pluginmanager.org/t/custom-fields",
actions: {
addField() {
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6
new file mode 100644
index 00000000..7e3fdff1
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6
@@ -0,0 +1,52 @@
+import discourseComputed from "discourse-common/utils/decorators";
+import { notEmpty } from "@ember/object/computed";
+import CustomWizardLogs from "../models/custom-wizard-logs";
+import Controller from "@ember/controller";
+
+export default Controller.extend({
+ refreshing: false,
+ hasLogs: notEmpty("logs"),
+ page: 0,
+ canLoadMore: true,
+ logs: [],
+ messageKey: "viewing",
+
+ loadLogs() {
+ if (!this.canLoadMore) {
+ return;
+ }
+ const page = this.get("page");
+ const wizardId = this.get("wizard.id");
+
+ this.set("refreshing", true);
+
+ CustomWizardLogs.list(wizardId, page)
+ .then((result) => {
+ this.set("logs", this.logs.concat(result.logs));
+ })
+ .finally(() => this.set("refreshing", false));
+ },
+
+ @discourseComputed("hasLogs", "refreshing")
+ noResults(hasLogs, refreshing) {
+ return !hasLogs && !refreshing;
+ },
+
+ actions: {
+ loadMore() {
+ if (!this.loadingMore && this.logs.length < this.total) {
+ this.set("page", (this.page += 1));
+ this.loadLogs();
+ }
+ },
+
+ refresh() {
+ this.setProperties({
+ canLoadMore: true,
+ page: 0,
+ logs: [],
+ });
+ this.loadLogs();
+ },
+ },
+});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6
index 9559b01b..7388a8d6 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6
@@ -1,50 +1,34 @@
-import discourseComputed from "discourse-common/utils/decorators";
-import { notEmpty } from "@ember/object/computed";
-import CustomWizardLogs from "../models/custom-wizard-logs";
import Controller from "@ember/controller";
+import { default as discourseComputed } from "discourse-common/utils/decorators";
export default Controller.extend({
- refreshing: false,
- hasLogs: notEmpty("logs"),
- page: 0,
- canLoadMore: true,
- logs: [],
+ documentationUrl: "https://thepavilion.io/t/2818",
- loadLogs() {
- if (!this.canLoadMore) {
- return;
+ @discourseComputed("wizardId")
+ wizardName(wizardId) {
+ let currentWizard = this.wizardList.find(
+ (wizard) => wizard.id === wizardId
+ );
+ if (currentWizard) {
+ return currentWizard.name;
+ }
+ },
+
+ @discourseComputed("wizardName")
+ messageOpts(wizardName) {
+ return {
+ wizardName,
+ };
+ },
+
+ @discourseComputed("wizardId")
+ messageKey(wizardId) {
+ let key = "select";
+
+ if (wizardId) {
+ key = "viewing";
}
- this.set("refreshing", true);
-
- CustomWizardLogs.list()
- .then((result) => {
- if (!result || result.length === 0) {
- this.set("canLoadMore", false);
- }
- this.set("logs", this.logs.concat(result));
- })
- .finally(() => this.set("refreshing", false));
- },
-
- @discourseComputed("hasLogs", "refreshing")
- noResults(hasLogs, refreshing) {
- return !hasLogs && !refreshing;
- },
-
- actions: {
- loadMore() {
- this.set("page", (this.page += 1));
- this.loadLogs();
- },
-
- refresh() {
- this.setProperties({
- canLoadMore: true,
- page: 0,
- logs: [],
- });
- this.loadLogs();
- },
+ return key;
},
});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6
index 7228d164..67016bd7 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6
@@ -7,7 +7,7 @@ import I18n from "I18n";
import { underscore } from "@ember/string";
export default Controller.extend({
- messageUrl: "https://thepavilion.io/t/3652",
+ messageUrl: "https://discourse.pluginmanager.org/t/wizard-manager",
messageKey: "info",
messageIcon: "info-circle",
messageClass: "info",
@@ -68,7 +68,7 @@ export default Controller.extend({
file: null,
filename: null,
});
- $("#file-upload").val("");
+ document.getElementById("custom-wizard-file-upload").value = "";
},
@observes("importing", "destroying")
@@ -83,7 +83,7 @@ export default Controller.extend({
actions: {
upload() {
- $("#file-upload").click();
+ document.getElementById("custom-wizard-file-upload").click();
},
clearFile() {
@@ -102,7 +102,7 @@ export default Controller.extend({
if (maxFileSize < file.size) {
this.setMessage("error", "file_size_error");
this.set("file", null);
- $("#file-upload").val("");
+ document.getElementById("custom-wizard-file-upload").value = "";
} else {
this.setProperties({
file,
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6
index f5f9926d..41dabbb4 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6
@@ -1,6 +1,72 @@
import Controller from "@ember/controller";
+import { empty } from "@ember/object/computed";
+import discourseComputed from "discourse-common/utils/decorators";
import { fmt } from "discourse/lib/computed";
+import showModal from "discourse/lib/show-modal";
+import CustomWizardAdmin from "../models/custom-wizard-admin";
+import { formatModel } from "../lib/wizard-submission";
export default Controller.extend({
downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"),
+ noResults: empty("submissions"),
+ page: 0,
+ total: 0,
+
+ loadMoreSubmissions() {
+ const page = this.get("page");
+ const wizardId = this.get("wizard.id");
+
+ this.set("loadingMore", true);
+ CustomWizardAdmin.submissions(wizardId, page)
+ .then((result) => {
+ if (result.submissions) {
+ const { submissions } = formatModel(result);
+
+ this.get("submissions").pushObjects(submissions);
+ }
+ })
+ .finally(() => {
+ this.set("loadingMore", false);
+ });
+ },
+
+ @discourseComputed("submissions.[]", "fields.@each.enabled")
+ displaySubmissions(submissions, fields) {
+ let result = [];
+
+ submissions.forEach((submission) => {
+ let sub = {};
+
+ Object.keys(submission).forEach((fieldId) => {
+ if (fields.some((f) => f.id === fieldId && f.enabled)) {
+ sub[fieldId] = submission[fieldId];
+ }
+ });
+ result.push(sub);
+ });
+
+ return result;
+ },
+
+ actions: {
+ loadMore() {
+ if (!this.loadingMore && this.submissions.length < this.total) {
+ this.set("page", this.get("page") + 1);
+ this.loadMoreSubmissions();
+ }
+ },
+
+ showEditColumnsModal() {
+ return showModal("admin-wizards-columns", {
+ model: {
+ columns: this.get("fields"),
+ reset: () => {
+ this.get("fields").forEach((field) => {
+ field.set("enabled", true);
+ });
+ },
+ },
+ });
+ },
+ },
});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6
new file mode 100644
index 00000000..7388a8d6
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6
@@ -0,0 +1,34 @@
+import Controller from "@ember/controller";
+import { default as discourseComputed } from "discourse-common/utils/decorators";
+
+export default Controller.extend({
+ documentationUrl: "https://thepavilion.io/t/2818",
+
+ @discourseComputed("wizardId")
+ wizardName(wizardId) {
+ let currentWizard = this.wizardList.find(
+ (wizard) => wizard.id === wizardId
+ );
+ if (currentWizard) {
+ return currentWizard.name;
+ }
+ },
+
+ @discourseComputed("wizardName")
+ messageOpts(wizardName) {
+ return {
+ wizardName,
+ };
+ },
+
+ @discourseComputed("wizardId")
+ messageKey(wizardId) {
+ let key = "select";
+
+ if (wizardId) {
+ key = "viewing";
+ }
+
+ return key;
+ },
+});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6
index 1eeb62e6..c9a80e0e 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6
@@ -36,7 +36,8 @@ export default Controller.extend({
@discourseComputed("wizard.id")
wizardUrl(wizardId) {
- return window.location.origin + "/w/" + dasherize(wizardId);
+ let baseUrl = window.location.href.split("/admin");
+ return baseUrl[0] + "/w/" + dasherize(wizardId);
},
@discourseComputed("wizard.after_time_scheduled")
@@ -92,7 +93,11 @@ export default Controller.extend({
wizard
.save(opts)
.then((result) => {
- this.send("afterSave", result.wizard_id);
+ if (result.wizard_id) {
+ this.send("afterSave", result.wizard_id);
+ } else if (result.errors) {
+ this.set("error", result.errors.join(", "));
+ }
})
.catch((result) => {
this.set("error", this.getErrorMessage(result));
@@ -118,10 +123,6 @@ export default Controller.extend({
controller.setup();
},
- toggleAdvanced() {
- this.toggleProperty("wizard.showAdvanced");
- },
-
copyUrl() {
const $copyRange = $('');
$copyRange.html(this.wizardUrl);
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6
index ddd63337..5178a1e1 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6
@@ -21,5 +21,6 @@ export default Controller.extend({
return key;
},
- messageUrl: "https://thepavilion.io/c/knowledge/discourse/custom-wizard",
+ messageUrl:
+ "https://discourse.pluginmanager.org/c/discourse-custom-wizard/documentation",
});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6
new file mode 100644
index 00000000..518893b6
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6
@@ -0,0 +1,9 @@
+import Controller from "@ember/controller";
+import { equal, or } from "@ember/object/computed";
+
+export default Controller.extend({
+ businessSubscription: equal("subscriptionType", "business"),
+ communitySubscription: equal("subscriptionType", "community"),
+ standardSubscription: equal("subscriptionType", "standard"),
+ showApi: or("businessSubscription", "communitySubscription"),
+});
diff --git a/assets/javascripts/discourse/controllers/custom-wizard-index.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard-index.js.es6
new file mode 100644
index 00000000..f56db02d
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/custom-wizard-index.js.es6
@@ -0,0 +1,24 @@
+import Controller from "@ember/controller";
+import { or } from "@ember/object/computed";
+import discourseComputed from "discourse-common/utils/decorators";
+
+const reasons = {
+ noWizard: "none",
+ requiresLogin: "requires_login",
+ notPermitted: "not_permitted",
+ completed: "completed",
+};
+
+export default Controller.extend({
+ noAccess: or("noWizard", "requiresLogin", "notPermitted", "completed"),
+
+ @discourseComputed("noAccessReason")
+ noAccessI18nKey(reason) {
+ return reason ? `wizard.${reasons[reason]}` : "wizard.none";
+ },
+
+ @discourseComputed
+ noAccessReason() {
+ return Object.keys(reasons).find((reason) => this.get(reason));
+ },
+});
diff --git a/assets/javascripts/wizard/controllers/custom-step.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard-step.js.es6
similarity index 74%
rename from assets/javascripts/wizard/controllers/custom-step.js.es6
rename to assets/javascripts/discourse/controllers/custom-wizard-step.js.es6
index b44c0fca..2dca2e70 100644
--- a/assets/javascripts/wizard/controllers/custom-step.js.es6
+++ b/assets/javascripts/discourse/controllers/custom-wizard-step.js.es6
@@ -1,7 +1,10 @@
-import StepController from "wizard/controllers/step";
+import Controller from "@ember/controller";
import getUrl from "discourse-common/lib/get-url";
-export default StepController.extend({
+export default Controller.extend({
+ wizard: null,
+ step: null,
+
actions: {
goNext(response) {
let nextStepId = response["next_step_id"];
@@ -12,12 +15,12 @@ export default StepController.extend({
const wizardId = this.get("wizard.id");
window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`);
} else {
- this.transitionToRoute("custom.step", nextStepId);
+ this.transitionToRoute("customWizardStep", nextStepId);
}
},
goBack() {
- this.transitionToRoute("custom.step", this.get("step.previous"));
+ this.transitionToRoute("customWizardStep", this.get("step.previous"));
},
showMessage(message) {
diff --git a/assets/javascripts/discourse/controllers/custom-wizard.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard.js.es6
new file mode 100644
index 00000000..35f964e2
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/custom-wizard.js.es6
@@ -0,0 +1,5 @@
+import Controller from "@ember/controller";
+
+export default Controller.extend({
+ queryParams: ["reset"],
+});
diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
index 90ab5359..272e276e 100644
--- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
+++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
@@ -43,7 +43,16 @@ export default {
}
);
- this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
+ this.route(
+ "adminWizardsLogs",
+ { path: "/logs", resetNamespace: true },
+ function () {
+ this.route("adminWizardsLogsShow", {
+ path: "/:wizardId/",
+ resetNamespace: true,
+ });
+ }
+ );
this.route("adminWizardsManager", {
path: "/manager",
diff --git a/assets/javascripts/discourse/custom-wizard-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-route-map.js.es6
new file mode 100644
index 00000000..08606301
--- /dev/null
+++ b/assets/javascripts/discourse/custom-wizard-route-map.js.es6
@@ -0,0 +1,12 @@
+export default function () {
+ this.route(
+ "customWizard",
+ { path: "/w/:wizard_id", resetNamespace: true },
+ function () {
+ this.route("customWizardStep", {
+ path: "/steps/:step_id",
+ resetNamespace: true,
+ });
+ }
+ );
+}
diff --git a/assets/javascripts/wizard/helpers/char-counter.js.es6 b/assets/javascripts/discourse/helpers/char-counter.js.es6
similarity index 100%
rename from assets/javascripts/wizard/helpers/char-counter.js.es6
rename to assets/javascripts/discourse/helpers/char-counter.js.es6
diff --git a/assets/javascripts/discourse/helpers/custom-wizard.js.es6 b/assets/javascripts/discourse/helpers/custom-wizard.js.es6
deleted file mode 100644
index fb5063cc..00000000
--- a/assets/javascripts/discourse/helpers/custom-wizard.js.es6
+++ /dev/null
@@ -1,6 +0,0 @@
-import { registerUnbound } from "discourse-common/lib/helpers";
-import { dasherize } from "@ember/string";
-
-registerUnbound("dasherize", function (string) {
- return dasherize(string);
-});
diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
index 20787b4e..2d13e703 100644
--- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
+++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
@@ -1,11 +1,12 @@
import DiscourseURL from "discourse/lib/url";
import { withPluginApi } from "discourse/lib/plugin-api";
import getUrl from "discourse-common/lib/get-url";
+import { observes } from "discourse-common/utils/decorators";
export default {
name: "custom-wizard-edits",
initialize(container) {
- const siteSettings = container.lookup("site-settings:main");
+ const siteSettings = container.lookup("service:site-settings");
if (!siteSettings.custom_wizard_enabled) {
return;
@@ -19,7 +20,7 @@ export default {
return existing.apply(this, [path, opts]);
};
- withPluginApi("0.8.7", (api) => {
+ withPluginApi("0.8.36", (api) => {
api.modifyClass("component:d-navigation", {
pluginId: "custom-wizard",
actions: {
@@ -35,6 +36,50 @@ export default {
},
},
});
+
+ api.modifyClass("component:uppy-image-uploader", {
+ pluginId: "custom-wizard",
+ // Needed to ensure appEvents get registered when navigating between steps
+ @observes("id")
+ initOnStepChange() {
+ if (/wizard-field|wizard-step/.test(this.id)) {
+ this._initialize();
+ }
+ },
+ });
+
+ api.modifyClass("component:d-editor", {
+ pluginId: "custom-wizard",
+
+ didInsertElement() {
+ this._super(...arguments);
+
+ if (this.wizardComposer) {
+ this.appEvents.on(
+ `wizard-editor:insert-text`,
+ this,
+ "_wizardInsertText"
+ );
+ this.appEvents.on(
+ "wizard-editor:replace-text",
+ this,
+ "_wizardReplaceText"
+ );
+ }
+ },
+
+ _wizardInsertText(text, options) {
+ if (this.session.wizardEventFieldId === this.fieldId) {
+ this.insertText(text, options);
+ }
+ },
+
+ _wizardReplaceText(oldVal, newVal, opts = {}) {
+ if (this.session.wizardEventFieldId === this.fieldId) {
+ this.replaceText(oldVal, newVal, opts);
+ }
+ },
+ });
});
},
};
diff --git a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6
index e413cd17..5667bdf8 100644
--- a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6
+++ b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6
@@ -5,8 +5,8 @@ export default {
after: "message-bus",
initialize: function (container) {
- const messageBus = container.lookup("message-bus:main");
- const siteSettings = container.lookup("site-settings:main");
+ const messageBus = container.lookup("service:message-bus");
+ const siteSettings = container.lookup("service:site-settings");
if (!siteSettings.custom_wizard_enabled || !messageBus) {
return;
diff --git a/assets/javascripts/discourse/lib/wizard-json.js.es6 b/assets/javascripts/discourse/lib/wizard-json.js.es6
index 79da60cb..95eaba49 100644
--- a/assets/javascripts/discourse/lib/wizard-json.js.es6
+++ b/assets/javascripts/discourse/lib/wizard-json.js.es6
@@ -97,11 +97,6 @@ function buildObjectArray(json, type) {
if (present(json)) {
json.forEach((objJson, objectIndex) => {
let object = buildObject(objJson, type, objectIndex);
-
- if (hasAdvancedProperties(object, type)) {
- object.set("showAdvanced", true);
- }
-
array.pushObject(object);
});
}
@@ -112,21 +107,11 @@ function buildObjectArray(json, type) {
function buildBasicProperties(json, type, props, objectIndex = null) {
listProperties(type).forEach((p) => {
props[p] = buildProperty(json, p, type, objectIndex);
-
- if (hasAdvancedProperties(json, type)) {
- props.showAdvanced = true;
- }
});
return props;
}
-function hasAdvancedProperties(object, type) {
- return Object.keys(object).some((p) => {
- return wizardSchema[type].advanced.indexOf(p) > -1 && present(object[p]);
- });
-}
-
/// to be removed: necessary due to action array being moved from step to wizard
function actionPatch(json) {
let actions = json.actions || [];
diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6
index 5445223a..8c3323ff 100644
--- a/assets/javascripts/discourse/lib/wizard-schema.js.es6
+++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6
@@ -19,7 +19,6 @@ const wizard = {
permitted: null,
},
mapped: ["permitted"],
- advanced: ["restart_on_revisit"],
required: ["id"],
dependent: {
after_time: "after_time_scheduled",
@@ -41,8 +40,8 @@ const step = {
id: null,
index: null,
title: null,
- key: null,
banner: null,
+ banner_upload_id: null,
raw_description: null,
required_data: null,
required_data_message: null,
@@ -51,7 +50,6 @@ const step = {
force_final: false,
},
mapped: ["required_data", "permitted_params", "condition", "index"],
- advanced: ["required_data", "permitted_params", "condition", "index"],
required: ["id"],
dependent: {},
objectArrays: {
@@ -68,15 +66,15 @@ const field = {
index: null,
label: null,
image: null,
+ image_upload_id: null,
description: null,
+ property: null,
required: null,
- key: null,
type: null,
condition: null,
},
types: {},
mapped: ["prefill", "content", "condition", "index"],
- advanced: ["property", "key", "condition", "index"],
required: ["id", "type"],
dependent: {},
objectArrays: {},
@@ -100,6 +98,8 @@ const action = {
custom_fields: null,
skip_redirect: null,
suppress_notifications: null,
+ add_event: null,
+ add_location: null,
},
send_message: {
title: null,
@@ -132,6 +132,12 @@ const action = {
wizard_user: true,
usernames: null,
},
+ watch_tags: {
+ tags: null,
+ notification_level: null,
+ wizard_user: true,
+ usernames: null,
+ },
send_to_api: {
api: null,
api_endpoint: null,
@@ -196,23 +202,27 @@ const action = {
"messageable_level",
"visibility_level",
"members_visibility_level",
- ],
- advanced: [
- "code",
- "custom_fields",
- "skip_redirect",
- "suppress_notifications",
- "required",
+ "add_event",
+ "add_location",
],
required: ["id", "type"],
dependent: {},
objectArrays: {},
};
+const custom_field = {
+ klass: ["topic", "post", "group", "category"],
+ type: ["string", "boolean", "integer", "json"],
+};
+
+field.type = Object.keys(field.types);
+action.type = Object.keys(action.types);
+
const wizardSchema = {
wizard,
step,
field,
+ custom_field,
action,
};
@@ -224,7 +234,7 @@ export function buildFieldValidations(validations) {
wizardSchema.field.validations = validations;
}
-const siteSettings = getOwner(this).lookup("site-settings:main");
+const siteSettings = getOwner(this).lookup("service:site-settings");
if (siteSettings.wizard_apis_enabled) {
wizardSchema.action.types.send_to_api = {
api: null,
diff --git a/assets/javascripts/discourse/lib/wizard-submission.js.es6 b/assets/javascripts/discourse/lib/wizard-submission.js.es6
new file mode 100644
index 00000000..8fa2a3be
--- /dev/null
+++ b/assets/javascripts/discourse/lib/wizard-submission.js.es6
@@ -0,0 +1,39 @@
+import EmberObject from "@ember/object";
+
+function formatModel(model) {
+ let fields = [
+ EmberObject.create({
+ id: "submitted_at",
+ label: "Submitted At",
+ enabled: true,
+ }),
+ EmberObject.create({ id: "username", label: "User", enabled: true }),
+ ];
+ let submissions = [];
+
+ model.submissions.forEach((s) => {
+ let submission = {
+ submitted_at: s.submitted_at,
+ username: s.user,
+ };
+
+ Object.keys(s.fields).forEach((fieldId) => {
+ if (!fields.some((field) => field.id === fieldId)) {
+ fields.push(
+ EmberObject.create({
+ id: fieldId,
+ label: s.fields[fieldId].label,
+ enabled: true,
+ })
+ );
+ }
+ submission[fieldId] = s.fields[fieldId];
+ });
+
+ submissions.push(EmberObject.create(submission));
+ });
+
+ return { fields, submissions };
+}
+
+export { formatModel };
diff --git a/assets/javascripts/discourse/lib/wizard.js.es6 b/assets/javascripts/discourse/lib/wizard.js.es6
index 98bdbfdd..2eaccb17 100644
--- a/assets/javascripts/discourse/lib/wizard.js.es6
+++ b/assets/javascripts/discourse/lib/wizard.js.es6
@@ -1,5 +1,6 @@
import EmberObject from "@ember/object";
import wizardSchema from "./wizard-schema";
+import I18n from "I18n";
function selectKitContent(content) {
return content.map((i) => ({ id: i, name: i }));
@@ -33,6 +34,10 @@ function camelCase(string) {
});
}
+function translationOrText(i18nKey, text) {
+ return I18n.findTranslation(i18nKey) ? I18n.t(i18nKey) : text;
+}
+
const userProperties = [
"name",
"username",
@@ -121,4 +126,5 @@ export {
notificationLevels,
wizardFieldList,
sentenceCase,
+ translationOrText,
};
diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6
new file mode 100644
index 00000000..34276c3f
--- /dev/null
+++ b/assets/javascripts/discourse/mixins/subscription.js.es6
@@ -0,0 +1,53 @@
+import Mixin from "@ember/object/mixin";
+import { getOwner } from "discourse-common/lib/get-owner";
+import { readOnly } from "@ember/object/computed";
+import discourseComputed from "discourse-common/utils/decorators";
+
+const PRODUCT_PAGE = "https://custom-wizard.pavilion.tech";
+const SUPPORT_MESSAGE =
+ "https://coop.pavilion.tech/new-message?username=support&title=Custom%20Wizard%20Support";
+const MANAGER_CATEGORY =
+ "https://discourse.pluginmanager.org/c/discourse-custom-wizard";
+
+export default Mixin.create({
+ subscriptionLandingUrl: PRODUCT_PAGE,
+ subscriptionClientUrl: "/admin/plugins/subscription-client",
+
+ @discourseComputed
+ adminWizards() {
+ return getOwner(this).lookup("controller:admin-wizards");
+ },
+
+ subscribed: readOnly("adminWizards.subscribed"),
+ subscriptionType: readOnly("adminWizards.subscriptionType"),
+ businessSubscription: readOnly("adminWizards.businessSubscription"),
+ communitySubscription: readOnly("adminWizards.communitySubscription"),
+ standardSubscription: readOnly("adminWizards.standardSubscription"),
+ subscriptionAttributes: readOnly("adminWizards.subscriptionAttributes"),
+ subscriptionClientInstalled: readOnly(
+ "adminWizards.subscriptionClientInstalled"
+ ),
+
+ @discourseComputed("subscriptionClientInstalled")
+ subscriptionLink(subscriptionClientInstalled) {
+ return subscriptionClientInstalled
+ ? this.subscriptionClientUrl
+ : this.subscriptionLandingUrl;
+ },
+
+ @discourseComputed("subscriptionType")
+ subscriptionCtaLink(subscriptionType) {
+ switch (subscriptionType) {
+ case "none":
+ return PRODUCT_PAGE;
+ case "standard":
+ return SUPPORT_MESSAGE;
+ case "business":
+ return SUPPORT_MESSAGE;
+ case "community":
+ return MANAGER_CATEGORY;
+ default:
+ return PRODUCT_PAGE;
+ }
+ },
+});
diff --git a/assets/javascripts/discourse/mixins/valid-state.js.es6 b/assets/javascripts/discourse/mixins/valid-state.js.es6
new file mode 100644
index 00000000..ca86d7e4
--- /dev/null
+++ b/assets/javascripts/discourse/mixins/valid-state.js.es6
@@ -0,0 +1,36 @@
+import discourseComputed from "discourse-common/utils/decorators";
+
+export const States = {
+ UNCHECKED: 0,
+ INVALID: 1,
+ VALID: 2,
+};
+
+export default {
+ _validState: null,
+ errorDescription: null,
+
+ init() {
+ this._super(...arguments);
+ this.set("_validState", States.UNCHECKED);
+ },
+
+ @discourseComputed("_validState")
+ valid: (state) => state === States.VALID,
+
+ @discourseComputed("_validState")
+ invalid: (state) => state === States.INVALID,
+
+ @discourseComputed("_validState")
+ unchecked: (state) => state === States.UNCHECKED,
+
+ setValid(valid, description) {
+ this.set("_validState", valid ? States.VALID : States.INVALID);
+
+ if (!valid && description && description.length) {
+ this.set("errorDescription", description);
+ } else {
+ this.set("errorDescription", null);
+ }
+ },
+};
diff --git a/assets/javascripts/discourse/models/custom-wizard-admin.js.es6 b/assets/javascripts/discourse/models/custom-wizard-admin.js.es6
new file mode 100644
index 00000000..65c7aa7f
--- /dev/null
+++ b/assets/javascripts/discourse/models/custom-wizard-admin.js.es6
@@ -0,0 +1,230 @@
+import EmberObject from "@ember/object";
+import { buildProperties, mapped, present } from "../lib/wizard-json";
+import { listProperties, snakeCase } from "../lib/wizard";
+import wizardSchema from "../lib/wizard-schema";
+import { Promise } from "rsvp";
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+
+const CustomWizardAdmin = EmberObject.extend({
+ save(opts) {
+ return new Promise((resolve, reject) => {
+ let wizard = this.buildJson(this, "wizard");
+
+ if (wizard.error) {
+ reject(wizard);
+ }
+
+ let data = {
+ wizard,
+ };
+
+ if (opts.create) {
+ data.create = true;
+ }
+
+ ajax(`/admin/wizards/wizard/${wizard.id}`, {
+ type: "PUT",
+ contentType: "application/json",
+ data: JSON.stringify(data),
+ }).then((result) => {
+ if (result.backend_validation_error) {
+ reject(result);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ },
+
+ buildJson(object, type, result = {}) {
+ let objectType = object.type || null;
+
+ if (wizardSchema[type].types) {
+ if (!objectType) {
+ result.error = {
+ type: "required",
+ params: { type, property: "type" },
+ };
+ return result;
+ }
+ }
+
+ for (let property of listProperties(type, { objectType })) {
+ let value = object.get(property);
+
+ result = this.validateValue(property, value, object, type, result);
+
+ if (result.error) {
+ break;
+ }
+
+ if (mapped(property, type)) {
+ value = this.buildMappedJson(value);
+ }
+
+ if (value !== undefined && value !== null) {
+ result[property] = value;
+ }
+ }
+
+ if (!result.error) {
+ for (let arrayObjectType of Object.keys(
+ wizardSchema[type].objectArrays
+ )) {
+ let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
+ let objectArray = object.get(arraySchema.property);
+
+ if (arraySchema.required && !present(objectArray)) {
+ result.error = {
+ type: "required",
+ params: { type, property: arraySchema.property },
+ };
+ break;
+ }
+
+ result[arraySchema.property] = [];
+
+ for (let item of objectArray) {
+ let itemProps = this.buildJson(item, arrayObjectType);
+
+ if (itemProps.error) {
+ result.error = itemProps.error;
+ break;
+ } else {
+ result[arraySchema.property].push(itemProps);
+ }
+ }
+ }
+ }
+
+ return result;
+ },
+
+ validateValue(property, value, object, type, result) {
+ if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
+ result.error = {
+ type: "required",
+ params: { type, property },
+ };
+ }
+
+ let dependent = wizardSchema[type].dependent[property];
+ if (dependent && value && !object[dependent]) {
+ result.error = {
+ type: "dependent",
+ params: { property, dependent },
+ };
+ }
+
+ if (property === "api_body") {
+ try {
+ value = JSON.parse(value);
+ } catch (e) {
+ result.error = {
+ type: "invalid",
+ params: { type, property },
+ };
+ }
+ }
+
+ return result;
+ },
+
+ buildMappedJson(value) {
+ if (typeof value === "string" || Number.isInteger(value)) {
+ return value;
+ }
+ if (!value || !value.length) {
+ return false;
+ }
+
+ let inputs = value;
+ let result = [];
+
+ inputs.forEach((inpt) => {
+ let input = {
+ type: inpt.type,
+ };
+
+ if (inpt.connector) {
+ input.connector = inpt.connector;
+ }
+
+ if (present(inpt.output)) {
+ input.output = inpt.output;
+ input.output_type = snakeCase(inpt.output_type);
+ input.output_connector = inpt.output_connector;
+ }
+
+ if (present(inpt.pairs)) {
+ input.pairs = [];
+
+ inpt.pairs.forEach((pr) => {
+ if (present(pr.key) && present(pr.value)) {
+ let pairParams = {
+ index: pr.index,
+ key: pr.key,
+ key_type: snakeCase(pr.key_type),
+ value: pr.value,
+ value_type: snakeCase(pr.value_type),
+ connector: pr.connector,
+ };
+
+ input.pairs.push(pairParams);
+ }
+ });
+ }
+
+ if (
+ (input.type === "assignment" && present(input.output)) ||
+ present(input.pairs)
+ ) {
+ result.push(input);
+ }
+ });
+
+ if (!result.length) {
+ result = false;
+ }
+
+ return result;
+ },
+
+ remove() {
+ return ajax(`/admin/wizards/wizard/${this.id}`, {
+ type: "DELETE",
+ })
+ .then(() => this.destroy())
+ .catch(popupAjaxError);
+ },
+});
+
+CustomWizardAdmin.reopenClass({
+ all() {
+ return ajax("/admin/wizards/wizard", {
+ type: "GET",
+ })
+ .then((result) => {
+ return result.wizard_list;
+ })
+ .catch(popupAjaxError);
+ },
+
+ submissions(wizardId, page = 0) {
+ return ajax(`/admin/wizards/submissions/${wizardId}`, {
+ type: "GET",
+ data: {
+ page,
+ },
+ }).catch(popupAjaxError);
+ },
+
+ create(wizardJson = {}) {
+ const wizard = this._super.apply(this);
+ wizard.setProperties(buildProperties(wizardJson));
+ return wizard;
+ },
+});
+
+export default CustomWizardAdmin;
diff --git a/assets/javascripts/discourse/models/custom-wizard-field.js.es6 b/assets/javascripts/discourse/models/custom-wizard-field.js.es6
new file mode 100644
index 00000000..a03c7c9e
--- /dev/null
+++ b/assets/javascripts/discourse/models/custom-wizard-field.js.es6
@@ -0,0 +1,79 @@
+import EmberObject from "@ember/object";
+import ValidState from "discourse/plugins/discourse-custom-wizard/discourse/mixins/valid-state";
+import discourseComputed from "discourse-common/utils/decorators";
+import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard";
+
+const StandardFieldValidation = [
+ "text",
+ "number",
+ "textarea",
+ "dropdown",
+ "tag",
+ "image",
+ "user_selector",
+ "text_only",
+ "composer",
+ "category",
+ "group",
+ "date",
+ "time",
+ "date_time",
+];
+
+export default EmberObject.extend(ValidState, {
+ id: null,
+ type: null,
+ value: null,
+ required: null,
+ warning: null,
+
+ @discourseComputed("wizardId", "stepId", "id")
+ i18nKey(wizardId, stepId, id) {
+ return `${wizardId}.${stepId}.${id}`;
+ },
+
+ @discourseComputed("i18nKey", "label")
+ translatedLabel(i18nKey, label) {
+ return translationOrText(`${i18nKey}.label`, label);
+ },
+
+ @discourseComputed("i18nKey", "placeholder")
+ translatedPlaceholder(i18nKey, placeholder) {
+ return translationOrText(`${i18nKey}.placeholder`, placeholder);
+ },
+
+ @discourseComputed("i18nKey", "description")
+ translatedDescription(i18nKey, description) {
+ return translationOrText(`${i18nKey}.description`, description);
+ },
+
+ check() {
+ if (this.customCheck) {
+ return this.customCheck();
+ }
+
+ let valid = this.valid;
+
+ if (!this.required) {
+ this.setValid(true);
+ return true;
+ }
+
+ const val = this.get("value");
+ const type = this.get("type");
+
+ if (type === "checkbox") {
+ valid = val;
+ } else if (type === "upload") {
+ valid = val && val.id > 0;
+ } else if (StandardFieldValidation.indexOf(type) > -1) {
+ valid = val && val.toString().length > 0;
+ } else if (type === "url") {
+ valid = true;
+ }
+
+ this.setValid(valid);
+
+ return valid;
+ },
+});
diff --git a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6
index e2de8a07..23565e2c 100644
--- a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6
@@ -3,14 +3,54 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import EmberObject from "@ember/object";
const CustomWizardLogs = EmberObject.extend();
+const logItemTypes = {
+ date: "date_time",
+ action: "text",
+ message: "long_text",
+ user: "user",
+ username: "text",
+};
+
+function logItem(item, attr) {
+ return {
+ value: item[attr],
+ type: logItemTypes[attr],
+ };
+}
CustomWizardLogs.reopenClass({
- list(page = 0) {
- return ajax("/admin/wizards/logs", {
- data: {
- page,
- },
- }).catch(popupAjaxError);
+ list(wizardId, page = 0) {
+ let data = {
+ page,
+ };
+
+ return ajax(`/admin/wizards/logs/${wizardId}`, { data })
+ .catch(popupAjaxError)
+ .then((result) => {
+ if (result.logs) {
+ result.logs = result.logs.map((item) => {
+ let map = {};
+
+ if (item.date) {
+ map.date = logItem(item, "date");
+ }
+ if (item.action) {
+ map.action = logItem(item, "action");
+ }
+ if (item.user) {
+ map.user = item.user;
+ } else {
+ map.user = logItem(item, "username");
+ }
+ if (item.message) {
+ map.message = logItem(item, "message");
+ }
+
+ return map;
+ });
+ }
+ return result;
+ });
},
});
diff --git a/assets/javascripts/discourse/models/custom-wizard-step.js.es6 b/assets/javascripts/discourse/models/custom-wizard-step.js.es6
new file mode 100644
index 00000000..f7cdc497
--- /dev/null
+++ b/assets/javascripts/discourse/models/custom-wizard-step.js.es6
@@ -0,0 +1,114 @@
+import EmberObject from "@ember/object";
+import ValidState from "discourse/plugins/discourse-custom-wizard/discourse/mixins/valid-state";
+import { ajax } from "discourse/lib/ajax";
+import discourseComputed from "discourse-common/utils/decorators";
+import { later } from "@ember/runloop";
+import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard";
+
+export default EmberObject.extend(ValidState, {
+ id: null,
+
+ @discourseComputed("wizardId", "id")
+ i18nKey(wizardId, stepId) {
+ return `${wizardId}.${stepId}`;
+ },
+
+ @discourseComputed("i18nKey", "title")
+ translatedTitle(i18nKey, title) {
+ return translationOrText(`${i18nKey}.title`, title);
+ },
+
+ @discourseComputed("i18nKey", "description")
+ translatedDescription(i18nKey, description) {
+ return translationOrText(`${i18nKey}.description`, description);
+ },
+
+ @discourseComputed("index")
+ displayIndex: (index) => index + 1,
+
+ @discourseComputed("fields.[]")
+ fieldsById(fields) {
+ const lookup = {};
+ fields.forEach((field) => (lookup[field.get("id")] = field));
+ return lookup;
+ },
+
+ validate() {
+ let allValid = true;
+ const result = { warnings: [] };
+
+ this.fields.forEach((field) => {
+ allValid = allValid && field.check();
+ const warning = field.get("warning");
+ if (warning) {
+ result.warnings.push(warning);
+ }
+ });
+
+ this.setValid(allValid);
+
+ return result;
+ },
+
+ fieldError(id, description) {
+ const field = this.fields.findBy("id", id);
+ if (field) {
+ field.setValid(false, description);
+ }
+ },
+
+ save() {
+ const wizardId = this.get("wizardId");
+ const fields = {};
+
+ this.get("fields").forEach((f) => {
+ if (f.type !== "text_only") {
+ fields[f.id] = f.value;
+ }
+ });
+
+ return ajax({
+ url: `/w/${wizardId}/steps/${this.get("id")}`,
+ type: "PUT",
+ data: { fields },
+ }).catch((response) => {
+ if (response.jqXHR) {
+ response = response.jqXHR;
+ }
+ if (response && response.responseJSON && response.responseJSON.errors) {
+ let wizardErrors = [];
+ response.responseJSON.errors.forEach((err) => {
+ if (err.field === wizardId) {
+ wizardErrors.push(err.description);
+ } else if (err.field) {
+ this.fieldError(err.field, err.description);
+ } else if (err) {
+ wizardErrors.push(err);
+ }
+ });
+ if (wizardErrors.length) {
+ this.handleWizardError(wizardErrors.join("\n"));
+ }
+ this.animateInvalidFields();
+ throw response;
+ }
+
+ if (response && response.responseText) {
+ const responseText = response.responseText;
+ const start = responseText.indexOf(">") + 1;
+ const end = responseText.indexOf("plugins");
+ const message = responseText.substring(start, end);
+ this.handleWizardError(message);
+ throw message;
+ }
+ });
+ },
+
+ handleWizardError(message) {
+ this.set("message", {
+ state: "error",
+ text: message,
+ });
+ later(() => this.set("message", null), 6000);
+ },
+});
diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6
index 2fa6bc4c..77f439c7 100644
--- a/assets/javascripts/discourse/models/custom-wizard.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard.js.es6
@@ -1,227 +1,127 @@
-import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object";
-import { buildProperties, mapped, present } from "../lib/wizard-json";
-import { listProperties, snakeCase } from "../lib/wizard";
-import wizardSchema from "../lib/wizard-schema";
-import { Promise } from "rsvp";
+import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
+import discourseComputed from "discourse-common/utils/decorators";
+import getUrl from "discourse-common/lib/get-url";
+import CustomWizardField from "./custom-wizard-field";
+import CustomWizardStep from "./custom-wizard-step";
const CustomWizard = EmberObject.extend({
- save(opts) {
- return new Promise((resolve, reject) => {
- let wizard = this.buildJson(this, "wizard");
+ @discourseComputed("steps.length")
+ totalSteps: (length) => length,
- if (wizard.error) {
- reject(wizard);
- }
-
- let data = {
- wizard,
- };
-
- if (opts.create) {
- data.create = true;
- }
-
- ajax(`/admin/wizards/wizard/${wizard.id}`, {
- type: "PUT",
- contentType: "application/json",
- data: JSON.stringify(data),
- }).then((result) => {
- if (result.backend_validation_error) {
- reject(result);
- } else {
- resolve(result);
- }
- });
- });
+ skip() {
+ if (this.required && !this.completed && this.permitted) {
+ return;
+ }
+ CustomWizard.skip(this.id);
},
- buildJson(object, type, result = {}) {
- let objectType = object.type || null;
-
- if (wizardSchema[type].types) {
- if (!objectType) {
- result.error = {
- type: "required",
- params: { type, property: "type" },
- };
- return result;
- }
- }
-
- for (let property of listProperties(type, { objectType })) {
- let value = object.get(property);
-
- result = this.validateValue(property, value, object, type, result);
-
- if (result.error) {
- break;
- }
-
- if (mapped(property, type)) {
- value = this.buildMappedJson(value);
- }
-
- if (value !== undefined && value !== null) {
- result[property] = value;
- }
- }
-
- if (!result.error) {
- for (let arrayObjectType of Object.keys(
- wizardSchema[type].objectArrays
- )) {
- let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
- let objectArray = object.get(arraySchema.property);
-
- if (arraySchema.required && !present(objectArray)) {
- result.error = {
- type: "required",
- params: { type, property: arraySchema.property },
- };
- break;
- }
-
- result[arraySchema.property] = [];
-
- for (let item of objectArray) {
- let itemProps = this.buildJson(item, arrayObjectType);
-
- if (itemProps.error) {
- result.error = itemProps.error;
- break;
- } else {
- result[arraySchema.property].push(itemProps);
- }
- }
- }
- }
-
- return result;
- },
-
- validateValue(property, value, object, type, result) {
- if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
- result.error = {
- type: "required",
- params: { type, property },
- };
- }
-
- let dependent = wizardSchema[type].dependent[property];
- if (dependent && value && !object[dependent]) {
- result.error = {
- type: "dependent",
- params: { property, dependent },
- };
- }
-
- if (property === "api_body") {
- try {
- value = JSON.parse(value);
- } catch (e) {
- result.error = {
- type: "invalid",
- params: { type, property },
- };
- }
- }
-
- return result;
- },
-
- buildMappedJson(value) {
- if (typeof value === "string" || Number.isInteger(value)) {
- return value;
- }
- if (!value || !value.length) {
- return false;
- }
-
- let inputs = value;
- let result = [];
-
- inputs.forEach((inpt) => {
- let input = {
- type: inpt.type,
- };
-
- if (inpt.connector) {
- input.connector = inpt.connector;
- }
-
- if (present(inpt.output)) {
- input.output = inpt.output;
- input.output_type = snakeCase(inpt.output_type);
- input.output_connector = inpt.output_connector;
- }
-
- if (present(inpt.pairs)) {
- input.pairs = [];
-
- inpt.pairs.forEach((pr) => {
- if (present(pr.key) && present(pr.value)) {
- let pairParams = {
- index: pr.index,
- key: pr.key,
- key_type: snakeCase(pr.key_type),
- value: pr.value,
- value_type: snakeCase(pr.value_type),
- connector: pr.connector,
- };
-
- input.pairs.push(pairParams);
- }
- });
- }
-
- if (
- (input.type === "assignment" && present(input.output)) ||
- present(input.pairs)
- ) {
- result.push(input);
- }
- });
-
- if (!result.length) {
- result = false;
- }
-
- return result;
- },
-
- remove() {
- return ajax(`/admin/wizards/wizard/${this.id}`, {
- type: "DELETE",
- })
- .then(() => this.destroy())
- .catch(popupAjaxError);
+ restart() {
+ CustomWizard.restart(this.id);
},
});
CustomWizard.reopenClass({
- all() {
- return ajax("/admin/wizards/wizard", {
- type: "GET",
- })
+ skip(wizardId) {
+ ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
.then((result) => {
- return result.wizard_list;
+ CustomWizard.finished(result);
})
.catch(popupAjaxError);
},
- submissions(wizardId) {
- return ajax(`/admin/wizards/submissions/${wizardId}`, {
- type: "GET",
- }).catch(popupAjaxError);
+ restart(wizardId) {
+ ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
+ .then(() => {
+ window.location.href = `/w/${wizardId}`;
+ })
+ .catch(popupAjaxError);
},
- create(wizardJson = {}) {
- const wizard = this._super.apply(this);
- wizard.setProperties(buildProperties(wizardJson));
- return wizard;
+ finished(result) {
+ let url = "/";
+ if (result.redirect_on_complete) {
+ url = result.redirect_on_complete;
+ }
+ window.location.href = getUrl(url);
+ },
+
+ build(wizardJson) {
+ if (!wizardJson) {
+ return null;
+ }
+
+ if (!wizardJson.completed && wizardJson.steps) {
+ wizardJson.steps = wizardJson.steps
+ .map((step) => {
+ const stepObj = CustomWizardStep.create(step);
+ stepObj.wizardId = wizardJson.id;
+
+ stepObj.fields.sort((a, b) => {
+ return parseFloat(a.number) - parseFloat(b.number);
+ });
+
+ let tabindex = 1;
+ stepObj.fields.forEach((f) => {
+ f.tabindex = tabindex;
+
+ if (["date_time"].includes(f.type)) {
+ tabindex = tabindex + 2;
+ } else {
+ tabindex++;
+ }
+ });
+
+ stepObj.fields = stepObj.fields.map((f) => {
+ f.wizardId = wizardJson.id;
+ f.stepId = stepObj.id;
+ return CustomWizardField.create(f);
+ });
+
+ return stepObj;
+ })
+ .sort((a, b) => {
+ return parseFloat(a.index) - parseFloat(b.index);
+ });
+ }
+ return CustomWizard.create(wizardJson);
},
});
+export function findCustomWizard(wizardId, params = {}) {
+ let url = `/w/${wizardId}.json`;
+
+ let paramKeys = Object.keys(params).filter((k) => {
+ if (k === "wizard_id") {
+ return false;
+ }
+ return !!params[k];
+ });
+
+ if (paramKeys.length) {
+ url += "?";
+ paramKeys.forEach((k, i) => {
+ if (i > 0) {
+ url += "&";
+ }
+ url += `${k}=${params[k]}`;
+ });
+ }
+
+ return ajax(url).then((result) => {
+ return CustomWizard.build(result);
+ });
+}
+
+let _wizard_store;
+
+export function updateCachedWizard(wizard) {
+ _wizard_store = wizard;
+}
+
+export function getCachedWizard() {
+ return _wizard_store;
+}
+
export default CustomWizard;
diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
index a1c625ad..a04d36f9 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6
@@ -8,7 +8,10 @@ export default DiscourseRoute.extend({
},
setupController(controller, model) {
- const customFields = A(model || []);
- controller.set("customFields", customFields);
+ const customFields = A(model.custom_fields || []);
+
+ controller.setProperties({
+ customFields,
+ });
},
});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6
new file mode 100644
index 00000000..474360ec
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6
@@ -0,0 +1,17 @@
+import CustomWizardLogs from "../models/custom-wizard-logs";
+import DiscourseRoute from "discourse/routes/discourse";
+import { A } from "@ember/array";
+
+export default DiscourseRoute.extend({
+ model(params) {
+ return CustomWizardLogs.list(params.wizardId);
+ },
+
+ setupController(controller, model) {
+ controller.setProperties({
+ wizard: model.wizard,
+ logs: A(model.logs),
+ total: model.total,
+ });
+ },
+});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6
index 56b91350..a1575050 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6
@@ -1,12 +1,24 @@
-import CustomWizardLogs from "../models/custom-wizard-logs";
import DiscourseRoute from "discourse/routes/discourse";
+import { ajax } from "discourse/lib/ajax";
export default DiscourseRoute.extend({
model() {
- return CustomWizardLogs.list();
+ return ajax(`/admin/wizards/wizard`);
},
setupController(controller, model) {
- controller.set("logs", model);
+ const showParams = this.paramsFor("adminWizardsLogsShow");
+
+ controller.setProperties({
+ wizardId: showParams.wizardId,
+ wizardList: model.wizard_list,
+ });
+ },
+
+ actions: {
+ changeWizard(wizardId) {
+ this.controllerFor("adminWizardsLogs").set("wizardId", wizardId);
+ this.transitionTo("adminWizardsLogsShow", wizardId);
+ },
},
});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-manager.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-manager.js.es6
index dfbfc472..b3314186 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-manager.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-manager.js.es6
@@ -1,9 +1,9 @@
-import CustomWizard from "../models/custom-wizard";
+import CustomWizardAdmin from "../models/custom-wizard-admin";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
model() {
- return CustomWizard.all();
+ return CustomWizardAdmin.all();
},
setupController(controller, model) {
diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
index 73168ff3..b616b5be 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
@@ -1,42 +1,21 @@
-import CustomWizard from "../models/custom-wizard";
+import { A } from "@ember/array";
+import CustomWizardAdmin from "../models/custom-wizard-admin";
import DiscourseRoute from "discourse/routes/discourse";
-
-const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
+import { formatModel } from "../lib/wizard-submission";
export default DiscourseRoute.extend({
model(params) {
- return CustomWizard.submissions(params.wizardId);
+ return CustomWizardAdmin.submissions(params.wizardId);
},
setupController(controller, model) {
- if (model && model.submissions) {
- let fields = ["username"];
- model.submissions.forEach((s) => {
- Object.keys(s.fields).forEach((k) => {
- if (!excludedMetaFields.includes(k) && fields.indexOf(k) < 0) {
- fields.push(k);
- }
- });
- });
+ const { fields, submissions } = formatModel(model);
- let submissions = [];
- model.submissions.forEach((s) => {
- let submission = {
- username: s.username,
- };
- Object.keys(s.fields).forEach((f) => {
- if (fields.includes(f)) {
- submission[f] = s.fields[f];
- }
- });
- submissions.push(submission);
- });
-
- controller.setProperties({
- wizard: model.wizard,
- submissions,
- fields,
- });
- }
+ controller.setProperties({
+ wizard: model.wizard,
+ fields: A(fields),
+ submissions: A(submissions),
+ total: model.total,
+ });
},
});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
index cb2d54c3..f55ff19e 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
@@ -1,4 +1,4 @@
-import CustomWizard from "../models/custom-wizard";
+import CustomWizardAdmin from "../models/custom-wizard-admin";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
@@ -20,7 +20,9 @@ export default DiscourseRoute.extend({
setupController(controller, model) {
const parentModel = this.modelFor("adminWizardsWizard");
- const wizard = CustomWizard.create(!model || model.create ? {} : model);
+ const wizard = CustomWizardAdmin.create(
+ !model || model.create ? {} : model
+ );
const fieldTypes = Object.keys(parentModel.field_types).map((type) => {
return {
id: type,
diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6
index 5de271a8..1fa786d3 100644
--- a/assets/javascripts/discourse/routes/admin-wizards.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6
@@ -1,7 +1,21 @@
import DiscourseRoute from "discourse/routes/discourse";
+import { ajax } from "discourse/lib/ajax";
export default DiscourseRoute.extend({
- beforeModel(transition) {
+ model() {
+ return ajax("/admin/wizards");
+ },
+
+ setupController(controller, model) {
+ controller.setProperties({
+ subscribed: model.subscribed,
+ subscriptionType: model.subscription_type,
+ subscriptionAttributes: model.subscription_attributes,
+ subscriptionClientInstalled: model.subscription_client_installed,
+ });
+ },
+
+ afterModel(model, transition) {
if (transition.targetName === "adminWizards.index") {
this.transitionTo("adminWizardsWizard");
}
diff --git a/assets/javascripts/wizard/routes/custom-index.js.es6 b/assets/javascripts/discourse/routes/custom-wizard-index.js.es6
similarity index 51%
rename from assets/javascripts/wizard/routes/custom-index.js.es6
rename to assets/javascripts/discourse/routes/custom-wizard-index.js.es6
index a8abc152..1d5a71c7 100644
--- a/assets/javascripts/wizard/routes/custom-index.js.es6
+++ b/assets/javascripts/discourse/routes/custom-wizard-index.js.es6
@@ -1,10 +1,17 @@
-import { getCachedWizard } from "../models/custom";
+import { getCachedWizard } from "../models/custom-wizard";
+import Route from "@ember/routing/route";
-export default Ember.Route.extend({
+export default Route.extend({
beforeModel() {
const wizard = getCachedWizard();
- if (wizard && wizard.permitted && !wizard.completed && wizard.start) {
- this.replaceWith("custom.step", wizard.start);
+ if (
+ wizard &&
+ wizard.user &&
+ wizard.permitted &&
+ !wizard.completed &&
+ wizard.start
+ ) {
+ this.replaceWith("customWizardStep", wizard.start);
}
},
@@ -19,15 +26,18 @@ export default Ember.Route.extend({
const wizardId = model.get("id");
const user = model.get("user");
const name = model.get("name");
+ const requiresLogin = !user;
+ const notPermitted = !permitted;
- controller.setProperties({
- requiresLogin: !user,
+ const props = {
+ requiresLogin,
user,
name,
completed,
- notPermitted: !permitted,
+ notPermitted,
wizardId,
- });
+ };
+ controller.setProperties(props);
} else {
controller.set("noWizard", true);
}
diff --git a/assets/javascripts/wizard/routes/custom-step.js.es6 b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6
similarity index 61%
rename from assets/javascripts/wizard/routes/custom-step.js.es6
rename to assets/javascripts/discourse/routes/custom-wizard-step.js.es6
index 8088727a..969df1eb 100644
--- a/assets/javascripts/wizard/routes/custom-step.js.es6
+++ b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6
@@ -1,9 +1,15 @@
-import WizardI18n from "../lib/wizard-i18n";
-import { getCachedWizard } from "../models/custom";
+import I18n from "I18n";
+import { getCachedWizard } from "../models/custom-wizard";
+import Route from "@ember/routing/route";
-export default Ember.Route.extend({
+export default Route.extend({
beforeModel() {
- this.set("wizard", getCachedWizard());
+ const wizard = getCachedWizard();
+ this.set("wizard", wizard);
+
+ if (!wizard || !wizard.user || !wizard.permitted || wizard.completed) {
+ this.replaceWith("customWizard");
+ }
},
model(params) {
@@ -19,7 +25,7 @@ export default Ember.Route.extend({
afterModel(model) {
if (model.completed) {
- return this.transitionTo("index");
+ return this.transitionTo("wizard.index");
}
return model.set("wizardId", this.wizard.id);
},
@@ -33,8 +39,7 @@ export default Ember.Route.extend({
if (!model.permitted) {
props["stepMessage"] = {
state: "not-permitted",
- text:
- model.permitted_message || WizardI18n("wizard.step_not_permitted"),
+ text: model.permitted_message || I18n.t("wizard.step_not_permitted"),
};
if (model.index > 0) {
props["showReset"] = true;
diff --git a/assets/javascripts/discourse/routes/custom-wizard.js.es6 b/assets/javascripts/discourse/routes/custom-wizard.js.es6
new file mode 100644
index 00000000..d03714a2
--- /dev/null
+++ b/assets/javascripts/discourse/routes/custom-wizard.js.es6
@@ -0,0 +1,89 @@
+import { findCustomWizard, updateCachedWizard } from "../models/custom-wizard";
+import I18n from "I18n";
+import DiscourseRoute from "discourse/routes/discourse";
+
+export default DiscourseRoute.extend({
+ titleToken() {
+ const wizard = this.modelFor("custom-wizard");
+ return wizard ? wizard.name || wizard.id : I18n.t("wizard.custom_title");
+ },
+
+ beforeModel(transition) {
+ if (transition.intent.queryParams) {
+ this.set("queryParams", transition.intent.queryParams);
+ }
+ },
+
+ model(params) {
+ return findCustomWizard(params.wizard_id, this.get("queryParams"));
+ },
+
+ showDialog(wizardModel) {
+ const title = I18n.t("wizard.incomplete_submission.title", {
+ date: moment(wizardModel.submission_last_updated_at).format(
+ "MMMM Do YYYY"
+ ),
+ });
+
+ const buttons = [
+ {
+ label: I18n.t("wizard.incomplete_submission.restart"),
+ class: "btn btn-default",
+ callback: () => {
+ wizardModel.restart();
+ },
+ },
+ {
+ label: I18n.t("wizard.incomplete_submission.resume"),
+ class: "btn btn-primary",
+ },
+ ];
+
+ const options = {
+ onEscape: false,
+ };
+
+ bootbox.dialog(title, buttons, options);
+ },
+
+ afterModel(model) {
+ updateCachedWizard(model);
+ },
+
+ setupController(controller, model) {
+ controller.setProperties({
+ customWizard: true,
+ logoUrl: this.siteSettings.logo_small,
+ reset: null,
+ model,
+ });
+
+ const stepModel = this.modelFor("custom-wizard-step");
+ if (
+ model.resume_on_revisit &&
+ model.submission_last_updated_at &&
+ stepModel.index > 0
+ ) {
+ this.showDialog(model);
+ }
+
+ const background = model.get("background");
+ if (background) {
+ document.body.style.background = background;
+ }
+ },
+
+ activate() {
+ if (!document.body.classList.contains("custom-wizard")) {
+ document.body.classList.add("custom-wizard");
+ }
+ },
+
+ deactivate() {
+ if (document.body.classList.contains("custom-wizard")) {
+ document.body.classList.remove("custom-wizard");
+ }
+
+ document.body.style.background = "";
+ },
+});
diff --git a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
index 4d3def3d..303b3f6d 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
@@ -21,11 +21,11 @@
{{/if}}
-
-
+
{{#if api.isNew}}
{{input value=api.name placeholder=(i18n "admin.wizard.api.name_placeholder")}}
{{else}}
- {{api.name}}
+ {{api.name}}
{{/if}}
@@ -63,7 +63,7 @@
{{/if}}
-
@@ -71,7 +71,7 @@
-
-
-
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs
new file mode 100644
index 00000000..270d5c21
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs
@@ -0,0 +1,45 @@
+{{#if logs}}
+
+
+
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
+ {{#if noResults}}
+
{{i18n "search.no_results"}}
+ {{else}}
+
+
+
+ {{i18n "admin.wizard.log.date"}} |
+ {{i18n "admin.wizard.log.action"}} |
+ {{i18n "admin.wizard.log.user"}} |
+ {{i18n "admin.wizard.log.message"}} |
+
+
+
+ {{#each logs as |log|}}
+
+ {{#each-in log as |field value|}}
+ {{wizard-table-field field=field value=value}} |
+ {{/each-in}}
+
+ {{/each}}
+
+
+ {{/if}}
+
+ {{conditional-loading-spinner condition=refreshing}}
+ {{/load-more}}
+
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
index b0dd3de6..45738a9f 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
@@ -1,34 +1,19 @@
-
-
{{i18n "admin.wizard.log.nav_label"}}
-
- {{d-button
- label="refresh"
- icon="sync"
- action="refresh"
- class="refresh"}}
+
+ {{combo-box
+ value=wizardId
+ content=wizardList
+ onChange=(route-action "changeWizard")
+ options=(hash
+ none="admin.wizard.select"
+ )}}
-{{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}}
- {{#if noResults}}
-
{{i18n "search.no_results"}}
- {{else}}
-
-
-
- Message |
- Date |
-
-
-
- {{#each logs as |log|}}
-
- {{log.message}} |
- {{bound-date log.date}} |
-
- {{/each}}
-
-
- {{/if}}
+{{wizard-message
+ key=messageKey
+ opts=messageOpts
+ url=documentationUrl
+ component="logs"}}
- {{conditional-loading-spinner condition=refreshing}}
-{{/load-more}}
+
+ {{outlet}}
+
diff --git a/assets/javascripts/discourse/templates/admin-wizards-manager.hbs b/assets/javascripts/discourse/templates/admin-wizards-manager.hbs
index 9ee2f080..4b91bd3d 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-manager.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-manager.hbs
@@ -12,10 +12,10 @@
{{/if}}
{{input
- id="file-upload"
+ id="custom-wizard-file-upload"
type="file"
accept="application/json"
- change=(action "setFile")}}
+ input=(action "setFile")}}
{{d-button
id="upload-button"
label="admin.wizard.manager.upload"
diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs
index 6d1f255b..72ec7c38 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs
@@ -1,8 +1,24 @@
{{#if submissions}}
-
-
-
-
- {{#each fields as |f|}}
- {{f}} |
- {{/each}}
-
-
-
- {{#each submissions as |s|}}
-
- {{#each-in s as |k v|}}
- {{v}} |
- {{/each-in}}
-
- {{/each}}
-
-
+
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
+ {{#if noResults}}
+
{{i18n "search.no_results"}}
+ {{else}}
+
+
+
+ {{#each fields as |field|}}
+ {{#if field.enabled}}
+
+ {{field.label}}
+ |
+ {{/if}}
+ {{/each}}
+
+
+
+ {{#each displaySubmissions as |submission|}}
+
+ {{#each-in submission as |field value|}}
+ {{wizard-table-field field=field value=value}} |
+ {{/each-in}}
+
+ {{/each}}
+
+
+ {{/if}}
+
+ {{conditional-loading-spinner condition=loadingMore}}
+ {{/load-more}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
index d843485a..07dd1682 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
@@ -1,4 +1,4 @@
-
+
{{combo-box
value=wizardId
content=wizardList
@@ -8,6 +8,12 @@
)}}
+{{wizard-message
+ key=messageKey
+ opts=messageOpts
+ url=documentationUrl
+ component="submissions"}}
+
{{outlet}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs
index c5ed70a7..11a2b415 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs
@@ -55,21 +55,11 @@
-
+
- {{input type="checkbox" checked=wizard.required}}
- {{i18n "admin.wizard.required_label"}}
-
-
-
-
-
-
-
-
- {{input type="checkbox" checked=wizard.after_signup}}
- {{i18n "admin.wizard.after_signup_label"}}
+ {{input type="checkbox" checked=wizard.save_submissions}}
+ {{i18n "admin.wizard.save_submissions_label"}}
@@ -83,6 +73,16 @@
+
+
+
+
+
+ {{input type="checkbox" checked=wizard.after_signup}}
+ {{i18n "admin.wizard.after_signup_label"}}
+
+
+
@@ -101,68 +101,52 @@
{{input type="checkbox" checked=wizard.after_time}}
{{i18n "admin.wizard.after_time_label"}}
{{d-button
- action="setNextSessionScheduled"
+ action=(action "setNextSessionScheduled")
translatedLabel=nextSessionScheduledLabel
class="btn-after-time"
icon="far-calendar"}}
-
-
-
-
-
- {{wizard-mapper
- inputs=wizard.permitted
- options=(hash
- context="wizard"
- inputTypes="assignment,validation"
- groupSelection="output"
- userFieldSelection="key"
- textSelection="value"
- inputConnector="and"
- )}}
-
-
-
- {{wizard-advanced-toggle showAdvanced=wizard.showAdvanced}}
-
- {{#if wizard.showAdvanced}}
-
-
-
-
-
-
-
- {{input type="checkbox" checked=wizard.save_submissions}}
- {{i18n "admin.wizard.save_submissions_label"}}
-
+ {{#wizard-subscription-container}}
+
+
+
-
-
-
-
-
-
- {{input type="checkbox" checked=wizard.restart_on_revisit}}
- {{i18n "admin.wizard.restart_on_revisit_label"}}
-
+
+ {{input type="checkbox" checked=wizard.required}}
+ {{i18n "admin.wizard.required_label"}}
-
-
-
-
-
-
- {{input type="checkbox" checked=wizard.resume_on_revisit}}
- {{i18n "admin.wizard.resume_on_revisit_label"}}
-
-
-
- {{/if}}
+
+
+
+
+
+
+ {{input type="checkbox" checked=wizard.restart_on_revisit}}
+ {{i18n "admin.wizard.restart_on_revisit_label"}}
+
+
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=wizard.permitted
+ options=(hash
+ context="wizard"
+ inputTypes="assignment,validation"
+ groupSelection="output"
+ userFieldSelection="key"
+ textSelection="value"
+ inputConnector="and"
+ )}}
+
+
+ {{/wizard-subscription-container}}
{{wizard-links
@@ -176,7 +160,8 @@
wizard=wizard
currentField=currentField
wizardFields=wizardFields
- fieldTypes=fieldTypes}}
+ fieldTypes=fieldTypes
+ subscribed=subscribed}}
{{/if}}
{{wizard-links
@@ -185,14 +170,15 @@
items=wizard.actions
generateLabels=true}}
- {{#each wizard.actions as |action|}}
+ {{#each wizard.actions as |wizardAction|}}
{{wizard-custom-action
- action=action
+ action=wizardAction
currentActionId=currentAction.id
wizard=wizard
apis=apis
removeAction="removeAction"
- wizardFields=wizardFields}}
+ wizardFields=wizardFields
+ fieldTypes=fieldTypes}}
{{/each}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs
index 081cd5f3..c96f8009 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs
@@ -8,7 +8,7 @@
)}}
{{d-button
- action="createWizard"
+ action=(route-action "createWizard")
label="admin.wizard.create"
icon="plus"}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs
index bd575aae..cea77942 100644
--- a/assets/javascripts/discourse/templates/admin-wizards.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards.hbs
@@ -2,11 +2,16 @@
{{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}}
{{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}}
{{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}}
- {{#if siteSettings.wizard_apis_enabled}}
+ {{#if showApi}}
{{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}}
{{/if}}
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
+
+
+ {{wizard-subscription-badge}}
+ {{wizard-subscription-cta}}
+
{{/admin-nav}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
index 43a97be8..c0bdaaff 100644
--- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
@@ -1,17 +1,23 @@
{{#if showInputs}}
- {{combo-box
+ {{wizard-subscription-selector
value=field.klass
- content=klassContent
- none="admin.wizard.custom_field.klass.select"
- onChange=(action (mut field.klass))}}
+ feature="custom_field"
+ attribute="klass"
+ onChange=(action (mut field.klass))
+ options=(hash
+ none="admin.wizard.custom_field.klass.select"
+ )}}
|
- {{combo-box
+ {{wizard-subscription-selector
value=field.type
- content=typeContent
- none="admin.wizard.custom_field.type.select"
- onChange=(action (mut field.type))}}
+ feature="custom_field"
+ attribute="type"
+ onChange=(action (mut field.type))
+ options=(hash
+ none="admin.wizard.custom_field.type.select"
+ )}}
|
{{input
@@ -22,8 +28,10 @@
{{multi-select
value=field.serializers
content=serializerContent
- none="admin.wizard.custom_field.serializers.select"
- onChange=(action (mut field.serializers))}}
+ onChange=(action (mut field.serializers))
+ options=(hash
+ none="admin.wizard.custom_field.serializers.select"
+ )}}
|
{{#if loading}}
@@ -34,17 +42,17 @@
{{/if}}
{{/if}}
{{d-button
- action="destroy"
+ action=(action "destroy")
icon="trash-alt"
class="destroy"
disabled=destroyDisabled}}
{{d-button
icon="save"
- action="save"
+ action=(action "save")
disabled=saveDisabled
class="save"}}
{{d-button
- action="close"
+ action=(action "close")
icon="times"
disabled=closeDisabled}}
|
@@ -69,7 +77,7 @@
{{else}}
- {{d-button action="edit" icon="pencil-alt"}}
+ {{d-button action=(action "edit") icon="pencil-alt"}}
|
{{/if}}
{{/if}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-composer-editor.hbs
similarity index 89%
rename from assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-composer-editor.hbs
index be98db8e..baa6a17a 100644
--- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-composer-editor.hbs
@@ -1,7 +1,7 @@
{{d-editor
tabindex=field.tabindex
value=composer.reply
- placeholderTranslated=replyPlaceholder
+ placeholderOverride=replyPlaceholder
previewUpdated=(action "previewUpdated")
markdownOptions=markdownOptions
extraButtons=(action "extraButtons")
@@ -11,7 +11,7 @@
validation=validation
loading=composer.loading
showLink=showLink
- wizardComposerEvents=true
+ wizardComposer=true
fieldId=field.id
disabled=disableTextarea
outletArgs=(hash composer=composer editorType="composer")}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-hyperlink.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs
similarity index 71%
rename from assets/javascripts/wizard/templates/components/wizard-composer-hyperlink.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs
index c4403633..f430fb59 100644
--- a/assets/javascripts/wizard/templates/components/wizard-composer-hyperlink.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs
@@ -1,13 +1,13 @@
-
{{wizard-i18n "composer.link_dialog_title"}}
+
{{i18n "composer.link_dialog_title"}}
{{input
class="composer-link-name"
- placeholder=(wizard-i18n "composer.link_optional_text")
+ placeholder=(i18n "composer.link_optional_text")
type="text"
value=linkName}}
{{input
class="composer-link-url"
- placeholder=(wizard-i18n "composer.link_url_placeholder")
+ placeholder=(i18n "composer.link_url_placeholder")
type="text"
value=linkUrl}}
{{d-button
diff --git a/assets/javascripts/wizard/templates/components/wizard-date-input.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/wizard-date-input.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs
diff --git a/assets/javascripts/wizard/templates/components/wizard-date-time-input.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-date-time-input.hbs
similarity index 86%
rename from assets/javascripts/wizard/templates/components/wizard-date-time-input.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-date-time-input.hbs
index 0981f739..4fed9c95 100644
--- a/assets/javascripts/wizard/templates/components/wizard-date-time-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-date-time-input.hbs
@@ -1,5 +1,5 @@
{{#unless timeFirst}}
- {{wizard-date-input
+ {{custom-wizard-date-input
date=date
relativeDate=relativeDate
onChange=(action "onChangeDate")
@@ -8,7 +8,7 @@
{{/unless}}
{{#if showTime}}
- {{wizard-time-input
+ {{custom-wizard-time-input
date=date
relativeDate=relativeDate
onChange=(action "onChangeTime")
@@ -17,7 +17,7 @@
{{/if}}
{{#if timeFirst}}
- {{wizard-date-input
+ {{custom-wizard-date-input
date=date
relativeDate=relativeDate
onChange=(action "onChangeDate")
diff --git a/assets/javascripts/wizard/templates/components/wizard-editor.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
similarity index 94%
rename from assets/javascripts/wizard/templates/components/wizard-editor.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
index 861d2449..5c1cc45b 100644
--- a/assets/javascripts/wizard/templates/components/wizard-editor.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
@@ -27,7 +27,7 @@
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs
new file mode 100644
index 00000000..9cce87bc
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs
@@ -0,0 +1,8 @@
+{{custom-wizard-category-selector
+ categories=categories
+ whitelist=field.content
+ onChange=(action (mut categories))
+ tabindex=field.tabindex
+ options=(hash
+ maximum=field.limit
+ )}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-composer-preview.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-composer-preview.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/wizard-field-composer-preview.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-composer-preview.hbs
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-composer.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
similarity index 82%
rename from assets/javascripts/wizard/templates/components/wizard-field-composer.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
index 262f0e69..51964a1b 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-composer.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
@@ -1,4 +1,4 @@
-{{wizard-composer-editor
+{{custom-wizard-composer-editor
field=field
composer=composer
wizard=wizard
@@ -10,7 +10,7 @@
{{#if field.char_counter}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-date-time.hbs
similarity index 70%
rename from assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-date-time.hbs
index dae4523d..f6d6c588 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-date-time.hbs
@@ -1,4 +1,4 @@
-{{wizard-date-time-input
+{{custom-wizard-date-time-input
date=dateTime
onChange=(action "onChange")
tabindex=field.tabindex
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-date.hbs
similarity index 77%
rename from assets/javascripts/wizard/templates/components/wizard-field-date.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-date.hbs
index ed4d14e3..d0368564 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-date.hbs
@@ -1,4 +1,4 @@
-{{wizard-date-input
+{{custom-wizard-date-input
date=date
onChange=(action "onChange")
tabindex=field.tabindex
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-dropdown.hbs
similarity index 82%
rename from assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-dropdown.hbs
index 7ce4c298..42fc63e8 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-dropdown.hbs
@@ -3,6 +3,7 @@
value=field.value
content=field.content
tabindex=field.tabindex
+ onChange=(action "onChangeValue")
options=(hash
none="select_kit.default_header_text"
)}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-group.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
similarity index 60%
rename from assets/javascripts/wizard/templates/components/wizard-field-group.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
index 92c08e2b..6873f9bd 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
@@ -1,10 +1,10 @@
-{{wizard-group-selector
- groups=wizard.groups
+{{custom-wizard-group-selector
+ groups=site.groups
field=field
whitelist=field.content
value=field.value
tabindex=field.tabindex
onChange=(action (mut field.value))
options=(hash
- none="group.select"
+ none="select_kit.default_header_text"
)}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-number.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/wizard-field-number.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs
new file mode 100644
index 00000000..90679ae7
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs
@@ -0,0 +1,9 @@
+{{custom-wizard-tag-chooser
+ tags=field.value
+ tabindex=field.tabindex
+ tagGroups=field.tag_groups
+ everyTag=true
+ options=(hash
+ maximum=field.limit
+ allowAny=field.can_create_tag
+ )}}
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs
new file mode 100644
index 00000000..08733d3f
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs
@@ -0,0 +1 @@
+{{input id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex autocomplete=autocomplete}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
similarity index 51%
rename from assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
index 6efb7560..dda299bc 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
@@ -1 +1 @@
-{{textarea id=field.id value=field.value class=fieldClass placeholder=field.placeholder tabindex=field.tabindex}}
+{{textarea id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-time.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-time.hbs
similarity index 72%
rename from assets/javascripts/wizard/templates/components/wizard-field-time.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-time.hbs
index d4cc425a..1c790a81 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-time.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-time.hbs
@@ -1,4 +1,4 @@
-{{wizard-time-input
+{{custom-wizard-time-input
date=time
onChange=(action "onChange")
tabindex=field.tabindex
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-upload.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-upload.hbs
similarity index 86%
rename from assets/javascripts/wizard/templates/components/wizard-field-upload.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-field-upload.hbs
index 4f4ee1f5..022e6a94 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-upload.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-upload.hbs
@@ -1,8 +1,8 @@
diff --git a/assets/javascripts/wizard/templates/components/wizard-time-input.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-time-input.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/wizard-time-input.hbs
rename to assets/javascripts/discourse/templates/components/custom-wizard-time-input.hbs
diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/discourse/templates/components/field-validators.hbs
similarity index 100%
rename from assets/javascripts/wizard/templates/components/field-validators.hbs
rename to assets/javascripts/discourse/templates/components/field-validators.hbs
diff --git a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs b/assets/javascripts/discourse/templates/components/similar-topics-validator.hbs
similarity index 54%
rename from assets/javascripts/wizard/templates/components/similar-topics-validator.hbs
rename to assets/javascripts/discourse/templates/components/similar-topics-validator.hbs
index 2e92333d..2f760196 100644
--- a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs
+++ b/assets/javascripts/discourse/templates/components/similar-topics-validator.hbs
@@ -1,13 +1,13 @@
{{#if currentState}}
{{#if insufficientCharactersCategories}}
- {{html-safe (wizard-i18n currentStateKey catLinks=catLinks)}}
+ {{html-safe (i18n currentStateKey catLinks=catLinks)}}
{{else}}
- {{wizard-i18n currentStateKey}}
+ {{i18n currentStateKey}}
{{/if}}
{{/if}}
{{#if showSimilarTopics}}
- {{wizard-similar-topics topics=similarTopics}}
+ {{custom-wizard-similar-topics topics=similarTopics}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/validator.hbs b/assets/javascripts/discourse/templates/components/validator.hbs
new file mode 100644
index 00000000..8e08379b
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/validator.hbs
@@ -0,0 +1,5 @@
+{{#if isValid}}
+ {{i18n validMessageKey}}
+{{else}}
+ {{i18n invalidMessageKey}}
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs b/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
deleted file mode 100644
index ec2bcb76..00000000
--- a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-{{d-button
- action="toggleAdvanced"
- label="admin.wizard.advanced"
- class=toggleClass}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
index 4c645cf7..8245f1b8 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -1,6 +1,6 @@
{{#if showUndo}}
{{d-button
- action="undoChanges"
+ action=(action "undoChanges")
icon=undoIcon
label=undoKey
class="undo-changes"}}
@@ -12,13 +12,15 @@
- {{combo-box
+ {{wizard-subscription-selector
value=action.type
- content=actionTypes
+ feature="action"
+ attribute="type"
onChange=(action "changeType")
options=(hash
none="admin.wizard.select_type"
- )}}
+ )
+ }}
@@ -158,6 +160,44 @@
)}}
+
+ {{#if hasEventField}}
+
+
+ {{i18n "admin.wizard.action.create_topic.add_event"}}
+
+
+
+ {{wizard-mapper
+ inputs=action.add_event
+ property="add_event"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ wizardFieldSelection=true
+ context="action"
+ )}}
+
+
+ {{/if}}
+
+ {{#if hasLocationField}}
+
+
+ {{i18n "admin.wizard.action.create_topic.add_location"}}
+
+
+
+ {{wizard-mapper
+ inputs=action.add_location
+ property="add_location"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ wizardFieldSelection=true
+ context="action"
+ )}}
+
+
+ {{/if}}
{{/if}}
{{#if sendMessage}}
@@ -346,7 +386,7 @@
- {{i18n "admin.wizard.action.watch_categories.notification_level.label"}}
+ {{i18n "admin.wizard.action.watch_x.notification_level.label"}}
@@ -356,14 +396,14 @@
onChange=(action (mut action.notification_level))
options=(hash
isDisabled=action.custom_title_enabled
- none="admin.wizard.action.watch_categories.select_a_notification_level"
+ none="admin.wizard.action.watch_x.select_a_notification_level"
)}}
- {{i18n "admin.wizard.action.watch_categories.wizard_user"}}
+ {{i18n "admin.wizard.action.watch_x.wizard_user"}}
@@ -373,7 +413,76 @@
- {{i18n "admin.wizard.action.watch_categories.usernames"}}
+ {{i18n "admin.wizard.action.watch_x.usernames"}}
+
+
+
+ {{wizard-mapper
+ inputs=action.usernames
+ property="usernames"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ context="action"
+ wizardFieldSelection=true
+ userFieldSelection="key,value"
+ userSelection="output"
+ )}}
+
+
+{{/if}}
+
+{{#if watchTags}}
+
+
+ {{i18n "admin.wizard.action.watch_tags.tags"}}
+
+
+
+ {{wizard-mapper
+ inputs=action.tags
+ property="tags"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ textSelection="key,value"
+ tagSelection="output"
+ wizardFieldSelection=true
+ wizardActionSelection=true
+ userFieldSelection="key,value"
+ context="action"
+ )}}
+
+
+
+
+
+ {{i18n "admin.wizard.action.watch_x.notification_level.label"}}
+
+
+
+ {{combo-box
+ value=action.notification_level
+ content=availableNotificationLevels
+ onChange=(action (mut action.notification_level))
+ options=(hash
+ isDisabled=action.custom_title_enabled
+ none="admin.wizard.action.watch_x.select_a_notification_level"
+ )}}
+
+
+
+
+
+ {{i18n "admin.wizard.action.watch_x.wizard_user"}}
+
+
+
+ {{input type="checkbox" checked=action.wizard_user}}
+
+
+
+
+
+ {{i18n "admin.wizard.action.watch_x.usernames"}}
@@ -714,99 +823,90 @@
{{/if}}
-{{#if showAdvanced}}
- {{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
-
- {{#if action.showAdvanced}}
-
-
- {{#if hasCustomFields}}
-
-
- {{i18n "admin.wizard.action.custom_fields.label"}}
-
-
-
- {{wizard-mapper
- inputs=action.custom_fields
- property="custom_fields"
- onUpdate=(action "mappedFieldUpdated")
- options=(hash
- inputTypes="association"
- customFieldSelection="key"
- wizardFieldSelection="value"
- wizardActionSelection="value"
- userFieldSelection="value"
- keyPlaceholder="admin.wizard.action.custom_fields.key"
- context=customFieldsContext
- )}}
-
-
- {{/if}}
-
- {{#if sendMessage}}
-
-
- {{i18n "admin.wizard.required"}}
-
-
-
- {{wizard-mapper
- inputs=action.required
- property="required"
- onUpdate=(action "mappedFieldUpdated")
- options=(hash
- textSelection="value"
- wizardFieldSelection=true
- userFieldSelection=true
- groupSelection=true
- context="action"
- )}}
-
-
- {{/if}}
-
- {{#if showPostAdvanced}}
-
-
- {{i18n "admin.wizard.action.skip_redirect.label"}}
-
-
-
- {{input type="checkbox" checked=action.skip_redirect}}
-
-
- {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
-
-
-
-
-
-
- {{i18n "admin.wizard.action.suppress_notifications.label"}}
-
-
-
- {{input type="checkbox" checked=action.suppress_notifications}}
-
-
- {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
-
-
-
- {{/if}}
-
- {{#if routeTo}}
-
-
- {{i18n "admin.wizard.action.route_to.code"}}
-
-
-
- {{input value=action.code}}
-
-
- {{/if}}
+{{#if hasCustomFields}}
+
+
+ {{i18n "admin.wizard.action.custom_fields.label"}}
- {{/if}}
+
+
+ {{wizard-mapper
+ inputs=action.custom_fields
+ property="custom_fields"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ inputTypes="association"
+ customFieldSelection="key"
+ wizardFieldSelection="value"
+ wizardActionSelection="value"
+ userFieldSelection="value"
+ keyPlaceholder="admin.wizard.action.custom_fields.key"
+ context=customFieldsContext
+ )}}
+
+
+{{/if}}
+
+{{#if sendMessage}}
+
+
+ {{i18n "admin.wizard.required"}}
+
+
+
+ {{wizard-mapper
+ inputs=action.required
+ property="required"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ textSelection="value"
+ wizardFieldSelection=true
+ userFieldSelection=true
+ groupSelection=true
+ context="action"
+ )}}
+
+
+{{/if}}
+
+{{#if showPostAdvanced}}
+
+
+ {{i18n "admin.wizard.action.skip_redirect.label"}}
+
+
+
+ {{input type="checkbox" checked=action.skip_redirect}}
+
+
+ {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
+
+
+
+
+
+
+ {{i18n "admin.wizard.action.suppress_notifications.label"}}
+
+
+
+ {{input type="checkbox" checked=action.suppress_notifications}}
+
+
+ {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
+
+
+
+{{/if}}
+
+{{#if routeTo}}
+
+
+ {{i18n "admin.wizard.action.route_to.code"}}
+
+
+
+ {{input value=action.code}}
+
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
index f51b9fbb..6273f9a9 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
@@ -1,6 +1,6 @@
{{#if showUndo}}
{{d-button
- action="undoChanges"
+ action=(action "undoChanges")
icon=undoIcon
label=undoKey
class="undo-changes"}}
@@ -19,7 +19,7 @@
{{i18n "admin.wizard.field.required"}}
-
+
{{i18n "admin.wizard.field.required_label"}}
{{input type="checkbox" checked=field.required}}
@@ -44,7 +44,7 @@
imageUrl=field.image
onUploadDone=(action "imageUploadDone")
onUploadDeleted=(action "imageUploadDeleted")
- type="wizard-step"
+ type="wizard-field-image"
class="no-repeat contain-image"
id=(concat "wizard-field-" field.id "-image-upload")}}
@@ -54,7 +54,7 @@
{{i18n "admin.wizard.type"}}
-
+
{{combo-box
value=field.type
@@ -216,76 +216,70 @@
{{tag-group-chooser
+ id=(concat field.id "-tag-groups")
tagGroups=field.tag_groups
+ onChange=(action (mut field.tag_groups))
}}
+
+
+
+ {{i18n "admin.wizard.field.can_create_tag"}}
+
+
+
+ {{input
+ type="checkbox"
+ checked=field.can_create_tag}}
+
+
{{/if}}
-{{#if showAdvanced}}
- {{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
+{{#wizard-subscription-container}}
+
+
+ {{i18n "admin.wizard.condition"}}
+
- {{#if field.showAdvanced}}
-
+
+ {{wizard-mapper
+ inputs=field.condition
+ options=fieldConditionOptions}}
+
+
-
-
- {{i18n "admin.wizard.condition"}}
-
+
+
+ {{i18n "admin.wizard.index"}}
+
-
- {{wizard-mapper
- inputs=field.condition
- options=fieldConditionOptions}}
-
+
+ {{wizard-mapper
+ inputs=field.index
+ options=fieldIndexOptions}}
+
+
+
+ {{#if isCategory}}
+
+
+ {{i18n "admin.wizard.field.property"}}
-
-
- {{i18n "admin.wizard.index"}}
-
-
-
- {{wizard-mapper
- inputs=field.index
- options=fieldIndexOptions}}
-
+
+ {{combo-box
+ value=field.property
+ content=categoryPropertyTypes
+ onChange=(action (mut field.property))
+ options=(hash
+ none="admin.wizard.selector.placeholder.property"
+ )}}
-
- {{#if isCategory}}
-
-
- {{i18n "admin.wizard.field.property"}}
-
-
-
- {{combo-box
- value=field.property
- content=categoryPropertyTypes
- onChange=(action (mut field.property))
- options=(hash
- none="admin.wizard.selector.placeholder.property"
- )}}
-
-
- {{/if}}
-
-
-
- {{i18n "admin.wizard.translation"}}
-
-
- {{input
- name="key"
- value=field.key
- class="medium"
- placeholderKey="admin.wizard.translation_placeholder"}}
-
-
-
- {{#if validations}}
- {{wizard-realtime-validations field=field validations=validations}}
- {{/if}}
{{/if}}
-{{/if}}
+
+ {{#if validations}}
+ {{wizard-realtime-validations field=field validations=validations}}
+ {{/if}}
+{{/wizard-subscription-container}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
index ad4623c2..40ac09e0 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
@@ -18,7 +18,7 @@
imageUrl=step.banner
onUploadDone=(action "bannerUploadDone")
onUploadDeleted=(action "bannerUploadDeleted")
- type="wizard-banner"
+ type="wizard-step-banner"
class="no-repeat contain-image"
id=(concat "wizard-step-" step.id "-banner-upload")}}
@@ -34,88 +34,72 @@
-{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
-
-{{#if step.showAdvanced}}
-
-
-
-
- {{i18n "admin.wizard.condition"}}
-
-
-
- {{wizard-mapper
- inputs=step.condition
- options=stepConditionOptions}}
-
+{{#wizard-subscription-container}}
+
+
+ {{i18n "admin.wizard.condition"}}
-
-
-
-
{{i18n "admin.wizard.step.force_final.label"}}
- {{input type="checkbox" checked=step.force_final}}
- {{i18n "admin.wizard.step.force_final.description"}}
-
-
-
-
-
- {{i18n "admin.wizard.step.required_data.label"}}
-
-
- {{wizard-mapper
- inputs=step.required_data
- options=(hash
- inputTypes="validation"
- inputConnector="and"
- wizardFieldSelection="value"
- userFieldSelection="value"
- keyPlaceholder="admin.wizard.submission_key"
- context="step"
- )}}
- {{#if step.required_data}}
-
-
- {{i18n "admin.wizard.step.required_data.not_permitted_message"}}
-
- {{input value=step.required_data_message}}
-
- {{/if}}
-
-
-
-
-
- {{i18n "admin.wizard.step.permitted_params.label"}}
-
-
- {{wizard-mapper
- inputs=step.permitted_params
- options=(hash
- pairConnector="set"
- inputTypes="association"
- keyPlaceholder="admin.wizard.param_key"
- valuePlaceholder="admin.wizard.submission_key"
- context="step"
- )}}
-
-
-
-
-
- {{i18n "admin.wizard.translation"}}
-
-
- {{input
- name="key"
- value=step.key
- placeholderKey="admin.wizard.translation_placeholder"}}
-
+
+ {{wizard-mapper
+ inputs=step.condition
+ options=stepConditionOptions}}
-{{/if}}
+
+
+
+
+
{{i18n "admin.wizard.step.force_final.label"}}
+ {{input type="checkbox" checked=step.force_final}}
+ {{i18n "admin.wizard.step.force_final.description"}}
+
+
+
+
+
+ {{i18n "admin.wizard.step.required_data.label"}}
+
+
+
+ {{wizard-mapper
+ inputs=step.required_data
+ options=(hash
+ inputTypes="validation"
+ inputConnector="and"
+ wizardFieldSelection="value"
+ userFieldSelection="value"
+ keyPlaceholder="admin.wizard.submission_key"
+ context="step"
+ )}}
+ {{#if step.required_data}}
+
+
+ {{i18n "admin.wizard.step.required_data.not_permitted_message"}}
+
+ {{input value=step.required_data_message}}
+
+ {{/if}}
+
+
+
+
+
+ {{i18n "admin.wizard.step.permitted_params.label"}}
+
+
+ {{wizard-mapper
+ inputs=step.permitted_params
+ options=(hash
+ pairConnector="set"
+ inputTypes="association"
+ keyPlaceholder="admin.wizard.param_key"
+ valuePlaceholder="admin.wizard.submission_key"
+ context="step"
+ )}}
+
+
+{{/wizard-subscription-container}}
{{wizard-links
itemType="field"
@@ -130,5 +114,6 @@
currentFieldId=currentField.id
fieldTypes=fieldTypes
removeField="removeField"
- wizardFields=wizardFields}}
+ wizardFields=wizardFields
+ subscribed=subscribed}}
{{/each}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-links.hbs b/assets/javascripts/discourse/templates/components/wizard-links.hbs
index a7a7662e..368acd35 100644
--- a/assets/javascripts/discourse/templates/components/wizard-links.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-links.hbs
@@ -4,17 +4,16 @@
{{#if anyLinks}}
{{#each links as |link|}}
- {{d-button action="change" actionParam=link.id translatedLabel=link.label class=link.classes}}
+ {{d-button action=(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"}}
+ {{d-button action=(action "back") actionParam=link icon="arrow-left" class="back"}}
{{/unless}}
{{#unless link.last}}
- {{d-button action="forward" actionParam=link icon="arrow-right" class="forward"}}
+ {{d-button action=(action "forward") actionParam=link icon="arrow-right" class="forward"}}
{{/unless}}
- {{d-button action="remove" actionParam=link.id icon="times" class="remove"}}
+ {{d-button action=(action "remove") actionParam=link.id icon="times" class="remove"}}
{{/each}}
{{/if}}
- {{d-button action="add" label="admin.wizard.add" icon="plus"}}
+ {{d-button action=(action "add") label="admin.wizard.add" icon="plus"}}
-
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
index 60f4c0cf..cb94b7ae 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
@@ -66,11 +66,13 @@
{{/if}}
{{#if showUser}}
- {{user-selector
- includeMessageableGroups="true"
+ {{email-group-user-chooser
placeholderKey=placeholderKey
- usernames=value
+ value=value
autocomplete="discourse"
- onChangeCallback=(action "changeUserValue")}}
+ onChange=(action "changeUserValue")
+ options=(hash
+ includeMessageableGroups="true"
+ )}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs
index 2de35e0d..c0cc6818 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs
@@ -15,6 +15,6 @@
{{#if canAdd}}
- {{d-button action="add" label="admin.wizard.add" icon="plus"}}
+ {{d-button action=(action "add") label="admin.wizard.add" icon="plus"}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
index cd1298a9..8269d6ca 100644
--- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
@@ -1,50 +1,53 @@
-
{{i18n "admin.wizard.field.validations.header"}}
-
-
- {{#each-in field.validations as |type props|}}
- -
-
-
{{i18n (concat "admin.wizard.field.validations." type)}}
- {{input type="checkbox" checked=props.status}}
- {{i18n "admin.wizard.field.validations.enabled"}}
-
-
-
-
-
{{i18n "admin.wizard.field.validations.categories"}}
+
+ {{i18n "admin.wizard.field.validations.header"}}
+
+
+
+ {{#each-in field.validations as |type props|}}
+ -
+
+
{{i18n (concat "admin.wizard.field.validations." type)}}
+ {{input type="checkbox" checked=props.status}}
+ {{i18n "admin.wizard.field.validations.enabled"}}
+
+
+
+
+ {{i18n "admin.wizard.field.validations.categories"}}
+
+
+ {{category-selector
+ categories=(get this (concat "validationBuffer." type ".categories"))
+ onChange=(action "updateValidationCategories" type props)
+ class="wizard"}}
+
-
- {{category-selector
- categories=(get this (concat "validationBuffer." type ".categories"))
- onChange=(action "updateValidationCategories" type props)
- class="wizard"}}
+
+
+ {{i18n "admin.wizard.field.validations.max_topic_age"}}
+
+
+ {{input type="number" class="time-n-value" value=props.time_n_value}}
+ {{combo-box
+ value=(readonly props.time_unit)
+ content=timeUnits
+ class="time-unit-selector"
+ onChange=(action (mut props.time_unit))}}
+
+
+
+
+ {{i18n "admin.wizard.field.validations.position"}}
+
+
+ {{radio-button name=(concat type field.id) value="above" selection=props.position}}
+ {{i18n "admin.wizard.field.validations.above"}}
+ {{radio-button name=(concat type field.id) value="below" selection=props.position}}
+ {{i18n "admin.wizard.field.validations.below"}}
+
-
-
- {{i18n "admin.wizard.field.validations.max_topic_age"}}
-
-
- {{input type="number" class="time-n-value" value=props.time_n_value}}
- {{combo-box
- value=(readonly props.time_unit)
- content=timeUnits
- class="time-unit-selector"
- onChange=(action (mut props.time_unit))}}
-
-
-
-
- {{i18n "admin.wizard.field.validations.position"}}
-
-
- {{radio-button name=(concat type field.id) value="above" selection=props.position}}
- {{i18n "admin.wizard.field.validations.above"}}
- {{radio-button name=(concat type field.id) value="below" selection=props.position}}
- {{i18n "admin.wizard.field.validations.below"}}
-
-
-
-
- {{/each-in}}
-
+
+ {{/each-in}}
+
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs
new file mode 100644
index 00000000..b2ce05bc5
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs
@@ -0,0 +1,6 @@
+
+
{{label}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs
new file mode 100644
index 00000000..01d436f5
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs
@@ -0,0 +1,12 @@
+
+
+
+ {{yield}}
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs
new file mode 100644
index 00000000..00569756
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs
@@ -0,0 +1 @@
+{{d-icon icon}}{{label}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs
new file mode 100644
index 00000000..d91e90f1
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs
@@ -0,0 +1,15 @@
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs
new file mode 100644
index 00000000..de24fd75
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs
@@ -0,0 +1,15 @@
+{{#if icons}}
+
+
+ {{#each icons as |icon|}}
+ {{d-icon icon translatedtitle=(dasherize title)}}
+ {{/each}}
+
+{{/if}}
+
+
+ {{html-safe label}}
+ {{#if item.subscriptionRequired}}
+ {{i18n item.selectorLabel}}
+ {{/if}}
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-table-field.hbs b/assets/javascripts/discourse/templates/components/wizard-table-field.hbs
new file mode 100644
index 00000000..af30f422
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-table-field.hbs
@@ -0,0 +1,161 @@
+{{#if hasValue}}
+ {{#if isText}}
+ {{value.value}}
+ {{/if}}
+
+ {{#if isLongtext}}
+
+
+ {{value.value}}
+
+
+ {{toggleText}}
+
+
+ {{/if}}
+
+ {{#if isComposer}}
+
+
+ {{value.value}}
+
+
+ {{toggleText}}
+
+
+ {{/if}}
+
+ {{#if isComposerPreview}}
+ {{d-icon "comment-alt"}}
+
+ {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}}
+
+ {{/if}}
+
+ {{#if isTextOnly}}
+ {{value.value}}
+ {{/if}}
+
+ {{#if isDate}}
+
+ {{d-icon "calendar"}}{{value.value}}
+
+ {{/if}}
+
+ {{#if isTime}}
+
+ {{d-icon "clock"}}{{value.value}}
+
+ {{/if}}
+
+ {{#if isDateTime}}
+
+ {{d-icon "calendar"}}{{format-date value.value format="medium"}}
+
+ {{/if}}
+
+ {{#if isNumber}}
+ {{value.value}}
+ {{/if}}
+
+ {{#if isCheckbox}}
+ {{#if checkboxValue}}
+
+ {{d-icon "check"}}{{value.value}}
+
+ {{else}}
+
+ {{d-icon "times"}}{{value.value}}
+
+ {{/if}}
+ {{/if}}
+
+ {{#if isUrl}}
+
+ {{d-icon "link"}}
+
+ {{value.value}}
+
+
+ {{/if}}
+
+ {{#if isUpload}}
+
+ {{file.original_filename}}
+
+ {{/if}}
+
+ {{#if isDropdown}}
+
+ {{d-icon "check-square"}}
+ {{value.value}}
+
+ {{/if}}
+
+ {{#if isTag}}
+ {{#each value.value as |tag|}}
+ {{discourse-tag tag}}
+ {{/each}}
+ {{/if}}
+
+ {{#if isCategory}}
+
+ {{i18n "admin.wizard.submissions.category_id"}}:
+
+
+ {{value.value}}
+
+ {{/if}}
+
+ {{#if isGroup}}
+
+ {{i18n "admin.wizard.submissions.group_id"}}:
+
+ {{value.value}}
+ {{/if}}
+
+ {{#if isUserSelector}}
+ {{#each submittedUsers as |user|}}
+ {{d-icon "user"}}
+
+ {{user.username}}
+
+ {{/each}}
+ {{/if}}
+
+ {{#if isUser}}
+ {{#link-to "user" value.username}}
+ {{avatar value imageSize="tiny"}}
+ {{/link-to}}
+ {{/if}}
+
+ {{#if showUsername}}
+
+ {{username}}
+
+ {{/if}}
+
+ {{#if isSubmittedAt}}
+
+ {{raw-date value}}
+
+ {{/if}}
+{{else}}
+ —
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
index c657049d..37a3e549 100644
--- a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
@@ -6,13 +6,13 @@
{{#if previewEnabled}}
{{d-button
- action="togglePreview"
+ action=(action "togglePreview")
translatedLabel=previewLabel}}
{{/if}}
{{#if fieldsEnabled}}
{{d-button
- action="togglePopover"
+ action=(action "togglePopover")
translatedLabel=popoverLabel}}
{{#if showPopover}}
diff --git a/assets/javascripts/discourse/templates/custom-wizard-index.hbs b/assets/javascripts/discourse/templates/custom-wizard-index.hbs
new file mode 100644
index 00000000..d208851d
--- /dev/null
+++ b/assets/javascripts/discourse/templates/custom-wizard-index.hbs
@@ -0,0 +1,3 @@
+{{#if noAccess}}
+ {{custom-wizard-no-access text=(i18n noAccessI18nKey) wizardId=wizardId reason=noAccessReason}}
+{{/if}}
diff --git a/assets/javascripts/wizard/templates/custom.step.hbs b/assets/javascripts/discourse/templates/custom-wizard-step.hbs
similarity index 54%
rename from assets/javascripts/wizard/templates/custom.step.hbs
rename to assets/javascripts/discourse/templates/custom-wizard-step.hbs
index 6456a59c..19357063 100644
--- a/assets/javascripts/wizard/templates/custom.step.hbs
+++ b/assets/javascripts/discourse/templates/custom-wizard-step.hbs
@@ -5,16 +5,16 @@
{{#if showReset}}
- {{wizard-i18n "wizard.reset"}}
+ {{i18n "wizard.reset"}}
{{/if}}
{{/if}}
{{#if step.permitted}}
- {{wizard-step step=step
- wizard=wizard
- goNext="goNext"
- goBack=(action "goBack")
- finished="finished"
- showMessage="showMessage"}}
+ {{custom-wizard-step
+ step=step
+ wizard=wizard
+ goNext=(action "goNext")
+ goBack=(action "goBack")
+ showMessage=(action "showMessage")}}
{{/if}}
diff --git a/assets/javascripts/wizard/templates/custom.hbs b/assets/javascripts/discourse/templates/custom-wizard.hbs
similarity index 86%
rename from assets/javascripts/wizard/templates/custom.hbs
rename to assets/javascripts/discourse/templates/custom-wizard.hbs
index 4701fec2..f6d6127e 100644
--- a/assets/javascripts/wizard/templates/custom.hbs
+++ b/assets/javascripts/discourse/templates/custom-wizard.hbs
@@ -1,7 +1,3 @@
-{{#if showCanvas}}
- {{wizard-canvas}}
-{{/if}}
-
{{outlet}}
diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs
new file mode 100644
index 00000000..eb5218b1
--- /dev/null
+++ b/assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs
@@ -0,0 +1,32 @@
+{{#d-modal-body title="admin.wizard.edit_columns"}}
+ {{#if loading}}
+ {{loading-spinner size="large"}}
+ {{else}}
+
+ {{#each model.columns as |column|}}
+
+
+
+ {{input type="checkbox" checked=column.enabled}}
+ {{directory-table-header-title field=column.label translated=true}}
+
+
+
+ {{/each}}
+
+ {{/if}}
+{{/d-modal-body}}
+
+
diff --git a/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs
index 1b138360..cbc9d610 100644
--- a/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs
+++ b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs
@@ -9,7 +9,7 @@