MOBILE-2338 data: Entry page
This commit is contained in:
		
							parent
							
								
									b7769ec2a4
								
							
						
					
					
						commit
						c626bee407
					
				@ -1,28 +0,0 @@
 | 
			
		||||
addon-mod-data-index {
 | 
			
		||||
    .addon-data-contents {
 | 
			
		||||
      overflow: visible;
 | 
			
		||||
      white-space: normal;
 | 
			
		||||
      word-break: break-word;
 | 
			
		||||
      padding: $content-padding;
 | 
			
		||||
      background-color: white;
 | 
			
		||||
      border-top-width: 1px;
 | 
			
		||||
      border-bottom-width: 1px;
 | 
			
		||||
      border-right-width: 0;
 | 
			
		||||
      border-left-width: 0;
 | 
			
		||||
      border-style: solid;
 | 
			
		||||
      border-color: $list-border-color;
 | 
			
		||||
 | 
			
		||||
      table, tbody {
 | 
			
		||||
        display: block;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      tr {
 | 
			
		||||
        @extend .row;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      td, th {
 | 
			
		||||
        @extend .col;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -75,11 +75,11 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
 | 
			
		||||
    protected fieldsArray: any;
 | 
			
		||||
 | 
			
		||||
    constructor(injector: Injector, private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider,
 | 
			
		||||
            private dataOffline: AddonModDataOfflineProvider, @Optional() @Optional() content: Content,
 | 
			
		||||
            private dataOffline: AddonModDataOfflineProvider, @Optional() content: Content,
 | 
			
		||||
            private dataSync: AddonModDataSyncProvider, private timeUtils: CoreTimeUtilsProvider,
 | 
			
		||||
            private groupsProvider: CoreGroupsProvider, private commentsProvider: CoreCommentsProvider,
 | 
			
		||||
            private modalCtrl: ModalController, private utils: CoreUtilsProvider, protected navCtrl: NavController) {
 | 
			
		||||
        super(injector);
 | 
			
		||||
        super(injector, content);
 | 
			
		||||
 | 
			
		||||
        // Refresh entries on change.
 | 
			
		||||
        this.entryChangedObserver = this.eventsProvider.on(AddonModDataProvider.ENTRY_CHANGED, (eventData) => {
 | 
			
		||||
@ -424,9 +424,9 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
 | 
			
		||||
    gotoEntry(entryId: number): void {
 | 
			
		||||
        const stateParams = {
 | 
			
		||||
            module: this.module,
 | 
			
		||||
            moduleid: this.module.id,
 | 
			
		||||
            courseid: this.courseId,
 | 
			
		||||
            entryid: entryId,
 | 
			
		||||
            moduleId: this.module.id,
 | 
			
		||||
            courseId: this.courseId,
 | 
			
		||||
            entryId: entryId,
 | 
			
		||||
            group: this.selectedGroup
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								src/addon/mod/data/data.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/addon/mod/data/data.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
.addon-data-contents {
 | 
			
		||||
  overflow: visible;
 | 
			
		||||
  white-space: normal;
 | 
			
		||||
  word-break: break-word;
 | 
			
		||||
  padding: $content-padding;
 | 
			
		||||
  background-color: white;
 | 
			
		||||
  border-top-width: 1px;
 | 
			
		||||
  border-bottom-width: 1px;
 | 
			
		||||
  border-right-width: 0;
 | 
			
		||||
  border-left-width: 0;
 | 
			
		||||
  border-style: solid;
 | 
			
		||||
  border-color: $list-border-color;
 | 
			
		||||
 | 
			
		||||
  table, tbody {
 | 
			
		||||
    display: block;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tr {
 | 
			
		||||
    @extend .row;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td, th {
 | 
			
		||||
    @extend .col;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								src/addon/mod/data/pages/entry/entry.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/addon/mod/data/pages/entry/entry.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-navbar>
 | 
			
		||||
        <ion-title><core-format-text [text]="title"></core-format-text></ion-title>
 | 
			
		||||
    </ion-navbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-refresher [enabled]="entryLoaded" (ionRefresh)="refreshDatabase($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
    <core-loading [hideUntil]="entryLoaded">
 | 
			
		||||
        <!-- Database entries found to be synchronized -->
 | 
			
		||||
        <div class="core-warning-card" icon-start *ngIf="hasOffline">
 | 
			
		||||
            <ion-icon name="warning"></ion-icon>
 | 
			
		||||
            {{ 'core.hasdatatosync' | translate: {$a: moduleName} }}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <ion-item text-wrap *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
 | 
			
		||||
            <ion-label id="addon-data-groupslabel" *ngIf="groupInfo.separateGroups">{{ 'core.groupsseparate' | translate }}</ion-label>
 | 
			
		||||
            <ion-label id="addon-data-groupslabel" *ngIf="groupInfo.visibleGroups">{{ 'core.groupsvisible' | translate }}</ion-label>
 | 
			
		||||
            <ion-select [(ngModel)]="selectedGroup" (ionChange)="setGroup(selectedGroup)" aria-labelledby="addon-data-groupslabel">
 | 
			
		||||
                <ion-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">{{groupOpt.name}}</ion-option>
 | 
			
		||||
            </ion-select>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <div class="addon-data-contents {{cssClass}}">
 | 
			
		||||
            <style *ngIf="cssTemplate">
 | 
			
		||||
                {{ cssTemplate }}
 | 
			
		||||
            </style>
 | 
			
		||||
 | 
			
		||||
            <core-compile-html [text]="entryRendered" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <ion-item *ngIf="data && entry">
 | 
			
		||||
            <core-comments contextLevel="module" [instanceId]="data.coursemodule" component="mod_data" [itemId]="entry.id" area="database_entry"></core-comments>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ion-grid *ngIf="previousId || nextId">
 | 
			
		||||
            <ion-row align-items-center>
 | 
			
		||||
                <ion-col *ngIf="previousId">
 | 
			
		||||
                    <button ion-button block outline icon-start (click)="gotoEntry(previousId)">
 | 
			
		||||
                        <ion-icon name="arrow-back"></ion-icon>
 | 
			
		||||
                        {{ 'core.previous' | translate }}
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ion-col>
 | 
			
		||||
                <ion-col *ngIf="nextId">
 | 
			
		||||
                    <button ion-button block icon-end (click)="gotoEntry(nextId)">
 | 
			
		||||
                        {{ 'core.next' | translate }}
 | 
			
		||||
                        <ion-icon name="arrow-forward"></ion-icon>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </ion-col>
 | 
			
		||||
            </ion-row>
 | 
			
		||||
        </ion-grid>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										39
									
								
								src/addon/mod/data/pages/entry/entry.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/addon/mod/data/pages/entry/entry.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
// (C) Copyright 2015 Martin Dougiamas
 | 
			
		||||
//
 | 
			
		||||
// 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 { IonicPageModule } from 'ionic-angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
 | 
			
		||||
import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module';
 | 
			
		||||
import { AddonModDataComponentsModule } from '../../components/components.module';
 | 
			
		||||
import { AddonModDataEntryPage } from './entry';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModDataEntryPage,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        AddonModDataComponentsModule,
 | 
			
		||||
        CoreCompileHtmlComponentModule,
 | 
			
		||||
        CoreCommentsComponentsModule,
 | 
			
		||||
        IonicPageModule.forChild(AddonModDataEntryPage),
 | 
			
		||||
        TranslateModule.forChild()
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModDataEntryPageModule {}
 | 
			
		||||
							
								
								
									
										307
									
								
								src/addon/mod/data/pages/entry/entry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								src/addon/mod/data/pages/entry/entry.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,307 @@
 | 
			
		||||
// (C) Copyright 2015 Martin Dougiamas
 | 
			
		||||
//
 | 
			
		||||
// 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, ViewChild } from '@angular/core';
 | 
			
		||||
import { Content, IonicPage, NavParams, NavController } from 'ionic-angular';
 | 
			
		||||
import { CoreUtilsProvider } from '@providers/utils/utils';
 | 
			
		||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
 | 
			
		||||
import { CoreSitesProvider } from '@providers/sites';
 | 
			
		||||
import { CoreGroupsProvider } from '@providers/groups';
 | 
			
		||||
import { CoreEventsProvider } from '@providers/events';
 | 
			
		||||
import { CoreCourseProvider } from '@core/course/providers/course';
 | 
			
		||||
import { AddonModDataProvider } from '../../providers/data';
 | 
			
		||||
import { AddonModDataHelperProvider } from '../../providers/helper';
 | 
			
		||||
import { AddonModDataOfflineProvider } from '../../providers/offline';
 | 
			
		||||
import { AddonModDataSyncProvider } from '../../providers/sync';
 | 
			
		||||
import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
 | 
			
		||||
import { AddonModDataComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the view entry page.
 | 
			
		||||
 */
 | 
			
		||||
@IonicPage({ segment: 'addon-mod-data-entry' })
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-data-entry',
 | 
			
		||||
    templateUrl: 'entry.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModDataEntryPage {
 | 
			
		||||
    @ViewChild(Content) content: Content;
 | 
			
		||||
 | 
			
		||||
    protected module: any;
 | 
			
		||||
    protected entryId: number;
 | 
			
		||||
    protected courseId: number;
 | 
			
		||||
    protected page: number;
 | 
			
		||||
    protected syncObserver: any; // It will observe the sync auto event.
 | 
			
		||||
    protected entryChangedObserver: any; // It will observe the changed entry event.
 | 
			
		||||
    protected fields = {};
 | 
			
		||||
 | 
			
		||||
    title = '';
 | 
			
		||||
    moduleName = 'data';
 | 
			
		||||
    component = AddonModDataProvider.COMPONENT;
 | 
			
		||||
    entryLoaded = false;
 | 
			
		||||
    selectedGroup = 0;
 | 
			
		||||
    entry: any;
 | 
			
		||||
    offlineActions = [];
 | 
			
		||||
    hasOffline = false;
 | 
			
		||||
    cssTemplate = '';
 | 
			
		||||
    previousId: number;
 | 
			
		||||
    nextId: number;
 | 
			
		||||
    access: any;
 | 
			
		||||
    data: any;
 | 
			
		||||
    groupInfo: any;
 | 
			
		||||
    showComments: any;
 | 
			
		||||
    entryRendered = '';
 | 
			
		||||
    siteId: string;
 | 
			
		||||
    cssClass = '';
 | 
			
		||||
    extraImports = [AddonModDataComponentsModule];
 | 
			
		||||
    jsData;
 | 
			
		||||
 | 
			
		||||
    constructor(params: NavParams, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider,
 | 
			
		||||
            protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate,
 | 
			
		||||
            protected courseProvider: CoreCourseProvider, protected dataProvider: AddonModDataProvider,
 | 
			
		||||
            protected dataOffline: AddonModDataOfflineProvider, protected dataHelper: AddonModDataHelperProvider,
 | 
			
		||||
            sitesProvider: CoreSitesProvider, protected navCtrl: NavController,
 | 
			
		||||
            protected eventsProvider: CoreEventsProvider) {
 | 
			
		||||
        this.module = params.get('module') || {};
 | 
			
		||||
        this.entryId = params.get('entryId') || null;
 | 
			
		||||
        this.courseId = params.get('courseId');
 | 
			
		||||
        this.selectedGroup = params.get('group') || 0;
 | 
			
		||||
        this.page = params.get('page') || null;
 | 
			
		||||
 | 
			
		||||
        this.siteId = sitesProvider.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        this.title = this.module.name;
 | 
			
		||||
        this.moduleName = this.courseProvider.translateModuleName('data');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * View loaded.
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidLoad(): void {
 | 
			
		||||
        this.fetchEntryData();
 | 
			
		||||
 | 
			
		||||
        // Refresh data if this discussion is synchronized automatically.
 | 
			
		||||
        this.syncObserver = this.eventsProvider.on(AddonModDataSyncProvider.AUTO_SYNCED, (data) => {
 | 
			
		||||
            if ((data.entryId == this.entryId || data.offlineEntryId == this.entryId) && this.data.id == data.dataId) {
 | 
			
		||||
                if (data.deleted) {
 | 
			
		||||
                    // If deleted, go back.
 | 
			
		||||
                    this.navCtrl.pop();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.entryId = data.entryid;
 | 
			
		||||
                    this.entryLoaded = false;
 | 
			
		||||
                    this.fetchEntryData(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, this.siteId);
 | 
			
		||||
 | 
			
		||||
        // Refresh entry on change.
 | 
			
		||||
        this.entryChangedObserver = this.eventsProvider.on(AddonModDataProvider.ENTRY_CHANGED, (data) => {
 | 
			
		||||
            if (data.entryId == this.entryId && data.id == data.dataId) {
 | 
			
		||||
                if (data.deleted) {
 | 
			
		||||
                    // If deleted, go back.
 | 
			
		||||
                    this.navCtrl.pop();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.entryLoaded = false;
 | 
			
		||||
                    this.fetchEntryData(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, this.siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch the entry data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {boolean}      refresh If refresh the current data or not.
 | 
			
		||||
     * @return {Promise<any>}         Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected fetchEntryData(refresh?: boolean): Promise<any> {
 | 
			
		||||
        return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => {
 | 
			
		||||
            this.title = data.name || this.title;
 | 
			
		||||
            this.data = data;
 | 
			
		||||
            this.cssClass = 'addon-data-entries-' + data.id;
 | 
			
		||||
 | 
			
		||||
            return this.setEntryIdFromPage(data.id, this.page, this.selectedGroup).then(() => {
 | 
			
		||||
                return this.dataProvider.getDatabaseAccessInformation(data.id);
 | 
			
		||||
            });
 | 
			
		||||
        }).then((accessData) => {
 | 
			
		||||
            this.access = accessData;
 | 
			
		||||
 | 
			
		||||
            return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule, accessData.canmanageentries)
 | 
			
		||||
                    .then((groupInfo) => {
 | 
			
		||||
                this.groupInfo = groupInfo;
 | 
			
		||||
 | 
			
		||||
                // Check selected group is accessible.
 | 
			
		||||
                if (groupInfo && groupInfo.groups && groupInfo.groups.length > 0) {
 | 
			
		||||
                    if (!groupInfo.groups.some((group) => this.selectedGroup == group.id)) {
 | 
			
		||||
                        this.selectedGroup = groupInfo.groups[0].id;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return this.dataOffline.getEntryActions(this.data.id, this.entryId);
 | 
			
		||||
            });
 | 
			
		||||
        }).then((actions) => {
 | 
			
		||||
            this.offlineActions = actions;
 | 
			
		||||
            this.hasOffline = !!actions.length;
 | 
			
		||||
 | 
			
		||||
            return this.dataProvider.getFields(this.data.id).then((fieldsData) => {
 | 
			
		||||
                this.fields = {};
 | 
			
		||||
                fieldsData.forEach((field) => {
 | 
			
		||||
                    this.fields[field.id] = field;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                return this.dataHelper.getEntry(this.data, this.entryId, this.offlineActions);
 | 
			
		||||
            });
 | 
			
		||||
        }).then((entry) => {
 | 
			
		||||
            entry = entry.entry;
 | 
			
		||||
            this.cssTemplate = this.dataHelper.prefixCSS(this.data.csstemplate, '.' + this.cssClass);
 | 
			
		||||
 | 
			
		||||
            // Index contents by fieldid.
 | 
			
		||||
            const contents = {};
 | 
			
		||||
            entry.contents.forEach((field) => {
 | 
			
		||||
                contents[field.fieldid] = field;
 | 
			
		||||
            });
 | 
			
		||||
            entry.contents = contents;
 | 
			
		||||
 | 
			
		||||
            const fieldsArray = this.utils.objectToArray(this.fields);
 | 
			
		||||
 | 
			
		||||
            return this.dataHelper.applyOfflineActions(entry, this.offlineActions, fieldsArray);
 | 
			
		||||
        }).then((entryData) => {
 | 
			
		||||
            this.entry = entryData;
 | 
			
		||||
 | 
			
		||||
            const actions = this.dataHelper.getActions(this.data, this.access, this.entry),
 | 
			
		||||
                fieldsArray = this.utils.objectToArray(this.fields);
 | 
			
		||||
 | 
			
		||||
            this.entryRendered = this.dataHelper.displayShowFields(this.data.singletemplate, fieldsArray,
 | 
			
		||||
                    this.entry, 'show', actions);
 | 
			
		||||
            this.showComments = actions.comments;
 | 
			
		||||
 | 
			
		||||
            const entries = {};
 | 
			
		||||
            entries[this.entryId] = this.entry;
 | 
			
		||||
 | 
			
		||||
            // Pass the input data to the component.
 | 
			
		||||
            this.jsData = {
 | 
			
		||||
                fields: this.fields,
 | 
			
		||||
                entries: entries,
 | 
			
		||||
                data: this.data
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return this.dataHelper.getPageInfoByEntry(this.data.id, this.entryId, this.selectedGroup).then((result) => {
 | 
			
		||||
                this.previousId = result.previousId;
 | 
			
		||||
                this.nextId = result.nextId;
 | 
			
		||||
            });
 | 
			
		||||
        }).catch((message) => {
 | 
			
		||||
            if (!refresh) {
 | 
			
		||||
                // Some call failed, retry without using cache since it might be a new activity.
 | 
			
		||||
                return this.refreshAllData();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
 | 
			
		||||
 | 
			
		||||
            return Promise.reject(null);
 | 
			
		||||
        }).finally(() => {
 | 
			
		||||
            this.content && this.content.scrollToTop();
 | 
			
		||||
            this.entryLoaded = true;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to selected entry without changing state.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}       entry Entry Id where to go.
 | 
			
		||||
     * @return {Promise<any>}       Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    gotoEntry(entry: number): Promise<any> {
 | 
			
		||||
        this.entryId = entry;
 | 
			
		||||
        this.page = null;
 | 
			
		||||
        this.entryLoaded = false;
 | 
			
		||||
 | 
			
		||||
        return this.fetchEntryData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh all the data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return {Promise<any>} Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected refreshAllData(): Promise<any> {
 | 
			
		||||
        const promises = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(this.dataProvider.invalidateDatabaseData(this.courseId));
 | 
			
		||||
        if (this.data) {
 | 
			
		||||
            promises.push(this.dataProvider.invalidateEntryData(this.data.id, this.entryId));
 | 
			
		||||
            promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.data.coursemodule));
 | 
			
		||||
            promises.push(this.dataProvider.invalidateEntriesData(this.data.id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Promise.all(promises).finally(() => {
 | 
			
		||||
            return this.fetchEntryData(true);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {any} [refresher] Refresher.
 | 
			
		||||
     * @return {Promise<any>} Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    refreshDatabase(refresher?: any): Promise<any> {
 | 
			
		||||
        if (this.entryLoaded) {
 | 
			
		||||
            return this.refreshAllData().finally(() => {
 | 
			
		||||
                refresher && refresher.complete();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set group to see the database.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}       groupId Group identifier to set.
 | 
			
		||||
     * @return {Promise<any>}         Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    setGroup(groupId: number): Promise<any> {
 | 
			
		||||
        this.selectedGroup = groupId;
 | 
			
		||||
        this.entryLoaded = false;
 | 
			
		||||
 | 
			
		||||
        return this.setEntryIdFromPage(this.data.id, 0, this.selectedGroup).then(() => {
 | 
			
		||||
            return this.fetchEntryData();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to translate page number to entry identifier.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}       dataId       Data Id.
 | 
			
		||||
     * @param  {number}       [pageNumber] Page number where to go
 | 
			
		||||
     * @param  {number}       group        Group Id to get the entry.
 | 
			
		||||
     * @return {Promise<any>}              Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected setEntryIdFromPage(dataId: number, pageNumber?: number, group?: number): Promise<any> {
 | 
			
		||||
        if (typeof pageNumber == 'number') {
 | 
			
		||||
            return this.dataHelper.getPageInfoByPage(dataId, pageNumber, group).then((result) => {
 | 
			
		||||
                this.entryId = result.entryId;
 | 
			
		||||
                this.page = null;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Promise.resolve();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.syncObserver && this.syncObserver.off();
 | 
			
		||||
        this.entryChangedObserver && this.entryChangedObserver.off();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -218,6 +218,59 @@ export class AddonModDataProvider {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs the whole fetch of the entries in the database.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}    dataId          Data ID.
 | 
			
		||||
     * @param  {number}    [groupId]       Group ID.
 | 
			
		||||
     * @param  {string}    [sort]          Sort the records by this field id. See AddonModDataProvider#getEntries for more info.
 | 
			
		||||
     * @param  {string}    [order]         The direction of the sorting.  See AddonModDataProvider#getEntries for more info.
 | 
			
		||||
     * @param  {number}    [perPage]       Records per page to fetch. It has to match with the prefetch.
 | 
			
		||||
     *                                     Default on AddonModDataProvider.PER_PAGE.
 | 
			
		||||
     * @param  {boolean}   [forceCache]    True to always get the value from cache, false otherwise. Default false.
 | 
			
		||||
     * @param  {boolean}   [ignoreCache]   True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @param  {string}    [siteId]        Site ID. If not defined, current site.
 | 
			
		||||
     * @return {Promise<any>}              Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    fetchAllEntries(dataId: number, groupId: number = 0, sort: string = '0', order: string = 'DESC',
 | 
			
		||||
            perPage: number = AddonModDataProvider.PER_PAGE, forceCache: boolean = false, ignoreCache: boolean = false,
 | 
			
		||||
            siteId?: string): Promise<any> {
 | 
			
		||||
        siteId = siteId || this.sitesProvider.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        return this.fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, [], 0, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Recursive call on fetch all entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}    dataId          Data ID.
 | 
			
		||||
     * @param  {number}    groupId         Group ID.
 | 
			
		||||
     * @param  {string}    sort            Sort the records by this field id. See AddonModDataProvider#getEntries for more info.
 | 
			
		||||
     * @param  {string}    order           The direction of the sorting.  See AddonModDataProvider#getEntries for more info.
 | 
			
		||||
     * @param  {number}    perPage         Records per page to fetch. It has to match with the prefetch.
 | 
			
		||||
     * @param  {boolean}   forceCache      True to always get the value from cache, false otherwise. Default false.
 | 
			
		||||
     * @param  {boolean}   ignoreCache     True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @param  {any}       entries         Entries already fetch (just to concatenate them).
 | 
			
		||||
     * @param  {number}    page            Page of records to return.
 | 
			
		||||
     * @param  {string}    siteId          Site ID.
 | 
			
		||||
     * @return {Promise<any>}              Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected fetchEntriesRecursive(dataId: number, groupId: number, sort: string, order: string, perPage: number,
 | 
			
		||||
            forceCache: boolean, ignoreCache: boolean, entries: any, page: number, siteId: string): Promise<any> {
 | 
			
		||||
        return this.getEntries(dataId, groupId, sort, order, page, perPage, forceCache, ignoreCache, siteId)
 | 
			
		||||
                .then((result) => {
 | 
			
		||||
            entries = entries.concat(result.entries);
 | 
			
		||||
 | 
			
		||||
            const canLoadMore = perPage > 0 && ((page + 1) * perPage) < result.totalcount;
 | 
			
		||||
            if (canLoadMore) {
 | 
			
		||||
                return this.fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, entries, page + 1,
 | 
			
		||||
                    siteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return entries;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for data data WS calls.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,6 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreSitesProvider } from '@providers/sites';
 | 
			
		||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
 | 
			
		||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { AddonModDataFieldsDelegate } from './fields-delegate';
 | 
			
		||||
import { AddonModDataOfflineProvider } from './offline';
 | 
			
		||||
@ -27,7 +26,7 @@ import { AddonModDataProvider } from './data';
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AddonModDataHelperProvider {
 | 
			
		||||
 | 
			
		||||
    constructor(private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
 | 
			
		||||
    constructor(private sitesProvider: CoreSitesProvider, protected dataProvider: AddonModDataProvider,
 | 
			
		||||
        private translate: TranslateService, private fieldsDelegate: AddonModDataFieldsDelegate,
 | 
			
		||||
        private dataOffline: AddonModDataOfflineProvider, private fileUploaderProvider: CoreFileUploaderProvider) { }
 | 
			
		||||
 | 
			
		||||
@ -175,6 +174,130 @@ export class AddonModDataHelperProvider {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch all entries and return it's Id
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}    dataId          Data ID.
 | 
			
		||||
     * @param  {number}    groupId         Group ID.
 | 
			
		||||
     * @param  {boolean}   [forceCache]    True to always get the value from cache, false otherwise. Default false.
 | 
			
		||||
     * @param  {boolean}   [ignoreCache]   True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @param  {string}    [siteId]        Site ID. Current if not defined.
 | 
			
		||||
     * @return {Promise<any>}              Resolved with an array of entry ID.
 | 
			
		||||
     */
 | 
			
		||||
    getAllEntriesIds(dataId: number, groupId: number, forceCache: boolean = false, ignoreCache: boolean = false, siteId?: string):
 | 
			
		||||
            Promise<any> {
 | 
			
		||||
        return this.dataProvider.fetchAllEntries(dataId, groupId, undefined, undefined, undefined, forceCache, ignoreCache, siteId)
 | 
			
		||||
                .then((entries) => {
 | 
			
		||||
            return entries.map((entry) => {
 | 
			
		||||
                return entry.id;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an online or offline entry.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {any} data             Database.
 | 
			
		||||
     * @param  {number} entryId       Entry ID.
 | 
			
		||||
     * @param  {any} [offlineActions] Offline data with the actions done. Required for offline entries.
 | 
			
		||||
     * @param  {string} [siteId]      Site ID. If not defined, current site.
 | 
			
		||||
     * @return {Promise<any>}         Promise resolved with the entry.
 | 
			
		||||
     */
 | 
			
		||||
    getEntry(data: any, entryId: number, offlineActions?: any, siteId?: string): Promise<any> {
 | 
			
		||||
        if (entryId > 0) {
 | 
			
		||||
            // It's an online entry, get it from WS.
 | 
			
		||||
            return this.dataProvider.getEntry(data.id, entryId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // It's an offline entry, search it in the offline actions.
 | 
			
		||||
        return this.sitesProvider.getSite(siteId).then((site) => {
 | 
			
		||||
            const offlineEntry = offlineActions.find((offlineAction) => {
 | 
			
		||||
                return offlineAction.action == 'add';
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (offlineEntry) {
 | 
			
		||||
                const siteInfo = site.getInfo();
 | 
			
		||||
 | 
			
		||||
                return {entry: {
 | 
			
		||||
                        id: offlineEntry.entryid,
 | 
			
		||||
                        canmanageentry: true,
 | 
			
		||||
                        approved: !data.approval || data.manageapproved,
 | 
			
		||||
                        dataid: offlineEntry.dataid,
 | 
			
		||||
                        groupid: offlineEntry.groupid,
 | 
			
		||||
                        timecreated: -offlineEntry.entryid,
 | 
			
		||||
                        timemodified: -offlineEntry.entryid,
 | 
			
		||||
                        userid: siteInfo.userid,
 | 
			
		||||
                        fullname: siteInfo.fullname,
 | 
			
		||||
                        contents: {}
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get page info related to an entry.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}    dataId          Data ID.
 | 
			
		||||
     * @param  {number}    entryId         Entry ID.
 | 
			
		||||
     * @param  {number}    groupId         Group ID.
 | 
			
		||||
     * @param  {boolean}   [forceCache]    True to always get the value from cache, false otherwise. Default false.
 | 
			
		||||
     * @param  {boolean}   [ignoreCache]   True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @param  {string}    [siteId]        Site ID. Current if not defined.
 | 
			
		||||
     * @return {Promise<any>}              Containing page number, if has next and have following page.
 | 
			
		||||
     */
 | 
			
		||||
    getPageInfoByEntry(dataId: number, entryId: number, groupId: number, forceCache: boolean = false,
 | 
			
		||||
            ignoreCache: boolean = false, siteId?: string): Promise<any> {
 | 
			
		||||
        return this.getAllEntriesIds(dataId, groupId, forceCache, ignoreCache, siteId).then((entries) => {
 | 
			
		||||
            const index = entries.findIndex((entry) => {
 | 
			
		||||
                return entry == entryId;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (index >= 0) {
 | 
			
		||||
                return {
 | 
			
		||||
                    previousId: entries[index - 1] || false,
 | 
			
		||||
                    nextId: entries[index + 1] || false,
 | 
			
		||||
                    entryId: entryId,
 | 
			
		||||
                    page: index + 1, // Parsed to natural language.
 | 
			
		||||
                    numEntries: entries.length
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get page info related to an entry by page number.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {number}    dataId          Data ID.
 | 
			
		||||
     * @param  {number}    page            Page number.
 | 
			
		||||
     * @param  {number}    groupId         Group ID.
 | 
			
		||||
     * @param  {boolean}   [forceCache]    True to always get the value from cache, false otherwise. Default false.
 | 
			
		||||
     * @param  {boolean}   [ignoreCache]   True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @param  {string}    [siteId]        Site ID. Current if not defined.
 | 
			
		||||
     * @return {Promise<any>}              Containing page number, if has next and have following page.
 | 
			
		||||
     */
 | 
			
		||||
    getPageInfoByPage(dataId: number, page: number, groupId: number, forceCache: boolean = false,
 | 
			
		||||
            ignoreCache: boolean = false, siteId?: string): Promise<any> {
 | 
			
		||||
        return this.getAllEntriesIds(dataId, groupId, forceCache, ignoreCache, siteId).then((entries) => {
 | 
			
		||||
            const index = page - 1,
 | 
			
		||||
                entryId = entries[index];
 | 
			
		||||
 | 
			
		||||
            if (entryId) {
 | 
			
		||||
                return {
 | 
			
		||||
                    previousId: entries[index - 1] || null,
 | 
			
		||||
                    nextId: entries[index + 1] || null,
 | 
			
		||||
                    entryId: entryId,
 | 
			
		||||
                    page: page, // Parsed to natural language.
 | 
			
		||||
                    numEntries: entries.length
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of stored attachment files for a new entry. See $mmaModDataHelper#storeFiles.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user