0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Merge pull request #5453 from cobbspur/teampermissions

Change permissions for team area
This commit is contained in:
Hannah Wolfe 2015-07-06 20:54:29 +01:00
commit dc06deaa29
11 changed files with 177 additions and 216 deletions

View file

@ -15,6 +15,8 @@ export default Ember.Controller.extend({
isNotOwnersProfile: Ember.computed.not('user.isOwner'),
isAdminUserOnOwnerProfile: Ember.computed.and('currentUser.isAdmin', 'user.isOwner'),
canAssignRoles: Ember.computed.or('currentUser.isAdmin', 'currentUser.isOwner'),
canMakeOwner: Ember.computed.and('currentUser.isOwner', 'isNotOwnProfile', 'user.isAdmin'),

View file

@ -0,0 +1,11 @@
import Ember from 'ember';
// Handlebars Helper {{gh-user-can-admin}} group users by admin and owner using if, or group them author using unless
// Usage: call helper as with aparameter of session.user
// e.g - {{#if (gh-user-can-admin session.user)}} 'block content' {{/if}}
// @param session.user
export function ghUserCanAdmin(params) {
return !!(params[0].get('isOwner') || params[0].get('isAdmin'));
}
export default Ember.HTMLBars.makeBoundHelper(ghUserCanAdmin);

View file

@ -1,17 +0,0 @@
import Ember from 'ember';
// Handlebars Helper {{gh-user-can}}
// Usage: call helper as with first parameter of session.user and second parameter the minimum role
// e.g - {{#if (gh-user-can session.user 'admin')}} 'block content' {{/if}}
// @param1 session.user
// @param2 'admin' or 'editor'
export function ghUserCan(params) {
if (params[1] === 'admin') {
return !!(params[0].get('isOwner') || params[0].get('isAdmin'));
} else if (params[1] === 'editor') {
return !!(params[0].get('isOwner') || params[0].get('isAdmin') || params[0].get('isEditor'));
}
return false;
}
export default Ember.HTMLBars.makeBoundHelper(ghUserCan);

View file

@ -3,8 +3,7 @@ import CurrentUserSettings from 'ghost/mixins/current-user-settings';
import PaginationRouteMixin from 'ghost/mixins/pagination-route';
import styleBody from 'ghost/mixins/style-body';
var paginationSettings,
TeamIndexRoute;
var paginationSettings;
paginationSettings = {
page: 1,
@ -12,10 +11,10 @@ paginationSettings = {
status: 'active'
};
TeamIndexRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, PaginationRouteMixin, {
export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, PaginationRouteMixin, {
titleToken: 'Team',
classNames: ['view-users'],
classNames: ['view-team'],
setupController: function (controller, model) {
this._super(controller, model);
@ -24,26 +23,14 @@ TeamIndexRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, Pagin
beforeModel: function (transition) {
this._super(transition);
return this.get('session.user')
.then(this.transitionAuthor());
},
model: function () {
var self = this;
return self.store.find('user', {limit: 'all', status: 'invited'}).then(function () {
return self.get('session.user').then(function (currentUser) {
if (currentUser.get('isEditor')) {
// Editors only see authors in the list
paginationSettings.role = 'Author';
}
return self.store.filter('user', paginationSettings, function (user) {
if (currentUser.get('isEditor')) {
return user.get('isAuthor') || user === currentUser;
}
return true;
});
return self.store.filter('user', paginationSettings, function () {
return true;
});
});
},
@ -54,5 +41,3 @@ TeamIndexRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, Pagin
}
}
});
export default TeamIndexRoute;

View file

@ -0,0 +1,18 @@
<span class="user-list-item-figure" style={{component.userImageBackground}}>
<span class="hidden">Photo of {{user.name}}</span>
</span>
<div class="user-list-item-body">
<span class="name">
{{user.name}}
</span>
<br>
<span class="description">Last seen: {{component.last_login}}</span>
</div>
<aside class="user-list-item-aside">
{{#unless session.user.isAuthor}}
{{#each user.roles as |role|}}
<span class="role-label {{role.lowerCaseName}}">{{role.name}}</span>
{{/each}}
{{/unless}}
</aside>

View file

@ -27,7 +27,7 @@
<li>{{#link-to "team" classNames="gh-nav-main-users"}}<i class="icon-team"></i>Team{{/link-to}}</li>
{{!<li><a href="#"><i class="icon-idea"></i>Ideas</a></li>}}
</ul>
{{#if (gh-user-can session.user 'admin')}}
{{#if (gh-user-can-admin session.user)}}
<ul class="gh-nav-list gh-nav-settings">
<li class="gh-nav-list-h">Settings</li>
<li>{{#link-to "settings.general" classNames="gh-nav-settings-general"}}<i class="icon-settings"></i>General{{/link-to}}</li>

View file

@ -1,73 +1,69 @@
<section class="gh-view">
<header class="view-header">
{{#gh-view-title openMobileMenu="openMobileMenu"}}Team{{/gh-view-title}}
<section class="view-actions">
<button class="btn btn-green" {{action "openModal" "invite-new-user"}} >Invite People</button>
</section>
{{!-- Do not show Invite user button to authors --}}
{{#unless session.user.isAuthor}}
<section class="view-actions">
<button class="btn btn-green" {{action "openModal" "invite-new-user"}} >Invite People</button>
</section>
{{/unless}}
</header>
{{#gh-infinite-scroll
fetch="loadNextPage"
isLoading=isLoading
tagName="section"
classNames="view-content settings-users"
classNames="view-content team"
}}
{{#if invitedUsers}}
<section class="user-list invited-users">
<h4 class="user-list-title">Invited users</h4>
{{#each invitedUsers as |user|}}
{{#gh-user-invited user=user reload="reload" as |component|}}
<div class="user-list-item">
<span class="user-list-item-icon icon-mail">ic</span>
<div class="user-list-item-body">
<span class="name">{{user.email}}</span><br>
{{#if user.model.pending}}
<span class="red">Invitation not sent - please try again</span>
{{else}}
<span class="description">
Invitation sent: {{component.createdAt}}
</span>
{{/if}}
{{!-- Do not show invited users to authors --}}
{{#unless session.user.isAuthor}}
{{#if invitedUsers}}
<section class="user-list invited-users">
<h4 class="user-list-title">Invited users</h4>
{{#each invitedUsers as |user|}}
{{#gh-user-invited user=user reload="reload" as |component|}}
<div class="user-list-item">
<span class="user-list-item-icon icon-mail">ic</span>
<div class="user-list-item-body">
<span class="name">{{user.email}}</span><br>
{{#if user.model.pending}}
<span class="red">Invitation not sent - please try again</span>
{{else}}
<span class="description">
Invitation sent: {{component.createdAt}}
</span>
{{/if}}
</div>
<aside class="user-list-item-aside">
<a class="user-list-action" href="#" {{action "revoke" target=component}}>
Revoke
</a>
<a class="user-list-action" href="#" {{action "resend" target=component}}>
Resend
</a>
</aside>
</div>
<aside class="user-list-item-aside">
<a class="user-list-action" href="#" {{action "revoke" target=component}}>
Revoke
</a>
<a class="user-list-action" href="#" {{action "resend" target=component}}>
Resend
</a>
</aside>
</div>
{{/gh-user-invited}}
{{/each}}
</section>
{{/if}}
{{/gh-user-invited}}
{{/each}}
</section>
{{/if}}
{{/unless}}
<section class="user-list active-users">
<h4 class="user-list-title">Active users</h4>
{{#each activeUsers key="id" as |user|}}
{{#gh-user-active user=user as |component|}}
{{#link-to 'team.user' user class="user-list-item" }}
<span class="user-list-item-figure" style={{component.userImageBackground}}>
<span class="hidden">Photo of {{user.name}}</span>
</span>
<div class="user-list-item-body">
<span class="name">
{{user.name}}
</span>
<br>
<span class="description">Last seen: {{component.lastLogin}}</span>
</div>
<aside class="user-list-item-aside">
{{#unless user.isAuthor}}
{{#each user.roles key="id" as |role|}}
<span class="role-label {{role.lowerCaseName}}">{{role.name}}</span>
{{/each}}
{{/unless}}
</aside>
{{/link-to}}
{{/gh-user-active}}
{{!-- For authors only shows users as a list, otherwise show users with links to user page --}}
{{#unless session.user.isAuthor}}
{{#gh-user-active user=user as |component|}}
{{#link-to 'team.user' user class="user-list-item"}}
{{partial 'user-list-item'}}
{{/link-to}}
{{/gh-user-active}}
{{else}}
{{#gh-user-active user=user as |component|}}
<li class="ember-view active user-list-item">{{partial 'user-list-item'}}</li>
{{/gh-user-active}}
{{/unless}}
{{/each}}
</section>
{{/gh-infinite-scroll}}

View file

@ -71,7 +71,12 @@
<div class="form-group">
<label for="user-email">Email</label>
{{input type="email" value=user.email id="user-email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off"}}
{{!-- Administrators only see text of Owner's email address but not input --}}
{{#unless isAdminUserOnOwnerProfile}}
{{input type="email" value=user.email id="user-email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off"}}
{{else}}
<span>{{user.email}}</span>
{{/unless}}
<p>Used for notifications</p>
</div>
{{#if rolesDropdownIsVisible}}
@ -108,30 +113,31 @@
<hr />
</fieldset>
{{!-- If an administrator is viewing Owner's profile then hide inputs for change password --}}
{{#unless isAdminUserOnOwnerProfile}}
<fieldset>
{{#unless isNotOwnProfile}}
<div class="form-group">
<label for="user-password-old">Old Password</label>
{{input value=user.password type="password" id="user-password-old" class="gh-input"}}
</div>
{{/unless}}
<fieldset>
{{#unless isNotOwnProfile}}
<div class="form-group">
<label for="user-password-old">Old Password</label>
{{input value=user.password type="password" id="user-password-old" class="gh-input"}}
</div>
{{/unless}}
<div class="form-group">
<label for="user-password-new">New Password</label>
{{input value=user.newPassword type="password" id="user-password-new" class="gh-input"}}
</div>
<div class="form-group">
<label for="user-password-new">New Password</label>
{{input value=user.newPassword type="password" id="user-password-new" class="gh-input"}}
</div>
<div class="form-group">
<label for="user-new-password-verification">Verify Password</label>
{{input value=user.ne2Password type="password" id="user-new-password-verification" class="gh-input"}}
</div>
<div class="form-group">
<button type="button" class="btn btn-red button-change-password" {{action "password"}}>Change Password</button>
</div>
</fieldset>
<div class="form-group">
<label for="user-new-password-verification">Verify Password</label>
{{input value=user.ne2Password type="password" id="user-new-password-verification" class="gh-input"}}
</div>
<div class="form-group">
<button type="button" class="btn btn-red button-change-password" {{action "password"}}>Change Password</button>
</div>
</fieldset>
{{/unless}}
</form>
</div>

View file

@ -0,0 +1,57 @@
import {
describeModule,
it
} from 'ember-mocha';
import {
ghUserCanAdmin
} from 'ghost/helpers/gh-user-can-admin';
describe ('GhUserCanAdminHelper', function () {
// Mock up roles and test for truthy
describe ('Owner role', function () {
var user = {get: function (role) {
if (role === 'isOwner') {
return true;
} else if (role === 'isAdmin') {
return false;
}
}
};
it(' - can be Admin', function () {
var result = ghUserCanAdmin([user]);
expect(result).to.equal(true);
});
});
describe ('Administrator role', function () {
var user = {
get: function (role) {
if (role === 'isOwner') {
return false;
} else if (role === 'isAdmin') {
return true;
}
}
};
it(' - can be Admin', function () {
var result = ghUserCanAdmin([user]);
expect(result).to.equal(true);
});
});
describe ('Editor and Author roles', function () {
var user = {
get: function (role) {
if (role === 'isOwner') {
return false;
} else if (role === 'isAdmin') {
return false;
}
}
};
it(' - cannot be Admin', function () {
var result = ghUserCanAdmin([user]);
expect(result).to.equal(false);
});
});
});

View file

@ -1,97 +0,0 @@
import {
describeModule,
it
} from 'ember-mocha';
import {
ghUserCan
} from 'ghost/helpers/gh-user-can';
describe ('GhUserCanHelper', function () {
// Mock up roles and test for truthy
describe ('Owner role', function () {
var user = {get: function (role) {
if (role === 'isOwner') {
return true;
} else if (role === 'isAdmin') {
return false;
} else if (role === 'isEditor') {
return false;
}
}
};
it(' - can be Admin', function () {
var result = ghUserCan([user, 'admin']);
expect(result).to.equal(true);
});
it(' - can be Editor', function () {
var result = ghUserCan([user, 'editor']);
expect(result).to.equal(true);
});
});
describe ('Administrator role', function () {
var user = {
get: function (role) {
if (role === 'isOwner') {
return false;
} else if (role === 'isAdmin') {
return true;
} else if (role === 'isEditor') {
return false;
}
}
};
it(' - can be Admin', function () {
var result = ghUserCan([user, 'admin']);
expect(result).to.equal(true);
});
it(' - can be Editor', function () {
var result = ghUserCan([user, 'editor']);
expect(result).to.equal(true);
});
});
describe ('Editor role', function () {
var user = {
get: function (role) {
if (role === 'isOwner') {
return false;
} else if (role === 'isAdmin') {
return false;
} else if (role === 'isEditor') {
return true;
}
}
};
it(' - cannot be Admin', function () {
var result = ghUserCan([user, 'admin']);
expect(result).to.equal(false);
});
it(' - can be Editor', function () {
var result = ghUserCan([user, 'editor']);
expect(result).to.equal(true);
});
});
describe ('Author role', function () {
var user = {
get: function (role) {
if (role === 'isOwner') {
return false;
} else if (role === 'isAdmin') {
return false;
} else if (role === 'isEditor') {
return false;
}
}
};
it(' - cannot be Admin', function () {
var result = ghUserCan([user, 'admin']);
expect(result).to.equal(false);
});
it(' - cannot be Editor', function () {
var result = ghUserCan([user, 'editor']);
expect(result).to.equal(false);
});
});
});

View file

@ -34,10 +34,10 @@ CasperTest.begin('Users screen is correct', 9, function suite(test) {
test.assertUrlMatch(/ghost\/team\/$/, 'team transitions to correct url');
});
casper.then(function usersScreenHasContent() {
test.assertSelectorHasText('.settings-users .user-list .user-list-title', 'Active users', 'active users text is correct');
test.assertExists('.settings-users .user-list .user-list-item', 'Has an active user');
test.assertSelectorHasText('.settings-users .user-list-item .name', 'Test User', 'test user text is correct');
test.assertExists('.settings-users .user-list-item .role-label.owner', 'First user has owner role displayed');
test.assertSelectorHasText('.team .user-list .user-list-title', 'Active users', 'active users text is correct');
test.assertExists('.team .user-list .user-list-item', 'Has an active user');
test.assertSelectorHasText('.team .user-list-item .name', 'Test User', 'test user text is correct');
test.assertExists('.team .user-list-item .role-label.owner', 'First user has owner role displayed');
test.assertExists('.view-actions .btn-green', 'Add user button is on page.');
});