summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmodels/EventGroup.js10
-rwxr-xr-xroutes.js118
-rw-r--r--views/emails/subscribed.handlebars9
-rwxr-xr-xviews/eventgroup.handlebars35
4 files changed, 171 insertions, 1 deletions
diff --git a/models/EventGroup.js b/models/EventGroup.js
index 6d2893b..c70ef95 100755
--- a/models/EventGroup.js
+++ b/models/EventGroup.js
@@ -1,5 +1,12 @@
const mongoose = require('mongoose');
+const Subscriber = new mongoose.Schema({
+ email: {
+ type: String,
+ trim: true
+ },
+})
+
const EventGroupSchema = new mongoose.Schema({
id: {
type: String,
@@ -43,7 +50,8 @@ const EventGroupSchema = new mongoose.Schema({
trim: true,
default: true
},
- events: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }]
+ events: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
+ subscribers: [Subscriber],
});
module.exports = mongoose.model('EventGroup', EventGroupSchema);
diff --git a/routes.js b/routes.js
index 00823cb..542cd65 100755
--- a/routes.js
+++ b/routes.js
@@ -741,6 +741,46 @@ router.post('/newevent', async (req, res) => {
}
});
}
+ // If the event was added to a group, send an email to any group
+ // subscribers
+ if (event.eventGroup && sendEmails) {
+ EventGroup.findOne({ _id: event.eventGroup._id })
+ .then((eventGroup) => {
+ const subscribers = eventGroup.subscribers.reduce((acc, current) => {
+ if (acc.includes(current.email)) {
+ return acc;
+ }
+ return [ current.email, ...acc ];
+ }, []);
+ subscribers.forEach(emailAddress => {
+ req.app.get('hbsInstance').renderView('./views/emails/eventgroupupdated.handlebars', { siteName, siteLogo, domain, eventID: req.params.eventID, eventGroupName: eventGroup.name, eventName: event.name, eventID: event.id, eventGroupID: eventGroup.id, emailAddress: encodeURIComponent(emailAddress), cache: true, layout: 'email.handlebars' }, function (err, html) {
+ const msg = {
+ to: emailAddress,
+ from: {
+ name: siteName,
+ email: contactEmail,
+ },
+ subject: `${siteName}: New event in ${eventGroup.name}`,
+ html,
+ };
+ switch (mailService) {
+ case 'sendgrid':
+ sgMail.send(msg).catch(e => {
+ console.error(e.toString());
+ res.status(500).end();
+ });
+ break;
+ case 'nodemailer':
+ nodemailerTransporter.sendMail(msg).catch(e => {
+ console.error(e.toString());
+ res.status(500).end();
+ });
+ break;
+ }
+ });
+ });
+ });
+ }
res.writeHead(302, {
'Location': '/' + eventID + '?e=' + editToken
});
@@ -1533,6 +1573,84 @@ router.post('/removeattendee/:eventID/:attendeeID', (req, res) => {
});
});
+/*
+ * Create an email subscription on an event group.
+ */
+router.post('/subscribe/:eventGroupID', (req, res) => {
+ const subscriber = {
+ email: req.body.emailAddress,
+ };
+ if (!subscriber.email) {
+ return res.sendStatus(500);
+ }
+
+ EventGroup.findOne(({
+ id: req.params.eventGroupID,
+ }))
+ .then((eventGroup) => {
+ if (!eventGroup) {
+ return res.sendStatus(404);
+ }
+ eventGroup.subscribers.push(subscriber);
+ eventGroup.save();
+ if (sendEmails) {
+ req.app.get('hbsInstance').renderView('./views/emails/subscribed.handlebars', { eventGroupName: eventGroup.name, eventGroupID: eventGroup.id, emailAddress: encodeURIComponent(subscriber.email), siteName, siteLogo, domain, cache: true, layout: 'email.handlebars' }, function (err, html) {
+ const msg = {
+ to: subscriber.email,
+ from: {
+ name: siteName,
+ email: contactEmail,
+ },
+ subject: `${siteName}: You have subscribed to an event group`,
+ html,
+ };
+ switch (mailService) {
+ case 'sendgrid':
+ sgMail.send(msg).catch(e => {
+ console.error(e.toString());
+ res.status(500).end();
+ });
+ break;
+ case 'nodemailer':
+ nodemailerTransporter.sendMail(msg).catch(e => {
+ console.error(e.toString());
+ res.status(500).end();
+ });
+ break;
+ }
+ });
+ }
+ return res.redirect(`/group/${eventGroup.id}`)
+ })
+ .catch((error) => {
+ addToLog("addSubscription", "error", "Attempt to subscribe " + req.body.emailAddress + " to event group " + req.params.eventGroupID + " failed with error: " + error);
+ return res.sendStatus(500);
+ });
+});
+
+/*
+ * Delete an existing email subscription on an event group.
+ */
+router.get('/unsubscribe/:eventGroupID', (req, res) => {
+ const email = req.query.email;
+ console.log(email);
+ if (!email) {
+ return res.sendStatus(500);
+ }
+
+ EventGroup.update(
+ { id: req.params.eventGroupID },
+ { $pull: { subscribers: { email } } }
+ )
+ .then(response => {
+ return res.redirect('/');
+ })
+ .catch((error) => {
+ addToLog("removeSubscription", "error", "Attempt to unsubscribe " + req.query.email + " from event group " + req.params.eventGroupID + " failed with error: " + error);
+ return res.sendStatus(500);
+ });
+});
+
router.post('/post/comment/:eventID', (req, res) => {
let commentID = nanoid();
const newComment = {
diff --git a/views/emails/subscribed.handlebars b/views/emails/subscribed.handlebars
new file mode 100644
index 0000000..3a3c4ad
--- /dev/null
+++ b/views/emails/subscribed.handlebars
@@ -0,0 +1,9 @@
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">You have been subscribed to the event group '{{eventGroupName}}' on {{siteName}}.</p>
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin:
+0; Margin-bottom: 15px;">You will receive emails when new events are added to
+the group, and can unsubscribe at any time.</p>
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Love,</p>
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{{siteName}}</p>
+<hr/>
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;"><strong>Hold up - I don't want to receive these emails any more!</strong></p>
+<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">If you didn't subscribe yourself to this event group on {{siteName}}, someone may have accidentally typed your email instead of theirs. <a href="https://{{domain}}/unsubscribe/{{eventGroupID}}?email={{emailAddress}}">Click here to unsubscribe</a>.</p>
diff --git a/views/eventgroup.handlebars b/views/eventgroup.handlebars
index 25b29d0..9afee2c 100755
--- a/views/eventgroup.handlebars
+++ b/views/eventgroup.handlebars
@@ -76,6 +76,11 @@
</div>
<div class="col-lg-3" id="eventActions">
<aside class="btn-group-vertical d-flex" role="group" aria-label="Event actions">
+ <button type="button" class="btn btn-outline-secondary btn-sm"
+ data-event-id="{{eventGroupData.id}}" data-toggle="modal"
+ data-target="#subscribeModal">
+ <i class="fas fa-envelope"></i> Subscribe to updates
+ </button>
<button type="button" id="exportICS" class="btn btn-outline-secondary
btn-sm" data-event-id="{{eventGroupData.id}}">
<i class="fas fa-download"></i> Export as ICS
@@ -157,6 +162,36 @@
{{/if}}
+<div class="modal fade" id="subscribeModal" tabindex="-1" role="dialog"
+ aria-labelledby="subscribeModalLabel" aria-hidden="true">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title" id="subscribeModalLabel">Subscribe to '{{eventGroupData.name}}'</h5>
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">&times;</span>
+ </button>
+ </div>
+ <form id="subscribeForm" action="/subscribe/{{eventGroupData.id}}" method="post">
+ <div class="modal-body">
+ <div class="form-group">
+ <p class="form-text small">Enter your email address to receive updates
+ whenever a new event is created in this group.</p>
+ </div>
+ <div class="form-group">
+ <input type="email" class="form-control" id="emailAddress"
+ name="emailAddress" placeholder="email@example.com" data-validation="required">
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="submit" class="btn btn-success">Subscribe</button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
+
<div class="modal fade" id="editTokenModal" tabindex="-1" role="dialog" aria-labelledby="editTokenModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">