summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json18
-rwxr-xr-xpackage.json1
-rwxr-xr-xpublic/css/style.css16
-rwxr-xr-xroutes.js43
-rwxr-xr-xviews/event.handlebars169
5 files changed, 177 insertions, 70 deletions
diff --git a/package-lock.json b/package-lock.json
index 2fdd84e..ab0ba0a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2810,6 +2810,24 @@
"rrule": "2.4.1"
}
},
+ "ical-generator": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-1.9.2.tgz",
+ "integrity": "sha512-z3OLKk/b9TbyOLKOIMpjLJ3u7gq/tXPNsH5uOz+Ai3sqn2kcpjFlZUafKrlduwZn3Xu3fV5WqZ2dddZFrhQTfg==",
+ "requires": {
+ "moment-timezone": "^0.5.27"
+ },
+ "dependencies": {
+ "moment-timezone": {
+ "version": "0.5.27",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
+ "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==",
+ "requires": {
+ "moment": ">= 2.9.0"
+ }
+ }
+ }
+ },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
diff --git a/package.json b/package.json
index fff1c2d..04f8a15 100755
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"greenlock": "^2.6.7",
"greenlock-express": "^2.6.7",
"ical": "^0.6.0",
+ "ical-generator": "^1.9.2",
"jimp": "^0.6.0",
"jsonwebtoken": "^8.4.0",
"marked": "^0.7.0",
diff --git a/public/css/style.css b/public/css/style.css
index 4085875..e55f07a 100755
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -115,6 +115,11 @@ body, html {
text-transform:capitalize;
}
+#eventActions {
+ padding-left: 0;
+ margin-top: 1rem;
+}
+
/*
.location, .eventLink {
display: flex;
@@ -162,10 +167,6 @@ body, html {
margin-left: 5px;
}
-.eventInformationAction:not(#copyEventLink) {
- margin-top: 0.25rem;
-}
-
.commentContainer {
background: #fafafa;
border-radius: 5px;
@@ -308,6 +309,13 @@ body, html {
}
}
+@media (min-width: 1120px) {
+ #eventActions {
+ margin-top: 0;
+ padding-left: 1rem;
+ }
+}
+
@media (min-width: 577px) {
#sidebar {
border-right: 2px solid #e0e0e0;
diff --git a/routes.js b/routes.js
index ee723e2..f2bca02 100755
--- a/routes.js
+++ b/routes.js
@@ -62,6 +62,11 @@ render_plain = function () {
}
const ical = require('ical');
+const icalGenerator = require('ical-generator');
+const cal = icalGenerator({
+ domain: 'gath.io',
+ name: 'Gathio'
+});
const sgMail = require('@sendgrid/mail');
@@ -408,6 +413,42 @@ router.get('/group/:eventGroupID', (req, res) => {
});
})
+router.get('/exportevent/:eventID', (req, res) => {
+ Event.findOne({
+ id: req.params.eventID
+ })
+ .populate('eventGroup')
+ .then((event) => {
+ if (event) {
+ const icalEvent = 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
+ });
+
+ let string = cal.toString();
+ console.log(string)
+ res.send(string);
+ }
+ })
+ .catch((err) => {
+ addToLog("exportEvent", "error", "Attempt to export event " + req.params.eventID + " failed with error: " + err);
+ console.log(err)
+ res.status(404);
+ res.render('404', { url: req.url });
+ return;
+ });
+})
+
// BACKEND ROUTES
//router.post('/login',
@@ -529,7 +570,7 @@ router.post('/importevent', (req, res) => {
image: '',
creatorEmail: creatorEmail,
url: '',
- hostName: importedEventData.organizer ? importedEventData.organizer.params.CN : "",
+ hostName: importedEventData.organizer ? importedEventData.organizer.params.CN.replace(/["]+/g, '') : "",
viewPassword: '',
editPassword: '',
editToken: editToken,
diff --git a/views/event.handlebars b/views/event.handlebars
index 4d0cf28..7a57783 100755
--- a/views/event.handlebars
+++ b/views/event.handlebars
@@ -8,82 +8,92 @@
<h3 id="eventName">{{eventData.name}}</h3>
</div>
{{#if editingEnabled}}
- <div class="col-lg-2 ml-2 edit-buttons">
+ <div class="col-lg-3 ml-2 edit-buttons">
<div class="btn-group" role="group" aria-label="Event controls">
- <button type="button" id="editEvent" class="btn btn-success" data-toggle="modal" data-target="#editModal" {{#if eventHasConcluded}}disabled{{/if}}><i class="fas fa-edit"></i></button>
- <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i></button>
+ <button type="button" id="editEvent" class="btn btn-success" data-toggle="modal" data-target="#editModal" {{#if eventHasConcluded}}disabled{{/if}}><i class="fas fa-edit"></i> Edit</button>
+ <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i> Delete</button>
</div>
</div>
{{/if}}
</div>
-<div class="card mt-4 mb-4">
- <div class="card-body">
- <ul class="fa-ul eventInformation">
- <li>
- <span class="fa-li">
- <i class="fas fa-map-marker-alt"></i>
- </span>
- {{eventData.location}}<br />
- <a target="_blank" href="http://maps.google.com/?q={{parsedLocation}}" class="eventInformationAction btn btn-outline-secondary btn-sm">
+<div class="container my-4 pr-0">
+ <div class="row">
+ <div class="col-lg-9 card">
+ <div class="card-body">
+ <ul class="fa-ul eventInformation">
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-map-marker-alt"></i>
+ </span>
+ {{eventData.location}}
+ </li>
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-calendar-day"></i>
+ </span>
+ {{{displayDate}}}
+ <br>
+ <span class="text-muted">
+ {{#if eventHasBegun}}{{#unless eventHasConcluded}}Started {{else}}Ended {{/unless}}{{/if}}{{fromNow}}
+ </span>
+ </li>
+ {{#if eventHasHost}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-user-circle"></i>
+ </span>
+ <span class="text-muted">Hosted by</span> {{eventData.hostName}}
+ </li>
+ {{/if}}
+ {{#if eventData.eventGroup}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-calendar-alt"></i>
+ </span>
+ <span class="text-muted">Part of</span> <a href="/group/{{eventData.eventGroup.id}}">{{eventData.eventGroup.name}}</a>
+ </li>
+ {{/if}}
+ {{#if eventData.url}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-link"></i>
+ </span>
+ <a href="{{eventData.url}}">
+ {{eventData.url}}
+ </a>
+ </li>
+ {{/if}}
+ <li>
+ <span class="fa-li">
+ <i class="fas fa-fw fa-share-square"></i>
+ </span>
+ <a href="https://gath.io/{{eventData.id}}">gath.io/{{eventData.id}}</a>
+ <button type="button" id="copyEventLink" class="eventInformationAction btn btn-outline-secondary btn-sm" data-clipboard-text="https://gath.io/{{eventData.id}}">
+ <i class="fas fa-copy"></i> Copy
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <div class="col-lg-3" id="eventActions">
+ <aside class="btn-group-vertical d-flex" role="group" aria-label="Event actions">
+ <a href="http://www.google.com/calendar/event?action=TEMPLATE&dates={{parsedStart}}%2F{{parsedEnd}}&text={{escapedName}}&location={{parsedLocation}}&ctz={{timezone}}" class="btn btn-outline-secondary btn-sm">
+ <i class="far fa-calendar-plus"></i> Add to Google Calendar
+ </a>
+ <button type="button" id="exportICS" class="btn btn-outline-secondary btn-sm" data-event-id="{{eventData.id}}">
+ <i class="fas fa-download"></i> Export as ICS
+ </button>
+ <a target="_blank" href="http://maps.google.com/?q={{parsedLocation}}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-map-marked"></i> Show on Google Maps
</a>
- &nbsp;
- <a target="_blank" href="https://www.openstreetmap.org/search?query={{parsedLocation}}" class="eventInformationAction btn btn-outline-secondary btn-sm">
+ <a target="_blank" href="https://www.openstreetmap.org/search?query={{parsedLocation}}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-map-marked"></i> Show on OpenStreetMap
</a>
- </li>
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-calendar-day"></i>
- </span>
- {{{displayDate}}}
- <br>
- <span class="text-muted">
- {{#if eventHasBegun}}{{#unless eventHasConcluded}}Started {{else}}Ended {{/unless}}{{/if}}{{fromNow}}
- </span>
- <br />
- <a href="http://www.google.com/calendar/event?action=TEMPLATE&dates={{parsedStart}}%2F{{parsedEnd}}&text={{escapedName}}&location={{parsedLocation}}&ctz={{timezone}}" class="eventInformationAction btn btn-outline-secondary btn-sm">
- <i class="far fa-calendar-plus"></i> Add to Google Calendar
- </a>
- </li>
- {{#if eventHasHost}}
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-user-circle"></i>
- </span>
- <span class="text-muted">Hosted by</span> {{eventData.hostName}}
- </li>
- {{/if}}
- {{#if eventData.eventGroup}}
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-calendar-alt"></i>
- </span>
- <span class="text-muted">Part of</span> <a href="/group/{{eventData.eventGroup.id}}">{{eventData.eventGroup.name}}</a>
- </li>
- {{/if}}
- {{#if eventData.url}}
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-link"></i>
- </span>
- <a href="{{eventData.url}}">
- {{eventData.url}}
- </a>
- </li>
- {{/if}}
- <li>
- <span class="fa-li">
- <i class="fas fa-fw fa-share-square"></i>
- </span>
- <a href="https://gath.io/{{eventData.id}}">gath.io/{{eventData.id}}</a>
- <button type="button" id="copyEventLink" class="eventInformationAction btn btn-outline-secondary btn-sm" data-clipboard-text="https://gath.io/{{eventData.id}}">
- <i class="fas fa-copy"></i> Copy
- </button>
- </li>
- </ul>
+ </aside>
+ </div>
</div>
</div>
+
{{#if eventHasConcluded}}
<div class="alert alert-warning mb-4" role="alert">
This event has concluded. It can no longer be edited, and will be automatically deleted <span class="daysToDeletion"></span>.
@@ -375,6 +385,29 @@
})
$(document).ready(function() {
+ // 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);
+ }
+
$.uploadPreview({
input_field: "#image-upload",
preview_box: "#image-preview",
@@ -394,6 +427,12 @@
{{/if}}
new ClipboardJS('#copyEventLink');
autosize($('textarea'));
+ $("#exportICS").click(function(){
+ let eventID = $(this).attr('data-event-id');
+ $.get('/exportevent/' + eventID, function(response) {
+ downloadFile(response, eventID + '.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);