Skip to content

Commit

Permalink
Merge pull request #204 from HackGT/custom-email-subjects
Browse files Browse the repository at this point in the history
Implement custom email subjects
  • Loading branch information
petschekr committed Mar 7, 2018
2 parents 0a2be53 + ce23fb3 commit 1ab70dc
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 29 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 HackGT
Copyright (c) 2018 HackGT

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,4 @@ If you have some time and want to help us out with development, thank you! You c

## License

Copyright © 2017 HackGT. Released under the MIT license. See [LICENSE](LICENSE) for more information.
Copyright © 2018 HackGT. Released under the MIT license. See [LICENSE](LICENSE) for more information.
9 changes: 8 additions & 1 deletion client/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ <h2>Applicants</h2>
<h2>Settings</h2>

<form method="post" data-action="/">
<div class="row center">
<input type="submit" class="btn" value="Save" />
</div>

<div class="row" style="text-align: center;">
<div class="col-6">
<label for="teams-enabled">Enable teams?</label>
Expand Down Expand Up @@ -304,6 +308,9 @@ <h4>Edit email content</h4>
{{/each}}
</optgroup>
</select>
<label for="email-subject">Subject:</label>
<input type="text" id="email-subject" />
<label for="email-content">Body:</label>
<textarea id="email-content"></textarea>
<h5>Rendered HTML and text:</h5>
<section id="email-rendered"></section>
Expand Down Expand Up @@ -361,7 +368,7 @@ <h4><code>config.json</code> options</h4>
</div>

