From 4afc3ec986ff3a77232827c7825f6f70037f06ee Mon Sep 17 00:00:00 2001 From: Sebastian Mihalache <sebastian.mihalache@gmail.con> Date: Mon, 10 Sep 2018 16:37:22 +0300 Subject: [PATCH] feat(component-manuscript-manager): update reviews and recommendations emails --- .../src/services/Collection.js | 5 + .../src/services/email/Email.js | 1 + .../src/services/email/helpers.js | 3 +- .../email/templates/partials/body.hbs | 8 +- .../email/templates/partials/intro.hbs | 1 + .../email/templates/partials/signature.hbs | 1 + .../fragmentsInvitations/emails/emailCopy.js | 2 + .../notifications/emailCopy.js | 79 +-- .../notifications/helpers.js | 351 +++++++++++++ .../notifications/notifications.js | 464 +++++------------- .../routes/fragmentsRecommendations/patch.js | 2 +- .../routes/fragmentsRecommendations/post.js | 1 + 12 files changed, 532 insertions(+), 386 deletions(-) create mode 100644 packages/component-helper-service/src/services/email/templates/partials/intro.hbs create mode 100644 packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/helpers.js diff --git a/packages/component-helper-service/src/services/Collection.js b/packages/component-helper-service/src/services/Collection.js index 5007bba30..36b0e91b8 100644 --- a/packages/component-helper-service/src/services/Collection.js +++ b/packages/component-helper-service/src/services/Collection.js @@ -97,6 +97,11 @@ class Collection { }) } } + + getHELastName() { + const names = this.handlingEditor.name.split(' ') + return names[names.length - 1] + } } module.exports = Collection diff --git a/packages/component-helper-service/src/services/email/Email.js b/packages/component-helper-service/src/services/email/Email.js index 53436b212..df63bb5c4 100644 --- a/packages/component-helper-service/src/services/email/Email.js +++ b/packages/component-helper-service/src/services/email/Email.js @@ -17,6 +17,7 @@ class Email { ctaText: '', signatureName: '', unsubscribeLink: '', + signatureJournal: '', }, }) { this.type = type diff --git a/packages/component-helper-service/src/services/email/helpers.js b/packages/component-helper-service/src/services/email/helpers.js index df1cfc280..f704b8d66 100644 --- a/packages/component-helper-service/src/services/email/helpers.js +++ b/packages/component-helper-service/src/services/email/helpers.js @@ -3,8 +3,9 @@ const handlebars = require('handlebars') const getNotificationBody = ({ replacements }) => { handlePartial('header', replacements) + if (replacements.hasIntro) handlePartial('intro', replacements) handlePartial('footer', replacements) - handlePartial('signature', replacements) + if (replacements.hasSignature) handlePartial('signature', replacements) if (replacements.hasLink) handlePartial('button', replacements) handlePartial('body', replacements) diff --git a/packages/component-helper-service/src/services/email/templates/partials/body.hbs b/packages/component-helper-service/src/services/email/templates/partials/body.hbs index 99513b980..59e123063 100644 --- a/packages/component-helper-service/src/services/email/templates/partials/body.hbs +++ b/packages/component-helper-service/src/services/email/templates/partials/body.hbs @@ -2,7 +2,9 @@ <tr> <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff"> <div> - <p data-pm-slice="1 1 []">Dear Dr. {{toUserName}},</p> + {{#if hasIntro}} + {{> intro}} + {{/if}} <p> </p> <p> {{{paragraph}}} @@ -13,7 +15,9 @@ {{> button }} {{/if}} </p> - {{> signature}} + {{#if hasSignature }} + {{> signature}} + {{/if}} <p> </p> </div> </td> diff --git a/packages/component-helper-service/src/services/email/templates/partials/intro.hbs b/packages/component-helper-service/src/services/email/templates/partials/intro.hbs new file mode 100644 index 000000000..0ce780b6d --- /dev/null +++ b/packages/component-helper-service/src/services/email/templates/partials/intro.hbs @@ -0,0 +1 @@ +<p data-pm-slice="1 1 []">Dear Dr. {{toUserName}},</p> \ No newline at end of file diff --git a/packages/component-helper-service/src/services/email/templates/partials/signature.hbs b/packages/component-helper-service/src/services/email/templates/partials/signature.hbs index 34a25ed42..eec0831b8 100644 --- a/packages/component-helper-service/src/services/email/templates/partials/signature.hbs +++ b/packages/component-helper-service/src/services/email/templates/partials/signature.hbs @@ -1,3 +1,4 @@ <p>Kind regards, <br /> {{ signatureName }} + <br /> {{ signatureJournal }} </p> \ No newline at end of file diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js index 4e53dc099..940afbfad 100644 --- a/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js +++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js @@ -66,6 +66,8 @@ const getEmailCopy = ({ upperContent, lowerContent, manuscriptText, + hasIntro: true, + hasSignature: true, } } diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js index b6f8c7752..f21a00aaa 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js @@ -1,3 +1,8 @@ +const config = require('config') + +const staffEmail = config.get('journal.staffEmail') +const journalName = config.get('journal.name') + const getEmailCopy = ({ customId, emailType, @@ -8,31 +13,38 @@ const getEmailCopy = ({ }) => { let paragraph let hasLink = true + let hasIntro = true + let hasSignature = true switch (emailType) { case 'author-request-to-revision': - paragraph = `In order for ${titleText} to proceed to publication, there needs to be a revision. <br/> - ${comments}<br/> - For more information about what is required, please visit the manuscript details page.` + paragraph = `After reviewing ${titleText}, I have requested further revisions to the manuscript. <br/> + ${comments}<br/> + For more information about the requested changes and to submit your revised manuscript, please visit the manuscript details page.` break case 'author-manuscript-rejected': - paragraph = `I am sorry to inform you that ${titleText} has been rejected for publication. <br/><br/> - ${comments}<br/><br/>` + paragraph = `The peer review of ${titleText} has now been completed. I regret to inform you that your manuscript has been rejected for publication.<br/><br/> + ${comments}<br/><br/> + Thank you for your submission to ${journalName}.` hasLink = false break case 'author-manuscript-published': - paragraph = `I am delighted to inform you that ${titleText} has passed through the review process and will be published in Hindawi.<br/><br/> - Thanks again for choosing to publish with us.` - hasLink = false + paragraph = `I am delighted to inform you that the review of ${titleText} has been completed and your article will be published in ${journalName}.<br/><br/> + Please visit the manuscript details page to review the editorial notes and any comments from external reviewers.<br/><br/> + Your article will now be passed to our production team for processing, and you will hear directly from them should we require any further information.<br/><br/> + Thank you for choosing to publish with ${journalName}.` break case 'he-manuscript-rejected': hasLink = false - paragraph = `Thank you for your recommendation to reject ${titleText} based on the reviews you received.<br/><br/> - I can confirm this article has now been rejected.` + hasIntro = false + hasSignature = false + paragraph = `${targetUserName} has confirmed your decision to reject the ${titleText}.<br/><br/> + No further action is required at this time. To review this decision, please visit the manuscript details page.<br/><br/> + Thank you for handling this manuscript on behalf of ${journalName}.` break case 'he-manuscript-published': - hasLink = false - paragraph = `Thank you for your recommendation to publish ${titleText} based on the reviews you received.<br/><br/> - I can confirm this article will now go through to publication.` + paragraph = `${targetUserName} has confirmed your decision to accept ${titleText}.<br/><br/> + No further action is required at this time. To review this decision, please visit the manuscript details.<br/><br/> + Thank you for handling this manuscript on behalf of ${journalName}.` break case 'he-manuscript-return-with-comments': hasLink = false @@ -41,42 +53,49 @@ const getEmailCopy = ({ break case 'accepted-reviewers-after-recommendation': hasLink = false - paragraph = `I appreciate any time you may have spent reviewing ${titleText}. However, an editorial decision has been made and the review of this manuscript is now complete. I apologize for any inconvenience. <br/> - If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible. <br/> - Thank you for your interest and I hope you will consider reviewing for Hindawi again.` + paragraph = `I appreciate any time you may have spent reviewing ${titleText}. However, I am prepared to make an editorial decision and your review is no longer required at this time. I apologize for any inconvenience. <br/><br/> + If you have comments on this manuscript you believe I should see, please email them to ${staffEmail} as soon as possible. <br/><br/> + Thank you for your interest and I hope you will consider reviewing for ${journalName} again.` break case 'pending-reviewers-after-recommendation': hasLink = false - paragraph = `An editorial decision has been made regarding ${titleText}. So, you do not need to proceed with the review of this manuscript. <br/><br/> - If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible.` + paragraph = `I appreciate any time you may have spent reviewing ${titleText} for ${journalName}. However, I am prepared to make an editorial decision and your review is no longer required at this time. I apologize for any inconvenience. <br/><br/> + If you have comments on this manuscript you believe I should see, please email them to ${staffEmail} as soon as possible. <br/><br/> + Thank you for your interest and I hope you will consider reviewing for ${journalName} again.` break case 'submitted-reviewers-after-publish': hasLink = false - paragraph = `Thank you for your review on ${titleText}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article will now be published.<br/><br/> + paragraph = `Thank you for your review of ${titleText}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article will now be published.<br/><br/> If you have any queries about this decision, then please email them to Hindawi as soon as possible.` break case 'submitted-reviewers-after-reject': - hasLink = false - paragraph = `Thank you for your review on ${titleText}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article has now been rejected.<br/><br/> - If you have any queries about this decision, then please email them to Hindawi as soon as possible.` + paragraph = `Thank you for your review of ${titleText} for ${journalName}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article has now been rejected.<br/><br/> + No further action is required at this time. To see more details about this decision please view the manuscript details page.<br/><br/> + If you have any questions about this decision, please email them to ${staffEmail} as soon as possible. Thank you for reviewing for ${journalName}.` break case 'he-review-submitted': paragraph = `We are pleased to inform you that Dr. ${targetUserName} has submitted a review for ${titleText}.<br/> - Please visit the manuscript details page to see the full review.` + To see the full report, please visit the manuscript details page.` break case 'eic-recommend-to-publish-from-he': - paragraph = `It is my recommendation, based on the reviews I have received for ${titleText} that we should proceed to publication.<br/> + hasIntro = false + hasSignature = false + paragraph = `Dr. ${targetUserName} has recommended accepting ${titleText} for publication.<br/> ${comments}<br/> - For more information, and to see the full review, please visit the manuscript details page.` + To review this decision, please visit the manuscript details page.` break case 'eic-recommend-to-reject-from-he': - paragraph = `It is my recommendation, based on the reviews I have received for ${titleText} that we should reject it for publication.<br/> + hasIntro = false + hasSignature = false + paragraph = `Dr. ${targetUserName} has recommended rejecting ${titleText}.<br/> ${comments}<br/> - For more information, and to see the full review, please visit the manuscript details page.` + To review this decision, please visit the manuscript details page.` break case 'eic-request-revision-from-he': - paragraph = `In order for ${titleText} to proceed to publication, there needs to be a revision. <br/><br/> - For more information about what is required, please visit the manuscript details page.` + hasIntro = false + hasSignature = false + paragraph = `Dr. ${targetUserName} has asked the authors to submit a revised version of ${titleText}.<br/><br/> + No action is required at this time. To see the requested changes, please visit the manuscript details page.` break case 'eqa-manuscript-request-for-approval': paragraph = `Manuscript ID ${customId} has passed peer-review and is now ready for EQA. Please click on the link below to either approve or return the manuscript to the Editor in Chief:` @@ -90,7 +109,7 @@ const getEmailCopy = ({ throw new Error(`The ${emailType} email type is not defined.`) } - return { paragraph, hasLink } + return { paragraph, hasLink, hasIntro, hasSignature } } module.exports = { diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/helpers.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/helpers.js new file mode 100644 index 000000000..5c85e7675 --- /dev/null +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/helpers.js @@ -0,0 +1,351 @@ +const config = require('config') +const { get, chain } = require('lodash') + +const { services } = require('pubsweet-component-helper-service') +const { getEmailCopy } = require('./emailCopy') + +const unsubscribeSlug = config.get('unsubscribe.url') + +module.exports = { + sendReviewersEmail: async ({ email, baseUrl, reviewers }) => { + reviewers.forEach(reviewer => { + email.toUser = { + email: reviewer.email, + name: `${reviewer.lastName}`, + } + email.content.unsubscribeLink = services.createUrl( + baseUrl, + unsubscribeSlug, + { + id: reviewer.id, + }, + ) + const { html, text } = email.getBody({ + body: { paragraph: reviewer.paragraph }, + }) + email.sendEmail({ html, text }) + }) + }, + getSubmittedReviewers: async ({ + UserModel, + titleText, + fragmentHelper, + recommendation, + }) => { + const emailType = + recommendation === 'publish' + ? 'submitted-reviewers-after-publish' + : 'submitted-reviewers-after-reject' + + const reviewers = (await fragmentHelper.getReviewers({ + UserModel, + type: 'submitted', + })).map(rev => ({ + ...rev, + emailType, + ...getEmailCopy({ + emailType, + titleText, + }), + })) + + return reviewers + }, + getNoResponseReviewers: async ({ fragmentHelper, UserModel, titleText }) => { + const acceptedReviewers = (await fragmentHelper.getReviewers({ + UserModel, + type: 'accepted', + })).map(rev => ({ + ...rev, + ...getEmailCopy({ + emailType: 'accepted-reviewers-after-recommendation', + titleText, + }), + })) + + const pendingReviewers = (await fragmentHelper.getReviewers({ + UserModel, + type: 'pending', + })).map(rev => ({ + ...rev, + ...getEmailCopy({ + emailType: 'pending-reviewers-after-recommendation', + titleText, + }), + })) + + const reviewers = [...acceptedReviewers, ...pendingReviewers] + return reviewers + }, + updateEmailContentForReviewers: ({ + email, + subject, + customId, + signatureName, + }) => { + email.content.subject = `${customId}: ${subject}` + email.content.signatureName = signatureName + return email + }, + sendHandlingEditorEmail: ({ + email, + comments, + emailType, + titleText, + targetUserName, + }) => { + const { html, text } = email.getBody({ + body: getEmailCopy({ + emailType, + titleText, + comments, + targetUserName, + }), + }) + email.sendEmail({ html, text }) + }, + updateEmailContentForHE: ({ + email, + baseUrl, + eicName, + customId, + heLastName, + handlingEditor, + recommendationType, + }) => { + if (recommendationType === 'review') { + email.content.subject = `${customId}: A review has been submitted` + } else { + email.content.subject = `${customId}: Editorial decision confirmed` + } + + email.toUser = { + email: handlingEditor.email, + name: heLastName, + } + email.content.unsubscribeLink = services.createUrl( + baseUrl, + unsubscribeSlug, + { + id: handlingEditor.id, + }, + ) + + email.content.signatureName = eicName + delete email.content.signatureJournal + + return email + }, + getEmailTypeByRecommendationForHE: ({ + recommendation, + recommendationType, + }) => { + let emailType + if (recommendationType === 'review') { + emailType = 'he-review-submitted' + } else { + switch (recommendation) { + case 'return-to-handling-editor': + emailType = 'he-manuscript-return-with-comments' + break + case 'publish': + emailType = 'he-manuscript-published' + break + case 'reject': + emailType = 'he-manuscript-rejected' + break + default: + throw new Error(`undefined recommendation: ${recommendation} `) + } + } + + return emailType + }, + getEiCCommentsForHE: ({ newRecommendation }) => { + const eicComments = chain(newRecommendation) + .get('comments') + .find(comm => !comm.public) + .get('content') + .value() + + return eicComments + }, + sendEiCsEmail: async ({ + email, + customId, + titleText, + userHelper, + targetUserName, + recommendation: { recommendation, comments: recComments = [] }, + }) => { + let emailType + + switch (recommendation) { + case 'minor': + case 'major': + emailType = 'eic-request-revision-from-he' + email.content.subject = `${customId}: Revision requested` + break + case 'publish': + emailType = 'eic-recommend-to-publish-from-he' + email.content.subject = `${customId}: Recommendation to publish` + break + case 'reject': + emailType = 'eic-recommend-to-reject-from-he' + email.content.subject = `${customId}: Recommendation to reject` + break + default: + throw new Error(`undefined recommendation: ${recommendation} `) + } + delete email.content.signatureJournal + + const privateNote = recComments.find(comm => !comm.public) + const content = get(privateNote, 'content') + const comments = content + ? `The editor provided the following comments: "${content}"` + : '' + + const editors = (await userHelper.getEditorsInChief()).map(eic => ({ + ...eic, + ...getEmailCopy({ + comments, + emailType, + titleText, + targetUserName, + }), + })) + + editors.forEach(eic => { + email.toUser = { + email: eic.email, + name: `${eic.firstName} ${eic.lastName}`, + } + const { html, text } = email.getBody({ + body: { paragraph: eic.paragraph }, + }) + email.sendEmail({ html, text }) + }) + }, + sendAuthorsEmail: async ({ email, authors, baseUrl }) => { + authors.forEach(author => { + email.toUser = { + email: author.email, + name: `${author.lastName}`, + } + email.content.unsubscribeLink = services.createUrl( + baseUrl, + unsubscribeSlug, + { + id: author.id, + }, + ) + const { html, text } = email.getBody({ + body: { + paragraph: author.paragraph, + }, + }) + email.sendEmail({ html, text }) + }) + }, + getSubmittingAuthor: ({ + title, + journalName, + authorNoteText, + submittingAuthor, + }) => { + const author = { + ...submittingAuthor, + ...getEmailCopy({ + emailType: 'author-request-to-revision', + titleText: `your submission "${title}" to ${journalName}`, + comments: authorNoteText, + }), + } + + return author + }, + getPrivateNoteTextForAuthor: ({ newRecommendation }) => { + const authorNote = newRecommendation.comments.find(comm => comm.public) + const content = get(authorNote, 'content') + const authorNoteText = content ? `Reason & Details: "${content}"` : '' + + return authorNoteText + }, + updateEmailContentForSA: ({ email, customId, signatureName }) => { + email.content.subject = `${customId}: Revision requested` + email.content.signatureName = signatureName + }, + getHEComments: ({ heRecommendation }) => { + const heComments = get(heRecommendation, 'comments', []) + if (heComments.length === 0) return + + const publicComment = heComments.find(comm => comm.public) + const content = get(publicComment, 'content') + if (!content) { + throw new Error('a public comment cannot be without content') + } + const comments = `Please find our editorial comments below<br/>: "${content}"` + + return comments + }, + getAllAuthors: ({ title, comments, emailType, fragmentAuthors }) => { + const authors = fragmentAuthors.map(author => ({ + ...author, + ...getEmailCopy({ + comments, + emailType, + titleText: `your manuscript titled "${title}"`, + }), + })) + + return authors + }, + sendSubmittingAuthorEmail: ({ email, author, baseUrl }) => { + email.toUser = { + email: author.email, + name: `${author.lastName}`, + } + email.content.unsubscribeLink = services.createUrl( + baseUrl, + unsubscribeSlug, + { + id: author.id, + }, + ) + const { html, text } = email.getBody({ + body: { + paragraph: author.paragraph, + }, + }) + email.sendEmail({ html, text }) + }, + updateEmailContentForAllAuthors: ({ email, recommendation, customId }) => { + switch (recommendation) { + case 'publish': + email.content.subject = `${customId}: Manuscript accepted` + break + case 'reject': + email.content.subject = `${customId}: Manuscript rejected` + break + default: + throw new Error(`Undefined recommendation: ${recommendation}`) + } + + return email + }, + getEmailTypeByRecommendationForAuthors: ({ recommendation }) => { + let emailType + switch (recommendation) { + case 'publish': + emailType = 'author-manuscript-published' + break + case 'reject': + emailType = 'author-manuscript-rejected' + break + default: + throw new Error(`Undefined recommendation: ${recommendation}`) + } + + return emailType + }, +} diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js index a84b5541b..bfcca7309 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js @@ -6,12 +6,15 @@ const { Email, services, Fragment, + Collection, } = require('pubsweet-component-helper-service') const { getEmailCopy } = require('./emailCopy') +const helpers = require('./helpers') const editorialAssistantEmail = config.get('mailer.editorialAssistant') const unsubscribeSlug = config.get('unsubscribe.url') +const journalName = config.get('journal.name') module.exports = { async sendNotifications({ @@ -38,12 +41,13 @@ module.exports = { const userHelper = new User({ UserModel }) const eicName = await userHelper.getEiCName() - const email = new Email({ + let email = new Email({ type: 'user', content: { + signatureName: eicName, unsubscribeLink: baseUrl, ctaText: 'MANUSCRIPT DETAILS', - signatureName: eicName, + signatureJournal: journalName, ctaLink: services.createUrl( baseUrl, `/projects/${collection.id}/versions/${fragment.id}/details`, @@ -51,9 +55,11 @@ module.exports = { }, }) + const { recommendation, recommendationType } = newRecommendation + // the EiC recommends to publish so an email to the EQA needs to be sent, // one requesting approval or one informing them that the manuscript has been published - if (isEditorInChief && newRecommendation.recommendation === 'publish') { + if (isEditorInChief && recommendation === 'publish') { sendEQAEmail({ email, eicName, @@ -64,81 +70,130 @@ module.exports = { }) } - let comments - if (isEditorInChief) { - const eicComments = chain(newRecommendation) - .get('comments') - .find(comm => !comm.public) - .get('content') - .value() - - comments = eicComments - } - const hasPeerReview = (collection = {}) => !isEmpty(collection.handlingEditor) + const { customId } = collection + const collHelper = new Collection({ collection }) if ( - (isEditorInChief || newRecommendation.recommendationType === 'review') && + (isEditorInChief || recommendationType === 'review') && hasPeerReview(collection) ) { - // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified - sendHandlingEditorEmail({ - email, - hasEQA, - baseUrl, - eicName, - comments, - titleText, - targetUserName, - subjectBaseText, - handlingEditor: get(collection, 'handlingEditor', {}), - recommendation: newRecommendation.recommendation, - recommendationType: newRecommendation.recommendationType, - }) + if (recommendation !== 'publish' || hasEQA) { + // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified + email = helpers.updateEmailContentForHE({ + email, + baseUrl, + eicName, + customId, + recommendationType, + heLastName: collHelper.getHELastName(), + handlingEditor: get(collection, 'handlingEditor', {}), + }) + const emailType = helpers.getEmailTypeByRecommendationForHE({ + recommendation, + recommendationType, + }) + const comments = helpers.getEiCCommentsForHE({ newRecommendation }) + helpers.sendHandlingEditorEmail({ + email, + comments, + emailType, + titleText, + targetUserName, + }) + } } if ( - newRecommendation.recommendationType !== 'review' && - newRecommendation.recommendation !== 'return-to-handling-editor' + recommendationType !== 'review' && + recommendation !== 'return-to-handling-editor' ) { - if (isEditorInChief || collection.status === 'revisionRequested') { - sendAuthorsEmail({ - email, - hasEQA, - baseUrl, - titleText, - parsedFragment, - fragmentAuthors, - isEditorInChief, - subjectBaseText, + if (isEditorInChief) { + if (recommendation !== 'publish' || hasEQA) { + // send all authors email + const emailType = helpers.getEmailTypeByRecommendationForAuthors({ + recommendation, + }) + const comments = helpers.getHEComments({ + heRecommendation: parsedFragment.heRecommendation, + }) + const authors = helpers.getAllAuthors({ + comments, + emailType, + title: parsedFragment.title, + fragmentAuthors: fragmentAuthors.activeAuthors, + }) + email = helpers.updateEmailContentForAllAuthors({ + email, + customId, + recommendation, + }) + helpers.sendAuthorsEmail({ email, authors, baseUrl }) + } + } + + if (collection.status === 'revisionRequested') { + // send SA email + const authorNoteText = helpers.getPrivateNoteTextForAuthor({ newRecommendation, - handlingEditorName: get(collection, 'handlingEditor.name', eicName), }) + const author = helpers.getSubmittingAuthor({ + authorNoteText, + journalName, + title: parsedFragment.title, + submittingAuthor: fragmentAuthors.submittingAuthor, + }) + email = helpers.updateEmailContentForSA({ + email, + customId, + signatureName: get(collection, 'handlingEditor.name', eicName), + }) + helpers.sendSubmittingAuthorEmail({ email, author, baseUrl }) } if (hasPeerReview(collection)) { - sendReviewersEmail({ - email, - hasEQA, - baseUrl, - UserModel, - titleText, - fragmentHelper, - isEditorInChief, - subjectBaseText, - recommendation: newRecommendation.recommendation, - handlingEditorName: get(collection, 'handlingEditor.name', eicName), - }) + let reviewers + if (isEditorInChief) { + if (recommendation !== 'publish' || hasEQA) { + email = helpers.updateEmailContentForReviewers({ + email, + customId, + signatureName: eicName, + subject: `A manuscript you reviewed has been rejected`, + }) + reviewers = helpers.getSubmittedReviewers({ + UserModel, + titleText, + fragmentHelper, + recommendation, + }) + } + } else { + email = helpers.updateEmailContentForReviewers({ + email, + customId, + subject: `Review no longer required`, + signatureName: get(collection, 'handlingEditor.name', eicName), + }) + + reviewers = helpers.getNoResponseReviewers({ + UserModel, + titleText, + fragmentHelper, + }) + } + helpers.sendReviewersEmail({ email, baseUrl, reviewers }) if (!isEditorInChief) { - sendEiCsEmail({ + helpers.sendEiCsEmail({ email, baseUrl, - userHelper, + customId, titleText, - subjectBaseText, + userHelper, recommendation: newRecommendation, + targetUserName: collHelper.getHELastName(), }) } } @@ -146,296 +201,6 @@ module.exports = { }, } -const sendHandlingEditorEmail = ({ - email, - hasEQA, - eicName, - baseUrl, - comments, - titleText, - handlingEditor, - targetUserName, - recommendation, - subjectBaseText, - recommendationType, -}) => { - let emailType - if (recommendationType === 'review') { - email.content.subject = `${subjectBaseText} Review` - emailType = 'he-review-submitted' - } else { - email.content.subject = `${subjectBaseText} Decision` - - if (recommendation === 'publish' && !hasEQA) { - // the EiC recommended to publish but the manuscript has not passed through the EQA process - // so the HE should not receive any email - return - } - - switch (recommendation) { - case 'return-to-handling-editor': - emailType = 'he-manuscript-return-with-comments' - break - case 'publish': - emailType = 'he-manuscript-published' - break - case 'reject': - emailType = 'he-manuscript-rejected' - break - default: - throw new Error(`undefined recommendation: ${recommendation} `) - } - } - - email.toUser = { - email: handlingEditor.email, - name: handlingEditor.name, - } - email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, { - id: handlingEditor.id, - }) - - email.content.signatureName = eicName - - const { html, text } = email.getBody({ - body: getEmailCopy({ - emailType, - titleText, - comments, - targetUserName, - }), - }) - email.sendEmail({ html, text }) -} - -const sendAuthorsEmail = async ({ - email, - hasEQA, - baseUrl, - titleText, - isEditorInChief, - subjectBaseText, - fragmentAuthors, - newRecommendation, - handlingEditorName, - parsedFragment: { heRecommendation }, -}) => { - let emailType, authors, comments - - if (isEditorInChief) { - const heComments = get(heRecommendation, 'comments', []) - if (heComments.length) { - const publicComment = heComments.find(comm => comm.public) - const content = get(publicComment, 'content') - if (!content) { - throw new Error('a public comment cannot be without content') - } - comments = `Reason & Details: "${content}"` - } - - if (newRecommendation.recommendation === 'publish') { - if (!hasEQA) { - // the manuscript did not pass through the EQA process - return - } - - emailType = 'author-manuscript-published' - email.content.subject = `${subjectBaseText} Published` - } else { - emailType = 'author-manuscript-rejected' - email.content.subject = `${subjectBaseText} Decision` - } - - authors = fragmentAuthors.activeAuthors.map(author => ({ - ...author, - ...getEmailCopy({ - emailType, - titleText, - comments, - }), - })) - } else { - emailType = 'author-request-to-revision' - - email.content.subject = `${subjectBaseText} Recommendation` - email.content.signatureName = handlingEditorName - - const authorNote = newRecommendation.comments.find(comm => comm.public) - const content = get(authorNote, 'content') - const authorNoteText = content ? `Reason & Details: "${content}"` : '' - authors = [ - { - ...fragmentAuthors.submittingAuthor, - ...getEmailCopy({ - emailType, - titleText, - comments: authorNoteText, - }), - }, - ] - } - - authors.forEach(author => { - email.toUser = { - email: author.email, - name: `${author.firstName} ${author.lastName}`, - } - email.content.unsubscribeLink = services.createUrl( - baseUrl, - unsubscribeSlug, - { - id: author.id, - }, - ) - const { html, text } = email.getBody({ - body: { - paragraph: author.paragraph, - hasLink: author.hasLink, - }, - }) - email.sendEmail({ html, text }) - }) -} - -const sendReviewersEmail = async ({ - email, - baseUrl, - hasEQA, - eicName, - titleText, - UserModel, - fragmentHelper, - recommendation, - isEditorInChief, - subjectBaseText, - handlingEditorName, -}) => { - let reviewers - if (isEditorInChief) { - email.content.subject = `${subjectBaseText} Decision` - const emailType = - recommendation === 'publish' - ? 'submitted-reviewers-after-publish' - : 'submitted-reviewers-after-reject' - - if (recommendation === 'publish' && !hasEQA) { - // the manuscript has not passed through the EQA process - // so the reviewers should not receive any email - return - } - - reviewers = (await fragmentHelper.getReviewers({ - UserModel, - type: 'submitted', - })).map(rev => ({ - ...rev, - emailType, - ...getEmailCopy({ - emailType, - titleText, - }), - })) - email.content.signatureName = eicName - } else { - email.content.signatureName = handlingEditorName - email.content.subject = `${subjectBaseText} ${getSubjectByRecommendation( - recommendation, - )}` - - const acceptedReviewers = (await fragmentHelper.getReviewers({ - UserModel, - type: 'accepted', - })).map(rev => ({ - ...rev, - ...getEmailCopy({ - emailType: 'accepted-reviewers-after-recommendation', - titleText, - }), - })) - - const pendingReviewers = (await fragmentHelper.getReviewers({ - UserModel, - type: 'pending', - })).map(rev => ({ - ...rev, - ...getEmailCopy({ - emailType: 'pending-reviewers-after-recommendation', - titleText, - }), - })) - - reviewers = [...acceptedReviewers, ...pendingReviewers] - } - - reviewers.forEach(reviewer => { - email.toUser = { - email: reviewer.email, - name: `${reviewer.firstName} ${reviewer.lastName}`, - } - email.content.unsubscribeLink = services.createUrl( - baseUrl, - unsubscribeSlug, - { - id: reviewer.id, - }, - ) - const { html, text } = email.getBody({ - body: { paragraph: reviewer.paragraph, hasLink: reviewer.hasLink }, - }) - email.sendEmail({ html, text }) - }) -} - -const sendEiCsEmail = async ({ - email, - titleText, - userHelper, - subjectBaseText, - recommendation: { recommendation, comments: recComments = [] }, -}) => { - let emailType - - email.content.subject = `${subjectBaseText} Recommendation` - switch (recommendation) { - case 'minor': - case 'major': - emailType = 'eic-request-revision-from-he' - break - case 'publish': - emailType = 'eic-recommend-to-publish-from-he' - break - case 'reject': - emailType = 'eic-recommend-to-reject-from-he' - break - default: - throw new Error(`undefined recommendation: ${recommendation} `) - } - - const privateNote = recComments.find(comm => !comm.public) - const content = get(privateNote, 'content') - const comments = content ? `Note to Editor: "${content}"` : '' - - const editors = (await userHelper.getEditorsInChief()).map(eic => ({ - ...eic, - ...getEmailCopy({ - emailType, - titleText, - comments, - }), - })) - - editors.forEach(eic => { - email.toUser = { - email: eic.email, - name: `${eic.firstName} ${eic.lastName}`, - } - const { html, text } = email.getBody({ - body: { paragraph: eic.paragraph, hasLink: eic.hasLink }, - }) - email.sendEmail({ html, text }) - }) -} - const sendEQAEmail = ({ email, eicName, @@ -488,8 +253,3 @@ const sendEQAEmail = ({ }) email.sendEmail({ html, text }) } - -const getSubjectByRecommendation = recommendation => - ['minor', 'major'].includes(recommendation) - ? 'Revision Requested' - : 'Recommendation Submitted' diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js index 1e0e4f242..43ea6d604 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js @@ -55,7 +55,7 @@ module.exports = models => async (req, res) => { UserModel: models.User, baseUrl: services.getBaseUrl(req), newRecommendation: recommendation, - targetUserName: `${user.firstName} ${user.lastName}`, + targetUserName: `${user.lastName}`, }) if (['underReview'].includes(collection.status)) { diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js index 4e7a82189..6b2ccae9c 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -118,6 +118,7 @@ module.exports = models => async (req, res) => { isEditorInChief, newRecommendation, UserModel: models.User, + targetUserName: reqUser.lastName, baseUrl: services.getBaseUrl(req), }) } -- GitLab