models.js 9.6 KB

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