From bcd223f7764ca492948462df4660b21db17c2161 Mon Sep 17 00:00:00 2001 From: Alexander Eimer Date: Thu, 3 May 2018 12:59:16 +0200 Subject: [PATCH 01/50] Adds comment to changelog and corrects link in changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fda0a15c..30750d9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + + ## [NEXT] ### Added - Added the possibility to sort all courses alphabetically. [#567](https://github.com/h-da/geli/issues/567) @@ -11,7 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added an account activation resend feature. [#601](https://github.com/h-da/geli/issues/601) - Added `SnackBarService` as wrapper for `MatSnackBar`. [#574](https://github.com/h-da/geli/issues/574) - Added new course & user API unit tests. [#654](https://github.com/h-da/geli/issues/654) [#691](https://github.com/h-da/geli/issues/691) -- Added details of courseAdmin and teacher to course detail view. on click profiles are shown.[#598] (https://github.com/h-da/geli/issues/598) +- Added details of courseAdmin and teacher to course detail view. on click profiles are shown. [#598](https://github.com/h-da/geli/issues/598) ### Changed - Refactored or slightly altered various course & user related APIs. [#654](https://github.com/h-da/geli/issues/654) [#691](https://github.com/h-da/geli/issues/691) From 87d8435c7661d937fd6ec83b2a99160c345300d3 Mon Sep 17 00:00:00 2001 From: Alexander Eimer Date: Thu, 3 May 2018 13:43:49 +0200 Subject: [PATCH 02/50] Links the versions in changelog --- CHANGELOG.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30750d9cb..a65db815a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,12 +40,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed multiple severe course related security issues. [#594](https://github.com/h-da/geli/issues/594) [#653](https://github.com/h-da/geli/issues/653) [#691](https://github.com/h-da/geli/issues/691) - Updated the dependencies for security. [#661](https://github.com/h-da/geli/issues/661) -## [0.6.0] - 2018-03-31 - Introduces MediaManager and some minor changes +## [[0.6.0](https://github.com/h-da/geli/releases/tag/v0.6.0)] - 2018-03-31 - Introduces MediaManager and some minor changes ### Added - MediaManager for file management in courses -## [0.5.0] - 2018-03-24 - WS 17/18 intermediate Release +## [[0.5.0](https://github.com/h-da/geli/releases/tag/v0.5.0)] - 2018-03-24 - WS 17/18 intermediate Release ### Added - selective download of the course - progress dashboard for teacher @@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - adds imprint -## [0.4.0] - 2017-12-04 - WS 17/18 Second feature release +## [[0.4.0](https://github.com/h-da/geli/releases/tag/v0.4.0)] - 2017-12-04 - WS 17/18 Second feature release ### Added - Responsivness improved - leave course function @@ -73,31 +73,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - randomize tasks after validation -## [0.3.1] - 2017-11-05 - Dynamic db name update +## [[0.3.1](https://github.com/h-da/geli/releases/tag/v0.3.1)] - 2017-11-05 - Dynamic db name update ### Added - The possibility to use a other database name then 'test' -## [0.3.0] - 2017-11-02 +## [[0.3.0](https://github.com/h-da/geli/releases/tag/v0.3.0)] - 2017-11-02 ### Added - a lot of major bugfixes and optimizations -## [0.2.2] - 2017-10-19 - Security improvements +## [[0.2.2](https://github.com/h-da/geli/releases/tag/v0.2.2)] - 2017-10-19 - Security improvements ### Added - security for free courses -## [0.2.1] - 2017-10-03 - First Live-Ready release +## [[0.2.1](https://github.com/h-da/geli/releases/tag/v0.2.1)] - 2017-10-03 - First Live-Ready release ### Added - first live functionality -## [0.2.0] - 2017-06-29 - Almost production ready +## [[0.2.0](https://github.com/h-da/geli/releases/tag/v0.2.0)] - 2017-06-29 - Almost production ready ### Added - Many new feature for production -## [0.1.0] - 2017-05-11 - Basics implemented +## [[0.1.0](https://github.com/h-da/geli/releases/tag/v0.1.0)] - 2017-05-11 - Basics implemented ### Added - Many basic implementations of ground functionality From 0a573361c9a71626c2ec950cc56099468fc67522 Mon Sep 17 00:00:00 2001 From: Alexander Eimer Date: Thu, 3 May 2018 13:48:27 +0200 Subject: [PATCH 03/50] Makes the comments in Issue and PullRequest Tempaltes invisible --- .github/ISSUE_TEMPLATE.md | 6 ++++-- .github/PULL_REQUEST_TEMPLATE.md | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index b19026114..8ae8ca007 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -14,5 +14,7 @@ ## Additional info: ------- -_Please tag this issue if you are sure to which tag(s) it belongs._ + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f63a83f51..67c90965a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,6 +9,8 @@ Closes # ## Known Issues: _NONE_ ------- -_Remember to prefix your PR-Title with `⚠ WIP: ` if you still work on it. -If you have reached a final state, remove the prefix._ + From 64464902f33d50a426812fb9b3ea2eb8c405771d Mon Sep 17 00:00:00 2001 From: Alexander Eimer Date: Thu, 3 May 2018 13:51:28 +0200 Subject: [PATCH 04/50] Adds Changelog entry for #707 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a65db815a..62980f8a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed notification icon spacing in the navbar for students. [#696](https://github.com/h-da/geli/issues/696) - Repair Angular CLI code generation. [#701](https://github.com/h-da/geli/pull/701) - Fixed `tsconfig.spec.ts` for `ng test`. [#656](https://github.com/h-da/geli/pull/656) +- Fixed and prettified the `CHANGELOG.md` [#707](https://github.com/h-da/geli/pull/707) ### Security - Fixed numerous severe user related security issues. [#691](https://github.com/h-da/geli/issues/691) From 3d9dfc2aa3cb13fd853e72ec19f72b8f89089728 Mon Sep 17 00:00:00 2001 From: Patrick Skowronek Date: Fri, 4 May 2018 17:17:01 +0200 Subject: [PATCH 05/50] Cahnged pre hooks to async --- api/src/models/Course.ts | 36 +++++++------ api/src/models/Lecture.ts | 20 +++---- api/src/models/mediaManager/Directory.ts | 66 +++++++++++++----------- api/src/models/mediaManager/File.ts | 29 ++++++----- api/src/models/units/Unit.ts | 26 ++++++---- 5 files changed, 96 insertions(+), 81 deletions(-) diff --git a/api/src/models/Course.ts b/api/src/models/Course.ts index ab980757b..de72a8b99 100644 --- a/api/src/models/Course.ts +++ b/api/src/models/Course.ts @@ -21,8 +21,10 @@ interface ICourseModel extends ICourse, mongoose.Document { populateLecturesFor: (user: IUser) => this; processLecturesFor: (user: IUser) => Promise; } + interface ICourseMongoose extends mongoose.Model { } + let Course: ICourseMongoose; const courseSchema = new mongoose.Schema({ @@ -42,10 +44,10 @@ const courseSchema = new mongoose.Schema({ ref: 'User' }, media: - { - type: mongoose.Schema.Types.ObjectId, - ref: 'Directory' - }, + { + type: mongoose.Schema.Types.ObjectId, + ref: 'Directory' + }, teachers: [ { type: mongoose.Schema.Types.ObjectId, @@ -82,7 +84,7 @@ const courseSchema = new mongoose.Schema({ { timestamps: true, toObject: { - transform: function (doc: ICourseModel, ret: any, {currentUser}: {currentUser?: IUser}) { + transform: function (doc: ICourseModel, ret: any, {currentUser}: { currentUser?: IUser }) { if (ret.hasOwnProperty('_id') && ret._id !== null) { ret._id = ret._id.toString(); } @@ -109,17 +111,15 @@ const courseSchema = new mongoose.Schema({ ); // Cascade delete -courseSchema.pre('remove', async function (next) { +courseSchema.pre('remove', async function () { const localCourse = this; try { - const deletedLectures = await Lecture.deleteMany({'_id': {$in: localCourse.lectures}}).exec(); - const deletedDirs = await Directory.deleteMany({'_id': {$in: localCourse.media}}).exec(); + await Lecture.deleteMany({'_id': {$in: localCourse.lectures}}).exec(); + await Directory.deleteMany({'_id': {$in: localCourse.media}}).exec(); } catch (error) { - const debug = 0; - next(); + winston.log('warn', 'course (' + localCourse._id + ') cloud not be deleted!'); + throw new Error('Delete Error: ' + error.toString()); } - - next(); }); courseSchema.methods.exportJSON = async function (sanitize: boolean = true) { @@ -214,10 +214,12 @@ courseSchema.methods.checkPrivileges = function (user: IUser) { const userCanEditCourse: boolean = userIsAdmin || userIsCourseAdmin || userIsCourseTeacher; const userCanViewCourse: boolean = (this.active && userIsCourseStudent) || userCanEditCourse; - return {userIsAdmin, ...userIs, - courseAdminId, - userIsCourseAdmin, userIsCourseTeacher, userIsCourseStudent, userIsCourseMember, - userCanEditCourse, userCanViewCourse}; + return { + userIsAdmin, ...userIs, + courseAdminId, + userIsCourseAdmin, userIsCourseTeacher, userIsCourseStudent, userIsCourseMember, + userCanEditCourse, userCanViewCourse + }; }; courseSchema.methods.forDashboard = function (user: IUser): ICourseDashboard { @@ -247,7 +249,7 @@ courseSchema.methods.forView = function (): ICourseView { _id: extractMongoId(this._id), name, description, courseAdmin: User.forCourseView(courseAdmin), - teachers: teachers.map((teacher: IUser) => User.forCourseView(teacher)), + teachers: teachers.map((teacher: IUser) => User.forCourseView(teacher)), lectures: lectures.map((lecture: any) => lecture.toObject()) }; }; diff --git a/api/src/models/Lecture.ts b/api/src/models/Lecture.ts index 185c9335c..c8aa05ae5 100644 --- a/api/src/models/Lecture.ts +++ b/api/src/models/Lecture.ts @@ -38,17 +38,19 @@ const lectureSchema = new mongoose.Schema({ ); // Cascade delete -lectureSchema.pre('remove', function(next: () => void) { +lectureSchema.pre('remove', async function () { // We cannot do this, because we need actual Unit instances so that their pre remove middleware gets called // Unit.remove({'_id': {$in: this.units}}).exec().then(next).catch(next); const localLecture = this; - Unit.find({'_id': {$in: localLecture.units}}).exec() - .then((units) => Promise.all(units.map(unit => unit.remove()))) - .then(next) - .catch(next); + try { + await Lecture.deleteMany({'_id': {$in: localLecture.units}}).exec(); + } catch (err) { + throw new Error('Delete Error: ' + err.toString()); + } + }); -lectureSchema.methods.exportJSON = async function() { +lectureSchema.methods.exportJSON = async function () { const obj = this.toObject(); // remove unwanted informations @@ -59,7 +61,7 @@ lectureSchema.methods.exportJSON = async function() { delete obj.updatedAt; // "populate" lectures - const units: Array = obj.units; + const units: Array = obj.units; obj.units = []; for (const unitId of units) { @@ -83,9 +85,9 @@ lectureSchema.methods.processUnitsFor = async function (user: IUser) { return this; }; -lectureSchema.statics.importJSON = async function(lecture: ILecture, courseId: string) { +lectureSchema.statics.importJSON = async function (lecture: ILecture, courseId: string) { // importTest lectures - const units: Array = lecture.units; + const units: Array = lecture.units; lecture.units = []; try { diff --git a/api/src/models/mediaManager/Directory.ts b/api/src/models/mediaManager/Directory.ts index 67ac3927e..91f7b70a0 100644 --- a/api/src/models/mediaManager/Directory.ts +++ b/api/src/models/mediaManager/Directory.ts @@ -24,43 +24,47 @@ const directorySchema = new mongoose.Schema({ } ] }, { - timestamps: true, - toObject: { - transform: function (doc: IDirectoryModel, ret: any) { - ret._id = ret._id.toString(); - ret.subDirectories = ret.subDirectories.map((dir: any) => { - if (!dir._id) { - dir = dir.toString(); - } - return dir; - }); - ret.files = ret.files.map((file: any) => { - if (!file._id) { - file = file.toString(); - } - return file; - }); - } - }, + timestamps: true, + toObject: { + transform: function (doc: IDirectoryModel, ret: any) { + ret._id = ret._id.toString(); + ret.subDirectories = ret.subDirectories.map((dir: any) => { + if (!dir._id) { + dir = dir.toString(); + } + return dir; + }); + ret.files = ret.files.map((file: any) => { + if (!file._id) { + file = file.toString(); + } + return file; + }); + } + }, }); -directorySchema.pre('remove', async function(next: () => void) { +directorySchema.pre('remove', async function () { const localDir = this; - for (const subdir of localDir.subDirectories) { - // linting won't let us use 'Directory' before it is actually declared - // tslint:disable-next-line:no-use-before-declare - const model = await Directory.findById(subdir); - if (model) { - await model.remove(); + try { + for (const subdir of localDir.subDirectories) { + // linting won't let us use 'Directory' before it is actually declared + // tslint:disable-next-line:no-use-before-declare + const model = await Directory.findById(subdir); + if (model) { + await model.remove(); + } } - } - for (const file of localDir.files) { - const model = await File.findById(file); - if (model) { - await model.remove(); + for (const file of localDir.files) { + const model = await File.findById(file); + if (model) { + await model.remove(); + } } } - next(); + catch (err) { + throw new Error('Delete Error: ' + err.toString()); + } }); const Directory = mongoose.model('Directory', directorySchema); diff --git a/api/src/models/mediaManager/File.ts b/api/src/models/mediaManager/File.ts index 0e78d4aa9..814c7f6a6 100644 --- a/api/src/models/mediaManager/File.ts +++ b/api/src/models/mediaManager/File.ts @@ -40,22 +40,25 @@ const fileSchema = new mongoose.Schema({ }, }); -fileSchema.pre('remove', async function(next: () => void) { +fileSchema.pre('remove', async function() { const localFile = this; - if (fs.existsSync(localFile.physicalPath)) { - await promisify(fs.unlink)(localFile.physicalPath); - } - - const units2Check: IFileUnitModel[] = await FileUnit.find({files: { $in: [ localFile._id ] }}); - Promise.all(units2Check.map(async unit => { - const index = unit.files.indexOf(localFile._id); - if (index > -1) { - unit.files.splice(index, 1); - await unit.save(); + try { + if (fs.existsSync(localFile.physicalPath)) { + await promisify(fs.unlink)(localFile.physicalPath); } - })); - next(); + const units2Check: IFileUnitModel[] = await FileUnit.find({files: {$in: [localFile._id]}}); + Promise.all(units2Check.map(async unit => { + const index = unit.files.indexOf(localFile._id); + if (index > -1) { + unit.files.splice(index, 1); + await unit.save(); + } + })); + } catch(err) { + throw new Error('Delete Error: ' + err.toString()); + } + }); const File = mongoose.model('File', fileSchema); diff --git a/api/src/models/units/Unit.ts b/api/src/models/units/Unit.ts index 774e2e0bb..acc0e6592 100644 --- a/api/src/models/units/Unit.ts +++ b/api/src/models/units/Unit.ts @@ -42,9 +42,9 @@ const unitSchema = new mongoose.Schema({ type: String }, unitCreator: { - type: mongoose.Schema.Types.ObjectId, - ref: 'User' - } + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + } }, { collection: 'units', @@ -66,7 +66,7 @@ unitSchema.virtual('progressData', { justOne: true }); -unitSchema.methods.exportJSON = function() { +unitSchema.methods.exportJSON = function () { const obj = this.toObject(); // remove unwanted informations @@ -82,29 +82,29 @@ unitSchema.methods.exportJSON = function() { return obj; }; -unitSchema.methods.calculateProgress = async function(): Promise { +unitSchema.methods.calculateProgress = async function (): Promise { return this.toObject(); }; -unitSchema.methods.populateUnit = async function(): Promise { +unitSchema.methods.populateUnit = async function (): Promise { if (this.unitCreator) { this.unitCreator = await User.findById(this.unitCreator); } return this; }; -unitSchema.methods.secureData = async function(user: IUser): Promise { +unitSchema.methods.secureData = async function (user: IUser): Promise { if (this.unitCreator) { this.unitCreator = User.forSafe(this.unitCreator); } return this; }; -unitSchema.methods.toFile = function(): String { +unitSchema.methods.toFile = function (): String { return ''; }; -unitSchema.statics.importJSON = async function(unit: IUnit, courseId: string, lectureId: string) { +unitSchema.statics.importJSON = async function (unit: IUnit, courseId: string, lectureId: string) { unit._course = courseId; try { @@ -125,8 +125,12 @@ unitSchema.statics.importJSON = async function(unit: IUnit, courseId: string, le }; // Cascade delete -unitSchema.pre('remove', function(next: () => void) { - Progress.remove({'unit': this._id}).exec().then(next).catch(next); +unitSchema.pre('remove', async function () { + try { + await Progress.remove({'unit': this._id}).exec(); + } catch (err) { + throw new Error('Delete Error: ' + err.toString()); + } }); const Unit = mongoose.model('Unit', unitSchema); From b535558f17bb2964e9fac811eda37ec1981c41cc Mon Sep 17 00:00:00 2001 From: Patrick Skowronek Date: Fri, 4 May 2018 22:29:58 +0200 Subject: [PATCH 06/50] Somesmall changes --- api/src/models/Course.ts | 2 +- api/src/models/Lecture.ts | 4 +--- api/src/models/mediaManager/Directory.ts | 11 +++-------- api/src/models/mediaManager/File.ts | 3 +-- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/api/src/models/Course.ts b/api/src/models/Course.ts index de72a8b99..d731c0170 100644 --- a/api/src/models/Course.ts +++ b/api/src/models/Course.ts @@ -115,7 +115,7 @@ courseSchema.pre('remove', async function () { const localCourse = this; try { await Lecture.deleteMany({'_id': {$in: localCourse.lectures}}).exec(); - await Directory.deleteMany({'_id': {$in: localCourse.media}}).exec(); + await Directory.deleteOne({_id: localCourse.media}).exec(); } catch (error) { winston.log('warn', 'course (' + localCourse._id + ') cloud not be deleted!'); throw new Error('Delete Error: ' + error.toString()); diff --git a/api/src/models/Lecture.ts b/api/src/models/Lecture.ts index c8aa05ae5..97106f156 100644 --- a/api/src/models/Lecture.ts +++ b/api/src/models/Lecture.ts @@ -39,11 +39,9 @@ const lectureSchema = new mongoose.Schema({ // Cascade delete lectureSchema.pre('remove', async function () { - // We cannot do this, because we need actual Unit instances so that their pre remove middleware gets called - // Unit.remove({'_id': {$in: this.units}}).exec().then(next).catch(next); const localLecture = this; try { - await Lecture.deleteMany({'_id': {$in: localLecture.units}}).exec(); + await Unit.deleteMany({'_id': {$in: localLecture.units}}).exec(); } catch (err) { throw new Error('Delete Error: ' + err.toString()); } diff --git a/api/src/models/mediaManager/Directory.ts b/api/src/models/mediaManager/Directory.ts index 91f7b70a0..c4e6b1153 100644 --- a/api/src/models/mediaManager/Directory.ts +++ b/api/src/models/mediaManager/Directory.ts @@ -1,6 +1,7 @@ import {IDirectory} from '../../../../shared/models/mediaManager/IDirectory'; import {File} from './File'; import * as mongoose from 'mongoose'; +import {Unit} from '../units/Unit'; interface IDirectoryModel extends IDirectory, mongoose.Document { @@ -55,14 +56,8 @@ directorySchema.pre('remove', async function () { await model.remove(); } } - for (const file of localDir.files) { - const model = await File.findById(file); - if (model) { - await model.remove(); - } - } - } - catch (err) { + await Unit.deleteMany({'_id': {$in: localDir.files}}).exec(); + } catch (err) { throw new Error('Delete Error: ' + err.toString()); } }); diff --git a/api/src/models/mediaManager/File.ts b/api/src/models/mediaManager/File.ts index 814c7f6a6..6c5f24f91 100644 --- a/api/src/models/mediaManager/File.ts +++ b/api/src/models/mediaManager/File.ts @@ -55,10 +55,9 @@ fileSchema.pre('remove', async function() { await unit.save(); } })); - } catch(err) { + } catch (err) { throw new Error('Delete Error: ' + err.toString()); } - }); const File = mongoose.model('File', fileSchema); From 77936b3f22e1d5990b93cd615f071d5448796dae Mon Sep 17 00:00:00 2001 From: Patrick Skowronek Date: Fri, 4 May 2018 23:17:34 +0200 Subject: [PATCH 07/50] Some more Refactoring --- api/src/controllers/UnitController.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/api/src/controllers/UnitController.ts b/api/src/controllers/UnitController.ts index aa2b87dc1..e4c1b8b42 100644 --- a/api/src/controllers/UnitController.ts +++ b/api/src/controllers/UnitController.ts @@ -81,23 +81,21 @@ export class UnitController { */ @Authorized(['teacher', 'admin']) @Post('/') - addUnit(@Body() data: any, @CurrentUser() currentUser: IUser) { + async addUnit(@Body() data: any, @CurrentUser() currentUser: IUser) { // discard invalid requests this.checkPostParam(data); // Set current user as creator, old unit's dont have a creator data.model.unitCreator = currentUser._id; - - return Unit.create(data.model) - .then((createdUnit) => { - return this.pushToLecture(data.lectureId, createdUnit); - }) - .catch((err) => { + try { + const createdUnit = await Unit.create(data.model); + return await this.pushToLecture(data.lectureId, createdUnit); + } catch (err) { if (err.name === 'ValidationError') { throw err; } else { throw new BadRequestError(err); } - }); + } } /** From f1ea95c54bf0f2e3753b714fc1c015e2904460eb Mon Sep 17 00:00:00 2001 From: fwitulski <32933845+fwitulski@users.noreply.github.com> Date: Sat, 5 May 2018 10:43:25 +0200 Subject: [PATCH 08/50] fixed FileUnit populate, so display works correct --- api/src/models/units/FileUnit.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/src/models/units/FileUnit.ts b/api/src/models/units/FileUnit.ts index b9aa29b21..ed8512361 100644 --- a/api/src/models/units/FileUnit.ts +++ b/api/src/models/units/FileUnit.ts @@ -1,6 +1,7 @@ import * as mongoose from 'mongoose'; import {IUnitModel} from './Unit'; import {IFileUnit} from '../../../../shared/models/units/IFileUnit'; +import {User} from "../User"; interface IFileUnitModel extends IFileUnit, IUnitModel { populateUnit: () => Promise; @@ -28,6 +29,9 @@ const fileUnitSchema = new mongoose.Schema({ }); fileUnitSchema.methods.populateUnit = async function() { + if (this.unitCreator) { + this.unitCreator = await User.findById(this.unitCreator); + } return this.populate('files').execPopulate(); }; From 2e42e4a8a848552158cdded3d5dd1645958634b9 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Sat, 5 May 2018 16:39:03 +0200 Subject: [PATCH 09/50] Fixed course pre hooks --- CHANGELOG.md | 1 + api/package-lock.json | 264 ++++++++++++++++++++--- api/package.json | 6 +- api/src/models/Course.ts | 8 +- api/src/models/mediaManager/Directory.ts | 8 +- api/src/models/mediaManager/File.ts | 3 +- api/src/models/units/FileUnit.ts | 2 +- 7 files changed, 249 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47575ef31..5f31031ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed `.travis.yml`. [#706](https://github.com/h-da/geli/pull/706) - Fixed wording of progress display on profile page. [#715](https://github.com/h-da/geli/issues/715) - Fixed form validator in create task [#579](https://github.com/h-da/geli/issues/579) +- Fixed Mongoose pre hook usage [#680](https://github.com/h-da/geli/issues/680) [#677](https://github.com/h-da/geli/issues/677) ### Added - Unit visibility toggle [#582](https://github.com/h-da/geli/issues/582) diff --git a/api/package-lock.json b/api/package-lock.json index d0d71986d..396334753 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -173,9 +173,9 @@ "dev": true }, "@types/mongodb": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.0.15.tgz", - "integrity": "sha512-sc96v9z3S3dS7acW6q6eKnAEvU2X3a42RIAslNAGpKpjy9Kw/M4dZfUtf83+mUNRM7OUejMIb/UP46OhIH+lYA==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.0.18.tgz", + "integrity": "sha512-0LflLyhsCEMOSiqcTb7QYl/VQv3m0acU0ouI7sPmyNmL4P1JNldiJZsNcmIliLyHFaKTQ3Rb+BJtUBLzlVhKzg==", "dev": true, "requires": { "@types/bson": "1.0.8", @@ -184,13 +184,13 @@ } }, "@types/mongoose": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.0.10.tgz", - "integrity": "sha512-KvtT1/EGwGflPmKBLbMBtAFeNkXEaQeqY+vZCI6gbu7q8aKzl0r4GAVv8PMolLRF68X2JY7M/43+qThnbGZiNA==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.0.12.tgz", + "integrity": "sha512-uRy65g/muXayu6sZm9irHQymwvBveMP5kIqYzqFLGGzPHh5m9YW/Yl7e/XKNgyWJ5+4XoE5eVsGEmovLFqoqSg==", "dev": true, "requires": { "@types/events": "1.2.0", - "@types/mongodb": "3.0.15", + "@types/mongodb": "3.0.18", "@types/node": "8.0.53" } }, @@ -206,8 +206,7 @@ "@types/node": { "version": "8.0.53", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.53.tgz", - "integrity": "sha512-54Dm6NwYeiSQmRB1BLXKr5GELi0wFapR1npi8bnZhEcu84d/yQKqnwwXQ56hZ0RUbTG6L5nqDZaN3dgByQXQRQ==", - "dev": true + "integrity": "sha512-54Dm6NwYeiSQmRB1BLXKr5GELi0wFapR1npi8bnZhEcu84d/yQKqnwwXQ56hZ0RUbTG6L5nqDZaN3dgByQXQRQ==" }, "@types/nodemailer": { "version": "4.6.0", @@ -280,6 +279,14 @@ "@types/mime": "2.0.0" } }, + "@types/sharp": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.17.8.tgz", + "integrity": "sha512-wQGqStKMaSInhLCCGyArCD967bNGZ96jtfTAjiMkDCX+7LF7mBIch5BwVVmPgSZGiyb7Wo7JdIvwXHvIDzWVvA==", + "requires": { + "@types/node": "8.0.53" + } + }, "@types/shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.7.8.tgz", @@ -1910,8 +1917,7 @@ "chownr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "optional": true + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, "class-transformer": { "version": "0.1.9", @@ -2133,11 +2139,19 @@ "object-visit": "1.0.1" } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "1.9.1", + "color-string": "1.5.2" + } + }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -2145,8 +2159,16 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.2.tgz", + "integrity": "sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k=", + "requires": { + "color-name": "1.1.3", + "simple-swizzle": "0.2.2" + } }, "color-support": { "version": "1.1.3", @@ -2272,14 +2294,14 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "coveralls": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.0.tgz", - "integrity": "sha512-ZppXR9y5PraUOrf/DzHJY6gzNUhXYE3b9D43xEXs4QYZ7/Oe0Gy0CS+IPKWFfvQFXB3RG9QduaQUFehzSpGAFw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.1.tgz", + "integrity": "sha512-FAzXwiDOYLGDWH+zgoIA+8GbWv50hlx+kpEJyvzLKOdnIBv9uWoVl4DhqGgyUHpiRjAlF8KYZSipWXYtllWH6Q==", "dev": true, "requires": { "js-yaml": "3.10.0", "lcov-parse": "0.0.10", - "log-driver": "1.2.5", + "log-driver": "1.2.7", "minimist": "1.2.0", "request": "2.83.0" }, @@ -2498,6 +2520,14 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "1.0.0" + } + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -2580,6 +2610,11 @@ "repeating": "2.0.1" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -2914,6 +2949,11 @@ "fill-range": "2.2.3" } }, + "expand-template": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz", + "integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ==" + }, "express": { "version": "4.16.3", "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", @@ -3230,6 +3270,11 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, + "fs-copy-file-sync": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-copy-file-sync/-/fs-copy-file-sync-1.1.0.tgz", + "integrity": "sha512-Pv0mBC3NXW6ql1sdG//3GL6TsxEoih6nVq8rO4PFqX4lo2C9soZvn8iiL8K0TUEtimtFKFvFrQbnWfJ0uazvsA==" + }, "fs-extra": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", @@ -3244,7 +3289,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", - "optional": true, "requires": { "minipass": "2.2.4" } @@ -4126,6 +4170,11 @@ "assert-plus": "1.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -5926,9 +5975,9 @@ } }, "kareem": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.6.tgz", - "integrity": "sha512-/C+l8gABdHsAIfNpykJNWmYodpTnDRyn+JhORkP2VgEf1GgdAc+oTHjVADwISwCJKta031EOIwY6+Hki5z8SpQ==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.7.tgz", + "integrity": "sha512-p8+lEpsNs4N0fvNOC1/zzDO0wDrD3Pb1G+OwfIG+gKVK3MyY5jeaGYh+9Qx6jb4fEG2b3E6U98vaE9MH7Gilsw==" }, "kind-of": { "version": "3.2.2", @@ -6451,9 +6500,9 @@ } }, "log-driver": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz", - "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", "dev": true }, "longest": { @@ -6763,6 +6812,11 @@ "mime-db": "1.30.0" } }, + "mimic-response": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", + "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -6796,7 +6850,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", - "optional": true, "requires": { "minipass": "2.2.4" } @@ -6903,13 +6956,13 @@ } }, "mongoose": { - "version": "5.0.16", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.16.tgz", - "integrity": "sha512-GZqoN85gpk7+MTxZkE+yEVTtvvGfG5//X3UMWfbPQJhNO3nmmF4GFdE1qro73Vsn0PCchxyst3z55JpqZuYAZA==", + "version": "5.0.17", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.17.tgz", + "integrity": "sha512-RV1WBQhzW7oOhStR+s7LQYfgQWTJm4hgmU3TqtgTiBCfnj5/sNliX2/SY+ef7tpIZRUqEBV5xITZdAlwQ6Ymdg==", "requires": { "async": "2.1.4", "bson": "1.0.6", - "kareem": "2.0.6", + "kareem": "2.0.7", "lodash.get": "4.4.2", "mongodb": "3.0.7", "mongoose-legacy-pluralize": "1.0.2", @@ -7029,8 +7082,7 @@ "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "optional": true + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" }, "nanomatch": { "version": "1.2.7", @@ -7099,6 +7151,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "node-abi": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.1.tgz", + "integrity": "sha512-pUlswqpHQ7zGPI9lGjZ4XDNIEUDbHxsltfIRb7dTnYdhgHWHOcB0MLZKLoCz6UMcGzSPG5wGl1HODZVQAUsH6w==", + "requires": { + "semver": "5.4.1" + } + }, "node-file-cache": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/node-file-cache/-/node-file-cache-1.0.2.tgz", @@ -7187,6 +7247,11 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, "nopt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", @@ -7883,6 +7948,35 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "prebuild-install": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz", + "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==", + "requires": { + "detect-libc": "1.0.3", + "expand-template": "1.1.0", + "github-from-package": "0.0.0", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "node-abi": "2.4.1", + "noop-logger": "0.1.1", + "npmlog": "4.1.2", + "os-homedir": "1.0.2", + "pump": "2.0.1", + "rc": "1.2.2", + "simple-get": "2.8.1", + "tar-fs": "1.16.2", + "tunnel-agent": "0.6.0", + "which-pm-runs": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -7959,7 +8053,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, "requires": { "end-of-stream": "1.4.0", "once": "1.4.0" @@ -8797,6 +8890,54 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" }, + "sharp": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.20.2.tgz", + "integrity": "sha512-cFL2qUT9eyR29bI+gj5kQjWlj+VIuTGdcY/CErQqmwJ4FKTXhmdE6d/zwjmrAokzr5n547KQed8HFQsmyUv9uw==", + "requires": { + "color": "3.0.0", + "detect-libc": "1.0.3", + "fs-copy-file-sync": "1.1.0", + "nan": "2.10.0", + "npmlog": "4.1.2", + "prebuild-install": "2.5.3", + "semver": "5.5.0", + "simple-get": "2.8.1", + "tar": "4.4.2", + "tunnel-agent": "0.6.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "tar": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.2.tgz", + "integrity": "sha512-BfkE9CciGGgDsATqkikUHrQrraBCO+ke/1f6SFAEMnxyyfN9lxC+nW1NFWMpqH865DhHIy9vQi682gk1X7friw==", + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + } + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + } + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -8834,6 +8975,36 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "3.3.0", + "once": "1.4.0", + "simple-concat": "1.0.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.1.tgz", + "integrity": "sha1-wt/DhquqDD4zxI2z/ocFnmkGXv0=" + } + } + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -9370,6 +9541,28 @@ "inherits": "2.0.3" } }, + "tar-fs": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.2.tgz", + "integrity": "sha512-LdknWjPEiZC1nOBwhv0JBzfJBGPJar08dZg2rwZe0ZTLQoRGEzgrl7vF3qUEkCHpI/wN9e7RyCuDhMsJUCLPPQ==", + "requires": { + "chownr": "1.0.1", + "mkdirp": "0.5.1", + "pump": "1.0.3", + "tar-stream": "1.5.5" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.0", + "once": "1.4.0" + } + } + } + }, "tar-pack": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", @@ -10431,6 +10624,11 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, "wide-align": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", diff --git a/api/package.json b/api/package.json index 64b5aa828..e7803cee4 100644 --- a/api/package.json +++ b/api/package.json @@ -43,7 +43,7 @@ "jsonwebtoken": "^8.2.1", "migrate-mongoose": "^3.2.2", "moment": "^2.22.1", - "mongoose": "^5.0.15", + "mongoose": "^5.0.17", "morgan": "^1.9.0", "multer": "^1.3.0", "node-file-cache": "^1.0.2", @@ -70,7 +70,7 @@ "@types/fs-extra": "^5.0.2", "@types/jsonwebtoken": "^7.2.7", "@types/mocha": "^5.2.0", - "@types/mongoose": "^5.0.10", + "@types/mongoose": "^5.0.12", "@types/morgan": "1.7.35", "@types/nodemailer": "^4.6.0", "@types/passport": "^0.4.5", @@ -81,7 +81,7 @@ "@types/winston": "^2.3.9", "chai": "4.1.2", "chai-http": "^4.0.0", - "coveralls": "^3.0.0", + "coveralls": "^3.0.1", "gulp": "3.9.1", "gulp-apidoc": "^0.2.7", "gulp-istanbul": "1.1.3", diff --git a/api/src/models/Course.ts b/api/src/models/Course.ts index 961dd5f19..2b91aa9a7 100644 --- a/api/src/models/Course.ts +++ b/api/src/models/Course.ts @@ -114,8 +114,12 @@ const courseSchema = new mongoose.Schema({ courseSchema.pre('remove', async function () { const localCourse = this; try { - await Lecture.deleteMany({'_id': {$in: localCourse.lectures}}).exec(); - await Directory.deleteOne({_id: localCourse.media}).exec(); + const dic = await Directory.findById(localCourse.media); + await dic.remove(); + for (const lec of localCourse.lectures) { + const lecDoc = await Lecture.findById(lec); + await lecDoc.remove(); + } } catch (error) { winston.log('warn', 'course (' + localCourse._id + ') cloud not be deleted!'); throw new Error('Delete Error: ' + error.toString()); diff --git a/api/src/models/mediaManager/Directory.ts b/api/src/models/mediaManager/Directory.ts index c4e6b1153..98c760ef7 100644 --- a/api/src/models/mediaManager/Directory.ts +++ b/api/src/models/mediaManager/Directory.ts @@ -1,7 +1,6 @@ import {IDirectory} from '../../../../shared/models/mediaManager/IDirectory'; import {File} from './File'; import * as mongoose from 'mongoose'; -import {Unit} from '../units/Unit'; interface IDirectoryModel extends IDirectory, mongoose.Document { @@ -56,7 +55,12 @@ directorySchema.pre('remove', async function () { await model.remove(); } } - await Unit.deleteMany({'_id': {$in: localDir.files}}).exec(); + for (const file of localDir.files) { + const model = await File.findById(file); + if (model) { + await model.remove(); + } + } } catch (err) { throw new Error('Delete Error: ' + err.toString()); } diff --git a/api/src/models/mediaManager/File.ts b/api/src/models/mediaManager/File.ts index 6c5f24f91..e363d886d 100644 --- a/api/src/models/mediaManager/File.ts +++ b/api/src/models/mediaManager/File.ts @@ -46,8 +46,7 @@ fileSchema.pre('remove', async function() { if (fs.existsSync(localFile.physicalPath)) { await promisify(fs.unlink)(localFile.physicalPath); } - - const units2Check: IFileUnitModel[] = await FileUnit.find({files: {$in: [localFile._id]}}); + const units2Check: IFileUnitModel[] = await FileUnit.find({files: {$in: [localFile._id]}}); Promise.all(units2Check.map(async unit => { const index = unit.files.indexOf(localFile._id); if (index > -1) { diff --git a/api/src/models/units/FileUnit.ts b/api/src/models/units/FileUnit.ts index ed8512361..21ab2937f 100644 --- a/api/src/models/units/FileUnit.ts +++ b/api/src/models/units/FileUnit.ts @@ -1,7 +1,7 @@ import * as mongoose from 'mongoose'; import {IUnitModel} from './Unit'; import {IFileUnit} from '../../../../shared/models/units/IFileUnit'; -import {User} from "../User"; +import {User} from '../User'; interface IFileUnitModel extends IFileUnit, IUnitModel { populateUnit: () => Promise; From d895339ae60bcc4b758f22404473c762f2130eec Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Sat, 5 May 2018 17:04:30 +0200 Subject: [PATCH 10/50] Fixed a bug delete empty folder of mediamanager --- api/src/models/Course.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/models/Course.ts b/api/src/models/Course.ts index 2b91aa9a7..7cfab2926 100644 --- a/api/src/models/Course.ts +++ b/api/src/models/Course.ts @@ -115,7 +115,9 @@ courseSchema.pre('remove', async function () { const localCourse = this; try { const dic = await Directory.findById(localCourse.media); + if (dic) { await dic.remove(); + } for (const lec of localCourse.lectures) { const lecDoc = await Lecture.findById(lec); await lecDoc.remove(); From 55a504d44ce1a4d21abb2c5dcca6747b03361f47 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Sat, 5 May 2018 17:48:54 +0200 Subject: [PATCH 11/50] Added more changelog entries --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a13f778..d75f7c875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added details of courseAdmin and teacher to course detail view. on click profiles are shown. [#598](https://github.com/h-da/geli/issues/598) - Added small auto linting scripts to package.json [#688](https://github.com/h-da/geli/issues/688) - Added changed size of drop down arrows for better usability. [#686](https://github.com/h-da/geli/issues/686) +- Added new contributors [#624](https://github.com/h-da/geli/issues/624) +- Added the date and the teacher under each unit [#582](https://github.com/h-da/geli/issues/582) +- Added E-Mail validation to reset password [#597](https://github.com/h-da/geli/issues/597) +- Added Language code to header [#554](https://github.com/h-da/geli/issues/554) ### Changed - Refactored or slightly altered various course & user related APIs. [#654](https://github.com/h-da/geli/issues/654) [#691](https://github.com/h-da/geli/issues/691) @@ -27,7 +31,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Refactored register and resend activation to use geli email validator with top level domain check. [#713](https://github.com/h-da/geli/issues/713) ### Fixed -- Fixed Course progress mechanism +- Fixed route `/users/roles` [#204](https://github.com/h-da/geli/issues/204) +- Fixed profile picture will be deleted after changing any other profile data [#504](https://github.com/h-da/geli/issues/504) +- Fixed some UI issues in create code kata unit [#543](https://github.com/h-da/geli/issues/543) +- Fixed reading wrong error message across the whole application [#572](https://github.com/h-da/geli/issues/572) +- Fixed admin can changed his own role [#606](https://github.com/h-da/geli/issues/606) +- Fixed a typo in admin panel [#533](https://github.com/h-da/geli/issues/533) +- Fixed an admin cannot delete any courses [#647](https://github.com/h-da/geli/issues/647) +- Fixed some issues with download a course [#659](https://github.com/h-da/geli/issues/659) +- Fixed an issue with deleting a course and the notification was not triggered [#642](https://github.com/h-da/geli/issues/543) +- Fixed Course progress mechanism [#593](https://github.com/h-da/geli/issues/593) - Fixed wasteful course data usage via specialized course model interfaces. [#654](https://github.com/h-da/geli/issues/654) - Fixed a broken documentation link. [#583](https://github.com/h-da/geli/issues/583) - Limited the first and last name to 64 characters in the registration- and edit page. [#585](https://github.com/h-da/geli/issues/585) From 1dd0945e88cd3963f8efb3d88717bfde829ed20a Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Sat, 5 May 2018 18:07:06 +0200 Subject: [PATCH 12/50] Bump version to 0.7.0 --- CHANGELOG.md | 2 ++ api/package.json | 2 +- app/webFrontend/package.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a62cbdc..66471bf03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. --> ## [NEXT] + +## [[0.7.0](https://github.com/h-da/geli/releases/tag/v0.7.0)] - 2018-05-05 - SS 18 intermediate Release ### Added - Added the possibility to sort all courses alphabetically. [#567](https://github.com/h-da/geli/issues/567) - Added a box for information on the homescreen. [#216](https://github.com/h-da/geli/issues/216) diff --git a/api/package.json b/api/package.json index e7803cee4..ab961c78c 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "geli-api", - "version": "0.6.0", + "version": "0.7.0", "license": "GPL-3.0", "repository": "h-da/geli", "description": "This is the API-Package.json for geli.", diff --git a/app/webFrontend/package.json b/app/webFrontend/package.json index 154b30032..5fc74bc1c 100644 --- a/app/webFrontend/package.json +++ b/app/webFrontend/package.json @@ -2,7 +2,7 @@ "name": "geli-web-frontend", "description": "This is the WebFronted-Package.json for geli.", "repository": "h-da/geli", - "version": "0.6.0", + "version": "0.7.0", "license": "GPL-3.0", "scripts": { "ng": "ng", From dd7dbd94fc08ab901376a1bd526cbd6cd044d6d1 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Sat, 5 May 2018 18:22:08 +0200 Subject: [PATCH 13/50] Fixed travis changelog regex --- .travis/changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/changelog.sh b/.travis/changelog.sh index 1b0849986..cfc1836d8 100755 --- a/.travis/changelog.sh +++ b/.travis/changelog.sh @@ -29,7 +29,7 @@ elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" curl --silent https://raw.githubusercontent.com/h-da/geli/$TRAVIS_BRANCH/CHANGELOG.md \ | diff CHANGELOG.md - \ - | grep -P '^< ## \[\d{1,3}\.\d{1,3}\.\d{1,3}\] - \d{4}-\d{2}-\d{2} - .{10,}' - -q + | grep -P '^< ## \[\[\d{1,3}\.\d{1,3}\.\d{1,3}\].*\] - \d{4}-\d{2}-\d{2} - .{10,}' - -q if [[ $? == 0 ]]; then echo -e "${GREEN}+ Update in CHANGELOG.md found, exit${NC}" From 6eefab27a2a443a92891dbcd3b4f0e7ae16c3e07 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Sun, 6 May 2018 22:25:42 +0200 Subject: [PATCH 14/50] Migrate to SnackBarService --- .../general-tab/general-tab.component.ts | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/app/webFrontend/src/app/course/course-edit/general-tab/general-tab.component.ts b/app/webFrontend/src/app/course/course-edit/general-tab/general-tab.component.ts index eb9af6910..6ed77d4f1 100644 --- a/app/webFrontend/src/app/course/course-edit/general-tab/general-tab.component.ts +++ b/app/webFrontend/src/app/course/course-edit/general-tab/general-tab.component.ts @@ -10,7 +10,7 @@ import { } from '../../../../../../../shared/models/ICourse'; import {ActivatedRoute, Router} from '@angular/router'; import {CourseService, DuplicationService, ExportService, NotificationService} from '../../../shared/services/data.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../../shared/services/snack-bar.service'; import {ShowProgressService} from '../../../shared/services/show-progress.service'; import {TitleService} from '../../../shared/services/title.service'; import {SaveFileService} from '../../../shared/services/save-file.service'; @@ -48,7 +48,7 @@ export class GeneralTabComponent implements OnInit { private router: Router, private formBuilder: FormBuilder, private courseService: CourseService, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, private ref: ChangeDetectorRef, private showProgress: ShowProgressService, private titleService: TitleService, @@ -63,22 +63,21 @@ export class GeneralTabComponent implements OnInit { this.route.params.subscribe(params => { this.id = params['id']; - this.courseService.readCourseToEdit(this.id).then( - (val: any) => { - this.course = val.name; - this.description = val.description; - this.accessKey = val.accessKey; - this.active = val.active; - this.enrollType = val.enrollType; - if (this.enrollType === 'whitelist') { - this.mode = true; - } - this.courseOb = val; - this.dataSharingService.setDataForKey('course', this.courseOb); - this.titleService.setTitleCut(['Edit Course: ', this.course]); - }, (error) => { - this.snackBar.open('Couldn\'t load Course-Item', '', {duration: 3000}); - }); + this.courseService.readCourseToEdit(this.id).then(course => { + this.courseOb = course; + + this.course = this.courseOb.name; + this.description = this.courseOb.description; + this.accessKey = this.courseOb.accessKey; + this.active = this.courseOb.active; + this.enrollType = this.courseOb.enrollType; + this.mode = (this.enrollType === 'whitelist'); + + this.dataSharingService.setDataForKey('course', this.courseOb); + this.titleService.setTitleCut(['Edit Course: ', this.course]); + }).catch(err => { + this.snackBar.open('Couldn\'t load Course-Item'); + }); }); } @@ -97,13 +96,13 @@ export class GeneralTabComponent implements OnInit { this.uploader.onCompleteItem = (item: any, response: any, status: any) => { if (status === 200) { const result = JSON.parse(response); - this.snackBar.open('Upload complete, there now are ' + result.newlength + ' whitelisted users!', '', {duration: 10000}); + this.snackBar.openLong('Upload complete, there now are ' + result.newlength + ' whitelisted users!'); setTimeout(() => { this.uploader.clearQueue(); }, 3000); } else { const error = JSON.parse(response); - this.snackBar.open('Upload failed with status ' + status + ' message was: ' + error.message, '', {duration: 20000}); + this.snackBar.openLong('Upload failed with status ' + status + ' message was: ' + error.message); setTimeout(() => { this.uploader.clearQueue(); }, 6000); @@ -143,10 +142,10 @@ export class GeneralTabComponent implements OnInit { }); this.showProgress.toggleLoadingGlobal(false); - this.snackBar.open('Saved successfully', '', {duration: 5000}); + this.snackBar.open('Saved successfully'); } catch (err) { this.showProgress.toggleLoadingGlobal(false); - this.snackBar.open('Saving course failed ' + err.error.message, 'Dismiss'); + this.snackBar.open('Saving course failed ' + err.error.message, ); } } @@ -155,7 +154,7 @@ export class GeneralTabComponent implements OnInit { const courseJSON = await this.exportService.exportCourse(this.courseOb); this.saveFileService.save(this.courseOb.name, JSON.stringify(courseJSON, null, 2)); } catch (err) { - this.snackBar.open('Export course failed ' + err.error.message, 'Dismiss'); + this.snackBar.open('Export course failed ' + err.error.message); } } @@ -163,9 +162,9 @@ export class GeneralTabComponent implements OnInit { try { const course = await this.duplicationService.duplicateCourse(this.courseOb, this.userService.user); this.router.navigate(['course', course._id, 'edit']); - this.snackBar.open('Course successfully duplicated', '', {duration: 3000}); + this.snackBar.open('Course successfully duplicated'); } catch (err) { - this.snackBar.open('Duplication of the course failed ' + err.error.message, 'Dismiss'); + this.snackBar.open('Duplication of the course failed ' + err.error.message); } } From fccb44623c52d7bc2ca44a76c976a632716d5b85 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 14:30:56 +0200 Subject: [PATCH 15/50] Migrate to SnackBarService --- .../auth/activation-resend/activation-resend.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/webFrontend/src/app/auth/activation-resend/activation-resend.component.ts b/app/webFrontend/src/app/auth/activation-resend/activation-resend.component.ts index 78427208a..f0438bfa5 100644 --- a/app/webFrontend/src/app/auth/activation-resend/activation-resend.component.ts +++ b/app/webFrontend/src/app/auth/activation-resend/activation-resend.component.ts @@ -3,7 +3,7 @@ import {Validators, FormGroup, FormBuilder, FormControl} from '@angular/forms'; import {AuthenticationService} from '../../shared/services/authentication.service'; import {Router} from '@angular/router'; import {ShowProgressService} from '../../shared/services/show-progress.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {errorCodes} from '../../../../../../api/src/config/errorCodes'; import {TitleService} from '../../shared/services/title.service'; import {emailValidator} from '../../shared/validators/validators'; @@ -32,7 +32,7 @@ export class ActivationResendComponent implements OnInit { constructor(private router: Router, private authenticationService: AuthenticationService, private showProgress: ShowProgressService, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, private formBuilder: FormBuilder, private titleService: TitleService, ) { } @@ -95,7 +95,7 @@ export class ActivationResendComponent implements OnInit { break; } default: { - this.snackBar.open('Activation Resend failed', 'Dismiss'); + this.snackBar.open('Activation Resend failed'); } } } From b3a4bf74cd80a9e826fbebcb036dcdb3196a241e Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 14:37:21 +0200 Subject: [PATCH 16/50] Migrate to SnackBarService --- .../course-user-list-overview.component.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/webFrontend/src/app/course/course-edit/course-user-list/course-user-list-overview/course-user-list-overview.component.ts b/app/webFrontend/src/app/course/course-edit/course-user-list/course-user-list-overview/course-user-list-overview.component.ts index d85b8c114..c8853cd2f 100644 --- a/app/webFrontend/src/app/course/course-edit/course-user-list/course-user-list-overview/course-user-list-overview.component.ts +++ b/app/webFrontend/src/app/course/course-edit/course-user-list/course-user-list-overview/course-user-list-overview.component.ts @@ -1,7 +1,7 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {IUser} from '../../../../../../../../shared/models/IUser'; import {DialogService} from '../../../../shared/services/dialog.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../../../shared/services/snack-bar.service'; import {CourseService, NotificationService} from '../../../../shared/services/data.service'; import {ICourse} from '../../../../../../../../shared/models/ICourse'; import {User} from '../../../../models/User'; @@ -27,7 +27,8 @@ export class CourseUserListOverviewComponent implements OnInit { return users.map((user: IUser) => `${user.profile.firstName} ${user.profile.lastName}<${user.email}>`).join(', '); } - constructor(private dialogService: DialogService, private snackBar: MatSnackBar, + constructor(private dialogService: DialogService, + private snackBar: SnackBarService, private courseService: CourseService, private notificationService: NotificationService) { } @@ -84,9 +85,9 @@ export class CourseUserListOverviewComponent implements OnInit { this.resetSelectedUsers(); try { await this.courseService.sendMailToSelectedUsers(mailData); - this.snackBar.open('Sending mail succeeded.', 'Dismiss', {duration: 2000}); + this.snackBar.open('Sending mail succeeded.'); } catch (err) { - this.snackBar.open('Sending mail failed.', 'Dismiss', {duration: 3000}); + this.snackBar.open('Sending mail failed.'); } } From 9ccda04bf18d648add4872d7e1c44452ebc0ca82 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:03:43 +0200 Subject: [PATCH 17/50] Refresh user list after user delete Migrate to SnackBarService --- .../admin/user-admin/user-admin.component.ts | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/app/webFrontend/src/app/admin/user-admin/user-admin.component.ts b/app/webFrontend/src/app/admin/user-admin/user-admin.component.ts index 0288079b3..3f537876e 100644 --- a/app/webFrontend/src/app/admin/user-admin/user-admin.component.ts +++ b/app/webFrontend/src/app/admin/user-admin/user-admin.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {UserDataService} from '../../shared/services/data.service'; import {Router} from '@angular/router'; import {ShowProgressService} from '../../shared/services/show-progress.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {IUser} from '../../../../../../shared/models/IUser'; import {DialogService} from '../../shared/services/dialog.service'; import {UserService} from '../../shared/services/user.service'; @@ -20,7 +20,7 @@ export class UserAdminComponent implements OnInit { constructor(private userDataService: UserDataService, private router: Router, private showProgress: ShowProgressService, - public snackBar: MatSnackBar, + public snackBar: SnackBarService, public dialogService: DialogService, private userService: UserService) { } @@ -46,18 +46,15 @@ export class UserAdminComponent implements OnInit { }); } - updateRole(userIndex: number) { + async updateRole(userIndex: number) { this.showProgress.toggleLoadingGlobal(true); - this.userDataService.updateItem(this.allUsers[userIndex]).then( - (val) => { - this.showProgress.toggleLoadingGlobal(false); - this.snackBar.open('Role of user ' + val.email + ' successfully updated to ' + val.role, '', {duration: 3000}); - }, - (error) => { - this.snackBar.open(error.error.message, '', {duration: 3000}); - this.showProgress.toggleLoadingGlobal(false); - } - ); + try { + const user = await this.userDataService.updateItem(this.allUsers[userIndex]); + this.snackBar.open('Role of user ' + user.email + ' successfully updated to ' + user.role); + } catch (err) { + this.snackBar.open(err.error.message); + } + this.showProgress.toggleLoadingGlobal(false); } editUser(userIndex: number) { @@ -66,22 +63,24 @@ export class UserAdminComponent implements OnInit { } deleteUser(userIndex: number) { - this.dialogService - .confirmDelete('user', this.allUsers[userIndex].email) - .subscribe(res => { - if (res) { + this.dialogService.confirmDelete('user', this.allUsers[userIndex].email) + .subscribe(async res => { + if (!res) { + return; + } + this.showProgress.toggleLoadingGlobal(true); - this.userDataService.deleteItem(this.allUsers[userIndex]).then( - (val) => { - this.showProgress.toggleLoadingGlobal(false); - this.snackBar.open('User ' + val + ' was successfully deleted.', '', {duration: 3000}); - }, - (error) => { - this.showProgress.toggleLoadingGlobal(false); - this.snackBar.open(error, '', {duration: 3000}); - } - ); - } + const user = this.allUsers[userIndex]; + + try { + await this.userDataService.deleteItem(user); + this.snackBar.open('User ' + user.email + ' was successfully deleted.'); + } catch (err) { + this.snackBar.open(err.error.message); + } + + this.getUsers(); + this.showProgress.toggleLoadingGlobal(false); }); } } From 178cf3c400a8218fbf1464966b5355db97671a32 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:13:21 +0200 Subject: [PATCH 18/50] Migrate to SnackBarService --- .../course-container.component.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/webFrontend/src/app/course/course-container/course-container.component.ts b/app/webFrontend/src/app/course/course-container/course-container.component.ts index 0c32d2dd1..1edc74013 100644 --- a/app/webFrontend/src/app/course/course-container/course-container.component.ts +++ b/app/webFrontend/src/app/course/course-container/course-container.component.ts @@ -1,5 +1,6 @@ import {Component, EventEmitter, Input, Output, OnInit, ViewEncapsulation} from '@angular/core'; -import {MatDialog, MatSnackBar} from '@angular/material'; +import {MatDialog} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {UserService} from '../../shared/services/user.service'; import {CourseService, UserDataService} from '../../shared/services/data.service'; import {Router} from '@angular/router'; @@ -30,7 +31,7 @@ export class CourseContainerComponent implements OnInit { private courseService: CourseService, private router: Router, private dialog: MatDialog, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, private userDataService: UserDataService) { } @@ -48,22 +49,21 @@ export class CourseContainerComponent implements OnInit { accessKey }).then((res) => { LastVisitedCourseContainerUpdater.addCourseToLastVisitedCourses(courseId, this.userService, this.userDataService); - this.snackBar.open('Successfully enrolled', '', {duration: 5000}); + this.snackBar.open('Successfully enrolled'); // reload courses to update enrollment status this.onEnroll.emit(); - }).catch((error) => { - const errormessage = error.error.message; - switch (errormessage) { + }).catch((err) => { + switch (err.error.message) { case errorCodes.course.accessKey.code: { - this.snackBar.open(`${errorCodes.course.accessKey.text}`, 'Dismiss'); + this.snackBar.open(`${errorCodes.course.accessKey.text}`); break; } case errorCodes.course.notOnWhitelist.code: { - this.snackBar.open(`${errorCodes.course.notOnWhitelist.text}`, 'Dismiss'); + this.snackBar.open(`${errorCodes.course.notOnWhitelist.text}`); break; } default: { - this.snackBar.open('Enroll failed', '', {duration: 5000}); + this.snackBar.open('Enroll failed'); } } }); From 8a9101505a09de26dcf9e2d1c91a7a2fc5547adc Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:22:45 +0200 Subject: [PATCH 19/50] Check if :id has valid format for mongoId --- api/src/controllers/CourseController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/controllers/CourseController.ts b/api/src/controllers/CourseController.ts index fb0970c8b..02fe0477d 100644 --- a/api/src/controllers/CourseController.ts +++ b/api/src/controllers/CourseController.ts @@ -162,7 +162,7 @@ export class CourseController { * @apiError NotFoundError Includes implicit authorization check. (In getCourse helper method.) * @apiError ForbiddenError (Redundant) Authorization check. */ - @Get('/:id') + @Get('/:id([a-fA-F0-9]{24})') async getCourseView(@Param('id') id: string, @CurrentUser() currentUser: IUser): Promise { const course = await this.getCourse(id, currentUser); From 55d602a5058dd06bf9f0e5b089d7f345da7ae3bd Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:23:59 +0200 Subject: [PATCH 20/50] Remove Error-Case 401 (when user does not have permission to view this course a 404 is triggered) Show err.error.message instead err.message Migrate to SnackBarService --- .../course/course-detail/course-detail.component.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/webFrontend/src/app/course/course-detail/course-detail.component.ts b/app/webFrontend/src/app/course/course-detail/course-detail.component.ts index 858de7696..117523302 100644 --- a/app/webFrontend/src/app/course/course-detail/course-detail.component.ts +++ b/app/webFrontend/src/app/course/course-detail/course-detail.component.ts @@ -7,6 +7,7 @@ import {UserService} from '../../shared/services/user.service'; import {IUser} from '../../../../../../shared/models/IUser'; import {User} from '../../models/User'; import {MatSnackBar, MatDialog} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {DownloadCourseDialogComponent} from './download-course-dialog/download-course-dialog.component'; import {TitleService} from '../../shared/services/title.service'; import {LastVisitedCourseContainerUpdater} from '../../shared/utils/LastVisitedCourseContainerUpdater'; @@ -27,7 +28,7 @@ export class CourseDetailComponent implements OnInit { private route: ActivatedRoute, private courseService: CourseService, public userService: UserService, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, private dialog: MatDialog, private titleService: TitleService, private userDataService: UserDataService, @@ -47,14 +48,12 @@ export class CourseDetailComponent implements OnInit { this.course = await this.courseService.readCourseToView(courseId); this.titleService.setTitleCut(['Course: ', this.course.name]); LastVisitedCourseContainerUpdater.addCourseToLastVisitedCourses(courseId, this.userService, this.userDataService); - } catch (errorResponse) { - if (errorResponse.status === 401) { - this.snackBar.open('You are not authorized to view this course.', '', {duration: 3000}); - } else if (errorResponse.status === 404) { - this.snackBar.open('Your selected course is not available.', '', {duration: 3000}); + } catch (err) { + if (err.status === 404) { + this.snackBar.open('Your selected course is not available.'); this.router.navigate(['/not-found']); } else { - this.snackBar.open('Something went wrong: ' + errorResponse.message, '', {duration: 3000}); + this.snackBar.open('Something went wrong: ' + err.error.message); } } } From 515530ffaba8b769836e877f8a09db2dbfa2b15c Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:29:59 +0200 Subject: [PATCH 21/50] Migrate to SnackBarService --- .../download-course-dialog.component.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/webFrontend/src/app/course/course-detail/download-course-dialog/download-course-dialog.component.ts b/app/webFrontend/src/app/course/course-detail/download-course-dialog/download-course-dialog.component.ts index 5a6b8b128..89e4e3185 100644 --- a/app/webFrontend/src/app/course/course-detail/download-course-dialog/download-course-dialog.component.ts +++ b/app/webFrontend/src/app/course/course-detail/download-course-dialog/download-course-dialog.component.ts @@ -1,5 +1,6 @@ import {Component, Inject, OnInit, QueryList, ViewChildren, ViewEncapsulation} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatSnackBar} from '@angular/material'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; +import {SnackBarService} from '../../../shared/services/snack-bar.service'; import {ICourse} from '../../../../../../../shared/models/ICourse'; import {LectureCheckboxComponent} from './downloadCheckBoxes/lecture-checkbox.component'; import {DownloadFileService} from 'app/shared/services/data.service'; @@ -28,7 +29,7 @@ export class DownloadCourseDialogComponent implements OnInit { constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, private downloadReq: DownloadFileService, - public snackBar: MatSnackBar, + public snackBar: SnackBarService, private saveFileService: SaveFileService) { } @@ -57,7 +58,7 @@ export class DownloadCourseDialogComponent implements OnInit { } onChildEvent() { - const childChecked: boolean[] = new Array(); + const childChecked: boolean[] = []; this.childLectures.forEach(lec => { if (lec.chkbox === true && !lec.childUnits.find(unit => unit.chkbox === false)) { @@ -109,7 +110,7 @@ export class DownloadCourseDialogComponent implements OnInit { this.disableDownloadButton = true; const obj = await this.buildObject(); if (obj.lectures.length === 0) { - this.snackBar.open('No units selected!', 'Dismiss', {duration: 3000}); + this.snackBar.open('No units selected!'); this.disableDownloadButton = false; return; } @@ -125,15 +126,13 @@ export class DownloadCourseDialogComponent implements OnInit { if (!this.keepDialogOpen) { this.dialogRef.close(); } - } catch (error) { + } catch (err) { this.showSpinner = false; this.disableDownloadButton = false; - this.snackBar.open('Woops! Something went wrong. Please try again in a few Minutes.', - 'Dismiss', {duration: 10000}); + this.snackBar.openLong('Woops! Something went wrong. Please try again in a few Minutes.'); } } else { - this.snackBar.open('Requested Download Package is too large! Please Download fewer Units in one Package.', - 'Dismiss', {duration: 10000}); + this.snackBar.openLong('Requested Download Package is too large! Please Download fewer Units in one Package.'); this.showSpinner = false; this.disableDownloadButton = false; } @@ -163,8 +162,7 @@ export class DownloadCourseDialogComponent implements OnInit { } }); - const downloadObj = {courseName: this.course._id, lectures: lectures}; - return downloadObj; + return {courseName: this.course._id, lectures: lectures}; } } From ee3b5c01861570c73b021f122894bae57bea6827 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:31:31 +0200 Subject: [PATCH 22/50] Remove unused dependency --- .../downloadCheckBoxes/unit-checkbox.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/webFrontend/src/app/course/course-detail/download-course-dialog/downloadCheckBoxes/unit-checkbox.component.ts b/app/webFrontend/src/app/course/course-detail/download-course-dialog/downloadCheckBoxes/unit-checkbox.component.ts index 3f1717dc7..f8c902733 100644 --- a/app/webFrontend/src/app/course/course-detail/download-course-dialog/downloadCheckBoxes/unit-checkbox.component.ts +++ b/app/webFrontend/src/app/course/course-detail/download-course-dialog/downloadCheckBoxes/unit-checkbox.component.ts @@ -5,7 +5,6 @@ import { import {IUnit} from '../../../../../../../../shared/models/units/IUnit'; import {IFileUnit} from '../../../../../../../../shared/models/units/IFileUnit'; import {UploadUnitCheckboxComponent} from './upload-unit-checkbox.component'; -import {MatSnackBar} from '@angular/material'; import {ConfigService} from '../../../../shared/services/data.service'; @@ -32,7 +31,7 @@ export class UnitCheckboxComponent implements OnInit { childUnitDesc: string; showCheckBox = true; // false if the unit has only large files - constructor(public snackBar: MatSnackBar, private configService: ConfigService) { + constructor(private configService: ConfigService) { } ngOnInit() { From d7bd6f71151b5662841482a300f1d44ca5eb5901 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 16:44:51 +0200 Subject: [PATCH 23/50] Migrate to SnackBarService --- .../src/app/unit/task-unit/task-unit.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/webFrontend/src/app/unit/task-unit/task-unit.component.ts b/app/webFrontend/src/app/unit/task-unit/task-unit.component.ts index 77f2614a6..c74ec2f27 100644 --- a/app/webFrontend/src/app/unit/task-unit/task-unit.component.ts +++ b/app/webFrontend/src/app/unit/task-unit/task-unit.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit, Input} from '@angular/core'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {ActivatedRoute} from '@angular/router'; import {ProgressService} from '../../shared/services/data/progress.service'; import {ITaskUnit} from '../../../../../../shared/models/units/ITaskUnit'; @@ -23,7 +23,7 @@ export class TaskUnitComponent implements OnInit { constructor(private route: ActivatedRoute, private progressService: ProgressService, - private snackBar: MatSnackBar) { + private snackBar: SnackBarService) { } ngOnInit() { @@ -82,10 +82,10 @@ export class TaskUnitComponent implements OnInit { promise .then((savedProgress) => { this.progress = savedProgress; - this.snackBar.open('Progress has been saved', '', {duration: 3000}); + this.snackBar.open('Progress has been saved'); }) .catch((err) => { - this.snackBar.open(`An error occurred: ${err.error.message}`, '', {duration: 3000}); + this.snackBar.open(`An error occurred: ${err.error.message}`); }); }; From 3165db5d070f87e5497a3aba9efba394350abf0f Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 17:06:01 +0200 Subject: [PATCH 24/50] Check if form is valid before submit --- .../src/app/course/course-new/course-new.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/webFrontend/src/app/course/course-new/course-new.component.html b/app/webFrontend/src/app/course/course-new/course-new.component.html index e231dee89..bd3192698 100644 --- a/app/webFrontend/src/app/course/course-new/course-new.component.html +++ b/app/webFrontend/src/app/course/course-new/course-new.component.html @@ -1,7 +1,7 @@

