Skip to content
Permalink
Browse files

Removing old "user settings" screen and putting in new MU "users sett…

…ings" screen and updating functional test cases.

Fixes #3078
- new "users" resource, with matching controller and template
- fetching real data from /ghost/api/v0.1/users/
- updated "user" route to accept a :slug as a URL parameter
- updated labels everywhere (from "user" to "users")
- updated "profile" link to header to point to proper "users/:slug" route
- updated core/client/.jshintrc to recognize moment as a valid global function
- adjusted DOM selector used in Casper to properly identify the new screen
- adding "slug" as a new property of the user data used during the Casper functional tests
  • Loading branch information...
morficus committed Jul 2, 2014
1 parent d5c2e1b commit ad9997e99537142082bcf128a1e06b7cdcff18f2
@@ -30,6 +30,7 @@
"validator": true,
"ic": true,
"_": true,
"NProgress": true
"NProgress": true,
"moment": true
}
}
@@ -0,0 +1,18 @@
/*global alert */
var UsersIndexController = Ember.ArrayController.extend({
activeUsers: function () {
return this.content.filterBy('status', 'active');
}.property('model'),

invitedUsers: function () {
return this.content.filterBy('status', 'invited');
}.property('model'),

actions: {
addUser: function () {
alert('@TODO: needs to show the "add user" modal - see issue #3079 on GitHub');
}
}
});

