MOBILE-2354 workshop: View submission page
parent
a420d4eb8a
commit
af51fb8a7e
|
@ -303,10 +303,6 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity
|
||||||
submission: this.submission
|
submission: this.submission
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.submission.id) {
|
|
||||||
params['submissionId'] = this.submission.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params);
|
this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params);
|
||||||
}
|
}
|
||||||
} else if (task.link) {
|
} else if (task.link) {
|
||||||
|
|
|
@ -123,7 +123,6 @@ export class AddonModWorkshopSubmissionComponent implements OnInit {
|
||||||
profile: this.profile,
|
profile: this.profile,
|
||||||
submission: this.submission,
|
submission: this.submission,
|
||||||
assessment: this.assessment,
|
assessment: this.assessment,
|
||||||
submissionId: this.submission.id
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.navCtrl.push('AddonModWorkshopSubmissionPage', params);
|
this.navCtrl.push('AddonModWorkshopSubmissionPage', params);
|
||||||
|
|
|
@ -42,7 +42,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
access: any;
|
access: any;
|
||||||
submission: any;
|
submission: any;
|
||||||
|
|
||||||
title: string;
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
component = AddonModWorkshopProvider.COMPONENT;
|
component = AddonModWorkshopProvider.COMPONENT;
|
||||||
componentId: number;
|
componentId: number;
|
||||||
|
@ -56,6 +55,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
protected forceLeave = false;
|
protected forceLeave = false;
|
||||||
protected siteId: string;
|
protected siteId: string;
|
||||||
protected workshop: any;
|
protected workshop: any;
|
||||||
|
protected isDestroyed = false;
|
||||||
|
|
||||||
constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected fileUploaderProvider: CoreFileUploaderProvider,
|
constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected fileUploaderProvider: CoreFileUploaderProvider,
|
||||||
protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider,
|
protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider,
|
||||||
|
@ -68,7 +68,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
this.access = navParams.get('access');
|
this.access = navParams.get('access');
|
||||||
this.submission = navParams.get('submission') || {};
|
this.submission = navParams.get('submission') || {};
|
||||||
|
|
||||||
this.title = this.module.name;
|
|
||||||
this.workshopId = this.module.instance;
|
this.workshopId = this.module.instance;
|
||||||
this.componentId = this.module.id;
|
this.componentId = this.module.id;
|
||||||
this.userId = sitesProvider.getCurrentSiteUserId();
|
this.userId = sitesProvider.getCurrentSiteUserId();
|
||||||
|
@ -83,6 +82,11 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (!this.isDestroyed) {
|
||||||
|
// Block the workshop.
|
||||||
|
this.syncProvider.blockOperation(this.component, this.workshopId);
|
||||||
|
}
|
||||||
|
|
||||||
this.fetchSubmissionData();
|
this.fetchSubmissionData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +186,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Create the form group and its controls.
|
|
||||||
this.editForm.controls['title'].setValue(this.submission.title);
|
this.editForm.controls['title'].setValue(this.submission.title);
|
||||||
this.editForm.controls['content'].setValue(this.submission.content);
|
this.editForm.controls['content'].setValue(this.submission.content);
|
||||||
|
|
||||||
|
@ -390,6 +393,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
|
||||||
* Component being destroyed.
|
* Component being destroyed.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
this.isDestroyed = true;
|
||||||
this.syncProvider.unblockOperation(this.component, this.workshopId);
|
this.syncProvider.unblockOperation(this.component, this.workshopId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
||||||
|
<ion-buttons end [hidden]="!loaded">
|
||||||
|
<button *ngIf="assessmentId" ion-button clear (click)="saveAssessment()" [attr.aria-label]="'core.save' | translate">
|
||||||
|
{{ 'core.save' | translate }}
|
||||||
|
</button>
|
||||||
|
<button *ngIf="canAddFeedback" ion-button clear (click)="saveEvaluation()" [attr.aria-label]="'core.save' | translate">
|
||||||
|
{{ 'core.save' | translate }}
|
||||||
|
</button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshSubmission($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ion-list *ngIf="submission">
|
||||||
|
<addon-mod-workshop-submission [submission]="submission" [courseId]="courseId" [module]="module" [workshop]="workshop" [access]="access"></addon-mod-workshop-submission>
|
||||||
|
<ion-item text-wrap *ngIf="canEdit || canDelete">
|
||||||
|
<button ion-button block icon-start *ngIf="canEdit" (click)="editSubmission()">
|
||||||
|
<ion-icon name="create"></ion-icon>
|
||||||
|
{{ 'addon.mod_workshop.editsubmission' | translate }}
|
||||||
|
</button>
|
||||||
|
<button ion-button block icon-start *ngIf="!submission.deleted && canDelete" color="danger" (click)="deleteSubmission()">
|
||||||
|
<ion-icon name="trash"></ion-icon>
|
||||||
|
{{ 'addon.mod_workshop.deletesubmission' | translate }}
|
||||||
|
</button>
|
||||||
|
<button ion-button block icon-start outline *ngIf="submission.deleted && canDelete" color="danger" (click)="undoDeleteSubmission()">
|
||||||
|
<ion-icon name="undo"></ion-icon>
|
||||||
|
{{ 'core.restore' | translate }}
|
||||||
|
</button>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<ion-list *ngIf="!canAddFeedback && evaluate && evaluate.text">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-avatar item-start *ngIf="evaluateByProfile">
|
||||||
|
<img [src]="evaluateByProfile.profileimageurl" core-external-content core-user-link [courseId]="courseId" [userId]="evaluateByProfile.id" [alt]="'core.pictureof' | translate:{$a: evaluateByProfile.fullname}" role="presentation" onError="this.src='assets/img/user-avatar.png'">
|
||||||
|
</ion-avatar>
|
||||||
|
<h2 *ngIf="evaluateByProfile && evaluateByProfile.fullname">{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateByProfile.fullname} }}</h2>
|
||||||
|
<core-format-text [text]="evaluate.text"></core-format-text>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<ion-list *ngIf="ownAssessment && !assessment">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<h2>{{ 'addon.mod_workshop.yourassessment' | translate }}</h2>
|
||||||
|
</ion-item>
|
||||||
|
<!--<addon-mod-workshop-assessment [submission]="submission" [assessment]="ownAssessment" [courseId]="courseId" summary="true" [access]="access" [module]="module" [workshop]="workshop"></addon-mod-workshop-assessment>-->
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<ion-list *ngIf="submissionInfo && submissionInfo.reviewedby && submissionInfo.reviewedby.length && !assessment">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<h2>{{ 'addon.mod_workshop.receivedgrades' | translate }}</h2>
|
||||||
|
</ion-item>
|
||||||
|
<!--<addon-mod-workshop-assessment *ngFor="let reviewer of submissionInfo.reviewedby" *ngIf="!reviewer.ownAssessment" [submission]="submission" [assessment]="reviewer" [courseId]="courseId" summary="true" [access]="access" [workshop]="workshop"></addon-mod-workshop-assessment>-->
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<ion-list *ngIf="submissionInfo && submissionInfo.reviewerof && submissionInfo.reviewerof.length && !assessment">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<h2>{{ 'addon.mod_workshop.givengrades' | translate }}</h2>
|
||||||
|
</ion-item>
|
||||||
|
<!--<addon-mod-workshop-assessment *ngFor="let reviewer of submissionInfo.reviewerof" [assessment]="reviewer" [courseId]="courseId" summary="true" [workshop]="workshop" [access]="access"></addon-mod-workshop-assessment>-->
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<form ion-list [formGroup]="feedbackForm" *ngIf="canAddFeedback">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<h2>{{ 'addon.mod_workshop.feedbackauthor' | translate }}</h2>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="access.canpublishsubmissions">
|
||||||
|
<ion-label>{{ 'addon.mod_workshop.publishsubmission' | translate }}</ion-label>
|
||||||
|
<ion-toggle formControlName="published"></ion-toggle>
|
||||||
|
<p class="item-help">{{ 'addon.mod_workshop.publishsubmission_help' | translate }}</p>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<h2>{{ 'addon.mod_workshop.gradecalculated' | translate }}</h2>
|
||||||
|
<p>{{ submission.submissiongrade }}</p>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-label stacked>{{ 'addon.mod_workshop.gradeover' | translate }}</ion-label>
|
||||||
|
<ion-select formControlName="grade">
|
||||||
|
<ion-option *ngFor="let grade of evaluationGrades" [value]="grade.value">{{grade.label}}</ion-option>
|
||||||
|
</ion-select>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item>
|
||||||
|
<ion-label stacked>{{ 'addon.mod_workshop.feedbackauthor' | translate }}</ion-label>
|
||||||
|
<core-rich-text-editor item-content [control]="feedbackForm.controls['text']" formControlName="text"></core-rich-text-editor>
|
||||||
|
</ion-item>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!--<addon-mod-workshop-assessment-strategy *ngIf="assessmentId" [workshop]="workshop" [access]="access" [assessmentId]="assessmentId" [userId]="assessmentUserId" [strategy]="strategy" [edit]="access.assessingallowed"></addon-mod-workshop-assessment-strategy>-->
|
||||||
|
|
||||||
|
<ion-list *ngIf="assessmentId && !access.assessingallowed && assessment.feedbackreviewer">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-avatar item-start *ngIf="evaluateGradingByProfile">
|
||||||
|
<img [src]="evaluateGradingByProfile.profileimageurl" core-external-content core-user-link [courseId]="courseId" [userId]="evaluateGradingByProfile.id" [alt]="'core.pictureof' | translate:{$a: evaluateGradingByProfile.fullname}" role="presentation" onError="this.src='assets/img/user-avatar.png'">
|
||||||
|
</ion-avatar>
|
||||||
|
<h2 *ngIf="evaluateGradingByProfile && evaluateGradingByProfile.fullname">{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateGradingByProfile.fullname} }}</h2>
|
||||||
|
<core-format-text [text]="assessment.feedbackreviewer"></core-format-text>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -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 {}
|
|
@ -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<void>} Resolved if we can leave it, rejected if not.
|
||||||
|
*/
|
||||||
|
ionViewCanLeave(): boolean | Promise<void> {
|
||||||
|
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<void>} Resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchSubmissionData(): Promise<void> {
|
||||||
|
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<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
protected refreshAllData(): Promise<any> {
|
||||||
|
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<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
protected sendEvaluation(): Promise<any> {
|
||||||
|
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<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
undoDeleteSubmission(): Promise<any> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,9 @@ export class AddonModWorkshopProvider {
|
||||||
static EXAMPLES_BEFORE_ASSESSMENT: 2;
|
static EXAMPLES_BEFORE_ASSESSMENT: 2;
|
||||||
|
|
||||||
static SUBMISSION_CHANGED = 'addon_mod_workshop_submission_changed';
|
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_SAVED = 'addon_mod_workshop_assessment_saved';
|
||||||
|
static ASSESSMENT_INVALIDATED = 'addon_mod_workshop_assessment_invalidated';
|
||||||
|
|
||||||
protected ROOT_CACHE_KEY = 'mmaModWorkshop:';
|
protected ROOT_CACHE_KEY = 'mmaModWorkshop:';
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue