models.js 8.5 KB

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