From af51fb8a7e873a832beb61a561268da23a96f703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 1 Jun 2018 15:54:06 +0200 Subject: [PATCH] MOBILE-2354 workshop: View submission page --- .../mod/workshop/components/index/index.ts | 4 - .../components/submission/submission.ts | 1 - .../pages/edit-submission/edit-submission.ts | 10 +- .../workshop/pages/submission/submission.html | 106 ++++ .../pages/submission/submission.module.ts | 35 ++ .../workshop/pages/submission/submission.ts | 514 ++++++++++++++++++ src/addon/mod/workshop/providers/workshop.ts | 2 + src/addon/mod/workshop/workshop.scss | 13 + 8 files changed, 677 insertions(+), 8 deletions(-) create mode 100644 src/addon/mod/workshop/pages/submission/submission.html create mode 100644 src/addon/mod/workshop/pages/submission/submission.module.ts create mode 100644 src/addon/mod/workshop/pages/submission/submission.ts create mode 100644 src/addon/mod/workshop/workshop.scss diff --git a/src/addon/mod/workshop/components/index/index.ts b/src/addon/mod/workshop/components/index/index.ts index dbe91b367..ed7555163 100644 --- a/src/addon/mod/workshop/components/index/index.ts +++ b/src/addon/mod/workshop/components/index/index.ts @@ -303,10 +303,6 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity submission: this.submission }; - if (this.submission.id) { - params['submissionId'] = this.submission.id; - } - this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params); } } else if (task.link) { diff --git a/src/addon/mod/workshop/components/submission/submission.ts b/src/addon/mod/workshop/components/submission/submission.ts index eb6220555..b704faac7 100644 --- a/src/addon/mod/workshop/components/submission/submission.ts +++ b/src/addon/mod/workshop/components/submission/submission.ts @@ -123,7 +123,6 @@ export class AddonModWorkshopSubmissionComponent implements OnInit { profile: this.profile, submission: this.submission, assessment: this.assessment, - submissionId: this.submission.id }; this.navCtrl.push('AddonModWorkshopSubmissionPage', params); diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts index 213aebe00..fd83fa272 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts @@ -42,7 +42,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { access: any; submission: any; - title: string; loaded = false; component = AddonModWorkshopProvider.COMPONENT; componentId: number; @@ -56,6 +55,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { protected forceLeave = false; protected siteId: string; protected workshop: any; + protected isDestroyed = false; constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected fileUploaderProvider: CoreFileUploaderProvider, protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider, @@ -68,7 +68,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { this.access = navParams.get('access'); this.submission = navParams.get('submission') || {}; - this.title = this.module.name; this.workshopId = this.module.instance; this.componentId = this.module.id; this.userId = sitesProvider.getCurrentSiteUserId(); @@ -83,6 +82,11 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { * Component being initialized. */ ngOnInit(): void { + if (!this.isDestroyed) { + // Block the workshop. + this.syncProvider.blockOperation(this.component, this.workshopId); + } + this.fetchSubmissionData(); } @@ -182,7 +186,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { }); }); }).then(() => { - // Create the form group and its controls. this.editForm.controls['title'].setValue(this.submission.title); this.editForm.controls['content'].setValue(this.submission.content); @@ -390,6 +393,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { * Component being destroyed. */ ngOnDestroy(): void { + this.isDestroyed = true; this.syncProvider.unblockOperation(this.component, this.workshopId); } } diff --git a/src/addon/mod/workshop/pages/submission/submission.html b/src/addon/mod/workshop/pages/submission/submission.html new file mode 100644 index 000000000..d77cd6d4a --- /dev/null +++ b/src/addon/mod/workshop/pages/submission/submission.html @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateByProfile.fullname} }}

+ +
+
+ + + +

{{ 'addon.mod_workshop.yourassessment' | translate }}

+
+ +
+ + + +

{{ 'addon.mod_workshop.receivedgrades' | translate }}

+
+ +
+ + + +

{{ 'addon.mod_workshop.givengrades' | translate }}

+
+ +
+ +
+ +

{{ 'addon.mod_workshop.feedbackauthor' | translate }}

+
+ + {{ 'addon.mod_workshop.publishsubmission' | translate }} + +

{{ 'addon.mod_workshop.publishsubmission_help' | translate }}

+
+ + +

{{ 'addon.mod_workshop.gradecalculated' | translate }}

+

{{ submission.submissiongrade }}

+
+ + {{ 'addon.mod_workshop.gradeover' | translate }} + + {{grade.label}} + + + + {{ 'addon.mod_workshop.feedbackauthor' | translate }} + + +
+ + + + + + + + +

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateGradingByProfile.fullname} }}

+ +
+
+
+
diff --git a/src/addon/mod/workshop/pages/submission/submission.module.ts b/src/addon/mod/workshop/pages/submission/submission.module.ts new file mode 100644 index 000000000..0c7c7fe8d --- /dev/null +++ b/src/addon/mod/workshop/pages/submission/submission.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { AddonModWorkshopComponentsModule } from '../../components/components.module'; +import { AddonModWorkshopSubmissionPage } from './submission'; + +@NgModule({ + declarations: [ + AddonModWorkshopSubmissionPage, + ], + imports: [ + CoreDirectivesModule, + CoreComponentsModule, + AddonModWorkshopComponentsModule, + IonicPageModule.forChild(AddonModWorkshopSubmissionPage), + TranslateModule.forChild() + ], +}) +export class AddonModWorkshopSubmissionPageModule {} diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts new file mode 100644 index 000000000..4a6ba7235 --- /dev/null +++ b/src/addon/mod/workshop/pages/submission/submission.ts @@ -0,0 +1,514 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, OnInit, OnDestroy, Optional } from '@angular/core'; +import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreSyncProvider } from '@providers/sync'; +import { CoreFileSessionProvider } from '@providers/file-session'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreUserProvider } from '@core/user/providers/user'; +import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; +import { AddonModWorkshopProvider } from '../../providers/workshop'; +import { AddonModWorkshopHelperProvider } from '../../providers/helper'; +import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; +import { AddonModWorkshopSyncProvider } from '../../providers/sync'; + +/** + * Page that displays a workshop submission. + */ +@IonicPage({ segment: 'addon-mod-workshop-submission' }) +@Component({ + selector: 'page-addon-mod-workshop-submission', + templateUrl: 'submission.html', +}) +export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { + + module: any; + workshop: any; + access: any; + assessment: any; + submissionInfo: any; + submission: any; + + courseId: number; + profile: any; + + title: string; + loaded = false; + ownAssessment = false; + strategy: any; + assessmentId: number; + assessmentUserId: number; + evaluate: any; + canAddFeedback = false; + canEdit = false; + canDelete = false; + evaluationGrades: any; + evaluateGradingByProfile: any; + evaluateByProfile: any; + feedbackForm: FormGroup; // The form group. + + protected submissionId: number; + protected workshopId: number; + protected currentUserId: number; + protected userId: number; + protected siteId: string; + protected originalEvaluation: any = {}; + protected hasOffline = false; + protected component = AddonModWorkshopProvider.COMPONENT; + protected forceLeave = false; + protected obsAssessmentSaved: any; + protected syncObserver: any; + protected isDestroyed = false; + + constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected fileUploaderProvider: CoreFileUploaderProvider, + protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider, + protected workshopHelper: AddonModWorkshopHelperProvider, protected navCtrl: NavController, + protected fileSessionprovider: CoreFileSessionProvider, protected syncProvider: CoreSyncProvider, + protected textUtils: CoreTextUtilsProvider, protected domUtils: CoreDomUtilsProvider, protected fb: FormBuilder, + protected translate: TranslateService, protected eventsProvider: CoreEventsProvider, + protected courseProvider: CoreCourseProvider, @Optional() protected content: Content, + protected gradesHelper: CoreGradesHelperProvider, protected userProvider: CoreUserProvider) { + this.module = navParams.get('module'); + this.workshop = navParams.get('workshop'); + this.access = navParams.get('access'); + this.courseId = navParams.get('courseId'); + this.profile = navParams.get('profile'); + this.submissionInfo = navParams.get('submission'); + this.assessment = navParams.get('assessment') || null; + + this.title = this.module.name; + this.workshopId = this.module.instance; + this.currentUserId = sitesProvider.getCurrentSiteUserId(); + this.siteId = sitesProvider.getCurrentSiteId(); + this.submissionId = this.submissionInfo.submissionid || this.submissionInfo.id; + this.userId = this.submissionInfo.userid || null; + this.strategy = (this.assessment && this.assessment.strategy) || (this.workshop && this.workshop.strategy); + this.assessmentId = this.assessment && (this.assessment.assessmentid || this.assessment.id); + this.assessmentUserId = this.assessment && (this.assessment.reviewerid || this.assessment.userid); + + this.feedbackForm = new FormGroup({}); + this.feedbackForm.addControl('published', this.fb.control('')); + this.feedbackForm.addControl('grade', this.fb.control('')); + this.feedbackForm.addControl('text', this.fb.control('')); + + this.obsAssessmentSaved = this.eventsProvider.on(AddonModWorkshopProvider.ASSESSMENT_SAVED, (data) => { + this.eventReceived(data); + }, this.siteId); + + // Refresh workshop on sync. + this.syncObserver = this.eventsProvider.on(AddonModWorkshopSyncProvider.AUTO_SYNCED, (data) => { + // Update just when all database is synced. + this.eventReceived(data); + }, this.siteId); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.fetchSubmissionData().then(() => { + this.workshopProvider.logViewSubmission(this.submissionId).then(() => { + this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus); + }); + }); + } + + /** + * Check if we can leave the page or not. + * + * @return {boolean|Promise} Resolved if we can leave it, rejected if not. + */ + ionViewCanLeave(): boolean | Promise { + if (this.forceLeave || !this.canAddFeedback) { + return true; + } + + if (!this.hasEvaluationChanged()) { + return Promise.resolve(); + } + + // Show confirmation if some data has been modified. + return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); + } + + /** + * Goto edit submission page. + */ + editSubmission(): void { + const params = { + module: module, + access: this.access, + courseid: this.courseId, + submission: this.submission + }; + + this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params); + } + + /** + * Function called when we receive an event of submission changes. + * + * @param {any} data Event data received. + */ + protected eventReceived(data: any): void { + if (this.workshopId === data.workshopid) { + this.content && this.content.scrollToTop(); + + this.loaded = false; + this.refreshAllData(); + } + } + + /** + * Fetch the submission data. + * + * @return {Promise} Resolved when done. + */ + protected fetchSubmissionData(): Promise { + return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId).then((submissionData) => { + const promises = []; + + this.submission = submissionData; + this.submission.submissiongrade = this.submissionInfo && this.submissionInfo.submissiongrade; + this.submission.gradinggrade = this.submissionInfo && this.submissionInfo.gradinggrade; + this.submission.submissiongradeover = this.submissionInfo && this.submissionInfo.submissiongradeover; + this.userId = submissionData.authorid || this.userId; + this.canEdit = this.currentUserId == this.userId && this.access.cansubmit && this.access.modifyingsubmissionallowed; + this.canDelete = this.access.candeletesubmissions; + this.canAddFeedback = !this.assessmentId && this.workshop.phase > AddonModWorkshopProvider.PHASE_ASSESSMENT && + this.workshop.phase < AddonModWorkshopProvider.PHASE_CLOSED && this.access.canoverridegrades; + this.ownAssessment = false; + + if (this.access.canviewallassessments) { + // Get new data, different that came from stateParams. + promises.push(this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId) + .then((subAssessments) => { + // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. + if (this.canDelete) { + this.canDelete = !subAssessments.length; + } + + this.submissionInfo.reviewedby = subAssessments; + + this.submissionInfo.reviewedby.forEach((assessment) => { + assessment.userid = assessment.reviewerid; + assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); + + if (this.currentUserId == assessment.userid) { + this.ownAssessment = assessment; + assessment.ownAssessment = true; + } + }); + })); + } else if (this.currentUserId == this.userId && this.assessmentId) { + // Get new data, different that came from stateParams. + promises.push(this.workshopProvider.getAssessment(this.workshopId, this.assessmentId).then((assessment) => { + // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. + if (this.canDelete) { + this.canDelete = !assessment; + } + + assessment.userid = assessment.reviewerid; + assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); + + if (this.currentUserId == assessment.userid) { + this.ownAssessment = assessment; + assessment.ownAssessment = true; + } + + this.submissionInfo.reviewedby = [assessment]; + })); + } + + if (this.canAddFeedback || this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { + this.evaluate = { + published: submissionData.published, + text: submissionData.feedbackauthor || '' + }; + } + + if (this.canAddFeedback) { + + if (!this.isDestroyed) { + // Block the workshop. + this.syncProvider.blockOperation(this.component, this.workshopId); + } + + const defaultGrade = this.translate.instant('addon.mod_workshop.notoverridden'); + + promises.push(this.gradesHelper.makeGradesMenu(this.workshop.grade, this.workshopId, defaultGrade, -1) + .then((grades) => { + this.evaluationGrades = grades; + + this.evaluate.grade = { + label: this.gradesHelper.getGradeLabelFromValue(grades, this.submissionInfo.submissiongradeover) || + defaultGrade, + value: this.submissionInfo.submissiongradeover || -1 + }; + + return this.workshopOffline.getEvaluateSubmission(this.workshopId, this.submissionId) + .then((offlineSubmission) => { + this.hasOffline = true; + this.evaluate.published = offlineSubmission.published; + this.evaluate.text = offlineSubmission.feedbacktext; + this.evaluate.grade = { + label: this.gradesHelper.getGradeLabelFromValue(grades, offlineSubmission.gradeover) || defaultGrade, + value: offlineSubmission.gradeover || -1 + }; + }).catch(() => { + this.hasOffline = false; + // Ignore errors. + }).finally(() => { + this.originalEvaluation.published = this.evaluate.published; + this.originalEvaluation.text = this.evaluate.text; + this.originalEvaluation.grade = this.evaluate.grade.value; + + this.feedbackForm.controls['published'].setValue(this.evaluate.published); + this.feedbackForm.controls['grade'].setValue(this.evaluate.grade.value); + this.feedbackForm.controls['text'].setValue(this.evaluate.text); + }); + })); + } else if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && submissionData.gradeoverby) { + promises.push(this.userProvider.getProfile(submissionData.gradeoverby, this.courseId, true).then((profile) => { + this.evaluateByProfile = profile; + })); + } + + if (this.assessmentId && !this.access.assessingallowed && this.assessment.feedbackreviewer && + this.assessment.gradinggradeoverby) { + promises.push(this.userProvider.getProfile(this.assessment.gradinggradeoverby, this.courseId, true) + .then((profile) => { + this.evaluateGradingByProfile = profile; + })); + } + + return Promise.all(promises); + }).then(() => { + return this.workshopOffline.getSubmissions(this.workshopId).then((submissionsActions) => { + const actions = this.workshopHelper.filterSubmissionActions(submissionsActions, this.submissionId); + + return this.workshopHelper.applyOfflineData(this.submission, actions).then((submission) => { + this.submission = submission; + }); + }); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); + }).finally(() => { + this.loaded = true; + }); + } + + /** + * Force leaving the page, without checking for changes. + */ + protected forceLeavePage(): void { + this.forceLeave = true; + this.navCtrl.pop(); + } + + /** + * Check if data has changed. + * + * @return {boolean} True if changed, false otherwise. + */ + protected hasEvaluationChanged(): boolean { + if (!this.loaded || !this.access.canoverridegrades) { + return false; + } + + const inputData = this.feedbackForm.value; + + if (this.originalEvaluation.published != inputData.published) { + return true; + } + + if (this.originalEvaluation.text != inputData.text) { + return true; + } + + if (this.originalEvaluation.grade != inputData.grade) { + return true; + } + + return false; + } + + /** + * Convenience function to refresh all the data. + * + * @return {Promise} Resolved when done. + */ + protected refreshAllData(): Promise { + const promises = []; + + promises.push(this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId)); + promises.push(this.workshopProvider.invalidateSubmissionsData(this.workshopId)); + promises.push(this.workshopProvider.invalidateSubmissionAssesmentsData(this.workshopId, this.submissionId)); + + if (this.assessmentId) { + promises.push(this.workshopProvider.invalidateAssessmentFormData(this.workshopId, this.assessmentId)); + promises.push(this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId)); + } + + return Promise.all(promises).finally(() => { + this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, this.siteId); + + return this.fetchSubmissionData(); + }); + } + + /** + * Pull to refresh. + * + * @param {any} refresher Refresher. + */ + refreshSubmission(refresher: any): void { + if (this.loaded) { + this.refreshAllData().finally(() => { + refresher.complete(); + }); + } + } + + /** + * Save the assessment. + */ + saveAssessment(): void { + // Call trigger to save. + this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_SAVE, undefined, this.siteId); + } + + /** + * Save the submission evaluation. + */ + saveEvaluation(): void { + // Check if data has changed. + if (this.hasEvaluationChanged()) { + this.sendEvaluation().then(() => { + this.forceLeavePage(); + }); + } else { + // Nothing to save, just go back. + this.forceLeavePage(); + } + } + + /** + * Sends the evaluation to be saved on the server. + * + * @return {Promise} Resolved when done. + */ + protected sendEvaluation(): Promise { + const modal = this.domUtils.showModalLoading('core.sending', true); + + // Check if rich text editor is enabled or not. + return this.domUtils.isRichTextEditorEnabled().then((rteEnabled) => { + const inputData = this.feedbackForm.value; + + inputData.grade = inputData.grade >= 0 ? inputData.grade : ''; + if (!rteEnabled) { + // Rich text editor not enabled, add some HTML to the message if needed. + inputData.text = this.textUtils.formatHtmlLines(inputData.text); + } + + // Try to send it to server. + return this.workshopProvider.evaluateSubmission(this.workshopId, this.submissionId, this.courseId, inputData.text, + inputData.published, inputData.grade); + }).then(() => { + const data = { + workshopId: this.workshopId, + cmId: this.module.cmid, + submissionId: this.submissionId + }; + + return this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId).finally(() => { + this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); + }); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'Cannot save submission evaluation'); + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * Perform the submission delete action. + */ + deleteSubmission(): void { + this.domUtils.showConfirm(this.translate.instant('addon.mod_workshop.submissiondeleteconfirm')).then(() => { + const modal = this.domUtils.showModalLoading('core.deleting', true); + let success = false; + this.workshopProvider.deleteSubmission(this.workshopId, this.submissionId, this.courseId).then(() => { + success = true; + + return this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'Cannot delete submission'); + }).finally(() => { + modal.dismiss(); + if (success) { + const data = { + workshopId: this.workshopId, + cmId: this.module.cmid, + submissionId: this.submissionId + }; + + this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); + + this.forceLeavePage(); + } + }); + }); + } + + /** + * Undo the submission delete action. + * + * @return {Promise} Resolved when done. + */ + undoDeleteSubmission(): Promise { + return this.workshopOffline.deleteSubmissionAction(this.workshopId, this.submissionId, 'delete').finally(() => { + + const data = { + workshopId: this.workshopId, + cmId: this.module.cmid, + submissionId: this.submissionId + }; + + this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); + + return this.refreshAllData(); + }); + } + + /** + * Component being destroyed. + */ + ngOnDestroy(): void { + this.isDestroyed = true; + + this.syncObserver && this.syncObserver.off(); + this.obsAssessmentSaved && this.obsAssessmentSaved.off(); + // Restore original back functions. + this.syncProvider.unblockOperation(this.component, this.workshopId); + } +} diff --git a/src/addon/mod/workshop/providers/workshop.ts b/src/addon/mod/workshop/providers/workshop.ts index 27660ef6f..ca1889686 100644 --- a/src/addon/mod/workshop/providers/workshop.ts +++ b/src/addon/mod/workshop/providers/workshop.ts @@ -36,7 +36,9 @@ export class AddonModWorkshopProvider { static EXAMPLES_BEFORE_ASSESSMENT: 2; static SUBMISSION_CHANGED = 'addon_mod_workshop_submission_changed'; + static ASSESSMENT_SAVE = 'addon_mod_workshop_assessment_save'; static ASSESSMENT_SAVED = 'addon_mod_workshop_assessment_saved'; + static ASSESSMENT_INVALIDATED = 'addon_mod_workshop_assessment_invalidated'; protected ROOT_CACHE_KEY = 'mmaModWorkshop:'; diff --git a/src/addon/mod/workshop/workshop.scss b/src/addon/mod/workshop/workshop.scss new file mode 100644 index 000000000..3f79267f3 --- /dev/null +++ b/src/addon/mod/workshop/workshop.scss @@ -0,0 +1,13 @@ +addon-mod-workshop-submission, +addon-mod-workshop-assessment, // TODO, change names +addon-mod-workshop-assessment { + + p.addon-overriden-grade { + color: color($colors, success); + } + + p.addon-has-overriden-grade { + color: color($colors, danger); + text-decoration: line-through; + } +}