From aa9c56eb70fc3202ccd84e72ff4b3ecf8e63500b Mon Sep 17 00:00:00 2001 From: Autumn Welles Date: Sun, 22 Sep 2019 13:11:07 -0400 Subject: Make attendee bubbles flexible width --- public/css/style.css | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 122c6ad..e9a0708 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -132,21 +132,15 @@ body, html { } .attendeesList > li { - height: 80px; - width: 80px; border: 4px solid #0ea130; - border-radius: 50%; + border-radius: 2em; + padding: .5em 1em; margin-right: 5px; margin-bottom: 5px; background: #57b76d; color: white; font-size: 0.95em; font-weight: bold; - overflow: hidden; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; } .expand { -- cgit v1.2.3 From 3ea394f87ee5f7db70ac0ce627ac2effdb319ec3 Mon Sep 17 00:00:00 2001 From: lowercasename Date: Sun, 22 Sep 2019 18:55:23 +0100 Subject: Tiny style changes, increased attendee name limit to 30 characters --- public/css/style.css | 2 +- views/event.handlebars | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index e9a0708..18f9b73 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -136,7 +136,7 @@ body, html { border-radius: 2em; padding: .5em 1em; margin-right: 5px; - margin-bottom: 5px; + margin-bottom: 10px; background: #57b76d; color: white; font-size: 0.95em; diff --git a/views/event.handlebars b/views/event.handlebars index a4d714d..d4637f4 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -119,7 +119,7 @@ {{#if eventAttendees}} {{else}} @@ -142,7 +142,7 @@
- +
-- cgit v1.2.3 From eddfe0389047ac1df5a8194d36c3bde1fcc05866 Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Mon, 30 Sep 2019 13:29:32 +0100 Subject: Event group functionality --- models/Event.js | 81 +++--- models/EventGroup.js | 49 ++++ public/css/style.css | 22 +- routes.js | 338 +++++++++++++++++++++++++- start.js | 1 + views/event.handlebars | 46 ++-- views/eventgroup.handlebars | 164 +++++++++++++ views/newevent.handlebars | 33 ++- views/partials/editeventgroupmodal.handlebars | 45 ++++ views/partials/editeventmodal.handlebars | 205 +++++++++------- views/partials/importeventform.handlebars | 1 + views/partials/neweventform.handlebars | 52 +++- views/partials/neweventgroupform.handlebars | 66 +++++ 13 files changed, 941 insertions(+), 162 deletions(-) create mode 100755 models/EventGroup.js create mode 100755 views/eventgroup.handlebars create mode 100644 views/partials/editeventgroupmodal.handlebars create mode 100755 views/partials/neweventgroupform.handlebars diff --git a/models/Event.js b/models/Event.js index 43af171..3c0bb8c 100755 --- a/models/Event.js +++ b/models/Event.js @@ -19,7 +19,7 @@ const ReplySchema = new mongoose.Schema({ id: { type: String, required: true, - unique: true, + unique: true, sparse: true }, author: { @@ -43,7 +43,7 @@ const CommentSchema = new mongoose.Schema({ id: { type: String, required: true, - unique: true, + unique: true, sparse: true }, author: { @@ -68,37 +68,37 @@ const EventSchema = new mongoose.Schema({ id: { type: String, required: true, - unique: true + unique: true }, type: { - type: String, - trim: true, + type: String, + trim: true, required: true - }, - name: { - type: String, - trim: true, + }, + name: { + type: String, + trim: true, required: true - }, - location: { - type: String, - trim: true, + }, + location: { + type: String, + trim: true, required: true - }, + }, start: { // Stored as a UTC timestamp - type: Date, - trim: true, + type: Date, + trim: true, required: true - }, + }, end: { // Stored as a UTC timestamp - type: Date, - trim: true, + type: Date, + trim: true, required: true - }, - timezone: { + }, + timezone: { type: String, default: 'Etc/UTC' - }, + }, description: { type: String, trim: true, @@ -121,34 +121,35 @@ const EventSchema = new mongoose.Schema({ trim: true }, viewPassword: { - type: String, - trim: true - }, + type: String, + trim: true + }, editPassword: { - type: String, - trim: true - }, + type: String, + trim: true + }, editToken: { - type: String, - trim: true, + type: String, + trim: true, minlength: 32, maxlength: 32 - }, + }, + eventGroup: { type: mongoose.Schema.Types.ObjectId, ref: 'EventGroup' }, usersCanAttend: { - type: Boolean, - trim: true, + type: Boolean, + trim: true, default: false - }, + }, showUsersList: { - type: Boolean, - trim: true, + type: Boolean, + trim: true, default: false - }, + }, usersCanComment: { - type: Boolean, - trim: true, + type: Boolean, + trim: true, default: false - }, + }, firstLoad: { type: Boolean, trim: true, diff --git a/models/EventGroup.js b/models/EventGroup.js new file mode 100755 index 0000000..336074c --- /dev/null +++ b/models/EventGroup.js @@ -0,0 +1,49 @@ +const mongoose = require('mongoose'); + +const EventGroupSchema = new mongoose.Schema({ + id: { + type: String, + required: true, + unique: true + }, + name: { + type: String, + trim: true, + required: true + }, + description: { + type: String, + trim: true, + required: true + }, + image: { + type: String, + trim: true + }, + url: { + type: String, + trim: true + }, + creatorEmail: { + type: String, + trim: true + }, + hostName: { + type: String, + trim: true + }, + editToken: { + type: String, + trim: true, + minlength: 32, + maxlength: 32 + }, + firstLoad: { + type: Boolean, + trim: true, + default: true + }, + events: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }] +}); + +module.exports = mongoose.model('EventGroup', EventGroupSchema); diff --git a/public/css/style.css b/public/css/style.css index 18f9b73..2202f8d 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -194,7 +194,7 @@ body, html { /* IMAGE UPLOAD FORM */ -#image-preview { +.image-preview { width: 100%; height: 200px; position: relative; @@ -204,14 +204,14 @@ body, html { border-radius: 5px; border: 1px dashed #ced4da; } -#image-preview input { +.image-preview input { line-height: 200px; font-size: 200px; position: absolute; opacity: 0; z-index: 10; } -#image-preview label { +.image-preview label { position: absolute; z-index: 5; opacity: 0.8; @@ -237,7 +237,8 @@ body, html { } #newEventFormContainer, -#importEventFormContainer { +#importEventFormContainer, +#newEventGroupFormContainer { display: none; } @@ -293,6 +294,10 @@ body, html { display: none; } +#eventGroupData { + display: none; +} + .edit-buttons { text-align: right; } @@ -324,3 +329,12 @@ body, html { margin-top: 0; } } + +.list-group-item-action:hover { + background-color: #d4edda; +} + +.code { + font-family: 'Courier New', Courier, monospace; + overflow-wrap: anywhere; +} \ No newline at end of file diff --git a/routes.js b/routes.js index 4e665ba..80014e5 100755 --- a/routes.js +++ b/routes.js @@ -13,6 +13,7 @@ const { body, validationResult } = require('express-validator/check'); const router = express.Router(); const Event = mongoose.model('Event'); +const EventGroup = mongoose.model('EventGroup'); const Log = mongoose.model('Log'); var moment = require('moment-timezone'); @@ -177,6 +178,7 @@ router.get('/:eventID', (req, res) => { Event.findOne({ id: req.params.eventID }) + .populate('eventGroup') .then((event) => { if (event) { parsedLocation = event.location.replace(/\s+/g, '+'); @@ -250,7 +252,7 @@ router.get('/:eventID', (req, res) => { if (spotsRemaining <= 0) { noMoreSpots = true; } - } + } let metadata = { title: event.name, description: marked(event.description, { renderer: render_plain()}).split(" ").splice(0,40).join(" ").trim(), @@ -298,6 +300,114 @@ router.get('/:eventID', (req, res) => { }); }) +router.get('/group/:eventGroupID', (req, res) => { + EventGroup.findOne({ + id: req.params.eventGroupID + }) + .then(async (eventGroup) => { + if (eventGroup) { + parsedDescription = marked(eventGroup.description); + eventGroupEditToken = eventGroup.editToken; + + escapedName = eventGroup.name.replace(/\s+/g, '+'); + + let eventGroupHasCoverImage = false; + if( eventGroup.image ) { + eventGroupHasCoverImage = true; + } + else { + eventGroupHasCoverImage = false; + } + let eventGroupHasHost = false; + if( eventGroup.hostName ) { + eventGroupHasHost = true; + } + else { + eventGroupHasHost = false; + } + + let events = await Event.find({eventGroup: eventGroup._id}).sort('start') + + events.forEach(event => { + if (moment.tz(event.end, event.timezone).isSame(event.start, 'day')){ + // Happening during one day + event.displayDate = moment.tz(event.start, event.timezone).format('D MMM YYYY'); + } + else { + event.displayDate = moment.tz(event.start, event.timezone).format('D MMM YYYY') + moment.tz(event.end, event.timezone).format(' - D MMM YYYY'); + } + if (moment.tz(event.end, event.timezone).isBefore(moment.tz(event.timezone))){ + event.eventHasConcluded = true; + } else { + event.eventHasConcluded = false; + } + }) + + let upcomingEventsExist = false; + if (events.some(e => e.eventHasConcluded == false)) { + upcomingEventsExist = true; + } + + let firstLoad = false; + if (eventGroup.firstLoad === true) { + firstLoad = true; + EventGroup.findOneAndUpdate({id: req.params.eventGroupID}, {firstLoad: false}, function(err, raw) { + if (err) { + res.send(err); + } + }); + } + let editingEnabled = false; + if (Object.keys(req.query).length !== 0) { + if (!req.query.e) { + editingEnabled = false; + console.log("No edit token set"); + } + else { + if (req.query.e == eventGroupEditToken){ + editingEnabled = true; + } + else { + editingEnabled = false; + } + } + } + let metadata = { + title: eventGroup.name, + description: marked(eventGroup.description, { renderer: render_plain()}).split(" ").splice(0,40).join(" ").trim(), + image: (eventGroupHasCoverImage ? 'https://gath.io/events/' + eventGroup.image : null), + url: 'https://gath.io/' + req.params.eventID + }; + res.set("X-Robots-Tag", "noindex"); + res.render('eventgroup', { + title: eventGroup.name, + eventGroupData: eventGroup, + escapedName: escapedName, + events: events, + upcomingEventsExist: upcomingEventsExist, + parsedDescription: parsedDescription, + editingEnabled: editingEnabled, + eventGroupHasCoverImage: eventGroupHasCoverImage, + eventGroupHasHost: eventGroupHasHost, + firstLoad: firstLoad, + metadata: metadata + }) + } + else { + res.status(404); + res.render('404', { url: req.url }); + } + + }) + .catch((err) => { + addToLog("displayEventGroup", "error", "Attempt to display event group " + req.params.eventGroupID + " failed with error: " + err); + console.log(err) + res.status(404); + res.render('404', { url: req.url }); + return; + }); +}) + // BACKEND ROUTES //router.post('/login', @@ -307,10 +417,11 @@ router.get('/:eventID', (req, res) => { //); -router.post('/newevent', (req, res) => { +router.post('/newevent', async (req, res) => { let eventID = shortid.generate(); let editToken = randomstring.generate(); let eventImageFilename = ""; + let isPartOfEventGroup = false; if (req.files && Object.keys(req.files).length != 0) { let eventImageBuffer = req.files.imageUpload.data; Jimp.read(eventImageBuffer, (err, img) => { @@ -324,6 +435,16 @@ router.post('/newevent', (req, res) => { } startUTC = moment.tz(req.body.eventStart, 'D MMMM YYYY, hh:mm a', req.body.timezone); endUTC = moment.tz(req.body.eventEnd, 'D MMMM YYYY, hh:mm a', req.body.timezone); + let eventGroup; + if (req.body.eventGroupCheckbox) { + eventGroup = await EventGroup.findOne({ + id: req.body.eventGroupID, + editToken: req.body.eventGroupEditToken + }) + if (eventGroup) { + isPartOfEventGroup = true; + } + } const event = new Event({ id: eventID, type: req.body.eventType, @@ -340,6 +461,7 @@ router.post('/newevent', (req, res) => { viewPassword: req.body.viewPassword, editPassword: req.body.editPassword, editToken: editToken, + eventGroup: isPartOfEventGroup ? eventGroup._id : null, usersCanAttend: req.body.joinCheckbox ? true : false, showUsersList: req.body.guestlistCheckbox ? true : false, usersCanComment: req.body.interactionCheckbox ? true : false, @@ -347,7 +469,7 @@ router.post('/newevent', (req, res) => { firstLoad: true }); event.save() - .then(() => { + .then((event) => { addToLog("createEvent", "success", "Event " + eventID + "created"); // Send email with edit link if (sendEmails) { @@ -374,7 +496,7 @@ router.post('/newevent', (req, res) => { }); res.end(); }) - .catch((err) => { res.send('Database error, please try again :('); addToLog("createEvent", "error", "Attempt to create event failed with error: " + err);}); + .catch((err) => { res.send('Database error, please try again :( - ' + err); addToLog("createEvent", "error", "Attempt to create event failed with error: " + err);}); }); router.post('/importevent', (req, res) => { @@ -443,12 +565,70 @@ router.post('/importevent', (req, res) => { } }); +router.post('/neweventgroup', (req, res) => { + let eventGroupID = shortid.generate(); + let editToken = randomstring.generate(); + let eventGroupImageFilename = ""; + if (req.files && Object.keys(req.files).length != 0) { + let eventImageBuffer = req.files.imageUpload.data; + Jimp.read(eventImageBuffer, (err, img) => { + if (err) addToLog("Jimp", "error", "Attempt to edit image failed with error: " + err); + img + .resize(920, Jimp.AUTO) // resize + .quality(80) // set JPEG quality + .write('./public/events/' + eventGroupID + '.jpg'); // save + }); + eventGroupImageFilename = eventGroupID + '.jpg'; + } + const eventGroup = new EventGroup({ + id: eventGroupID, + name: req.body.eventGroupName, + description: req.body.eventGroupDescription, + image: eventGroupImageFilename, + creatorEmail: req.body.creatorEmail, + url: req.body.eventGroupURL, + hostName: req.body.hostName, + editToken: editToken, + firstLoad: true + }); + eventGroup.save() + .then(() => { + addToLog("createEventGroup", "success", "Event group " + eventGroupID + " created"); + // Send email with edit link + if (sendEmails) { + const msg = { + to: req.body.creatorEmail, + from: { + name: 'Gathio', + email: 'notifications@gath.io', + }, + templateId: 'd-4c5ddcb34ac44ec5b2313c6da4e405f3', + dynamic_template_data: { + subject: 'gathio: ' + req.body.eventGroupName, + eventGroupID: eventGroupID, + editToken: editToken + }, + }; + sgMail.send(msg).catch(e => { + console.error(e.toString()); + res.status(500).end(); + }); + } + res.writeHead(302, { + 'Location': '/group/' + eventGroupID + '?e=' + editToken + }); + res.end(); + }) + .catch((err) => { res.send('Database error, please try again :( - ' + err); addToLog("createEvent", "error", "Attempt to create event failed with error: " + err);}); +}); + router.post('/editevent/:eventID/:editToken', (req, res) => { + console.log(req.body); let submittedEditToken = req.params.editToken; Event.findOne(({ id: req.params.eventID, })) - .then((event) => { + .then(async (event) => { if (event.editToken === submittedEditToken) { // Token matches @@ -468,6 +648,17 @@ router.post('/editevent/:eventID/:editToken', (req, res) => { } startUTC = moment.tz(req.body.eventStart, 'D MMMM YYYY, hh:mm a', req.body.timezone); endUTC = moment.tz(req.body.eventEnd, 'D MMMM YYYY, hh:mm a', req.body.timezone); + + var isPartOfEventGroup = false; + if (req.body.eventGroupCheckbox) { + var eventGroup = await EventGroup.findOne({ + id: req.body.eventGroupID, + editToken: req.body.eventGroupEditToken + }) + if (eventGroup) { + isPartOfEventGroup = true; + } + } const updatedEvent = { name: req.body.eventName, location: req.body.eventLocation, @@ -482,7 +673,7 @@ router.post('/editevent/:eventID/:editToken', (req, res) => { showUsersList: req.body.guestlistCheckbox ? true : false, usersCanComment: req.body.interactionCheckbox ? true : false, maxAttendees: req.body.maxAttendeesCheckbox ? req.body.maxAttendees : null, - + eventGroup: isPartOfEventGroup ? eventGroup._id : null } Event.findOneAndUpdate({id: req.params.eventID}, updatedEvent, function(err, raw) { if (err) { @@ -534,6 +725,86 @@ router.post('/editevent/:eventID/:editToken', (req, res) => { .catch((err) => { console.error(err); res.send('Sorry! Something went wrong!'); addToLog("editEvent", "error", "Attempt to edit event " + req.params.eventID + " failed with error: " + err);}); }); +router.post('/editeventgroup/:eventGroupID/:editToken', (req, res) => { + let submittedEditToken = req.params.editToken; + EventGroup.findOne(({ + id: req.params.eventGroupID, + })) + .then((eventGroup) => { + if (eventGroup.editToken === submittedEditToken) { + // Token matches + + // If there is a new image, upload that first + let eventGroupID = req.params.eventGroupID; + let eventGroupImageFilename = eventGroup.image; + if (req.files && Object.keys(req.files).length != 0) { + let eventImageBuffer = req.files.eventGroupImageUpload.data; + Jimp.read(eventImageBuffer, (err, img) => { + if (err) throw err; + img + .resize(920, Jimp.AUTO) // resize + .quality(80) // set JPEG + .write('./public/events/' + eventGroupID + '.jpg'); // save + }); + eventGroupImageFilename = eventGroupID + '.jpg'; + } + const updatedEventGroup = { + name: req.body.eventGroupName, + description: req.body.eventGroupDescription, + url: req.body.eventGroupURL, + hostName: req.body.hostName, + image: eventGroupImageFilename + } + EventGroup.findOneAndUpdate({id: req.params.eventGroupID}, updatedEventGroup, function(err, raw) { + if (err) { + addToLog("editEventGroup", "error", "Attempt to edit event group " + req.params.eventGroupID + " failed with error: " + err); + res.send(err); + } + }) + .then(() => { + addToLog("editEventGroup", "success", "Event group " + req.params.eventGroupID + " edited"); + // if (sendEmails) { + // Event.findOne({id: req.params.eventID}).distinct('attendees.email', function(error, ids) { + // attendeeEmails = ids; + // if (!error && attendeeEmails != ""){ + // console.log("Sending emails to: " + attendeeEmails); + // const msg = { + // to: attendeeEmails, + // from: { + // name: 'Gathio', + // email: 'notifications@gath.io', + // }, + // templateId: 'd-e21f3ca49d82476b94ddd8892c72a162', + // dynamic_template_data: { + // subject: 'gathio: Event edited', + // actionType: 'edited', + // eventExists: true, + // eventID: req.params.eventID + // } + // } + // sgMail.sendMultiple(msg); + // } + // else { + // console.log("Nothing to send!"); + // } + // }) + // } + res.writeHead(302, { + 'Location': '/group/' + req.params.eventGroupID + '?e=' + req.params.editToken + }); + res.end(); + }) + .catch((err) => { console.error(err); res.send('Sorry! Something went wrong!'); addToLog("editEventGroup", "error", "Attempt to edit event group " + req.params.eventGroupID + " failed with error: " + err);}); + } + else { + // Token doesn't match + res.send('Sorry! Something went wrong'); + addToLog("editEventGroup", "error", "Attempt to edit event group " + req.params.eventGroupID + " failed with error: token does not match"); + } + }) + .catch((err) => { console.error(err); res.send('Sorry! Something went wrong!'); addToLog("editEventGroup", "error", "Attempt to edit event group " + req.params.eventGroupID + " failed with error: " + err);}); +}); + router.post('/deleteevent/:eventID/:editToken', (req, res) => { let submittedEditToken = req.params.editToken; Event.findOne(({ @@ -609,6 +880,61 @@ router.post('/deleteevent/:eventID/:editToken', (req, res) => { .catch((err) => { res.send('Sorry! Something went wrong: ' + err); addToLog("deleteEvent", "error", "Attempt to delete event " + req.params.eventID + " failed with error: " + err);}); }); +router.post('/deleteeventgroup/:eventGroupID/:editToken', (req, res) => { + let submittedEditToken = req.params.editToken; + EventGroup.findOne(({ + id: req.params.eventGroupID, + })) + .then(async (eventGroup) => { + if (eventGroup.editToken === submittedEditToken) { + // Token matches + + let linkedEvents = await Event.find({eventGroup: eventGroup._id}); + + let linkedEventIDs = linkedEvents.map(event => event._id); + let eventGroupImage = false; + if (eventGroup.image){ + eventGroupImage = eventGroup.image; + } + + EventGroup.deleteOne({id: req.params.eventGroupID}, function(err, raw) { + if (err) { + res.send(err); + addToLog("deleteEventGroup", "error", "Attempt to delete event group " + req.params.eventGroupID + " failed with error: " + err); + } + }) + .then(() => { + // Delete image + if (eventGroupImage){ + fs.unlink(global.appRoot + '/public/events/' + eventGroupImage, (err) => { + if (err) { + res.send(err); + addToLog("deleteEventGroup", "error", "Attempt to delete event image for event group " + req.params.eventGroupID + " failed with error: " + err); + } + }) + } + Event.update({_id: {$in: linkedEventIDs}}, { $set: { eventGroup: null } }, { multi: true }) + .then(response => { + console.log(response); + addToLog("deleteEventGroup", "success", "Event group " + req.params.eventGroupID + " deleted"); + res.writeHead(302, { + 'Location': '/' + }); + res.end(); + }) + .catch((err) => { res.send('Sorry! Something went wrong (error deleting): ' + err); addToLog("deleteEventGroup", "error", "Attempt to delete event group " + req.params.eventGroupID + " failed with error: " + err);}); + }) + .catch((err) => { res.send('Sorry! Something went wrong (error deleting): ' + err); addToLog("deleteEventGroup", "error", "Attempt to delete event group " + req.params.eventGroupID + " failed with error: " + err);}); + } + else { + // Token doesn't match + res.send('Sorry! Something went wrong'); + addToLog("deleteEventGroup", "error", "Attempt to delete event group " + req.params.eventGroupID + " failed with error: token does not match"); + } + }) + .catch((err) => { res.send('Sorry! Something went wrong: ' + err); addToLog("deleteEventGroup", "error", "Attempt to delete event group " + req.params.eventGroupID + " failed with error: " + err);}); +}); + router.post('/attendevent/:eventID', (req, res) => { const newAttendee = { name: req.body.attendeeName, diff --git a/start.js b/start.js index 1bf0456..b47d73c 100755 --- a/start.js +++ b/start.js @@ -20,6 +20,7 @@ mongoose.connection require('./models/Event'); require('./models/Log'); +require('./models/EventGroup'); const app = require('./app'); diff --git a/views/event.handlebars b/views/event.handlebars index d4637f4..4d0cf28 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -32,17 +32,9 @@ Show on OpenStreetMap - {{#if eventHasHost}} -
  • - - - - Hosted by {{eventData.hostName}} -
  • - {{/if}}
  • - + {{{displayDate}}}
    @@ -54,10 +46,26 @@ Add to Google Calendar
  • + {{#if eventHasHost}} +
  • + + + + Hosted by {{eventData.hostName}} +
  • + {{/if}} + {{#if eventData.eventGroup}} +
  • + + + + Part of {{eventData.eventGroup.name}} +
  • + {{/if}} {{#if eventData.url}}
  • - + {{eventData.url}} @@ -66,11 +74,9 @@ {{/if}}
  • - + - - gath.io/{{eventData.id}} - + gath.io/{{eventData.id}} @@ -416,6 +422,18 @@ $("#maxAttendeesContainer").slideUp('fast'); $("#maxAttendees").attr("data-validation-optional","true").val("").removeClass('is-valid is-invalid'); } + }); + $("#eventGroupCheckbox").on("click", function() { + if ($(this).is(':checked')) { + $("#eventGroupData").slideDown('fast'); + $("#eventGroupID").removeAttr("data-validation-optional").attr("data-validation","required"); + $("#eventGroupEditToken").removeAttr("data-validation-optional").attr("data-validation","required"); + } + else { + $("#eventGroupData").slideUp('fast'); + $("#eventGroupID").removeAttr("data-validation").attr("data-validation-optional","true").val(""); + $("#eventGroupEditToken").removeAttr("data-validation").attr("data-validation-optional","true").val(""); + } }); }); diff --git a/views/eventgroup.handlebars b/views/eventgroup.handlebars new file mode 100755 index 0000000..00bae2c --- /dev/null +++ b/views/eventgroup.handlebars @@ -0,0 +1,164 @@ +{{#if eventGroupHasCoverImage}} +
    +{{else}} +
    +{{/if}} +
    +
    +

    {{eventGroupData.name}}

    +
    + {{#if editingEnabled}} +
    +
    + + +
    +
    + {{/if}} +
    +{{#if firstLoad}} + +{{/if}} +
    +
    + +
    +
    + +{{#if editingEnabled}} +
    +

    To add an event to this group, copy and paste the two codes below into the 'Event Group' box when creating a new event or editing an existing event.

    +
    + + + + + + + + + +
    Event group ID{{eventGroupData.id}}
    Event group secret editing code{{eventGroupData.editToken}}
    +
    + +
    +{{/if}} + +
    +
    About
    +
    + {{{parsedDescription}}} +
    +
    +
    +
    Upcoming events
    +
    + {{#if upcomingEventsExist}} + {{#each events}} + {{#unless this.eventHasConcluded}} + + + {{this.name}} + {{this.displayDate}} + + {{/unless}} + {{/each}} + {{else}} +
    No events!
    + {{/if}} +
    +
    + +{{#if editingEnabled}} +{{> editeventgroupmodal }} + + + +{{/if}} + + diff --git a/views/newevent.handlebars b/views/newevent.handlebars index b63b43c..81d39c5 100755 --- a/views/newevent.handlebars +++ b/views/newevent.handlebars @@ -34,12 +34,15 @@
    -
    +
    -
    +
    +
    + +
    @@ -58,6 +61,9 @@ {{>importeventform}}
    +
    + {{>neweventgroupform}} +
    diff --git a/views/partials/neweventgroupform.handlebars b/views/partials/neweventgroupform.handlebars new file mode 100755 index 0000000..b7524d4 --- /dev/null +++ b/views/partials/neweventgroupform.handlebars @@ -0,0 +1,66 @@ +

    Create an event group

    +

    An event group is a holding area for a set of linked events, like a series of film nights, a festival, or a band tour. You can share a public link to your event group just like an individual event link, and people who know the secret editing code (sent in an email when you create the event group) will be able to add future events to the group.

    +

    Event groups do not get automatically removed like events do, but events which have been removed from Gathio will of course not show up in an event group.

    +
    +
    + +
    + +
    +
    +
    + +
    + + Markdown formatting supported. +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + + +
    + Recommended dimensions (w x h): 920px by 300px. +
    +
    +
    + +
    + +
    +
    +
    + +
    + + We will send your secret editing link to this email address. +
    +
    +
    +
    + +
    +
    +
    + + -- cgit v1.2.3 From 40ade71e0019bbb59710a2e2e5d74197c47cb1b0 Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Mon, 30 Sep 2019 13:57:28 +0100 Subject: Fixes to iCal parser --- public/js/generate-timezones.js | 3 ++- routes.js | 17 +++++++++++++---- views/partials/editeventgroupmodal.handlebars | 4 ++-- views/partials/editeventmodal.handlebars | 6 +++--- views/partials/importeventform.handlebars | 11 ++++++++++- views/partials/neweventform.handlebars | 4 ++-- views/partials/neweventgroupform.handlebars | 2 +- 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/public/js/generate-timezones.js b/public/js/generate-timezones.js index 2c7cbf9..01c9989 100644 --- a/public/js/generate-timezones.js +++ b/public/js/generate-timezones.js @@ -343,7 +343,8 @@ const timezones = [ "Pacific/Tarawa", "Pacific/Tongatapu", "Pacific/Wake", - "Pacific/Wallis" + "Pacific/Wallis", + "Etc/UTC" ]; const dateTimeUtc = moment().utc(); diff --git a/routes.js b/routes.js index 80014e5..ee723e2 100755 --- a/routes.js +++ b/routes.js @@ -505,9 +505,18 @@ router.post('/importevent', (req, res) => { if (req.files && Object.keys(req.files).length != 0) { importediCalObject = ical.parseICS(req.files.icsImportControl.data.toString('utf8')); for (var key in importediCalObject) { - importedEventData = importediCalObject[key]; + importedEventData = importediCalObject[key]; } - creatorEmail = importedEventData.organizer.val.replace("MAILTO:", "") + console.log(importedEventData) + let creatorEmail; + if (req.body.creatorEmail) { + creatorEmail = req.body.creatorEmail + } else if (importedEventData.organizer) { + creatorEmail = importedEventData.organizer.val.replace("MAILTO:", ""); + } else { + res.status(500).send("Please supply an email address on the previous page."); + } + const event = new Event({ id: eventID, type: 'public', @@ -515,12 +524,12 @@ router.post('/importevent', (req, res) => { location: importedEventData.location, start: importedEventData.start, end: importedEventData.end, - timezone: importedEventData.start.tz, + timezone: typeof importedEventData.start.tz != 'undefined' ? importedEventData.start.tz : "Etc/UTC", description: importedEventData.description, image: '', creatorEmail: creatorEmail, url: '', - hostName: importedEventData.organizer.params.CN, + hostName: importedEventData.organizer ? importedEventData.organizer.params.CN : "", viewPassword: '', editPassword: '', editToken: editToken, diff --git a/views/partials/editeventgroupmodal.handlebars b/views/partials/editeventgroupmodal.handlebars index 7ab1221..64fba9b 100644 --- a/views/partials/editeventgroupmodal.handlebars +++ b/views/partials/editeventgroupmodal.handlebars @@ -11,7 +11,7 @@