import { Get , Post , Put , Delete , Param , Body , CurrentUser , Authorized , JsonController , UploadedFile , UseBefore , BadRequestError , ForbiddenError , NotFoundError } from 'routing-controllers' ; import passportJwtMiddleware from '../security/passportJwtMiddleware' ; import { errorCodes } from '../config/errorCodes' ; import config from '../config/main' ; import { ICourse } from '../../../shared/models/ICourse' ; import { ICourseDashboard } from '../../../shared/models/ICourseDashboard' ; import { ICourseView } from '../../../shared/models/ICourseView' ; import { IUser } from '../../../shared/models/IUser' ; import { Course , ICourseModel } from '../models/Course' ; import { WhitelistUser } from '../models/WhitelistUser' ; import emailService from '../services/EmailService' ; const multer = require ( 'multer' ) ; import { API_NOTIFICATION_TYPE_ALL_CHANGES , NotificationSettings } from '../models/NotificationSettings' ; import { IWhitelistUser } from '../../../shared/models/IWhitelistUser' ; import { DocumentToObjectOptions } from 'mongoose' ; import * as fs from 'fs' ; import * as path from 'path' ; import ResponsiveImageService from '../services/ResponsiveImageService' ; import { IResponsiveImageData } from '../../../shared/models/IResponsiveImageData' ; import { Picture } from '../models/mediaManager/File' ; const coursePictureUploadOptions = { storage : multer . diskStorage ( { destination : ( req , file , cb ) => { cb ( null , path . join ( config . uploadFolder , 'courses' ) ) ; } , filename : ( req , file , cb ) => { const id = req . params . id ; const extPos = file . originalname . lastIndexOf ( '.' ) ; const ext = ( extPos !== - 1 ) ? `.${ file . originalname . substr ( extPos + 1 ) . toLowerCase ( ) }` : '' ; cb ( null , id + '_' + new Date ( ) . getTime ( ) . toString ( ) + ext ) ; } } ) , } ; @ JsonController ( '/courses' ) @ UseBefore ( passportJwtMiddleware ) export class CourseController { api apiName apiGroup apiParam apiSuccess apiSuccessExample @ Get ( '/' ) async getCourses ( @ CurrentUser ( ) currentUser ) { const whitelistUsers = await WhitelistUser . find ( { uid : currentUser . uid } ) ; const conditions = this . userReadConditions ( currentUser ) ; if ( conditions . $or ) { conditions . $or . push ( { enrollType : 'free' } ) ; conditions . $or . push ( { enrollType : 'accesskey' } ) ; conditions . $or . push ( { enrollType : 'whitelist' , whitelist : { $elemMatch : { $in : whitelistUsers } } } ) ; } const courses = await Course . find ( conditions ) ; return await Promise . all ( courses . map ( async ( course ) => { return course . forDashboard ( currentUser ) ; } ) ) ; } api apiName apiGroup apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Get ( '/:id([a-fA-F0-9]{24})' ) async getCourseView ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const course = await this . getCourse ( id , currentUser ) ; if ( ! course . checkPrivileges ( currentUser ) . userCanViewCourse ) { throw new ForbiddenError ( ) ; } await course . populateLecturesFor ( currentUser ) . populate ( 'courseAdmin' ) . populate ( 'teachers' ) . execPopulate ( ) ; await course . processLecturesFor ( currentUser ) ; return course . forView ( currentUser ) ; } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Get ( '/:id([a-fA-F0-9]{24})/edit' ) async getCourseEdit ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const course = await this . getCourse ( id , currentUser ) ; if ( ! course . checkPrivileges ( currentUser ) . userCanEditCourse ) { throw new ForbiddenError ( ) ; } await course . populateLecturesFor ( currentUser ) . populate ( 'media' ) . populate ( 'courseAdmin' ) . populate ( 'teachers' ) . populate ( 'students' ) . populate ( 'whitelist' ) . populate ( 'image' ) . execPopulate ( ) ; await course . processLecturesFor ( currentUser ) ; return course . toObject ( < DocumentToObjectOptions > { currentUser } ) ; } private userReadConditions ( currentUser ) { const conditions = { } ; if ( currentUser . role === 'admin' ) { return conditions ; } conditions . $or = [ ] ; if ( currentUser . role === 'student' ) { conditions . active = true ; conditions . $or . push ( { students : currentUser . _id } ) ; } else { conditions . $or . push ( { teachers : currentUser . _id } ) ; conditions . $or . push ( { courseAdmin : currentUser . _id } ) ; } return conditions ; } private async getCourse ( id , currentUser ) { const course = await Course . findOne ( { ... this . userReadConditions ( currentUser ) , _id : id } ) ; if ( ! course ) { throw new NotFoundError ( ) ; } return course ; } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiSuccess apiSuccessExample apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Post ( '/' ) async addCourse ( @ Body ( ) course , @ CurrentUser ( ) currentUser ) { const existingCourse = await Course . findOne ( { name : course . name } ) ; if ( existingCourse ) { throw new BadRequestError ( errorCodes . course . duplicateName . code ) ; } course . courseAdmin = currentUser ; const newCourse = new Course ( course ) ; await newCourse . save ( ) ; return newCourse . toObject ( ) ; } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiSuccess apiSuccessExample @ Authorized ( [ 'teacher' , 'admin' ] ) @ Post ( '/mail' ) sendMailToSelectedUsers ( @ Body ( ) mailData , @ CurrentUser ( ) currentUser ) { return emailService . sendFreeFormMail ( { ... mailData , replyTo : `${ currentUser . profile . firstName } ${ currentUser . profile . lastName }<${ currentUser . email }>` , } ) ; } api apiName apiGroup apiPermission apiParam apiParam apiParam apiSuccess apiSuccessExample apiError apiError apiError @ Authorized ( [ 'student' ] ) @ Post ( '/:id/enroll' ) async enrollStudent ( @ Param ( 'id' ) id , @ Body ( ) data , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) ; if ( ! course ) { throw new NotFoundError ( ) ; } if ( course . enrollType === 'whitelist' ) { const wUsers = await WhitelistUser . find ( ) . where ( { courseId : course . _id } ) ; if ( wUsers . filter ( e => e . uid === currentUser . uid ) . length < 1 ) { throw new ForbiddenError ( errorCodes . course . notOnWhitelist . code ) ; } } else if ( course . accessKey && course . accessKey !== data . accessKey ) { throw new ForbiddenError ( errorCodes . course . accessKey . code ) ; } if ( course . students . indexOf ( currentUser . _id ) < 0 ) { course . students . push ( currentUser ) ; await new NotificationSettings ( { 'user' : currentUser , 'course' : course , 'notificationType' : API_NOTIFICATION_TYPE_ALL_CHANGES , 'emailNotification' : false } ) . save ( ) ; await course . save ( ) ; } return { } ; } api apiName apiGroup apiPermission apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'student' ] ) @ Post ( '/:id/leave' ) async leaveStudent ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) ; if ( ! course ) { throw new NotFoundError ( ) ; } const index = course . students . indexOf ( currentUser . _id ) ; if ( index >= 0 ) { course . students . splice ( index , 1 ) ; await NotificationSettings . findOne ( { 'user' : currentUser , 'course' : course } ) . remove ( ) ; await course . save ( ) ; return { } ; } else { throw new ForbiddenError ( ) ; } } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Post ( '/:id/whitelist' ) async whitelistStudents ( @ Param ( 'id' ) id , @ Body ( ) whitelist , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) ; if ( ! course . checkPrivileges ( currentUser ) . userCanEditCourse ) { throw new ForbiddenError ( ) ; } if ( ! whitelist || whitelist . length === 0 ) { throw new BadRequestError ( ) ; } if ( course . whitelist . length > 0 ) { for ( const wuser of course . whitelist ) { const whitelistUser = await WhitelistUser . findById ( wuser ) ; if ( whitelistUser ) { await whitelistUser . remove ( ) ; } } } course . whitelist = [ ] ; for ( const whiteListUser of whitelist ) { const wUser = new WhitelistUser ( ) ; wUser . firstName = whiteListUser . firstName ; wUser . lastName = whiteListUser . lastName ; wUser . uid = whiteListUser . uid ; wUser . courseId = course . _id ; await wUser . save ( ) ; course . whitelist . push ( wUser . _id ) ; } await course . save ( ) ; return whitelist ; } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiParam apiSuccess apiSuccessExample apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Put ( '/:id' ) async updateCourse ( @ Param ( 'id' ) id , @ Body ( ) course , @ CurrentUser ( ) currentUser ) { const conditions = { _id : id } ; if ( currentUser . role !== 'admin' ) { conditions . $or = [ { teachers : currentUser . _id } , { courseAdmin : currentUser . _id } ] ; } const updatedCourse = await Course . findOneAndUpdate ( conditions , course , { 'new' : true } ) ; if ( updatedCourse ) { return { _id : updatedCourse . id , name : updatedCourse . name } ; } else { throw new NotFoundError ( ) ; } } api apiName apiGroup apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Delete ( '/:id' ) async deleteCourse ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) ; if ( ! course ) { throw new NotFoundError ( ) ; } if ( ! course . checkPrivileges ( currentUser ) . userCanEditCourse ) { throw new ForbiddenError ( ) ; } await course . remove ( ) ; return { } ; } api apiName apiGroup apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Delete ( '/picture/:id' ) async deleteCoursePicture ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) . orFail ( new NotFoundError ( ) ) ; if ( ! course . checkPrivileges ( currentUser ) . userCanEditCourse ) { throw new ForbiddenError ( ) ; } if ( ! course . image ) { throw new NotFoundError ( ) ; } const picture = await Picture . findById ( course . image ) ; if ( picture ) { picture . remove ( ) ; } await Course . updateOne ( { _id : id } , { $unset : { image : 1 } } ) ; return { } ; } api apiName apiGroup apiParam apiParam apiParam apiSuccess apiSuccessExample apiError apiError @ Authorized ( [ 'teacher' , 'admin' ] ) @ Post ( '/picture/:id' ) async addCoursePicture ( @ UploadedFile ( 'file' , { options : coursePictureUploadOptions } ) file , @ Param ( 'id' ) id , @ Body ( ) responsiveImageDataRaw , @ CurrentUser ( ) currentUser ) { const course = await Course . findById ( id ) . orFail ( new NotFoundError ( ) ) ; if ( ! course . checkPrivileges ( currentUser ) . userCanEditCourse ) { throw new ForbiddenError ( ) ; } const mimeFamily = file . mimetype . split ( '/' , 1 ) [ 0 ] ; if ( mimeFamily !== 'image' ) { await fs . unlinkSync ( file . path ) ; throw new BadRequestError ( 'Forbidden format of uploaded picture: ' + mimeFamily ) ; } const picture = await Picture . findById ( course . image ) ; if ( picture ) { picture . remove ( ) ; } const responsiveImageData = < IResponsiveImageData > JSON . parse ( responsiveImageDataRaw . imageData ) ; await ResponsiveImageService . generateResponsiveImages ( file , responsiveImageData ) ; const image = new Picture ( { name : file . filename , physicalPath : responsiveImageData . pathToImage , link : responsiveImageData . pathToImage , size : 0 , mimeType : file . mimetype , breakpoints : responsiveImageData . breakpoints } ) ; await image . save ( ) ; course . image = image ; await course . save ( ) ; return image . toObject ( ) ; } }