commit
						c9b99927c4
					
				@ -46,7 +46,7 @@ jobs:
 | 
			
		||||
      - tools
 | 
			
		||||
      - platform-tools
 | 
			
		||||
      - build-tools-29.0.3
 | 
			
		||||
      - android-28
 | 
			
		||||
      - android-29
 | 
			
		||||
      - extra-google-google_play_services
 | 
			
		||||
      - extra-google-m2repository
 | 
			
		||||
      - extra-android-m2repository
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { SQLiteDB } from '@classes/sqlitedb';
 | 
			
		||||
import { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
import { AddonModDataAction } from '../data';
 | 
			
		||||
 | 
			
		||||
@ -59,14 +58,6 @@ export const ADDON_MOD_DATA_OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
            primaryKeys: ['dataid', 'entryid', 'action'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    async migrate(db: SQLiteDB, oldVersion: number): Promise<void> {
 | 
			
		||||
        if (oldVersion > 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Move the records from the old table.
 | 
			
		||||
        await db.migrateTable('addon_mod_data_entry', DATA_ENTRY_TABLE);
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { ContextLevel, CoreConstants } from '@/core/constants';
 | 
			
		||||
import { Component, OnDestroy, ViewChild, OnInit, AfterViewInit, ElementRef, Optional } from '@angular/core';
 | 
			
		||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
			
		||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
@ -88,8 +88,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
        isprivatereply: false,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    refreshIcon = 'spinner';
 | 
			
		||||
    syncIcon = 'spinner';
 | 
			
		||||
    refreshIcon = CoreConstants.ICON_LOADING;
 | 
			
		||||
    syncIcon = CoreConstants.ICON_LOADING;
 | 
			
		||||
    discussionStr = '';
 | 
			
		||||
    component = AddonModForumProvider.COMPONENT;
 | 
			
		||||
    cmId!: number;
 | 
			
		||||
@ -509,8 +509,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
            CoreDomUtils.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.discussionLoaded = true;
 | 
			
		||||
            this.refreshIcon = 'refresh';
 | 
			
		||||
            this.syncIcon = 'sync';
 | 
			
		||||
            this.refreshIcon = CoreConstants.ICON_REFRESH;
 | 
			
		||||
            this.syncIcon = CoreConstants.ICON_SYNC;
 | 
			
		||||
 | 
			
		||||
            if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) {
 | 
			
		||||
                // // Add log in Moodle and mark unread posts as readed.
 | 
			
		||||
@ -630,8 +630,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
     */
 | 
			
		||||
    refreshPosts(sync?: boolean, showErrors?: boolean): Promise<void> {
 | 
			
		||||
        this.content.scrollToTop();
 | 
			
		||||
        this.refreshIcon = 'spinner';
 | 
			
		||||
        this.syncIcon = 'spinner';
 | 
			
		||||
        this.refreshIcon = CoreConstants.ICON_LOADING;
 | 
			
		||||
        this.syncIcon = CoreConstants.ICON_LOADING;
 | 
			
		||||
 | 
			
		||||
        const promises = [
 | 
			
		||||
            AddonModForum.invalidateForumData(this.courseId),
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreFileEntry } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreRatingInfo } from '@features/rating/services/rating';
 | 
			
		||||
import { CoreTagItem } from '@features/tag/services/tag';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
@ -1513,18 +1514,7 @@ export type AddonModForumLegacyPost = {
 | 
			
		||||
    userpictureurl?: string; // Post author picture.
 | 
			
		||||
    deleted: boolean; // This post has been removed.
 | 
			
		||||
    isprivatereply: boolean; // The post is a private reply.
 | 
			
		||||
    tags?: { // Tags.
 | 
			
		||||
        id: number; // Tag id.
 | 
			
		||||
        name: string; // Tag name.
 | 
			
		||||
        rawname: string; // The raw, unnormalised name for the tag as entered by users.
 | 
			
		||||
        isstandard: boolean; // Whether this tag is standard.
 | 
			
		||||
        tagcollid: number; // Tag collection id.
 | 
			
		||||
        taginstanceid: number; // Tag instance id.
 | 
			
		||||
        taginstancecontextid: number; // Context the tag instance belongs to.
 | 
			
		||||
        itemid: number; // Id of the record tagged.
 | 
			
		||||
        ordering: number; // Tag ordering.
 | 
			
		||||
        flag: number; // Whether the tag is flagged as inappropriate.
 | 
			
		||||
    }[];
 | 
			
		||||
    tags?: CoreTagItem[]; // Tags.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,7 @@ import { AddonModH5PActivityModule } from './h5pactivity/h5pactivity.module';
 | 
			
		||||
import { AddonModSurveyModule } from './survey/survey.module';
 | 
			
		||||
import { AddonModScormModule } from './scorm/scorm.module';
 | 
			
		||||
import { AddonModChoiceModule } from './choice/choice.module';
 | 
			
		||||
import { AddonModWikiModule } from './wiki/wiki.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -51,6 +52,7 @@ import { AddonModChoiceModule } from './choice/choice.module';
 | 
			
		||||
        AddonModSurveyModule,
 | 
			
		||||
        AddonModScormModule,
 | 
			
		||||
        AddonModChoiceModule,
 | 
			
		||||
        AddonModWikiModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModModule { }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										40
									
								
								src/addons/mod/wiki/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/addons/mod/wiki/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
// (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 { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
			
		||||
import { CoreTagComponentsModule } from '@features/tag/components/components.module';
 | 
			
		||||
import { AddonModWikiIndexComponent } from './index/index';
 | 
			
		||||
import { AddonModWikiMapModalComponent } from './map/map';
 | 
			
		||||
import { AddonModWikiSubwikiPickerComponent } from './subwiki-picker/subwiki-picker';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModWikiIndexComponent,
 | 
			
		||||
        AddonModWikiSubwikiPickerComponent,
 | 
			
		||||
        AddonModWikiMapModalComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCourseComponentsModule,
 | 
			
		||||
        CoreTagComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModWikiIndexComponent,
 | 
			
		||||
        AddonModWikiSubwikiPickerComponent,
 | 
			
		||||
        AddonModWikiMapModalComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiComponentsModule {}
 | 
			
		||||
@ -0,0 +1,97 @@
 | 
			
		||||
<!-- Buttons to add to the header. -->
 | 
			
		||||
<core-navbar-buttons slot="end">
 | 
			
		||||
    <!-- Select subwiki. -->
 | 
			
		||||
    <ion-button *ngIf="subwikiData.count > 1" (click)="showSubwikiPicker($event)"
 | 
			
		||||
        [attr.aria-label]="'addon.mod_wiki.subwiki' | translate">
 | 
			
		||||
        <ion-icon *ngIf="!groupWiki" name="fas-user"></ion-icon>
 | 
			
		||||
        <ion-icon *ngIf="groupWiki" name="fas-users"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
 | 
			
		||||
    <ion-button *ngIf="loaded && currentPageObj" (click)="openMap()" [attr.aria-label]="'addon.mod_wiki.map' | translate"
 | 
			
		||||
        aria-haspopup="true">
 | 
			
		||||
        <ion-icon name="fas-map"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
 | 
			
		||||
    <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 *ngIf="loaded && !hasOffline && isOnline && !pageIsOffline" [priority]="700"
 | 
			
		||||
            [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon"
 | 
			
		||||
            [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="loaded && isOnline && (hasOffline || pageIsOffline)" [priority]="600"
 | 
			
		||||
            [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon"
 | 
			
		||||
            [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="canEdit && (isOnline || pageIsOffline)" [priority]="590" [content]="'core.edit' | translate"
 | 
			
		||||
            iconAction="fas-edit" (action)="goToEditPage()">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="canEdit" [priority]="580" [content]="'addon.mod_wiki.createpage' | translate"
 | 
			
		||||
            iconAction="fas-plus" (action)="goToNewPage()">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)"
 | 
			
		||||
            [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="size" [priority]="400" [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. -->
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-loading [hideUntil]="loaded" class="core-loading-center">
 | 
			
		||||
        <div *ngIf="description || pageIsOffline || hasOffline || pageWarning">
 | 
			
		||||
            <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
 | 
			
		||||
                contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
            </core-course-module-description>
 | 
			
		||||
 | 
			
		||||
            <!-- Wiki has something offline. -->
 | 
			
		||||
            <ion-card class="core-warning-card" *ngIf="pageIsOffline || hasOffline">
 | 
			
		||||
                <ion-item>
 | 
			
		||||
                    <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <span *ngIf="pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: pageStr} }}</span>
 | 
			
		||||
                        <span *ngIf="!pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: moduleName} }}</span>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
 | 
			
		||||
            <!-- Page warning. -->
 | 
			
		||||
            <ion-card class="core-warning-card" *ngIf="pageWarning">
 | 
			
		||||
                <ion-item>
 | 
			
		||||
                    <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
 | 
			
		||||
                    <ion-label>{{ pageWarning }}</ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="ion-padding addon-mod_wiki-page-content">
 | 
			
		||||
            <article [ngClass]="{'addon-mod_wiki-noedit': !canEdit}">
 | 
			
		||||
                <core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"
 | 
			
		||||
                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
                <core-empty-box *ngIf="!pageContent" icon="fas-file-alt" [message]="'addon.mod_wiki.nocontent' | translate"
 | 
			
		||||
                    [inline]="true">
 | 
			
		||||
                </core-empty-box>
 | 
			
		||||
            </article>
 | 
			
		||||
 | 
			
		||||
            <div class="ion-margin-top" *ngIf="tagsEnabled && tags.length > 0">
 | 
			
		||||
                <b>{{ 'core.tag.tags' | translate }}:</b>
 | 
			
		||||
                <core-tag-list [tags]="tags"></core-tag-list>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
 | 
			
		||||
    <ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canEdit">
 | 
			
		||||
        <ion-fab-button (click)="goToNewPage()" [attr.aria-label]="'addon.mod_wiki.createpage' | translate">
 | 
			
		||||
            <ion-icon name="fas-plus"></ion-icon>
 | 
			
		||||
        </ion-fab-button>
 | 
			
		||||
    </ion-fab>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										63
									
								
								src/addons/mod/wiki/components/index/index.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/addons/mod/wiki/components/index/index.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
@import "~theme/globals";
 | 
			
		||||
 | 
			
		||||
$addon-mod-wiki-toc-level-padding:    12px !default;
 | 
			
		||||
 | 
			
		||||
:host {
 | 
			
		||||
    --addon-mod-wiki-newentry-link-color: var(--red);
 | 
			
		||||
    --addon-mod-wiki-toc-border-color: var(--gray-dark);
 | 
			
		||||
    --addon-mod-wiki-toc-background-color: var(--gray-light);
 | 
			
		||||
 | 
			
		||||
    background-color: var(--ion-item-background);
 | 
			
		||||
 | 
			
		||||
    .addon-mod_wiki-page-content {
 | 
			
		||||
        background-color: var(--ion-item-background);
 | 
			
		||||
        border-top: 1px solid var(--gray);
 | 
			
		||||
        padding-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .addon-mod_wiki-page-content core-format-text ::ng-deep {
 | 
			
		||||
        .wiki-toc {
 | 
			
		||||
            margin: 16px;
 | 
			
		||||
            padding: 8px;
 | 
			
		||||
            border: 1px solid var(--addon-mod-wiki-toc-border-color);
 | 
			
		||||
            background: var(--addon-mod-wiki-toc-background-color);
 | 
			
		||||
            p {
 | 
			
		||||
                color: var(--ion-text-color);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .wiki-toc-title {
 | 
			
		||||
            font-size: 1.1em;
 | 
			
		||||
            font-variant: small-caps;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .wiki-toc-section {
 | 
			
		||||
            padding: 0;
 | 
			
		||||
            margin: 2px 8px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .wiki-toc-section-2 {
 | 
			
		||||
            @include padding-horizontal($addon-mod-wiki-toc-level-padding, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .wiki-toc-section-3 {
 | 
			
		||||
            @include padding-horizontal($addon-mod-wiki-toc-level-padding * 2, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .wiki_newentry {
 | 
			
		||||
            color: var(--addon-mod-wiki-newentry-link-color);
 | 
			
		||||
            font-style: italic;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Hide edit section links */
 | 
			
		||||
        .addon-mod_wiki-noedit a.wiki_edit_section {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:host-context(body.dark) {
 | 
			
		||||
    --addon-mod-wiki-newentry-link-color: var(--red-light);
 | 
			
		||||
    --addon-mod-wiki-toc-background-color: var(--gray-darker);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1055
									
								
								src/addons/mod/wiki/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1055
									
								
								src/addons/mod/wiki/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										39
									
								
								src/addons/mod/wiki/components/map/map.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/addons/mod/wiki/components/map/map.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-title>{{ 'addon.mod_wiki.map' | translate }}</ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <nav>
 | 
			
		||||
        <ion-list>
 | 
			
		||||
            <!-- Go to "home". -->
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="homeView" (click)="goToWikiHome()" tappable>
 | 
			
		||||
                <ion-icon name="fas-home" slot="start"></ion-icon>
 | 
			
		||||
                <ion-label>{{ 'addon.mod_wiki.gowikihome' | translate }}</ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
            <ng-container *ngFor="let letter of map">
 | 
			
		||||
                <ion-item-divider *ngIf="letter.label">
 | 
			
		||||
                    <ion-label>{{ letter.label }}</ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngFor="let page of letter.pages" (click)="goToPage(page)"
 | 
			
		||||
                    [class.core-selected-item]="selectedTitle == page.title" tappable>
 | 
			
		||||
                    <ion-icon name="fas-home" slot="start" *ngIf="page.firstpage"></ion-icon>
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <core-format-text [text]="page.title" contextLevel="module" [contextInstanceId]="moduleId"
 | 
			
		||||
                            [courseId]="courseId">
 | 
			
		||||
                        </core-format-text>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                    <ion-note *ngIf="!page.id" slot="end">
 | 
			
		||||
                        <ion-icon name="fas-clock"></ion-icon>
 | 
			
		||||
                        <span class="ion-text-wrap">{{ 'core.notsent' | translate }}</span>
 | 
			
		||||
                    </ion-note>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
    </nav>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										105
									
								
								src/addons/mod/wiki/components/map/map.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/addons/mod/wiki/components/map/map.ts
									
									
									
									
									
										Normal file
									
								
							@ -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 { Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { ModalController } from '@singletons';
 | 
			
		||||
import { AddonModWikiPageDBRecord } from '../../services/database/wiki';
 | 
			
		||||
import { AddonModWikiSubwikiPage } from '../../services/wiki';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modal to display the map of a Wiki.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-wiki-map',
 | 
			
		||||
    templateUrl: 'map.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiMapModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() pages: (AddonModWikiSubwikiPage | AddonModWikiPageDBRecord)[] = [];
 | 
			
		||||
    @Input() selectedTitle?: string;
 | 
			
		||||
    @Input() moduleId?: number;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() homeView?: string;
 | 
			
		||||
 | 
			
		||||
    map: AddonModWikiPagesMapLetter[] = []; // Map of pages, categorized by letter.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.constructMap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function called when a page is clicked.
 | 
			
		||||
     *
 | 
			
		||||
     * @param page Clicked page.
 | 
			
		||||
     */
 | 
			
		||||
    goToPage(page: AddonModWikiSubwikiPage | AddonModWikiPageDBRecord): void {
 | 
			
		||||
        ModalController.dismiss({ type: 'page', goto: page });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Go back to the initial page of the wiki.
 | 
			
		||||
     */
 | 
			
		||||
    goToWikiHome(): void {
 | 
			
		||||
        ModalController.dismiss({ type: 'home', goto: this.homeView });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Construct the map of pages.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pages List of pages.
 | 
			
		||||
     */
 | 
			
		||||
    protected constructMap(): void {
 | 
			
		||||
        let letter: AddonModWikiPagesMapLetter;
 | 
			
		||||
        let initialLetter: string;
 | 
			
		||||
 | 
			
		||||
        this.map = [];
 | 
			
		||||
        this.pages.sort((a, b) => {
 | 
			
		||||
            const compareA = a.title.toLowerCase().trim();
 | 
			
		||||
            const compareB = b.title.toLowerCase().trim();
 | 
			
		||||
 | 
			
		||||
            return compareA.localeCompare(compareB);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.pages.forEach((page) => {
 | 
			
		||||
            const letterCandidate = page.title.charAt(0).toLocaleUpperCase();
 | 
			
		||||
 | 
			
		||||
            // Should we create a new grouping?
 | 
			
		||||
            if (letterCandidate !== initialLetter) {
 | 
			
		||||
                initialLetter = letterCandidate;
 | 
			
		||||
                letter = { label: letterCandidate, pages: [] };
 | 
			
		||||
 | 
			
		||||
                this.map.push(letter);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Add the subwiki to the currently active grouping.
 | 
			
		||||
            letter.pages.push(page);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
     */
 | 
			
		||||
    closeModal(): void {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddonModWikiPagesMapLetter = {
 | 
			
		||||
    label: string;
 | 
			
		||||
    pages: (AddonModWikiSubwikiPage | AddonModWikiPageDBRecord)[];
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<ion-list>
 | 
			
		||||
    <ng-container *ngFor="let group of subwikis">
 | 
			
		||||
        <ion-item-divider *ngIf="group.label">
 | 
			
		||||
            <ion-label><strong>{{ group.label }}</strong></ion-label>
 | 
			
		||||
        </ion-item-divider>
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngFor="let subwiki of group.subwikis" (click)="openSubwiki(subwiki)"
 | 
			
		||||
            [attr.disabled]="!subwiki.canedit && subwiki.id <= 0 ? true : null" tappable
 | 
			
		||||
            [class.core-selected-item]="isSubwikiSelected(subwiki)" detail="false">
 | 
			
		||||
            <ion-label>{{ subwiki.name }}</ion-label>
 | 
			
		||||
            <ion-icon *ngIf="isSubwikiSelected(subwiki)" name="fas-check" slot="end"></ion-icon>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </ng-container>
 | 
			
		||||
</ion-list>
 | 
			
		||||
@ -0,0 +1,61 @@
 | 
			
		||||
// (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, Input } from '@angular/core';
 | 
			
		||||
import { PopoverController } from '@singletons';
 | 
			
		||||
import { AddonModWikiSubwiki, AddonModWikiSubwikiListGrouping } from '../../services/wiki';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display the a list of subwikis in a wiki.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-wiki-subwiki-picker',
 | 
			
		||||
    templateUrl: 'addon-mod-wiki-subwiki-picker.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiSubwikiPickerComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() subwikis: AddonModWikiSubwikiListGrouping[] = [];
 | 
			
		||||
    @Input() currentSubwiki!: AddonModWikiSubwiki;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the given subwiki is the one currently selected.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwiki Subwiki to check.
 | 
			
		||||
     * @return Whether it's the selected subwiki.
 | 
			
		||||
     */
 | 
			
		||||
    isSubwikiSelected(subwiki: AddonModWikiSubwiki): boolean {
 | 
			
		||||
 | 
			
		||||
        if (subwiki.id > 0 && this.currentSubwiki.id > 0) {
 | 
			
		||||
            return subwiki.id == this.currentSubwiki.id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return subwiki.userid == this.currentSubwiki.userid && subwiki.groupid == this.currentSubwiki.groupid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function called when a subwiki is clicked.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwiki The subwiki to open.
 | 
			
		||||
     */
 | 
			
		||||
    openSubwiki(subwiki: AddonModWikiSubwiki): void {
 | 
			
		||||
        // Check if the subwiki is disabled.
 | 
			
		||||
        if (subwiki.id > 0 || subwiki.canedit) {
 | 
			
		||||
            // Check if it isn't current subwiki.
 | 
			
		||||
            if (subwiki != this.currentSubwiki) {
 | 
			
		||||
                PopoverController.dismiss(subwiki);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/addons/mod/wiki/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/addons/mod/wiki/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
{
 | 
			
		||||
    "cannoteditpage": "You can not edit this page.",
 | 
			
		||||
    "createpage": "Create page",
 | 
			
		||||
    "editingpage": "Editing this page '{{$a}}'",
 | 
			
		||||
    "errorloadingpage": "An error occurred while loading the page.",
 | 
			
		||||
    "errornowikiavailable": "This wiki does not have any content yet.",
 | 
			
		||||
    "gowikihome": "Go to the wiki first page",
 | 
			
		||||
    "map": "Map",
 | 
			
		||||
    "modulenameplural": "Wikis",
 | 
			
		||||
    "newpagehdr": "New page",
 | 
			
		||||
    "newpagetitle": "New page title",
 | 
			
		||||
    "nocontent": "There is no content for this page",
 | 
			
		||||
    "notingroup": "Not in group",
 | 
			
		||||
    "pageexists": "This page already exists.",
 | 
			
		||||
    "pagename": "Page name",
 | 
			
		||||
    "subwiki": "Sub-wiki",
 | 
			
		||||
    "tagarea_wiki_pages": "Wiki pages",
 | 
			
		||||
    "titleshouldnotbeempty": "The title should not be empty",
 | 
			
		||||
    "viewpage": "View page",
 | 
			
		||||
    "wikipage": "Wiki page",
 | 
			
		||||
    "wrongversionlock": "Another user has edited this page while you were editing and your content is obsolete."
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/addons/mod/wiki/pages/edit/edit.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/addons/mod/wiki/pages/edit/edit.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
<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]="cmId" [courseId]="courseId">
 | 
			
		||||
            </core-format-text>
 | 
			
		||||
        </ion-title>
 | 
			
		||||
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button fill="clear" (click)="save()" [attr.aria-label]="'core.save' | translate">
 | 
			
		||||
                {{ 'core.save' | translate }}
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <form [formGroup]="pageForm" #editPageForm *ngIf="loaded">
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="canEditTitle">
 | 
			
		||||
                <ion-label></ion-label>
 | 
			
		||||
                <ion-input name="title" type="text" [placeholder]="'addon.mod_wiki.newpagetitle' | translate"
 | 
			
		||||
                    formControlName="title">
 | 
			
		||||
                </ion-input>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
 | 
			
		||||
            <ion-item>
 | 
			
		||||
                <ion-label></ion-label>
 | 
			
		||||
                <core-rich-text-editor [control]="contentControl" [placeholder]="'core.content' | translate"
 | 
			
		||||
                    name="wiki_page_content" [component]="component" [componentId]="cmId" [autoSave]="true" contextLevel="module"
 | 
			
		||||
                    [contextInstanceId]="cmId" elementId="newcontent_editor" [draftExtraParams]="editorExtraParams">
 | 
			
		||||
                </core-rich-text-editor>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
 | 
			
		||||
            <ion-item *ngIf="!wrongVersionLock" class="ion-text-center addon-mod_wiki-wrongversionlock" >
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <ion-badge color="danger" class="ion-padding">{{ 'addon.mod_wiki.wrongversionlock' | translate }}</ion-badge>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </form>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										448
									
								
								src/addons/mod/wiki/pages/edit/edit.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										448
									
								
								src/addons/mod/wiki/pages/edit/edit.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,448 @@
 | 
			
		||||
// (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, OnDestroy, ViewChild, ElementRef } from '@angular/core';
 | 
			
		||||
import { FormControl, FormGroup, FormBuilder } from '@angular/forms';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CanLeave } from '@guards/can-leave';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreSync } from '@services/sync';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreForms } from '@singletons/form';
 | 
			
		||||
import { AddonModWiki, AddonModWikiProvider } from '../../services/wiki';
 | 
			
		||||
import { AddonModWikiOffline } from '../../services/wiki-offline';
 | 
			
		||||
import { AddonModWikiSync } from '../../services/wiki-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that allows adding or editing a wiki page.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-wiki-edit',
 | 
			
		||||
    templateUrl: 'edit.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
    @ViewChild('editPageForm') formElement?: ElementRef;
 | 
			
		||||
 | 
			
		||||
    cmId!: number; // Course module ID.
 | 
			
		||||
    courseId!: number; // Course the wiki belongs to.
 | 
			
		||||
    title?: string; // Title to display.
 | 
			
		||||
    pageForm?: FormGroup; // The form group.
 | 
			
		||||
    contentControl?: FormControl; // The FormControl for the page content.
 | 
			
		||||
    canEditTitle = false; // Whether title can be edited.
 | 
			
		||||
    loaded = false; // Whether the data has been loaded.
 | 
			
		||||
    component = AddonModWikiProvider.COMPONENT; // Component to link the files to.
 | 
			
		||||
    wrongVersionLock = false; // Whether the page lock doesn't match the initial one.
 | 
			
		||||
    editorExtraParams: Record<string, unknown> = {};
 | 
			
		||||
 | 
			
		||||
    protected subwikiId?: number; // Subwiki ID the page belongs to.
 | 
			
		||||
    protected wikiId?: number; // Wiki ID the page belongs to.
 | 
			
		||||
    protected pageId?: number; // The page ID (if editing a page).
 | 
			
		||||
    protected section?: string; // The section being edited.
 | 
			
		||||
    protected groupId?: number; // The group the subwiki belongs to.
 | 
			
		||||
    protected userId?: number; // The user the subwiki belongs to.
 | 
			
		||||
    protected blockId?: string; // ID to block the subwiki.
 | 
			
		||||
    protected editing = false; // Whether the user is editing a page (true) or creating a new one (false).
 | 
			
		||||
    protected editOffline = false; // Whether the user is editing an offline page.
 | 
			
		||||
    protected subwikiFiles: CoreWSExternalFile[] = []; // List of files of the subwiki.
 | 
			
		||||
    protected originalContent?: string; // The original page content.
 | 
			
		||||
    protected version?: number; // Page version.
 | 
			
		||||
    protected renewLockInterval?: number; // An interval to renew the lock every certain time.
 | 
			
		||||
    protected forceLeave = false; // To allow leaving the page without checking for changes.
 | 
			
		||||
    protected isDestroyed = false; // Whether the page has been destroyed.
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        protected formBuilder: FormBuilder,
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        this.cmId = CoreNavigator.getRouteNumberParam('cmId')!;
 | 
			
		||||
        this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
 | 
			
		||||
        this.subwikiId = CoreNavigator.getRouteNumberParam('subwikiId');
 | 
			
		||||
        this.wikiId = CoreNavigator.getRouteNumberParam('wikiId');
 | 
			
		||||
        this.pageId = CoreNavigator.getRouteNumberParam('pageId');
 | 
			
		||||
        this.section = CoreNavigator.getRouteParam('section');
 | 
			
		||||
        this.groupId = CoreNavigator.getRouteNumberParam('groupId');
 | 
			
		||||
        this.userId = CoreNavigator.getRouteNumberParam('userId');
 | 
			
		||||
 | 
			
		||||
        let pageTitle = CoreNavigator.getRouteParam<string>('pageTitle');
 | 
			
		||||
        pageTitle = pageTitle ? pageTitle.replace(/\+/g, ' ') : '';
 | 
			
		||||
 | 
			
		||||
        this.canEditTitle = !pageTitle;
 | 
			
		||||
        this.title = pageTitle ?
 | 
			
		||||
            Translate.instant('addon.mod_wiki.editingpage', { $a: pageTitle }) :
 | 
			
		||||
            Translate.instant('addon.mod_wiki.newpagehdr');
 | 
			
		||||
        this.blockId = AddonModWikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId);
 | 
			
		||||
 | 
			
		||||
        // Create the form group and its controls.
 | 
			
		||||
        this.contentControl = this.formBuilder.control('');
 | 
			
		||||
        this.pageForm = this.formBuilder.group({
 | 
			
		||||
            title: pageTitle,
 | 
			
		||||
        });
 | 
			
		||||
        this.pageForm.addControl('text', this.contentControl);
 | 
			
		||||
 | 
			
		||||
        // Block the wiki so it cannot be synced.
 | 
			
		||||
        CoreSync.blockOperation(this.component, this.blockId);
 | 
			
		||||
 | 
			
		||||
        if (this.pageId) {
 | 
			
		||||
            this.editorExtraParams.pageid = this.pageId;
 | 
			
		||||
 | 
			
		||||
            if (this.section) {
 | 
			
		||||
                this.editorExtraParams.section = this.section;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (pageTitle) {
 | 
			
		||||
            this.editorExtraParams.pagetitle = pageTitle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const success = await this.fetchWikiPageData();
 | 
			
		||||
 | 
			
		||||
            if (success && !this.isDestroyed) {
 | 
			
		||||
                // Block the subwiki now that we have blockId for sure.
 | 
			
		||||
                const newBlockId = AddonModWikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId);
 | 
			
		||||
                if (newBlockId != this.blockId) {
 | 
			
		||||
                    CoreSync.unblockOperation(this.component, this.blockId);
 | 
			
		||||
                    this.blockId = newBlockId;
 | 
			
		||||
                    CoreSync.blockOperation(this.component, this.blockId);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to get wiki page data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved with boolean: whether it was successful.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchWikiPageData(): Promise<boolean> {
 | 
			
		||||
        let canEdit = false;
 | 
			
		||||
        let fetchFailed = false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Wait for sync to be over (if any).
 | 
			
		||||
            const syncResult = await AddonModWikiSync.waitForSync(this.blockId!);
 | 
			
		||||
 | 
			
		||||
            if (this.pageId) {
 | 
			
		||||
                // Editing a page that already exists.
 | 
			
		||||
                this.canEditTitle = false;
 | 
			
		||||
                this.editing = true;
 | 
			
		||||
                this.editOffline = false; // Cannot edit pages in offline.
 | 
			
		||||
 | 
			
		||||
                // Get page contents to obtain title and editing permission
 | 
			
		||||
                const pageContents = await AddonModWiki.getPageContents(this.pageId, { cmId: this.cmId });
 | 
			
		||||
 | 
			
		||||
                this.pageForm!.controls.title.setValue(pageContents.title); // Set the title in the form group.
 | 
			
		||||
                this.wikiId = pageContents.wikiid;
 | 
			
		||||
                this.subwikiId = pageContents.subwikiid;
 | 
			
		||||
                this.title = Translate.instant('addon.mod_wiki.editingpage', { $a: pageContents.title });
 | 
			
		||||
                this.groupId = pageContents.groupid;
 | 
			
		||||
                this.userId = pageContents.userid;
 | 
			
		||||
                canEdit = pageContents.caneditpage;
 | 
			
		||||
 | 
			
		||||
                // Get subwiki files, needed to replace URLs for rich text editor.
 | 
			
		||||
                this.subwikiFiles = await AddonModWiki.getSubwikiFiles(this.wikiId, {
 | 
			
		||||
                    groupId: this.groupId,
 | 
			
		||||
                    userId: this.userId,
 | 
			
		||||
                    cmId: this.cmId,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Get editable text of the page/section.
 | 
			
		||||
                const editContents = await AddonModWiki.getPageForEditing(this.pageId, this.section);
 | 
			
		||||
 | 
			
		||||
                // Get the original page contents, treating file URLs if needed.
 | 
			
		||||
                const content = CoreTextUtils.replacePluginfileUrls(editContents.content || '', this.subwikiFiles);
 | 
			
		||||
 | 
			
		||||
                this.contentControl!.setValue(content);
 | 
			
		||||
                this.originalContent = content;
 | 
			
		||||
                this.version = editContents.version;
 | 
			
		||||
 | 
			
		||||
                if (canEdit) {
 | 
			
		||||
                    // Renew the lock every certain time.
 | 
			
		||||
                    this.renewLockInterval = window.setInterval(() => {
 | 
			
		||||
                        this.renewLock();
 | 
			
		||||
                    }, AddonModWikiProvider.RENEW_LOCK_TIME);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                const pageTitle = this.pageForm!.controls.title.value;
 | 
			
		||||
                this.editing = false;
 | 
			
		||||
                canEdit = !!this.blockId; // If no blockId, the user cannot edit the page.
 | 
			
		||||
 | 
			
		||||
                // Make sure we have the wiki ID.
 | 
			
		||||
                if (!this.wikiId) {
 | 
			
		||||
                    const module = await CoreCourse.getModule(this.cmId, this.courseId, undefined, true);
 | 
			
		||||
 | 
			
		||||
                    this.wikiId = module.instance;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (pageTitle) {
 | 
			
		||||
                    // Title is set, it could be editing an offline page or creating a new page using an edit link.
 | 
			
		||||
                    // First of all, verify if this page was created in the current sync.
 | 
			
		||||
                    if (syncResult) {
 | 
			
		||||
                        const page = syncResult.created.find((page) => page.title == pageTitle);
 | 
			
		||||
 | 
			
		||||
                        if (page && page.pageId > 0) {
 | 
			
		||||
                            // Page was created, now it exists in the site.
 | 
			
		||||
                            this.pageId = page.pageId;
 | 
			
		||||
 | 
			
		||||
                            return this.fetchWikiPageData();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Check if there's already some offline data for this page.
 | 
			
		||||
                    const page = await CoreUtils.ignoreErrors(
 | 
			
		||||
                        AddonModWikiOffline.getNewPage(pageTitle, this.subwikiId, this.wikiId, this.userId, this.groupId),
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    if (page) {
 | 
			
		||||
                        // Load offline content.
 | 
			
		||||
                        this.contentControl!.setValue(page.cachedcontent);
 | 
			
		||||
                        this.originalContent = page.cachedcontent;
 | 
			
		||||
                        this.editOffline = true;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // No offline data found.
 | 
			
		||||
                        this.editOffline = false;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.editOffline = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'Error getting wiki data.');
 | 
			
		||||
            fetchFailed = true;
 | 
			
		||||
 | 
			
		||||
            // Go back.
 | 
			
		||||
            this.forceLeavePage();
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (!canEdit && !fetchFailed) {
 | 
			
		||||
                // Cannot edit, show alert and go back.
 | 
			
		||||
                CoreDomUtils.showAlert(Translate.instant('core.notice'), Translate.instant('addon.mod_wiki.cannoteditpage'));
 | 
			
		||||
                this.forceLeavePage();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Force leaving the page, without checking for changes.
 | 
			
		||||
     */
 | 
			
		||||
    protected forceLeavePage(): void {
 | 
			
		||||
        this.forceLeave = true;
 | 
			
		||||
        CoreNavigator.back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Navigate to a page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param title Page title.
 | 
			
		||||
     */
 | 
			
		||||
    protected goToPage(title: string): void {
 | 
			
		||||
        // Not the firstpage.
 | 
			
		||||
        AddonModWiki.setEditedPageData({
 | 
			
		||||
            cmId: this.cmId,
 | 
			
		||||
            courseId: this.courseId,
 | 
			
		||||
            pageId: this.pageId,
 | 
			
		||||
            pageTitle: title,
 | 
			
		||||
            wikiId: this.wikiId!,
 | 
			
		||||
            subwikiId: this.subwikiId,
 | 
			
		||||
            userId: this.userId,
 | 
			
		||||
            groupId: this.groupId,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.forceLeavePage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if data has changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether data has changed.
 | 
			
		||||
     */
 | 
			
		||||
    protected hasDataChanged(): boolean {
 | 
			
		||||
        const values = this.pageForm!.value;
 | 
			
		||||
 | 
			
		||||
        return !(this.originalContent == values.text || (!this.editing && !values.text && !values.title));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async canLeave(): Promise<boolean> {
 | 
			
		||||
        if (this.forceLeave) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if data has changed.
 | 
			
		||||
        if (this.hasDataChanged()) {
 | 
			
		||||
            await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidLeave(): void {
 | 
			
		||||
        // When going back, the ionViewDidEnter of the previous page should be called before this ionViewDidLeave.
 | 
			
		||||
        // But just in case, use a timeout to make sure it does.
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
            // Remove the edited page data (if any) if the previous page isn't a wiki page.
 | 
			
		||||
            AddonModWiki.consumeEditedPageData();
 | 
			
		||||
        }, 200);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save the data.
 | 
			
		||||
     */
 | 
			
		||||
    async save(): Promise<void> {
 | 
			
		||||
        const values = this.pageForm!.value;
 | 
			
		||||
        const title = values.title;
 | 
			
		||||
        let text = values.text;
 | 
			
		||||
 | 
			
		||||
        const modal = await CoreDomUtils.showModalLoading('core.sending', true);
 | 
			
		||||
 | 
			
		||||
        text = CoreTextUtils.restorePluginfileUrls(text, this.subwikiFiles);
 | 
			
		||||
        text = CoreTextUtils.formatHtmlLines(text);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (this.editing) {
 | 
			
		||||
                // Edit existing page.
 | 
			
		||||
                await AddonModWiki.editPage(this.pageId!, text, this.section);
 | 
			
		||||
 | 
			
		||||
                CoreForms.triggerFormSubmittedEvent(this.formElement, true, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
                // Invalidate page since it changed.
 | 
			
		||||
                await AddonModWiki.invalidatePage(this.pageId!);
 | 
			
		||||
 | 
			
		||||
                return this.goToPage(title);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Creating a new page.
 | 
			
		||||
            if (!title) {
 | 
			
		||||
                // Title is mandatory, stop.
 | 
			
		||||
                modal.dismiss();
 | 
			
		||||
                CoreDomUtils.showAlert(
 | 
			
		||||
                    Translate.instant('core.notice'),
 | 
			
		||||
                    Translate.instant('addon.mod_wiki.titleshouldnotbeempty'),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.editOffline) {
 | 
			
		||||
                // Check if the user has an offline page with the same title.
 | 
			
		||||
                const page = await CoreUtils.ignoreErrors(
 | 
			
		||||
                    AddonModWikiOffline.getNewPage(title, this.subwikiId, this.wikiId, this.userId, this.groupId),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (page) {
 | 
			
		||||
                    // There's a page with same title, reject with error message.
 | 
			
		||||
                    throw new CoreError(Translate.instant('addon.mod_wiki.pageexists'));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Try to send the page.
 | 
			
		||||
            const id = await AddonModWiki.newPage(title, text, {
 | 
			
		||||
                subwikiId: this.subwikiId,
 | 
			
		||||
                wikiId: this.wikiId,
 | 
			
		||||
                userId: this.userId,
 | 
			
		||||
                groupId: this.groupId,
 | 
			
		||||
                cmId: this.cmId,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            CoreForms.triggerFormSubmittedEvent(this.formElement, id > 0, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
            if (id <= 0) {
 | 
			
		||||
                // Page stored in offline. Go to see the offline page.
 | 
			
		||||
                return this.goToPage(title);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Page was created, get its data and go to the page.
 | 
			
		||||
            CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'wiki' });
 | 
			
		||||
            this.pageId = id;
 | 
			
		||||
 | 
			
		||||
            const pageContents = await AddonModWiki.getPageContents(this.pageId, { cmId: this.cmId });
 | 
			
		||||
 | 
			
		||||
            const promises: Promise<unknown>[] = [];
 | 
			
		||||
            this.wikiId = pageContents.wikiid;
 | 
			
		||||
 | 
			
		||||
            // Invalidate subwiki pages since there are new.
 | 
			
		||||
            promises.push(AddonModWiki.invalidateSubwikiPages(this.wikiId));
 | 
			
		||||
            if (!this.subwikiId) {
 | 
			
		||||
                // Subwiki was not created, invalidate subwikis as well.
 | 
			
		||||
                promises.push(AddonModWiki.invalidateSubwikis(this.wikiId));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.subwikiId = pageContents.subwikiid;
 | 
			
		||||
            this.userId = pageContents.userid;
 | 
			
		||||
            this.groupId = pageContents.groupid;
 | 
			
		||||
 | 
			
		||||
            await CoreUtils.ignoreErrors(Promise.all(promises));
 | 
			
		||||
 | 
			
		||||
            // Notify page created.
 | 
			
		||||
            CoreEvents.trigger(AddonModWikiProvider.PAGE_CREATED_EVENT, {
 | 
			
		||||
                pageId: this.pageId,
 | 
			
		||||
                subwikiId: this.subwikiId,
 | 
			
		||||
                pageTitle: title,
 | 
			
		||||
            }, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
            this.goToPage(title);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'Error saving wiki data.');
 | 
			
		||||
        } finally {
 | 
			
		||||
            modal.dismiss();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Renew lock and control versions.
 | 
			
		||||
     */
 | 
			
		||||
    protected async renewLock(): Promise<void> {
 | 
			
		||||
        const response = await AddonModWiki.getPageForEditing(this.pageId!, this.section, true);
 | 
			
		||||
 | 
			
		||||
        if (response.version && this.version != response.version) {
 | 
			
		||||
            this.wrongVersionLock = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.isDestroyed = true;
 | 
			
		||||
        clearInterval(this.renewLockInterval);
 | 
			
		||||
 | 
			
		||||
        // Unblock the subwiki.
 | 
			
		||||
        if (this.blockId) {
 | 
			
		||||
            CoreSync.unblockOperation(this.component, this.blockId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/addons/mod/wiki/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/addons/mod/wiki/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
<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]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
 | 
			
		||||
    <addon-mod-wiki-index [module]="module" [courseId]="courseId" [action]="action" [pageId]="pageId" [pageTitle]="pageTitle"
 | 
			
		||||
        [subwikiId]="subwikiId" [userId]="userId" [groupId]="groupId" (dataRetrieved)="updateData($event)">
 | 
			
		||||
    </addon-mod-wiki-index>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										83
									
								
								src/addons/mod/wiki/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/addons/mod/wiki/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
// (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 { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { AddonModWikiIndexComponent } from '../../components/index/index';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays a wiki page.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-wiki-index',
 | 
			
		||||
    templateUrl: 'index.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiIndexPage extends CoreCourseModuleMainActivityPage<AddonModWikiIndexComponent> implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(AddonModWikiIndexComponent) activityComponent?: AddonModWikiIndexComponent;
 | 
			
		||||
 | 
			
		||||
    action?: string;
 | 
			
		||||
    pageId?: number;
 | 
			
		||||
    pageTitle?: string;
 | 
			
		||||
    subwikiId?: number;
 | 
			
		||||
    userId?: number;
 | 
			
		||||
    groupId?: number;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        super.ngOnInit();
 | 
			
		||||
 | 
			
		||||
        this.action = CoreNavigator.getRouteParam('action') || 'page';
 | 
			
		||||
        this.pageId = CoreNavigator.getRouteNumberParam('pageId');
 | 
			
		||||
        this.pageTitle = CoreNavigator.getRouteParam('pageTitle');
 | 
			
		||||
        this.subwikiId = CoreNavigator.getRouteNumberParam('subwikiId');
 | 
			
		||||
        this.userId = CoreNavigator.getRouteNumberParam('userId');
 | 
			
		||||
        this.groupId = CoreNavigator.getRouteNumberParam('groupId');
 | 
			
		||||
 | 
			
		||||
        this.title = this.pageTitle || this.module.name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update some data based on the data received.
 | 
			
		||||
     *
 | 
			
		||||
     * @param data The data received.
 | 
			
		||||
     */
 | 
			
		||||
    updateData(data: { name: string } | string): void {
 | 
			
		||||
        if (typeof data == 'string') {
 | 
			
		||||
            // We received the title to display.
 | 
			
		||||
            this.title = data;
 | 
			
		||||
        } else {
 | 
			
		||||
            // We received a wiki instance.
 | 
			
		||||
            this.title = this.pageTitle || data.name || this.title;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * User entered the page.
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidEnter(): void {
 | 
			
		||||
        this.activityComponent?.ionViewDidEnter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * User left the page.
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidLeave(): void {
 | 
			
		||||
        this.activityComponent?.ionViewDidLeave();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								src/addons/mod/wiki/services/database/wiki.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/addons/mod/wiki/services/database/wiki.ts
									
									
									
									
									
										Normal file
									
								
							@ -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 { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for AddonModWikiOfflineProvider.
 | 
			
		||||
 */
 | 
			
		||||
export const NEW_PAGES_TABLE_NAME = 'addon_mod_wiki_new_pages_store';
 | 
			
		||||
export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'AddonModWikiOfflineProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: NEW_PAGES_TABLE_NAME,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'wikiid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'subwikiid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'userid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'groupid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'title',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'cachedcontent',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'contentformat',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timecreated',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'caneditpage',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['wikiid', 'subwikiid', 'userid', 'groupid', 'title'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Wiki new page data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiPageDBRecord = {
 | 
			
		||||
    wikiid: number;
 | 
			
		||||
    subwikiid: number;
 | 
			
		||||
    userid: number;
 | 
			
		||||
    groupid: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    cachedcontent: string;
 | 
			
		||||
    contentformat: string;
 | 
			
		||||
    courseid?: null; // Currently not used.
 | 
			
		||||
    timecreated: number;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
    caneditpage: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										151
									
								
								src/addons/mod/wiki/services/handlers/create-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/addons/mod/wiki/services/handlers/create-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
// (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 { ActivatedRoute } from '@angular/router';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWikiIndexPage } from '../../pages/index';
 | 
			
		||||
import { AddonModWiki } from '../wiki';
 | 
			
		||||
import { AddonModWikiModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to create a wiki page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiCreateLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiCreateLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModWiki';
 | 
			
		||||
    pattern = /\/mod\/wiki\/create\.php.*([&?]swid=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the current view is a wiki page of the same wiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param route Activated route if current route is wiki index page, null otherwise.
 | 
			
		||||
     * @param subwikiId Subwiki ID to check.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved with boolean: whether current view belongs to the same wiki.
 | 
			
		||||
     */
 | 
			
		||||
    protected async currentStateIsSameWiki(route: ActivatedRoute | null, subwikiId: number, siteId: string): Promise<boolean> {
 | 
			
		||||
        if (!route) {
 | 
			
		||||
            // Current view isn't wiki index.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const params = route.snapshot.params;
 | 
			
		||||
        const queryParams = route.snapshot.queryParams;
 | 
			
		||||
 | 
			
		||||
        if (queryParams.subwikiId == subwikiId) {
 | 
			
		||||
            // Same subwiki, so it's same wiki.
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const options = {
 | 
			
		||||
            cmId: params.cmId,
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.PreferCache,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (queryParams.pageId) {
 | 
			
		||||
            // Get the page contents to check the subwiki.
 | 
			
		||||
            try {
 | 
			
		||||
                const page = await AddonModWiki.getPageContents(queryParams.pageId, options);
 | 
			
		||||
 | 
			
		||||
                return page.subwikiid == subwikiId;
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Not found, check next case.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Get the wiki.
 | 
			
		||||
            const wiki = await AddonModWiki.getWiki(params.courseId, params.cmId, options);
 | 
			
		||||
 | 
			
		||||
            // Check if the subwiki belongs to this wiki.
 | 
			
		||||
            return await AddonModWiki.wikiHasSubwiki(wiki.id, subwikiId, options);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Not found, return false.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(
 | 
			
		||||
        siteIds: string[],
 | 
			
		||||
        url: string,
 | 
			
		||||
        params: Record<string, string>,
 | 
			
		||||
        courseId?: number,
 | 
			
		||||
    ): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
 | 
			
		||||
        courseId = Number(courseId || params.courseid || params.cid);
 | 
			
		||||
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const route = CoreNavigator.getCurrentRoute({ pageComponent: AddonModWikiIndexPage });
 | 
			
		||||
                    const subwikiId = parseInt(params.swid, 10);
 | 
			
		||||
                    const wikiId = parseInt(params.wid, 10);
 | 
			
		||||
                    let moduleId: number;
 | 
			
		||||
 | 
			
		||||
                    // Check if the link is inside the same wiki.
 | 
			
		||||
                    const isSameWiki = await this.currentStateIsSameWiki(route, subwikiId, siteId);
 | 
			
		||||
 | 
			
		||||
                    if (isSameWiki) {
 | 
			
		||||
                        // User is seeing the wiki, we can get the module from the wiki params.
 | 
			
		||||
                        moduleId = route!.snapshot.params.cmId;
 | 
			
		||||
                        courseId = route!.snapshot.params.courseId;
 | 
			
		||||
                    } else if (wikiId) {
 | 
			
		||||
                        // The URL specifies which wiki it belongs to. Get the module.
 | 
			
		||||
                        const module = await CoreCourse.getModuleBasicInfoByInstance(wikiId, 'wiki', siteId);
 | 
			
		||||
 | 
			
		||||
                        moduleId = module.id;
 | 
			
		||||
                        courseId = module.course;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Not enough data.
 | 
			
		||||
                        throw new CoreError();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Open the page.
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModWikiModuleHandlerService.PAGE_NAME + `/${courseId}/${moduleId}/edit`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                pageTitle: params.title,
 | 
			
		||||
                                subwikiId: subwikiId,
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'addon.mod_wiki.errorloadingpage', true);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiCreateLinkHandler = makeSingleton(AddonModWikiCreateLinkHandlerService);
 | 
			
		||||
							
								
								
									
										84
									
								
								src/addons/mod/wiki/services/handlers/edit-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/addons/mod/wiki/services/handlers/edit-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWiki } from '../wiki';
 | 
			
		||||
import { AddonModWikiModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to edit a wiki page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiEditLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiEditLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModWiki';
 | 
			
		||||
    pattern = /\/mod\/wiki\/edit\.php.*([&?]pageid=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(
 | 
			
		||||
        siteIds: string[],
 | 
			
		||||
        url: string,
 | 
			
		||||
        params: Record<string, string>,
 | 
			
		||||
        courseId?: number,
 | 
			
		||||
    ): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
 | 
			
		||||
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const pageId = Number(params.pageid);
 | 
			
		||||
 | 
			
		||||
                    const pageContents = await AddonModWiki.getPageContents(pageId, { siteId });
 | 
			
		||||
 | 
			
		||||
                    const module = await CoreCourse.getModuleBasicInfoByInstance(pageContents.wikiid, 'wiki', siteId);
 | 
			
		||||
 | 
			
		||||
                    let section = '';
 | 
			
		||||
                    if (typeof params.section != 'undefined') {
 | 
			
		||||
                        section = params.section.replace(/\+/g, ' ');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    courseId = module.course || courseId || Number(params.courseid || params.cid);
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModWikiModuleHandlerService.PAGE_NAME + `/${courseId}/${module.id}/edit`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                section: section,
 | 
			
		||||
                                pageId: pageId,
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'addon.mod_wiki.errorloadingpage', true);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiEditLinkHandler = makeSingleton(AddonModWikiEditLinkHandlerService);
 | 
			
		||||
							
								
								
									
										33
									
								
								src/addons/mod/wiki/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addons/mod/wiki/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to wiki index.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiIndexLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModWiki', 'wiki', 'wid');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiIndexLinkHandler = makeSingleton(AddonModWikiIndexLinkHandlerService);
 | 
			
		||||
							
								
								
									
										33
									
								
								src/addons/mod/wiki/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addons/mod/wiki/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to wiki list page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiListLinkHandlerService extends CoreContentLinksModuleListHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiListLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModWiki', 'wiki');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiListLinkHandler = makeSingleton(AddonModWikiListLinkHandlerService);
 | 
			
		||||
							
								
								
									
										84
									
								
								src/addons/mod/wiki/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/addons/mod/wiki/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
// (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 { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { Injectable, Type } from '@angular/core';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModule } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
 | 
			
		||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWikiIndexComponent } from '../../components/index';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support wiki modules.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiModuleHandlerService implements CoreCourseModuleHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'mod_wiki';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWiki';
 | 
			
		||||
    modName = 'wiki';
 | 
			
		||||
 | 
			
		||||
    supportedFeatures = {
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPINGS]: true,
 | 
			
		||||
        [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,
 | 
			
		||||
        [CoreConstants.FEATURE_RATE]: false,
 | 
			
		||||
        [CoreConstants.FEATURE_COMMENT]: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
 | 
			
		||||
            title: module.name,
 | 
			
		||||
            class: 'addon-mod_wiki-handler',
 | 
			
		||||
            showDownloadButton: true,
 | 
			
		||||
            action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => {
 | 
			
		||||
                options = options || {};
 | 
			
		||||
                options.params = options.params || {};
 | 
			
		||||
                Object.assign(options.params, { module });
 | 
			
		||||
                const routeParams = `/${courseId}/${module.id}/page/root`;
 | 
			
		||||
 | 
			
		||||
                CoreNavigator.navigateToSitePath(AddonModWikiModuleHandlerService.PAGE_NAME + routeParams, options);
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getMainComponent(): Promise<Type<unknown>> {
 | 
			
		||||
        return AddonModWikiIndexComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiModuleHandler = makeSingleton(AddonModWikiModuleHandlerService);
 | 
			
		||||
							
								
								
									
										109
									
								
								src/addons/mod/wiki/services/handlers/page-or-map-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/addons/mod/wiki/services/handlers/page-or-map-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { Md5 } from 'ts-md5';
 | 
			
		||||
import { AddonModWiki } from '../wiki';
 | 
			
		||||
import { AddonModWikiModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to a wiki page or the wiki map.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiPageOrMapLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiPageOrMapLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModWiki';
 | 
			
		||||
    pattern = /\/mod\/wiki\/(view|map)\.php.*([&?]pageid=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(
 | 
			
		||||
        siteIds: string[],
 | 
			
		||||
        url: string,
 | 
			
		||||
        params: Record<string, string>,
 | 
			
		||||
        courseId?: number,
 | 
			
		||||
    ): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
 | 
			
		||||
 | 
			
		||||
        courseId = Number(courseId || params.courseid || params.cid);
 | 
			
		||||
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
                const pageId = parseInt(params.pageid, 10);
 | 
			
		||||
                const action = url.indexOf('mod/wiki/map.php') != -1 ? 'map' : 'page';
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    // Get the page data to obtain wikiId, subwikiId, etc.
 | 
			
		||||
                    const page = await AddonModWiki.getPageContents(pageId, { siteId });
 | 
			
		||||
 | 
			
		||||
                    const module = await CoreCourse.getModuleBasicInfoByInstance(page.wikiid, 'wiki', siteId);
 | 
			
		||||
 | 
			
		||||
                    const hash = <string> Md5.hashAsciiStr(JSON.stringify({
 | 
			
		||||
                        pageId: page.id,
 | 
			
		||||
                        pageTitle: page.title,
 | 
			
		||||
                        subwikiId: page.subwikiid,
 | 
			
		||||
                        action: action,
 | 
			
		||||
                        timestamp: Date.now(),
 | 
			
		||||
                    }));
 | 
			
		||||
                    courseId = courseId || module.course;
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModWikiModuleHandlerService.PAGE_NAME + `/${courseId}/${module.id}/page/${hash}`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                pageId: page.id,
 | 
			
		||||
                                pageTitle: page.title,
 | 
			
		||||
                                subwikiId: page.subwikiid,
 | 
			
		||||
                                action: action,
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'addon.mod_wiki.errorloadingpage', true);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        const isMap = url.indexOf('mod/wiki/map.php') != -1;
 | 
			
		||||
 | 
			
		||||
        if (params.id && !isMap) {
 | 
			
		||||
            // ID param is more prioritary than pageid in index page, it's a index URL.
 | 
			
		||||
            return false;
 | 
			
		||||
        } else if (isMap && typeof params.option != 'undefined' && params.option != '5') {
 | 
			
		||||
            // Map link but the option isn't "Page list", not supported.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiPageOrMapLinkHandler = makeSingleton(AddonModWikiPageOrMapLinkHandlerService);
 | 
			
		||||
							
								
								
									
										206
									
								
								src/addons/mod/wiki/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								src/addons/mod/wiki/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,206 @@
 | 
			
		||||
// (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 { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
 | 
			
		||||
import { CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreGroups } from '@services/groups';
 | 
			
		||||
import { CoreFileSizeSum, CorePluginFileDelegate } from '@services/plugin-file-delegate';
 | 
			
		||||
import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWiki, AddonModWikiProvider, AddonModWikiSubwikiPage } from '../wiki';
 | 
			
		||||
import { AddonModWikiSync, AddonModWikiSyncWikiResult } from '../wiki-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to prefetch wikis.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWiki';
 | 
			
		||||
    modName = 'wiki';
 | 
			
		||||
    component = AddonModWikiProvider.COMPONENT;
 | 
			
		||||
    updatesNames = /^.*files$|^pages$/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list of pages that can be downloaded.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module The module object returned by WS.
 | 
			
		||||
     * @param courseId The course ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return List of pages.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getAllPages(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        options: CoreSitesCommonWSOptions = {},
 | 
			
		||||
    ): Promise<AddonModWikiSubwikiPage[]> {
 | 
			
		||||
        options.siteId = options.siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const wiki = await AddonModWiki.getWiki(courseId, module.id, options);
 | 
			
		||||
 | 
			
		||||
            return await AddonModWiki.getWikiPageList(wiki, options);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Wiki not found, return empty list.
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getDownloadSize(module: CoreCourseAnyModuleData, courseId: number, single?: boolean): Promise<CoreFileSizeSum> {
 | 
			
		||||
        const promises: Promise<CoreFileSizeSum>[] = [];
 | 
			
		||||
        const siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        promises.push(this.getFiles(module, courseId, single, siteId).then((files) =>
 | 
			
		||||
            CorePluginFileDelegate.getFilesDownloadSize(files)));
 | 
			
		||||
 | 
			
		||||
        promises.push(this.getAllPages(module, courseId, {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        }).then((pages) => {
 | 
			
		||||
            let size = 0;
 | 
			
		||||
 | 
			
		||||
            pages.forEach((page) => {
 | 
			
		||||
                if (page.contentsize) {
 | 
			
		||||
                    size = size + page.contentsize;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return { size: size, total: true };
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        const sizes = await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            size: sizes[0].size + sizes[1].size,
 | 
			
		||||
            total: sizes[0].total && sizes[1].total,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getFiles(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        single?: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreWSExternalFile[]> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const wiki = await AddonModWiki.getWiki(courseId, module.id, { siteId });
 | 
			
		||||
 | 
			
		||||
            const introFiles = this.getIntroFilesFromInstance(module, wiki);
 | 
			
		||||
 | 
			
		||||
            const files = await AddonModWiki.getWikiFileList(wiki, { siteId });
 | 
			
		||||
 | 
			
		||||
            return introFiles.concat(files);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Wiki not found, return empty list.
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModWiki.invalidateContent(moduleId, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async prefetch(module: CoreCourseAnyModuleData, courseId?: number, single?: boolean): Promise<void> {
 | 
			
		||||
        // Get the download time of the package before starting the download (otherwise we'd always get current time).
 | 
			
		||||
        const siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const data = await CoreUtils.ignoreErrors(CoreFilepool.getPackageData(siteId, this.component, module.id));
 | 
			
		||||
 | 
			
		||||
        const downloadTime = data?.downloadTime || 0;
 | 
			
		||||
 | 
			
		||||
        return this.prefetchPackage(module, courseId, this.prefetchWiki.bind(this, module, courseId, single, downloadTime, siteId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a wiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @param single True if we're downloading a single module, false if we're downloading a whole section.
 | 
			
		||||
     * @param downloadTime The previous download time, 0 if no previous download.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchWiki(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        single: boolean,
 | 
			
		||||
        downloadTime: number,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const userId = CoreSites.getCurrentSiteUserId();
 | 
			
		||||
 | 
			
		||||
        const commonOptions = {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
        const modOptions = {
 | 
			
		||||
            cmId: module.id,
 | 
			
		||||
            ...commonOptions, // Include all common options.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Get the list of pages.
 | 
			
		||||
        const pages = await this.getAllPages(module, courseId, commonOptions);
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        pages.forEach((page) => {
 | 
			
		||||
            // Fetch page contents if it needs to be fetched.
 | 
			
		||||
            if (page.timemodified > downloadTime) {
 | 
			
		||||
                promises.push(AddonModWiki.getPageContents(page.id, modOptions));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Fetch group data.
 | 
			
		||||
        promises.push(CoreGroups.getActivityGroupInfo(module.id, false, userId, siteId));
 | 
			
		||||
 | 
			
		||||
        // Fetch info to provide wiki links.
 | 
			
		||||
        promises.push(AddonModWiki.getWiki(courseId, module.id, { siteId }).then((wiki) =>
 | 
			
		||||
            CoreCourseHelper.getModuleCourseIdByInstance(wiki.id, 'wiki', siteId)));
 | 
			
		||||
 | 
			
		||||
        // Get related page files and fetch them.
 | 
			
		||||
        promises.push(this.getFiles(module, courseId, single, siteId).then((files) =>
 | 
			
		||||
            CoreFilepool.addFilesToQueue(siteId, files, this.component, module.id)));
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModWikiSyncWikiResult> {
 | 
			
		||||
        return AddonModWikiSync.syncWiki(module.instance!, module.course, module.id, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiPrefetchHandler = makeSingleton(AddonModWikiPrefetchHandlerService);
 | 
			
		||||
							
								
								
									
										44
									
								
								src/addons/mod/wiki/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/addons/mod/wiki/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -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 { CoreCronHandler } from '@services/cron';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWikiSync } from '../wiki-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Synchronization cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiSyncCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiSyncCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    execute(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return AddonModWikiSync.syncAllWikis(siteId, force);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return AddonModWikiSync.syncInterval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiSyncCronHandler = makeSingleton(AddonModWikiSyncCronHandlerService);
 | 
			
		||||
							
								
								
									
										53
									
								
								src/addons/mod/wiki/services/handlers/tag-area.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/addons/mod/wiki/services/handlers/tag-area.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
// (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 { CoreTagFeedComponent } from '@features/tag/components/feed/feed';
 | 
			
		||||
import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate';
 | 
			
		||||
import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support tags.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiTagAreaHandlerService implements CoreTagAreaHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWikiTagAreaHandler';
 | 
			
		||||
    type = 'mod_wiki/wiki_pages';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    parseContent(content: string): CoreTagFeedElement[] {
 | 
			
		||||
        return CoreTagHelper.parseFeedContent(content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getComponent(): Type<unknown> {
 | 
			
		||||
        return CoreTagFeedComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiTagAreaHandler = makeSingleton(AddonModWikiTagAreaHandlerService);
 | 
			
		||||
							
								
								
									
										233
									
								
								src/addons/mod/wiki/services/wiki-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/addons/mod/wiki/services/wiki-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,233 @@
 | 
			
		||||
// (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 { CoreSites } from '@services/sites';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWikiPageDBRecord, NEW_PAGES_TABLE_NAME } from './database/wiki';
 | 
			
		||||
import { AddonModWikiSubwiki } from './wiki';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle offline wiki.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiOfflineProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a value to a positive number. If not a number or less than 0, 0 will be returned.
 | 
			
		||||
     *
 | 
			
		||||
     * @param value Value to convert.
 | 
			
		||||
     * @return Converted value.
 | 
			
		||||
     */
 | 
			
		||||
    convertToPositiveNumber(value: string | number | undefined): number {
 | 
			
		||||
        value = Number(value);
 | 
			
		||||
 | 
			
		||||
        return value > 0 ? value : 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete a new page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param title Title of the page.
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param userId User ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if deleted, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteNewPage(
 | 
			
		||||
        title: string,
 | 
			
		||||
        subwikiId?: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        subwikiId = this.convertToPositiveNumber(subwikiId);
 | 
			
		||||
        wikiId = this.convertToPositiveNumber(wikiId);
 | 
			
		||||
        userId = this.convertToPositiveNumber(userId);
 | 
			
		||||
        groupId = this.convertToPositiveNumber(groupId);
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(NEW_PAGES_TABLE_NAME, <Partial<AddonModWikiPageDBRecord>> {
 | 
			
		||||
            subwikiid: subwikiId,
 | 
			
		||||
            wikiid: wikiId,
 | 
			
		||||
            userid: userId,
 | 
			
		||||
            groupid: groupId,
 | 
			
		||||
            title: title,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the stored new pages from all the wikis.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with pages.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllNewPages(siteId?: string): Promise<AddonModWikiPageDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getAllRecords(NEW_PAGES_TABLE_NAME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a stored new page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param title Title of the page.
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param userId User ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with page.
 | 
			
		||||
     */
 | 
			
		||||
    async getNewPage(
 | 
			
		||||
        title: string,
 | 
			
		||||
        subwikiId?: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWikiPageDBRecord> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        subwikiId = this.convertToPositiveNumber(subwikiId);
 | 
			
		||||
        wikiId = this.convertToPositiveNumber(wikiId);
 | 
			
		||||
        userId = this.convertToPositiveNumber(userId);
 | 
			
		||||
        groupId = this.convertToPositiveNumber(groupId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecord(NEW_PAGES_TABLE_NAME, <Partial<AddonModWikiPageDBRecord>> {
 | 
			
		||||
            subwikiid: subwikiId,
 | 
			
		||||
            wikiid: wikiId,
 | 
			
		||||
            userid: userId,
 | 
			
		||||
            groupid: groupId,
 | 
			
		||||
            title: title,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the stored new pages from a certain subwiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param userId User ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with pages.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubwikiNewPages(
 | 
			
		||||
        subwikiId?: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWikiPageDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        subwikiId = this.convertToPositiveNumber(subwikiId);
 | 
			
		||||
        wikiId = this.convertToPositiveNumber(wikiId);
 | 
			
		||||
        userId = this.convertToPositiveNumber(userId);
 | 
			
		||||
        groupId = this.convertToPositiveNumber(groupId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords(NEW_PAGES_TABLE_NAME, <Partial<AddonModWikiPageDBRecord>> {
 | 
			
		||||
            subwikiid: subwikiId,
 | 
			
		||||
            wikiid: wikiId,
 | 
			
		||||
            userid: userId,
 | 
			
		||||
            groupid: groupId,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the stored new pages from a list of subwikis.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikis List of subwiki.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with pages.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubwikisNewPages(subwikis: AddonModWikiSubwiki[], siteId?: string): Promise<AddonModWikiPageDBRecord[]> {
 | 
			
		||||
        let pages: AddonModWikiPageDBRecord[] = [];
 | 
			
		||||
 | 
			
		||||
        await Promise.all(subwikis.map(async (subwiki) => {
 | 
			
		||||
            const subwikiPages = await this.getSubwikiNewPages(subwiki.id, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId);
 | 
			
		||||
 | 
			
		||||
            pages = pages.concat(subwikiPages);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return pages;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save a new page to be sent later.
 | 
			
		||||
     *
 | 
			
		||||
     * @param title Title of the page.
 | 
			
		||||
     * @param content Content of the page.
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param userId User ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used create subwiki if not informed.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async saveNewPage(
 | 
			
		||||
        title: string,
 | 
			
		||||
        content: string,
 | 
			
		||||
        subwikiId?: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const now = Date.now();
 | 
			
		||||
        const entry: AddonModWikiPageDBRecord = {
 | 
			
		||||
            title: title,
 | 
			
		||||
            cachedcontent: content,
 | 
			
		||||
            subwikiid: this.convertToPositiveNumber(subwikiId),
 | 
			
		||||
            wikiid: this.convertToPositiveNumber(wikiId),
 | 
			
		||||
            userid: this.convertToPositiveNumber(userId),
 | 
			
		||||
            groupid: this.convertToPositiveNumber(groupId),
 | 
			
		||||
            contentformat: 'html',
 | 
			
		||||
            timecreated: now,
 | 
			
		||||
            timemodified: now,
 | 
			
		||||
            caneditpage: 1,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(NEW_PAGES_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a list of subwikis have offline data stored.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikis List of subwikis.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with boolean: whether it has offline data.
 | 
			
		||||
     */
 | 
			
		||||
    async subwikisHaveOfflineData(subwikis: AddonModWikiSubwiki[], siteId?: string): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            const pages = await this.getSubwikisNewPages(subwikis, siteId);
 | 
			
		||||
 | 
			
		||||
            return !!pages.length;
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Error, return false.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiOffline = makeSingleton(AddonModWikiOfflineProvider);
 | 
			
		||||
							
								
								
									
										417
									
								
								src/addons/mod/wiki/services/wiki-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										417
									
								
								src/addons/mod/wiki/services/wiki-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,417 @@
 | 
			
		||||
// (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 { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync';
 | 
			
		||||
import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreGroups } from '@services/groups';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreSync } from '@services/sync';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { AddonModWikiPageDBRecord } from './database/wiki';
 | 
			
		||||
import { AddonModWiki, AddonModWikiProvider } from './wiki';
 | 
			
		||||
import { AddonModWikiOffline } from './wiki-offline';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to sync wikis.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWikiSyncProvider extends CoreSyncBaseProvider<AddonModWikiSyncSubwikiResult> {
 | 
			
		||||
 | 
			
		||||
    static readonly AUTO_SYNCED = 'addon_mod_wiki_autom_synced';
 | 
			
		||||
    static readonly MANUAL_SYNCED = 'addon_mod_wiki_manual_synced';
 | 
			
		||||
 | 
			
		||||
    protected componentTranslatableString = 'wiki';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModWikiSyncProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a string to identify a subwiki. If it doesn't have a subwiki ID it will be identified by wiki ID, user ID and group ID.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @return Identifier.
 | 
			
		||||
     */
 | 
			
		||||
    getSubwikiBlockId(subwikiId?: number, wikiId?: number, userId?: number, groupId?: number): string {
 | 
			
		||||
        subwikiId = AddonModWikiOffline.convertToPositiveNumber(subwikiId);
 | 
			
		||||
 | 
			
		||||
        if (subwikiId && subwikiId > 0) {
 | 
			
		||||
            return String(subwikiId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wikiId = AddonModWikiOffline.convertToPositiveNumber(wikiId);
 | 
			
		||||
        userId = AddonModWikiOffline.convertToPositiveNumber(userId);
 | 
			
		||||
        groupId = AddonModWikiOffline.convertToPositiveNumber(groupId);
 | 
			
		||||
 | 
			
		||||
        return `${wikiId}:${userId}:${groupId}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize all the wikis in a certain site or in all sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID to sync. If not defined, sync all sites.
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    syncAllWikis(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return this.syncOnSites('all wikis', this.syncAllWikisFunc.bind(this, !!force), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync all wikis on a site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @param siteId Site ID to sync.
 | 
			
		||||
     * @param Promise resolved if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncAllWikisFunc(force: boolean, siteId: string): Promise<void> {
 | 
			
		||||
        // Get all the pages created in offline.
 | 
			
		||||
        const pages = await AddonModWikiOffline.getAllNewPages(siteId);
 | 
			
		||||
 | 
			
		||||
        const subwikis: Record<string, boolean> = {};
 | 
			
		||||
 | 
			
		||||
        // Sync all subwikis.
 | 
			
		||||
        await Promise.all(pages.map(async (page) => {
 | 
			
		||||
            const index = this.getSubwikiBlockId(page.subwikiid, page.wikiid, page.userid, page.groupid);
 | 
			
		||||
 | 
			
		||||
            if (subwikis[index]) {
 | 
			
		||||
                // Already synced.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            subwikis[index] = true;
 | 
			
		||||
 | 
			
		||||
            const result = force ?
 | 
			
		||||
                await this.syncSubwiki(page.subwikiid, page.wikiid, page.userid, page.groupid, siteId) :
 | 
			
		||||
                await this.syncSubwikiIfNeeded(page.subwikiid, page.wikiid, page.userid, page.groupid, siteId);
 | 
			
		||||
 | 
			
		||||
            if (result?.updated) {
 | 
			
		||||
                // Sync successful, send event.
 | 
			
		||||
                CoreEvents.trigger(AddonModWikiSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    siteId: siteId,
 | 
			
		||||
                    subwikiId: page.subwikiid,
 | 
			
		||||
                    wikiId: page.wikiid,
 | 
			
		||||
                    userId: page.userid,
 | 
			
		||||
                    groupId: page.groupid,
 | 
			
		||||
                    created: result.created,
 | 
			
		||||
                    discarded: result.discarded,
 | 
			
		||||
                    warnings: result.warnings,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync a subwiki only if a certain time has passed since the last time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when subwiki is synced or doesn't need to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async syncSubwikiIfNeeded(
 | 
			
		||||
        subwikiId: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWikiSyncSubwikiResult | undefined> {
 | 
			
		||||
 | 
			
		||||
        const blockId = this.getSubwikiBlockId(subwikiId, wikiId, userId, groupId);
 | 
			
		||||
 | 
			
		||||
        const needed = await this.isSyncNeeded(blockId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (needed) {
 | 
			
		||||
            return this.syncSubwiki(subwikiId, wikiId, userId, groupId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a subwiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    syncSubwiki(
 | 
			
		||||
        subwikiId: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWikiSyncSubwikiResult> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const subwikiBlockId = this.getSubwikiBlockId(subwikiId, wikiId, userId, groupId);
 | 
			
		||||
 | 
			
		||||
        if (this.isSyncing(subwikiBlockId, siteId)) {
 | 
			
		||||
            // There's already a sync ongoing for this subwiki, return the promise.
 | 
			
		||||
            return this.getOngoingSync(subwikiBlockId, siteId)!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Verify that subwiki isn't blocked.
 | 
			
		||||
        if (CoreSync.isBlocked(AddonModWikiProvider.COMPONENT, subwikiBlockId, siteId)) {
 | 
			
		||||
            this.logger.debug(`Cannot sync subwiki ${subwikiBlockId} because it is blocked.`);
 | 
			
		||||
 | 
			
		||||
            throw new CoreSyncBlockedError(Translate.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Try to sync subwiki ${subwikiBlockId}`);
 | 
			
		||||
 | 
			
		||||
        return this.addOngoingSync(subwikiBlockId, this.performSyncSubwiki(subwikiId, wikiId, userId, groupId, siteId), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a subwiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined.
 | 
			
		||||
     * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async performSyncSubwiki(
 | 
			
		||||
        subwikiId: number,
 | 
			
		||||
        wikiId?: number,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        groupId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWikiSyncSubwikiResult> {
 | 
			
		||||
        const result: AddonModWikiSyncSubwikiResult = {
 | 
			
		||||
            warnings: [],
 | 
			
		||||
            updated: false,
 | 
			
		||||
            created: [],
 | 
			
		||||
            discarded: [],
 | 
			
		||||
        };
 | 
			
		||||
        const subwikiBlockId = this.getSubwikiBlockId(subwikiId, wikiId, userId, groupId);
 | 
			
		||||
 | 
			
		||||
        // Get offline pages to be sent.
 | 
			
		||||
        const pages = await CoreUtils.ignoreErrors(
 | 
			
		||||
            AddonModWikiOffline.getSubwikiNewPages(subwikiId, wikiId, userId, groupId, siteId),
 | 
			
		||||
            <AddonModWikiPageDBRecord[]> [],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!pages || !pages.length) {
 | 
			
		||||
            // Nothing to sync.
 | 
			
		||||
            await CoreUtils.ignoreErrors(this.setSyncTime(subwikiBlockId, siteId));
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // Cannot sync in offline.
 | 
			
		||||
            throw new CoreNetworkError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Send the pages.
 | 
			
		||||
        await Promise.all(pages.map(async (page) => {
 | 
			
		||||
            try {
 | 
			
		||||
                const pageId = await AddonModWiki.newPageOnline(page.title, page.cachedcontent, {
 | 
			
		||||
                    subwikiId,
 | 
			
		||||
                    wikiId,
 | 
			
		||||
                    userId,
 | 
			
		||||
                    groupId,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
                result.created.push({
 | 
			
		||||
                    pageId: pageId,
 | 
			
		||||
                    title: page.title,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Delete the local page.
 | 
			
		||||
                await AddonModWikiOffline.deleteNewPage(page.title, subwikiId, wikiId, userId, groupId, siteId);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                if (!CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                    // Couldn't connect to server, reject.
 | 
			
		||||
                    throw error;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // The WebService has thrown an error, this means that the page cannot be submitted. Delete it.
 | 
			
		||||
                await AddonModWikiOffline.deleteNewPage(page.title, subwikiId, wikiId, userId, groupId, siteId);
 | 
			
		||||
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
 | 
			
		||||
                // Page deleted, add the page to discarded pages and add a warning.
 | 
			
		||||
                const warning = Translate.instant('core.warningofflinedatadeleted', {
 | 
			
		||||
                    component: Translate.instant('addon.mod_wiki.wikipage'),
 | 
			
		||||
                    name: page.title,
 | 
			
		||||
                    error: CoreTextUtils.getErrorMessageFromError(error),
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                result.discarded.push({
 | 
			
		||||
                    title: page.title,
 | 
			
		||||
                    warning: warning,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                result.warnings.push(warning);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Sync finished, set sync time.
 | 
			
		||||
        await CoreUtils.ignoreErrors(this.setSyncTime(subwikiBlockId, siteId));
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tries to synchronize a wiki.
 | 
			
		||||
     *
 | 
			
		||||
     * @param wikiId Wiki ID.
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param cmId Wiki course module ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async syncWiki(wikiId: number, courseId?: number, cmId?: number, siteId?: string): Promise<AddonModWikiSyncWikiResult> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // Sync offline logs.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(AddonModWikiProvider.COMPONENT, wikiId, siteId));
 | 
			
		||||
 | 
			
		||||
        // Sync is done at subwiki level, get all the subwikis.
 | 
			
		||||
        const subwikis = await AddonModWiki.getSubwikis(wikiId, { cmId, siteId });
 | 
			
		||||
 | 
			
		||||
        const result: AddonModWikiSyncWikiResult = {
 | 
			
		||||
            warnings: [],
 | 
			
		||||
            updated: false,
 | 
			
		||||
            subwikis: {},
 | 
			
		||||
            siteId: siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await Promise.all(subwikis.map(async (subwiki) => {
 | 
			
		||||
            const data = await this.syncSubwiki(subwiki.id, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId);
 | 
			
		||||
 | 
			
		||||
            if (data && data.updated) {
 | 
			
		||||
                result.warnings = result.warnings.concat(data.warnings);
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
                result.subwikis[subwiki.id] = {
 | 
			
		||||
                    created: data.created,
 | 
			
		||||
                    discarded: data.discarded,
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (result.updated) {
 | 
			
		||||
            const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
            // Something has changed, invalidate data.
 | 
			
		||||
            if (wikiId) {
 | 
			
		||||
                promises.push(AddonModWiki.invalidateSubwikis(wikiId));
 | 
			
		||||
                promises.push(AddonModWiki.invalidateSubwikiPages(wikiId));
 | 
			
		||||
                promises.push(AddonModWiki.invalidateSubwikiFiles(wikiId));
 | 
			
		||||
            }
 | 
			
		||||
            if (courseId) {
 | 
			
		||||
                promises.push(AddonModWiki.invalidateWikiData(courseId));
 | 
			
		||||
            }
 | 
			
		||||
            if (cmId) {
 | 
			
		||||
                promises.push(CoreGroups.invalidateActivityAllowedGroups(cmId));
 | 
			
		||||
                promises.push(CoreGroups.invalidateActivityGroupMode(cmId));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await CoreUtils.ignoreErrors(Promise.all(promises));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModWikiSync = makeSingleton(AddonModWikiSyncProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a subwiki sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiSyncSubwikiResult = {
 | 
			
		||||
    warnings: string[]; // List of warnings.
 | 
			
		||||
    updated: boolean; // Whether data was updated in the site.
 | 
			
		||||
    created: AddonModWikiCreatedPage[]; // List of created pages.
 | 
			
		||||
    discarded: AddonModWikiDiscardedPage[]; // List of discarded pages.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a wiki sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiSyncWikiResult = {
 | 
			
		||||
    warnings: string[]; // List of warnings.
 | 
			
		||||
    updated: boolean; // Whether data was updated in the site.
 | 
			
		||||
    subwikis: {
 | 
			
		||||
        [subwikiId: number]: { // List of subwikis.
 | 
			
		||||
            created: AddonModWikiCreatedPage[];
 | 
			
		||||
            discarded: AddonModWikiDiscardedPage[];
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
    siteId: string; // Site ID.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a wiki sync for each subwiki synced.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiSyncWikiSubwiki = {
 | 
			
		||||
    created: AddonModWikiCreatedPage[];
 | 
			
		||||
    discarded: AddonModWikiDiscardedPage[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data to identify a page created in sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiCreatedPage = {
 | 
			
		||||
    pageId: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data to identify a page discarded in sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiDiscardedPage = {
 | 
			
		||||
    title: string;
 | 
			
		||||
    warning: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to AUTO_SYNCED event.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiAutoSyncData = {
 | 
			
		||||
    siteId: string;
 | 
			
		||||
    subwikiId: number;
 | 
			
		||||
    wikiId: number;
 | 
			
		||||
    userId: number;
 | 
			
		||||
    groupId: number;
 | 
			
		||||
    created: AddonModWikiCreatedPage[];
 | 
			
		||||
    discarded: AddonModWikiDiscardedPage[];
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to MANUAL_SYNCED event.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWikiManualSyncData = AddonModWikiSyncWikiResult & {
 | 
			
		||||
    wikiId: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1227
									
								
								src/addons/mod/wiki/services/wiki.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1227
									
								
								src/addons/mod/wiki/services/wiki.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										48
									
								
								src/addons/mod/wiki/wiki-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/addons/mod/wiki/wiki-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
// (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 { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModWikiComponentsModule } from './components/components.module';
 | 
			
		||||
import { AddonModWikiIndexPage } from './pages/index/index';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
import { CanLeaveGuard } from '@guards/can-leave';
 | 
			
		||||
import { AddonModWikiEditPage } from './pages/edit/edit';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/page/:hash',
 | 
			
		||||
        component: AddonModWikiIndexPage,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/edit',
 | 
			
		||||
        component: AddonModWikiEditPage,
 | 
			
		||||
        canDeactivate: [CanLeaveGuard],
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonModWikiComponentsModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModWikiIndexPage,
 | 
			
		||||
        AddonModWikiEditPage,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiLazyModule {}
 | 
			
		||||
							
								
								
									
										81
									
								
								src/addons/mod/wiki/wiki.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/addons/mod/wiki/wiki.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
// (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, Type } 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 { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate';
 | 
			
		||||
import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
import { AddonModWikiComponentsModule } from './components/components.module';
 | 
			
		||||
import { OFFLINE_SITE_SCHEMA } from './services/database/wiki';
 | 
			
		||||
import { AddonModWikiCreateLinkHandler } from './services/handlers/create-link';
 | 
			
		||||
import { AddonModWikiEditLinkHandler } from './services/handlers/edit-link';
 | 
			
		||||
import { AddonModWikiIndexLinkHandler } from './services/handlers/index-link';
 | 
			
		||||
import { AddonModWikiListLinkHandler } from './services/handlers/list-link';
 | 
			
		||||
import { AddonModWikiModuleHandler, AddonModWikiModuleHandlerService } from './services/handlers/module';
 | 
			
		||||
import { AddonModWikiPageOrMapLinkHandler } from './services/handlers/page-or-map-link';
 | 
			
		||||
import { AddonModWikiPrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { AddonModWikiSyncCronHandler } from './services/handlers/sync-cron';
 | 
			
		||||
import { AddonModWikiTagAreaHandler } from './services/handlers/tag-area';
 | 
			
		||||
import { AddonModWikiProvider } from './services/wiki';
 | 
			
		||||
import { AddonModWikiOfflineProvider } from './services/wiki-offline';
 | 
			
		||||
import { AddonModWikiSyncProvider } from './services/wiki-sync';
 | 
			
		||||
 | 
			
		||||
export const ADDON_MOD_WIKI_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    AddonModWikiProvider,
 | 
			
		||||
    AddonModWikiOfflineProvider,
 | 
			
		||||
    AddonModWikiSyncProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonModWikiModuleHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('./wiki-lazy.module').then(m => m.AddonModWikiLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        AddonModWikiComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [OFFLINE_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                CoreCourseModuleDelegate.registerHandler(AddonModWikiModuleHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.registerHandler(AddonModWikiPrefetchHandler.instance);
 | 
			
		||||
                CoreCronDelegate.register(AddonModWikiSyncCronHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWikiIndexLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWikiListLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWikiCreateLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWikiEditLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWikiPageOrMapLinkHandler.instance);
 | 
			
		||||
                CoreTagAreaDelegate.registerHandler(AddonModWikiTagAreaHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWikiModule {}
 | 
			
		||||
@ -17,6 +17,8 @@ import { SQLiteObject } from '@ionic-native/sqlite/ngx';
 | 
			
		||||
import { SQLite, Platform } from '@singletons';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
 | 
			
		||||
type SQLiteDBColumnType = 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Schema of a table.
 | 
			
		||||
 */
 | 
			
		||||
@ -64,7 +66,7 @@ export interface SQLiteDBColumnSchema {
 | 
			
		||||
    /**
 | 
			
		||||
     * Column's type.
 | 
			
		||||
     */
 | 
			
		||||
    type?: 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB';
 | 
			
		||||
    type?: SQLiteDBColumnType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the column is a primary key. Use it only if primary key is a single column.
 | 
			
		||||
@ -145,6 +147,30 @@ export class SQLiteDB {
 | 
			
		||||
        this.init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a column to an existing table.
 | 
			
		||||
     *
 | 
			
		||||
     * @param table Table name.
 | 
			
		||||
     * @param column Name of the column to add.
 | 
			
		||||
     * @param type Type of the column to add.
 | 
			
		||||
     * @param constraints Other constraints (e.g. NOT NULL).
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async addColumn(table: string, column: string, type: SQLiteDBColumnType, constraints?: string): Promise<void> {
 | 
			
		||||
        constraints = constraints || '';
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.execute(`ALTER TABLE ${table} ADD COLUMN ${column} ${type} ${constraints}`);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error && error.code == 5 && error?.message.indexOf('duplicate column name') != -1) {
 | 
			
		||||
                // Column already exists.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper function to create a table if it doesn't exist.
 | 
			
		||||
     *
 | 
			
		||||
@ -839,25 +865,19 @@ export class SQLiteDB {
 | 
			
		||||
     *
 | 
			
		||||
     * @param table The database table to be inserted into.
 | 
			
		||||
     * @param source The database table to get the records from.
 | 
			
		||||
     * @param conditions The conditions to build the where clause. Must not contain numeric indexes.
 | 
			
		||||
     * @param fields A comma separated list of fields to return.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async insertRecordsFrom(
 | 
			
		||||
        table: string,
 | 
			
		||||
        source: string,
 | 
			
		||||
        conditions?: SQLiteDBRecordValues,
 | 
			
		||||
        fields: string = '*',
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const selectAndParams = this.whereClause(conditions);
 | 
			
		||||
        const select = selectAndParams.sql ? 'WHERE ' + selectAndParams.sql : '';
 | 
			
		||||
        const params = selectAndParams.params;
 | 
			
		||||
        const records = await this.getAllRecords<SQLiteDBRecordValues>(source);
 | 
			
		||||
 | 
			
		||||
        await this.execute(`INSERT INTO ${table} SELECT ${fields} FROM ${source} ${select}`, params);
 | 
			
		||||
        await Promise.all(records.map((record) => this.insertRecord(table, record)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper migration function for tables.
 | 
			
		||||
     * Migrate all the data from a table to another table.
 | 
			
		||||
     * It will check if old table exists and drop it when finished.
 | 
			
		||||
     *
 | 
			
		||||
     * @param oldTable Old table name.
 | 
			
		||||
 | 
			
		||||
@ -141,7 +141,7 @@ import { ADDON_MOD_RESOURCE_SERVICES } from '@addons/mod/resource/resource.modul
 | 
			
		||||
import { ADDON_MOD_SCORM_SERVICES } from '@addons/mod/scorm/scorm.module';
 | 
			
		||||
import { ADDON_MOD_SURVEY_SERVICES } from '@addons/mod/survey/survey.module';
 | 
			
		||||
import { ADDON_MOD_URL_SERVICES } from '@addons/mod/url/url.module';
 | 
			
		||||
// @todo import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module';
 | 
			
		||||
import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module';
 | 
			
		||||
// @todo import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module';
 | 
			
		||||
import { ADDON_NOTES_SERVICES } from '@addons/notes/notes.module';
 | 
			
		||||
import { ADDON_NOTIFICATIONS_SERVICES } from '@addons/notifications/notifications.module';
 | 
			
		||||
@ -307,7 +307,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...ADDON_MOD_SCORM_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_SURVEY_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_URL_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_WIKI_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_WIKI_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_WORKSHOP_SERVICES,
 | 
			
		||||
            ...ADDON_NOTES_SERVICES,
 | 
			
		||||
            ...ADDON_NOTIFICATIONS_SERVICES,
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,7 @@ import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CoreCourseLogCronHandler } from './handlers/log-cron';
 | 
			
		||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
 | 
			
		||||
import { CoreCourseAutoSyncData, CoreCourseSyncProvider } from './sync';
 | 
			
		||||
import { CoreTagItem } from '@features/tag/services/tag';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmCourse:';
 | 
			
		||||
 | 
			
		||||
@ -1473,18 +1474,7 @@ export type CoreCourseModuleContentFile = {
 | 
			
		||||
    userid: number; // User who added this content to moodle.
 | 
			
		||||
    author: string; // Content owner.
 | 
			
		||||
    license: string; // Content license.
 | 
			
		||||
    tags?: { // Tags.
 | 
			
		||||
        id: number; // Tag id.
 | 
			
		||||
        name: string; // Tag name.
 | 
			
		||||
        rawname: string; // The raw, unnormalised name for the tag as entered by users.
 | 
			
		||||
        isstandard: boolean; // Whether this tag is standard.
 | 
			
		||||
        tagcollid: number; // Tag collection id.
 | 
			
		||||
        taginstanceid: number; // Tag instance id.
 | 
			
		||||
        taginstancecontextid: number; // Context the tag instance belongs to.
 | 
			
		||||
        itemid: number; // Id of the record tagged.
 | 
			
		||||
        ordering: number; // Tag ordering.
 | 
			
		||||
        flag: number; // Whether the tag is flagged as inappropriate.
 | 
			
		||||
    }[];
 | 
			
		||||
    tags?: CoreTagItem[]; // Tags.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ import { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
 */
 | 
			
		||||
// DB table names.
 | 
			
		||||
export const CONTENT_TABLE_NAME = 'h5p_content'; // H5P content.
 | 
			
		||||
export const LIBRARIES_TABLE_NAME = 'h5p_libraries_2'; // Installed libraries.
 | 
			
		||||
export const LIBRARIES_TABLE_NAME = 'h5p_libraries'; // Installed libraries.
 | 
			
		||||
export const LIBRARY_DEPENDENCIES_TABLE_NAME = 'h5p_library_dependencies'; // Library dependencies.
 | 
			
		||||
export const CONTENTS_LIBRARIES_TABLE_NAME = 'h5p_contents_libraries'; // Which library is used in which content.
 | 
			
		||||
export const LIBRARIES_CACHEDASSETS_TABLE_NAME = 'h5p_libraries_cachedassets'; // H5P cached library assets.
 | 
			
		||||
@ -249,8 +249,8 @@ export const SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Move the records from the old table.
 | 
			
		||||
        await db.migrateTable('h5p_libraries', LIBRARIES_TABLE_NAME);
 | 
			
		||||
        // Add the metadata column to the table.
 | 
			
		||||
        await db.addColumn(LIBRARIES_TABLE_NAME, 'metadatasettings', 'TEXT');
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.refreshIcon = 'spinner';
 | 
			
		||||
        this.refreshIcon = CoreConstants.ICON_LOADING;
 | 
			
		||||
 | 
			
		||||
        if (!this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -428,14 +428,14 @@ export type CoreTagIndex = {
 | 
			
		||||
 * Structure of a tag item returned by WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreTagItem = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    rawname: string;
 | 
			
		||||
    isstandard: boolean;
 | 
			
		||||
    tagcollid: number;
 | 
			
		||||
    taginstanceid: number;
 | 
			
		||||
    taginstancecontextid: number;
 | 
			
		||||
    itemid: number;
 | 
			
		||||
    ordering: number;
 | 
			
		||||
    flag: number;
 | 
			
		||||
    id: number; // Tag id.
 | 
			
		||||
    name: string; // Tag name.
 | 
			
		||||
    rawname: string; // The raw, unnormalised name for the tag as entered by users.
 | 
			
		||||
    isstandard: boolean; // Whether this tag is standard.
 | 
			
		||||
    tagcollid: number; // Tag collection id.
 | 
			
		||||
    taginstanceid: number; // Tag instance id.
 | 
			
		||||
    taginstancecontextid: number; // Context the tag instance belongs to.
 | 
			
		||||
    itemid: number; // Id of the record tagged.
 | 
			
		||||
    ordering: number; // Tag ordering.
 | 
			
		||||
    flag: number; // Whether the tag is flagged as inappropriate.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -151,7 +151,7 @@ export class CoreAppProvider {
 | 
			
		||||
        if (schema.tables) {
 | 
			
		||||
            await this.db.createTablesFromSchema(schema.tables);
 | 
			
		||||
        }
 | 
			
		||||
        if (schema.migrate) {
 | 
			
		||||
        if (schema.migrate && oldVersion > 0) {
 | 
			
		||||
            await schema.migrate(this.db, oldVersion);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -144,7 +144,7 @@ export const SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    async migrate(db: SQLiteDB, oldVersion: number): Promise<void> {
 | 
			
		||||
        if (oldVersion && oldVersion < 2) {
 | 
			
		||||
        if (oldVersion < 2) {
 | 
			
		||||
            await db.migrateTable('wscache', CoreSite.WS_CACHE_TABLE, (record) => ({
 | 
			
		||||
                id: record.id,
 | 
			
		||||
                data: record.data,
 | 
			
		||||
 | 
			
		||||
@ -96,8 +96,17 @@ export class CoreNavigatorService {
 | 
			
		||||
     * @return Current main menu tab or null if the current route is not using the main menu.
 | 
			
		||||
     */
 | 
			
		||||
    getCurrentMainMenuTab(): string | null {
 | 
			
		||||
        const currentPath = this.getCurrentPath();
 | 
			
		||||
        const matches = /^\/main\/([^/]+).*$/.exec(currentPath);
 | 
			
		||||
        return this.getMainMenuTabFromPath(this.getCurrentPath());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get main menu tab from a path.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path The path to check.
 | 
			
		||||
     * @return Path's main menu tab or null if the path is not using the main menu.
 | 
			
		||||
     */
 | 
			
		||||
    getMainMenuTabFromPath(path: string): string | null {
 | 
			
		||||
        const matches = /^\/main\/([^/]+).*$/.exec(path);
 | 
			
		||||
 | 
			
		||||
        return matches?.[1] ?? null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1581,7 +1581,7 @@ export class CoreSitesProvider {
 | 
			
		||||
        if (schema.tables) {
 | 
			
		||||
            await db.createTablesFromSchema(schema.tables);
 | 
			
		||||
        }
 | 
			
		||||
        if (schema.migrate) {
 | 
			
		||||
        if (schema.migrate && oldVersion > 0) {
 | 
			
		||||
            await schema.migrate(db, oldVersion, site.id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user