Renamed isAdmin/isOwner/isAdminOrOwner to reduce confusion

issue https://github.com/TryGhost/Team/issues/857

- The goal is to avoid testing for the owner role only is cases where we should be testing for the owner or admin role
- `isOwner` => `isOwnerOnly`
- `isAdmin` => `isAdminOnly`
- `isOwnerOrAdmin` => `isAdmin` (concerns now both Owner and Admins)
This commit is contained in:
Thibaut Patel 2021-07-12 14:55:56 +02:00
parent a23b191257
commit dc9c812d9c
25 changed files with 54 additions and 54 deletions

View File

@ -8,7 +8,7 @@ export default class GhContentfilterComponent extends Component {
@service router; @service router;
get showCustomViewManagement() { get showCustomViewManagement() {
let isOwnerOrAdmin = get(this.args.currentUser || {}, 'isOwnerOrAdmin'); let isAdmin = get(this.args.currentUser || {}, 'isAdmin');
let onPostsScreen = this.router.currentRouteName === 'posts'; let onPostsScreen = this.router.currentRouteName === 'posts';
let isDefaultView = this.customViews?.activeView?.isDefault; let isDefaultView = this.customViews?.activeView?.isDefault;
let hasFilter = this.args.selectedType.value let hasFilter = this.args.selectedType.value
@ -17,7 +17,7 @@ export default class GhContentfilterComponent extends Component {
|| this.args.selectedTag.slug || this.args.selectedTag.slug
|| this.args.selectedOrder.value; || this.args.selectedOrder.value;
return isOwnerOrAdmin && onPostsScreen && !isDefaultView && hasFilter; return isAdmin && onPostsScreen && !isDefaultView && hasFilter;
} }
calculateActionsDropdownPosition(trigger, content) { calculateActionsDropdownPosition(trigger, content) {

View File

@ -176,7 +176,7 @@ export default class GhMembersRecipientSelect extends Component {
*fetchMemberCountsTask() { *fetchMemberCountsTask() {
const user = yield this.session.user; const user = yield this.session.user;
if (!user.isOwnerOrAdmin) { if (!user.isAdmin) {
return; return;
} }

View File

@ -1,4 +1,4 @@
{{#if this.session.user.isOwnerOrAdmin}} {{#if this.session.user.isAdmin}}
<span <span
class="segment-totals" class="segment-totals"
{{did-insert (perform this.fetchTotalsTask)}} {{did-insert (perform this.fetchTotalsTask)}}

View File

@ -10,7 +10,7 @@ export default Component.extend({
whatsNew: service(), whatsNew: service(),
feature: service(), feature: service(),
showDropdownExtension: and('config.clientExtensions.dropdown', 'session.user.isOwner'), showDropdownExtension: and('config.clientExtensions.dropdown', 'session.user.isOwnerOnly'),
isSettingsRoute: match('router.currentRouteName', /^settings/), isSettingsRoute: match('router.currentRouteName', /^settings/),
// equivalent to "left: auto; right: -20px" // equivalent to "left: auto; right: -20px"
@ -23,4 +23,4 @@ export default Component.extend({
return {horizontalPosition, verticalPosition, style}; return {horizontalPosition, verticalPosition, style};
} }
}); });

View File

@ -88,7 +88,7 @@
<li><LinkTo @route="staff" data-test-nav="staff">{{svg-jar "staff"}}Staff</LinkTo></li> <li><LinkTo @route="staff" data-test-nav="staff">{{svg-jar "staff"}}Staff</LinkTo></li>
</ul> </ul>
{{#if this.session.user.isOwner}} {{#if this.session.user.isOwnerOnly}}
<ul class="gh-nav-list"> <ul class="gh-nav-list">
{{#if this.showBilling}} {{#if this.showBilling}}
<li class="relative"> <li class="relative">

View File

@ -37,9 +37,9 @@ export default Component.extend(ShortcutsMixin, {
// be a bug in Ember that's preventing it from working immediately after login // be a bug in Ember that's preventing it from working immediately after login
isOnSite: equal('router.currentRouteName', 'site'), isOnSite: equal('router.currentRouteName', 'site'),
showTagsNavigation: or('session.user.isOwnerOrAdmin', 'session.user.isEditor'), showTagsNavigation: or('session.user.isAdmin', 'session.user.isEditor'),
showMenuExtension: and('config.clientExtensions.menu', 'session.user.isOwner'), showMenuExtension: and('config.clientExtensions.menu', 'session.user.isOwnerOnly'),
showScriptExtension: and('config.clientExtensions.script', 'session.user.isOwner'), showScriptExtension: and('config.clientExtensions.script', 'session.user.isOwnerOnly'),
showBilling: computed.reads('config.hostSettings.billing.enabled'), showBilling: computed.reads('config.hostSettings.billing.enabled'),
init() { init() {

View File

@ -44,8 +44,8 @@ export default Component.extend({
twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'), twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'),
twitterTitle: or('twitterTitleScratch', 'seoTitle'), twitterTitle: or('twitterTitleScratch', 'seoTitle'),
showVisibilityInput: or('session.user.isOwner', 'session.user.isAdmin', 'session.user.isEditor'), showVisibilityInput: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
showEmailNewsletter: or('session.user.isOwner', 'session.user.isAdmin', 'session.user.isEditor'), showEmailNewsletter: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () { seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () {
return this.metaTitleScratch || this.post.titleScratch || '(Untitled)'; return this.metaTitleScratch || this.post.titleScratch || '(Untitled)';

View File

@ -50,7 +50,7 @@
</div> </div>
<div class="pa5 pt3 pb3 f7 bb b--whitegrey"> <div class="pa5 pt3 pb3 f7 bb b--whitegrey">
<p class="mb2 lh-copy"> <p class="mb2 lh-copy">
{{#if this.session.user.isOwner}} {{#if this.session.user.isOwnerOnly}}
Email failed to send when publishing this post. Please verify your email settings if the error persists. Email failed to send when publishing this post. Please verify your email settings if the error persists.
{{else}} {{else}}
Email failed to send when publishing this post. Please verify your email settings if the error persists. Email failed to send when publishing this post. Please verify your email settings if the error persists.

View File

@ -21,7 +21,7 @@ export default class GhPublishMenuDraftComponent extends Component {
get disableEmailOption() { get disableEmailOption() {
// TODO: remove owner or admin check when editors can count members // TODO: remove owner or admin check when editors can count members
return this.session.user.isOwnerOrAdmin && (this.totalMemberCount === 0 || this.countTotalMembersTask.isRunning); return this.session.user.isAdmin && (this.totalMemberCount === 0 || this.countTotalMembersTask.isRunning);
} }
constructor() { constructor() {
@ -82,7 +82,7 @@ export default class GhPublishMenuDraftComponent extends Component {
*countTotalMembersTask() { *countTotalMembersTask() {
const user = yield this.session.user; const user = yield this.session.user;
if (user.isOwnerOrAdmin) { if (user.isAdmin) {
const result = yield this.store.query('member', {limit: 1, filter: 'subscribed:true'}); const result = yield this.store.query('member', {limit: 1, filter: 'subscribed:true'});
this.totalMemberCount = result.meta.pagination.total; this.totalMemberCount = result.meta.pagination.total;
} }

View File

@ -38,7 +38,7 @@ export default Component.extend({
forcePublishedMenu: reads('post.pastScheduledTime'), forcePublishedMenu: reads('post.pastScheduledTime'),
hasEmailPermission: or('session.user.isOwner', 'session.user.isAdmin', 'session.user.isEditor'), hasEmailPermission: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
canSendEmail: computed('hasEmailPermission', 'post.{isPost,email}', 'settings.{editorDefaultEmailRecipients,membersSignupAccess,mailgunIsConfigured}', 'config.mailgunIsConfigured', function () { canSendEmail: computed('hasEmailPermission', 'post.{isPost,email}', 'settings.{editorDefaultEmailRecipients,membersSignupAccess,mailgunIsConfigured}', 'config.mailgunIsConfigured', function () {
let isDisabled = this.settings.get('editorDefaultEmailRecipients') === 'disabled' || this.settings.get('membersSignupAccess') === 'none'; let isDisabled = this.settings.get('editorDefaultEmailRecipients') === 'disabled' || this.settings.get('membersSignupAccess') === 'none';

View File

@ -39,7 +39,7 @@
<button class="close" title="Close" {{on "click" this.closeModal}}>{{svg-jar "close"}}<span class="hidden">Close</span></button> <button class="close" title="Close" {{on "click" this.closeModal}}>{{svg-jar "close"}}<span class="hidden">Close</span></button>
<div class="modal-body"> <div class="modal-body">
{{#if this.session.user.isOwner}} {{#if this.session.user.isOwnerOnly}}
<p>Your post has been published but the email failed to send. Please verify your email settings if the error persists.</p> <p>Your post has been published but the email failed to send. Please verify your email settings if the error persists.</p>
{{else}} {{else}}
<p>Your post has been published but the email failed to send. Please verify your email settings if the error persists.</p> <p>Your post has been published but the email failed to send. Please verify your email settings if the error persists.</p>

View File

@ -154,9 +154,9 @@ export default Controller.extend({
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
}), }),
canManageSnippets: computed('session.user.{isOwnerOrAdmin,isEditor}', function () { canManageSnippets: computed('session.user.{isAdmin,isEditor}', function () {
let {user} = this.session; let {user} = this.session;
if (user.get('isOwnerOrAdmin') || user.get('isEditor')) { if (user.get('isAdmin') || user.get('isEditor')) {
return true; return true;
} }
return false; return false;

View File

@ -46,11 +46,11 @@ export default Controller.extend({
canChangeEmail: not('isAdminUserOnOwnerProfile'), canChangeEmail: not('isAdminUserOnOwnerProfile'),
canChangePassword: not('isAdminUserOnOwnerProfile'), canChangePassword: not('isAdminUserOnOwnerProfile'),
canMakeOwner: and('currentUser.isOwner', 'isNotOwnProfile', 'user.isAdmin', 'isNotSuspended'), canMakeOwner: and('currentUser.isOwnerOnly', 'isNotOwnProfile', 'user.isAdminOnly', 'isNotSuspended'),
isAdminUserOnOwnerProfile: and('currentUser.isAdmin', 'user.isOwner'), isAdminUserOnOwnerProfile: and('currentUser.isAdminOnly', 'user.isOwnerOnly'),
isNotOwnersProfile: not('user.isOwner'), isNotOwnersProfile: not('user.isOwnerOnly'),
isNotSuspended: not('user.isSuspended'), isNotSuspended: not('user.isSuspended'),
rolesDropdownIsVisible: and('currentUser.isOwnerOrAdmin', 'isNotOwnProfile', 'isNotOwnersProfile'), rolesDropdownIsVisible: and('currentUser.isAdmin', 'isNotOwnProfile', 'isNotOwnersProfile'),
userActionsAreVisible: or('deleteUserActionIsVisible', 'canMakeOwner'), userActionsAreVisible: or('deleteUserActionIsVisible', 'canMakeOwner'),
isNotOwnProfile: not('isOwnProfile'), isNotOwnProfile: not('isOwnProfile'),
@ -58,7 +58,7 @@ export default Controller.extend({
return this.get('user.id') === this.get('currentUser.id'); return this.get('user.id') === this.get('currentUser.id');
}), }),
deleteUserActionIsVisible: computed('currentUser.{isOwnerOrAdmin,isEditor}', 'user.{isOwner,isAuthorOrContributor}', 'isOwnProfile', function () { deleteUserActionIsVisible: computed('currentUser.{isAdmin,isEditor}', 'user.{isOwnerOnly,isAuthorOrContributor}', 'isOwnProfile', function () {
// users can't delete themselves // users can't delete themselves
if (this.isOwnProfile) { if (this.isOwnProfile) {
return false; return false;
@ -66,7 +66,7 @@ export default Controller.extend({
if ( if (
// owners/admins can delete any non-owner user // owners/admins can delete any non-owner user
(this.currentUser.get('isOwnerOrAdmin') && !this.user.isOwner) || (this.currentUser.get('isAdmin') && !this.user.isOwnerOnly) ||
// editors can delete any author or contributor // editors can delete any author or contributor
(this.currentUser.get('isEditor') && this.user.isAuthorOrContributor) (this.currentUser.get('isEditor') && this.user.isAuthorOrContributor)
) { ) {

View File

@ -6,7 +6,7 @@ import {helper} from '@ember/component/helper';
// @param session.user // @param session.user
export function ghUserCanAdmin(params) { export function ghUserCanAdmin(params) {
return !!(params[0].get('isOwnerOrAdmin')); return !!(params[0].get('isAdmin'));
} }
export default helper(function (params) { export default helper(function (params) {

View File

@ -47,11 +47,11 @@ export default BaseModel.extend(ValidationEngine, {
isContributor: equal('role.name', 'Contributor'), isContributor: equal('role.name', 'Contributor'),
isAuthor: equal('role.name', 'Author'), isAuthor: equal('role.name', 'Author'),
isEditor: equal('role.name', 'Editor'), isEditor: equal('role.name', 'Editor'),
isAdmin: equal('role.name', 'Administrator'), isAdminOnly: equal('role.name', 'Administrator'),
isOwner: equal('role.name', 'Owner'), isOwnerOnly: equal('role.name', 'Owner'),
// These are used in enough places that it's useful to throw them here // These are used in enough places that it's useful to throw them here
isOwnerOrAdmin: or('isOwner', 'isAdmin'), isAdmin: or('isOwnerOnly', 'isAdminOnly'),
isAuthorOrContributor: or('isAuthor', 'isContributor'), isAuthorOrContributor: or('isAuthor', 'isContributor'),
isLoggedIn: computed('id', 'session.user.id', function () { isLoggedIn: computed('id', 'session.user.id', function () {

View File

@ -3,7 +3,7 @@ import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
export default class DashboardRoute extends AuthenticatedRoute { export default class DashboardRoute extends AuthenticatedRoute {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.session.user.isOwnerOrAdmin) { if (!this.session.user.isAdmin) {
return this.transitionTo('site'); return this.transitionTo('site');
} }
} }

View File

@ -6,7 +6,7 @@ export default class LaunchRoute extends AuthenticatedRoute {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.session.user.isOwner) { if (!this.session.user.isOwnerOnly) {
return this.transitionTo('home'); return this.transitionTo('home');
} }
} }

View File

@ -18,7 +18,7 @@ export default class MembersRoute extends AuthenticatedRoute {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.session.user.isOwnerOrAdmin) { if (!this.session.user.isAdmin) {
return this.transitionTo('home'); return this.transitionTo('home');
} }
} }

View File

@ -16,7 +16,7 @@ export default class MembersRoute extends AuthenticatedRoute {
// - logged in user isn't owner/admin // - logged in user isn't owner/admin
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.session.user.isOwnerOrAdmin) { if (!this.session.user.isAdmin) {
return this.transitionTo('home'); return this.transitionTo('home');
} }
} }

View File

@ -13,7 +13,7 @@ export default AuthenticatedRoute.extend({
beforeModel(transition) { beforeModel(transition) {
this._super(...arguments); this._super(...arguments);
if (!this.session.user.isOwner) { if (!this.session.user.isOwnerOnly) {
return this.transitionTo('home'); return this.transitionTo('home');
} }

View File

@ -25,7 +25,7 @@ export default class ProductRoute extends AuthenticatedRoute {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.session.user.isOwnerOrAdmin) { if (!this.session.user.isAdmin) {
return this.transitionTo('home'); return this.transitionTo('home');
} }
} }

View File

@ -7,7 +7,7 @@
<div class="view-container gh-dashboard"> <div class="view-container gh-dashboard">
{{#if (and this.session.user.isOwner (not this.feature.launchComplete))}} {{#if (and this.session.user.isOwnerOnly (not this.feature.launchComplete))}}
<section class="gh-dashboard-area lw-banner"> <section class="gh-dashboard-area lw-banner">
<div class="gh-lw-banner" style="background-image:url(assets/img/launch-wizard-bg.png);"> <div class="gh-lw-banner" style="background-image:url(assets/img/launch-wizard-bg.png);">
<h1>Select your publication style</h1> <h1>Select your publication style</h1>
@ -309,7 +309,7 @@
</div> </div>
{{/if}} {{/if}}
{{#unless (and this.session.user.isOwner (not this.feature.launchComplete))}} {{#unless (and this.session.user.isOwnerOnly (not this.feature.launchComplete))}}
<div class="gh-dashboard-box grey activity-feed"> <div class="gh-dashboard-box grey activity-feed">
<h4 class="gh-dashboard-header">Activity feed</h4> <h4 class="gh-dashboard-header">Activity feed</h4>
<div class="content"> <div class="content">

View File

@ -11,7 +11,7 @@
</h2> </h2>
<section class="view-actions"> <section class="view-actions">
{{#if this.session.user.isOwnerOrAdmin}} {{#if this.session.user.isAdmin}}
{{#unless this.member.isNew}} {{#unless this.member.isNew}}
<button <button
class="gh-btn gh-btn-white gh-btn-icon mr2" class="gh-btn gh-btn-white gh-btn-icon mr2"

View File

@ -7,9 +7,10 @@ describe('Unit: Helper: gh-user-can-admin', function () {
describe('Owner or admin roles', function () { describe('Owner or admin roles', function () {
let user = { let user = {
get(role) { get(role) {
if (role === 'isOwnerOrAdmin') { if (role === 'isAdmin') {
return true; return true;
} }
throw new Error('unsupported'); // Make sure we only call get('isAdmin')
} }
}; };
@ -22,11 +23,10 @@ describe('Unit: Helper: gh-user-can-admin', function () {
describe('Editor, Author & Contributor roles', function () { describe('Editor, Author & Contributor roles', function () {
let user = { let user = {
get(role) { get(role) {
if (role === 'isOwner') { if (role === 'isAdmin') {
return false;
} else if (role === 'isAdmin') {
return false; return false;
} }
throw new Error('unsupported'); // Make sure we only call get('isAdmin')
} }
}; };

View File

@ -68,8 +68,8 @@ describe('Unit: Model: user', function () {
expect(model.get('isAuthorOrContributor')).to.be.ok; expect(model.get('isAuthorOrContributor')).to.be.ok;
expect(model.get('isAuthor')).to.not.be.ok; expect(model.get('isAuthor')).to.not.be.ok;
expect(model.get('isEditor')).to.not.be.ok; expect(model.get('isEditor')).to.not.be.ok;
expect(model.get('isAdmin')).to.not.be.ok; expect(model.get('isAdminOnly')).to.not.be.ok;
expect(model.get('isOwner')).to.not.be.ok; expect(model.get('isOwnerOnly')).to.not.be.ok;
}); });
it('isAuthor property is correct', function () { it('isAuthor property is correct', function () {
@ -83,8 +83,8 @@ describe('Unit: Model: user', function () {
expect(model.get('isContributor')).to.not.be.ok; expect(model.get('isContributor')).to.not.be.ok;
expect(model.get('isAuthorOrContributor')).to.be.ok; expect(model.get('isAuthorOrContributor')).to.be.ok;
expect(model.get('isEditor')).to.not.be.ok; expect(model.get('isEditor')).to.not.be.ok;
expect(model.get('isAdmin')).to.not.be.ok; expect(model.get('isAdminOnly')).to.not.be.ok;
expect(model.get('isOwner')).to.not.be.ok; expect(model.get('isOwnerOnly')).to.not.be.ok;
}); });
it('isEditor property is correct', function () { it('isEditor property is correct', function () {
@ -98,37 +98,37 @@ describe('Unit: Model: user', function () {
expect(model.get('isAuthor')).to.not.be.ok; expect(model.get('isAuthor')).to.not.be.ok;
expect(model.get('isContributor')).to.not.be.ok; expect(model.get('isContributor')).to.not.be.ok;
expect(model.get('isAuthorOrContributor')).to.not.be.ok; expect(model.get('isAuthorOrContributor')).to.not.be.ok;
expect(model.get('isAdmin')).to.not.be.ok; expect(model.get('isAdminOnly')).to.not.be.ok;
expect(model.get('isOwner')).to.not.be.ok; expect(model.get('isOwnerOnly')).to.not.be.ok;
}); });
it('isAdmin property is correct', function () { it('isAdminOnly property is correct', function () {
let model = store.createRecord('user'); let model = store.createRecord('user');
run(() => { run(() => {
let role = store.push({data: {id: 1, type: 'role', attributes: {name: 'Administrator'}}}); let role = store.push({data: {id: 1, type: 'role', attributes: {name: 'Administrator'}}});
model.set('role', role); model.set('role', role);
}); });
expect(model.get('isAdmin')).to.be.ok; expect(model.get('isAdminOnly')).to.be.ok;
expect(model.get('isAuthor')).to.not.be.ok; expect(model.get('isAuthor')).to.not.be.ok;
expect(model.get('isContributor')).to.not.be.ok; expect(model.get('isContributor')).to.not.be.ok;
expect(model.get('isAuthorOrContributor')).to.not.be.ok; expect(model.get('isAuthorOrContributor')).to.not.be.ok;
expect(model.get('isEditor')).to.not.be.ok; expect(model.get('isEditor')).to.not.be.ok;
expect(model.get('isOwner')).to.not.be.ok; expect(model.get('isOwnerOnly')).to.not.be.ok;
}); });
it('isOwner property is correct', function () { it('isOwnerOnly property is correct', function () {
let model = store.createRecord('user'); let model = store.createRecord('user');
run(() => { run(() => {
let role = store.push({data: {id: 1, type: 'role', attributes: {name: 'Owner'}}}); let role = store.push({data: {id: 1, type: 'role', attributes: {name: 'Owner'}}});
model.set('role', role); model.set('role', role);
}); });
expect(model.get('isOwner')).to.be.ok; expect(model.get('isOwnerOnly')).to.be.ok;
expect(model.get('isAuthor')).to.not.be.ok; expect(model.get('isAuthor')).to.not.be.ok;
expect(model.get('isContributor')).to.not.be.ok; expect(model.get('isContributor')).to.not.be.ok;
expect(model.get('isAuthorOrContributor')).to.not.be.ok; expect(model.get('isAuthorOrContributor')).to.not.be.ok;
expect(model.get('isAdmin')).to.not.be.ok; expect(model.get('isAdminOnly')).to.not.be.ok;
expect(model.get('isEditor')).to.not.be.ok; expect(model.get('isEditor')).to.not.be.ok;
}); });
}); });