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('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='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
|
||||
{{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
|
||||
{{nav-item route='adminWizardsTransfer' label='admin.wizard.transfer.nav_label'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<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}}
|
|
@ -224,11 +224,11 @@
|
|||
|
||||
.required-data .setting-value {
|
||||
flex-flow: wrap;
|
||||
|
||||
|
||||
.custom-inputs {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
.required-data-message .label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
@ -480,3 +480,40 @@
|
|||
.wizard-step-contents{
|
||||
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:
|
||||
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:
|
||||
location:
|
||||
name:
|
||||
|
|
|
@ -10,6 +10,15 @@ en:
|
|||
too_short: "%{label} must be at least %{min} characters"
|
||||
none: "We couldn't find a wizard at that address."
|
||||
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:
|
||||
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', 'stylesheets', 'wizard')
|
||||
|
||||
|
||||
if Rails.env.production?
|
||||
config.assets.precompile += %w{
|
||||
wizard-custom-lib.js
|
||||
|
@ -75,6 +76,10 @@ after_initialize do
|
|||
delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs'
|
||||
get 'admin/wizards/apis/:name/redirect' => 'api#redirect'
|
||||
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
|
||||
|
||||
|
@ -89,6 +94,8 @@ after_initialize do
|
|||
load File.expand_path('../controllers/wizard.rb', __FILE__)
|
||||
load File.expand_path('../controllers/steps.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('../lib/api/api.rb', __FILE__)
|
||||
|
@ -145,7 +152,7 @@ after_initialize do
|
|||
@excluded_routes ||= SiteSetting.wizard_redirect_exclude_paths.split('|') + ['/w/']
|
||||
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\//
|
||||
CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
|
||||
end
|
||||
|
@ -157,7 +164,7 @@ after_initialize do
|
|||
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
|
||||
SiteSerializer.class_eval do
|
||||
|
@ -169,7 +176,7 @@ after_initialize do
|
|||
|
||||
def complete_custom_wizard
|
||||
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
|
||||
|
||||
|
|
Laden …
In neuem Issue referenzieren