MOBILE-3650 page: Implement page module
parent
2d19033bb8
commit
823c834265
|
@ -376,13 +376,13 @@ export class AddonModBookProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the WS call is successful.
|
* @return Promise resolved when the WS call is successful.
|
||||||
*/
|
*/
|
||||||
logView(id: number, chapterId?: number, name?: string, siteId?: string): Promise<void> {
|
async logView(id: number, chapterId?: number, name?: string, siteId?: string): Promise<void> {
|
||||||
const params: AddonModBookViewBookWSParams = {
|
const params: AddonModBookViewBookWSParams = {
|
||||||
bookid: id,
|
bookid: id,
|
||||||
chapterid: chapterId,
|
chapterid: chapterId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return CoreCourseLogHelper.instance.logSingle(
|
await CoreCourseLogHelper.instance.logSingle(
|
||||||
'mod_book_view_book',
|
'mod_book_view_book',
|
||||||
params,
|
params,
|
||||||
AddonModBookProvider.COMPONENT,
|
AddonModBookProvider.COMPONENT,
|
||||||
|
|
|
@ -16,12 +16,14 @@ import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { AddonModBookModule } from './book/book.module';
|
import { AddonModBookModule } from './book/book.module';
|
||||||
import { AddonModLessonModule } from './lesson/lesson.module';
|
import { AddonModLessonModule } from './lesson/lesson.module';
|
||||||
|
import { AddonModPageModule } from './page/page.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
imports: [
|
||||||
AddonModBookModule,
|
AddonModBookModule,
|
||||||
AddonModLessonModule,
|
AddonModLessonModule,
|
||||||
|
AddonModPageModule,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
exports: [],
|
exports: [],
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
// (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 { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { IonicModule } from '@ionic/angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
|
||||||
|
|
||||||
|
import { AddonModPageIndexComponent } from './index/index';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModPageIndexComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
FormsModule,
|
||||||
|
CoreSharedModule,
|
||||||
|
CoreCourseComponentsModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonModPageIndexComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModPageComponentsModule {}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<!-- Buttons to add to the header. -->
|
||||||
|
<core-navbar-buttons slot="end">
|
||||||
|
<core-context-menu>
|
||||||
|
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
|
||||||
|
[href]="externalUrl" iconAction="fas-external-link-alt">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
|
||||||
|
(action)="expandDescription()" iconAction="fas-arrow-right">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"
|
||||||
|
iconAction="far-newspaper" (action)="gotoBlog()">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)"
|
||||||
|
[iconAction]="refreshIcon" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="prefetchText" (action)="prefetch($event)"
|
||||||
|
[iconAction]="prefetchStatusIcon" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="size" [priority]="500" [content]="'core.clearstoreddata' | translate:{$a: size}"
|
||||||
|
iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
</core-context-menu>
|
||||||
|
</core-navbar-buttons>
|
||||||
|
|
||||||
|
<!-- Content. -->
|
||||||
|
<core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page">
|
||||||
|
|
||||||
|
<core-course-module-description *ngIf="displayDescription" [description]="description" [component]="component"
|
||||||
|
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||||
|
</core-course-module-description>
|
||||||
|
|
||||||
|
<ion-card class="core-warning-card" *ngIf="warning">
|
||||||
|
<ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
|
||||||
|
<span [innerHTML]="warning"></span>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<div class="ion-padding">
|
||||||
|
<core-format-text [component]="component" [componentId]="componentId" [text]="contents" contextLevel="module"
|
||||||
|
[contextInstanceId]="module?.id" [courseId]="courseId">
|
||||||
|
</core-format-text>
|
||||||
|
|
||||||
|
<p class="ion-padding-bottom addon-mod_page-timemodified" *ngIf="displayTimemodified && timemodified">
|
||||||
|
{{ 'core.lastmodified' | translate}}: {{ timemodified! * 1000 | coreFormatDate }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</core-loading>
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* Solves iframe height */
|
||||||
|
.core-loading-content > div[padding] {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
core-format-text > .no-overflow {
|
||||||
|
display: inline;
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
// (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, Optional } from '@angular/core';
|
||||||
|
import {
|
||||||
|
CoreCourseModuleMainResourceComponent,
|
||||||
|
} from '@features/course/classes/main-resource-component';
|
||||||
|
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
||||||
|
import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { AddonModPageProvider, AddonModPagePage, AddonModPage } from '../../services/page';
|
||||||
|
import { AddonModPageHelper } from '../../services/page-helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays a page.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-mod-page-index',
|
||||||
|
templateUrl: 'addon-mod-page-index.html',
|
||||||
|
styleUrls: ['index.scss'],
|
||||||
|
})
|
||||||
|
export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit {
|
||||||
|
|
||||||
|
component = AddonModPageProvider.COMPONENT;
|
||||||
|
canGetPage = false;
|
||||||
|
contents?: string;
|
||||||
|
displayDescription = true;
|
||||||
|
displayTimemodified = true;
|
||||||
|
timemodified?: number;
|
||||||
|
page?: CoreCourseWSModule | AddonModPagePage;
|
||||||
|
warning?: string;
|
||||||
|
|
||||||
|
protected fetchContentDefaultError = 'addon.mod_page.errorwhileloadingthepage';
|
||||||
|
|
||||||
|
constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) {
|
||||||
|
super('AddonModPageIndexComponent', courseContentsPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
super.ngOnInit();
|
||||||
|
|
||||||
|
this.canGetPage = AddonModPage.instance.isGetPageWSAvailable();
|
||||||
|
|
||||||
|
await this.loadContent();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await AddonModPage.instance.logView(this.module!.instance!, this.module!.name);
|
||||||
|
CoreCourse.instance.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||||
|
} catch {
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the invalidate content function.
|
||||||
|
*
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected async invalidateContent(): Promise<void> {
|
||||||
|
await AddonModPage.instance.invalidateContent(this.module!.id, this.courseId!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download page contents.
|
||||||
|
*
|
||||||
|
* @param refresh Whether we're refreshing data.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchContent(refresh?: boolean): Promise<void> {
|
||||||
|
// Download the resource if it needs to be downloaded.
|
||||||
|
try {
|
||||||
|
const downloadResult = await this.downloadResourceIfNeeded(refresh);
|
||||||
|
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
let getPagePromise: Promise<CoreCourseWSModule | AddonModPagePage>;
|
||||||
|
|
||||||
|
// Get the module to get the latest title and description. Data should've been updated in download.
|
||||||
|
if (this.canGetPage) {
|
||||||
|
getPagePromise = AddonModPage.instance.getPageData(this.courseId!, this.module!.id);
|
||||||
|
} else {
|
||||||
|
getPagePromise = CoreCourse.instance.getModule(this.module!.id, this.courseId!);
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(getPagePromise.then((page) => {
|
||||||
|
if (!page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.description = 'intro' in page ? page.intro : page.description;
|
||||||
|
this.dataRetrieved.emit(page);
|
||||||
|
|
||||||
|
if (!this.canGetPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.page = page;
|
||||||
|
|
||||||
|
// Check if description and timemodified should be displayed.
|
||||||
|
if ('displayoptions' in this.page) {
|
||||||
|
const options: Record<string, string | boolean> =
|
||||||
|
CoreTextUtils.instance.unserialize(this.page.displayoptions) || {};
|
||||||
|
|
||||||
|
this.displayDescription = typeof options.printintro == 'undefined' ||
|
||||||
|
CoreUtils.instance.isTrueOrOne(options.printintro);
|
||||||
|
this.displayTimemodified = typeof options.printlastmodified == 'undefined' ||
|
||||||
|
CoreUtils.instance.isTrueOrOne(options.printlastmodified);
|
||||||
|
} else {
|
||||||
|
this.displayDescription = true;
|
||||||
|
this.displayTimemodified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timemodified = 'timemodified' in this.page ? this.page.timemodified : undefined;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}).catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Get the page HTML.
|
||||||
|
promises.push(AddonModPageHelper.instance.getPageHtml(this.module!.contents, this.module!.id).then((content) => {
|
||||||
|
|
||||||
|
this.contents = content;
|
||||||
|
this.warning = downloadResult?.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : '';
|
||||||
|
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
} finally {
|
||||||
|
this.fillContextMenu(refresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"errorwhileloadingthepage": "Error while loading the page content.",
|
||||||
|
"modulenameplural": "Pages"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// (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 { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: ':courseId/:cmdId',
|
||||||
|
loadChildren: () => import('./pages/index/index.module').then( m => m.AddonModPageIndexPageModule),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
})
|
||||||
|
export class AddonModPageLazyModule {}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// (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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
import { Routes } from '@angular/router';
|
||||||
|
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||||
|
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||||
|
import { CorePluginFileDelegate } from '@services/plugin-file-delegate';
|
||||||
|
import { AddonModPageComponentsModule } from './components/components.module';
|
||||||
|
import { AddonModPageIndexLinkHandler } from './services/handlers/index-link';
|
||||||
|
import { AddonModPageListLinkHandler } from './services/handlers/list-link';
|
||||||
|
import { AddonModPageModuleHandler, AddonModPageModuleHandlerService } from './services/handlers/module';
|
||||||
|
import { AddonModPagePluginFileHandler } from './services/handlers/pluginfile';
|
||||||
|
import { AddonModPagePrefetchHandler } from './services/handlers/prefetch';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: AddonModPageModuleHandlerService.PAGE_NAME,
|
||||||
|
loadChildren: () => import('./page-lazy.module').then(m => m.AddonModPageLazyModule),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CoreMainMenuTabRoutingModule.forChild(routes),
|
||||||
|
AddonModPageComponentsModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
CoreCourseModuleDelegate.instance.registerHandler(AddonModPageModuleHandler.instance);
|
||||||
|
CoreContentLinksDelegate.instance.registerHandler(AddonModPageIndexLinkHandler.instance);
|
||||||
|
CoreContentLinksDelegate.instance.registerHandler(AddonModPageListLinkHandler.instance);
|
||||||
|
CoreCourseModulePrefetchDelegate.instance.registerHandler(AddonModPagePrefetchHandler.instance);
|
||||||
|
CorePluginFileDelegate.instance.registerHandler(AddonModPagePluginFileHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModPageModule {}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>
|
||||||
|
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-title>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<!-- The buttons defined by the component will be added in here. -->
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!pageComponent?.loaded" (ionRefresh)="pageComponent?.doRefresh($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
|
||||||
|
<addon-mod-page-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)">
|
||||||
|
</addon-mod-page-index>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,46 @@
|
||||||
|
// (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 { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { IonicModule } from '@ionic/angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonModPageComponentsModule } from '../../components/components.module';
|
||||||
|
import { AddonModPageIndexPage } from './index';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AddonModPageIndexPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
CoreSharedModule,
|
||||||
|
AddonModPageComponentsModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AddonModPageIndexPage,
|
||||||
|
],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AddonModPageIndexPageModule {}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// (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, ViewChild } from '@angular/core';
|
||||||
|
import { CoreCourseWSModule } from '@features/course/services/course';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { AddonModPageIndexComponent } from '../../components/index/index';
|
||||||
|
import { AddonModPagePage } from '../../services/page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a page.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-mod-page-index',
|
||||||
|
templateUrl: 'index.html',
|
||||||
|
})
|
||||||
|
export class AddonModPageIndexPage implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(AddonModPageIndexComponent) pageComponent?: AddonModPageIndexComponent;
|
||||||
|
|
||||||
|
title?: string;
|
||||||
|
module?: CoreCourseWSModule;
|
||||||
|
courseId?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.module = CoreNavigator.instance.getRouteParam('module');
|
||||||
|
this.courseId = CoreNavigator.instance.getRouteNumberParam('courseId');
|
||||||
|
this.title = this.module?.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update some data based on the page instance.
|
||||||
|
*
|
||||||
|
* @param page Page instance.
|
||||||
|
*/
|
||||||
|
updateData(page: CoreCourseWSModule | AddonModPagePage): void {
|
||||||
|
this.title = 'name' in page ? page.name : this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModPage } from '../page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to page resource.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPageIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
|
||||||
|
|
||||||
|
name = 'AddonModPageLinkHandler';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('AddonModPage', 'page', 'p');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||||
|
*
|
||||||
|
* @param siteId The site ID.
|
||||||
|
* @return Whether the handler is enabled for the URL and site.
|
||||||
|
*/
|
||||||
|
isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
return AddonModPage.instance.isPluginEnabled(siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModPageIndexLinkHandler extends makeSingleton(AddonModPageIndexLinkHandlerService) {}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModPage } from '../page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to page list page.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPageListLinkHandlerService extends CoreContentLinksModuleListHandler {
|
||||||
|
|
||||||
|
name = 'AddonModPageListLinkHandler';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('AddonModPage', 'page');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @param siteId The site ID.
|
||||||
|
* @return Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
return AddonModPage.instance.isPluginEnabled(siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModPageListLinkHandler extends makeSingleton(AddonModPageListLinkHandlerService) {}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable, Type } from '@angular/core';
|
||||||
|
import { AddonModPage } from '../page';
|
||||||
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support page modules.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPageModuleHandlerService implements CoreCourseModuleHandler {
|
||||||
|
|
||||||
|
static readonly PAGE_NAME = 'mod_page';
|
||||||
|
|
||||||
|
name = 'AddonModPage';
|
||||||
|
modName = 'page';
|
||||||
|
|
||||||
|
supportedFeatures = {
|
||||||
|
[CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE,
|
||||||
|
[CoreConstants.FEATURE_GROUPS]: false,
|
||||||
|
[CoreConstants.FEATURE_GROUPINGS]: false,
|
||||||
|
[CoreConstants.FEATURE_MOD_INTRO]: true,
|
||||||
|
[CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
|
||||||
|
[CoreConstants.FEATURE_GRADE_HAS_GRADE]: false,
|
||||||
|
[CoreConstants.FEATURE_GRADE_OUTCOMES]: false,
|
||||||
|
[CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
|
||||||
|
[CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): Promise<boolean> {
|
||||||
|
return AddonModPage.instance.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.
|
||||||
|
*/
|
||||||
|
getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
|
||||||
|
return {
|
||||||
|
icon: CoreCourse.instance.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.instance.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<Type<unknown> | undefined> {
|
||||||
|
return AddonModPageIndexComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class AddonModPageModuleHandler extends makeSingleton(AddonModPageModuleHandlerService) {}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CorePluginFileHandler } from '@services/plugin-file-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to page.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPagePluginFileHandlerService implements CorePluginFileHandler {
|
||||||
|
|
||||||
|
name = 'AddonModPagePluginFileHandler';
|
||||||
|
component = 'mod_page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the RegExp to match the revision on pluginfile URLs.
|
||||||
|
*
|
||||||
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
||||||
|
* @return RegExp to match the revision on pluginfile URLs.
|
||||||
|
*/
|
||||||
|
getComponentRevisionRegExp(args: string[]): RegExp | undefined {
|
||||||
|
// Check filearea.
|
||||||
|
if (args[2] == 'content') {
|
||||||
|
// Component + Filearea + Revision
|
||||||
|
return new RegExp('/mod_page/content/([0-9]+)/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the string to remove the revision on pluginfile url.
|
||||||
|
*
|
||||||
|
* @return String to remove the revision on pluginfile url.
|
||||||
|
*/
|
||||||
|
getComponentRevisionReplace(): string {
|
||||||
|
// Component + Filearea + Revision
|
||||||
|
return '/mod_page/content/0/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModPagePluginFileHandler extends makeSingleton(AddonModPagePluginFileHandlerService) {}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler';
|
||||||
|
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModPage, AddonModPageProvider } from '../page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to prefetch pages.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPagePrefetchHandlerService extends CoreCourseResourcePrefetchHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonModPage';
|
||||||
|
modName = 'page';
|
||||||
|
component = AddonModPageProvider.COMPONENT;
|
||||||
|
updatesNames = /^configuration$|^.*files$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download or prefetch the content.
|
||||||
|
*
|
||||||
|
* @param module The module object returned by WS.
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param prefetch True to prefetch, false to download right away.
|
||||||
|
* @return Promise resolved when all content is downloaded. Data returned is not reliable.
|
||||||
|
*/
|
||||||
|
async downloadOrPrefetch(module: CoreCourseWSModule, courseId: number, prefetch?: boolean): Promise<void> {
|
||||||
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
|
promises.push(super.downloadOrPrefetch(module, courseId, prefetch));
|
||||||
|
|
||||||
|
if (AddonModPage.instance.isGetPageWSAvailable()) {
|
||||||
|
promises.push(AddonModPage.instance.getPageData(courseId, module.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate the prefetched content.
|
||||||
|
*
|
||||||
|
* @param moduleId The module ID.
|
||||||
|
* @param courseId Course ID the module belongs to.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateContent(moduleId: number, courseId: number): Promise<void> {
|
||||||
|
await AddonModPage.instance.invalidateContent(moduleId, courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate WS calls needed to determine module status.
|
||||||
|
*
|
||||||
|
* @param module Module.
|
||||||
|
* @param courseId Course ID the module belongs to.
|
||||||
|
* @return Promise resolved when invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
|
||||||
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
|
promises.push(AddonModPage.instance.invalidatePageData(courseId));
|
||||||
|
promises.push(CoreCourse.instance.invalidateModule(module.id));
|
||||||
|
|
||||||
|
await CoreUtils.instance.allPromises(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled.
|
||||||
|
*/
|
||||||
|
isEnabled(): Promise<boolean> {
|
||||||
|
return AddonModPage.instance.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class AddonModPagePrefetchHandler extends makeSingleton(AddonModPagePrefetchHandlerService) {}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AddonModPageProvider } from './page';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreFile } from '@services/file';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreFilepool } from '@services/filepool';
|
||||||
|
import { CoreWS } from '@services/ws';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { CoreCourseModuleContentFile } from '@features/course/services/course';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features for page.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPageHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the page HTML.
|
||||||
|
*
|
||||||
|
* @param contents The module contents.
|
||||||
|
* @param moduleId The module ID.
|
||||||
|
* @return The HTML of the page.
|
||||||
|
*/
|
||||||
|
async getPageHtml(contents: CoreCourseModuleContentFile[], moduleId: number): Promise<string> {
|
||||||
|
let indexUrl: string | undefined;
|
||||||
|
const paths: Record<string, string> = {};
|
||||||
|
|
||||||
|
// Extract the information about paths from the module contents.
|
||||||
|
contents.forEach((content) => {
|
||||||
|
const url = content.fileurl;
|
||||||
|
|
||||||
|
if (this.isMainPage(content)) {
|
||||||
|
// This seems to be the most reliable way to spot the index page.
|
||||||
|
indexUrl = url;
|
||||||
|
} else {
|
||||||
|
let key = content.filename;
|
||||||
|
if (content.filepath !== '/') {
|
||||||
|
// Add the folders without the leading slash.
|
||||||
|
key = content.filepath.substr(1) + key;
|
||||||
|
}
|
||||||
|
paths[CoreTextUtils.instance.decodeURIComponent(key)] = url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Promise handling when we are in a browser.
|
||||||
|
if (!indexUrl) {
|
||||||
|
// If ever that happens.
|
||||||
|
throw new CoreError('Could not locate the index page');
|
||||||
|
}
|
||||||
|
|
||||||
|
let url: string;
|
||||||
|
if (CoreFile.instance.isAvailable()) {
|
||||||
|
// The file system is available.
|
||||||
|
url = await CoreFilepool.instance.downloadUrl(
|
||||||
|
CoreSites.instance.getCurrentSiteId(),
|
||||||
|
indexUrl,
|
||||||
|
false,
|
||||||
|
AddonModPageProvider.COMPONENT,
|
||||||
|
moduleId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// We return the live URL.
|
||||||
|
url = await CoreSites.instance.getCurrentSite()?.checkAndFixPluginfileURL(indexUrl) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await CoreWS.instance.getText(url);
|
||||||
|
|
||||||
|
// Now that we have the content, we update the SRC to point back to the external resource.
|
||||||
|
// That will be caught by core-format-text.
|
||||||
|
return CoreDomUtils.instance.restoreSourcesInHtml(content, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the file is the main page of the module.
|
||||||
|
*
|
||||||
|
* @param file An object returned from WS containing file info.
|
||||||
|
* @return Whether the file is the main page or not.
|
||||||
|
*/
|
||||||
|
protected isMainPage(file: CoreCourseModuleContentFile): boolean {
|
||||||
|
const filename = file.filename || '';
|
||||||
|
const fileurl = file.fileurl || '';
|
||||||
|
const url = '/mod_page/content/index.html';
|
||||||
|
const encodedUrl = encodeURIComponent(url);
|
||||||
|
|
||||||
|
return (filename === 'index.html' && (fileurl.indexOf(url) > 0 || fileurl.indexOf(encodedUrl) > 0 ));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class AddonModPageHelper extends makeSingleton(AddonModPageHelperProvider) {}
|
|
@ -0,0 +1,225 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreSitesCommonWSOptions, CoreSites } from '@services/sites';
|
||||||
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@services/ws';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { CoreFilepool } from '@services/filepool';
|
||||||
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
|
||||||
|
import { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
|
||||||
|
const ROOT_CACHE_KEY = 'mmaModPage:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features for page.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModPageProvider {
|
||||||
|
|
||||||
|
static readonly COMPONENT = 'mmaModPage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a page by course module ID.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param cmId Course module ID.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the page is retrieved.
|
||||||
|
*/
|
||||||
|
getPageData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModPagePage> {
|
||||||
|
return this.getPageByKey(courseId, 'coursemodule', cmId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a page.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param key Name of the property to check.
|
||||||
|
* @param value Value to search.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the page is retrieved.
|
||||||
|
*/
|
||||||
|
protected async getPageByKey(
|
||||||
|
courseId: number,
|
||||||
|
key: string,
|
||||||
|
value: number,
|
||||||
|
options: CoreSitesCommonWSOptions = {},
|
||||||
|
): Promise<AddonModPagePage> {
|
||||||
|
const site = await CoreSites.instance.getSite(options.siteId);
|
||||||
|
|
||||||
|
const params: AddonModPageGetPagesByCoursesWSParams = {
|
||||||
|
courseids: [courseId],
|
||||||
|
};
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getPageCacheKey(courseId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
component: AddonModPageProvider.COMPONENT,
|
||||||
|
...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await site.read<AddonModPageGetPagesByCoursesWSResponse>('mod_page_get_pages_by_courses', params, preSets);
|
||||||
|
|
||||||
|
const currentPage = response.pages.find((page) => page[key] == value);
|
||||||
|
if (currentPage) {
|
||||||
|
return currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CoreWSError('Page not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for page data WS calls.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @return Cache key.
|
||||||
|
*/
|
||||||
|
protected getPageCacheKey(courseId: number): string {
|
||||||
|
return ROOT_CACHE_KEY + 'page:' + courseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate the prefetched content.
|
||||||
|
*
|
||||||
|
* @param moduleId The module ID.
|
||||||
|
* @param courseId Course ID of the module.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise<void> {
|
||||||
|
siteId = siteId || CoreSites.instance.getCurrentSiteId();
|
||||||
|
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
promises.push(this.invalidatePageData(courseId, siteId));
|
||||||
|
promises.push(CoreFilepool.instance.invalidateFilesByComponent(siteId, AddonModPageProvider.COMPONENT, moduleId));
|
||||||
|
promises.push(CoreCourse.instance.invalidateModule(moduleId, siteId));
|
||||||
|
|
||||||
|
return CoreUtils.instance.allPromises(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates page data.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidatePageData(courseId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
await site.invalidateWsCacheForKey(this.getPageCacheKey(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not getPage WS available or not.
|
||||||
|
*
|
||||||
|
* @return If WS is avalaible.
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
isGetPageWSAvailable(): boolean {
|
||||||
|
return CoreSites.instance.wsAvailableInCurrentSite('mod_page_get_pages_by_courses');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether or not the plugin is enabled.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise.
|
||||||
|
*/
|
||||||
|
async isPluginEnabled(siteId?: string): Promise<boolean> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
return site.canDownloadFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report a page as being viewed.
|
||||||
|
*
|
||||||
|
* @param pageid Module ID.
|
||||||
|
* @param name Name of the page.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the WS call is successful.
|
||||||
|
*/
|
||||||
|
logView(pageid: number, name?: string, siteId?: string): Promise<void> {
|
||||||
|
const params: AddonModPageViewPageWSParams = {
|
||||||
|
pageid,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CoreCourseLogHelper.instance.logSingle(
|
||||||
|
'mod_page_view_page',
|
||||||
|
params,
|
||||||
|
AddonModPageProvider.COMPONENT,
|
||||||
|
pageid,
|
||||||
|
name,
|
||||||
|
'page',
|
||||||
|
{},
|
||||||
|
siteId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModPage extends makeSingleton(AddonModPageProvider) {}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page returned by mod_page_get_pages_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModPagePage = {
|
||||||
|
id: number; // Module id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Page name.
|
||||||
|
intro: string; // Summary.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles: CoreWSExternalFile[];
|
||||||
|
content: string; // Page content.
|
||||||
|
contentformat: number; // Content format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
contentfiles: CoreWSExternalFile[];
|
||||||
|
legacyfiles: number; // Legacy files flag.
|
||||||
|
legacyfileslast: number; // Legacy files last control flag.
|
||||||
|
display: number; // How to display the page.
|
||||||
|
displayoptions: string; // Display options (width, height).
|
||||||
|
revision: number; // Incremented when after each file changes, to avoid cache.
|
||||||
|
timemodified: number; // Last time the page was modified.
|
||||||
|
section: number; // Course section id.
|
||||||
|
visible: number; // Module visibility.
|
||||||
|
groupmode: number; // Group mode.
|
||||||
|
groupingid: number; // Grouping id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_page_get_pages_by_courses.
|
||||||
|
*/
|
||||||
|
type AddonModPageGetPagesByCoursesWSResponse = {
|
||||||
|
pages: AddonModPagePage[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_page_view_page WS.
|
||||||
|
*/
|
||||||
|
type AddonModPageViewPageWSParams = {
|
||||||
|
pageid: number; // Page instance id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_page_get_pages_by_courses WS.
|
||||||
|
*/
|
||||||
|
type AddonModPageGetPagesByCoursesWSParams = {
|
||||||
|
courseids?: number[]; // Array of course ids.
|
||||||
|
};
|
|
@ -310,7 +310,7 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
|
||||||
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
||||||
* @return RegExp to match the revision on pluginfile URLs.
|
* @return RegExp to match the revision on pluginfile URLs.
|
||||||
*/
|
*/
|
||||||
getComponentRevisionRegExp?(args: string[]): RegExp;
|
getComponentRevisionRegExp?(args: string[]): RegExp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should return the string to remove the revision on pluginfile url.
|
* Should return the string to remove the revision on pluginfile url.
|
||||||
|
|
Loading…
Reference in New Issue