MOBILE-3099 module: Add preview page to show restricted activities

main
Pau Ferrer Ocaña 2021-12-01 12:26:46 +01:00
parent 132007b207
commit 007835b6b8
17 changed files with 323 additions and 148 deletions

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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.

View File

@ -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>

View File

@ -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 });
}
/**

View File

@ -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>

View File

@ -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>

View File

@ -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',

View File

@ -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>

View File

@ -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 { }

View File

@ -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?.();
}
}

View File

@ -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>

View File

@ -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,
});
}
}

View File

@ -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);
}
}

View File

@ -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.
*

View File

@ -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);
}
}