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 { 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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user