Commits vergleichen
1 Commit
main
...
test_compa
Autor | SHA1 | Datum | |
---|---|---|---|
|
73bdad7fdc |
626 geänderte Dateien mit 8940 neuen und 77041 gelöschten Zeilen
|
@ -1,3 +1 @@
|
|||
3.1.999: 1f35b80f85e5fd1efb7f4851f0845700432febdc
|
||||
2.7.99: e07a57e398b6b1676ab42a7e34467556fca5416b
|
||||
2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c
|
||||
v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a
|
|
@ -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.");
|
||||
}
|
9
.gitignore
gevendort
9
.gitignore
gevendort
|
@ -1,8 +1 @@
|
|||
coverage/*
|
||||
!coverage/.last_run.json
|
||||
gems/*
|
||||
.bundle/
|
||||
auto_generated
|
||||
.DS_Store
|
||||
node_modules/
|
||||
vendor/*
|
||||
coverage
|
|
@ -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,10 +1,12 @@
|
|||
# We want to use the KVM-based system, so require sudo
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- git clone --depth=1 https://github.com/discourse/discourse-plugin-ci
|
||||
|
||||
install: true
|
||||
install: true # Prevent travis doing bundle install
|
||||
|
||||
script:
|
||||
- discourse-plugin-ci/script.sh
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
|
||||
<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)
|
||||
See further: https://meta.discourse.org/t/custom-wizard-plugin/73345
|
||||
|
|
|
@ -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,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::AdminController < ::Admin::AdminController
|
||||
before_action :ensure_admin
|
||||
|
||||
def index
|
||||
subcription = CustomWizard::Subscription.new
|
||||
render_json_dump(
|
||||
subscribed: subcription.subscribed?,
|
||||
subscription_type: subcription.type,
|
||||
subscription_attributes: CustomWizard::Subscription.attributes,
|
||||
subscription_client_installed: CustomWizard::Subscription.client_installed?
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_wizard
|
||||
params.require(:wizard_id)
|
||||
@wizard = CustomWizard::Wizard.create(params[:wizard_id].underscore)
|
||||
raise Discourse::InvalidParameters.new(:wizard_id) unless @wizard
|
||||
end
|
||||
|
||||
def custom_field_list
|
||||
serialize_data(CustomWizard::CustomField.full_list, CustomWizard::CustomFieldSerializer)
|
||||
end
|
||||
|
||||
def render_error(message)
|
||||
render json: failed_json.merge(error: message)
|
||||
end
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
||||
def index
|
||||
render_json_dump(
|
||||
custom_fields: custom_field_list
|
||||
)
|
||||
end
|
||||
|
||||
def update
|
||||
errors = []
|
||||
field_id = nil
|
||||
field_data = {}
|
||||
|
||||
if saved_field = CustomWizard::CustomField.find(field_params[:id].to_i)
|
||||
CustomWizard::CustomField::ATTRS.each do |attr|
|
||||
field_data[attr] = saved_field.send(attr)
|
||||
end
|
||||
field_id = saved_field.id
|
||||
end
|
||||
|
||||
CustomWizard::CustomField::ATTRS.each do |attr|
|
||||
field_data[attr] = field_params[attr]
|
||||
end
|
||||
|
||||
field = CustomWizard::CustomField.new(field_id, field_data)
|
||||
|
||||
PluginStoreRow.transaction do
|
||||
unless field.save
|
||||
field_errors = field.errors.any? ?
|
||||
field.errors.full_messages.join("\n\n") :
|
||||
I18n.t("wizard.custom_field.error.save_default", name: field.name)
|
||||
errors << field_errors
|
||||
raise ActiveRecord::Rollback.new
|
||||
end
|
||||
end
|
||||
|
||||
if errors.any?
|
||||
render json: failed_json.merge(messages: errors)
|
||||
else
|
||||
render json: success_json
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
params.require(:name)
|
||||
|
||||
if CustomWizard::CustomField.destroy(params[:name])
|
||||
render json: success_json
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def field_params
|
||||
params.required(:custom_field)
|
||||
.permit(
|
||||
:id,
|
||||
:name,
|
||||
:klass,
|
||||
:type,
|
||||
serializers: []
|
||||
)
|
||||
end
|
||||
end
|
|
@ -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,125 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::AdminManagerController < CustomWizard::AdminController
|
||||
skip_before_action :check_xhr, only: [:export]
|
||||
before_action :get_wizard_ids, except: [:import]
|
||||
|
||||
def export
|
||||
templates = []
|
||||
|
||||
@wizard_ids.each do |wizard_id|
|
||||
if template = CustomWizard::Template.find(wizard_id)
|
||||
templates.push(template)
|
||||
end
|
||||
end
|
||||
|
||||
if templates.empty?
|
||||
return render_error(I18n.t('wizard.export.error.invalid_wizards'))
|
||||
end
|
||||
|
||||
basename = SiteSetting.title.parameterize || 'discourse'
|
||||
time = Time.now.to_i
|
||||
filename = "#{basename}-wizards-#{time}.json"
|
||||
|
||||
send_data templates.to_json,
|
||||
type: "application/json",
|
||||
disposition: 'attachment',
|
||||
filename: filename
|
||||
end
|
||||
|
||||
def import
|
||||
file = File.read(params['file'].tempfile)
|
||||
|
||||
if file.nil?
|
||||
return render_error(I18n.t('wizard.export.error.no_file'))
|
||||
end
|
||||
|
||||
file_size = file.size
|
||||
max_file_size = 512 * 1024
|
||||
|
||||
if max_file_size < file_size
|
||||
return render_error(I18n.t('wizard.import.error.file_large'))
|
||||
end
|
||||
|
||||
begin
|
||||
template_json = JSON.parse(file)
|
||||
rescue JSON::ParserError
|
||||
return render_error(I18n.t('wizard.import.error.invalid_json'))
|
||||
end
|
||||
|
||||
imported = []
|
||||
failures = []
|
||||
templates = template_json.is_a?(Array) ? template_json : [template_json]
|
||||
|
||||
templates.each do |raw_template|
|
||||
template = CustomWizard::Template.new(raw_template)
|
||||
template.save(skip_jobs: true, create: true)
|
||||
|
||||
if template.errors.any?
|
||||
failures.push(
|
||||
id: template.data['id'],
|
||||
messages: template.errors.full_messages.join(', ')
|
||||
)
|
||||
else
|
||||
imported.push(
|
||||
id: template.data['id'],
|
||||
name: template.data['name']
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
render json: success_json.merge(
|
||||
imported: imported,
|
||||
failures: failures
|
||||
)
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroyed = []
|
||||
failures = []
|
||||
|
||||
@wizard_ids.each do |wizard_id|
|
||||
template = CustomWizard::Template.find(wizard_id)
|
||||
|
||||
if template && CustomWizard::Template.remove(wizard_id)
|
||||
destroyed.push(
|
||||
id: wizard_id,
|
||||
name: template['name']
|
||||
)
|
||||
else
|
||||
failures.push(
|
||||
id: wizard_id,
|
||||
messages: I18n.t("wizard.destroy.error.#{template ? 'default' : 'no_template'}")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
render json: success_json.merge(
|
||||
destroyed: destroyed,
|
||||
failures: failures
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_wizard_ids
|
||||
if params['wizard_ids'].blank?
|
||||
return render_error(I18n.t('wizard.export.error.select_one'))
|
||||
end
|
||||
|
||||
wizard_ids = []
|
||||
|
||||
params['wizard_ids'].each do |wizard_id|
|
||||
begin
|
||||
wizard_ids.push(wizard_id.underscore)
|
||||
rescue
|
||||
#
|
||||
end
|
||||
end
|
||||
|
||||
if wizard_ids.empty?
|
||||
return render_error(I18n.t('wizard.export.error.invalid_wizards'))
|
||||
end
|
||||
|
||||
@wizard_ids = wizard_ids
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
|
||||
skip_before_action :preload_json, :check_xhr, only: [:download]
|
||||
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),
|
||||
submissions: ActiveModel::ArraySerializer.new(
|
||||
submission_list.submissions,
|
||||
each_serializer: CustomWizard::SubmissionSerializer
|
||||
),
|
||||
total: submission_list.total
|
||||
)
|
||||
end
|
||||
|
||||
def download
|
||||
content = ActiveModel::ArraySerializer.new(
|
||||
CustomWizard::Submission.list(@wizard).submissions,
|
||||
each_serializer: CustomWizard::SubmissionSerializer
|
||||
)
|
||||
|
||||
send_data content.to_json,
|
||||
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
|
||||
content_type: "application/json",
|
||||
disposition: "attachment"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def submission_list
|
||||
CustomWizard::Submission.list(@wizard, page: params[:page].to_i)
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::RealtimeValidationsController < ::ApplicationController
|
||||
def validate
|
||||
klass_str = "CustomWizard::RealtimeValidation::#{validation_params[:type].camelize}"
|
||||
result = klass_str.constantize.new(current_user).perform(validation_params)
|
||||
render_serialized(result.items, "#{klass_str}Serializer".constantize, result.serializer_opts)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validation_params
|
||||
params.require(:type)
|
||||
settings = ::CustomWizard::RealtimeValidation.types[params[:type].to_sym]
|
||||
params.require(settings[:required_params]) if settings[:required_params].present?
|
||||
params
|
||||
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,4 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::CustomFieldSerializer < ApplicationSerializer
|
||||
attributes :id, :klass, :name, :type, :serializers
|
||||
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,3 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class ::CustomWizard::RealtimeValidation::SimilarTopicsSerializer < ::SimilarTopicSerializer
|
||||
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,126 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { alias, equal, or } from "@ember/object/computed";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "tr",
|
||||
topicSerializers: ["topic_view", "topic_list_item"],
|
||||
postSerializers: ["post"],
|
||||
groupSerializers: ["basic_group"],
|
||||
categorySerializers: ["basic_category"],
|
||||
showInputs: or("field.new", "field.edit"),
|
||||
classNames: ["custom-field-input"],
|
||||
loading: or("saving", "destroying"),
|
||||
destroyDisabled: alias("loading"),
|
||||
closeDisabled: alias("loading"),
|
||||
isExternal: equal("field.id", "external"),
|
||||
|
||||
didInsertElement() {
|
||||
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
|
||||
},
|
||||
|
||||
@discourseComputed("field.klass")
|
||||
serializerContent(klass) {
|
||||
const serializers = this.get(`${klass}Serializers`);
|
||||
|
||||
if (serializers) {
|
||||
return serializers.reduce((result, key) => {
|
||||
result.push({
|
||||
id: key,
|
||||
name: I18n.t(`admin.wizard.custom_field.serializers.${key}`),
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
},
|
||||
|
||||
@observes("field.klass")
|
||||
clearSerializersWhenClassChanges() {
|
||||
this.set("field.serializers", null);
|
||||
},
|
||||
|
||||
compareArrays(array1, array2) {
|
||||
return (
|
||||
array1.length === array2.length &&
|
||||
array1.every((value, index) => {
|
||||
return value === array2[index];
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"saving",
|
||||
"isExternal",
|
||||
"field.name",
|
||||
"field.klass",
|
||||
"field.type",
|
||||
"field.serializers"
|
||||
)
|
||||
saveDisabled(saving, isExternal) {
|
||||
if (saving || isExternal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const originalField = this.originalField;
|
||||
if (!originalField) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ["name", "klass", "type", "serializers"].every((attr) => {
|
||||
let current = this.get(attr);
|
||||
let original = originalField[attr];
|
||||
|
||||
if (!current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attr === "serializers") {
|
||||
return this.compareArrays(current, original);
|
||||
} else {
|
||||
return current === original;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.set("field.edit", true);
|
||||
},
|
||||
|
||||
close() {
|
||||
if (this.field.edit) {
|
||||
this.set("field.edit", false);
|
||||
}
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.set("destroying", true);
|
||||
this.removeField(this.field);
|
||||
},
|
||||
|
||||
save() {
|
||||
this.set("saving", true);
|
||||
|
||||
const field = this.field;
|
||||
|
||||
let data = {
|
||||
id: field.id,
|
||||
klass: field.klass,
|
||||
type: field.type,
|
||||
serializers: field.serializers,
|
||||
name: field.name,
|
||||
};
|
||||
|
||||
this.saveField(data).then((result) => {
|
||||
this.set("saving", false);
|
||||
if (result.success) {
|
||||
this.set("field.edit", false);
|
||||
} else {
|
||||
this.set("saveIcon", "times");
|
||||
}
|
||||
setTimeout(() => this.set("saveIcon", null), 10000);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,146 +0,0 @@
|
|||
import {
|
||||
default as computed,
|
||||
observes,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { renderAvatar } from "discourse/helpers/user-avatar";
|
||||
import userSearch from "discourse/lib/user-search";
|
||||
import I18n from "I18n";
|
||||
import Handlebars from "handlebars";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import TextField from "discourse/components/text-field";
|
||||
|
||||
const template = function (params) {
|
||||
const options = params.options;
|
||||
let html = "<div class='autocomplete'>";
|
||||
|
||||
if (options.users) {
|
||||
html += "<ul>";
|
||||
options.users.forEach((u) => {
|
||||
html += `<li><a href title="${u.name}">`;
|
||||
html += renderAvatar(u, { imageSize: "tiny" });
|
||||
html += `<span class='username'>${u.username}</span>`;
|
||||
if (u.name) {
|
||||
html += `<span class='name'>${u.name}</span>`;
|
||||
}
|
||||
html += `</a></li>`;
|
||||
});
|
||||
html += "</ul>";
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
|
||||
return new Handlebars.SafeString(html).string;
|
||||
};
|
||||
|
||||
export default TextField.extend({
|
||||
attributeBindings: ["autofocus", "maxLength"],
|
||||
autocorrect: false,
|
||||
autocapitalize: false,
|
||||
name: "user-selector",
|
||||
id: "custom-member-selector",
|
||||
|
||||
@computed("placeholderKey")
|
||||
placeholder(placeholderKey) {
|
||||
return placeholderKey ? I18n.t(placeholderKey) : "";
|
||||
},
|
||||
|
||||
@observes("usernames")
|
||||
_update() {
|
||||
if (this.get("canReceiveUpdates") === "true") {
|
||||
this.didInsertElement({ updateData: true });
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement(opts) {
|
||||
this._super();
|
||||
let self = this,
|
||||
selected = [],
|
||||
groups = [],
|
||||
includeMentionableGroups =
|
||||
this.get("includeMentionableGroups") === "true",
|
||||
includeMessageableGroups =
|
||||
this.get("includeMessageableGroups") === "true",
|
||||
includeGroups = this.get("includeGroups") === "true",
|
||||
allowedUsers = this.get("allowedUsers") === "true";
|
||||
|
||||
function excludedUsernames() {
|
||||
// hack works around some issues with allowAny eventing
|
||||
const usernames = self.get("single") ? [] : selected;
|
||||
return usernames;
|
||||
}
|
||||
$(this.element)
|
||||
.val(this.get("usernames"))
|
||||
.autocomplete({
|
||||
template,
|
||||
disabled: this.get("disabled"),
|
||||
single: this.get("single"),
|
||||
allowAny: this.get("allowAny"),
|
||||
updateData: opts && opts.updateData ? opts.updateData : false,
|
||||
|
||||
dataSource(term) {
|
||||
const termRegex = /[^a-zA-Z0-9_\-\.@\+]/;
|
||||
let results = userSearch({
|
||||
term: term.replace(termRegex, ""),
|
||||
topicId: self.get("topicId"),
|
||||
exclude: excludedUsernames(),
|
||||
includeGroups,
|
||||
allowedUsers,
|
||||
includeMentionableGroups,
|
||||
includeMessageableGroups,
|
||||
});
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
transformComplete(v) {
|
||||
if (v.username || v.name) {
|
||||
if (!v.username) {
|
||||
groups.push(v.name);
|
||||
}
|
||||
return v.username || v.name;
|
||||
} else {
|
||||
let excludes = excludedUsernames();
|
||||
return v.usernames.filter(function (item) {
|
||||
return excludes.indexOf(item) === -1;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onChangeItems(items) {
|
||||
let hasGroups = false;
|
||||
items = items.map(function (i) {
|
||||
if (groups.indexOf(i) > -1) {
|
||||
hasGroups = true;
|
||||
}
|
||||
return i.username ? i.username : i;
|
||||
});
|
||||
self.set("usernames", items.join(","));
|
||||
self.set("hasGroups", hasGroups);
|
||||
|
||||
selected = items;
|
||||
if (self.get("onChangeCallback")) {
|
||||
self.sendAction("onChangeCallback");
|
||||
}
|
||||
},
|
||||
|
||||
reverseTransform(i) {
|
||||
return { username: i };
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
$(this.element).autocomplete("destroy");
|
||||
},
|
||||
|
||||
// THIS IS A HUGE HACK TO SUPPORT CLEARING THE INPUT
|
||||
@observes("usernames")
|
||||
_clearInput: function () {
|
||||
if (arguments.length > 1) {
|
||||
if (isEmpty(this.get("usernames"))) {
|
||||
$(this.element).parent().find("a").click();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
import CategorySelector from "select-kit/components/category-selector";
|
||||
import { computed } from "@ember/object";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
|
||||
export default CategorySelector.extend({
|
||||
classNames: ["category-selector", "wizard-category-selector"],
|
||||
content: computed(
|
||||
"categories.[]",
|
||||
"blacklist.[]",
|
||||
"whitelist.[]",
|
||||
function () {
|
||||
return this._super().filter((category) => {
|
||||
const whitelist = makeArray(this.whitelist);
|
||||
return !whitelist.length || whitelist.indexOf(category.id) > -1;
|
||||
});
|
||||
}
|
||||
),
|
||||
});
|
|
@ -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,16 +0,0 @@
|
|||
import DateTimeInput from "discourse/components/date-time-input";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default DateTimeInput.extend({
|
||||
classNameBindings: ["fieldClass"],
|
||||
|
||||
@discourseComputed("timeFirst", "tabindex")
|
||||
timeTabindex(timeFirst, tabindex) {
|
||||
return timeFirst ? tabindex : tabindex + 1;
|
||||
},
|
||||
|
||||
@discourseComputed("timeFirst", "tabindex")
|
||||
dateTabindex(timeFirst, tabindex) {
|
||||
return timeFirst ? tabindex + 1 : tabindex;
|
||||
},
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
import { observes } from "discourse-common/utils/decorators";
|
||||
import Category from "discourse/models/category";
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
didInsertElement() {
|
||||
const property = this.field.property || "id";
|
||||
const value = this.field.value;
|
||||
|
||||
if (value) {
|
||||
this.set(
|
||||
"categories",
|
||||
[...value].reduce((result, v) => {
|
||||
let val =
|
||||
property === "id" ? Category.findById(v) : Category.findBySlug(v);
|
||||
if (val) {
|
||||
result.push(val);
|
||||
}
|
||||
return result;
|
||||
}, [])
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@observes("categories")
|
||||
setValue() {
|
||||
const categories = (this.categories || []).filter((c) => !!c);
|
||||
const property = this.field.property || "id";
|
||||
|
||||
if (categories.length) {
|
||||
this.set(
|
||||
"field.value",
|
||||
categories.reduce((result, c) => {
|
||||
if (c && c[property]) {
|
||||
result.push(c[property]);
|
||||
}
|
||||
return result;
|
||||
}, [])
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -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,48 +0,0 @@
|
|||
import {
|
||||
default as computed,
|
||||
observes,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import EmberObject from "@ember/object";
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
showPreview: false,
|
||||
classNameBindings: [
|
||||
":wizard-field-composer",
|
||||
"showPreview:show-preview:hide-preview",
|
||||
],
|
||||
|
||||
didInsertElement() {
|
||||
this.set(
|
||||
"composer",
|
||||
EmberObject.create({
|
||||
loading: false,
|
||||
reply: this.get("field.value") || "",
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@observes("composer.reply")
|
||||
setField() {
|
||||
this.set("field.value", this.get("composer.reply"));
|
||||
},
|
||||
|
||||
@computed("showPreview")
|
||||
togglePreviewLabel(showPreview) {
|
||||
return showPreview
|
||||
? "wizard_composer.hide_preview"
|
||||
: "wizard_composer.show_preview";
|
||||
},
|
||||
|
||||
actions: {
|
||||
togglePreview() {
|
||||
this.toggleProperty("showPreview");
|
||||
},
|
||||
|
||||
groupsMentioned() {},
|
||||
afterRefresh() {},
|
||||
cannotSeeMention() {},
|
||||
importQuote() {},
|
||||
showUploadSelector() {},
|
||||
},
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
@observes("dateTime")
|
||||
setValue() {
|
||||
this.set("field.value", this.dateTime.format(this.field.format));
|
||||
},
|
||||
|
||||
actions: {
|
||||
onChange(value) {
|
||||
this.set("dateTime", moment(value));
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
@observes("date")
|
||||
setValue() {
|
||||
this.set("field.value", this.date.format(this.field.format));
|
||||
},
|
||||
|
||||
actions: {
|
||||
onChange(value) {
|
||||
this.set("date", moment(value));
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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,23 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: ["fieldClass"],
|
||||
|
||||
@observes("time")
|
||||
setValue() {
|
||||
this.set("field.value", this.time.format(this.field.format));
|
||||
},
|
||||
|
||||
actions: {
|
||||
onChange(value) {
|
||||
this.set(
|
||||
"time",
|
||||
moment({
|
||||
hours: value.hours,
|
||||
minutes: value.minutes,
|
||||
})
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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,19 +0,0 @@
|
|||
import ComboBox from "select-kit/components/combo-box";
|
||||
import { computed } from "@ember/object";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
|
||||
export default ComboBox.extend({
|
||||
content: computed("groups.[]", "field.content.[]", function () {
|
||||
const whitelist = makeArray(this.field.content);
|
||||
return this.groups
|
||||
.filter((group) => {
|
||||
return !whitelist.length || whitelist.indexOf(group.id) > -1;
|
||||
})
|
||||
.map((g) => {
|
||||
return {
|
||||
id: g.id,
|
||||
name: g.full_name ? g.full_name : g.name,
|
||||
};
|
||||
});
|
||||
}),
|
||||
});
|
|
@ -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,38 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { bind } from "@ember/runloop";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["wizard-similar-topics"],
|
||||
showTopics: true,
|
||||
|
||||
didInsertElement() {
|
||||
$(document).on("click", bind(this, this.documentClick));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
$(document).off("click", bind(this, this.documentClick));
|
||||
},
|
||||
|
||||
documentClick(e) {
|
||||
if (this._state === "destroying") {
|
||||
return;
|
||||
}
|
||||
let $target = $(e.target);
|
||||
|
||||
if (!$target.hasClass("show-topics")) {
|
||||
this.set("showTopics", false);
|
||||
}
|
||||
},
|
||||
|
||||
@observes("topics")
|
||||
toggleShowWhenTopicsChange() {
|
||||
this.set("showTopics", true);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleShowTopics() {
|
||||
this.set("showTopics", true);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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,44 +0,0 @@
|
|||
import computed from "discourse-common/utils/decorators";
|
||||
import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction";
|
||||
import I18n from "I18n";
|
||||
import TextField from "discourse/components/text-field";
|
||||
|
||||
export default TextField.extend({
|
||||
attributeBindings: [
|
||||
"autocorrect",
|
||||
"autocapitalize",
|
||||
"autofocus",
|
||||
"maxLength",
|
||||
"dir",
|
||||
],
|
||||
|
||||
@computed
|
||||
dir() {
|
||||
if (this.siteSettings.support_mixed_text_direction) {
|
||||
let val = this.value;
|
||||
if (val) {
|
||||
return isRTL(val) ? "rtl" : "ltr";
|
||||
} else {
|
||||
return siteDir();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
keyUp() {
|
||||
if (this.siteSettings.support_mixed_text_direction) {
|
||||
let val = this.value;
|
||||
if (isRTL(val)) {
|
||||
this.set("dir", "rtl");
|
||||
} else if (isLTR(val)) {
|
||||
this.set("dir", "ltr");
|
||||
} else {
|
||||
this.set("dir", siteDir());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed("placeholderKey")
|
||||
placeholder(placeholderKey) {
|
||||
return placeholderKey ? I18n.t(placeholderKey) : "";
|
||||
},
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
import TimeInput from "discourse/components/time-input";
|
||||
|
||||
export default TimeInput.extend({});
|
|
@ -1,9 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
perform() {
|
||||
this.appEvents.trigger("custom-wizard:validate");
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
import WizardFieldValidator from "discourse/plugins/discourse-custom-wizard/discourse/components/validator";
|
||||
import { deepMerge } from "discourse-common/lib/object";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { cancel, later } from "@ember/runloop";
|
||||
import { A } from "@ember/array";
|
||||
import EmberObject, { computed } from "@ember/object";
|
||||
import { and, equal, notEmpty } from "@ember/object/computed";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
import { dasherize } from "@ember/string";
|
||||
|
||||
export default WizardFieldValidator.extend({
|
||||
classNames: ["similar-topics-validator"],
|
||||
similarTopics: null,
|
||||
hasInput: notEmpty("field.value"),
|
||||
hasSimilarTopics: notEmpty("similarTopics"),
|
||||
hasNotSearched: equal("similarTopics", null),
|
||||
noSimilarTopics: computed("similarTopics", function () {
|
||||
return this.similarTopics !== null && this.similarTopics.length === 0;
|
||||
}),
|
||||
showSimilarTopics: computed("typing", "hasSimilarTopics", function () {
|
||||
return this.hasSimilarTopics && !this.typing;
|
||||
}),
|
||||
showNoSimilarTopics: computed("typing", "noSimilarTopics", function () {
|
||||
return this.noSimilarTopics && !this.typing;
|
||||
}),
|
||||
hasValidationCategories: notEmpty("validationCategories"),
|
||||
insufficientCharacters: computed("typing", "field.value", function () {
|
||||
return this.hasInput && this.field.value.length < 5 && !this.typing;
|
||||
}),
|
||||
insufficientCharactersCategories: and(
|
||||
"insufficientCharacters",
|
||||
"hasValidationCategories"
|
||||
),
|
||||
|
||||
@discourseComputed("validation.categories")
|
||||
validationCategories(categoryIds) {
|
||||
if (categoryIds) {
|
||||
return categoryIds.map((id) => this.site.categoriesById[id]);
|
||||
}
|
||||
|
||||
return A();
|
||||
},
|
||||
|
||||
@discourseComputed("validationCategories")
|
||||
catLinks(categories) {
|
||||
return categories.map((category) => categoryBadgeHTML(category)).join("");
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"loading",
|
||||
"showSimilarTopics",
|
||||
"showNoSimilarTopics",
|
||||
"insufficientCharacters",
|
||||
"insufficientCharactersCategories"
|
||||
)
|
||||
currentState(
|
||||
loading,
|
||||
showSimilarTopics,
|
||||
showNoSimilarTopics,
|
||||
insufficientCharacters,
|
||||
insufficientCharactersCategories
|
||||
) {
|
||||
switch (true) {
|
||||
case loading:
|
||||
return "loading";
|
||||
case showSimilarTopics:
|
||||
return "results";
|
||||
case showNoSimilarTopics:
|
||||
return "no_results";
|
||||
case insufficientCharactersCategories:
|
||||
return "insufficient_characters_categories";
|
||||
case insufficientCharacters:
|
||||
return "insufficient_characters";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("currentState")
|
||||
currentStateClass(currentState) {
|
||||
if (currentState) {
|
||||
return `similar-topics-${dasherize(currentState)}`;
|
||||
}
|
||||
|
||||
return "similar-topics";
|
||||
},
|
||||
|
||||
@discourseComputed("currentState")
|
||||
currentStateKey(currentState) {
|
||||
if (currentState) {
|
||||
return `realtime_validations.similar_topics.${currentState}`;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
validate() {},
|
||||
|
||||
@observes("field.value")
|
||||
customValidate() {
|
||||
const field = this.field;
|
||||
|
||||
if (!field.value) {
|
||||
this.set("similarTopics", null);
|
||||
return;
|
||||
}
|
||||
const value = field.value;
|
||||
|
||||
this.set("typing", true);
|
||||
|
||||
const lastKeyUp = new Date();
|
||||
this._lastKeyUp = lastKeyUp;
|
||||
|
||||
// One second from now, check to see if the last key was hit when
|
||||
// we recorded it. If it was, the user paused typing.
|
||||
cancel(this._lastKeyTimeout);
|
||||
this._lastKeyTimeout = later(() => {
|
||||
if (lastKeyUp !== this._lastKeyUp) {
|
||||
return;
|
||||
}
|
||||
this.set("typing", false);
|
||||
|
||||
if (value && value.length < 5) {
|
||||
this.set("similarTopics", null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSimilarTopics();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
updateSimilarTopics() {
|
||||
this.set("similarTopics", null);
|
||||
this.set("updating", true);
|
||||
|
||||
this.backendValidate({
|
||||
title: this.get("field.value"),
|
||||
categories: this.get("validation.categories"),
|
||||
time_unit: this.get("validation.time_unit"),
|
||||
time_n_value: this.get("validation.time_n_value"),
|
||||
})
|
||||
.then((result) => {
|
||||
const similarTopics = A(
|
||||
deepMerge(result["topics"], result["similar_topics"])
|
||||
);
|
||||
similarTopics.forEach(function (topic, index) {
|
||||
similarTopics[index] = EmberObject.create(topic);
|
||||
});
|
||||
|
||||
this.set("similarTopics", similarTopics);
|
||||
})
|
||||
.finally(() => this.set("updating", false));
|
||||
},
|
||||
|
||||
actions: {
|
||||
closeMessage() {
|
||||
this.set("showMessage", false);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { equal } from "@ember/object/computed";
|
||||
import { ajax, getToken } from "discourse/lib/ajax";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["validator"],
|
||||
classNameBindings: ["isValid", "isInvalid"],
|
||||
validMessageKey: null,
|
||||
invalidMessageKey: null,
|
||||
isValid: null,
|
||||
isInvalid: equal("isValid", false),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get("validation.backend")) {
|
||||
// set a function that can be called as often as it need to
|
||||
// from the derived component
|
||||
this.backendValidate = (params) => {
|
||||
return ajax("/realtime-validations", {
|
||||
data: {
|
||||
type: this.get("type"),
|
||||
authenticity_token: getToken(),
|
||||
...params,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this.appEvents.on("custom-wizard:validate", this, this.checkIsValid);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this.appEvents.off("custom-wizard:validate", this, this.checkIsValid);
|
||||
},
|
||||
|
||||
checkIsValid() {
|
||||
this.set("isValid", this.validate());
|
||||
},
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
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,104 +1,87 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { empty, equal, or } from "@ember/object/computed";
|
||||
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { equal, empty, or, and } from "@ember/object/computed";
|
||||
import { generateName, selectKitContent } from '../lib/wizard';
|
||||
import { computed } from "@ember/object";
|
||||
import UndoChanges from "../mixins/undo-changes";
|
||||
import wizardSchema from '../lib/wizard-schema';
|
||||
import UndoChanges from '../mixins/undo-changes';
|
||||
import Component from "@ember/component";
|
||||
import { notificationLevels } from '../lib/wizard';
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend(UndoChanges, {
|
||||
componentType: "action",
|
||||
classNameBindings: [":wizard-custom-action", "visible"],
|
||||
visible: computed("currentActionId", function () {
|
||||
return this.action.id === this.currentActionId;
|
||||
}),
|
||||
createTopic: equal("action.type", "create_topic"),
|
||||
updateProfile: equal("action.type", "update_profile"),
|
||||
watchCategories: equal("action.type", "watch_categories"),
|
||||
watchTags: equal("action.type", "watch_tags"),
|
||||
sendMessage: equal("action.type", "send_message"),
|
||||
openComposer: equal("action.type", "open_composer"),
|
||||
sendToApi: equal("action.type", "send_to_api"),
|
||||
addToGroup: equal("action.type", "add_to_group"),
|
||||
routeTo: equal("action.type", "route_to"),
|
||||
createCategory: equal("action.type", "create_category"),
|
||||
createGroup: equal("action.type", "create_group"),
|
||||
apiEmpty: empty("action.api"),
|
||||
groupPropertyTypes: selectKitContent(["id", "name"]),
|
||||
hasCustomFields: or(
|
||||
"basicTopicFields",
|
||||
"updateProfile",
|
||||
"createGroup",
|
||||
"createCategory"
|
||||
),
|
||||
basicTopicFields: or("createTopic", "sendMessage", "openComposer"),
|
||||
publicTopicFields: or("createTopic", "openComposer"),
|
||||
showPostAdvanced: or("createTopic", "sendMessage"),
|
||||
availableNotificationLevels: notificationLevels.map((type) => {
|
||||
componentType: 'action',
|
||||
classNameBindings: [':wizard-custom-action', 'visible'],
|
||||
visible: computed('currentActionId', function() { return this.action.id === this.currentActionId }),
|
||||
createTopic: equal('action.type', 'create_topic'),
|
||||
updateProfile: equal('action.type', 'update_profile'),
|
||||
watchCategories: equal('action.type', 'watch_categories'),
|
||||
sendMessage: equal('action.type', 'send_message'),
|
||||
openComposer: equal('action.type', 'open_composer'),
|
||||
sendToApi: equal('action.type', 'send_to_api'),
|
||||
addToGroup: equal('action.type', 'add_to_group'),
|
||||
routeTo: equal('action.type', 'route_to'),
|
||||
apiEmpty: empty('action.api'),
|
||||
groupPropertyTypes: selectKitContent(['id', 'name']),
|
||||
hasAdvanced: or('hasCustomFields', 'routeTo'),
|
||||
showAdvanced: and('hasAdvanced', 'action.type'),
|
||||
hasCustomFields: or('basicTopicFields', 'updateProfile'),
|
||||
basicTopicFields: or('createTopic', 'sendMessage', 'openComposer'),
|
||||
publicTopicFields: or('createTopic', 'openComposer'),
|
||||
showSkipRedirect: or('createTopic', 'sendMessage'),
|
||||
actionTypes: Object.keys(wizardSchema.action.types).map(type => {
|
||||
return {
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.action.watch_x.notification_level.${type}`),
|
||||
name: I18n.t(`admin.wizard.action.${type}.label`)
|
||||
};
|
||||
}),
|
||||
availableNotificationLevels: notificationLevels.map((type, index) => {
|
||||
return {
|
||||
id: 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) {
|
||||
let key = "type";
|
||||
let key = 'type';
|
||||
if (type) {
|
||||
key = "edit";
|
||||
key = 'edit';
|
||||
}
|
||||
return key;
|
||||
},
|
||||
|
||||
@discourseComputed("action.type")
|
||||
customFieldsContext(type) {
|
||||
return `action.${type}`;
|
||||
},
|
||||
|
||||
@discourseComputed("wizard.steps")
|
||||
@discourseComputed('wizard.steps')
|
||||
runAfterContent(steps) {
|
||||
let content = steps.map(function(step) {
|
||||
return {
|
||||
id: step.id,
|
||||
name: step.title || step.id,
|
||||
name: step.title || step.id
|
||||
};
|
||||
});
|
||||
|
||||
content.unshift({
|
||||
id: "wizard_completion",
|
||||
name: I18n.t("admin.wizard.action.run_after.wizard_completion"),
|
||||
id: 'wizard_completion',
|
||||
name: I18n.t('admin.wizard.action.run_after.wizard_completion')
|
||||
});
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
@discourseComputed("apis")
|
||||
@discourseComputed('apis')
|
||||
availableApis(apis) {
|
||||
return apis.map((a) => {
|
||||
return apis.map(a => {
|
||||
return {
|
||||
id: a.name,
|
||||
name: a.title,
|
||||
name: a.title
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("apis", "action.api")
|
||||
@discourseComputed('apis', 'action.api')
|
||||
availableEndpoints(apis, api) {
|
||||
if (!api) {
|
||||
return [];
|
||||
if (!api) return [];
|
||||
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,159 +1,97 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { equal, or } from "@ember/object/computed";
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { equal, or, alias } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import { selectKitContent } from "../lib/wizard";
|
||||
import UndoChanges from "../mixins/undo-changes";
|
||||
import { selectKitContent } from '../lib/wizard';
|
||||
import UndoChanges from '../mixins/undo-changes';
|
||||
import Component from "@ember/component";
|
||||
import wizardSchema from "../lib/wizard-schema";
|
||||
|
||||
export default Component.extend(UndoChanges, {
|
||||
componentType: "field",
|
||||
classNameBindings: [":wizard-custom-field", "visible"],
|
||||
visible: computed("currentFieldId", function () {
|
||||
return this.field.id === this.currentFieldId;
|
||||
}),
|
||||
isDropdown: equal("field.type", "dropdown"),
|
||||
isUpload: equal("field.type", "upload"),
|
||||
isCategory: equal("field.type", "category"),
|
||||
isGroup: equal("field.type", "group"),
|
||||
isTag: equal("field.type", "tag"),
|
||||
isText: equal("field.type", "text"),
|
||||
isTextarea: equal("field.type", "textarea"),
|
||||
isUrl: equal("field.type", "url"),
|
||||
isComposer: equal("field.type", "composer"),
|
||||
showPrefill: or("isText", "isCategory", "isTag", "isGroup", "isDropdown"),
|
||||
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
|
||||
showLimit: or("isCategory", "isTag"),
|
||||
isTextType: or("isText", "isTextarea", "isComposer"),
|
||||
isComposerPreview: equal("field.type", "composer_preview"),
|
||||
categoryPropertyTypes: selectKitContent(["id", "slug"]),
|
||||
messageUrl: "https://discourse.pluginmanager.org/t/field-settings",
|
||||
componentType: 'field',
|
||||
classNameBindings: [':wizard-custom-field', 'visible'],
|
||||
visible: computed('currentFieldId', function() { return this.field.id === this.currentFieldId }),
|
||||
isDropdown: equal('field.type', 'dropdown'),
|
||||
isUpload: equal('field.type', 'upload'),
|
||||
isCategory: equal('field.type', 'category'),
|
||||
isGroup: equal('field.type', 'group'),
|
||||
isTag: equal('field.type', 'tag'),
|
||||
isText: equal('field.type', 'text'),
|
||||
isTextarea: equal('field.type', 'textarea'),
|
||||
isUrl: equal('field.type', 'url'),
|
||||
showPrefill: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
|
||||
showContent: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
|
||||
showLimit: or('isCategory', 'isTag'),
|
||||
showMinLength: or('isText', 'isTextarea', 'isUrl', 'isComposer'),
|
||||
categoryPropertyTypes: selectKitContent(['id', 'slug']),
|
||||
showAdvanced: alias('field.type'),
|
||||
messageUrl: 'https://thepavilion.io/t/2809',
|
||||
|
||||
@discourseComputed("field.type")
|
||||
validations(type) {
|
||||
const applicableToField = [];
|
||||
|
||||
for (let validation in wizardSchema.field.validations) {
|
||||
if (wizardSchema.field.validations[validation]["types"].includes(type)) {
|
||||
applicableToField.push(validation);
|
||||
}
|
||||
}
|
||||
|
||||
return applicableToField;
|
||||
},
|
||||
|
||||
@discourseComputed("field.type")
|
||||
isDateTime(type) {
|
||||
return ["date_time", "date", "time"].indexOf(type) > -1;
|
||||
},
|
||||
|
||||
@discourseComputed("field.type")
|
||||
@discourseComputed('field.type')
|
||||
messageKey(type) {
|
||||
let key = "type";
|
||||
let key = 'type';
|
||||
if (type) {
|
||||
key = "edit";
|
||||
key = 'edit';
|
||||
}
|
||||
return key;
|
||||
},
|
||||
|
||||
setupTypeOutput(fieldType, options) {
|
||||
const selectionType = {
|
||||
category: "category",
|
||||
tag: "tag",
|
||||
group: "group",
|
||||
category: 'category',
|
||||
tag: 'tag',
|
||||
group: 'group'
|
||||
}[fieldType];
|
||||
|
||||
if (selectionType) {
|
||||
options[`${selectionType}Selection`] = "output";
|
||||
options[`${selectionType}Selection`] = 'output';
|
||||
options.outputDefaultSelection = selectionType;
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
@discourseComputed("field.type")
|
||||
@discourseComputed('field.type')
|
||||
contentOptions(fieldType) {
|
||||
let options = {
|
||||
wizardFieldSelection: true,
|
||||
textSelection: "key,value",
|
||||
userFieldSelection: "key,value",
|
||||
context: "field",
|
||||
};
|
||||
textSelection: 'key,value',
|
||||
userFieldSelection: 'key,value',
|
||||
context: 'field'
|
||||
}
|
||||
|
||||
options = this.setupTypeOutput(fieldType, options);
|
||||
|
||||
if (this.isDropdown) {
|
||||
options.wizardFieldSelection = "key,value";
|
||||
options.userFieldOptionsSelection = "output";
|
||||
options.textSelection = "key,value";
|
||||
options.inputTypes = "association,conditional,assignment";
|
||||
options.pairConnector = "association";
|
||||
options.keyPlaceholder = "admin.wizard.key";
|
||||
options.valuePlaceholder = "admin.wizard.value";
|
||||
options.wizardFieldSelection = 'key,value';
|
||||
options.userFieldOptionsSelection = 'output';
|
||||
options.textSelection = 'key,value,output';
|
||||
options.inputTypes = 'conditional,association,assignment';
|
||||
options.pairConnector = 'association';
|
||||
options.keyPlaceholder = 'admin.wizard.key';
|
||||
options.valuePlaceholder = 'admin.wizard.value';
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
@discourseComputed("field.type")
|
||||
@discourseComputed('field.type')
|
||||
prefillOptions(fieldType) {
|
||||
let options = {
|
||||
wizardFieldSelection: true,
|
||||
textSelection: true,
|
||||
userFieldSelection: "key,value",
|
||||
context: "field",
|
||||
};
|
||||
userFieldSelection: 'key,value',
|
||||
context: 'field'
|
||||
}
|
||||
|
||||
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: {
|
||||
imageUploadDone(upload) {
|
||||
this.setProperties({
|
||||
"field.image": upload.url,
|
||||
"field.image_upload_id": upload.id,
|
||||
});
|
||||
this.set("field.image", upload.url);
|
||||
},
|
||||
|
||||
imageUploadDeleted() {
|
||||
this.setProperties({
|
||||
"field.image": null,
|
||||
"field.image_upload_id": null,
|
||||
});
|
||||
},
|
||||
},
|
||||
this.set("field.image", null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,40 +1,16 @@
|
|||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
|
||||
export default Component.extend({
|
||||
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;
|
||||
},
|
||||
classNames: 'wizard-custom-step',
|
||||
|
||||
actions: {
|
||||
bannerUploadDone(upload) {
|
||||
this.setProperties({
|
||||
"step.banner": upload.url,
|
||||
"step.banner_upload_id": upload.id,
|
||||
});
|
||||
this.set("step.banner", upload.url);
|
||||
},
|
||||
|
||||
bannerUploadDeleted() {
|
||||
this.setProperties({
|
||||
"step.banner": null,
|
||||
"step.banner_upload_id": null,
|
||||
});
|
||||
},
|
||||
},
|
||||
this.set("step.banner", null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
46
assets/javascripts/discourse/components/wizard-export.js.es6
Normale Datei
46
assets/javascripts/discourse/components/wizard-export.js.es6
Normale Datei
|
@ -0,0 +1,46 @@
|
|||
import Component from "@ember/component";
|
||||
import { A } from "@ember/array";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['container', 'export'],
|
||||
selected: A(),
|
||||
|
||||
actions: {
|
||||
checkChanged(event) {
|
||||
this.set('exportMessage', '');
|
||||
|
||||
let selected = this.get('selected');
|
||||
|
||||
if (event.target.checked) {
|
||||
selected.addObject(event.target.id);
|
||||
} else if (!event.target.checked) {
|
||||
selected.removeObject(event.target.id);
|
||||
}
|
||||
|
||||
this.set('selected', selected);
|
||||
},
|
||||
|
||||
export() {
|
||||
const wizards = this.get('selected');
|
||||
|
||||
if (!wizards.length) {
|
||||
this.set('exportMessage', I18n.t("admin.wizard.transfer.export.none_selected"));
|
||||
} else {
|
||||
this.set('exportMessage', '');
|
||||
|
||||
let url = Discourse.BaseUrl;
|
||||
let route = '/admin/wizards/transfer/export';
|
||||
url += route + '?';
|
||||
|
||||
wizards.forEach((wizard) => {
|
||||
let step = 'wizards[]=' + wizard;
|
||||
step += '&';
|
||||
url += step;
|
||||
});
|
||||
|
||||
location.href = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
84
assets/javascripts/discourse/components/wizard-import.js.es6
Normale Datei
84
assets/javascripts/discourse/components/wizard-import.js.es6
Normale Datei
|
@ -0,0 +1,84 @@
|
|||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['container', 'import'],
|
||||
hasLogs: notEmpty('logs'),
|
||||
|
||||
@discourseComputed('successIds', 'failureIds')
|
||||
logs(successIds, failureIds) {
|
||||
let logs = [];
|
||||
|
||||
if (successIds) {
|
||||
logs.push(...successIds.map(id => {
|
||||
return { id, type: 'success' };
|
||||
}));
|
||||
}
|
||||
|
||||
if (failureIds) {
|
||||
logs.push(...failureIds.map(id => {
|
||||
return { id, type: 'failure' };
|
||||
}));
|
||||
}
|
||||
|
||||
return logs;
|
||||
},
|
||||
|
||||
actions: {
|
||||
setFilePath(event) {
|
||||
this.set('importMessage', '');
|
||||
|
||||
// 512 kb is the max file size
|
||||
let maxFileSize = 512 * 1024;
|
||||
|
||||
if (event.target.files[0] === undefined) {
|
||||
this.set('filePath', null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxFileSize < event.target.files[0].size) {
|
||||
this.setProperties({
|
||||
importMessage: I18n.t('admin.wizard.transfer.import.file_size_error'),
|
||||
filePath: null
|
||||
});
|
||||
$('#file-url').val('');
|
||||
} else {
|
||||
this.set('filePath', event.target.files[0]);
|
||||
}
|
||||
},
|
||||
|
||||
import() {
|
||||
const filePath = this.get('filePath');
|
||||
let $formData = new FormData();
|
||||
|
||||
if (filePath) {
|
||||
$formData.append('file', filePath);
|
||||
|
||||
ajax('/admin/wizards/transfer/import', {
|
||||
type: 'POST',
|
||||
data: $formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
}).then(result => {
|
||||
if (result.error) {
|
||||
this.set('importMessage', result.error);
|
||||
} else {
|
||||
this.setProperties({
|
||||
successIds: result.success,
|
||||
failureIds: result.failed,
|
||||
fileName: $('#file-url')[0].files[0].name
|
||||
});
|
||||
}
|
||||
|
||||
this.set('filePath', null);
|
||||
$('#file-url').val('');
|
||||
});
|
||||
} else {
|
||||
this.set('importMessage', I18n.t("admin.wizard.transfer.import.no_file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,47 +1,51 @@
|
|||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { generateName } from "../lib/wizard";
|
||||
import {
|
||||
setWizardDefaults,
|
||||
default as wizardSchema,
|
||||
} from "../lib/wizard-schema";
|
||||
import { default as discourseComputed, on, observes } from 'discourse-common/utils/decorators';
|
||||
import { generateName } from '../lib/wizard';
|
||||
import { default as wizardSchema, setWizardDefaults } from '../lib/wizard-schema';
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import { scheduleOnce, bind } from "@ember/runloop";
|
||||
import EmberObject from "@ember/object";
|
||||
import Component from "@ember/component";
|
||||
import { A } from "@ember/array";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":wizard-links", "itemType"],
|
||||
classNameBindings: [':wizard-links', 'itemType'],
|
||||
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) {
|
||||
const items = this.items;
|
||||
const item = items.findBy("id", itemId);
|
||||
const item = items.findBy('id', itemId);
|
||||
items.removeObject(item);
|
||||
item.set("index", newIndex);
|
||||
items.insertAt(newIndex, item);
|
||||
scheduleOnce('afterRender', this, () => this.applySortable());
|
||||
},
|
||||
|
||||
@discourseComputed("itemType")
|
||||
@discourseComputed('itemType')
|
||||
header: (itemType) => `admin.wizard.${itemType}.header`,
|
||||
|
||||
@discourseComputed(
|
||||
"current",
|
||||
"items.@each.id",
|
||||
"items.@each.type",
|
||||
"items.@each.label",
|
||||
"items.@each.title"
|
||||
)
|
||||
@discourseComputed('current', 'items.@each.id', 'items.@each.type', 'items.@each.label', 'items.@each.title')
|
||||
links(current, items) {
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
if (!items) return;
|
||||
|
||||
return items.map((item, index) => {
|
||||
return items.map((item) => {
|
||||
if (item) {
|
||||
let link = {
|
||||
id: item.id,
|
||||
};
|
||||
id: item.id
|
||||
}
|
||||
|
||||
let label = item.label || item.title || item.id;
|
||||
if (this.generateLabels && item.type) {
|
||||
|
@ -50,49 +54,39 @@ export default Component.extend({
|
|||
|
||||
link.label = `${label} (${item.id})`;
|
||||
|
||||
let classes = "btn";
|
||||
let classes = 'btn';
|
||||
if (current && item.id === current.id) {
|
||||
classes += " btn-primary";
|
||||
}
|
||||
classes += ' btn-primary';
|
||||
};
|
||||
|
||||
link.classes = classes;
|
||||
link.index = index;
|
||||
|
||||
if (index === 0) {
|
||||
link.first = true;
|
||||
}
|
||||
|
||||
if (index === items.length - 1) {
|
||||
link.last = true;
|
||||
}
|
||||
|
||||
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: {
|
||||
add() {
|
||||
const items = this.get("items");
|
||||
const items = this.get('items');
|
||||
const itemType = this.itemType;
|
||||
let params = setWizardDefaults({}, itemType);
|
||||
|
||||
params.isNew = true;
|
||||
params.index = this.getNextIndex();
|
||||
|
||||
let id = `${itemType}_${params.index + 1}`;
|
||||
if (itemType === "field") {
|
||||
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') {
|
||||
id = `${this.parentId}_${id}`;
|
||||
}
|
||||
|
||||
|
@ -100,27 +94,19 @@ export default Component.extend({
|
|||
|
||||
let objectArrays = wizardSchema[itemType].objectArrays;
|
||||
if (objectArrays) {
|
||||
Object.keys(objectArrays).forEach((objectType) => {
|
||||
Object.keys(objectArrays).forEach(objectType => {
|
||||
params[objectArrays[objectType].property] = A();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const newItem = EmberObject.create(params);
|
||||
items.pushObject(newItem);
|
||||
|
||||
this.set("current", newItem);
|
||||
},
|
||||
|
||||
back(item) {
|
||||
this.updateItemOrder(item.id, item.index - 1);
|
||||
},
|
||||
|
||||
forward(item) {
|
||||
this.updateItemOrder(item.id, item.index + 1);
|
||||
this.set('current', newItem);
|
||||
},
|
||||
|
||||
change(itemId) {
|
||||
this.set("current", this.items.findBy("id", itemId));
|
||||
this.set('current', this.items.findBy('id', itemId));
|
||||
},
|
||||
|
||||
remove(itemId) {
|
||||
|
@ -137,14 +123,14 @@ export default Component.extend({
|
|||
|
||||
let nextIndex;
|
||||
if (this.current.id === itemId) {
|
||||
nextIndex = index < items.length - 2 ? index + 1 : index - 1;
|
||||
nextIndex = index < (items.length-2) ? (index+1) : (index-1);
|
||||
}
|
||||
|
||||
items.removeObject(item);
|
||||
|
||||
if (nextIndex) {
|
||||
this.set("current", items[nextIndex]);
|
||||
this.set('current', items[nextIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import Component from "@ember/component";
|
||||
import { gt } from "@ember/object/computed";
|
||||
import { gt } from '@ember/object/computed';
|
||||
import { computed } from "@ember/object";
|
||||
import { defaultConnector } from "../lib/wizard-mapper";
|
||||
import { defaultConnector } from '../lib/wizard-mapper';
|
||||
import { later } from "@ember/runloop";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [
|
||||
":mapper-connector",
|
||||
":mapper-block",
|
||||
"hasMultiple::single",
|
||||
],
|
||||
hasMultiple: gt("connectors.length", 1),
|
||||
classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'],
|
||||
hasMultiple: gt('connectors.length', 1),
|
||||
connectorLabel: computed(function() {
|
||||
let key = this.connector;
|
||||
let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`;
|
||||
|
@ -22,7 +19,7 @@ export default Component.extend({
|
|||
if (!this.connector) {
|
||||
later(() => {
|
||||
this.set(
|
||||
"connector",
|
||||
'connector',
|
||||
defaultConnector(this.connectorType, this.inputType, this.options)
|
||||
);
|
||||
});
|
||||
|
@ -31,8 +28,8 @@ export default Component.extend({
|
|||
|
||||
actions: {
|
||||
changeConnector(value) {
|
||||
this.set("connector", value);
|
||||
this.onUpdate("connector", this.connectorType);
|
||||
},
|
||||
},
|
||||
this.set('connector', value);
|
||||
this.onUpdate('connector', this.connectorType);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,49 +1,36 @@
|
|||
import { computed, set } from "@ember/object";
|
||||
import { alias, equal, not, or } from "@ember/object/computed";
|
||||
import {
|
||||
connectorContent,
|
||||
defaultConnector,
|
||||
defaultSelectionType,
|
||||
inputTypesContent,
|
||||
newPair,
|
||||
} from "../lib/wizard-mapper";
|
||||
import { alias, equal, or, not } from "@ember/object/computed";
|
||||
import { newPair, connectorContent, inputTypesContent, defaultSelectionType, defaultConnector } from '../lib/wizard-mapper';
|
||||
import Component from "@ember/component";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { A } from "@ember/array";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":mapper-input", "inputType"],
|
||||
inputType: alias("input.type"),
|
||||
isConditional: equal("inputType", "conditional"),
|
||||
isAssignment: equal("inputType", "assignment"),
|
||||
isAssociation: equal("inputType", "association"),
|
||||
isValidation: equal("inputType", "validation"),
|
||||
hasOutput: or("isConditional", "isAssignment"),
|
||||
hasPairs: or("isConditional", "isAssociation", "isValidation"),
|
||||
canAddPair: not("isAssignment"),
|
||||
connectors: computed(function () {
|
||||
return connectorContent("output", this.input.type, this.options);
|
||||
}),
|
||||
inputTypes: computed(function () {
|
||||
return inputTypesContent(this.options);
|
||||
}),
|
||||
classNameBindings: [':mapper-input', 'inputType'],
|
||||
inputType: alias('input.type'),
|
||||
isConditional: equal('inputType', 'conditional'),
|
||||
isAssignment: equal('inputType', 'assignment'),
|
||||
isAssociation: equal('inputType', 'association'),
|
||||
isValidation: equal('inputType', 'validation'),
|
||||
hasOutput: or('isConditional', 'isAssignment'),
|
||||
hasPairs: or('isConditional', 'isAssociation', 'isValidation'),
|
||||
canAddPair: not('isAssignment'),
|
||||
connectors: computed(function() { return connectorContent('output', this.input.type, this.options) }),
|
||||
inputTypes: computed(function() { return inputTypesContent(this.options) }),
|
||||
|
||||
@observes("input.type")
|
||||
@observes('input.type')
|
||||
setupType() {
|
||||
if (this.hasPairs && (!this.input.pairs || this.input.pairs.length < 1)) {
|
||||
this.send("addPair");
|
||||
this.send('addPair');
|
||||
}
|
||||
|
||||
if (this.hasOutput) {
|
||||
this.set("input.output", null);
|
||||
this.set('input.output', null);
|
||||
|
||||
if (!this.input.outputConnector) {
|
||||
const options = this.options;
|
||||
this.set("input.output_type", defaultSelectionType("output", options));
|
||||
this.set(
|
||||
"input.output_connector",
|
||||
defaultConnector("output", this.inputType, options)
|
||||
);
|
||||
this.set('input.output_type', defaultSelectionType('output', options));
|
||||
this.set('input.output_connector', defaultConnector('output', this.inputType, options));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -51,18 +38,22 @@ export default Component.extend({
|
|||
actions: {
|
||||
addPair() {
|
||||
if (!this.input.pairs) {
|
||||
this.set("input.pairs", A());
|
||||
this.set('input.pairs', A());
|
||||
}
|
||||
|
||||
const pairs = this.input.pairs;
|
||||
const pairCount = pairs.length + 1;
|
||||
|
||||
pairs.forEach((p) => set(p, "pairCount", pairCount));
|
||||
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
|
||||
|
||||
pairs.pushObject(
|
||||
newPair(
|
||||
this.input.type,
|
||||
Object.assign({}, this.options, { index: pairs.length, pairCount })
|
||||
Object.assign(
|
||||
{},
|
||||
this.options,
|
||||
{ index: pairs.length, pairCount }
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -71,8 +62,8 @@ export default Component.extend({
|
|||
const pairs = this.input.pairs;
|
||||
const pairCount = pairs.length - 1;
|
||||
|
||||
pairs.forEach((p) => set(p, "pairCount", pairCount));
|
||||
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
|
||||
pairs.removeObject(pair);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import { connectorContent } from "../lib/wizard-mapper";
|
||||
import { alias, gt } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import { connectorContent } from '../lib/wizard-mapper';
|
||||
import { gt, or, alias } from "@ember/object/computed";
|
||||
import { computed, observes } from "@ember/object";
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":mapper-pair", "hasConnector::no-connector"],
|
||||
firstPair: gt("pair.index", 0),
|
||||
showRemove: alias("firstPair"),
|
||||
showJoin: computed("pair.pairCount", function () {
|
||||
return this.pair.index < this.pair.pairCount - 1;
|
||||
}),
|
||||
connectors: computed(function () {
|
||||
return connectorContent("pair", this.inputType, this.options);
|
||||
}),
|
||||
classNameBindings: [':mapper-pair', 'hasConnector::no-connector'],
|
||||
firstPair: gt('pair.index', 0),
|
||||
showRemove: alias('firstPair'),
|
||||
showJoin: computed('pair.pairCount', function() { return this.pair.index < (this.pair.pairCount - 1) }),
|
||||
connectors: computed(function() { return connectorContent('pair', this.inputType, this.options) })
|
||||
});
|
|
@ -1,16 +1,14 @@
|
|||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseComputed from 'discourse-common/utils/decorators';
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "a",
|
||||
classNameBindings: ["active"],
|
||||
tagName: 'a',
|
||||
classNameBindings: ['active'],
|
||||
|
||||
@discourseComputed("item.type", "activeType")
|
||||
active(type, activeType) {
|
||||
return type === activeType;
|
||||
},
|
||||
@discourseComputed('item.type', 'activeType')
|
||||
active(type, activeType) { return type === activeType },
|
||||
|
||||
click() {
|
||||
this.toggle(this.item.type);
|
||||
},
|
||||
});
|
||||
this.toggle(this.item.type)
|
||||
}
|
||||
})
|
|
@ -1,176 +1,44 @@
|
|||
import { alias, gt, or } from "@ember/object/computed";
|
||||
import { alias, or, gt } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
|
||||
import {
|
||||
generateName,
|
||||
sentenceCase,
|
||||
snakeCase,
|
||||
userProperties,
|
||||
} from "../lib/wizard";
|
||||
import { default as discourseComputed, observes, on } from "discourse-common/utils/decorators";
|
||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||
import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper';
|
||||
import { snakeCase, generateName, userProperties } from '../lib/wizard';
|
||||
import Component from "@ember/component";
|
||||
import { bind, later } from "@ember/runloop";
|
||||
import I18n from "I18n";
|
||||
import Subscription from "../mixins/subscription";
|
||||
|
||||
const customFieldActionMap = {
|
||||
topic: ["create_topic", "send_message"],
|
||||
post: ["create_topic", "send_message"],
|
||||
category: ["create_category"],
|
||||
group: ["create_group"],
|
||||
user: ["update_profile"],
|
||||
};
|
||||
export default Component.extend({
|
||||
classNameBindings: [':mapper-selector', 'activeType'],
|
||||
|
||||
const values = ["present", "true", "false"];
|
||||
showText: computed('activeType', function() { return this.showInput('text') }),
|
||||
showWizardField: computed('activeType', function() { return this.showInput('wizardField') }),
|
||||
showUserField: computed('activeType', function() { return this.showInput('userField') }),
|
||||
showUserFieldOptions: computed('activeType', function() { return this.showInput('userFieldOptions') }),
|
||||
showCategory: computed('activeType', function() { return this.showInput('category') }),
|
||||
showTag: computed('activeType', function() { return this.showInput('tag') }),
|
||||
showGroup: computed('activeType', function() { return this.showInput('group') }),
|
||||
showUser: computed('activeType', function() { return this.showInput('user') }),
|
||||
showList: computed('activeType', function() { return this.showInput('list') }),
|
||||
textEnabled: computed('options.textSelection', 'inputType', function() { return this.optionEnabled('textSelection') }),
|
||||
wizardFieldEnabled: computed('options.wizardFieldSelection', 'inputType', function() { return this.optionEnabled('wizardFieldSelection') }),
|
||||
userFieldEnabled: computed('options.userFieldSelection', 'inputType', function() { return this.optionEnabled('userFieldSelection') }),
|
||||
userFieldOptionsEnabled: computed('options.userFieldOptionsSelection', 'inputType', function() { return this.optionEnabled('userFieldOptionsSelection') }),
|
||||
categoryEnabled: computed('options.categorySelection', 'inputType', function() { return this.optionEnabled('categorySelection') }),
|
||||
tagEnabled: computed('options.tagSelection', 'inputType', function() { return this.optionEnabled('tagSelection') }),
|
||||
groupEnabled: computed('options.groupSelection', 'inputType', function() { return this.optionEnabled('groupSelection') }),
|
||||
userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
|
||||
listEnabled: computed('options.listSelection', 'inputType', function() { return this.optionEnabled('listSelection') }),
|
||||
|
||||
export default Component.extend(Subscription, {
|
||||
classNameBindings: [":mapper-selector", "activeType"],
|
||||
|
||||
showText: computed("activeType", function () {
|
||||
return this.showInput("text");
|
||||
}),
|
||||
showWizardField: computed("activeType", function () {
|
||||
return this.showInput("wizardField");
|
||||
}),
|
||||
showWizardAction: computed("activeType", function () {
|
||||
return this.showInput("wizardAction");
|
||||
}),
|
||||
showUserField: computed("activeType", function () {
|
||||
return this.showInput("userField");
|
||||
}),
|
||||
showUserFieldOptions: computed("activeType", function () {
|
||||
return this.showInput("userFieldOptions");
|
||||
}),
|
||||
showCategory: computed("activeType", function () {
|
||||
return this.showInput("category");
|
||||
}),
|
||||
showTag: computed("activeType", function () {
|
||||
return this.showInput("tag");
|
||||
}),
|
||||
showGroup: computed("activeType", function () {
|
||||
return this.showInput("group");
|
||||
}),
|
||||
showUser: computed("activeType", function () {
|
||||
return this.showInput("user");
|
||||
}),
|
||||
showList: computed("activeType", function () {
|
||||
return this.showInput("list");
|
||||
}),
|
||||
showCustomField: computed("activeType", function () {
|
||||
return this.showInput("customField");
|
||||
}),
|
||||
showValue: computed("activeType", function () {
|
||||
return this.showInput("value");
|
||||
}),
|
||||
textEnabled: computed("options.textSelection", "inputType", function () {
|
||||
return this.optionEnabled("textSelection");
|
||||
}),
|
||||
wizardFieldEnabled: computed(
|
||||
"options.wizardFieldSelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("wizardFieldSelection");
|
||||
}
|
||||
),
|
||||
wizardActionEnabled: computed(
|
||||
"options.wizardActionSelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("wizardActionSelection");
|
||||
}
|
||||
),
|
||||
customFieldEnabled: computed(
|
||||
"options.customFieldSelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("customFieldSelection");
|
||||
}
|
||||
),
|
||||
userFieldEnabled: computed(
|
||||
"options.userFieldSelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("userFieldSelection");
|
||||
}
|
||||
),
|
||||
userFieldOptionsEnabled: computed(
|
||||
"options.userFieldOptionsSelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("userFieldOptionsSelection");
|
||||
}
|
||||
),
|
||||
categoryEnabled: computed(
|
||||
"options.categorySelection",
|
||||
"inputType",
|
||||
function () {
|
||||
return this.optionEnabled("categorySelection");
|
||||
}
|
||||
),
|
||||
tagEnabled: computed("options.tagSelection", "inputType", function () {
|
||||
return this.optionEnabled("tagSelection");
|
||||
}),
|
||||
groupEnabled: computed("options.groupSelection", "inputType", function () {
|
||||
return this.optionEnabled("groupSelection");
|
||||
}),
|
||||
guestGroup: computed("options.guestGroup", "inputType", function () {
|
||||
return this.optionEnabled("guestGroup");
|
||||
}),
|
||||
userEnabled: computed("options.userSelection", "inputType", function () {
|
||||
return this.optionEnabled("userSelection");
|
||||
}),
|
||||
listEnabled: computed("options.listSelection", "inputType", function () {
|
||||
return this.optionEnabled("listSelection");
|
||||
}),
|
||||
valueEnabled: computed("connector", function () {
|
||||
return this.connector === "is";
|
||||
}),
|
||||
|
||||
@discourseComputed("site.groups", "guestGroup", "subscriptionType")
|
||||
groups(groups, guestGroup, subscriptionType) {
|
||||
let result = groups;
|
||||
if (!guestGroup) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (["standard", "business"].includes(subscriptionType)) {
|
||||
let guestIndex;
|
||||
result.forEach((r, index) => {
|
||||
if (r.id === 0) {
|
||||
r.name = I18n.t("admin.wizard.selector.label.users");
|
||||
guestIndex = index;
|
||||
}
|
||||
});
|
||||
result.splice(guestIndex, 0, {
|
||||
id: -1,
|
||||
name: I18n.t("admin.wizard.selector.label.guests"),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
categories: alias("site.categories"),
|
||||
showComboBox: or(
|
||||
"showWizardField",
|
||||
"showWizardAction",
|
||||
"showUserField",
|
||||
"showUserFieldOptions",
|
||||
"showCustomField",
|
||||
"showValue"
|
||||
),
|
||||
showMultiSelect: or("showCategory", "showGroup"),
|
||||
hasTypes: gt("selectorTypes.length", 1),
|
||||
groups: alias('site.groups'),
|
||||
categories: alias('site.categories'),
|
||||
showComboBox: or('showWizardField', 'showUserField', 'showUserFieldOptions'),
|
||||
showMultiSelect: or('showCategory', 'showGroup'),
|
||||
hasTypes: gt('selectorTypes.length', 1),
|
||||
showTypes: false,
|
||||
|
||||
didInsertElement() {
|
||||
if (
|
||||
!this.activeType ||
|
||||
(this.activeType && !this[`${this.activeType}Enabled`])
|
||||
) {
|
||||
if (!this.activeType || (this.activeType && !this[`${this.activeType}Enabled`])) {
|
||||
later(() => this.resetActiveType());
|
||||
}
|
||||
|
||||
|
@ -182,165 +50,93 @@ export default Component.extend(Subscription, {
|
|||
},
|
||||
|
||||
documentClick(e) {
|
||||
if (this._state === "destroying") {
|
||||
return;
|
||||
}
|
||||
if (this._state == "destroying") return;
|
||||
let $target = $(e.target);
|
||||
|
||||
if (!$target.parents(".type-selector").length && this.showTypes) {
|
||||
this.set("showTypes", false);
|
||||
if (!$target.parents('.type-selector').length && this.showTypes) {
|
||||
this.set('showTypes', false);
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("connector")
|
||||
@discourseComputed
|
||||
selectorTypes() {
|
||||
return selectionTypes
|
||||
.filter((type) => this[`${type}Enabled`])
|
||||
.map((type) => ({ type, label: this.typeLabel(type) }));
|
||||
return selectionTypes.filter(type => (this[`${type}Enabled`]))
|
||||
.map(type => ({ type, label: this.typeLabel(type) }));
|
||||
},
|
||||
|
||||
@discourseComputed("activeType")
|
||||
@discourseComputed('activeType')
|
||||
activeTypeLabel(activeType) {
|
||||
return this.typeLabel(activeType);
|
||||
},
|
||||
|
||||
typeLabel(type) {
|
||||
return type
|
||||
? I18n.t(`admin.wizard.selector.label.${snakeCase(type)}`)
|
||||
: null;
|
||||
return type ? I18n.t(`admin.wizard.selector.label.${snakeCase(type)}`) : null;
|
||||
},
|
||||
|
||||
comboBoxAllowAny: or("showWizardField", "showWizardAction"),
|
||||
comboBoxAllowAny: alias('showWizardField'),
|
||||
|
||||
@discourseComputed
|
||||
showController() {
|
||||
return getOwner(this).lookup("controller:admin-wizards-wizard-show");
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"activeType",
|
||||
"showController.wizardFields.[]",
|
||||
"showController.wizard.actions.[]",
|
||||
"showController.userFields.[]",
|
||||
"showController.currentField.id",
|
||||
"showController.currentAction.id",
|
||||
"showController.customFields"
|
||||
)
|
||||
comboBoxContent(
|
||||
activeType,
|
||||
wizardFields,
|
||||
wizardActions,
|
||||
userFields,
|
||||
currentFieldId,
|
||||
currentActionId,
|
||||
customFields
|
||||
) {
|
||||
@discourseComputed('activeType')
|
||||
comboBoxContent(activeType) {
|
||||
const controller = getOwner(this).lookup('controller:admin-wizards-wizard-show');
|
||||
const wizardFields = controller.wizardFields;
|
||||
const userFields = controller.userFields;
|
||||
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;
|
||||
|
||||
if (context === "field") {
|
||||
content = content.filter((field) => field.id !== currentFieldId);
|
||||
if (this.options.context === 'field') {
|
||||
content = content.filter(field => field.id !== controller.currentField.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeType === "wizardAction") {
|
||||
content = wizardActions.map((a) => ({
|
||||
id: a.id,
|
||||
label: `${generateName(a.type)} (${a.id})`,
|
||||
type: a.type,
|
||||
}));
|
||||
|
||||
if (context === "action") {
|
||||
content = content.filter((a) => a.id !== currentActionId);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeType === "userField") {
|
||||
content = userProperties
|
||||
.map((f) => ({
|
||||
if (activeType === 'userField') {
|
||||
content = userProperties.map((f) => ({
|
||||
id: f,
|
||||
name: generateName(f),
|
||||
}))
|
||||
.concat(userFields || []);
|
||||
name: generateName(f)
|
||||
})).concat((userFields || []));
|
||||
|
||||
if (
|
||||
context === "action" &&
|
||||
this.inputType === "association" &&
|
||||
this.selectorType === "key"
|
||||
) {
|
||||
const excludedFields = ["username", "email", "trust_level"];
|
||||
content = content.filter(
|
||||
(userField) => excludedFields.indexOf(userField.id) === -1
|
||||
);
|
||||
if (this.options.context === 'action' &&
|
||||
this.inputType === 'association' &&
|
||||
this.selectorType === 'key') {
|
||||
|
||||
const excludedFields = ['username','email', 'trust_level'];
|
||||
content = content.filter(userField => excludedFields.indexOf(userField.id) === -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeType === "userFieldOptions") {
|
||||
if (activeType === 'userFieldOptions') {
|
||||
content = userFields;
|
||||
}
|
||||
|
||||
if (activeType === "customField") {
|
||||
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;
|
||||
},
|
||||
|
||||
@discourseComputed("activeType")
|
||||
@discourseComputed('activeType')
|
||||
multiSelectContent(activeType) {
|
||||
return {
|
||||
category: this.categories,
|
||||
group: this.groups,
|
||||
list: "",
|
||||
list: ''
|
||||
}[activeType];
|
||||
},
|
||||
|
||||
@discourseComputed("activeType", "inputType")
|
||||
placeholderKey(activeType) {
|
||||
if (
|
||||
activeType === "text" &&
|
||||
this.options[`${this.selectorType}Placeholder`]
|
||||
) {
|
||||
@discourseComputed('activeType', 'inputType')
|
||||
placeholderKey(activeType, inputType) {
|
||||
if (activeType === 'text' && this.options[`${this.selectorType}Placeholder`]) {
|
||||
return this.options[`${this.selectorType}Placeholder`];
|
||||
} else {
|
||||
return `admin.wizard.selector.placeholder.${snakeCase(activeType)}`;
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("activeType")
|
||||
@discourseComputed('activeType')
|
||||
multiSelectOptions(activeType) {
|
||||
let result = {
|
||||
none: this.placeholderKey,
|
||||
none: this.placeholderKey
|
||||
};
|
||||
|
||||
if (activeType === "list") {
|
||||
if (activeType === 'list') {
|
||||
result.allowAny = true;
|
||||
}
|
||||
|
||||
|
@ -349,20 +145,14 @@ export default Component.extend(Subscription, {
|
|||
|
||||
optionEnabled(type) {
|
||||
const options = this.options;
|
||||
if (!options) {
|
||||
return false;
|
||||
}
|
||||
if (!options) return false;
|
||||
|
||||
const option = options[type];
|
||||
if (option === true) {
|
||||
return true;
|
||||
}
|
||||
if (typeof option !== "string") {
|
||||
return false;
|
||||
}
|
||||
if (option === true) return true;
|
||||
if (typeof option !== 'string') return false;
|
||||
|
||||
return option.split(",").filter((o) => {
|
||||
return [this.selectorType, this.inputType].indexOf(o) !== -1;
|
||||
return option.split(',').filter(option => {
|
||||
return [this.selectorType, this.inputType].indexOf(option) !== -1;
|
||||
}).length;
|
||||
},
|
||||
|
||||
|
@ -371,28 +161,25 @@ export default Component.extend(Subscription, {
|
|||
},
|
||||
|
||||
changeValue(value) {
|
||||
this.set("value", value);
|
||||
this.onUpdate("selector", this.activeType);
|
||||
this.set('value', value);
|
||||
this.onUpdate('selector', this.activeType);
|
||||
},
|
||||
|
||||
@observes("inputType")
|
||||
@observes('inputType')
|
||||
resetActiveType() {
|
||||
this.set(
|
||||
"activeType",
|
||||
defaultSelectionType(this.selectorType, this.options, this.connector)
|
||||
);
|
||||
this.set('activeType', defaultSelectionType(this.selectorType, this.options));
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleType(type) {
|
||||
this.set("activeType", type);
|
||||
this.set("showTypes", false);
|
||||
this.set("value", null);
|
||||
this.onUpdate("selector");
|
||||
this.set('activeType', type);
|
||||
this.set('showTypes', false);
|
||||
this.set('value', null);
|
||||
this.onUpdate('selector');
|
||||
},
|
||||
|
||||
toggleTypes() {
|
||||
this.toggleProperty("showTypes");
|
||||
this.toggleProperty('showTypes');
|
||||
},
|
||||
|
||||
changeValue(value) {
|
||||
|
@ -403,8 +190,8 @@ export default Component.extend(Subscription, {
|
|||
this.changeValue(event.target.value);
|
||||
},
|
||||
|
||||
changeUserValue(value) {
|
||||
changeUserValue(previousValue, value) {
|
||||
this.changeValue(value);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,71 +1,69 @@
|
|||
import { newInput, selectionTypes } from "../lib/wizard-mapper";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||
import { newInput, selectionTypes } from '../lib/wizard-mapper';
|
||||
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
|
||||
import { later } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { A } from "@ember/array";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: "wizard-mapper",
|
||||
classNames: 'wizard-mapper',
|
||||
|
||||
didReceiveAttrs() {
|
||||
if (this.inputs && this.inputs.constructor !== Array) {
|
||||
later(() => this.set("inputs", null));
|
||||
later(() => this.set('inputs', null));
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("inputs.@each.type")
|
||||
@discourseComputed('inputs.@each.type')
|
||||
canAdd(inputs) {
|
||||
return (
|
||||
!inputs ||
|
||||
return !inputs ||
|
||||
inputs.constructor !== Array ||
|
||||
inputs.every((i) => {
|
||||
return ["assignment", "association"].indexOf(i.type) === -1;
|
||||
})
|
||||
);
|
||||
inputs.every(i => {
|
||||
return ['assignment','association'].indexOf(i.type) === -1;
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("options.@each.inputType")
|
||||
@discourseComputed('options.@each.inputType')
|
||||
inputOptions(options) {
|
||||
let result = {
|
||||
inputTypes: options.inputTypes || "assignment,conditional",
|
||||
inputConnector: options.inputConnector || "or",
|
||||
inputTypes: options.inputTypes || 'assignment,conditional',
|
||||
inputConnector: options.inputConnector || 'or',
|
||||
pairConnector: options.pairConnector || null,
|
||||
outputConnector: options.outputConnector || null,
|
||||
context: options.context || null,
|
||||
guestGroup: options.guestGroup || false,
|
||||
};
|
||||
context: options.context || null
|
||||
}
|
||||
|
||||
let inputTypes = ["key", "value", "output"];
|
||||
inputTypes.forEach((type) => {
|
||||
let inputTypes = ['key', 'value', 'output'];
|
||||
inputTypes.forEach(type => {
|
||||
result[`${type}Placeholder`] = options[`${type}Placeholder`] || null;
|
||||
result[`${type}DefaultSelection`] =
|
||||
options[`${type}DefaultSelection`] || null;
|
||||
result[`${type}DefaultSelection`] = options[`${type}DefaultSelection`] || null;
|
||||
});
|
||||
|
||||
selectionTypes.forEach((type) => {
|
||||
selectionTypes.forEach(type => {
|
||||
if (options[`${type}Selection`] !== undefined) {
|
||||
result[`${type}Selection`] = options[`${type}Selection`];
|
||||
result[`${type}Selection`] = options[`${type}Selection`]
|
||||
} else {
|
||||
result[`${type}Selection`] = type === "text" ? true : false;
|
||||
result[`${type}Selection`] = type === 'text' ? true : false;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
onUpdate() {},
|
||||
onUpdate() {
|
||||
},
|
||||
|
||||
actions: {
|
||||
add() {
|
||||
if (!this.get("inputs")) {
|
||||
this.set("inputs", A());
|
||||
if (!this.get('inputs')) {
|
||||
this.set('inputs', A());
|
||||
}
|
||||
|
||||
this.get("inputs").pushObject(
|
||||
this.get('inputs').pushObject(
|
||||
newInput(this.inputOptions, this.inputs.length)
|
||||
);
|
||||
|
||||
this.onUpdate(this.property, "input");
|
||||
this.onUpdate(this.property, 'input');
|
||||
},
|
||||
|
||||
remove(input) {
|
||||
|
@ -73,14 +71,14 @@ export default Component.extend({
|
|||
inputs.removeObject(input);
|
||||
|
||||
if (inputs.length) {
|
||||
inputs[0].set("connector", null);
|
||||
inputs[0].set('connector', null);
|
||||
}
|
||||
|
||||
this.onUpdate(this.property, "input");
|
||||
this.onUpdate(this.property, 'input');
|
||||
},
|
||||
|
||||
inputUpdated(component, type) {
|
||||
this.onUpdate(this.property, component, type);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,33 +1,17 @@
|
|||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { not, notEmpty } from "@ember/object/computed";
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
|
||||
const icons = {
|
||||
error: "times-circle",
|
||||
success: "check-circle",
|
||||
warn: "exclamation-circle",
|
||||
info: "info-circle",
|
||||
};
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":wizard-message", "type", "loading"],
|
||||
showDocumentation: not("loading"),
|
||||
showIcon: not("loading"),
|
||||
hasItems: notEmpty("items"),
|
||||
classNames: 'wizard-message',
|
||||
|
||||
@discourseComputed("type")
|
||||
icon(type) {
|
||||
return icons[type] || "info-circle";
|
||||
@discourseComputed('key', 'component')
|
||||
message(key, component) {
|
||||
return I18n.t(`admin.wizard.message.${component}.${key}`);
|
||||
},
|
||||
|
||||
@discourseComputed("key", "component", "opts")
|
||||
message(key, component, opts) {
|
||||
return I18n.t(`admin.wizard.message.${component}.${key}`, opts || {});
|
||||
},
|
||||
|
||||
@discourseComputed("component")
|
||||
@discourseComputed('component')
|
||||
documentation(component) {
|
||||
return I18n.t(`admin.wizard.message.${component}.documentation`);
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
|
@ -1,58 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import EmberObject from "@ember/object";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
import Category from "discourse/models/category";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["realtime-validations", "setting", "full", "subscription"],
|
||||
|
||||
@discourseComputed
|
||||
timeUnits() {
|
||||
return ["days", "weeks", "months", "years"].map((unit) => {
|
||||
return {
|
||||
id: unit,
|
||||
name: I18n.t(`admin.wizard.field.validations.time_units.${unit}`),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
if (!this.validations) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.field.validations) {
|
||||
const validations = {};
|
||||
|
||||
this.validations.forEach((validation) => {
|
||||
validations[validation] = {};
|
||||
});
|
||||
|
||||
this.set("field.validations", EmberObject.create(validations));
|
||||
}
|
||||
|
||||
const validationBuffer = cloneJSON(this.get("field.validations"));
|
||||
let bufferCategories;
|
||||
if (
|
||||
validationBuffer.similar_topics &&
|
||||
(bufferCategories = validationBuffer.similar_topics.categories)
|
||||
) {
|
||||
const categories = Category.findByIds(bufferCategories);
|
||||
validationBuffer.similar_topics.categories = categories;
|
||||
}
|
||||
this.set("validationBuffer", validationBuffer);
|
||||
},
|
||||
|
||||
actions: {
|
||||
updateValidationCategories(type, validation, categories) {
|
||||
this.set(`validationBuffer.${type}.categories`, categories);
|
||||
this.set(
|
||||
`field.validations.${type}.categories`,
|
||||
categories.map((category) => category.id)
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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}`;
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,68 +1,58 @@
|
|||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { default as discourseComputed, on } from 'discourse-common/utils/decorators';
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import { userProperties } from "../lib/wizard";
|
||||
import { userProperties } from '../lib/wizard';
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
|
||||
const excludedUserProperties = ["profile_background", "card_background"];
|
||||
|
||||
export default Component.extend({
|
||||
classNames: "wizard-text-editor",
|
||||
classNames: 'wizard-text-editor',
|
||||
barEnabled: true,
|
||||
previewEnabled: true,
|
||||
fieldsEnabled: true,
|
||||
hasWizardFields: notEmpty("wizardFieldList"),
|
||||
hasWizardActions: notEmpty("wizardActionList"),
|
||||
hasWizardFields: notEmpty('wizardFieldList'),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (!this.barEnabled) {
|
||||
scheduleOnce("afterRender", () => {
|
||||
$(this.element).find(".d-editor-button-bar").addClass("hidden");
|
||||
scheduleOnce('afterRender', () => {
|
||||
$(this.element).find('.d-editor-button-bar').addClass('hidden');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("forcePreview")
|
||||
@discourseComputed('forcePreview')
|
||||
previewLabel(forcePreview) {
|
||||
return I18n.t("admin.wizard.editor.preview", {
|
||||
action: I18n.t(`admin.wizard.editor.${forcePreview ? "hide" : "show"}`),
|
||||
action: I18n.t(`admin.wizard.editor.${forcePreview ? 'hide' : 'show'}`)
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("showPopover")
|
||||
@discourseComputed('showPopover')
|
||||
popoverLabel(showPopover) {
|
||||
return I18n.t("admin.wizard.editor.popover", {
|
||||
action: I18n.t(`admin.wizard.editor.${showPopover ? "hide" : "show"}`),
|
||||
action: I18n.t(`admin.wizard.editor.${showPopover ? 'hide' : 'show'}`)
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
userPropertyList() {
|
||||
return userProperties
|
||||
.filter((f) => !excludedUserProperties.includes(f))
|
||||
.map((f) => ` u{${f}}`);
|
||||
return userProperties.map((f) => ` u{${f}}`);
|
||||
},
|
||||
|
||||
@discourseComputed("wizardFields")
|
||||
@discourseComputed('wizardFields')
|
||||
wizardFieldList(wizardFields) {
|
||||
return (wizardFields || []).map((f) => ` w{${f.id}}`);
|
||||
},
|
||||
|
||||
@discourseComputed("wizardActions")
|
||||
wizardActionList(wizardActions) {
|
||||
return (wizardActions || []).map((a) => ` w{${a.id}}`);
|
||||
return wizardFields.map((f) => ` w{${f.id}}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
togglePreview() {
|
||||
this.toggleProperty("forcePreview");
|
||||
this.toggleProperty('forcePreview');
|
||||
},
|
||||
|
||||
togglePopover() {
|
||||
this.toggleProperty("showPopover");
|
||||
},
|
||||
},
|
||||
this.toggleProperty('showPopover');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import ValueList from "admin/components/value-list";
|
||||
import ValueList from 'admin/components/value-list';
|
||||
|
||||
export default ValueList.extend({
|
||||
_saveValues() {
|
||||
|
@ -8,5 +8,5 @@ export default ValueList.extend({
|
|||
}
|
||||
|
||||
this.onChange(this.collection.join(this.inputDelimiter || "\n"));
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
|
@ -1,7 +1,3 @@
|
|||
{{#if currentUser.admin}}
|
||||
{{nav-item route="adminWizards" label="admin.wizard.nav_label"}}
|
||||
|
||||
{{#if wizardErrorNotice}}
|
||||
{{d-icon "exclaimation-circle"}}
|
||||
{{/if}}
|
||||
{{nav-item route='adminWizards' label='admin.wizard.nav_label'}}
|
||||
{{/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|}}
|
||||
<div class="row">
|
||||
<div class="alert alert-info alert-wizard">
|
||||
<a href={{wizard.url}}>{{i18n
|
||||
"wizard.complete_custom"
|
||||
name=wizard.name
|
||||
}}</a>
|
||||
<div class='row'>
|
||||
<div class='alert alert-info alert-wizard'>
|
||||
<a href="{{wizard.url}}">{{i18n 'wizard.complete_custom' name=wizard.name}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
shouldRender(_, ctx) {
|
||||
return (
|
||||
ctx.siteSettings.custom_wizard_enabled && ctx.site.complete_custom_wizard
|
||||
);
|
||||
},
|
||||
};
|
||||
return ctx.siteSettings.custom_wizard_enabled &&
|
||||
ctx.site.complete_custom_wizard;
|
||||
}
|
||||
}
|
|
@ -1,130 +1,93 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import CustomWizardApi from "../models/custom-wizard-api";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import { and, equal, not } from "@ember/object/computed";
|
||||
import { selectKitContent } from "../lib/wizard";
|
||||
import { underscore } from "@ember/string";
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import CustomWizardApi from '../models/custom-wizard-api';
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { not, and, equal } from "@ember/object/computed";
|
||||
import { selectKitContent } from '../lib/wizard';
|
||||
import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Controller.extend({
|
||||
router: service(),
|
||||
|
||||
queryParams: ["refresh_list"],
|
||||
queryParams: ['refresh_list'],
|
||||
loadingSubscriptions: false,
|
||||
notAuthorized: not("api.authorized"),
|
||||
endpointMethods: selectKitContent(["PUT", "POST", "PATCH", "DELETE"]),
|
||||
showRemove: not("isNew"),
|
||||
showRedirectUri: and("threeLeggedOauth", "api.name"),
|
||||
notAuthorized: not('api.authorized'),
|
||||
endpointMethods: selectKitContent(['GET', 'PUT', 'POST', 'PATCH', 'DELETE']),
|
||||
showRemove: not('isNew'),
|
||||
showRedirectUri: and('threeLeggedOauth', 'api.name'),
|
||||
responseIcon: null,
|
||||
contentTypes: selectKitContent([
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
]),
|
||||
successCodes: selectKitContent([
|
||||
100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301,
|
||||
302, 303, 303, 304, 305, 306, 307, 308,
|
||||
]),
|
||||
contentTypes: selectKitContent(['application/json', 'application/x-www-form-urlencoded']),
|
||||
successCodes: selectKitContent([100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 303, 304, 305, 306, 307, 308]),
|
||||
|
||||
@discourseComputed(
|
||||
"saveDisabled",
|
||||
"api.authType",
|
||||
"api.authUrl",
|
||||
"api.tokenUrl",
|
||||
"api.clientId",
|
||||
"api.clientSecret",
|
||||
"threeLeggedOauth"
|
||||
)
|
||||
authDisabled(
|
||||
saveDisabled,
|
||||
authType,
|
||||
authUrl,
|
||||
tokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
threeLeggedOauth
|
||||
) {
|
||||
if (saveDisabled || !authType || !tokenUrl || !clientId || !clientSecret) {
|
||||
return true;
|
||||
}
|
||||
if (threeLeggedOauth) {
|
||||
return !authUrl;
|
||||
}
|
||||
@discourseComputed('saveDisabled', 'api.authType', 'api.authUrl', 'api.tokenUrl', 'api.clientId', 'api.clientSecret', 'threeLeggedOauth')
|
||||
authDisabled(saveDisabled, authType, authUrl, tokenUrl, clientId, clientSecret, threeLeggedOauth) {
|
||||
if (saveDisabled || !authType || !tokenUrl || !clientId || !clientSecret) return true;
|
||||
if (threeLeggedOauth) return !authUrl;
|
||||
return false;
|
||||
},
|
||||
|
||||
@discourseComputed("api.name", "api.authType")
|
||||
@discourseComputed('api.name', 'api.authType')
|
||||
saveDisabled(name, authType) {
|
||||
return !name || !authType;
|
||||
},
|
||||
|
||||
authorizationTypes: selectKitContent(["none", "basic", "oauth_2", "oauth_3"]),
|
||||
isBasicAuth: equal("api.authType", "basic"),
|
||||
authorizationTypes: selectKitContent(['none', 'basic', 'oauth_2', 'oauth_3']),
|
||||
isBasicAuth: equal('api.authType', 'basic'),
|
||||
|
||||
@discourseComputed("api.authType")
|
||||
@discourseComputed('api.authType')
|
||||
isOauth(authType) {
|
||||
return authType && authType.indexOf("oauth") > -1;
|
||||
return authType && authType.indexOf('oauth') > -1;
|
||||
},
|
||||
|
||||
twoLeggedOauth: equal("api.authType", "oauth_2"),
|
||||
threeLeggedOauth: equal("api.authType", "oauth_3"),
|
||||
|
||||
@discourseComputed("api.isNew")
|
||||
nameClass(isNew) {
|
||||
return isNew ? "new" : "saved";
|
||||
},
|
||||
twoLeggedOauth: equal('api.authType', 'oauth_2'),
|
||||
threeLeggedOauth: equal('api.authType', 'oauth_3'),
|
||||
|
||||
actions: {
|
||||
addParam() {
|
||||
this.get("api.authParams").pushObject({});
|
||||
this.get('api.authParams').pushObject({});
|
||||
},
|
||||
|
||||
removeParam(param) {
|
||||
this.get("api.authParams").removeObject(param);
|
||||
this.get('api.authParams').removeObject(param);
|
||||
},
|
||||
|
||||
addEndpoint() {
|
||||
this.get("api.endpoints").pushObject({});
|
||||
this.get('api.endpoints').pushObject({});
|
||||
},
|
||||
|
||||
removeEndpoint(endpoint) {
|
||||
this.get("api.endpoints").removeObject(endpoint);
|
||||
this.get('api.endpoints').removeObject(endpoint);
|
||||
},
|
||||
|
||||
authorize() {
|
||||
const api = this.get("api");
|
||||
const api = this.get('api');
|
||||
const { name, authType, authUrl, authParams } = api;
|
||||
|
||||
this.set("authErrorMessage", "");
|
||||
this.set('authErrorMessage', '');
|
||||
|
||||
if (authType === "oauth_2") {
|
||||
this.set("authorizing", true);
|
||||
ajax(`/admin/wizards/apis/${underscore(name)}/authorize`)
|
||||
.catch(popupAjaxError)
|
||||
.then((result) => {
|
||||
if (authType === 'oauth_2') {
|
||||
this.set('authorizing', true);
|
||||
ajax(`/admin/wizards/apis/${name.underscore()}/authorize`).catch(popupAjaxError)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
this.set("api", CustomWizardApi.create(result.api));
|
||||
this.set('api', CustomWizardApi.create(result.api));
|
||||
} else if (result.failed && result.message) {
|
||||
this.set("authErrorMessage", result.message);
|
||||
this.set('authErrorMessage', result.message);
|
||||
} else {
|
||||
this.set("authErrorMessage", "Authorization Failed");
|
||||
this.set('authErrorMessage', 'Authorization Failed');
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.set("authErrorMessage", "");
|
||||
this.set('authErrorMessage', '');
|
||||
}, 6000);
|
||||
})
|
||||
.finally(() => this.set("authorizing", false));
|
||||
} else if (authType === "oauth_3") {
|
||||
let query = "?";
|
||||
}).finally(() => this.set('authorizing', false));
|
||||
} else if (authType === 'oauth_3') {
|
||||
let query = '?';
|
||||
|
||||
query += `client_id=${api.clientId}`;
|
||||
query += `&redirect_uri=${encodeURIComponent(api.redirectUri)}`;
|
||||
query += `&response_type=code`;
|
||||
|
||||
if (authParams) {
|
||||
authParams.forEach((p) => {
|
||||
authParams.forEach(p => {
|
||||
query += `&${p.key}=${encodeURIComponent(p.value)}`;
|
||||
});
|
||||
}
|
||||
|
@ -134,129 +97,119 @@ export default Controller.extend({
|
|||
},
|
||||
|
||||
save() {
|
||||
const api = this.get("api");
|
||||
const api = this.get('api');
|
||||
const name = api.name;
|
||||
const authType = api.authType;
|
||||
let refreshList = false;
|
||||
let error;
|
||||
|
||||
if (!name || !authType) {
|
||||
return;
|
||||
}
|
||||
if (!name || !authType) return;
|
||||
|
||||
let data = {
|
||||
auth_type: authType,
|
||||
auth_type: authType
|
||||
};
|
||||
|
||||
if (api.title) {
|
||||
data["title"] = api.title;
|
||||
if (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")) {
|
||||
data["new"] = true;
|
||||
}
|
||||
if (api.get('isNew')) {
|
||||
data['new'] = true;
|
||||
};
|
||||
|
||||
let requiredParams;
|
||||
|
||||
if (authType === "basic") {
|
||||
requiredParams = ["username", "password"];
|
||||
} else if (authType === "oauth_2") {
|
||||
requiredParams = ["tokenUrl", "clientId", "clientSecret"];
|
||||
} else if (authType === "oauth_3") {
|
||||
requiredParams = ["authUrl", "tokenUrl", "clientId", "clientSecret"];
|
||||
if (authType === 'basic') {
|
||||
requiredParams = ['username', 'password'];
|
||||
} else if (authType === 'oauth_2') {
|
||||
requiredParams = ['tokenUrl', 'clientId', 'clientSecret'];
|
||||
} else if (authType === 'oauth_3') {
|
||||
requiredParams = ['authUrl', 'tokenUrl', 'clientId', 'clientSecret'];
|
||||
}
|
||||
|
||||
if (requiredParams) {
|
||||
for (let rp of requiredParams) {
|
||||
if (!api[rp]) {
|
||||
let key = rp.replace("auth", "");
|
||||
error = `${I18n.t(
|
||||
`admin.wizard.api.auth.${underscore(key)}`
|
||||
)} is required for ${authType}`;
|
||||
let key = rp.replace('auth', '');
|
||||
error = `${I18n.t(`admin.wizard.api.auth.${key.underscore()}`)} is required for ${authType}`;
|
||||
break;
|
||||
}
|
||||
data[underscore(rp)] = api[rp];
|
||||
data[rp.underscore()] = api[rp];
|
||||
}
|
||||
}
|
||||
|
||||
const params = api.authParams;
|
||||
if (params.length) {
|
||||
data["auth_params"] = JSON.stringify(params);
|
||||
data['auth_params'] = JSON.stringify(params);
|
||||
}
|
||||
|
||||
const endpoints = api.endpoints;
|
||||
if (endpoints.length) {
|
||||
for (let e of endpoints) {
|
||||
if (!e.name) {
|
||||
error = "Every endpoint must have a name";
|
||||
error = 'Every endpoint must have a name';
|
||||
break;
|
||||
}
|
||||
}
|
||||
data["endpoints"] = JSON.stringify(endpoints);
|
||||
data['endpoints'] = JSON.stringify(endpoints);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
this.set("error", error);
|
||||
this.set('error', error);
|
||||
setTimeout(() => {
|
||||
this.set("error", "");
|
||||
this.set('error', '');
|
||||
}, 6000);
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("updating", true);
|
||||
this.set('updating', true);
|
||||
|
||||
ajax(`/admin/wizards/api/${underscore(name)}`, {
|
||||
type: "PUT",
|
||||
data,
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.then((result) => {
|
||||
ajax(`/admin/wizards/api/${name.underscore()}`, {
|
||||
type: 'PUT',
|
||||
data
|
||||
}).catch(popupAjaxError)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
this.send("afterSave", result.api.name);
|
||||
this.send('afterSave', result.api.name);
|
||||
} else {
|
||||
this.set("responseIcon", "times");
|
||||
this.set('responseIcon', 'times');
|
||||
}
|
||||
})
|
||||
.finally(() => this.set("updating", false));
|
||||
}).finally(() => this.set('updating', false));
|
||||
},
|
||||
|
||||
remove() {
|
||||
const name = this.get("api.name");
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const name = this.get('api.name');
|
||||
if (!name) return;
|
||||
|
||||
this.set("updating", true);
|
||||
this.set('updating', true);
|
||||
|
||||
ajax(`/admin/wizards/api/${underscore(name)}`, {
|
||||
type: "DELETE",
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.then((result) => {
|
||||
ajax(`/admin/wizards/api/${name.underscore()}`, {
|
||||
type: 'DELETE'
|
||||
}).catch(popupAjaxError)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
this.send("afterDestroy");
|
||||
this.send('afterDestroy');
|
||||
}
|
||||
})
|
||||
.finally(() => this.set("updating", false));
|
||||
}).finally(() => this.set('updating', false));
|
||||
},
|
||||
|
||||
clearLogs() {
|
||||
const name = this.get("api.name");
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const name = this.get('api.name');
|
||||
if (!name) return;
|
||||
|
||||
ajax(`/admin/wizards/api/${underscore(name)}/logs`, {
|
||||
type: "DELETE",
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.then((result) => {
|
||||
ajax(`/admin/wizards/api/${name.underscore()}/logs`, {
|
||||
type: 'DELETE'
|
||||
}).catch(popupAjaxError)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
this.router.transitionTo("adminWizardsApis").then(() => {
|
||||
this.send("refreshModel");
|
||||
this.transitionToRoute('adminWizardsApis').then(() => {
|
||||
this.send('refreshModel');
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => this.set("updating", false));
|
||||
},
|
||||
},
|
||||
}).finally(() => this.set('updating', false));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import Controller from "@ember/controller";
|
||||
import CustomWizardCustomField from "../models/custom-wizard-custom-field";
|
||||
|
||||
export default Controller.extend({
|
||||
messageKey: "create",
|
||||
fieldKeys: ["klass", "type", "name", "serializers"],
|
||||
documentationUrl: "https://discourse.pluginmanager.org/t/custom-fields",
|
||||
|
||||
actions: {
|
||||
addField() {
|
||||
this.get("customFields").unshiftObject(
|
||||
CustomWizardCustomField.create({ edit: true })
|
||||
);
|
||||
},
|
||||
|
||||
saveField(field) {
|
||||
return CustomWizardCustomField.saveField(field).then((result) => {
|
||||
if (result.success) {
|
||||
this.setProperties({
|
||||
messageKey: "saved",
|
||||
messageType: "success",
|
||||
});
|
||||
} else {
|
||||
if (result.messages) {
|
||||
this.setProperties({
|
||||
messageKey: "error",
|
||||
messageType: "error",
|
||||
messageOpts: { messages: result.messages },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() =>
|
||||
this.setProperties({
|
||||
messageKey: "create",
|
||||
messageType: null,
|
||||
messageOpts: null,
|
||||
}),
|
||||
10000
|
||||
);
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
removeField(field) {
|
||||
return CustomWizardCustomField.destroyField(field).then(() => {
|
||||
this.get("customFields").removeObject(field);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden …
In neuem Issue referenzieren