diff --git a/src/addons/badges/pages/issued-badge/issued-badge.page.ts b/src/addons/badges/pages/issued-badge/issued-badge.page.ts index 038dcc365..959ac2dac 100644 --- a/src/addons/badges/pages/issued-badge/issued-badge.page.ts +++ b/src/addons/badges/pages/issued-badge/issued-badge.page.ts @@ -52,7 +52,7 @@ export class AddonBadgesIssuedBadgePage implements OnInit { */ ngOnInit(): void { this.courseId = CoreNavigator.getRouteNumberParam('courseId') || this.courseId; // Use 0 for site badges. - this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSite()!.getUserId(); + this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getRequiredCurrentSite().getUserId(); this.badgeHash = CoreNavigator.getRouteParam('badgeHash') || ''; this.fetchIssuedBadge().finally(() => { diff --git a/src/addons/block/timeline/components/timeline/timeline.ts b/src/addons/block/timeline/components/timeline/timeline.ts index 9c5547050..29f2e1487 100644 --- a/src/addons/block/timeline/components/timeline/timeline.ts +++ b/src/addons/block/timeline/components/timeline/timeline.ts @@ -69,12 +69,12 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen * Component being initialized. */ async ngOnInit(): Promise { - this.currentSite = CoreSites.getCurrentSite(); + this.currentSite = CoreSites.getRequiredCurrentSite(); - this.filter = await this.currentSite!.getLocalSiteConfig('AddonBlockTimelineFilter', this.filter); + this.filter = await this.currentSite.getLocalSiteConfig('AddonBlockTimelineFilter', this.filter); this.switchFilter(this.filter); - this.sort = await this.currentSite!.getLocalSiteConfig('AddonBlockTimelineSort', this.sort); + this.sort = await this.currentSite.getLocalSiteConfig('AddonBlockTimelineSort', this.sort); super.ngOnInit(); } diff --git a/src/addons/calendar/calendar-lazy.module.ts b/src/addons/calendar/calendar-lazy.module.ts index 5ead2dbca..20af6f9a7 100644 --- a/src/addons/calendar/calendar-lazy.module.ts +++ b/src/addons/calendar/calendar-lazy.module.ts @@ -13,22 +13,11 @@ // limitations under the License. import { Injector, NgModule } from '@angular/core'; -import { Route, RouterModule, ROUTES, Routes } from '@angular/router'; +import { RouterModule, ROUTES, Routes } from '@angular/router'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; import { AddonCalendarMainMenuHandlerService } from './services/handlers/mainmenu'; -export const AddonCalendarEditRoute: Route = { - path: 'edit/:eventId', - loadChildren: () => - import('@/addons/calendar/pages/edit-event/edit-event.module').then(m => m.AddonCalendarEditEventPageModule), -}; - -export const AddonCalendarEventRoute: Route ={ - path: 'event/:id', - loadChildren: () => import('@/addons/calendar/pages/event/event.module').then(m => m.AddonCalendarEventPageModule), -}; - function buildRoutes(injector: Injector): Routes { return [ { @@ -48,8 +37,15 @@ function buildRoutes(injector: Injector): Routes { loadChildren: () => import('@/addons/calendar/pages/day/day.module').then(m => m.AddonCalendarDayPageModule), }, - AddonCalendarEventRoute, - AddonCalendarEditRoute, + { + path: 'event/:id', + loadChildren: () => import('@/addons/calendar/pages/event/event.module').then(m => m.AddonCalendarEventPageModule), + }, + { + path: 'edit/:eventId', + loadChildren: () => + import('@/addons/calendar/pages/edit-event/edit-event.module').then(m => m.AddonCalendarEditEventPageModule), + }, ...buildTabMainRoutes(injector, { redirectTo: 'index', pathMatch: 'full', diff --git a/src/addons/calendar/pages/edit-event/edit-event.page.ts b/src/addons/calendar/pages/edit-event/edit-event.page.ts index fa21d737d..366e07fa9 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.page.ts +++ b/src/addons/calendar/pages/edit-event/edit-event.page.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, ViewChild, ElementRef, Optional } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'; import { IonRefresher } from '@ionic/angular'; import { CoreEvents } from '@singletons/events'; @@ -23,7 +23,6 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { CoreCategoryData, CoreCourses, CoreCourseSearchedData, CoreEnrolledCourseData } from '@features/courses/services/courses'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreEditorRichTextEditorComponent } from '@features/editor/components/rich-text-editor/rich-text-editor'; import { AddonCalendarProvider, @@ -94,9 +93,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { constructor( protected fb: FormBuilder, - @Optional() protected svComponent: CoreSplitViewComponent, ) { - this.currentSite = CoreSites.getCurrentSite()!; + this.currentSite = CoreSites.getRequiredCurrentSite(); this.errors = { required: Translate.instant('core.required'), }; @@ -572,15 +570,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { } } - if (this.svComponent?.outletActivated) { - // Empty form. - this.hasOffline = false; - this.form.reset(this.originalData); - this.originalData = CoreUtils.clone(this.form.value); - } else { - this.originalData = undefined; // Avoid asking for confirmation. - CoreNavigator.back(); - } + this.originalData = undefined; // Avoid asking for confirmation. + CoreNavigator.back(); } /** diff --git a/src/addons/calendar/pages/event/event.html b/src/addons/calendar/pages/event/event.html index 445329e5c..6244bc546 100644 --- a/src/addons/calendar/pages/event/event.html +++ b/src/addons/calendar/pages/event/event.html @@ -15,26 +15,23 @@ [contextInstanceId]="event.contextInstanceId"> - + + + + + + + + - - - - - - - - - - @@ -50,33 +47,13 @@ - - - - - - {{ 'addon.calendar.type' + event.formattedType | translate }} - {{ event.iconTitle }} - -

{{ 'addon.calendar.eventname' | translate }}

-

- -

-
- - {{ 'core.deletedoffline' | translate }} - -

{{ 'addon.calendar.when' | translate }}

- + {{ 'core.deletedoffline' | translate }}
diff --git a/src/addons/calendar/pages/event/event.page.ts b/src/addons/calendar/pages/event/event.page.ts index e92b62b8e..88d4597f9 100644 --- a/src/addons/calendar/pages/event/event.page.ts +++ b/src/addons/calendar/pages/event/event.page.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnDestroy, OnInit, Optional } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { IonRefresher } from '@ionic/angular'; import { AlertOptions } from '@ionic/core'; import { @@ -32,14 +32,12 @@ import { CoreLocalNotifications } from '@services/local-notifications'; import { CoreCourse } from '@features/course/services/course'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreGroups } from '@services/groups'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { Network, NgZone, Translate } from '@singletons'; import { Subscription } from 'rxjs'; import { CoreNavigator } from '@services/navigator'; import { CoreUtils } from '@services/utils/utils'; import { AddonCalendarReminderDBRecord } from '../../services/database/calendar'; import { ActivatedRoute } from '@angular/router'; -import { CoreScreen } from '@services/screen'; import { CoreConstants } from '@/core/constants'; import { CoreLang } from '@services/lang'; @@ -81,18 +79,15 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { hasOffline = false; isOnline = false; syncIcon = CoreConstants.ICON_LOADING; // Sync icon. - isSplitViewOn = false; monthNames?: string[]; constructor( - @Optional() protected svComponent: CoreSplitViewComponent, protected route: ActivatedRoute, ) { this.notificationsEnabled = CoreLocalNotifications.isAvailable(); this.siteHomeId = CoreSites.getCurrentSiteHomeId(); this.currentSiteId = CoreSites.getCurrentSiteId(); - this.isSplitViewOn = this.svComponent?.outletActivated; // Check if site supports editing. No need to check allowed types, event.canedit already does it. this.canEdit = AddonCalendar.canEditEventsInSite(); @@ -147,7 +142,16 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { * View loaded. */ ngOnInit(): void { - this.eventId = CoreNavigator.getRouteNumberParam('id')!; + try { + this.eventId = CoreNavigator.getRequiredRouteNumberParam('id'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + this.syncIcon = CoreConstants.ICON_LOADING; this.fetchEvent(); @@ -471,9 +475,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { CoreDomUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000); // Event deleted, close the view. - if (CoreScreen.isMobile) { - CoreNavigator.back(); - } + CoreNavigator.back(); } else { // Event deleted in offline, just mark it as deleted. this.event.deleted = true; @@ -528,9 +530,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { CoreDomUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000); // Event was deleted, close the view. - if (CoreScreen.isMobile) { - CoreNavigator.back(); - } + CoreNavigator.back(); } else if (data.events && (!isManual || data.source != 'event')) { const event = data.events.find((ev) => ev.id == this.eventId); diff --git a/src/addons/competency/pages/competency/competency.ts b/src/addons/competency/pages/competency/competency.ts index e4ebe432e..f9bd310cc 100644 --- a/src/addons/competency/pages/competency/competency.ts +++ b/src/addons/competency/pages/competency/competency.ts @@ -60,11 +60,19 @@ export class AddonCompetencyCompetencyPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.competencyId = CoreNavigator.getRouteNumberParam('competencyId')!; - this.planId = CoreNavigator.getRouteNumberParam('planId'); - if (!this.planId) { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId'); + try { + this.competencyId = CoreNavigator.getRequiredRouteNumberParam('competencyId'); + this.planId = CoreNavigator.getRouteNumberParam('planId'); + if (!this.planId) { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.userId = CoreNavigator.getRouteNumberParam('userId'); + } + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; } try { diff --git a/src/addons/competency/pages/competencysummary/competencysummary.ts b/src/addons/competency/pages/competencysummary/competencysummary.ts index 451d85553..2d150eb4e 100644 --- a/src/addons/competency/pages/competencysummary/competencysummary.ts +++ b/src/addons/competency/pages/competencysummary/competencysummary.ts @@ -40,9 +40,17 @@ export class AddonCompetencyCompetencySummaryPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.competencyId = CoreNavigator.getRouteNumberParam('competencyId')!; - this.contextLevel = CoreNavigator.getRouteParam('contextLevel'); - this.contextInstanceId = CoreNavigator.getRouteNumberParam('contextInstanceId'); + try { + this.competencyId = CoreNavigator.getRequiredRouteNumberParam('competencyId'); + this.contextLevel = CoreNavigator.getRouteParam('contextLevel'); + this.contextInstanceId = CoreNavigator.getRouteNumberParam('contextInstanceId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { await this.fetchCompetency(); diff --git a/src/addons/competency/pages/coursecompetencies/coursecompetencies.page.ts b/src/addons/competency/pages/coursecompetencies/coursecompetencies.page.ts index 58c91bd56..cbe9a49eb 100644 --- a/src/addons/competency/pages/coursecompetencies/coursecompetencies.page.ts +++ b/src/addons/competency/pages/coursecompetencies/coursecompetencies.page.ts @@ -42,8 +42,16 @@ export class AddonCompetencyCourseCompetenciesPage implements OnInit { * View loaded. */ ngOnInit(): void { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId')!; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.userId = CoreNavigator.getRequiredRouteNumberParam('userId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchCourseCompetencies().finally(() => { this.competenciesLoaded = true; diff --git a/src/addons/competency/pages/plan/plan.ts b/src/addons/competency/pages/plan/plan.ts index 71fedb68a..bf86a1a46 100644 --- a/src/addons/competency/pages/plan/plan.ts +++ b/src/addons/competency/pages/plan/plan.ts @@ -39,7 +39,15 @@ export class AddonCompetencyPlanPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.planId = CoreNavigator.getRouteNumberParam('planId')!; + try { + this.planId = CoreNavigator.getRequiredRouteNumberParam('planId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchLearningPlan().finally(() => { this.loaded = true; diff --git a/src/addons/coursecompletion/pages/report/report.ts b/src/addons/coursecompletion/pages/report/report.ts index 5cda867eb..027d0480e 100644 --- a/src/addons/coursecompletion/pages/report/report.ts +++ b/src/addons/coursecompletion/pages/report/report.ts @@ -44,11 +44,15 @@ export class AddonCourseCompletionReportPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); + } catch (error) { + CoreDomUtils.showErrorModal(error); - if (!this.userId) { - this.userId = CoreSites.getCurrentSiteUserId(); + CoreNavigator.back(); + + return; } this.fetchCompletion().finally(() => { diff --git a/src/addons/messages/services/handlers/user-send-message.ts b/src/addons/messages/services/handlers/user-send-message.ts index 548d69d51..6afaa6080 100644 --- a/src/addons/messages/services/handlers/user-send-message.ts +++ b/src/addons/messages/services/handlers/user-send-message.ts @@ -54,7 +54,7 @@ export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfi * @return Promise resolved with true if enabled, resolved with false otherwise. */ async isEnabledForUser(user: CoreUserProfile): Promise { - const currentSite = CoreSites.getCurrentSite()!; + const currentSite = CoreSites.getRequiredCurrentSite(); // From 3.7 you can send messages to yourself. return user.id != CoreSites.getCurrentSiteUserId() || currentSite.isVersionGreaterEqualThan('3.7'); diff --git a/src/addons/mod/assign/pages/edit/edit.ts b/src/addons/mod/assign/pages/edit/edit.ts index 98884b13e..01ddb7499 100644 --- a/src/addons/mod/assign/pages/edit/edit.ts +++ b/src/addons/mod/assign/pages/edit/edit.ts @@ -78,9 +78,17 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave { * Component being initialized. */ ngOnInit(): void { - this.moduleId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.isBlind = !!CoreNavigator.getRouteNumberParam('blindId'); + try { + this.moduleId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.isBlind = !!CoreNavigator.getRouteNumberParam('blindId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchAssignment().finally(() => { this.loaded = true; diff --git a/src/addons/mod/assign/pages/submission-list/submission-list.page.ts b/src/addons/mod/assign/pages/submission-list/submission-list.page.ts index 86443ad08..90f02a382 100644 --- a/src/addons/mod/assign/pages/submission-list/submission-list.page.ts +++ b/src/addons/mod/assign/pages/submission-list/submission-list.page.ts @@ -120,10 +120,18 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro * Component being initialized. */ ngAfterViewInit(): void { - this.moduleId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.groupId = CoreNavigator.getRouteNumberParam('groupId') || 0; - this.selectedStatus = CoreNavigator.getRouteParam('status'); + try { + this.moduleId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.groupId = CoreNavigator.getRouteNumberParam('groupId') || 0; + this.selectedStatus = CoreNavigator.getRouteParam('status'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } if (this.selectedStatus) { if (this.selectedStatus == AddonModAssignProvider.NEED_GRADING) { diff --git a/src/addons/mod/assign/pages/submission-review/submission-review.ts b/src/addons/mod/assign/pages/submission-review/submission-review.ts index 831dea8b8..803074c9f 100644 --- a/src/addons/mod/assign/pages/submission-review/submission-review.ts +++ b/src/addons/mod/assign/pages/submission-review/submission-review.ts @@ -55,10 +55,18 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, CanLeave { */ ngOnInit(): void { this.route.queryParams.subscribe((params) => { - this.moduleId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.submitId = CoreNavigator.getRouteNumberParam('submitId') || 0; - this.blindId = CoreNavigator.getRouteNumberParam('blindId', { params }); + try { + this.moduleId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.submitId = CoreNavigator.getRouteNumberParam('submitId') || 0; + this.blindId = CoreNavigator.getRouteNumberParam('blindId', { params }); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchSubmission().finally(() => { this.loaded = true; diff --git a/src/addons/mod/assign/services/handlers/module.ts b/src/addons/mod/assign/services/handlers/module.ts index 781ef2900..7b1395e0f 100644 --- a/src/addons/mod/assign/services/handlers/module.ts +++ b/src/addons/mod/assign/services/handlers/module.ts @@ -14,23 +14,22 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { AddonModAssignIndexComponent } from '../../components/index'; import { makeSingleton } from '@singletons'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support assign modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModAssignModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModAssignModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_assign'; name = 'AddonModAssign'; modName = 'assign'; + protected pageName = AddonModAssignModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -48,42 +47,7 @@ export class AddonModAssignModuleHandlerService implements CoreCourseModuleHandl }; /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - async isEnabled(): Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @return Data to render the module. - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_assign-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModAssignModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @return The component to use, undefined if not found. + * @inheritdoc */ async getMainComponent(): Promise | undefined> { return AddonModAssignIndexComponent; diff --git a/src/addons/mod/book/services/book.ts b/src/addons/mod/book/services/book.ts index 7abf1dead..edf9c5800 100644 --- a/src/addons/mod/book/services/book.ts +++ b/src/addons/mod/book/services/book.ts @@ -134,7 +134,7 @@ export class AddonModBookProvider { if (!CoreFile.isAvailable()) { // We return the live URL. - return CoreSites.getCurrentSite()!.checkAndFixPluginfileURL(indexUrl); + return CoreSites.getRequiredCurrentSite().checkAndFixPluginfileURL(indexUrl); } const siteId = CoreSites.getCurrentSiteId(); diff --git a/src/addons/mod/book/services/handlers/module.ts b/src/addons/mod/book/services/handlers/module.ts index 4ce95616e..ca5fcdd4d 100644 --- a/src/addons/mod/book/services/handlers/module.ts +++ b/src/addons/mod/book/services/handlers/module.ts @@ -15,23 +15,22 @@ import { Injectable, Type } from '@angular/core'; import { AddonModBookIndexComponent } from '../../components/index'; import { AddonModBook } from '../book'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; -import { CoreCourseModule } from '@features/course/services/course-helper'; import { CoreConstants } from '@/core/constants'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support book modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModBookModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModBookModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_book'; name = 'AddonModBook'; modName = 'book'; + protected pageName = AddonModBookModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -46,45 +45,14 @@ export class AddonModBookModuleHandlerService implements CoreCourseModuleHandler }; /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. + * @inheritdoc */ isEnabled(): Promise { return AddonModBook.isPluginEnabled(); } /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_book-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModBookModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @return The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ async getMainComponent(): Promise | undefined> { return AddonModBookIndexComponent; diff --git a/src/addons/mod/chat/pages/chat/chat.ts b/src/addons/mod/chat/pages/chat/chat.ts index 76ffdeb6b..809d5106b 100644 --- a/src/addons/mod/chat/pages/chat/chat.ts +++ b/src/addons/mod/chat/pages/chat/chat.ts @@ -78,12 +78,12 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave { * @inheritdoc */ async ngOnInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.chatId = CoreNavigator.getRouteNumberParam('chatId')!; - this.title = CoreNavigator.getRouteParam('title') || ''; - try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.chatId = CoreNavigator.getRequiredRouteNumberParam('chatId'); + this.title = CoreNavigator.getRouteParam('title') || ''; + await this.loginUser(); await this.fetchMessages(); diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.ts b/src/addons/mod/chat/pages/session-messages/session-messages.ts index 00b7e8a71..02d185a30 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.ts +++ b/src/addons/mod/chat/pages/session-messages/session-messages.ts @@ -47,12 +47,20 @@ export class AddonModChatSessionMessagesPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.sessionStart = CoreNavigator.getRouteNumberParam('sessionStart')!; - this.sessionEnd = CoreNavigator.getRouteNumberParam('sessionEnd')!; - this.chatId = CoreNavigator.getRouteNumberParam('chatId')!; - this.groupId = CoreNavigator.getRouteNumberParam('groupId') || 0; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.sessionStart = CoreNavigator.getRequiredRouteNumberParam('sessionStart'); + this.sessionEnd = CoreNavigator.getRequiredRouteNumberParam('sessionEnd'); + this.chatId = CoreNavigator.getRequiredRouteNumberParam('chatId'); + this.groupId = CoreNavigator.getRouteNumberParam('groupId') || 0; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.currentUserId = CoreSites.getCurrentSiteUserId(); diff --git a/src/addons/mod/chat/pages/sessions/sessions.ts b/src/addons/mod/chat/pages/sessions/sessions.ts index c579fcb35..c262d4d74 100644 --- a/src/addons/mod/chat/pages/sessions/sessions.ts +++ b/src/addons/mod/chat/pages/sessions/sessions.ts @@ -53,10 +53,18 @@ export class AddonModChatSessionsPage implements AfterViewInit, OnDestroy { * @inheritdoc */ async ngAfterViewInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.chatId = CoreNavigator.getRouteNumberParam('chatId')!; - this.sessions.setChatId(this.chatId); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.chatId = CoreNavigator.getRequiredRouteNumberParam('chatId'); + this.sessions.setChatId(this.chatId); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } await this.fetchSessions(); diff --git a/src/addons/mod/chat/services/handlers/module.ts b/src/addons/mod/chat/services/handlers/module.ts index 451bda34d..16cfa87aa 100644 --- a/src/addons/mod/chat/services/handlers/module.ts +++ b/src/addons/mod/chat/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModChatIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModChatIndexComponent } from '../../components/index'; * Handler to support chat modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModChatModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModChatModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_chat'; name = 'AddonModChat'; modName = 'chat'; + protected pageName = AddonModChatModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -43,35 +42,6 @@ export class AddonModChatModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - const data: CoreCourseModuleHandlerData = { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_chat-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModChatModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - - return data; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/choice/services/handlers/module.ts b/src/addons/mod/choice/services/handlers/module.ts index 5f6d333cb..532204f81 100644 --- a/src/addons/mod/choice/services/handlers/module.ts +++ b/src/addons/mod/choice/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModChoiceIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModChoiceIndexComponent } from '../../components/index'; * Handler to support choice modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModChoiceModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModChoiceModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_choice'; name = 'AddonModChoice'; modName = 'choice'; + protected pageName = AddonModChoiceModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -44,33 +43,6 @@ export class AddonModChoiceModuleHandlerService implements CoreCourseModuleHandl [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_choice-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModChoiceModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/data/components/field-plugin/field-plugin.ts b/src/addons/mod/data/components/field-plugin/field-plugin.ts index 28d20115f..74ed1e710 100644 --- a/src/addons/mod/data/components/field-plugin/field-plugin.ts +++ b/src/addons/mod/data/components/field-plugin/field-plugin.ts @@ -53,7 +53,7 @@ export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { return; } - try{ + try { // Check if the plugin has defined its own component to render itself. this.fieldComponent = await AddonModDataFieldsDelegate.getComponentForField(this.field); diff --git a/src/addons/mod/data/pages/edit/edit.ts b/src/addons/mod/data/pages/edit/edit.ts index 2b1786d41..5af31f6ed 100644 --- a/src/addons/mod/data/pages/edit/edit.ts +++ b/src/addons/mod/data/pages/edit/edit.ts @@ -96,10 +96,18 @@ export class AddonModDataEditPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.module = CoreNavigator.getRouteParam('module')!; - this.entryId = CoreNavigator.getRouteNumberParam('entryId') || undefined; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + try { + this.module = CoreNavigator.getRequiredRouteParam('module'); + this.entryId = CoreNavigator.getRouteNumberParam('entryId') || undefined; + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } // If entryId is lower than 0 or null, it is a new entry or an offline entry. this.isEditing = typeof this.entryId != 'undefined' && this.entryId > 0; diff --git a/src/addons/mod/data/pages/entry/entry.ts b/src/addons/mod/data/pages/entry/entry.ts index c9c813307..95e37b326 100644 --- a/src/addons/mod/data/pages/entry/entry.ts +++ b/src/addons/mod/data/pages/entry/entry.ts @@ -132,11 +132,20 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy { * @inheritdoc */ async ngOnInit(): Promise { - this.module = CoreNavigator.getRouteParam('module')!; - this.entryId = CoreNavigator.getRouteNumberParam('entryId') || undefined; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; - this.offset = CoreNavigator.getRouteNumberParam('offset'); + try { + this.module = CoreNavigator.getRequiredRouteParam('module'); + this.entryId = CoreNavigator.getRouteNumberParam('entryId') || undefined; + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + this.offset = CoreNavigator.getRouteNumberParam('offset'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + this.title = this.module.name; this.commentsEnabled = !CoreComments.areCommentsDisabledInSite(); diff --git a/src/addons/mod/data/services/data-helper.ts b/src/addons/mod/data/services/data-helper.ts index c0a0cc74f..869f2be38 100644 --- a/src/addons/mod/data/services/data-helper.ts +++ b/src/addons/mod/data/services/data-helper.ts @@ -225,7 +225,7 @@ export class AddonModDataHelperProvider { let render = ''; if (action == AddonModDataAction.MOREURL) { // Render more url directly because it can be part of an HTML attribute. - render = CoreSites.getCurrentSite()!.getURL() + '/mod/data/view.php?d={{database.id}}&rid=' + entry.id; + render = CoreSites.getRequiredCurrentSite().getURL() + '/mod/data/view.php?d={{database.id}}&rid=' + entry.id; } else if (action == 'approvalstatus') { render = Translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved')); } else { diff --git a/src/addons/mod/data/services/handlers/module.ts b/src/addons/mod/data/services/handlers/module.ts index 1ccb3d958..dc930ee0f 100644 --- a/src/addons/mod/data/services/handlers/module.ts +++ b/src/addons/mod/data/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModDataIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModDataIndexComponent } from '../../components/index'; * Handler to support data modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModDataModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModDataModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_data'; name = 'AddonModData'; modName = 'data'; + protected pageName = AddonModDataModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -46,33 +45,6 @@ export class AddonModDataModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_COMMENT]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_data-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModDataModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/feedback/pages/attempt/attempt.ts b/src/addons/mod/feedback/pages/attempt/attempt.ts index 52e9736fe..194f22f06 100644 --- a/src/addons/mod/feedback/pages/attempt/attempt.ts +++ b/src/addons/mod/feedback/pages/attempt/attempt.ts @@ -49,9 +49,17 @@ export class AddonModFeedbackAttemptPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.attemptId = CoreNavigator.getRouteNumberParam('attemptId')!; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.attemptId = CoreNavigator.getRequiredRouteNumberParam('attemptId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchData(); } diff --git a/src/addons/mod/feedback/pages/form/form.ts b/src/addons/mod/feedback/pages/form/form.ts index 8d8612304..1a11ebb21 100644 --- a/src/addons/mod/feedback/pages/form/form.ts +++ b/src/addons/mod/feedback/pages/form/form.ts @@ -77,7 +77,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { completedOffline = false; constructor() { - this.currentSite = CoreSites.getCurrentSite()!; + this.currentSite = CoreSites.getRequiredCurrentSite(); // Refresh online status when changes. this.onlineObserver = Network.onChange().subscribe(() => { @@ -92,12 +92,20 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave { * @inheritdoc */ async ngOnInit(): Promise { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.currentPage = CoreNavigator.getRouteNumberParam('page'); - this.title = CoreNavigator.getRouteParam('title'); - this.preview = !!CoreNavigator.getRouteBooleanParam('preview'); - this.fromIndex = !!CoreNavigator.getRouteBooleanParam('fromIndex'); + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.currentPage = CoreNavigator.getRouteNumberParam('page'); + this.title = CoreNavigator.getRouteParam('title'); + this.preview = !!CoreNavigator.getRouteBooleanParam('preview'); + this.fromIndex = !!CoreNavigator.getRouteBooleanParam('fromIndex'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } await this.fetchData(); diff --git a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts index d95111480..12d312bc0 100644 --- a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts +++ b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts @@ -47,9 +47,17 @@ export class AddonModFeedbackNonRespondentsPage implements OnInit { * @inheritdoc */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchData(); } diff --git a/src/addons/mod/feedback/pages/respondents/respondents.ts b/src/addons/mod/feedback/pages/respondents/respondents.ts index 8539b2cb2..3ef2266a7 100644 --- a/src/addons/mod/feedback/pages/respondents/respondents.ts +++ b/src/addons/mod/feedback/pages/respondents/respondents.ts @@ -63,9 +63,17 @@ export class AddonModFeedbackRespondentsPage implements AfterViewInit { * @inheritdoc */ async ngAfterViewInit(): Promise { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } await this.fetchData(); diff --git a/src/addons/mod/feedback/services/handlers/module.ts b/src/addons/mod/feedback/services/handlers/module.ts index 5b7b1342c..da1fedf27 100644 --- a/src/addons/mod/feedback/services/handlers/module.ts +++ b/src/addons/mod/feedback/services/handlers/module.ts @@ -14,23 +14,22 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModFeedbackIndexComponent } from '../../components/index'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support feedback modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModFeedbackModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModFeedbackModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_feedback'; name = 'AddonModFeedback'; modName = 'feedback'; + protected pageName = AddonModFeedbackModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -44,33 +43,6 @@ export class AddonModFeedbackModuleHandlerService implements CoreCourseModuleHan [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_feedback-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModFeedbackModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/folder/services/handlers/module.ts b/src/addons/mod/folder/services/handlers/module.ts index f00241460..b127b468c 100644 --- a/src/addons/mod/folder/services/handlers/module.ts +++ b/src/addons/mod/folder/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModFolderIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModFolderIndexComponent } from '../../components/index'; * Handler to support folder modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModFolderModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModFolderModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_folder'; name = 'AddonModFolder'; modName = 'folder'; + protected pageName = AddonModFolderModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -44,33 +43,6 @@ export class AddonModFolderModuleHandlerService implements CoreCourseModuleHandl [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_folder-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModFolderModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts index 8c7a51b87..fd1662e5a 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts @@ -58,7 +58,7 @@ export class AddonModForumPostOptionsMenuComponent implements OnInit, OnDestroy }); if (this.post.id > 0) { - const site = CoreSites.getCurrentSite()!; + const site = CoreSites.getRequiredCurrentSite(); this.url = site.createSiteUrl('/mod/forum/discuss.php', { d: this.post.discussionid.toString() }, 'p' + this.post.id); this.offlinePost = false; } else { diff --git a/src/addons/mod/forum/pages/discussion/discussion.page.ts b/src/addons/mod/forum/pages/discussion/discussion.page.ts index 44452fe37..3894c38c4 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.page.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.page.ts @@ -65,7 +65,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes discussionId!: number; forum: Partial = {}; accessInfo: AddonModForumAccessInformation = {}; - discussion!: AddonModForumDiscussion; + discussion?: AddonModForumDiscussion; startingPost?: Post; posts!: Post[]; discussionLoaded = false; @@ -100,8 +100,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes leavingPage = false; protected forumId!: number; - protected postId!: number; - protected parent!: number; + protected postId?: number; + protected parent?: number; protected onlineObserver?: Subscription; protected syncObserver?: CoreEventObserver; protected syncManualObserver?: CoreEventObserver; @@ -122,16 +122,25 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes } ngOnInit(): void { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.forumId = CoreNavigator.getRouteNumberParam('forumId')!; - this.discussion = CoreNavigator.getRouteParam('discussion')!; - this.discussionId = this.discussion - ? this.discussion.discussion - : CoreNavigator.getRouteNumberParam('discussionId')!; - this.trackPosts = CoreNavigator.getRouteBooleanParam('trackPosts')!; - this.postId = CoreNavigator.getRouteNumberParam('postId')!; - this.parent = CoreNavigator.getRouteNumberParam('parent')!; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.forumId = CoreNavigator.getRequiredRouteNumberParam('forumId'); + this.discussion = CoreNavigator.getRouteParam('discussion'); + this.discussionId = this.discussion + ? this.discussion.discussion + : CoreNavigator.getRequiredRouteNumberParam('discussionId'); + this.trackPosts = CoreNavigator.getRouteBooleanParam('trackPosts') || false; + this.postId = CoreNavigator.getRouteNumberParam('postId'); + this.parent = CoreNavigator.getRouteNumberParam('parent'); + + } catch (error) { + CoreDomUtils.showErrorModal(error); + + this.goBack(); + + return; + } this.isOnline = CoreApp.isOnline(); this.onlineObserver = Network.onChange().subscribe(() => { @@ -148,11 +157,9 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * View loaded. */ async ngAfterViewInit(): Promise { - if (this.parent) { - this.sort = 'nested'; // Force nested order. - } else { - this.sort = await this.getUserSort(); - } + this.sort = this.parent + ? 'nested' // Force nested order. + : await this.getUserSort(); await this.fetchPosts(true, false, true); @@ -183,6 +190,16 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes await this.loadDiscussion(this.forumId, this.cmId, this.discussionId); } + if (!this.discussion) { + CoreDomUtils.showErrorModal('Cannot get the discussion'); + + this.goBack(); + + return; + } + + const discussion = this.discussion; + // Refresh data if this discussion is synchronized automatically. this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, data => { if (data.forumId == this.forumId && this.discussionId == data.discussionId @@ -204,7 +221,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes }, CoreSites.getCurrentSiteId()); // Invalidate discussion list if it was not read. - if (this.discussion.numunread > 0) { + if (discussion.numunread > 0) { AddonModForum.invalidateDiscussionsList(this.forumId); } @@ -227,22 +244,18 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes if ((this.forumId && this.forumId === data.forumId) || data.cmId === this.cmId) { AddonModForum.invalidateDiscussionsList(this.forumId).finally(() => { if (typeof data.locked != 'undefined') { - this.discussion.locked = data.locked; + discussion.locked = data.locked; } if (typeof data.pinned != 'undefined') { - this.discussion.pinned = data.pinned; + discussion.pinned = data.pinned; } if (typeof data.starred != 'undefined') { - this.discussion.starred = data.starred; + discussion.starred = data.starred; } if (typeof data.deleted != 'undefined' && data.deleted) { if (!data.post?.parentid) { - if (this.splitView?.outletActivated) { - CoreNavigator.navigate('../'); - } else { - CoreNavigator.back(); - } + this.goBack(); } else { this.discussionLoaded = false; this.refreshPosts(); @@ -272,6 +285,17 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes return true; } + /** + * Helper function to go back. + */ + protected goBack(): void { + if (this.splitView?.outletActivated) { + CoreNavigator.navigate('../'); + } else { + CoreNavigator.back(); + } + } + /** * Runs when the page is about to leave and no longer be the active page. */ @@ -298,7 +322,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes */ protected async getUserSort(): Promise { try { - const value = await CoreSites.getCurrentSite()!.getLocalSiteConfig('AddonModForumDiscussionSort'); + const value = await CoreSites.getRequiredCurrentSite().getLocalSiteConfig('AddonModForumDiscussionSort'); return value; } catch (error) { @@ -515,7 +539,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes this.syncIcon = CoreConstants.ICON_SYNC; if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) { - // // Add log in Moodle and mark unread posts as readed. + // Add log in Moodle and mark unread posts as readed. AddonModForum.logDiscussionView(this.discussionId, this.forumId || -1, this.forum.name).catch(() => { // Ignore errors. }).finally(() => { @@ -543,14 +567,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes return; } - try { - const discussion = await AddonModForumHelper.getDiscussionById(forumId, cmId, discussionId); - - this.discussion = discussion; - this.discussionId = this.discussion.discussion; - } catch (error) { - // Ignore errors. - } + this.discussion = await AddonModForumHelper.getDiscussionById(forumId, cmId, discussionId); + this.discussionId = this.discussion.discussion; } /** @@ -630,7 +648,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * @param showErrors Whether to show errors in a modal. * @return Promise resolved when done. */ - refreshPosts(sync?: boolean, showErrors?: boolean): Promise { + async refreshPosts(sync?: boolean, showErrors?: boolean): Promise { this.content.scrollToTop(); this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; @@ -642,9 +660,9 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes AddonModForum.invalidateCanAddDiscussion(this.forumId), ]; - return CoreUtils.allPromises(promises).catch(() => { - // Ignore errors. - }).then(() => this.fetchPosts(sync, showErrors)); + await CoreUtils.ignoreErrors(CoreUtils.allPromises(promises)); + + await this.fetchPosts(sync, showErrors); } /** @@ -656,7 +674,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes changeSort(type: SortType): Promise { this.discussionLoaded = false; this.sort = type; - CoreSites.getCurrentSite()!.setLocalSiteConfig('AddonModForumDiscussionSort', this.sort); + CoreSites.getRequiredCurrentSite().setLocalSiteConfig('AddonModForumDiscussionSort', this.sort); this.content.scrollToTop(); return this.fetchPosts(); @@ -668,6 +686,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * @param locked True to lock the discussion, false to unlock. */ async setLockState(locked: boolean): Promise { + if (!this.discussion) { + return; + } + const modal = await CoreDomUtils.showModalLoading('core.sending', true); try { @@ -696,6 +718,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * @param pinned True to pin the discussion, false to unpin it. */ async setPinState(pinned: boolean): Promise { + if (!this.discussion) { + return; + } + const modal = await CoreDomUtils.showModalLoading('core.sending', true); try { @@ -725,6 +751,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes * @param starred True to star the discussion, false to unstar it. */ async toggleFavouriteState(starred: boolean): Promise { + if (!this.discussion) { + return; + } + const modal = await CoreDomUtils.showModalLoading('core.sending', true); try { diff --git a/src/addons/mod/forum/pages/new-discussion/new-discussion.page.ts b/src/addons/mod/forum/pages/new-discussion/new-discussion.page.ts index 52995909a..f5495fb71 100644 --- a/src/addons/mod/forum/pages/new-discussion/new-discussion.page.ts +++ b/src/addons/mod/forum/pages/new-discussion/new-discussion.page.ts @@ -103,10 +103,18 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea * Component being initialized. */ ngOnInit(): void { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.forumId = CoreNavigator.getRouteNumberParam('forumId')!; - this.timeCreated = CoreNavigator.getRouteNumberParam('timeCreated')!; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.forumId = CoreNavigator.getRequiredRouteNumberParam('forumId'); + this.timeCreated = CoreNavigator.getRequiredRouteNumberParam('timeCreated'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + this.goBack(); + + return; + } this.fetchDiscussionData().finally(() => { this.groupsLoaded = true; @@ -595,6 +603,17 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea delete this.syncObserver; } + /** + * Helper function to go back. + */ + protected goBack(): void { + if (this.splitView?.outletActivated) { + CoreNavigator.navigate('../../'); + } else { + CoreNavigator.back(); + } + } + /** * Page destroyed. */ diff --git a/src/addons/mod/forum/services/handlers/module.ts b/src/addons/mod/forum/services/handlers/module.ts index c9bc2e41e..3b7327923 100644 --- a/src/addons/mod/forum/services/handlers/module.ts +++ b/src/addons/mod/forum/services/handlers/module.ts @@ -14,9 +14,7 @@ import { Injectable, Type } from '@angular/core'; import { AddonModForum, AddonModForumProvider } from '../forum'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreCourseAnyModuleData } from '@features/course/services/course'; import { makeSingleton, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; @@ -24,17 +22,19 @@ import { CoreUtils } from '@services/utils/utils'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreConstants } from '@/core/constants'; import { AddonModForumIndexComponent } from '../../components/index'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support forum modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModForumModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_forum'; name = 'AddonModForum'; modName = 'forum'; + protected pageName = AddonModForumModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -51,39 +51,10 @@ export class AddonModForumModuleHandlerService implements CoreCourseModuleHandle }; /** - * 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); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. + * @inheritdoc */ getData(module: CoreCourseAnyModuleData, courseId: number): CoreCourseModuleHandlerData { - const data: CoreCourseModuleHandlerData = { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_forum-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - - CoreNavigator.navigateToSitePath( - `${AddonModForumModuleHandlerService.PAGE_NAME}/${courseId}/${module.id}`, - options, - ); - }, - }; + const data = super.getData(module, courseId); if ('afterlink' in module && !!module.afterlink) { data.extraBadgeColor = ''; @@ -111,20 +82,14 @@ export class AddonModForumModuleHandlerService implements CoreCourseModuleHandle } /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @return The component to use, undefined if not found. + * @inheritdoc */ async getMainComponent(): Promise | undefined> { return AddonModForumIndexComponent; } /** - * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be - * included in the template that calls the doRefresh method of the component. Defaults to true. - * - * @return Whether the refresher should be displayed. + * @inheritdoc */ displayRefresherInSingleActivity(): boolean { return false; @@ -161,7 +126,7 @@ export class AddonModForumModuleHandlerService implements CoreCourseModuleHandle { $a : forum.unreadpostscount }, ) : ''; - } catch (error) { + } catch { // Ignore errors. data.extraBadgeColor = ''; data.extraBadge = ''; diff --git a/src/addons/mod/glossary/pages/edit/edit.ts b/src/addons/mod/glossary/pages/edit/edit.ts index 854c74247..33f384fc3 100644 --- a/src/addons/mod/glossary/pages/edit/edit.ts +++ b/src/addons/mod/glossary/pages/edit/edit.ts @@ -73,7 +73,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { }; protected timecreated!: number; - protected concept?: string; + protected concept = ''; protected syncId?: string; protected syncObserver?: CoreEventObserver; protected isDestroyed = false; @@ -86,11 +86,19 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { * Component being initialized. */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.timecreated = CoreNavigator.getRouteNumberParam('timecreated')!; - this.concept = CoreNavigator.getRouteParam('concept')!; - this.editorExtraParams.timecreated = this.timecreated; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.timecreated = CoreNavigator.getRequiredRouteNumberParam('timecreated'); + this.concept = CoreNavigator.getRouteParam('concept') || ''; + this.editorExtraParams.timecreated = this.timecreated; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + this.goBack(); + + return; + } this.fetchData(); } @@ -116,7 +124,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingglossary', true); - CoreNavigator.back(); + this.goBack(); } } @@ -126,7 +134,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { * @return Promise resolved when done. */ protected async loadOfflineData(): Promise { - const entry = await AddonModGlossaryOffline.getNewEntry(this.glossary!.id, this.concept || '', this.timecreated); + const entry = await AddonModGlossaryOffline.getNewEntry(this.glossary!.id, this.concept, this.timecreated); this.entry.concept = entry.concept || ''; this.entry.definition = entry.definition || ''; @@ -367,4 +375,15 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { } } + /** + * Helper function to go back. + */ + protected goBack(): void { + if (this.splitView?.outletActivated) { + CoreNavigator.navigate('../../'); + } else { + CoreNavigator.back(); + } + } + } diff --git a/src/addons/mod/glossary/pages/entry/entry.ts b/src/addons/mod/glossary/pages/entry/entry.ts index 5db347e85..ae3c836e4 100644 --- a/src/addons/mod/glossary/pages/entry/entry.ts +++ b/src/addons/mod/glossary/pages/entry/entry.ts @@ -57,10 +57,18 @@ export class AddonModGlossaryEntryPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.entryId = CoreNavigator.getRouteNumberParam('entryId')!; - this.tagsEnabled = CoreTag.areTagsAvailableInSite(); - this.commentsEnabled = !CoreComments.areCommentsDisabledInSite(); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.entryId = CoreNavigator.getRequiredRouteNumberParam('entryId'); + this.tagsEnabled = CoreTag.areTagsAvailableInSite(); + this.commentsEnabled = !CoreComments.areCommentsDisabledInSite(); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { await this.fetchEntry(); diff --git a/src/addons/mod/glossary/services/handlers/module.ts b/src/addons/mod/glossary/services/handlers/module.ts index f49bfef4e..b7eb1a0f6 100644 --- a/src/addons/mod/glossary/services/handlers/module.ts +++ b/src/addons/mod/glossary/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModGlossaryIndexComponent } from '../../components/index/index'; @@ -25,12 +23,13 @@ import { AddonModGlossaryIndexComponent } from '../../components/index/index'; * Handler to support glossary modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModGlossaryModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModGlossaryModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_glossary'; name = 'AddonModGlossary'; modName = 'glossary'; + protected pageName = AddonModGlossaryModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: false, @@ -46,33 +45,6 @@ export class AddonModGlossaryModuleHandlerService implements CoreCourseModuleHan [CoreConstants.FEATURE_PLAGIARISM]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_glossary-handler', - showDownloadButton: true, - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModGlossaryModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/h5pactivity/components/index/index.ts b/src/addons/mod/h5pactivity/components/index/index.ts index 37989271f..d7bd10f69 100644 --- a/src/addons/mod/h5pactivity/components/index/index.ts +++ b/src/addons/mod/h5pactivity/components/index/index.ts @@ -96,7 +96,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv ) { super('AddonModH5PActivityIndexComponent', content, courseContentsPage); - this.site = CoreSites.getCurrentSite()!; + this.site = CoreSites.getRequiredCurrentSite(); this.siteCanDownload = this.site.canDownloadFiles() && !CoreH5P.isOfflineDisabledInSite(); // Listen for messages from the iframe. diff --git a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts index 3f3d79731..113d66430 100644 --- a/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ b/src/addons/mod/h5pactivity/pages/attempt-results/attempt-results.ts @@ -50,9 +50,17 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.attemptId = CoreNavigator.getRouteNumberParam('attemptId')!; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.attemptId = CoreNavigator.getRequiredRouteNumberParam('attemptId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { await this.fetchData(); diff --git a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts index 5f09b4c02..cda950b21 100644 --- a/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/user-attempts/user-attempts.ts @@ -51,9 +51,18 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + this.isCurrentUser = this.userId == CoreSites.getCurrentSiteUserId(); try { diff --git a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts index 7dee400fe..65d0f2510 100644 --- a/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts +++ b/src/addons/mod/h5pactivity/pages/users-attempts/users-attempts.ts @@ -49,8 +49,16 @@ export class AddonModH5PActivityUsersAttemptsPage implements OnInit { * @inheritdoc */ async ngOnInit(): Promise { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { await this.fetchData(); diff --git a/src/addons/mod/h5pactivity/services/handlers/module.ts b/src/addons/mod/h5pactivity/services/handlers/module.ts index 1875bed1f..dfa1c7900 100644 --- a/src/addons/mod/h5pactivity/services/handlers/module.ts +++ b/src/addons/mod/h5pactivity/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModH5PActivityIndexComponent } from '../../components/index'; import { AddonModH5PActivity } from '../h5pactivity'; @@ -26,12 +24,13 @@ import { AddonModH5PActivity } from '../h5pactivity'; * Handler to support H5P activities. */ @Injectable({ providedIn: 'root' }) -export class AddonModH5PActivityModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModH5PActivityModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_h5pactivity'; name = 'AddonModH5PActivity'; modName = 'h5pactivity'; + protected pageName = AddonModH5PActivityModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -52,27 +51,6 @@ export class AddonModH5PActivityModuleHandlerService implements CoreCourseModule return AddonModH5PActivity.isPluginEnabled(); } - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_h5pactivity-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModH5PActivityModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/imscp/services/handlers/module.ts b/src/addons/mod/imscp/services/handlers/module.ts index 996d09304..d1dea4491 100644 --- a/src/addons/mod/imscp/services/handlers/module.ts +++ b/src/addons/mod/imscp/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModImscpIndexComponent } from '../../components/index'; import { AddonModImscp } from '../imscp'; @@ -26,12 +24,13 @@ import { AddonModImscp } from '../imscp'; * Handler to support IMSCP modules. */ @Injectable( { providedIn: 'root' }) -export class AddonModImscpModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModImscpModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_imscp'; name = 'AddonModImscp'; modName = 'imscp'; + protected pageName = AddonModImscpModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -55,27 +54,7 @@ export class AddonModImscpModuleHandlerService implements CoreCourseModuleHandle /** * @inheritdoc */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_imscp-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModImscpModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * @inheritdoc - */ - async getMainComponent(): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModImscpIndexComponent; } diff --git a/src/addons/mod/label/services/handlers/module.ts b/src/addons/mod/label/services/handlers/module.ts index 46e0ad9da..9fd176d90 100644 --- a/src/addons/mod/label/services/handlers/module.ts +++ b/src/addons/mod/label/services/handlers/module.ts @@ -14,6 +14,7 @@ import { CoreConstants } from '@/core/constants'; import { Injectable } from '@angular/core'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; import { CoreCourseWSModule } from '@features/course/services/course'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; @@ -22,7 +23,7 @@ import { makeSingleton } from '@singletons'; * Handler to support label modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModLabelModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModLabelModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { name = 'AddonModLabel'; modName = 'label'; @@ -40,13 +41,6 @@ export class AddonModLabelModuleHandlerService implements CoreCourseModuleHandle [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - /** * @inheritdoc */ @@ -57,7 +51,7 @@ export class AddonModLabelModuleHandlerService implements CoreCourseModuleHandle return { icon: '', - title: title, + title, a11yTitle: '', class: 'addon-mod-label-handler', }; diff --git a/src/addons/mod/lesson/pages/player/player.page.ts b/src/addons/mod/lesson/pages/player/player.page.ts index 9d5d7db72..e0c4f251d 100644 --- a/src/addons/mod/lesson/pages/player/player.page.ts +++ b/src/addons/mod/lesson/pages/player/player.page.ts @@ -119,12 +119,20 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { * Component being initialized. */ async ngOnInit(): Promise { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.password = CoreNavigator.getRouteParam('password'); - this.review = !!CoreNavigator.getRouteBooleanParam('review'); - this.currentPage = CoreNavigator.getRouteNumberParam('pageId'); - this.retakeToReview = CoreNavigator.getRouteNumberParam('retake'); + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.password = CoreNavigator.getRouteParam('password'); + this.review = !!CoreNavigator.getRouteBooleanParam('review'); + this.currentPage = CoreNavigator.getRouteNumberParam('pageId'); + this.retakeToReview = CoreNavigator.getRouteNumberParam('retake'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { // Fetch the Lesson data. diff --git a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts index 58c826758..634e08740 100644 --- a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts +++ b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts @@ -64,10 +64,18 @@ export class AddonModLessonUserRetakePage implements OnInit { * Component being initialized. */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); - this.retakeNumber = CoreNavigator.getRouteNumberParam('retake'); + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); + this.retakeNumber = CoreNavigator.getRouteNumberParam('retake'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } // Fetch the data. this.fetchData().finally(() => { diff --git a/src/addons/mod/lesson/services/handlers/module.ts b/src/addons/mod/lesson/services/handlers/module.ts index 68cf0e871..7563848e7 100644 --- a/src/addons/mod/lesson/services/handlers/module.ts +++ b/src/addons/mod/lesson/services/handlers/module.ts @@ -15,24 +15,22 @@ import { Injectable, Type } from '@angular/core'; import { CoreConstants } from '@/core/constants'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { AddonModLessonIndexComponent } from '../../components/index'; -import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { makeSingleton } from '@singletons'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** - * Handler to support quiz modules. + * Handler to support lesson modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModLessonModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModLessonModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_lesson'; name = 'AddonModLesson'; modName = 'lesson'; + protected pageName = AddonModLessonModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -47,55 +45,9 @@ export class AddonModLessonModuleHandlerService implements CoreCourseModuleHandl }; /** - * Check if the handler is enabled on a site level. - * - * @return Promise resolved with boolean: whether or not the handler is enabled on a site level. + * @inheritdoc */ - async isEnabled(): Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @param forCoursePage Whether the data will be used to render the course page. - * @return Data to render the module. - */ - getData( - module: CoreCourseAnyModuleData, - courseId: number, // eslint-disable-line @typescript-eslint/no-unused-vars - sectionId: number, // eslint-disable-line @typescript-eslint/no-unused-vars - forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars - ): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_lesson-handler', - showDownloadButton: true, - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModLessonModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getMainComponent(course: CoreCourseAnyCourseData, module: CoreCourseWSModule): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModLessonIndexComponent; } diff --git a/src/addons/mod/lti/services/handlers/module.ts b/src/addons/mod/lti/services/handlers/module.ts index 6ad46c087..6d5e78673 100644 --- a/src/addons/mod/lti/services/handlers/module.ts +++ b/src/addons/mod/lti/services/handlers/module.ts @@ -16,28 +16,29 @@ import { Injectable, Type } from '@angular/core'; import { CoreConstants } from '@/core/constants'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; +import { CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreCourseModule } from '@features/course/services/course-helper'; import { CoreApp } from '@services/app'; import { CoreFilepool } from '@services/filepool'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { DomSanitizer, makeSingleton } from '@singletons'; import { AddonModLtiHelper } from '../lti-helper'; import { AddonModLti, AddonModLtiProvider } from '../lti'; import { AddonModLtiIndexComponent } from '../../components/index'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support LTI modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_lti'; name = 'AddonModLti'; modName = 'lti'; + protected pageName = AddonModLtiModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: false, @@ -50,42 +51,26 @@ export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - /** * @inheritdoc */ getData( module: CoreCourseAnyModuleData, courseId: number, + sectionId?: number, + forCoursePage?: boolean, ): CoreCourseModuleHandlerData { + const data = super.getData(module, courseId, sectionId, forCoursePage); + data.showDownloadButton = false; - const data: CoreCourseModuleHandlerData = { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_lti-handler', - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModLtiModuleHandlerService.PAGE_NAME + routeParams, options); + data.buttons = [{ + icon: 'fas-external-link-alt', + label: 'addon.mod_lti.launchactivity', + action: (event: Event, module: CoreCourseModule, courseId: number): void => { + // Launch the LTI. + AddonModLtiHelper.getDataAndLaunch(courseId, module); }, - buttons: [{ - icon: 'fas-external-link-alt', - label: 'addon.mod_lti.launchactivity', - action: (event: Event, module: CoreCourseModule, courseId: number): void => { - // Launch the LTI. - AddonModLtiHelper.getDataAndLaunch(courseId, module); - }, - }], - }; + }]; // Handle custom icons. CoreUtils.ignoreErrors(this.loadCustomIcon(module, courseId, data)); @@ -133,7 +118,7 @@ export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler /** * @inheritdoc */ - async getMainComponent(): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModLtiIndexComponent; } diff --git a/src/addons/mod/page/services/handlers/module.ts b/src/addons/mod/page/services/handlers/module.ts index 7344acbd6..688d9eabc 100644 --- a/src/addons/mod/page/services/handlers/module.ts +++ b/src/addons/mod/page/services/handlers/module.ts @@ -14,24 +14,23 @@ import { Injectable, Type } from '@angular/core'; import { AddonModPage } from '../page'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { CoreConstants } from '@/core/constants'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { AddonModPageIndexComponent } from '../../components/index'; import { makeSingleton } from '@singletons'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support page modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModPageModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModPageModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_page'; name = 'AddonModPage'; modName = 'page'; + protected pageName = AddonModPageModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -46,46 +45,16 @@ export class AddonModPageModuleHandlerService implements CoreCourseModuleHandler }; /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. + * @inheritdoc */ isEnabled(): Promise { return AddonModPage.isPluginEnabled(); } /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @return Data to render the module. + * @inheritdoc */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_page-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModPageModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - async getMainComponent(): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModPageIndexComponent; } diff --git a/src/addons/mod/quiz/pages/attempt/attempt.page.ts b/src/addons/mod/quiz/pages/attempt/attempt.page.ts index 74fa31df3..85481178d 100644 --- a/src/addons/mod/quiz/pages/attempt/attempt.page.ts +++ b/src/addons/mod/quiz/pages/attempt/attempt.page.ts @@ -52,9 +52,17 @@ export class AddonModQuizAttemptPage implements OnInit { * Component being initialized. */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.attemptId = CoreNavigator.getRouteNumberParam('attemptId')!; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.attemptId = CoreNavigator.getRequiredRouteNumberParam('attemptId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.fetchQuizData().finally(() => { this.loaded = true; diff --git a/src/addons/mod/quiz/pages/player/player.page.ts b/src/addons/mod/quiz/pages/player/player.page.ts index 27ea916c6..c2da33f1b 100644 --- a/src/addons/mod/quiz/pages/player/player.page.ts +++ b/src/addons/mod/quiz/pages/player/player.page.ts @@ -106,9 +106,17 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { * Component being initialized. */ ngOnInit(): void { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.moduleUrl = CoreNavigator.getRouteParam('moduleUrl'); + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.moduleUrl = CoreNavigator.getRouteParam('moduleUrl'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } // Create the auto save instance. this.autoSave = new AddonModQuizAutoSave( diff --git a/src/addons/mod/quiz/pages/review/review.page.ts b/src/addons/mod/quiz/pages/review/review.page.ts index bc7ba288a..98d01d3da 100644 --- a/src/addons/mod/quiz/pages/review/review.page.ts +++ b/src/addons/mod/quiz/pages/review/review.page.ts @@ -83,11 +83,19 @@ export class AddonModQuizReviewPage implements OnInit { * Component being initialized. */ async ngOnInit(): Promise { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.attemptId = CoreNavigator.getRouteNumberParam('attemptId')!; - this.currentPage = CoreNavigator.getRouteNumberParam('page') || -1; - this.showAll = this.currentPage == -1; + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.attemptId = CoreNavigator.getRequiredRouteNumberParam('attemptId'); + this.currentPage = CoreNavigator.getRouteNumberParam('page') || -1; + this.showAll = this.currentPage == -1; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { await this.fetchData(); diff --git a/src/addons/mod/quiz/services/handlers/module.ts b/src/addons/mod/quiz/services/handlers/module.ts index 41f13113a..70cad4de5 100644 --- a/src/addons/mod/quiz/services/handlers/module.ts +++ b/src/addons/mod/quiz/services/handlers/module.ts @@ -15,23 +15,22 @@ import { Injectable, Type } from '@angular/core'; import { CoreConstants } from '@/core/constants'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { AddonModQuizIndexComponent } from '../../components/index'; import { makeSingleton } from '@singletons'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; /** * Handler to support quiz modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModQuizModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModQuizModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_quiz'; name = 'AddonModQuiz'; modName = 'quiz'; + protected pageName = AddonModQuizModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -48,46 +47,7 @@ export class AddonModQuizModuleHandlerService implements CoreCourseModuleHandler }; /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - async isEnabled(): Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_quiz-handler', - showDownloadButton: true, - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModQuizModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. + * @inheritdoc */ async getMainComponent(): Promise> { return AddonModQuizIndexComponent; diff --git a/src/addons/mod/resource/services/handlers/module.ts b/src/addons/mod/resource/services/handlers/module.ts index 317bd0c1f..e2639eeea 100644 --- a/src/addons/mod/resource/services/handlers/module.ts +++ b/src/addons/mod/resource/services/handlers/module.ts @@ -14,12 +14,12 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from '@features/course/services/course'; import { CoreCourseModule } from '@features/course/services/course-helper'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreFileHelper } from '@services/file-helper'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; @@ -33,12 +33,13 @@ import { AddonModResourceHelper } from '../resource-helper'; * Handler to support resource modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModResourceModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_resource'; name = 'AddonModResource'; modName = 'resource'; + protected pageName = AddonModResourceModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -60,46 +61,37 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan } /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. + * @inheritdoc */ - getData(module: CoreCourseAnyModuleData, courseId: number): CoreCourseModuleHandlerData { + getData( + module: CoreCourseAnyModuleData, + courseId: number, + sectionId?: number, + forCoursePage?: boolean, + ): CoreCourseModuleHandlerData { const updateStatus = (status: string): void => { - handlerData.buttons![0].hidden = status !== CoreConstants.DOWNLOADED || + if (!handlerData.buttons) { + return; + } + + handlerData.buttons[0].hidden = status !== CoreConstants.DOWNLOADED || AddonModResourceHelper.isDisplayedInIframe(module); }; const openWithPicker = CoreFileHelper.defaultIsOpenWithPicker(); - const handlerData: CoreCourseModuleHandlerData = { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_resource-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModResourceModuleHandlerService.PAGE_NAME + routeParams, options); + const handlerData = super.getData(module, courseId, sectionId, forCoursePage); + handlerData.updateStatus = updateStatus.bind(this); + handlerData.buttons = [{ + hidden: true, + icon: openWithPicker ? 'fas-share-square' : 'fas-file', + label: module.name + ': ' + Translate.instant(openWithPicker ? 'core.openwith' : 'addon.mod_resource.openthefile'), + action: async (event: Event, module: CoreCourseModule, courseId: number): Promise => { + const hide = await this.hideOpenButton(module, courseId); + if (!hide) { + AddonModResourceHelper.openModuleFile(module, courseId); + } }, - updateStatus: updateStatus.bind(this), - buttons: [{ - hidden: true, - icon: openWithPicker ? 'fas-share-square' : 'fas-file', - label: module.name + ': ' + Translate.instant(openWithPicker ? 'core.openwith' : 'addon.mod_resource.openthefile'), - action: async (event: Event, module: CoreCourseModule, courseId: number): Promise => { - const hide = await this.hideOpenButton(module, courseId); - if (!hide) { - AddonModResourceHelper.openModuleFile(module, courseId); - } - }, - }], - }; + }]; this.getResourceData(module, courseId, handlerData).then((data) => { handlerData.icon = data.icon; @@ -149,7 +141,11 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan // Check if the button needs to be shown or not. promises.push(this.hideOpenButton(module, courseId).then((hideOpenButton) => { - handlerData.buttons![0].hidden = hideOpenButton; + if (!handlerData.buttons) { + return; + } + + handlerData.buttons[0].hidden = hideOpenButton; return; })); @@ -237,7 +233,7 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan // No previously set, just set the icon. if (resourceData.icon == '') { - resourceData.icon = CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined); + resourceData.icon = CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined); } return resourceData; @@ -246,7 +242,7 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan /** * @inheritdoc */ - async getMainComponent(): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModResourceIndexComponent; } diff --git a/src/addons/mod/resource/services/resource-helper.ts b/src/addons/mod/resource/services/resource-helper.ts index b942f7e36..4cb2ccad1 100644 --- a/src/addons/mod/resource/services/resource-helper.ts +++ b/src/addons/mod/resource/services/resource-helper.ts @@ -83,7 +83,7 @@ export class AddonModResourceHelperProvider { // Error getting directory, there was an error downloading or we're in browser. Return online URL. if (CoreApp.isOnline() && mainFile.fileurl) { // This URL is going to be injected in an iframe, we need this to make it work. - return CoreSites.getCurrentSite()!.checkAndFixPluginfileURL(mainFile.fileurl); + return CoreSites.getRequiredCurrentSite().checkAndFixPluginfileURL(mainFile.fileurl); } throw e; diff --git a/src/addons/mod/scorm/pages/player/player.ts b/src/addons/mod/scorm/pages/player/player.ts index c24a3d9fd..74bf853c9 100644 --- a/src/addons/mod/scorm/pages/player/player.ts +++ b/src/addons/mod/scorm/pages/player/player.ts @@ -88,14 +88,22 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { * @inheritdoc */ async ngOnInit(): Promise { - this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.mode = CoreNavigator.getRouteParam('mode') || AddonModScormProvider.MODENORMAL; - this.moduleUrl = CoreNavigator.getRouteParam('moduleUrl') || ''; - this.newAttempt = !!CoreNavigator.getRouteBooleanParam('newAttempt'); - this.organizationId = CoreNavigator.getRouteParam('organizationId'); - this.initialScoId = CoreNavigator.getRouteNumberParam('scoId'); - this.siteId = CoreSites.getCurrentSiteId(); + try { + this.cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.mode = CoreNavigator.getRouteParam('mode') || AddonModScormProvider.MODENORMAL; + this.moduleUrl = CoreNavigator.getRouteParam('moduleUrl') || ''; + this.newAttempt = !!CoreNavigator.getRouteBooleanParam('newAttempt'); + this.organizationId = CoreNavigator.getRouteParam('organizationId'); + this.initialScoId = CoreNavigator.getRouteNumberParam('scoId'); + this.siteId = CoreSites.getCurrentSiteId(); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } try { // Fetch the SCORM data. diff --git a/src/addons/mod/scorm/services/handlers/module.ts b/src/addons/mod/scorm/services/handlers/module.ts index 4017f1c3c..7996d4921 100644 --- a/src/addons/mod/scorm/services/handlers/module.ts +++ b/src/addons/mod/scorm/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModScormIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModScormIndexComponent } from '../../components/index'; * Handler to support SCORM modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModScormModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModScormModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_scorm'; name = 'AddonModScorm'; modName = 'scorm'; + protected pageName = AddonModScormModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -44,33 +43,6 @@ export class AddonModScormModuleHandlerService implements CoreCourseModuleHandle [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_scorm-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModScormModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/scorm/services/scorm-offline.ts b/src/addons/mod/scorm/services/scorm-offline.ts index 58b3f349d..2971656e9 100644 --- a/src/addons/mod/scorm/services/scorm-offline.ts +++ b/src/addons/mod/scorm/services/scorm-offline.ts @@ -730,7 +730,7 @@ export class AddonModScormOfflineProvider { } const scoUserData = scoData?.userdata || {}; - const db = CoreSites.getCurrentSite()!.getDb(); + const db = CoreSites.getRequiredCurrentSite().getDb(); let lessonStatusInserted = false; if (forceCompleted) { diff --git a/src/addons/mod/survey/services/handlers/module.ts b/src/addons/mod/survey/services/handlers/module.ts index 704249350..8ec3f3969 100644 --- a/src/addons/mod/survey/services/handlers/module.ts +++ b/src/addons/mod/survey/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModSurveyIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModSurveyIndexComponent } from '../../components/index'; * Handler to support survey modules. */ @Injectable( { providedIn: 'root' }) -export class AddonModSurveyModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModSurveyModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_survey'; name = 'AddonModSurvey'; modName = 'survey'; + protected pageName = AddonModSurveyModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -44,35 +43,6 @@ export class AddonModSurveyModuleHandlerService implements CoreCourseModuleHandl [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData( - module: CoreCourseAnyModuleData, - ): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_survey-handler', - showDownloadButton: true, - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModSurveyModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/url/services/handlers/module.ts b/src/addons/mod/url/services/handlers/module.ts index 642dc208a..bd5f28b2c 100644 --- a/src/addons/mod/url/services/handlers/module.ts +++ b/src/addons/mod/url/services/handlers/module.ts @@ -15,6 +15,7 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; import { CoreCourseModule } from '@features/course/services/course-helper'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; @@ -30,12 +31,13 @@ import { AddonModUrlHelper } from '../url-helper'; * Handler to support url modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_url'; name = 'AddonModUrl'; modName = 'url'; + protected pageName = AddonModUrlModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, @@ -49,13 +51,6 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - /** * @inheritdoc */ @@ -82,7 +77,7 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler }; const handlerData: CoreCourseModuleHandlerData = { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), + icon: CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined), title: module.name, class: 'addon-mod_url-handler', showDownloadButton: false, @@ -117,12 +112,16 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler }; this.hideLinkButton(module, courseId).then((hideButton) => { - handlerData.buttons![0]!.hidden = hideButton; + if (!handlerData.buttons) { + return; + } + + handlerData.buttons[0].hidden = hideButton; if (module.contents && module.contents[0]) { // Calculate the icon to use. handlerData.icon = AddonModUrl.guessIcon(module.contents[0].fileurl) || - CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined); + CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined); } return; @@ -154,7 +153,7 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler /** * @inheritdoc */ - async getMainComponent(): Promise | undefined> { + async getMainComponent(): Promise> { return AddonModUrlIndexComponent; } @@ -165,7 +164,7 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler * @param courseId Course ID. * @return Promise resolved with boolean. */ - protected async shouldOpenLink(module: CoreCourseModule, courseId: number): Promise { + protected async shouldOpenLink(module: CoreCourseModule, courseId?: number): Promise { try { const contents = await CoreCourse.getModuleContents(module, courseId, undefined, false, false, undefined, this.modName); @@ -177,7 +176,7 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler return true; } else { // Not handled by the app, check the display type. - const url = await CoreUtils.ignoreErrors(AddonModUrl.getUrl(courseId, module.id)); + const url = courseId ? await CoreUtils.ignoreErrors(AddonModUrl.getUrl(courseId, module.id)) : undefined; const displayType = AddonModUrl.getFinalDisplayType(url); return displayType == CoreConstants.RESOURCELIB_DISPLAY_OPEN || @@ -192,7 +191,7 @@ export class AddonModUrlModuleHandlerService implements CoreCourseModuleHandler * @inheritdoc */ manualCompletionAlwaysShown(module: CoreCourseModule): Promise { - return this.shouldOpenLink(module, module.course!); + return this.shouldOpenLink(module, module.course); } } diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index e0a5f3da2..8bbd2a3a8 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -664,7 +664,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp content = content.trim(); if (content.length > 0) { - const editUrl = CoreTextUtils.concatenatePaths(CoreSites.getCurrentSite()!.getURL(), '/mod/wiki/edit.php'); + const editUrl = CoreTextUtils.concatenatePaths(CoreSites.getRequiredCurrentSite().getURL(), '/mod/wiki/edit.php'); content = content.replace(/href="edit\.php/g, 'href="' + editUrl); } diff --git a/src/addons/mod/wiki/services/handlers/module.ts b/src/addons/mod/wiki/services/handlers/module.ts index 917ad30d1..33b5af3aa 100644 --- a/src/addons/mod/wiki/services/handlers/module.ts +++ b/src/addons/mod/wiki/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModWikiIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModWikiIndexComponent } from '../../components/index'; * Handler to support wiki modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModWikiModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModWikiModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_wiki'; name = 'AddonModWiki'; modName = 'wiki'; + protected pageName = AddonModWikiModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -45,33 +44,6 @@ export class AddonModWikiModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_COMMENT]: true, }; - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_wiki-handler', - showDownloadButton: true, - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = `/${courseId}/${module.id}/page/root`; - - CoreNavigator.navigateToSitePath(AddonModWikiModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - /** * @inheritdoc */ diff --git a/src/addons/mod/wiki/wiki-lazy.module.ts b/src/addons/mod/wiki/wiki-lazy.module.ts index 37015b8f4..3c7225328 100644 --- a/src/addons/mod/wiki/wiki-lazy.module.ts +++ b/src/addons/mod/wiki/wiki-lazy.module.ts @@ -22,6 +22,10 @@ import { CanLeaveGuard } from '@guards/can-leave'; import { AddonModWikiEditPage } from './pages/edit/edit'; const routes: Routes = [ + { + path: ':courseId/:cmId', + redirectTo: ':courseId/:cmId/page/root', + }, { path: ':courseId/:cmId/page/:hash', component: AddonModWikiIndexPage, diff --git a/src/addons/mod/workshop/pages/assessment/assessment.ts b/src/addons/mod/workshop/pages/assessment/assessment.ts index ea8b2ec52..0c074a4f9 100644 --- a/src/addons/mod/workshop/pages/assessment/assessment.ts +++ b/src/addons/mod/workshop/pages/assessment/assessment.ts @@ -117,10 +117,18 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy, CanLea * Component being initialized. */ ngOnInit(): void { - this.assessment = CoreNavigator.getRouteParam('assessment')!; - this.submission = CoreNavigator.getRouteParam('submission')!; - this.profile = CoreNavigator.getRouteParam('profile')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; + try { + this.assessment = CoreNavigator.getRequiredRouteParam('assessment'); + this.submission = CoreNavigator.getRequiredRouteParam('submission'); + this.profile = CoreNavigator.getRequiredRouteParam('profile'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.assessmentId = this.assessment.id; this.workshopId = this.submission.workshopid; diff --git a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts index 3cc680eb7..c91033454 100644 --- a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts @@ -99,10 +99,18 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy, Ca * Component being initialized. */ ngOnInit(): void { - this.module = CoreNavigator.getRouteParam('module')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.access = CoreNavigator.getRouteParam('access')!; - this.submissionId = CoreNavigator.getRouteNumberParam('submissionId') || 0; + try { + this.module = CoreNavigator.getRequiredRouteParam('module'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.access = CoreNavigator.getRequiredRouteParam('access'); + this.submissionId = CoreNavigator.getRouteNumberParam('submissionId') || 0; + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } if (this.submissionId > 0) { this.editorExtraParams.id = this.submissionId; diff --git a/src/addons/mod/workshop/pages/submission/submission.ts b/src/addons/mod/workshop/pages/submission/submission.ts index 349c00c28..dee18605e 100644 --- a/src/addons/mod/workshop/pages/submission/submission.ts +++ b/src/addons/mod/workshop/pages/submission/submission.ts @@ -130,15 +130,22 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea * Component being initialized. */ async ngOnInit(): Promise { + try { + this.submissionId = CoreNavigator.getRequiredRouteNumberParam('submissionId'); + this.module = CoreNavigator.getRequiredRouteParam('module'); + this.workshop = CoreNavigator.getRequiredRouteParam('workshop'); + this.access = CoreNavigator.getRequiredRouteParam('access'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.profile = CoreNavigator.getRouteParam('profile'); + this.submissionInfo = CoreNavigator.getRequiredRouteParam('submission'); + this.assessment = CoreNavigator.getRouteParam('assessment'); + } catch (error) { + CoreDomUtils.showErrorModal(error); - this.submissionId = CoreNavigator.getRouteNumberParam('submissionId')!; - this.module = CoreNavigator.getRouteParam('module')!; - this.workshop = CoreNavigator.getRouteParam('workshop')!; - this.access = CoreNavigator.getRouteParam('access')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.profile = CoreNavigator.getRouteParam('profile'); - this.submissionInfo = CoreNavigator.getRouteParam('submission')!; - this.assessment = CoreNavigator.getRouteParam('assessment'); + CoreNavigator.back(); + + return; + } this.title = this.module.name; this.workshopId = this.module.instance || this.workshop.id; diff --git a/src/addons/mod/workshop/services/handlers/module.ts b/src/addons/mod/workshop/services/handlers/module.ts index 86463eee3..4364cc552 100644 --- a/src/addons/mod/workshop/services/handlers/module.ts +++ b/src/addons/mod/workshop/services/handlers/module.ts @@ -14,10 +14,8 @@ import { CoreConstants } from '@/core/constants'; import { Injectable, Type } from '@angular/core'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; +import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; import { AddonModWorkshopIndexComponent } from '../../components/index'; @@ -25,12 +23,13 @@ import { AddonModWorkshopIndexComponent } from '../../components/index'; * Handler to support workshop modules. */ @Injectable({ providedIn: 'root' }) -export class AddonModWorkshopModuleHandlerService implements CoreCourseModuleHandler { +export class AddonModWorkshopModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { static readonly PAGE_NAME = 'mod_workshop'; name = 'AddonModWorkshop'; modName = 'workshop'; + protected pageName = AddonModWorkshopModuleHandlerService.PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -46,30 +45,6 @@ export class AddonModWorkshopModuleHandlerService implements CoreCourseModuleHan /** * @inheritdoc */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData { - return { - icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), - title: module.name, - class: 'addon-mod_workshop-handler', - showDownloadButton: true, - action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModWorkshopModuleHandlerService.PAGE_NAME + routeParams, options); - }, - }; - } - async getMainComponent(): Promise> { return AddonModWorkshopIndexComponent; } diff --git a/src/addons/notes/pages/list/list.page.ts b/src/addons/notes/pages/list/list.page.ts index 487b86f20..4480fc250 100644 --- a/src/addons/notes/pages/list/list.page.ts +++ b/src/addons/notes/pages/list/list.page.ts @@ -40,7 +40,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { @ViewChild(IonContent) content?: IonContent; - courseId: number; + courseId!: number; userId?: number; type: AddonNotesPublishState = 'course'; refreshIcon = CoreConstants.ICON_LOADING; @@ -51,13 +51,21 @@ export class AddonNotesListPage implements OnInit, OnDestroy { user?: CoreUserProfile; showDelete = false; canDeleteNotes = false; - currentUserId: number; + currentUserId!: number; - protected syncObserver: CoreEventObserver; + protected syncObserver!: CoreEventObserver; constructor() { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId'); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.userId = CoreNavigator.getRouteNumberParam('userId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } // Refresh data if notes are synchronized automatically. this.syncObserver = CoreEvents.on(AddonNotesSyncProvider.AUTO_SYNCED, (data) => { diff --git a/src/addons/privatefiles/pages/index/index.ts b/src/addons/privatefiles/pages/index/index.ts index b7a6b0da4..098214023 100644 --- a/src/addons/privatefiles/pages/index/index.ts +++ b/src/addons/privatefiles/pages/index/index.ts @@ -69,19 +69,27 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { * Component being initialized. */ ngOnInit(): void { - this.root = CoreNavigator.getRouteParam('root'); - const contextId = CoreNavigator.getRouteNumberParam('contextid'); + try { + this.root = CoreNavigator.getRouteParam('root'); + const contextId = CoreNavigator.getRouteNumberParam('contextid'); - if (contextId) { - // Loading a certain folder. - this.path = { - contextid: contextId, - component: CoreNavigator.getRouteParam('component')!, - filearea: CoreNavigator.getRouteParam('filearea')!, - itemid: CoreNavigator.getRouteNumberParam('itemid')!, - filepath: CoreNavigator.getRouteParam('filepath')!, - filename: CoreNavigator.getRouteParam('filename')!, - }; + if (contextId) { + // Loading a certain folder. + this.path = { + contextid: contextId, + component: CoreNavigator.getRequiredRouteParam('component'), + filearea: CoreNavigator.getRequiredRouteParam('filearea'), + itemid: CoreNavigator.getRequiredRouteNumberParam('itemid'), + filepath: CoreNavigator.getRequiredRouteParam('filepath'), + filename: CoreNavigator.getRequiredRouteParam('filename'), + }; + } + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; } this.title = this.path?.filename || Translate.instant('addon.privatefiles.files'); diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.ts b/src/addons/storagemanager/pages/course-storage/course-storage.ts index 2e467f96a..752059a7a 100644 --- a/src/addons/storagemanager/pages/course-storage/course-storage.ts +++ b/src/addons/storagemanager/pages/course-storage/course-storage.ts @@ -42,7 +42,15 @@ export class AddonStorageManagerCourseStoragePage implements OnInit { * View loaded. */ async ngOnInit(): Promise { - this.course = CoreNavigator.getRouteParam('course')!; + try { + this.course = CoreNavigator.getRequiredRouteParam('course'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.sections = await CoreCourse.getSections(this.course.id, false, true); CoreCourseHelper.addHandlerDataForModules(this.sections, this.course.id); diff --git a/src/core/components/split-view/split-view.ts b/src/core/components/split-view/split-view.ts index 1a50991a5..a69cc700c 100644 --- a/src/core/components/split-view/split-view.ts +++ b/src/core/components/split-view/split-view.ts @@ -66,14 +66,8 @@ export class CoreSplitViewComponent implements AfterViewInit, OnDestroy { this.disableScrollOnParent(); this.subscriptions = [ - this.contentOutlet.activateEvents.subscribe(() => { - this.updateClasses(); - this.outletRouteSubject.next(this.contentOutlet.activatedRoute.snapshot); - }), - this.contentOutlet.deactivateEvents.subscribe(() => { - this.updateClasses(); - this.outletRouteSubject.next(null); - }), + this.contentOutlet.activateEvents.subscribe(() => this.updateOutletRoute()), + this.contentOutlet.deactivateEvents.subscribe(() => this.updateOutletRoute()), CoreScreen.layoutObservable.subscribe(() => this.updateClasses()), ]; @@ -89,6 +83,17 @@ export class CoreSplitViewComponent implements AfterViewInit, OnDestroy { this.enableScrollOnParent(); } + /** + * Update outlet status. + */ + private updateOutletRoute(): void { + const outletRoute = this.contentOutlet.isActivated ? this.contentOutlet.activatedRoute.snapshot : null; + + this.updateClasses(); + + this.outletRouteSubject.next(outletRoute); + } + /** * Update host classes. */ diff --git a/src/core/features/comments/pages/viewer/viewer.page.ts b/src/core/features/comments/pages/viewer/viewer.page.ts index f0522caca..befbfd691 100644 --- a/src/core/features/comments/pages/viewer/viewer.page.ts +++ b/src/core/features/comments/pages/viewer/viewer.page.ts @@ -110,19 +110,28 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * View loaded. */ async ngOnInit(): Promise { + try { + this.contextLevel = CoreNavigator.getRequiredRouteParam('contextLevel'); + this.instanceId = CoreNavigator.getRequiredRouteNumberParam('instanceId'); + this.componentName = CoreNavigator.getRequiredRouteParam('componentName'); + this.itemId = CoreNavigator.getRequiredRouteNumberParam('itemId'); + this.area = CoreNavigator.getRouteParam('area') || ''; + this.title = CoreNavigator.getRouteNumberParam('title') || + Translate.instant('core.comments.comments'); + this.courseId = CoreNavigator.getRouteNumberParam('courseId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + // Is implicit the user can delete if he can add. this.addDeleteCommentsAvailable = await CoreComments.isAddCommentsAvailable(); this.currentUserId = CoreSites.getCurrentSiteUserId(); this.commentsLoaded = false; - this.contextLevel = CoreNavigator.getRouteParam('contextLevel')!; - this.instanceId = CoreNavigator.getRouteNumberParam('instanceId')!; - this.componentName = CoreNavigator.getRouteParam('componentName')!; - this.itemId = CoreNavigator.getRouteNumberParam('itemId')!; - this.area = CoreNavigator.getRouteParam('area') || ''; - this.title = CoreNavigator.getRouteNumberParam('title') || - Translate.instant('core.comments.comments'); - this.courseId = CoreNavigator.getRouteNumberParam('courseId'); await this.fetchComments(true); } diff --git a/src/core/features/course/classes/main-activity-page.ts b/src/core/features/course/classes/main-activity-page.ts index 4c66ba3b0..de7604457 100644 --- a/src/core/features/course/classes/main-activity-page.ts +++ b/src/core/features/course/classes/main-activity-page.ts @@ -14,10 +14,10 @@ import { Component, OnInit } from '@angular/core'; import { CoreNavigator } from '@services/navigator'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourseAnyModuleData } from '../services/course'; import { CoreCourseModuleMainResourceComponent } from './main-resource-component'; -/** /** * Template class to easily create CoreCourseModuleMainComponent of resources (or activities without syncing). */ @@ -36,8 +36,17 @@ export class CoreCourseModuleMainActivityPage('module')!; - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; + try { + this.module = CoreNavigator.getRequiredRouteParam('module'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + this.title = this.module?.name; } diff --git a/src/core/features/course/classes/module-base-handler.ts b/src/core/features/course/classes/module-base-handler.ts new file mode 100644 index 000000000..16ab933ed --- /dev/null +++ b/src/core/features/course/classes/module-base-handler.ts @@ -0,0 +1,59 @@ +// (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 { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreCourse, CoreCourseAnyModuleData } from '../services/course'; +import { CoreCourseModule } from '../services/course-helper'; +import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../services/module-delegate'; + +/** + * Base module handler to be registered. + */ +export class CoreModuleHandlerBase implements Partial { + + protected pageName = ''; + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + + /** + * @inheritdoc + */ + getData( + module: CoreCourseAnyModuleData, + courseId: number, // eslint-disable-line @typescript-eslint/no-unused-vars + sectionId?: number, // eslint-disable-line @typescript-eslint/no-unused-vars + forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars + ): CoreCourseModuleHandlerData { + return { + icon: CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined), + title: module.name, + class: 'addon-mod_' + module.modname + '-handler', + showDownloadButton: true, + action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void => { + options = options || {}; + options.params = options.params || {}; + Object.assign(options.params, { module }); + const routeParams = '/' + courseId + '/' + module.id; + + CoreNavigator.navigateToSitePath(this.pageName + routeParams, options); + }, + }; + } + +} diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 8e62b143e..9a8d1ffc4 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -78,17 +78,17 @@ export type CoreCourseModulePrefetchInfo = { /** * Downloaded size. */ - size?: number; + size: number; /** * Downloadable size in a readable format. */ - sizeReadable?: string; + sizeReadable: string; /** * Module status. */ - status?: string; + status: string; /** * Icon's name of the module status. @@ -98,12 +98,12 @@ export type CoreCourseModulePrefetchInfo = { /** * Time when the module was last downloaded. */ - downloadTime?: number; + downloadTime: number; /** * Download time in a readable format. */ - downloadTimeReadable?: string; + downloadTimeReadable: string; }; /** @@ -539,18 +539,14 @@ export class CoreCourseHelperProvider { total: true, }; - if (!section && !sections) { - throw new CoreError('Either section or list of sections needs to be supplied.'); - } - // Calculate the size of the download. if (section && section.id != CoreCourseProvider.ALL_SECTIONS_ID) { sizeSum = await CoreCourseModulePrefetchDelegate.getDownloadSize(section.modules, courseId); // Check if the section has embedded files in the description. hasEmbeddedFiles = CoreFilepool.extractDownloadableFilesFromHtml(section.summary).length > 0; - } else { - await Promise.all(sections!.map(async (section) => { + } else if (sections) { + await Promise.all(sections.map(async (section) => { if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) { return; } @@ -565,6 +561,8 @@ export class CoreCourseHelperProvider { hasEmbeddedFiles = true; } })); + } else { + throw new CoreError('Either section or list of sections needs to be supplied.'); } if (hasEmbeddedFiles) { @@ -1057,7 +1055,7 @@ export class CoreCourseHelperProvider { const moduleInfo = await this.getModulePrefetchInfo(module, courseId, invalidateCache, component); - instance.size = moduleInfo.size && moduleInfo.size > 0 ? moduleInfo.sizeReadable! : ''; + instance.size = moduleInfo.sizeReadable; instance.prefetchStatusIcon = moduleInfo.statusIcon; instance.prefetchStatus = moduleInfo.status; @@ -1443,7 +1441,7 @@ export class CoreCourseHelperProvider { invalidateCache?: boolean, component?: string, ): Promise { - const moduleInfo: CoreCourseModulePrefetchInfo = {}; + const siteId = CoreSites.getCurrentSiteId(); if (invalidateCache) { @@ -1459,45 +1457,59 @@ export class CoreCourseHelperProvider { ]); // Treat stored size. - moduleInfo.size = results[0]; - moduleInfo.sizeReadable = CoreTextUtils.bytesToSize(results[0], 2); + const size = results[0]; + const sizeReadable = CoreTextUtils.bytesToSize(results[0], 2); // Treat module status. - moduleInfo.status = results[1]; + const status = results[1]; + let statusIcon: string | undefined; switch (results[1]) { case CoreConstants.NOT_DOWNLOADED: - moduleInfo.statusIcon = CoreConstants.ICON_NOT_DOWNLOADED; + statusIcon = CoreConstants.ICON_NOT_DOWNLOADED; break; case CoreConstants.DOWNLOADING: - moduleInfo.statusIcon = CoreConstants.ICON_DOWNLOADING; + statusIcon = CoreConstants.ICON_DOWNLOADING; break; case CoreConstants.OUTDATED: - moduleInfo.statusIcon = CoreConstants.ICON_OUTDATED; + statusIcon = CoreConstants.ICON_OUTDATED; break; case CoreConstants.DOWNLOADED: break; default: - moduleInfo.statusIcon = ''; + statusIcon = ''; break; } // Treat download time. if (!results[2] || !results[2].downloadTime || !CoreFileHelper.isStateDownloaded(results[2].status || '')) { // Not downloaded. - moduleInfo.downloadTime = 0; - - return moduleInfo; + return { + size, + sizeReadable, + status, + statusIcon, + downloadTime: 0, + downloadTimeReadable: '', + }; } const now = CoreTimeUtils.timestamp(); - moduleInfo.downloadTime = results[2].downloadTime; + const downloadTime = results[2].downloadTime; + let downloadTimeReadable = ''; if (now - results[2].downloadTime < 7 * 86400) { - moduleInfo.downloadTimeReadable = moment(results[2].downloadTime * 1000).fromNow(); + downloadTimeReadable = moment(results[2].downloadTime * 1000).fromNow(); } else { - moduleInfo.downloadTimeReadable = moment(results[2].downloadTime * 1000).calendar(); + downloadTimeReadable = moment(results[2].downloadTime * 1000).calendar(); } - return moduleInfo; + return { + size, + sizeReadable, + status, + statusIcon, + downloadTime, + downloadTimeReadable, + }; } /** @@ -1616,7 +1628,7 @@ export class CoreCourseHelperProvider { this.logger.warn('navCtrl was not passed to navigateToModule by the link handler for ' + module.modname); - const params = { + const params: Params = { course: { id: courseId }, module: module, sectionId: sectionId, @@ -1690,20 +1702,20 @@ export class CoreCourseHelperProvider { courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[], siteId?: string, ): Promise { - siteId = siteId || CoreSites.getCurrentSiteId(); + const requiredSiteId = siteId || CoreSites.getRequiredCurrentSite().getId(); - if (this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][course.id]) { + if (this.courseDwnPromises[requiredSiteId] && this.courseDwnPromises[requiredSiteId][course.id] !== undefined) { // There's already a download ongoing for this course, return the promise. - return this.courseDwnPromises[siteId][course.id]; - } else if (!this.courseDwnPromises[siteId]) { - this.courseDwnPromises[siteId] = {}; + return this.courseDwnPromises[requiredSiteId][course.id]; + } else if (!this.courseDwnPromises[requiredSiteId]) { + this.courseDwnPromises[requiredSiteId] = {}; } // First of all, mark the course as being downloaded. - this.courseDwnPromises[siteId][course.id] = CoreCourse.setCourseStatus( + this.courseDwnPromises[requiredSiteId][course.id] = CoreCourse.setCourseStatus( course.id, CoreConstants.DOWNLOADING, - siteId, + requiredSiteId, ).then(async () => { const promises: Promise[] = []; @@ -1740,17 +1752,17 @@ export class CoreCourseHelperProvider { await CoreUtils.allPromises(promises); // Download success, mark the course as downloaded. - return CoreCourse.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId); + return CoreCourse.setCourseStatus(course.id, CoreConstants.DOWNLOADED, requiredSiteId); }).catch(async (error) => { // Error, restore previous status. - await CoreCourse.setCoursePreviousStatus(course.id, siteId); + await CoreCourse.setCoursePreviousStatus(course.id, requiredSiteId); throw error; }).finally(() => { - delete this.courseDwnPromises[siteId!][course.id]; + delete this.courseDwnPromises[requiredSiteId][course.id]; }); - return this.courseDwnPromises[siteId][course.id]; + return this.courseDwnPromises[requiredSiteId][course.id]; } /** diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index 4a0752ff5..c714008fa 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -633,9 +633,6 @@ export class CoreCourseProvider { * @return The IMG src. */ getModuleIconSrc(moduleName: string, modicon?: string): string { - // @TODO: Check modicon url theme to apply other theme icons. - - // Use default icon on core themes. if (this.CORE_MODULES.indexOf(moduleName) < 0) { if (modicon) { return modicon; @@ -644,6 +641,7 @@ export class CoreCourseProvider { moduleName = 'external-tool'; } + // Use default icon on core modules. return 'assets/img/mod/' + moduleName + '.svg'; } diff --git a/src/core/features/course/services/handlers/default-module.ts b/src/core/features/course/services/handlers/default-module.ts index 052878766..75dfd0586 100644 --- a/src/core/features/course/services/handlers/default-module.ts +++ b/src/core/features/course/services/handlers/default-module.ts @@ -16,8 +16,7 @@ import { Injectable, Type } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../module-delegate'; -import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '../course'; -import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; +import { CoreCourse, CoreCourseAnyModuleData } from '../course'; import { CoreCourseModule } from '../course-helper'; import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; @@ -30,29 +29,20 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { name = 'CoreCourseModuleDefault'; modName = 'default'; + protected pageName = ''; /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. + * @inheritdoc */ async isEnabled(): Promise { return true; } /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. + * @inheritdoc */ getData( module: CoreCourseAnyModuleData, - courseId: number, // eslint-disable-line @typescript-eslint/no-unused-vars - sectionId?: number, // eslint-disable-line @typescript-eslint/no-unused-vars - forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars ): CoreCourseModuleHandlerData { // Return the default data. const defaultData: CoreCourseModuleHandlerData = { @@ -71,6 +61,8 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { }; if ('url' in module && module.url) { + const url = module.url; + defaultData.buttons = [{ icon: 'fas-external-link-alt', label: 'core.openinbrowser', @@ -78,7 +70,7 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { e.preventDefault(); e.stopPropagation(); - CoreSites.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(module.url!); + CoreSites.getRequiredCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); }, }]; } @@ -87,24 +79,14 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { } /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param course The course object. - * @param module The module object. - * @return The component (or promise resolved with component) to use, undefined if not found. + * @inheritdoc */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getMainComponent(course: CoreCourseAnyCourseData, module: CoreCourseWSModule): Promise | undefined> { + async getMainComponent(): Promise> { return CoreCourseUnsupportedModuleComponent; } /** - * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be - * included in the template that calls the doRefresh method of the component. Defaults to true. - * - * @return Whether the refresher should be displayed. + * @inheritdoc */ displayRefresherInSingleActivity(): boolean { return true; diff --git a/src/core/features/course/services/module-delegate.ts b/src/core/features/course/services/module-delegate.ts index f1cfb206e..072188859 100644 --- a/src/core/features/course/services/module-delegate.ts +++ b/src/core/features/course/services/module-delegate.ts @@ -345,9 +345,10 @@ export class CoreCourseModuleDelegateService extends CoreDelegate(modname, 'getIconSrc') || - CoreCourse.getModuleIconSrc(modname, modicon); + CoreCourse.getModuleIconSrc(modname, modicon) || + ''; } /** diff --git a/src/core/features/courses/pages/categories/categories.ts b/src/core/features/courses/pages/categories/categories.ts index 1714f4ebb..df71c1c88 100644 --- a/src/core/features/courses/pages/categories/categories.ts +++ b/src/core/features/courses/pages/categories/categories.ts @@ -59,7 +59,7 @@ export class CoreCoursesCategoriesPage implements OnInit { * @return Promise resolved when done. */ protected async fetchCategories(): Promise { - try{ + try { const categories: CoreCategoryData[] = await CoreCourses.getCategories(this.categoryId, true); this.currentCategory = undefined; diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts index e47cf8819..ee13e8248 100644 --- a/src/core/features/grades/pages/course/course.page.ts +++ b/src/core/features/grades/pages/course/course.page.ts @@ -40,14 +40,26 @@ import { CoreNavigator } from '@services/navigator'; }) export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { - grades: CoreGradesCourseManager; + grades!: CoreGradesCourseManager; splitViewMode?: CoreSplitViewMode; @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; constructor(protected route: ActivatedRoute) { - const courseId = CoreNavigator.getRouteNumberParam('courseId', { route })!; - const userId = CoreNavigator.getRouteNumberParam('userId', { route }) ?? CoreSites.getCurrentSiteUserId(); + let courseId: number; + let userId: number; + + try { + courseId = CoreNavigator.getRequiredRouteNumberParam('courseId', { route }); + userId = CoreNavigator.getRouteNumberParam('userId', { route }) ?? CoreSites.getCurrentSiteUserId(); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + const useSplitView = route.snapshot.data.useSplitView ?? true; const outsideGradesTab = route.snapshot.data.outsideGradesTab ?? false; diff --git a/src/core/features/grades/pages/grade/grade.page.ts b/src/core/features/grades/pages/grade/grade.page.ts index 4424c1bd2..b77e08ce6 100644 --- a/src/core/features/grades/pages/grade/grade.page.ts +++ b/src/core/features/grades/pages/grade/grade.page.ts @@ -31,16 +31,24 @@ import { CoreNavigator } from '@services/navigator'; }) export class CoreGradesGradePage implements OnInit { - courseId: number; - userId: number; - gradeId: number; + courseId!: number; + userId!: number; + gradeId!: number; grade?: CoreGradesFormattedRow | null; gradeLoaded = false; constructor() { - this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; - this.gradeId = CoreNavigator.getRouteNumberParam('gradeId')!; - this.userId = CoreNavigator.getRouteNumberParam('userId') ?? CoreSites.getCurrentSiteUserId(); + try { + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.gradeId = CoreNavigator.getRequiredRouteNumberParam('gradeId'); + this.userId = CoreNavigator.getRouteNumberParam('userId') ?? CoreSites.getCurrentSiteUserId(); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } } /** diff --git a/src/core/features/login/login.scss b/src/core/features/login/login.scss index 73b08e7ae..129a185de 100644 --- a/src/core/features/login/login.scss +++ b/src/core/features/login/login.scss @@ -15,6 +15,11 @@ color: var(--color); } } + + form div { + color: var(--color); + } + ion-button.core-button-as-link { --color: var(--core-login-text-color); text-decoration-color: var(--core-login-text-color); diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index 1f567ebad..6cc44b83f 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -35,7 +35,7 @@ import { CoreTextUtils } from '@services/utils/text'; import { CoreForms } from '@singletons/form'; /** - * Page that displays a "splash screen" while the app is being initialized. + * Site (url) chooser when adding a new site. */ @Component({ selector: 'page-core-login-site', diff --git a/src/core/features/user/pages/participants/participants.page.ts b/src/core/features/user/pages/participants/participants.page.ts index bc135514e..b1508eb2b 100644 --- a/src/core/features/user/pages/participants/participants.page.ts +++ b/src/core/features/user/pages/participants/participants.page.ts @@ -33,7 +33,7 @@ import { CoreUtils } from '@services/utils/utils'; }) export class CoreUserParticipantsPage implements OnInit, AfterViewInit, OnDestroy { - participants: CoreUserParticipantsManager; + participants!: CoreUserParticipantsManager; searchQuery: string | null = null; searchInProgress = false; searchEnabled = false; @@ -43,7 +43,17 @@ export class CoreUserParticipantsPage implements OnInit, AfterViewInit, OnDestro @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; constructor() { - const courseId = CoreNavigator.getRouteNumberParam('courseId')!; + let courseId: number; + + try { + courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } this.participants = new CoreUserParticipantsManager(CoreUserParticipantsPage, courseId); } diff --git a/src/core/services/navigator.ts b/src/core/services/navigator.ts index 8707bf47d..2e3ad5a0c 100644 --- a/src/core/services/navigator.ts +++ b/src/core/services/navigator.ts @@ -30,6 +30,7 @@ import { makeSingleton, NavController, Router } from '@singletons'; import { CoreScreen } from './screen'; import { CoreApp } from './app'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; +import { CoreError } from '@classes/errors/error'; const DEFAULT_MAIN_MENU_TAB = CoreMainMenuHomeHandlerService.PAGE_NAME; @@ -269,7 +270,7 @@ export class CoreNavigatorService { const value = route.snapshot.queryParams[name] ?? route.snapshot.params[name]; - if (typeof value != 'undefined') { + if (value !== undefined) { return value; } @@ -300,7 +301,7 @@ export class CoreNavigatorService { value = routeOptions.params[name]; } - if (typeof value == 'undefined') { + if (value === undefined) { return; } @@ -345,7 +346,7 @@ export class CoreNavigatorService { getRouteBooleanParam(name: string, routeOptions: CoreNavigatorCurrentRouteOptions = {}): boolean | undefined { const value = this.getRouteParam(name, routeOptions); - if (typeof value == 'undefined') { + if (value === undefined) { return value; } @@ -360,6 +361,67 @@ export class CoreNavigatorService { return Boolean(value); } + /** + * Get a parameter for the current route. + * Please notice that objects can only be retrieved once. You must call this function only once per page and parameter, + * unless there's a new navigation to the page. + * + * This function will fail if parameter is not found. + * + * @param name Name of the parameter. + * @param routeOptions Optional routeOptions to get the params or route value from. If missing, it will autodetect. + * @return Value of the parameter, undefined if not found. + */ + getRequiredRouteParam(name: string, routeOptions: CoreNavigatorCurrentRouteOptions = {}): T { + const value = this.getRouteParam(name, routeOptions); + + if (value === undefined) { + throw new CoreError(`Required param '${name}' not found.`); + } + + return value; + } + + /** + * Get a number route param. + * Angular router automatically converts numbers to string, this function automatically converts it back to number. + * + * This function will fail if parameter is not found. + * + * @param name Name of the parameter. + * @param routeOptions Optional routeOptions to get the params or route value from. If missing, it will autodetect. + * @return Value of the parameter, undefined if not found. + */ + getRequiredRouteNumberParam(name: string, routeOptions: CoreNavigatorCurrentRouteOptions = {}): number { + const value = this.getRouteNumberParam(name, routeOptions); + + if (value === undefined) { + throw new CoreError(`Required number param '${name}' not found.`); + } + + return value; + } + + /** + * Get a boolean route param. + * Angular router automatically converts booleans to string, this function automatically converts it back to boolean. + * + * This function will fail if parameter is not found. + * + * @param name Name of the parameter. + * @param routeOptions Optional routeOptions to get the params or route value from. If missing, it will autodetect. + * @return Value of the parameter, undefined if not found. + */ + getRequiredRouteBooleanParam(name: string, routeOptions: CoreNavigatorCurrentRouteOptions = {}): boolean { + const value = this.getRouteBooleanParam(name, routeOptions); + + if (value === undefined) { + throw new CoreError(`Required boolean param '${name}' not found.`); + } + + return value; + } + /** * Navigate back. * diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 85de9ea31..38a35cd28 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -305,7 +305,7 @@ export class CoreSitesProvider { if (data.errorcode && (data.errorcode == 'enablewsdescription' || data.errorcode == 'requirecorrectaccess')) { throw new CoreSiteError({ errorcode: data.errorcode, - message: data.error!, + message: data.error ?? '', }); } @@ -816,11 +816,24 @@ export class CoreSitesProvider { } /** - * Get current site. + * Get current site or undefined if none. + * + * @return Current site or undefined if none. + */ + getCurrentSite(): CoreSite | undefined { + return this.currentSite; + } + + /** + * Get current site or fail if none. * * @return Current site. */ - getCurrentSite(): CoreSite | undefined { + getRequiredCurrentSite(): CoreSite { + if (!this.currentSite) { + throw new CoreError('You aren\'t authenticated in any site.'); + } + return this.currentSite; } @@ -1487,7 +1500,9 @@ export class CoreSitesProvider { return Promise.resolve(); } - if (this.siteSchemasMigration[site.id]) { + const siteId = site.id; + + if (this.siteSchemasMigration[site.id] !== undefined) { return this.siteSchemasMigration[site.id]; } @@ -1500,7 +1515,7 @@ export class CoreSitesProvider { this.siteSchemasMigration[site.id] = promise; return promise.finally(() => { - delete this.siteSchemasMigration[site.id!]; + delete this.siteSchemasMigration[siteId]; }); } diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index cdca1e799..ef149d2ca 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -548,6 +548,10 @@ ion-card ion-item img.core-module-icon[slot="start"] { margin-right: 12px; } +ion-card ion-item:only-child { + --inner-border-width: 0; +} + [dir=rtl] ion-item img.core-module-icon[slot="start"] { margin-right: unset; margin-left: 32px;