5 var crypto = require( 'crypto' );
7 var mongoose = require( 'mongoose' );
9 var Schema = mongoose.Schema;
10 var ObjectId = mongoose.SchemaTypes.ObjectId;
15 return Math.round( ( new Date().valueOf() * Math.random() ) ).toString();
22 var UserSchema = new Schema( {
23 email : { type : String, require: true, index : { unique : true } },
27 created : { type : Date, default : Date.now },
30 activateCode : String,
31 resetPassCode : String,
35 showName : { 'type' : Boolean, 'default' : true },
36 admin : { 'type' : Boolean, 'default' : false }
39 UserSchema.virtual( 'sanitized' ).get(function() {
44 showName: this.showName,
51 UserSchema.virtual( 'displayName' )
60 UserSchema.virtual( 'password' )
61 .set( function( password ) {
63 this.hashed = this.encrypt( password );
66 UserSchema.virtual( 'isComplete' )
68 // build on this as the schema develops
70 return ( this.name && this.affil && this.hashed );
73 UserSchema.method( 'encrypt', function( password ) {
74 var hmac = crypto.createHmac( 'sha1', this.salt );
76 return hmac.update( password ).digest( 'hex' );
79 UserSchema.method( 'authenticate', function( plaintext ) {
80 return ( this.encrypt( plaintext ) === this.hashed );
83 UserSchema.method('genRandomPassword', function () {
84 // this function generates the random password, it does not keep or save it.
88 var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
89 for (var i = 0; i < len; i++) {
90 var randomPoz = Math.floor(Math.random() * charSet.length);
91 plaintext += charSet.substring(randomPoz, randomPoz + 1);
97 UserSchema.method( 'setResetPassCode', function ( code ) {
98 this.resetPassCode = code;
99 this.resetPassDate = new Date();
100 return this.resetPassCode;
103 UserSchema.method( 'canResetPassword', function ( code ) {
104 // ensure the passCode is valid, matches and the date has not yet expired, lets say 2 weeks for good measure.
107 var expDate = new Date();
108 expDate.setDate(expDate.getDate() - 14);
110 // we have a valid code and date
111 if (this.resetPassCode != null && this.resetPassDate != null && this.resetPassDate >= expDate && this.resetPassCode == code) {
118 UserSchema.method( 'resetPassword', function ( code, newPass1, newPass2) {
119 // ensure the date has not expired, lets say 2 weeks for good measure.
122 if (this.canResetPassword(code) && newPass1 != null && newPass1.length > 0 && newPass1 == newPass2) {
123 this.password = newPass1;
124 this.resetPassCode = null;
125 this.resetPassDate = null;
132 var User = mongoose.model( 'User', UserSchema );
136 var SchoolSchema = new Schema( {
137 name : { type : String, required : true },
138 description : String,
141 created : { type : Date, default : Date.now },
148 // slug is the url version of a school
150 SchoolSchema.virtual( 'sanitized' ).get(function() {
154 description: this.description,
162 SchoolSchema.method( 'authorize', function( user, cb ) {
163 return cb(user.admin || ( this.users.indexOf( user._id ) !== -1 ));
166 var School = mongoose.model( 'School', SchoolSchema );
170 var HarvardCourses = new Schema( {
171 cat_num : Number, // Catalog numb, unique_togther w/ term
172 term : String, // FALL or SPRING
174 field : String, // TODO: Add docs
175 number : Number, // string, int, float, intStringFloat
177 faculty : String, // hash fk to faculty table
178 description : String,
179 prerequisites : String,
181 meetings : String, // TODO: try to auto parse this
182 building : String, // FIXME: Most == '', this is why we have to update
186 HarvardCourses.virtual( 'sanitized' ).get(function() {
191 number : this.number,
192 desc : this.description,
193 meetings: this.meetings,
194 building: this.building,
196 faculty : this.faculty
203 var CourseSchema = new Schema( {
204 name : { type : String, required : true },
206 description : String,
207 instructor : ObjectId,
210 // courses are tied to one school
213 // XXX: room for additional resources
214 created : { type : Date, default : Date.now },
218 // many users may subscribe to a course
222 CourseSchema.virtual( 'sanitized' ).get(function() {
226 number: this.number || 'None',
227 description: this.description || 'None',
228 subject: this.subject || 'None',
229 department: this.department || 'None'
235 CourseSchema.virtual( 'displayName' )
238 return this.number + ': ' + this.name;
244 CourseSchema.method( 'authorize', function( user, cb ) {
245 School.findById( this.school, function( err, school ) {
247 school.authorize( user, function( result ) {
254 CourseSchema.method( 'subscribed', function( user ) {
255 return ( this.users.indexOf( user ) > -1 ) ;
258 CourseSchema.method( 'subscribe', function( user, callback ) {
261 // mongoose issue #404
262 Course.collection.update( { '_id' : id }, { '$addToSet' : { 'users' : user } }, function( err ) {
267 CourseSchema.method( 'unsubscribe', function( user, callback ) {
270 // mongoose issue #404
271 Course.collection.update( { '_id' : id }, { '$pull' : { 'users' : user } }, function( err ) {
276 CourseSchema.method( 'delete', function( callback ) {
279 Course.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
280 if (callback) callback( err );
281 Lecture.find( { course: id }, function( err, lectures) {
282 if (lectures.length > 0) {
283 lectures.forEach(function(lecture) {
291 var Course = mongoose.model( 'Course', CourseSchema );
295 var LectureSchema = new Schema( {
296 name : { type : String, required : true },
297 date : { type : Date, default: Date.now },
305 LectureSchema.virtual( 'sanitized' ).get(function() {
316 LectureSchema.method( 'authorize', function( user, cb ) {
317 Course.findById( this.course, function( err, course ) {
319 course.authorize( user, function( res ) {
328 LectureSchema.method( 'delete', function( callback ) {
331 Lecture.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
332 if (callback) callback( err );
333 Note.find( { lecture : id }, function(err, notes) {
334 notes.forEach(function(note) {
338 Post.find( { lecture : id }, function(err, posts) {
339 posts.forEach(function(post) {
346 var Lecture = mongoose.model( 'Lecture', LectureSchema );
350 var NoteSchema = new Schema( {
351 name : { type : String, required : true },
356 created : { type : Date, default : Date.now },
362 collaborators : [String]
365 NoteSchema.virtual( 'sanitized').get(function() {
378 NoteSchema.method( 'authorize', function( user, cb ) {
379 Lecture.findById( this.lecture, function( err, lecture ) {
381 lecture.authorize( user, function( res ) {
390 NoteSchema.method( 'addVisit', function() {
393 Note.collection.update( { '_id' : id }, { '$inc' : { 'visits' : 1 } } );
396 NoteSchema.method( 'delete', function( callback ) {
399 Note.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
400 if (callback) callback( err );
404 var Note = mongoose.model( 'Note', NoteSchema );
408 var PostSchema = new Schema({
409 date : { type : Date, default : Date.now },
415 userid : String, // ObjectId,
421 lecture : String, // ObjectId
425 PostSchema.method( 'delete', function( callback ) {
428 Post.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
429 if (callback) callback( err );
433 mongoose.model( 'Post', PostSchema );
435 var ArchivedCourse = new Schema({
444 mongoose.model( 'ArchivedCourse', ArchivedCourse )
446 var ArchivedNote = new Schema({
452 ArchivedNote.virtual( 'sanitized' ).get(function() {
455 topic: this.topic === '' ? (this.text.replace(/(<(.|\n)*?>)|[\r\n\t]*/g, '')).substr(0, 15) + '...' : this.topic
460 mongoose.model( 'ArchivedNote', ArchivedNote )
462 var ArchivedSubject = new Schema({
467 mongoose.model( 'ArchivedSubject', ArchivedSubject )
469 module.exports.mongoose = mongoose;