/* vim: set ts=2: */ // DEPENDENCIES var crypto = require( 'crypto' ); var mongoose = require( 'mongoose' ); var Schema = mongoose.Schema; var ObjectId = mongoose.SchemaTypes.ObjectId; // SUPPORT FUNCTIONS function salt() { return Math.round( ( new Date().valueOf() * Math.random() ) ).toString(); } // MODELS // user var UserSchema = new Schema( { email : { type : String, require: true, index : { unique : true } }, school : String, name : String, affil : String, created : { type : Date, default : Date.now }, hashed : String, activated : Boolean, activateCode : String, resetPassCode : String, resetPassDate : Date, salt : String, session : String, showName : { 'type' : Boolean, 'default' : true }, admin : { 'type' : Boolean, 'default' : false } }); UserSchema.virtual( 'sanitized' ).get(function() { var user = { email: this.email, name: this.name, affil: this.affil, showName: this.showName, admin: this.admin } return user; }); UserSchema.virtual( 'displayName' ) .get( function() { if( this.showName ) { return this.name; } else { return this.email; } }); UserSchema.virtual( 'password' ) .set( function( password ) { this.salt = salt(); this.hashed = this.encrypt( password ); }); UserSchema.virtual( 'isComplete' ) .get( function() { // build on this as the schema develops return ( this.name && this.affil && this.hashed ); }); UserSchema.method( 'encrypt', function( password ) { var hmac = crypto.createHmac( 'sha1', this.salt ); return hmac.update( password ).digest( 'hex' ); }); UserSchema.method( 'authenticate', function( plaintext ) { return ( this.encrypt( plaintext ) === this.hashed ); }); UserSchema.method('genRandomPassword', function () { // this function generates the random password, it does not keep or save it. var plaintext = ''; var len = 8; var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (var i = 0; i < len; i++) { var randomPoz = Math.floor(Math.random() * charSet.length); plaintext += charSet.substring(randomPoz, randomPoz + 1); } return plaintext; }); UserSchema.method( 'setResetPassCode', function ( code ) { this.resetPassCode = code; this.resetPassDate = new Date(); return this.resetPassCode; }); UserSchema.method( 'canResetPassword', function ( code ) { // ensure the passCode is valid, matches and the date has not yet expired, lets say 2 weeks for good measure. var value = false; var expDate = new Date(); expDate.setDate(expDate.getDate() - 14); // we have a valid code and date if (this.resetPassCode != null && this.resetPassDate != null && this.resetPassDate >= expDate && this.resetPassCode == code) { value = true; } return value; }); UserSchema.method( 'resetPassword', function ( code, newPass1, newPass2) { // ensure the date has not expired, lets say 2 weeks for good measure. var success = false; if (this.canResetPassword(code) && newPass1 != null && newPass1.length > 0 && newPass1 == newPass2) { this.password = newPass1; this.resetPassCode = null; this.resetPassDate = null; success = true; } return success; }); var User = mongoose.model( 'User', UserSchema ); // schools var SchoolSchema = new Schema( { name : { type : String, required : true }, description : String, url : String, created : { type : Date, default : Date.now }, hostnames : Array, users : Array, courseNum : Number }); SchoolSchema.virtual( 'sanitized' ).get(function() { var school = { _id: this._id, name: this.name, description: this.description, url: this.url, coursesNum: this.courseNum } return school; }) SchoolSchema.method( 'authorize', function( user, cb ) { return cb(user.admin || ( this.users.indexOf( user._id ) !== -1 )); }); var School = mongoose.model( 'School', SchoolSchema ); // harvardcourses var HarvardCourses = new Schema( { cat_num : Number, // Catalog numb, unique_togther w/ term term : String, // FALL or SPRING bracketed : Boolean, field : String, // TODO: Add docs number : Number, // string, int, float, intStringFloat title : String, faculty : String, // hash fk to faculty table description : String, prerequisites : String, notes : String, meetings : String, // TODO: try to auto parse this building : String, // FIXME: Most == '', this is why we have to update room : String }); HarvardCourses.virtual( 'sanitized' ).get(function() { var hcourse = { _id : this._id, title : this.name, field : this.field, number : this.number, desc : this.description, meetings: this.meetings, building: this.building, room : this.room, faculty : this.faculty } return hcourse }) // courses var CourseSchema = new Schema( { name : { type : String, required : true }, number : String, description : String, instructor : ObjectId, subject : String, department : String, // courses are tied to one school school : ObjectId, // XXX: room for additional resources created : { type : Date, default : Date.now }, creator : ObjectId, deleted : Boolean, // many users may subscribe to a course users : Array, lectureNum : Number }); CourseSchema.virtual( 'sanitized' ).get(function() { var course = { _id: this._id, name: this.name, number: this.number || 'None', description: this.description || 'None', subject: this.subject || 'None', department: this.department || 'None', lectureNum: this.lecturenum } return course; }); CourseSchema.virtual( 'displayName' ) .get( function() { if( this.number ) { return this.number + ': ' + this.name; } else { return this.name; } }); CourseSchema.method( 'authorize', function( user, cb ) { School.findById( this.school, function( err, school ) { if ( school ) { school.authorize( user, function( result ) { return cb( result ); }) } }); }); CourseSchema.method( 'subscribed', function( user ) { return ( this.users.indexOf( user ) > -1 ) ; }); CourseSchema.method( 'subscribe', function( user, callback ) { var id = this._id; // mongoose issue #404 Course.collection.update( { '_id' : id }, { '$addToSet' : { 'users' : user } }, function( err ) { callback( err ); }); }); CourseSchema.method( 'unsubscribe', function( user, callback ) { var id = this._id; // mongoose issue #404 Course.collection.update( { '_id' : id }, { '$pull' : { 'users' : user } }, function( err ) { callback( err ); }); }); CourseSchema.method( 'delete', function( callback ) { var id = this._id; Course.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) { if (callback) callback( err ); Lecture.find( { course: id }, function( err, lectures) { if (lectures.length > 0) { lectures.forEach(function(lecture) { lecture.delete(); }) } }) }) }); var Course = mongoose.model( 'Course', CourseSchema ); // lectures var LectureSchema = new Schema( { name : { type : String, required : true }, date : { type : Date, default: Date.now }, live : Boolean, creator : ObjectId, deleted : Boolean, course : ObjectId }); LectureSchema.virtual( 'sanitized' ).get(function() { var lecture = { _id: this._id, name: this.name, date: this.date, live: this.live } return lecture; }) LectureSchema.method( 'authorize', function( user, cb ) { Course.findById( this.course, function( err, course ) { if (course) { course.authorize( user, function( res ) { return cb( res ); }) } else { return cb( false ); } }); }); LectureSchema.method( 'delete', function( callback ) { var id = this._id; Lecture.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) { if (callback) callback( err ); Note.find( { lecture : id }, function(err, notes) { notes.forEach(function(note) { note.delete(); }) }) Post.find( { lecture : id }, function(err, posts) { posts.forEach(function(post) { post.delete(); }) }) }) }); var Lecture = mongoose.model( 'Lecture', LectureSchema ); // notes var NoteSchema = new Schema( { name : { type : String, required : true }, path : String, public : Boolean, roID : String, visits : Number, created : { type : Date, default : Date.now }, creator : ObjectId, deleted : Boolean, lecture : ObjectId, collaborators : [String] }); NoteSchema.virtual( 'sanitized').get(function() { var note = { _id: this._id, name: this.name, path: this.path, public: this.public, roID: this.roID, visits: this.visits } return note; }); NoteSchema.method( 'authorize', function( user, cb ) { Lecture.findById( this.lecture, function( err, lecture ) { if (lecture) { lecture.authorize( user, function( res ) { return cb( res ); }) } else { return cb( false ); } }); }); NoteSchema.method( 'addVisit', function() { var id = this._id; Note.collection.update( { '_id' : id }, { '$inc' : { 'visits' : 1 } } ); }); NoteSchema.method( 'delete', function( callback ) { var id = this._id; Note.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) { if (callback) callback( err ); }) }); var Note = mongoose.model( 'Note', NoteSchema ); // comments var PostSchema = new Schema({ date : { type : Date, default : Date.now }, body : String, votes : [String], reports : [String], public : Boolean, userid : String, // ObjectId, userName : String, userAffil : String, comments : Array, lecture : String, // ObjectId deleted : Boolean }) PostSchema.method( 'delete', function( callback ) { var id = this._id; Post.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) { if (callback) callback( err ); }) }); mongoose.model( 'Post', PostSchema ); var ArchivedCourse = new Schema({ id: Number, instructor: String, section: String, name: String, description: String, subject_id: Number }) mongoose.model( 'ArchivedCourse', ArchivedCourse ) var ArchivedNote = new Schema({ course_id: Number, topic: String, text: String }) ArchivedNote.virtual( 'sanitized' ).get(function() { var note = { _id: this._id, topic: this.topic === '' ? (this.text.replace(/(<(.|\n)*?>)|[\r\n\t]*/g, '')).substr(0, 15) + '...' : this.topic } return note; }) mongoose.model( 'ArchivedNote', ArchivedNote ) var ArchivedSubject = new Schema({ id: Number, name: String }) mongoose.model( 'ArchivedSubject', ArchivedSubject ) module.exports.mongoose = mongoose;