From 90bd9e795816bf91b4b748570680070e2aadb8c8 Mon Sep 17 00:00:00 2001
From: Sebastian Mihalache <sebastian.mihalache@gmail.con>
Date: Tue, 25 Sep 2018 11:02:19 +0300
Subject: [PATCH] feat(email-templating): add files and parameters

---
 .../component-email-templating/.gitignore     |   8 +
 packages/component-email-templating/README.md |  64 +++++++
 .../config/default.js                         |  18 ++
 .../component-email-templating/config/test.js |   3 +
 packages/component-email-templating/index.js  |   1 +
 .../component-email-templating/package.json   |  33 ++++
 .../src/EmailTemplate.js                      |  96 +++++++++++
 .../component-email-templating/src/helpers.js |  53 ++++++
 .../src/templates/invitation.html             |   5 +
 .../src/templates/notification.html           |   3 +
 .../src/templates/partials/body.hbs           |  25 +++
 .../src/templates/partials/button.hbs         |  19 ++
 .../src/templates/partials/footer.hbs         |  36 ++++
 .../src/templates/partials/header.hbs         | 163 ++++++++++++++++++
 .../src/templates/partials/intro.hbs          |   1 +
 .../src/templates/partials/invButtons.hbs     |  88 ++++++++++
 .../src/templates/partials/invHeader.hbs      | 161 +++++++++++++++++
 .../templates/partials/invLowerContent.hbs    |  20 +++
 .../templates/partials/invManuscriptData.hbs  |  15 ++
 .../templates/partials/invUpperContent.hbs    |  13 ++
 .../src/templates/partials/signature.hbs      |   4 +
 .../tests/helpers.test.js                     |  60 +++++++
 .../src/routes/emails/helpers.js              |   8 +-
 .../src/routes/emails/notifications.js        |   4 +-
 .../app/config/journal/metadata.js            |   6 +
 packages/xpub-faraday/config/components.json  |   1 +
 packages/xpub-faraday/config/default.js       |   3 +
 27 files changed, 905 insertions(+), 6 deletions(-)
 create mode 100644 packages/component-email-templating/.gitignore
 create mode 100644 packages/component-email-templating/README.md
 create mode 100644 packages/component-email-templating/config/default.js
 create mode 100644 packages/component-email-templating/config/test.js
 create mode 100644 packages/component-email-templating/index.js
 create mode 100644 packages/component-email-templating/package.json
 create mode 100644 packages/component-email-templating/src/EmailTemplate.js
 create mode 100644 packages/component-email-templating/src/helpers.js
 create mode 100644 packages/component-email-templating/src/templates/invitation.html
 create mode 100644 packages/component-email-templating/src/templates/notification.html
 create mode 100644 packages/component-email-templating/src/templates/partials/body.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/button.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/footer.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/header.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/intro.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/invButtons.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/invHeader.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/invLowerContent.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/invManuscriptData.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/invUpperContent.hbs
 create mode 100644 packages/component-email-templating/src/templates/partials/signature.hbs
 create mode 100644 packages/component-email-templating/tests/helpers.test.js

