diff --git a/app.js b/app.js index 86571c7..0562a06 100644 --- a/app.js +++ b/app.js @@ -103,4 +103,7 @@ if (process.env.INSTALL === 'yes' || args[0] === 'install') { }); } +const child_process = require('child_process'); +child_process.fork('rsvp_sender'); + module.exports = app; diff --git a/public/css/style.css b/public/css/style.css index 467d838..4762661 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -366,7 +366,6 @@ input::-webkit-calendar-picker-indicator { padding-top: 10px; z-index: 1050; font-size: 15px; - background-color: #51a351; color: #ffffff; text-align: center; border-bottom-left-radius: 20px 10px; diff --git a/routes/event/event.js b/routes/event/event.js index 8b1d7e2..bffc0ad 100644 --- a/routes/event/event.js +++ b/routes/event/event.js @@ -6,31 +6,54 @@ const authService = require('../services/authorization_service'); router.get('/', function (req, res, next) { let offset = 0; - event_db.getEventSet(offset, function (err, eventsSet) { + let uid = req.session.uid; + event_db.getEventSet(offset, function (err, events_set) { if (err) { console.log(err); res.status(400).json({msg: 'Database Error'}); return; } - user_db.getUserRoleByUid(req.session.uid, function (err, user_role) { + user_db.getUserRoleByUid(uid, (err, user_role) => { if (err) { + console.log(err); res.status(400).json({msg: 'Database Error'}); return; } - eventsSet = eventsSet.sort(function (a, b) { - return a.event_time - b.event_time - }) - let now = new Date(); - now = new Date(now - (5.5 * 60 * 60 * 1000)); - var centerIdx = eventsSet.length - 1; - for (var i = eventsSet.length - 1; i >= 0; i--) { - // show the event thats coming up soon - if (eventsSet[i].event_time < now) { - break; + user_db.getUserFollowedEventsByUid(uid, (err, followed_events) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Database Error'}); + return; } - centerIdx = i; - } - res.render('event/index', {eventsSet: eventsSet, uid: req.session.uid, all_user_role: authService.UserRole, user_role: user_role, centerIdx: centerIdx}); + user_db.getUserRSVPEventsByUid(uid, (err, rsvp_events) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Database Error'}); + return; + } + events_set = events_set.sort((event_a, event_b) => { + return event_a.event_time - event_b.event_time + }) + let now = new Date(); + now = new Date(now - (5.5 * 60 * 60 * 1000)); + let center_idx = events_set.length - 1; + for (let i = events_set.length - 1; i >= 0; i--) { + if (events_set[i].event_time < now) { + break; + } + center_idx = i; + } + res.render('event/index', { + followed_events: JSON.parse(followed_events), + rsvp_events: JSON.parse(rsvp_events), + eventsSet: events_set, + uid: req.session.uid, + all_user_role: authService.UserRole, + user_role: user_role, + centerIdx: center_idx + }); + }); + }); }); }); }); @@ -86,4 +109,43 @@ router.get('/edit', function (req, res, next) { }); }); +router.get('/followed', function (req, res, next) { + let uid = req.query.user_id ? req.query.user_id : req.session.uid; + authService.authorizationCheck(null, req.session.uid, function(err, authorized){ + if (err) { + console.log(err); + res.status(400).json({msg: 'Database Error'}); + } + else if(!authorized) { + res.redirect('/login'); + } else { + user_db.getUserFollowedEventsByUid(uid, (err, followed_events) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Database Error'}); + } else { + event_db.getEventSet((err, event_set) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Database Error'}); + } + let followed_event_ids = JSON.parse(followed_events); + let followed_event_set = []; + + event_set.forEach(function (event) { + if (followed_event_ids.includes(event.id)) { + followed_event_set.push(event); + } + }); + + res.render('event/followed', { + event_set: followed_event_set + }); + }); + } + }); + } + }); +}); + module.exports = router; diff --git a/routes/event/event_api.js b/routes/event/event_api.js index 0a0000a..90efb77 100644 --- a/routes/event/event_api.js +++ b/routes/event/event_api.js @@ -1,5 +1,6 @@ const express = require('express'); const event_db = require('../../server/event_db'); +const user_db = require('../../server/user_db'); const router = express.Router(); const authService = require('../services/authorization_service'); @@ -106,4 +107,101 @@ router.post('/changeEventStatus', function(req, res, next){ }); }); +router.post('/followEvent', function(req, res, next) { + if(!req.body.uid){ + res.status(403).json({msg: 'Not Authorized'}); + return; + } + user_db.hasUserEventEntry(req.body.uid, (err, has_entry) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to check entry'}); + return; + } + if (has_entry) { + user_db.followEvent(req.body.uid, req.body.eid, (err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to follow event'}); + return; + } + res.json({}); + }); + } else { + user_db.createEntryAndFollowEvent(req.body.uid, req.body.eid, (err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to create entry and follow event'}); + return; + } + res.json({}); + }) + } + }); +}); + +router.post('/unfollowEvent', function(req, res, next) { + if(!req.body.uid){ + res.status(403).json({msg: 'Not Authorized'}); + return; + } + user_db.unfollowEvent(req.body.uid, req.body.eid, ((err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to unfollow event'}); + return; + } + res.json({}); + })) +}); + +router.post('/rsvpEvent', function (req, res, next) { + if(!req.body.uid){ + res.status(403).json({msg: 'Not Authorized'}); + return; + } + user_db.hasUserEventEntry(req.body.uid, (err, has_entry) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to check entry'}); + return; + } + if (has_entry) { + user_db.rsvpEvent(req.body.uid, req.body.eid, (err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to RSVP event'}); + return; + } + + res.json({}); + }); + } else { + user_db.createEntryAndRSVPEvent(req.body.uid, req.body.eid, (err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to create entry and RSVP event'}); + return; + } + res.json({}); + }); + } + }); +}); + +router.post('/unrsvpEvent', function(req, res, next) { + if(!req.body.uid){ + res.status(403).json({msg: 'Not Authorized'}); + return; + } + user_db.unrsvpEvent(req.body.uid, req.body.eid, ((err) => { + if (err) { + console.log(err); + res.status(400).json({msg: 'Failed to unfollow event'}); + return; + } + res.json({}); + })) +}); + module.exports = router; diff --git a/rsvp_sender.js b/rsvp_sender.js new file mode 100644 index 0000000..665f56d --- /dev/null +++ b/rsvp_sender.js @@ -0,0 +1,56 @@ +const nodemailer = require('nodemailer'); +const user_db = require('./server/user_db'); +const event_db = require('./server/event_db'); + + +setInterval(() => { + user_db.getAllEntriesFromUserEvent((err, user_event_entries) => { + if (err) { + console.log("Failed to get all entries from user event"); + return; + } + user_event_entries.rows.forEach((row) => { + const uid = row.uid; + const rsvp_events = JSON.parse(row.rsvp_events); + rsvp_events.forEach((rsvp_event) => { + event_db.getEventById(rsvp_event, (err, event_info) => { + if (!err) { + let now = new Date(); + // TODO: !!!!!!!! + if (!(now.toLocaleDateString() === event_info.event_time.toLocaleDateString())) { + user_db.getUserEmailById(uid, (err, user_email) => { + if (!err) { + let mail_options = { + from: process.env.EMAILUSER, + to: user_email.email, + subject: 'Event Reminder: ' + event_info.title, + text: event_info.description, + }; + let transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAILUSER, + pass: process.env.EMAILPASS, + } + }); + transporter.sendMail(mail_options, (err, info) => { + if (err) { + console.log(err); + } else { + user_db.unrsvpEvent(uid, event_info.id, (err) => { + if (err) { + console.log(err); + } + console.log('Email sent: ' + info.response); + }); + } + }); + } + }); + } + } + }); + }); + }) + }); +}, 1000); diff --git a/server/user_db.js b/server/user_db.js index 7c18a6e..eedb393 100644 --- a/server/user_db.js +++ b/server/user_db.js @@ -276,6 +276,17 @@ exports.getUserByEmail = function(email, callback){ }); }; +exports.getUserEmailById = (user_id, callback) => { + const query = 'SELECT email FROM users WHERE id=$1;'; + db.query(query, [user_id], (err, result) => { + if (err) { + callback(err); + } else { + callback(null, result.rows[0]); + } + }) +} + exports.resetPassword = function(password, email, user_id, callback) { var saltRounds = 10; bcrypt.hash(password, saltRounds, function(err, hash) { @@ -305,4 +316,234 @@ exports.getUnconfirmedUsers = function(callback) { callback(null, result.rows); } }) -}; \ No newline at end of file +}; +exports.hasProfile = (user_id, callback) => { + if (!user_id) { + callback(null, 'NO_USR_ID'); + return; + } + let query = 'SELECT * FROM user_profile WHERE uid = $1;'; + db.query(query, [user_id], (err, result) => { + if (err) { + callback(err); + } else { + if (result.rows[0]) + callback(null, 'TRUE'); + else + callback(null, 'FALSE'); + } + }); +} + +/** + * Get events that the user corresponding to the `user_id` has followed. + * If `user_id` is empty, i.e. no user logs in, or the user has not followed any events, return `[]` + * @param user_id user id + * @param callback callback function + */ +exports.getUserFollowedEventsByUid = (user_id, callback) => { + if (!user_id) { + callback(null, '[]'); + return; + } + const query = 'SELECT * FROM user_event WHERE uid = $1;'; + db.query(query, [user_id], (err, result) => { + if (err) { + callback(err); + } else { + if (result.rows[0]) { + callback(null, result.rows[0].followed_events); + } else { + callback(null, '[]'); + } + } + }); +} + +/** + * Get events that the user corresponding to the `user_id` has RSVP. + * If `user_id` is empty, i.e. no user logs in, or the user has not followed any events, return `[]` + * @param user_id user id + * @param callback callback function + */ +exports.getUserRSVPEventsByUid = (user_id, callback) => { + if (!user_id) { + callback(null, '[]'); + return; + } + const query = 'SELECT * FROM user_event WHERE uid = $1;'; + db.query(query, [user_id], (err, result) => { + if (err) { + callback(err); + } else { + if (result.rows[0]) { + callback(null, result.rows[0].rsvp_events); + } else { + callback(null, '[]'); + } + } + }); +} + +/** + * Check if `user_id` has an entry (row) inside the `user_event` table + * @param user_id user id + * @param callback callback function + */ +exports.hasUserEventEntry = (user_id, callback) => { + const query = 'SELECT EXISTS(SELECT 1 FROM user_event WHERE uid = $1);' + db.query(query, [user_id], (err, result) => { + if (err) { + callback(err); + } else { + callback(null, result.rows[0].exists) + } + }); +} + +/** + * Create an entry corresponding to `user_id` and add the `event_id` to `followed_events` in the entry + * @param user_id user id + * @param event_id event id + * @param callback callback function + */ +exports.createEntryAndFollowEvent = (user_id, event_id, callback) => { + const insert = 'INSERT INTO user_event (uid, followed_events, rsvp_events) VALUES ($1, $2, $3)'; + db.query(insert, [user_id, JSON.stringify([parseInt(event_id)]), "[]"], (err) => { + if (err) { + callback(err); + } else { + callback(null); + } + }); +} + +exports.followEvent = function(user_id, event_id, callback) { + db.query('BEGIN;'); + let query = 'SELECT followed_events FROM user_event WHERE uid = $1 FOR UPDATE;'; + db.query(query, [user_id], function (err, result) { + if (err) { + db.query('COMMIT;'); + callback(err); + } else { + const followed_event = JSON.parse(result.rows[0]['followed_events']); + followed_event.push(parseInt(event_id)); + query = 'UPDATE user_event SET followed_events = ' + "'" + JSON.stringify(followed_event) + "'" + ' WHERE uid = $1;'; + db.query(query, [user_id], function (err_update) { + if (err_update) { + db.query('ROLLBACK;') + callback(err_update); + } else { + db.query('COMMIT;'); + callback(null); + } + }); + } + }); +}; + +exports.unfollowEvent = function(user_id, event_id, callback) { + db.query('BEGIN;'); + let query = 'SELECT followed_events FROM user_event WHERE uid = $1 FOR UPDATE;'; + db.query(query, [user_id], function (err, result) { + if (err) { + db.query('COMMIT;'); + callback(err); + } else { + const followed_event = JSON.parse(result.rows[0]['followed_events']); + const index = followed_event.indexOf(parseInt(event_id)); + if (index > -1) { + followed_event.splice(index, 1); + } + query = 'UPDATE user_event SET followed_events = ' + "'" + JSON.stringify(followed_event) + "'" + ' WHERE uid = $1;'; + db.query(query, [user_id], function (err_update) { + if (err_update) { + db.query('ROLLBACK;') + callback(err_update); + } else { + db.query('COMMIT;'); + callback(null); + } + }); + } + }); +}; + +/** + * Create an entry corresponding to `user_id` and add the `event_id` to `rsvp_events` in the entry + * @param user_id user id + * @param event_id event id + * @param callback callback function + */ +exports.createEntryAndRSVPEvent = (user_id, event_id, callback) => { + const insert = 'INSERT INTO user_event (uid, followed_events, rsvp_events) VALUES ($1, $2, $3)'; + db.query(insert, [user_id, "[]", JSON.stringify([parseInt(event_id)])], (err) => { + if (err) { + callback(err); + } else { + callback(null); + } + }); +} + +exports.rsvpEvent = function(user_id, event_id, callback) { + db.query('BEGIN;'); + let query = 'SELECT rsvp_events FROM user_event WHERE uid = $1 FOR UPDATE;'; + db.query(query, [user_id], function (err, result) { + if (err) { + db.query('COMMIT;'); + callback(err); + } else { + const followed_event = JSON.parse(result.rows[0]['rsvp_events']); + followed_event.push(parseInt(event_id)); + query = 'UPDATE user_event SET rsvp_events = ' + "'" + JSON.stringify(followed_event) + "'" + ' WHERE uid = $1;'; + db.query(query, [user_id], function (err_update) { + if (err_update) { + db.query('ROLLBACK;') + callback(err_update); + } else { + db.query('COMMIT;'); + callback(null); + } + }); + } + }); +}; + +exports.unrsvpEvent = function(user_id, event_id, callback) { + db.query('BEGIN;'); + let query = 'SELECT rsvp_events FROM user_event WHERE uid = $1 FOR UPDATE;'; + db.query(query, [user_id], function (err, result) { + if (err) { + db.query('COMMIT;'); + callback(err); + } else { + const followed_event = JSON.parse(result.rows[0]['rsvp_events']); + const index = followed_event.indexOf(parseInt(event_id)); + if (index > -1) { + followed_event.splice(index, 1); + } + query = 'UPDATE user_event SET rsvp_events = ' + "'" + JSON.stringify(followed_event) + "'" + ' WHERE uid = $1;'; + db.query(query, [user_id], function (err_update) { + if (err_update) { + db.query('ROLLBACK;') + callback(err_update); + } else { + db.query('COMMIT;'); + callback(null); + } + }); + } + }); +}; + +exports.getAllEntriesFromUserEvent = (callback) => { + const query = 'SELECT * FROM user_event'; + db.query(query, [], (err, result) => { + if (err) { + callback(err); + } else { + callback(null, result); + } + }); +} diff --git a/views/event/followed.ejs b/views/event/followed.ejs new file mode 100644 index 0000000..b1e0515 --- /dev/null +++ b/views/event/followed.ejs @@ -0,0 +1,57 @@ + + + + <% include ../template/head.ejs %> + + + + + + + +<% include ../template/nav.ejs %> +
+ +
+
+

Search:

+
+
+ +
+
+ + + + + + + + + + + + <% if (event_set.length === 0) { %> + + + + <% } %> + + <% event_set.forEach(function(event) { %> + > + + + + + + <% }); %> + + +
Event NameEvent DetailLocationTime
You are not following any event.
<%= event.title %><%= event.description %><%= event.event_time.toLocaleTimeString() %><%= event.location %>
+
+ +<% include ../template/footer.ejs %> + + \ No newline at end of file diff --git a/views/event/index.ejs b/views/event/index.ejs index b22e9ff..e6c3bf1 100644 --- a/views/event/index.ejs +++ b/views/event/index.ejs @@ -6,9 +6,106 @@ + + <% include ../template/nav.ejs %>
+ <% if (uid) { %> + My Followed Events + <% } %> <% if (auth([all_user_role.Developer, all_user_role.Admin, all_user_role.EventExecutive], user_role)) { %> @@ -62,13 +159,37 @@ <% } %> <% } %>

<%= eventsSet[i].description %>

- <% if (auth([all_user_role.Developer, - all_user_role.Admin, - all_user_role.EventExecutive], user_role)) { %>
- Edit Event + <% + var canFollow = true; + for (var j = 0; j < followed_events.length; j++) { + if (followed_events[j] === eventsSet[i].id) { + canFollow = false; + } + } + var canRSVP = true; + for (var j = 0; j < rsvp_events.length; j++) { + if (rsvp_events[j] === eventsSet[i].id) { + canRSVP = false; + } + } + %> + <% if (canFollow) { %> + + <% } else { %> + + <% } %> + <% if (canRSVP) { %> + + <% } else { %> + + <% } %> + <% if (auth([all_user_role.Developer, + all_user_role.Admin, + all_user_role.EventExecutive], user_role)) { %> + Edit Event + <% } %>
- <% } %>
<% if (i < eventsSet.length - 1) { %>
-

Your submission has successfully been saved!

+

Your submission has successfully been saved!