Skip to content
Snippets Groups Projects
notification.js 18.3 KiB
Newer Older
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`,
        ),
      },
  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`,
        ),
      },
  }

  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: {
      },
      fromEmail: `${eicName} <${staffEmail}>`,
      content: {
        subject: `${
          this.collection.customId
        }: Editorial decision returned with comments`,
        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`,
        ),
      },
  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`,
        unsubscribeLink: this.baseUrl,
      },
  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`,
        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,
          },
        ),
      },
  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,
    })

      const email = new Email({
        type: 'user',
        toUser: {
          email: author.email,
          name: author.lastName,
        },
        fromEmail: `${eicName} <${staffEmail}>`,
        content: {
          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,
        },
  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,
        },
  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`,
        ),
      },
  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}>`,
          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,
        },
    }

    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,
          ctaText: 'MANUSCRIPT DETAILS',
          unsubscribeLink: this.baseUrl,
          ctaLink: services.createUrl(
            this.baseUrl,
            `/projects/${this.collection.id}/versions/${
              this.fragment.id
            }/details`,
          ),
        },
  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`,
          ctaText: 'MANUSCRIPT DETAILS',
          unsubscribeLink: this.baseUrl,
          ctaLink: services.createUrl(
            this.baseUrl,
            `/projects/${this.collection.id}/versions/${
              this.fragment.id
            }/details`,
          ),
        },
    })
  }

  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`,
          ctaText: 'MANUSCRIPT DETAILS',
          unsubscribeLink: this.baseUrl,
          ctaLink: services.createUrl(
            this.baseUrl,
            `/projects/${this.collection.id}/versions/${
              this.fragment.id
            }/details`,
          ),
        },
    })
  }

  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,
    }