diff --git a/packages/worker/src/api/controllers/admin/templates.js b/packages/worker/src/api/controllers/admin/templates.js
index d91323e0a1..ccf057c485 100644
--- a/packages/worker/src/api/controllers/admin/templates.js
+++ b/packages/worker/src/api/controllers/admin/templates.js
@@ -4,11 +4,32 @@ const {
StaticDatabases,
} = require("@budibase/auth").db
const { CouchDB } = require("../../../db")
-const { TemplatePurposePretty } = require("../../../constants")
+const { TemplatePurposePretty, TemplateTypes, EmailTemplatePurpose, TemplatePurpose } = require("../../../constants")
+const { getTemplateByPurpose } = require("../../../constants/templates")
const GLOBAL_DB = StaticDatabases.GLOBAL.name
const GLOBAL_OWNER = "global"
+function addBaseTemplates(templates, type = null) {
+ let purposeList
+ switch (type) {
+ case TemplateTypes.EMAIL:
+ purposeList = Object.values(EmailTemplatePurpose)
+ break
+ default:
+ purposeList = Object.values(TemplatePurpose)
+ break
+ }
+ for (let purpose of purposeList) {
+ // check if a template exists already for purpose
+ if (templates.find(template => template.purpose === purpose)) {
+ continue
+ }
+ templates.push(getTemplateByPurpose(purpose))
+ }
+ return templates
+}
+
async function getTemplates({ ownerId, type, id } = {}) {
const db = new CouchDB(GLOBAL_DB)
const response = await db.allDocs(
@@ -17,10 +38,15 @@ async function getTemplates({ ownerId, type, id } = {}) {
})
)
let templates = response.rows.map(row => row.doc)
+ // should only be one template with ID
+ if (id) {
+ return templates[0]
+ }
if (type) {
templates = templates.filter(template => template.type === type)
}
- return templates
+
+ return addBaseTemplates(templates, type)
}
exports.save = async ctx => {
@@ -73,7 +99,8 @@ exports.find = async ctx => {
}
exports.destroy = async ctx => {
- // TODO
- // const db = new CouchDB(GLOBAL_DB)
- ctx.body = {}
+ const db = new CouchDB(GLOBAL_DB)
+ await db.remove(ctx.params.id, ctx.params.rev)
+ ctx.message = `Template ${ctx.params.id} deleted.`
+ ctx.status = 200
}
diff --git a/packages/worker/src/api/routes/admin/templates.js b/packages/worker/src/api/routes/admin/templates.js
index 2207b72458..81bd814cbc 100644
--- a/packages/worker/src/api/routes/admin/templates.js
+++ b/packages/worker/src/api/routes/admin/templates.js
@@ -25,5 +25,5 @@ router
.get("/api/admin/template", controller.fetch)
.get("/api/admin/template/:type", controller.fetchByType)
.get("/api/admin/template/:ownerId", controller.fetchByOwner)
- .delete("/api/admin/template/:id", controller.destroy)
.get("/api/admin/template/:id", controller.find)
+ .delete("/api/admin/template/:id/:rev", controller.destroy)
diff --git a/packages/worker/src/constants/index.js b/packages/worker/src/constants/index.js
index 5d52ce798f..8edba58fda 100644
--- a/packages/worker/src/constants/index.js
+++ b/packages/worker/src/constants/index.js
@@ -14,27 +14,52 @@ exports.Configs = {
GOOGLE: "google",
}
-exports.TemplateTypes = {
+const TemplateTypes = {
EMAIL: "email",
}
-exports.TemplatePurpose = {
+const EmailTemplatePurpose = {
+ HEADER: "header",
+ FOOTER: "footer",
+ STYLES: "styles",
PASSWORD_RECOVERY: "password_recovery",
INVITATION: "invitation",
CUSTOM: "custom",
}
-exports.TemplatePurposePretty = [
- {
- name: "Password Recovery",
- value: exports.TemplatePurpose.PASSWORD_RECOVERY,
- },
- {
- name: "New User Invitation",
- value: exports.TemplatePurpose.INVITATION,
- },
- {
- name: "Custom",
- value: exports.TemplatePurpose.CUSTOM,
- },
-]
+const TemplatePurposePretty = {
+ [TemplateTypes.EMAIL]: [
+ {
+ name: "Styling",
+ value: EmailTemplatePurpose.STYLES,
+ },
+ {
+ name: "Header",
+ value: EmailTemplatePurpose.HEADER,
+ },
+ {
+ name: "Footer",
+ value: EmailTemplatePurpose.FOOTER,
+ },
+ {
+ name: "Password Recovery",
+ value: EmailTemplatePurpose.PASSWORD_RECOVERY
+ },
+ {
+ name: "New User Invitation",
+ value: EmailTemplatePurpose.INVITATION,
+ },
+ {
+ name: "Custom",
+ value: EmailTemplatePurpose.CUSTOM,
+ }
+ ]
+}
+
+// all purpose combined
+exports.TemplatePurpose = {
+ ...EmailTemplatePurpose,
+}
+exports.TemplateTypes = TemplateTypes
+exports.EmailTemplatePurpose = EmailTemplatePurpose
+exports.TemplatePurposePretty = TemplatePurposePretty
diff --git a/packages/worker/src/constants/templates/footer.html b/packages/worker/src/constants/templates/footer.html
new file mode 100644
index 0000000000..693fd3c0c0
--- /dev/null
+++ b/packages/worker/src/constants/templates/footer.html
@@ -0,0 +1,48 @@
+
\ No newline at end of file
diff --git a/packages/worker/src/constants/templates/header.html b/packages/worker/src/constants/templates/header.html
new file mode 100644
index 0000000000..7709bd30a8
--- /dev/null
+++ b/packages/worker/src/constants/templates/header.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+ {{ body }}
+ |
+
+
+
+ {{ footer }}
+ |
+
+
+
+
\ No newline at end of file
diff --git a/packages/worker/src/constants/templates/index.js b/packages/worker/src/constants/templates/index.js
new file mode 100644
index 0000000000..42fbcf70ab
--- /dev/null
+++ b/packages/worker/src/constants/templates/index.js
@@ -0,0 +1,19 @@
+const { readStaticFile } = require("../../utilities/fileSystem")
+const { EmailTemplatePurpose } = require("../index")
+const { join } = require("path")
+
+const TEMPLATE_PATH = join(__dirname, "..", "constants", "templates")
+
+exports.EmailTemplates = {
+ [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile(join(TEMPLATE_PATH, "passwordRecovery.html")),
+ [EmailTemplatePurpose.INVITATION]: readStaticFile(join(TEMPLATE_PATH, "invitation.html")),
+ [EmailTemplatePurpose.HEADER]: readStaticFile(join(TEMPLATE_PATH, "header.html")),
+ [EmailTemplatePurpose.FOOTER]: readStaticFile(join(TEMPLATE_PATH, "footer.html")),
+ [EmailTemplatePurpose.STYLES]: readStaticFile(join(TEMPLATE_PATH, "style.css")),
+}
+
+exports.getTemplateByPurpose = purpose => {
+ if (exports.EmailTemplates[purpose]) {
+ return exports.EmailTemplates[purpose]
+ }
+}
diff --git a/packages/worker/src/constants/templates/invitation.html b/packages/worker/src/constants/templates/invitation.html
new file mode 100644
index 0000000000..8e154fe189
--- /dev/null
+++ b/packages/worker/src/constants/templates/invitation.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Budibase Invitation
+ You've been invited to join {{ company }}'s Budibase platform!
+ Please follow the below link to finish your registration.
+ Finish registration
+
+ |
+
+
+
\ No newline at end of file
diff --git a/packages/worker/src/constants/templates/passwordRecovery.html b/packages/worker/src/constants/templates/passwordRecovery.html
new file mode 100644
index 0000000000..e6b179ec81
--- /dev/null
+++ b/packages/worker/src/constants/templates/passwordRecovery.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+ Budibase Password reset
+ Please follow the below link to reset your password.
+ Reset password
+ This password reset was required for {{ user }} if you did not
+ request this then please contact your administrator.
+
+ |
+
+
+
\ No newline at end of file
diff --git a/packages/worker/src/constants/templates/style.css b/packages/worker/src/constants/templates/style.css
new file mode 100644
index 0000000000..abcd797830
--- /dev/null
+++ b/packages/worker/src/constants/templates/style.css
@@ -0,0 +1,269 @@
+@font-face {
+ font-family: 'Playfair Display';
+ font-style: italic;
+ font-weight: 400;
+ src: url(/fonts.gstatic.com/s/playfairdisplay/v22/nuFkD-vYSZviVYUb_rj3ij__anPXDTnohkk72xU.woff2) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+html,
+body {
+ margin: 0 auto !important;
+ padding: 0 !important;
+ height: 100% !important;
+ width: 100% !important;
+ background: #f1f1f1;
+}
+* {
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+div[style*="margin: 16px 0"] {
+ margin: 0 !important;
+}
+table,
+td {
+ mso-table-lspace: 0pt !important;
+ mso-table-rspace: 0pt !important;
+}
+table {
+ border-spacing: 0 !important;
+ border-collapse: collapse !important;
+ table-layout: fixed !important;
+ margin: 0 auto !important;
+}
+img {
+ -ms-interpolation-mode:bicubic;
+}
+a {
+ text-decoration: none;
+}
+*[x-apple-data-detectors], /* iOS */
+.unstyle-auto-detected-links *,
+.aBn {
+ border-bottom: 0 !important;
+ cursor: default !important;
+ color: inherit !important;
+ text-decoration: none !important;
+ font-size: inherit !important;
+ font-family: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+}
+.a6S {
+ display: none !important;
+ opacity: 0.01 !important;
+}
+.im {
+ color: inherit !important;
+}
+img.g-img + div {
+ display: none !important;
+}
+@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
+ u ~ div .email-container {
+ min-width: 320px !important;
+ }
+}
+@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
+ u ~ div .email-container {
+ min-width: 375px !important;
+ }
+}
+@media only screen and (min-device-width: 414px) {
+ u ~ div .email-container {
+ min-width: 414px !important;
+ }
+}
+.primary{
+ background: #f3a333;
+}
+.bg_white{
+ background: #ffffff;
+}
+.bg_light{
+ background: #fafafa;
+}
+.bg_black{
+ background: #000000;
+}
+.bg_dark{
+ background: rgba(0,0,0,.8);
+}
+.email-section{
+ padding:2.5em;
+}
+.btn{
+ padding: 10px 15px;
+}
+.btn.btn-primary{
+ border-radius: 30px;
+ background: #f3a333;
+ color: #ffffff;
+}
+h1,h2,h3,h4,h5,h6{
+ font-family: 'Playfair Display', serif;
+ color: #000000;
+ margin-top: 0;
+}
+body{
+ font-family: 'Montserrat', sans-serif;
+ font-weight: 400;
+ font-size: 15px;
+ line-height: 1.8;
+ color: rgba(0,0,0,.4);
+}
+a{
+ color: #f3a333;
+}
+table{
+}
+.logo h1{
+ margin: 0;
+}
+.logo h1 a{
+ color: #000;
+ font-size: 20px;
+ font-weight: 700;
+ text-transform: uppercase;
+ font-family: 'Montserrat', sans-serif;
+}
+.hero{
+ position: relative;
+}
+.hero img{
+
+}
+.hero .text{
+ color: rgba(255,255,255,.8);
+}
+.hero .text h2{
+ color: #ffffff;
+ font-size: 30px;
+ margin-bottom: 0;
+}
+.heading-section{
+}
+.heading-section h2{
+ color: #000000;
+ font-size: 28px;
+ margin-top: 0;
+ line-height: 1.4;
+}
+.heading-section .subheading{
+ margin-bottom: 20px !important;
+ display: inline-block;
+ font-size: 13px;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: rgba(0,0,0,.4);
+ position: relative;
+}
+.heading-section .subheading::after{
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: -10px;
+ content: '';
+ width: 100%;
+ height: 2px;
+ background: #f3a333;
+ margin: 0 auto;
+}
+.heading-section-white{
+ color: rgba(255,255,255,.8);
+}
+.heading-section-white h2{
+ font-size: 28px;
+ line-height: 1;
+ padding-bottom: 0;
+}
+.heading-section-white h2{
+ color: #ffffff;
+}
+.heading-section-white .subheading{
+ margin-bottom: 0;
+ display: inline-block;
+ font-size: 13px;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: rgba(255,255,255,.4);
+}
+.icon{
+ text-align: center;
+}
+.icon img{
+}
+.text-services{
+ padding: 10px 10px 0;
+ text-align: center;
+}
+.text-services h3{
+ font-size: 20px;
+}
+.text-services .meta{
+ text-transform: uppercase;
+ font-size: 14px;
+}
+.img{
+ width: 100%;
+ height: auto;
+ position: relative;
+}
+.img .icon{
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ margin-top: -25px;
+}
+.img .icon a{
+ display: block;
+ width: 60px;
+ position: absolute;
+ top: 0;
+ left: 50%;
+ margin-left: -25px;
+}
+.counter-text{
+ text-align: center;
+}
+.counter-text .num{
+ display: block;
+ color: #ffffff;
+ font-size: 34px;
+ font-weight: 700;
+}
+.counter-text .name{
+ display: block;
+ color: rgba(255,255,255,.9);
+ font-size: 13px;
+}
+.footer{
+ color: rgba(255,255,255,.5);
+}
+.footer .heading{
+ color: #ffffff;
+ font-size: 20px;
+}
+.footer ul{
+ margin: 0;
+ padding: 0;
+}
+.footer ul li{
+ list-style: none;
+ margin-bottom: 10px;
+}
+.footer ul li a{
+ color: rgba(255,255,255,1);
+}
+@media screen and (max-width: 500px) {
+ .icon{
+ text-align: left;
+ }
+ .text-services{
+ padding-left: 0;
+ padding-right: 20px;
+ text-align: left;
+ }
+}
\ No newline at end of file
diff --git a/packages/worker/src/utilities/email.js b/packages/worker/src/utilities/email.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/worker/src/utilities/fileSystem.js b/packages/worker/src/utilities/fileSystem.js
new file mode 100644
index 0000000000..7df21db695
--- /dev/null
+++ b/packages/worker/src/utilities/fileSystem.js
@@ -0,0 +1,5 @@
+const { readFileSync } = require("fs")
+
+exports.readStaticFile = path => {
+ return readFileSync(path, "utf-8")
+}
\ No newline at end of file