summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Kabo <raphaelkabo@gmail.com>2022-04-23 16:09:57 +0100
committerRaphael Kabo <raphaelkabo@gmail.com>2022-04-23 16:09:57 +0100
commit80b0f2d4f76af2667507d69d25b06f1f9374f56a (patch)
treefafcecb1df44388ab2c12ac04d5a496a6d23ff15
parent699ec4a110c1f67dcf2a3205d8c56579b2da898f (diff)
feat: Add event group ical feeds
-rw-r--r--helpers.js38
-rwxr-xr-xroutes.js75
-rwxr-xr-xviews/eventgroup.handlebars122
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!
</div>
{{/if}}
-<div class="card mt-4 mb-4">
- <div class="card-body">
- <ul class="fa-ul eventInformation">
- {{#if eventGroupHasHost}}
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-user-circle"></i>
- </span>
- <span class="text-muted">Hosted by</span> {{eventGroupData.hostName}}
- </li>
- {{/if}}
- {{#if eventGroupData.url}}
- <li>
- <span class="fa-li">
- <i class="fas fa-link"></i>
- </span>
- <a href="{{eventGroupData.url}}">
- {{eventGroupData.url}}
- </a>
- </li>
- {{/if}}
- <li>
- <span class="fa-li">
- <i class="fas fa-share-square"></i>
- </span>
- <a href="https://{{domain}}/group/{{eventGroupData.id}}">
- {{domain}}/group/{{eventGroupData.id}}
- </a>
- <button type="button" id="copyEventLink" class="eventInformationAction btn btn-outline-secondary btn-sm" data-clipboard-text="https://{{domain}}/group/{{eventGroupData.id}}">
- <i class="fas fa-copy"></i> Copy
+<div class="container my-4 pr-0">
+ <div class="row">
+ <div class="col-lg-9 card p-0">
+ <div class="card">
+ <div class="card-body">
+ <ul class="fa-ul eventInformation">
+ {{#if eventGroupHasHost}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-user-circle"></i>
+ </span>
+ <span class="text-muted">Hosted by</span> {{eventGroupData.hostName}}
+ </li>
+ {{/if}}
+ {{#if eventGroupData.url}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-link"></i>
+ </span>
+ <a href="{{eventGroupData.url}}">
+ {{eventGroupData.url}}
+ </a>
+ </li>
+ {{/if}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-share-square"></i>
+ </span>
+ <a href="https://{{domain}}/group/{{eventGroupData.id}}">{{domain}}/group/{{eventGroupData.id}}</a>
+ <button type="button" id="copyEventLink" class="eventInformationAction btn btn-outline-secondary btn-sm" data-clipboard-text="https://{{domain}}/group/{{eventGroupData.id}}">
+ <i class="fas fa-copy"></i> Copy
+ </button>
+ </li>
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-rss"></i>
+ </span>
+ <a
+ href="/group/{{eventGroupData.id}}/feed.ics">{{domain}}/group/{{eventGroupData.id}}/feed.ics</a>&nbsp;
+ <button type="button" id="copyFeedLink"
+ class="eventInformationAction btn btn-outline-secondary btn-sm"
+ data-clipboard-text="{{domain}}/group/{{eventGroupData.id}}/feed.ics">
+ <i class="fas fa-copy"></i> Copy
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div> <!-- /card -->
+ </div>
+ <div class="col-lg-3" id="eventActions">
+ <aside class="btn-group-vertical d-flex" role="group" aria-label="Event actions">
+ <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
</button>
- </li>
- </ul>
+ </aside>
+ </div>
</div>
</div>
@@ -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('<i class="fas fa-copy"></i> Copied!');
setTimeout(function(){ $("#copyEventLink").html('<i class="fas fa-copy"></i> Copy');}, 5000);
});
+ $("#copyFeedLink").click(function(){
+ $(this).html('<i class="fas fa-copy"></i> Copied!');
+ setTimeout(function(){ $("#copyFeedLink").html('<i class="fas fa-copy"></i> 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);
+ }
+
});
</script>