0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

Use Ember.inject instead of needs and initializers

No Issue
- Switches to the newer style of dependency injection.
- Instead of injection Controllers via "needs," use
  Ember.inject.controller().
- Get rid of initializers that were only injecting objects
  into various factories. Converts these objects into Ember.Service
  objects and declaratively inject them where needed via
  Ember.inject.service().  The added benefit to this is that it's no
  longer a mystery where these properties/methods come from and it's
  straightforward to inject them where needed.
This commit is contained in:
Jason Williams 2015-05-25 21:10:50 -05:00
parent 3ece83f68d
commit c3ad1ae9e2
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;