From 007835b6b800818725d5a8063dc3a9ce8eb15d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= <crazyserver@gmail.com> Date: Wed, 1 Dec 2021 12:26:46 +0100 Subject: [PATCH] MOBILE-3099 module: Add preview page to show restricted activities --- .../mod/url/services/handlers/module.ts | 9 +- .../course/classes/module-base-handler.ts | 29 ++++- .../module-info/core-course-module-info.html | 13 +- .../components/module-info/module-info.ts | 2 + .../core-course-module-manual-completion.html | 8 +- .../module-navigation/module-navigation.ts | 41 +++--- .../components/module/core-course-module.html | 4 +- .../core-course-unsupported-module.html | 3 - .../features/course/course-lazy.module.ts | 6 +- .../pages/module-preview/module-preview.html | 65 ++++++++++ .../module-preview.module.ts} | 8 +- .../module-preview/module-preview.page.ts | 118 ++++++++++++++++++ .../unsupported-module.html | 27 ---- .../unsupported-module.page.ts | 54 -------- .../services/handlers/default-module.ts | 18 ++- .../course/services/module-delegate.ts | 31 +++++ .../classes/handlers/module-handler.ts | 35 ++++-- 17 files changed, 323 insertions(+), 148 deletions(-) create mode 100644 src/core/features/course/pages/module-preview/module-preview.html rename src/core/features/course/pages/{unsupported-module/unsupported-module.module.ts => module-preview/module-preview.module.ts} (83%) create mode 100644 src/core/features/course/pages/module-preview/module-preview.page.ts delete mode 100644 src/core/features/course/pages/unsupported-module/unsupported-module.html delete mode 100644 src/core/features/course/pages/unsupported-module/unsupported-module.page.ts diff --git a/src/addons/mod/url/services/handlers/module.ts b/src/addons/mod/url/services/handlers/module.ts index e6257f845..69c7d2c4b 100644 --- a/src/addons/mod/url/services/handlers/module.ts +++ b/src/addons/mod/url/services/handlers/module.ts @@ -19,7 +19,7 @@ import { CoreModuleHandlerBase } from '@features/course/classes/module-base-hand import { CoreCourse } 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 { CoreNavigationOptions } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; @@ -90,12 +90,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple if (shouldOpen) { openUrl(module, courseId); } else { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - CoreNavigator.navigateToSitePath(AddonModUrlModuleHandlerService.PAGE_NAME + routeParams, options); + this.openActivityPage(module, courseId, options); } } finally { modal.dismiss(); diff --git a/src/core/features/course/classes/module-base-handler.ts b/src/core/features/course/classes/module-base-handler.ts index 474fb231c..3d59a9d00 100644 --- a/src/core/features/course/classes/module-base-handler.ts +++ b/src/core/features/course/classes/module-base-handler.ts @@ -51,14 +51,31 @@ export class CoreModuleHandlerBase implements Partial<CoreCourseModuleHandler> { courseId: number, options?: CoreNavigationOptions, ): Promise<void> => { - options = options || {}; - options.params = options.params || {}; - Object.assign(options.params, { module }); - const routeParams = '/' + courseId + '/' + module.id; - - await CoreNavigator.navigateToSitePath(this.pageName + routeParams, options); + await this.openActivityPage(module, courseId, options); }, }; } + /** + * Opens the activity page. + * + * @param module The module object. + * @param courseId The course ID. + * @param options Options for the navigation. + * @return Promise resolved when done. + */ + async openActivityPage(module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): Promise<void> { + if (!CoreCourse.moduleHasView(module)) { + return; + } + + options = options || {}; + options.params = options.params || {}; + Object.assign(options.params, { module }); + + const routeParams = '/' + courseId + '/' + module.id; + + await CoreNavigator.navigateToSitePath(this.pageName + routeParams, options); + } + } diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html index 7afb15368..702b6ff54 100644 --- a/src/core/features/course/components/module-info/core-course-module-info.html +++ b/src/core/features/course/components/module-info/core-course-module-info.html @@ -13,24 +13,25 @@ <ion-item class="ion-text-wrap" *ngIf="description" lines="none"> <ion-label> <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module" - [contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="120"> + [contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="expandDescription ? null : 120"> </core-format-text> </ion-label> </ion-item> <ng-content select="[description]"></ng-content> -<ion-item class="ion-text-wrap" lines="none" *ngIf="showCompletion && (module.dates?.length || module.completiondata)"> +<ion-item class="ion-text-wrap" lines="none" *ngIf="showCompletion && (module.dates?.length || + (module.completiondata && (module.completiondata.isautomatic || showManualCompletion) && module.uservisible))"> <ion-label> <!-- Activity dates. --> - <div *ngIf="module.dates && module.dates.length" class="core-module-dates"> + <div *ngIf="module.dates?.length" class="core-module-dates"> <p *ngFor="let date of module.dates"> <strong>{{ date.label }}</strong> {{ date.timestamp * 1000 | coreFormatDate:'strftimedatetime' }} </p> </div> <!-- Module completion. --> - <core-course-module-completion *ngIf="module.completiondata" [completion]="module.completiondata" [moduleName]="module.name" - [moduleId]="module.id" [showCompletionConditions]="true" [showManualCompletion]="true" - (completionChanged)="completionChanged.emit($event)"> + <core-course-module-completion *ngIf="module.completiondata && module.uservisible" [completion]="module.completiondata" + [moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="true" + [showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)"> </core-course-module-completion> </ion-label> </ion-item> diff --git a/src/core/features/course/components/module-info/module-info.ts b/src/core/features/course/components/module-info/module-info.ts index 063ebc88f..2c958c99f 100644 --- a/src/core/features/course/components/module-info/module-info.ts +++ b/src/core/features/course/components/module-info/module-info.ts @@ -36,12 +36,14 @@ import { CoreSites } from '@services/sites'; export class CoreCourseModuleInfoComponent implements OnInit { @Input() module!: CoreCourseModule; // The module to render. + @Input() showManualCompletion = true; // Whether to show manual completion, true by default. @Input() courseId!: number; // The courseId the module belongs to. @Input() component!: string; // Component for format text directive. @Input() componentId!: string | number; // Component ID to use in conjunction with the component. @Input() description?: string | false; // The description to display. If false, no description will be shown. + @Input() expandDescription = false; // If the description should be expanded by default. @Input() hasDataToSync = false; // If the activity has any data to be synced. diff --git a/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html b/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html index ab4055ff0..d205a5a65 100644 --- a/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html +++ b/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html @@ -2,20 +2,22 @@ <ng-container *ngIf="completion.istrackeduser"> <ng-container *ngIf="completion.state"> - <ion-button color="success" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"> + <ion-button color="success" expand="block" fill="outline" [attr.aria-label]="accessibleDescription" + (click)="completionClicked($event)"> <ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon> {{ 'core.course.completion_manual:done' | translate }} </ion-button> </ng-container> <ng-container *ngIf="!completion.state"> - <ion-button color="light" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"> + <ion-button color="dark" expand="block" fill="outline" [attr.aria-label]="accessibleDescription" + (click)="completionClicked($event)"> {{ 'core.course.completion_manual:markdone' | translate }} </ion-button> </ng-container> </ng-container> <ng-container *ngIf="!completion.istrackeduser"> - <ion-button disabled="true" color="light"> + <ion-button disabled="true" color="dark" expand="block" fill="outline"> {{ 'core.course.completion_manual:markdone' | translate }} </ion-button> </ng-container> diff --git a/src/core/features/course/components/module-navigation/module-navigation.ts b/src/core/features/course/components/module-navigation/module-navigation.ts index 627f0bbd5..dfb9602a4 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -14,10 +14,11 @@ import { Component, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/course/services/course'; -import { CoreCourseModule } from '@features/course/services/course-helper'; +import { CoreCourseModule, CoreCourseSection } from '@features/course/services/course-helper'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { IonContent } from '@ionic/angular'; import { ScrollDetail } from '@ionic/core'; +import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -41,6 +42,8 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { nextModule?: CoreCourseModule; previousModule?: CoreCourseModule; + nextModuleSection?: CoreCourseSection; + previousModuleSection?: CoreCourseSection; loaded = false; protected element: HTMLElement; @@ -201,9 +204,10 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { for (let j = startModule; j < section.modules.length && this.nextModule == undefined; j++) { const module = section.modules[j]; - const found = await this.isModuleAvailable(module, section.id); + const found = await this.isModuleAvailable(module); if (found) { this.nextModule = module; + this.nextModuleSection = section; } } } @@ -224,9 +228,10 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { for (let j = startModule; j >= 0 && this.previousModule == undefined; j--) { const module = section.modules[j]; - const found = await this.isModuleAvailable(module, section.id); + const found = await this.isModuleAvailable(module); if (found) { this.previousModule = module; + this.previousModuleSection = section; } } } @@ -237,20 +242,10 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { * Module is visible by the user and it has a specific view (e.g. not a label). * * @param module Module to check. - * @param sectionId Section ID the module belongs to. * @return Wether the module is available to the user or not. */ - protected async isModuleAvailable(module: CoreCourseModule, sectionId: number): Promise<boolean> { - if (module.uservisible === false || !CoreCourse.instance.moduleHasView(module)) { - return false; - } - - if (!module.handlerData) { - module.handlerData = - await CoreCourseModuleDelegate.getModuleDataFor(module.modname, module, this.courseId, sectionId); - } - - return !!module.handlerData?.action; + protected async isModuleAvailable(module: CoreCourseModule): Promise<boolean> { + return CoreCourse.instance.moduleHasView(module); } /** @@ -291,11 +286,19 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { return; } - if (!module.handlerData?.action) { - return; + if (module.uservisible === false) { + const section = next ? this.nextModuleSection : this.previousModuleSection; + const options: CoreNavigationOptions = { + replace: true, + params: { + module, + section, + }, + }; + CoreNavigator.navigateToSitePath('course/' + this.courseId + '/' + module.id +'/module-preview', options); + } else { + CoreCourseModuleDelegate.openActivityPage(module.modname, module, this.courseId, { replace: true }); } - - module.handlerData.action(new Event('click'), module, this.courseId, { replace: true }); } /** diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html index ea1bec5df..65637562c 100644 --- a/src/core/features/course/components/module/core-course-module.html +++ b/src/core/features/course/components/module/core-course-module.html @@ -75,8 +75,8 @@ </div> <!-- Module completion. --> - <core-course-module-completion *ngIf="module.completiondata" [completion]="module.completiondata" [moduleName]="module.name" - [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions" + <core-course-module-completion *ngIf="module.completiondata && module.uservisible" [completion]="module.completiondata" + [moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions" [showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)"> </core-course-module-completion> diff --git a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html index 6b7fe1848..a55256ca3 100644 --- a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html +++ b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html @@ -1,6 +1,3 @@ -<core-course-module-info [description]="module?.description" [courseId]="courseId" [module]="module"> -</core-course-module-info> - <div class="ion-padding"> <h2 *ngIf="!isDisabledInSite && isSupportedByTheApp">{{ 'core.whoops' | translate }}</h2> <h2 *ngIf="isDisabledInSite || !isSupportedByTheApp">{{ 'core.uhoh' | translate }}</h2> diff --git a/src/core/features/course/course-lazy.module.ts b/src/core/features/course/course-lazy.module.ts index 80f28334e..5233864dd 100644 --- a/src/core/features/course/course-lazy.module.ts +++ b/src/core/features/course/course-lazy.module.ts @@ -23,9 +23,9 @@ const routes: Routes = [ loadChildren: () => import('./pages/index/index.module').then( m => m.CoreCourseIndexPageModule), }, { - path: ':courseId/unsupported-module', - loadChildren: () => import('./pages/unsupported-module/unsupported-module.module') - .then( m => m.CoreCourseUnsupportedModulePageModule), + path: ':courseId/:cmId/module-preview', + loadChildren: () => import('./pages/module-preview/module-preview.module') + .then( m => m.CoreCourseModulePreviewPageModule), }, { path: ':courseId/list-mod-type', diff --git a/src/core/features/course/pages/module-preview/module-preview.html b/src/core/features/course/pages/module-preview/module-preview.html new file mode 100644 index 000000000..8f352f3b5 --- /dev/null +++ b/src/core/features/course/pages/module-preview/module-preview.html @@ -0,0 +1,65 @@ +<ion-header collapsible> + <ion-toolbar> + <ion-buttons slot="start"> + <ion-back-button [text]="'core.back' | translate"></ion-back-button> + </ion-buttons> + <ion-title> + <h1> + <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> + </core-format-text> + </h1> + </ion-title> + + <ion-buttons slot="end"> + <core-context-menu> + <core-context-menu-item [priority]="900" *ngIf="module.url" [href]="module!.url" + [content]="'core.openinbrowser' | translate" iconAction="fas-external-link-alt"> + </core-context-menu-item> + </core-context-menu> + </ion-buttons> + </ion-toolbar> +</ion-header> +<ion-content> + <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)"> + <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> + </ion-refresher> + <core-loading [hideUntil]="loaded"> + <core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname" + [componentId]="module.id" (completionChanged)="onCompletionChange()" [expandDescription]="true" + [showManualCompletion]="showManualCompletion"> + + <div class="safe-area-padding-horizontal ion-padding" *ngIf="module.handlerData?.extraBadge"> + <ion-badge class="ion-text-wrap ion-text-start" [color]="module.handlerData?.extraBadgeColor"> + <span [innerHTML]="module.handlerData?.extraBadge"></span> + </ion-badge> + </div> + <div class="safe-area-padding-horizontal ion-padding" *ngIf="module.visible === 0 && (!section || section.visible)"> + <ion-badge class="ion-text-wrap"> + {{ 'core.course.hiddenfromstudents' | translate }} + </ion-badge> + </div> + <div class="safe-area-padding-horizontal ion-padding" *ngIf="module.visible !== 0 && module.isStealth"> + <ion-badge class="ion-text-wrap"> + {{ 'core.course.hiddenoncoursepage' | translate }} + </ion-badge> + </div> + <div class="safe-area-padding-horizontal ion-padding core-module-availabilityinfo" *ngIf="module.availabilityinfo"> + <ion-badge class="ion-text-wrap">{{ 'core.restricted' | translate }}</ion-badge> + <div> + <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" + [courseId]="courseId" class="ion-text-wrap"> + </core-format-text> + </div> + </div> + <div class="safe-area-padding-horizontal ion-padding" *ngIf="module.completiondata?.offline"> + <ion-badge color="warning" class="ion-text-wrap"> + {{ 'core.course.manualcompletionnotsynced' | translate }} + </ion-badge> + </div> + + <core-course-unsupported-module *ngIf="unsupported" [module]="module" [courseId]="courseId"></core-course-unsupported-module> + </core-course-module-info> + </core-loading> + + <core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id"></core-course-module-navigation> +</ion-content> diff --git a/src/core/features/course/pages/unsupported-module/unsupported-module.module.ts b/src/core/features/course/pages/module-preview/module-preview.module.ts similarity index 83% rename from src/core/features/course/pages/unsupported-module/unsupported-module.module.ts rename to src/core/features/course/pages/module-preview/module-preview.module.ts index 596d96650..ab346fcb0 100644 --- a/src/core/features/course/pages/unsupported-module/unsupported-module.module.ts +++ b/src/core/features/course/pages/module-preview/module-preview.module.ts @@ -16,13 +16,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; -import { CoreCourseUnsupportedModulePage } from './unsupported-module.page'; +import { CoreCourseModulePreviewPage } from './module-preview.page'; import { CoreCourseComponentsModule } from '@features/course/components/components.module'; const routes: Routes = [ { path: '', - component: CoreCourseUnsupportedModulePage, + component: CoreCourseModulePreviewPage, }, ]; @@ -33,8 +33,8 @@ const routes: Routes = [ CoreCourseComponentsModule, ], declarations: [ - CoreCourseUnsupportedModulePage, + CoreCourseModulePreviewPage, ], exports: [RouterModule], }) -export class CoreCourseUnsupportedModulePageModule {} +export class CoreCourseModulePreviewPageModule { } diff --git a/src/core/features/course/pages/module-preview/module-preview.page.ts b/src/core/features/course/pages/module-preview/module-preview.page.ts new file mode 100644 index 000000000..51dc29b23 --- /dev/null +++ b/src/core/features/course/pages/module-preview/module-preview.page.ts @@ -0,0 +1,118 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, OnInit } from '@angular/core'; +import { CoreCourse } from '@features/course/services/course'; +import { CoreCourseHelper, CoreCourseModule, CoreCourseSection } from '@features/course/services/course-helper'; +import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; +import { IonRefresher } from '@ionic/angular'; +import { CoreNavigator } from '@services/navigator'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreUtils } from '@services/utils/utils'; + +/** + * Page that displays a module preview. + */ +@Component({ + selector: 'page-core-course-module-preview', + templateUrl: 'module-preview.html', +}) +export class CoreCourseModulePreviewPage implements OnInit { + + title!: string; + module!: CoreCourseModule; + section?: CoreCourseSection; // The section the module belongs to. + courseId!: number; + loaded = false; + unsupported = false; + showManualCompletion = false; + + protected debouncedUpdateModule?: () => void; // Update the module after a certain time. + + /** + * @inheritdoc + */ + async ngOnInit(): Promise<void> { + try { + this.module = CoreNavigator.getRequiredRouteParam<CoreCourseModule>('module'); + this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); + this.section = CoreNavigator.getRouteParam<CoreCourseSection>('section'); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + CoreNavigator.back(); + + return; + } + + this.debouncedUpdateModule = CoreUtils.debounce(() => { + this.doRefresh(); + }, 10000); + + await this.fetchModule(); + } + + /** + * Fetch module. + * + * @return Promise resolved when done. + */ + protected async fetchModule(refresh = false): Promise<void> { + if (refresh) { + this.module = await CoreCourse.getModule(this.module.id, this.courseId); + } + + CoreCourseHelper.calculateModuleCompletionData(this.module, this.courseId); + + await CoreCourseHelper.loadModuleOfflineCompletion(this.courseId, this.module); + + this.unsupported = !CoreCourseModuleDelegate.getHandlerName(this.module.modname); + if (!this.unsupported) { + this.module.handlerData = + await CoreCourseModuleDelegate.getModuleDataFor(this.module.modname, this.module, this.courseId); + } + + this.title = this.module.name; + + this.showManualCompletion = await CoreCourseModuleDelegate.manualCompletionAlwaysShown(this.module); + + this.loaded = true; + } + + /** + * Refresh the data. + * + * @param refresher Refresher. + * @return Promise resolved when done. + */ + async doRefresh(refresher?: IonRefresher): Promise<void> { + + await CoreCourse.invalidateModule(this.module.id); + + this.fetchModule(true); + + refresher?.complete(); + } + + /** + * The completion of the modules has changed. + * + * @return Promise resolved when done. + */ + async onCompletionChange(): Promise<void> { + // Update the module data after a while. + this.debouncedUpdateModule?.(); + } + +} diff --git a/src/core/features/course/pages/unsupported-module/unsupported-module.html b/src/core/features/course/pages/unsupported-module/unsupported-module.html deleted file mode 100644 index 6760fda91..000000000 --- a/src/core/features/course/pages/unsupported-module/unsupported-module.html +++ /dev/null @@ -1,27 +0,0 @@ -<ion-header collapsible> - <ion-toolbar> - <ion-buttons slot="start"> - <ion-back-button [text]="'core.back' | translate"></ion-back-button> - </ion-buttons> - <ion-title> - <h1> - <core-format-text [text]="module?.name" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"> - </core-format-text> - </h1> - </ion-title> - - <ion-buttons slot="end"> - <core-context-menu> - <core-context-menu-item [priority]="900" *ngIf="module?.url" [href]="module!.url" - [content]="'core.openinbrowser' | translate" iconAction="fas-external-link-alt"> - </core-context-menu-item> - <core-context-menu-item [priority]="800" *ngIf="module?.description" [content]="'core.moduleintro' | translate" - (action)="expandDescription()" iconAction="fas-arrow-right"> - </core-context-menu-item> - </core-context-menu> - </ion-buttons> - </ion-toolbar> -</ion-header> -<ion-content> - <core-course-unsupported-module [module]="module" [courseId]="courseId"></core-course-unsupported-module> -</ion-content> diff --git a/src/core/features/course/pages/unsupported-module/unsupported-module.page.ts b/src/core/features/course/pages/unsupported-module/unsupported-module.page.ts deleted file mode 100644 index 78bb28a44..000000000 --- a/src/core/features/course/pages/unsupported-module/unsupported-module.page.ts +++ /dev/null @@ -1,54 +0,0 @@ -// (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 { Component, OnInit } from '@angular/core'; - -import { CoreCourseWSModule } from '@features/course/services/course'; -import { CoreNavigator } from '@services/navigator'; -import { CoreTextUtils } from '@services/utils/text'; -import { Translate } from '@singletons'; - -/** - * Page that displays info about an unsupported module. - */ -@Component({ - selector: 'page-core-course-unsupported-module', - templateUrl: 'unsupported-module.html', -}) -export class CoreCourseUnsupportedModulePage implements OnInit { - - module?: CoreCourseWSModule; - courseId?: number; - - /** - * @inheritDoc - */ - ngOnInit(): void { - this.module = CoreNavigator.getRouteParam('module'); - this.courseId = CoreNavigator.getRouteNumberParam('courseId'); - } - - /** - * Expand the description. - */ - expandDescription(): void { - CoreTextUtils.viewText(Translate.instant('core.description'), this.module!.description!, { - filter: true, - contextLevel: 'module', - instanceId: this.module!.id, - courseId: this.courseId, - }); - } - -} diff --git a/src/core/features/course/services/handlers/default-module.ts b/src/core/features/course/services/handlers/default-module.ts index 25bf41663..6e8cb25d6 100644 --- a/src/core/features/course/services/handlers/default-module.ts +++ b/src/core/features/course/services/handlers/default-module.ts @@ -49,14 +49,11 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon), title: module.name, class: 'core-course-default-handler core-course-module-' + module.modname + '-handler', - action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { + action: async (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { event.preventDefault(); event.stopPropagation(); - options = options || {}; - options.params = { module }; - - CoreNavigator.navigateToSitePath('course/' + courseId + '/unsupported-module', options); + await this.openActivityPage(module, courseId, options); }, }; @@ -92,4 +89,15 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { return true; } + /** + * @inheritdoc + */ + async openActivityPage(module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): Promise<void> { + options = options || {}; + options.params = options.params || {}; + Object.assign(options.params, { module }); + + await CoreNavigator.navigateToSitePath('course/' + courseId + '/' + module.id +'/module-preview', options); + } + } diff --git a/src/core/features/course/services/module-delegate.ts b/src/core/features/course/services/module-delegate.ts index 66dec4184..2776043ff 100644 --- a/src/core/features/course/services/module-delegate.ts +++ b/src/core/features/course/services/module-delegate.ts @@ -102,6 +102,16 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler { * @return Promise resolved with boolean: whether the manual completion should always be displayed. */ manualCompletionAlwaysShown?(module: CoreCourseModule): Promise<boolean>; + + /** + * Opens the activity page. + * + * @param module The module object. + * @param courseId The course ID. + * @param options Options for the navigation. + * @return Promise resolved when done. + */ + openActivityPage(module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): Promise<void>; } /** @@ -295,6 +305,27 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu ); } + /** + * Opens the activity page. + * + * @param module The module object. + * @param courseId The course ID. + * @param options Options for the navigation. + * @return Promise resolved when done. + */ + async openActivityPage( + modname: string, + module: CoreCourseModule, + courseId: number, + options?: CoreNavigationOptions, + ): Promise<void> { + return await this.executeFunctionOnEnabled<void>( + modname, + 'openActivityPage', + [module, courseId, options], + ); + } + /** * Check if a certain module type is disabled in a site. * diff --git a/src/core/features/siteplugins/classes/handlers/module-handler.ts b/src/core/features/siteplugins/classes/handlers/module-handler.ts index 1276a3a87..847d2894b 100644 --- a/src/core/features/siteplugins/classes/handlers/module-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/module-handler.ts @@ -15,7 +15,7 @@ import { Type } from '@angular/core'; import { CoreConstants } from '@/core/constants'; -import { CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course'; +import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course'; import { CoreCourseModule } from '@features/course/services/course-helper'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index'; @@ -92,17 +92,16 @@ export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler imp if (this.handlerSchema.method) { // There is a method, add an action. - handlerData.action = (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => { + handlerData.action = async ( + event: Event, + module: CoreCourseModule, + courseId: number, + options?: CoreNavigationOptions, + ) => { event.preventDefault(); event.stopPropagation(); - options = options || {}; - options.params = { - title: module.name, - module, - }; - - CoreNavigator.navigateToSitePath(`siteplugins/module/${courseId}/${module.id}`, options); + await this.openActivityPage(module, courseId, options); }; } @@ -229,4 +228,22 @@ export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler imp return false; } + /** + * @inheritdoc + */ + async openActivityPage(module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): Promise<void> { + if (!CoreCourse.moduleHasView(module)) { + return; + } + + options = options || {}; + options.params = options.params || {}; + Object.assign(options.params, { + title: module.name, + module, + }); + + CoreNavigator.navigateToSitePath(`siteplugins/module/${courseId}/${module.id}`, options); + } + }