From b3492a5fbfeb3d59267cf277e60558963d74237f Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 24 Apr 2024 16:30:35 +0200 Subject: [PATCH] MOBILE-4577 course: Implement course section link handler --- src/core/features/courses/courses.module.ts | 2 + .../services/handlers/base-link-handler.ts | 108 +++++++++++++++++ .../courses/services/handlers/course-link.ts | 92 +-------------- .../courses/services/handlers/section-link.ts | 110 ++++++++++++++++++ 4 files changed, 223 insertions(+), 89 deletions(-) create mode 100644 src/core/features/courses/services/handlers/base-link-handler.ts create mode 100644 src/core/features/courses/services/handlers/section-link.ts diff --git a/src/core/features/courses/courses.module.ts b/src/core/features/courses/courses.module.ts index 1a9faaa6b..7cc29f32d 100644 --- a/src/core/features/courses/courses.module.ts +++ b/src/core/features/courses/courses.module.ts @@ -38,6 +38,7 @@ import { } from './services/handlers/my-courses-mainmenu'; import { CoreCoursesRequestPushClickHandler } from './services/handlers/request-push-click'; import { CoreCoursesMyCoursesLinkHandler } from './services/handlers/my-courses-link'; +import { CoreCoursesSectionLinkHandler } from '@features/courses/services/handlers/section-link'; /** * Get courses services. @@ -89,6 +90,7 @@ const routes: Routes = [ CoreContentLinksDelegate.registerHandler(CoreCoursesIndexLinkHandler.instance); CoreContentLinksDelegate.registerHandler(CoreCoursesMyCoursesLinkHandler.instance); CoreContentLinksDelegate.registerHandler(CoreCoursesDashboardLinkHandler.instance); + CoreContentLinksDelegate.registerHandler(CoreCoursesSectionLinkHandler.instance); CorePushNotificationsDelegate.registerClickHandler(CoreCoursesEnrolPushClickHandler.instance); CorePushNotificationsDelegate.registerClickHandler(CoreCoursesRequestPushClickHandler.instance); diff --git a/src/core/features/courses/services/handlers/base-link-handler.ts b/src/core/features/courses/services/handlers/base-link-handler.ts new file mode 100644 index 000000000..80880f4ee --- /dev/null +++ b/src/core/features/courses/services/handlers/base-link-handler.ts @@ -0,0 +1,108 @@ +// (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 { CoreDomUtils } from '@services/utils/dom'; +import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreCourseHelper } from '@features/course/services/course-helper'; +import { CoreCourses } from '../courses'; +import { Params } from '@angular/router'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreNavigator } from '@services/navigator'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreSites } from '@services/sites'; +import { CoreCourse } from '@features/course/services/course'; + +/** + * Base handler to treat course links. + */ +export class CoreCoursesLinksHandlerBase extends CoreContentLinksHandlerBase { + + /** + * Get actions to open course content. + */ + protected getCourseActions(url: string, courseId: number, pageParams: Params = {}): CoreContentLinksAction[] { + return [{ + action: async (siteId): Promise => { + siteId = siteId || CoreSites.getCurrentSiteId(); + if (siteId === CoreSites.getCurrentSiteId()) { + // Check if we already are in the course index page. + if (CoreCourse.currentViewIsCourse(courseId)) { + // Current view is this course, just select the contents tab. + CoreCourse.selectCourseTab('', pageParams); + + return; + } + + await CoreUtils.ignoreErrors(this.actionOpen(courseId, url, pageParams)); + + return; + } + + // Make the course the new history root (to avoid "loops" in history). + await CoreCourseHelper.getAndOpenCourse(courseId, pageParams, siteId); + }, + }]; + } + + /** + * Try to open the course, asking the user to enrol if needed. + * + * @param courseId Course ID. + * @param url Treated URL. + * @param pageParams Params to send to the new page. + * @returns Promise resolved when done. + */ + protected async actionOpen(courseId: number, url: string, pageParams: Params): Promise { + const isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/); + if (isEnrolUrl) { + this.navigateCourseSummary(courseId, pageParams); + + return; + } + + const modal = await CoreDomUtils.showModalLoading(); + + // Check if user is enrolled in the course. + const hasAccess = await CoreCourseHelper.userHasAccessToCourse(courseId); + + const guestInfo = await CoreCourseHelper.courseUsesGuestAccessInfo(courseId); + pageParams.isGuest = guestInfo.guestAccess; + + if (hasAccess && !guestInfo.guestAccess && !guestInfo.requiresUserInput) { + // Direct access. + const course = await CoreUtils.ignoreErrors(CoreCourses.getUserCourse(courseId), { id: courseId }); + + CoreCourseHelper.openCourse(course, { params: pageParams }); + } else { + this.navigateCourseSummary(courseId, pageParams); + + } + + modal.dismiss(); + } + + /** + * Navigate course summary. + * + * @param courseId Course ID. + * @param pageParams Params to send to the new page. + */ + protected navigateCourseSummary(courseId: number, pageParams: Params): void { + CoreNavigator.navigateToSitePath( + `/course/${courseId}/summary`, + { params: pageParams }, + ); + } + +} diff --git a/src/core/features/courses/services/handlers/course-link.ts b/src/core/features/courses/services/handlers/course-link.ts index 9903c7ecd..7beb42f21 100644 --- a/src/core/features/courses/services/handlers/course-link.ts +++ b/src/core/features/courses/services/handlers/course-link.ts @@ -14,36 +14,20 @@ import { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; -import { CoreDomUtils } from '@services/utils/dom'; -import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; -import { CoreCourse } from '@features/course/services/course'; -import { CoreCourseHelper } from '@features/course/services/course-helper'; -import { CoreCourses } from '../courses'; -import { CoreLogger } from '@singletons/logger'; import { makeSingleton } from '@singletons'; import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; import { Params } from '@angular/router'; -import { CoreUtils } from '@services/utils/utils'; -import { CoreNavigator } from '@services/navigator'; +import { CoreCoursesLinksHandlerBase } from '@features/courses/services/handlers/base-link-handler'; /** * Handler to treat links to course view or enrol (except site home). */ @Injectable({ providedIn: 'root' }) -export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandlerBase { +export class CoreCoursesCourseLinkHandlerService extends CoreCoursesLinksHandlerBase { name = 'CoreCoursesCourseLinkHandler'; pattern = /((\/enrol\/index\.php)|(\/course\/enrol\.php)|(\/course\/view\.php)).*([?&]id=\d+)/; - protected waitStart = 0; - protected logger: CoreLogger; - - constructor() { - super(); - - this.logger = CoreLogger.getInstance('CoreCoursesCourseLinkHandler'); - } - /** * @inheritdoc */ @@ -77,27 +61,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler } } - return [{ - action: async (siteId): Promise => { - siteId = siteId || CoreSites.getCurrentSiteId(); - if (siteId === CoreSites.getCurrentSiteId()) { - // Check if we already are in the course index page. - if (CoreCourse.currentViewIsCourse(courseId)) { - // Current view is this course, just select the contents tab. - CoreCourse.selectCourseTab('', pageParams); - - return; - } - - await CoreUtils.ignoreErrors(this.actionOpen(courseId, url, pageParams)); - - return; - } - - // Make the course the new history root (to avoid "loops" in history). - await CoreCourseHelper.getAndOpenCourse(courseId, pageParams, siteId); - }, - }]; + return this.getCourseActions(url, courseId, pageParams); } /** @@ -114,56 +78,6 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler return CoreSites.getSiteHomeId(siteId).then((siteHomeId) => courseId != siteHomeId); } - /** - * Try to open the course, asking the user to enrol if needed. - * - * @param courseId Course ID. - * @param url Treated URL. - * @param pageParams Params to send to the new page. - * @returns Promise resolved when done. - */ - protected async actionOpen(courseId: number, url: string, pageParams: Params): Promise { - const isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/); - if (isEnrolUrl) { - this.navigateCourseSummary(courseId, pageParams); - - return; - } - - const modal = await CoreDomUtils.showModalLoading(); - - // Check if user is enrolled in the course. - const hasAccess = await CoreCourseHelper.userHasAccessToCourse(courseId); - - const guestInfo = await CoreCourseHelper.courseUsesGuestAccessInfo(courseId); - pageParams.isGuest = guestInfo.guestAccess; - - if (hasAccess && !guestInfo.guestAccess && !guestInfo.requiresUserInput) { - // Direct access. - const course = await CoreUtils.ignoreErrors(CoreCourses.getUserCourse(courseId), { id: courseId }); - - CoreCourseHelper.openCourse(course, { params: pageParams }); - } else { - this.navigateCourseSummary(courseId, pageParams); - - } - - modal.dismiss(); - } - - /** - * Navigate course summary. - * - * @param courseId Course ID. - * @param pageParams Params to send to the new page. - */ - protected navigateCourseSummary(courseId: number, pageParams: Params): void { - CoreNavigator.navigateToSitePath( - `/course/${courseId}/summary`, - { params: pageParams }, - ); - } - } export const CoreCoursesCourseLinkHandler = makeSingleton(CoreCoursesCourseLinkHandlerService); diff --git a/src/core/features/courses/services/handlers/section-link.ts b/src/core/features/courses/services/handlers/section-link.ts new file mode 100644 index 000000000..14c9a4a98 --- /dev/null +++ b/src/core/features/courses/services/handlers/section-link.ts @@ -0,0 +1,110 @@ +// (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 { makeSingleton } from '@singletons'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses'; +import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; +import { CoreCourse } from '@features/course/services/course'; +import { CoreCoursesLinksHandlerBase } from '@features/courses/services/handlers/base-link-handler'; +import { CoreLogger } from '@singletons/logger'; + +/** + * Handler to treat links to course section. + */ +@Injectable({ providedIn: 'root' }) +export class CoreCoursesSectionLinkHandlerService extends CoreCoursesLinksHandlerBase { + + name = 'CoreCoursesSectionLinkHandler'; + pattern = /\/course\/section\.php.*([?&]id=\d+)/; + + private logger: CoreLogger; + + constructor() { + super(); + + this.logger = CoreLogger.getInstance('CoreCoursesSectionLinkHandler'); + } + + /** + * @inheritdoc + */ + async getActions( + siteIds: string[], + url: string, + params: Record, + ): Promise { + try { + const siteId = siteIds[0] ?? false; + const sectionId = params.id ? Number(params.id) : false; + const siteHomeId = await CoreSites.getSiteHomeId(siteId); + const course = await this.getSectionCourse(sectionId, siteId); + + if (!sectionId || !course || course.id === siteHomeId) { + return []; + } + + return this.getCourseActions(url, course.id, { sectionId }); + } catch (error) { + this.logger.error(`Failed getting actions for url: '${url}'`, error); + + return []; + } + } + + /** + * Get which course a section belongs to. + * + * @param sectionId Section id. + * @param siteId Site id. + * @returns Course. + */ + private async getSectionCourse(sectionId: number | false, siteId: string | false): Promise { + if (!siteId || !sectionId) { + return null; + } + + // Ideally, we would use a webservice to get this information; but such webservice doesn't exists. + // Given that getting all the courses from a user could be very network intensive, all the requests + // in this method will only use cache. + + const courses = await CoreCourses.getUserCourses(true, siteId, CoreSitesReadingStrategy.ONLY_CACHE); + + for (const course of courses) { + const courseSections = await CoreCourse.getSections( + course.id, + true, + true, + { + ...CoreSites.getReadingStrategyPreSets(CoreSitesReadingStrategy.ONLY_CACHE), + getCacheUsingCacheKey: true, + }, + ); + + const courseSection = courseSections.find(section => section.id === sectionId); + + if (!courseSection) { + continue; + } + + return course; + } + + return null; + } + +} + +export const CoreCoursesSectionLinkHandler = makeSingleton(CoreCoursesSectionLinkHandlerService);