diff --git a/.discourse-compatibility b/.discourse-compatibility index b53ff04d..ff1e994a 100644 --- a/.discourse-compatibility +++ b/.discourse-compatibility @@ -1,2 +1,4 @@ +3.2.0.beta2: 1ee2f7d8babafe32912372fbbfa50c89f5b09ba9 +3.1.999: 1f35b80f85e5fd1efb7f4851f0845700432febdc 2.7.99: e07a57e398b6b1676ab42a7e34467556fca5416b 2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c diff --git a/.github/workflows/discourse-plugin.yml b/.github/workflows/discourse-plugin.yml new file mode 100644 index 00000000..13850e3e --- /dev/null +++ b/.github/workflows/discourse-plugin.yml @@ -0,0 +1,13 @@ +name: Discourse Plugin + +on: + push: + branches: + - main + pull_request: + schedule: + - cron: "0 0 * * *" + +jobs: + ci: + uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 diff --git a/.github/workflows/plugin-linting.yml b/.github/workflows/plugin-linting.yml deleted file mode 100644 index a915ea7f..00000000 --- a/.github/workflows/plugin-linting.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Linting - -on: - push: - branches: - - master - - main - - stable - pull_request: - schedule: - - cron: '0 0 * * *' - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 - with: - node-version: 14 - - - 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 - - - name: ESLint - run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts - - - name: Prettier - run: | - yarn prettier -v - if [ -d "assets" ]; then \ - yarn prettier --list-different "assets/**/*.{scss,js,es6}" ; \ - fi - if [ -d "test" ]; 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 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 deleted file mode 100644 index 497486ea..00000000 --- a/.github/workflows/plugin-tests.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: Plugin Tests - -on: - push: - branches: - - stable - - master - - main - pull_request: - schedule: - - cron: '0 */12 * * *' - -jobs: - build: - name: ${{ matrix.build_type }} - runs-on: ubuntu-latest - timeout-minutes: 60 - - env: - DISCOURSE_HOSTNAME: www.example.com - RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072 - RAILS_ENV: test - PGHOST: localhost - PGUSER: discourse - PGPASSWORD: discourse - - strategy: - fail-fast: false - - 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@v3 - with: - repository: discourse/discourse - ref: ${{ steps.discourse_branch.outputs.value }} - fetch-depth: 1 - - - name: Install plugin - uses: actions/checkout@v2 - with: - path: plugins/${{ github.event.repository.name }} - ref: "${{ github.head_ref }}" - fetch-depth: 1 - - - name: Setup Git - run: | - git config --global user.email "ci@ci.invalid" - git config --global user.name "Discourse CI" - - - name: Setup packages - 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 - - - name: Update imagemagick - if: matrix.build_type == 'backend' - run: | - wget https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-imagemagick - chmod +x install-imagemagick - sudo ./install-imagemagick - - - name: Setup redis - uses: shogo82148/actions-setup-redis@v1 - with: - redis-version: ${{ matrix.redis }} - - - name: Setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - - 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" - - - 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 - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.os }}-yarn- - - - name: Yarn install - run: yarn install --dev - - - name: Migrate database - run: | - bin/rake db:create - bin/rake db:migrate - - - name: Plugin RSpec with Coverage - if: matrix.build_type == 'backend' - run: | - if [ -e plugins/${{ steps.repo-name.outputs.value }}/.simplecov ] - then - cp plugins/${{ steps.repo-name.outputs.value }}/.simplecov .simplecov - export COVERAGE=1 - fi - bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] - - - name: Plugin QUnit - if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] - 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..74c8c853 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,2 +1,11 @@ inherit_gem: rubocop-discourse: default.yml + +RSpec/ContextWording: + Enabled: false + +RSpec/DescribeClass: + Enabled: false + +Discourse/TimeEqMatcher: + Enabled: false diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 48cea364..66b401ac 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,4 +1,4 @@ -All code in this repository is Copyright 2018 by Angus McLeod. +All code in this repository is Copyright 2023 by Angus McLeod. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by 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..063ceaa2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,31 @@ -# 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. + + + +👋 Looking to report an issue? We're managing issues for this plugin using our [bug report wizard](https://pavilion.tech/products/discourse-custom-wizard-plugin/support/bug-report). + +## 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://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/), or go directly to the relevant section + +- [Wizard Administration](https://coop.pavilion.tech/t/1602) +- [Wizard Settings](https://coop.pavilion.tech/t/1614) +- [Step Settings](https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/step-settings) +- [Field Settings](https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/field-settings) +- [Conditional Settings](https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/conditional-settings) +- [Field Interpolation](https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/field-interpolation) +- [Handling Dates and Times](https://coop.pavilion.tech/t/1708) + +## Support + +- [Report an issue](https://pavilion.tech/products/discourse-custom-wizard-plugin/support/bug-report) + +## Statistics + +For improved service and development, this plugin collects some generalised quantitative data related to version and usage. No personal or sensitive information is gathered. Please email contact@pavilion.tech if you have any questions or concerns about our data collection. 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/app/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb index c99954d6..2b950b23 100644 --- a/app/controllers/custom_wizard/admin/admin.rb +++ b/app/controllers/custom_wizard/admin/admin.rb @@ -2,9 +2,6 @@ class CustomWizard::AdminController < ::Admin::AdminController before_action :ensure_admin - def index - end - private def find_wizard diff --git a/app/controllers/custom_wizard/admin/api.rb b/app/controllers/custom_wizard/admin/api.rb index a913e9ca..e4ac31e9 100644 --- a/app/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/app/controllers/custom_wizard/admin/custom_fields.rb b/app/controllers/custom_wizard/admin/custom_fields.rb index c52759c9..111e9faf 100644 --- a/app/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 index 976814f8..7ca37bb2 100644 --- a/app/controllers/custom_wizard/admin/logs.rb +++ b/app/controllers/custom_wizard/admin/logs.rb @@ -1,9 +1,44 @@ # frozen_string_literal: true class CustomWizard::AdminLogsController < CustomWizard::AdminController + before_action :find_wizard, except: [:index] + def index - render_serialized( - CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i), - CustomWizard::LogSerializer + 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/app/controllers/custom_wizard/admin/submissions.rb b/app/controllers/custom_wizard/admin/submissions.rb index 4cb2a0e4..d5994c96 100644 --- a/app/controllers/custom_wizard/admin/submissions.rb +++ b/app/controllers/custom_wizard/admin/submissions.rb @@ -13,12 +13,21 @@ 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, + content = ActiveModel::ArraySerializer.new( + CustomWizard::Submission.list(@wizard).submissions, + each_serializer: CustomWizard::SubmissionSerializer + ) + + send_data content.to_json, filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json", content_type: "application/json", disposition: "attachment" @@ -26,7 +35,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/app/controllers/custom_wizard/admin/subscription.rb b/app/controllers/custom_wizard/admin/subscription.rb new file mode 100644 index 00000000..7b596ec6 --- /dev/null +++ b/app/controllers/custom_wizard/admin/subscription.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +class CustomWizard::SubscriptionController < ::Admin::AdminController + before_action :ensure_admin + + def index + if params[:update_from_remote] + subscription = CustomWizard::Subscription.new(true) + else + subscription = CustomWizard::Subscription.new + end + + render_json_dump( + subscribed: subscription.subscribed?, + subscription_type: subscription.type, + subscription_attributes: CustomWizard::Subscription.attributes, + ) + end +end diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb index 0a59e02b..08e7b6d0 100644 --- a/app/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/app/controllers/custom_wizard/steps.rb b/app/controllers/custom_wizard/steps.rb index df3c2cb3..2a4305c7 100644 --- a/app/controllers/custom_wizard/steps.rb +++ b/app/controllers/custom_wizard/steps.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -class CustomWizard::StepsController < ::ApplicationController - before_action :ensure_logged_in +class CustomWizard::StepsController < ::CustomWizard::WizardClientController before_action :ensure_can_update def update @@ -22,7 +21,7 @@ class CustomWizard::StepsController < ::ApplicationController if updater.success? wizard_id = update_params[:wizard_id] - builder = CustomWizard::Builder.new(wizard_id, current_user) + builder = CustomWizard::Builder.new(wizard_id, current_user, guest_id) @wizard = builder.build(force: true) current_step = @wizard.find_step(update[:step_id]) @@ -85,7 +84,6 @@ class CustomWizard::StepsController < ::ApplicationController private def ensure_can_update - @builder = CustomWizard::Builder.new(update_params[:wizard_id], current_user) raise Discourse::InvalidParameters.new(:wizard_id) if @builder.template.nil? raise Discourse::InvalidAccess.new if !@builder.wizard || !@builder.wizard.can_access? diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index bb53670b..dd4ea4ca 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -1,39 +1,10 @@ # frozen_string_literal: true -class CustomWizard::WizardController < ::ActionController::Base - helper ApplicationHelper - - include CurrentUser - include CanonicalURL::ControllerExtensions - include GlobalPath - - prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'app', 'views')) - layout :set_wizard_layout - - before_action :preload_wizard_json - before_action :ensure_plugin_enabled - before_action :ensure_logged_in, only: [:skip] - - helper_method :wizard_page_title - helper_method :wizard_theme_id - helper_method :wizard_theme_lookup - helper_method :wizard_theme_translations_lookup - - def set_wizard_layout - action_name === 'qunit' ? 'qunit' : 'wizard' - end - - def index - respond_to do |format| - format.json do - 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 - format.html do - render "default/empty" - end +class CustomWizard::WizardController < ::CustomWizard::WizardClientController + 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 @@ -57,69 +28,12 @@ class CustomWizard::WizardController < ::ActionController::Base render json: result end - def qunit - raise Discourse::InvalidAccess.new if Rails.env.production? - - respond_to do |format| - format.html do - render "default/empty" - end - end - end - protected - def ensure_logged_in - raise Discourse::NotLoggedIn.new unless current_user.present? - end - - def guardian - @guardian ||= Guardian.new(current_user, request) - end - 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 - - def wizard_page_title - wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title') - end - - def wizard_theme_id - wizard ? wizard.theme_id : nil - end - - def wizard_theme_lookup(name) - Theme.lookup_field(wizard_theme_id, view_context.mobile_view? ? :mobile : :desktop, name) - end - - def wizard_theme_translations_lookup - Theme.lookup_field(wizard_theme_id, :translations, I18n.locale) - end - - def preload_wizard_json - return if request.xhr? || request.format.json? - return if request.method != "GET" - - store_preloaded("siteSettings", SiteSetting.client_settings_json) - end - - def store_preloaded(key, json) - @preloaded ||= {} - @preloaded[key] = json.gsub(" - - <%= discourse_color_scheme_stylesheets %> - - <%= discourse_stylesheet_link_tag :wizard, theme_id: nil %> - <%= discourse_stylesheet_link_tag :wizard_custom %> - <%- if wizard_theme_id.present? %> - <%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme), theme_id: wizard_theme_id %> - <%- end %> - - <%= preload_script "locales/#{I18n.locale}" %> - <%= 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" %> - - <%= csrf_meta_tags %> - - <%- unless customization_disabled? %> - <%= wizard_theme_translations_lookup %> - <%= raw wizard_theme_lookup("head_tag") %> - <%- end %> - - <%= server_plugin_outlet "custom_wizard" %> - - <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %> - - - - - <%= render partial: "layouts/head" %> - <%= wizard_page_title %> - - - - <%- unless customization_disabled? %> - <%= raw wizard_theme_lookup("header") %> - <%- end %> - -
- - <%- unless customization_disabled? %> - <%= raw wizard_theme_lookup("body_tag") %> - <%- end %> - - <%- if current_user %> - <%= preload_script 'wizard-custom-start' %> - <%- else %> - <%= preload_script 'wizard-custom-guest' %> - <%- end %> - - - - - - 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 94% rename from assets/javascripts/wizard/components/custom-user-selector.js.es6 rename to assets/javascripts/discourse/components/custom-user-selector.js.es6 index 56eb8f57..59711ec2 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/discourse/components/custom-user-selector.js.es6 @@ -3,11 +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"; +import TextField from "discourse/components/text-field"; const template = function (params) { const options = params.options; @@ -41,7 +41,7 @@ export default TextField.extend({ @computed("placeholderKey") placeholder(placeholderKey) { - return placeholderKey ? WizardI18n(placeholderKey) : ""; + return placeholderKey ? I18n.t(placeholderKey) : ""; }, @observes("usernames") 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 68% rename from assets/javascripts/wizard/components/wizard-composer-editor.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 index 4f44d439..95ae56c0 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,16 +9,21 @@ 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"; +import InsertHyperlink from "discourse/components/modal/insert-hyperlink"; +import { inject as service } from "@ember/service"; + +const IMAGE_MARKDOWN_REGEX = + /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g; export default ComposerEditor.extend({ - layoutName: "wizard/templates/components/wizard-composer-editor", + modal: service(), + classNameBindings: ["fieldClass"], allowUpload: true, showLink: false, - showHyperlinkBox: false, topic: null, showToolbar: true, focusTarget: "reply", @@ -26,7 +32,8 @@ export default ComposerEditor.extend({ popupMenuOptions: [], draftStatus: "null", replyPlaceholder: alias("field.translatedPlaceholder"), - uploadingFieldId: null, + wizardEventFieldId: null, + composerEventPrefix: "wizard-editor", @on("didInsertElement") _composerEditorInit() { @@ -35,7 +42,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) => { @@ -75,43 +82,13 @@ export default ComposerEditor.extend({ $input.on("scroll", this._throttledSyncEditorAndPreviewScroll); this._bindUploadTarget(); - 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(":"); - let currentEventPrefix = eventParts[0]; - let currentEventName = eventParts[1]; + const field = this.field; + this.editorInputClass = `.${dasherize(field.type)}-${dasherize( + field.id + )} .d-editor-input`; - if ( - 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); - } else { - return this._super(name, ...args); - } - }, + this._uppyInstance.on("file-added", () => { + this.session.set("wizardEventFieldId", field.id); }); }, @@ -133,17 +110,33 @@ export default ComposerEditor.extend({ return uploadIcon(false, this.siteSettings); }, - click(e) { - if ($(e.target).hasClass("wizard-composer-hyperlink")) { - this.set("showHyperlinkBox", false); + @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", @@ -159,7 +152,7 @@ export default ComposerEditor.extend({ shortcut: "K", trimLeading: true, unshift: true, - sendAction: () => component.set("showHyperlinkBox", true), + sendAction: (event) => component.send("showLinkModal", event), }); if (this.siteSettings.mentionables_enabled) { @@ -188,33 +181,32 @@ 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); }, - addLink(linkName, linkUrl) { - let link = `[${linkName}](${linkUrl})`; - this.appEvents.trigger("wizard-editor:insert-text", { - fieldId: this.field.id, - text: link, - }); - this.set("showHyperlinkBox", false); - }, + showLinkModal(toolbarEvent) { + let linkText = ""; + this._lastSel = toolbarEvent.selected; - hideBox() { - this.set("showHyperlinkBox", false); + if (this._lastSel) { + linkText = this._lastSel.value; + } + this.modal.show(InsertHyperlink, { + model: { linkText, toolbarEvent }, + }); }, 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-date-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6 similarity index 84% rename from assets/javascripts/wizard/components/wizard-date-input.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-date-input.js.es6 index da2711c7..2805c370 100644 --- a/assets/javascripts/wizard/components/wizard-date-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6 @@ -3,7 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, - layoutName: "wizard/templates/components/wizard-date-input", + classNameBindings: ["fieldClass"], @discourseComputed() placeholder() { 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 86% 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 index 84a2b03e..1fcb62f5 100644 --- a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6 @@ -2,7 +2,7 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ - layoutName: "wizard/templates/components/wizard-date-time-input", + classNameBindings: ["fieldClass"], @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { 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 93% rename from assets/javascripts/wizard/components/wizard-field-category.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-category.js.es6 index 441f83d3..65e4a1c7 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-category.js.es6 @@ -3,8 +3,6 @@ import Category from "discourse/models/category"; import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-category", - 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 93% 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 index a2056a86..b49233f2 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-composer-preview.js.es6 @@ -7,8 +7,6 @@ import { ajax } from "discourse/lib/ajax"; import { on } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-composer-preview", - @on("init") updatePreview() { if (this.isDestroyed) { 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 93% rename from assets/javascripts/wizard/components/wizard-field-composer.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-composer.js.es6 index 255982ea..1a25344c 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-composer.js.es6 @@ -6,8 +6,6 @@ import EmberObject from "@ember/object"; import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-composer", - 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 83% 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 index eee98892..2d918636 100644 --- a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-date-time.js.es6 @@ -2,8 +2,6 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-date-time", - @observes("dateTime") setValue() { this.set("field.value", this.dateTime.format(this.field.format)); 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 84% rename from assets/javascripts/wizard/components/wizard-field-date.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-date.js.es6 index df35638c..d5d0a830 100644 --- a/assets/javascripts/wizard/components/wizard-field-date.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-date.js.es6 @@ -2,8 +2,6 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-date", - @observes("date") setValue() { this.set("field.value", this.date.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6 similarity index 76% rename from assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6 index e6b08102..659b8f29 100644 --- a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-dropdown.js.es6 @@ -1,8 +1,6 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-dropdown", - keyPress(e) { e.stopPropagation(); }, 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/wizard/components/wizard-field-text.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-text.js.es6 similarity index 66% rename from assets/javascripts/wizard/components/wizard-field-text.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-text.js.es6 index d9e7cca8..3e49cb35 100644 --- a/assets/javascripts/wizard/components/wizard-field-text.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-text.js.es6 @@ -1,8 +1,6 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-text", - keyPress(e) { e.stopPropagation(); }, diff --git a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6 similarity index 65% rename from assets/javascripts/wizard/components/wizard-field-textarea.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6 index e59a1707..3e49cb35 100644 --- a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-textarea.js.es6 @@ -1,8 +1,6 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-textarea", - 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 87% rename from assets/javascripts/wizard/components/wizard-field-time.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-time.js.es6 index a2f2a10d..1406d63b 100644 --- a/assets/javascripts/wizard/components/wizard-field-time.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-time", + classNameBindings: ["fieldClass"], @observes("time") setValue() { 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 4774e942..990d7daa 100644 --- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6 @@ -3,9 +3,8 @@ import Component from "@ember/component"; import { computed } from "@ember/object"; export default Component.extend(UppyUploadMixin, { - layoutName: "wizard/templates/components/wizard-field-upload", classNames: ["wizard-field-upload"], - classNameBindings: ["isImage"], + classNameBindings: ["isImage", "fieldClass"], uploading: false, type: computed(function () { return `wizard_${this.field.id}`; 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/wizard/components/wizard-field-tag.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6 similarity index 56% rename from assets/javascripts/wizard/components/wizard-field-tag.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6 index 473bba08..64741c6b 100644 --- a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field-tag", + classNameBindings: ["fieldClass"], }); diff --git a/assets/javascripts/wizard/components/wizard-field.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field.js.es6 similarity index 70% rename from assets/javascripts/wizard/components/wizard-field.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-field.js.es6 index 493d7676..bb368310 100644 --- a/assets/javascripts/wizard/components/wizard-field.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-field.js.es6 @@ -1,10 +1,9 @@ import Component from "@ember/component"; import { dasherize } from "@ember/string"; import discourseComputed from "discourse-common/utils/decorators"; -import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; +import { cook } from "discourse/lib/text"; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-field", classNameBindings: [ ":wizard-field", "typeClasses", @@ -12,6 +11,14 @@ export default Component.extend({ "field.id", ], + didReceiveAttrs() { + this._super(...arguments); + + cook(this.field.translatedDescription).then((cookedDescription) => { + this.set("cookedDescription", cookedDescription); + }); + }, + @discourseComputed("field.type", "field.id") typeClasses: (type, id) => `${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`, @@ -24,12 +31,7 @@ export default Component.extend({ if (["text_only"].includes(type)) { return false; } - return dasherize(type === "component" ? id : `wizard-field-${type}`); - }, - - @discourseComputed("field.translatedDescription") - cookedDescription(description) { - return cook(description); + return dasherize(type === "component" ? id : `custom-wizard-field-${type}`); }, @discourseComputed("field.type") 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 89% rename from assets/javascripts/wizard/components/wizard-group-selector.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-group-selector.js.es6 index 4ff56ec9..cb613107 100644 --- a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-group-selector.js.es6 @@ -3,7 +3,6 @@ import { computed } from "@ember/object"; import { makeArray } from "discourse-common/lib/helpers"; export default ComboBox.extend({ - layoutName: "wizard/templates/components/wizard-group-selector", content: computed("groups.[]", "field.content.[]", function () { const whitelist = makeArray(this.field.content); return this.groups diff --git a/assets/javascripts/wizard/components/wizard-no-access.js.es6 b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 similarity index 65% rename from assets/javascripts/wizard/components/wizard-no-access.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 index 492a41dc..b3b2e26c 100644 --- a/assets/javascripts/wizard/components/wizard-no-access.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 @@ -1,11 +1,11 @@ -import CustomWizard from "../models/wizard"; +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"], - layoutName: "wizard/templates/components/wizard-no-access", @discourseComputed("reason") reasonClass(reason) { @@ -19,7 +19,11 @@ export default Component.extend({ actions: { skip() { - CustomWizard.skip(this.get("wizardId")); + 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 92% rename from assets/javascripts/wizard/components/wizard-similar-topics.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-similar-topics.js.es6 index 6a56873e..687cfa86 100644 --- a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-similar-topics.js.es6 @@ -4,7 +4,6 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ classNames: ["wizard-similar-topics"], - layoutName: "wizard/templates/components/wizard-similar-topics", showTopics: true, didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-step-form.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step-form.js.es6 similarity index 100% rename from assets/javascripts/wizard/components/wizard-step-form.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-step-form.js.es6 diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step.js.es6 similarity index 78% rename from assets/javascripts/wizard/components/wizard-step.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-step.js.es6 index cc23c5bf..250f9140 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-step.js.es6 @@ -4,15 +4,16 @@ import I18n from "I18n"; import getUrl from "discourse-common/lib/get-url"; import { htmlSafe } from "@ember/template"; import { schedule } from "@ember/runloop"; -import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; -import { updateCachedWizard } from "discourse/plugins/discourse-custom-wizard/wizard/models/wizard"; +import { cook } from "discourse/lib/text"; +import CustomWizard, { + updateCachedWizard, +} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard"; import { alias, not } from "@ember/object/computed"; -import CustomWizard from "../models/wizard"; +import discourseLater from "discourse-common/lib/later"; const alreadyWarned = {}; export default Component.extend({ - layoutName: "wizard/templates/components/wizard-step", classNameBindings: [":wizard-step", "step.id"], saving: null, @@ -21,6 +22,17 @@ export default Component.extend({ this.set("stylingDropdown", {}); }, + didReceiveAttrs() { + this._super(...arguments); + + cook(this.step.translatedTitle).then((cookedTitle) => { + this.set("cookedTitle", cookedTitle); + }); + cook(this.step.translatedDescription).then((cookedDescription) => { + this.set("cookedDescription", cookedDescription); + }); + }, + didInsertElement() { this._super(...arguments); this.autoFocus(); @@ -32,16 +44,6 @@ export default Component.extend({ showNextButton: not("step.final"), showDoneButton: alias("step.final"), - @discourseComputed("step.translatedTitle") - cookedTitle(title) { - return cook(title); - }, - - @discourseComputed("step.translatedDescription") - cookedDescription(description) { - return cook(description); - }, - @discourseComputed( "step.index", "step.displayIndex", @@ -90,16 +92,6 @@ export default Component.extend({ this.showMessage(message); }, - keyPress(event) { - if (event.key === "Enter") { - if (this.showDoneButton) { - this.send("quit"); - } else { - this.send("nextStep"); - } - } - }, - @discourseComputed("step.index", "wizard.totalSteps") barStyle(displayIndex, totalSteps) { let ratio = parseFloat(displayIndex) / parseFloat(totalSteps - 1); @@ -119,34 +111,24 @@ export default Component.extend({ }, autoFocus() { - schedule("afterRender", () => { - const $invalid = $( - ".wizard-field.invalid:nth-of-type(1) .wizard-focusable" - ); - - if ($invalid.length) { - return $invalid.focus(); - } - - $(".wizard-focusable:first").focus(); + discourseLater(() => { + schedule("afterRender", () => { + if ($(".invalid .wizard-focusable").length) { + this.animateInvalidFields(); + } + }); }); }, animateInvalidFields() { schedule("afterRender", () => { - let $element = $( - ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit" - ); - - if ($element.length) { + let $invalid = $(".invalid .wizard-focusable"); + if ($invalid.length) { $([document.documentElement, document.body]).animate( { - scrollTop: $element.offset().top - 200, + scrollTop: $invalid.offset().top - 200, }, - 400, - function () { - $element.wiggle(2, 100); - } + 400 ); } }); 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 70% rename from assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-tag-chooser.js.es6 index 32a1fd6a..8d439aa4 100644 --- a/assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js.es6 @@ -4,7 +4,10 @@ export default TagChooser.extend({ searchTags(url, data, callback) { if (this.tagGroups) { let tagGroupsString = this.tagGroups.join(","); - data.tag_groups = tagGroupsString; + data.filterForInput = { + name: "custom-wizard-tag-chooser", + groups: tagGroupsString, + }; } return this._super(url, data, callback); 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 84% rename from assets/javascripts/wizard/components/wizard-text-field.js.es6 rename to assets/javascripts/discourse/components/custom-wizard-text-field.js.es6 index 5991eefc..d2832282 100644 --- a/assets/javascripts/wizard/components/wizard-text-field.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-text-field.js.es6 @@ -1,7 +1,7 @@ import computed from "discourse-common/utils/decorators"; import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction"; -import WizardI18n from "../lib/wizard-i18n"; -import TextField from "@ember/component/text-field"; +import I18n from "I18n"; +import TextField from "discourse/components/text-field"; export default TextField.extend({ attributeBindings: [ @@ -39,6 +39,6 @@ export default TextField.extend({ @computed("placeholderKey") placeholder(placeholderKey) { - return placeholderKey ? WizardI18n(placeholderKey) : ""; + return placeholderKey ? I18n.t(placeholderKey) : ""; }, }); diff --git a/assets/javascripts/discourse/components/custom-wizard-time-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-time-input.js.es6 new file mode 100644 index 00000000..c09ed110 --- /dev/null +++ b/assets/javascripts/discourse/components/custom-wizard-time-input.js.es6 @@ -0,0 +1,3 @@ +import TimeInput from "discourse/components/time-input"; + +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 73% rename from assets/javascripts/wizard/components/field-validators.js.es6 rename to assets/javascripts/discourse/components/field-validators.js.es6 index 7284241c..8b9b39da 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/discourse/components/field-validators.js.es6 @@ -1,8 +1,6 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: "wizard/templates/components/field-validators", - actions: { perform() { this.appEvents.trigger("custom-wizard:validate"); diff --git a/assets/javascripts/discourse/components/modal/admin-wizards-columns.hbs b/assets/javascripts/discourse/components/modal/admin-wizards-columns.hbs new file mode 100644 index 00000000..5e3829da --- /dev/null +++ b/assets/javascripts/discourse/components/modal/admin-wizards-columns.hbs @@ -0,0 +1,34 @@ + + {{#if loading}} + + {{else}} +
+ {{#each @model.columns as |column|}} +
+
+ +
+
+ {{/each}} +
+ {{/if}} + +
\ No newline at end of file diff --git a/assets/javascripts/discourse/components/modal/admin-wizards-columns.js b/assets/javascripts/discourse/components/modal/admin-wizards-columns.js new file mode 100644 index 00000000..8204f6c3 --- /dev/null +++ b/assets/javascripts/discourse/components/modal/admin-wizards-columns.js @@ -0,0 +1,17 @@ +import Component from "@glimmer/component"; +import { action } from "@ember/object"; +import I18n from "I18n"; + +export default class AdminWizardsColumnComponent extends Component { + title = I18n.t("admin.wizard.edit_columns"); + + @action + save() { + this.args.closeModal(); + } + + @action + resetToDefault() { + this.args.model.reset(); + } +} diff --git a/assets/javascripts/discourse/components/modal/next-session-scheduled.hbs b/assets/javascripts/discourse/components/modal/next-session-scheduled.hbs new file mode 100644 index 00000000..2bb4a784 --- /dev/null +++ b/assets/javascripts/discourse/components/modal/next-session-scheduled.hbs @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/assets/javascripts/discourse/components/modal/next-session-scheduled.js b/assets/javascripts/discourse/components/modal/next-session-scheduled.js new file mode 100644 index 00000000..e5b719f8 --- /dev/null +++ b/assets/javascripts/discourse/components/modal/next-session-scheduled.js @@ -0,0 +1,32 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { action } from "@ember/object"; +import I18n from "I18n"; + +export default class NextSessionScheduledComponent extends Component { + @tracked bufferedDateTime; + title = I18n.t("admin.wizard.after_time_modal.title"); + + constructor() { + super(...arguments); + this.bufferedDateTime = this.args.model.dateTime + ? moment(this.args.model.dateTime) + : moment(Date.now()); + } + + get submitDisabled() { + return moment().isAfter(this.bufferedDateTime); + } + + @action + submit() { + const dateTime = this.bufferedDateTime; + this.args.model.update(moment(dateTime).utc().toISOString()); + this.args.closeModal(); + } + + @action + dateTimeChanged(dateTime) { + this.bufferedDateTime = dateTime; + } +} diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/discourse/components/similar-topics-validator.js.es6 similarity index 96% rename from assets/javascripts/wizard/components/similar-topics-validator.js.es6 rename to assets/javascripts/discourse/components/similar-topics-validator.js.es6 index 4f722123..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"; @@ -10,7 +10,6 @@ import { dasherize } from "@ember/string"; export default WizardFieldValidator.extend({ classNames: ["similar-topics-validator"], - layoutName: "wizard/templates/components/similar-topics-validator", similarTopics: null, hasInput: notEmpty("field.value"), hasSimilarTopics: notEmpty("similarTopics"), diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/discourse/components/validator.js.es6 similarity index 87% rename from assets/javascripts/wizard/components/validator.js.es6 rename to assets/javascripts/discourse/components/validator.js.es6 index aa68660c..3c19cc3d 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/discourse/components/validator.js.es6 @@ -1,12 +1,10 @@ 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"], classNameBindings: ["isValid", "isInvalid"], - layoutName: "wizard/templates/components/validator", validMessageKey: null, invalidMessageKey: null, isValid: null, 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..b9329617 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,15 @@ 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://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/action-settings", @discourseComputed("action.type") messageKey(type) { @@ -101,4 +92,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..b19667ad 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,8 @@ 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://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/field-settings", @discourseComputed("field.type") validations(type) { @@ -144,11 +144,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 bf0dd242..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,7 +89,7 @@ export default Component.extend({ let params = setWizardDefaults({}, itemType); params.isNew = true; - params.index = items.length; + params.index = this.getNextIndex(); let id = `${itemType}_${params.index + 1}`; if (itemType === "field") { diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 index 8a2ae9ce..eb9e735a 100644 --- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 @@ -4,7 +4,7 @@ import { default as discourseComputed, observes, } from "discourse-common/utils/decorators"; -import { getOwner } from "discourse-common/lib/get-owner"; +import { getOwner } from "@ember/application"; import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper"; import { generateName, @@ -15,6 +15,7 @@ import { import Component from "@ember/component"; import { bind, later } from "@ember/runloop"; import I18n from "I18n"; +import { inject as service } from "@ember/service"; const customFieldActionMap = { topic: ["create_topic", "send_message"], @@ -28,6 +29,7 @@ const values = ["present", "true", "false"]; export default Component.extend({ classNameBindings: [":mapper-selector", "activeType"], + subscription: service(), showText: computed("activeType", function () { return this.showInput("text"); @@ -116,6 +118,9 @@ export default Component.extend({ groupEnabled: computed("options.groupSelection", "inputType", function () { return this.optionEnabled("groupSelection"); }), + guestGroup: computed("options.guestGroup", "inputType", function () { + return this.optionEnabled("guestGroup"); + }), userEnabled: computed("options.userSelection", "inputType", function () { return this.optionEnabled("userSelection"); }), @@ -126,7 +131,33 @@ export default Component.extend({ return this.connector === "is"; }), - groups: alias("site.groups"), + @discourseComputed( + "site.groups", + "guestGroup", + "subscription.subscriptionType" + ) + groups(groups, guestGroup, subscriptionType) { + let result = groups; + if (!guestGroup) { + return result; + } + + if (["standard", "business"].includes(subscriptionType)) { + let guestIndex; + result.forEach((r, index) => { + if (r.id === 0) { + r.name = I18n.t("admin.wizard.selector.label.users"); + guestIndex = index; + } + }); + result.splice(guestIndex, 0, { + id: -1, + name: I18n.t("admin.wizard.selector.label.guests"), + }); + } + + return result; + }, categories: alias("site.categories"), showComboBox: or( "showWizardField", @@ -377,7 +408,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-mapper.js.es6 b/assets/javascripts/discourse/components/wizard-mapper.js.es6 index 95aabb1c..ec58e3f2 100644 --- a/assets/javascripts/discourse/components/wizard-mapper.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper.js.es6 @@ -32,6 +32,7 @@ export default Component.extend({ pairConnector: options.pairConnector || null, outputConnector: options.outputConnector || null, context: options.context || null, + guestGroup: options.guestGroup || false, }; let inputTypes = ["key", "value", "output"]; 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.hbs b/assets/javascripts/discourse/components/wizard-subscription-badge.hbs new file mode 100644 index 00000000..992504bb --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.hbs @@ -0,0 +1,19 @@ + + {{#if this.updating}} + {{loading-spinner size="small"}} + {{/if}} + + + {{d-icon "pavilion-logo"}} + {{this.label}} + \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js b/assets/javascripts/discourse/components/wizard-subscription-badge.js new file mode 100644 index 00000000..be14b6f8 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js @@ -0,0 +1,46 @@ +import { inject as service } from "@ember/service"; +import { action, computed } from "@ember/object"; +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import I18n from "I18n"; + +export default class WizardSubscriptionBadge extends Component { + @service subscription; + @tracked updating = false; + @tracked updateIcon = "sync"; + basePath = "/admin/plugins/subscription-client"; + + @computed("subscription.subscriptionType") + get i18nKey() { + return `admin.wizard.subscription.type.${ + this.subscription.subscriptionType + ? this.subscription.subscriptionType + : "none" + }`; + } + + @computed("i18nKey") + get title() { + return `${this.i18nKey}.title`; + } + + @computed("i18nKey") + get label() { + return I18n.t(`${this.i18nKey}.label`); + } + + @action + click() { + window.open(this.subscription.subscriptionCtaLink, "_blank").focus(); + } + + @action + update() { + this.updating = true; + this.updateIcon = null; + this.subscription.updateSubscriptionStatus().finally(() => { + this.updateIcon = "sync"; + this.updating = false; + }); + } +} diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.hbs b/assets/javascripts/discourse/components/wizard-subscription-container.hbs new file mode 100644 index 00000000..a59263ed --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-container.hbs @@ -0,0 +1,17 @@ +
+
+

{{i18n "admin.wizard.subscription.title"}}

+ + + {{d-icon subscribedIcon}} + {{i18n subscribedLabel}} + +
+ +
+ {{yield}} +
+
\ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js b/assets/javascripts/discourse/components/wizard-subscription-container.js new file mode 100644 index 00000000..060d7af6 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js @@ -0,0 +1,26 @@ +import Component from "@glimmer/component"; +import { computed } from "@ember/object"; +import { inject as service } from "@ember/service"; + +export default class WizardSubscriptionContainer extends Component { + @service subscription; + + @computed("subscription.subscribed") + get subscribedIcon() { + return this.subscription.subscribed ? "check" : "times"; + } + + @computed("subscription.subscribed") + get subscribedLabel() { + return `admin.wizard.subscription.${ + this.subscription.subscribed ? "subscribed" : "not_subscribed" + }.label`; + } + + @computed("subscription.subscribed") + get subscribedTitle() { + return `admin.wizard.subscription.${ + this.subscription.subscribed ? "subscribed" : "not_subscribed" + }.title`; + } +} 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..b7203c30 --- /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 { inject as service } from "@ember/service"; +import { filterValues } 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({ + classNames: ["combo-box", "wizard-subscription-selector"], + subscription: service(), + + 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.subscription.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", "wizard.allowGuests") + content(feature, attribute) { + return filterValues(this.wizard, 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.subscription.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-subscription-status.hbs b/assets/javascripts/discourse/components/wizard-subscription-status.hbs new file mode 100644 index 00000000..5309558c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-status.hbs @@ -0,0 +1,22 @@ +
+ + {{#if authorized}} + {{conditional-loading-spinner size="small" condition=unauthorizing}} + + {{i18n "admin.wizard.subscription.deauthorize.label"}} + + {{else}} + + {{/if}} +
\ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-subscription-status.js b/assets/javascripts/discourse/components/wizard-subscription-status.js new file mode 100644 index 00000000..e8efc49c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-status.js @@ -0,0 +1,53 @@ +import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; + +export default class WizardSubscriptionStatus extends Component { + @service siteSettings; + @service subscription; + @tracked supplierId = null; + @tracked authorized = false; + @tracked unauthorizing = false; + basePath = "/admin/plugins/subscription-client/suppliers"; + + constructor() { + super(...arguments); + ajax(`${this.basePath}`) + .then((result) => { + this.supplierId = result.suppliers[0].id; + this.authorized = result.suppliers[0].authorized; + }) + .finally(() => { + this.subscription.retrieveSubscriptionStatus(); + }); + } + + @action + authorize() { + window.location.href = `${this.basePath}/authorize?supplier_id=${this.supplierId}&final_landing_path=/admin/wizards/wizard`; + } + + @action + deauthorize() { + this.unauthorizing = true; + + ajax(`${this.basePath}/authorize`, { + type: "DELETE", + data: { + supplier_id: this.supplierId, + }, + }) + .then((result) => { + this.supplierId = result.supplier.id; + this.authorized = !(result.supplier.authorized_at === null); + }) + .finally(() => { + this.unauthorizing = false; + this.subscription.retrieveSubscriptionStatus(); + }) + .catch(popupAjaxError); + } +} 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..0aef2dcb 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}} + + {{#if wizardErrorNotice}} + {{d-icon "exclaimation-circle"}} + {{/if}} +{{/if}} \ No newline at end of file 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..2c6a6975 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,10 @@
{{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") + }}
- + \ No newline at end of file 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/connectors/top-notices/prompt-completion.hbs b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs index 70c0b7c4..10057345 100644 --- a/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs +++ b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs @@ -1,7 +1,10 @@ {{#each site.complete_custom_wizard as |wizard|}}
- {{i18n "wizard.complete_custom" name=wizard.name}} + {{i18n + "wizard.complete_custom" + name=wizard.name + }}
-{{/each}} +{{/each}} \ No newline at end of file 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..31b91e16 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 @@ -4,14 +4,18 @@ import CustomWizardApi from "../models/custom-wizard-api"; import { default as discourseComputed } from "discourse-common/utils/decorators"; import { and, equal, not } from "@ember/object/computed"; import { selectKitContent } from "../lib/wizard"; +import { underscore } from "@ember/string"; import Controller from "@ember/controller"; import I18n from "I18n"; +import { inject as service } from "@ember/service"; export default Controller.extend({ + router: service(), + 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, @@ -20,29 +24,8 @@ export default Controller.extend({ "application/x-www-form-urlencoded", ]), successCodes: selectKitContent([ - 100, - 101, - 102, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 226, - 300, - 301, - 302, - 303, - 303, - 304, - 305, - 306, - 307, - 308, + 100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, + 302, 303, 303, 304, 305, 306, 307, 308, ]), @discourseComputed( @@ -88,6 +71,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({}); @@ -113,7 +101,7 @@ export default Controller.extend({ if (authType === "oauth_2") { this.set("authorizing", true); - ajax(`/admin/wizards/apis/${name.underscore()}/authorize`) + ajax(`/admin/wizards/apis/${underscore(name)}/authorize`) .catch(popupAjaxError) .then((result) => { if (result.success) { @@ -149,7 +137,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 +151,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; } @@ -188,11 +170,11 @@ export default Controller.extend({ if (!api[rp]) { let key = rp.replace("auth", ""); error = `${I18n.t( - `admin.wizard.api.auth.${key.underscore()}` + `admin.wizard.api.auth.${underscore(key)}` )} is required for ${authType}`; break; } - data[rp.underscore()] = api[rp]; + data[underscore(rp)] = api[rp]; } } @@ -222,7 +204,7 @@ export default Controller.extend({ this.set("updating", true); - ajax(`/admin/wizards/api/${name.underscore()}`, { + ajax(`/admin/wizards/api/${underscore(name)}`, { type: "PUT", data, }) @@ -245,7 +227,7 @@ export default Controller.extend({ this.set("updating", true); - ajax(`/admin/wizards/api/${name.underscore()}`, { + ajax(`/admin/wizards/api/${underscore(name)}`, { type: "DELETE", }) .catch(popupAjaxError) @@ -263,13 +245,13 @@ export default Controller.extend({ return; } - ajax(`/admin/wizards/api/${name.underscore()}/logs`, { + ajax(`/admin/wizards/api/${underscore(name)}/logs`, { type: "DELETE", }) .catch(popupAjaxError) .then((result) => { if (result.success) { - this.transitionToRoute("adminWizardsApis").then(() => { + this.router.transitionTo("adminWizardsApis").then(() => { this.send("refreshModel"); }); } 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..11e66782 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,8 @@ 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://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/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..0d321948 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 @@ -1,50 +1,35 @@ -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://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/", - 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..65b8987a 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-manager.js.es6 @@ -7,7 +7,8 @@ import I18n from "I18n"; import { underscore } from "@ember/string"; export default Controller.extend({ - messageUrl: "https://thepavilion.io/t/3652", + messageUrl: + "https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/wizard-manager", messageKey: "info", messageIcon: "info-circle", messageClass: "info", @@ -68,7 +69,7 @@ export default Controller.extend({ file: null, filename: null, }); - $("#file-upload").val(""); + document.getElementById("custom-wizard-file-upload").value = ""; }, @observes("importing", "destroying") @@ -83,7 +84,7 @@ export default Controller.extend({ actions: { upload() { - $("#file-upload").click(); + document.getElementById("custom-wizard-file-upload").click(); }, clearFile() { @@ -102,7 +103,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..dfcf3b7e 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,74 @@ 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 { inject as service } from "@ember/service"; +import AdminWizardsColumnsModal from "../components/modal/admin-wizards-columns"; +import CustomWizardAdmin from "../models/custom-wizard-admin"; +import { formatModel } from "../lib/wizard-submission"; export default Controller.extend({ + modal: service(), 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 this.modal.show(AdminWizardsColumnsModal, { + 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..0d321948 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 @@ -0,0 +1,35 @@ +import Controller from "@ember/controller"; +import { default as discourseComputed } from "discourse-common/utils/decorators"; + +export default Controller.extend({ + documentationUrl: + "https://pavilion.tech/products/discourse-custom-wizard-plugin/documentation/", + + @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..7ae48709 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -3,15 +3,18 @@ import { observes, } from "discourse-common/utils/decorators"; import { notEmpty } from "@ember/object/computed"; -import showModal from "discourse/lib/show-modal"; +import { inject as service } from "@ember/service"; +import NextSessionScheduledModal from "../components/modal/next-session-scheduled"; import { generateId, wizardFieldList } from "../lib/wizard"; import { dasherize } from "@ember/string"; import { later, scheduleOnce } from "@ember/runloop"; import Controller from "@ember/controller"; import copyText from "discourse/lib/copy-text"; import I18n from "I18n"; +import { filterValues } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; export default Controller.extend({ + modal: service(), hasName: notEmpty("wizard.name"), @observes("currentStep") @@ -36,7 +39,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") @@ -58,6 +62,19 @@ export default Controller.extend({ } return wizardFieldList(steps); }, + + @discourseComputed("fieldTypes", "wizard.allowGuests") + filteredFieldTypes(fieldTypes) { + const fieldTypeIds = fieldTypes.map((f) => f.id); + const allowedTypeIds = filterValues( + this.wizard, + "field", + "type", + fieldTypeIds + ); + return fieldTypes.filter((f) => allowedTypeIds.includes(f.id)); + }, + getErrorMessage(result) { if (result.backend_validation_error) { return result.backend_validation_error; @@ -92,7 +109,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)); @@ -107,19 +128,13 @@ export default Controller.extend({ }, setNextSessionScheduled() { - let controller = showModal("next-session-scheduled", { + this.modal.show(NextSessionScheduledModal, { model: { dateTime: this.wizard.after_time_scheduled, update: (dateTime) => this.set("wizard.after_time_scheduled", dateTime), }, }); - - controller.setup(); - }, - - toggleAdvanced() { - this.toggleProperty("wizard.showAdvanced"); }, copyUrl() { diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6 index ddd63337..994dd1a6 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://pavilion.tech/products/discourse-custom-wizard-plugin/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..ded14b91 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -0,0 +1,12 @@ +import Controller from "@ember/controller"; +import { or } from "@ember/object/computed"; +import { inject as service } from "@ember/service"; + +export default Controller.extend({ + subscription: service(), + + showApi: or( + "subscription.businessSubscription", + "subscription.communitySubscription" + ), +}); diff --git a/assets/javascripts/wizard/controllers/wizard-index.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard-index.js.es6 similarity index 100% rename from assets/javascripts/wizard/controllers/wizard-index.js.es6 rename to assets/javascripts/discourse/controllers/custom-wizard-index.js.es6 diff --git a/assets/javascripts/wizard/controllers/step.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard-step.js.es6 similarity index 79% rename from assets/javascripts/wizard/controllers/step.js.es6 rename to assets/javascripts/discourse/controllers/custom-wizard-step.js.es6 index 4b321173..d1a299fd 100644 --- a/assets/javascripts/wizard/controllers/step.js.es6 +++ b/assets/javascripts/discourse/controllers/custom-wizard-step.js.es6 @@ -1,7 +1,9 @@ import Controller from "@ember/controller"; import getUrl from "discourse-common/lib/get-url"; +import { inject as service } from "@ember/service"; export default Controller.extend({ + router: service(), wizard: null, step: null, @@ -15,12 +17,12 @@ export default Controller.extend({ const wizardId = this.get("wizard.id"); window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`); } else { - this.transitionToRoute("step", nextStepId); + this.router.transitionTo("customWizardStep", nextStepId); } }, goBack() { - this.transitionToRoute("step", this.get("step.previous")); + this.router.transitionTo("customWizardStep", this.get("step.previous")); }, showMessage(message) { diff --git a/assets/javascripts/wizard/controllers/wizard.js.es6 b/assets/javascripts/discourse/controllers/custom-wizard.js.es6 similarity index 100% rename from assets/javascripts/wizard/controllers/wizard.js.es6 rename to assets/javascripts/discourse/controllers/custom-wizard.js.es6 diff --git a/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 b/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 deleted file mode 100644 index 4ea22d38..00000000 --- a/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 +++ /dev/null @@ -1,27 +0,0 @@ -import { default as discourseComputed } from "discourse-common/utils/decorators"; -import Controller from "@ember/controller"; - -export default Controller.extend({ - title: "admin.wizard.after_time_modal.title", - - setup() { - this.set("bufferedDateTime", moment(this.model.dateTime)); - }, - - @discourseComputed("bufferedDateTime") - submitDisabled(dateTime) { - return moment().isAfter(dateTime); - }, - - actions: { - submit() { - const dateTime = this.get("bufferedDateTime"); - this.get("model.update")(moment(dateTime).utc().toISOString()); - this.send("closeModal"); - }, - - dateTimeChanged(dateTime) { - this.set("bufferedDateTime", dateTime); - }, - }, -}); 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/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/wizard/helpers/char-counter.js.es6 b/assets/javascripts/discourse/helpers/wizard-char-counter.js similarity index 81% rename from assets/javascripts/wizard/helpers/char-counter.js.es6 rename to assets/javascripts/discourse/helpers/wizard-char-counter.js index a700a432..97f7e98b 100644 --- a/assets/javascripts/wizard/helpers/char-counter.js.es6 +++ b/assets/javascripts/discourse/helpers/wizard-char-counter.js @@ -1,8 +1,7 @@ -import { registerUnbound } from "discourse-common/lib/helpers"; import I18n from "I18n"; import Handlebars from "handlebars"; -export default registerUnbound("char-counter", function (body, maxLength) { +export default function wizardCharCounter(body, maxLength) { let bodyLength = body ? body.length : 0; let finalString; @@ -19,4 +18,4 @@ export default registerUnbound("char-counter", function (body, maxLength) { } return new Handlebars.SafeString(finalString); -}); +} diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index cecf2a03..2fef6452 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -6,7 +6,7 @@ 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; @@ -20,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: { @@ -38,6 +38,7 @@ export default { }); api.modifyClass("component:uppy-image-uploader", { + pluginId: "custom-wizard", // Needed to ensure appEvents get registered when navigating between steps @observes("id") initOnStepChange() { @@ -46,6 +47,57 @@ export default { } }, }); + + 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.element + ) { + this.insertText(text, options); + } + }, + + _wizardReplaceText(oldVal, newVal, opts = {}) { + if (this.session.wizardEventFieldId === this.fieldId) { + this.replaceText(oldVal, newVal, opts); + } + }, + }); + + api.modifyClass("component:category-chooser", { + pluginId: "custom-wizard", + + categoriesByScope(options = {}) { + let categories = this._super(options); + const currentUser = this.currentUser; + if (!currentUser?.staff) { + categories = categories.filter((category) => { + return !category.custom_fields?.create_topic_wizard; + }); + } + return categories; + }, + }); }); }, }; diff --git a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 index e413cd17..c02f0f3d 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 @@ -1,14 +1,16 @@ -import ApplicationRoute from "discourse/routes/application"; +import DiscourseURL from "discourse/lib/url"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import { dasherize } from "@ember/string"; export default { name: "custom-wizard-redirect", after: "message-bus", - initialize: function (container) { - const messageBus = container.lookup("message-bus:main"); - const siteSettings = container.lookup("site-settings:main"); + initialize(container) { + const messageBus = container.lookup("service:message-bus"); + const siteSettings = container.lookup("service:site-settings"); - if (!siteSettings.custom_wizard_enabled || !messageBus) { + if (!siteSettings.custom_wizard_enabled) { return; } @@ -17,28 +19,27 @@ export default { window.location.href = wizardUrl; }); - ApplicationRoute.reopen({ - actions: { - willTransition(transition) { - const redirectToWizard = this.get("currentUser.redirect_to_wizard"); - const excludedPaths = this.siteSettings.wizard_redirect_exclude_paths + withPluginApi("0.8.36", (api) => { + api.onAppEvent("page:changed", (data) => { + const currentUser = api.getCurrentUser(); + + if (currentUser) { + const redirectToWizard = currentUser.redirect_to_wizard; + const excludedPaths = siteSettings.wizard_redirect_exclude_paths .split("|") .concat(["loading"]); - if ( redirectToWizard && - (!transition.intent.name || - !excludedPaths.find((p) => { - return transition.intent.name.indexOf(p) > -1; - })) + !data.url.includes("ignore_redirect") && + data.currentRouteName !== "customWizardStep" && + !excludedPaths.find((p) => { + return data.currentRouteName.indexOf(p) > -1; + }) ) { - transition.abort(); - window.location = "/w/" + redirectToWizard.dasherize(); + DiscourseURL.routeTo(`/w/${dasherize(redirectToWizard)}`); } - - return this._super(transition); - }, - }, + } + }); }); }, }; 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-mapper.js.es6 b/assets/javascripts/discourse/lib/wizard-mapper.js.es6 index c398eaf4..9037bec5 100644 --- a/assets/javascripts/discourse/lib/wizard-mapper.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-mapper.js.es6 @@ -35,6 +35,7 @@ function inputTypesContent(options = {}) { const connectors = { pair: [ "equal", + "not_equal", "greater", "less", "greater_or_equal", diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 5445223a..959185da 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -1,5 +1,5 @@ import { get, set } from "@ember/object"; -import { getOwner } from "discourse-common/lib/get-owner"; +import { getOwnerWithFallback } from "discourse-common/lib/get-owner"; const wizard = { basic: { @@ -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,16 @@ const field = { index: null, label: null, image: null, + image_upload_id: null, description: null, + property: null, required: null, - key: null, type: null, condition: null, + tag_groups: null, }, types: {}, mapped: ["prefill", "content", "condition", "index"], - advanced: ["property", "key", "condition", "index"], required: ["id", "type"], dependent: {}, objectArrays: {}, @@ -100,6 +99,8 @@ const action = { custom_fields: null, skip_redirect: null, suppress_notifications: null, + add_event: null, + add_location: null, }, send_message: { title: null, @@ -132,6 +133,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,35 +203,83 @@ 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 wizardSchema = { - wizard, - step, - field, - action, +const filters = { + allow_guests: { + field: { + type: [ + "text", + "textarea", + "text_only", + "date", + "time", + "date_time", + "number", + "checkbox", + "url", + "dropdown", + "tag", + "category", + "group", + "user_selector", + ], + }, + action: { + type: ["route_to", "send_message"], + }, + }, +}; + +const custom_field = { + klass: ["topic", "post", "group", "category"], + type: ["string", "boolean", "integer", "json"], }; export function buildFieldTypes(types) { wizardSchema.field.types = types; + wizardSchema.field.type = Object.keys(types); } +field.type = Object.keys(field.types); +action.type = Object.keys(action.types); + +const wizardSchema = { + wizard, + step, + field, + custom_field, + action, + filters, +}; + export function buildFieldValidations(validations) { wizardSchema.field.validations = validations; } -const siteSettings = getOwner(this).lookup("site-settings:main"); +export function filterValues(currentWizard, feature, attribute, values = null) { + values = values || wizardSchema[feature][attribute]; + + if (currentWizard && currentWizard.allowGuests) { + const filteredFeature = wizardSchema.filters.allow_guests[feature]; + if (filteredFeature) { + const filtered = filteredFeature[attribute]; + if (filtered) { + values = values.filter((v) => filtered.includes(v)); + } + } + } + + return values; +} + +const siteSettings = getOwnerWithFallback(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..9ea9382d --- /dev/null +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -0,0 +1,53 @@ +import Mixin from "@ember/object/mixin"; +import { getOwner } from "@ember/application"; +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://pavilion.tech/products/discourse-custom-wizard-plugin/support"; + +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/undo-changes.js.es6 b/assets/javascripts/discourse/mixins/undo-changes.js.es6 index b2ab322d..e98cfb0e 100644 --- a/assets/javascripts/discourse/mixins/undo-changes.js.es6 +++ b/assets/javascripts/discourse/mixins/undo-changes.js.es6 @@ -4,6 +4,8 @@ import { get, set } from "@ember/object"; import Mixin from "@ember/object/mixin"; import { deepEqual } from "discourse-common/lib/object"; +const observedCache = []; + export default Mixin.create({ didInsertElement() { this._super(...arguments); @@ -32,7 +34,13 @@ export default Mixin.create({ }; listProperties(componentType, opts).forEach((property) => { - obj.removeObserver(property, this, this.toggleUndo); + if (observedCache.includes(property)) { + obj.removeObserver(property, this, this.toggleUndo); + let index = observedCache.indexOf(property); + if (index !== -1) { + observedCache.splice(index, 1); + } + } }); }, @@ -45,6 +53,9 @@ export default Mixin.create({ }; listProperties(componentType, opts).forEach((property) => { + if (observedCache.indexOf(property) === -1) { + observedCache.push(property); + } obj.addObserver(property, this, this.toggleUndo); }); }, 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..afca4833 --- /dev/null +++ b/assets/javascripts/discourse/models/custom-wizard-admin.js.es6 @@ -0,0 +1,242 @@ +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"; + +const GUEST_GROUP_ID = -1; + +const CustomWizardAdmin = EmberObject.extend({ + @discourseComputed("permitted.@each.output") + allowGuests(permitted) { + return ( + permitted && + permitted.filter((p) => p.output && p.output.includes(GUEST_GROUP_ID)) + .length + ); + }, + + 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/wizard/models/field.js.es6 b/assets/javascripts/discourse/models/custom-wizard-field.js.es6 similarity index 77% rename from assets/javascripts/wizard/models/field.js.es6 rename to assets/javascripts/discourse/models/custom-wizard-field.js.es6 index 2b88140e..2afe79d9 100644 --- a/assets/javascripts/wizard/models/field.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-field.js.es6 @@ -1,7 +1,7 @@ import EmberObject from "@ember/object"; -import ValidState from "wizard/mixins/valid-state"; +import ValidState from "discourse/plugins/discourse-custom-wizard/discourse/mixins/valid-state"; import discourseComputed from "discourse-common/utils/decorators"; -import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; +import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard"; const StandardFieldValidation = [ "text", @@ -34,17 +34,17 @@ export default EmberObject.extend(ValidState, { @discourseComputed("i18nKey", "label") translatedLabel(i18nKey, label) { - return translatedText(`${i18nKey}.label`, label); + return translationOrText(`${i18nKey}.label`, label); }, @discourseComputed("i18nKey", "placeholder") translatedPlaceholder(i18nKey, placeholder) { - return translatedText(`${i18nKey}.placeholder`, placeholder); + return translationOrText(`${i18nKey}.placeholder`, placeholder); }, @discourseComputed("i18nKey", "description") translatedDescription(i18nKey, description) { - return translatedText(`${i18nKey}.description`, description); + return translationOrText(`${i18nKey}.description`, description); }, check() { @@ -72,7 +72,7 @@ export default EmberObject.extend(ValidState, { valid = true; } - this.setValid(valid); + this.setValid(Boolean(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/wizard/models/step.js.es6 b/assets/javascripts/discourse/models/custom-wizard-step.js.es6 similarity index 86% rename from assets/javascripts/wizard/models/step.js.es6 rename to assets/javascripts/discourse/models/custom-wizard-step.js.es6 index 36503276..f7cdc497 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-step.js.es6 @@ -1,9 +1,9 @@ import EmberObject from "@ember/object"; -import ValidState from "wizard/mixins/valid-state"; -import { ajax } from "wizard/lib/ajax"; +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 { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; import { later } from "@ember/runloop"; +import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard"; export default EmberObject.extend(ValidState, { id: null, @@ -15,12 +15,12 @@ export default EmberObject.extend(ValidState, { @discourseComputed("i18nKey", "title") translatedTitle(i18nKey, title) { - return translatedText(`${i18nKey}.title`, title); + return translationOrText(`${i18nKey}.title`, title); }, @discourseComputed("i18nKey", "description") translatedDescription(i18nKey, description) { - return translatedText(`${i18nKey}.description`, description); + return translationOrText(`${i18nKey}.description`, description); }, @discourseComputed("index") @@ -72,6 +72,9 @@ export default EmberObject.extend(ValidState, { 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) => { 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-api-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6 index 6c0ed7a8..a431e9ae 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6 @@ -1,7 +1,10 @@ import CustomWizardApi from "../models/custom-wizard-api"; import DiscourseRoute from "discourse/routes/discourse"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model(params) { if (params.name === "create") { return CustomWizardApi.create({ isNew: true }); @@ -10,6 +13,12 @@ export default DiscourseRoute.extend({ } }, + afterModel(model) { + if (model === null) { + return this.router.transitionTo("adminWizardsApi"); + } + }, + setupController(controller, model) { controller.set("api", model); }, diff --git a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 index 541ab028..2b108460 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 @@ -1,7 +1,10 @@ import DiscourseRoute from "discourse/routes/discourse"; import CustomWizardApi from "../models/custom-wizard-api"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model() { return CustomWizardApi.list(); }, @@ -25,11 +28,11 @@ export default DiscourseRoute.extend({ actions: { changeApi(apiName) { this.controllerFor("adminWizardsApi").set("apiName", apiName); - this.transitionTo("adminWizardsApiShow", apiName); + this.router.transitionTo("adminWizardsApiShow", apiName); }, afterDestroy() { - this.transitionTo("adminWizardsApi").then(() => this.refresh()); + this.router.transitionTo("adminWizardsApi").then(() => this.refresh()); }, afterSave(apiName) { @@ -38,7 +41,7 @@ export default DiscourseRoute.extend({ createApi() { this.controllerFor("adminWizardsApi").set("apiName", "create"); - this.transitionTo("adminWizardsApiShow", "create"); + this.router.transitionTo("adminWizardsApiShow", "create"); }, }, }); 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..9c096a1e --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 @@ -0,0 +1,26 @@ +import CustomWizardLogs from "../models/custom-wizard-logs"; +import DiscourseRoute from "discourse/routes/discourse"; +import { A } from "@ember/array"; +import { inject as service } from "@ember/service"; + +export default DiscourseRoute.extend({ + router: service(), + + model(params) { + return CustomWizardLogs.list(params.wizardId); + }, + + afterModel(model) { + if (model === null) { + return this.router.transitionTo("adminWizardsLogs"); + } + }, + + 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..6bb5864b 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 @@ -1,12 +1,27 @@ -import CustomWizardLogs from "../models/custom-wizard-logs"; import DiscourseRoute from "discourse/routes/discourse"; +import { ajax } from "discourse/lib/ajax"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + 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.router.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..e9bad625 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,30 @@ -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"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model(params) { - return CustomWizard.submissions(params.wizardId); + return CustomWizardAdmin.submissions(params.wizardId); + }, + + afterModel(model) { + if (model === null) { + return this.router.transitionTo("adminWizardsSubmissions"); + } }, 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-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 index 9ecb183d..dc0ef5e0 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 @@ -1,7 +1,10 @@ import DiscourseRoute from "discourse/routes/discourse"; import { ajax } from "discourse/lib/ajax"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model() { return ajax(`/admin/wizards/wizard`); }, @@ -18,7 +21,7 @@ export default DiscourseRoute.extend({ actions: { changeWizard(wizardId) { this.controllerFor("adminWizardsSubmissions").set("wizardId", wizardId); - this.transitionTo("adminWizardsSubmissionsShow", wizardId); + this.router.transitionTo("adminWizardsSubmissionsShow", wizardId); }, }, }); 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..2ed2627f 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -1,9 +1,12 @@ -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"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model(params) { if (params.wizardId === "create") { return { create: true }; @@ -14,13 +17,15 @@ export default DiscourseRoute.extend({ afterModel(model) { if (model.none) { - return this.transitionTo("adminWizardsWizard"); + return this.router.transitionTo("adminWizardsWizard"); } }, 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-wizard.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 index b23b63f6..6ae31e82 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 @@ -4,8 +4,11 @@ import EmberObject, { set } from "@ember/object"; import { A } from "@ember/array"; import { all } from "rsvp"; import { ajax } from "discourse/lib/ajax"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + router: service(), + model() { return ajax("/admin/wizards/wizard"); }, @@ -80,14 +83,14 @@ export default DiscourseRoute.extend({ this.controllerFor("adminWizardsWizard").set("wizardId", wizardId); if (wizardId) { - this.transitionTo("adminWizardsWizardShow", wizardId); + this.router.transitionTo("adminWizardsWizardShow", wizardId); } else { - this.transitionTo("adminWizardsWizard"); + this.router.transitionTo("adminWizardsWizard"); } }, afterDestroy() { - this.transitionTo("adminWizardsWizard").then(() => this.refresh()); + this.router.transitionTo("adminWizardsWizard").then(() => this.refresh()); }, afterSave(wizardId) { @@ -96,7 +99,7 @@ export default DiscourseRoute.extend({ createWizard() { this.controllerFor("adminWizardsWizard").set("wizardId", "create"); - this.transitionTo("adminWizardsWizardShow", "create"); + this.router.transitionTo("adminWizardsWizardShow", "create"); }, }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 5de271a8..a16df9ae 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -1,9 +1,12 @@ import DiscourseRoute from "discourse/routes/discourse"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ - beforeModel(transition) { + router: service(), + + afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { - this.transitionTo("adminWizardsWizard"); + this.router.transitionTo("adminWizardsWizard"); } }, }); diff --git a/assets/javascripts/wizard/routes/wizard-index.js.es6 b/assets/javascripts/discourse/routes/custom-wizard-index.js.es6 similarity index 69% rename from assets/javascripts/wizard/routes/wizard-index.js.es6 rename to assets/javascripts/discourse/routes/custom-wizard-index.js.es6 index 264cb0a2..78495607 100644 --- a/assets/javascripts/wizard/routes/wizard-index.js.es6 +++ b/assets/javascripts/discourse/routes/custom-wizard-index.js.es6 @@ -1,17 +1,14 @@ -import { getCachedWizard } from "../models/wizard"; +import { getCachedWizard } from "../models/custom-wizard"; import Route from "@ember/routing/route"; +import { inject as service } from "@ember/service"; export default Route.extend({ + router: service(), + beforeModel() { const wizard = getCachedWizard(); - if ( - wizard && - wizard.user && - wizard.permitted && - !wizard.completed && - wizard.start - ) { - this.replaceWith("step", wizard.start); + if (wizard && wizard.permitted && !wizard.completed && wizard.start) { + this.router.replaceWith("customWizardStep", wizard.start); } }, @@ -19,10 +16,6 @@ export default Route.extend({ return getCachedWizard(); }, - renderTemplate() { - this.render("wizard/templates/wizard-index"); - }, - setupController(controller, model) { if (model && model.id) { const completed = model.get("completed"); @@ -30,7 +23,7 @@ export default Route.extend({ const wizardId = model.get("id"); const user = model.get("user"); const name = model.get("name"); - const requiresLogin = !user; + const requiresLogin = !user && !permitted; const notPermitted = !permitted; const props = { diff --git a/assets/javascripts/wizard/routes/step.js.es6 b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6 similarity index 56% rename from assets/javascripts/wizard/routes/step.js.es6 rename to assets/javascripts/discourse/routes/custom-wizard-step.js.es6 index a076951f..963307e7 100644 --- a/assets/javascripts/wizard/routes/step.js.es6 +++ b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6 @@ -1,10 +1,20 @@ -import WizardI18n from "../lib/wizard-i18n"; -import { getCachedWizard } from "../models/wizard"; +import I18n from "I18n"; +import { getCachedWizard } from "../models/custom-wizard"; import Route from "@ember/routing/route"; +import { scrollTop } from "discourse/mixins/scroll-top"; +import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; export default Route.extend({ + router: service(), + beforeModel() { - this.set("wizard", getCachedWizard()); + const wizard = getCachedWizard(); + this.set("wizard", wizard); + + if (!wizard || !wizard.permitted || wizard.completed) { + this.router.replaceWith("customWizard"); + } }, model(params) { @@ -20,15 +30,11 @@ export default Route.extend({ afterModel(model) { if (model.completed) { - return this.transitionTo("wizard.index"); + return this.router.transitionTo("wizard.index"); } return model.set("wizardId", this.wizard.id); }, - renderTemplate() { - this.render("wizard/templates/step"); - }, - setupController(controller, model) { let props = { step: model, @@ -38,8 +44,7 @@ export default 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; @@ -48,4 +53,10 @@ export default Route.extend({ controller.setProperties(props); }, + + @action + didTransition() { + scrollTop(); + return true; + }, }); diff --git a/assets/javascripts/wizard/routes/wizard.js.es6 b/assets/javascripts/discourse/routes/custom-wizard.js.es6 similarity index 52% rename from assets/javascripts/wizard/routes/wizard.js.es6 rename to assets/javascripts/discourse/routes/custom-wizard.js.es6 index a2c34f13..1a214a2d 100644 --- a/assets/javascripts/wizard/routes/wizard.js.es6 +++ b/assets/javascripts/discourse/routes/custom-wizard.js.es6 @@ -1,10 +1,14 @@ -import { findCustomWizard, updateCachedWizard } from "../models/wizard"; -import WizardI18n from "../lib/wizard-i18n"; -import Route from "@ember/routing/route"; -import { scheduleOnce } from "@ember/runloop"; -import { getOwner } from "discourse-common/lib/get-owner"; +import { findCustomWizard, updateCachedWizard } from "../models/custom-wizard"; +import I18n from "I18n"; +import DiscourseRoute from "discourse/routes/discourse"; +import bootbox from "bootbox"; + +export default DiscourseRoute.extend({ + titleToken() { + const wizard = this.modelFor("custom-wizard"); + return wizard ? wizard.name || wizard.id : I18n.t("wizard.custom_title"); + }, -export default Route.extend({ beforeModel(transition) { if (transition.intent.queryParams) { this.set("queryParams", transition.intent.queryParams); @@ -16,7 +20,7 @@ export default Route.extend({ }, showDialog(wizardModel) { - const title = WizardI18n("wizard.incomplete_submission.title", { + const title = I18n.t("wizard.incomplete_submission.title", { date: moment(wizardModel.submission_last_updated_at).format( "MMMM Do YYYY" ), @@ -24,14 +28,14 @@ export default Route.extend({ const buttons = [ { - label: WizardI18n("wizard.incomplete_submission.restart"), + label: I18n.t("wizard.incomplete_submission.restart"), class: "btn btn-default", callback: () => { wizardModel.restart(); }, }, { - label: WizardI18n("wizard.incomplete_submission.resume"), + label: I18n.t("wizard.incomplete_submission.resume"), class: "btn btn-primary", }, ]; @@ -47,28 +51,15 @@ export default Route.extend({ updateCachedWizard(model); }, - renderTemplate() { - this.render("wizard/templates/wizard"); - }, - setupController(controller, model) { - const background = model ? model.get("background") : ""; - - scheduleOnce("afterRender", this, function () { - $("body").css("background", background); - - if (model && model.id) { - $(getOwner(this).rootElement).addClass(model.id.dasherize()); - } - }); - controller.setProperties({ customWizard: true, logoUrl: this.siteSettings.logo_small, reset: null, + model, }); - const stepModel = this.modelFor("step"); + const stepModel = this.modelFor("custom-wizard-step"); if ( model.resume_on_revisit && model.submission_last_updated_at && @@ -76,5 +67,24 @@ export default Route.extend({ ) { 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/services/subscription.js b/assets/javascripts/discourse/services/subscription.js new file mode 100644 index 00000000..499e734c --- /dev/null +++ b/assets/javascripts/discourse/services/subscription.js @@ -0,0 +1,65 @@ +import Service from "@ember/service"; +import { tracked } from "@glimmer/tracking"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; + +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://coop.pavilion.tech/c/support/discourse-custom-wizard"; + +export default class SubscriptionService extends Service { + @tracked subscribed = false; + @tracked subscriptionType = ""; + @tracked businessSubscription = false; + @tracked communitySubscription = false; + @tracked standardSubscription = false; + @tracked subscriptionAttributes = {}; + + async init() { + super.init(...arguments); + await this.retrieveSubscriptionStatus(); + } + + async retrieveSubscriptionStatus() { + let result = await ajax("/admin/wizards/subscription").catch( + popupAjaxError + ); + + this.subscribed = result.subscribed; + this.subscriptionType = result.subscription_type; + this.subscriptionAttributes = result.subscription_attributes; + this.businessSubscription = this.subscriptionType === "business"; + this.communitySubscription = this.subscriptionType === "community"; + this.standardSubscription = this.subscriptionType === "standard"; + } + + async updateSubscriptionStatus() { + let result = await ajax( + "/admin/wizards/subscription?update_from_remote=true" + ).catch(popupAjaxError); + + this.subscribed = result.subscribed; + this.subscriptionType = result.subscription_type; + this.subscriptionAttributes = result.subscription_attributes; + this.businessSubscription = this.subscriptionType === "business"; + this.communitySubscription = this.subscriptionType === "community"; + this.standardSubscription = this.subscriptionType === "standard"; + } + + get subscriptionCtaLink() { + switch (this.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/templates/admin-wizards-api-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs index 4d3def3d..5270aa28 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs @@ -8,7 +8,12 @@ {{/if}} {{/if}} - {{d-button label="admin.wizard.api.save" action=(action "save") class="btn-primary" disabled=saveDisabled}} + {{d-button + label="admin.wizard.api.save" + action=(action "save") + class="btn-primary" + disabled=saveDisabled + }} {{#if showRemove}} {{d-button action=(action "remove") label="admin.wizard.api.remove"}} @@ -21,26 +26,32 @@ {{/if}} -
+
{{#if api.isNew}} {{i18n "admin.wizard.api.new"}} {{else}} - {{api.title}} + {{api.title}} {{/if}}
- {{input value=api.title placeholder=(i18n "admin.wizard.api.title_placeholder")}} +
-
+
{{#if api.isNew}} - {{input value=api.name placeholder=(i18n "admin.wizard.api.name_placeholder")}} + {{else}} - {{api.name}} + {{api.name}} {{/if}}
@@ -56,14 +67,16 @@ {{authErrorMessage}} {{/if}} {{/if}} - {{d-button label="admin.wizard.api.auth.btn" - action=(action "authorize") - disabled=authDisabled - class="btn-primary"}} + {{d-button + label="admin.wizard.api.auth.btn" + action=(action "authorize") + disabled=authDisabled + class="btn-primary" + }} {{/if}}
-
+
{{i18n "admin.wizard.api.auth.label"}}
@@ -71,7 +84,7 @@
-
+
{{i18n "admin.wizard.api.auth.settings"}}
@@ -93,9 +106,8 @@ value=api.authType content=authorizationTypes onChange=(action (mut api.authType)) - options=(hash - none="admin.wizard.api.auth.type_none" - )}} + options=(hash none="admin.wizard.api.auth.type_none") + }}
@@ -104,7 +116,7 @@
- {{input value=api.authUrl}} +
{{/if}} @@ -112,21 +124,21 @@
- {{input value=api.tokenUrl}} +
- {{input value=api.clientId}} +
- {{input value=api.clientSecret}} +
@@ -135,12 +147,26 @@
{{#each api.authParams as |param|}}
- {{input value=param.key placeholder=(i18n "admin.wizard.key")}} - {{input value=param.value placeholder=(i18n "admin.wizard.value")}} - {{d-button action=(action "removeParam") actionParam=param icon="times"}} + + + {{d-button + action=(action "removeParam") + actionParam=param + icon="times" + }}
{{/each}} - {{d-button label="admin.wizard.api.auth.params.new" icon="plus" action=(action "addParam")}} + {{d-button + label="admin.wizard.api.auth.params.new" + icon="plus" + action=(action "addParam") + }}
{{/if}} @@ -149,14 +175,14 @@
- {{input value=api.username}} +
- {{input value=api.password}} +
{{/if}} @@ -174,7 +200,7 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.status.label"}}
@@ -220,12 +246,16 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.endpoint.label"}}
- {{d-button action=(action "addEndpoint") label="admin.wizard.api.endpoint.add" icon="plus"}} + {{d-button + action=(action "addEndpoint") + label="admin.wizard.api.endpoint.add" + icon="plus" + }} {{#if api.endpoints}}
@@ -235,38 +265,43 @@
- {{input value=endpoint.name - placeholder=(i18n "admin.wizard.api.endpoint.name")}} - {{input value=endpoint.url - placeholder=(i18n "admin.wizard.api.endpoint.url") - class="endpoint-url"}} - {{d-button action=(action "removeEndpoint") - actionParam=endpoint - icon="times" - class="remove-endpoint"}} + + + {{d-button + action=(action "removeEndpoint") + actionParam=endpoint + icon="times" + class="remove-endpoint" + }}
{{combo-box content=endpointMethods value=endpoint.method onChange=(action (mut endpoint.method)) - options=(hash - none="admin.wizard.api.endpoint.method" - )}} + options=(hash none="admin.wizard.api.endpoint.method") + }} {{combo-box content=contentTypes value=endpoint.content_type onChange=(action (mut endpoint.content_type)) - options=(hash - none="admin.wizard.api.endpoint.content_type" - )}} + options=(hash none="admin.wizard.api.endpoint.content_type") + }} {{multi-select value=endpoint.success_codes content=successCodes onChange=(action (mut endpoint.success_codes)) options=(hash none="admin.wizard.api.endpoint.success_codes" - )}} + ) + }}
@@ -277,11 +312,16 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.log.label"}} - {{d-button action=(action "clearLogs") - icon="trash-alt" - class="clear-logs"}} + +
+ {{d-button + action=(action "clearLogs") + class="clear-logs" + label="admin.wizard.api.log.clear" + }} +
@@ -300,7 +340,10 @@ {{logentry.time}} {{logentry.status}} @@ -311,4 +354,4 @@
-
+
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs index 00d8ad60..6a6c5832 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs @@ -3,16 +3,16 @@ value=apiName content=apiList onChange=(route-action "changeApi") - options=(hash - none="admin.wizard.api.select" - )}} + options=(hash none="admin.wizard.api.select") + }} {{d-button - action="createApi" + action=(route-action "createApi") label="admin.wizard.api.create" - icon="plus"}} + icon="plus" + }}
{{outlet}} -
+ \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index 10501498..4dda0ac8 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -5,7 +5,8 @@ {{d-button label="admin.wizard.custom_field.add" icon="plus" - action="addField"}} + action=(action "addField") + }} @@ -14,7 +15,8 @@ opts=messageOpts type=messageType url=documentationUrl - component="custom_fields"}} + component="custom_fields" +}}
{{#if customFields}} @@ -32,9 +34,10 @@ {{custom-field-input field=field removeField=(action "removeField") - saveField=(action "saveField")}} + saveField=(action "saveField") + }} {{/each}} {{/if}} -
+ \ No newline at end of file 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..deb7ed8b --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs @@ -0,0 +1,49 @@ +{{#if logs}} +
+ + +
+ {{d-button + label="refresh" + icon="sync" + action=(action "refresh") + class="refresh" + }} +
+
+ +
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} + {{#if noResults}} +

{{i18n "search.no_results"}}

+ {{else}} + + + + + + + + + + + {{#each logs as |log|}} + + {{#each-in log as |field value|}} + + {{/each-in}} + + {{/each}} + +
{{i18n "admin.wizard.log.date"}}{{i18n "admin.wizard.log.action"}}{{i18n "admin.wizard.log.user"}}{{i18n "admin.wizard.log.message"}}
{{wizard-table-field + field=field + value=value + }}
+ {{/if}} + + {{conditional-loading-spinner condition=refreshing}} + {{/load-more}} +
+{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs index b0dd3de6..703f4fa3 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}} - - - - - - - - - {{#each logs as |log|}} - - - - - {{/each}} - -
MessageDate
{{log.message}}{{bound-date log.date}}
- {{/if}} +{{wizard-message + key=messageKey + opts=messageOpts + url=documentationUrl + component="logs" +}} - {{conditional-loading-spinner condition=refreshing}} -{{/load-more}} +
+ {{outlet}} +
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-manager.hbs b/assets/javascripts/discourse/templates/admin-wizards-manager.hbs index 9ee2f080..f2de0783 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-manager.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-manager.hbs @@ -11,30 +11,35 @@
{{/if}} - {{input - id="file-upload" - type="file" + {{d-button id="upload-button" label="admin.wizard.manager.upload" - action=(action "upload")}} + action=(action "upload") + }} {{d-button id="import-button" label="admin.wizard.manager.import" action=(action "import") - disabled=importDisabled}} + disabled=importDisabled + }} {{d-button id="export-button" label="admin.wizard.manager.export" action=(action "export") - disabled=exportDisabled}} + disabled=exportDisabled + }} {{d-button id="destroy-button" label="admin.wizard.manager.destroy" action=(action "destroy") - disabled=destoryDisabled}} + disabled=destoryDisabled + }} @@ -45,7 +50,8 @@ opts=messageOpts items=messageItems loading=loading - component="manager"}} + component="manager" +}}
@@ -65,19 +71,21 @@ {{/link-to}} {{/each}}
- {{input - type="checkbox" + - {{input - type="checkbox" +
-
+ \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs index 6d1f255b..96a24a9d 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}}
- + - +
+ {{d-button + icon="sliders-h" + label="admin.wizard.edit_columns" + action=(action "showEditColumnsModal") + class="btn-default open-edit-columns-btn download-link" + }} +
+ +
{{d-icon "download"}} {{i18n "admin.wizard.submissions.download"}} @@ -10,24 +26,36 @@
-
- - - - {{#each fields as |f|}} - - {{/each}} - - - - {{#each submissions as |s|}} - - {{#each-in s as |k v|}} - - {{/each-in}} - - {{/each}} - -
{{f}}
{{v}}
+
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} + {{#if noResults}} +

{{i18n "search.no_results"}}

+ {{else}} + + + + {{#each fields as |field|}} + {{#if field.enabled}} + + {{/if}} + {{/each}} + + + + {{#each displaySubmissions as |submission|}} + + {{#each-in submission as |field value|}} + + {{/each-in}} + + {{/each}} + +
+ {{field.label}} +
{{wizard-table-field field=field value=value}}
+ {{/if}} + + {{conditional-loading-spinner condition=loadingMore}} + {{/load-more}}
-{{/if}} +{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs index d843485a..1ccaa3b8 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs @@ -1,13 +1,19 @@ -
+
{{combo-box value=wizardId content=wizardList onChange=(route-action "changeWizard") - options=(hash - none="admin.wizard.select" - )}} + options=(hash none="admin.wizard.select") + }}
+{{wizard-message + key=messageKey + opts=messageOpts + url=documentationUrl + component="submissions" +}} +
{{outlet}} -
+
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index c5ed70a7..053deb73 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -1,18 +1,31 @@ {{#if wizard}}
- {{input +
{{#if wizard.name}} {{#if copiedUrl}} - {{d-button class="btn-hover pull-right" icon="copy" label="ip_lookup.copied"}} + {{d-button + class="btn-hover pull-right" + icon="copy" + label="ip_lookup.copied" + }} {{else}} - {{d-button action=(action "copyUrl") class="pull-right no-text" icon="copy"}} + {{d-button + action=(action "copyUrl") + class="pull-right no-text" + icon="copy" + }} {{/if}} - {{wizardUrl}} + {{wizardUrl}} {{/if}}
@@ -23,11 +36,12 @@
- {{input +
@@ -41,9 +55,8 @@ valueProperty="id" value=wizard.theme_id onChange=(action (mut wizard.theme_id)) - options=(hash - none="admin.wizard.no_theme" - )}} + options=(hash none="admin.wizard.no_theme") + }} @@ -55,21 +68,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"}} + + {{i18n "admin.wizard.save_submissions_label"}}
@@ -78,17 +81,27 @@
- {{input type="checkbox" checked=wizard.multiple_submissions}} + {{i18n "admin.wizard.multiple_submissions_label"}}
+
+
+ +
+
+ + {{i18n "admin.wizard.after_signup_label"}} +
+
+
- {{input type="checkbox" checked=wizard.prompt_completion}} + {{i18n "admin.wizard.prompt_completion_label"}}
@@ -98,77 +111,61 @@
- {{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"}} + 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"}} -
+
+ + {{i18n "admin.wizard.required_label"}}
- -
-
- -
-
- {{input type="checkbox" checked=wizard.resume_on_revisit}} - {{i18n "admin.wizard.resume_on_revisit_label"}} -
-
-
- {{/if}} + +
+
+ +
+
+ + {{i18n "admin.wizard.restart_on_revisit_label"}} +
+
+ +
+
+ +
+
+ {{wizard-mapper + inputs=wizard.permitted + options=(hash + context="wizard" + inputTypes="assignment,validation" + groupSelection="output" + guestGroup=true + userFieldSelection="key" + textSelection="value" + inputConnector="and" + ) + }} +
+
+ {{/wizard-subscription-container}}
- {{wizard-links - itemType="step" - current=currentStep - items=wizard.steps}} + {{wizard-links itemType="step" current=currentStep items=wizard.steps}} {{#if currentStep}} {{wizard-custom-step @@ -176,27 +173,37 @@ wizard=wizard currentField=currentField wizardFields=wizardFields - fieldTypes=fieldTypes}} + fieldTypes=filteredFieldTypes + subscribed=subscribed + }} {{/if}} {{wizard-links itemType="action" current=currentAction items=wizard.actions - generateLabels=true}} + 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=filteredFieldTypes + }} {{/each}}
- @@ -212,4 +219,4 @@ {{d-icon "times"}}{{error}} {{/if}}
-{{/if}} +{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs index 081cd5f3..7a33dac1 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs @@ -3,21 +3,18 @@ value=wizardListVal content=wizardList onChange=(route-action "changeWizard") - options=(hash - none="admin.wizard.select" - )}} + options=(hash none="admin.wizard.select") + }} {{d-button - action="createWizard" + action=(route-action "createWizard") label="admin.wizard.create" - icon="plus"}} + icon="plus" + }}
-{{wizard-message - key=messageKey - url=messageUrl - component="wizard"}} +{{wizard-message key=messageKey url=messageUrl component="wizard"}}
{{outlet}} -
+
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index bd575aae..d650986d 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -1,14 +1,27 @@ {{#admin-nav}} {{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}} + {{nav-item + route="adminWizardsCustomFields" + label="admin.wizard.custom_field.nav_label" + }} + {{nav-item + route="adminWizardsSubmissions" + label="admin.wizard.submissions.nav_label" + }} + {{#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"}} + {{nav-item + route="adminWizardsManager" + label="admin.wizard.manager.nav_label" + }} + +
+ +
{{/admin-nav}}
{{outlet}} -
+ \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index 43a97be8..4e3fa2f0 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -1,29 +1,35 @@ {{#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 - value=field.name - placeholder=(i18n "admin.wizard.custom_field.name.select")}} + {{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,19 +40,18 @@ {{/if}} {{/if}} {{d-button - action="destroy" + action=(action "destroy") icon="trash-alt" class="destroy" - disabled=destroyDisabled}} + disabled=destroyDisabled + }} {{d-button icon="save" - action="save" + action=(action "save") disabled=saveDisabled - class="save"}} - {{d-button - action="close" - icon="times" - disabled=closeDisabled}} + class="save" + }} + {{d-button action=(action "close") icon="times" disabled=closeDisabled}} {{else}} @@ -69,7 +74,7 @@ {{else}} - {{d-button action="edit" icon="pencil-alt"}} + {{d-button action=(action "edit") icon="pencil-alt"}} {{/if}} -{{/if}} +{{/if}} \ No newline at end of file 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 86% rename from assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs rename to assets/javascripts/discourse/templates/components/custom-wizard-composer-editor.hbs index 7d453a0b..70286603 100644 --- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs +++ b/assets/javascripts/discourse/templates/components/custom-wizard-composer-editor.hbs @@ -11,10 +11,11 @@ validation=validation loading=composer.loading showLink=showLink - wizardComposerEvents=true + wizardComposer=true fieldId=field.id disabled=disableTextarea - outletArgs=(hash composer=composer editorType="composer")}} + outletArgs=(hash composer=composer editorType="composer") +}} +/> \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs new file mode 100644 index 00000000..3f1cc4c6 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs @@ -0,0 +1,11 @@ + + +
\ No newline at end of file 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 71% 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..051ce6f8 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") @@ -26,9 +26,5 @@ {{/if}} {{#if clearable}} - {{d-button - class="clear-date-time" - icon="times" - action=(action "onClear") - }} -{{/if}} + {{d-button class="clear-date-time" icon="times" action=(action "onClear")}} +{{/if}} \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/wizard-editor.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs similarity index 64% rename from assets/javascripts/wizard/templates/components/wizard-editor.hbs rename to assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs index 861d2449..fd10299c 100644 --- a/assets/javascripts/wizard/templates/components/wizard-editor.hbs +++ b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs @@ -2,7 +2,7 @@
{{#if showPreview}} -
+
{{html-safe preview}}
@@ -18,16 +18,19 @@ onExpand=(action b.action b) class=b.className content=popupMenuOptions - options=(hash - popupTitle=b.title - icon=b.icon - )}} + options=(hash popupTitle=b.title icon=b.icon) + }} {{else}}
{{d.icon}}
- {{/if}} @@ -40,7 +43,12 @@
{{conditional-loading-spinner condition=loading}} - {{textarea tabindex=tabindex value=value class="d-editor-input" placeholder=placeholder}} +