Wired email alert settings for staff users on admin (#15313)

refs TryGhost/Team#1826

- allows staff users to manage their email alert settings behind the flag
- only owner and admin users are able to toggle their email alerts
This commit is contained in:
Rishabh Garg 2022-08-25 15:47:14 +05:30 committed by GitHub
parent b51d81843b
commit da62355e18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 4 deletions

View File

@ -21,6 +21,7 @@ export default Controller.extend({
session: service(), session: service(),
slugGenerator: service(), slugGenerator: service(),
utils: service(), utils: service(),
membersUtils: service(),
personalToken: null, personalToken: null,
limitErrorMessage: null, limitErrorMessage: null,
@ -47,6 +48,8 @@ export default Controller.extend({
canChangeEmail: not('isAdminUserOnOwnerProfile'), canChangeEmail: not('isAdminUserOnOwnerProfile'),
canChangePassword: not('isAdminUserOnOwnerProfile'), canChangePassword: not('isAdminUserOnOwnerProfile'),
canToggleMemberAlerts: or('currentUser.isOwnerOnly', 'isAdminUserOnOwnProfile'),
isAdminUserOnOwnProfile: and('currentUser.isAdminOnly', 'isOwnProfile'),
canMakeOwner: and('currentUser.isOwnerOnly', 'isNotOwnProfile', 'user.isAdminOnly', 'isNotSuspended'), canMakeOwner: and('currentUser.isOwnerOnly', 'isNotOwnProfile', 'user.isAdminOnly', 'isNotSuspended'),
isAdminUserOnOwnerProfile: and('currentUser.isAdminOnly', 'user.isOwnerOnly'), isAdminUserOnOwnerProfile: and('currentUser.isAdminOnly', 'user.isOwnerOnly'),
isNotOwnersProfile: not('user.isOwnerOnly'), isNotOwnersProfile: not('user.isOwnerOnly'),
@ -385,6 +388,16 @@ export default Controller.extend({
this.user.commentNotifications = event.target.checked; this.user.commentNotifications = event.target.checked;
}), }),
toggleMemberEmailAlerts: action(function (type, event) {
if (type === 'free-signup') {
this.user.freeMemberSignupNotification = event.target.checked;
} else if (type === 'paid-started') {
this.user.paidSubscriptionStartedNotification = event.target.checked;
} else if (type === 'paid-canceled') {
this.user.paidSubscriptionCanceledNotification = event.target.checked;
}
}),
deleteUser: task(function *() { deleteUser: task(function *() {
try { try {
yield this.user.destroyRecord(); yield this.user.destroyRecord();

View File

@ -36,6 +36,9 @@ export default BaseModel.extend(ValidationEngine, {
twitter: attr('twitter-url-user'), twitter: attr('twitter-url-user'),
tour: attr('json-string'), tour: attr('json-string'),
commentNotifications: attr(), commentNotifications: attr(),
freeMemberSignupNotification: attr(),
paidSubscriptionStartedNotification: attr(),
paidSubscriptionCanceledNotification: attr(),
ghostPaths: service(), ghostPaths: service(),
ajax: service(), ajax: service(),

View File

@ -196,7 +196,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-top: 12px; margin-top: 20px;
} }
.user-setting-toggle label { .user-setting-toggle label {
@ -205,4 +205,8 @@
.user-setting-toggle p { .user-setting-toggle p {
margin-top: 0; margin-top: 0;
}
.user-settings-subgroup .for-switch {
width: 38px !important;
} }

View File

@ -306,16 +306,16 @@
You've used {{gh-count-down-characters this.user.bio 200}} You've used {{gh-count-down-characters this.user.bio 200}}
</p> </p>
</GhFormGroup> </GhFormGroup>
{{#if this.membersUtils.isMembersEnabled}}
<div class="user-settings-subgroup"> <div class="user-settings-subgroup">
<h4 class="user-settings-heading">Email notifications</h4> <h4 class="user-settings-heading">Email notifications</h4>
<GhFormGroup @errors={{this.user.errors}} @hasValidated={{this.user.hasValidated}} @property="email"> <GhFormGroup @errors={{this.user.errors}} @hasValidated={{this.user.hasValidated}} @property="email">
<div class="user-setting-toggle"> <div class="user-setting-toggle">
<div> <div>
<label for="user-email">Comments</label> <label for="user-email">Comments</label>
<p>Receive notifications when members comment on one of your posts</p> <p>Every time a member comments on one of your posts</p>
</div> </div>
<div class="for-switch"> <div class="for-switch small">
<label class="switch" for="comment-notifications"> <label class="switch" for="comment-notifications">
<input <input
id="comment-notifications" id="comment-notifications"
@ -329,8 +329,70 @@
</label> </label>
</div> </div>
</div> </div>
{{#if (and this.canToggleMemberAlerts (feature 'emailAlerts'))}}
<div class="user-setting-toggle">
<div>
<label for="user-email">New signups</label>
<p>Every time a new free member signs up</p>
</div>
<div class="for-switch small">
<label class="switch" for="free-signup-notifications" data-test-label="free-signup-notifications">
<input
id="free-signup-notifications"
type="checkbox"
checked={{this.user.freeMemberSignupNotification}}
class="gh-input"
{{on "change" (fn this.toggleMemberEmailAlerts 'free-signup')}}
data-test-checkbox="free-signup-notifications"
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
{{#if this.membersUtils.paidMembersEnabled}}
<div class="user-setting-toggle">
<div>
<label for="user-email">New paid members</label>
<p>Every time a member starts a new paid subscription</p>
</div>
<div class="for-switch small">
<label class="switch" for="paid-started-notifications" data-test-label="paid-started-notifications">
<input
id="paid-started-notifications"
type="checkbox"
checked={{this.user.paidSubscriptionStartedNotification}}
class="gh-input"
{{on "change" (fn this.toggleMemberEmailAlerts 'paid-started')}}
data-test-checkbox="paid-started-notifications"
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
<div class="user-setting-toggle">
<div>
<label for="user-email">Paid member cancellations</label>
<p>Every time a member cancels their paid subscription</p>
</div>
<div class="for-switch small">
<label class="switch" for="paid-canceled-notifications" data-test-label="paid-canceled-notifications">
<input
id="paid-canceled-notifications"
type="checkbox"
checked={{this.user.paidSubscriptionCanceledNotification}}
class="gh-input"
{{on "change" (fn this.toggleMemberEmailAlerts 'paid-canceled')}}
data-test-checkbox="paid-canceled-notifications"
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
{{/if}}
{{/if}}
</GhFormGroup> </GhFormGroup>
</div> </div>
{{/if}}
</fieldset> </fieldset>
</div> </div>

View File

@ -4,6 +4,10 @@ import windowProxy from 'ghost-admin/utils/window-proxy';
import {Response} from 'miragejs'; import {Response} from 'miragejs';
import {afterEach, beforeEach, describe, it} from 'mocha'; import {afterEach, beforeEach, describe, it} from 'mocha';
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support'; import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
import {enableLabsFlag} from '../helpers/labs-flag';
import {enableMembers} from '../helpers/members';
import {enableStripe} from '../helpers/stripe';
import { import {
blur, blur,
click, click,
@ -73,7 +77,11 @@ describe('Acceptance: Staff', function () {
beforeEach(async function () { beforeEach(async function () {
this.server.loadFixtures('roles'); this.server.loadFixtures('roles');
this.server.loadFixtures('settings');
adminRole = this.server.schema.roles.find(1); adminRole = this.server.schema.roles.find(1);
enableMembers(this.server);
enableStripe(this.server);
enableLabsFlag(this.server, 'emailAlerts');
admin = this.server.create('user', {email: 'admin@example.com', roles: [adminRole]}); admin = this.server.create('user', {email: 'admin@example.com', roles: [adminRole]});
@ -816,6 +824,13 @@ describe('Acceptance: Staff', function () {
expect(find('[data-test-slug-input]').value).to.be.equal('test-1'); expect(find('[data-test-slug-input]').value).to.be.equal('test-1');
expect(find('[data-test-facebook-input]').value).to.be.equal('https://www.facebook.com/test'); expect(find('[data-test-facebook-input]').value).to.be.equal('https://www.facebook.com/test');
}); });
it('cannot see email alerts for other user', async function () {
await visit('/settings/staff/test-1');
expect(find('[data-test-checkbox="free-signup-notifications"]'), 'free signup alert').to.not.exist;
expect(find('[data-test-checkbox="paid-started-notifications"]'), 'paid start alert').to.not.exist;
expect(find('[data-test-checkbox="paid-canceled-notifications"]'), 'paid cancel alert').to.not.exist;
});
}); });
describe('own user', function () { describe('own user', function () {
@ -856,6 +871,24 @@ describe('Acceptance: Staff', function () {
'old password validation is in error state after typing' 'old password validation is in error state after typing'
).to.not.have.class('error'); ).to.not.have.class('error');
}); });
it('can toggle email alerts for own user', async function () {
await visit(`/settings/staff/${admin.slug}`);
expect(find('[data-test-checkbox="free-signup-notifications"]')).to.not.be.checked;
expect(find('[data-test-checkbox="paid-started-notifications"]')).to.not.be.checked;
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.not.be.checked;
await click('[data-test-label="free-signup-notifications"]');
await click('[data-test-label="paid-started-notifications"]');
await click('[data-test-label="paid-canceled-notifications"]');
await click('[data-test-save-button]');
await visit(`/settings/staff/${admin.slug}`);
expect(find('[data-test-checkbox="free-signup-notifications"]')).to.be.checked;
expect(find('[data-test-checkbox="paid-started-notifications"]')).to.be.checked;
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.be.checked;
});
}); });
it('redirects to 404 when user does not exist', async function () { it('redirects to 404 when user does not exist', async function () {

View File

@ -2,6 +2,10 @@ export function enableMembers(server) {
server.db.settings.find({key: 'members_signup_access'}) server.db.settings.find({key: 'members_signup_access'})
? server.db.settings.update({key: 'members_signup_access'}, {value: 'all'}) ? server.db.settings.update({key: 'members_signup_access'}, {value: 'all'})
: server.create('setting', {key: 'members_signup_access', value: 'all', group: 'members'}); : server.create('setting', {key: 'members_signup_access', value: 'all', group: 'members'});
server.db.settings.find({key: 'members_enabled'})
? server.db.settings.update({key: 'members_enabled'}, {value: true})
: server.create('setting', {key: 'members_enabled', value: true, group: 'members'});
} }
export function disableMembers(server) { export function disableMembers(server) {