diff --git a/packages/component-manuscript-manager/src/notifications/emailCopy.js b/packages/component-manuscript-manager/src/notifications/emailCopy.js index 67bfe04a10871271dec8db264da90e0b619fb7b0..32d6890b75be44875109fc4db222f9f89d3c3608 100644 --- a/packages/component-manuscript-manager/src/notifications/emailCopy.js +++ b/packages/component-manuscript-manager/src/notifications/emailCopy.js @@ -13,7 +13,7 @@ const getEmailCopy = ({ eicName = 'Editor in Chief', expectedDate = new Date(), }) => { - let paragraph + let upperContent, manuscriptText, subText, lowerContent, paragraph let hasLink = true let hasIntro = true let hasSignature = true @@ -155,11 +155,31 @@ const getEmailCopy = ({ ${comments}<br/><br/> For more information about what is required, please click the link below.<br/><br/>` break + case 'reviewer-invitation': + upperContent = `A new version of ${titleText}, has been submitted to ${journalName} for consideration.<div> </div> + As you reviewed the previous version of this manuscript, I would be delighted if you would agree to review the new version and let me know whether you feel it is suitable for publication.` + subText = `The manuscript's abstract and author information is below to help you decide. Once you have agreed to review, you will be able to download the full article PDF.` + lowerContent = `If a potential conflict of interest exists between yourself and either the authors or + the subject of the manuscript, please decline to handle the manuscript. If a conflict + becomes apparent during the review process, please let me know at the earliest possible + opportunity. For more information about our conflicts of interest policies, please + see: + <a style="color:#007e92; text-decoration: none;" href="https://www.hindawi.com/ethics/#coi">https://www.hindawi.com/ethics/#coi</a>.` + break default: throw new Error(`The ${emailType} email type is not defined.`) } - return { paragraph, hasLink, hasIntro, hasSignature } + return { + paragraph, + hasLink, + hasIntro, + hasSignature, + upperContent, + subText, + lowerContent, + manuscriptText, + } } module.exports = { diff --git a/packages/component-manuscript-manager/src/notifications/notification.js b/packages/component-manuscript-manager/src/notifications/notification.js index c80f5c29a2efa52a545033d57ca6929d5365ccd5..e5a56becba2b801e06df091a44ee644437bed079 100644 --- a/packages/component-manuscript-manager/src/notifications/notification.js +++ b/packages/component-manuscript-manager/src/notifications/notification.js @@ -1,5 +1,5 @@ const config = require('config') -const { get, isEmpty, chain } = require('lodash') +const { get, isEmpty, chain, find } = require('lodash') const Email = require('@pubsweet/component-email-templating') const { User, @@ -13,6 +13,7 @@ const { getEmailCopy } = require('./emailCopy') const { name: journalName, staffEmail } = config.get('journal') const unsubscribeSlug = config.get('unsubscribe.url') +const inviteReviewerPath = config.get('invite-reviewer.url') class Notification { constructor({ @@ -718,50 +719,87 @@ class Notification { }) } - async notifyReviewersWhenAuthorSubmitsMajorRevision(newFragmentId) { + async notifyReviewersWhenAuthorSubmitsMajorRevision(newFragment, baseUrl) { const { fragmentHelper } = await this._getNotificationProperties() const { collection, UserModel } = this const handlingEditor = get(collection, 'handlingEditor') - const parsedFragment = await fragmentHelper.getFragmentData({ + const { title, abstract } = await fragmentHelper.getFragmentData({ handlingEditor, }) + const { + activeAuthors: authors, + submittingAuthor, + } = await fragmentHelper.getAuthorData({ + UserModel, + }) + const reviewers = await fragmentHelper.getReviewers({ UserModel, type: 'submitted', }) - const { paragraph, ...bodyProps } = getEmailCopy({ - emailType: 'submitted-reviewers-after-revision', - titleText: `the manuscript titled "${parsedFragment.title}"`, - expectedDate: services.getExpectedDate({ daysExpected: 14 }), - }) + const authorsList = authors + .map(author => `${author.firstName} ${author.lastName}`) + .join(', ') + const authorName = `${submittingAuthor.firstName} ${ + submittingAuthor.lastName + }` + + const detailsPath = `/projects/${collection.id}/versions/${ + newFragment.id + }/details` + + newFragment.invitations.forEach(invitation => { + const reviewer = find(reviewers, ['id', invitation.userId]) + + const declineLink = services.createUrl(baseUrl, inviteReviewerPath, { + invitationId: invitation.id, + agree: false, + fragmentId: newFragment.id, + collectionId: collection.id, + invitationToken: reviewer.accessTokens.invitation, + }) + + const agreeLink = services.createUrl(baseUrl, detailsPath, { + invitationId: invitation.id, + agree: true, + }) + + const { paragraph, ...bodyProps } = getEmailCopy({ + emailType: 'reviewer-invitation', + titleText: `the manuscript titled <strong>"${title}"</strong> by <strong>${authorName}</strong> et al.`, + expectedDate: services.getExpectedDate({ daysExpected: 14 }), + }) - reviewers.forEach(reviewer => { const email = new Email({ type: 'user', + templateType: 'invitation', fromEmail: `${handlingEditor.name} <${staffEmail}>`, toUser: { email: reviewer.email, name: `${reviewer.lastName}`, }, content: { - subject: `${ - collection.customId - }: A manuscript you reviewed has been revised`, - paragraph, - signatureName: handlingEditor.name, - signatureJournal: journalName, + subject: `${collection.customId}: Review invitation: New Version`, ctaLink: services.createUrl( this.baseUrl, - `/projects/${collection.id}/versions/${newFragmentId}/details`, + `/projects/${collection.id}/versions/${newFragment.id}/details`, ), ctaText: 'MANUSCRIPT DETAILS', + title, + paragraph, + authorsList, + signatureName: handlingEditor.name, unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: reviewer.id, token: reviewer.accessTokens.unsubscribe, }), + signatureJournal: journalName, + agreeLink, + declineLink, + abstract, }, bodyProps, }) diff --git a/packages/component-manuscript-manager/src/routes/fragments/patch.js b/packages/component-manuscript-manager/src/routes/fragments/patch.js index 4f975c391c34232517734c2c3e920ad4bfefe7ca..e152f3796922967cb6314a7325061fedfa58a058 100644 --- a/packages/component-manuscript-manager/src/routes/fragments/patch.js +++ b/packages/component-manuscript-manager/src/routes/fragments/patch.js @@ -60,9 +60,12 @@ module.exports = models => async (req, res) => { baseUrl: services.getBaseUrl(req), }) + const baseUrl = services.getBaseUrl(req) + try { const newFragment = await strategies[role].execute({ models, + baseUrl, userHelper, notification, fragmentHelper, diff --git a/packages/component-manuscript-manager/src/routes/fragments/strategies/heRequestRevision.js b/packages/component-manuscript-manager/src/routes/fragments/strategies/heRequestRevision.js index 2a20da7ba12d003e43ddd97e7e0e814bc1ed225e..3dc103235e488f92aebf3aad995e1a46dc6de196 100644 --- a/packages/component-manuscript-manager/src/routes/fragments/strategies/heRequestRevision.js +++ b/packages/component-manuscript-manager/src/routes/fragments/strategies/heRequestRevision.js @@ -1,6 +1,7 @@ module.exports = { execute: async ({ models, + baseUrl, userHelper, TeamHelper, notification, @@ -30,6 +31,14 @@ module.exports = { members: reviewerIds, objectType: 'fragment', }) + newFragment.invitations = newFragment.invitations.map(invitation => ({ + ...invitation, + hasAnswer: false, + invitedOn: Date.now(), + isAccepted: false, + respondedOn: null, + })) + await newFragment.save() } else { delete newFragment.invitations await newFragment.save() @@ -66,7 +75,8 @@ module.exports = { if (heRequestToRevision.recommendation === 'major') { await notification.notifyReviewersWhenAuthorSubmitsMajorRevision( - newFragment.id, + newFragment, + baseUrl, ) }