mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Added Mentions email notification settings (#16245)
closes https://github.com/TryGhost/Team/issues/2526 - Mention emails can now be toggled inside staff user' profiles, if they have the webmention flag enabled on their Ghost site. - Removed the flag dedicated to webmention email notifications and is now handled by the `webmention` flag. - Does not send email notification if `webmention` flag is not enabled. - Updated tests. --------- Co-authored-by: Fabien "egg" O'Carroll <fabien@allou.is>
This commit is contained in:
parent
ea2c69565f
commit
ce567b9816
13 changed files with 60 additions and 27 deletions
|
@ -211,6 +211,11 @@ export default class UserController extends Controller {
|
||||||
this.user.commentNotifications = event.target.checked;
|
this.user.commentNotifications = event.target.checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleMentionNotifications(event) {
|
||||||
|
this.user.mentionNotifications = event.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleMemberEmailAlerts(type, event) {
|
toggleMemberEmailAlerts(type, event) {
|
||||||
if (type === 'free-signup') {
|
if (type === 'free-signup') {
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default BaseModel.extend(ValidationEngine, {
|
||||||
freeMemberSignupNotification: attr(),
|
freeMemberSignupNotification: attr(),
|
||||||
paidSubscriptionStartedNotification: attr(),
|
paidSubscriptionStartedNotification: attr(),
|
||||||
paidSubscriptionCanceledNotification: attr(),
|
paidSubscriptionCanceledNotification: attr(),
|
||||||
|
mentionNotifications: attr(),
|
||||||
ghostPaths: service(),
|
ghostPaths: service(),
|
||||||
ajax: service(),
|
ajax: service(),
|
||||||
session: service(),
|
session: service(),
|
||||||
|
|
|
@ -69,7 +69,6 @@ export default class FeatureService extends Service {
|
||||||
@feature('emailStability') emailStability;
|
@feature('emailStability') emailStability;
|
||||||
@feature('webmentions') webmentions;
|
@feature('webmentions') webmentions;
|
||||||
@feature('outboundLinkTagging') outboundLinkTagging;
|
@feature('outboundLinkTagging') outboundLinkTagging;
|
||||||
@feature('webmentionEmail') webmentionEmail;
|
|
||||||
@feature('emailErrors') emailErrors;
|
@feature('emailErrors') emailErrors;
|
||||||
@feature('milestoneEmails') milestoneEmails;
|
@feature('milestoneEmails') milestoneEmails;
|
||||||
|
|
||||||
|
|
|
@ -213,19 +213,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-expandable-block">
|
|
||||||
<div class="gh-expandable-header">
|
|
||||||
<div>
|
|
||||||
<h4 class="gh-expandable-title">Webmention Email Notifications</h4>
|
|
||||||
<p class="gh-expandable-description">
|
|
||||||
Receive email notifications when a webmention is received.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="for-switch">
|
|
||||||
<GhFeatureFlag @flag="webmentionEmail" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gh-expandable-block">
|
<div class="gh-expandable-block">
|
||||||
<div class="gh-expandable-header">
|
<div class="gh-expandable-header">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -327,6 +327,27 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if (feature 'webmentions')}}
|
||||||
|
<div class="user-setting-toggle">
|
||||||
|
<div>
|
||||||
|
<label for="user-email">New Mentions</label>
|
||||||
|
<p>When you receive a new webmention</p>
|
||||||
|
</div>
|
||||||
|
<div class="for-switch small">
|
||||||
|
<label class="switch" for="mention-notifications" data-test-label="mention-notifications">
|
||||||
|
<input
|
||||||
|
id="mention-notifications"
|
||||||
|
type="checkbox"
|
||||||
|
checked={{this.user.mentionNotifications}}
|
||||||
|
class="gh-input"
|
||||||
|
{{on "change" this.toggleMentionNotifications}}
|
||||||
|
data-test-checkbox="mention-notifications"
|
||||||
|
>
|
||||||
|
<span class="input-toggle-component"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</GhFormGroup>
|
</GhFormGroup>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -19,7 +19,6 @@ export default Factory.extend({
|
||||||
updatedAt: '2015-11-02T16:12:05.000Z',
|
updatedAt: '2015-11-02T16:12:05.000Z',
|
||||||
updatedBy: '1',
|
updatedBy: '1',
|
||||||
website: 'http://example.com',
|
website: 'http://example.com',
|
||||||
|
|
||||||
posts() { return []; },
|
posts() { return []; },
|
||||||
roles() { return []; }
|
roles() { return []; }
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
focus,
|
focus,
|
||||||
triggerEvent
|
triggerEvent
|
||||||
} from '@ember/test-helpers';
|
} from '@ember/test-helpers';
|
||||||
|
import {enableLabsFlag} from '../helpers/labs-flag';
|
||||||
import {enableMembers} from '../helpers/members';
|
import {enableMembers} from '../helpers/members';
|
||||||
import {enableStripe} from '../helpers/stripe';
|
import {enableStripe} from '../helpers/stripe';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
@ -79,6 +80,7 @@ describe('Acceptance: Staff', function () {
|
||||||
adminRole = this.server.schema.roles.find(1);
|
adminRole = this.server.schema.roles.find(1);
|
||||||
enableMembers(this.server);
|
enableMembers(this.server);
|
||||||
enableStripe(this.server);
|
enableStripe(this.server);
|
||||||
|
enableLabsFlag(this.server, 'webmentions');
|
||||||
|
|
||||||
admin = this.server.create('user', {email: 'admin@example.com', roles: [adminRole]});
|
admin = this.server.create('user', {email: 'admin@example.com', roles: [adminRole]});
|
||||||
|
|
||||||
|
@ -869,17 +871,20 @@ describe('Acceptance: Staff', function () {
|
||||||
expect(find('[data-test-checkbox="free-signup-notifications"]')).to.not.be.checked;
|
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-started-notifications"]')).to.not.be.checked;
|
||||||
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.not.be.checked;
|
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.not.be.checked;
|
||||||
|
expect(find('[data-test-checkbox="mention-notifications"]')).to.not.be.checked;
|
||||||
|
|
||||||
await click('[data-test-label="free-signup-notifications"]');
|
await click('[data-test-label="free-signup-notifications"]');
|
||||||
await click('[data-test-label="paid-started-notifications"]');
|
await click('[data-test-label="paid-started-notifications"]');
|
||||||
await click('[data-test-label="paid-canceled-notifications"]');
|
await click('[data-test-label="paid-canceled-notifications"]');
|
||||||
|
await click('[data-test-label="mention-notifications"]');
|
||||||
await click('[data-test-save-button]');
|
await click('[data-test-save-button]');
|
||||||
|
|
||||||
await visit(`/settings/staff/${admin.slug}`);
|
await visit(`/settings/staff/${admin.slug}`);
|
||||||
|
|
||||||
expect(find('[data-test-checkbox="free-signup-notifications"]')).to.be.checked;
|
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-started-notifications"]')).to.be.checked;
|
||||||
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.be.checked;
|
expect(find('[data-test-checkbox="paid-canceled-notifications"]')).to.be.checked;
|
||||||
|
expect(find('[data-test-checkbox="mention-notifications"]')).to.be.checked;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -505,6 +505,8 @@ User = ghostBookshelf.Model.extend({
|
||||||
filter += '+paid_subscription_started_notification:true';
|
filter += '+paid_subscription_started_notification:true';
|
||||||
} else if (type === 'paid-canceled') {
|
} else if (type === 'paid-canceled') {
|
||||||
filter += '+paid_subscription_canceled_notification:true';
|
filter += '+paid_subscription_canceled_notification:true';
|
||||||
|
} else if (type === 'mention-received') {
|
||||||
|
filter += '+mention_notifications:true';
|
||||||
}
|
}
|
||||||
const updatedOptions = _.merge({}, options, {filter, withRelated: ['roles']});
|
const updatedOptions = _.merge({}, options, {filter, withRelated: ['roles']});
|
||||||
return this.findAll(updatedOptions).then((users) => {
|
return this.findAll(updatedOptions).then((users) => {
|
||||||
|
|
|
@ -35,7 +35,6 @@ const ALPHA_FEATURES = [
|
||||||
'urlCache',
|
'urlCache',
|
||||||
'beforeAfterCard',
|
'beforeAfterCard',
|
||||||
'lexicalEditor',
|
'lexicalEditor',
|
||||||
'webmentionEmail',
|
|
||||||
'outboundLinkTagging',
|
'outboundLinkTagging',
|
||||||
'milestoneEmails'
|
'milestoneEmails'
|
||||||
];
|
];
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe('Webmentions (receiving)', function () {
|
||||||
agent = await agentProvider.getWebmentionsAPIAgent();
|
agent = await agentProvider.getWebmentionsAPIAgent();
|
||||||
await fixtureManager.init('posts');
|
await fixtureManager.init('posts');
|
||||||
nock.disableNetConnect();
|
nock.disableNetConnect();
|
||||||
mockManager.mockLabsEnabled('webmentionEmail');
|
mockManager.mockLabsEnabled('webmentions');
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function () {
|
after(function () {
|
||||||
|
@ -136,7 +136,7 @@ describe('Webmentions (receiving)', function () {
|
||||||
await processWebmentionJob;
|
await processWebmentionJob;
|
||||||
await DomainEvents.allSettled();
|
await DomainEvents.allSettled();
|
||||||
|
|
||||||
const users = await models.User.findAll();
|
const users = await models.User.getEmailAlertUsers('mention-received');
|
||||||
users.forEach(async (user) => {
|
users.forEach(async (user) => {
|
||||||
await mockManager.assert.sentEmail({
|
await mockManager.assert.sentEmail({
|
||||||
subject: 'You\'ve been mentioned!',
|
subject: 'You\'ve been mentioned!',
|
||||||
|
@ -147,7 +147,7 @@ describe('Webmentions (receiving)', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not send notification with flag disabled', async function () {
|
it('does not send notification with flag disabled', async function () {
|
||||||
mockManager.mockLabsDisabled('webmentionEmail');
|
mockManager.mockLabsDisabled('webmentions');
|
||||||
const processWebmentionJob = jobsService.awaitCompletion('processWebmention');
|
const processWebmentionJob = jobsService.awaitCompletion('processWebmention');
|
||||||
const targetUrl = new URL('integrations/', urlUtils.getSiteUrl());
|
const targetUrl = new URL('integrations/', urlUtils.getSiteUrl());
|
||||||
const sourceUrl = new URL('http://testpage.com/external-article-123-email-test/');
|
const sourceUrl = new URL('http://testpage.com/external-article-123-email-test/');
|
||||||
|
|
|
@ -143,9 +143,24 @@ class StaffServiceEmails {
|
||||||
}
|
}
|
||||||
|
|
||||||
async notifyMentionReceived({mention}) {
|
async notifyMentionReceived({mention}) {
|
||||||
const users = await this.models.User.findAll(); // sending to all staff users for now
|
const users = await this.models.User.getEmailAlertUsers('mention-received');
|
||||||
|
let resource = null;
|
||||||
|
if (mention.resourceId) {
|
||||||
|
try {
|
||||||
|
const postModel = await this.models.Post.findOne({id: mention.resourceId.toString()});
|
||||||
|
if (postModel) {
|
||||||
|
resource = {
|
||||||
|
id: postModel.id,
|
||||||
|
name: postModel.get('title'),
|
||||||
|
type: 'post'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.logging.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
const to = user.toJSON().email;
|
const to = user.email;
|
||||||
const subject = `💌 New mention from: ${mention.sourceSiteTitle}`;
|
const subject = `💌 New mention from: ${mention.sourceSiteTitle}`;
|
||||||
|
|
||||||
const templateData = {
|
const templateData = {
|
||||||
|
@ -155,13 +170,14 @@ class StaffServiceEmails {
|
||||||
sourceSiteTitle: mention.sourceSiteTitle,
|
sourceSiteTitle: mention.sourceSiteTitle,
|
||||||
sourceFavicon: mention.sourceFavicon,
|
sourceFavicon: mention.sourceFavicon,
|
||||||
sourceAuthor: mention.sourceAuthor,
|
sourceAuthor: mention.sourceAuthor,
|
||||||
|
resource,
|
||||||
siteTitle: this.settingsCache.get('title'),
|
siteTitle: this.settingsCache.get('title'),
|
||||||
siteUrl: this.urlUtils.getSiteUrl(),
|
siteUrl: this.urlUtils.getSiteUrl(),
|
||||||
siteDomain: this.siteDomain,
|
siteDomain: this.siteDomain,
|
||||||
accentColor: this.settingsCache.get('accent_color'),
|
accentColor: this.settingsCache.get('accent_color'),
|
||||||
fromEmail: this.fromEmailAddress,
|
fromEmail: this.fromEmailAddress,
|
||||||
toEmail: to,
|
toEmail: to,
|
||||||
staffUrl: this.urlUtils.urlJoin(this.urlUtils.urlFor('admin', true), '#', `/settings/staff/${user.toJSON().slug}`)
|
staffUrl: this.urlUtils.urlJoin(this.urlUtils.urlFor('admin', true), '#', `/settings/staff/${user.slug}`)
|
||||||
};
|
};
|
||||||
const {html, text} = await this.renderEmailTemplate('new-mention-received', templateData);
|
const {html, text} = await this.renderEmailTemplate('new-mention-received', templateData);
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ class StaffService {
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
async handleEvent(type, event) {
|
async handleEvent(type, event) {
|
||||||
if (type === MentionCreatedEvent && event.data.mention && this.labs.isSet('webmentionEmail')) {
|
if (type === MentionCreatedEvent && event.data.mention && this.labs.isSet('webmentions')) {
|
||||||
await this.emails.notifyMentionReceived(event.data);
|
await this.emails.notifyMentionReceived(event.data);
|
||||||
}
|
}
|
||||||
if (!['api', 'member'].includes(event.data.source)) {
|
if (!['api', 'member'].includes(event.data.source)) {
|
||||||
|
|
|
@ -269,7 +269,7 @@ describe('StaffService', function () {
|
||||||
urlUtils,
|
urlUtils,
|
||||||
settingsHelpers,
|
settingsHelpers,
|
||||||
labs: {
|
labs: {
|
||||||
isSet: () => 'webmentionEmail'
|
isSet: () => 'webmentions'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue