MOBILE-2431 coursecompletion: Migrate course completion
This commit is contained in:
		
							parent
							
								
									59d572a783
								
							
						
					
					
						commit
						9048bd148c
					
				
							
								
								
									
										45
									
								
								src/addon/coursecompletion/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/addon/coursecompletion/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||||
							
								
								
									
										96
									
								
								src/addon/coursecompletion/components/report/report.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/addon/coursecompletion/components/report/report.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								src/addon/coursecompletion/coursecompletion.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/addon/coursecompletion/coursecompletion.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								src/addon/coursecompletion/lang/en.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/addon/coursecompletion/lang/en.json
									
									
									
									
									
										Normal file
									
								
							| @ -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" | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/addon/coursecompletion/pages/report/report.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/addon/coursecompletion/pages/report/report.html
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||||
							
								
								
									
										33
									
								
								src/addon/coursecompletion/pages/report/report.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addon/coursecompletion/pages/report/report.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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 {} | ||||||
							
								
								
									
										35
									
								
								src/addon/coursecompletion/pages/report/report.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/addon/coursecompletion/pages/report/report.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										221
									
								
								src/addon/coursecompletion/providers/coursecompletion.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/addon/coursecompletion/providers/coursecompletion.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										101
									
								
								src/addon/coursecompletion/providers/user-handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/addon/coursecompletion/providers/user-handler.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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 { AddonBadgesModule } from '@addon/badges/badges.module'; | ||||||
| import { AddonCalendarModule } from '@addon/calendar/calendar.module'; | import { AddonCalendarModule } from '@addon/calendar/calendar.module'; | ||||||
| import { AddonCompetencyModule } from '@addon/competency/competency.module'; | import { AddonCompetencyModule } from '@addon/competency/competency.module'; | ||||||
|  | import { AddonCourseCompletionModule } from '@addon/coursecompletion/coursecompletion.module'; | ||||||
| import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module'; | import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module'; | ||||||
| import { AddonFilesModule } from '@addon/files/files.module'; | import { AddonFilesModule } from '@addon/files/files.module'; | ||||||
| import { AddonModAssignModule } from '@addon/mod/assign/assign.module'; | import { AddonModAssignModule } from '@addon/mod/assign/assign.module'; | ||||||
| @ -184,6 +185,7 @@ export const CORE_PROVIDERS: any[] = [ | |||||||
|         AddonBadgesModule, |         AddonBadgesModule, | ||||||
|         AddonCalendarModule, |         AddonCalendarModule, | ||||||
|         AddonCompetencyModule, |         AddonCompetencyModule, | ||||||
|  |         AddonCourseCompletionModule, | ||||||
|         AddonUserProfileFieldModule, |         AddonUserProfileFieldModule, | ||||||
|         AddonFilesModule, |         AddonFilesModule, | ||||||
|         AddonModAssignModule, |         AddonModAssignModule, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user