From 59d572a7838d607bc25a9093f1c885c689cf4a9e Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 15 Jun 2018 12:29:11 +0200 Subject: [PATCH 1/3] MOBILE-2431 badges: Fix PTR --- src/addon/badges/pages/issued-badge/issued-badge.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addon/badges/pages/issued-badge/issued-badge.html b/src/addon/badges/pages/issued-badge/issued-badge.html index e28898229..9d9f5dc2c 100644 --- a/src/addon/badges/pages/issued-badge/issued-badge.html +++ b/src/addon/badges/pages/issued-badge/issued-badge.html @@ -4,7 +4,7 @@ - + From 9048bd148c6a1151c2af7b26976cf9b5302db399 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 15 Jun 2018 18:50:00 +0200 Subject: [PATCH 2/3] MOBILE-2431 coursecompletion: Migrate course completion --- .../components/components.module.ts | 45 ++++ .../addon-course-completion-report.html | 51 ++++ .../components/report/report.ts | 96 ++++++++ .../coursecompletion.module.ts | 42 ++++ src/addon/coursecompletion/lang/en.json | 21 ++ .../coursecompletion/pages/report/report.html | 6 + .../pages/report/report.module.ts | 33 +++ .../coursecompletion/pages/report/report.ts | 35 +++ .../providers/course-option-handler.ts | 89 +++++++ .../providers/coursecompletion.ts | 221 ++++++++++++++++++ .../providers/user-handler.ts | 101 ++++++++ src/app/app.module.ts | 2 + 12 files changed, 742 insertions(+) create mode 100644 src/addon/coursecompletion/components/components.module.ts create mode 100644 src/addon/coursecompletion/components/report/addon-course-completion-report.html create mode 100644 src/addon/coursecompletion/components/report/report.ts create mode 100644 src/addon/coursecompletion/coursecompletion.module.ts create mode 100644 src/addon/coursecompletion/lang/en.json create mode 100644 src/addon/coursecompletion/pages/report/report.html create mode 100644 src/addon/coursecompletion/pages/report/report.module.ts create mode 100644 src/addon/coursecompletion/pages/report/report.ts create mode 100644 src/addon/coursecompletion/providers/course-option-handler.ts create mode 100644 src/addon/coursecompletion/providers/coursecompletion.ts create mode 100644 src/addon/coursecompletion/providers/user-handler.ts diff --git a/src/addon/coursecompletion/components/components.module.ts b/src/addon/coursecompletion/components/components.module.ts new file mode 100644 index 000000000..140b2b3ff --- /dev/null +++ b/src/addon/coursecompletion/components/components.module.ts @@ -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 {} diff --git a/src/addon/coursecompletion/components/report/addon-course-completion-report.html b/src/addon/coursecompletion/components/report/addon-course-completion-report.html new file mode 100644 index 000000000..af292dc8e --- /dev/null +++ b/src/addon/coursecompletion/components/report/addon-course-completion-report.html @@ -0,0 +1,51 @@ + + + + + + + +

{{ 'addon.coursecompletion.status' | translate }}

+

{{ completion.statusText | translate }}

+
+ +

{{ 'addon.coursecompletion.required' | translate }}

+

{{ 'addon.coursecompletion.criteriarequiredall' | translate }}

+

{{ 'addon.coursecompletion.criteriarequiredany' | translate }}

+
+
+ + {{ 'addon.coursecompletion.requiredcriteria' | translate }} + +

+