Create Course

-
+
From 7a9ad5a8c231d8f17edcd593b2477af4bffa413a Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 17:09:52 +0200 Subject: [PATCH 25/50] Migrate to SnackBarService --- .../course/course-new/course-new.component.ts | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/webFrontend/src/app/course/course-new/course-new.component.ts b/app/webFrontend/src/app/course/course-new/course-new.component.ts index 908ff9a2f..4e7c49258 100644 --- a/app/webFrontend/src/app/course/course-new/course-new.component.ts +++ b/app/webFrontend/src/app/course/course-new/course-new.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import {CourseService} from '../../shared/services/data.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {Router} from '@angular/router'; import {errorCodes} from '../../../../../../api/src/config/errorCodes'; import {TitleService} from '../../shared/services/title.service'; @@ -19,7 +19,7 @@ export class CourseNewComponent implements OnInit { constructor(private router: Router, private formBuilder: FormBuilder, private courseService: CourseService, - public snackBar: MatSnackBar, + private snackBar: SnackBarService, private titleService: TitleService) { } @@ -34,17 +34,16 @@ export class CourseNewComponent implements OnInit { createCourse() { this.resetErrors(); - this.courseService.createItem(this.newCourse.value).then( - (val) => { - this.snackBar.open('Course created', 'Dismiss', {duration: 5000}); - this.router.navigate(['course', val._id, 'edit']); - }, (err) => { - if (err.error.message === errorCodes.course.duplicateName.code) { - this.nameError = errorCodes.course.duplicateName.text; - } else { - this.snackBar.open('Error creating course ' + err.error.message, 'Dismiss'); - } - }); + this.courseService.createItem(this.newCourse.value).then((val) => { + this.snackBar.open('Course created'); + this.router.navigate(['course', val._id, 'edit']); + }).catch((err) => { + if (err.error.message === errorCodes.course.duplicateName.code) { + this.nameError = errorCodes.course.duplicateName.text; + } else { + this.snackBar.open('Error creating course ' + err.error.message); + } + }); } generateForm() { From 7deeadef0b8ce799bd377a5bc8163da45ce76130 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 17:13:23 +0200 Subject: [PATCH 26/50] Migrate to SnackBarService --- .../src/app/course/course-new/course-new.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/webFrontend/src/app/course/course-new/course-new.component.ts b/app/webFrontend/src/app/course/course-new/course-new.component.ts index 4e7c49258..0469c603d 100644 --- a/app/webFrontend/src/app/course/course-new/course-new.component.ts +++ b/app/webFrontend/src/app/course/course-new/course-new.component.ts @@ -34,10 +34,10 @@ export class CourseNewComponent implements OnInit { createCourse() { this.resetErrors(); - this.courseService.createItem(this.newCourse.value).then((val) => { + this.courseService.createItem(this.newCourse.value).then(val => { this.snackBar.open('Course created'); this.router.navigate(['course', val._id, 'edit']); - }).catch((err) => { + }).catch(err => { if (err.error.message === errorCodes.course.duplicateName.code) { this.nameError = errorCodes.course.duplicateName.text; } else { From 4a3f6286549a08101d7f50960cf131cb97e8cf3e Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 7 May 2018 17:23:31 +0200 Subject: [PATCH 27/50] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66471bf03..b828c3caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. --> ## [NEXT] +### Changed +- Migrate MatSnackBar to SnackBarService. [#724](https://github.com/h-da/geli/pull/724) +- Reload user list after deleting an account. [#724](https://github.com/h-da/geli/pull/724) +- Validate form before submit when creating a new course. [#724](https://github.com/h-da/geli/pull/724) +- Validate :id for CourseController details route. [#724](https://github.com/h-da/geli/pull/724) ## [[0.7.0](https://github.com/h-da/geli/releases/tag/v0.7.0)] - 2018-05-05 - SS 18 intermediate Release ### Added From 548b3baa61a8dc9f5f16ad07ec119f3b6d2090cc Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 8 May 2018 08:12:43 +0200 Subject: [PATCH 28/50] Migrate to SnackBarService --- .../pick-media-dialog/pick-media-dialog.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/webFrontend/src/app/shared/components/pick-media-dialog/pick-media-dialog.component.ts b/app/webFrontend/src/app/shared/components/pick-media-dialog/pick-media-dialog.component.ts index badfbb3e7..2ac257327 100644 --- a/app/webFrontend/src/app/shared/components/pick-media-dialog/pick-media-dialog.component.ts +++ b/app/webFrontend/src/app/shared/components/pick-media-dialog/pick-media-dialog.component.ts @@ -1,5 +1,6 @@ import {Component, Inject, OnInit, ViewChild} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef, MatSelectionList, MatSnackBar} from '@angular/material'; +import {MAT_DIALOG_DATA, MatDialogRef, MatSelectionList} from '@angular/material'; +import {SnackBarService} from '../../services/snack-bar.service'; import {IFile} from '../../../../../../../shared/models/mediaManager/IFile'; import {IDirectory} from '../../../../../../../shared/models/mediaManager/IDirectory'; import {MediaService} from '../../services/data.service'; @@ -19,7 +20,7 @@ export class PickMediaDialog implements OnInit { constructor(private dialogRef: MatDialogRef, private mediaService: MediaService, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, @Inject(MAT_DIALOG_DATA) public data: any) { } @@ -38,7 +39,7 @@ export class PickMediaDialog implements OnInit { save(): void { if (this.selectedOptions.length === 0) { - this.snackBar.open('Please choose at least one file', '', {duration: 2000}); + this.snackBar.open('Please choose at least one file'); return; } @@ -48,5 +49,4 @@ export class PickMediaDialog implements OnInit { cancel(): void { this.dialogRef.close(false); } - } From 1ac165951b4ac0243faa712be055c9495a4de69a Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 8 May 2018 08:24:47 +0200 Subject: [PATCH 29/50] Migrate to SnackBarService --- .../components/upload-form/upload-form.component.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts b/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts index d1f404bd8..79cffaa65 100644 --- a/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts +++ b/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts @@ -1,6 +1,6 @@ import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core'; import {FileUploader, FileUploaderOptions} from 'ng2-file-upload'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../services/snack-bar.service'; @Component({ selector: 'app-upload-form', @@ -40,7 +40,7 @@ export class UploadFormComponent implements OnInit, OnChanges { private updateUploadParams = false; private error = false; - constructor(private snackBar: MatSnackBar) { + constructor(private snackBar: SnackBarService) { } ngOnInit() { @@ -61,7 +61,7 @@ export class UploadFormComponent implements OnInit, OnChanges { this.fileUploader.onCancelItem = (file) => { // set error true, so dialog is not closed automatically this.error = true; - this.snackBar.open(`upload cancelled for ${file._file.name}`, 'Dismiss'); + this.snackBar.open(`upload cancelled for ${file._file.name}`); }; this.fileUploader.onBuildItemForm = (fileItem: any, form: any) => { @@ -81,7 +81,7 @@ export class UploadFormComponent implements OnInit, OnChanges { this.fileUploader.onCompleteAll = () => { if (!this.error && this.fileUploader.queue.length === 0) { this.onAllUploaded.emit(); - this.snackBar.open('All items uploaded!', '', {duration: 3000}); + this.snackBar.open('All items uploaded!'); } }; @@ -113,7 +113,7 @@ export class UploadFormComponent implements OnInit, OnChanges { // reset the error state to try again later item.isError = false; item.isUploaded = false; - this.snackBar.open(`${item._file.name} failed to upload`, 'Dismiss'); + this.snackBar.open(`${item._file.name} failed to upload`); }; } @@ -127,7 +127,7 @@ export class UploadFormComponent implements OnInit, OnChanges { clearQueue() { this.fileUploader.clearQueue(); if (this.fileUploader.queue.length > 0) { - this.snackBar.open('Queue couldn\'t be cleared.', 'Dismiss'); + this.snackBar.open('Queue couldn\'t be cleared.'); } else { this.onFileSelectedChange.emit(false); } From 6c2e1fed0546b41f5c38cb2a6e1ebc496ef3d647 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 8 May 2018 08:30:42 +0200 Subject: [PATCH 30/50] Migrate to SnackBarService --- .../upload-form-dialog/upload-form-dialog.component.ts | 10 +++++----- .../components/upload-form/upload-form.component.ts | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/webFrontend/src/app/shared/components/upload-form-dialog/upload-form-dialog.component.ts b/app/webFrontend/src/app/shared/components/upload-form-dialog/upload-form-dialog.component.ts index 967963469..0fbad7367 100644 --- a/app/webFrontend/src/app/shared/components/upload-form-dialog/upload-form-dialog.component.ts +++ b/app/webFrontend/src/app/shared/components/upload-form-dialog/upload-form-dialog.component.ts @@ -1,5 +1,6 @@ import {Component, Inject, OnInit, ViewChild} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef, MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../services/snack-bar.service'; import {UploadDialog} from '../upload-dialog/upload-dialog.component'; import {IFileUnit} from '../../../../../../../shared/models/units/IFileUnit'; import {UploadFormComponent} from '../upload-form/upload-form.component'; @@ -18,7 +19,7 @@ export class UploadFormDialog implements OnInit { uploadPath: string; constructor(public dialogRef: MatDialogRef, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, @Inject(MAT_DIALOG_DATA) public data: any) { this.uploadPath = this.uploadPathTmp + data.targetDir._id; } @@ -31,15 +32,14 @@ export class UploadFormDialog implements OnInit { onAllUploaded() { this.dialogRef.close(true); + this.snackBar.open('All items uploaded!'); } uploadAll() { this.uploadForm.fileUploader.uploadAll(); this.uploadForm.onAllUploaded.subscribe( - result => - this.onAllUploaded() - , error => - this.snackBar.open('Could not upload files', '', {duration: 3000}) + result => this.onAllUploaded(), + error => this.snackBar.open('Could not upload files') ); } diff --git a/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts b/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts index 79cffaa65..eb6f35360 100644 --- a/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts +++ b/app/webFrontend/src/app/shared/components/upload-form/upload-form.component.ts @@ -81,7 +81,6 @@ export class UploadFormComponent implements OnInit, OnChanges { this.fileUploader.onCompleteAll = () => { if (!this.error && this.fileUploader.queue.length === 0) { this.onAllUploaded.emit(); - this.snackBar.open('All items uploaded!'); } }; From 50836aca3f0768bf8df7ea7972e003e452763c8c Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 8 May 2018 09:13:51 +0200 Subject: [PATCH 31/50] Migrate to SnackBarService --- .../lecture-edit/lecture-edit.component.ts | 84 +++++++++---------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/app/webFrontend/src/app/course/course-edit/course-manage-content/lecture-edit/lecture-edit.component.ts b/app/webFrontend/src/app/course/course-edit/course-manage-content/lecture-edit/lecture-edit.component.ts index 94bd0d4b5..2586681ac 100644 --- a/app/webFrontend/src/app/course/course-edit/course-manage-content/lecture-edit/lecture-edit.component.ts +++ b/app/webFrontend/src/app/course/course-edit/course-manage-content/lecture-edit/lecture-edit.component.ts @@ -8,7 +8,7 @@ import { import {ShowProgressService} from 'app/shared/services/show-progress.service'; import {DialogService} from '../../../../shared/services/dialog.service'; import {UserService} from '../../../../shared/services/user.service'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../../../shared/services/snack-bar.service'; import {IUnit} from '../../../../../../../../shared/models/units/IUnit'; import {DragulaService} from 'ng2-dragula'; import {SaveFileService} from '../../../../shared/services/save-file.service'; @@ -33,7 +33,7 @@ export class LectureEditComponent implements OnInit, OnDestroy { private lectureService: LectureService, private unitService: UnitService, private showProgress: ShowProgressService, - private snackBar: MatSnackBar, + private snackBar: SnackBarService, private dialogService: DialogService, private dragulaService: DragulaService, private duplicationService: DuplicationService, @@ -60,22 +60,22 @@ export class LectureEditComponent implements OnInit, OnDestroy { async duplicateLecture(lecture: ILecture) { try { const duplicateLecture = await this.duplicationService.duplicateLecture(lecture, this.course._id); - this.snackBar.open('Lecture duplicated.', '', {duration: 3000}); + this.snackBar.open('Lecture duplicated.'); await this.reloadCourse(); this.navigateToLecture(duplicateLecture._id); } catch (err) { - this.snackBar.open(err, '', {duration: 3000}); + this.snackBar.open(err.error.message); } } async duplicateUnit(unit: IUnit) { try { - const duplicateUnit = await this.duplicationService.duplicateUnit(unit, this.lecture._id , this.course._id); - this.snackBar.open('Unit duplicated.', '', {duration: 3000}); + const duplicateUnit = await this.duplicationService.duplicateUnit(unit, this.lecture._id, this.course._id); + this.snackBar.open('Unit duplicated.'); await this.reloadCourse(); this.navigateToUnitEdit(duplicateUnit._id); } catch (err) { - this.snackBar.open(err, '', {duration: 3000}); + this.snackBar.open(err.error.message); } } @@ -85,7 +85,7 @@ export class LectureEditComponent implements OnInit, OnDestroy { this.saveFileService.save(lecture.name, JSON.stringify(lectureJSON, null, 2)); } catch (err) { - this.snackBar.open('Export lecture failed ' + err.error.message, 'Dismiss'); + this.snackBar.open('Export lecture failed ' + err.error.message); } } @@ -105,26 +105,25 @@ export class LectureEditComponent implements OnInit, OnDestroy { } - deleteUnit(unit: IUnit) { - this.dialogService - .confirmDelete('unit', unit.__t) - .subscribe(res => { - if (res) { - this.showProgress.toggleLoadingGlobal(true); - this.unitService.deleteItem(unit) - .then(async () => { - this.snackBar.open('Unit deleted.', '', {duration: 3000}); - await this.reloadCourse(); - this.closeEditUnit(); - }) - .catch((error) => { - this.snackBar.open(error, '', {duration: 3000}); - }) - .then(() => { - this.showProgress.toggleLoadingGlobal(false); - }); + this.dialogService.confirmDelete('unit', unit.__t) + .subscribe(async res => { + if (!res) { + return; + } + + this.showProgress.toggleLoadingGlobal(true); + + try { + await this.unitService.deleteItem(unit); + this.closeEditUnit(); + await this.reloadCourse(); + this.snackBar.open('Unit deleted.'); + } catch (err) { + this.snackBar.open(err.message); // error response looks faulty } + + this.showProgress.toggleLoadingGlobal(false); }); } @@ -133,26 +132,25 @@ export class LectureEditComponent implements OnInit, OnDestroy { } deleteLecture(lecture: ILecture) { - this.dialogService - .confirmDelete('lecture', lecture.name) - .subscribe(res => { + this.dialogService.confirmDelete('lecture', lecture.name) + .subscribe(async res => { if (!res) { return; } + this.showProgress.toggleLoadingGlobal(true); - this.lectureService.deleteItem(lecture) - .then((val) => { - this.snackBar.open('Lecture deleted.', '', {duration: 3000}); - this.unsetUnitEdit(); - this.closeEditLecture(); - this.reloadCourse(); - }) - .catch((error) => { - this.snackBar.open(error, '', {duration: 3000}); - }) - .then(() => { - this.showProgress.toggleLoadingGlobal(false); - }); + + try { + await this.lectureService.deleteItem(lecture); + this.unsetUnitEdit(); + this.closeEditLecture(); + await this.reloadCourse(); + this.snackBar.open('Lecture deleted.'); + } catch (err) { + this.snackBar.open(err.message); // error response looks faulty + } + + this.showProgress.toggleLoadingGlobal(false); }); } @@ -215,7 +213,7 @@ export class LectureEditComponent implements OnInit, OnDestroy { this.saveFileService.save(unit.name, JSON.stringify(unitJSON, null, 2)); } catch (err) { - this.snackBar.open('Export unit failed ' + err.error.message, 'Dismiss'); + this.snackBar.open('Export unit failed ' + err.error.message); } } From 0529b24a54f4d2a54cb251fae6eef2c67068995c Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 8 May 2018 09:23:49 +0200 Subject: [PATCH 32/50] Show success snackbar after updateItem Migrate to SnackBarService --- .../admin-markdown-edit.component.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/webFrontend/src/app/shared/components/admin-markdown-edit/admin-markdown-edit.component.ts b/app/webFrontend/src/app/shared/components/admin-markdown-edit/admin-markdown-edit.component.ts index 435af207b..2838c6a01 100644 --- a/app/webFrontend/src/app/shared/components/admin-markdown-edit/admin-markdown-edit.component.ts +++ b/app/webFrontend/src/app/shared/components/admin-markdown-edit/admin-markdown-edit.component.ts @@ -3,7 +3,7 @@ import {IConfig} from '../../../../../../../shared/models/IConfig'; import {ConfigService} from '../../services/data.service'; import {MarkdownService} from '../../services/markdown.service'; import {errorCodes} from '../../../../../../../api/src/config/errorCodes'; -import {MatSnackBar} from '@angular/material'; +import {SnackBarService} from '../../services/snack-bar.service'; import {ActivatedRoute} from '@angular/router'; @Component({ @@ -20,8 +20,9 @@ export class AdminMarkdownEditComponent implements OnInit { constructor(private service: ConfigService, private mdService: MarkdownService, - private snackBar: MatSnackBar, - private route: ActivatedRoute) { } + private snackBar: SnackBarService, + private route: ActivatedRoute) { + } ngOnInit() { this.route.queryParams.subscribe(params => { @@ -39,15 +40,17 @@ export class AdminMarkdownEditComponent implements OnInit { this.text = ''; } } - onSave(markdown: string ) { + + async onSave(markdown: string) { try { - void this.service.updateItem({_id: this.type, data: markdown}); - this.snackBar.open( this.headingType + ' saved', '', {duration: 3000}); - } catch (error) { - this.snackBar.open(errorCodes.save.couldNotSaveImprint.text, '', {duration: 3000}); + await this.service.updateItem({_id: this.type, data: markdown}); + this.snackBar.open(this.headingType + ' saved'); + } catch (err) { + this.snackBar.open(errorCodes.save.couldNotSaveImprint.text); } void this.loadConfig(); } + onCancel() { void this.loadConfig(); } From d0b857fbf2f494fc556375b1c4d95fe58f19b235 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 15:00:17 +0200 Subject: [PATCH 33/50] Migrate to SnackBarService --- .../components/upload-dialog/upload-dialog.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/webFrontend/src/app/shared/components/upload-dialog/upload-dialog.component.ts b/app/webFrontend/src/app/shared/components/upload-dialog/upload-dialog.component.ts index a72964ac1..93c2e5312 100644 --- a/app/webFrontend/src/app/shared/components/upload-dialog/upload-dialog.component.ts +++ b/app/webFrontend/src/app/shared/components/upload-dialog/upload-dialog.component.ts @@ -2,7 +2,7 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {FileItem, FileUploader} from 'ng2-file-upload'; import {IUser} from '../../../../../../../shared/models/IUser'; import {MatDialogRef, MatSnackBar} from '@angular/material'; - +import {SnackBarService} from '../../services/snack-bar.service'; @Component({ selector: 'app-upload-dialog', @@ -23,7 +23,7 @@ export class UploadDialog implements OnInit { constructor( public dialogRef: MatDialogRef, - private snackBar: MatSnackBar) { } + private snackBar: SnackBarService) {} ngOnInit() { this.uploader = new FileUploader({ @@ -62,7 +62,7 @@ export class UploadDialog implements OnInit { nativeVideo.play(); }) .catch(error => { - this.snackBar.open('Couldn\'t start webcam', '', {duration: 3000}); + this.snackBar.open('Couldn\'t start webcam'); }); } } @@ -123,7 +123,7 @@ export class UploadDialog implements OnInit { try { this.uploader.uploadAll(); } catch (error) { - this.snackBar.open('An error occured during the Upload', '', { duration: 3000 }); + this.snackBar.open('An error occured during the Upload'); } } From 62c220ee5acdff0c6f7e7067372b4a9d1dedacef Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 15:24:42 +0200 Subject: [PATCH 34/50] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b828c3caf..6ca62f880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Reload user list after deleting an account. [#724](https://github.com/h-da/geli/pull/724) - Validate form before submit when creating a new course. [#724](https://github.com/h-da/geli/pull/724) - Validate :id for CourseController details route. [#724](https://github.com/h-da/geli/pull/724) +- Another MatSnackBar to SnackBarService migration. [#730](https://github.com/h-da/geli/pull/730) ## [[0.7.0](https://github.com/h-da/geli/releases/tag/v0.7.0)] - 2018-05-05 - SS 18 intermediate Release ### Added From a0d8f23bf98c816cdc0cae60994c61a36b97ba2f Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 17:00:22 +0200 Subject: [PATCH 35/50] Use ICourseDashboard instead ICourse --- app/webFrontend/src/app/models/NotificationSettings.ts | 6 +++--- shared/models/INotificationSettings.ts | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/webFrontend/src/app/models/NotificationSettings.ts b/app/webFrontend/src/app/models/NotificationSettings.ts index a3af951b2..c64b8045f 100644 --- a/app/webFrontend/src/app/models/NotificationSettings.ts +++ b/app/webFrontend/src/app/models/NotificationSettings.ts @@ -1,16 +1,16 @@ import {INotificationSettings} from '../../../../../shared/models/INotificationSettings'; -import {ICourse} from '../../../../../shared/models/ICourse'; import {IUser} from '../../../../../shared/models/IUser'; +import {ICourseDashboard} from '../../../../../shared/models/ICourseDashboard'; export class NotificationSettings implements INotificationSettings { _id: any; - course: ICourse; + course: ICourseDashboard; user: IUser; notificationType: string; emailNotification: boolean; - constructor(user: IUser, course: ICourse) { + constructor(user: IUser, course: ICourseDashboard) { this.course = course; this.user = user; } diff --git a/shared/models/INotificationSettings.ts b/shared/models/INotificationSettings.ts index cb18cb4b9..76fa9b137 100644 --- a/shared/models/INotificationSettings.ts +++ b/shared/models/INotificationSettings.ts @@ -1,5 +1,5 @@ -import {ICourse} from './ICourse'; import {IUser} from './IUser'; +import {ICourseDashboard} from './ICourseDashboard'; export const NOTIFICATION_TYPE_NONE = 'none'; export const NOTIFICATION_TYPE_CHANGES_WITH_RELATIONIONSHIP = 'relatedChanges'; @@ -12,9 +12,8 @@ export const NOTIFICATION_TYPES = [ export interface INotificationSettings { _id: any; - course: ICourse; + course: ICourseDashboard; user: IUser; notificationType: string; emailNotification: boolean; } - From f659b876d35202d0a2333077b4e70fd86cc66165 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 17:21:43 +0200 Subject: [PATCH 36/50] When getNotificationSettings called no new notification settings created Show SnackBar after all items are processed Use ICourseDashboard instead of ICourse Migrate to SnackBarService --- .../user-settings/user-settings.component.ts | 147 +++++++++--------- 1 file changed, 72 insertions(+), 75 deletions(-) diff --git a/app/webFrontend/src/app/user/user-settings/user-settings.component.ts b/app/webFrontend/src/app/user/user-settings/user-settings.component.ts index dca107698..f14c456b4 100644 --- a/app/webFrontend/src/app/user/user-settings/user-settings.component.ts +++ b/app/webFrontend/src/app/user/user-settings/user-settings.component.ts @@ -1,129 +1,126 @@ import {Component, OnInit} from '@angular/core'; import {UserService} from '../../shared/services/user.service'; import {CourseService, NotificationSettingsService} from '../../shared/services/data.service'; -import {ICourse} from '../../../../../../shared/models/ICourse'; -import {MatSnackBar, MatTableDataSource} from '@angular/material'; +import {MatTableDataSource} from '@angular/material'; +import {SnackBarService} from '../../shared/services/snack-bar.service'; import {SelectionModel} from '@angular/cdk/collections'; import { - INotificationSettings, NOTIFICATION_TYPE_ALL_CHANGES, + INotificationSettings, + NOTIFICATION_TYPE_ALL_CHANGES, NOTIFICATION_TYPE_NONE } from '../../../../../../shared/models/INotificationSettings'; import {NotificationSettings} from '../../models/NotificationSettings'; -import {isNullOrUndefined} from 'util'; -import {AfterContentInit} from '@angular/core/src/metadata/lifecycle_hooks'; +import {ICourseDashboard} from '../../../../../../shared/models/ICourseDashboard'; @Component({ selector: 'app-user-settings', templateUrl: './user-settings.component.html', styleUrls: ['./user-settings.component.scss'] }) -export class UserSettingsComponent implements OnInit, AfterContentInit { +export class UserSettingsComponent implements OnInit { - myCourses: ICourse[]; + myCourses: ICourseDashboard[]; displayedColumns = ['name', 'Notifications', 'email']; - dataSource: MatTableDataSource; - notificationSelection = new SelectionModel(true, []); - emailSelection = new SelectionModel(true, []); + dataSource: MatTableDataSource; + notificationSelection = new SelectionModel(true, []); + emailSelection = new SelectionModel(true, []); notificationSettings: INotificationSettings[]; - constructor(public userService: UserService, - public courseService: CourseService, - public notificationSettingsService: NotificationSettingsService, - public snackBar: MatSnackBar) { + constructor(private userService: UserService, + private courseService: CourseService, + private notificationSettingsService: NotificationSettingsService, + private snackBar: SnackBarService) { } - ngOnInit() { - this.getCourses(); - this.getNotificationSettings().then(() => { - this.setSelection(); - }); - } - async ngAfterContentInit() { + async ngOnInit() { + await this.getCourses(); await this.getNotificationSettings(); - this.setSelection(); + this.setSelection(); } - getCourses() { + async getCourses() { this.myCourses = []; - this.courseService.readItems().then(courses => { - courses.forEach((course: ICourse) => { - if (this.userService.isMemberOfCourse(course)) { - this.myCourses.push(course); - } - }); - this.dataSource = new MatTableDataSource(this.myCourses); - }); + try { + this.myCourses = (await this.courseService.readItems()) + .filter((course: ICourseDashboard) => course.userIsCourseMember); + this.dataSource = new MatTableDataSource(this.myCourses); + } catch (err) { + this.snackBar.open('Could not fetch courses'); + } } async getNotificationSettings() { - const settings = await this.notificationSettingsService.getNotificationSettingsPerUser(this.userService.user); - if (settings.length <= 0) { - await Promise.all(this.myCourses.map(async (course) => { - const newSetting = await this.notificationSettingsService.createItem({user: this.userService.user, course: course}); - settings.push(newSetting); - })); - } - this.notificationSettings = settings; + this.notificationSettings = await this.notificationSettingsService.getNotificationSettingsPerUser(this.userService.user);; } setSelection() { - if (this.notificationSettings) { - this.notificationSettings.forEach((setting: INotificationSettings) => { - const courseWithNotificationSettings = this.myCourses.find(x => x._id === setting.course._id); - if (courseWithNotificationSettings) { - if (setting.notificationType === NOTIFICATION_TYPE_ALL_CHANGES) { - this.notificationSelection.select(courseWithNotificationSettings); - } - if (setting.emailNotification) { - this.emailSelection.select(courseWithNotificationSettings); - } - } - }); + if (!this.notificationSettings) { + return; } + + this.notificationSettings.forEach((setting: INotificationSettings) => { + const course = this.myCourses.find(tmp => tmp._id === setting.course._id); + + if (course === undefined) { + return; + } + + if (setting.notificationType === NOTIFICATION_TYPE_ALL_CHANGES) { + this.notificationSelection.select(course); + } + + if (setting.emailNotification) { + this.emailSelection.select(course); + } + }); } - saveNotificationSettings() { - try { - this.myCourses.forEach(async course => { - let settings: INotificationSettings; - this.notificationSettings.forEach((x) => { - if (x.course._id === course._id) { - settings = x; - } - }); - if (settings === null || settings === undefined) { + async saveNotificationSettings() { + const errors = []; + + for (const course of this.myCourses) { + try { + let settings = this.notificationSettings.find(tmp => tmp.course._id === course._id); + + if (settings === undefined) { settings = await this.notificationSettingsService.createItem(new NotificationSettings(this.userService.user, course)); this.notificationSettings.push(settings); } + if (this.notificationSelection.isSelected(course)) { settings.notificationType = NOTIFICATION_TYPE_ALL_CHANGES; } else { settings.notificationType = NOTIFICATION_TYPE_NONE; } + if (this.emailSelection.isSelected(course)) { settings.emailNotification = true; } else { settings.emailNotification = false; } + await this.notificationSettingsService.updateItem(settings); - }); - this.snackBar.open('Notification settings updated successfully.', '', {duration: 3000}); - } catch (error) { - this.snackBar.open(error.message, 'Dismiss'); + } catch (err) { + if (errors.indexOf(err.error.message) === -1) { + errors.push(err.error.message); + } + } } - } - isAllSelected(selectionModel: SelectionModel) { - const numSelected = selectionModel.selected.length; - const numRows = this.dataSource.data.length; - return numSelected === numRows; + if (errors.length === 0) { + this.snackBar.open('Notification settings updated successfully.'); + } else { + this.snackBar.openLong('Failed to save notification settings: ' + errors.join(', ')); + } } - masterToggle(selectionModel: SelectionModel) { - this.isAllSelected(selectionModel) ? - selectionModel.clear() : - this.dataSource.data.forEach(row => selectionModel.select(row)); + isAllSelected(selectionModel: SelectionModel) { + return selectionModel.selected.length === this.dataSource.data.length; } - + masterToggle(selectionModel: SelectionModel) { + this.isAllSelected(selectionModel) + ? selectionModel.clear() + : this.dataSource.data.forEach(row => selectionModel.select(row)); + } } From dc8032f576b0b1d0c1ea50defc10788a4c4790e2 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 17:24:19 +0200 Subject: [PATCH 37/50] Add some wise word to changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b828c3caf..d0ee46ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Reload user list after deleting an account. [#724](https://github.com/h-da/geli/pull/724) - Validate form before submit when creating a new course. [#724](https://github.com/h-da/geli/pull/724) - Validate :id for CourseController details route. [#724](https://github.com/h-da/geli/pull/724) +- `getNotificationSettings` does not create new notification settings. [#731](https://github.com/h-da/geli/issues/731) + +### Fixed +- Fixed broken notification settings. [#731](https://github.com/h-da/geli/issues/731) ## [[0.7.0](https://github.com/h-da/geli/releases/tag/v0.7.0)] - 2018-05-05 - SS 18 intermediate Release ### Added From 856d48191f61d23f6dc16cc6c08e1b672c1c97f5 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 17:32:25 +0200 Subject: [PATCH 38/50] Remove defunct methods --- CHANGELOG.md | 1 + .../src/app/shared/services/user.service.ts | 19 ------------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0ee46ffe..e61cc1ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Validate form before submit when creating a new course. [#724](https://github.com/h-da/geli/pull/724) - Validate :id for CourseController details route. [#724](https://github.com/h-da/geli/pull/724) - `getNotificationSettings` does not create new notification settings. [#731](https://github.com/h-da/geli/issues/731) +- Remove `isCourseTeacherOrAdmin` and `isMemberOfCourse`from UserService. [#731](https://github.com/h-da/geli/issues/731) ### Fixed - Fixed broken notification settings. [#731](https://github.com/h-da/geli/issues/731) diff --git a/app/webFrontend/src/app/shared/services/user.service.ts b/app/webFrontend/src/app/shared/services/user.service.ts index 326305674..a4ab41cbe 100644 --- a/app/webFrontend/src/app/shared/services/user.service.ts +++ b/app/webFrontend/src/app/shared/services/user.service.ts @@ -44,23 +44,4 @@ export class UserService { isLoggedInUser(user: IUser): boolean { return this.user._id === user._id; } - - isCourseTeacherOrAdmin(course: ICourse) { - if (this.isStudent()) { - return false; - } - if (this.isAdmin()) { - return true; - } - - if (course.courseAdmin._id === this.user._id) { - return true; - } - - return ( course.teachers.filter(teacher => teacher._id === this.user._id).length); - } - - isMemberOfCourse(course: ICourse) { - return course.students.filter(obj => obj._id === this.user._id).length > 0; - } } From 56ec38d670987d3e9a18b0e1709615e1af3696ee Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Wed, 9 May 2018 17:56:51 +0200 Subject: [PATCH 39/50] Fix CS --- .../src/app/user/user-settings/user-settings.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/webFrontend/src/app/user/user-settings/user-settings.component.ts b/app/webFrontend/src/app/user/user-settings/user-settings.component.ts index f14c456b4..1ad864a84 100644 --- a/app/webFrontend/src/app/user/user-settings/user-settings.component.ts +++ b/app/webFrontend/src/app/user/user-settings/user-settings.component.ts @@ -50,7 +50,7 @@ export class UserSettingsComponent implements OnInit { } async getNotificationSettings() { - this.notificationSettings = await this.notificationSettingsService.getNotificationSettingsPerUser(this.userService.user);; + this.notificationSettings = await this.notificationSettingsService.getNotificationSettingsPerUser(this.userService.user); } setSelection() { From 2adbde850e09c5d7775d027a49e573095d801bd6 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Sun, 13 May 2018 16:02:07 +0200 Subject: [PATCH 40/50] Fix apidoc syntax --- api/src/controllers/CourseController.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/controllers/CourseController.ts b/api/src/controllers/CourseController.ts index 02fe0477d..de420f94d 100644 --- a/api/src/controllers/CourseController.ts +++ b/api/src/controllers/CourseController.ts @@ -482,7 +482,7 @@ export class CourseController { * @apiParam {Object} data Data (with access key). * @apiParam {IUser} currentUser Currently logged in user. * - * @apiSuccess {{}} result Empty object. + * @apiSuccess {Object} result Empty object. * * @apiSuccessExample {json} Success-Response: * {} @@ -533,7 +533,7 @@ export class CourseController { * @apiParam {Object} data Body. * @apiParam {IUser} currentUser Currently logged in user. * - * @apiSuccess {{}} result Empty object. + * @apiSuccess {Object} result Empty object. * * @apiSuccessExample {json} Success-Response: * {} @@ -653,7 +653,7 @@ export class CourseController { * @apiParam {String} id Course ID. * @apiParam {IUser} currentUser Currently logged in user. * - * @apiSuccess {{}} result Empty object. + * @apiSuccess {Object} result Empty object. * * @apiSuccessExample {json} Success-Response: * {} From 08120e47740da262e51824cfef23e7aad320b7f9 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Sun, 13 May 2018 17:41:26 +0200 Subject: [PATCH 41/50] Fix apidoc --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b828c3caf..0bec51f24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed wording of progress display on profile page. [#715](https://github.com/h-da/geli/issues/715) - Fixed form validator in create task [#579](https://github.com/h-da/geli/issues/579) - Fixed Mongoose pre hook usage [#680](https://github.com/h-da/geli/issues/680) [#677](https://github.com/h-da/geli/issues/677) +- Fixed broken Apidoc [#737](https://github.com/h-da/geli/issues/737) ### Security - Fixed numerous severe user related security issues. [#691](https://github.com/h-da/geli/issues/691) [#709](https://github.com/h-da/geli/pull/709) From 20ad7517dfafa68661aa5cd881cf94042ab81858 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Sun, 13 May 2018 17:43:54 +0200 Subject: [PATCH 42/50] Add changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bec51f24..bb6a27c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Validate form before submit when creating a new course. [#724](https://github.com/h-da/geli/pull/724) - Validate :id for CourseController details route. [#724](https://github.com/h-da/geli/pull/724) +### Fixed +- Fixed broken Apidoc [#737](https://github.com/h-da/geli/issues/737) + ## [[0.7.0](https://github.com/h-da/geli/releases/tag/v0.7.0)] - 2018-05-05 - SS 18 intermediate Release ### Added - Added the possibility to sort all courses alphabetically. [#567](https://github.com/h-da/geli/issues/567) @@ -69,7 +72,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed wording of progress display on profile page. [#715](https://github.com/h-da/geli/issues/715) - Fixed form validator in create task [#579](https://github.com/h-da/geli/issues/579) - Fixed Mongoose pre hook usage [#680](https://github.com/h-da/geli/issues/680) [#677](https://github.com/h-da/geli/issues/677) -- Fixed broken Apidoc [#737](https://github.com/h-da/geli/issues/737) ### Security - Fixed numerous severe user related security issues. [#691](https://github.com/h-da/geli/issues/691) [#709](https://github.com/h-da/geli/pull/709) From 609347302f93ca4aeaa9b6ba9179b5986f79b529 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Sun, 13 May 2018 23:12:47 +0200 Subject: [PATCH 43/50] Build apidoc on travis (fail build when syntax error) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ff4b3cc9d..e935aa3c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,6 +72,7 @@ script: - cd api - sed -i 's/$TRAVIS_COMMIT/'$TRAVIS_COMMIT'/' src/server.ts - npm run test # run backend test + - npm run apidoc # generate apidoc - cd .. # run frontend-tests and return back to root for following steps - cd app/webFrontend From 6c00aef1de2d27b1571c27e4c5d8be456327046c Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Mon, 14 May 2018 09:56:39 +0200 Subject: [PATCH 44/50] Added api-doc check --- .travis.yml | 1 + .travis/apidoc_check.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .travis/apidoc_check.sh diff --git a/.travis.yml b/.travis.yml index ff4b3cc9d..675869d03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,6 +72,7 @@ script: - cd api - sed -i 's/$TRAVIS_COMMIT/'$TRAVIS_COMMIT'/' src/server.ts - npm run test # run backend test + - ./travis/apidoc_check.sh - cd .. # run frontend-tests and return back to root for following steps - cd app/webFrontend diff --git a/.travis/apidoc_check.sh b/.travis/apidoc_check.sh new file mode 100644 index 000000000..798c3333e --- /dev/null +++ b/.travis/apidoc_check.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Path to this file +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# Path the script was called from +IPWD="$(pwd)" +# Import shared vars +. ${DIR}/_shared-vars.sh + + +echo +echo "+++ Check API-Doc +++" +echo + +if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ]; then + echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" + cd api + npm run api-doc + fi +elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then + echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" + cd api + npm run api-doc + fi +else + echo -e "${YELLOW}+ WARNING: No Pull Request agiainst Develop or Master -> skipping automate api-doc check${NC}"; +fi From ad3bcc60eca1c4e56b7b5fd72d1d96e0c830f0b6 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Mon, 14 May 2018 10:08:01 +0200 Subject: [PATCH 45/50] Fixed travis script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 74cc9f9b8..367c23eee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,8 +72,8 @@ script: - cd api - sed -i 's/$TRAVIS_COMMIT/'$TRAVIS_COMMIT'/' src/server.ts - npm run test # run backend test - - .travis/apidoc_check.sh - cd .. + - .travis/apidoc_check.sh # run frontend-tests and return back to root for following steps - cd app/webFrontend - npm run lint # run linter From 2a05d0e23e643d49617dce0c2e08dd066218f546 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 14 May 2018 10:28:55 +0200 Subject: [PATCH 46/50] Add chmod +x for new script --- .travis/apidoc_check.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .travis/apidoc_check.sh diff --git a/.travis/apidoc_check.sh b/.travis/apidoc_check.sh old mode 100644 new mode 100755 From 950af26bd40f7bb5905384bae434e499c678248d Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Mon, 14 May 2018 10:37:32 +0200 Subject: [PATCH 47/50] Drop fi before elif/else --- .travis/apidoc_check.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis/apidoc_check.sh b/.travis/apidoc_check.sh index 781f0908a..deb41cc75 100755 --- a/.travis/apidoc_check.sh +++ b/.travis/apidoc_check.sh @@ -16,12 +16,10 @@ if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ]; t echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" cd api npm run apidoc - fi elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" cd api npm run apidoc - fi else echo -e "${YELLOW}+ WARNING: No Pull Request agiainst Develop or Master -> skipping automate api doc check${NC}"; fi From ff9ade3ad000cd9a4ee3102d84135bea2abf2b37 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Mon, 14 May 2018 10:58:41 +0200 Subject: [PATCH 48/50] Fixed script permission --- .travis/apidoc_check.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .travis/apidoc_check.sh diff --git a/.travis/apidoc_check.sh b/.travis/apidoc_check.sh old mode 100644 new mode 100755 From 499852e431a1f141c6e3f3e74c75f9cc87ec7b93 Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Mon, 14 May 2018 11:09:37 +0200 Subject: [PATCH 49/50] Changed name and merged if --- .travis.yml | 2 +- .travis/{apidoc_check.sh => apidoc-check.sh} | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) rename .travis/{apidoc_check.sh => apidoc-check.sh} (70%) diff --git a/.travis.yml b/.travis.yml index 367c23eee..ce6bc5ab3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,7 +73,7 @@ script: - sed -i 's/$TRAVIS_COMMIT/'$TRAVIS_COMMIT'/' src/server.ts - npm run test # run backend test - cd .. - - .travis/apidoc_check.sh + - .travis/apidoc-check.sh # run frontend-tests and return back to root for following steps - cd app/webFrontend - npm run lint # run linter diff --git a/.travis/apidoc_check.sh b/.travis/apidoc-check.sh similarity index 70% rename from .travis/apidoc_check.sh rename to .travis/apidoc-check.sh index deb41cc75..c75b902fc 100755 --- a/.travis/apidoc_check.sh +++ b/.travis/apidoc-check.sh @@ -12,11 +12,7 @@ echo echo "+++ Check API-Doc +++" echo -if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ]; then - echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" - cd api - npm run apidoc -elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then +if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ] || [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" cd api npm run apidoc From 2c77b012430a923163c3cc59c9b3bdbb3ab41c7a Mon Sep 17 00:00:00 2001 From: PatrickSkowronek Date: Mon, 14 May 2018 11:55:25 +0200 Subject: [PATCH 50/50] Fixed the if --- .travis/apidoc-check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/apidoc-check.sh b/.travis/apidoc-check.sh index c75b902fc..e4b7b551c 100755 --- a/.travis/apidoc-check.sh +++ b/.travis/apidoc-check.sh @@ -12,7 +12,7 @@ echo echo "+++ Check API-Doc +++" echo -if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ] || [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then +if ([ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "develop" ]) || ([ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]); then echo "+ detected pull request from ($TRAVIS_PULL_REQUEST_BRANCH) to $TRAVIS_BRANCH" cd api npm run apidoc