diff options
| -rwxr-xr-x | models/EventGroup.js | 10 | ||||
| -rwxr-xr-x | routes.js | 118 | ||||
| -rw-r--r-- | views/emails/subscribed.handlebars | 9 | ||||
| -rwxr-xr-x | views/eventgroup.handlebars | 35 | 
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); @@ -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">×</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">  | 
