models.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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.method( 'authorize', function( user, cb ) {
  103. return cb(user.admin || ( this.users.indexOf( user._id ) !== -1 ));
  104. });
  105. var School = mongoose.model( 'School', SchoolSchema );
  106. // courses
  107. var CourseSchema = new Schema( {
  108. name : { type : String, required : true },
  109. number : String,
  110. description : String,
  111. instructor : ObjectId,
  112. subject : String,
  113. department : String,
  114. // courses are tied to one school
  115. school : ObjectId,
  116. // XXX: room for additional resources
  117. created : { type : Date, default : Date.now },
  118. creator : ObjectId,
  119. deleted : Boolean,
  120. // many users may subscribe to a course
  121. users : Array
  122. });
  123. CourseSchema.virtual( 'displayName' )
  124. .get( function() {
  125. if( this.number ) {
  126. return this.number + ': ' + this.name;
  127. } else {
  128. return this.name;
  129. }
  130. });
  131. CourseSchema.method( 'authorize', function( user, cb ) {
  132. School.findById( this.school, function( err, school ) {
  133. if ( school ) {
  134. school.authorize( user, function( result ) {
  135. return cb( result );
  136. })
  137. }
  138. });
  139. });
  140. CourseSchema.method( 'subscribed', function( user ) {
  141. return ( this.users.indexOf( user ) > -1 ) ;
  142. });
  143. CourseSchema.method( 'subscribe', function( user, callback ) {
  144. var id = this._id;
  145. // mongoose issue #404
  146. Course.collection.update( { '_id' : id }, { '$addToSet' : { 'users' : user } }, function( err ) {
  147. callback( err );
  148. });
  149. });
  150. CourseSchema.method( 'unsubscribe', function( user, callback ) {
  151. var id = this._id;
  152. // mongoose issue #404
  153. Course.collection.update( { '_id' : id }, { '$pull' : { 'users' : user } }, function( err ) {
  154. callback( err );
  155. });
  156. });
  157. CourseSchema.method( 'delete', function( callback ) {
  158. var id = this._id;
  159. Course.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
  160. if (callback) callback( err );
  161. Lecture.find( { course: id }, function( err, lectures) {
  162. if (lectures.length > 0) {
  163. lectures.forEach(function(lecture) {
  164. lecture.delete();
  165. })
  166. }
  167. })
  168. })
  169. });
  170. var Course = mongoose.model( 'Course', CourseSchema );
  171. // lectures
  172. var LectureSchema = new Schema( {
  173. name : { type : String, required : true },
  174. date : { type : Date, default: Date.now },
  175. live : Boolean,
  176. creator : ObjectId,
  177. deleted : Boolean,
  178. course : ObjectId
  179. });
  180. LectureSchema.method( 'authorize', function( user, cb ) {
  181. Course.findById( this.course, function( err, course ) {
  182. if (course) {
  183. course.authorize( user, function( res ) {
  184. return cb( res );
  185. })
  186. } else {
  187. return cb( false );
  188. }
  189. });
  190. });
  191. LectureSchema.method( 'delete', function( callback ) {
  192. var id = this._id;
  193. Lecture.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
  194. if (callback) callback( err );
  195. Note.find( { lecture : id }, function(err, notes) {
  196. notes.forEach(function(note) {
  197. note.delete();
  198. })
  199. })
  200. Post.find( { lecture : id }, function(err, posts) {
  201. posts.forEach(function(post) {
  202. post.delete();
  203. })
  204. })
  205. })
  206. });
  207. var Lecture = mongoose.model( 'Lecture', LectureSchema );
  208. // notes
  209. var NoteSchema = new Schema( {
  210. name : { type : String, required : true },
  211. path : String,
  212. public : Boolean,
  213. roID : String,
  214. visits : Number,
  215. created : { type : Date, default : Date.now },
  216. creator : ObjectId,
  217. deleted : Boolean,
  218. lecture : ObjectId,
  219. collaborators : [String]
  220. });
  221. NoteSchema.method( 'authorize', function( user, cb ) {
  222. Lecture.findById( this.lecture, function( err, lecture ) {
  223. if (lecture) {
  224. lecture.authorize( user, function( res ) {
  225. return cb( res );
  226. })
  227. } else {
  228. return cb( false );
  229. }
  230. });
  231. });
  232. NoteSchema.method( 'addVisit', function() {
  233. var id = this._id;
  234. Note.collection.update( { '_id' : id }, { '$inc' : { 'visits' : 1 } } );
  235. });
  236. NoteSchema.method( 'delete', function( callback ) {
  237. var id = this._id;
  238. Note.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
  239. if (callback) callback( err );
  240. })
  241. });
  242. var Note = mongoose.model( 'Note', NoteSchema );
  243. // comments
  244. var PostSchema = new Schema({
  245. date : { type : Date, default : Date.now },
  246. body : String,
  247. votes : [String],
  248. reports : [String],
  249. public : Boolean,
  250. userid : String, // ObjectId,
  251. userName : String,
  252. userAffil : String,
  253. comments : Array,
  254. lecture : String, // ObjectId
  255. deleted : Boolean
  256. })
  257. PostSchema.method( 'delete', function( callback ) {
  258. var id = this._id;
  259. Post.collection.update( { '_id' : id }, { '$set' : { 'deleted' : true } }, function( err ) {
  260. if (callback) callback( err );
  261. })
  262. });
  263. mongoose.model( 'Post', PostSchema );
  264. var ArchivedCourse = new Schema({
  265. id: Number,
  266. instructor: String,
  267. section: String,
  268. name: String,
  269. description: String,
  270. subject_id: Number
  271. })
  272. mongoose.model( 'ArchivedCourse', ArchivedCourse )
  273. var ArchivedNote = new Schema({
  274. course_id: Number,
  275. topic: String,
  276. text: String
  277. })
  278. mongoose.model( 'ArchivedNote', ArchivedNote )
  279. var ArchivedSubject = new Schema({
  280. id: Number,
  281. name: String
  282. })
  283. mongoose.model( 'ArchivedSubject', ArchivedSubject )
  284. module.exports.mongoose = mongoose;