export default UsersIndexController;
@@ -3,7 +3,10 @@ var SettingsUserController = Ember.ObjectController.extend({

user: Ember.computed.alias('model'),

email: Ember.computed.readOnly('user.email'),

coverDefault: '/shared/img/user-cover.png',

cover: function () {
// @TODO: add {{asset}} subdir path
var cover = this.get('user.cover');
@@ -19,10 +22,31 @@ var SettingsUserController = Ember.ObjectController.extend({

image: function () {
// @TODO: add {{asset}} subdir path
return 'background-image: url(' + this.getWithDefault('user.image', '/shared/img/user-image.png') + ')';
return 'background-image: url(' + this.getWithDefault('user.image', '/shared/img/user-image.png') + ')';
}.property('user.image'),

imageUrl: function () {
// @TODO: add {{asset}} subdir path
return this.getWithDefault('user.image', '/shared/img/user-image.png');
}.property('user.image'),

last_login: function () {
return moment(this.get('user.last_login')).fromNow();
}.property('user.last_login'),

created_at: function () {
return moment(this.get('user.created_at')).fromNow();
}.property('user.created_at'),

actions: {
revoke: function () {
alert('@TODO: revoke users invitation');
},

resend: function () {
alert('@TODO: resend users invitation');
},

save: function () {
var user = this.get('user'),
self = this;
@@ -30,7 +30,9 @@ Router.map(function () {
});
this.resource('settings', function () {
this.route('general');
this.route('user');
this.resource('settings.users', { path: '/users' }, function () {
this.route('user', { path: '/:slug' });
});
this.route('apps');
});
this.route('debug');
@@ -0,0 +1,3 @@
var UsersRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin);

export default UsersRoute;
@@ -0,0 +1,8 @@
var UsersIndexRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, {

model: function () {
return this.store.find('user');
}
});

export default UsersIndexRoute;
File renamed without changes.
@@ -18,7 +18,7 @@
<span class="name">{{session.user.name}}</span>
{{/gh-popover-button}}
{{#gh-popover tagName="ul" classNames="overlay" name="user-menu" closeOnClick="true"}}
<li class="usermenu-profile">{{#link-to "settings.user"}}Your Profile{{/link-to}}</li>
<li class="usermenu-profile">{{#link-to "settings.users.user" session.user.slug}}Your Profile{{/link-to}}</li>
<li class="divider"></li>
<li class="usermenu-help"><a href="http://support.ghost.org/">Help / Support</a></li>
<li class="divider"></li>
@@ -9,7 +9,7 @@
{{/view}}

{{#view "item-view" tagName="li" class="users"}}
{{#link-to "settings.user"}}User{{/link-to}}
{{#link-to "settings.users"}}Users{{/link-to}}
{{/view}}

{{#if showApps}}
@@ -0,0 +1,63 @@
<header class="fade-in">
<button class="button-back">Back</button>
<h2 class="title">Users</h2>
<section class="page-actions">
<a class="button-add" href="#" {{action "addUser"}} >New&nbsp;User</a>
</section>
</header>

<section class="content fade-in settings-users">
<section class="object-list">

<h4 class="object-list-title">Invited users</h4>

{{#each invitedUsers itemController="settings/users/user"}}
<div class="object-list-item">
<span class="object-list-item-icon icon-mail">ic</span>

<div class="object-list-item-body">
<span class="name">{{email}}</span><br>
<span class="description">Invitation sent: {{created_at}}</span>
</div>
<aside class="object-list-item-aside">
<a class="object-list-action" href="#" {{action "revoke"}}>Revoke</a>
<a class="object-list-action" href="#" {{action "resend"}}>Resend</a>
</aside>
</div>
{{else}}
<div class="object-list-item">
No invited users.
</div>
{{/each}}


</section>

<section class="object-list">

<h4 class="object-list-title">Active users</h4>


{{#each activeUsers itemController="settings/users/user"}}
<div class="object-list-item">
<img class="object-list-item-figure"
src="{{unbound imageUrl}}"
alt="Photo of {{unbound name}}" />

<div class="object-list-item-body">
{{#link-to 'settings.users.user' slug class="ember-view name" }}
{{user.name}}
{{/link-to}}
<br>
<span class="description">Last seen: {{unbound last_login}}</span>
</div>
<!-- @TODO: replace these with real access level once API and data model are updated -->
<aside class="object-list-item-aside">
<span class="role-label editor">Editor</span>
<span class="role-label owner">Owner</span>
</aside>
</div>
{{/each}}

</section>
</section>
File renamed without changes.
@@ -30,6 +30,7 @@ var DEBUG = false, // TOGGLE THIS TO GET MORE SCREENSHOTS
url = 'http://' + host + (noPort ? '/' : ':' + port + '/'),
newUser = {
name: 'Test User',
slug: 'test-user',
email: email,
password: password
},
@@ -54,7 +54,7 @@ CasperTest.begin('Admin navigation bar is correct', 27, function suite(test) {
test.assertExists('#usermenu li.usermenu-profile a', 'Profile menu item exists');
test.assertSelectorHasText('#usermenu li.usermenu-profile a', 'Your Profile',
'Profile menu item has correct text');
test.assertEquals(profileHref, '/ghost/settings/user/', 'Profile href is correct');
test.assertEquals(profileHref, '/ghost/settings/users/' + newUser.slug + '/', 'Profile href is correct');

test.assertExists('#usermenu li.usermenu-help a', 'Help menu item exists');
test.assertSelectorHasText('#usermenu li.usermenu-help a', 'Help / Support', 'Help menu item has correct text');
@@ -6,7 +6,7 @@
// These classes relate to elements which only appear when a given tab is loaded.
// These are used to check that a switch to a tab is complete, or that we are on the right tab.
var generalTabDetector = '.settings-content form#settings-general',
userTabDetector = '.settings-content form.user-profile';
usersTabDetector = '.settings-content .settings-users';

CasperTest.begin('Settings screen is correct', 17, function suite(test) {
casper.thenOpenAndWaitForPageLoad('settings', function testTitleAndUrl() {
@@ -30,11 +30,11 @@ CasperTest.begin('Settings screen is correct', 17, function suite(test) {

casper.then(function testSwitchingTabs() {
casper.thenClick('.settings-menu .users a');
casper.waitForSelector(userTabDetector, function then () {
casper.waitForSelector(usersTabDetector, function then () {
// assert that the right menu item is active
test.assertExists('.settings-menu .users.active', 'User tab is active');
test.assertDoesntExist('.settings-menu .general.active', 'General tab is not active');
}, casper.failOnTimeout(test, 'waitForSelector `userTabDetector` timed out'));
}, casper.failOnTimeout(test, 'waitForSelector `usersTabDetector` timed out'));

casper.thenClick('.settings-menu .general a');
casper.waitForSelector(generalTabDetector, function then () {
@@ -368,4 +368,4 @@ CasperTest.begin('General settings validation is correct', 7, function suite(tes
// casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
// test.assertSelectorHasText('.notification-error', 'is too long');
// }, casper.failOnTimeout(test, 'Location field length error did not appear', 2000));
//});
//});

0 comments on commit ad9997e

Please sign in to comment.
You can’t perform that action at this time.