From 291d9fc65e73ba8cf4c2e6ec0e5203fbcaa1c656 Mon Sep 17 00:00:00 2001 From: riking Date: Sat, 16 May 2015 19:56:19 -0700 Subject: [PATCH] FEATURE: Import customizations from a JSON file --- .../admin-customize-css-html.js.es6 | 6 ++ .../admin/models/site_customization.js | 1 + .../admin/templates/customize_css_html.hbs | 1 + .../components/json-file-uploader.js.es6 | 76 +++++++++++++++++++ .../controllers/upload-customization.js.es6 | 52 +++++++++++++ .../components/json-file-uploader.hbs | 10 +++ .../templates/modal/upload-customization.hbs | 8 ++ .../views/upload-customization.js.es6 | 6 ++ app/assets/stylesheets/common/base/modal.scss | 23 ++++++ .../site_customization_serializer.rb | 2 +- config/locales/client.en.yml | 4 + 11 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/discourse/components/json-file-uploader.js.es6 create mode 100644 app/assets/javascripts/discourse/controllers/upload-customization.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs create mode 100644 app/assets/javascripts/discourse/templates/modal/upload-customization.hbs create mode 100644 app/assets/javascripts/discourse/views/upload-customization.js.es6 diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 index 266f06c3e8d..be6834333d9 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 @@ -1,3 +1,5 @@ +import showModal from 'discourse/lib/show-modal'; + /** This controller supports interface for creating custom CSS skins in Discourse. @@ -21,6 +23,10 @@ export default Ember.ArrayController.extend({ this.set('selectedItem', item); }, + importModal: function() { + showModal('upload-customization'); + }, + /** Select a given style diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js index 4d9ca8c7bf6..f7b6346a958 100644 --- a/app/assets/javascripts/admin/models/site_customization.js +++ b/app/assets/javascripts/admin/models/site_customization.js @@ -78,6 +78,7 @@ Discourse.SiteCustomization = Discourse.Model.extend({ siteCustomization.set('savingStatus', I18n.t('saved')); siteCustomization.set('saving',false); siteCustomization.startTrackingChanges(); + return siteCustomization; }); }, diff --git a/app/assets/javascripts/admin/templates/customize_css_html.hbs b/app/assets/javascripts/admin/templates/customize_css_html.hbs index 8b9fff5fcf0..e2cc7951a01 100644 --- a/app/assets/javascripts/admin/templates/customize_css_html.hbs +++ b/app/assets/javascripts/admin/templates/customize_css_html.hbs @@ -8,6 +8,7 @@ + {{d-button action="importModal" icon="upload" label="admin.customize.import"}} {{#if selectedItem}} diff --git a/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 b/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 new file mode 100644 index 00000000000..954d5f8d2ae --- /dev/null +++ b/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 @@ -0,0 +1,76 @@ + +export default Em.Component.extend({ + fileInput: null, + loading: false, + expectedRootObjectName: null, + + classNames: ['json-uploader'], + + _initialize: function() { + const $this = this.$(); + const self = this; + + const $fileInput = $this.find('#js-file-input'); + this.set('fileInput', $fileInput[0]); + + $fileInput.on('change', function() { + self.fileSelected(this.files); + }); + + const $fileSelect = $this.find('.fileSelect'); + + $fileSelect.on('dragover dragenter', function(e) { + if (e.preventDefault) e.preventDefault(); + return false; + }); + $fileSelect.on('drop', function(e) { + if (e.preventDefault) e.preventDefault(); + + self.fileSelected(e.dataTransfer.files); + return false; + }); + + }.on('didInsertElement'), + + setReady: function() { + let parsed; + try { + parsed = JSON.parse(this.get('value')); + } catch (e) { + this.set('ready', false); + return; + } + + const rootObject = parsed[this.get('expectedRootObjectName')]; + + if (rootObject !== null && rootObject !== undefined) { + this.set('ready', true); + } else { + this.set('ready', false); + } + }.observes('destination', 'expectedRootObjectName'), + + actions: { + selectFile: function() { + const $fileInput = $(this.get('fileInput')); + $fileInput.click(); + } + }, + + fileSelected(fileList) { + const self = this; + const numFiles = fileList.length; + const firstFile = fileList[0]; + + this.set('loading', true); + + let reader = new FileReader(); + reader.onload = function(evt) { + self.set('value', evt.target.result); + self.set('loading', false); + }; + + reader.readAsText(firstFile); + } + +}); diff --git a/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 b/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 new file mode 100644 index 00000000000..09158f53752 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 @@ -0,0 +1,52 @@ +import ModalFunctionality from 'discourse/mixins/modal-functionality'; + +export default Ember.Controller.extend(ModalFunctionality, { + notReady: Em.computed.not('ready'), + + needs: ['admin-customize-css-html'], + + title: "hi", + + ready: function() { + let parsed; + try { + parsed = JSON.parse(this.get('customizationFile')); + } catch (e) { + return false; + } + + return !!parsed["site_customization"]; + }.property('customizationFile'), + + actions: { + createCustomization: function() { + const self = this; + const object = JSON.parse(this.get('customizationFile')).site_customization; + + // Slight fixup before creating object + object.enabled = false; + delete object.id; + delete object.key; + + const customization = Discourse.SiteCustomization.create(object); + + this.set('loading', true); + customization.save().then(function(customization) { + self.send('closeModal'); + self.set('loading', false); + + const parentController = self.get('controllers.admin-customize-css-html'); + parentController.pushObject(customization); + parentController.set('selectedItem', customization); + }).catch(function(xhr) { + self.set('loading', false); + if (xhr.responseJSON) { + bootbox.alert(xhr.responseJSON.errors.join("
")); + } else { + bootbox.alert(I18n.t('generic_error')); + } + }); + } + } + +}); diff --git a/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs b/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs new file mode 100644 index 00000000000..655e3f7340d --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs @@ -0,0 +1,10 @@ +
+ + {{d-button class="fileSelect" action="selectFile" class="" icon="upload" label="upload_selector.select_file"}} + {{conditional-loading-spinner condition=loading size="small"}} +
+
{{i18n "alternation"}}
+
+ {{textarea value=value}} +
+ diff --git a/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs b/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs new file mode 100644 index 00000000000..8cc7851b3f3 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs @@ -0,0 +1,8 @@ +
+ + +
diff --git a/app/assets/javascripts/discourse/views/upload-customization.js.es6 b/app/assets/javascripts/discourse/views/upload-customization.js.es6 new file mode 100644 index 00000000000..c6e336157c2 --- /dev/null +++ b/app/assets/javascripts/discourse/views/upload-customization.js.es6 @@ -0,0 +1,6 @@ +import ModalBodyView from "discourse/views/modal-body"; + +export default ModalBodyView.extend({ + templateName: 'modal/upload-customization', + title: I18n.t('admin.customize.import_title') +}); diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index d5aa63a9b3d..1115a89accd 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -138,6 +138,29 @@ .raw-email-textarea { height: 300px; } + .json-uploader { + display: table-row; + .jsfu-file { + display: table-cell; + vertical-align: middle; + min-width: 120px; + } + .jsfu-separator { + vertical-align: middle; + display: table-cell; + font-size: 36px; + padding-left: 10px; + padding-right: 10px; + } + .jsfu-paste { + display: table-cell; + width: 100%; + textarea { + margin-bottom: 0; + margin-top: 4px; + } + } + } } .password-confirmation { display: none; diff --git a/app/serializers/site_customization_serializer.rb b/app/serializers/site_customization_serializer.rb index 7d524235595..1c8ff8f9da7 100644 --- a/app/serializers/site_customization_serializer.rb +++ b/app/serializers/site_customization_serializer.rb @@ -1,6 +1,6 @@ class SiteCustomizationSerializer < ApplicationSerializer - attributes :name, :enabled, :created_at, :updated_at, + attributes :id, :name, :key, :enabled, :created_at, :updated_at, :stylesheet, :header, :footer, :top, :mobile_stylesheet, :mobile_header, :mobile_footer, :mobile_top, :head_tag, :body_tag diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 9286504b2f0..064a928f6aa 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -143,6 +143,7 @@ en: every_two_weeks: "every two weeks" every_three_days: "every three days" max_of_count: "max of {{count}}" + alternation: "or" character_count: one: "{{count}} character" other: "{{count}} characters" @@ -849,6 +850,7 @@ en: hint: "(you can also drag & drop into the editor to upload them)" hint_for_supported_browsers: "(you can also drag and drop or paste images into the editor to upload them)" uploading: "Uploading" + select_file: "Select File" image_link: "link your image will point to" search: @@ -1894,6 +1896,8 @@ en: save: "Save" new: "New" new_style: "New Style" + import: "Import" + import_title: "Select a file or paste text" delete: "Delete" delete_confirm: "Delete this customization?" about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start."