From 80b0f2d4f76af2667507d69d25b06f1f9374f56a Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Sat, 23 Apr 2022 16:09:57 +0100 Subject: feat: Add event group ical feeds --- helpers.js | 38 +++++++++++++- routes.js | 75 +++++++++++++++++---------- views/eventgroup.handlebars | 122 ++++++++++++++++++++++++++++++++------------ 3 files changed, 176 insertions(+), 59 deletions(-) diff --git a/helpers.js b/helpers.js index 168ef8c..f628a11 100644 --- a/helpers.js +++ b/helpers.js @@ -1,6 +1,10 @@ +const domain = require('./config/domain.js').domain; +const siteName = require('./config/domain.js').sitename; + const mongoose = require('mongoose'); const Log = mongoose.model('Log'); var moment = require('moment-timezone'); +const icalGenerator = require('ical-generator'); // LOGGING @@ -14,6 +18,38 @@ function addToLog(process, status, message) { logEntry.save().catch(() => { console.log("Error saving log entry!") }); } +function exportIcal(events) { + // Create a new icalGenerator... generator + const cal = icalGenerator({ + domain: domain, + name: siteName + }); + if (events instanceof Array === false) { + events = [ events ]; + } + events.forEach(event => { + // Add the event to the generator + cal.createEvent({ + start: moment.tz(event.start, event.timezone), + end: moment.tz(event.end, event.timezone), + timezone: event.timezone, + timestamp: moment(), + summary: event.name, + description: event.description, + organizer: { + name: event.hostName || "Anonymous", + email: event.creatorEmail || 'anonymous@anonymous.com', + }, + location: event.location, + url: domain + '/' + event.id + }); + }); + // Stringify it! + const string = cal.toString(); + return string; +} + module.exports = { - addToLog + addToLog, + exportIcal, } diff --git a/routes.js b/routes.js index 81242e0..9a29bab 100755 --- a/routes.js +++ b/routes.js @@ -82,8 +82,7 @@ function render_plain() { } const ical = require('ical'); -const icalGenerator = require('ical-generator'); - +const {exportIcal} = require('./helpers.js'); const sgMail = require('@sendgrid/mail'); const nodemailer = require("nodemailer"); @@ -548,7 +547,6 @@ router.get('/group/:eventGroupID', (req, res) => { } } } - console.log(events); let metadata = { title: eventGroup.name, description: marked.parse(eventGroup.description, { renderer: render_plain() }).split(" ").splice(0, 40).join(" ").trim(), @@ -586,6 +584,31 @@ router.get('/group/:eventGroupID', (req, res) => { }); }) +router.get('/group/:eventGroupID/feed.ics', (req, res) => { + EventGroup.findOne({ + id: req.params.eventGroupID + }) + .lean() // Required, see: https://stackoverflow.com/questions/59690923/handlebars-access-has-been-denied-to-resolve-the-property-from-because-it-is + .then(async (eventGroup) => { + if (eventGroup) { + let events = await Event.find({ eventGroup: eventGroup._id }).lean().sort('start'); + const string = exportIcal(events); + res.writeHead(200, { + 'Content-Type': 'text/calendar', + 'Content-Length': string.length, + }); + return res.write(string); + } + }) + .catch((err) => { + addToLog("eventGroupFeed", "error", "Attempt to display event group feed for " + req.params.eventGroupID + " failed with error: " + err); + console.log(err) + res.status(404); + res.render('404', { url: req.url }); + return; + }); +}); + router.get('/exportevent/:eventID', (req, res) => { Event.findOne({ id: req.params.eventID @@ -593,28 +616,7 @@ router.get('/exportevent/:eventID', (req, res) => { .populate('eventGroup') .then((event) => { if (event) { - // Create a new icalGenerator... generator - const cal = icalGenerator({ - domain: domain, - name: siteName - }); - // Add the event to it - cal.createEvent({ - start: moment.tz(event.start, event.timezone), - end: moment.tz(event.start, event.timezone), - timezone: event.timezone, - timestamp: moment(), - summary: event.name, - description: event.description, - organizer: { - name: event.hostName ? event.hostName : "Anonymous", - email: event.creatorEmail - }, - location: event.location, - url: 'https://gath.io/' + event.id - }); - // Stringify it! - let string = cal.toString(); + const string = exportIcal([ event ]); res.send(string); } }) @@ -625,7 +627,28 @@ router.get('/exportevent/:eventID', (req, res) => { res.render('404', { url: req.url }); return; }); -}) +}); + +router.get('/exportgroup/:eventGroupID', (req, res) => { + EventGroup.findOne({ + id: req.params.eventGroupID + }) + .lean() // Required, see: https://stackoverflow.com/questions/59690923/handlebars-access-has-been-denied-to-resolve-the-property-from-because-it-is + .then(async (eventGroup) => { + if (eventGroup) { + let events = await Event.find({ eventGroup: eventGroup._id }).lean().sort('start'); + const string = exportIcal(events); + res.send(string); + } + }) + .catch((err) => { + addToLog("exportEvent", "error", "Attempt to export event group " + req.params.eventGroupID + " failed with error: " + err); + console.log(err) + res.status(404); + res.render('404', { url: req.url }); + return; + }); +}); // BACKEND ROUTES diff --git a/views/eventgroup.handlebars b/views/eventgroup.handlebars index d7726bd..e659915 100755 --- a/views/eventgroup.handlebars +++ b/views/eventgroup.handlebars @@ -23,39 +23,63 @@ Welcome to your event group! We've just sent you an email with your secret editing link, which you can also see in the address bar above. Haven't got the email? Check your spam or junk folder. To share your event group, use the link you can see just below this message - that way your attendees won't be able to edit or delete your event group! {{/if}} -
-
- +
+
+ +
+ +
@@ -224,11 +248,22 @@ $("#eventGroupImagePreview").css("background-size", "cover"); $("#eventGroupImagePreview").css("background-position", "center center"); new ClipboardJS('#copyEventLink'); + new ClipboardJS('#copyFeedLink'); autosize($('textarea')); + $("#exportICS").click(function(){ + let eventGroupID = $(this).attr('data-event-id'); + $.get('/exportgroup/' + eventGroupID, function(response) { + downloadFile(response, eventGroupID + '.ics'); + }) + }) $("#copyEventLink").click(function(){ $(this).html(' Copied!'); setTimeout(function(){ $("#copyEventLink").html(' Copy');}, 5000); }); + $("#copyFeedLink").click(function(){ + $(this).html(' Copied!'); + setTimeout(function(){ $("#copyFeedLink").html(' Copy');}, 5000); + }); $('#verifyTokenForm').on('submit', function(e) { e.preventDefault(); @@ -254,5 +289,28 @@ $('#editModal').modal('hide'); }) + // From https://davidwalsh.name/javascript-download + function downloadFile(data, fileName, type="text/plain") { + // Create an invisible A element + const a = document.createElement("a"); + a.style.display = "none"; + document.body.appendChild(a); + + // Set the HREF to a Blob representation of the data to be downloaded + a.href = window.URL.createObjectURL( + new Blob([data], { type }) + ); + + // Use download attribute to set set desired file name + a.setAttribute("download", fileName); + + // Trigger the download by simulating click + a.click(); + + // Cleanup + window.URL.revokeObjectURL(a.href); + document.body.removeChild(a); + } + }); -- cgit v1.2.3