commit
5f1d39ad8d
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="badgeLoaded" (ionRefresh)="refreshEvent($event)">
|
||||
<ion-refresher [enabled]="badgeLoaded" (ionRefresh)="refreshBadges($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="badgeLoaded">
|
||||
|
|
|
@ -116,7 +116,7 @@ export class AddonCalendarEventPage {
|
|||
}
|
||||
this.title = title;
|
||||
|
||||
if (event.courseid != this.siteHomeId) {
|
||||
if (event.courseid && event.courseid != this.siteHomeId) {
|
||||
// It's a course event, retrieve the course name.
|
||||
return this.coursesProvider.getUserCourse(event.courseid, true).then((course) => {
|
||||
this.courseName = course.fullname;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// (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 { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { AddonCourseCompletionReportComponent } from './report/report';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonCourseCompletionReportComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
AddonCourseCompletionReportComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonCourseCompletionReportComponent
|
||||
]
|
||||
})
|
||||
export class AddonCourseCompletionComponentsModule {}
|
|
@ -0,0 +1,51 @@
|
|||
<ion-content>
|
||||
<ion-refresher [enabled]="completionLoaded" (ionRefresh)="refreshCompletion($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="completionLoaded">
|
||||
<ion-card *ngIf="completion">
|
||||
<ion-item text-wrap>
|
||||
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
||||
<p>{{ completion.statusText | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<h2>{{ 'addon.coursecompletion.required' | translate }}</h2>
|
||||
<p *ngIf="completion.aggregation === 1">{{ 'addon.coursecompletion.criteriarequiredall' | translate }}</p>
|
||||
<p *ngIf="completion.aggregation === 2">{{ 'addon.coursecompletion.criteriarequiredany' | translate }}</p>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
<ion-card *ngIf="completion">
|
||||
<ion-item-divider>{{ 'addon.coursecompletion.requiredcriteria' | translate }}</ion-item-divider>
|
||||
<ion-item class="hidden-tablet" text-wrap *ngFor="let criteria of completion.completions">
|
||||
<h2><core-format-text clean="true" [text]="criteria.details.criteria"></core-format-text></h2>
|
||||
<p><core-format-text clean="true" [text]="criteria.details.requirement"></core-format-text></p>
|
||||
<strong item-end>{{ criteria.status }}</strong>
|
||||
</ion-item>
|
||||
<ion-item class="hidden-phone" text-wrap>
|
||||
<ion-row>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.criteriagroup' | translate }}</strong></ion-col>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.criteria' | translate }}</strong></ion-col>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.requirement' | translate }}</strong></ion-col>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.status' | translate }}</strong></ion-col>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.complete' | translate }}</strong></ion-col>
|
||||
<ion-col><strong>{{ 'addon.coursecompletion.completiondate' | translate }}</strong></ion-col>
|
||||
</ion-row>
|
||||
<ion-row *ngFor="let criteria of completion.completions">
|
||||
<ion-col><core-format-text clean="true" [text]="criteria.title"></core-format-text></ion-col>
|
||||
<ion-col><core-format-text clean="true" [text]="criteria.details.criteria"></core-format-text></ion-col>
|
||||
<ion-col><core-format-text clean="true" [text]="criteria.details.requirement"></core-format-text></ion-col>
|
||||
<ion-col><core-format-text [text]="criteria.details.status"></core-format-text></ion-col>
|
||||
<ion-col>{{ criteria.status }}</ion-col>
|
||||
<ion-col *ngIf="criteria.timecompleted">{{ criteria.timecompleted | coreToLocaleString }}</ion-col>
|
||||
<ion-col *ngIf="!criteria.timecompleted"></ion-col>
|
||||
</ion-row>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
<ion-card *ngIf="showSelfComplete">
|
||||
<ion-item-divider>{{ 'addon.coursecompletion.manualselfcompletion' | translate }}</ion-item-divider>
|
||||
<ion-item>
|
||||
<button ion-button block (click)="completeCourse()">{{ 'addon.coursecompletion.completecourse' | translate }}</button>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,96 @@
|
|||
// (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, Input, OnInit } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonCourseCompletionProvider } from '../../providers/coursecompletion';
|
||||
|
||||
/**
|
||||
* Component that displays the course completion report.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-course-completion-report',
|
||||
templateUrl: 'addon-course-completion-report.html',
|
||||
})
|
||||
export class AddonCourseCompletionReportComponent implements OnInit {
|
||||
@Input() courseId: number;
|
||||
@Input() userId: number;
|
||||
|
||||
completionLoaded = false;
|
||||
completion: any;
|
||||
showSelfComplete: boolean;
|
||||
|
||||
constructor(
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
private domUtils: CoreDomUtilsProvider,
|
||||
private courseCompletionProvider: AddonCourseCompletionProvider) {}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (!this.userId) {
|
||||
this.userId = this.sitesProvider.getCurrentSiteUserId();
|
||||
}
|
||||
|
||||
this.fetchCompletion().finally(() => {
|
||||
this.completionLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch compleiton data.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchCompletion(): Promise<any> {
|
||||
return this.courseCompletionProvider.getCompletion(this.courseId, this.userId).then((completion) => {
|
||||
|
||||
completion.statusText = this.courseCompletionProvider.getCompletedStatusText(completion);
|
||||
|
||||
this.completion = completion;
|
||||
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'addon.coursecompletion.couldnotloadreport', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh completion data on PTR.
|
||||
*
|
||||
* @param {any} [refresher] Refresher instance.
|
||||
*/
|
||||
refreshCompletion(refresher?: any): void {
|
||||
this.courseCompletionProvider.invalidateCourseCompletion(this.courseId, this.userId).finally(() => {
|
||||
this.fetchCompletion().finally(() => {
|
||||
refresher && refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark course as completed.
|
||||
*/
|
||||
completeCourse(): void {
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.courseCompletionProvider.markCourseAsSelfCompleted(this.courseId).then(() => {
|
||||
return this.refreshCompletion();
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModal(message);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// (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 { AddonCourseCompletionProvider } from './providers/coursecompletion';
|
||||
import { AddonCourseCompletionCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { AddonCourseCompletionUserHandler } from './providers/user-handler';
|
||||
import { AddonCourseCompletionComponentsModule } from './components/components.module';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
AddonCourseCompletionComponentsModule
|
||||
],
|
||||
providers: [
|
||||
AddonCourseCompletionProvider,
|
||||
AddonCourseCompletionCourseOptionHandler,
|
||||
AddonCourseCompletionUserHandler
|
||||
]
|
||||
})
|
||||
export class AddonCourseCompletionModule {
|
||||
constructor(courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonCourseCompletionCourseOptionHandler,
|
||||
userDelegate: CoreUserDelegate, userHandler: AddonCourseCompletionUserHandler) {
|
||||
// Register handlers.
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"complete": "Complete",
|
||||
"completecourse": "Complete course",
|
||||
"completed": "Completed",
|
||||
"completiondate": "Completion date",
|
||||
"couldnotloadreport": "Could not load the course completion report. Please try again later.",
|
||||
"coursecompletion": "Course completion",
|
||||
"criteria": "Criteria",
|
||||
"criteriagroup": "Criteria group",
|
||||
"criteriarequiredall": "All criteria below are required.",
|
||||
"criteriarequiredany": "Any criteria below are required.",
|
||||
"inprogress": "In progress",
|
||||
"manualselfcompletion": "Manual self completion",
|
||||
"notyetstarted": "Not yet started",
|
||||
"pending": "Pending",
|
||||
"required": "Required",
|
||||
"requiredcriteria": "Required criteria",
|
||||
"requirement": "Requirement",
|
||||
"status": "Status",
|
||||
"viewcoursereport": "View course report"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'addon.coursecompletion.coursecompletion' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<addon-course-completion-report class="core-avoid-header" [courseId]="courseId" [userId]="userId"></addon-course-completion-report>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { AddonCourseCompletionComponentsModule } from '../../components/components.module';
|
||||
import { AddonCourseCompletionReportPage } from './report';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonCourseCompletionReportPage,
|
||||
],
|
||||
imports: [
|
||||
CoreDirectivesModule,
|
||||
AddonCourseCompletionComponentsModule,
|
||||
IonicPageModule.forChild(AddonCourseCompletionReportPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class AddonModFolderIndexPageModule {}
|
|
@ -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 { Component } from '@angular/core';
|
||||
import { IonicPage, NavParams } from 'ionic-angular';
|
||||
|
||||
/**
|
||||
* Page that displays the course completion report.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-course-completion-report' })
|
||||
@Component({
|
||||
selector: 'page-addon-course-completion-report',
|
||||
templateUrl: 'report.html',
|
||||
})
|
||||
export class AddonCourseCompletionReportPage {
|
||||
|
||||
courseId: number;
|
||||
userId: number;
|
||||
|
||||
constructor(navParams: NavParams) {
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.userId = navParams.get('userId');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
// (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 { Injectable, Injector } from '@angular/core';
|
||||
import { AddonCourseCompletionProvider } from './coursecompletion';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate';
|
||||
import { AddonCourseCompletionReportComponent } from '../components/report/report';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into the course main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonCourseCompletionCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'AddonCourseCompletion';
|
||||
priority = 200;
|
||||
|
||||
constructor(private courseCompletionProvider: AddonCourseCompletionProvider) {}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.courseCompletionProvider.isPluginViewEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} accessData Access type and data. Default, guest, ...
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
}
|
||||
|
||||
return this.courseCompletionProvider.isPluginViewEnabledForCourse(courseId).then((courseEnabled) => {
|
||||
// If is not enabled in the course, is not enabled for the user.
|
||||
if (!courseEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the user can see his own report, teachers can't.
|
||||
return this.courseCompletionProvider.isPluginViewEnabledForUser(courseId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @return {CoreCourseOptionsHandlerData} Data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, courseId: number): CoreCourseOptionsHandlerData {
|
||||
return {
|
||||
title: 'addon.coursecompletion.coursecompletion',
|
||||
class: 'addon-coursecompletion-course-handler',
|
||||
component: AddonCourseCompletionReportComponent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Should invalidate the data to determine if the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise<any> {
|
||||
return this.courseCompletionProvider.invalidateCourseCompletion(courseId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Service to handle course completion.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonCourseCompletionProvider {
|
||||
|
||||
protected ROOT_CACHE_KEY = 'mmaCourseCompletion:';
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider,
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
private coursesProvider: CoreCoursesProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
this.logger = logger.getInstance('AddonCourseCompletionProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the user can mark a course as self completed.
|
||||
* It can if it's configured in the course and it hasn't been completed yet.
|
||||
*
|
||||
* @param {number} userId User ID.
|
||||
* @param {any} completion Course completion.
|
||||
* @return {boolean} True if user can mark course as self completed, false otherwise.
|
||||
*/
|
||||
canMarkSelfCompleted(userId: number, completion: any): boolean {
|
||||
let selfCompletionActive = false,
|
||||
alreadyMarked = false;
|
||||
|
||||
if (this.sitesProvider.getCurrentSiteUserId() != userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
completion.completions.forEach((criteria) => {
|
||||
if (criteria.type === 1) {
|
||||
// Self completion criteria found.
|
||||
selfCompletionActive = true;
|
||||
alreadyMarked = criteria.complete;
|
||||
}
|
||||
});
|
||||
|
||||
return selfCompletionActive && !alreadyMarked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completed status text. The language code returned is meant to be translated.
|
||||
*
|
||||
* @param {any} completion Course completion.
|
||||
* @return {string} Language code of the text to show.
|
||||
*/
|
||||
getCompletedStatusText(completion: any): string {
|
||||
if (completion.completed) {
|
||||
return 'addon.coursecompletion.completed';
|
||||
} else {
|
||||
// Let's calculate status.
|
||||
let hasStarted = false;
|
||||
completion.completions.forEach((criteria) => {
|
||||
if (criteria.timecompleted || criteria.complete) {
|
||||
hasStarted = true;
|
||||
}
|
||||
});
|
||||
if (hasStarted) {
|
||||
return 'addon.coursecompletion.inprogress';
|
||||
} else {
|
||||
return 'addon.coursecompletion.notyetstarted';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get course completion status for a certain course and user.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} [userId] User ID. If not defined, use current user.
|
||||
* @param {any} [preSets] Presets to use when calling the WebService.
|
||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||
* @return {Promise<any>} Promise to be resolved when the completion is retrieved.
|
||||
*/
|
||||
getCompletion(courseId: number, userId?: number, preSets?: any, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
preSets = preSets || {};
|
||||
|
||||
this.logger.debug('Get completion for course ' + courseId + ' and user ' + userId);
|
||||
|
||||
const data = {
|
||||
courseid: courseId,
|
||||
userid: userId
|
||||
};
|
||||
|
||||
preSets.cacheKey = this.getCompletionCacheKey(courseId, userId);
|
||||
|
||||
return site.read('core_completion_get_course_completion_status', data, preSets).then((data) => {
|
||||
if (data.completionstatus) {
|
||||
return data.completionstatus;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for get completion WS calls.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} useIid User ID.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCompletionCacheKey(courseId: number, userId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'view:' + courseId + ':' + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates view course completion WS call.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} [userId] User ID. If not defined, use current user.
|
||||
* @return {Promise<any>} Promise resolved when the list is invalidated.
|
||||
*/
|
||||
invalidateCourseCompletion(courseId: number, userId?: number): Promise<any> {
|
||||
userId = userId || this.sitesProvider.getCurrentSiteUserId();
|
||||
|
||||
return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getCompletionCacheKey(courseId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the view course completion plugin is enabled for the current site.
|
||||
*
|
||||
* @return {boolean} True if plugin enabled, false otherwise.
|
||||
*/
|
||||
isPluginViewEnabled(): boolean {
|
||||
return this.sitesProvider.isLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the view course completion plugin is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {boolean} [preferCache=true] True if shouldn't call WS if data is cached, false otherwise.
|
||||
* @return {Promise<boolean>} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isPluginViewEnabledForCourse(courseId: number, preferCache: boolean = true): Promise<boolean> {
|
||||
if (!courseId) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
return this.coursesProvider.getUserCourse(courseId, preferCache).then((course) => {
|
||||
return !(course && typeof course.enablecompletion != 'undefined' && course.enablecompletion == 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the view course completion plugin is enabled for a certain user.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} [userId] User ID. If not defined, use current user.
|
||||
* @return {Promise<boolean>} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isPluginViewEnabledForUser(courseId: number, userId?: number): Promise<boolean> {
|
||||
// Disable emergency cache to be able to detect that the plugin has been disabled (WS will fail).
|
||||
const preSets: any = {
|
||||
emergencyCache: 0
|
||||
};
|
||||
|
||||
return this.getCompletion(courseId, userId, preSets).then(() => {
|
||||
return true;
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WS returned an error, plugin is not enabled.
|
||||
return false;
|
||||
} else {
|
||||
// Not a WS error. Check if we have a cached value.
|
||||
preSets.omitExpires = true;
|
||||
|
||||
return this.getCompletion(courseId, userId, preSets).then(() => {
|
||||
return true;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a course as self completed.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @return {Promise<any>} Resolved on success.
|
||||
*/
|
||||
markCourseAsSelfCompleted(courseId: number): Promise<any> {
|
||||
const params = {
|
||||
courseid: courseId
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('core_completion_mark_course_self_completed', params).then((response) => {
|
||||
if (!response.status) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonCourseCompletionProvider } from './coursecompletion';
|
||||
|
||||
/**
|
||||
* Profile course completion handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonCourseCompletionUserHandler implements CoreUserProfileHandler {
|
||||
name = 'AddonCourseCompletion';
|
||||
type = CoreUserDelegate.TYPE_NEW_PAGE;
|
||||
priority = 200;
|
||||
|
||||
protected enabledCache = {};
|
||||
|
||||
constructor(eventsProvider: CoreEventsProvider, private courseCompletionProvider: AddonCourseCompletionProvider) {
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
|
||||
this.enabledCache = {};
|
||||
});
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, (data) => {
|
||||
const cacheKey = data.userId + '-' + data.courseId;
|
||||
|
||||
delete this.enabledCache[cacheKey];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.courseCompletionProvider.isPluginViewEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if handler is enabled for this user in this context.
|
||||
*
|
||||
* @param {any} user User to check.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {boolean|Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise.
|
||||
*/
|
||||
isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
if (!courseId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.courseCompletionProvider.isPluginViewEnabledForCourse(courseId).then((courseEnabled) => {
|
||||
// If is not enabled in the course, is not enabled for the user.
|
||||
if (!courseEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cacheKey = user.id + '-' + courseId;
|
||||
if (typeof this.enabledCache[cacheKey] !== 'undefined') {
|
||||
return this.enabledCache[cacheKey];
|
||||
}
|
||||
|
||||
return this.courseCompletionProvider.isPluginViewEnabledForUser(courseId, user.id).then((enabled) => {
|
||||
this.enabledCache[cacheKey] = enabled;
|
||||
|
||||
return enabled;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreUserProfileHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData {
|
||||
return {
|
||||
icon: 'checkbox-outline',
|
||||
title: 'addon.coursecompletion.coursecompletion',
|
||||
class: 'addon-coursecompletion-handler',
|
||||
action: (event, navCtrl, user, courseId): void => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
navCtrl.push('AddonCourseCompletionReportPage', {courseId: courseId, userId: user.id });
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -77,6 +77,7 @@ import { CoreCommentsModule } from '@core/comments/comments.module';
|
|||
import { AddonBadgesModule } from '@addon/badges/badges.module';
|
||||
import { AddonCalendarModule } from '@addon/calendar/calendar.module';
|
||||
import { AddonCompetencyModule } from '@addon/competency/competency.module';
|
||||
import { AddonCourseCompletionModule } from '@addon/coursecompletion/coursecompletion.module';
|
||||
import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
||||
import { AddonFilesModule } from '@addon/files/files.module';
|
||||
import { AddonModAssignModule } from '@addon/mod/assign/assign.module';
|
||||
|
@ -184,6 +185,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonBadgesModule,
|
||||
AddonCalendarModule,
|
||||
AddonCompetencyModule,
|
||||
AddonCourseCompletionModule,
|
||||
AddonUserProfileFieldModule,
|
||||
AddonFilesModule,
|
||||
AddonModAssignModule,
|
||||
|
|
Loading…
Reference in New Issue