Add epl submodule back in
[oweals/finalsclub.git] / models.js
1 /* vim: set ts=2: */
2
3 // DEPENDENCIES
4
5 var crypto = require( 'crypto' );
6
7 var mongoose    = require( 'mongoose' );
8
9 var Schema              = mongoose.Schema;
10 var ObjectId    = mongoose.SchemaTypes.ObjectId;
11
12 // SUPPORT FUNCTIONS
13
14 function salt() {
15         return Math.round( ( new Date().valueOf() * Math.random() ) ).toString();
16 }
17
18 // MODELS
19
20 // user
21
22 var UserSchema = new Schema( {
23         email                                           : { type : String, require: true, index : { unique : true } },
24         school                                  : String,
25         name                                            : String,
26         affil                                           : String,
27   created         : { type : Date, default : Date.now },
28         hashed                                  : String,
29         activated                               : Boolean,
30         activateCode            : String,
31         resetPassCode           : String,
32         resetPassDate           : Date,
33         salt                                            : String,
34         session                                 : String,
35         showName                                : { 'type' : Boolean, 'default' : true },
36         admin                                           : { 'type' : Boolean, 'default' : false }
37 });
38
39 UserSchema.virtual( 'displayName' )
40         .get( function() {
41                 if( this.showName ) {
42                         return this.name;
43                 } else {
44                         return this.email;
45                 }
46         });
47
48 UserSchema.virtual( 'password' )
49         .set( function( password ) {
50                 this.salt                               = salt();
51                 this.hashed                     = this.encrypt( password );
52         });
53
54 UserSchema.virtual( 'isComplete' )
55         .get( function() {
56                 // build on this as the schema develops
57
58                 return ( this.name && this.affil && this.hashed );
59         });
60
61 UserSchema.method( 'encrypt', function( password ) {
62         var hmac = crypto.createHmac( 'sha1', this.salt );
63
64         return hmac.update( password ).digest( 'hex' );
65 });
66
67 UserSchema.method( 'authenticate', function( plaintext ) {
68         return ( this.encrypt( plaintext ) === this.hashed );
69 });
70
71 UserSchema.method('genRandomPassword', function () {
72         // this function generates the random password, it does not keep or save it.
73         var plaintext = '';
74
75         var len = 8;
76         var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
77         for (var i = 0; i < len; i++) {
78                 var randomPoz = Math.floor(Math.random() * charSet.length);
79                 plaintext += charSet.substring(randomPoz, randomPoz + 1);
80         }
81
82         return plaintext;
83 });
84
85 UserSchema.method( 'setResetPassCode', function ( code ) {
86         this.resetPassCode = code;
87         this.resetPassDate = new Date();
88         return this.resetPassCode;
89 });
90
91 UserSchema.method( 'canResetPassword', function ( code ) {
92         // ensure the passCode is valid, matches and the date has not yet expired, lets say 2 weeks for good measure.
93         var value = false;
94
95         var expDate = new Date();
96         expDate.setDate(expDate.getDate() - 14);
97
98         // we have a valid code and date
99         if (this.resetPassCode != null && this.resetPassDate != null && this.resetPassDate >= expDate && this.resetPassCode == code) {
100                 value = true;
101         }
102
103         return value;
104 });
105
106 UserSchema.method( 'resetPassword', function ( code, newPass1,  newPass2) {
107         // ensure the date has not expired, lets say 2 weeks for good measure.
108
109         var success = false;
110         if (this.canResetPassword(code) && newPass1 != null && newPass1.length > 0 && newPass1 == newPass2) {
111                 this.password = newPass1;
112                 this.resetPassCode = null;
113                 this.resetPassDate = null;
114                 success = true;
115         }
116
117         return success;
118 });
119
120 var User = mongoose.model( 'User', UserSchema );
121
122 // schools
123
124 var SchoolSchema = new Schema( {
125         name                            : { type : String, required : true },
126         description     : String,
127         url                                     : String,
128
129   created     : { type : Date, default : Date.now },
130         hostnames               : Array,
131
132         users                           : Array
133 });
134
135 SchoolSchema.method( 'authorize', function( user, cb ) {
136         return cb(user.admin || ( this.users.indexOf( user._id ) !== -1 ));
137 });
138
139 var School = mongoose.model( 'School', SchoolSchema );
140
141 // courses
142
143 var CourseSchema = new Schema( {
144         name                            : { type : String, required : true },
145         number                  : String,
146         description     : String,
147   instructor  : ObjectId,
148   subject     : String,
149   department  : String,
150         // courses are tied to one school
151         school                  : ObjectId,
152
153         // XXX: room for additional resources
154   created     : { type : Date, default : Date.now },
155   creator     : ObjectId,
156   deleted     : Boolean,
157
158         // many users may subscribe to a course
159         users                           : Array
160 });
161
162 CourseSchema.virtual( 'displayName' )
163         .get( function() {
164                 if( this.number ) {
165                         return this.number + ': ' + this.name;
166                 } else {
167                         return this.name;
168                 }
169         });
170
171 CourseSchema.method( 'authorize', function( user, cb ) {
172         School.findById( this.school, function( err, school ) {
173                 if ( school ) {
174                         school.authorize( user, function( result ) {
175                                                         return cb( result );
176                         })
177                 }
178         });
179 });
180
181 CourseSchema.method( 'subscribed', function( user ) {
182         return ( this.users.indexOf( user ) > -1 ) ;
183 });
184
185 CourseSchema.method( 'subscribe', function( user, callback ) {
186         var id = this._id;
187
188         // mongoose issue #404
189         Course.collection.update( { '_id' : id }, { '$addToSet' : { 'users' : user } }, function( err ) {
190                 callback( err );
191         });
192 });
193
194 CourseSchema.method( 'unsubscribe', function( user, callback ) {
195         var id = this._id;
196
197         // mongoose issue #404
198         Course.collection.update( { '_id' : id }, { '$pull' : { 'users' : user } }, function( err ) {
199                 callback( err );
200         });
201 });
202
203 CourseSchema.method( 'delete', function( callback ) {
204   var id = this._id;
205
206   Course.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
207     if (callback) callback( err );
208     Lecture.find( { course: id }, function( err, lectures) {
209       if (lectures.length > 0) {
210         lectures.forEach(function(lecture) {
211           lecture.delete();
212         })
213       }
214     })
215   })
216 });
217
218 var Course = mongoose.model( 'Course', CourseSchema );
219
220 // lectures
221
222 var LectureSchema       = new Schema( {
223         name                                    : { type : String, required : true },
224         date                                    : { type : Date, default: Date.now },
225         live                                    : Boolean,
226   creator       : ObjectId,
227   deleted       : Boolean,
228
229         course                          : ObjectId
230 });
231
232 LectureSchema.method( 'authorize', function( user, cb ) {
233         Course.findById( this.course, function( err, course ) {
234                 if (course) {
235                         course.authorize( user, function( res ) {
236                                 return cb( res );
237                         })
238                 } else {
239                  return cb( false );
240                 }
241         });
242 });
243
244 LectureSchema.method( 'delete', function( callback ) {
245   var id = this._id;
246
247   Lecture.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
248     if (callback) callback( err );
249     Note.find( { lecture : id }, function(err, notes) {
250       notes.forEach(function(note) {
251         note.delete();
252       })
253     })
254     Post.find( { lecture : id }, function(err, posts) {
255       posts.forEach(function(post) {
256         post.delete();
257       })
258     })
259   })
260 });
261
262 var Lecture = mongoose.model( 'Lecture', LectureSchema );
263
264 // notes
265
266 var NoteSchema = new Schema( {
267         name                                    : { type : String, required : true },
268         path                                    : String,
269   public        : Boolean,
270   roID          : String,
271         visits                          : Number,
272   created         : { type : Date, default : Date.now },
273   creator       : ObjectId,
274   deleted       : Boolean,
275
276         lecture                         : ObjectId,
277
278         collaborators : [String]
279 });
280
281 NoteSchema.method( 'authorize', function( user, cb ) {
282         Lecture.findById( this.lecture, function( err, lecture ) {
283                 if (lecture) {
284                         lecture.authorize( user, function( res ) {
285                                 return cb( res );
286                         })
287                 } else {
288                         return cb( false );
289                 }
290         });
291 });
292
293 NoteSchema.method( 'addVisit', function() {
294         var id = this._id;
295
296         Note.collection.update( { '_id' : id }, { '$inc' : { 'visits' : 1 } } );
297 });
298
299 NoteSchema.method( 'delete', function( callback ) {
300   var id = this._id;
301
302   Note.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
303     if (callback) callback( err );
304   })
305 });
306
307 var Note = mongoose.model( 'Note', NoteSchema );
308
309 // comments
310
311 var PostSchema = new Schema({
312   date      : { type : Date, default : Date.now },
313   body      : String,
314   votes     : [String],
315   reports   : [String],
316   public    : Boolean,
317
318   userid    : String, // ObjectId,
319   userName  : String,
320   userAffil : String,
321
322   comments   : Array,
323
324   lecture   : String, // ObjectId
325   deleted   : Boolean
326 })
327
328 PostSchema.method( 'delete', function( callback ) {
329   var id = this._id;
330
331   Post.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
332     if (callback) callback( err );
333   })
334 });
335
336 mongoose.model( 'Post', PostSchema );
337
338 var ArchivedCourse = new Schema({
339   id: Number,
340   instructor: String,
341   section: String,
342   name: String,
343   description: String,
344   subject_id: Number
345 })
346
347 mongoose.model( 'ArchivedCourse', ArchivedCourse )
348
349 var ArchivedNote = new Schema({
350   course_id: Number,
351   topic: String,
352   text: String
353 })
354
355 mongoose.model( 'ArchivedNote', ArchivedNote )
356
357 var ArchivedSubject = new Schema({
358   id: Number,
359   name: String
360 })
361
362 mongoose.model( 'ArchivedSubject', ArchivedSubject )
363
364 module.exports.mongoose = mongoose;