<div class="row center">
<input type="submit" class="btn" value="Update" />
<input type="submit" class="btn" value="Save" />
</div>
</form>
</section>
Expand Down
26 changes: 19 additions & 7 deletions client/js/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ sendAcceptancesButton.addEventListener("click", async e => {
declare let SimpleMDE: any;

const emailTypeSelect = document.getElementById("email-type") as HTMLSelectElement;
const emailSubject = document.getElementById("email-subject") as HTMLInputElement;
let emailRenderedArea: HTMLElement | ShadowRoot = document.getElementById("email-rendered") as HTMLElement;
if (document.head.attachShadow) {
// Browser supports Shadow DOM
Expand Down Expand Up @@ -616,8 +617,9 @@ async function emailTypeChange(): Promise<void> {

// Load editor content via AJAX
try {
let content = (await fetch(`/api/settings/email_content/${emailTypeSelect.value}`, { credentials: "same-origin" }).then(checkStatus).then(parseJSON)).content as string;
markdownEditor.value(content);
let emailSettings: { subject: string; content: string } = await fetch(`/api/settings/email_content/${emailTypeSelect.value}`, { credentials: "same-origin" }).then(checkStatus).then(parseJSON);
emailSubject.value = emailSettings.subject;
markdownEditor.value(emailSettings.content);
}
catch {
markdownEditor.value("Couldn't retrieve email content");
Expand Down Expand Up @@ -645,14 +647,23 @@ function parseDateTime(dateTime: string) {
let digits = dateTime.split(/\D+/).map(num => parseInt(num, 10));
return new Date(digits[0], digits[1] - 1, digits[2], digits[3], digits[4], digits[5] || 0, digits[6] || 0);
}
let settingsUpdateButton = document.querySelector("#settings input[type=submit]") as HTMLInputElement;
let settingsUpdateButtons = document.querySelectorAll("#settings input[type=submit]") as NodeListOf<HTMLInputElement>;
let settingsForm = document.querySelector("#settings form") as HTMLFormElement;
settingsUpdateButton.addEventListener("click", e => {
for (let i = 0; i < settingsUpdateButtons.length; i++) {
settingsUpdateButtons[i].addEventListener("click", settingsUpdate);
}
function settingsUpdateButtonDisabled(disabled: boolean) {
for (let i = 0; i < settingsUpdateButtons.length; i++) {
settingsUpdateButtons[i].disabled = disabled;
}
}

function settingsUpdate(e: MouseEvent) {
if (!settingsForm.checkValidity() || !settingsForm.dataset.action) {
return;
}
e.preventDefault();
settingsUpdateButton.disabled = true;
settingsUpdateButtonDisabled(true);

let teamsEnabledData = new FormData();
teamsEnabledData.append("enabled", (document.getElementById("teams-enabled") as HTMLInputElement).checked ? "true" : "false");
Expand Down Expand Up @@ -700,6 +711,7 @@ settingsUpdateButton.addEventListener("click", e => {
}

let emailContentData = new FormData();
emailContentData.append("subject", emailSubject.value);
emailContentData.append("content", markdownEditor.value());

const defaultOptions: RequestInit = {
Expand Down Expand Up @@ -739,9 +751,9 @@ settingsUpdateButton.addEventListener("click", e => {
window.location.reload();
}).catch(async (err: Error) => {
await sweetAlert("Oh no!", err.message, "error");
settingsUpdateButton.disabled = false;
settingsUpdateButtonDisabled(false);
});
});
}

//
// Graphs
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "registration",
"version": "1.12.4",
"version": "1.12.5",
"description": "TBD",
"main": "server/app.js",
"scripts": {
Expand Down Expand Up @@ -51,7 +51,7 @@
"json-schema-to-typescript": "^4.4.0",
"json2csv": "^3.7.3",
"marked": "^0.3.9",
"moment": "^2.18.1",
"moment": "^2.21.0",
"moment-timezone": "^0.5.13",
"mongoose": "^4.10.3",
"morgan": "^1.8.2",
Expand Down
5 changes: 5 additions & 0 deletions server/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ import * as marked from "marked";
const striptags = require("striptags");
import { IUser, Team, IFormItem } from "./schema";

export const defaultEmailSubjects = {
apply: `[${config.eventName}] - Thank you for applying!`,
accept: `[${config.eventName}] - You've been accepted!`,
attend: `[${config.eventName}] - Thank you for RSVPing!`
};
interface IMailObject {
to: string;
from: string;
Expand Down
30 changes: 26 additions & 4 deletions server/routes/api/settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as express from "express";

import {
getSetting, updateSetting, setDefaultSettings, renderEmailHTML, renderEmailText
getSetting, updateSetting, setDefaultSettings, renderEmailHTML, renderEmailText, defaultEmailSubjects
} from "../../common";
import {
isAdmin, uploadHandler
Expand Down Expand Up @@ -180,19 +180,41 @@ settingsRoutes.route("/branch_roles")
settingsRoutes.route("/email_content/:type")
.get(isAdmin, async (request, response) => {
let content: string;
let subject: string;
try {
content = await getSetting<string>(`${request.params.type}-email`, false);
}
catch (err) {
catch {
// Content not set yet
content = "";
}
try {
subject = await getSetting<string>(`${request.params.type}-email-subject`, false);
}
catch {
// Subject not set yet
let type: string = request.params.type;
if (type.match(/-apply$/)) {
subject = defaultEmailSubjects.apply;
}
else if (type.match(/-accept$/)) {
subject = defaultEmailSubjects.accept;
}
else if (type.match(/-attend$/)) {
subject = defaultEmailSubjects.attend;
}
else {
subject = "";
}
}

response.json({ content });
response.json({ subject, content });
})
.put(isAdmin, uploadHandler.any(), async (request, response) => {
let content = request.body.content as string;
let subject: string = request.body.subject;
let content: string = request.body.content;
try {
await updateSetting<string>(`${request.params.type}-email-subject`, subject);
await updateSetting<string>(`${request.params.type}-email`, content);
response.json({
"success": true
Expand Down
28 changes: 21 additions & 7 deletions server/routes/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as moment from "moment-timezone";
import {
STORAGE_ENGINE,
formatSize,
config, getSetting, renderEmailHTML, renderEmailText, sendMailAsync
config, getSetting, renderEmailHTML, renderEmailText, sendMailAsync, defaultEmailSubjects
} from "../../common";
import {
MAX_FILE_SIZE, postParser, uploadHandler,
Expand Down Expand Up @@ -177,12 +177,19 @@ async function postApplicationBranchHandler(request: express.Request, response:
return item;
});
// Email the applicant to confirm
let type = requestType === ApplicationType.Application ? "apply" : "attend";
let emailSubject: string | null;
try {
emailSubject = await getSetting<string>(`${questionBranch.name}-${type}-email-subject`, false);
}
catch {
emailSubject = null;
}
let emailMarkdown: string;
try {
let type = requestType === ApplicationType.Application ? "apply" : "attend";
emailMarkdown = await getSetting<string>(`${questionBranch.name}-${type}-email`, false);
}
catch (err) {
catch {
// Content not set yet
emailMarkdown = "";
}
Expand All @@ -195,7 +202,7 @@ async function postApplicationBranchHandler(request: express.Request, response:
await sendMailAsync({
from: config.email.from,
to: user.email,
subject: `[${config.eventName}] - Thank you for applying!`,
subject: emailSubject || defaultEmailSubjects.apply,
html: emailHTML,
text: emailText
});
Expand All @@ -222,7 +229,7 @@ async function postApplicationBranchHandler(request: express.Request, response:
await sendMailAsync({
from: config.email.from,
to: user.email,
subject: `[${config.eventName}] - Thank you for RSVPing!`,
subject: emailSubject || defaultEmailSubjects.attend,
html: emailHTML,
text: emailText
});
Expand Down Expand Up @@ -337,11 +344,18 @@ userRoutes.route("/send_acceptances").post(isAdmin, async (request, response): P
let users = await User.find({ "accepted": true, "acceptedEmailSent": { $ne: true } });
for (let user of users) {
// Email the applicant about their acceptance
let emailSubject: string | null;
try {
emailSubject = await getSetting<string>(`${user.applicationBranch}-accept-email-subject`, false);
}
catch {
emailSubject = null;
}
let emailMarkdown: string;
try {
emailMarkdown = await getSetting<string>(`${user.applicationBranch}-accept-email`, false);
}
catch (err) {
catch {
// Content not set yet
emailMarkdown = "";
}
Expand All @@ -352,7 +366,7 @@ userRoutes.route("/send_acceptances").post(isAdmin, async (request, response): P
await sendMailAsync({
from: config.email.from,
to: user.email,
subject: `[${config.eventName}] - You've been accepted!`,
subject: emailSubject || defaultEmailSubjects.accept,
html: emailHTML,
text: emailText
});
Expand Down

0 comments on commit 1ab70dc

Please sign in to comment.