From 41f308e62eb126d6a792e7b3e2067e872ec29f64 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 27 Jan 2021 11:31:17 +0100 Subject: [PATCH] MOBILE-3661 grades: Migrate grades handlers --- src/core/directives/link.ts | 5 +- .../contentlinks/classes/base-handler.ts | 3 + .../services/course-options-delegate.ts | 5 +- src/core/features/grades/grades.module.ts | 18 ++- .../grades/services/handlers/course-option.ts | 113 +++++++++++++++++ .../grades/services/handlers/overview-link.ts | 57 +++++++++ .../grades/services/handlers/user-link.ts | 82 ++++++++++++ .../features/grades/services/handlers/user.ts | 118 ++++++++++++++++++ 8 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 src/core/features/grades/services/handlers/course-option.ts create mode 100644 src/core/features/grades/services/handlers/overview-link.ts create mode 100644 src/core/features/grades/services/handlers/user-link.ts create mode 100644 src/core/features/grades/services/handlers/user.ts diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 40efb60b9..9d31dfdda 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -58,7 +58,7 @@ export class CoreLinkDirective implements OnInit { // @todo: Handle split view? - this.element.addEventListener('click', (event) => { + this.element.addEventListener('click', async (event) => { if (event.defaultPrevented) { return; // Link already treated, stop. } @@ -77,7 +77,8 @@ export class CoreLinkDirective implements OnInit { if (CoreUtils.instance.isTrueOrOne(this.capture)) { href = CoreTextUtils.instance.decodeURI(href); - const treated = CoreContentLinksHelper.instance.handleLink(href, undefined, true, true); + const treated = await CoreContentLinksHelper.instance.handleLink(href, undefined, true, true); + if (!treated) { this.navigate(href, openIn); } diff --git a/src/core/features/contentlinks/classes/base-handler.ts b/src/core/features/contentlinks/classes/base-handler.ts index e216559d8..0e0e43d3a 100644 --- a/src/core/features/contentlinks/classes/base-handler.ts +++ b/src/core/features/contentlinks/classes/base-handler.ts @@ -58,6 +58,7 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler { * @param url The URL to treat. * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} * @param courseId Course ID related to the URL. Optional but recommended. + * @param data Extra data to handle the URL. * @return List of (or promise resolved with list of) actions. */ getActions( @@ -69,6 +70,8 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler { params: Params, // eslint-disable-next-line @typescript-eslint/no-unused-vars courseId?: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + data?: unknown, ): CoreContentLinksAction[] | Promise { return []; } diff --git a/src/core/features/course/services/course-options-delegate.ts b/src/core/features/course/services/course-options-delegate.ts index 9c28d0071..2b13c9efe 100644 --- a/src/core/features/course/services/course-options-delegate.ts +++ b/src/core/features/course/services/course-options-delegate.ts @@ -48,7 +48,7 @@ export interface CoreCourseOptionsHandler extends CoreDelegateHandler { * @return True or promise resolved with true if enabled. */ isEnabledForCourse(courseId: number, - accessData: any, // @todo: define type. + accessData: CoreCourseAccessData, navOptions?: CoreCourseUserAdminOrNavOptionIndexed, admOptions?: CoreCourseUserAdminOrNavOptionIndexed, ): boolean | Promise; @@ -672,3 +672,6 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate { CoreMainMenuDelegate.instance.registerHandler(CoreGradesMainMenuHandler.instance); + CoreUserDelegate.instance.registerHandler(CoreGradesUserHandler.instance); + CoreContentLinksDelegate.instance.registerHandler(CoreGradesUserLinkHandler.instance); + CoreContentLinksDelegate.instance.registerHandler(CoreGradesOverviewLinkHandler.instance); + CoreCourseOptionsDelegate.instance.registerHandler(CoreGradesCourseOptionHandler.instance); }, }, ], diff --git a/src/core/features/grades/services/handlers/course-option.ts b/src/core/features/grades/services/handlers/course-option.ts new file mode 100644 index 000000000..5f5f050c7 --- /dev/null +++ b/src/core/features/grades/services/handlers/course-option.ts @@ -0,0 +1,113 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreCourseProvider } from '@features/course/services/course'; +import { + CoreCourseAccessData, + CoreCourseOptionsHandler, + CoreCourseOptionsHandlerData, +} from '@features/course/services/course-options-delegate'; +import { CoreCourses, CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses'; +import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper'; +import { makeSingleton } from '@singletons'; +import { CoreGrades } from '../grades'; + +/** + * Course nav handler. + */ +@Injectable({ providedIn: 'root' }) +export class CoreGradesCourseOptionHandlerService implements CoreCourseOptionsHandler { + + name = 'CoreGrades'; + priority = 400; + + /** + * Should invalidate the data to determine if the handler is enabled for a certain course. + * + * @param courseId The course ID. + * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @return Promise resolved when done. + */ + invalidateEnabledForCourse(courseId: number, navOptions?: CoreCourseUserAdminOrNavOptionIndexed): Promise { + if (navOptions && typeof navOptions.grades != 'undefined') { + // No need to invalidate anything. + return Promise.resolve(); + } + + return CoreCourses.instance.invalidateUserCourses(); + } + + /** + * Check if the handler is enabled on a site level. + * + * @return Whether or not the handler is enabled on a site level. + */ + isEnabled(): Promise { + return Promise.resolve(true); + } + + /** + * Whether or not the handler is enabled for a certain course. + * + * @param courseId The course ID. + * @param accessData Access type and data. Default, guest, ... + * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @return True or promise resolved with true if enabled. + */ + isEnabledForCourse( + courseId: number, + accessData: CoreCourseAccessData, + navOptions?: CoreCourseUserAdminOrNavOptionIndexed, + ): boolean | Promise { + if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) { + return false; // Not enabled for guests. + } + + if (navOptions && typeof navOptions.grades != 'undefined') { + return navOptions.grades; + } + + return CoreGrades.instance.isPluginEnabledForCourse(courseId); + } + + /** + * Returns the data needed to render the handler. + * + * @return Data or promise resolved with the data. + */ + getDisplayData(): CoreCourseOptionsHandlerData | Promise { + throw new Error('CoreGradesCourseOptionHandler.getDisplayData is not implemented'); + + // @todo + // return { + // title: 'core.grades.grades', + // class: 'core-grades-course-handler', + // component: CoreGradesCourseComponent, + // }; + } + + /** + * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. + * + * @param course The course. + * @return Promise resolved when done. + */ + async prefetch(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise { + await CoreGrades.instance.getCourseGradesTable(course.id, undefined, undefined, true); + } + +} + +export class CoreGradesCourseOptionHandler extends makeSingleton(CoreGradesCourseOptionHandlerService) {} diff --git a/src/core/features/grades/services/handlers/overview-link.ts b/src/core/features/grades/services/handlers/overview-link.ts new file mode 100644 index 000000000..325dff4a9 --- /dev/null +++ b/src/core/features/grades/services/handlers/overview-link.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { makeSingleton } from '@singletons'; +import { CoreGrades } from '../grades'; + +/** + * Handler to treat links to overview courses grades. + */ +@Injectable({ providedIn: 'root' }) +export class CoreGradesOverviewLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'CoreGradesOverviewLinkHandler'; + pattern = /\/grade\/report\/overview\/index.php/; + + /** + * Get the list of actions for a link (url). + * + * @return List of (or promise resolved with list of) actions. + */ + getActions(): CoreContentLinksAction[] | Promise { + return [{ + action: siteId => { + CoreNavigator.instance.navigateToSitePath('/grades', { siteId }); + }, + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param siteId The site ID. + * @return Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string): boolean | Promise { + return CoreGrades.instance.isCourseGradesEnabled(siteId); + } + +} + +export class CoreGradesOverviewLinkHandler extends makeSingleton(CoreGradesOverviewLinkHandlerService) {} diff --git a/src/core/features/grades/services/handlers/user-link.ts b/src/core/features/grades/services/handlers/user-link.ts new file mode 100644 index 000000000..e6a3116d5 --- /dev/null +++ b/src/core/features/grades/services/handlers/user-link.ts @@ -0,0 +1,82 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { Params } from '@angular/router'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreGrades } from '@features/grades/services/grades'; +import { CoreGradesHelper } from '@features/grades/services/grades-helper'; +import { makeSingleton } from '@singletons'; + +/** + * Handler to treat links to user grades. + */ +@Injectable({ providedIn: 'root' }) +export class CoreGradesUserLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'CoreGradesUserLinkHandler'; + pattern = /\/grade\/report(\/user)?\/index.php/; + + /** + * Get the list of actions for a link (url). + * + * @param siteIds List of sites the URL belongs to. + * @param url The URL to treat. + * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param courseId Course ID related to the URL. Optional but recommended. + * @param data Extra data to handle the URL. + * @return List of (or promise resolved with list of) actions. + */ + getActions( + siteIds: string[], + url: string, + params: Params, + courseId?: number, + data?: { cmid?: string }, + ): CoreContentLinksAction[] | Promise { + courseId = courseId || params.id; + data = data || {}; + + return [{ + action: (siteId, navCtrl?): void => { + const userId = params.userid && parseInt(params.userid, 10); + const moduleId = data?.cmid && parseInt(data.cmid, 10) || undefined; + + CoreGradesHelper.instance.goToGrades(courseId!, userId, moduleId, navCtrl, siteId); + }, + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param siteId The site ID. + * @param url The URL to treat. + * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param courseId Course ID related to the URL. Optional but recommended. + * @return Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string, url: string, params: Params, courseId?: number): boolean | Promise { + if (!courseId && !params.id) { + return false; + } + + return CoreGrades.instance.isPluginEnabledForCourse(courseId || params.id, siteId); + } + +} + +export class CoreGradesUserLinkHandler extends makeSingleton(CoreGradesUserLinkHandlerService) {} diff --git a/src/core/features/grades/services/handlers/user.ts b/src/core/features/grades/services/handlers/user.ts new file mode 100644 index 000000000..2895a2fe4 --- /dev/null +++ b/src/core/features/grades/services/handlers/user.ts @@ -0,0 +1,118 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreGrades } from '@features/grades/services/grades'; +import { CoreUserProfile } from '@features/user/services/user'; +import { + CoreUserDelegateService , + CoreUserProfileHandler, + CoreUserProfileHandlerData, +} from '@features/user/services/user-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { CoreUtils } from '@services/utils/utils'; +import { makeSingleton } from '@singletons'; + +/** + * Profile grades handler. + */ +@Injectable({ providedIn: 'root' }) +export class CoreGradesUserHandlerService implements CoreUserProfileHandler { + + name = 'CoreGrades:viewGrades'; + priority = 400; + type = CoreUserDelegateService.TYPE_NEW_PAGE; + viewGradesEnabledCache = {}; + + /** + * Clear view grades cache. + * If a courseId and userId are specified, it will only delete the entry for that user and course. + * + * @param courseId Course ID. + * @param userId User ID. + */ + clearViewGradesCache(courseId?: number, userId?: number): void { + if (courseId && userId) { + delete this.viewGradesEnabledCache[this.getCacheKey(courseId, userId)]; + } else { + this.viewGradesEnabledCache = {}; + } + } + + /** + * Get a cache key to identify a course and a user. + * + * @param courseId Course ID. + * @param userId User ID. + * @return Cache key. + */ + protected getCacheKey(courseId: number, userId: number): string { + return courseId + '#' + userId; + } + + /** + * Check if handler is enabled. + * + * @return Always enabled. + */ + isEnabled(): Promise { + return Promise.resolve(true); + } + + /** + * Check if handler is enabled for this user in this context. + * + * @param user User to check. + * @param courseId Course ID. + * @return Promise resolved with true if enabled, resolved with false otherwise. + */ + async isEnabledForUser(user: CoreUserProfile, courseId: number): Promise { + const cacheKey = this.getCacheKey(courseId, user.id); + const cache = this.viewGradesEnabledCache[cacheKey]; + + if (typeof cache != 'undefined') { + return cache; + } + + const enabled = await CoreUtils.instance.ignoreErrors(CoreGrades.instance.isPluginEnabledForCourse(courseId), false); + + this.viewGradesEnabledCache[cacheKey] = enabled; + + return enabled; + } + + /** + * Returns the data needed to render the handler. + * + * @return Data needed to render the handler. + */ + getDisplayData(): CoreUserProfileHandlerData { + return { + icon: 'stats-chart', + title: 'core.grades.grades', + class: 'core-grades-user-handler', + action: (event, user, courseId): void => { + event.preventDefault(); + event.stopPropagation(); + CoreNavigator.instance.navigateToSitePath(`/grades/${courseId}`, { + params: { userId: user.id }, + }); + }, + }; + } + +} + +export class CoreGradesUserHandler extends makeSingleton(CoreGradesUserHandlerService) {}