mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
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.
This commit is contained in:
parent
2dd5c5218a
commit
18a433debd
21
ghost/admin/adapters/setting.js
Normal file
21
ghost/admin/adapters/setting.js
Normal file
@ -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;
|
@ -43,6 +43,7 @@ var UploadModal = ModalDialog.extend({
|
||||
func.apply(this);
|
||||
}
|
||||
this.sendAction();
|
||||
this.sendAction('confirm' + type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
export default SettingsGeneralController;
|
||||
|
15
ghost/admin/models/setting.js
Normal file
15
ghost/admin/models/setting.js
Normal file
@ -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;
|
@ -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;
|
@ -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;
|
||||
export default SettingsGeneralRoute;
|
||||
|
@ -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;
|
||||
export default SettingsIndexRoute;
|
||||
|
37
ghost/admin/serializers/setting.js
Normal file
37
ghost/admin/serializers/setting.js
Normal file
@ -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;
|
@ -67,11 +67,13 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="activeTheme">Theme</label>
|
||||
<select id="activeTheme" name="general[activeTheme]">
|
||||
{{#each availableThemes}}
|
||||
<option value="{{name}}" {{#if active}}selected{{/if}}>{{#if package}}{{package.name}} - {{package.version}}{{else}}{{name}}{{/if}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{view Ember.Select
|
||||
id="activeTheme"
|
||||
name="general[activeTheme]"
|
||||
content=themes
|
||||
optionValuePath="content.name"
|
||||
optionLabelPath="content.label"
|
||||
value=activeTheme}}
|
||||
<p>Select a theme for your blog</p>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user