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:
commit
dc06deaa29
11 changed files with 177 additions and 216 deletions
|
@ -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'),
|
||||
|
|
11
core/client/app/helpers/gh-user-can-admin.js
Normal file
11
core/client/app/helpers/gh-user-can-admin.js
Normal 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);
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
18
core/client/app/templates/-user-list-item.hbs
Normal file
18
core/client/app/templates/-user-list-item.hbs
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
57
core/client/tests/unit/helpers/gh-user-can-admin-test.js
Normal file
57
core/client/tests/unit/helpers/gh-user-can-admin-test.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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.');
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue