Removing EveryAuth from app.js and from requirements
[oweals/finalsclub.git] / app.js
diff --git a/app.js b/app.js
index 2799c583dffecf8ebfb870ff52631e41485036b3..ebe2d0c1c84dd436a418251ea9a10970b5348726 100644 (file)
--- 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' )
   }));
 
@@ -157,18 +162,14 @@ app.configure(function(){
   app.use( express.methodOverride() );
   // Static files are loaded when no dynamic views match.
   app.use( express.static( __dirname + '/public' ) );
+
   // Sets the routers middleware to load after everything set
   // before it, but before static files.
   app.use( app.router );
 
   app.use(express.logger({ format: ':method :url' }));
-  // 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 );
+  // This is the command to use the default express error logger/handler
+  app.use(express.errorHandler({ dumpExceptions: true }));
 });
 
 
@@ -291,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
@@ -312,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;
 
@@ -322,12 +327,39 @@ 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.
+      sendJson(res,  {status: 'not_found', message: 'Invalid school specified!'} );
+    }
+  });
+}
+
+function loadSchoolSlug( req, res, next ) {
+  var user    = req.user;
+  var schoolSlug = req.params.slug;
+
+  console.log("loading a school by slug");
+  //console.log(schoolSlug);
+
+  School.findOne({ 'slug': schoolSlug }, function( err, school ) {
+    console.log( school );
+    if( school ) {
+      req.school = school;
+
+      // If a school is found, the user is checked to see if they are
+      // authorized to see or interact with anything related to that
+      // school.
+      next()
+      //school.authorize( user, function( authorized ){
+        //req.school.authorized = authorized;
+        //next();
+      //});
     } else {
       // If no school is found, display an appropriate error.
-      sendJson(res,  {status: 'error', message: 'Invalid school specified!'} );
+      sendJson(res,  {status: 'not_found', message: 'Invalid school specified!'} );
     }
   });
 }
@@ -351,7 +383,7 @@ function loadCourse( req, res, next ) {
       });
     } else {
       // If no course is found, display an appropriate error.
-      sendJson(res,  {status: 'error', message: 'Invalid course specified!'} );
+      sendJson(res,  {status: 'not_found', message: 'Invalid course specified!'} );
     }
   });
 }
@@ -375,7 +407,7 @@ function loadLecture( req, res, next ) {
       });
     } else {
       // If no lecture is found, display an appropriate error.
-      sendJson(res,  {status: 'error', message: 'Invalid lecture specified!'} );
+      sendJson(res,  {status: 'not_found', message: 'Invalid lecture specified!'} );
     }
   });
 }
@@ -460,7 +492,7 @@ app.dynamicHelpers( {
 });
 
 function sendJson( res, obj ) {
-  res.header('Cache-Control', 'no-cache');
+  res.header('Cache-Control', 'no-cache, no-store');
   res.json(obj);
 }
 
@@ -483,6 +515,8 @@ app.get( '/', loadUser, function( req, res ) {
 // in those schools.
 // Public with some private information
 app.get( '/schools', checkAjax, loadUser, function( req, res ) {
+  sys.puts('loading schools');
+  console.log(req.user);
   var user = req.user;
 
   var schoolList = [];
@@ -492,9 +526,27 @@ 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.
-      sendJson(res, { 'user': user.sanitized, '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
+                };
+                return school;
+              })
+            });
+          }
+        });
+      });
     } else {
       // If no schools have been found, display none
       //res.render( 'schools', { 'schools' : [] } );
@@ -503,9 +555,54 @@ app.get( '/schools', checkAjax, loadUser, function( req, res ) {
   });
 });
 
-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;
+  var courses;
+  console.log( 'loading a school by school/:id now name' );
+
+  //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.
+      sys.puts(courses);
+      if( courses.length > 0 ) {
+        courses = courses.filter(function(course) {
+          if (!course.deleted) return course;
+        }).map(function(course) {
+          return course.sanitized;
+        });
+      } else {
+        school.courses = [];
+      }
+      sanitizedSchool.courses = courses;
+      sys.puts(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.
+      sendJson(res, { 'school': sanitizedSchool, 'user': user.sanitized })
+    });
+  //});
+});
+
+// FIXME: version of the same using school slugs instead of ids
+// TODO: merge this with the :id funciton or depricate it
+app.get( '/schoolslug/:slug', checkAjax, loadUser, loadSchoolSlug, function( req, res ) {
   var school = req.school;
   var user = req.user;
+  console.log( 'loading a schoolslug/:slug' );
 
   school.authorize( user, function( authorized ) {
     // This is used to display interface elements for those users
@@ -533,25 +630,9 @@ app.get( '/school/:id', checkAjax, loadUser, loadSchool, function( req, res ) {
   });
 });
 
-// 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;
@@ -559,15 +640,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
@@ -592,21 +674,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,
@@ -635,10 +709,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
@@ -666,7 +737,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'} )
             }
           });
         }
