Ghost/ghost/admin/controllers/settings/users/user.js
Robert Jackson d8da21520f Remove ObjectController proxying behavior.
Ember.ObjectController (and Ember.ArrayController) will be deprecated in
Ember 1.11 (and removed from core in Ember 2.0). The reasoning is
detailed in the Ember 2.0 RFC.

This PR does the following:

* Updates templates/controllers/views to explicitly reference model
  properties (instead of relying on proxying behavior).
* Clearly delineate where certain properties are being set or retrieved
  from (for example it was not clear exactly where `scratch` and
  `titleScratch` were stored).
* Remove usage of `Ember.ObjectController`.
* Add JSCS rule to prevent future PR's from adding regressions.
2015-01-01 21:51:20 -05:00

231 lines
8.3 KiB
JavaScript

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({
user: Ember.computed.alias('model'),
email: Ember.computed.readOnly('model.email'),
slugValue: boundOneWay('model.slug'),
lastPromise: null,
coverDefault: Ember.computed('ghostPaths', function () {
return this.get('ghostPaths.url').asset('/shared/img/user-cover.png');
}),
userDefault: Ember.computed('ghostPaths', function () {
return this.get('ghostPaths.url').asset('/shared/img/user-image.png');
}),
cover: Ember.computed('user.cover', 'coverDefault', function () {
var cover = this.get('user.cover');
if (Ember.isBlank(cover)) {
cover = this.get('coverDefault');
}
return 'background-image: url(' + cover + ')';
}),
coverTitle: Ember.computed('user.name', function () {
return this.get('user.name') + '\'s Cover Image';
}),
image: Ember.computed('imageUrl', function () {
return 'background-image: url(' + this.get('imageUrl') + ')';
}),
imageUrl: Ember.computed('user.image', function () {
return this.get('user.image') || this.get('userDefault');
}),
last_login: Ember.computed('user.last_login', function () {
var lastLogin = this.get('user.last_login');
return lastLogin ? lastLogin.fromNow() : '(Never)';
}),
created_at: Ember.computed('user.created_at', function () {
var createdAt = this.get('user.created_at');
return createdAt ? createdAt.fromNow() : '';
}),
// Lazy load the slug generator for slugPlaceholder
slugGenerator: Ember.computed(function () {
return SlugGenerator.create({
ghostPaths: this.get('ghostPaths'),
slugType: 'user'
});
}),
actions: {
changeRole: function (newRole) {
this.set('model.role', newRole);
},
revoke: function () {
var self = this,
model = this.get('model'),
email = this.get('email');
// reload the model to get the most up-to-date user information
model.reload().then(function () {
if (model.get('invited')) {
model.destroyRecord().then(function () {
var notificationText = 'Invitation revoked. (' + email + ')';
self.notifications.showSuccess(notificationText, false);
}).catch(function (error) {
self.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});
}
});
},
resend: function () {
var self = this;
this.get('model').resendInvite().then(function (result) {
var notificationText = 'Invitation resent! (' + self.get('email') + ')';
// 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.');
} else {
self.get('model').set('status', result.users[0].status);
self.notifications.showSuccess(notificationText);
}
}).catch(function (error) {
self.notifications.showAPIError(error);
});
},
save: function () {
var user = this.get('user'),
slugValue = this.get('slugValue'),
afterUpdateSlug = this.get('lastPromise'),
promise,
slugChanged,
self = this;
if (user.get('slug') !== slugValue) {
slugChanged = true;
user.set('slug', slugValue);
}
promise = Ember.RSVP.resolve(afterUpdateSlug).then(function () {
return user.save({format: false});
}).then(function (model) {
var currentPath,
newPath;
self.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
if (slugChanged) {
currentPath = window.history.state.path;
newPath = currentPath.split('/');
newPath[newPath.length - 2] = model.get('slug');
newPath = newPath.join('/');
window.history.replaceState({path: newPath}, '', newPath);
}
return model;
}).catch(function (errors) {
self.notifications.showErrors(errors);
});
this.set('lastPromise', promise);
},
password: function () {
var user = this.get('user'),
self = this;
if (user.get('isPasswordValid')) {
user.saveNewPassword().then(function (model) {
// Clear properties from view
user.setProperties({
password: '',
newPassword: '',
ne2Password: ''
});
self.notifications.showSuccess('Password updated.');
return model;
}).catch(function (errors) {
self.notifications.showAPIError(errors);
});
} else {
self.notifications.showErrors(user.get('passwordValidationErrors'));
}
},
updateSlug: function (newSlug) {
var self = this,
afterSave = this.get('lastPromise'),
promise;
promise = Ember.RSVP.resolve(afterSave).then(function () {
var slug = self.get('model.slug');
newSlug = newSlug || slug;
newSlug = newSlug.trim();
// Ignore unchanged slugs or candidate slugs that are empty
if (!newSlug || slug === newSlug) {
self.set('slugValue', slug);
return;
}
return self.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
// If after getting the sanitized and unique slug back from the API
// we end up with a slug that matches the existing slug, abort the change
if (serverSlug === slug) {
return;
}
// Because the server transforms the candidate slug by stripping
// certain characters and appending a number onto the end of slugs
// to enforce uniqueness, there are cases where we can get back a
// candidate slug that is a duplicate of the original except for
// the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)
// get the last token out of the slug candidate and see if it's a number
var slugTokens = serverSlug.split('-'),
check = Number(slugTokens.pop());
// if the candidate slug is the same as the existing slug except
// for the incrementor then the existing slug should be used
if (isNumber(check) && check > 0) {
if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
self.set('slugValue', slug);
return;
}
}
self.set('slugValue', serverSlug);
});
});
this.set('lastPromise', promise);
}
}
});
export default SettingsUserController;