From 18a433debd9c9e8f01f6461d9727cd4fd08163eb Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Fri, 20 Jun 2014 02:29:49 +0000 Subject: [PATCH] Convert general settings page to ember data Issue #2846 -Implement custom RESTAdapter and serializer for settings data. -Convert theme selector to Ember.Select. -Finish upload modal and wire into settings page. --- ghost/admin/adapters/setting.js | 21 ++++++ ghost/admin/components/gh-upload-modal.js | 1 + ghost/admin/controllers/modals/upload.js | 18 ++--- ghost/admin/controllers/settings/general.js | 59 ++++++---------- ghost/admin/models/setting.js | 15 ++++ ghost/admin/models/settings.js | 76 --------------------- ghost/admin/routes/settings/general.js | 8 +-- ghost/admin/routes/settings/index.js | 3 +- ghost/admin/serializers/setting.js | 37 ++++++++++ ghost/admin/templates/settings/general.hbs | 12 ++-- 10 files changed, 116 insertions(+), 134 deletions(-) create mode 100644 ghost/admin/adapters/setting.js create mode 100644 ghost/admin/models/setting.js delete mode 100644 ghost/admin/models/settings.js create mode 100644 ghost/admin/serializers/setting.js diff --git a/ghost/admin/adapters/setting.js b/ghost/admin/adapters/setting.js new file mode 100644 index 0000000000..00f576850d --- /dev/null +++ b/ghost/admin/adapters/setting.js @@ -0,0 +1,21 @@ +import ApplicationAdapter from 'ghost/adapters/application'; + +var SettingAdapter = ApplicationAdapter.extend({ + updateRecord: function (store, type, record) { + var data = {}, + serializer = store.serializerFor(type.typeKey); + + // remove the fake id that we added onto the model. + delete record.id; + + // use the SettingSerializer to transform the model back into + // an array of settings objects like the API expects + serializer.serializeIntoHash(data, type, record); + + // use the ApplicationAdapter's buildURL method but do not + // pass in an id. + return this.ajax(this.buildURL(type.typeKey), 'PUT', { data: data }); + } +}); + +export default SettingAdapter; diff --git a/ghost/admin/components/gh-upload-modal.js b/ghost/admin/components/gh-upload-modal.js index d5a6775290..33242ec040 100644 --- a/ghost/admin/components/gh-upload-modal.js +++ b/ghost/admin/components/gh-upload-modal.js @@ -43,6 +43,7 @@ var UploadModal = ModalDialog.extend({ func.apply(this); } this.sendAction(); + this.sendAction('confirm' + type); } } }); diff --git a/ghost/admin/controllers/modals/upload.js b/ghost/admin/controllers/modals/upload.js index 953dfe2bb7..1664b93a56 100644 --- a/ghost/admin/controllers/modals/upload.js +++ b/ghost/admin/controllers/modals/upload.js @@ -2,15 +2,17 @@ var UploadController = Ember.Controller.extend({ acceptEncoding: 'image/*', actions: { - confirmReject: function () { - return true; - } - }, + confirmAccept: function () { + var self = this; - confirm: { - reject: { - buttonClass: true, - text: 'Cancel' // The reject button text + this.get('model').save().then(function (model) { + self.notifications.showSuccess('Saved'); + return model; + }).catch(this.notifications.showErrors); + }, + + confirmReject: function () { + return false; } } }); diff --git a/ghost/admin/controllers/settings/general.js b/ghost/admin/controllers/settings/general.js index 95aeea9396..159e03688a 100644 --- a/ghost/admin/controllers/settings/general.js +++ b/ghost/admin/controllers/settings/general.js @@ -1,10 +1,3 @@ -var elementLookup = { - title: '#blog-title', - description: '#blog-description', - email: '#email-address', - postsPerPage: '#postsPerPage' -}; - var SettingsGeneralController = Ember.ObjectController.extend({ isDatedPermalinks: function (key, value) { // setter @@ -18,41 +11,31 @@ var SettingsGeneralController = Ember.ObjectController.extend({ return slugForm !== '/:slug/'; }.property('permalinks'), + themes: function () { + return this.get('availableThemes').reduce(function (themes, t) { + var theme = {}; + + theme.name = t.name; + theme.label = t.package ? t.package.name + ' - ' + t.package.version : t.name; + theme.package = t.package; + theme.active = !!t.active; + + themes.push(theme); + + return themes; + }, []); + }.property('availableThemes').readOnly(), + actions: { save: function () { - // Validate and save settings - var model = this.get('model'), - // @TODO: Don't know how to scope this to this controllers view because this.view is null - errs = model.validate(); + var self = this; - if (errs.length > 0) { - // Set the actual element from this view based on the error - errs.forEach(function (err) { - // @TODO: Probably should still be scoped to this controllers root element. - err.el = $(elementLookup[err.el]); - }); - - // Let the applicationRoute handle validation errors - this.send('handleErrors', errs); - } else { - model.save().then(function () { - // @TODO: Notification of success - window.alert('Saved data!'); - }, function () { - // @TODO: Notification of error - window.alert('Error saving data'); - }); - } + return this.get('model').save().then(function (model) { + self.notifications.showSuccess('Settings successfully saved.'); + return model; + }).catch(this.notifications.showErrors); }, - - uploadLogo: function () { - // @TODO: Integrate with Modal component - }, - - uploadCover: function () { - // @TODO: Integrate with Modal component - } } }); -export default SettingsGeneralController; \ No newline at end of file +export default SettingsGeneralController; diff --git a/ghost/admin/models/setting.js b/ghost/admin/models/setting.js new file mode 100644 index 0000000000..d77930c302 --- /dev/null +++ b/ghost/admin/models/setting.js @@ -0,0 +1,15 @@ +var Setting = DS.Model.extend({ + title: DS.attr('string'), + description: DS.attr('string'), + email: DS.attr('string'), + logo: DS.attr('string'), + cover: DS.attr('string'), + defaultLang: DS.attr('string'), + postsPerPage: DS.attr('number'), + forceI18n: DS.attr('boolean'), + permalinks: DS.attr('string'), + activeTheme: DS.attr('string'), + availableThemes: DS.attr() +}); + +export default Setting; diff --git a/ghost/admin/models/settings.js b/ghost/admin/models/settings.js deleted file mode 100644 index 65a3a617c9..0000000000 --- a/ghost/admin/models/settings.js +++ /dev/null @@ -1,76 +0,0 @@ -var validator = window.validator; - -import BaseModel from 'ghost/models/base'; - -var SettingsModel = BaseModel.extend({ - url: BaseModel.apiRoot + '/settings/?type=blog,theme,app', - - title: null, - description: null, - email: null, - logo: null, - cover: null, - defaultLang: null, - postsPerPage: null, - forceI18n: null, - permalinks: null, - activeTheme: null, - activeApps: null, - installedApps: null, - availableThemes: null, - availableApps: null, - - validate: function () { - var validationErrors = [], - postsPerPage; - - if (!validator.isLength(this.get('title'), 0, 150)) { - validationErrors.push({message: 'Title is too long', el: 'title'}); - } - - if (!validator.isLength(this.get('description'), 0, 200)) { - validationErrors.push({message: 'Description is too long', el: 'description'}); - } - - if (!validator.isEmail(this.get('email')) || !validator.isLength(this.get('email'), 0, 254)) { - validationErrors.push({message: 'Please supply a valid email address', el: 'email'}); - } - - postsPerPage = this.get('postsPerPage'); - if (!validator.isInt(postsPerPage) || postsPerPage > 1000) { - validationErrors.push({message: 'Please use a number less than 1000', el: 'postsPerPage'}); - } - - if (!validator.isInt(postsPerPage) || postsPerPage < 0) { - validationErrors.push({message: 'Please use a number greater than 0', el: 'postsPerPage'}); - } - - return validationErrors; - }, - exportPath: BaseModel.adminRoot + '/export/', - importFrom: function (file) { - var formData = new FormData(); - formData.append('importfile', file); - return ic.ajax.request(BaseModel.apiRoot + '/db/', { - headers: { - 'X-CSRF-Token': $('meta[name="csrf-param"]').attr('content') - }, - type: 'POST', - data: formData, - dataType: 'json', - cache: false, - contentType: false, - processData: false - }); - }, - sendTestEmail: function () { - return ic.ajax.request(BaseModel.apiRoot + '/mail/test/', { - type: 'POST', - headers: { - 'X-CSRF-Token': $('meta[name="csrf-param"]').attr('content') - } - }); - } -}); - -export default SettingsModel; diff --git a/ghost/admin/routes/settings/general.js b/ghost/admin/routes/settings/general.js index 25be847219..bc91262b01 100644 --- a/ghost/admin/routes/settings/general.js +++ b/ghost/admin/routes/settings/general.js @@ -1,13 +1,11 @@ -import ajax from 'ghost/utils/ajax'; import AuthenticatedRoute from 'ghost/routes/authenticated'; -import SettingsModel from 'ghost/models/settings'; var SettingsGeneralRoute = AuthenticatedRoute.extend({ model: function () { - return ajax('/ghost/api/v0.1/settings/?type=blog,theme,app').then(function (resp) { - return SettingsModel.create(resp); + return this.store.find('setting', { type: 'blog,theme' }).then(function (records) { + return records.get('firstObject'); }); } }); -export default SettingsGeneralRoute; \ No newline at end of file +export default SettingsGeneralRoute; diff --git a/ghost/admin/routes/settings/index.js b/ghost/admin/routes/settings/index.js index 274311f817..a2c4bb5567 100644 --- a/ghost/admin/routes/settings/index.js +++ b/ghost/admin/routes/settings/index.js @@ -1,10 +1,9 @@ import AuthenticatedRoute from 'ghost/routes/authenticated'; var SettingsIndexRoute = AuthenticatedRoute.extend({ - // redirect to general tab redirect: function () { this.transitionTo('settings.general'); } }); -export default SettingsIndexRoute; \ No newline at end of file +export default SettingsIndexRoute; diff --git a/ghost/admin/serializers/setting.js b/ghost/admin/serializers/setting.js new file mode 100644 index 0000000000..bdb2b80150 --- /dev/null +++ b/ghost/admin/serializers/setting.js @@ -0,0 +1,37 @@ +import ApplicationSerializer from 'ghost/serializers/application'; + +var SettingSerializer = ApplicationSerializer.extend({ + serializeIntoHash: function (hash, type, record, options) { + // Settings API does not want ids + options = options || {}; + options.includeId = false; + + var root = Ember.String.pluralize(type.typeKey), + data = this.serialize(record, options), + payload = []; + + delete data.id; + + Object.keys(data).forEach(function (k) { + payload.push({ key: k, value: data[k] }); + }); + + hash[root] = payload; + }, + + extractArray: function (store, type, _payload) { + var payload = { id: '0' }; + + _payload.settings.forEach(function (setting) { + payload[setting.key] = setting.value; + }); + + return [payload]; + }, + + extractSingle: function (store, type, payload) { + return this.extractArray(store, type, payload).pop(); + } +}); + +export default SettingSerializer; diff --git a/ghost/admin/templates/settings/general.hbs b/ghost/admin/templates/settings/general.hbs index db488de400..6bc521cb92 100644 --- a/ghost/admin/templates/settings/general.hbs +++ b/ghost/admin/templates/settings/general.hbs @@ -67,11 +67,13 @@
- + {{view Ember.Select + id="activeTheme" + name="general[activeTheme]" + content=themes + optionValuePath="content.name" + optionLabelPath="content.label" + value=activeTheme}}

Select a theme for your blog