Commits vergleichen
1 Commit
Autor | SHA1 | Datum | |
---|---|---|---|
|
d110deefca |
592 geänderte Dateien mit 7778 neuen und 68299 gelöschten Zeilen
|
@ -1,3 +1 @@
|
||||||
3.1.999: 1f35b80f85e5fd1efb7f4851f0845700432febdc
|
|
||||||
2.7.99: e07a57e398b6b1676ab42a7e34467556fca5416b
|
|
||||||
2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c
|
2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "eslint-config-discourse"
|
|
||||||
}
|
|
13
.github/workflows/discourse-plugin.yml
gevendort
13
.github/workflows/discourse-plugin.yml
gevendort
|
@ -1,13 +0,0 @@
|
||||||
name: Discourse Plugin
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1
|
|
44
.github/workflows/plugin-metadata.yml
gevendort
44
.github/workflows/plugin-metadata.yml
gevendort
|
@ -1,44 +0,0 @@
|
||||||
name: Metadata
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout head repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Store head version
|
|
||||||
run: |
|
|
||||||
sed -n -e 's/^.*version: /head_version=/p' plugin.rb >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout base repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
ref: "${{ github.base_ref }}"
|
|
||||||
|
|
||||||
- name: Store base version
|
|
||||||
run: |
|
|
||||||
sed -n -e 's/^.*version: /base_version=/p' plugin.rb >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
|
|
||||||
- name: Install semver
|
|
||||||
run: npm install --include=dev
|
|
||||||
|
|
||||||
- name: Check version
|
|
||||||
uses: actions/github-script@v5
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
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.");
|
|
||||||
}
|
|
7
.gitignore
gevendort
7
.gitignore
gevendort
|
@ -1,8 +1,3 @@
|
||||||
coverage/*
|
coverage/*
|
||||||
!coverage/.last_run.json
|
!coverage/.last_run.json
|
||||||
gems/*
|
gems/
|
||||||
.bundle/
|
|
||||||
auto_generated
|
|
||||||
.DS_Store
|
|
||||||
node_modules/
|
|
||||||
vendor/*
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,8 +0,0 @@
|
||||||
inherit_gem:
|
|
||||||
rubocop-discourse: default.yml
|
|
||||||
|
|
||||||
RSpec/ContextWording:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
RSpec/DescribeClass:
|
|
||||||
Enabled: false
|
|
|
@ -1,7 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
plugin = "discourse-custom-wizard"
|
|
||||||
|
|
||||||
SimpleCov.configure do
|
|
||||||
track_files "plugins/#{plugin}/**/*.rb"
|
|
||||||
add_filter { |src| !(src.filename =~ /(\/#{plugin}\/app\/|\/#{plugin}\/lib\/)/) }
|
|
||||||
end
|
|
|
@ -1,4 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
plugins: ["ember-template-lint-plugin-discourse"],
|
|
||||||
extends: "discourse:recommended",
|
|
||||||
};
|
|
|
@ -1,4 +1,4 @@
|
||||||
All code in this repository is Copyright 2023 by Angus McLeod.
|
All code in this repository is Copyright 2018 by Angus McLeod.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
7
Gemfile
7
Gemfile
|
@ -1,7 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
|
||||||
|
|
||||||
group :development do
|
|
||||||
gem 'rubocop-discourse'
|
|
||||||
end
|
|
39
Gemfile.lock
39
Gemfile.lock
|
@ -1,39 +0,0 @@
|
||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
ast (2.4.2)
|
|
||||||
json (2.6.2)
|
|
||||||
parallel (1.22.1)
|
|
||||||
parser (3.1.2.1)
|
|
||||||
ast (~> 2.4.1)
|
|
||||||
rainbow (3.1.1)
|
|
||||||
regexp_parser (2.6.0)
|
|
||||||
rexml (3.2.5)
|
|
||||||
rubocop (1.36.0)
|
|
||||||
json (~> 2.3)
|
|
||||||
parallel (~> 1.10)
|
|
||||||
parser (>= 3.1.2.1)
|
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
|
||||||
regexp_parser (>= 1.8, < 3.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.22.0)
|
|
||||||
parser (>= 3.1.1.0)
|
|
||||||
rubocop-discourse (3.0)
|
|
||||||
rubocop (>= 1.1.0)
|
|
||||||
rubocop-rspec (>= 2.0.0)
|
|
||||||
rubocop-rspec (2.13.2)
|
|
||||||
rubocop (~> 1.33)
|
|
||||||
ruby-progressbar (1.11.0)
|
|
||||||
unicode-display_width (2.3.0)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
rubocop-discourse
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.2.16
|
|
28
README.md
28
README.md
|
@ -1,27 +1,3 @@
|
||||||
# Discourse Custom Wizard Plugin
|
# discourse-custom-wizard
|
||||||
|
|
||||||
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.
|
See further: https://thepavilion.io/c/knowledge/discourse/custom-wizard/118
|
||||||
|
|
||||||
<img src="https://camo.githubusercontent.com/593432f1fc9658ffca104065668cc88fa21dffcd3002cb78ffd50c71f33a2523/68747470733a2f2f706176696c696f6e2d6173736574732e6e7963332e63646e2e6469676974616c6f6365616e7370616365732e636f6d2f706c7567696e732f77697a6172642d7265706f7369746f72792d62616e6e65722e706e67" alt="" data-canonical-src="https://pavilion-assets.nyc3.cdn.digitaloceanspaces.com/plugins/wizard-repository-banner.png" style="max-width: 100%;" width="400">
|
|
||||||
|
|
||||||
👋 Looking to report an issue? We're managing issues for this plugin using our [bug report wizard](https://coop.pavilion.tech/w/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://coop.pavilion.tech/c/82), 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://coop.pavilion.tech/t/1735)
|
|
||||||
- [Field Settings](https://coop.pavilion.tech/t/1580)
|
|
||||||
- [Conditional Settings](https://coop.pavilion.tech/t/1673)
|
|
||||||
- [Field Interpolation](https://coop.pavilion.tech/t/1557)
|
|
||||||
- [Handling Dates and Times](https://coop.pavilion.tech/t/1708)
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
- [Report an issue](https://coop.pavilion.tech/w/bug-report)
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# 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.
|
|
|
@ -1,44 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
class CustomWizard::AdminLogsController < CustomWizard::AdminController
|
|
||||||
before_action :find_wizard, except: [:index]
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: ActiveModel::ArraySerializer.new(
|
|
||||||
CustomWizard::Wizard.list(current_user),
|
|
||||||
each_serializer: CustomWizard::BasicWizardSerializer
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
render_json_dump(
|
|
||||||
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
|
|
||||||
logs: ActiveModel::ArraySerializer.new(
|
|
||||||
log_list.logs,
|
|
||||||
each_serializer: CustomWizard::LogSerializer
|
|
||||||
),
|
|
||||||
total: log_list.total
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def log_list
|
|
||||||
@log_list ||= begin
|
|
||||||
list = CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i, params[:wizard_id])
|
|
||||||
|
|
||||||
if list.logs.any? && (usernames = list.logs.map(&:username)).present?
|
|
||||||
user_map = User.where(username: usernames)
|
|
||||||
.reduce({}) do |result, user|
|
|
||||||
result[user.username] = user
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
list.logs.each do |log_item|
|
|
||||||
log_item.user = user_map[log_item.username]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,113 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
class CustomWizard::StepsController < ::CustomWizard::WizardClientController
|
|
||||||
before_action :ensure_can_update
|
|
||||||
|
|
||||||
def update
|
|
||||||
update = update_params.to_h
|
|
||||||
|
|
||||||
update[:fields] = {}
|
|
||||||
if params[:fields]
|
|
||||||
field_ids = @builder.wizard.field_ids
|
|
||||||
params[:fields].each do |k, v|
|
|
||||||
update[:fields][k] = v if field_ids.include? k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@builder.build
|
|
||||||
|
|
||||||
updater = @builder.wizard.create_updater(update[:step_id], update[:fields])
|
|
||||||
updater.update
|
|
||||||
@result = updater.result
|
|
||||||
|
|
||||||
if updater.success?
|
|
||||||
wizard_id = update_params[:wizard_id]
|
|
||||||
builder = CustomWizard::Builder.new(wizard_id, current_user, guest_id)
|
|
||||||
@wizard = builder.build(force: true)
|
|
||||||
|
|
||||||
current_step = @wizard.find_step(update[:step_id])
|
|
||||||
current_submission = @wizard.current_submission
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
if current_step.conditional_final_step && !current_step.last_step
|
|
||||||
current_step.force_final = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if current_step.final?
|
|
||||||
builder.template.actions.each do |action_template|
|
|
||||||
if action_template['run_after'] === 'wizard_completion'
|
|
||||||
action_result = CustomWizard::Action.new(
|
|
||||||
action: action_template,
|
|
||||||
wizard: @wizard,
|
|
||||||
submission: current_submission
|
|
||||||
).perform
|
|
||||||
|
|
||||||
if action_result.success?
|
|
||||||
current_submission = action_result.submission
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
current_submission.save
|
|
||||||
|
|
||||||
if redirect = get_redirect
|
|
||||||
updater.result[:redirect_on_complete] = redirect
|
|
||||||
end
|
|
||||||
|
|
||||||
@wizard.cleanup_on_complete!
|
|
||||||
|
|
||||||
result[:final] = true
|
|
||||||
else
|
|
||||||
current_submission.save
|
|
||||||
|
|
||||||
result[:final] = false
|
|
||||||
result[:next_step_id] = current_step.next.id
|
|
||||||
end
|
|
||||||
|
|
||||||
result.merge!(updater.result) if updater.result.present?
|
|
||||||
result[:refresh_required] = true if updater.refresh_required?
|
|
||||||
result[:wizard] = ::CustomWizard::WizardSerializer.new(
|
|
||||||
@wizard,
|
|
||||||
scope: Guardian.new(current_user),
|
|
||||||
root: false
|
|
||||||
).as_json
|
|
||||||
|
|
||||||
render json: result
|
|
||||||
else
|
|
||||||
errors = []
|
|
||||||
updater.errors.messages.each do |field, msg|
|
|
||||||
errors << { field: field, description: msg.join(',') }
|
|
||||||
end
|
|
||||||
render json: { errors: errors }, status: 422
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def ensure_can_update
|
|
||||||
raise Discourse::InvalidParameters.new(:wizard_id) if @builder.template.nil?
|
|
||||||
raise Discourse::InvalidAccess.new if !@builder.wizard || !@builder.wizard.can_access?
|
|
||||||
|
|
||||||
@step_template = @builder.template.steps.select do |s|
|
|
||||||
s['id'] == update_params[:step_id]
|
|
||||||
end.first
|
|
||||||
raise Discourse::InvalidParameters.new(:step_id) if !@step_template
|
|
||||||
raise Discourse::InvalidAccess.new if !@builder.check_condition(@step_template)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_params
|
|
||||||
@update_params || begin
|
|
||||||
params.require(:step_id)
|
|
||||||
params.require(:wizard_id)
|
|
||||||
params.permit(:wizard_id, :step_id).transform_values { |v| v.underscore }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_redirect
|
|
||||||
return @result[:redirect_on_next] if @result[:redirect_on_next].present?
|
|
||||||
|
|
||||||
submission = @wizard.current_submission
|
|
||||||
return nil unless submission.present?
|
|
||||||
## route_to set by actions, redirect_on_complete set by actions, redirect_to set at wizard entry
|
|
||||||
submission.route_to || submission.redirect_on_complete || submission.redirect_to
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
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
|
|
||||||
|
|
||||||
def skip
|
|
||||||
params.require(:wizard_id)
|
|
||||||
|
|
||||||
if wizard.required && !wizard.completed? && wizard.permitted?
|
|
||||||
return render json: { error: I18n.t('wizard.no_skip') }
|
|
||||||
end
|
|
||||||
|
|
||||||
result = { success: 'OK' }
|
|
||||||
|
|
||||||
if current_user && wizard.can_access?
|
|
||||||
if redirect_to = wizard.current_submission&.redirect_to
|
|
||||||
result.merge!(redirect_to: redirect_to)
|
|
||||||
end
|
|
||||||
|
|
||||||
wizard.cleanup_on_skip!
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: result
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def wizard
|
|
||||||
@wizard ||= begin
|
|
||||||
return nil unless @builder.present?
|
|
||||||
@builder.build({ reset: params[:reset] }, params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
class CustomWizard::WizardClientController < ::ApplicationController
|
|
||||||
before_action :ensure_plugin_enabled
|
|
||||||
before_action :set_builder
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def ensure_plugin_enabled
|
|
||||||
unless SiteSetting.custom_wizard_enabled
|
|
||||||
redirect_to path("/")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def guest_id
|
|
||||||
return nil if current_user.present?
|
|
||||||
cookies[:custom_wizard_guest_id] ||= CustomWizard::Wizard.generate_guest_id
|
|
||||||
cookies[:custom_wizard_guest_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_builder
|
|
||||||
@builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user, guest_id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CustomWizard::LogSerializer < ApplicationSerializer
|
|
||||||
attributes :date,
|
|
||||||
:action,
|
|
||||||
:username,
|
|
||||||
:message
|
|
||||||
|
|
||||||
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
|
|
||||||
end
|
|
|
@ -1,35 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
class CustomWizard::SubmissionSerializer < ApplicationSerializer
|
|
||||||
attributes :id,
|
|
||||||
:fields,
|
|
||||||
:submitted_at,
|
|
||||||
:user
|
|
||||||
|
|
||||||
def include_user?
|
|
||||||
object.wizard.user.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def user
|
|
||||||
::BasicUserSerializer.new(object.wizard.user, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def fields
|
|
||||||
@fields ||= begin
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
object.wizard.template['steps'].each do |step|
|
|
||||||
step['fields'].each do |field|
|
|
||||||
if value = object.fields[field['id']]
|
|
||||||
result[field['id']] = {
|
|
||||||
value: value,
|
|
||||||
type: field['type'],
|
|
||||||
label: field['label']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,138 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CustomWizard::FieldSerializer < ::ApplicationSerializer
|
|
||||||
|
|
||||||
attributes :id,
|
|
||||||
:index,
|
|
||||||
:type,
|
|
||||||
:required,
|
|
||||||
:value,
|
|
||||||
:label,
|
|
||||||
:placeholder,
|
|
||||||
:description,
|
|
||||||
:image,
|
|
||||||
:file_types,
|
|
||||||
:format,
|
|
||||||
:limit,
|
|
||||||
:property,
|
|
||||||
:content,
|
|
||||||
:tag_groups,
|
|
||||||
:can_create_tag,
|
|
||||||
:validations,
|
|
||||||
:max_length,
|
|
||||||
:char_counter,
|
|
||||||
:preview_template
|
|
||||||
|
|
||||||
def id
|
|
||||||
object.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
object.index
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
object.type
|
|
||||||
end
|
|
||||||
|
|
||||||
def required
|
|
||||||
object.required
|
|
||||||
end
|
|
||||||
|
|
||||||
def value
|
|
||||||
object.value
|
|
||||||
end
|
|
||||||
|
|
||||||
def label
|
|
||||||
I18n.t("#{i18n_key}.label", default: object.label, base_url: Discourse.base_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_label?
|
|
||||||
label.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def description
|
|
||||||
I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_description?
|
|
||||||
description.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def placeholder
|
|
||||||
I18n.t("#{i18n_key}.placeholder", default: object.placeholder)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_placeholder?
|
|
||||||
placeholder.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def image
|
|
||||||
object.image
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_image?
|
|
||||||
object.image.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_types
|
|
||||||
object.file_types
|
|
||||||
end
|
|
||||||
|
|
||||||
def format
|
|
||||||
object.format
|
|
||||||
end
|
|
||||||
|
|
||||||
def limit
|
|
||||||
object.limit
|
|
||||||
end
|
|
||||||
|
|
||||||
def property
|
|
||||||
object.property
|
|
||||||
end
|
|
||||||
|
|
||||||
def content
|
|
||||||
object.content
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_groups
|
|
||||||
object.tag_groups
|
|
||||||
end
|
|
||||||
|
|
||||||
def can_create_tag
|
|
||||||
object.can_create_tag
|
|
||||||
end
|
|
||||||
|
|
||||||
def validations
|
|
||||||
validations = {}
|
|
||||||
object.validations&.each do |type, props|
|
|
||||||
next unless props["status"]
|
|
||||||
validations[props["position"]] ||= {}
|
|
||||||
validations[props["position"]][type] = props.merge CustomWizard::RealtimeValidation.types[type.to_sym]
|
|
||||||
end
|
|
||||||
|
|
||||||
validations
|
|
||||||
end
|
|
||||||
|
|
||||||
def max_length
|
|
||||||
object.max_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def char_counter
|
|
||||||
object.char_counter
|
|
||||||
end
|
|
||||||
|
|
||||||
def preview_template
|
|
||||||
object.preview_template
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def i18n_key
|
|
||||||
@i18n_key ||= "#{object.step.wizard.id}.#{object.step.id}.#{object.id}".underscore
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscribed?
|
|
||||||
@subscribed ||= CustomWizard::Subscription.subscribed?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,83 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CustomWizard::StepSerializer < ::ApplicationSerializer
|
|
||||||
|
|
||||||
attributes :id,
|
|
||||||
:index,
|
|
||||||
:next,
|
|
||||||
:previous,
|
|
||||||
:description,
|
|
||||||
:title,
|
|
||||||
:banner,
|
|
||||||
:permitted,
|
|
||||||
:permitted_message,
|
|
||||||
:final
|
|
||||||
|
|
||||||
has_many :fields, serializer: ::CustomWizard::FieldSerializer, embed: :objects
|
|
||||||
|
|
||||||
def id
|
|
||||||
object.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
object.index
|
|
||||||
end
|
|
||||||
|
|
||||||
def next
|
|
||||||
object.next.id if object.next.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_next?
|
|
||||||
object.next.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def previous
|
|
||||||
object.previous.id if object.previous.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_previous?
|
|
||||||
object.previous.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def title
|
|
||||||
I18n.t("#{i18n_key}.title", default: object.title, base_url: Discourse.base_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_title?
|
|
||||||
title.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def description
|
|
||||||
I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_description?
|
|
||||||
description.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def banner
|
|
||||||
object.banner
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_banner?
|
|
||||||
object.banner.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def permitted
|
|
||||||
object.permitted
|
|
||||||
end
|
|
||||||
|
|
||||||
def permitted_message
|
|
||||||
object.permitted_message
|
|
||||||
end
|
|
||||||
|
|
||||||
def final
|
|
||||||
object.final?
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def i18n_key
|
|
||||||
@i18n_key ||= "#{object.wizard.id}.#{object.id}".underscore
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,28 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Custom Wizard QUnit Test Runner</title>
|
|
||||||
<%= discourse_stylesheet_link_tag(:test_helper, theme_id: nil) %>
|
|
||||||
<%= discourse_stylesheet_link_tag :wizard, theme_id: nil %>
|
|
||||||
<%= discourse_stylesheet_link_tag :wizard_custom %>
|
|
||||||
<%= preload_script "locales/en" %>
|
|
||||||
<%= preload_script "ember_jquery" %>
|
|
||||||
<%= preload_script "wizard-vendor" %>
|
|
||||||
<%= preload_script "wizard-custom" %>
|
|
||||||
<%= preload_script "wizard-raw-templates" %>
|
|
||||||
<%= preload_script "wizard-plugin" %>
|
|
||||||
<%= preload_script "pretty-text-bundle" %>
|
|
||||||
<%= preload_script "wizard-qunit" %>
|
|
||||||
<%= csrf_meta_tags %>
|
|
||||||
|
|
||||||
<script src="<%= ExtraLocalesController.url("wizard") %>"></script>
|
|
||||||
|
|
||||||
<%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
|
|
||||||
<meta name="discourse_theme_id" content="">
|
|
||||||
<meta name="discourse-base-uri" content="<%= Discourse.base_path %>">
|
|
||||||
</head>
|
|
||||||
<body class="custom-wizard">
|
|
||||||
<div id="qunit"></div>
|
|
||||||
<div id="qunit-fixture"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,20 +1,34 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
import { alias, equal, or } from "@ember/object/computed";
|
import { alias, or } from "@ember/object/computed";
|
||||||
import I18n from "I18n";
|
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({
|
export default Component.extend({
|
||||||
tagName: "tr",
|
tagName: "tr",
|
||||||
topicSerializers: ["topic_view", "topic_list_item"],
|
topicSerializers: ["topic_view", "topic_list_item"],
|
||||||
postSerializers: ["post"],
|
postSerializers: ["post"],
|
||||||
groupSerializers: ["basic_group"],
|
groupSerializers: ["basic_group"],
|
||||||
categorySerializers: ["basic_category"],
|
categorySerializers: ["basic_category"],
|
||||||
|
klassContent: generateContent(
|
||||||
|
["topic", "post", "group", "category"],
|
||||||
|
"klass"
|
||||||
|
),
|
||||||
|
typeContent: generateContent(
|
||||||
|
["string", "boolean", "integer", "json"],
|
||||||
|
"type"
|
||||||
|
),
|
||||||
showInputs: or("field.new", "field.edit"),
|
showInputs: or("field.new", "field.edit"),
|
||||||
classNames: ["custom-field-input"],
|
classNames: ["custom-field-input"],
|
||||||
loading: or("saving", "destroying"),
|
loading: or("saving", "destroying"),
|
||||||
destroyDisabled: alias("loading"),
|
destroyDisabled: alias("loading"),
|
||||||
closeDisabled: alias("loading"),
|
closeDisabled: alias("loading"),
|
||||||
isExternal: equal("field.id", "external"),
|
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
||||||
|
@ -25,13 +39,9 @@ export default Component.extend({
|
||||||
const serializers = this.get(`${klass}Serializers`);
|
const serializers = this.get(`${klass}Serializers`);
|
||||||
|
|
||||||
if (serializers) {
|
if (serializers) {
|
||||||
return serializers.reduce((result, key) => {
|
return generateContent(serializers, "serializers");
|
||||||
result.push({
|
} else {
|
||||||
id: key,
|
return [];
|
||||||
name: I18n.t(`admin.wizard.custom_field.serializers.${key}`),
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -51,14 +61,13 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"saving",
|
"saving",
|
||||||
"isExternal",
|
|
||||||
"field.name",
|
"field.name",
|
||||||
"field.klass",
|
"field.klass",
|
||||||
"field.type",
|
"field.type",
|
||||||
"field.serializers"
|
"field.serializers"
|
||||||
)
|
)
|
||||||
saveDisabled(saving, isExternal) {
|
saveDisabled(saving) {
|
||||||
if (saving || isExternal) {
|
if (saving) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,213 +0,0 @@
|
||||||
import ComposerEditor from "discourse/components/composer-editor";
|
|
||||||
import {
|
|
||||||
bind,
|
|
||||||
default as discourseComputed,
|
|
||||||
on,
|
|
||||||
} from "discourse-common/utils/decorators";
|
|
||||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
|
||||||
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 "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({
|
|
||||||
modal: service(),
|
|
||||||
|
|
||||||
classNameBindings: ["fieldClass"],
|
|
||||||
allowUpload: true,
|
|
||||||
showLink: false,
|
|
||||||
topic: null,
|
|
||||||
showToolbar: true,
|
|
||||||
focusTarget: "reply",
|
|
||||||
canWhisper: false,
|
|
||||||
lastValidatedAt: "lastValidatedAt",
|
|
||||||
popupMenuOptions: [],
|
|
||||||
draftStatus: "null",
|
|
||||||
replyPlaceholder: alias("field.translatedPlaceholder"),
|
|
||||||
wizardEventFieldId: null,
|
|
||||||
composerEventPrefix: "wizard-editor",
|
|
||||||
|
|
||||||
@on("didInsertElement")
|
|
||||||
_composerEditorInit() {
|
|
||||||
const $input = $(this.element.querySelector(".d-editor-input"));
|
|
||||||
|
|
||||||
if (this.siteSettings.enable_mentions) {
|
|
||||||
$input.autocomplete({
|
|
||||||
template: findRawTemplate("user-selector-autocomplete"),
|
|
||||||
dataSource: (term) => this._userSearchTerm.call(this, term),
|
|
||||||
key: "@",
|
|
||||||
transformComplete: (v) => v.username || v.name,
|
|
||||||
afterComplete: (value) => {
|
|
||||||
this.composer.set("reply", value);
|
|
||||||
scheduleOnce("afterRender", () => $input.blur().focus());
|
|
||||||
},
|
|
||||||
triggerRule: (textarea) =>
|
|
||||||
!inCodeBlock(textarea.value, caretPosition(textarea)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const siteSettings = this.siteSettings;
|
|
||||||
if (siteSettings.mentionables_enabled) {
|
|
||||||
Site.currentProp("mentionable_items", this.wizard.mentionable_items);
|
|
||||||
const { SEPARATOR } = requirejs(
|
|
||||||
"discourse/plugins/discourse-mentionables/discourse/lib/discourse-markdown/mentionable-items"
|
|
||||||
);
|
|
||||||
const { searchMentionableItem } = requirejs(
|
|
||||||
"discourse/plugins/discourse-mentionables/discourse/lib/mentionable-item-search"
|
|
||||||
);
|
|
||||||
|
|
||||||
$input.autocomplete({
|
|
||||||
template: findRawTemplate("javascripts/mentionable-item-autocomplete"),
|
|
||||||
key: SEPARATOR,
|
|
||||||
afterComplete: (value) => {
|
|
||||||
this.composer.set("reply", value);
|
|
||||||
scheduleOnce("afterRender", () => $input.blur().focus());
|
|
||||||
},
|
|
||||||
transformComplete: (item) => item.model.slug,
|
|
||||||
dataSource: (term) =>
|
|
||||||
term.match(/\s/) ? null : searchMentionableItem(term, siteSettings),
|
|
||||||
triggerRule: (textarea) =>
|
|
||||||
!inCodeBlock(textarea.value, caretPosition(textarea)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$input.on("scroll", this._throttledSyncEditorAndPreviewScroll);
|
|
||||||
this._bindUploadTarget();
|
|
||||||
|
|
||||||
const field = this.field;
|
|
||||||
this.editorInputClass = `.${dasherize(field.type)}-${dasherize(
|
|
||||||
field.id
|
|
||||||
)} .d-editor-input`;
|
|
||||||
|
|
||||||
this._uppyInstance.on("file-added", () => {
|
|
||||||
this.session.set("wizardEventFieldId", field.id);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("field.id")
|
|
||||||
fileUploadElementId(fieldId) {
|
|
||||||
return `file-uploader-${dasherize(fieldId)}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
allowedFileTypes() {
|
|
||||||
return this.siteSettings.authorized_extensions
|
|
||||||
.split("|")
|
|
||||||
.map((ext) => "." + ext)
|
|
||||||
.join(",");
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed()
|
|
||||||
uploadIcon() {
|
|
||||||
return uploadIcon(false, this.siteSettings);
|
|
||||||
},
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
toolbar.addButton({
|
|
||||||
id: "upload",
|
|
||||||
group: "insertions",
|
|
||||||
icon: this.uploadIcon,
|
|
||||||
title: "upload",
|
|
||||||
sendAction: (event) => component.send("showUploadModal", event),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar.addButton({
|
|
||||||
id: "link",
|
|
||||||
group: "insertions",
|
|
||||||
shortcut: "K",
|
|
||||||
trimLeading: true,
|
|
||||||
unshift: true,
|
|
||||||
sendAction: (event) => component.send("showLinkModal", event),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.siteSettings.mentionables_enabled) {
|
|
||||||
const { SEPARATOR } = requirejs(
|
|
||||||
"discourse/plugins/discourse-mentionables/discourse/lib/discourse-markdown/mentionable-items"
|
|
||||||
);
|
|
||||||
|
|
||||||
toolbar.addButton({
|
|
||||||
id: "insert-mentionable",
|
|
||||||
group: "extras",
|
|
||||||
icon: this.siteSettings.mentionables_composer_button_icon,
|
|
||||||
title: "mentionables.composer.insert.title",
|
|
||||||
perform: () => {
|
|
||||||
this.appEvents.trigger("wizard-editor:insert-text", {
|
|
||||||
fieldId: this.field.id,
|
|
||||||
text: SEPARATOR,
|
|
||||||
});
|
|
||||||
const $textarea = $(
|
|
||||||
document.querySelector(
|
|
||||||
`.composer-field.${this.field.id} textarea.d-editor-input`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$textarea.trigger("keyup.autocomplete");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
showLinkModal(toolbarEvent) {
|
|
||||||
let linkText = "";
|
|
||||||
this._lastSel = toolbarEvent.selected;
|
|
||||||
|
|
||||||
if (this._lastSel) {
|
|
||||||
linkText = this._lastSel.value;
|
|
||||||
}
|
|
||||||
this.modal.show(InsertHyperlink, {
|
|
||||||
model: { linkText, toolbarEvent },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
showUploadModal() {
|
|
||||||
this.session.set("wizardEventFieldId", this.field.id);
|
|
||||||
document.getElementById(this.fileUploadElementId).click();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
import DateInput from "discourse/components/date-input";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default DateInput.extend({
|
|
||||||
useNativePicker: false,
|
|
||||||
classNameBindings: ["fieldClass"],
|
|
||||||
|
|
||||||
@discourseComputed()
|
|
||||||
placeholder() {
|
|
||||||
return this.format;
|
|
||||||
},
|
|
||||||
_opts() {
|
|
||||||
return {
|
|
||||||
format: this.format || "LL",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({});
|
|
|
@ -1,49 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
|
||||||
import { schedule } from "@ember/runloop";
|
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
|
||||||
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import { on } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
@on("init")
|
|
||||||
updatePreview() {
|
|
||||||
if (this.isDestroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
if (this._state !== "inDOM" || !this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const $preview = $(this.element);
|
|
||||||
|
|
||||||
if ($preview.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.previewUpdated($preview);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
previewUpdated($preview) {
|
|
||||||
// Paint oneboxes
|
|
||||||
const paintFunc = () => {
|
|
||||||
loadOneboxes(
|
|
||||||
$preview[0],
|
|
||||||
ajax,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
this.siteSettings.max_oneboxes_per_post,
|
|
||||||
true // refresh on every load
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
discourseDebounce(this, paintFunc, 450);
|
|
||||||
|
|
||||||
// Short upload urls need resolution
|
|
||||||
resolveAllShortUrls(ajax, this.siteSettings, $preview[0]);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
keyPress(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
onChangeValue(value) {
|
|
||||||
this.set("field.value", value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({});
|
|
|
@ -1,7 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
keyPress(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
keyPress(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,27 +0,0 @@
|
||||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
|
|
||||||
export default Component.extend(UppyUploadMixin, {
|
|
||||||
classNames: ["wizard-field-upload"],
|
|
||||||
classNameBindings: ["isImage", "fieldClass"],
|
|
||||||
uploading: false,
|
|
||||||
type: computed(function () {
|
|
||||||
return `wizard_${this.field.id}`;
|
|
||||||
}),
|
|
||||||
id: computed(function () {
|
|
||||||
return `wizard_field_upload_${this.field.id}`;
|
|
||||||
}),
|
|
||||||
isImage: computed("field.value.extension", function () {
|
|
||||||
return (
|
|
||||||
this.field.value &&
|
|
||||||
this.siteSettings.wizard_recognised_image_upload_formats
|
|
||||||
.split("|")
|
|
||||||
.includes(this.field.value.extension)
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
|
|
||||||
uploadDone(upload) {
|
|
||||||
this.set("field.value", upload);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({});
|
|
|
@ -1,5 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
classNameBindings: ["fieldClass"],
|
|
||||||
});
|
|
|
@ -1,41 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { cookAsync } from "discourse/lib/text";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
classNameBindings: [
|
|
||||||
":wizard-field",
|
|
||||||
"typeClasses",
|
|
||||||
"field.invalid",
|
|
||||||
"field.id",
|
|
||||||
],
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
cookAsync(this.field.translatedDescription).then((cookedDescription) => {
|
|
||||||
this.set("cookedDescription", cookedDescription);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("field.type", "field.id")
|
|
||||||
typeClasses: (type, id) =>
|
|
||||||
`${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`,
|
|
||||||
|
|
||||||
@discourseComputed("field.id")
|
|
||||||
fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`,
|
|
||||||
|
|
||||||
@discourseComputed("field.type", "field.id")
|
|
||||||
inputComponentName(type, id) {
|
|
||||||
if (["text_only"].includes(type)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return dasherize(type === "component" ? id : `custom-wizard-field-${type}`);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("field.type")
|
|
||||||
textType(fieldType) {
|
|
||||||
return ["text", "textarea"].includes(fieldType);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,29 +0,0 @@
|
||||||
import CustomWizard from "../models/custom-wizard";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
classNameBindings: [":wizard-no-access", "reasonClass"],
|
|
||||||
|
|
||||||
@discourseComputed("reason")
|
|
||||||
reasonClass(reason) {
|
|
||||||
return dasherize(reason);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
siteName() {
|
|
||||||
return this.siteSettings.title || "";
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
skip() {
|
|
||||||
if (this.currentUser) {
|
|
||||||
CustomWizard.skip(this.get("wizardId"));
|
|
||||||
} else {
|
|
||||||
window.location = getURL("/");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
classNameBindings: [":wizard-step-form", "customStepClass"],
|
|
||||||
|
|
||||||
@discourseComputed("step.id")
|
|
||||||
customStepClass: (stepId) => `wizard-step-${stepId}`,
|
|
||||||
});
|
|
|
@ -1,227 +0,0 @@
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import I18n from "I18n";
|
|
||||||
import getUrl from "discourse-common/lib/get-url";
|
|
||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import { schedule } from "@ember/runloop";
|
|
||||||
import { cookAsync } from "discourse/lib/text";
|
|
||||||
import CustomWizard, {
|
|
||||||
updateCachedWizard,
|
|
||||||
} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard";
|
|
||||||
import { alias, not } from "@ember/object/computed";
|
|
||||||
import discourseLater from "discourse-common/lib/later";
|
|
||||||
|
|
||||||
const alreadyWarned = {};
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
classNameBindings: [":wizard-step", "step.id"],
|
|
||||||
saving: null,
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this.set("stylingDropdown", {});
|
|
||||||
},
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
cookAsync(this.step.translatedTitle).then((cookedTitle) => {
|
|
||||||
this.set("cookedTitle", cookedTitle);
|
|
||||||
});
|
|
||||||
cookAsync(this.step.translatedDescription).then((cookedDescription) => {
|
|
||||||
this.set("cookedDescription", cookedDescription);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this.autoFocus();
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.index", "wizard.required")
|
|
||||||
showQuitButton: (index, required) => index === 0 && !required,
|
|
||||||
|
|
||||||
showNextButton: not("step.final"),
|
|
||||||
showDoneButton: alias("step.final"),
|
|
||||||
|
|
||||||
@discourseComputed(
|
|
||||||
"step.index",
|
|
||||||
"step.displayIndex",
|
|
||||||
"wizard.totalSteps",
|
|
||||||
"wizard.completed"
|
|
||||||
)
|
|
||||||
showFinishButton: (index, displayIndex, total, completed) => {
|
|
||||||
return index !== 0 && displayIndex !== total && completed;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.index")
|
|
||||||
showBackButton: (index) => index > 0,
|
|
||||||
|
|
||||||
@discourseComputed("step.banner")
|
|
||||||
bannerImage(src) {
|
|
||||||
if (!src) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return getUrl(src);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.id")
|
|
||||||
bannerAndDescriptionClass(id) {
|
|
||||||
return `wizard-banner-and-description wizard-banner-and-description-${id}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.fields.[]")
|
|
||||||
primaryButtonIndex(fields) {
|
|
||||||
return fields.length + 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.fields.[]")
|
|
||||||
secondaryButtonIndex(fields) {
|
|
||||||
return fields.length + 2;
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("step.id")
|
|
||||||
_stepChanged() {
|
|
||||||
this.set("saving", false);
|
|
||||||
this.autoFocus();
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("step.message")
|
|
||||||
_handleMessage: function () {
|
|
||||||
const message = this.get("step.message");
|
|
||||||
this.showMessage(message);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.index", "wizard.totalSteps")
|
|
||||||
barStyle(displayIndex, totalSteps) {
|
|
||||||
let ratio = parseFloat(displayIndex) / parseFloat(totalSteps - 1);
|
|
||||||
if (ratio < 0) {
|
|
||||||
ratio = 0;
|
|
||||||
}
|
|
||||||
if (ratio > 1) {
|
|
||||||
ratio = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return htmlSafe(`width: ${ratio * 200}px`);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.fields")
|
|
||||||
includeSidebar(fields) {
|
|
||||||
return !!fields.findBy("show_in_sidebar");
|
|
||||||
},
|
|
||||||
|
|
||||||
autoFocus() {
|
|
||||||
discourseLater(() => {
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
if ($(".invalid .wizard-focusable").length) {
|
|
||||||
this.animateInvalidFields();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
animateInvalidFields() {
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
let $invalid = $(".invalid .wizard-focusable");
|
|
||||||
if ($invalid.length) {
|
|
||||||
$([document.documentElement, document.body]).animate(
|
|
||||||
{
|
|
||||||
scrollTop: $invalid.offset().top - 200,
|
|
||||||
},
|
|
||||||
400
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
advance() {
|
|
||||||
this.set("saving", true);
|
|
||||||
this.get("step")
|
|
||||||
.save()
|
|
||||||
.then((response) => {
|
|
||||||
updateCachedWizard(CustomWizard.build(response["wizard"]));
|
|
||||||
|
|
||||||
if (response["final"]) {
|
|
||||||
CustomWizard.finished(response);
|
|
||||||
} else {
|
|
||||||
this.goNext(response);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => this.animateInvalidFields())
|
|
||||||
.finally(() => this.set("saving", false));
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
quit() {
|
|
||||||
this.get("wizard").skip();
|
|
||||||
},
|
|
||||||
|
|
||||||
done() {
|
|
||||||
this.send("nextStep");
|
|
||||||
},
|
|
||||||
|
|
||||||
showMessage(message) {
|
|
||||||
this.sendAction(message);
|
|
||||||
},
|
|
||||||
|
|
||||||
stylingDropdownChanged(id, value) {
|
|
||||||
this.set("stylingDropdown", { id, value });
|
|
||||||
},
|
|
||||||
|
|
||||||
exitEarly() {
|
|
||||||
const step = this.step;
|
|
||||||
step.validate();
|
|
||||||
|
|
||||||
if (step.get("valid")) {
|
|
||||||
this.set("saving", true);
|
|
||||||
|
|
||||||
step
|
|
||||||
.save()
|
|
||||||
.then(() => this.send("quit"))
|
|
||||||
.finally(() => this.set("saving", false));
|
|
||||||
} else {
|
|
||||||
this.autoFocus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
backStep() {
|
|
||||||
if (this.saving) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.goBack();
|
|
||||||
},
|
|
||||||
|
|
||||||
nextStep() {
|
|
||||||
if (this.saving) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const step = this.step;
|
|
||||||
const result = step.validate();
|
|
||||||
|
|
||||||
if (result.warnings.length) {
|
|
||||||
const unwarned = result.warnings.filter((w) => !alreadyWarned[w]);
|
|
||||||
if (unwarned.length) {
|
|
||||||
unwarned.forEach((w) => (alreadyWarned[w] = true));
|
|
||||||
return window.bootbox.confirm(
|
|
||||||
unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"),
|
|
||||||
I18n.t("no_value"),
|
|
||||||
I18n.t("yes_value"),
|
|
||||||
(confirmed) => {
|
|
||||||
if (confirmed) {
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (step.get("valid")) {
|
|
||||||
this.advance();
|
|
||||||
} else {
|
|
||||||
this.autoFocus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,15 +0,0 @@
|
||||||
import TagChooser from "select-kit/components/tag-chooser";
|
|
||||||
|
|
||||||
export default TagChooser.extend({
|
|
||||||
searchTags(url, data, callback) {
|
|
||||||
if (this.tagGroups) {
|
|
||||||
let tagGroupsString = this.tagGroups.join(",");
|
|
||||||
data.filterForInput = {
|
|
||||||
name: "custom-wizard-tag-chooser",
|
|
||||||
groups: tagGroupsString,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._super(url, data, callback);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,34 +0,0 @@
|
||||||
<DModal @closeModal={{@closeModal}} @title={{this.title}}>
|
|
||||||
{{#if loading}}
|
|
||||||
<LoadingSpinner size="large" />
|
|
||||||
{{else}}
|
|
||||||
<div class="edit-directory-columns-container">
|
|
||||||
{{#each @model.columns as |column|}}
|
|
||||||
<div class="edit-directory-column">
|
|
||||||
<div class="left-content">
|
|
||||||
<label class="column-name">
|
|
||||||
<Input @type="checkbox" @checked={{column.enabled}} />
|
|
||||||
{{directory-table-header-title
|
|
||||||
field=column.label
|
|
||||||
translated=true
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DButton
|
|
||||||
class="btn-primary"
|
|
||||||
@label="directory.edit_columns.save"
|
|
||||||
@action={{action "save"}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
class="btn-secondary reset-to-default"
|
|
||||||
@label="directory.edit_columns.reset_to_default"
|
|
||||||
@action={{action "resetToDefault"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DModal>
|
|
|
@ -1,15 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<DModal
|
|
||||||
@closeModal={{@closeModal}}
|
|
||||||
class="next-session-time-modal"
|
|
||||||
@title={{this.title}}
|
|
||||||
>
|
|
||||||
<DateTimeInput
|
|
||||||
@date={{this.bufferedDateTime}}
|
|
||||||
@onChange={{action "dateTimeChanged"}}
|
|
||||||
@showTime="true"
|
|
||||||
@clearable="true"
|
|
||||||
/>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DButton
|
|
||||||
@action={{action "submit"}}
|
|
||||||
class="btn-primary"
|
|
||||||
@label="admin.wizard.after_time_modal.done"
|
|
||||||
@disabled={{this.submitDisabled}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DModal>
|
|
|
@ -1,30 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
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");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,7 +1,8 @@
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||||
import { empty, equal, or } from "@ember/object/computed";
|
import { and, empty, equal, or } from "@ember/object/computed";
|
||||||
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
|
import wizardSchema from "../lib/wizard-schema";
|
||||||
import UndoChanges from "../mixins/undo-changes";
|
import UndoChanges from "../mixins/undo-changes";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
@ -15,7 +16,6 @@ export default Component.extend(UndoChanges, {
|
||||||
createTopic: equal("action.type", "create_topic"),
|
createTopic: equal("action.type", "create_topic"),
|
||||||
updateProfile: equal("action.type", "update_profile"),
|
updateProfile: equal("action.type", "update_profile"),
|
||||||
watchCategories: equal("action.type", "watch_categories"),
|
watchCategories: equal("action.type", "watch_categories"),
|
||||||
watchTags: equal("action.type", "watch_tags"),
|
|
||||||
sendMessage: equal("action.type", "send_message"),
|
sendMessage: equal("action.type", "send_message"),
|
||||||
openComposer: equal("action.type", "open_composer"),
|
openComposer: equal("action.type", "open_composer"),
|
||||||
sendToApi: equal("action.type", "send_to_api"),
|
sendToApi: equal("action.type", "send_to_api"),
|
||||||
|
@ -25,6 +25,8 @@ export default Component.extend(UndoChanges, {
|
||||||
createGroup: equal("action.type", "create_group"),
|
createGroup: equal("action.type", "create_group"),
|
||||||
apiEmpty: empty("action.api"),
|
apiEmpty: empty("action.api"),
|
||||||
groupPropertyTypes: selectKitContent(["id", "name"]),
|
groupPropertyTypes: selectKitContent(["id", "name"]),
|
||||||
|
hasAdvanced: or("hasCustomFields", "routeTo"),
|
||||||
|
showAdvanced: and("hasAdvanced", "action.type"),
|
||||||
hasCustomFields: or(
|
hasCustomFields: or(
|
||||||
"basicTopicFields",
|
"basicTopicFields",
|
||||||
"updateProfile",
|
"updateProfile",
|
||||||
|
@ -34,14 +36,22 @@ export default Component.extend(UndoChanges, {
|
||||||
basicTopicFields: or("createTopic", "sendMessage", "openComposer"),
|
basicTopicFields: or("createTopic", "sendMessage", "openComposer"),
|
||||||
publicTopicFields: or("createTopic", "openComposer"),
|
publicTopicFields: or("createTopic", "openComposer"),
|
||||||
showPostAdvanced: or("createTopic", "sendMessage"),
|
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) => {
|
availableNotificationLevels: notificationLevels.map((type) => {
|
||||||
return {
|
return {
|
||||||
id: type,
|
id: type,
|
||||||
name: I18n.t(`admin.wizard.action.watch_x.notification_level.${type}`),
|
name: I18n.t(
|
||||||
|
`admin.wizard.action.watch_categories.notification_level.${type}`
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
messageUrl: "https://discourse.pluginmanager.org/t/action-settings",
|
messageUrl: "https://thepavilion.io/t/2810",
|
||||||
|
|
||||||
@discourseComputed("action.type")
|
@discourseComputed("action.type")
|
||||||
messageKey(type) {
|
messageKey(type) {
|
||||||
|
@ -52,11 +62,6 @@ export default Component.extend(UndoChanges, {
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("action.type")
|
|
||||||
customFieldsContext(type) {
|
|
||||||
return `action.${type}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("wizard.steps")
|
@discourseComputed("wizard.steps")
|
||||||
runAfterContent(steps) {
|
runAfterContent(steps) {
|
||||||
let content = steps.map(function (step) {
|
let content = steps.map(function (step) {
|
||||||
|
@ -91,14 +96,4 @@ export default Component.extend(UndoChanges, {
|
||||||
}
|
}
|
||||||
return apis.find((a) => a.name === api).endpoints;
|
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");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||||
import { equal, or } from "@ember/object/computed";
|
import { alias, equal, or } from "@ember/object/computed";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
import { selectKitContent } from "../lib/wizard";
|
import { selectKitContent } from "../lib/wizard";
|
||||||
import UndoChanges from "../mixins/undo-changes";
|
import UndoChanges from "../mixins/undo-changes";
|
||||||
|
@ -25,9 +25,9 @@ export default Component.extend(UndoChanges, {
|
||||||
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
|
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
|
||||||
showLimit: or("isCategory", "isTag"),
|
showLimit: or("isCategory", "isTag"),
|
||||||
isTextType: or("isText", "isTextarea", "isComposer"),
|
isTextType: or("isText", "isTextarea", "isComposer"),
|
||||||
isComposerPreview: equal("field.type", "composer_preview"),
|
|
||||||
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
||||||
messageUrl: "https://discourse.pluginmanager.org/t/field-settings",
|
showAdvanced: alias("field.type"),
|
||||||
|
messageUrl: "https://thepavilion.io/t/2809",
|
||||||
|
|
||||||
@discourseComputed("field.type")
|
@discourseComputed("field.type")
|
||||||
validations(type) {
|
validations(type) {
|
||||||
|
@ -85,8 +85,8 @@ export default Component.extend(UndoChanges, {
|
||||||
if (this.isDropdown) {
|
if (this.isDropdown) {
|
||||||
options.wizardFieldSelection = "key,value";
|
options.wizardFieldSelection = "key,value";
|
||||||
options.userFieldOptionsSelection = "output";
|
options.userFieldOptionsSelection = "output";
|
||||||
options.textSelection = "key,value";
|
options.textSelection = "key,value,output";
|
||||||
options.inputTypes = "association,conditional,assignment";
|
options.inputTypes = "conditional,association,assignment";
|
||||||
options.pairConnector = "association";
|
options.pairConnector = "association";
|
||||||
options.keyPlaceholder = "admin.wizard.key";
|
options.keyPlaceholder = "admin.wizard.key";
|
||||||
options.valuePlaceholder = "admin.wizard.value";
|
options.valuePlaceholder = "admin.wizard.value";
|
||||||
|
@ -107,53 +107,13 @@ export default Component.extend(UndoChanges, {
|
||||||
return this.setupTypeOutput(fieldType, options);
|
return this.setupTypeOutput(fieldType, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("step.index")
|
|
||||||
fieldConditionOptions(stepIndex) {
|
|
||||||
const options = {
|
|
||||||
inputTypes: "validation",
|
|
||||||
context: "field",
|
|
||||||
textSelection: "value",
|
|
||||||
userFieldSelection: true,
|
|
||||||
groupSelection: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stepIndex > 0) {
|
|
||||||
options.wizardFieldSelection = true;
|
|
||||||
options.wizardActionSelection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("step.index")
|
|
||||||
fieldIndexOptions(stepIndex) {
|
|
||||||
const options = {
|
|
||||||
context: "field",
|
|
||||||
userFieldSelection: true,
|
|
||||||
groupSelection: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stepIndex > 0) {
|
|
||||||
options.wizardFieldSelection = true;
|
|
||||||
options.wizardActionSelection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
imageUploadDone(upload) {
|
imageUploadDone(upload) {
|
||||||
this.setProperties({
|
this.set("field.image", upload.url);
|
||||||
"field.image": upload.url,
|
|
||||||
"field.image_upload_id": upload.id,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
imageUploadDeleted() {
|
imageUploadDeleted() {
|
||||||
this.setProperties({
|
this.set("field.image", null);
|
||||||
"field.image": null,
|
|
||||||
"field.image_upload_id": null,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,40 +1,15 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: "wizard-custom-step",
|
classNames: "wizard-custom-step",
|
||||||
|
|
||||||
@discourseComputed("step.index")
|
|
||||||
stepConditionOptions(stepIndex) {
|
|
||||||
const options = {
|
|
||||||
inputTypes: "validation",
|
|
||||||
context: "step",
|
|
||||||
textSelection: "value",
|
|
||||||
userFieldSelection: true,
|
|
||||||
groupSelection: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stepIndex > 0) {
|
|
||||||
options["wizardFieldSelection"] = true;
|
|
||||||
options["wizardActionSelection"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
bannerUploadDone(upload) {
|
bannerUploadDone(upload) {
|
||||||
this.setProperties({
|
this.set("step.banner", upload.url);
|
||||||
"step.banner": upload.url,
|
|
||||||
"step.banner_upload_id": upload.id,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
bannerUploadDeleted() {
|
bannerUploadDeleted() {
|
||||||
this.setProperties({
|
this.set("step.banner", null);
|
||||||
"step.banner": null,
|
|
||||||
"step.banner_upload_id": null,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import {
|
||||||
|
default as discourseComputed,
|
||||||
|
observes,
|
||||||
|
on,
|
||||||
|
} from "discourse-common/utils/decorators";
|
||||||
import { generateName } from "../lib/wizard";
|
import { generateName } from "../lib/wizard";
|
||||||
import {
|
import {
|
||||||
setWizardDefaults,
|
setWizardDefaults,
|
||||||
default as wizardSchema,
|
default as wizardSchema,
|
||||||
} from "../lib/wizard-schema";
|
} from "../lib/wizard-schema";
|
||||||
import { notEmpty } from "@ember/object/computed";
|
import { notEmpty } from "@ember/object/computed";
|
||||||
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { A } from "@ember/array";
|
import { A } from "@ember/array";
|
||||||
|
@ -14,12 +19,27 @@ export default Component.extend({
|
||||||
items: A(),
|
items: A(),
|
||||||
anyLinks: notEmpty("links"),
|
anyLinks: notEmpty("links"),
|
||||||
|
|
||||||
|
@on("didInsertElement")
|
||||||
|
@observes("links.[]")
|
||||||
|
setupSortable() {
|
||||||
|
scheduleOnce("afterRender", () => this.applySortable());
|
||||||
|
},
|
||||||
|
|
||||||
|
applySortable() {
|
||||||
|
$(this.element)
|
||||||
|
.find(".link-list")
|
||||||
|
.sortable({ tolerance: "pointer" })
|
||||||
|
.on("sortupdate", (e, ui) => {
|
||||||
|
this.updateItemOrder(ui.item.data("id"), ui.item.index());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateItemOrder(itemId, newIndex) {
|
updateItemOrder(itemId, newIndex) {
|
||||||
const items = this.items;
|
const items = this.items;
|
||||||
const item = items.findBy("id", itemId);
|
const item = items.findBy("id", itemId);
|
||||||
items.removeObject(item);
|
items.removeObject(item);
|
||||||
item.set("index", newIndex);
|
|
||||||
items.insertAt(newIndex, item);
|
items.insertAt(newIndex, item);
|
||||||
|
scheduleOnce("afterRender", this, () => this.applySortable());
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("itemType")
|
@discourseComputed("itemType")
|
||||||
|
@ -37,7 +57,7 @@ export default Component.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.map((item, index) => {
|
return items.map((item) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
let link = {
|
let link = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
@ -56,32 +76,12 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
link.classes = classes;
|
link.classes = classes;
|
||||||
link.index = index;
|
|
||||||
|
|
||||||
if (index === 0) {
|
|
||||||
link.first = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index === items.length - 1) {
|
|
||||||
link.last = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
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: {
|
actions: {
|
||||||
add() {
|
add() {
|
||||||
const items = this.get("items");
|
const items = this.get("items");
|
||||||
|
@ -89,9 +89,23 @@ export default Component.extend({
|
||||||
let params = setWizardDefaults({}, itemType);
|
let params = setWizardDefaults({}, itemType);
|
||||||
|
|
||||||
params.isNew = true;
|
params.isNew = true;
|
||||||
params.index = this.getNextIndex();
|
|
||||||
|
|
||||||
let id = `${itemType}_${params.index + 1}`;
|
let next = 1;
|
||||||
|
|
||||||
|
if (items.length) {
|
||||||
|
next =
|
||||||
|
Math.max.apply(
|
||||||
|
Math,
|
||||||
|
items.map((i) => {
|
||||||
|
let parts = i.id.split("_");
|
||||||
|
let lastPart = parts[parts.length - 1];
|
||||||
|
return isNaN(lastPart) ? 0 : lastPart;
|
||||||
|
})
|
||||||
|
) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = `${itemType}_${next}`;
|
||||||
|
|
||||||
if (itemType === "field") {
|
if (itemType === "field") {
|
||||||
id = `${this.parentId}_${id}`;
|
id = `${this.parentId}_${id}`;
|
||||||
}
|
}
|
||||||
|
@ -111,14 +125,6 @@ export default Component.extend({
|
||||||
this.set("current", newItem);
|
this.set("current", newItem);
|
||||||
},
|
},
|
||||||
|
|
||||||
back(item) {
|
|
||||||
this.updateItemOrder(item.id, item.index - 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
forward(item) {
|
|
||||||
this.updateItemOrder(item.id, item.index + 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
change(itemId) {
|
change(itemId) {
|
||||||
this.set("current", this.items.findBy("id", itemId));
|
this.set("current", this.items.findBy("id", itemId));
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,28 +6,12 @@ import {
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
||||||
import {
|
import { generateName, snakeCase, userProperties } from "../lib/wizard";
|
||||||
generateName,
|
|
||||||
sentenceCase,
|
|
||||||
snakeCase,
|
|
||||||
userProperties,
|
|
||||||
} from "../lib/wizard";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { bind, later } from "@ember/runloop";
|
import { bind, later } from "@ember/runloop";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import Subscription from "../mixins/subscription";
|
|
||||||
|
|
||||||
const customFieldActionMap = {
|
export default Component.extend({
|
||||||
topic: ["create_topic", "send_message"],
|
|
||||||
post: ["create_topic", "send_message"],
|
|
||||||
category: ["create_category"],
|
|
||||||
group: ["create_group"],
|
|
||||||
user: ["update_profile"],
|
|
||||||
};
|
|
||||||
|
|
||||||
const values = ["present", "true", "false"];
|
|
||||||
|
|
||||||
export default Component.extend(Subscription, {
|
|
||||||
classNameBindings: [":mapper-selector", "activeType"],
|
classNameBindings: [":mapper-selector", "activeType"],
|
||||||
|
|
||||||
showText: computed("activeType", function () {
|
showText: computed("activeType", function () {
|
||||||
|
@ -63,9 +47,6 @@ export default Component.extend(Subscription, {
|
||||||
showCustomField: computed("activeType", function () {
|
showCustomField: computed("activeType", function () {
|
||||||
return this.showInput("customField");
|
return this.showInput("customField");
|
||||||
}),
|
}),
|
||||||
showValue: computed("activeType", function () {
|
|
||||||
return this.showInput("value");
|
|
||||||
}),
|
|
||||||
textEnabled: computed("options.textSelection", "inputType", function () {
|
textEnabled: computed("options.textSelection", "inputType", function () {
|
||||||
return this.optionEnabled("textSelection");
|
return this.optionEnabled("textSelection");
|
||||||
}),
|
}),
|
||||||
|
@ -117,50 +98,21 @@ export default Component.extend(Subscription, {
|
||||||
groupEnabled: computed("options.groupSelection", "inputType", function () {
|
groupEnabled: computed("options.groupSelection", "inputType", function () {
|
||||||
return this.optionEnabled("groupSelection");
|
return this.optionEnabled("groupSelection");
|
||||||
}),
|
}),
|
||||||
guestGroup: computed("options.guestGroup", "inputType", function () {
|
|
||||||
return this.optionEnabled("guestGroup");
|
|
||||||
}),
|
|
||||||
userEnabled: computed("options.userSelection", "inputType", function () {
|
userEnabled: computed("options.userSelection", "inputType", function () {
|
||||||
return this.optionEnabled("userSelection");
|
return this.optionEnabled("userSelection");
|
||||||
}),
|
}),
|
||||||
listEnabled: computed("options.listSelection", "inputType", function () {
|
listEnabled: computed("options.listSelection", "inputType", function () {
|
||||||
return this.optionEnabled("listSelection");
|
return this.optionEnabled("listSelection");
|
||||||
}),
|
}),
|
||||||
valueEnabled: computed("connector", function () {
|
|
||||||
return this.connector === "is";
|
|
||||||
}),
|
|
||||||
|
|
||||||
@discourseComputed("site.groups", "guestGroup", "subscriptionType")
|
groups: alias("site.groups"),
|
||||||
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"),
|
categories: alias("site.categories"),
|
||||||
showComboBox: or(
|
showComboBox: or(
|
||||||
"showWizardField",
|
"showWizardField",
|
||||||
"showWizardAction",
|
"showWizardAction",
|
||||||
"showUserField",
|
"showUserField",
|
||||||
"showUserFieldOptions",
|
"showUserFieldOptions",
|
||||||
"showCustomField",
|
"showCustomField"
|
||||||
"showValue"
|
|
||||||
),
|
),
|
||||||
showMultiSelect: or("showCategory", "showGroup"),
|
showMultiSelect: or("showCategory", "showGroup"),
|
||||||
hasTypes: gt("selectorTypes.length", 1),
|
hasTypes: gt("selectorTypes.length", 1),
|
||||||
|
@ -192,7 +144,7 @@ export default Component.extend(Subscription, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("connector")
|
@discourseComputed
|
||||||
selectorTypes() {
|
selectorTypes() {
|
||||||
return selectionTypes
|
return selectionTypes
|
||||||
.filter((type) => this[`${type}Enabled`])
|
.filter((type) => this[`${type}Enabled`])
|
||||||
|
@ -236,19 +188,11 @@ export default Component.extend(Subscription, {
|
||||||
customFields
|
customFields
|
||||||
) {
|
) {
|
||||||
let content;
|
let content;
|
||||||
let context;
|
|
||||||
let contextType;
|
|
||||||
|
|
||||||
if (this.options.context) {
|
|
||||||
let contextAttrs = this.options.context.split(".");
|
|
||||||
context = contextAttrs[0];
|
|
||||||
contextType = contextAttrs[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeType === "wizardField") {
|
if (activeType === "wizardField") {
|
||||||
content = wizardFields;
|
content = wizardFields;
|
||||||
|
|
||||||
if (context === "field") {
|
if (this.options.context === "field") {
|
||||||
content = content.filter((field) => field.id !== currentFieldId);
|
content = content.filter((field) => field.id !== currentFieldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,7 +204,7 @@ export default Component.extend(Subscription, {
|
||||||
type: a.type,
|
type: a.type,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (context === "action") {
|
if (this.options.context === "action") {
|
||||||
content = content.filter((a) => a.id !== currentActionId);
|
content = content.filter((a) => a.id !== currentActionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +218,7 @@ export default Component.extend(Subscription, {
|
||||||
.concat(userFields || []);
|
.concat(userFields || []);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
context === "action" &&
|
this.options.context === "action" &&
|
||||||
this.inputType === "association" &&
|
this.inputType === "association" &&
|
||||||
this.selectorType === "key"
|
this.selectorType === "key"
|
||||||
) {
|
) {
|
||||||
|
@ -290,24 +234,7 @@ export default Component.extend(Subscription, {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeType === "customField") {
|
if (activeType === "customField") {
|
||||||
content = customFields
|
content = customFields;
|
||||||
.filter((f) => {
|
|
||||||
return (
|
|
||||||
f.type !== "json" &&
|
|
||||||
customFieldActionMap[f.klass].includes(contextType)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((f) => ({
|
|
||||||
id: f.name,
|
|
||||||
name: `${sentenceCase(f.klass)} ${f.name} (${f.type})`,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeType === "value") {
|
|
||||||
content = values.map((value) => ({
|
|
||||||
id: value,
|
|
||||||
name: value,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
|
@ -379,7 +306,7 @@ export default Component.extend(Subscription, {
|
||||||
resetActiveType() {
|
resetActiveType() {
|
||||||
this.set(
|
this.set(
|
||||||
"activeType",
|
"activeType",
|
||||||
defaultSelectionType(this.selectorType, this.options, this.connector)
|
defaultSelectionType(this.selectorType, this.options)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -403,7 +330,7 @@ export default Component.extend(Subscription, {
|
||||||
this.changeValue(event.target.value);
|
this.changeValue(event.target.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeUserValue(value) {
|
changeUserValue(previousValue, value) {
|
||||||
this.changeValue(value);
|
this.changeValue(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,7 +32,6 @@ export default Component.extend({
|
||||||
pairConnector: options.pairConnector || null,
|
pairConnector: options.pairConnector || null,
|
||||||
outputConnector: options.outputConnector || null,
|
outputConnector: options.outputConnector || null,
|
||||||
context: options.context || null,
|
context: options.context || null,
|
||||||
guestGroup: options.guestGroup || false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let inputTypes = ["key", "value", "output"];
|
let inputTypes = ["key", "value", "output"];
|
||||||
|
|
|
@ -6,7 +6,6 @@ import I18n from "I18n";
|
||||||
const icons = {
|
const icons = {
|
||||||
error: "times-circle",
|
error: "times-circle",
|
||||||
success: "check-circle",
|
success: "check-circle",
|
||||||
warn: "exclamation-circle",
|
|
||||||
info: "info-circle",
|
info: "info-circle",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: ["realtime-validations", "setting", "full", "subscription"],
|
classNames: ["realtime-validations"],
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
timeUnits() {
|
timeUnits() {
|
||||||
return ["days", "weeks", "months", "years"].map((unit) => {
|
return ["days", "weeks", "months", "years"].map((unit) => {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import Subscription from "../mixins/subscription";
|
|
||||||
import DiscourseURL from "discourse/lib/url";
|
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
export default Component.extend(Subscription, {
|
|
||||||
tagName: "a",
|
|
||||||
classNameBindings: [":wizard-subscription-badge", "subscriptionType"],
|
|
||||||
attributeBindings: ["title"],
|
|
||||||
|
|
||||||
@discourseComputed("subscriptionType")
|
|
||||||
i18nKey(type) {
|
|
||||||
return `admin.wizard.subscription.type.${type ? type : "none"}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey")
|
|
||||||
title(i18nKey) {
|
|
||||||
return I18n.t(`${i18nKey}.title`);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey")
|
|
||||||
label(i18nKey) {
|
|
||||||
return I18n.t(`${i18nKey}.label`);
|
|
||||||
},
|
|
||||||
|
|
||||||
click() {
|
|
||||||
DiscourseURL.routeTo(this.subscriptionLink);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,26 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import Subscription from "../mixins/subscription";
|
|
||||||
|
|
||||||
export default Component.extend(Subscription, {
|
|
||||||
classNameBindings: [":wizard-subscription-container", "subscribed"],
|
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
|
||||||
subscribedIcon(subscribed) {
|
|
||||||
return subscribed ? "check" : "times";
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
|
||||||
subscribedLabel(subscribed) {
|
|
||||||
return `admin.wizard.subscription.${
|
|
||||||
subscribed ? "subscribed" : "not_subscribed"
|
|
||||||
}.label`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
|
||||||
subscribedTitle(subscribed) {
|
|
||||||
return `admin.wizard.subscription.${
|
|
||||||
subscribed ? "subscribed" : "not_subscribed"
|
|
||||||
}.title`;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,36 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import Subscription from "../mixins/subscription";
|
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
export default Component.extend(Subscription, {
|
|
||||||
tagName: "a",
|
|
||||||
classNameBindings: [":btn", ":btn-pavilion-support", "subscriptionType"],
|
|
||||||
attributeBindings: ["title"],
|
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
|
||||||
i18nKey(subscribed) {
|
|
||||||
return `admin.wizard.subscription.cta.${
|
|
||||||
subscribed ? "subscribed" : "none"
|
|
||||||
}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
|
||||||
icon(subscribed) {
|
|
||||||
return subscribed ? "far-life-ring" : "external-link-alt";
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey")
|
|
||||||
title(i18nKey) {
|
|
||||||
return I18n.t(`${i18nKey}.title`);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey")
|
|
||||||
label(i18nKey) {
|
|
||||||
return I18n.t(`${i18nKey}.label`);
|
|
||||||
},
|
|
||||||
|
|
||||||
click() {
|
|
||||||
window.open(this.subscriptionCtaLink, "_blank").focus();
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,96 +0,0 @@
|
||||||
import SingleSelectComponent from "select-kit/components/single-select";
|
|
||||||
import Subscription from "../mixins/subscription";
|
|
||||||
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(Subscription, {
|
|
||||||
classNames: ["combo-box", "wizard-subscription-selector"],
|
|
||||||
|
|
||||||
selectKitOptions: {
|
|
||||||
autoFilterable: false,
|
|
||||||
filterable: false,
|
|
||||||
showFullTitle: true,
|
|
||||||
headerComponent:
|
|
||||||
"wizard-subscription-selector/wizard-subscription-selector-header",
|
|
||||||
caretUpIcon: "caret-up",
|
|
||||||
caretDownIcon: "caret-down",
|
|
||||||
},
|
|
||||||
|
|
||||||
allowedSubscriptionTypes(feature, attribute, value) {
|
|
||||||
let attributes = this.subscriptionAttributes[feature];
|
|
||||||
if (!attributes || !attributes[attribute]) {
|
|
||||||
return ["none"];
|
|
||||||
}
|
|
||||||
let allowedTypes = [];
|
|
||||||
Object.keys(attributes[attribute]).forEach((subscriptionType) => {
|
|
||||||
let values = attributes[attribute][subscriptionType];
|
|
||||||
if (values[0] === "*" || values.includes(value)) {
|
|
||||||
allowedTypes.push(subscriptionType);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return allowedTypes;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("feature", "attribute", "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.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";
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
});
|
|
|
@ -1,20 +0,0 @@
|
||||||
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;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,139 +0,0 @@
|
||||||
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}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -5,7 +5,11 @@ import { scheduleOnce } from "@ember/runloop";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
const excludedUserProperties = ["profile_background", "card_background"];
|
const excludedUserProperties = [
|
||||||
|
"avatar",
|
||||||
|
"profile_background",
|
||||||
|
"card_background",
|
||||||
|
];
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: "wizard-text-editor",
|
classNames: "wizard-text-editor",
|
||||||
|
@ -48,12 +52,12 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed("wizardFields")
|
@discourseComputed("wizardFields")
|
||||||
wizardFieldList(wizardFields) {
|
wizardFieldList(wizardFields) {
|
||||||
return (wizardFields || []).map((f) => ` w{${f.id}}`);
|
return wizardFields.map((f) => ` w{${f.id}}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("wizardActions")
|
@discourseComputed("wizardActions")
|
||||||
wizardActionList(wizardActions) {
|
wizardActionList(wizardActions) {
|
||||||
return (wizardActions || []).map((a) => ` w{${a.id}}`);
|
return wizardActions.map((a) => ` w{${a.id}}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
{{#if currentUser.admin}}
|
{{#if currentUser.admin}}
|
||||||
{{nav-item route="adminWizards" label="admin.wizard.nav_label"}}
|
{{nav-item route="adminWizards" label="admin.wizard.nav_label"}}
|
||||||
|
|
||||||
{{#if wizardErrorNotice}}
|
|
||||||
{{d-icon "exclaimation-circle"}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -1,15 +0,0 @@
|
||||||
<h3>{{i18n "admin.wizard.category_settings.custom_wizard.title"}}</h3>
|
|
||||||
|
|
||||||
<section class="field new-topic-wizard">
|
|
||||||
<label for="new-topic-wizard">
|
|
||||||
{{i18n "admin.wizard.category_settings.custom_wizard.create_topic_wizard"}}
|
|
||||||
</label>
|
|
||||||
<div class="controls">
|
|
||||||
{{combo-box
|
|
||||||
value=wizardListVal
|
|
||||||
content=wizardList
|
|
||||||
onChange=(action "changeWizard")
|
|
||||||
options=(hash none="admin.wizard.select")
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -1,24 +0,0 @@
|
||||||
import CustomWizardAdmin from "../../models/custom-wizard-admin";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
setupComponent(attrs, component) {
|
|
||||||
CustomWizardAdmin.all()
|
|
||||||
.then((result) => {
|
|
||||||
component.set("wizardList", result);
|
|
||||||
})
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
|
|
||||||
component.set(
|
|
||||||
"wizardListVal",
|
|
||||||
attrs?.category?.custom_fields?.create_topic_wizard
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
changeWizard(wizard) {
|
|
||||||
this.set("wizardListVal", wizard);
|
|
||||||
this.set("category.custom_fields.create_topic_wizard", wizard);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,10 +1,7 @@
|
||||||
{{#each site.complete_custom_wizard as |wizard|}}
|
{{#each site.complete_custom_wizard as |wizard|}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="alert alert-info alert-wizard">
|
<div class="alert alert-info alert-wizard">
|
||||||
<a href={{wizard.url}}>{{i18n
|
<a href={{wizard.url}}>{{i18n "wizard.complete_custom" name=wizard.name}}</a>
|
||||||
"wizard.complete_custom"
|
|
||||||
name=wizard.name
|
|
||||||
}}</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
|
@ -4,18 +4,14 @@ import CustomWizardApi from "../models/custom-wizard-api";
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||||
import { and, equal, not } from "@ember/object/computed";
|
import { and, equal, not } from "@ember/object/computed";
|
||||||
import { selectKitContent } from "../lib/wizard";
|
import { selectKitContent } from "../lib/wizard";
|
||||||
import { underscore } from "@ember/string";
|
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
router: service(),
|
|
||||||
|
|
||||||
queryParams: ["refresh_list"],
|
queryParams: ["refresh_list"],
|
||||||
loadingSubscriptions: false,
|
loadingSubscriptions: false,
|
||||||
notAuthorized: not("api.authorized"),
|
notAuthorized: not("api.authorized"),
|
||||||
endpointMethods: selectKitContent(["PUT", "POST", "PATCH", "DELETE"]),
|
endpointMethods: selectKitContent(["GET", "PUT", "POST", "PATCH", "DELETE"]),
|
||||||
showRemove: not("isNew"),
|
showRemove: not("isNew"),
|
||||||
showRedirectUri: and("threeLeggedOauth", "api.name"),
|
showRedirectUri: and("threeLeggedOauth", "api.name"),
|
||||||
responseIcon: null,
|
responseIcon: null,
|
||||||
|
@ -24,8 +20,29 @@ export default Controller.extend({
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
]),
|
]),
|
||||||
successCodes: selectKitContent([
|
successCodes: selectKitContent([
|
||||||
100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301,
|
100,
|
||||||
302, 303, 303, 304, 305, 306, 307, 308,
|
101,
|
||||||
|
102,
|
||||||
|
200,
|
||||||
|
201,
|
||||||
|
202,
|
||||||
|
203,
|
||||||
|
204,
|
||||||
|
205,
|
||||||
|
206,
|
||||||
|
207,
|
||||||
|
208,
|
||||||
|
226,
|
||||||
|
300,
|
||||||
|
301,
|
||||||
|
302,
|
||||||
|
303,
|
||||||
|
303,
|
||||||
|
304,
|
||||||
|
305,
|
||||||
|
306,
|
||||||
|
307,
|
||||||
|
308,
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
|
@ -71,11 +88,6 @@ export default Controller.extend({
|
||||||
twoLeggedOauth: equal("api.authType", "oauth_2"),
|
twoLeggedOauth: equal("api.authType", "oauth_2"),
|
||||||
threeLeggedOauth: equal("api.authType", "oauth_3"),
|
threeLeggedOauth: equal("api.authType", "oauth_3"),
|
||||||
|
|
||||||
@discourseComputed("api.isNew")
|
|
||||||
nameClass(isNew) {
|
|
||||||
return isNew ? "new" : "saved";
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addParam() {
|
addParam() {
|
||||||
this.get("api.authParams").pushObject({});
|
this.get("api.authParams").pushObject({});
|
||||||
|
@ -101,7 +113,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
if (authType === "oauth_2") {
|
if (authType === "oauth_2") {
|
||||||
this.set("authorizing", true);
|
this.set("authorizing", true);
|
||||||
ajax(`/admin/wizards/apis/${underscore(name)}/authorize`)
|
ajax(`/admin/wizards/apis/${name.underscore()}/authorize`)
|
||||||
.catch(popupAjaxError)
|
.catch(popupAjaxError)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
@ -137,6 +149,7 @@ export default Controller.extend({
|
||||||
const api = this.get("api");
|
const api = this.get("api");
|
||||||
const name = api.name;
|
const name = api.name;
|
||||||
const authType = api.authType;
|
const authType = api.authType;
|
||||||
|
let refreshList = false; // eslint-disable-line
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
if (!name || !authType) {
|
if (!name || !authType) {
|
||||||
|
@ -151,6 +164,11 @@ export default Controller.extend({
|
||||||
data["title"] = api.title;
|
data["title"] = api.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const originalTitle = this.get("api.originalTitle");
|
||||||
|
if (api.get("isNew") || (originalTitle && api.title !== originalTitle)) {
|
||||||
|
refreshList = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (api.get("isNew")) {
|
if (api.get("isNew")) {
|
||||||
data["new"] = true;
|
data["new"] = true;
|
||||||
}
|
}
|
||||||
|
@ -170,11 +188,11 @@ export default Controller.extend({
|
||||||
if (!api[rp]) {
|
if (!api[rp]) {
|
||||||
let key = rp.replace("auth", "");
|
let key = rp.replace("auth", "");
|
||||||
error = `${I18n.t(
|
error = `${I18n.t(
|
||||||
`admin.wizard.api.auth.${underscore(key)}`
|
`admin.wizard.api.auth.${key.underscore()}`
|
||||||
)} is required for ${authType}`;
|
)} is required for ${authType}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data[underscore(rp)] = api[rp];
|
data[rp.underscore()] = api[rp];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +222,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
this.set("updating", true);
|
this.set("updating", true);
|
||||||
|
|
||||||
ajax(`/admin/wizards/api/${underscore(name)}`, {
|
ajax(`/admin/wizards/api/${name.underscore()}`, {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
@ -227,7 +245,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
this.set("updating", true);
|
this.set("updating", true);
|
||||||
|
|
||||||
ajax(`/admin/wizards/api/${underscore(name)}`, {
|
ajax(`/admin/wizards/api/${name.underscore()}`, {
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError)
|
.catch(popupAjaxError)
|
||||||
|
@ -245,13 +263,13 @@ export default Controller.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ajax(`/admin/wizards/api/${underscore(name)}/logs`, {
|
ajax(`/admin/wizards/api/${name.underscore()}/logs`, {
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError)
|
.catch(popupAjaxError)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.router.transitionTo("adminWizardsApis").then(() => {
|
this.transitionToRoute("adminWizardsApis").then(() => {
|
||||||
this.send("refreshModel");
|
this.send("refreshModel");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ import CustomWizardCustomField from "../models/custom-wizard-custom-field";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
messageKey: "create",
|
messageKey: "create",
|
||||||
fieldKeys: ["klass", "type", "name", "serializers"],
|
fieldKeys: ["klass", "type", "serializers", "name"],
|
||||||
documentationUrl: "https://discourse.pluginmanager.org/t/custom-fields",
|
documentationUrl: "https://thepavilion.io/t/3572",
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addField() {
|
addField() {
|
||||||
this.get("customFields").unshiftObject(
|
this.get("customFields").pushObject(
|
||||||
CustomWizardCustomField.create({ edit: true })
|
CustomWizardCustomField.create({ edit: true })
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
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();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,34 +1,50 @@
|
||||||
|
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 Controller from "@ember/controller";
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
documentationUrl: "https://thepavilion.io/t/2818",
|
refreshing: false,
|
||||||
|
hasLogs: notEmpty("logs"),
|
||||||
|
page: 0,
|
||||||
|
canLoadMore: true,
|
||||||
|
logs: [],
|
||||||
|
|
||||||
@discourseComputed("wizardId")
|
loadLogs() {
|
||||||
wizardName(wizardId) {
|
if (!this.canLoadMore) {
|
||||||
let currentWizard = this.wizardList.find(
|
return;
|
||||||
(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;
|
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();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ import I18n from "I18n";
|
||||||
import { underscore } from "@ember/string";
|
import { underscore } from "@ember/string";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
messageUrl: "https://discourse.pluginmanager.org/t/wizard-manager",
|
messageUrl: "https://thepavilion.io/t/3652",
|
||||||
messageKey: "info",
|
messageKey: "info",
|
||||||
messageIcon: "info-circle",
|
messageIcon: "info-circle",
|
||||||
messageClass: "info",
|
messageClass: "info",
|
||||||
|
@ -68,7 +68,7 @@ export default Controller.extend({
|
||||||
file: null,
|
file: null,
|
||||||
filename: null,
|
filename: null,
|
||||||
});
|
});
|
||||||
document.getElementById("custom-wizard-file-upload").value = "";
|
$("#file-upload").val("");
|
||||||
},
|
},
|
||||||
|
|
||||||
@observes("importing", "destroying")
|
@observes("importing", "destroying")
|
||||||
|
@ -83,7 +83,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
upload() {
|
upload() {
|
||||||
document.getElementById("custom-wizard-file-upload").click();
|
$("#file-upload").click();
|
||||||
},
|
},
|
||||||
|
|
||||||
clearFile() {
|
clearFile() {
|
||||||
|
@ -102,7 +102,7 @@ export default Controller.extend({
|
||||||
if (maxFileSize < file.size) {
|
if (maxFileSize < file.size) {
|
||||||
this.setMessage("error", "file_size_error");
|
this.setMessage("error", "file_size_error");
|
||||||
this.set("file", null);
|
this.set("file", null);
|
||||||
document.getElementById("custom-wizard-file-upload").value = "";
|
$("#file-upload").val("");
|
||||||
} else {
|
} else {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
file,
|
file,
|
||||||
|
|
|
@ -1,74 +1,6 @@
|
||||||
import Controller from "@ember/controller";
|
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 { 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({
|
export default Controller.extend({
|
||||||
modal: service(),
|
|
||||||
downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"),
|
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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
documentationUrl: "https://thepavilion.io/t/2818",
|
|
||||||
|
|
||||||
@discourseComputed("wizardId")
|
|
||||||
wizardName(wizardId) {
|
|
||||||
let currentWizard = this.wizardList.find(
|
|
||||||
(wizard) => wizard.id === wizardId
|
|
||||||
);
|
|
||||||
if (currentWizard) {
|
|
||||||
return currentWizard.name;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("wizardName")
|
|
||||||
messageOpts(wizardName) {
|
|
||||||
return {
|
|
||||||
wizardName,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("wizardId")
|
|
||||||
messageKey(wizardId) {
|
|
||||||
let key = "select";
|
|
||||||
|
|
||||||
if (wizardId) {
|
|
||||||
key = "viewing";
|
|
||||||
}
|
|
||||||
|
|
||||||
return key;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -3,18 +3,15 @@ import {
|
||||||
observes,
|
observes,
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
import { notEmpty } from "@ember/object/computed";
|
import { notEmpty } from "@ember/object/computed";
|
||||||
import { inject as service } from "@ember/service";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import NextSessionScheduledModal from "../components/modal/next-session-scheduled";
|
|
||||||
import { generateId, wizardFieldList } from "../lib/wizard";
|
import { generateId, wizardFieldList } from "../lib/wizard";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
import { later, scheduleOnce } from "@ember/runloop";
|
import { later, scheduleOnce } from "@ember/runloop";
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import copyText from "discourse/lib/copy-text";
|
import copyText from "discourse/lib/copy-text";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { filterValues } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
modal: service(),
|
|
||||||
hasName: notEmpty("wizard.name"),
|
hasName: notEmpty("wizard.name"),
|
||||||
|
|
||||||
@observes("currentStep")
|
@observes("currentStep")
|
||||||
|
@ -39,8 +36,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
@discourseComputed("wizard.id")
|
@discourseComputed("wizard.id")
|
||||||
wizardUrl(wizardId) {
|
wizardUrl(wizardId) {
|
||||||
let baseUrl = window.location.href.split("/admin");
|
return window.location.origin + "/w/" + dasherize(wizardId);
|
||||||
return baseUrl[0] + "/w/" + dasherize(wizardId);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("wizard.after_time_scheduled")
|
@discourseComputed("wizard.after_time_scheduled")
|
||||||
|
@ -63,34 +59,6 @@ export default Controller.extend({
|
||||||
return wizardFieldList(steps);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorType = "failed";
|
|
||||||
let errorParams = {};
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
errorType = result.error.type;
|
|
||||||
errorParams = result.error.params;
|
|
||||||
}
|
|
||||||
|
|
||||||
return I18n.t(`admin.wizard.error.${errorType}`, errorParams);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -109,14 +77,21 @@ export default Controller.extend({
|
||||||
wizard
|
wizard
|
||||||
.save(opts)
|
.save(opts)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.wizard_id) {
|
|
||||||
this.send("afterSave", result.wizard_id);
|
this.send("afterSave", result.wizard_id);
|
||||||
} else if (result.errors) {
|
|
||||||
this.set("error", result.errors.join(", "));
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((result) => {
|
.catch((result) => {
|
||||||
this.set("error", this.getErrorMessage(result));
|
let errorType = "failed";
|
||||||
|
let errorParams = {};
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
errorType = result.error.type;
|
||||||
|
errorParams = result.error.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set(
|
||||||
|
"error",
|
||||||
|
I18n.t(`admin.wizard.error.${errorType}`, errorParams)
|
||||||
|
);
|
||||||
|
|
||||||
later(() => this.set("error", null), 10000);
|
later(() => this.set("error", null), 10000);
|
||||||
})
|
})
|
||||||
|
@ -128,13 +103,19 @@ export default Controller.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setNextSessionScheduled() {
|
setNextSessionScheduled() {
|
||||||
this.modal.show(NextSessionScheduledModal, {
|
let controller = showModal("next-session-scheduled", {
|
||||||
model: {
|
model: {
|
||||||
dateTime: this.wizard.after_time_scheduled,
|
dateTime: this.wizard.after_time_scheduled,
|
||||||
update: (dateTime) =>
|
update: (dateTime) =>
|
||||||
this.set("wizard.after_time_scheduled", dateTime),
|
this.set("wizard.after_time_scheduled", dateTime),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
controller.setup();
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleAdvanced() {
|
||||||
|
this.toggleProperty("wizard.showAdvanced");
|
||||||
},
|
},
|
||||||
|
|
||||||
copyUrl() {
|
copyUrl() {
|
||||||
|
|
|
@ -21,6 +21,5 @@ export default Controller.extend({
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
|
|
||||||
messageUrl:
|
messageUrl: "https://thepavilion.io/c/knowledge/discourse/custom-wizard",
|
||||||
"https://discourse.pluginmanager.org/c/discourse-custom-wizard/documentation",
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { equal, or } from "@ember/object/computed";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
businessSubscription: equal("subscriptionType", "business"),
|
|
||||||
communitySubscription: equal("subscriptionType", "community"),
|
|
||||||
standardSubscription: equal("subscriptionType", "standard"),
|
|
||||||
showApi: or("businessSubscription", "communitySubscription"),
|
|
||||||
});
|
|
|
@ -1,24 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { or } from "@ember/object/computed";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
const reasons = {
|
|
||||||
noWizard: "none",
|
|
||||||
requiresLogin: "requires_login",
|
|
||||||
notPermitted: "not_permitted",
|
|
||||||
completed: "completed",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
noAccess: or("noWizard", "requiresLogin", "notPermitted", "completed"),
|
|
||||||
|
|
||||||
@discourseComputed("noAccessReason")
|
|
||||||
noAccessI18nKey(reason) {
|
|
||||||
return reason ? `wizard.${reasons[reason]}` : "wizard.none";
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
noAccessReason() {
|
|
||||||
return Object.keys(reasons).find((reason) => this.get(reason));
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
queryParams: ["reset"],
|
|
||||||
});
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -43,16 +43,7 @@ export default {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.route(
|
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
|
||||||
"adminWizardsLogs",
|
|
||||||
{ path: "/logs", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route("adminWizardsLogsShow", {
|
|
||||||
path: "/:wizardId/",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.route("adminWizardsManager", {
|
this.route("adminWizardsManager", {
|
||||||
path: "/manager",
|
path: "/manager",
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
export default function () {
|
|
||||||
this.route(
|
|
||||||
"customWizard",
|
|
||||||
{ path: "/w/:wizard_id", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route("customWizardStep", {
|
|
||||||
path: "/steps/:step_id",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
6
assets/javascripts/discourse/helpers/custom-wizard.js.es6
Normale Datei
6
assets/javascripts/discourse/helpers/custom-wizard.js.es6
Normale Datei
|
@ -0,0 +1,6 @@
|
||||||
|
import { registerUnbound } from "discourse-common/lib/helpers";
|
||||||
|
import { dasherize } from "@ember/string";
|
||||||
|
|
||||||
|
registerUnbound("dasherize", function (string) {
|
||||||
|
return dasherize(string);
|
||||||
|
});
|
|
@ -1,25 +0,0 @@
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import I18n from "I18n";
|
|
||||||
import Handlebars from "handlebars";
|
|
||||||
|
|
||||||
export default registerUnbound(
|
|
||||||
"wizard-char-counter",
|
|
||||||
function (body, maxLength) {
|
|
||||||
let bodyLength = body ? body.length : 0;
|
|
||||||
let finalString;
|
|
||||||
|
|
||||||
if (maxLength) {
|
|
||||||
let isOverMax = bodyLength > maxLength ? "true" : "false";
|
|
||||||
finalString = `<div class="body-length" data-length=${bodyLength} data-over-max=${isOverMax}>${bodyLength} / ${I18n.t(
|
|
||||||
"wizard.x_characters",
|
|
||||||
{ count: parseInt(maxLength, 10) }
|
|
||||||
)}</div>`;
|
|
||||||
} else {
|
|
||||||
finalString = `<div class="body-length">${I18n.t("wizard.x_characters", {
|
|
||||||
count: parseInt(bodyLength, 10),
|
|
||||||
})}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Handlebars.SafeString(finalString);
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,12 +1,9 @@
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
|
||||||
import getUrl from "discourse-common/lib/get-url";
|
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "custom-wizard-edits",
|
name: "custom-wizard-edits",
|
||||||
initialize(container) {
|
initialize(container) {
|
||||||
const siteSettings = container.lookup("service:site-settings");
|
const siteSettings = container.lookup("site-settings:main");
|
||||||
|
|
||||||
if (!siteSettings.custom_wizard_enabled) {
|
if (!siteSettings.custom_wizard_enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -19,80 +16,5 @@ export default {
|
||||||
}
|
}
|
||||||
return existing.apply(this, [path, opts]);
|
return existing.apply(this, [path, opts]);
|
||||||
};
|
};
|
||||||
|
|
||||||
withPluginApi("0.8.36", (api) => {
|
|
||||||
api.modifyClass("component:d-navigation", {
|
|
||||||
pluginId: "custom-wizard",
|
|
||||||
actions: {
|
|
||||||
clickCreateTopicButton() {
|
|
||||||
let createTopicWizard = this.get(
|
|
||||||
"category.custom_fields.create_topic_wizard"
|
|
||||||
);
|
|
||||||
if (createTopicWizard) {
|
|
||||||
window.location.href = getUrl(`/w/${createTopicWizard}`);
|
|
||||||
} else {
|
|
||||||
this._super();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
api.modifyClass("component:uppy-image-uploader", {
|
|
||||||
pluginId: "custom-wizard",
|
|
||||||
// Needed to ensure appEvents get registered when navigating between steps
|
|
||||||
@observes("id")
|
|
||||||
initOnStepChange() {
|
|
||||||
if (/wizard-field|wizard-step/.test(this.id)) {
|
|
||||||
this._initialize();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
api.modifyClass("component:d-editor", {
|
|
||||||
pluginId: "custom-wizard",
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
if (this.wizardComposer) {
|
|
||||||
this.appEvents.on(
|
|
||||||
`wizard-editor:insert-text`,
|
|
||||||
this,
|
|
||||||
"_wizardInsertText"
|
|
||||||
);
|
|
||||||
this.appEvents.on(
|
|
||||||
"wizard-editor:replace-text",
|
|
||||||
this,
|
|
||||||
"_wizardReplaceText"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_wizardInsertText(text, options) {
|
|
||||||
if (
|
|
||||||
this.session.wizardEventFieldId === this.fieldId &&
|
|
||||||
this.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", {
|
|
||||||
categoriesByScope(options = {}) {
|
|
||||||
let categories = this._super(options);
|
|
||||||
|
|
||||||
return categories.filter((category) => {
|
|
||||||
return !category.custom_fields?.create_topic_wizard;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import ApplicationRoute from "discourse/routes/application";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "custom-wizard-redirect",
|
name: "custom-wizard-redirect",
|
||||||
after: "message-bus",
|
after: "message-bus",
|
||||||
|
|
||||||
initialize(container) {
|
initialize: function (container) {
|
||||||
const messageBus = container.lookup("service:message-bus");
|
const messageBus = container.lookup("message-bus:main");
|
||||||
const siteSettings = container.lookup("service:site-settings");
|
const siteSettings = container.lookup("site-settings:main");
|
||||||
|
|
||||||
if (!siteSettings.custom_wizard_enabled) {
|
if (!siteSettings.custom_wizard_enabled || !messageBus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,27 +17,28 @@ export default {
|
||||||
window.location.href = wizardUrl;
|
window.location.href = wizardUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
withPluginApi("0.8.36", (api) => {
|
ApplicationRoute.reopen({
|
||||||
api.onAppEvent("page:changed", (data) => {
|
actions: {
|
||||||
const currentUser = api.getCurrentUser();
|
willTransition(transition) {
|
||||||
|
const redirectToWizard = this.get("currentUser.redirect_to_wizard");
|
||||||
if (currentUser) {
|
const excludedPaths = Discourse.SiteSettings.wizard_redirect_exclude_paths
|
||||||
const redirectToWizard = currentUser.redirect_to_wizard;
|
|
||||||
const excludedPaths = siteSettings.wizard_redirect_exclude_paths
|
|
||||||
.split("|")
|
.split("|")
|
||||||
.concat(["loading"]);
|
.concat(["loading"]);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
redirectToWizard &&
|
redirectToWizard &&
|
||||||
!data.url.includes("ignore_redirect") &&
|
(!transition.intent.name ||
|
||||||
data.currentRouteName !== "customWizardStep" &&
|
|
||||||
!excludedPaths.find((p) => {
|
!excludedPaths.find((p) => {
|
||||||
return data.currentRouteName.indexOf(p) > -1;
|
return transition.intent.name.indexOf(p) > -1;
|
||||||
})
|
}))
|
||||||
) {
|
) {
|
||||||
DiscourseURL.routeTo(`/w/${dasherize(redirectToWizard)}`);
|
transition.abort();
|
||||||
|
window.location = "/w/" + redirectToWizard.dasherize();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
return this._super(transition);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,10 @@ function castCase(property, value) {
|
||||||
return property.indexOf("_type") > -1 ? camelCase(value) : value;
|
return property.indexOf("_type") > -1 ? camelCase(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMappedProperty(value) {
|
function buildProperty(json, property, type) {
|
||||||
|
let value = json[property];
|
||||||
|
|
||||||
|
if (mapped(property, type) && present(value) && value.constructor === Array) {
|
||||||
let inputs = [];
|
let inputs = [];
|
||||||
|
|
||||||
value.forEach((inputJson) => {
|
value.forEach((inputJson) => {
|
||||||
|
@ -56,36 +59,18 @@ function buildMappedProperty(value) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return A(inputs);
|
return A(inputs);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
function buildProperty(json, property, type, objectIndex) {
|
|
||||||
let value = json[property];
|
|
||||||
if (
|
|
||||||
property === "index" &&
|
|
||||||
(value === null || value === undefined) &&
|
|
||||||
(objectIndex !== null || objectIndex !== undefined)
|
|
||||||
) {
|
|
||||||
return objectIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!mapped(property, type) ||
|
|
||||||
!present(value) ||
|
|
||||||
!value.constructor === Array
|
|
||||||
) {
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildMappedProperty(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildObject(json, type, objectIndex) {
|
function buildObject(json, type) {
|
||||||
let props = {
|
let props = {
|
||||||
isNew: false,
|
isNew: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(json).forEach((prop) => {
|
Object.keys(json).forEach((prop) => {
|
||||||
props[prop] = buildProperty(json, prop, type, objectIndex);
|
props[prop] = buildProperty(json, prop, type);
|
||||||
});
|
});
|
||||||
|
|
||||||
return EmberObject.create(props);
|
return EmberObject.create(props);
|
||||||
|
@ -95,8 +80,13 @@ function buildObjectArray(json, type) {
|
||||||
let array = A();
|
let array = A();
|
||||||
|
|
||||||
if (present(json)) {
|
if (present(json)) {
|
||||||
json.forEach((objJson, objectIndex) => {
|
json.forEach((objJson) => {
|
||||||
let object = buildObject(objJson, type, objectIndex);
|
let object = buildObject(objJson, type);
|
||||||
|
|
||||||
|
if (hasAdvancedProperties(object, type)) {
|
||||||
|
object.set("showAdvanced", true);
|
||||||
|
}
|
||||||
|
|
||||||
array.pushObject(object);
|
array.pushObject(object);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -104,14 +94,24 @@ function buildObjectArray(json, type) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildBasicProperties(json, type, props, objectIndex = null) {
|
function buildBasicProperties(json, type, props) {
|
||||||
listProperties(type).forEach((p) => {
|
listProperties(type).forEach((p) => {
|
||||||
props[p] = buildProperty(json, p, type, objectIndex);
|
props[p] = buildProperty(json, p, type);
|
||||||
|
|
||||||
|
if (hasAdvancedProperties(json, type)) {
|
||||||
|
props.showAdvanced = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return props;
|
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
|
/// to be removed: necessary due to action array being moved from step to wizard
|
||||||
function actionPatch(json) {
|
function actionPatch(json) {
|
||||||
let actions = json.actions || [];
|
let actions = json.actions || [];
|
||||||
|
@ -142,17 +142,12 @@ function buildProperties(json) {
|
||||||
props = buildBasicProperties(json, "wizard", props);
|
props = buildBasicProperties(json, "wizard", props);
|
||||||
|
|
||||||
if (present(json.steps)) {
|
if (present(json.steps)) {
|
||||||
json.steps.forEach((stepJson, objectIndex) => {
|
json.steps.forEach((stepJson) => {
|
||||||
let stepProps = {
|
let stepProps = {
|
||||||
isNew: false,
|
isNew: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
stepProps = buildBasicProperties(
|
stepProps = buildBasicProperties(stepJson, "step", stepProps);
|
||||||
stepJson,
|
|
||||||
"step",
|
|
||||||
stepProps,
|
|
||||||
objectIndex
|
|
||||||
);
|
|
||||||
stepProps.fields = buildObjectArray(stepJson.fields, "field");
|
stepProps.fields = buildObjectArray(stepJson.fields, "field");
|
||||||
|
|
||||||
props.steps.pushObject(EmberObject.create(stepProps));
|
props.steps.pushObject(EmberObject.create(stepProps));
|
||||||
|
|
|
@ -35,7 +35,6 @@ function inputTypesContent(options = {}) {
|
||||||
const connectors = {
|
const connectors = {
|
||||||
pair: [
|
pair: [
|
||||||
"equal",
|
"equal",
|
||||||
"not_equal",
|
|
||||||
"greater",
|
"greater",
|
||||||
"less",
|
"less",
|
||||||
"greater_or_equal",
|
"greater_or_equal",
|
||||||
|
@ -106,18 +105,13 @@ const selectionTypes = [
|
||||||
"tag",
|
"tag",
|
||||||
"user",
|
"user",
|
||||||
"customField",
|
"customField",
|
||||||
"value",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function defaultSelectionType(inputType, options = {}, connector = null) {
|
function defaultSelectionType(inputType, options = {}) {
|
||||||
if (options[`${inputType}DefaultSelection`]) {
|
if (options[`${inputType}DefaultSelection`]) {
|
||||||
return options[`${inputType}DefaultSelection`];
|
return options[`${inputType}DefaultSelection`];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connector === "is") {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
let type = selectionTypes[0];
|
let type = selectionTypes[0];
|
||||||
|
|
||||||
for (let t of selectionTypes) {
|
for (let t of selectionTypes) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { get, set } from "@ember/object";
|
import { get, set } from "@ember/object";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
|
||||||
|
|
||||||
const wizard = {
|
const wizard = {
|
||||||
basic: {
|
basic: {
|
||||||
|
@ -14,11 +13,11 @@ const wizard = {
|
||||||
required: null,
|
required: null,
|
||||||
prompt_completion: null,
|
prompt_completion: null,
|
||||||
restart_on_revisit: null,
|
restart_on_revisit: null,
|
||||||
resume_on_revisit: null,
|
|
||||||
theme_id: null,
|
theme_id: null,
|
||||||
permitted: null,
|
permitted: null,
|
||||||
},
|
},
|
||||||
mapped: ["permitted"],
|
mapped: ["permitted"],
|
||||||
|
advanced: ["restart_on_revisit"],
|
||||||
required: ["id"],
|
required: ["id"],
|
||||||
dependent: {
|
dependent: {
|
||||||
after_time: "after_time_scheduled",
|
after_time: "after_time_scheduled",
|
||||||
|
@ -38,18 +37,16 @@ const wizard = {
|
||||||
const step = {
|
const step = {
|
||||||
basic: {
|
basic: {
|
||||||
id: null,
|
id: null,
|
||||||
index: null,
|
|
||||||
title: null,
|
title: null,
|
||||||
|
key: null,
|
||||||
banner: null,
|
banner: null,
|
||||||
banner_upload_id: null,
|
|
||||||
raw_description: null,
|
raw_description: null,
|
||||||
required_data: null,
|
required_data: null,
|
||||||
required_data_message: null,
|
required_data_message: null,
|
||||||
permitted_params: null,
|
permitted_params: null,
|
||||||
condition: null,
|
|
||||||
force_final: false,
|
|
||||||
},
|
},
|
||||||
mapped: ["required_data", "permitted_params", "condition", "index"],
|
mapped: ["required_data", "permitted_params"],
|
||||||
|
advanced: ["required_data", "permitted_params"],
|
||||||
required: ["id"],
|
required: ["id"],
|
||||||
dependent: {},
|
dependent: {},
|
||||||
objectArrays: {
|
objectArrays: {
|
||||||
|
@ -63,19 +60,16 @@ const step = {
|
||||||
const field = {
|
const field = {
|
||||||
basic: {
|
basic: {
|
||||||
id: null,
|
id: null,
|
||||||
index: null,
|
|
||||||
label: null,
|
label: null,
|
||||||
image: null,
|
image: null,
|
||||||
image_upload_id: null,
|
|
||||||
description: null,
|
description: null,
|
||||||
property: null,
|
|
||||||
required: null,
|
required: null,
|
||||||
|
key: null,
|
||||||
type: null,
|
type: null,
|
||||||
condition: null,
|
|
||||||
tag_groups: null,
|
|
||||||
},
|
},
|
||||||
types: {},
|
types: {},
|
||||||
mapped: ["prefill", "content", "condition", "index"],
|
mapped: ["prefill", "content"],
|
||||||
|
advanced: ["property", "key"],
|
||||||
required: ["id", "type"],
|
required: ["id", "type"],
|
||||||
dependent: {},
|
dependent: {},
|
||||||
objectArrays: {},
|
objectArrays: {},
|
||||||
|
@ -99,8 +93,6 @@ const action = {
|
||||||
custom_fields: null,
|
custom_fields: null,
|
||||||
skip_redirect: null,
|
skip_redirect: null,
|
||||||
suppress_notifications: null,
|
suppress_notifications: null,
|
||||||
add_event: null,
|
|
||||||
add_location: null,
|
|
||||||
},
|
},
|
||||||
send_message: {
|
send_message: {
|
||||||
title: null,
|
title: null,
|
||||||
|
@ -133,12 +125,6 @@ const action = {
|
||||||
wizard_user: true,
|
wizard_user: true,
|
||||||
usernames: null,
|
usernames: null,
|
||||||
},
|
},
|
||||||
watch_tags: {
|
|
||||||
tags: null,
|
|
||||||
notification_level: null,
|
|
||||||
wizard_user: true,
|
|
||||||
usernames: null,
|
|
||||||
},
|
|
||||||
send_to_api: {
|
send_to_api: {
|
||||||
api: null,
|
api: null,
|
||||||
api_endpoint: null,
|
api_endpoint: null,
|
||||||
|
@ -203,84 +189,35 @@ const action = {
|
||||||
"messageable_level",
|
"messageable_level",
|
||||||
"visibility_level",
|
"visibility_level",
|
||||||
"members_visibility_level",
|
"members_visibility_level",
|
||||||
"add_event",
|
],
|
||||||
"add_location",
|
advanced: [
|
||||||
|
"code",
|
||||||
|
"custom_fields",
|
||||||
|
"skip_redirect",
|
||||||
|
"suppress_notifications",
|
||||||
|
"required",
|
||||||
],
|
],
|
||||||
required: ["id", "type"],
|
required: ["id", "type"],
|
||||||
dependent: {},
|
dependent: {},
|
||||||
objectArrays: {},
|
objectArrays: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = {
|
const wizardSchema = {
|
||||||
wizard,
|
wizard,
|
||||||
step,
|
step,
|
||||||
field,
|
field,
|
||||||
custom_field,
|
|
||||||
action,
|
action,
|
||||||
filters,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function buildFieldTypes(types) {
|
||||||
|
wizardSchema.field.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildFieldValidations(validations) {
|
export function buildFieldValidations(validations) {
|
||||||
wizardSchema.field.validations = validations;
|
wizardSchema.field.validations = validations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterValues(currentWizard, feature, attribute, values = null) {
|
if (Discourse.SiteSettings.wizard_apis_enabled) {
|
||||||
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 = getOwner(this).lookup("service:site-settings");
|
|
||||||
if (siteSettings.wizard_apis_enabled) {
|
|
||||||
wizardSchema.action.types.send_to_api = {
|
wizardSchema.action.types.send_to_api = {
|
||||||
api: null,
|
api: null,
|
||||||
api_endpoint: null,
|
api_endpoint: null,
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
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 };
|
|
|
@ -1,6 +1,5 @@
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import wizardSchema from "./wizard-schema";
|
import wizardSchema from "./wizard-schema";
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
function selectKitContent(content) {
|
function selectKitContent(content) {
|
||||||
return content.map((i) => ({ id: i, name: i }));
|
return content.map((i) => ({ id: i, name: i }));
|
||||||
|
@ -34,10 +33,6 @@ function camelCase(string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function translationOrText(i18nKey, text) {
|
|
||||||
return I18n.findTranslation(i18nKey) ? I18n.t(i18nKey) : text;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userProperties = [
|
const userProperties = [
|
||||||
"name",
|
"name",
|
||||||
"username",
|
"username",
|
||||||
|
@ -125,6 +120,4 @@ export {
|
||||||
listProperties,
|
listProperties,
|
||||||
notificationLevels,
|
notificationLevels,
|
||||||
wizardFieldList,
|
wizardFieldList,
|
||||||
sentenceCase,
|
|
||||||
translationOrText,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import Mixin from "@ember/object/mixin";
|
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
|
||||||
import { readOnly } from "@ember/object/computed";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
const PRODUCT_PAGE = "https://custom-wizard.pavilion.tech";
|
|
||||||
const SUPPORT_MESSAGE =
|
|
||||||
"https://coop.pavilion.tech/new-message?username=support&title=Custom%20Wizard%20Support";
|
|
||||||
const MANAGER_CATEGORY =
|
|
||||||
"https://discourse.pluginmanager.org/c/discourse-custom-wizard";
|
|
||||||
|
|
||||||
export default Mixin.create({
|
|
||||||
subscriptionLandingUrl: PRODUCT_PAGE,
|
|
||||||
subscriptionClientUrl: "/admin/plugins/subscription-client",
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
adminWizards() {
|
|
||||||
return getOwner(this).lookup("controller:admin-wizards");
|
|
||||||
},
|
|
||||||
|
|
||||||
subscribed: readOnly("adminWizards.subscribed"),
|
|
||||||
subscriptionType: readOnly("adminWizards.subscriptionType"),
|
|
||||||
businessSubscription: readOnly("adminWizards.businessSubscription"),
|
|
||||||
communitySubscription: readOnly("adminWizards.communitySubscription"),
|
|
||||||
standardSubscription: readOnly("adminWizards.standardSubscription"),
|
|
||||||
subscriptionAttributes: readOnly("adminWizards.subscriptionAttributes"),
|
|
||||||
subscriptionClientInstalled: readOnly(
|
|
||||||
"adminWizards.subscriptionClientInstalled"
|
|
||||||
),
|
|
||||||
|
|
||||||
@discourseComputed("subscriptionClientInstalled")
|
|
||||||
subscriptionLink(subscriptionClientInstalled) {
|
|
||||||
return subscriptionClientInstalled
|
|
||||||
? this.subscriptionClientUrl
|
|
||||||
: this.subscriptionLandingUrl;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("subscriptionType")
|
|
||||||
subscriptionCtaLink(subscriptionType) {
|
|
||||||
switch (subscriptionType) {
|
|
||||||
case "none":
|
|
||||||
return PRODUCT_PAGE;
|
|
||||||
case "standard":
|
|
||||||
return SUPPORT_MESSAGE;
|
|
||||||
case "business":
|
|
||||||
return SUPPORT_MESSAGE;
|
|
||||||
case "community":
|
|
||||||
return MANAGER_CATEGORY;
|
|
||||||
default:
|
|
||||||
return PRODUCT_PAGE;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -4,8 +4,6 @@ import { get, set } from "@ember/object";
|
||||||
import Mixin from "@ember/object/mixin";
|
import Mixin from "@ember/object/mixin";
|
||||||
import { deepEqual } from "discourse-common/lib/object";
|
import { deepEqual } from "discourse-common/lib/object";
|
||||||
|
|
||||||
const observedCache = [];
|
|
||||||
|
|
||||||
export default Mixin.create({
|
export default Mixin.create({
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
@ -34,13 +32,7 @@ export default Mixin.create({
|
||||||
};
|
};
|
||||||
|
|
||||||
listProperties(componentType, opts).forEach((property) => {
|
listProperties(componentType, opts).forEach((property) => {
|
||||||
if (observedCache.includes(property)) {
|
|
||||||
obj.removeObserver(property, this, this.toggleUndo);
|
obj.removeObserver(property, this, this.toggleUndo);
|
||||||
let index = observedCache.indexOf(property);
|
|
||||||
if (index !== -1) {
|
|
||||||
observedCache.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -53,9 +45,6 @@ export default Mixin.create({
|
||||||
};
|
};
|
||||||
|
|
||||||
listProperties(componentType, opts).forEach((property) => {
|
listProperties(componentType, opts).forEach((property) => {
|
||||||
if (observedCache.indexOf(property) === -1) {
|
|
||||||
observedCache.push(property);
|
|
||||||
}
|
|
||||||
obj.addObserver(property, this, this.toggleUndo);
|
obj.addObserver(property, this, this.toggleUndo);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,242 +0,0 @@
|
||||||
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;
|
|
|
@ -1,79 +0,0 @@
|
||||||
import EmberObject from "@ember/object";
|
|
||||||
import ValidState from "discourse/plugins/discourse-custom-wizard/discourse/mixins/valid-state";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard";
|
|
||||||
|
|
||||||
const StandardFieldValidation = [
|
|
||||||
"text",
|
|
||||||
"number",
|
|
||||||
"textarea",
|
|
||||||
"dropdown",
|
|
||||||
"tag",
|
|
||||||
"image",
|
|
||||||
"user_selector",
|
|
||||||
"text_only",
|
|
||||||
"composer",
|
|
||||||
"category",
|
|
||||||
"group",
|
|
||||||
"date",
|
|
||||||
"time",
|
|
||||||
"date_time",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default EmberObject.extend(ValidState, {
|
|
||||||
id: null,
|
|
||||||
type: null,
|
|
||||||
value: null,
|
|
||||||
required: null,
|
|
||||||
warning: null,
|
|
||||||
|
|
||||||
@discourseComputed("wizardId", "stepId", "id")
|
|
||||||
i18nKey(wizardId, stepId, id) {
|
|
||||||
return `${wizardId}.${stepId}.${id}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey", "label")
|
|
||||||
translatedLabel(i18nKey, label) {
|
|
||||||
return translationOrText(`${i18nKey}.label`, label);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey", "placeholder")
|
|
||||||
translatedPlaceholder(i18nKey, placeholder) {
|
|
||||||
return translationOrText(`${i18nKey}.placeholder`, placeholder);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey", "description")
|
|
||||||
translatedDescription(i18nKey, description) {
|
|
||||||
return translationOrText(`${i18nKey}.description`, description);
|
|
||||||
},
|
|
||||||
|
|
||||||
check() {
|
|
||||||
if (this.customCheck) {
|
|
||||||
return this.customCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
let valid = this.valid;
|
|
||||||
|
|
||||||
if (!this.required) {
|
|
||||||
this.setValid(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const val = this.get("value");
|
|
||||||
const type = this.get("type");
|
|
||||||
|
|
||||||
if (type === "checkbox") {
|
|
||||||
valid = val;
|
|
||||||
} else if (type === "upload") {
|
|
||||||
valid = val && val.id > 0;
|
|
||||||
} else if (StandardFieldValidation.indexOf(type) > -1) {
|
|
||||||
valid = val && val.toString().length > 0;
|
|
||||||
} else if (type === "url") {
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setValid(Boolean(valid));
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -3,54 +3,14 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
const CustomWizardLogs = EmberObject.extend();
|
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({
|
CustomWizardLogs.reopenClass({
|
||||||
list(wizardId, page = 0) {
|
list(page = 0) {
|
||||||
let data = {
|
return ajax("/admin/wizards/logs", {
|
||||||
|
data: {
|
||||||
page,
|
page,
|
||||||
};
|
},
|
||||||
|
}).catch(popupAjaxError);
|
||||||
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;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
|
|
||||||
const CustomWizardManager = EmberObject.extend();
|
const CustomWizardManager = EmberObject.extend();
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ CustomWizardManager.reopenClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
export(wizardIds) {
|
export(wizardIds) {
|
||||||
let url = `${getURL()}/${basePath}/export?`;
|
let url = `${Discourse.BaseUrl}/${basePath}/export?`;
|
||||||
|
|
||||||
wizardIds.forEach((wizardId, index) => {
|
wizardIds.forEach((wizardId, index) => {
|
||||||
let step = "wizard_ids[]=" + wizardId;
|
let step = "wizard_ids[]=" + wizardId;
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
import EmberObject from "@ember/object";
|
|
||||||
import ValidState from "discourse/plugins/discourse-custom-wizard/discourse/mixins/valid-state";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { later } from "@ember/runloop";
|
|
||||||
import { translationOrText } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard";
|
|
||||||
|
|
||||||
export default EmberObject.extend(ValidState, {
|
|
||||||
id: null,
|
|
||||||
|
|
||||||
@discourseComputed("wizardId", "id")
|
|
||||||
i18nKey(wizardId, stepId) {
|
|
||||||
return `${wizardId}.${stepId}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey", "title")
|
|
||||||
translatedTitle(i18nKey, title) {
|
|
||||||
return translationOrText(`${i18nKey}.title`, title);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("i18nKey", "description")
|
|
||||||
translatedDescription(i18nKey, description) {
|
|
||||||
return translationOrText(`${i18nKey}.description`, description);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("index")
|
|
||||||
displayIndex: (index) => index + 1,
|
|
||||||
|
|
||||||
@discourseComputed("fields.[]")
|
|
||||||
fieldsById(fields) {
|
|
||||||
const lookup = {};
|
|
||||||
fields.forEach((field) => (lookup[field.get("id")] = field));
|
|
||||||
return lookup;
|
|
||||||
},
|
|
||||||
|
|
||||||
validate() {
|
|
||||||
let allValid = true;
|
|
||||||
const result = { warnings: [] };
|
|
||||||
|
|
||||||
this.fields.forEach((field) => {
|
|
||||||
allValid = allValid && field.check();
|
|
||||||
const warning = field.get("warning");
|
|
||||||
if (warning) {
|
|
||||||
result.warnings.push(warning);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setValid(allValid);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
fieldError(id, description) {
|
|
||||||
const field = this.fields.findBy("id", id);
|
|
||||||
if (field) {
|
|
||||||
field.setValid(false, description);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
|
||||||
const wizardId = this.get("wizardId");
|
|
||||||
const fields = {};
|
|
||||||
|
|
||||||
this.get("fields").forEach((f) => {
|
|
||||||
if (f.type !== "text_only") {
|
|
||||||
fields[f.id] = f.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return ajax({
|
|
||||||
url: `/w/${wizardId}/steps/${this.get("id")}`,
|
|
||||||
type: "PUT",
|
|
||||||
data: { fields },
|
|
||||||
}).catch((response) => {
|
|
||||||
if (response.jqXHR) {
|
|
||||||
response = response.jqXHR;
|
|
||||||
}
|
|
||||||
if (response && response.responseJSON && response.responseJSON.errors) {
|
|
||||||
let wizardErrors = [];
|
|
||||||
response.responseJSON.errors.forEach((err) => {
|
|
||||||
if (err.field === wizardId) {
|
|
||||||
wizardErrors.push(err.description);
|
|
||||||
} else if (err.field) {
|
|
||||||
this.fieldError(err.field, err.description);
|
|
||||||
} else if (err) {
|
|
||||||
wizardErrors.push(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (wizardErrors.length) {
|
|
||||||
this.handleWizardError(wizardErrors.join("\n"));
|
|
||||||
}
|
|
||||||
this.animateInvalidFields();
|
|
||||||
throw response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response && response.responseText) {
|
|
||||||
const responseText = response.responseText;
|
|
||||||
const start = responseText.indexOf(">") + 1;
|
|
||||||
const end = responseText.indexOf("plugins");
|
|
||||||
const message = responseText.substring(start, end);
|
|
||||||
this.handleWizardError(message);
|
|
||||||
throw message;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleWizardError(message) {
|
|
||||||
this.set("message", {
|
|
||||||
state: "error",
|
|
||||||
text: message,
|
|
||||||
});
|
|
||||||
later(() => this.set("message", null), 6000);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,127 +1,223 @@
|
||||||
import EmberObject from "@ember/object";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
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 { popupAjaxError } from "discourse/lib/ajax-error";
|
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({
|
const CustomWizard = EmberObject.extend({
|
||||||
@discourseComputed("steps.length")
|
save(opts) {
|
||||||
totalSteps: (length) => length,
|
return new Promise((resolve, reject) => {
|
||||||
|
let wizard = this.buildJson(this, "wizard");
|
||||||
|
|
||||||
skip() {
|
if (wizard.error) {
|
||||||
if (this.required && !this.completed && this.permitted) {
|
reject(wizard);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
CustomWizard.skip(this.id);
|
|
||||||
|
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.error) {
|
||||||
|
reject(result);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
restart() {
|
buildJson(object, type, result = {}) {
|
||||||
CustomWizard.restart(this.id);
|
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(inputs) {
|
||||||
|
if (!inputs || !inputs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
CustomWizard.reopenClass({
|
CustomWizard.reopenClass({
|
||||||
skip(wizardId) {
|
all() {
|
||||||
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
return ajax("/admin/wizards/wizard", {
|
||||||
|
type: "GET",
|
||||||
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
CustomWizard.finished(result);
|
return result.wizard_list;
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
},
|
||||||
|
|
||||||
restart(wizardId) {
|
submissions(wizardId) {
|
||||||
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
return ajax(`/admin/wizards/submissions/${wizardId}`, {
|
||||||
.then(() => {
|
type: "GET",
|
||||||
window.location.href = `/w/${wizardId}`;
|
}).catch(popupAjaxError);
|
||||||
})
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
finished(result) {
|
create(wizardJson = {}) {
|
||||||
let url = "/";
|
const wizard = this._super.apply(this);
|
||||||
if (result.redirect_on_complete) {
|
wizard.setProperties(buildProperties(wizardJson));
|
||||||
url = result.redirect_on_complete;
|
return wizard;
|
||||||
}
|
|
||||||
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;
|
export default CustomWizard;
|
||||||
|
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden …
In neuem Issue referenzieren