diff --git a/plugins/talk-plugin-profile-data/server/connect.js b/plugins/talk-plugin-profile-data/server/connect.js index eab76828f..5bb59c125 100644 --- a/plugins/talk-plugin-profile-data/server/connect.js +++ b/plugins/talk-plugin-profile-data/server/connect.js @@ -1,10 +1,12 @@ const path = require('path'); const moment = require('moment'); const { CronJob } = require('cron'); +const { get } = require('lodash'); +const { ErrMissingEmail } = require('errors'); module.exports = connectors => { const { - services: { Mailer }, + services: { Mailer, I18n }, models: { User }, graph: { Context }, } = connectors; @@ -28,6 +30,13 @@ module.exports = connectors => { // Create the context we'll use to perform user deletions. const ctx = Context.forSystem(); + // Grab some settings. + const { loaders: { Settings } } = ctx; + const { + organizationName, + organizationContactEmail, + } = await Settings.load(['organizationName', 'organizationContactEmail']); + // rescheduledDeletionDate is the date in the future that we'll set the // user's account to be deleted on if this delete fails. const rescheduledDeletionDate = moment() @@ -61,6 +70,26 @@ module.exports = connectors => { break; } + // Get the user's email address. + const reply = await ctx.graphql( + ` + query GetUserEmailAddress($user_id: ID!) { + user(id: $user_id) { + email + } + } + `, + { user_id: user.id } + ); + if (reply.errors) { + throw reply.errors; + } + + const email = get(reply, 'data.user.email'); + if (!email) { + throw new ErrMissingEmail(); + } + ctx.log.info( { userID: user.id, @@ -91,6 +120,20 @@ module.exports = connectors => { } ctx.log.info({ userID: user.id }, 'user was deleted successfully'); + + // Send the download link via the user's attached email account. + await Mailer.send({ + template: 'plain', + locals: { + body: I18n.t( + 'email.deleted.body', + organizationName, + organizationContactEmail + ), + }, + subject: I18n.t('email.deleted.subject'), + email, + }); } } catch (err) { ctx.log.error({ err }, 'could not handle user deletions'); diff --git a/plugins/talk-plugin-profile-data/server/mutators.js b/plugins/talk-plugin-profile-data/server/mutators.js index 82e5ede4a..dfbcd2d01 100644 --- a/plugins/talk-plugin-profile-data/server/mutators.js +++ b/plugins/talk-plugin-profile-data/server/mutators.js @@ -104,31 +104,64 @@ async function sendDownloadLink(ctx) { // requestDeletion will schedule the current user to have their account deleted // by setting the `scheduledDeletionDate` on the user // ${scheduledDeletionDelayHours} hours from now. -async function requestDeletion({ user, connectors: { models: { User } } }) { +async function requestDeletion({ + user, + loaders: { Settings }, + connectors: { models: { User }, services: { Users, I18n } }, +}) { // Ensure the user doesn't already have a deletion scheduled. if (get(user, 'metadata.scheduledDeletionDate')) { throw new ErrDeletionAlreadyScheduled(); } // Get the date in the future ${scheduledDeletionDelayHours} hours from now. - const scheduledDeletionDate = moment() - .add(scheduledDeletionDelayHours, 'hours') - .toDate(); + const scheduledDeletionDate = moment().add( + scheduledDeletionDelayHours, + 'hours' + ); // Amend the scheduledDeletionDate on the user. await User.update( { id: user.id }, - { $set: { 'metadata.scheduledDeletionDate': scheduledDeletionDate } } + { + $set: { + 'metadata.scheduledDeletionDate': scheduledDeletionDate.toDate(), + }, + } ); - return scheduledDeletionDate; + const { organizationName } = await Settings.load('organizationName'); + + // Send the download link via the user's attached email account. + await Users.sendEmail(user, { + template: 'plain', + locals: { + body: I18n.t( + 'email.delete.body', + organizationName, + scheduledDeletionDate.format('MMM Do YYYY, h:mm:ss a') + ), + }, + subject: I18n.t('email.delete.subject'), + }); + + return scheduledDeletionDate.toDate(); } // cancelDeletion will unset the scheduled deletion date on the user account // that is used to indicate that the user was scheduled for deletion. -async function cancelDeletion({ user, connectors: { models: { User } } }) { +async function cancelDeletion({ + user, + loaders: { Settings }, + connectors: { models: { User }, services: { I18n, Users } }, +}) { // Ensure the user has a deletion scheduled. - if (!get(user, 'metadata.scheduledDeletionDate', null)) { + const scheduledDeletionDate = get( + user, + 'metadata.scheduledDeletionDate', + null + ); + if (!scheduledDeletionDate) { throw new ErrDeletionNotScheduled(); } @@ -137,6 +170,21 @@ async function cancelDeletion({ user, connectors: { models: { User } } }) { { id: user.id }, { $unset: { 'metadata.scheduledDeletionDate': 1 } } ); + + const { organizationName } = await Settings.load('organizationName'); + + // Send the download link via the user's attached email account. + await Users.sendEmail(user, { + template: 'plain', + locals: { + body: I18n.t( + 'email.cancelDelete.body', + organizationName, + moment(scheduledDeletionDate).format('MMM Do YYYY, h:mm:ss a') + ), + }, + subject: I18n.t('email.cancelDelete.subject'), + }); } // downloadUser will return the download file url that can be used to directly diff --git a/plugins/talk-plugin-profile-data/translations.yml b/plugins/talk-plugin-profile-data/translations.yml index d1059d470..03a2c7d9d 100644 --- a/plugins/talk-plugin-profile-data/translations.yml +++ b/plugins/talk-plugin-profile-data/translations.yml @@ -14,5 +14,17 @@ en: subject: "Your comments are ready for download from {0}" download_link_ready: "Click here to download your comments from {0} as of {1}:" download_archive: "Download Archive" + delete: + subject: "Commenter Account Deletion Request" + body: "You have submitted a request to delete your commenter account on {0}. Your account will be deleted on {1}. If you change your mind, you can reactivate your account by logging in and cancelling the request." + deleted: + subject: "Commenter Account Deleted" + body: | + Your commenter account for {0} is now deleted. We’re sorry to see you go! + If you’d like to re-join the discussion in the future, you can sign up for a new account. + If you’d like to give us feedback on why you left and what we can do to make the commenting experience better, please email us at {1}. + cancelDelete: + subject: "Commenter Account Reactivated" + body: "You have cancelled your account deletion request for {0}. Your account is now reactivated." error: DOWNLOAD_TOKEN_INVALID: "Your download link is not valid."