0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Merge pull request #5351 from jaswilli/injection

Use Ember.inject instead of needs and initializers
This commit is contained in:
Matt Enlow 2015-05-27 16:55:13 +02:00
commit 557a888873
61 changed files with 377 additions and 483 deletions

1
.gitignore vendored
View file

@ -60,6 +60,7 @@ CHANGELOG.md
config.js
/core/client/config.js
!/core/client/app/services/config.js
# Built asset files
/core/built

View file

@ -1,10 +1,12 @@
import Ember from 'ember';
var AlertComponent = Ember.Component.extend({
export default Ember.Component.extend({
tagName: 'article',
classNames: ['gh-alert', 'gh-alert-blue'],
classNameBindings: ['typeClass'],
notifications: Ember.inject.service(),
typeClass: Ember.computed(function () {
var classes = '',
message = this.get('message'),
@ -31,10 +33,7 @@ var AlertComponent = Ember.Component.extend({
actions: {
closeNotification: function () {
var self = this;
self.notifications.closeNotification(self.get('message'));
this.get('notifications').closeNotification(this.get('message'));
}
}
});
export default AlertComponent;

View file

@ -1,7 +1,8 @@
import Ember from 'ember';
var blogUrl = Ember.Component.extend({
tagName: ''
tagName: '',
config: Ember.inject.service()
});
export default blogUrl;

View file

@ -1,7 +1,7 @@
import Ember from 'ember';
import DropdownMixin from 'ghost/mixins/dropdown-mixin';
var DropdownButton = Ember.Component.extend(DropdownMixin, {
export default Ember.Component.extend(DropdownMixin, {
tagName: 'button',
attributeBindings: 'role',
role: 'button',
@ -9,11 +9,11 @@ var DropdownButton = Ember.Component.extend(DropdownMixin, {
// matches with the dropdown this button toggles
dropdownName: null,
dropdown: Ember.inject.service(),
// Notify dropdown service this dropdown should be toggled
click: function (event) {
this._super(event);
this.get('dropdown').toggleDropdown(this.get('dropdownName'), this);
}
});
export default DropdownButton;

View file

@ -1,8 +1,10 @@
import Ember from 'ember';
import DropdownMixin from 'ghost/mixins/dropdown-mixin';
var GhostDropdown = Ember.Component.extend(DropdownMixin, {
export default Ember.Component.extend(DropdownMixin, {
classNames: 'ghost-dropdown',
classNameBindings: ['fadeIn:fade-in-scale:fade-out', 'isOpen:open:closed'],
name: null,
closeOnClick: false,
@ -17,7 +19,7 @@ var GhostDropdown = Ember.Component.extend(DropdownMixin, {
return this.get('isOpen') && !this.get('closing');
}),
classNameBindings: ['fadeIn:fade-in-scale:fade-out', 'isOpen:open:closed'],
dropdown: Ember.inject.service(),
open: function () {
this.set('isOpen', true);
@ -88,5 +90,3 @@ var GhostDropdown = Ember.Component.extend(DropdownMixin, {
dropdownService.off('toggle', this, this.toggle);
}
});
export default GhostDropdown;

View file

@ -2,6 +2,8 @@ import Ember from 'ember';
import uploader from 'ghost/assets/lib/uploader';
var Preview = Ember.Component.extend({
config: Ember.inject.service(),
didInsertElement: function () {
this.set('scrollWrapper', this.$().closest('.entry-preview-content'));
Ember.run.scheduleOnce('afterRender', this, this.dropzoneHandler);

View file

@ -5,6 +5,8 @@ export default Ember.Component.extend({
classNames: ['gh-nav'],
classNameBindings: ['open'],
config: Ember.inject.service(),
open: false,
autoNav: null,

View file

@ -1,10 +1,14 @@
import Ember from 'ember';
var NotificationComponent = Ember.Component.extend({
export default Ember.Component.extend({
tagName: 'article',
classNames: ['gh-notification', 'gh-notification-green'],
classNameBindings: ['typeClass'],
message: null,
notifications: Ember.inject.service(),
typeClass: Ember.computed(function () {
var classes = '',
message = this.get('message'),
@ -34,17 +38,14 @@ var NotificationComponent = Ember.Component.extend({
self.$().on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function (event) {
if (event.originalEvent.animationName === 'fade-out') {
self.notifications.removeObject(self.get('message'));
self.get('notifications').removeObject(self.get('message'));
}
});
},
actions: {
closeNotification: function () {
var self = this;
self.notifications.closeNotification(self.get('message'));
this.get('notifications').closeNotification(this.get('message'));
}
}
});
export default NotificationComponent;

View file

@ -1,14 +1,15 @@
import Ember from 'ember';
var NotificationsComponent = Ember.Component.extend({
export default Ember.Component.extend({
tagName: 'aside',
classNames: 'gh-notifications',
messages: Ember.computed.filter('notifications', function (notification) {
notifications: Ember.inject.service(),
messages: Ember.computed.filter('notifications.content', function (notification) {
var displayStatus = (typeof notification.toJSON === 'function') ?
notification.get('status') : notification.status;
return displayStatus === 'passive';
})
});
export default NotificationsComponent;

View file

@ -1,8 +1,10 @@
import Ember from 'ember';
import DropdownButton from 'ghost/components/gh-dropdown-button';
var PopoverButton = DropdownButton.extend({
click: Ember.K, // We don't want clicks on popovers, but dropdowns have them. So `K`ill them here.
export default DropdownButton.extend({
dropdown: Ember.inject.service(),
click: Ember.K,
mouseEnter: function (event) {
this._super(event);
@ -14,5 +16,3 @@ var PopoverButton = DropdownButton.extend({
this.get('dropdown').toggleDropdown(this.get('popoverName'), this);
}
});
export default PopoverButton;

View file

@ -1,7 +1,7 @@
import Ember from 'ember';
import GhostDropdown from 'ghost/components/gh-dropdown';
var GhostPopover = GhostDropdown.extend({
classNames: 'ghost-popover'
export default GhostDropdown.extend({
classNames: 'ghost-popover',
dropdown: Ember.inject.service()
});
export default GhostPopover;

View file

@ -6,6 +6,8 @@ import cajaSanitizers from 'ghost/utils/caja-sanitizers';
var UploadModal = ModalDialog.extend({
layoutName: 'components/gh-modal-dialog',
config: Ember.inject.service(),
didInsertElement: function () {
this._super();
upload.call(this.$('.js-drop-zone'), {fileStorage: this.get('config.fileStorage')});

View file

@ -4,6 +4,8 @@ import uploader from 'ghost/assets/lib/uploader';
var PostImageUploader = Ember.Component.extend({
classNames: ['image-uploader', 'js-post-image-upload'],
config: Ember.inject.service(),
imageSource: Ember.computed('image', function () {
return this.get('image') || '';
}),

View file

@ -8,9 +8,11 @@ var urlPreview = Ember.Component.extend({
prefix: null,
slug: null,
config: Ember.inject.service(),
url: Ember.computed('slug', function () {
// Get the blog URL and strip the scheme
var blogUrl = this.get('config').blogUrl,
var blogUrl = this.get('config.blogUrl'),
noSchemeBlogUrl = blogUrl.substr(blogUrl.indexOf('://') + 3), // Remove `http[s]://`
// Get the prefix and slug values

View file

@ -1,6 +1,8 @@
import Ember from 'ember';
export default Ember.Controller.extend({
dropdown: Ember.inject.service(),
// jscs: disable
signedOut: Ember.computed.match('currentPath', /(signin|signup|setup|reset)/),
// jscs: enable

View file

@ -1,8 +1,5 @@
import Ember from 'ember';
var CopyHTMLController = Ember.Controller.extend({
export default Ember.Controller.extend({
generatedHTML: Ember.computed.alias('model.generatedHTML')
});
export default CopyHTMLController;

View file

@ -2,6 +2,9 @@ import Ember from 'ember';
import {request as ajax} from 'ic-ajax';
export default Ember.Controller.extend({
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
actions: {
confirmAccept: function () {
var self = this;
@ -9,11 +12,11 @@ export default Ember.Controller.extend({
ajax(this.get('ghostPaths.url').api('db'), {
type: 'DELETE'
}).then(function () {
self.notifications.showSuccess('All content deleted from database.');
self.get('notifications').showSuccess('All content deleted from database.');
self.store.unloadAll('post');
self.store.unloadAll('tag');
}).catch(function (response) {
self.notifications.showErrors(response);
self.get('notifications').showErrors(response);
});
},

View file

@ -1,5 +1,9 @@
import Ember from 'ember';
var DeletePostController = Ember.Controller.extend({
export default Ember.Controller.extend({
dropdown: Ember.inject.service(),
notifications: Ember.inject.service(),
actions: {
confirmAccept: function () {
var self = this,
@ -11,9 +15,9 @@ var DeletePostController = Ember.Controller.extend({
model.destroyRecord().then(function () {
self.get('dropdown').closeDropdowns();
self.transitionToRoute('posts.index');
self.notifications.showSuccess('Your post has been deleted.', {delayed: true});
self.get('notifications').showSuccess('Your post has been deleted.', {delayed: true});
}, function () {
self.notifications.showError('Your post could not be deleted. Please try again.');
self.get('notifications').showError('Your post could not be deleted. Please try again.');
});
},
@ -33,5 +37,3 @@ var DeletePostController = Ember.Controller.extend({
}
}
});
export default DeletePostController;

View file

@ -1,5 +1,8 @@
import Ember from 'ember';
var DeleteTagController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
postInflection: Ember.computed('model.post_count', function () {
return this.get('model.post_count') > 1 ? 'posts' : 'post';
}),
@ -13,9 +16,9 @@ var DeleteTagController = Ember.Controller.extend({
this.send('closeSettingsMenu');
tag.destroyRecord().then(function () {
self.notifications.showSuccess('Deleted ' + name);
self.get('notifications').showSuccess('Deleted ' + name);
}).catch(function (error) {
self.notifications.showAPIError(error);
self.get('notifications').showAPIError(error);
});
},
@ -35,5 +38,3 @@ var DeleteTagController = Ember.Controller.extend({
}
}
});
export default DeleteTagController;

View file

@ -1,5 +1,8 @@
import Ember from 'ember';
var DeleteUserController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
userPostCount: Ember.computed('model.id', function () {
var promise,
query = {
@ -28,9 +31,9 @@ var DeleteUserController = Ember.Controller.extend({
user.destroyRecord().then(function () {
self.store.unloadAll('post');
self.transitionToRoute('settings.users');
self.notifications.showSuccess('The user has been deleted.', {delayed: true});
self.get('notifications').showSuccess('The user has been deleted.', {delayed: true});
}, function () {
self.notifications.showError('The user could not be deleted. Please try again.');
self.get('notifications').showError('The user could not be deleted. Please try again.');
});
},
@ -50,5 +53,3 @@ var DeleteUserController = Ember.Controller.extend({
}
}
});
export default DeleteUserController;

View file

@ -1,5 +1,8 @@
import Ember from 'ember';
var InviteNewUserController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
// Used to set the initial value for the dropdown
authorRole: Ember.computed(function () {
var self = this;
@ -45,9 +48,9 @@ var InviteNewUserController = Ember.Controller.extend({
if (invitedUser) {
if (invitedUser.get('status') === 'invited' || invitedUser.get('status') === 'invited-pending') {
self.notifications.showWarn('A user with that email address was already invited.');
self.get('notifications').showWarn('A user with that email address was already invited.');
} else {
self.notifications.showWarn('A user with that email address already exists.');
self.get('notifications').showWarn('A user with that email address already exists.');
}
} else {
newUser = self.store.createRecord('user', {
@ -62,13 +65,13 @@ var InviteNewUserController = Ember.Controller.extend({
// If sending the invitation email fails, the API will still return a status of 201
// but the user's status in the response object will be 'invited-pending'.
if (newUser.get('status') === 'invited-pending') {
self.notifications.showWarn('Invitation email was not sent. Please try resending.');
self.get('notifications').showWarn('Invitation email was not sent. Please try resending.');
} else {
self.notifications.showSuccess(notificationText);
self.get('notifications').showSuccess(notificationText);
}
}).catch(function (errors) {
newUser.deleteRecord();
self.notifications.showErrors(errors);
self.get('notifications').showErrors(errors);
});
}
});
@ -79,5 +82,3 @@ var InviteNewUserController = Ember.Controller.extend({
}
}
});
export default InviteNewUserController;

View file

@ -1,5 +1,8 @@
import Ember from 'ember';
var LeaveEditorController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
args: Ember.computed.alias('model'),
actions: {
@ -16,7 +19,7 @@ var LeaveEditorController = Ember.Controller.extend({
}
if (!transition || !editorController) {
this.notifications.showError('Sorry, there was an error in the application. Please let the Ghost team know what happened.');
this.get('notifications').showError('Sorry, there was an error in the application. Please let the Ghost team know what happened.');
return true;
}
@ -56,5 +59,3 @@ var LeaveEditorController = Ember.Controller.extend({
}
}
});
export default LeaveEditorController;

View file

@ -2,17 +2,18 @@ import Ember from 'ember';
import ValidationEngine from 'ghost/mixins/validation-engine';
export default Ember.Controller.extend(ValidationEngine, {
needs: 'application',
validationType: 'signin',
application: Ember.inject.controller(),
notifications: Ember.inject.service(),
identification: Ember.computed('session.user.email', function () {
return this.get('session.user.email');
}),
actions: {
authenticate: function () {
var appController = this.get('controllers.application'),
var appController = this.get('application'),
authStrategy = 'simple-auth-authenticator:oauth2-password-grant',
data = this.getProperties('identification', 'password'),
self = this;
@ -21,7 +22,7 @@ export default Ember.Controller.extend(ValidationEngine, {
this.get('session').authenticate(authStrategy, data).then(function () {
self.send('closeModal');
self.notifications.showSuccess('Login successful.');
self.get('notifications').showSuccess('Login successful.');
self.set('password', '');
}).catch(function () {
// if authentication fails a rejected promise will be returned.
@ -40,10 +41,10 @@ export default Ember.Controller.extend(ValidationEngine, {
$('#login').find('input').trigger('change');
this.validate({format: false}).then(function () {
self.notifications.closePassive();
self.get('notifications').closePassive();
self.send('authenticate');
}).catch(function (errors) {
self.notifications.showErrors(errors);
self.get('notifications').showErrors(errors);
});
},

View file

@ -2,6 +2,10 @@ import Ember from 'ember';
import {request as ajax} from 'ic-ajax';
export default Ember.Controller.extend({
dropdown: Ember.inject.service(),
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
actions: {
confirmAccept: function () {
var user = this.get('model'),
@ -29,9 +33,9 @@ export default Ember.Controller.extend({
});
}
self.notifications.showSuccess('Ownership successfully transferred to ' + user.get('name'));
self.get('notifications').showSuccess('Ownership successfully transferred to ' + user.get('name'));
}).catch(function (error) {
self.notifications.showAPIError(error);
self.get('notifications').showAPIError(error);
});
},

View file

@ -1,16 +1,20 @@
import Ember from 'ember';
var UploadController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
acceptEncoding: 'image/*',
actions: {
confirmAccept: function () {
var self = this;
var notifications = this.get('notifications');
this.get('model').save().then(function (model) {
self.notifications.showSuccess('Saved');
notifications.showSuccess('Saved');
return model;
}).catch(function (err) {
self.notifications.showErrors(err);
notifications.showErrors(err);
});
},
@ -19,5 +23,3 @@ var UploadController = Ember.Controller.extend({
}
}
});
export default UploadController;

View file

@ -1,18 +1,22 @@
import Ember from 'ember';
/* global moment */
import Ember from 'ember';
import {parseDateString, formatDate} from 'ghost/utils/date-formatting';
import SettingsMenuMixin from 'ghost/mixins/settings-menu-controller';
import SlugGenerator from 'ghost/models/slug-generator';
import boundOneWay from 'ghost/utils/bound-one-way';
import isNumber from 'ghost/utils/isNumber';
var PostSettingsMenuController = Ember.Controller.extend(SettingsMenuMixin, {
export default Ember.Controller.extend(SettingsMenuMixin, {
debounceId: null,
lastPromise: null,
selectedAuthor: null,
uploaderReference: null,
application: Ember.inject.controller(),
config: Ember.inject.service(),
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
initializeSelectedAuthor: function () {
var self = this;
@ -212,11 +216,11 @@ var PostSettingsMenuController = Ember.Controller.extend(SettingsMenuMixin, {
showErrors: function (errors) {
errors = Ember.isArray(errors) ? errors : [errors];
this.notifications.showErrors(errors);
this.get('notifications').showErrors(errors);
},
showSuccess: function (message) {
this.notifications.showSuccess(message);
this.get('notifications').showSuccess(message);
},
actions: {
@ -465,5 +469,3 @@ var PostSettingsMenuController = Ember.Controller.extend(SettingsMenuMixin, {
}
}
});
export default PostSettingsMenuController;

View file

@ -1,8 +1,13 @@
import Ember from 'ember';
var PostController = Ember.Controller.extend({
isPublished: Ember.computed.equal('model.status', 'published'),
export default Ember.Controller.extend({
classNameBindings: ['model.featured'],
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
isPublished: Ember.computed.equal('model.status', 'published'),
authorName: Ember.computed('model.author.name', 'model.author.email', function () {
return this.get('model.author.name') || this.get('model.author.email');
}),
@ -17,17 +22,16 @@ var PostController = Ember.Controller.extend({
actions: {
toggleFeatured: function () {
var self = this;
var notifications = this.get('notifications');
this.toggleProperty('model.featured');
this.get('model').save().catch(function (errors) {
self.notifications.showErrors(errors);
notifications.showErrors(errors);
});
},
showPostContent: function () {
this.transitionToRoute('posts.post', this.get('model'));
}
}
});
export default PostController;

View file

@ -10,6 +10,9 @@ export default Ember.Controller.extend(ValidationEngine, {
validationType: 'reset',
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
email: Ember.computed('token', function () {
// The token base64 encodes the email (and some other stuff),
// each section is divided by a '|'. Email comes second.
@ -40,18 +43,18 @@ export default Ember.Controller.extend(ValidationEngine, {
}
}).then(function (resp) {
self.toggleProperty('submitting');
self.notifications.showSuccess(resp.passwordreset[0].message, true);
self.get('notifications').showSuccess(resp.passwordreset[0].message, true);
self.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', {
identification: self.get('email'),
password: credentials.newPassword
});
}).catch(function (response) {
self.notifications.showAPIError(response);
self.get('notifications').showAPIError(response);
self.toggleProperty('submitting');
});
}).catch(function (error) {
self.toggleProperty('submitting');
self.notifications.showErrors(error);
self.get('notifications').showErrors(error);
});
}
}

View file

@ -1,20 +1,19 @@
import Ember from 'ember';
var SettingsCodeInjectionController = Ember.Controller.extend({
export default Ember.Controller.extend({
actions: {
save: function () {
var self = this;
var notifications = this.get('notifications');
return this.get('model').save().then(function (model) {
self.notifications.closePassive();
self.notifications.showSuccess('Settings successfully saved.');
notifications.closePassive();
notifications.showSuccess('Settings successfully saved.');
return model;
}).catch(function (errors) {
self.notifications.closePassive();
self.notifications.showErrors(errors);
notifications.closePassive();
notifications.showErrors(errors);
});
}
}
});
export default SettingsCodeInjectionController;

View file

@ -1,7 +1,9 @@
import Ember from 'ember';
import randomPassword from 'ghost/utils/random-password';
var SettingsGeneralController = Ember.Controller.extend({
export default Ember.Controller.extend({
notifications: Ember.inject.service(),
selectedTheme: null,
logoImageSource: Ember.computed('model.logo', function () {
@ -47,14 +49,14 @@ var SettingsGeneralController = Ember.Controller.extend({
actions: {
save: function () {
var self = this;
var notifications = this.get('notifications');
return this.get('model').save().then(function (model) {
self.notifications.showSuccess('Settings successfully saved.');
notifications.showSuccess('Settings successfully saved.');
return model;
}).catch(function (errors) {
self.notifications.showErrors(errors);
notifications.showErrors(errors);
});
},
@ -67,5 +69,3 @@ var SettingsGeneralController = Ember.Controller.extend({
}
}
});
export default SettingsGeneralController;

View file

@ -2,10 +2,12 @@ import Ember from 'ember';
import {request as ajax} from 'ic-ajax';
export default Ember.Controller.extend(Ember.Evented, {
needs: ['feature'],
uploadButtonText: 'Import',
importErrors: '',
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
labsJSON: Ember.computed('model.labs', function () {
return JSON.parse(this.get('model.labs') || {});
}),
@ -28,11 +30,12 @@ export default Ember.Controller.extend(Ember.Evented, {
actions: {
onUpload: function (file) {
var self = this,
formData = new FormData();
formData = new FormData(),
notifications = this.get('notifications');
this.set('uploadButtonText', 'Importing');
this.set('importErrors', '');
this.notifications.closePassive();
notifications.closePassive();
formData.append('importfile', file);
@ -51,13 +54,13 @@ export default Ember.Controller.extend(Ember.Evented, {
self.store.unloadAll('role');
self.store.unloadAll('setting');
self.store.unloadAll('notification');
self.notifications.showSuccess('Import successful.');
notifications.showSuccess('Import successful.');
}).catch(function (response) {
if (response && response.jqXHR && response.jqXHR.responseJSON && response.jqXHR.responseJSON.errors) {
self.set('importErrors', response.jqXHR.responseJSON.errors);
}
self.notifications.showError('Import Failed');
notifications.showError('Import Failed');
}).finally(function () {
self.set('uploadButtonText', 'Import');
self.trigger('reset');
@ -77,17 +80,17 @@ export default Ember.Controller.extend(Ember.Evented, {
},
sendTestEmail: function () {
var self = this;
var notifications = this.get('notifications');
ajax(this.get('ghostPaths.url').api('mail', 'test'), {
type: 'POST'
}).then(function () {
self.notifications.showSuccess('Check your email for the test message.');
notifications.showSuccess('Check your email for the test message.');
}).catch(function (error) {
if (typeof error.jqXHR !== 'undefined') {
self.notifications.showAPIError(error);
notifications.showAPIError(error);
} else {
self.notifications.showErrors(error);
notifications.showErrors(error);
}
});
}

View file

@ -1,8 +1,6 @@
import Ember from 'ember';
var NavigationController,
NavItem;
NavItem = Ember.Object.extend({
var NavItem = Ember.Object.extend({
label: '',
url: '',
last: false,
@ -12,7 +10,10 @@ NavItem = Ember.Object.extend({
})
});
NavigationController = Ember.Controller.extend({
export default Ember.Controller.extend({
config: Ember.inject.service(),
notifications: Ember.inject.service(),
blogUrl: Ember.computed('config.blogUrl', function () {
var url = this.get('config.blogUrl');
@ -96,18 +97,18 @@ NavigationController = Ember.Controller.extend({
},
save: function () {
var self = this,
navSetting,
var navSetting,
blogUrl = this.get('config').blogUrl,
blogUrlRegex = new RegExp('^' + blogUrl + '(.*)', 'i'),
navItems = this.get('navigationItems'),
message = 'One of your navigation items has an empty label. ' +
'<br /> Please enter a new label or delete the item before saving.',
match;
match,
notifications = this.get('notifications');
// Don't save if there's a blank label.
if (navItems.find(function (item) { return !item.get('isComplete') && !item.get('last');})) {
self.notifications.showErrors([message.htmlSafe()]);
if (navItems.find(function (item) {return !item.get('isComplete') && !item.get('last');})) {
notifications.showErrors([message.htmlSafe()]);
return;
}
@ -147,15 +148,13 @@ NavigationController = Ember.Controller.extend({
// we need to have navigationItems recomputed.
this.get('model').notifyPropertyChange('navigation');
this.notifications.closePassive();
notifications.closePassive();
this.get('model').save().then(function () {
self.notifications.showSuccess('Navigation items saved.');
notifications.showSuccess('Navigation items saved.');
}).catch(function (err) {
self.notifications.showErrors(err);
notifications.showErrors(err);
});
}
}
});
export default NavigationController;

View file

@ -3,7 +3,7 @@ import PaginationMixin from 'ghost/mixins/pagination-controller';
import SettingsMenuMixin from 'ghost/mixins/settings-menu-controller';
import boundOneWay from 'ghost/utils/bound-one-way';
var TagsController = Ember.ArrayController.extend(PaginationMixin, SettingsMenuMixin, {
export default Ember.ArrayController.extend(PaginationMixin, SettingsMenuMixin, {
tags: Ember.computed.alias('model'),
activeTag: null,
@ -20,10 +20,12 @@ var TagsController = Ember.ArrayController.extend(PaginationMixin, SettingsMenuM
},
application: Ember.inject.controller(),
config: Ember.inject.service(),
notifications: Ember.inject.service(),
showErrors: function (errors) {
errors = Ember.isArray(errors) ? errors : [errors];
this.notifications.showErrors(errors);
this.get('notifications').showErrors(errors);
},
saveActiveTagProperty: function (propKey, newValue) {
@ -40,7 +42,7 @@ var TagsController = Ember.ArrayController.extend(PaginationMixin, SettingsMenuM
activeTag.set(propKey, newValue);
this.notifications.closePassive();
this.get('notifications').closePassive();
activeTag.save().catch(function (errors) {
self.showErrors(errors);
@ -62,7 +64,7 @@ var TagsController = Ember.ArrayController.extend(PaginationMixin, SettingsMenuM
}),
seoURL: Ember.computed('activeTagSlugScratch', function () {
var blogUrl = this.get('config').blogUrl,
var blogUrl = this.get('config.blogUrl'),
seoSlug = this.get('activeTagSlugScratch') ? this.get('activeTagSlugScratch') : '',
seoURL = blogUrl + '/tag/' + seoSlug;
@ -137,5 +139,3 @@ var TagsController = Ember.ArrayController.extend(PaginationMixin, SettingsMenuM
}
}
});
export default TagsController;

View file

@ -3,7 +3,9 @@ import SlugGenerator from 'ghost/models/slug-generator';
import isNumber from 'ghost/utils/isNumber';
import boundOneWay from 'ghost/utils/bound-one-way';
var SettingsUserController = Ember.Controller.extend({
export default Ember.Controller.extend({
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
user: Ember.computed.alias('model'),
@ -16,8 +18,10 @@ var SettingsUserController = Ember.Controller.extend({
coverDefault: Ember.computed('ghostPaths', function () {
return this.get('ghostPaths.url').asset('/shared/img/user-cover.png');
}),
coverImageBackground: Ember.computed('user.cover', 'coverDefault', function () {
var url = this.get('user.cover') || this.get('coverDefault');
return `background-image: url(${url})`.htmlSafe();
}),
@ -28,8 +32,10 @@ var SettingsUserController = Ember.Controller.extend({
userDefault: Ember.computed('ghostPaths', function () {
return this.get('ghostPaths.url').asset('/shared/img/user-image.png');
}),
userImageBackground: Ember.computed('user.image', 'userDefault', function () {
var url = this.get('user.image') || this.get('userDefault');
return `background-image: url(${url})`.htmlSafe();
}),
@ -68,14 +74,15 @@ var SettingsUserController = Ember.Controller.extend({
if (model.get('invited')) {
model.destroyRecord().then(function () {
var notificationText = 'Invitation revoked. (' + email + ')';
self.notifications.showSuccess(notificationText, false);
self.get('notifications').showSuccess(notificationText, false);
}).catch(function (error) {
self.notifications.showAPIError(error);
self.get('notifications').showAPIError(error);
});
} else {
// if the user is no longer marked as "invited", then show a warning and reload the route
self.get('target').send('reload');
self.notifications.showError('This user has already accepted the invitation.', {delayed: 500});
self.get('notifications').showError('This user has already accepted the invitation.', {delayed: 500});
}
});
},
@ -88,13 +95,13 @@ var SettingsUserController = Ember.Controller.extend({
// If sending the invitation email fails, the API will still return a status of 201
// but the user's status in the response object will be 'invited-pending'.
if (result.users[0].status === 'invited-pending') {
self.notifications.showWarn('Invitation email was not sent. Please try resending.');
self.get('notifications').showWarn('Invitation email was not sent. Please try resending.');
} else {
self.get('model').set('status', result.users[0].status);
self.notifications.showSuccess(notificationText);
self.get('notifications').showSuccess(notificationText);
}
}).catch(function (error) {
self.notifications.showAPIError(error);
self.get('notifications').showAPIError(error);
});
},
@ -117,7 +124,7 @@ var SettingsUserController = Ember.Controller.extend({
var currentPath,
newPath;
self.notifications.showSuccess('Settings successfully saved.');
self.get('notifications').showSuccess('Settings successfully saved.');
// If the user's slug has changed, change the URL and replace
// the history so refresh and back button still work
@ -133,7 +140,7 @@ var SettingsUserController = Ember.Controller.extend({
return model;
}).catch(function (errors) {
self.notifications.showErrors(errors);
self.get('notifications').showErrors(errors);
});
this.set('lastPromise', promise);
@ -152,14 +159,14 @@ var SettingsUserController = Ember.Controller.extend({
ne2Password: ''
});
self.notifications.showSuccess('Password updated.');
self.get('notifications').showSuccess('Password updated.');
return model;
}).catch(function (errors) {
self.notifications.showAPIError(errors);
self.get('notifications').showAPIError(errors);
});
} else {
self.notifications.showErrors(user.get('passwordValidationErrors'));
self.get('notifications').showErrors(user.get('passwordValidationErrors'));
}
},
@ -217,5 +224,3 @@ var SettingsUserController = Ember.Controller.extend({
}
}
});
export default SettingsUserController;

View file

@ -12,12 +12,16 @@ export default Ember.Controller.extend(ValidationEngine, {
// ValidationEngine settings
validationType: 'setup',
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
actions: {
setup: function () {
var self = this,
data = self.getProperties('blogTitle', 'name', 'email', 'password');
data = self.getProperties('blogTitle', 'name', 'email', 'password'),
notifications = this.get('notifications');
self.notifications.closePassive();
notifications.closePassive();
this.toggleProperty('submitting');
this.validate({format: false}).then(function () {
@ -39,11 +43,11 @@ export default Ember.Controller.extend(ValidationEngine, {
});
}).catch(function (resp) {
self.toggleProperty('submitting');
self.notifications.showAPIError(resp);
notifications.showAPIError(resp);
});
}).catch(function (errors) {
self.toggleProperty('submitting');
self.notifications.showErrors(errors);
notifications.showErrors(errors);
});
}
}

View file

@ -7,6 +7,9 @@ export default Ember.Controller.extend(ValidationEngine, {
submitting: false,
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
actions: {
authenticate: function () {
var model = this.get('model'),
@ -28,10 +31,10 @@ export default Ember.Controller.extend(ValidationEngine, {
$('#login').find('input').trigger('change');
this.validate({format: false}).then(function () {
self.notifications.closePassive();
self.get('notifications').closePassive();
self.send('authenticate');
}).catch(function (errors) {
self.notifications.showErrors(errors);
self.get('notifications').showErrors(errors);
});
},
@ -55,10 +58,10 @@ export default Ember.Controller.extend(ValidationEngine, {
}
}).then(function () {
self.set('submitting', false);
self.notifications.showSuccess('Please check your email for instructions.');
self.get('notifications').showSuccess('Please check your email for instructions.');
}).catch(function (resp) {
self.set('submitting', false);
self.notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.'});
self.get('notifications').showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.'});
});
}
}

View file

@ -3,18 +3,22 @@ import {request as ajax} from 'ic-ajax';
import ValidationEngine from 'ghost/mixins/validation-engine';
export default Ember.Controller.extend(ValidationEngine, {
submitting: false,
// ValidationEngine settings
validationType: 'signup',
submitting: false,
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
actions: {
signup: function () {
var self = this,
model = this.get('model'),
data = model.getProperties('name', 'email', 'password', 'token');
data = model.getProperties('name', 'email', 'password', 'token'),
notifications = this.get('notifications');
self.notifications.closePassive();
notifications.closePassive();
this.toggleProperty('submitting');
this.validate({format: false}).then(function () {
@ -37,11 +41,11 @@ export default Ember.Controller.extend(ValidationEngine, {
});
}, function (resp) {
self.toggleProperty('submitting');
self.notifications.showAPIError(resp);
notifications.showAPIError(resp);
});
}, function (errors) {
self.toggleProperty('submitting');
self.notifications.showErrors(errors);
notifications.showErrors(errors);
});
}
}

View file

@ -1,24 +0,0 @@
import DropdownService from 'ghost/utils/dropdown-service';
var dropdownInitializer = {
name: 'dropdown',
initialize: function (container, application) {
application.register('dropdown:service', DropdownService);
// Inject dropdowns
application.inject('component:gh-dropdown', 'dropdown', 'dropdown:service');
application.inject('component:gh-dropdown-button', 'dropdown', 'dropdown:service');
application.inject('controller:application', 'dropdown', 'dropdown:service');
application.inject('controller:modals.delete-post', 'dropdown', 'dropdown:service');
application.inject('controller:modals.transfer-owner', 'dropdown', 'dropdown:service');
application.inject('route:application', 'dropdown', 'dropdown:service');
// Inject popovers
application.inject('component:gh-popover', 'dropdown', 'dropdown:service');
application.inject('component:gh-popover-button', 'dropdown', 'dropdown:service');
application.inject('route:application', 'dropdown', 'dropdown:service');
}
};
export default dropdownInitializer;

View file

@ -1,17 +0,0 @@
import getConfig from 'ghost/utils/config-parser';
var ConfigInitializer = {
name: 'config',
initialize: function (container, application) {
var config = getConfig();
application.register('ghost:config', config, {instantiate: false});
application.inject('route', 'config', 'ghost:config');
application.inject('model:post', 'config', 'ghost:config');
application.inject('controller', 'config', 'ghost:config');
application.inject('component', 'config', 'ghost:config');
}
};
export default ConfigInitializer;

View file

@ -1,16 +0,0 @@
import ghostPaths from 'ghost/utils/ghost-paths';
var ghostPathsInitializer = {
name: 'ghost-paths',
after: 'store',
initialize: function (container, application) {
application.register('ghost:paths', ghostPaths(), {instantiate: false});
application.inject('route', 'ghostPaths', 'ghost:paths');
application.inject('model', 'ghostPaths', 'ghost:paths');
application.inject('controller', 'ghostPaths', 'ghost:paths');
}
};
export default ghostPathsInitializer;

View file

@ -1,17 +0,0 @@
import Notifications from 'ghost/utils/notifications';
var injectNotificationsInitializer = {
name: 'injectNotifications',
before: 'authentication',
initialize: function (container, application) {
application.register('notifications:main', Notifications);
application.inject('controller', 'notifications', 'notifications:main');
application.inject('component', 'notifications', 'notifications:main');
application.inject('router', 'notifications', 'notifications:main');
application.inject('route', 'notifications', 'notifications:main');
}
};
export default injectNotificationsInitializer;

View file

@ -5,24 +5,24 @@ import PostModel from 'ghost/models/post';
import boundOneWay from 'ghost/utils/bound-one-way';
import imageManager from 'ghost/utils/ed-image-manager';
var watchedProps,
EditorControllerMixin;
// this array will hold properties we need to watch
// to know if the model has been changed (`controller.isDirty`)
watchedProps = ['model.scratch', 'model.titleScratch', 'model.isDirty', 'model.tags.[]'];
var watchedProps = ['model.scratch', 'model.titleScratch', 'model.isDirty', 'model.tags.[]'];
PostModel.eachAttribute(function (name) {
watchedProps.push('model.' + name);
});
EditorControllerMixin = Ember.Mixin.create({
needs: ['post-tags-input', 'post-settings-menu'],
export default Ember.Mixin.create({
postTagsInputController: Ember.inject.controller('post-tags-input'),
postSettingsMenuController: Ember.inject.controller('post-settings-menu'),
autoSaveId: null,
timedSaveId: null,
editor: null,
notifications: Ember.inject.service(),
init: function () {
var self = this;
@ -32,6 +32,7 @@ EditorControllerMixin = Ember.Mixin.create({
return self.get('isDirty') ? self.unloadDirtyMessage() : null;
};
},
autoSave: function () {
// Don't save just because we swapped out models
if (this.get('model.isDraft') && !this.get('model.isNew')) {
@ -213,21 +214,24 @@ EditorControllerMixin = Ember.Mixin.create({
showSaveNotification: function (prevStatus, status, delay) {
var message = this.messageMap.success.post[prevStatus][status],
path = this.get('model.absoluteUrl'),
type = this.get('postOrPage');
type = this.get('postOrPage'),
notifications = this.get('notifications');
if (status === 'published') {
message += `&nbsp;<a href="${path}">View ${type}</a>`;
}
this.notifications.showSuccess(message.htmlSafe(), {delayed: delay});
notifications.showSuccess(message.htmlSafe(), {delayed: delay});
},
showErrorNotification: function (prevStatus, status, errors, delay) {
var message = this.messageMap.errors.post[prevStatus][status],
error = (errors && errors[0] && errors[0].message) || 'Unknown Error';
error = (errors && errors[0] && errors[0].message) || 'Unknown Error',
notifications = this.get('notifications');
message += '<br />' + error;
this.notifications.showError(message.htmlSafe(), {delayed: delay});
notifications.showError(message.htmlSafe(), {delayed: delay});
},
shouldFocusTitle: Ember.computed.alias('model.isNew'),
@ -241,8 +245,9 @@ EditorControllerMixin = Ember.Mixin.create({
autoSaveId = this.get('autoSaveId'),
timedSaveId = this.get('timedSaveId'),
self = this,
psmController = this.get('controllers.post-settings-menu'),
promise;
psmController = this.get('postSettingsMenuController'),
promise,
notifications = this.get('notifications');
options = options || {};
@ -263,10 +268,10 @@ EditorControllerMixin = Ember.Mixin.create({
this.set('timedSaveId', null);
}
self.notifications.closePassive();
notifications.closePassive();
// ensure an incomplete tag is finalised before save
this.get('controllers.post-tags-input').send('addNewTag');
this.get('postTagsInputController').send('addNewTag');
// Set the properties that are indirected
// set markdown equal to what's in the editor, minus the image markers.
@ -367,5 +372,3 @@ EditorControllerMixin = Ember.Mixin.create({
}
}
});
export default EditorControllerMixin;

View file

@ -2,6 +2,8 @@ import Ember from 'ember';
import getRequestErrorMessage from 'ghost/utils/ajax';
export default Ember.Mixin.create({
notifications: Ember.inject.service(),
// set from PaginationRouteMixin
paginationSettings: null,
@ -23,7 +25,7 @@ export default Ember.Mixin.create({
message += '.';
}
this.notifications.showError(message);
this.get('notifications').showError(message);
},
actions: {

View file

@ -1,122 +0,0 @@
import Ember from 'ember';
// SelectiveSaveMixin adds a saveOnly method to a DS.Model.
//
// saveOnly provides a way to save one or more properties of a model while
// preserving outstanding changes to other properties.
var SelectiveSaveMixin = Ember.Mixin.create({
saveOnly: function () {
if (arguments.length === 0) {
return Ember.RSVP.resolve();
}
if (arguments.length === 1 && Ember.isArray(arguments[0])) {
return this.saveOnly.apply(this, Array.prototype.slice.call(arguments[0]));
}
var propertiesToSave = Array.prototype.slice.call(arguments),
changed,
hasMany = {},
belongsTo = {},
self = this;
changed = this.changedAttributes();
// disable observers so we can make changes to the model but not have
// them reflected by the UI
this.beginPropertyChanges();
// make a copy of any relations the model may have so they can
// be reapplied later
this.eachRelationship(function (name, meta) {
if (meta.kind === 'hasMany') {
hasMany[name] = self.get(name).slice();
return;
}
if (meta.kind === 'belongsTo') {
belongsTo[name] = self.get(name);
return;
}
});
try {
// roll back all changes to the model and then reapply only those that
// are part of the saveOnly
self.rollback();
propertiesToSave.forEach(function (name) {
if (hasMany.hasOwnProperty(name)) {
self.get(name).clear();
hasMany[name].forEach(function (relatedType) {
self.get(name).pushObject(relatedType);
});
return;
}
if (belongsTo.hasOwnProperty(name)) {
return self.updateBelongsTo(name, belongsTo[name]);
}
if (changed.hasOwnProperty(name)) {
return self.set(name, changed[name][1]);
}
});
}
catch (err) {
// if we were not able to get the model into the correct state
// put it back the way we found it and return a rejected promise
Ember.keys(changed).forEach(function (name) {
self.set(name, changed[name][1]);
});
Ember.keys(hasMany).forEach(function (name) {
self.updateHasMany(name, hasMany[name]);
});
Ember.keys(belongsTo).forEach(function (name) {
self.updateBelongsTo(name, belongsTo[name]);
});
self.endPropertyChanges();
return Ember.RSVP.reject(new Error(err.message || 'Error during saveOnly. Changes NOT saved.'));
}
return this.save().finally(function () {
// reapply any changes that were not part of the save
Ember.keys(changed).forEach(function (name) {
if (propertiesToSave.hasOwnProperty(name)) {
return;
}
self.set(name, changed[name][1]);
});
Ember.keys(hasMany).forEach(function (name) {
if (propertiesToSave.hasOwnProperty(name)) {
return;
}
self.updateHasMany(name, hasMany[name]);
});
Ember.keys(belongsTo).forEach(function (name) {
if (propertiesToSave.hasOwnProperty(name)) {
return;
}
self.updateBelongsTo(name, belongsTo[name]);
});
// signal that we're finished and normal model observation may continue
self.endPropertyChanges();
});
}
});
export default SelectiveSaveMixin;

View file

@ -1,10 +1,11 @@
import Ember from 'ember';
var SettingsMenuControllerMixin = Ember.Mixin.create({
needs: 'application',
isViewingSubview: Ember.computed('controllers.application.showSettingsMenu', function (key, value) {
export default Ember.Mixin.create({
application: Ember.inject.controller(),
isViewingSubview: Ember.computed('application.showSettingsMenu', function (key, value) {
// Not viewing a subview if we can't even see the PSM
if (!this.get('controllers.application.showSettingsMenu')) {
if (!this.get('application.showSettingsMenu')) {
return false;
}
if (arguments.length > 1) {
@ -24,5 +25,3 @@ var SettingsMenuControllerMixin = Ember.Mixin.create({
}
}
});
export default SettingsMenuControllerMixin;

View file

@ -2,7 +2,7 @@ import Ember from 'ember';
import DS from 'ember-data';
import ValidationEngine from 'ghost/mixins/validation-engine';
var Post = DS.Model.extend(ValidationEngine, {
export default DS.Model.extend(ValidationEngine, {
validationType: 'post',
uuid: DS.attr('string'),
@ -28,6 +28,9 @@ var Post = DS.Model.extend(ValidationEngine, {
tags: DS.hasMany('tag', {embedded: 'always'}),
url: DS.attr('string'),
config: Ember.inject.service(),
ghostPaths: Ember.inject.service('ghost-paths'),
absoluteUrl: Ember.computed('url', 'ghostPaths.url', 'config.blogUrl', function () {
var blogUrl = this.get('config.blogUrl'),
postUrl = this.get('url');
@ -69,5 +72,3 @@ var Post = DS.Model.extend(ValidationEngine, {
}
});
export default Post;

View file

@ -2,10 +2,11 @@ import Ember from 'ember';
import {request as ajax} from 'ic-ajax';
export default Ember.Object.extend({
ghostPaths: null,
slugType: null,
value: null,
ghostPaths: Ember.inject.service('ghost-paths'),
toString: function () {
return this.get('value');
},

View file

@ -2,9 +2,8 @@ import Ember from 'ember';
import DS from 'ember-data';
import {request as ajax} from 'ic-ajax';
import ValidationEngine from 'ghost/mixins/validation-engine';
import SelectiveSaveMixin from 'ghost/mixins/selective-save';
export default DS.Model.extend(SelectiveSaveMixin, ValidationEngine, {
export default DS.Model.extend(ValidationEngine, {
validationType: 'user',
uuid: DS.attr('string'),
@ -28,6 +27,8 @@ export default DS.Model.extend(SelectiveSaveMixin, ValidationEngine, {
updated_by: DS.attr('number'),
roles: DS.hasMany('role', {embedded: 'always'}),
ghostPaths: Ember.inject.service('ghost-paths'),
role: Ember.computed('roles', function (name, value) {
if (arguments.length > 1) {
// Only one role per user, so remove any old data.
@ -93,13 +94,13 @@ export default DS.Model.extend(SelectiveSaveMixin, ValidationEngine, {
isPasswordValid: Ember.computed.empty('passwordValidationErrors.[]'),
active: function () {
active: Ember.computed('status', function () {
return ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'].indexOf(this.get('status')) > -1;
}.property('status'),
}),
invited: function () {
invited: Ember.computed('status', function () {
return ['invited', 'invited-pending'].indexOf(this.get('status')) > -1;
}.property('status'),
}),
pending: Ember.computed.equal('status', 'invited-pending').property('status')
});

View file

@ -6,9 +6,13 @@ var Router = Ember.Router.extend({
location: 'trailing-history', // use HTML5 History API instead of hash-tag based URLs
rootURL: ghostPaths().adminRoot, // admin interface lives under sub-directory /ghost
notifications: Ember.inject.service(),
clearNotifications: Ember.on('didTransition', function () {
this.notifications.closePassive();
this.notifications.displayDelayed();
var notifications = this.get('notifications');
notifications.closePassive();
notifications.displayDelayed();
})
});

View file

@ -1,3 +1,4 @@
import Ember from 'ember';
import {request as ajax} from 'ic-ajax';
import AuthenticatedRoute from 'ghost/routes/authenticated';
import styleBody from 'ghost/mixins/style-body';
@ -7,6 +8,8 @@ export default AuthenticatedRoute.extend(styleBody, {
classNames: ['view-about'],
ghostPaths: Ember.inject.service('ghost-paths'),
cachedConfig: false,
model: function () {

View file

@ -6,16 +6,19 @@ import Configuration from 'simple-auth/configuration';
import ShortcutsRoute from 'ghost/mixins/shortcuts-route';
import ctrlOrCmd from 'ghost/utils/ctrl-or-cmd';
var ApplicationRoute,
shortcuts = {};
var shortcuts = {};
shortcuts.esc = {action: 'closePopups', scope: 'all'};
shortcuts.enter = {action: 'confirmModal', scope: 'modal'};
shortcuts[ctrlOrCmd + '+s'] = {action: 'save', scope: 'all'};
ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
export default Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
shortcuts: shortcuts,
config: Ember.inject.service(),
dropdown: Ember.inject.service(),
notifications: Ember.inject.service(),
afterModel: function (model, transition) {
if (this.get('session').isAuthenticated) {
transition.send('loadServerNotifications');
@ -68,10 +71,10 @@ ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
err.message = err.message.htmlSafe();
});
this.notifications.showErrors(error.errors);
this.get('notifications').showErrors(error.errors);
} else {
// connection errors don't return proper status message, only req.body
this.notifications.showError('There was a problem on the server.');
this.get('notifications').showError('There was a problem on the server.');
}
},
@ -96,7 +99,7 @@ ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
},
sessionInvalidationFailed: function (error) {
this.notifications.showError(error.message);
this.get('notifications').showError(error.message);
},
openModal: function (modalName, model, type) {
@ -149,7 +152,7 @@ ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
if (!user.get('isAuthor') && !user.get('isEditor')) {
self.store.findAll('notification').then(function (serverNotifications) {
serverNotifications.forEach(function (notification) {
self.notifications.handleNotification(notification, isDelayed);
self.get('notifications').handleNotification(notification, isDelayed);
});
});
}
@ -158,11 +161,11 @@ ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
},
handleErrors: function (errors) {
var self = this;
var notifications = this.get('notifications');
this.notifications.clear();
notifications.clear();
errors.forEach(function (errorObj) {
self.notifications.showError(errorObj.message || errorObj);
notifications.showError(errorObj.message || errorObj);
if (errorObj.hasOwnProperty('el')) {
errorObj.el.addClass('input-error');
@ -174,5 +177,3 @@ ApplicationRoute = Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
save: Ember.K
}
});
export default ApplicationRoute;

View file

@ -2,12 +2,14 @@ import Ember from 'ember';
import Configuration from 'simple-auth/configuration';
import styleBody from 'ghost/mixins/style-body';
var ResetRoute = Ember.Route.extend(styleBody, {
export default Ember.Route.extend(styleBody, {
classNames: ['ghost-reset'],
notifications: Ember.inject.service(),
beforeModel: function () {
if (this.get('session').isAuthenticated) {
this.notifications.showWarn('You can\'t reset your password while you\'re signed in.', {delayed: true});
this.get('notifications').showWarn('You can\'t reset your password while you\'re signed in.', {delayed: true});
this.transitionTo(Configuration.routeAfterAuthentication);
}
},
@ -22,5 +24,3 @@ var ResetRoute = Ember.Route.extend(styleBody, {
this.controller.clearData();
}
});
export default ResetRoute;

View file

@ -1,12 +1,15 @@
import Ember from 'ember';
import AuthenticatedRoute from 'ghost/routes/authenticated';
import CurrentUserSettings from 'ghost/mixins/current-user-settings';
import styleBody from 'ghost/mixins/style-body';
var AppsRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
titleToken: 'Apps',
classNames: ['settings-view-apps'],
config: Ember.inject.service(),
beforeModel: function () {
if (!this.get('config.apps')) {
return this.transitionTo('settings.general');
@ -21,5 +24,3 @@ var AppsRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
return this.store.find('app');
}
});
export default AppsRoute;

View file

@ -8,9 +8,10 @@ export default Ember.Route.extend(styleBody, {
classNames: ['ghost-setup'],
ghostPaths: Ember.inject.service('ghost-paths'),
// use the beforeModel hook to check to see whether or not setup has been
// previously completed. If it has, stop the transition into the setup page.
beforeModel: function () {
var self = this;

View file

@ -2,13 +2,15 @@ import Ember from 'ember';
import AuthenticatedRoute from 'ghost/routes/authenticated';
import styleBody from 'ghost/mixins/style-body';
var SignoutRoute = AuthenticatedRoute.extend(styleBody, {
export default AuthenticatedRoute.extend(styleBody, {
titleToken: 'Sign Out',
classNames: ['ghost-signout'],
notifications: Ember.inject.service(),
afterModel: function (model, transition) {
this.notifications.clear();
this.get('notifications').closeAll();
if (Ember.canInvoke(transition, 'send')) {
transition.send('invalidateSession');
transition.abort();
@ -17,5 +19,3 @@ var SignoutRoute = AuthenticatedRoute.extend(styleBody, {
}
}
});
export default SignoutRoute;

View file

@ -6,9 +6,12 @@ import styleBody from 'ghost/mixins/style-body';
export default Ember.Route.extend(styleBody, {
classNames: ['ghost-signup'],
ghostPaths: Ember.inject.service('ghost-paths'),
notifications: Ember.inject.service(),
beforeModel: function () {
if (this.get('session').isAuthenticated) {
this.notifications.showWarn('You need to sign out to register as a new user.', {delayed: true});
this.get('notifications').showWarn('You need to sign out to register as a new user.', {delayed: true});
this.transitionTo(Configuration.routeAfterAuthentication);
}
},
@ -22,7 +25,7 @@ export default Ember.Route.extend(styleBody, {
return new Ember.RSVP.Promise(function (resolve) {
if (!re.test(params.token)) {
self.notifications.showError('Invalid token.', {delayed: true});
self.get('notifications').showError('Invalid token.', {delayed: true});
return resolve(self.transitionTo('signin'));
}
@ -42,7 +45,7 @@ export default Ember.Route.extend(styleBody, {
}
}).then(function (response) {
if (response && response.invitation && response.invitation[0].valid === false) {
self.notifications.showError('The invitation does not exist or is no longer valid.', {delayed: true});
self.get('notifications').showError('The invitation does not exist or is no longer valid.', {delayed: true});
return resolve(self.transitionTo('signin'));
}

View file

@ -0,0 +1,43 @@
import Ember from 'ember';
function isNumeric(num) {
return !isNaN(num);
}
function _mapType(val) {
if (val === '') {
return null;
} else if (val === 'true') {
return true;
} else if (val === 'false') {
return false;
} else if (isNumeric(val)) {
return +val;
} else if (val.indexOf('{') === 0) {
try {
return JSON.parse(val);
} catch (e) {
/*jshint unused:false */
return val;
}
} else {
return val;
}
}
export default Ember.Service.extend(Ember._ProxyMixin, {
content: Ember.computed(function () {
var metaConfigTags = Ember.$('meta[name^="env-"]'),
config = {};
metaConfigTags.each(function (i, el) {
var key = el.name,
value = el.content,
propertyName = key.substring(4);
config[propertyName] = _mapType(value);
});
return config;
})
});

View file

@ -2,17 +2,17 @@ import Ember from 'ember';
// This is used by the dropdown initializer (and subsequently popovers) to manage closing & toggling
import BodyEventListener from 'ghost/mixins/body-event-listener';
var DropdownService = Ember.Object.extend(Ember.Evented, BodyEventListener, {
export default Ember.Service.extend(Ember.Evented, BodyEventListener, {
bodyClick: function (event) {
/*jshint unused:false */
this.closeDropdowns();
},
closeDropdowns: function () {
this.trigger('close');
},
toggleDropdown: function (dropdownName, dropdownButton) {
this.trigger('toggle', {target: dropdownName, button: dropdownButton});
}
});
export default DropdownService;

View file

@ -0,0 +1,6 @@
import Ember from 'ember';
import ghostPaths from 'ghost/utils/ghost-paths';
export default Ember.Service.extend(Ember._ProxyMixin, {
content: ghostPaths()
});

View file

@ -1,8 +1,8 @@
import Ember from 'ember';
import Notification from 'ghost/models/notification';
var Notifications = Ember.ArrayProxy.extend({
delayedNotifications: [],
export default Ember.Service.extend({
delayedNotifications: Ember.A(),
content: Ember.A(),
timeout: 3000,
@ -25,6 +25,7 @@ var Notifications = Ember.ArrayProxy.extend({
this._super(object);
},
handleNotification: function (message, delayed) {
if (typeof message.toJSON === 'function') {
// If this is a persistent message from the server, treat it as html safe
@ -42,11 +43,12 @@ var Notifications = Ember.ArrayProxy.extend({
}
if (!delayed) {
this.pushObject(message);
this.get('content').pushObject(message);
} else {
this.delayedNotifications.push(message);
this.delayedNotifications.pushObject(message);
}
},
showError: function (message, options) {
options = options || {};
@ -59,6 +61,7 @@ var Notifications = Ember.ArrayProxy.extend({
message: message
}, options.delayed);
},
showErrors: function (errors, options) {
options = options || {};
@ -70,6 +73,7 @@ var Notifications = Ember.ArrayProxy.extend({
this.showError(errors[i].message || errors[i], {doNotClosePassive: true});
}
},
showAPIError: function (resp, options) {
options = options || {};
@ -89,6 +93,7 @@ var Notifications = Ember.ArrayProxy.extend({
this.showError(options.defaultErrorText, {doNotClosePassive: true});
}
},
showInfo: function (message, options) {
options = options || {};
@ -101,6 +106,7 @@ var Notifications = Ember.ArrayProxy.extend({
message: message
}, options.delayed);
},
showSuccess: function (message, options) {
options = options || {};
@ -113,6 +119,7 @@ var Notifications = Ember.ArrayProxy.extend({
message: message
}, options.delayed);
},
showWarn: function (message, options) {
options = options || {};
@ -125,35 +132,38 @@ var Notifications = Ember.ArrayProxy.extend({
message: message
}, options.delayed);
},
displayDelayed: function () {
var self = this;
self.delayedNotifications.forEach(function (message) {
self.pushObject(message);
self.get('content').pushObject(message);
});
self.delayedNotifications = [];
},
closeNotification: function (notification) {
var self = this;
var content = this.get('content');
if (notification instanceof Notification) {
notification.deleteRecord();
notification.save().finally(function () {
self.removeObject(notification);
content.removeObject(notification);
});
} else {
this.removeObject(notification);
content.removeObject(notification);
}
},
closePassive: function () {
this.set('content', this.rejectBy('status', 'passive'));
this.set('content', this.get('content').rejectBy('status', 'passive'));
},
closePersistent: function () {
this.set('content', this.rejectBy('status', 'persistent'));
this.set('content', this.get('content').rejectBy('status', 'persistent'));
},
closeAll: function () {
this.clear();
this.get('content').clear();
}
});
export default Notifications;

View file

@ -1,43 +0,0 @@
var isNumeric = function (num) {
return !isNaN(num);
},
_mapType = function (val) {
if (val === '') {
return null;
} else if (val === 'true') {
return true;
} else if (val === 'false') {
return false;
} else if (isNumeric(val)) {
return +val;
} else if (val.indexOf('{') === 0) {
try {
return JSON.parse(val);
} catch (e) {
/*jshint unused:false */
return val;
}
} else {
return val;
}
},
parseConfiguration = function () {
var metaConfigTags = $('meta[name^="env-"]'),
propertyName,
config = {},
value,
key,
i;
for (i = 0; i < metaConfigTags.length; i += 1) {
key = $(metaConfigTags[i]).prop('name');
value = $(metaConfigTags[i]).prop('content');
propertyName = key.substring(4); // produce config name ignoring the initial 'env-'.
config[propertyName] = _mapType(value); // map string values to types if possible
}
return config;
};
export default parseConfiguration;