Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-26 11:00:28 +01:00
Commit
6ad821024a
14 geänderte Dateien mit 339 neuen und 5 gelöschten Zeilen
42
assets/javascripts/discourse/components/wizard-export.js.es6
Normale Datei
42
assets/javascripts/discourse/components/wizard-export.js.es6
Normale Datei
|
@ -0,0 +1,42 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['container', 'export'],
|
||||||
|
selected: Ember.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
81
assets/javascripts/discourse/components/wizard-import.js.es6
Normale Datei
81
assets/javascripts/discourse/components/wizard-import.js.es6
Normale Datei
|
@ -0,0 +1,81 @@
|
||||||
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['container', 'import'],
|
||||||
|
hasLogs: Ember.computed.notEmpty('logs'),
|
||||||
|
|
||||||
|
@computed('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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
export default Ember.Controller.extend();
|
|
@ -11,6 +11,9 @@ export default {
|
||||||
this.route('adminWizardsApis', { path: '/apis', resetNamespace: true }, function() {
|
this.route('adminWizardsApis', { path: '/apis', resetNamespace: true }, function() {
|
||||||
this.route('adminWizardsApi', { path: '/:name', resetNamespace: true });
|
this.route('adminWizardsApi', { path: '/:name', resetNamespace: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.route('adminWizardsTransfer', { path: '/transfer', resetNamespace: true });
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import CustomWizard from '../models/custom-wizard';
|
||||||
|
|
||||||
|
export default Discourse.Route.extend({
|
||||||
|
model() {
|
||||||
|
return CustomWizard.all();
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,2 @@
|
||||||
|
{{wizard-export wizards=model}}
|
||||||
|
{{wizard-import}}
|
|
@ -2,6 +2,7 @@
|
||||||
{{nav-item route='adminWizardsCustom' label='admin.wizard.custom_label'}}
|
{{nav-item route='adminWizardsCustom' label='admin.wizard.custom_label'}}
|
||||||
{{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
|
{{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
|
||||||
{{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
|
{{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
|
||||||
|
{{nav-item route='adminWizardsTransfer' label='admin.wizard.transfer.nav_label'}}
|
||||||
{{/admin-nav}}
|
{{/admin-nav}}
|
||||||
|
|
||||||
<div class="admin-container">
|
<div class="admin-container">
|
||||||
|
|
25
assets/javascripts/discourse/templates/components/wizard-export.hbs
Normale Datei
25
assets/javascripts/discourse/templates/components/wizard-export.hbs
Normale Datei
|
@ -0,0 +1,25 @@
|
||||||
|
<h2>{{i18n 'admin.wizard.transfer.export.label'}}</h2>
|
||||||
|
|
||||||
|
<ul class="wizard-list-select">
|
||||||
|
{{#each wizards as |w|}}
|
||||||
|
<li>
|
||||||
|
{{input type="checkbox"
|
||||||
|
id=(dasherize w.id)
|
||||||
|
change=(action 'checkChanged')}}
|
||||||
|
{{#link-to "adminWizard" (dasherize w.id)}}
|
||||||
|
{{w.name}}
|
||||||
|
{{/link-to}}
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{d-button id="export-button"
|
||||||
|
class="btn btn-primary side"
|
||||||
|
label="admin.wizard.transfer.export.label"
|
||||||
|
action=(action "export")}}
|
||||||
|
|
||||||
|
{{#if exportMessage}}
|
||||||
|
<div class="export-message">
|
||||||
|
{{exportMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
32
assets/javascripts/discourse/templates/components/wizard-import.hbs
Normale Datei
32
assets/javascripts/discourse/templates/components/wizard-import.hbs
Normale Datei
|
@ -0,0 +1,32 @@
|
||||||
|
<h2>{{i18n 'admin.wizard.transfer.import.label'}}</h2>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
{{input id='file-url' type="file" change=(action "setFilePath")}}
|
||||||
|
|
||||||
|
{{#if importMessage}}
|
||||||
|
<div class="import-message">
|
||||||
|
{{importMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{d-button id="import-button"
|
||||||
|
class="btn btn-primary side"
|
||||||
|
label="admin.wizard.transfer.import.label"
|
||||||
|
action=(action "import")}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if hasLogs}}
|
||||||
|
<div class="import-logs">
|
||||||
|
<div class="title">
|
||||||
|
{{i18n 'admin.wizard.transfer.import.logs' fileName=fileName}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{{#each logs as |l|}}
|
||||||
|
<li class="import-log">
|
||||||
|
{{i18n (concat 'admin.wizard.transfer.import.' l.type) id=l.id}}
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -480,3 +480,40 @@
|
||||||
.wizard-step-contents{
|
.wizard-step-contents{
|
||||||
height: unset !important;
|
height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tansfer tab
|
||||||
|
|
||||||
|
.admin-wizards-transfer .admin-container .container {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-url {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-list-select {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-message {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-logs {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 800;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,19 @@ en:
|
||||||
log:
|
log:
|
||||||
label: "Logs"
|
label: "Logs"
|
||||||
|
|
||||||
|
transfer:
|
||||||
|
nav_label: "Transfer"
|
||||||
|
export:
|
||||||
|
label: "Export"
|
||||||
|
none_selected: "Please select atleast one wizard"
|
||||||
|
import:
|
||||||
|
label: "Import"
|
||||||
|
logs: "Import logs for {{fileName}}"
|
||||||
|
success: 'Wizard "{{id}}" saved successfully'
|
||||||
|
failure: 'Wizard "{{id}}" could not be saved'
|
||||||
|
no_file: "Please choose a file to import"
|
||||||
|
file_size_error: "The file must be JSON and 512kb or less"
|
||||||
|
|
||||||
wizard_js:
|
wizard_js:
|
||||||
location:
|
location:
|
||||||
name:
|
name:
|
||||||
|
|
|
@ -10,6 +10,15 @@ en:
|
||||||
too_short: "%{label} must be at least %{min} characters"
|
too_short: "%{label} must be at least %{min} characters"
|
||||||
none: "We couldn't find a wizard at that address."
|
none: "We couldn't find a wizard at that address."
|
||||||
no_skip: "Wizard can't be skipped"
|
no_skip: "Wizard can't be skipped"
|
||||||
|
export:
|
||||||
|
error:
|
||||||
|
select_one: "Please select atleast one wizard"
|
||||||
|
import:
|
||||||
|
error:
|
||||||
|
no_file: "No file selected"
|
||||||
|
file_large: "File too large"
|
||||||
|
invalid_json: "File is not a valid json file"
|
||||||
|
no_valid_wizards: "File doesn't contain any valid wizards"
|
||||||
|
|
||||||
site_settings:
|
site_settings:
|
||||||
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
|
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
|
||||||
|
|
74
controllers/transfer.rb
Normale Datei
74
controllers/transfer.rb
Normale Datei
|
@ -0,0 +1,74 @@
|
||||||
|
class CustomWizard::TransferController < ::ApplicationController
|
||||||
|
before_action :ensure_logged_in
|
||||||
|
before_action :ensure_admin
|
||||||
|
skip_before_action :check_xhr, :only => [:export]
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def export
|
||||||
|
wizards = params['wizards']
|
||||||
|
wizard_objects = []
|
||||||
|
|
||||||
|
if wizards.nil?
|
||||||
|
render json: { error: I18n.t('wizard.export.error.select_one') }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
wizards.each do |w|
|
||||||
|
wizard_objects.push(PluginStore.get('custom_wizard', w.tr('-', '_')))
|
||||||
|
end
|
||||||
|
|
||||||
|
send_data wizard_objects.to_json,
|
||||||
|
type: "application/json",
|
||||||
|
disposition: 'attachment',
|
||||||
|
filename: 'wizards.json'
|
||||||
|
end
|
||||||
|
|
||||||
|
def import
|
||||||
|
file = File.read(params['file'].tempfile)
|
||||||
|
|
||||||
|
if file.nil?
|
||||||
|
render json: { error: I18n.t('wizard.import.error.no_file') }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
fileSize = file.size
|
||||||
|
maxFileSize = 512 * 1024
|
||||||
|
|
||||||
|
if maxFileSize < fileSize
|
||||||
|
render json: { error: I18n.t('wizard.import.error.file_large') }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
jsonObject = JSON.parse file
|
||||||
|
rescue JSON::ParserError
|
||||||
|
render json: { error: I18n.t('wizard.import.error.invalid_json') }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
countValid = 0
|
||||||
|
success_ids = []
|
||||||
|
failed_ids = []
|
||||||
|
|
||||||
|
jsonObject.each do |o|
|
||||||
|
if !CustomWizard::Template.new(o)
|
||||||
|
failed_ids.push o['id']
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
countValid += 1
|
||||||
|
pluginStoreEntry = PluginStore.new 'custom_wizard'
|
||||||
|
saved = pluginStoreEntry.set(o['id'], o) unless pluginStoreEntry.get(o['id'])
|
||||||
|
success_ids.push o['id'] if !!saved
|
||||||
|
failed_ids.push o['id'] if !saved
|
||||||
|
end
|
||||||
|
|
||||||
|
if countValid == 0
|
||||||
|
render json: { error: I18n.t('wizard.import.error.no_valid_wizards') }
|
||||||
|
else
|
||||||
|
render json: { success: success_ids, failed: failed_ids }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
plugin.rb
13
plugin.rb
|
@ -12,6 +12,7 @@ config = Rails.application.config
|
||||||
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts')
|
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts')
|
||||||
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard')
|
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard')
|
||||||
|
|
||||||
|
|
||||||
if Rails.env.production?
|
if Rails.env.production?
|
||||||
config.assets.precompile += %w{
|
config.assets.precompile += %w{
|
||||||
wizard-custom-lib.js
|
wizard-custom-lib.js
|
||||||
|
@ -75,6 +76,10 @@ after_initialize do
|
||||||
delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs'
|
delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs'
|
||||||
get 'admin/wizards/apis/:name/redirect' => 'api#redirect'
|
get 'admin/wizards/apis/:name/redirect' => 'api#redirect'
|
||||||
get 'admin/wizards/apis/:name/authorize' => 'api#authorize'
|
get 'admin/wizards/apis/:name/authorize' => 'api#authorize'
|
||||||
|
#transfer code
|
||||||
|
get 'admin/wizards/transfer' => 'transfer#index'
|
||||||
|
get 'admin/wizards/transfer/export' => 'transfer#export'
|
||||||
|
post 'admin/wizards/transfer/import' => 'transfer#import'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -89,6 +94,8 @@ after_initialize do
|
||||||
load File.expand_path('../controllers/wizard.rb', __FILE__)
|
load File.expand_path('../controllers/wizard.rb', __FILE__)
|
||||||
load File.expand_path('../controllers/steps.rb', __FILE__)
|
load File.expand_path('../controllers/steps.rb', __FILE__)
|
||||||
load File.expand_path('../controllers/admin.rb', __FILE__)
|
load File.expand_path('../controllers/admin.rb', __FILE__)
|
||||||
|
#transfer code
|
||||||
|
load File.expand_path('../controllers/transfer.rb', __FILE__)
|
||||||
|
|
||||||
load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__)
|
load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__)
|
||||||
load File.expand_path('../lib/api/api.rb', __FILE__)
|
load File.expand_path('../lib/api/api.rb', __FILE__)
|
||||||
|
@ -145,7 +152,7 @@ after_initialize do
|
||||||
@excluded_routes ||= SiteSetting.wizard_redirect_exclude_paths.split('|') + ['/w/']
|
@excluded_routes ||= SiteSetting.wizard_redirect_exclude_paths.split('|') + ['/w/']
|
||||||
url = request.referer || request.original_url
|
url = request.referer || request.original_url
|
||||||
|
|
||||||
if request.format === 'text/html' && !@excluded_routes.any? { |str| /#{str}/ =~ url } && wizard_id
|
if request.format === 'text/html' && !@excluded_routes.any? {|str| /#{str}/ =~ url} && wizard_id
|
||||||
if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
|
if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
|
||||||
CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
|
CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
|
||||||
end
|
end
|
||||||
|
@ -157,7 +164,7 @@ after_initialize do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
add_to_serializer(:current_user, :redirect_to_wizard) { object.custom_fields['redirect_to_wizard'] }
|
add_to_serializer(:current_user, :redirect_to_wizard) {object.custom_fields['redirect_to_wizard']}
|
||||||
|
|
||||||
## TODO limit this to the first admin
|
## TODO limit this to the first admin
|
||||||
SiteSerializer.class_eval do
|
SiteSerializer.class_eval do
|
||||||
|
@ -169,7 +176,7 @@ after_initialize do
|
||||||
|
|
||||||
def complete_custom_wizard
|
def complete_custom_wizard
|
||||||
if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user)
|
if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user)
|
||||||
requires_completion.map { |w| { name: w[:name], url: "/w/#{w[:id]}" } }
|
requires_completion.map {|w| {name: w[:name], url: "/w/#{w[:id]}"}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren