From 63fb0658e907efab026d5088d32c08fa690b8b76 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Sun, 28 Jul 2019 02:38:22 +0530 Subject: [PATCH 01/10] added a feature for importing and exporting wizards --- .../controllers/admin-wizards-transfer.js.es6 | 103 ++++++++++++++++++ .../custom-wizard-admin-route-map.js.es6 | 4 + .../routes/admin-wizards-transfer.js.es6 | 11 ++ .../templates/admin-wizards-transfer.hbs | 34 ++++++ .../discourse/templates/admin-wizards.hbs | 1 + config/locales/client.en.yml | 5 + controllers/transfer.rb | 43 ++++++++ plugin.rb | 6 + 8 files changed, 207 insertions(+) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 create mode 100644 assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 create mode 100644 assets/javascripts/discourse/templates/admin-wizards-transfer.hbs create mode 100644 controllers/transfer.rb diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 new file mode 100644 index 00000000..0bcf7fac --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -0,0 +1,103 @@ +import {ajax} from 'discourse/lib/ajax'; + +export default Ember.Controller.extend({ + init() { + + this._super(); + this.set('selected', new Set()); + this.set('filePath', []); + // this.setProperties({selected:[]}) + + }, + + + actions: { + + checkChanged(event) { + // return true; + // console.log(event.target.checked) + + let selected = this.get('selected') + + + if (event.target.checked) { + + + selected.add(event.target.id) + + } else if (!event.target.checked) { + selected.delete(event.target.id) + } + console.log(selected) + this.set('selected', selected) + + // console.log(this.get('selected')) + + + }, + + + export() { + let wizards = this.get('selected') + 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; + + console.log(url) + // return ajax('/admin/wizards/transfer/export', { + // type: "POST", + // data: { + // wizards: wizards + // } + // + // }) + + + }, + + setFilePath(event) { + console.log(event.target.files[0]) + + this.set('filePath', event.target.files[0]) + + } + + , + import() { + let fileReader = new FileReader(); + fileReader.onload = function () { + let upload = {'fileJson': fileReader.result}; + // ajax('admin/wizard/transfer/import'); + console.log(fileReader.result) + //ajax call + + ajax('/admin/wizards/transfer/import',{ + type: 'POST' , + data:upload , + + }).then(result=>{ + if(result.error){ + console.log(result.error) + + }else{ + alert('wizards imported successfully') + } + }) + } + fileReader.readAsText(this.get('filePath')) + + } + + + } + + +}); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index f5022153..5fac4221 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -11,6 +11,10 @@ export default { this.route('adminWizardsApis', { path: '/apis', resetNamespace: true }, function() { this.route('adminWizardsApi', { path: '/:name', resetNamespace: true }); }); + + this.route('adminWizardsTransfer',{path:'/transfer', resetNamespace:true}); + + }); } }; diff --git a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 new file mode 100644 index 00000000..92a9f5a2 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 @@ -0,0 +1,11 @@ +import CustomWizard from '../models/custom-wizard'; +export default Discourse.Route.extend({ + + model(){ + return CustomWizard.all() + }, + isEmberized: true + // isEmberized(){ + // return true; + // } +}) diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs new file mode 100644 index 00000000..4b8f8e0f --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -0,0 +1,34 @@ +

Select Wizards to export

+{{log this}} + +
+ + {{!-- --}} + {{d-button label="admin.wizard.transfer.export" action=(action "export")}} + + +
+ +{{input id='file_url' type="file" change=(action "setFilePath")}} +
+
+{{d-button label="admin.wizard.transfer.import" action=(action "import")}} + +{{!--{{resumable-upload--}} +{{!-- target="transfer/import"--}} +{{!-- success=(action "jsonSuccess")--}} +{{!-- error=(action "jsonError")--}} +{{!-- uploadText="Import"--}} + diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index 3c26e24a..4ebb6a36 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -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}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0aa235d8..fad2628c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -215,6 +215,11 @@ en: log: label: "Logs" + transfer: + nav_label: "Transfer" + export: "Export" + import: "Import" + wizard_js: location: name: diff --git a/controllers/transfer.rb b/controllers/transfer.rb new file mode 100644 index 00000000..d6f72e46 --- /dev/null +++ b/controllers/transfer.rb @@ -0,0 +1,43 @@ +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 = [] + wizards.each do + |w| + # p w + + wizard_objects.push(PluginStore.get('custom_wizard',w.tr('-','_'))) + + end + puts 'wizard_objects' + p wizard_objects + send_data wizard_objects.to_json ,type: "application/json", disposition:'attachment' ,filename: 'wizards.json' + + end + + def import + json = params['fileJson'] + jsonObject = JSON.parse json + puts 'json file' + # p jsonObject + jsonObject.each do |o| + puts 'json entity' + pluginStoreEntry = PluginStore.new 'custom_wizard' + #plugin store detects the json object type and sets proper `type_name` for the entry + pluginStoreEntry.set(o['id'],o) + end + end + # admin/wizards/transfer/import + +end diff --git a/plugin.rb b/plugin.rb index 1709b7ea..b02f615f 100644 --- a/plugin.rb +++ b/plugin.rb @@ -75,6 +75,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 +93,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__) From 98d8eeffca331422c626d5d2b66283c0decc5c3d Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 29 Jul 2019 13:30:56 +0530 Subject: [PATCH 02/10] added some validations and changed the import logic to use FormData instead of FileReader --- .../controllers/admin-wizards-transfer.js.es6 | 77 +++++++++---------- controllers/transfer.rb | 46 +++++++++-- 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index 0bcf7fac..2876a24f 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -6,7 +6,7 @@ export default Ember.Controller.extend({ this._super(); this.set('selected', new Set()); this.set('filePath', []); - // this.setProperties({selected:[]}) + }, @@ -14,10 +14,8 @@ export default Ember.Controller.extend({ actions: { checkChanged(event) { - // return true; - // console.log(event.target.checked) - let selected = this.get('selected') + let selected = this.get('selected'); if (event.target.checked) { @@ -28,71 +26,68 @@ export default Ember.Controller.extend({ } else if (!event.target.checked) { selected.delete(event.target.id) } - console.log(selected) + console.log(selected); this.set('selected', selected) - // console.log(this.get('selected')) - }, export() { - let wizards = this.get('selected') - let url = Discourse.BaseUrl - let route = '/admin/wizards/transfer/export' - url += route + '?' + let wizards = this.get('selected'); + let url = Discourse.BaseUrl; + let route = '/admin/wizards/transfer/export'; + url += route + '?'; wizards.forEach((wizard) => { let step = 'wizards[]=' + wizard; - step += '&' + step += '&'; url += step - }) + }); location.href = url; console.log(url) - // return ajax('/admin/wizards/transfer/export', { - // type: "POST", - // data: { - // wizards: wizards - // } - // - // }) - }, setFilePath(event) { - console.log(event.target.files[0]) + console.log(event.target.files[0]); - this.set('filePath', event.target.files[0]) + // 512 kb is the max file size + let maxFileSize = 512 * 1024; + + if (maxFileSize < event.target.files[0].size) { + this.set('fileError', 'The file size is too big') + } else { + + this.set('filePath', event.target.files[0]) + + } } , import() { - let fileReader = new FileReader(); - fileReader.onload = function () { - let upload = {'fileJson': fileReader.result}; - // ajax('admin/wizard/transfer/import'); - console.log(fileReader.result) - //ajax call - ajax('/admin/wizards/transfer/import',{ - type: 'POST' , - data:upload , + let $formData = new FormData(); + $formData.append('file', this.get('filePath')); + console.log($formData); - }).then(result=>{ - if(result.error){ - console.log(result.error) + ajax('/admin/wizards/transfer/import', { + type: 'POST', + data: $formData, + processData: false, + contentType: false, - }else{ - alert('wizards imported successfully') - } - }) - } - fileReader.readAsText(this.get('filePath')) + }).then(result => { + if (result.error) { + alert(result.error) + + } else { + alert(result.success) + } + }) } diff --git a/controllers/transfer.rb b/controllers/transfer.rb index d6f72e46..c1a631a0 100644 --- a/controllers/transfer.rb +++ b/controllers/transfer.rb @@ -17,25 +17,55 @@ class CustomWizard::TransferController < ::ApplicationController |w| # p w - wizard_objects.push(PluginStore.get('custom_wizard',w.tr('-','_'))) + wizard_objects.push(PluginStore.get('custom_wizard', w.tr('-', '_'))) end puts 'wizard_objects' p wizard_objects - send_data wizard_objects.to_json ,type: "application/json", disposition:'attachment' ,filename: 'wizards.json' + send_data wizard_objects.to_json, type: "application/json", disposition: 'attachment', filename: 'wizards.json' end + def is_json(string) + begin + !!JSON.parse(string) + rescue + false + end + end + def import - json = params['fileJson'] - jsonObject = JSON.parse json - puts 'json file' - # p jsonObject + file = File.read(params['file'].tempfile) + fileSize = file.size + maxFileSize = 512 * 1024 + if maxFileSize < fileSize + render json: {error: "File too large"} + end + + unless is_json file + render json: {error: "File is not a valid json file"} + end + + jsonObject = JSON.parse file + + countValid = 0 jsonObject.each do |o| + # validate whether the given json is a valid "wizard" + next unless CustomWizard::Template.new(o) + countValid += 1 + + puts 'json entity' - pluginStoreEntry = PluginStore.new 'custom_wizard' + pluginStoreEntry = PluginStore.new 'custom_wizard' #plugin store detects the json object type and sets proper `type_name` for the entry - pluginStoreEntry.set(o['id'],o) + pluginStoreEntry.set(o['id'], o) + + end + + if countValid == 0 + render json: {error: "File doesn't contain any valid wizards"} + else + render json: {success: "Wizards imported successfully"} end end # admin/wizards/transfer/import From bf9ceb6dd3eb1af46573283e1b88efae881ebcd3 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 30 Jul 2019 22:34:18 +0530 Subject: [PATCH 03/10] made the changes discussed with Angus, some UI improvements and some edge-case validations --- .../controllers/admin-wizards-transfer.js.es6 | 107 ++++++++---------- .../custom-wizard-admin-route-map.js.es6 | 3 +- .../routes/admin-wizards-transfer.js.es6 | 10 +- .../templates/admin-wizards-transfer.hbs | 71 ++++++++---- .../stylesheets/wizard/wizard_transfer.scss | 19 ++++ controllers/transfer.rb | 39 +++---- plugin.rb | 12 +- 7 files changed, 143 insertions(+), 118 deletions(-) create mode 100644 assets/stylesheets/wizard/wizard_transfer.scss diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index 2876a24f..5fd428e0 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -2,97 +2,84 @@ import {ajax} from 'discourse/lib/ajax'; export default Ember.Controller.extend({ init() { - this._super(); - this.set('selected', new Set()); - this.set('filePath', []); - - + this.set('selected', Ember.A()); + this.set('filePath', Ember.A()); }, actions: { - checkChanged(event) { - let selected = this.get('selected'); - - if (event.target.checked) { - - - selected.add(event.target.id) - + selected.addObject(event.target.id) } else if (!event.target.checked) { - selected.delete(event.target.id) + selected.removeObject(event.target.id) } console.log(selected); this.set('selected', selected) - - }, - export() { let wizards = this.get('selected'); 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; - - console.log(url) - + if (!wizards.length) { + this.set('noneSelected', "Please select atleast one wizard") + } else { + this.set('noneSelected', '') + wizards.forEach((wizard) => { + let step = 'wizards[]=' + wizard; + step += '&'; + url += step + }); + location.href = url; + } }, setFilePath(event) { - console.log(event.target.files[0]); - // 512 kb is the max file size let maxFileSize = 512 * 1024; - + if (event.target.files[0] === undefined) { + this.get('filePath').length = 0 + return + } if (maxFileSize < event.target.files[0].size) { this.set('fileError', 'The file size is too big') } else { - - this.set('filePath', event.target.files[0]) - + // emptying the array as we allow only one file upload at a time + this.get('filePath').length = 0 + // interestingly, this.get gives us the actual reference to the object so modifying it + // actually modifies the actual value + this.get('filePath').addObject(event.target.files[0]) + console.log(this.get('filePath')) } + }, - } - - , import() { - let $formData = new FormData(); - $formData.append('file', this.get('filePath')); - console.log($formData); - - ajax('/admin/wizards/transfer/import', { - type: 'POST', - data: $formData, - processData: false, - contentType: false, - - }).then(result => { - if (result.error) { - alert(result.error) - - } else { - alert(result.success) - } - }) - + console.log(this.get('filePath')) + if (this.get('filePath').length) { + this.set('noFile', '') + $formData.append('file', this.get('filePath')[0]); + console.log($formData); + ajax('/admin/wizards/transfer/import', { + type: 'POST', + data: $formData, + processData: false, + contentType: false, + }).then(result => { + if (result.error) { + this.set('error', result.error) + } else { + this.set('success_ids', result.success) + this.set('failure_ids', result.failed) + } + }) + } else { + this.set('noFile', 'Please choose a file to export') + } } - - } - - }); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 5fac4221..001569f2 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -12,8 +12,7 @@ export default { this.route('adminWizardsApi', { path: '/:name', resetNamespace: true }); }); - this.route('adminWizardsTransfer',{path:'/transfer', resetNamespace:true}); - + this.route('adminWizardsTransfer', { path: '/transfer', resetNamespace: true }); }); } diff --git a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 index 92a9f5a2..ead2d991 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 @@ -1,11 +1,7 @@ import CustomWizard from '../models/custom-wizard'; -export default Discourse.Route.extend({ - model(){ - return CustomWizard.all() +export default Discourse.Route.extend({ + model() { + return CustomWizard.all() }, - isEmberized: true - // isEmberized(){ - // return true; - // } }) diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index 4b8f8e0f..de856753 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -1,34 +1,55 @@ -

Select Wizards to export

-{{log this}} +

Export

-
-
    - {{#each model as |w|}} +
      + {{#each model as |w|}} -
    • - {{input type="checkbox" id=(dasherize w.id) change=(action 'checkChanged')}} +
    • + {{input type="checkbox" id=(dasherize w.id) change=(action 'checkChanged')}} - {{#link-to "adminWizard" (dasherize w.id)}}{{w.name}}{{/link-to}} -
    • - {{/each}} + {{#link-to "adminWizard" (dasherize w.id)}}{{w.name}}{{/link-to}} + + {{/each}} -
    - {{!-- --}} - {{d-button label="admin.wizard.transfer.export" action=(action "export")}} +
+ +{{d-button id="export-button" class="btn btn-primary side" label="admin.wizard.transfer.export" + action=(action "export")}} -
-{{input id='file_url' type="file" change=(action "setFilePath")}} -
-
-{{d-button label="admin.wizard.transfer.import" action=(action "import")}} +

Import

+{{input id='file_url' type="file" change=(action "setFilePath")}} + + + +{{d-button id="import-button" class="btn btn-primary side" label="admin.wizard.transfer.import" + action=(action "import")}} + +{{#if this.noneSelected}} +

{{this.noneSelected}}

+{{/if}} + +{{#if this.noFile}} +

{{this.noFile}}

+{{/if}} +{{#if this.fileError}} +

{{this.fileError}}

+{{/if}} + +{{#if this.error}} +

{{this.error}}

+{{/if}} + +{{#if this.success_ids}} + {{#each this.success_ids as |id|}} +

Wizard: {{id}} saved successfully

+ {{/each}} +{{/if}} + +{{#if this.failure_ids}} + {{#each this.failure_ids as |id|}} +

Wizard: {{id}} could not be saved

+ {{/each}} +{{/if}} -{{!--{{resumable-upload--}} -{{!-- target="transfer/import"--}} -{{!-- success=(action "jsonSuccess")--}} -{{!-- error=(action "jsonError")--}} -{{!-- uploadText="Import"--}} diff --git a/assets/stylesheets/wizard/wizard_transfer.scss b/assets/stylesheets/wizard/wizard_transfer.scss new file mode 100644 index 00000000..61c15b61 --- /dev/null +++ b/assets/stylesheets/wizard/wizard_transfer.scss @@ -0,0 +1,19 @@ +#export-button{ + margin-left: 30px; +} +#import-button{ + margin-left: 15px; +} +#file_url { + display: inline; +} + +.wizard-list-select { + display: inline-block; + +} + +.wizard-action-buttons{ + display: inline-flex; + flex-direction: column; +} diff --git a/controllers/transfer.rb b/controllers/transfer.rb index c1a631a0..999811b2 100644 --- a/controllers/transfer.rb +++ b/controllers/transfer.rb @@ -1,27 +1,23 @@ 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 = [] - wizards.each do - |w| - # p w - - wizard_objects.push(PluginStore.get('custom_wizard', w.tr('-', '_'))) - + if wizards.nil? + render json: {error: 'Please select atleast one wizard'} + return end - puts 'wizard_objects' - p wizard_objects + + 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 @@ -36,6 +32,10 @@ class CustomWizard::TransferController < ::ApplicationController def import file = File.read(params['file'].tempfile) + if file.nil? + render json: {error: "No file selected"} + return + end fileSize = file.size maxFileSize = 512 * 1024 if maxFileSize < fileSize @@ -49,25 +49,26 @@ class CustomWizard::TransferController < ::ApplicationController jsonObject = JSON.parse file countValid = 0 + success_ids = [] + failed_ids = [] jsonObject.each do |o| # validate whether the given json is a valid "wizard" next unless CustomWizard::Template.new(o) countValid += 1 - - - puts 'json entity' pluginStoreEntry = PluginStore.new 'custom_wizard' #plugin store detects the json object type and sets proper `type_name` for the entry - pluginStoreEntry.set(o['id'], o) - + # this condition helps us avoid updating an existing wizard instead of adding a new one + 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: "File doesn't contain any valid wizards"} else - render json: {success: "Wizards imported successfully"} + render json: {success: success_ids, failed: failed_ids} end end - # admin/wizards/transfer/import + end diff --git a/plugin.rb b/plugin.rb index b02f615f..20260e32 100644 --- a/plugin.rb +++ b/plugin.rb @@ -5,6 +5,7 @@ # url: https://github.com/angusmcleod/discourse-custom-wizard register_asset 'stylesheets/wizard_custom_admin.scss' +register_asset 'stylesheets/wizard/wizard_transfer.scss' register_asset 'lib/jquery.timepicker.min.js' register_asset 'lib/jquery.timepicker.scss' @@ -12,6 +13,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 @@ -76,7 +78,7 @@ after_initialize do 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' => 'transfer#index' get 'admin/wizards/transfer/export' => 'transfer#export' post 'admin/wizards/transfer/import' => 'transfer#import' end @@ -94,7 +96,7 @@ after_initialize do 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('../controllers/transfer.rb', __FILE__) load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__) load File.expand_path('../lib/api/api.rb', __FILE__) @@ -151,7 +153,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 @@ -163,7 +165,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 @@ -175,7 +177,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 From e4de1d41456807ffde3c0d98ee11548e64cf6ace Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Wed, 31 Jul 2019 10:27:40 +0530 Subject: [PATCH 04/10] converted all the server side strings to translation ready strings --- config/locales/server.en.yml | 9 +++++++++ controllers/transfer.rb | 12 +++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 8ce4b4bb..d1c42401 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -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." diff --git a/controllers/transfer.rb b/controllers/transfer.rb index 999811b2..61da5c75 100644 --- a/controllers/transfer.rb +++ b/controllers/transfer.rb @@ -10,7 +10,7 @@ class CustomWizard::TransferController < ::ApplicationController wizards = params['wizards'] wizard_objects = [] if wizards.nil? - render json: {error: 'Please select atleast one wizard'} + render json: {error: I18n.t('wizard.export.error.select_one')} return end @@ -33,17 +33,19 @@ class CustomWizard::TransferController < ::ApplicationController def import file = File.read(params['file'].tempfile) if file.nil? - render json: {error: "No file selected"} + render json: {error: I18n.t('wizard.import.error.no_file')} return end fileSize = file.size maxFileSize = 512 * 1024 if maxFileSize < fileSize - render json: {error: "File too large"} + render json: {error: I18n.t('wizard.import.error.file_large')} + return end unless is_json file - render json: {error: "File is not a valid json file"} + render json: {error: I18n.t('wizard.import.error.invalid_json')} + return end jsonObject = JSON.parse file @@ -64,7 +66,7 @@ class CustomWizard::TransferController < ::ApplicationController end if countValid == 0 - render json: {error: "File doesn't contain any valid wizards"} + render json: {error: I18n.t('wizard.import.error.no_valid_wizards')} else render json: {success: success_ids, failed: failed_ids} end From 25792f92e48c3b03ead81b1b4ae66cf56163cdc1 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 6 Aug 2019 16:38:05 +0530 Subject: [PATCH 05/10] Fixed code formatting, tweaked css, moved remaining hardcoded strings to translations and removed all console.log calls --- .../controllers/admin-wizards-transfer.js.es6 | 16 ++--- .../templates/admin-wizards-transfer.hbs | 69 ++++++++----------- .../stylesheets/wizard/wizard_transfer.scss | 19 ----- assets/stylesheets/wizard_custom_admin.scss | 23 ++++++- config/locales/client.en.yml | 11 ++- controllers/transfer.rb | 8 +-- plugin.rb | 1 - 7 files changed, 70 insertions(+), 77 deletions(-) delete mode 100644 assets/stylesheets/wizard/wizard_transfer.scss diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index 5fd428e0..91bf2638 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -1,4 +1,4 @@ -import {ajax} from 'discourse/lib/ajax'; +import { ajax } from 'discourse/lib/ajax'; export default Ember.Controller.extend({ init() { @@ -7,16 +7,15 @@ export default Ember.Controller.extend({ this.set('filePath', Ember.A()); }, - actions: { checkChanged(event) { + this.set('noneSelected','') let selected = this.get('selected'); if (event.target.checked) { selected.addObject(event.target.id) } else if (!event.target.checked) { selected.removeObject(event.target.id) } - console.log(selected); this.set('selected', selected) }, @@ -26,7 +25,7 @@ export default Ember.Controller.extend({ let route = '/admin/wizards/transfer/export'; url += route + '?'; if (!wizards.length) { - this.set('noneSelected', "Please select atleast one wizard") + this.set('noneSelected', I18n.t("admin.wizard.transfer.export.noneSelected")) } else { this.set('noneSelected', '') wizards.forEach((wizard) => { @@ -39,6 +38,7 @@ export default Ember.Controller.extend({ }, setFilePath(event) { + this.set('noFile', '') // 512 kb is the max file size let maxFileSize = 512 * 1024; if (event.target.files[0] === undefined) { @@ -46,24 +46,22 @@ export default Ember.Controller.extend({ return } if (maxFileSize < event.target.files[0].size) { - this.set('fileError', 'The file size is too big') + this.set('fileSizeError', I18n.t('admin.wizard.transfer.import.fileSizeError')) } else { + this.set('fileSizeError', '') // emptying the array as we allow only one file upload at a time this.get('filePath').length = 0 // interestingly, this.get gives us the actual reference to the object so modifying it // actually modifies the actual value this.get('filePath').addObject(event.target.files[0]) - console.log(this.get('filePath')) } }, import() { let $formData = new FormData(); - console.log(this.get('filePath')) if (this.get('filePath').length) { this.set('noFile', '') $formData.append('file', this.get('filePath')[0]); - console.log($formData); ajax('/admin/wizards/transfer/import', { type: 'POST', data: $formData, @@ -78,7 +76,7 @@ export default Ember.Controller.extend({ } }) } else { - this.set('noFile', 'Please choose a file to export') + this.set('noFile',I18n.t("admin.wizard.transfer.import.noFile")) } } } diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index de856753..ad8df8d4 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -1,55 +1,44 @@ -

Export

- +
+

{{i18n 'admin.wizard.transfer.export.label'}}

    {{#each model as |w|}} - -
  • +
  • {{input type="checkbox" id=(dasherize w.id) change=(action 'checkChanged')}} - {{#link-to "adminWizard" (dasherize w.id)}}{{w.name}}{{/link-to}}
  • {{/each}} -
+ {{d-button id="export-button" class="btn btn-primary side" label="admin.wizard.transfer.export.label" action=(action "export")}} + {{#if this.noneSelected}} +

{{this.noneSelected}}

+ {{/if}} +
-{{d-button id="export-button" class="btn btn-primary side" label="admin.wizard.transfer.export" - action=(action "export")}} - - - -

Import

+
+

{{i18n 'admin.wizard.transfer.import.label'}}

{{input id='file_url' type="file" change=(action "setFilePath")}} +{{d-button id="import-button" class="btn btn-primary side" label="admin.wizard.transfer.import.label" action=(action "import")}} + {{#if this.noFile}} +

{{this.noFile}}

+ {{/if}} + {{#if this.fileSizeError}} +

{{this.fileSizeError}}

+ {{/if}} + {{#if this.success_ids}} + {{#each this.success_ids as |id|}} +

{{i18n "admin.wizard.transfer.import.success"}}

+ {{/each}} + {{/if}} -{{d-button id="import-button" class="btn btn-primary side" label="admin.wizard.transfer.import" - action=(action "import")}} - -{{#if this.noneSelected}} -

{{this.noneSelected}}

-{{/if}} - -{{#if this.noFile}} -

{{this.noFile}}

-{{/if}} -{{#if this.fileError}} -

{{this.fileError}}

-{{/if}} - + {{#if this.failure_ids}} + {{#each this.failure_ids as |id|}} +

{{i18n "admin.wizard.transfer.import.failure" id=id}}

+ {{/each}} + {{/if}} +
+{{!-- server side error--}} {{#if this.error}}

{{this.error}}

{{/if}} - -{{#if this.success_ids}} - {{#each this.success_ids as |id|}} -

Wizard: {{id}} saved successfully

- {{/each}} -{{/if}} - -{{#if this.failure_ids}} - {{#each this.failure_ids as |id|}} -

Wizard: {{id}} could not be saved

- {{/each}} -{{/if}} - - diff --git a/assets/stylesheets/wizard/wizard_transfer.scss b/assets/stylesheets/wizard/wizard_transfer.scss deleted file mode 100644 index 61c15b61..00000000 --- a/assets/stylesheets/wizard/wizard_transfer.scss +++ /dev/null @@ -1,19 +0,0 @@ -#export-button{ - margin-left: 30px; -} -#import-button{ - margin-left: 15px; -} -#file_url { - display: inline; -} - -.wizard-list-select { - display: inline-block; - -} - -.wizard-action-buttons{ - display: inline-flex; - flex-direction: column; -} diff --git a/assets/stylesheets/wizard_custom_admin.scss b/assets/stylesheets/wizard_custom_admin.scss index 3399b148..652b47df 100644 --- a/assets/stylesheets/wizard_custom_admin.scss +++ b/assets/stylesheets/wizard_custom_admin.scss @@ -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,22 @@ .wizard-step-contents{ height: unset !important; } + +// Tansfer tab +#file_url { + display: block; + margin-bottom: 10px; +} + +.wizard-list-select { + list-style-type: none; +} + +.wizard-action-buttons{ + flex-direction: column; +} + +.container{ + margin-bottom: 20px; +} + diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index fad2628c..b2364c7e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -217,8 +217,15 @@ en: transfer: nav_label: "Transfer" - export: "Export" - import: "Import" + export: + label: "Export" + noneSelected: "Please select atleast one wizard" + import: + label: "Import" + success: "Wizard: {{id}} saved successfully" + failure: "Wizard: {{id}} could not be saved" + noFile: "Please choose a file to import" + fileSizeError: "The file size is too big" wizard_js: location: diff --git a/controllers/transfer.rb b/controllers/transfer.rb index 61da5c75..bcf95299 100644 --- a/controllers/transfer.rb +++ b/controllers/transfer.rb @@ -9,6 +9,7 @@ class CustomWizard::TransferController < ::ApplicationController def export wizards = params['wizards'] wizard_objects = [] + if wizards.nil? render json: {error: I18n.t('wizard.export.error.select_one')} return @@ -19,7 +20,6 @@ class CustomWizard::TransferController < ::ApplicationController end send_data wizard_objects.to_json, type: "application/json", disposition: 'attachment', filename: 'wizards.json' - end def is_json(string) @@ -32,12 +32,15 @@ class CustomWizard::TransferController < ::ApplicationController 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 @@ -49,7 +52,6 @@ class CustomWizard::TransferController < ::ApplicationController end jsonObject = JSON.parse file - countValid = 0 success_ids = [] failed_ids = [] @@ -71,6 +73,4 @@ class CustomWizard::TransferController < ::ApplicationController render json: {success: success_ids, failed: failed_ids} end end - - end diff --git a/plugin.rb b/plugin.rb index 20260e32..1acacf99 100644 --- a/plugin.rb +++ b/plugin.rb @@ -5,7 +5,6 @@ # url: https://github.com/angusmcleod/discourse-custom-wizard register_asset 'stylesheets/wizard_custom_admin.scss' -register_asset 'stylesheets/wizard/wizard_transfer.scss' register_asset 'lib/jquery.timepicker.min.js' register_asset 'lib/jquery.timepicker.scss' From 861ab40a5af6f35c4054fb4cd2a22f4f5c6aed91 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 6 Aug 2019 16:47:03 +0530 Subject: [PATCH 06/10] minor issue: passing parameter to translation string --- .../javascripts/discourse/templates/admin-wizards-transfer.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index ad8df8d4..a7e70091 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -28,7 +28,7 @@ {{#if this.success_ids}} {{#each this.success_ids as |id|}} -

{{i18n "admin.wizard.transfer.import.success"}}

+

{{i18n "admin.wizard.transfer.import.success" id=id}}

{{/each}} {{/if}} From ee717197930a1cc9cfed618b3a42206190534ba2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 7 Aug 2019 18:18:46 +1000 Subject: [PATCH 07/10] client-side improvements --- .../controllers/admin-wizards-transfer.js.es6 | 98 +++++++++++++------ .../templates/admin-wizards-transfer.hbs | 89 ++++++++++------- assets/stylesheets/wizard_custom_admin.scss | 22 ++++- config/locales/client.en.yml | 11 ++- 4 files changed, 144 insertions(+), 76 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index 91bf2638..a9b1be3a 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -1,67 +1,97 @@ import { ajax } from 'discourse/lib/ajax'; +import { default as computed } from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend({ init() { this._super(); - this.set('selected', Ember.A()); - this.set('filePath', Ember.A()); + this.setProperties({ + selected: Ember.A() + }); }, + @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; + }, + + hasLogs: Ember.computed.notEmpty('logs'), + actions: { checkChanged(event) { - this.set('noneSelected','') + this.set('exportMessage', ''); + let selected = this.get('selected'); + if (event.target.checked) { - selected.addObject(event.target.id) + selected.addObject(event.target.id); } else if (!event.target.checked) { - selected.removeObject(event.target.id) + selected.removeObject(event.target.id); } - this.set('selected', selected) + + this.set('selected', selected); }, export() { - let wizards = this.get('selected'); - let url = Discourse.BaseUrl; - let route = '/admin/wizards/transfer/export'; - url += route + '?'; + const wizards = this.get('selected'); + if (!wizards.length) { - this.set('noneSelected', I18n.t("admin.wizard.transfer.export.noneSelected")) + this.set('exportMessage', I18n.t("admin.wizard.transfer.export.none_selected")); } else { - this.set('noneSelected', '') + 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 + url += step; }); + location.href = url; } }, setFilePath(event) { - this.set('noFile', '') + this.set('importMessage', ''); + // 512 kb is the max file size let maxFileSize = 512 * 1024; + if (event.target.files[0] === undefined) { - this.get('filePath').length = 0 - return + this.set('filePath', null); + return; } + if (maxFileSize < event.target.files[0].size) { - this.set('fileSizeError', I18n.t('admin.wizard.transfer.import.fileSizeError')) + this.set('importMessage', I18n.t('admin.wizard.transfer.import.file_size_error')); } else { - this.set('fileSizeError', '') - // emptying the array as we allow only one file upload at a time - this.get('filePath').length = 0 - // interestingly, this.get gives us the actual reference to the object so modifying it - // actually modifies the actual value - this.get('filePath').addObject(event.target.files[0]) + this.set('filePath', event.target.files[0]); } }, import() { + const filePath = this.get('filePath'); let $formData = new FormData(); - if (this.get('filePath').length) { - this.set('noFile', '') - $formData.append('file', this.get('filePath')[0]); + + if (filePath) { + $formData.append('file', filePath); + ajax('/admin/wizards/transfer/import', { type: 'POST', data: $formData, @@ -69,14 +99,20 @@ export default Ember.Controller.extend({ contentType: false, }).then(result => { if (result.error) { - this.set('error', result.error) + this.set('error', result.error); } else { - this.set('success_ids', result.success) - this.set('failure_ids', result.failed) + 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('noFile',I18n.t("admin.wizard.transfer.import.noFile")) + this.set('importMessage', I18n.t("admin.wizard.transfer.import.no_file")); } } } diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index a7e70091..12de208f 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -1,44 +1,61 @@
-

{{i18n 'admin.wizard.transfer.export.label'}}

-
    - {{#each model as |w|}} -
  • - {{input type="checkbox" id=(dasherize w.id) change=(action 'checkChanged')}} - {{#link-to "adminWizard" (dasherize w.id)}}{{w.name}}{{/link-to}} -
  • - {{/each}} -
- {{d-button id="export-button" class="btn btn-primary side" label="admin.wizard.transfer.export.label" action=(action "export")}} - {{#if this.noneSelected}} -

{{this.noneSelected}}

+

{{i18n 'admin.wizard.transfer.export.label'}}

+ +
    + {{#each model as |w|}} +
  • + {{input type="checkbox" + id=(dasherize w.id) + change=(action 'checkChanged')}} + {{#link-to "adminWizard" (dasherize w.id)}} + {{w.name}} + {{/link-to}} +
  • + {{/each}} +
+ + {{d-button id="export-button" + class="btn btn-primary side" + label="admin.wizard.transfer.export.label" + action=(action "export")}} + + {{#if exportMessage}} +
+ {{exportMessage}} +
{{/if}}
-

{{i18n 'admin.wizard.transfer.import.label'}}

-{{input id='file_url' type="file" change=(action "setFilePath")}} -{{d-button id="import-button" class="btn btn-primary side" label="admin.wizard.transfer.import.label" action=(action "import")}} - {{#if this.noFile}} -

{{this.noFile}}

- {{/if}} +

{{i18n 'admin.wizard.transfer.import.label'}}

- {{#if this.fileSizeError}} -

{{this.fileSizeError}}

- {{/if}} - - {{#if this.success_ids}} - {{#each this.success_ids as |id|}} -

{{i18n "admin.wizard.transfer.import.success" id=id}}

- {{/each}} - {{/if}} - - {{#if this.failure_ids}} - {{#each this.failure_ids as |id|}} -

{{i18n "admin.wizard.transfer.import.failure" id=id}}

- {{/each}} +
+ {{input id='file-url' type="file" change=(action "setFilePath")}} + {{d-button id="import-button" + class="btn btn-primary side" + label="admin.wizard.transfer.import.label" + action=(action "import")}} + + {{#if importMessage}} +
+ {{importMessage}} +
+ {{/if}} +
+ + {{#if hasLogs}} +
+
+ {{i18n 'admin.wizard.transfer.import.logs' fileName=fileName}} +
+ +
    + {{#each logs as |l|}} +
  • + {{i18n (concat 'admin.wizard.transfer.import.' l.type) id=l.id}} +
  • + {{/each}} +
+
{{/if}}
-{{!-- server side error--}} -{{#if this.error}} -

{{this.error}}

-{{/if}} diff --git a/assets/stylesheets/wizard_custom_admin.scss b/assets/stylesheets/wizard_custom_admin.scss index 652b47df..e4a4b915 100644 --- a/assets/stylesheets/wizard_custom_admin.scss +++ b/assets/stylesheets/wizard_custom_admin.scss @@ -482,7 +482,12 @@ } // Tansfer tab -#file_url { + +.admin-wizards-transfer .admin-container .container { + padding-top: 20px; +} + +#file-url { display: block; margin-bottom: 10px; } @@ -491,11 +496,20 @@ list-style-type: none; } -.wizard-action-buttons{ +.wizard-action-buttons { flex-direction: column; } -.container{ - margin-bottom: 20px; +.import-logs { + margin-top: 20px; + + .title { + font-weight: 800; + margin-bottom: 10px; + } + + ul { + list-style: none; + } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index b2364c7e..3c07c78a 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -219,13 +219,14 @@ en: nav_label: "Transfer" export: label: "Export" - noneSelected: "Please select atleast one wizard" + none_selected: "Please select atleast one wizard" import: label: "Import" - success: "Wizard: {{id}} saved successfully" - failure: "Wizard: {{id}} could not be saved" - noFile: "Please choose a file to import" - fileSizeError: "The file size is too big" + 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 size is too big" wizard_js: location: From 922afdc1bde5aaa032594d13ecff731dfcbe5869 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 7 Aug 2019 20:16:05 +1000 Subject: [PATCH 08/10] Client and server-side fixes --- .../controllers/admin-wizards-transfer.js.es6 | 8 +++- .../templates/admin-wizards-transfer.hbs | 9 ++-- assets/stylesheets/wizard_custom_admin.scss | 4 ++ config/locales/client.en.yml | 2 +- controllers/transfer.rb | 44 +++++++++---------- 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index a9b1be3a..3afb4cc9 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -79,7 +79,11 @@ export default Ember.Controller.extend({ } if (maxFileSize < event.target.files[0].size) { - this.set('importMessage', I18n.t('admin.wizard.transfer.import.file_size_error')); + 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]); } @@ -99,7 +103,7 @@ export default Ember.Controller.extend({ contentType: false, }).then(result => { if (result.error) { - this.set('error', result.error); + this.set('importMessage', result.error); } else { this.setProperties({ successIds: result.success, diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index 12de208f..b326fd4d 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -31,16 +31,17 @@
{{input id='file-url' type="file" change=(action "setFilePath")}} - {{d-button id="import-button" - class="btn btn-primary side" - label="admin.wizard.transfer.import.label" - action=(action "import")}} {{#if importMessage}}
{{importMessage}}
{{/if}} + + {{d-button id="import-button" + class="btn btn-primary side" + label="admin.wizard.transfer.import.label" + action=(action "import")}}
{{#if hasLogs}} diff --git a/assets/stylesheets/wizard_custom_admin.scss b/assets/stylesheets/wizard_custom_admin.scss index e4a4b915..7ae598ac 100644 --- a/assets/stylesheets/wizard_custom_admin.scss +++ b/assets/stylesheets/wizard_custom_admin.scss @@ -500,6 +500,10 @@ flex-direction: column; } +.import-message { + margin: 10px 0; +} + .import-logs { margin-top: 20px; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3c07c78a..d65c42c0 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -226,7 +226,7 @@ en: 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 size is too big" + file_size_error: "The file must be JSON and 512kb or less" wizard_js: location: diff --git a/controllers/transfer.rb b/controllers/transfer.rb index bcf95299..4da52bb9 100644 --- a/controllers/transfer.rb +++ b/controllers/transfer.rb @@ -11,7 +11,7 @@ class CustomWizard::TransferController < ::ApplicationController wizard_objects = [] if wizards.nil? - render json: {error: I18n.t('wizard.export.error.select_one')} + render json: { error: I18n.t('wizard.export.error.select_one') } return end @@ -19,22 +19,17 @@ class CustomWizard::TransferController < ::ApplicationController 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 is_json(string) - begin - !!JSON.parse(string) - rescue - false - 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')} + render json: { error: I18n.t('wizard.import.error.no_file') } return end @@ -42,35 +37,38 @@ class CustomWizard::TransferController < ::ApplicationController maxFileSize = 512 * 1024 if maxFileSize < fileSize - render json: {error: I18n.t('wizard.import.error.file_large')} + render json: { error: I18n.t('wizard.import.error.file_large') } return end - - unless is_json file - render json: {error: I18n.t('wizard.import.error.invalid_json')} + + begin + jsonObject = JSON.parse file + rescue JSON::ParserError + render json: { error: I18n.t('wizard.import.error.invalid_json') } return end - - jsonObject = JSON.parse file + countValid = 0 success_ids = [] failed_ids = [] + jsonObject.each do |o| - # validate whether the given json is a valid "wizard" - next unless CustomWizard::Template.new(o) + if !CustomWizard::Template.new(o) + failed_ids.push o['id'] + next + end + countValid += 1 pluginStoreEntry = PluginStore.new 'custom_wizard' - #plugin store detects the json object type and sets proper `type_name` for the entry - # this condition helps us avoid updating an existing wizard instead of adding a new one 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')} + render json: { error: I18n.t('wizard.import.error.no_valid_wizards') } else - render json: {success: success_ids, failed: failed_ids} + render json: { success: success_ids, failed: failed_ids } end end end From c156dcb6d14e1b0489dbc801cf04787893882bc6 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 7 Aug 2019 20:38:50 +1000 Subject: [PATCH 09/10] Componentize --- .../discourse/components/wizard-export.js.es6 | 42 ++++++ .../discourse/components/wizard-import.js.es6 | 81 ++++++++++++ .../controllers/admin-wizards-transfer.js.es6 | 124 +----------------- .../templates/admin-wizards-transfer.hbs | 64 +-------- .../templates/components/wizard-export.hbs | 25 ++++ .../templates/components/wizard-import.hbs | 32 +++++ 6 files changed, 183 insertions(+), 185 deletions(-) create mode 100644 assets/javascripts/discourse/components/wizard-export.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-import.js.es6 create mode 100644 assets/javascripts/discourse/templates/components/wizard-export.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-import.hbs diff --git a/assets/javascripts/discourse/components/wizard-export.js.es6 b/assets/javascripts/discourse/components/wizard-export.js.es6 new file mode 100644 index 00000000..a2505d24 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-export.js.es6 @@ -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; + } + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-import.js.es6 b/assets/javascripts/discourse/components/wizard-import.js.es6 new file mode 100644 index 00000000..1cf6618d --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-import.js.es6 @@ -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")); + } + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 index 3afb4cc9..77c79b72 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 @@ -1,123 +1 @@ -import { ajax } from 'discourse/lib/ajax'; -import { default as computed } from 'ember-addons/ember-computed-decorators'; - -export default Ember.Controller.extend({ - init() { - this._super(); - this.setProperties({ - selected: Ember.A() - }); - }, - - @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; - }, - - hasLogs: Ember.computed.notEmpty('logs'), - - 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; - } - }, - - 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")); - } - } - } -}); +export default Ember.Controller.extend(); diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs index b326fd4d..ff36a823 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs @@ -1,62 +1,2 @@ -
-

{{i18n 'admin.wizard.transfer.export.label'}}

- -
    - {{#each model as |w|}} -
  • - {{input type="checkbox" - id=(dasherize w.id) - change=(action 'checkChanged')}} - {{#link-to "adminWizard" (dasherize w.id)}} - {{w.name}} - {{/link-to}} -
  • - {{/each}} -
- - {{d-button id="export-button" - class="btn btn-primary side" - label="admin.wizard.transfer.export.label" - action=(action "export")}} - - {{#if exportMessage}} -
- {{exportMessage}} -
- {{/if}} -
- -
-

{{i18n 'admin.wizard.transfer.import.label'}}

- -
- {{input id='file-url' type="file" change=(action "setFilePath")}} - - {{#if importMessage}} -
- {{importMessage}} -
- {{/if}} - - {{d-button id="import-button" - class="btn btn-primary side" - label="admin.wizard.transfer.import.label" - action=(action "import")}} -
- - {{#if hasLogs}} -
-
- {{i18n 'admin.wizard.transfer.import.logs' fileName=fileName}} -
- -
    - {{#each logs as |l|}} -
  • - {{i18n (concat 'admin.wizard.transfer.import.' l.type) id=l.id}} -
  • - {{/each}} -
-
- {{/if}} -
+{{wizard-export wizards=model}} +{{wizard-import}} diff --git a/assets/javascripts/discourse/templates/components/wizard-export.hbs b/assets/javascripts/discourse/templates/components/wizard-export.hbs new file mode 100644 index 00000000..869b372a --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-export.hbs @@ -0,0 +1,25 @@ +

{{i18n 'admin.wizard.transfer.export.label'}}

+ +
    + {{#each wizards as |w|}} +
  • + {{input type="checkbox" + id=(dasherize w.id) + change=(action 'checkChanged')}} + {{#link-to "adminWizard" (dasherize w.id)}} + {{w.name}} + {{/link-to}} +
  • + {{/each}} +
+ +{{d-button id="export-button" + class="btn btn-primary side" + label="admin.wizard.transfer.export.label" + action=(action "export")}} + +{{#if exportMessage}} +
+ {{exportMessage}} +
+{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-import.hbs b/assets/javascripts/discourse/templates/components/wizard-import.hbs new file mode 100644 index 00000000..9f46e32b --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-import.hbs @@ -0,0 +1,32 @@ +

{{i18n 'admin.wizard.transfer.import.label'}}

+ +
+ {{input id='file-url' type="file" change=(action "setFilePath")}} + + {{#if importMessage}} +
+ {{importMessage}} +
+ {{/if}} + + {{d-button id="import-button" + class="btn btn-primary side" + label="admin.wizard.transfer.import.label" + action=(action "import")}} +
+ +{{#if hasLogs}} +
+
+ {{i18n 'admin.wizard.transfer.import.logs' fileName=fileName}} +
+ +
    + {{#each logs as |l|}} +
  • + {{i18n (concat 'admin.wizard.transfer.import.' l.type) id=l.id}} +
  • + {{/each}} +
+
+{{/if}} \ No newline at end of file From 7cd50690696cc5dcce56194bde4e43acd7dc4766 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 7 Aug 2019 20:46:05 +1000 Subject: [PATCH 10/10] minor formatting --- .../discourse/routes/admin-wizards-transfer.js.es6 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 index ead2d991..b86b49cc 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 @@ -2,6 +2,6 @@ import CustomWizard from '../models/custom-wizard'; export default Discourse.Route.extend({ model() { - return CustomWizard.all() - }, -}) + return CustomWizard.all(); + } +});