@@ -680,9 +751,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,
@@ -706,14 +775,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'} )
       }
     }
   })
@@ -727,18 +795,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
-      sendJson(res,  { 'user': req.user.sanitized, '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;
@@ -823,53 +925,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
@@ -891,11 +947,17 @@ app.get( '/lecture/:id', checkAjax, loadUser, loadLecture, function( req, res )
               if ( note.public ) return note;
             })
           }
+          var sanitizedInstructor = instructor.sanitized;
+          var sanitizedLecture = lecture.sanitized;
+          if (!lecture.authorized) {
+            delete sanitizedInstructor.email;
+          } else {
+            sanitizedLecture.authorized = lecture.authorized;
+          }
           sendJson(res,  {
-            'lecture'                  : lecture.sanitized,
+            'lecture'                  : sanitizedLecture,
             'course'                   : course.sanitized,
-            'instructor'  : instructor.sanitized,
-            'user'        : req.user.sanitized,
+            'instructor'  : sanitizedInstructor,
             'notes'                            : notes.map(function(note) {
               return note.sanitized;
             })
@@ -903,35 +965,18 @@ app.get( '/lecture/:id', checkAjax, loadUser, loadLecture, function( req, res )
         });
       })
     } else {
-      sendJson(res,  { 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
@@ -948,12 +993,9 @@ 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'})
     }
   });
 });
@@ -1055,6 +1097,7 @@ app.get( '/note/:id', /*checkAjax,*/ loadUser, loadNote, function( req, res ) {
 });
 
 // Static pages and redirects
+/*
 app.get( '/about', loadUser, function( req, res ) {
   res.redirect( 'http://blog.finalsclub.org/about.html' );
 });
@@ -1078,6 +1121,7 @@ app.get( '/contact', loadUser, function( req, res ) {
 app.get( '/privacy', loadUser, function( req, res ) {
   res.render( 'static/privacy' );
 });
+*/
 
 
 // Authentication routes
@@ -1085,33 +1129,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 ) ) {
@@ -1129,42 +1177,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
 
@@ -1209,17 +1245,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
@@ -1235,19 +1271,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");
 
@@ -1256,9 +1292,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
@@ -1268,30 +1305,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;
   }
 
@@ -1305,19 +1339,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
@@ -1338,8 +1368,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 = {
@@ -1356,8 +1385,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,
@@ -1406,21 +1434,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;
@@ -1432,13 +1458,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.'} );
           }
         });
       });
@@ -1447,7 +1469,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
@@ -1458,23 +1481,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;
 
@@ -1482,17 +1498,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;
   }
@@ -1505,41 +1517,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 } );
-  }
+    }
+  });
 });
 
 
@@ -1548,8 +1548,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 ) {
-        sendJson(res,  {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()
@@ -1563,8 +1563,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 ) {
-        sendJson(res,  {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()
@@ -1597,13 +1597,13 @@ 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 ) {
+    if ( err || subjects.length === 0) {
       sendJson(res,  {status: 'error', message: 'There was a problem gathering the archived courses, please try again later.'} );
     } else {
       sendJson(res,  { 'subjects' : subjects, 'user': req.user.sanitized } );
@@ -1613,8 +1613,8 @@ app.get( '/archive', checkAjax, loadUser, function( req, res ) {
 
 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 ) {
-      sendJson(res,  {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 {
       sendJson(res,  { 'courses' : courses, 'subject': req.subject, 'user': req.user.sanitized } );
     }
@@ -1623,22 +1623,24 @@ app.get( '/archive/subject/:id', checkAjax, loadUser, loadSubject, function( req
 
 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 ) {
-      sendJson(res,  {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 {
-      sendJson(res,  { 'notes' : notes, 'course' : req.course, 'user': req.user.sanitized } );
+      notes = notes.map(function(note) { return note.sanitized });
+      sendJson(res,  { 'notes': notes, 'course' : req.course, 'user': req.user.sanitized } );
     }
   })
 })
 
 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 ) {
-      sendJson(res,  {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 ) {
-          sendJson(res,  {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 {
           sendJson(res,  { 'layout' : 'notesLayout', 'note' : note, 'course': course, 'user': req.user.sanitized } );
         }
@@ -1794,6 +1796,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);
@@ -1913,8 +1916,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') );
 
@@ -1934,3 +1937,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