MOBILE-2338 data: Entry page
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
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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 {}
|
|
@ -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…
Reference in New Issue