diff --git a/packages/component-email-templating/.gitignore b/packages/component-email-templating/.gitignore
new file mode 100644
index 000000000..3614a8100
--- /dev/null
+++ b/packages/component-email-templating/.gitignore
@@ -0,0 +1,8 @@
+_build/
+api/
+logs/
+node_modules/
+uploads/
+.env.*
+.env
+config/local*.*
\ No newline at end of file
diff --git a/packages/component-email-templating/README.md b/packages/component-email-templating/README.md
new file mode 100644
index 000000000..a163de5cb
--- /dev/null
+++ b/packages/component-email-templating/README.md
@@ -0,0 +1,64 @@
+# Email Template
+
+## About
+
+The `email-template` component contains an `Email` class with two instance methods: `getBody` for retrieving the email body (html and text) and `sendEmail` for sending the email using the `send-email` component from PubSweet.
+
+1.  `getBody({ body = {}, isReviewerInvitation = false })`:
+    * accepts two parameters:
+      * the `body` object, which for simple notifications should have only two properties: `paragraph` and `hasLink`
+      * the `isReviewerInvitation` boolean to indicate wether or not you need to send a Reviewer Invitation, which uses a more complex template
+    * returns the HTML and text parts of the email which can then be used to send it
+1.  `sendEmail({ text, html })`:
+    * accepts the text and HTML parts of an email and then uses the `send-email` component from PubSweet to actually send the email.
+
+The `Email` class also provides a `constructor` whose properties will be used when sending the email:
+
+1.  `type`: a String that can be either `user` or `system` which can be used in the unsubscribe process
+1.  `toUser`: an Object with two properties: `email` and `name`. The `name` property will be used when addressing the recipient in the email content - for example: "Dear Dr. Rachel Smith".
+1.  `content`: an Object which contains data about the email:
+    1.  `subject`
+    1.  `signatureName`
+    1.  `ctaLink` - the URL which will be placed in the button
+    1.  `ctaText` - the text which appears on the button
+    1.  `unsubscribeLink`
+
+## Usage
+
+1.  **Notifications**
+    These are the most basic emails, which contain at least a piece of text, called a `Paragraph` and may or may not contain an `Action Button`. The `paragraph` and `hasLink` are passed to the `getBody()` function as properties of the `body` parameter.
+
+    ![notification](https://gitlab.coko.foundation/xpub/xpub-faraday/uploads/27cb6acc8ff4a07758f55e5ea0504d28/notification.png)
+
+    ```javascript
+    const emailTemplate = require('@pubsweet/component-email-template')
+
+    const sendNotifications = ({ user, editor, collection, fragment }) => {
+      const email = new emailTemplate({
+        type: 'user',
+        toUser: {
+          email: user.email,
+          name: `${user.firstName} ${user.lastName}`,
+        },
+        content: {
+          subject: `${collection.customId}: Manuscript Update`,
+          signatureName: `${editor.name}`,
+          ctaLink: `http://localhost:3000/projects/${collection.id}/versions/${
+            fragment.id
+          }/details`,
+          ctaText: 'MANUSCRIPT DETAILS',
+          unsubscribeLink: `http://localhost:3000/unsubscribe/${user.id}`,
+        },
+      })
+
+      const paragraph = `We are please to inform you that the manuscript has passed the technical check process and is now submitted. Please click the link below to access the manuscript.`
+
+      const { html, text } = email.getBody({ paragraph, hasLink: true })
+      email.sendEmail({ html, text })
+    }
+    ```
+
+1.  **Reviewer Invitation**
+    This email template is specific to Hindawi and it requires some data that might not be available in other PubSweet apps.
+
+    ![invitation](https://gitlab.coko.foundation/xpub/xpub-faraday/uploads/438af32b5da5532ed2bd6ca46588be50/Screen_Shot_2018-08-14_at_12.49.37.png)
diff --git a/packages/component-email-templating/config/default.js b/packages/component-email-templating/config/default.js
new file mode 100644
index 000000000..0d5f73e62
--- /dev/null
+++ b/packages/component-email-templating/config/default.js
@@ -0,0 +1,18 @@
+module.exports = {
+  // sender: {
+  //   state: 'CA',
+  //   zipCode: '94103',
+  //   city: 'San Francisco',
+  //   name: 'Coko Foundation',
+  //   address: '2973 16th St., Suite 300',
+  //   fromEmail: ,
+  // },
+  journal: {
+    name: 'Coko Foundation',
+    staffEmail: 'Coko <team@coko.foundation>',
+    logo:
+      'https://marketing-image-production.s3.amazonaws.com/uploads/5870a9c1bd8e6213ef1e02718d2db115be163584a9fc401accb63866479b952fe83d646a32a55996a3701a0b40943c47900cb1c2d255ae34e7925555b2cdc24a.png',
+    ctaColor: '#EE2B77',
+    logoLink: 'https://coko.foundation/',
+  },
+}
diff --git a/packages/component-email-templating/config/test.js b/packages/component-email-templating/config/test.js
new file mode 100644
index 000000000..f41006674
--- /dev/null
+++ b/packages/component-email-templating/config/test.js
@@ -0,0 +1,3 @@
+const defaultConfig = require('./default')
+
+module.exports = defaultConfig
diff --git a/packages/component-email-templating/index.js b/packages/component-email-templating/index.js
new file mode 100644
index 000000000..3ad0f7ec7
--- /dev/null
+++ b/packages/component-email-templating/index.js
@@ -0,0 +1 @@
+module.exports = require('./src/EmailTemplate')
diff --git a/packages/component-email-templating/package.json b/packages/component-email-templating/package.json
new file mode 100644
index 000000000..4fee49627
--- /dev/null
+++ b/packages/component-email-templating/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@pubsweet/component-email-templating",
+  "version": "0.0.1",
+  "description": "build email template and send email for pubsweet",
+  "license": "MIT",
+  "author": "Collaborative Knowledge Foundation",
+  "files": [
+    "src"
+  ],
+  "main": "index.js",
+  "dependencies": {
+    "@pubsweet/component-send-email": "0.2.4",
+    "handlebars": "^4.0.2"
+  },
+  "peerDependencies": {
+    "@pubsweet/logger": "^0.0.1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.coko.foundation/pubsweet/pubsweet",
+    "path": "SendEmail"
+  },
+  "jest": {
+    "verbose": true,
+    "testRegex": "/tests/.*.test.js$"
+  },
+  "scripts": {
+    "test": "jest"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/component-email-templating/src/EmailTemplate.js b/packages/component-email-templating/src/EmailTemplate.js
new file mode 100644
index 000000000..51d944c0e
--- /dev/null
+++ b/packages/component-email-templating/src/EmailTemplate.js
@@ -0,0 +1,96 @@
+const config = require('config')
+const helpers = require('./helpers')
+const SendEmail = require('@pubsweet/component-send-email')
+
+const configData = {}
+class Email {
+  constructor({
+    type = 'system',
+    fromEmail = config.get('journal.fromEmail'),
+    toUser = {
+      id: '',
+      email: '',
+      name: '',
+    },
+    content = {
+      subject: '',
+      ctaLink: '',
+      ctaText: '',
+      ctaColor: '',
+      signatureName: '',
+      unsubscribeLink: '',
+      signatureJournal: '',
+    },
+  }) {
+    this.type = type
+    this.toUser = toUser
+    this.content = content
+    this.fromEmail = fromEmail
+  }
+
+  set _toUser(newToUser) {
+    this.toUser = newToUser
+  }
+
+  set _subject(newSubject) {
+    this.subject = newSubject
+  }
+
+  set _content(newContent) {
+    this.content = newContent
+  }
+
+  getInvitationBody({ emailBodyProps }) {
+    return {
+      html: helpers.getInvitationBody({
+        replacements: {
+          ...configData,
+          ...this.content,
+          ...emailBodyProps,
+          toUserName: this.toUser.name,
+        },
+      }),
+      text: `${emailBodyProps.resend} ${emailBodyProps.upperContent} ${
+        emailBodyProps.manuscriptText
+      } ${emailBodyProps.lowerContent} ${this.content.signatureName}`,
+    }
+  }
+
+  getNotificationBody({ emailBodyProps }) {
+    return {
+      html: helpers.getCompiledNotificationBody({
+        replacements: {
+          ...configData,
+          ...this.content,
+          ...emailBodyProps,
+          toUserName: this.toUser.name,
+        },
+      }),
+      text: `${emailBodyProps.paragraph} ${this.content.ctaLink} ${
+        this.content.ctaText
+      } ${this.content.signatureName}`,
+    }
+  }
+
+  async sendEmail({ text, html }) {
+    const { fromEmail: from } = this
+    const { email: to } = this.toUser
+    const { subject } = this.content
+
+    const mailData = {
+      to,
+      text,
+      html,
+      from,
+      subject,
+    }
+
+    try {
+      await SendEmail.send(mailData)
+    } catch (e) {
+      throw new Error(e)
+    }
+  }
+}
+
+module.exports = Email
diff --git a/packages/component-email-templating/src/helpers.js b/packages/component-email-templating/src/helpers.js
new file mode 100644
index 000000000..8df0e7950
--- /dev/null
+++ b/packages/component-email-templating/src/helpers.js
@@ -0,0 +1,53 @@
+const fs = require('fs')
+const handlebars = require('handlebars')
+
+const getCompiledNotificationBody = ({ replacements }) => {
+  handlePartial('header', replacements)
+  if (replacements.hasIntro) handlePartial('intro', replacements)
+  handlePartial('footer', replacements)
+  if (replacements.hasSignature) handlePartial('signature', replacements)
+  if (replacements.hasLink) handlePartial('button', replacements)
+  handlePartial('body', replacements)
+
+  return compileBody({ fileName: 'notification', context: replacements })
+}
+
+const getCompiledInvitationBody = ({ replacements }) => {
+  handlePartial('invHeader', replacements)
+  handlePartial('footer', replacements)
+  handlePartial('invUpperContent', replacements)
+  handlePartial('invButtons', replacements)
+  handlePartial('invManuscriptData', replacements)
+  handlePartial('signature', replacements)
+  handlePartial('invLowerContent', replacements)
+
+  return compileBody({ fileName: 'invitation', context: replacements })
+}
+
+const readFile = path =>
+  fs.readFileSync(path, { encoding: 'utf-8' }, (err, file) => {
+    if (err) {
+      throw err
+    } else {
+      return file
+    }
+  })
+
+const handlePartial = (partialName, context = {}) => {
+  let partial = readFile(`${__dirname}/templates/partials/${partialName}.hbs`)
+  const template = handlebars.compile(partial)
+  partial = template(context)
+  handlebars.registerPartial(partialName, partial)
+}
+
+const compileBody = ({ fileName, context }) => {
+  const htmlFile = readFile(`${__dirname}/templates/${fileName}.html`)
+  const htmlTemplate = handlebars.compile(htmlFile)
+  const htmlBody = htmlTemplate(context)
+  return htmlBody
+}
+
+module.exports = {
+  getCompiledNotificationBody,
+  getCompiledInvitationBody,
+}
diff --git a/packages/component-email-templating/src/templates/invitation.html b/packages/component-email-templating/src/templates/invitation.html
new file mode 100644
index 000000000..f9d5ed18c
--- /dev/null
+++ b/packages/component-email-templating/src/templates/invitation.html
@@ -0,0 +1,5 @@
+{{> invHeader }}
+{{> invUpperContent }}
+{{> invButtons }}
+{{> invLowerContent }}
+{{> footer }}
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/notification.html b/packages/component-email-templating/src/templates/notification.html
new file mode 100644
index 000000000..02cbf2a6e
--- /dev/null
+++ b/packages/component-email-templating/src/templates/notification.html
@@ -0,0 +1,3 @@
+{{> header }}
+{{> body }}
+{{> footer}}
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/body.hbs b/packages/component-email-templating/src/templates/partials/body.hbs
new file mode 100644
index 000000000..59e123063
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/body.hbs
@@ -0,0 +1,25 @@
+<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
+  <tr>
+    <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
+      <div>
+        {{#if hasIntro}}
+          {{> intro}}
+        {{/if}}
+        <p>&nbsp;</p>
+        <p>
+          {{{paragraph}}}
+        </p>
+        <p>&nbsp;</p>
+        <p>
+          {{#if hasLink }}
+            {{> button }}
+          {{/if}}
+        </p>
+        {{#if hasSignature }}
+          {{> signature}}
+        {{/if}}
+        <p>&nbsp;</p>
+      </div>
+    </td>
+  </tr>
+</table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/button.hbs b/packages/component-email-templating/src/templates/partials/button.hbs
new file mode 100644
index 000000000..95f80f289
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/button.hbs
@@ -0,0 +1,19 @@
+<table border="0" cellPadding="0" cellSpacing="0" class="module" data-role="module-button" data-type="button" role="module"
+  style="table-layout:fixed" width="100%">
+  <tbody>
+    <tr>
+      <td align="center" bgcolor="#FFFFFF" class="outer-td" style="padding:0px 0px 30px 0px;background-color:#FFFFFF">
+        <table border="0" cellPadding="0" cellSpacing="0" class="button-css__deep-table___2OZyb wrapper-mobile" style="text-align:center">
+          <tbody>
+            <tr>
+              <td align="center" bgcolor="{{ctaColor}}" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
+                <a href="{{ ctaLink }}" style="background-color:{{ctaColor}};border:1px solid #333333;border-color:{{ctaColor}};border-radius:0px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
+                  target="_blank">{{ ctaText }}</a>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </td>
+    </tr>
+  </tbody>
+</table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/footer.hbs b/packages/component-email-templating/src/templates/partials/footer.hbs
new file mode 100644
index 000000000..ef5a6c5d0
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/footer.hbs
@@ -0,0 +1,36 @@
+<div data-role="module-unsubscribe" class="module unsubscribe-css__unsubscribe___2CDlR" role="module" data-type="unsubscribe"
+  style="color:#444444;font-size:12px;line-height:20px;padding:16px 16px 16px 16px;text-align:center">
+  <div class="Unsubscribe--addressLine">
+    <p class="Unsubscribe--senderName" style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">{{ senderName }}</p>
+    <p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">
+      <span class="Unsubscribe--senderAddress">{{ senderAddress }}</span>,
+      <span class="Unsubscribe--senderCity">{{ senderCity }}</span>,
+      <span class="Unsubscribe--senderState">{{ senderState }}</span>
+      <span class="Unsubscribe--senderZip">{{ senderZip }}</span>
+    </p>
+  </div>
+  <p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">
+    <a class="Unsubscribe--unsubscribeLink" href="{{ unsubscribeLink }}">Unsubscribe</a>
+  </p>
+</div>
+</td>
+</tr>
+</table>
+<!--[if mso]>
+                          </td></tr></table>
+                          </center>
+                          <![endif]-->
+</td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</div>
+</center>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/header.hbs b/packages/component-email-templating/src/templates/partials/header.hbs
new file mode 100644
index 000000000..fbc2385fb
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/header.hbs
@@ -0,0 +1,163 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
+  <!--[if !mso]><!-->
+  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
+  <!--<![endif]-->
+  <!--[if (gte mso 9)|(IE)]>
+    <xml>
+    <o:OfficeDocumentSettings>
+    <o:AllowPNG/>
+    <o:PixelsPerInch>96</o:PixelsPerInch>
+    </o:OfficeDocumentSettings>
+    </xml>
+    <![endif]-->
+  <!--[if (gte mso 9)|(IE)]>
+    <style type="text/css">
+      body {width: 600px;margin: 0 auto;}
+      table {border-collapse: collapse;}
+      table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
+      img {-ms-interpolation-mode: bicubic;}
+    </style>
+    <![endif]-->
+
+  <style type="text/css">
+    body,
+    p,
+    div {
+      font-family: helvetica, arial, sans-serif;
+      font-size: 14px;
+    }
+
+    body {
+      color: #626262;
+    }
+
+    body a {
+      color: #0D78F2;
+      text-decoration: none;
+    }
+
+    p {
+      margin: 0;
+      padding: 0;
+    }
+
+    table.wrapper {
+      width: 100% !important;
+      table-layout: fixed;
+      -webkit-font-smoothing: antialiased;
+      -webkit-text-size-adjust: 100%;
+      -moz-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+    }
+
+    img.max-width {
+      max-width: 100% !important;
+    }
+
+    .column.of-2 {
+      width: 50%;
+    }
+
+    .column.of-3 {
+      width: 33.333%;
+    }
+
+    .column.of-4 {
+      width: 25%;
+    }
+
+    @media screen and (max-width:480px) {
+      .preheader .rightColumnContent,
+      .footer .rightColumnContent {
+        text-align: left !important;
+      }
+      .preheader .rightColumnContent div,
+      .preheader .rightColumnContent span,
+      .footer .rightColumnContent div,
+      .footer .rightColumnContent span {
+        text-align: left !important;
+      }
+      .preheader .rightColumnContent,
+      .preheader .leftColumnContent {
+        font-size: 80% !important;
+        padding: 5px 0;
+      }
+      table.wrapper-mobile {
+        width: 100% !important;
+        table-layout: fixed;
+      }
+      img.max-width {
+        height: auto !important;
+        max-width: 480px !important;
+      }
+      a.bulletproof-button {
+        display: block !important;
+        width: auto !important;
+        font-size: 80%;
+        padding-left: 0 !important;
+        padding-right: 0 !important;
+      }
+      .columns {
+        width: 100% !important;
+      }
+      .column {
+        display: block !important;
+        width: 100% !important;
+        padding-left: 0 !important;
+        padding-right: 0 !important;
+        margin-left: 0 !important;
+        margin-right: 0 !important;
+      }
+    }
+  </style>
+  <!--user entered Head Start-->
+
+  <!--End Head user entered-->
+</head>
+
+<body>
+  <center class="wrapper" data-link-color="#0D78F2" data-body-style="font-size: 14px; font-family: helvetica,arial,sans-serif; color: #626262; background-color: #F4F4F4;">
+    <div class="webkit">
+      <table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#F4F4F4">
+        <tr>
+          <td valign="top" bgcolor="#F4F4F4" width="100%">
+            <table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
+              <tr>
+                <td width="100%">
+                  <table width="100%" cellpadding="0" cellspacing="0" border="0">
+                    <tr>
+                      <td>
+                        <!--[if mso]>
+                          <center>
+                          <table><tr><td width="600">
+                          <![endif]-->
+                        <table width="100%" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width:600px;" align="center">
+                          <tr>
+                            <td role="modules-container" style="padding: 0px 0px 0px 0px; color: #626262; text-align: left;" bgcolor="#F4F4F4" width="100%"
+                              align="left">
+
+                              <table class="module preheader preheader-hide" role="module" data-type="preheader" border="0" cellpadding="0" cellspacing="0"
+                                width="100%" style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
+                                <tr>
+                                  <td role="module-content">
+                                    <p>you have a new notification</p>
+                                  </td>
+                                </tr>
+                              </table>
+
+                              <table class="wrapper" role="module" data-type="image" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
+                                <tr>
+                                  <td style="font-size:6px;line-height:10px;padding:20px 0px 20px 0px;" valign="top" align="center">
+                                   <a href="{{ logoLink }}">
+                                      <img class="max-width" border="0" style="display:block;color:#000000;text-decoration:none;font-family:Helvetica, arial, sans-serif;font-size:16px;max-width:10% !important;width:10%;height:auto !important;"
+                                        src="{{ logo }}"
+                                        alt="" width="60">
+                                    </a>
+                                  </td>
+                                </tr>
+                              </table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/intro.hbs b/packages/component-email-templating/src/templates/partials/intro.hbs
new file mode 100644
index 000000000..0ce780b6d
--- /dev/null
+++ b/packages/component-email-templating/src/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-email-templating/src/templates/partials/invButtons.hbs b/packages/component-email-templating/src/templates/partials/invButtons.hbs
new file mode 100644
index 000000000..5c5041dec
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/invButtons.hbs
@@ -0,0 +1,88 @@
+<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" role="module" data-type="columns" data-version="2"
+  style="padding:20px 0px 20px 0px;background-color:#ffffff;box-sizing:border-box;" bgcolor="#ffffff">
+  <tr role='module-content'>
+    <td height="100%" valign="top">
+      <!--[if (gte mso 9)|(IE)]>
+              <center>
+                <table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-spacing:0;border-collapse:collapse;table-layout: fixed;" >
+                  <tr>
+            <![endif]-->
+
+      <!--[if (gte mso 9)|(IE)]>
+      <td width="300.000px" valign="top" style="padding: 0px 0px 0px 0px;border-collapse: collapse;" >
+    <![endif]-->
+
+      <table width="300.000" style="width:300.000px;border-spacing:0;border-collapse:collapse;margin:0px 0px 0px 0px;" cellpadding="0"
+        cellspacing="0" align="left" border="0" bgcolor="#ffffff" class="column column-0 of-2
+                  empty">
+        <tr>
+          <td style="padding:0px;margin:0px;border-spacing:0;">
+            <table border="0" cellPadding="0" cellSpacing="0" class="module" data-role="module-button" data-type="button" role="module"
+              style="table-layout:fixed" width="100%">
+              <tbody>
+                <tr>
+                  <td align="center" class="outer-td" style="padding:0px 0px 0px 0px">
+                    <table border="0" cellPadding="0" cellSpacing="0" class="button-css__deep-table___2OZyb wrapper-mobile" style="text-align:center">
+                      <tbody>
+                        <tr>
+                          <td align="center" bgcolor="#0d78f2" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
+                            <a style="background-color:#0d78f2;border:1px solid #333333;border-color:#0d78f2;border-radius:0px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
+                              href="{{ agreeLink }}" target="_blank">AGREE</a>
+                          </td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </table>
+
+      <!--[if (gte mso 9)|(IE)]>
+      </td>
+    <![endif]-->
+      <!--[if (gte mso 9)|(IE)]>
+      <td width="300.000px" valign="top" style="padding: 0px 0px 0px 0px;border-collapse: collapse;" >
+    <![endif]-->
+
+      <table width="300.000" style="width:300.000px;border-spacing:0;border-collapse:collapse;margin:0px 0px 0px 0px;" cellpadding="0"
+        cellspacing="0" align="left" border="0" bgcolor="#ffffff" class="column column-1 of-2
+                  empty">
+        <tr>
+          <td style="padding:0px;margin:0px;border-spacing:0;">
+            <table border="0" cellPadding="0" cellSpacing="0" class="module" data-role="module-button" data-type="button" role="module"
+              style="table-layout:fixed" width="100%">
+              <tbody>
+                <tr>
+                  <td align="center" class="outer-td" style="padding:0px 0px 0px 0px">
+                    <table border="0" cellPadding="0" cellSpacing="0" class="button-css__deep-table___2OZyb wrapper-mobile" style="text-align:center">
+                      <tbody>
+                        <tr>
+                          <td align="center" bgcolor="#e4dfdf" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
+                            <a style="background-color:#e4dfdf;border:1px solid #333333;border-color:#E4DFDF;border-radius:0px;border-width:1px;color:#302e2e;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
+                              href="{{ declineLink }}" target="_blank">DECLINE</a>
+                          </td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </table>
+
+      <!--[if (gte mso 9)|(IE)]>
+      </td>
+    <![endif]-->
+      <!--[if (gte mso 9)|(IE)]>
+                  <tr>
+                </table>
+              </center>
+            <![endif]-->
+    </td>
+  </tr>
+</table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/invHeader.hbs b/packages/component-email-templating/src/templates/partials/invHeader.hbs
new file mode 100644
index 000000000..d90452b55
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/invHeader.hbs
@@ -0,0 +1,161 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
+  <!--[if !mso]><!-->
+  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
+  <!--<![endif]-->
+  <!--[if (gte mso 9)|(IE)]>
+    <xml>
+    <o:OfficeDocumentSettings>
+    <o:AllowPNG/>
+    <o:PixelsPerInch>96</o:PixelsPerInch>
+    </o:OfficeDocumentSettings>
+    </xml>
+    <![endif]-->
+  <!--[if (gte mso 9)|(IE)]>
+    <style type="text/css">
+      body {width: 600px;margin: 0 auto;}
+      table {border-collapse: collapse;}
+      table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
+      img {-ms-interpolation-mode: bicubic;}
+    </style>
+    <![endif]-->
+
+  <style type="text/css">
+    body,
+    p,
+    div {
+      font-family: helvetica, arial, sans-serif;
+      font-size: 14px;
+    }
+
+    body {
+      color: #626262;
+    }
+
+    body a {
+      color: #0D78F2;
+      text-decoration: none;
+    }
+
+    p {
+      margin: 0;
+      padding: 0;
+    }
+
+    table.wrapper {
+      width: 100% !important;
+      table-layout: fixed;
+      -webkit-font-smoothing: antialiased;
+      -webkit-text-size-adjust: 100%;
+      -moz-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+    }
+
+    img.max-width {
+      max-width: 100% !important;
+    }
+
+    .column.of-2 {
+      width: 50%;
+    }
+
+    .column.of-3 {
+      width: 33.333%;
+    }
+
+    .column.of-4 {
+      width: 25%;
+    }
+
+    @media screen and (max-width:480px) {
+      .preheader .rightColumnContent,
+      .footer .rightColumnContent {
+        text-align: left !important;
+      }
+      .preheader .rightColumnContent div,
+      .preheader .rightColumnContent span,
+      .footer .rightColumnContent div,
+      .footer .rightColumnContent span {
+        text-align: left !important;
+      }
+      .preheader .rightColumnContent,
+      .preheader .leftColumnContent {
+        font-size: 80% !important;
+        padding: 5px 0;
+      }
+      table.wrapper-mobile {
+        width: 100% !important;
+        table-layout: fixed;
+      }
+      img.max-width {
+        height: auto !important;
+        max-width: 480px !important;
+      }
+      a.bulletproof-button {
+        display: block !important;
+        width: auto !important;
+        font-size: 80%;
+        padding-left: 0 !important;
+        padding-right: 0 !important;
+      }
+      .columns {
+        width: 100% !important;
+      }
+      .column {
+        display: block !important;
+        width: 100% !important;
+        padding-left: 0 !important;
+        padding-right: 0 !important;
+        margin-left: 0 !important;
+        margin-right: 0 !important;
+      }
+    }
+  </style>
+  <!--user entered Head Start-->
+
+  <!--End Head user entered-->
+</head>
+
+<body>
+  <center class="wrapper" data-link-color="#0D78F2" data-body-style="font-size: 14px; font-family: helvetica,arial,sans-serif; color: #626262; background-color: #F4F4F4;">
+    <div class="webkit">
+      <table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#F4F4F4">
+        <tr>
+          <td valign="top" bgcolor="#F4F4F4" width="100%">
+            <table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
+              <tr>
+                <td width="100%">
+                  <table width="100%" cellpadding="0" cellspacing="0" border="0">
+                    <tr>
+                      <td>
+                        <!--[if mso]>
+                          <center>
+                          <table><tr><td width="600">
+                          <![endif]-->
+                        <table width="100%" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width:600px;" align="center">
+                          <tr>
+                            <td role="modules-container" style="padding: 0px 0px 0px 0px; color: #626262; text-align: left;" bgcolor="#F4F4F4" width="100%"
+                              align="left">
+
+                              <table class="module preheader preheader-hide" role="module" data-type="preheader" border="0" cellpadding="0" cellspacing="0"
+                                width="100%" style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
+                                <tr>
+                                  <td role="module-content">
+                                    <p>new invitation</p>
+                                  </td>
+                                </tr>
+                              </table>
+
+                              <table class="wrapper" role="module" data-type="image" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
+                                <tr>
+                                  <td style="font-size:6px;line-height:10px;padding:20px 0px 20px 0px;" valign="top" align="center">
+                                    <img class="max-width" border="0" style="display:block;color:#000000;text-decoration:none;font-family:Helvetica, arial, sans-serif;font-size:16px;max-width:10% !important;width:10%;height:auto !important;"
+                                      src="{{ logo }}"
+                                      alt="" width="60">
+                                  </td>
+                                </tr>
+                              </table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/invLowerContent.hbs b/packages/component-email-templating/src/templates/partials/invLowerContent.hbs
new file mode 100644
index 000000000..2e391bc6b
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/invLowerContent.hbs
@@ -0,0 +1,20 @@
+<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
+  <tr>
+    <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
+      <p data-pm-slice="1 1 []">
+        <a href="{{ detailsLink }}">See more information</a>
+      </p>
+
+      <p data-pm-slice="1 1 []">&nbsp;</p>
+
+      {{#unless resend }}
+        {{> invManuscriptData }}
+      {{/unless}}
+      <p data-pm-slice="1 1 []">{{{ lowerContent }}}</p>
+
+      <p>&nbsp;</p>
+      {{> signature }}
+      <div style="text-align: center;">&nbsp;</div>
+    </td>
+  </tr>
+</table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/invManuscriptData.hbs b/packages/component-email-templating/src/templates/partials/invManuscriptData.hbs
new file mode 100644
index 000000000..36d60dbe2
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/invManuscriptData.hbs
@@ -0,0 +1,15 @@
+<p data-pm-slice="1 1 []">{{ manuscriptText }}</p>
+<p>&nbsp;</p>
+<h2>{{ title }}</h2>
+<p>&nbsp;</p>
+<h4>
+  <span style="font-family:arial,helvetica,sans-serif;">{{ authorsList }}</span>
+</h4>
+<p>
+  <br />
+  <em>
+    <span style="font-family:arial,helvetica,sans-serif;">{{{abstract}}}</span>
+  </em>
+</p>
+
+<p>&nbsp;</p>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/invUpperContent.hbs b/packages/component-email-templating/src/templates/partials/invUpperContent.hbs
new file mode 100644
index 000000000..2b9dc858c
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/invUpperContent.hbs
@@ -0,0 +1,13 @@
+<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
+  <tr>
+    <td style="padding:30px 23px 20px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
+      <div>
+        <p data-pm-slice="1 1 []">Dear Dr. {{ toUserName }},</p>
+
+        <p>&nbsp;</p>
+        <p>{{{ upperContent }}}</p>
+      </div>
+
+    </td>
+  </tr>
+</table>
\ No newline at end of file
diff --git a/packages/component-email-templating/src/templates/partials/signature.hbs b/packages/component-email-templating/src/templates/partials/signature.hbs
new file mode 100644
index 000000000..eec0831b8
--- /dev/null
+++ b/packages/component-email-templating/src/templates/partials/signature.hbs
@@ -0,0 +1,4 @@
+<p>Kind regards,
+  <br /> {{ signatureName }}
+  <br /> {{ signatureJournal }}
+</p>
\ No newline at end of file
diff --git a/packages/component-email-templating/tests/helpers.test.js b/packages/component-email-templating/tests/helpers.test.js
new file mode 100644
index 000000000..e655ec191
--- /dev/null
+++ b/packages/component-email-templating/tests/helpers.test.js
@@ -0,0 +1,60 @@
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
+process.env.SUPPRESS_NO_CONFIG_WARNING = true
+
+const { cloneDeep } = require('lodash')
+const helpers = require('../src/helpers')
+
+const emailProps = {
+  toUserName: 'Peter Griffin',
+  paragraph: 'This is a paragraph',
+  hasLink: true,
+  ctaText: 'CLICK HERE',
+  hasIntro: true,
+  hasSignature: true,
+}
+
+describe('Email template helpers', () => {
+  let replacements = {}
+  beforeEach(() => {
+    replacements = cloneDeep(emailProps)
+  })
+  it('should return the notification HTML with CTA', () => {
+    const notificationBody = helpers.getCompiledNotificationBody({
+      replacements,
+    })
+
+    expect(notificationBody).toContain('Peter Griffin')
+    expect(notificationBody).toContain('This is a paragraph')
+    expect(notificationBody).toContain('CLICK HERE')
+    expect(notificationBody).toContain('Dear Dr.')
+    expect(notificationBody).toContain('Kind regards')
+  })
+
+  it('should return the notification HTML without CTA', () => {
+    replacements.hasLink = false
+    const notificationBody = helpers.getCompiledNotificationBody({
+      replacements,
+    })
+    expect(notificationBody).toContain('Peter Griffin')
+    expect(notificationBody).toContain('This is a paragraph')
+    expect(notificationBody).not.toContain('CLICK HERE')
+  })
+  it('should return the notification HTML without intro', () => {
+    replacements.hasIntro = false
+    const notificationBody = helpers.getCompiledNotificationBody({
+      replacements,
+    })
+    expect(notificationBody).not.toContain('Peter Griffin')
+    expect(notificationBody).toContain('This is a paragraph')
+    expect(notificationBody).not.toContain('Dear Dr.')
+  })
+  it('should return the notification HTML without signature', () => {
+    replacements.hasSignature = false
+    const notificationBody = helpers.getCompiledNotificationBody({
+      replacements,
+    })
+    expect(notificationBody).toContain('Peter Griffin')
+    expect(notificationBody).toContain('This is a paragraph')
+    expect(notificationBody).not.toContain('Kind regards')
+  })
+})
diff --git a/packages/component-email/src/routes/emails/helpers.js b/packages/component-email/src/routes/emails/helpers.js
index f0473d53b..8f484b0a5 100644
--- a/packages/component-email/src/routes/emails/helpers.js
+++ b/packages/component-email/src/routes/emails/helpers.js
@@ -18,8 +18,8 @@ module.exports = {
       emailType = 'user-added-by-admin'
     }
 
-    const { html, text } = email.getBody({
-      body: getEmailCopy({
+    const { html, text } = email.getNotificationBody({
+      emailBodyProps: getEmailCopy({
         role,
         emailType,
       }),
@@ -34,8 +34,8 @@ module.exports = {
       confirmationToken: user.accessTokens.confirmation,
     })
 
-    const { html, text } = email.getBody({
-      body: getEmailCopy({
+    const { html, text } = email.getNotificationBody({
+      emailBodyProps: getEmailCopy({
         emailType: 'user-signup',
       }),
     })
diff --git a/packages/component-email/src/routes/emails/notifications.js b/packages/component-email/src/routes/emails/notifications.js
index 0d4f67258..10dd129a5 100644
--- a/packages/component-email/src/routes/emails/notifications.js
+++ b/packages/component-email/src/routes/emails/notifications.js
@@ -3,8 +3,8 @@ const config = require('config')
 const unsubscribeSlug = config.get('unsubscribe.url')
 const resetPath = config.get('invite-reset-password.url')
 const staffEmail = config.get('journal.staffEmail')
-
-const { Email, services } = require('pubsweet-component-helper-service')
+const Email = require('@pubsweet/component-email-templating')
+const { services } = require('pubsweet-component-helper-service')
 
 const { sendNewUserEmail, sendSignupEmail } = require('./helpers')
 
diff --git a/packages/xpub-faraday/app/config/journal/metadata.js b/packages/xpub-faraday/app/config/journal/metadata.js
index d23e29ac8..38c8bb3b5 100644
--- a/packages/xpub-faraday/app/config/journal/metadata.js
+++ b/packages/xpub-faraday/app/config/journal/metadata.js
@@ -13,4 +13,10 @@ module.exports = {
     issn: '2474-7394',
     prefix: 'RESEARCH-BCA-',
   },
+  emailData: {
+    logo:
+      'https://marketing-image-production.s3.amazonaws.com/uploads/bb39b20cf15e52c1c0933676e25f2b2402737c6560b8098c204ad6932b84eb2058804376dbc4db138c7a21dcaed9325bde36185648afac5bc97e3d73d4e12718.png',
+    ctaColor: '#63a945',
+    logoLink: 'https://hindawi.com',
+  },
 }
diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json
index bbb47dcc4..01db85a3f 100644
--- a/packages/xpub-faraday/config/components.json
+++ b/packages/xpub-faraday/config/components.json
@@ -6,6 +6,7 @@
   "pubsweet-components-faraday",
   "@pubsweet/component-aws-s3",
   "@pubsweet/component-send-email",
+  "@pubsweet/component-email-templating",
   "pubsweet-component-invite",
   "pubsweet-component-user-manager",
   "pubsweet-component-email",
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index b00ae42ca..5122682df 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -123,6 +123,9 @@ module.exports = {
   journal: {
     name: get(journalConfig, 'metadata.nameText'),
     staffEmail: get(journalConfig, 'metadata.email'),
+    logo: get(journalConfig, 'metadata.emailData.logo'),
+    ctaColor: get(journalConfig, 'metadata.emailData.ctaColor'),
+    logoLink: get(journalConfig, 'metadata.emailData.logoLink'),
   },
   features: {
     mts: JSON.parse(get(process, 'env.FEATURE_MTS', true)),
-- 
GitLab