const config = require('config') const { get, isEmpty, chain } = require('lodash') const Email = require('@pubsweet/component-email-templating') const { User, services, Fragment, Collection, } = require('pubsweet-component-helper-service') const helpers = require('./helpers') const { getEmailCopy } = require('./emailCopy') const { name: journalName, staffEmail } = config.get('journal') const unsubscribeSlug = config.get('unsubscribe.url') class Notification { constructor({ baseUrl = '', fragment = {}, UserModel = {}, collection = {}, newRecommendation = {}, }) { this.baseUrl = baseUrl this.fragment = fragment this.UserModel = UserModel this.collection = collection this.newRecommendation = newRecommendation } async notifyHEWhenReviewerSubmitsReport(reviewerLastName) { const { eicName, titleText } = await this._getNotificationProperties() const handlingEditorId = get(this.collection, 'handlingEditor.id') const heUser = await this.UserModel.find(handlingEditorId) const { paragraph, ...bodyProps } = getEmailCopy({ emailType: 'he-review-submitted', titleText, targetUserName: reviewerLastName, }) const email = new Email({ type: 'user', toUser: { email: heUser.email, name: heUser.lastName, }, fromEmail: `${eicName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: A review has been submitted`, paragraph, signatureName: eicName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: heUser.id, token: heUser.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() } async notifyHEWhenEiCMakesDecision() { const { eicName, titleText, recommendation, } = await this._getNotificationProperties() const handlingEditorId = get(this.collection, 'handlingEditor.id') const heUser = await this.UserModel.find(handlingEditorId) const { paragraph, ...bodyProps } = getEmailCopy({ titleText, targetUserName: eicName, emailType: recommendation === 'publish' ? 'he-manuscript-published' : 'he-manuscript-rejected', }) const email = new Email({ type: 'user', toUser: { email: heUser.email, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: Editorial decision confirmed`, paragraph, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: heUser.id, token: heUser.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() } async notifyHEWhenEiCReturnsToHE() { const { eicName, titleText } = await this._getNotificationProperties() const handlingEditorId = get(this.collection, 'handlingEditor.id') const heUser = await this.UserModel.find(handlingEditorId) const eicComments = chain(this.newRecommendation) .get('comments') .find(comm => !comm.public) .get('content') .value() const { paragraph, ...bodyProps } = getEmailCopy({ titleText, comments: eicComments, targetUserName: eicName, emailType: 'he-manuscript-return-with-comments', }) const email = new Email({ type: 'user', toUser: { email: heUser.email, name: heUser.lastName, }, fromEmail: `${eicName} <${staffEmail}>`, content: { subject: `${ this.collection.customId }: Editorial decision returned with comments`, paragraph, signatureName: eicName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: heUser.id, token: heUser.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() } async notifyEAWhenEiCMakesFinalDecision() { const { eicName, titleText } = await this._getNotificationProperties() const subjectBaseText = `${this.collection.customId}: Manuscript` const { paragraph, ...bodyProps } = getEmailCopy({ eicName, titleText, emailType: 'eqa-manuscript-published', }) const email = new Email({ type: 'system', toUser: { email: staffEmail, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject: `${subjectBaseText} decision finalized`, paragraph, unsubscribeLink: this.baseUrl, }, bodyProps, }) return email.sendEmail() } async notifyEAWhenEiCRequestsEQAApproval() { const { eicName } = await this._getNotificationProperties() const subjectBaseText = `${this.collection.customId}: Manuscript` const { paragraph, ...bodyProps } = getEmailCopy({ eicName, customId: this.collection.customId, emailType: 'eqa-manuscript-request-for-approval', }) const email = new Email({ type: 'system', toUser: { email: staffEmail, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject: `${subjectBaseText} Request for EQA approval`, paragraph, unsubscribeLink: this.baseUrl, ctaText: 'MAKE DECISION', ctaLink: services.createUrl( this.baseUrl, config.get('eqa-decision.url'), { collectionId: this.collection.id, customId: this.collection.customId, token: this.collection.technicalChecks.token, }, ), }, bodyProps, }) return email.sendEmail() } async notifyAuthorsWhenEiCMakesDecision() { const { eicName, titleText, activeAuthors, recommendation, parsedFragment, } = await this._getNotificationProperties() const subjectOpts = { publish: `${this.collection.customId}: Manuscript accepted`, reject: `${this.collection.customId}: Manuscript rejected`, } const subject = subjectOpts[recommendation] if (isEmpty(subject)) { throw new Error(`Undefined recommendation: ${recommendation}`) } const hasPeerReview = !isEmpty(this.collection.handlingEditor) const emailType = helpers.getEmailTypeByRecommendationForAuthors({ recommendation, hasPeerReview, }) const comments = hasPeerReview ? helpers.getHEComments({ heRecommendation: parsedFragment.heRecommendation, }) : this.newRecommendation.comments[0].content const { paragraph, ...bodyProps } = getEmailCopy({ titleText, emailType, comments, }) activeAuthors.forEach(async author => { const email = new Email({ type: 'user', toUser: { email: author.email, name: author.lastName, }, fromEmail: `${eicName} <${staffEmail}>`, content: { subject, paragraph, signatureName: eicName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: author.id, token: author.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), signatureJournal: journalName, }, bodyProps, }) return email.sendEmail() }) } async notifyReviewersWhenEiCMakesDecision() { const { eicName, titleText, recommendation, fragmentHelper, } = await this._getNotificationProperties() const emailType = recommendation === 'publish' ? 'submitted-reviewers-after-publish' : 'submitted-reviewers-after-reject' const subject = recommendation === 'publish' ? 'A manuscript you reviewed has been accepted' : 'A manuscript you reviewed has been rejected' const reviewers = await fragmentHelper.getReviewers({ UserModel: this.UserModel, type: 'submitted', }) const { paragraph, ...bodyProps } = getEmailCopy({ emailType, titleText, }) reviewers.forEach(reviewer => { const email = new Email({ type: 'user', toUser: { email: reviewer.email, name: reviewer.lastName, }, fromEmail: `${eicName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: ${subject}`, paragraph, signatureName: eicName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: reviewer.id, token: reviewer.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), signatureJournal: journalName, }, bodyProps, }) return email.sendEmail() }) } async notifySAWhenHERequestsRevision() { const { eicName, submittingAuthor, parsedFragment: { title }, } = await this._getNotificationProperties() const authorNoteText = helpers.getPrivateNoteTextForAuthor({ newRecommendation: this.newRecommendation, }) const signatureName = get(this.collection, 'handlingEditor.name', eicName) const { paragraph, ...bodyProps } = getEmailCopy({ emailType: 'author-request-to-revision', titleText: `your submission "${title}" to ${journalName}`, comments: authorNoteText, }) const email = new Email({ type: 'user', toUser: { email: submittingAuthor.email, name: submittingAuthor.lastName, }, fromEmail: `${signatureName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: Revision requested`, paragraph, signatureName, ctaText: 'MANUSCRIPT DETAILS', signatureJournal: journalName, unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: submittingAuthor.id, token: submittingAuthor.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() } async notifyReviewersWhenHEMakesRecommendation() { const { eicName, titleText, fragmentHelper, } = await this._getNotificationProperties() const signatureName = get(this.collection, 'handlingEditor.name', eicName) const acceptedReviewers = await fragmentHelper.getReviewers({ UserModel: this.UserModel, type: 'accepted', }) const acceptedReviewersEmailBody = getEmailCopy({ emailType: 'accepted-reviewers-after-recommendation', titleText, }) const pendingReviewers = await fragmentHelper.getReviewers({ UserModel: this.UserModel, type: 'pending', }) const pendingReviewersEmailBody = getEmailCopy({ emailType: 'pending-reviewers-after-recommendation', titleText, }) const buildSendEmailFunction = emailBodyProps => reviewer => { const { paragraph, ...bodyProps } = emailBodyProps const email = new Email({ type: 'user', toUser: { email: reviewer.email, name: reviewer.lastName, }, fromEmail: `${signatureName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: Review no longer required`, paragraph, signatureName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: services.createUrl(this.baseUrl, unsubscribeSlug, { id: reviewer.id, token: reviewer.accessTokens.unsubscribe, }), ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), signatureJournal: journalName, }, bodyProps, }) return email.sendEmail() } acceptedReviewers.forEach( buildSendEmailFunction(acceptedReviewersEmailBody), ) pendingReviewers.forEach(buildSendEmailFunction(pendingReviewersEmailBody)) } async notifyEiCWhenHEMakesRecommendation() { const { eicName, titleText, recommendation, } = await this._getNotificationProperties() let emailType, subject switch (recommendation) { case 'minor': case 'major': emailType = 'eic-request-revision-from-he' subject = `${this.collection.customId}: Revision requested` break case 'publish': emailType = 'eic-recommend-to-publish-from-he' subject = `${this.collection.customId}: Recommendation to publish` break case 'reject': emailType = 'eic-recommend-to-reject-from-he' subject = `${this.collection.customId}: Recommendation to reject` break default: throw new Error(`undefined recommendation: ${recommendation} `) } const privateNote = this.newRecommendation.comments.find( comm => !comm.public, ) const content = get(privateNote, 'content') const comments = content ? `The editor provided the following comments: "${content}"` : '' const collHelper = new Collection({ collection: this.collection }) const targetUserName = collHelper.getHELastName() const userHelper = new User({ UserModel: this.UserModel }) const editors = await userHelper.getEditorsInChief() const { paragraph, ...bodyProps } = getEmailCopy({ comments, emailType, titleText, targetUserName, }) editors.forEach(eic => { const email = new Email({ type: 'user', toUser: { email: eic.email, name: `${eic.firstName} ${eic.lastName}`, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject, paragraph, signatureName: eicName, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: this.baseUrl, ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() }) } async notifyEiCWhenEQSAcceptsManuscript() { const { submittingAuthor } = await this._getNotificationProperties() const userHelper = new User({ UserModel: this.UserModel }) const editors = await userHelper.getEditorsInChief() const fragmentHelper = new Fragment({ fragment: this.fragment }) const parsedFragment = await fragmentHelper.getFragmentData({}) const { paragraph, ...bodyProps } = getEmailCopy({ emailType: 'eic-manuscript-accepted-by-eqs', titleText: `manuscript titled "${parsedFragment.title}" by ${ submittingAuthor.firstName } ${submittingAuthor.lastName}`, }) editors.forEach(eic => { const email = new Email({ type: 'system', toUser: { email: eic.email, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject: `${this.collection.customId}: New manuscript submitted`, paragraph, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: this.baseUrl, ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() }) } async notifyEiCWhenEQARejectsManuscript(comments) { const { titleText } = await this._getNotificationProperties() const userHelper = new User({ UserModel: this.UserModel }) const editors = await userHelper.getEditorsInChief() const { paragraph, ...bodyProps } = getEmailCopy({ comments, titleText, emailType: 'eic-manuscript-returned-by-eqa', }) editors.forEach(eic => { const email = new Email({ type: 'system', toUser: { email: eic.email, }, fromEmail: `${journalName} <${staffEmail}>`, content: { subject: `${ this.collection.customId }: Manuscript returned with comments`, paragraph, ctaText: 'MANUSCRIPT DETAILS', unsubscribeLink: this.baseUrl, ctaLink: services.createUrl( this.baseUrl, `/projects/${this.collection.id}/versions/${ this.fragment.id }/details`, ), }, bodyProps, }) return email.sendEmail() }) } async _getNotificationProperties() { const fragmentHelper = new Fragment({ fragment: this.fragment }) const parsedFragment = await fragmentHelper.getFragmentData({ handlingEditor: this.collection.handlingEditor, }) const { submittingAuthor, activeAuthors, } = await fragmentHelper.getAuthorData({ UserModel: this.UserModel, }) const userHelper = new User({ UserModel: this.UserModel }) const eicName = await userHelper.getEiCName() const titleText = `the manuscript titled "${parsedFragment.title}" by ${ submittingAuthor.firstName } ${submittingAuthor.lastName}` const { recommendation, recommendationType } = this.newRecommendation return { recommendation, recommendationType, eicName, titleText, submittingAuthor, activeAuthors, parsedFragment, fragmentHelper, } } } module.exports = Notification