forked from EVOgeek/Vmeda.Online
333 lines
13 KiB
TypeScript
333 lines
13 KiB
TypeScript
// (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, Optional, Injector, ViewChild } from '@angular/core';
|
|
import { Content, NavController } from 'ionic-angular';
|
|
import { CoreGroupsProvider } from '@providers/groups';
|
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
|
import { AddonModAssignProvider } from '../../providers/assign';
|
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
|
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
|
import * as moment from 'moment';
|
|
import { AddonModAssignSubmissionComponent } from '../submission/submission';
|
|
|
|
/**
|
|
* Component that displays an assignment.
|
|
*/
|
|
@Component({
|
|
selector: 'addon-mod-assign-index',
|
|
templateUrl: 'addon-mod-assign-index.html',
|
|
})
|
|
export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityComponent {
|
|
@ViewChild(AddonModAssignSubmissionComponent) submissionComponent: AddonModAssignSubmissionComponent;
|
|
|
|
component = AddonModAssignProvider.COMPONENT;
|
|
moduleName = 'assign';
|
|
|
|
assign: any; // The assign object.
|
|
canViewSubmissions: boolean; // Whether the user can view all submissions.
|
|
timeRemaining: string; // Message about time remaining to submit.
|
|
lateSubmissions: string; // Message about late submissions.
|
|
showNumbers = true; // Whether to show number of submissions with each status.
|
|
summary: any; // The summary.
|
|
needsGradingAvalaible: boolean; // Whether we can see the submissions that need grading.
|
|
|
|
// Status.
|
|
submissionStatusSubmitted = AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED;
|
|
submissionStatusDraft = AddonModAssignProvider.SUBMISSION_STATUS_DRAFT;
|
|
needGrading = AddonModAssignProvider.NEED_GRADING;
|
|
|
|
protected userId: number; // Current user ID.
|
|
protected syncEventName = AddonModAssignSyncProvider.AUTO_SYNCED;
|
|
|
|
// Observers.
|
|
protected savedObserver;
|
|
protected submittedObserver;
|
|
protected gradedObserver;
|
|
|
|
constructor(injector: Injector, protected assignProvider: AddonModAssignProvider, @Optional() content: Content,
|
|
protected assignHelper: AddonModAssignHelperProvider, protected assignOffline: AddonModAssignOfflineProvider,
|
|
protected syncProvider: AddonModAssignSyncProvider, protected timeUtils: CoreTimeUtilsProvider,
|
|
protected groupsProvider: CoreGroupsProvider, protected navCtrl: NavController) {
|
|
super(injector, content);
|
|
}
|
|
|
|
/**
|
|
* Component being initialized.
|
|
*/
|
|
ngOnInit(): void {
|
|
super.ngOnInit();
|
|
|
|
this.userId = this.sitesProvider.getCurrentSiteUserId();
|
|
|
|
this.loadContent(false, true).then(() => {
|
|
this.assignProvider.logView(this.assign.id).then(() => {
|
|
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
|
|
}).catch(() => {
|
|
// Ignore errors.
|
|
});
|
|
|
|
if (!this.canViewSubmissions) {
|
|
// User can only see his submission, log view the user submission.
|
|
this.assignProvider.logSubmissionView(this.assign.id).catch(() => {
|
|
// Ignore errors.
|
|
});
|
|
} else {
|
|
// User can see all submissions, log grading view.
|
|
this.assignProvider.logGradingView(this.assign.id).catch(() => {
|
|
// Ignore errors.
|
|
});
|
|
}
|
|
});
|
|
|
|
// Listen to events.
|
|
this.savedObserver = this.eventsProvider.on(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, (data) => {
|
|
if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) {
|
|
// Assignment submission saved, refresh data.
|
|
this.showLoadingAndRefresh(true, false);
|
|
}
|
|
}, this.siteId);
|
|
|
|
this.submittedObserver = this.eventsProvider.on(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, (data) => {
|
|
if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) {
|
|
// Assignment submitted, check completion.
|
|
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
|
|
|
|
// Reload data since it can have offline data now.
|
|
this.showLoadingAndRefresh(true, false);
|
|
}
|
|
}, this.siteId);
|
|
|
|
this.gradedObserver = this.eventsProvider.on(AddonModAssignProvider.GRADED_EVENT, (data) => {
|
|
if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) {
|
|
// Assignment graded, refresh data.
|
|
this.showLoadingAndRefresh(true, false);
|
|
}
|
|
}, this.siteId);
|
|
}
|
|
|
|
/**
|
|
* Expand the description.
|
|
*/
|
|
expandDescription(ev?: Event): void {
|
|
ev && ev.preventDefault();
|
|
ev && ev.stopPropagation();
|
|
|
|
if (this.assign && (this.description || this.assign.introattachments)) {
|
|
this.textUtils.expandText(this.translate.instant('core.description'), this.description, this.component,
|
|
this.module.id, this.assign.introattachments);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get assignment data.
|
|
*
|
|
* @param {boolean} [refresh=false] If it's refreshing content.
|
|
* @param {boolean} [sync=false] If the refresh is needs syncing.
|
|
* @param {boolean} [showErrors=false] If show errors to the user of hide them.
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
*/
|
|
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
|
|
|
// Get assignment data.
|
|
return this.assignProvider.getAssignment(this.courseId, this.module.id).then((assignData) => {
|
|
this.assign = assignData;
|
|
|
|
this.dataRetrieved.emit(this.assign);
|
|
this.description = this.assign.intro || this.description;
|
|
|
|
if (sync) {
|
|
// Try to synchronize the assign.
|
|
return this.syncActivity(showErrors).catch(() => {
|
|
// Ignore errors.
|
|
});
|
|
}
|
|
}).then(() => {
|
|
// Check if there's any offline data for this assign.
|
|
return this.assignOffline.hasAssignOfflineData(this.assign.id);
|
|
}).then((hasOffline) => {
|
|
this.hasOffline = hasOffline;
|
|
|
|
// Get assignment submissions.
|
|
return this.assignProvider.getSubmissions(this.assign.id).then((data) => {
|
|
const time = this.timeUtils.timestamp();
|
|
|
|
this.canViewSubmissions = data.canviewsubmissions;
|
|
|
|
if (data.canviewsubmissions) {
|
|
|
|
// Calculate the messages to display about time remaining and late submissions.
|
|
if (this.assign.duedate > 0) {
|
|
if (this.assign.duedate - time <= 0) {
|
|
this.timeRemaining = this.translate.instant('addon.mod_assign.assignmentisdue');
|
|
} else {
|
|
this.timeRemaining = this.timeUtils.formatDuration(this.assign.duedate - time, 3);
|
|
|
|
if (this.assign.cutoffdate) {
|
|
if (this.assign.cutoffdate > time) {
|
|
const dateFormat = this.translate.instant('core.dfmediumdate');
|
|
|
|
this.lateSubmissions = this.translate.instant('addon.mod_assign.latesubmissionsaccepted',
|
|
{$a: moment(this.assign.cutoffdate * 1000).format(dateFormat)});
|
|
} else {
|
|
this.lateSubmissions = this.translate.instant('addon.mod_assign.nomoresubmissionsaccepted');
|
|
}
|
|
} else {
|
|
this.lateSubmissions = '';
|
|
}
|
|
}
|
|
} else {
|
|
this.timeRemaining = '';
|
|
this.lateSubmissions = '';
|
|
}
|
|
|
|
// Check if groupmode is enabled to avoid showing wrong numbers.
|
|
return this.groupsProvider.activityHasGroups(this.assign.cmid).then((hasGroups) => {
|
|
this.showNumbers = !hasGroups;
|
|
|
|
return this.assignProvider.getSubmissionStatus(this.assign.id).then((response) => {
|
|
this.summary = response.gradingsummary;
|
|
|
|
this.needsGradingAvalaible = response.gradingsummary.submissionsneedgradingcount > 0 &&
|
|
this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.2');
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}).then(() => {
|
|
// All data obtained, now fill the context menu.
|
|
this.fillContextMenu(refresh);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Go to view a list of submissions.
|
|
*
|
|
* @param {string} status Status to see.
|
|
* @param {number} count Number of submissions with the status.
|
|
*/
|
|
goToSubmissionList(status: string, count: number): void {
|
|
if (typeof status == 'undefined') {
|
|
this.navCtrl.push('AddonModAssignSubmissionListPage', {
|
|
courseId: this.courseId,
|
|
moduleId: this.module.id,
|
|
moduleName: this.moduleName
|
|
});
|
|
} else if (count || !this.showNumbers) {
|
|
this.navCtrl.push('AddonModAssignSubmissionListPage', {
|
|
status: status,
|
|
courseId: this.courseId,
|
|
moduleId: this.module.id,
|
|
moduleName: this.moduleName
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if sync has succeed from result sync data.
|
|
*
|
|
* @param {any} result Data returned by the sync function.
|
|
* @return {boolean} If succeed or not.
|
|
*/
|
|
protected hasSyncSucceed(result: any): boolean {
|
|
if (result.updated) {
|
|
this.submissionComponent && this.submissionComponent.invalidateAndRefresh();
|
|
}
|
|
|
|
return result.updated;
|
|
}
|
|
|
|
/**
|
|
* Perform the invalidate content function.
|
|
*
|
|
* @return {Promise<any>} Resolved when done.
|
|
*/
|
|
protected invalidateContent(): Promise<any> {
|
|
const promises = [];
|
|
|
|
promises.push(this.assignProvider.invalidateAssignmentData(this.courseId));
|
|
|
|
if (this.assign) {
|
|
promises.push(this.assignProvider.invalidateAllSubmissionData(this.assign.id));
|
|
|
|
if (this.canViewSubmissions) {
|
|
promises.push(this.assignProvider.invalidateSubmissionStatusData(this.assign.id));
|
|
}
|
|
}
|
|
|
|
return Promise.all(promises).finally(() => {
|
|
this.submissionComponent && this.submissionComponent.invalidateAndRefresh();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* User entered the page that contains the component.
|
|
*/
|
|
ionViewDidEnter(): void {
|
|
super.ionViewDidEnter();
|
|
|
|
this.submissionComponent && this.submissionComponent.ionViewDidEnter();
|
|
}
|
|
|
|
/**
|
|
* User left the page that contains the component.
|
|
*/
|
|
ionViewDidLeave(): void {
|
|
super.ionViewDidLeave();
|
|
|
|
this.submissionComponent && this.submissionComponent.ionViewDidLeave();
|
|
}
|
|
|
|
/**
|
|
* Compares sync event data with current data to check if refresh content is needed.
|
|
*
|
|
* @param {any} syncEventData Data receiven on sync observer.
|
|
* @return {boolean} True if refresh is needed, false otherwise.
|
|
*/
|
|
protected isRefreshSyncNeeded(syncEventData: any): boolean {
|
|
if (this.assign && syncEventData.assignId == this.assign.id) {
|
|
if (syncEventData.warnings && syncEventData.warnings.length) {
|
|
// Show warnings.
|
|
this.domUtils.showErrorModal(syncEventData.warnings[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Performs the sync of the activity.
|
|
*
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
*/
|
|
protected sync(): Promise<any> {
|
|
return this.syncProvider.syncAssign(this.assign.id);
|
|
}
|
|
|
|
/**
|
|
* Component being destroyed.
|
|
*/
|
|
ngOnDestroy(): void {
|
|
super.ngOnDestroy();
|
|
|
|
this.savedObserver && this.savedObserver.off();
|
|
this.submittedObserver && this.submittedObserver.off();
|
|
this.gradedObserver && this.gradedObserver.off();
|
|
}
|
|
}
|