X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=app.js;h=ebaaecc06762ba7483053983949ba77cd8e79ee4;hb=a3f8989a1c590d2cdbbc6a04bef6072a0038ec1b;hp=7c98b192a6d91923dc02a69b477a260dcb38ac04;hpb=5c0292c051b8fa1b1021096df8c511f5908d3f2b;p=oweals%2Ffinalsclub.git diff --git a/app.js b/app.js index 7c98b19..ebaaecc 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,5 @@ // FinalsClub Server -// +// // This file consists of the main webserver for FinalsClub.org // and is split between a standard CRUD style webserver and // a websocket based realtime webserver. @@ -22,7 +22,8 @@ var hat = require('hat'); var connect = require( 'connect' ); var Session = connect.middleware.session.Session; var parseCookie = connect.utils.parseCookie; -var Backchannel = require('../bc/backchannel'); +var Backchannel = require('./bc/backchannel'); + // Depracated // Used for initial testing @@ -33,11 +34,11 @@ var app = module.exports = express.createServer(); // Load Mongoose Schemas // The actual schemas are located in models.j -var User = mongoose.model( 'User' ); -var School = mongoose.model( 'School' ); -var Course = mongoose.model( 'Course' ); -var Lecture = mongoose.model( 'Lecture' ); -var Note = mongoose.model( 'Note' ); +var User = mongoose.model( 'User' ); +var School = mongoose.model( 'School' ); +var Course = mongoose.model( 'Course' ); +var Lecture = mongoose.model( 'Lecture' ); +var Note = mongoose.model( 'Note' ); // More schemas used for legacy data var ArchivedCourse = mongoose.model( 'ArchivedCourse' ); @@ -45,7 +46,7 @@ var ArchivedNote = mongoose.model( 'ArchivedNote' ); var ArchivedSubject = mongoose.model( 'ArchivedSubject' ); // XXX Not sure if necessary -var ObjectId = mongoose.SchemaTypes.ObjectId; +var ObjectId = mongoose.SchemaTypes.ObjectId; // Configuration // Use the environment variable DEV_EMAIL for testing @@ -89,7 +90,7 @@ app.configure( 'development', function() { // If a port wasn't set earlier, set to 3000 if ( !serverPort ) { serverPort = 3000; - } + } }); // Production configuration settings @@ -116,15 +117,18 @@ app.configure( 'production', function() { // Set to port 80 if not set through environment variables if ( !serverPort ) { serverPort = 80; - } + } }); // General Express configuration settings app.configure(function(){ + // Views are rendered from public/index.html and main.js except for the pad that surrounds EPL and BC + // FIXME: make all views exist inside of public/index.html // Views are housed in the views folder app.set( 'views', __dirname + '/views' ); // All templates use jade for rendering app.set( 'view engine', 'jade' ); + // Bodyparser is required to handle form submissions // without manually parsing them. app.use( express.bodyParser() ); @@ -134,6 +138,7 @@ app.configure(function(){ // Sessions are stored in mongodb which allows them // to be persisted even between server restarts. app.set( 'sessionStore', new mongoStore( { + 'db' : 'fc', 'url' : app.set( 'dbUri' ) })); @@ -155,19 +160,16 @@ app.configure(function(){ // methodOverride is used to handle PUT and DELETE HTTP // requests that otherwise aren't handled by default. app.use( express.methodOverride() ); + // Static files are loaded when no dynamic views match. + app.use( express.static( __dirname + '/public', {maxAge: 900000} ) ); + // Sets the routers middleware to load after everything set // before it, but before static files. app.use( app.router ); - // Static files are loaded when no dynamic views match. - app.use( express.static( __dirname + '/public' ) ); - - // This is the errorHandler set in configuration earlier - // being set to a variable to be used after all other - // middleware is loaded. Error handling should always - // come last or near the bottom. - var errorHandler = app.set( 'errorHandler' ); - app.use( errorHandler ); + app.use(express.logger({ format: ':method :url' })); + // This is the command to use the default express error logger/handler + app.use(express.errorHandler({ dumpExceptions: true })); }); @@ -290,6 +292,8 @@ function loadUser( req, res, next ) { res.redirect( '/' ); } + } else if('a'==='b'){ + console.log('never. in. behrlin.'); } else { // If no user record was found, then we store the requested // path they intended to view and redirect them after they @@ -299,7 +303,9 @@ function loadUser( req, res, next ) { // Set req.user to an empty object so it doesn't throw errors // later on that it isn't defined. - req.user = {}; + req.user = { + sanitized: {} + }; next(); } @@ -309,9 +315,11 @@ function loadUser( req, res, next ) { // loadSchool is used to load a school by it's id function loadSchool( req, res, next ) { var user = req.user; - var schoolId = req.params.id; + var schoolName = req.params.name; + console.log( 'loading a school by id' ); - School.findById( schoolId, function( err, school ) { + School.findOne({'name': schoolName}).run( function( err, school ) { + //sys.puts(school); if( school ) { req.school = school; @@ -319,12 +327,12 @@ function loadSchool( req, res, next ) { // authorized to see or interact with anything related to that // school. school.authorize( user, function( authorized ){ - req.school.authorized = authorized; - next(); + req.school.authorized = authorized; }); + next(); } else { // If no school is found, display an appropriate error. - res.json( {status: 'error', message: 'Invalid school specified!'} ); + sendJson(res, {status: 'not_found', message: 'Invalid school specified!'} ); } }); } @@ -348,7 +356,7 @@ function loadCourse( req, res, next ) { }); } else { // If no course is found, display an appropriate error. - res.json( {status: 'error', message: 'Invalid course specified!'} ); + sendJson(res, {status: 'not_found', message: 'Invalid course specified!'} ); } }); } @@ -372,7 +380,7 @@ function loadLecture( req, res, next ) { }); } else { // If no lecture is found, display an appropriate error. - res.json( {status: 'error', message: 'Invalid lecture specified!'} ); + sendJson(res, {status: 'not_found', message: 'Invalid lecture specified!'} ); } }); } @@ -404,7 +412,7 @@ function loadNote( req, res, next ) { } else { // If the user is not authorized and the note is private // then display and error. - res.json( {status: 'error', message: 'You do not have permission to access that note.'} ); + sendJson(res, {status: 'error', message: 'You do not have permission to access that note.'} ); } }) } else if ( note && note.public && !note.deleted ) { @@ -421,10 +429,10 @@ function loadNote( req, res, next ) { // in they will be redirected to the note, at which time authorization // handling will be put in effect above. //req.session.redirect = '/note/' + note._id; - res.json( {status: 'error', message: 'You must be logged in to view that note.'} ); + sendJson(res, {status: 'error', message: 'You must be logged in to view that note.'} ); } else { // No note was found - res.json( {status: 'error', message: 'Invalid note specified!'} ); + sendJson(res, {status: 'error', message: 'Invalid note specified!'} ); } }); } @@ -433,7 +441,11 @@ function checkAjax( req, res, next ) { if ( req.xhr ) { next(); } else { - res.sendfile( 'public/index.html' ); + res.sendfile( 'public/index.html', function(err){ + if(err){ + console.log(err); + } + }); } } @@ -456,21 +468,14 @@ app.dynamicHelpers( { } }); -// Routes -// The following are the main CRUD routes that are used -// to make up this web app. - -// Homepage -// Public -app.get( '/', loadUser, function( req, res ) { - log3("get / page"); +function sendJson( res, obj ) { + res.header('Cache-Control', 'no-cache, no-store'); + res.json(obj); +} - res.render( 'index' ); -}); // Schools list -// Used to display all available schools and any courses -// in those schools. +// Used to display all available schools // Public with some private information app.get( '/schools', checkAjax, loadUser, function( req, res ) { var user = req.user; @@ -482,30 +487,57 @@ app.get( '/schools', checkAjax, loadUser, function( req, res ) { if( schools ) { // If schools are found, loop through them gathering any courses that are // associated with them and then render the page with that information. - res.json({ 'schools' : schools.map(function(school) { - return school.sanitized; - })}) + var schools_todo = schools.length; + schools.map(function (school) { + Course.find( { 'school': school.id } ).run(function (err, courses) { + school.courses_length = courses.length + schools_todo -= 1; + if (schools_todo <= 0) { + sendJson(res, { 'user': user.sanitized, 'schools': schools.map( function(s) { + var school = { + _id: s._id, + name: s.name, + description: s.description, + url: s.url, + slug: s.slug, + courses: s.courses_length, + courseNum: s.courseNum + }; + return school; + }) + }); + } + }); + }); } else { // If no schools have been found, display none //res.render( 'schools', { 'schools' : [] } ); - res.json({ 'schools' : [] }); + sendJson(res, { 'schools' : [] , 'user': user.sanitized }); } }); }); -app.get( '/school/:id', checkAjax, loadUser, loadSchool, function( req, res ) { +app.get( '/school/:name', checkAjax, loadUser, loadSchool, function( req, res ) { var school = req.school; var user = req.user; + console.log("Loading a school"); school.authorize( user, function( authorized ) { // This is used to display interface elements for those users // that are are allowed to see th)m, for instance a 'New Course' button. var sanitizedSchool = school.sanitized; + //var sanitizedSchool = { + // _id: school.id, + // name: school.name, + // description: school.description, + // url: school.url + //}; sanitizedSchool.authorized = authorized; // Find all courses for school by it's id and sort by name Course.find( { 'school' : school._id } ).sort( 'name', '1' ).run( function( err, courses ) { // If any courses are found, set them to the appropriate school, otherwise // leave empty. + if( courses.length > 0 ) { sanitizedSchool.courses = courses.filter(function(course) { if (!course.deleted) return course; @@ -513,35 +545,20 @@ app.get( '/school/:id', checkAjax, loadUser, loadSchool, function( req, res ) { return course.sanitized; }); } else { - sanitizedSchool.courses = []; + school.courses = []; } + // This tells async (the module) that each iteration of forEach is // done and will continue to call the rest until they have all been // completed, at which time the last function below will be called. - res.json({ 'school': sanitizedSchool }) + sendJson(res, { 'school': sanitizedSchool, 'user': user.sanitized }) }); }); }); -// New course page -// Displays form to create new course -// Private, requires user to be authorized -app.get( '/:id/course/new', loadUser, loadSchool, function( req, res ) { - // Load school from middleware - var school = req.school; - - // If school was not loaded for whatever reason, or the user is not authorized - // then redirect to the main schools page. - if( ( ! school ) || ( ! school.authorized ) ) { - return res.redirect( '/schools' ); - } - - // If they are authorized and the school exists, then render the page - res.render( 'course/new', { 'school': school } ); -}); // Recieves new course form -app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { +app.post( '/school/:id', checkAjax, loadUser, loadSchool, function( req, res ) { var school = req.school; // Creates new course from Course Schema var course = new Course; @@ -549,15 +566,16 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { var instructorEmail = req.body.email.toLowerCase(); var instructorName = req.body.instructorName; - // If school doesn't exist or user is not authorized redirect to main schools page if( ( ! school ) || ( ! school.authorized ) ) { - res.redirect( '/schools' ); + return sendJson(res, {status: 'error', message: 'There was a problem trying to create a course'}) } - // If instructorEmail isn't set, or name isn't set, display error and re-render the page. - if ( !instructorEmail || !instructorName ) { - req.flash( 'error', 'Invalid parameters!' ) - return res.render( 'course/new' ); + if ( !instructorName ) { + return sendJson(res, {status: 'error', message: 'Invalid parameters!'} ) + } + + if ( ( instructorEmail === '' ) || ( !isValidEmail( instructorEmail ) ) ) { + return sendJson(res, {status: 'error', message:'Please enter a valid email'} ); } // Fill out the course with information from the form @@ -582,21 +600,13 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { user.activated = false; - // Validate instructorEmail - // XXX Probably could be done before checking db - if ( ( user.email === '' ) || ( !isValidEmail( user.email ) ) ) { - req.flash( 'error', 'Please enter a valid email' ); - // XXX This needs to be fixed, this is not the proper flow - return res.redirect( '/register' ); - } // Once the new user information has been completed, save the user // to the database then email them the instructor welcome email. user.save(function( err ) { // If there was an error saving the instructor, prompt the user to fill out // the information again. if ( err ) { - req.flash( 'error', 'Invalid parameters!' ) - return res.render( 'course/new' ); + return sendJson(res, {status: 'error', message: 'Invalid parameters!'} ) } else { var message = { to : user.email, @@ -625,10 +635,7 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { course.instructor = user._id; course.save( function( err ) { if( err ) { - // XXX better validation - req.flash( 'error', 'Invalid parameters!' ); - - return res.render( 'course/new' ); + return sendJson(res, {status: 'error', message: 'Invalid parameters!'} ) } else { // Once the course has been completed email the admin with information // on the course and new instructor @@ -656,7 +663,7 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { // Redirect the user to the schools page where they can see // their new course. // XXX Redirect to the new course instead - res.redirect( '/schools' ); + return sendJson(res, {status: 'ok', message: 'Course created'} ) } }); } @@ -670,9 +677,7 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { course.save( function( err ) { if( err ) { // XXX better validation - req.flash( 'error', 'Invalid parameters!' ); - - return res.render( 'course/new' ); + return sendJson(res, {status: 'error', message: 'Invalid parameters!'} ) } else { var message = { to : ADMIN_EMAIL, @@ -696,14 +701,13 @@ app.post( '/:id/course/new', loadUser, loadSchool, function( req, res ) { } }) // XXX Redirect to the new course instead - res.redirect( '/schools' ); + return sendJson(res, {status: 'ok', message: 'Course created'} ) } }); } else { // The existing user isn't an instructor, so the user is notified of the error // and the course isn't created. - req.flash( 'error', 'The existing user\'s email you entered is not an instructor' ); - res.render( 'course/new' ); + sendJson(res, {status: 'error', message: 'The existing user\'s email you entered is not an instructor'} ) } } }) @@ -717,18 +721,52 @@ app.get( '/course/:id', checkAjax, loadUser, loadCourse, function( req, res ) { // Check if the user is subscribed to the course // XXX Not currently used for anything - var subscribed = course.subscribed( userId ); + //var subscribed = course.subscribed( userId ); // Find lectures associated with this course and sort by name Lecture.find( { 'course' : course._id } ).sort( 'name', '1' ).run( function( err, lectures ) { // Get course instructor information using their id User.findById( course.instructor, function( err, instructor ) { // Render course and lectures - res.json( { 'course' : course.sanitized, 'instructor': instructor.sanitized, 'subscribed' : subscribed, 'lectures' : lectures.map(function(lecture) { return lecture.sanitized })} ); + var sanitizedInstructor = instructor.sanitized; + var sanitizedCourse = course.sanitized; + if (!course.authorized) { + delete sanitizedInstructor.email; + } else { + sanitizedCourse.authorized = course.authorized; + } + sendJson(res, { 'course' : sanitizedCourse, 'instructor': sanitizedInstructor, 'lectures' : lectures.map(function(lecture) { return lecture.sanitized })} ); }) }); }); +// Recieve New Lecture Form +app.post( '/course/:id', checkAjax, loadUser, loadCourse, function( req, res ) { + var course = req.course; + // Create new lecture from Lecture schema + var lecture = new Lecture; + + if( ( ! course ) || ( ! course.authorized ) ) { + return sendJson(res, {status: 'error', message: 'There was a problem trying to create a lecture'}) + } + + // Populate lecture with form data + lecture.name = req.body.name; + lecture.date = req.body.date; + lecture.course = course._id; + lecture.creator = req.user._id; + + // Save lecture to database + lecture.save( function( err ) { + if( err ) { + // XXX better validation + sendJson(res, {status: 'error', message: 'Invalid parameters!'} ); + } else { + sendJson(res, {status: 'ok', message: 'Created new lecture'} ); + } + }); +}); + // Edit Course app.get( '/course/:id/edit', loadUser, loadCourse, function( req, res) { var course = req.course; @@ -813,53 +851,7 @@ app.get( '/course/:id/unsubscribe', loadUser, loadCourse, function( req, res ) { }); }); -// Create new lecture -app.get( '/course/:id/lecture/new', loadUser, loadCourse, function( req, res ) { - var courseId = req.params.id; - var course = req.course; - var lecture = {}; - - // If course isn't valid or user isn't authorized for course, redirect - if( ( ! course ) || ( ! course.authorized ) ) { - return res.redirect( '/course/' + courseId ); - } - - // Render new lecture form - res.render( 'lecture/new', { 'lecture' : lecture } ); -}); - -// Recieve New Lecture Form -app.post( '/course/:id/lecture/new', loadUser, loadCourse, function( req, res ) { - var courseId = req.params.id; - var course = req.course; - // Create new lecture from Lecture schema - var lecture = new Lecture; - - if( ( ! course ) || ( ! course.authorized ) ) { - res.redirect( '/course/' + courseId ); - return; - } - - // Populate lecture with form data - lecture.name = req.body.name; - lecture.date = req.body.date; - lecture.course = course._id; - lecture.creator = req.user._id; - - // Save lecture to database - lecture.save( function( err ) { - if( err ) { - // XXX better validation - req.flash( 'error', 'Invalid parameters!' ); - - res.render( 'lecture/new', { 'lecture' : lecture } ); - } else { - // XXX Redirect to new lecture instead - res.redirect( '/course/' + course._id ); - } - }); -}); // Display individual lecture and related notes @@ -881,10 +873,17 @@ app.get( '/lecture/:id', checkAjax, loadUser, loadLecture, function( req, res ) if ( note.public ) return note; }) } - res.json( { - 'lecture' : lecture.sanitized, + var sanitizedInstructor = instructor.sanitized; + var sanitizedLecture = lecture.sanitized; + if (!lecture.authorized) { + delete sanitizedInstructor.email; + } else { + sanitizedLecture.authorized = lecture.authorized; + } + sendJson(res, { + 'lecture' : sanitizedLecture, 'course' : course.sanitized, - 'instructor' : instructor.sanitized, + 'instructor' : sanitizedInstructor, 'notes' : notes.map(function(note) { return note.sanitized; }) @@ -892,35 +891,18 @@ app.get( '/lecture/:id', checkAjax, loadUser, loadLecture, function( req, res ) }); }) } else { - res.json( { status: 'error', msg: 'This course is orphaned' }) + sendJson(res, { status: 'not_found', msg: 'This course is orphaned' }) } }); }); -// Display new note form -app.get( '/lecture/:id/notes/new', loadUser, loadLecture, function( req, res ) { - var lectureId = req.params.id; - var lecture = req.lecture; - var note = {}; - - if( ( ! lecture ) || ( ! lecture.authorized ) ) { - res.redirect( '/lecture/' + lectureId ); - - return; - } - - res.render( 'notes/new', { 'note' : note } ); -}); // Recieve new note form -app.post( '/lecture/:id/notes/new', loadUser, loadLecture, function( req, res ) { - var lectureId = req.params.id; +app.post( '/lecture/:id', checkAjax, loadUser, loadLecture, function( req, res ) { var lecture = req.lecture; if( ( ! lecture ) || ( ! lecture.authorized ) ) { - res.redirect( '/lecture/' + lectureId ); - - return; + return sendJson(res, {status: 'error', message: 'There was a problem trying to create a note pad'}) } // Create note from Note schema @@ -937,19 +919,16 @@ app.post( '/lecture/:id/notes/new', loadUser, loadLecture, function( req, res ) note.save( function( err ) { if( err ) { // XXX better validation - req.flash( 'error', 'Invalid parameters!' ); - - res.render( 'notes/new', { 'note' : note } ); + sendJson(res, {status: 'error', message: 'There was a problem trying to create a note pad'}) } else { - // XXX Redirect to new note instead - res.redirect( '/lecture/' + lecture._id ); + sendJson(res, {status: 'ok', message: 'Successfully created a new note pad'}) } }); }); // Display individual note page -app.get( '/note/:id', checkAjax, loadUser, loadNote, function( req, res ) { +app.get( '/note/:id', /*checkAjax,*/ loadUser, loadNote, function( req, res ) { var note = req.note; // Set read only id for etherpad-lite or false for later check var roID = note.roID || false; @@ -999,7 +978,8 @@ app.get( '/note/:id', checkAjax, loadUser, loadNote, function( req, res ) { // Find notes based on lecture id, which will be displayed in a dropdown // on the page Note.find( { 'lecture' : lecture._id }, function( err, otherNotes ) { - res.json({ + /* + sendJson(res, { 'host' : serverHost, 'note' : note.sanitized, 'lecture' : lecture.sanitized, @@ -1009,12 +989,41 @@ app.get( '/note/:id', checkAjax, loadUser, loadNote, function( req, res ) { 'RO' : req.RO, 'roID' : roID, }); + */ + if( !req.RO ) { + // User is logged in and sees full notepad + + res.render( 'notes/index', { + 'layout' : 'noteLayout', + 'host' : serverHost, + 'note' : note, + 'lecture' : lecture, + 'otherNotes' : otherNotes, + 'RO' : false, + 'roID' : roID, + 'stylesheets' : [ 'dropdown.css', 'fc2.css' ], + 'javascripts' : [ 'dropdown.js', 'counts.js', 'backchannel.js', 'jquery.tmpl.min.js' ] + }); + } else { + // User is not logged in and sees notepad that is public + res.render( 'notes/public', { + 'layout' : 'noteLayout', + 'host' : serverHost, + 'note' : note, + 'otherNotes' : otherNotes, + 'roID' : roID, + 'lecture' : lecture, + 'stylesheets' : [ 'dropdown.css', 'fc2.css' ], + 'javascripts' : [ 'dropdown.js', 'counts.js', 'backchannel.js', 'jquery.tmpl.min.js' ] + }); + } }); }); } }); // Static pages and redirects +/* app.get( '/about', loadUser, function( req, res ) { res.redirect( 'http://blog.finalsclub.org/about.html' ); }); @@ -1038,6 +1047,7 @@ app.get( '/contact', loadUser, function( req, res ) { app.get( '/privacy', loadUser, function( req, res ) { res.render( 'static/privacy' ); }); +*/ // Authentication routes @@ -1045,33 +1055,37 @@ app.get( '/privacy', loadUser, function( req, res ) { // and other user authentication purposes // Render login page +/* app.get( '/login', function( req, res ) { log3("get login page") res.render( 'login' ); }); +*/ + +app.get( '/checkuser', checkAjax, loadUser, function( req, res ) { + sendJson(res, {user: req.user.sanitized}); +}); // Recieve login form -app.post( '/login', function( req, res ) { +app.post( '/login', checkAjax, function( req, res ) { var email = req.body.email; var password = req.body.password; log3("post login ...") // Find user from email User.findOne( { 'email' : email.toLowerCase() }, function( err, user ) { - log3(err) - log3(user) + log3(err) + log3(user) // If user exists, check if activated, if not notify them and send them to // the login form if( user ) { if( ! user.activated ) { // (undocumented) markdown-esque link functionality in req.flash - req.flash( 'error', 'This account isn\'t activated. Check your inbox or [click here](/resendActivation) to resend the activation email.' ); - req.session.activateCode = user._id; + sendJson(res, {status: 'error', message: 'This account isn\'t activated.'} ); - res.render( 'login' ); } else { // If user is activated, check if their password is correct if( user.authenticate( password ) ) { @@ -1089,42 +1103,30 @@ app.post( '/login', function( req, res ) { req.session.email = email; // alert the successful login - req.flash( 'info', 'Successfully logged in!' ); + sendJson(res, {status: 'ok', message:'Successfully logged in!'} ); // redirect to profile if we don't have a stashed request - res.redirect( redirect || '/profile' ); + //res.redirect( redirect || '/profile' ); }); } else { // Notify user of bad login - req.flash( 'error', 'Invalid login!' ); + sendJson(res, {status: 'error', message: 'Invalid login!'} ); - res.render( 'login' ); + //res.render( 'login' ); } } } else { // Notify user of bad login log3("bad login") - req.flash( 'error', 'Invalid login!' ); + sendJson(res, {status: 'error', message: 'Invalid login!'} ); - res.render( 'login' ); + //res.render( 'login' ); } }); }); -// Request reset password -app.get( '/resetpw', function( req, res ) { - log3("get resetpw page"); - res.render( 'resetpw' ); -}); - -// Display reset password from requested email -app.get( '/resetpw/:id', function( req, res ) { - var resetPassCode = req.params.id - res.render( 'resetpw', { 'verify': true, 'resetPassCode' : resetPassCode } ); -}); - // Recieve reset password request form -app.post( '/resetpw', function( req, res ) { +app.post( '/resetpass', checkAjax, function( req, res ) { log3("post resetpw"); var email = req.body.email @@ -1169,17 +1171,17 @@ app.post( '/resetpw', function( req, res ) { }); // Render request success page - res.render( 'resetpw-success', { 'email' : email } ); + sendJson(res, {status: 'ok', message: 'Your password has been reset. An email has been sent to ' + email }) }); } else { // Notify of error - res.render( 'resetpw-error', { 'email' : email } ); + sendJson(res, {status: 'error', message: 'We were unable to reset the password using that email address. Please try again.' }) } }); }); // Recieve reset password form -app.post( '/resetpw/:id', function( req, res ) { +app.post( '/resetpw/:id', checkAjax, function( req, res ) { log3("post resetpw.code"); var resetPassCode = req.params.id var email = req.body.email @@ -1195,19 +1197,19 @@ app.post( '/resetpw/:id', function( req, res ) { var valid = user.resetPassword(resetPassCode, pass1, pass2); if (valid) { user.save( function( err ) { - res.render( 'resetpw-success', { 'verify' : true, 'email' : email, 'resetPassCode' : resetPassCode } ); - }); + sendJson(res, {status: 'ok', message: 'Your password has been reset. You can now login with your the new password you just created.'}) + }); } } - // If there was a problem, notify user if (!valid) { - res.render( 'resetpw-error', { 'verify' : true, 'email' : email } ); + sendJson(res, {status: 'error', message: 'We were unable to reset the password. Please try again.' }) } }); }); // Display registration page +/* app.get( '/register', function( req, res ) { log3("get reg page"); @@ -1216,9 +1218,10 @@ app.get( '/register', function( req, res ) { res.render( 'register', { 'schools' : schools } ); }) }); +*/ // Recieve registration form -app.post( '/register', function( req, res ) { +app.post( '/register', checkAjax, function( req, res ) { var sid = req.sessionId; // Create new user from User schema @@ -1228,30 +1231,27 @@ app.post( '/register', function( req, res ) { user.email = req.body.email.toLowerCase(); user.password = req.body.password; user.session = sid; - // If school is set to other, then fill in school as what the - // user entered - user.school = req.body.school === 'Other' ? req.body.otherSchool : req.body.school; + // If school is set to other, then fill in school as what the user entered + user.school = req.body.school === 'Other' ? req.body.otherSchool : req.body.school; user.name = req.body.name; user.affil = req.body.affil; user.activated = false; // Validate email if ( ( user.email === '' ) || ( !isValidEmail( user.email ) ) ) { - req.flash( 'error', 'Please enter a valid email' ); - return res.redirect( '/register' ); + return sendJson(res, {status: 'error', message: 'Please enter a valid email'} ); } // Check if password is greater than 6 characters, otherwise notify user if ( req.body.password.length < 6 ) { - req.flash( 'error', 'Please enter a password longer than eight characters' ); - return res.redirect( '/register' ); + return sendJson(res, {status: 'error', message: 'Please enter a password longer than eight characters'} ); } // Pull out hostname from email var hostname = user.email.split( '@' ).pop(); // Check if email is from one of the special domains - if( /^(finalsclub.org|sleepless.com)$/.test( hostname ) ) { + if( /^(finalsclub.org)$/.test( hostname ) ) { user.admin = true; } @@ -1265,19 +1265,15 @@ app.post( '/register', function( req, res ) { User.findOne({ 'email' : user.email }, function(err, result ) { if (result.activated) { // If activated, make sure they know how to contact the admin - req.flash( 'error', 'There is already someone registered with this email, if this is in error contact info@finalsclub.org for help' ) - return res.redirect( '/register' ) + return sendJson(res, {status: 'error', message: 'There is already someone registered with this email, if this is in error contact info@finalsclub.org for help'} ); } else { // If not activated, direct them to the resendActivation page - req.flash( 'error', 'There is already someone registered with this email, if this is you, please check your email for the activation code' ) - return res.redirect( '/resendActivation' ) + return sendJson(res, {status: 'error', message: 'There is already someone registered with this email, if this is you, please check your email for the activation code'} ); } }); } else { // If any other type of error, prompt them to enter the registration again - req.flash( 'error', 'An error occurred during registration.' ); - - return res.redirect( '/register' ); + return sendJson(res, {status: 'error', message: 'An error occurred during registration.'} ); } } else { // send user activation email @@ -1298,8 +1294,7 @@ app.post( '/register', function( req, res ) { school.save( function( err ) { log3('school.save() done'); // Notify user that they have been added to the school - req.flash( 'info', 'You have automatically been added to the ' + school.name + ' network. Please check your email for the activation link' ); - res.redirect( '/' ); + sendJson(res, {status: 'ok', message: 'You have automatically been added to the ' + school.name + ' network. Please check your email for the activation link'} ); }); // Construct admin email about user registration var message = { @@ -1316,8 +1311,7 @@ app.post( '/register', function( req, res ) { // If there isn't a match, send associated welcome message sendUserWelcome( user, false ); // Tell user to check for activation link - req.flash( 'info', 'Your account has been created, please check your email for the activation link' ) - res.redirect( '/' ); + sendJson(res, {status: 'ok', message: 'Your account has been created, please check your email for the activation link'} ); // Construct admin email about user registration var message = { 'to' : ADMIN_EMAIL, @@ -1366,21 +1360,19 @@ app.get( '/resendActivation', function( req, res ) { }); // Display activation page -app.get( '/activate/:code', function( req, res ) { +app.get( '/activate/:code', checkAjax, function( req, res ) { var code = req.params.code; // XXX could break this out into a middleware if( ! code ) { - res.redirect( '/' ); + return sendJson(res, {status:'error', message: 'Invalid activation code!'} ); } // Find user by activation code User.findById( code, function( err, user ) { if( err || ! user ) { // If not found, notify user of invalid code - req.flash( 'error', 'Invalid activation code!' ); - - res.redirect( '/' ); + sendJson(res, {status:'error', message:'Invalid activation code!'} ); } else { // If valid, then activate user user.activated = true; @@ -1392,13 +1384,9 @@ app.get( '/activate/:code', function( req, res ) { // Save user to database user.save( function( err ) { if( err ) { - req.flash( 'error', 'Unable to activate account.' ); - - res.redirect( '/' ); + sendJson(res, {status: 'error', message: 'Unable to activate account.'} ); } else { - req.flash( 'info', 'Account successfully activated. Please complete your profile.' ); - - res.redirect( '/profile' ); + sendJson(res, {status: 'info', message: 'Account successfully activated. Please complete your profile.'} ); } }); }); @@ -1407,7 +1395,8 @@ app.get( '/activate/:code', function( req, res ) { }); // Logut user -app.get( '/logout', function( req, res ) { +app.get( '/logout', checkAjax, function( req, res ) { + sys.puts("logging out"); var sid = req.sessionID; // Find user by session id @@ -1418,23 +1407,16 @@ app.get( '/logout', function( req, res ) { // Save user to database user.save( function( err ) { - res.redirect( '/' ); + sendJson(res, {status: 'ok', message: 'Successfully logged out'}); }); } else { - res.redirect( '/' ); + sendJson(res, {status: 'ok', message: ''}); } }); }); -// Display users profile page -app.get( '/profile', loadUser, loggedIn, function( req, res ) { - var user = req.user; - - res.render( 'profile/index', { 'user' : user } ); -}); - // Recieve profile edit page form -app.post( '/profile', loadUser, loggedIn, function( req, res ) { +app.post( '/profile', checkAjax, loadUser, loggedIn, function( req, res ) { var user = req.user; var fields = req.body; @@ -1442,17 +1424,13 @@ app.post( '/profile', loadUser, loggedIn, function( req, res ) { var wasComplete = user.isComplete; if( ! fields.name ) { - req.flash( 'error', 'Please enter a valid name!' ); - - error = true; + return sendJson(res, {status: 'error', message: 'Please enter a valid name!'} ); } else { user.name = fields.name; } if( [ 'Student', 'Teachers Assistant' ].indexOf( fields.affiliation ) == -1 ) { - req.flash( 'error', 'Please select a valid affiliation!' ); - - error = true; + return sendJson(res, {status: 'error', message: 'Please select a valid affiliation!'} ); } else { user.affil = fields.affiliation; } @@ -1465,41 +1443,29 @@ app.post( '/profile', loadUser, loggedIn, function( req, res ) { user.password = fields.newPassword; } else { - req.flash( 'error', 'Mismatch in new password!' ); - - error = true; + return sendJson(res, {status: 'error', message: 'Mismatch in new password!'} ); } } else { - req.flash( 'error', 'Please supply your existing password.' ); - - error = true; + return sendJson(res, {status: 'error', message: 'Please supply your existing password.'} ); } } - user.major = fields.major; - user.bio = fields.bio; + user.major = fields.major; + user.bio = fields.bio; user.showName = ( fields.showName ? true : false ); - if( ! error ) { - user.save( function( err ) { - if( err ) { - req.flash( 'error', 'Unable to save user profile!' ); + user.save( function( err ) { + if( err ) { + sendJson(res, {status: 'error', message: 'Unable to save user profile!'} ); + } else { + if( ( user.isComplete ) && ( ! wasComplete ) ) { + sendJson(res, {status: 'ok', message: 'Your account is now fully activated. Thank you for joining FinalsClub!'} ); } else { - if( ( user.isComplete ) && ( ! wasComplete ) ) { - req.flash( 'info', 'Your account is now fully activated. Thank you for joining FinalsClub!' ); - - res.redirect( '/' ); - } else { - res.render( 'info', 'Your profile was successfully updated!' ); - - res.render( 'profile/index', { 'user' : user } ); - } + sendJson(res, {status:'ok', message:'Your profile was successfully updated!'} ); } - }); - } else { - res.render( 'profile/index', { 'user' : user } ); - } + } + }); }); @@ -1508,8 +1474,8 @@ app.post( '/profile', loadUser, loggedIn, function( req, res ) { function loadSubject( req, res, next ) { if( url.parse( req.url ).pathname.match(/subject/) ) { ArchivedSubject.findOne({id: req.params.id }, function(err, subject) { - if ( err ) { - res.json( {status: 'error', message: 'Subject with this ID does not exist'} ) + if ( err || !subject) { + sendJson(res, {status: 'not_found', message: 'Subject with this ID does not exist'} ) } else { req.subject = subject; next() @@ -1523,8 +1489,8 @@ function loadSubject( req, res, next ) { function loadOldCourse( req, res, next ) { if( url.parse( req.url ).pathname.match(/course/) ) { ArchivedCourse.findOne({id: req.params.id }, function(err, course) { - if ( err ) { - res.json( {status: 'error', message: 'Course with this ID does not exist'} ) + if ( err || !course ) { + sendJson(res, {status: 'not_found', message: 'Course with this ID does not exist'} ) } else { req.course = course; next() @@ -1557,56 +1523,63 @@ app.get( '/learn', loadUser, function( req, res ) { res.render( 'archive/learn', { 'courses' : featuredCourses } ); }) -app.get( '/learn/random', loadUser, function( req, res ) { - res.redirect( '/archive/course/'+ featuredCourses[Math.floor(Math.random()*featuredCourses.length)].id); +app.get( '/learn/random', checkAjax, function( req, res ) { + sendJson(res, {status: 'ok', data: '/archive/course/'+ featuredCourses[Math.floor(Math.random()*featuredCourses.length)].id }); }) app.get( '/archive', checkAjax, loadUser, function( req, res ) { ArchivedSubject.find({}).sort( 'name', '1' ).run( function( err, subjects ) { - if ( err ) { - res.json( {status: 'error', message: 'There was a problem gathering the archived courses, please try again later.'} ); + if ( err || subjects.length === 0) { + sendJson(res, {status: 'error', message: 'There was a problem gathering the archived courses, please try again later.'} ); } else { - res.json( { 'subjects' : subjects } ); + sendJson(res, { 'subjects' : subjects, 'user': req.user.sanitized } ); } }) }) app.get( '/archive/subject/:id', checkAjax, loadUser, loadSubject, function( req, res ) { ArchivedCourse.find({subject_id: req.params.id}).sort('name', '1').run(function(err, courses) { - if ( err ) { - res.json( {status: 'error', message: 'There are no archived courses'} ); + if ( err || courses.length === 0 ) { + sendJson(res, {status: 'not_found', message: 'There are no archived courses'} ); } else { - res.json( { 'courses' : courses, 'subject': req.subject } ); + sendJson(res, { 'courses' : courses, 'subject': req.subject, 'user': req.user.sanitized } ); } }) }) app.get( '/archive/course/:id', checkAjax, loadUser, loadOldCourse, function( req, res ) { ArchivedNote.find({course_id: req.params.id}).sort('name', '1').run(function(err, notes) { - if ( err ) { - res.json( {status: 'error', message: 'There are no notes in this course'} ); + if ( err || notes.length === 0) { + sendJson(res, {status: 'not_found', message: 'There are no notes in this course'} ); } else { - res.json( { 'notes' : notes, 'course' : req.course } ); + notes = notes.map(function(note) { return note.sanitized }); + sendJson(res, { 'notes': notes, 'course' : req.course, 'user': req.user.sanitized } ); } }) }) -app.get( '/archive/note/:id', loadUser, function( req, res ) { +app.get( '/archive/note/:id', checkAjax, loadUser, function( req, res ) { + console.log( "id="+req.params.id) ArchivedNote.findById(req.params.id, function(err, note) { - if ( err ) { - res.json( {status: 'error', message: 'This is not a valid id for a note'} ); + if ( err || !note ) { + sendJson(res, {status: 'not_found', message: 'This is not a valid id for a note'} ); } else { ArchivedCourse.findOne({id: note.course_id}, function(err, course) { - if ( err ) { - res.json( {status: 'error', message: 'There is no course for this note'} ) + if ( err || !course ) { + sendJson(res, {status: 'not_found', message: 'There is no course for this note'} ) } else { - res.json( { 'layout' : 'notesLayout', 'note' : note, 'course': course } ); + sendJson(res, { 'layout' : 'notesLayout', 'note' : note, 'course': course, 'user': req.user.sanitized } ); } }) } }) }) + +app.get( '*', function(req, res) { + res.sendfile('public/index.html'); +}); + // socket.io server // The finalsclub backchannel server uses socket.io to handle communication between the server and @@ -1749,6 +1722,7 @@ io.set('authorization', function ( handshake, next ) { }); var backchannel = new Backchannel(app, io.of('/backchannel'), { + // TODO: if lecture belongs to course (find pinker's courseId) pass a 'no-answers' true/false subscribe: function(lecture, send) { Post.find({'lecture': lecture}, function(err, posts) { send(posts); @@ -1868,8 +1842,8 @@ process.on('uncaughtException', function (e) { // Launch -mongoose.connect( app.set( 'dbUri' ) ); -mongoose.connection.db.serverConfig.connection.autoReconnect = true +// mongoose now exepects a mongo url +mongoose.connect( 'mongodb://localhost/fc' ); // FIXME: make relative to hostname var mailer = new Mailer( app.set('awsAccessKey'), app.set('awsSecretKey') ); @@ -1889,3 +1863,5 @@ function isValidEmail(email) { var re = /[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/; return email.match(re); } + +// Facebook connect