+ {{ criteria.status }} +
+ + + {{ 'addon.coursecompletion.criteriagroup' | translate }} + {{ 'addon.coursecompletion.criteria' | translate }} + {{ 'addon.coursecompletion.requirement' | translate }} + {{ 'addon.coursecompletion.status' | translate }} + {{ 'addon.coursecompletion.complete' | translate }} + {{ 'addon.coursecompletion.completiondate' | translate }} + + + + + + + {{ criteria.status }} + {{ criteria.timecompleted | coreToLocaleString }} + + + +
+ + {{ 'addon.coursecompletion.manualselfcompletion' | translate }} + + + + +
+
diff --git a/src/addon/coursecompletion/components/report/report.ts b/src/addon/coursecompletion/components/report/report.ts new file mode 100644 index 000000000..69583770f --- /dev/null +++ b/src/addon/coursecompletion/components/report/report.ts @@ -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} Promise resolved when done. + */ + protected fetchCompletion(): Promise { + 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(); + }); + } +} diff --git a/src/addon/coursecompletion/coursecompletion.module.ts b/src/addon/coursecompletion/coursecompletion.module.ts new file mode 100644 index 000000000..db3699489 --- /dev/null +++ b/src/addon/coursecompletion/coursecompletion.module.ts @@ -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); + } +} diff --git a/src/addon/coursecompletion/lang/en.json b/src/addon/coursecompletion/lang/en.json new file mode 100644 index 000000000..69547f49a --- /dev/null +++ b/src/addon/coursecompletion/lang/en.json @@ -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" +} \ No newline at end of file diff --git a/src/addon/coursecompletion/pages/report/report.html b/src/addon/coursecompletion/pages/report/report.html new file mode 100644 index 000000000..eb63cf5a2 --- /dev/null +++ b/src/addon/coursecompletion/pages/report/report.html @@ -0,0 +1,6 @@ + + + {{ 'addon.coursecompletion.coursecompletion' | translate }} + + + diff --git a/src/addon/coursecompletion/pages/report/report.module.ts b/src/addon/coursecompletion/pages/report/report.module.ts new file mode 100644 index 000000000..83f0a10e6 --- /dev/null +++ b/src/addon/coursecompletion/pages/report/report.module.ts @@ -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 {} diff --git a/src/addon/coursecompletion/pages/report/report.ts b/src/addon/coursecompletion/pages/report/report.ts new file mode 100644 index 000000000..d874ae161 --- /dev/null +++ b/src/addon/coursecompletion/pages/report/report.ts @@ -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'); + } +} diff --git a/src/addon/coursecompletion/providers/course-option-handler.ts b/src/addon/coursecompletion/providers/course-option-handler.ts new file mode 100644 index 000000000..52e2ed2cd --- /dev/null +++ b/src/addon/coursecompletion/providers/course-option-handler.ts @@ -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} Whether or not the handler is enabled on a site level. + */ + isEnabled(): boolean | Promise { + 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} True or promise resolved with true if enabled. + */ + isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { + 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} Promise resolved when done. + */ + invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise { + return this.courseCompletionProvider.invalidateCourseCompletion(courseId); + } +} diff --git a/src/addon/coursecompletion/providers/coursecompletion.ts b/src/addon/coursecompletion/providers/coursecompletion.ts new file mode 100644 index 000000000..a490f2b95 --- /dev/null +++ b/src/addon/coursecompletion/providers/coursecompletion.ts @@ -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} Promise to be resolved when the completion is retrieved. + */ + getCompletion(courseId: number, userId?: number, preSets?: any, siteId?: string): Promise { + 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} Promise resolved when the list is invalidated. + */ + invalidateCourseCompletion(courseId: number, userId?: number): Promise { + 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} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. + */ + isPluginViewEnabledForCourse(courseId: number, preferCache: boolean = true): Promise { + 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} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. + */ + isPluginViewEnabledForUser(courseId: number, userId?: number): Promise { + // 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} Resolved on success. + */ + markCourseAsSelfCompleted(courseId: number): Promise { + 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); + } + }); + } +} diff --git a/src/addon/coursecompletion/providers/user-handler.ts b/src/addon/coursecompletion/providers/user-handler.ts new file mode 100644 index 000000000..8faf49ccd --- /dev/null +++ b/src/addon/coursecompletion/providers/user-handler.ts @@ -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} Whether or not the handler is enabled on a site level. + */ + isEnabled(): boolean | Promise { + 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} Promise resolved with true if enabled, resolved with false otherwise. + */ + isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { + 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 }); + } + }; + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b63339826..ca2323574 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -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, From 77c0066f637fd1079f2576579fc1d49e890408e9 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Mon, 18 Jun 2018 13:10:03 +0200 Subject: [PATCH 3/3] MOBILE-2431 calendar: Fix category events --- src/addon/calendar/pages/event/event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addon/calendar/pages/event/event.ts b/src/addon/calendar/pages/event/event.ts index 0be9e4935..14d7a3f44 100644 --- a/src/addon/calendar/pages/event/event.ts +++ b/src/addon/calendar/pages/event/event.ts @@ -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;