commit
980af8d852
|
@ -12,10 +12,13 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, Input, OnInit, Injector } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { AddonModDataProvider } from '../../providers/data';
|
||||
import { AddonModDataHelperProvider } from '../../providers/helper';
|
||||
import { AddonModDataOfflineProvider } from '../../providers/offline';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
|
||||
/**
|
||||
|
@ -30,6 +33,8 @@ export class AddonModDataActionComponent implements OnInit {
|
|||
@Input() action: string; // The field to render.
|
||||
@Input() entry?: any; // The value of the field.
|
||||
@Input() database: any; // Database object.
|
||||
@Input() module: any; // Module object.
|
||||
@Input() group: number; // Module object.
|
||||
@Input() offset?: number; // Offset of the entry.
|
||||
|
||||
siteId: string;
|
||||
|
@ -39,11 +44,72 @@ export class AddonModDataActionComponent implements OnInit {
|
|||
|
||||
constructor(protected injector: Injector, protected dataProvider: AddonModDataProvider,
|
||||
protected dataOffline: AddonModDataOfflineProvider, protected eventsProvider: CoreEventsProvider,
|
||||
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider) {
|
||||
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider, private navCtrl: NavController,
|
||||
protected linkHelper: CoreContentLinksHelperProvider, private dataHelper: AddonModDataHelperProvider) {
|
||||
this.rootUrl = sitesProvider.getCurrentSite().getURL();
|
||||
this.siteId = sitesProvider.getCurrentSiteId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (this.action == 'userpicture') {
|
||||
this.userProvider.getProfile(this.entry.userid, this.database.courseid).then((profile) => {
|
||||
this.userPicture = profile.profileimageurl;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve the entry.
|
||||
*/
|
||||
approveEntry(): void {
|
||||
this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, true, this.database.courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show confirmation modal for deleting the entry.
|
||||
*/
|
||||
deleteEntry(): void {
|
||||
this.dataHelper.showDeleteEntryModal(this.database.id, this.entry.id, this.database.courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disapprove the entry.
|
||||
*/
|
||||
disapproveEntry(): void {
|
||||
this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, false, this.database.courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the edit page of the entry.
|
||||
*/
|
||||
editEntry(): void {
|
||||
const pageParams = {
|
||||
courseId: this.database.course,
|
||||
module: this.module,
|
||||
entryId: this.entry.id
|
||||
};
|
||||
|
||||
this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEditPage', pageParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the view page of the entry.
|
||||
*/
|
||||
viewEntry(): void {
|
||||
const pageParams: any = {
|
||||
courseId: this.database.course,
|
||||
module: this.module,
|
||||
entryId: this.entry.id,
|
||||
group: this.group,
|
||||
offset: this.offset
|
||||
};
|
||||
|
||||
this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEntryPage', pageParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo delete action.
|
||||
*
|
||||
|
@ -60,37 +126,4 @@ export class AddonModDataActionComponent implements OnInit {
|
|||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, this.siteId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
switch (this.action) {
|
||||
case 'more':
|
||||
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&rid=' + this.entry.id;
|
||||
if (typeof this.offset == 'number') {
|
||||
this.url += '&mode=single&page=' + this.offset;
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
this.url = this.rootUrl + '/mod/data/edit.php?d= ' + this.entry.dataid + '&rid=' + this.entry.id;
|
||||
break;
|
||||
case 'delete':
|
||||
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&delete=' + this.entry.id;
|
||||
break;
|
||||
case 'approve':
|
||||
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&approve=' + this.entry.id;
|
||||
break;
|
||||
case 'disapprove':
|
||||
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&disapprove=' + this.entry.id;
|
||||
break;
|
||||
case 'userpicture':
|
||||
this.userProvider.getProfile(this.entry.userid, this.database.courseid).then((profile) => {
|
||||
this.userPicture = profile.profileimageurl;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<a *ngIf="action == 'more'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.more' | translate">
|
||||
<a *ngIf="action == 'more'" ion-button icon-only clear (click)="viewEntry()" [title]="'addon.mod_data.more' | translate">
|
||||
<ion-icon name="search"></ion-icon>
|
||||
</a>
|
||||
|
||||
<a *ngIf="action == 'edit'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'core.edit' | translate">
|
||||
<a *ngIf="action == 'edit'" ion-button icon-only clear (click)="editEntry()" [title]="'core.edit' | translate">
|
||||
<ion-icon name="cog"></ion-icon>
|
||||
</a>
|
||||
|
||||
<a *ngIf="action == 'delete' && !entry.deleted" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'core.delete' | translate">
|
||||
<a *ngIf="action == 'delete' && !entry.deleted" ion-button icon-only clear (click)="deleteEntry()" [title]="'core.delete' | translate">
|
||||
<ion-icon name="trash"></ion-icon>
|
||||
</a>
|
||||
|
||||
|
@ -14,11 +14,11 @@
|
|||
<ion-icon name="undo"></ion-icon>
|
||||
</a>
|
||||
|
||||
<a *ngIf="action == 'approve'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.approve' | translate">
|
||||
<a *ngIf="action == 'approve'" ion-button icon-only clear (click)="approveEntry()" [title]="'addon.mod_data.approve' | translate">
|
||||
<ion-icon name="thumbs-up"></ion-icon>
|
||||
</a>
|
||||
|
||||
<a *ngIf="action == 'disapprove'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.disapprove' | translate">
|
||||
<a *ngIf="action == 'disapprove'" ion-button icon-only clear (click)="disapproveEntry()" [title]="'addon.mod_data.disapprove' | translate">
|
||||
<ion-icon name="thumbs-down"></ion-icon>
|
||||
</a>
|
||||
|
||||
|
|
|
@ -20,11 +20,9 @@ import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
|||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||
import { CoreRatingProvider } from '@core/rating/providers/rating';
|
||||
import { CoreRatingOfflineProvider } from '@core/rating/providers/offline';
|
||||
import { CoreRatingSyncProvider } from '@core/rating/providers/sync';
|
||||
import { AddonModDataProvider } from '../../providers/data';
|
||||
import { AddonModDataHelperProvider } from '../../providers/helper';
|
||||
import { AddonModDataOfflineProvider } from '../../providers/offline';
|
||||
import { AddonModDataSyncProvider } from '../../providers/sync';
|
||||
import { AddonModDataComponentsModule } from '../components.module';
|
||||
import { AddonModDataPrefetchHandler } from '../../providers/prefetch-handler';
|
||||
|
@ -65,8 +63,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
advanced: []
|
||||
};
|
||||
hasNextPage = false;
|
||||
offlineActions: any;
|
||||
offlineEntries: any;
|
||||
entriesRendered = '';
|
||||
extraImports = [AddonModDataComponentsModule];
|
||||
jsData;
|
||||
|
@ -81,12 +77,19 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
protected ratingOfflineObserver: any;
|
||||
protected ratingSyncObserver: any;
|
||||
|
||||
constructor(injector: Injector, private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider,
|
||||
private dataOffline: AddonModDataOfflineProvider, @Optional() content: Content,
|
||||
private prefetchHandler: AddonModDataPrefetchHandler, private timeUtils: CoreTimeUtilsProvider,
|
||||
private groupsProvider: CoreGroupsProvider, private commentsProvider: CoreCommentsProvider,
|
||||
private modalCtrl: ModalController, private utils: CoreUtilsProvider, protected navCtrl: NavController,
|
||||
private ratingOffline: CoreRatingOfflineProvider) {
|
||||
constructor(
|
||||
injector: Injector,
|
||||
@Optional() content: Content,
|
||||
private dataProvider: AddonModDataProvider,
|
||||
private dataHelper: AddonModDataHelperProvider,
|
||||
private prefetchHandler: AddonModDataPrefetchHandler,
|
||||
private timeUtils: CoreTimeUtilsProvider,
|
||||
private groupsProvider: CoreGroupsProvider,
|
||||
private commentsProvider: CoreCommentsProvider,
|
||||
private modalCtrl: ModalController,
|
||||
private utils: CoreUtilsProvider,
|
||||
protected navCtrl: NavController) {
|
||||
|
||||
super(injector, content);
|
||||
|
||||
// Refresh entries on change.
|
||||
|
@ -233,8 +236,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
this.selectedGroup = groupInfo.groups[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
return this.fetchOfflineEntries();
|
||||
});
|
||||
}).then(() => {
|
||||
return this.dataProvider.getFields(this.data.id).then((fields) => {
|
||||
|
@ -270,21 +271,19 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
// Update values for current group.
|
||||
this.access.canaddentry = accessData.canaddentry;
|
||||
|
||||
if (this.search.searching) {
|
||||
const text = this.search.searchingAdvanced ? undefined : this.search.text,
|
||||
advanced = this.search.searchingAdvanced ? this.search.advanced : undefined;
|
||||
const search = this.search.searching && !this.search.searchingAdvanced ? this.search.text : undefined;
|
||||
const advSearch = this.search.searching && this.search.searchingAdvanced ? this.search.advanced : undefined;
|
||||
|
||||
return this.dataProvider.searchEntries(this.data.id, this.selectedGroup, text, advanced, this.search.sortBy,
|
||||
this.search.sortDirection, this.search.page);
|
||||
} else {
|
||||
return this.dataProvider.getEntries(this.data.id, this.selectedGroup, this.search.sortBy, this.search.sortDirection,
|
||||
this.search.page);
|
||||
}
|
||||
return this.dataHelper.fetchEntries(this.data, this.fieldsArray, this.selectedGroup, search, advSearch,
|
||||
this.search.sortBy, this.search.sortDirection, this.search.page);
|
||||
}).then((entries) => {
|
||||
const numEntries = (entries && entries.entries && entries.entries.length) || 0;
|
||||
this.isEmpty = !numEntries && !Object.keys(this.offlineActions).length && !Object.keys(this.offlineEntries).length;
|
||||
const numEntries = entries.entries.length;
|
||||
const numOfflineEntries = entries.offlineEntries.length;
|
||||
this.isEmpty = !numEntries && !entries.offlineEntries.length;
|
||||
this.hasNextPage = numEntries >= AddonModDataProvider.PER_PAGE && ((this.search.page + 1) *
|
||||
AddonModDataProvider.PER_PAGE) < entries.totalcount;
|
||||
this.hasOffline = entries.hasOfflineActions;
|
||||
this.hasOfflineRatings = entries.hasOfflineRatings;
|
||||
this.entriesRendered = '';
|
||||
|
||||
if (typeof entries.maxcount != 'undefined') {
|
||||
|
@ -298,79 +297,40 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
}
|
||||
|
||||
if (!this.isEmpty) {
|
||||
const siteInfo = this.sitesProvider.getCurrentSite().getInfo(),
|
||||
promises = [];
|
||||
this.entries = entries.offlineEntries.concat(entries.entries);
|
||||
|
||||
this.utils.objectToArray(this.offlineEntries).forEach((offlineActions) => {
|
||||
const offlineEntry = offlineActions.find((offlineEntry) => offlineEntry.action == 'add');
|
||||
let entriesHTML = this.data.listtemplateheader || '';
|
||||
|
||||
if (offlineEntry) {
|
||||
const entry = {
|
||||
id: offlineEntry.entryid,
|
||||
canmanageentry: true,
|
||||
approved: !this.data.approval || this.data.manageapproved,
|
||||
dataid: offlineEntry.dataid,
|
||||
groupid: offlineEntry.groupid,
|
||||
timecreated: -offlineEntry.entryid,
|
||||
timemodified: -offlineEntry.entryid,
|
||||
userid: siteInfo.userid,
|
||||
fullname: siteInfo.fullname,
|
||||
contents: {}
|
||||
};
|
||||
// Get first entry from the whole list.
|
||||
if (!this.search.searching || !this.firstEntry) {
|
||||
this.firstEntry = this.entries[0].id;
|
||||
}
|
||||
|
||||
if (offlineActions.length > 0) {
|
||||
promises.push(this.dataHelper.applyOfflineActions(entry, offlineActions, this.fieldsArray));
|
||||
} else {
|
||||
promises.push(Promise.resolve(entry));
|
||||
}
|
||||
}
|
||||
const template = this.data.listtemplate || this.dataHelper.getDefaultTemplate('list', this.fieldsArray);
|
||||
|
||||
const entriesById = {};
|
||||
this.entries.forEach((entry, index) => {
|
||||
entriesById[entry.id] = entry;
|
||||
|
||||
const actions = this.dataHelper.getActions(this.data, this.access, entry);
|
||||
const offset = this.search.searching ? undefined :
|
||||
this.search.page * AddonModDataProvider.PER_PAGE + index - numOfflineEntries;
|
||||
|
||||
entriesHTML += this.dataHelper.displayShowFields(template, this.fieldsArray, entry, offset, 'list', actions);
|
||||
});
|
||||
entriesHTML += this.data.listtemplatefooter || '';
|
||||
|
||||
entries.entries.forEach((entry) => {
|
||||
// Index contents by fieldid.
|
||||
entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid');
|
||||
this.entriesRendered = entriesHTML;
|
||||
|
||||
if (typeof this.offlineActions[entry.id] != 'undefined') {
|
||||
promises.push(this.dataHelper.applyOfflineActions(entry, this.offlineActions[entry.id], this.fieldsArray));
|
||||
} else {
|
||||
promises.push(Promise.resolve(entry));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises).then((entries) => {
|
||||
this.entries = entries;
|
||||
|
||||
let entriesHTML = this.data.listtemplateheader || '';
|
||||
|
||||
// Get first entry from the whole list.
|
||||
if (entries && entries[0] && (!this.search.searching || !this.firstEntry)) {
|
||||
this.firstEntry = entries[0].id;
|
||||
}
|
||||
|
||||
const template = this.data.listtemplate || this.dataHelper.getDefaultTemplate('list', this.fieldsArray);
|
||||
|
||||
const entriesById = {};
|
||||
entries.forEach((entry, index) => {
|
||||
entriesById[entry.id] = entry;
|
||||
|
||||
const actions = this.dataHelper.getActions(this.data, this.access, entry);
|
||||
const offset = this.search.page * AddonModDataProvider.PER_PAGE + index;
|
||||
|
||||
entriesHTML += this.dataHelper.displayShowFields(template, this.fieldsArray, entry, offset, 'list',
|
||||
actions);
|
||||
});
|
||||
entriesHTML += this.data.listtemplatefooter || '';
|
||||
|
||||
this.entriesRendered = entriesHTML;
|
||||
|
||||
// Pass the input data to the component.
|
||||
this.jsData = {
|
||||
fields: this.fields,
|
||||
entries: entriesById,
|
||||
data: this.data,
|
||||
gotoEntry: this.gotoEntry.bind(this)
|
||||
};
|
||||
});
|
||||
// Pass the input data to the component.
|
||||
this.jsData = {
|
||||
fields: this.fields,
|
||||
entries: entriesById,
|
||||
data: this.data,
|
||||
module: this.module,
|
||||
group: this.selectedGroup,
|
||||
gotoEntry: this.gotoEntry.bind(this)
|
||||
};
|
||||
} else if (!this.search.searching) {
|
||||
// Empty and no searching.
|
||||
this.canSearch = false;
|
||||
|
@ -435,6 +395,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
*/
|
||||
setGroup(groupId: number): Promise<any> {
|
||||
this.selectedGroup = groupId;
|
||||
this.search.page = 0;
|
||||
|
||||
return this.fetchEntriesData().catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
||||
|
@ -479,42 +440,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
this.navCtrl.push('AddonModDataEntryPage', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch offline entries.
|
||||
*
|
||||
* @return {Promise<any>} Resolved then done.
|
||||
*/
|
||||
protected fetchOfflineEntries(): Promise<any> {
|
||||
// Check if there are entries stored in offline.
|
||||
return this.dataOffline.getDatabaseEntries(this.data.id).then((offlineEntries) => {
|
||||
this.hasOffline = !!offlineEntries.length;
|
||||
|
||||
this.offlineActions = {};
|
||||
this.offlineEntries = {};
|
||||
|
||||
// Only show offline entries on first page.
|
||||
if (this.search.page == 0 && this.hasOffline) {
|
||||
offlineEntries.forEach((entry) => {
|
||||
if (entry.entryid > 0) {
|
||||
if (typeof this.offlineActions[entry.entryid] == 'undefined') {
|
||||
this.offlineActions[entry.entryid] = [];
|
||||
}
|
||||
this.offlineActions[entry.entryid].push(entry);
|
||||
} else {
|
||||
if (typeof this.offlineActions[entry.entryid] == 'undefined') {
|
||||
this.offlineEntries[entry.entryid] = [];
|
||||
}
|
||||
this.offlineEntries[entry.entryid].push(entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
return this.ratingOffline.hasRatings('mod_data', 'entry', 'module', this.data.coursemodule).then((hasRatings) => {
|
||||
this.hasOfflineRatings = hasRatings;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the sync of the activity.
|
||||
*
|
||||
|
|
|
@ -45,7 +45,6 @@ export class AddonModDataEditPage {
|
|||
protected data: any;
|
||||
protected entryId: number;
|
||||
protected entry: any;
|
||||
protected offlineActions = [];
|
||||
protected fields = {};
|
||||
protected fieldsArray = [];
|
||||
protected siteId: string;
|
||||
|
@ -145,31 +144,14 @@ export class AddonModDataEditPage {
|
|||
});
|
||||
}
|
||||
}).then(() => {
|
||||
return this.dataOffline.getEntryActions(this.data.id, this.entryId);
|
||||
}).then((actions) => {
|
||||
this.offlineActions = actions;
|
||||
|
||||
return this.dataProvider.getFields(this.data.id);
|
||||
}).then((fieldsData) => {
|
||||
this.fieldsArray = fieldsData;
|
||||
this.fields = this.utils.arrayToObject(fieldsData, 'id');
|
||||
|
||||
return this.dataHelper.getEntry(this.data, this.entryId, this.offlineActions);
|
||||
return this.dataHelper.fetchEntry(this.data, fieldsData, this.entryId);
|
||||
}).then((entry) => {
|
||||
if (entry) {
|
||||
entry = entry.entry;
|
||||
|
||||
// Index contents by fieldid.
|
||||
entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid');
|
||||
} else {
|
||||
entry = {
|
||||
contents: {}
|
||||
};
|
||||
}
|
||||
|
||||
return this.dataHelper.applyOfflineActions(entry, this.offlineActions, this.fieldsArray);
|
||||
}).then((entryData) => {
|
||||
this.entry = entryData;
|
||||
this.entry = entry.entry;
|
||||
|
||||
this.editFormRender = this.displayEditFields();
|
||||
}).catch((message) => {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-refresher>
|
||||
<core-loading [hideUntil]="entryLoaded && (isPullingToRefresh || !renderingEntry && !loadingRating && !loadingComments)">
|
||||
<!-- Database entries found to be synchronized -->
|
||||
<div class="core-warning-card" icon-start *ngIf="hasOffline">
|
||||
<div class="core-warning-card" icon-start *ngIf="entry && entry.hasOffline">
|
||||
<ion-icon name="warning"></ion-icon>
|
||||
{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<core-rating-rate *ngIf="data && entry && ratingInfo && (!data.approval || entry.approved)" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="data.coursemodule" [itemId]="entry.id" [itemSetId]="0" [courseId]="courseId" [aggregateMethod]="data.assessed" [scaleId]="data.scale" [userId]="entry.userid" (onLoading)="setLoadingRating($event)" (onUpdate)="ratingUpdated()"></core-rating-rate>
|
||||
<core-rating-aggregate *ngIf="data && entry && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="data.coursemodule" [itemId]="entry.id" [courseId]="courseId" [aggregateMethod]="data.assessed" [scaleId]="data.scale"></core-rating-aggregate>
|
||||
|
||||
<ion-item *ngIf="data && entry">
|
||||
<ion-item *ngIf="data && entry && entry.id > 0">
|
||||
<core-comments contextLevel="module" [instanceId]="data.coursemodule" component="mod_data" [itemId]="entry.id" area="database_entry" [displaySpinner]="false" (onLoading)="setLoadingComments($event)"></core-comments>
|
||||
</ion-item>
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
|||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||
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';
|
||||
|
@ -46,6 +45,7 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
protected syncObserver: any; // It will observe the sync auto event.
|
||||
protected entryChangedObserver: any; // It will observe the changed entry event.
|
||||
protected fields = {};
|
||||
protected fieldsArray = [];
|
||||
|
||||
title = '';
|
||||
moduleName = 'data';
|
||||
|
@ -56,8 +56,6 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
loadingRating = false;
|
||||
selectedGroup = 0;
|
||||
entry: any;
|
||||
offlineActions = [];
|
||||
hasOffline = false;
|
||||
previousOffset: number;
|
||||
nextOffset: number;
|
||||
access: any;
|
||||
|
@ -74,7 +72,7 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
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,
|
||||
protected dataHelper: AddonModDataHelperProvider,
|
||||
sitesProvider: CoreSitesProvider, protected navCtrl: NavController, protected eventsProvider: CoreEventsProvider,
|
||||
private cdr: ChangeDetectorRef) {
|
||||
this.module = params.get('module') || {};
|
||||
|
@ -131,16 +129,19 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected fetchEntryData(refresh?: boolean, isPtr?: boolean): Promise<any> {
|
||||
let fieldsArray;
|
||||
|
||||
this.isPullingToRefresh = isPtr;
|
||||
|
||||
return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => {
|
||||
this.title = data.name || this.title;
|
||||
this.data = data;
|
||||
|
||||
return this.setEntryIdFromOffset(data.id, this.offset, this.selectedGroup).then(() => {
|
||||
return this.dataProvider.getDatabaseAccessInformation(data.id);
|
||||
return this.dataProvider.getFields(this.data.id).then((fieldsData) => {
|
||||
this.fields = this.utils.arrayToObject(fieldsData, 'id');
|
||||
this.fieldsArray = fieldsData;
|
||||
});
|
||||
}).then(() => {
|
||||
return this.setEntryFromOffset().then(() => {
|
||||
return this.dataProvider.getDatabaseAccessInformation(this.data.id);
|
||||
});
|
||||
}).then((accessData) => {
|
||||
this.access = accessData;
|
||||
|
@ -155,35 +156,13 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
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 = this.utils.arrayToObject(fieldsData, 'id');
|
||||
|
||||
return this.dataHelper.getEntry(this.data, this.entryId, this.offlineActions);
|
||||
});
|
||||
}).then((entry) => {
|
||||
this.ratingInfo = entry.ratinginfo;
|
||||
entry = entry.entry;
|
||||
|
||||
// Index contents by fieldid.
|
||||
entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid');
|
||||
|
||||
fieldsArray = this.utils.objectToArray(this.fields);
|
||||
|
||||
return this.dataHelper.applyOfflineActions(entry, this.offlineActions, fieldsArray);
|
||||
}).then((entryData) => {
|
||||
this.entry = entryData;
|
||||
|
||||
}).then(() => {
|
||||
const actions = this.dataHelper.getActions(this.data, this.access, this.entry);
|
||||
|
||||
const templte = this.data.singletemplate || this.dataHelper.getDefaultTemplate('single', fieldsArray);
|
||||
this.entryHtml = this.dataHelper.displayShowFields(templte, fieldsArray, this.entry, this.offset, 'show', actions);
|
||||
const template = this.data.singletemplate || this.dataHelper.getDefaultTemplate('single', this.fieldsArray);
|
||||
this.entryHtml = this.dataHelper.displayShowFields(template, this.fieldsArray, this.entry, this.offset, 'show',
|
||||
actions);
|
||||
this.showComments = actions.comments;
|
||||
|
||||
const entries = {};
|
||||
|
@ -193,7 +172,9 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
this.jsData = {
|
||||
fields: this.fields,
|
||||
entries: entries,
|
||||
data: this.data
|
||||
data: this.data,
|
||||
module: this.module,
|
||||
group: this.selectedGroup
|
||||
};
|
||||
}).catch((message) => {
|
||||
if (!refresh) {
|
||||
|
@ -266,7 +247,7 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
*/
|
||||
setGroup(groupId: number): Promise<any> {
|
||||
this.selectedGroup = groupId;
|
||||
this.offset = 0;
|
||||
this.offset = null;
|
||||
this.entry = null;
|
||||
this.entryId = null;
|
||||
this.entryLoaded = false;
|
||||
|
@ -275,46 +256,73 @@ export class AddonModDataEntryPage implements OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function to translate offset to entry identifier and set next/previous entries.
|
||||
* Convenience function to fetch the entry and set next/previous entries.
|
||||
*
|
||||
* @param {number} dataId Data Id.
|
||||
* @param {number} [offset] Offset of the entry.
|
||||
* @param {number} [groupId] Group Id to get the entry.
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected setEntryIdFromOffset(dataId: number, offset?: number, groupId?: number): Promise<any> {
|
||||
if (typeof offset != 'number') {
|
||||
protected setEntryFromOffset(): Promise<any> {
|
||||
const emptyOffset = typeof this.offset != 'number';
|
||||
|
||||
if (emptyOffset && typeof this.entryId == 'number') {
|
||||
// Entry id passed as navigation parameter instead of the offset.
|
||||
// We don't display next/previous buttons in this case.
|
||||
this.nextOffset = null;
|
||||
this.previousOffset = null;
|
||||
|
||||
return Promise.resolve();
|
||||
return this.dataHelper.fetchEntry(this.data, this.fieldsArray, this.entryId).then((entry) => {
|
||||
this.entry = entry.entry;
|
||||
this.ratingInfo = entry.ratinginfo;
|
||||
});
|
||||
}
|
||||
|
||||
const perPage = AddonModDataProvider.PER_PAGE;
|
||||
const page = Math.floor(offset / perPage);
|
||||
const pageOffset = offset % perPage;
|
||||
const page = !emptyOffset && this.offset >= 0 ? Math.floor(this.offset / perPage) : 0;
|
||||
|
||||
return this.dataProvider.getEntries(dataId, groupId, undefined, undefined, page, perPage).then((entries) => {
|
||||
if (!entries || !entries.entries || !entries.entries.length || pageOffset >= entries.entries.length) {
|
||||
return Promise.reject(null);
|
||||
return this.dataHelper.fetchEntries(this.data, this.fieldsArray, this.selectedGroup, undefined, undefined, '0', 'DESC',
|
||||
page, perPage).then((entries) => {
|
||||
|
||||
const pageEntries = entries.offlineEntries.concat(entries.entries);
|
||||
let pageIndex; // Index of the entry when concatenating offline and online page entries.
|
||||
if (emptyOffset) {
|
||||
// No offset passed, display the first entry.
|
||||
pageIndex = 0;
|
||||
} else if (this.offset > 0) {
|
||||
// Online entry.
|
||||
pageIndex = this.offset % perPage + entries.offlineEntries.length;
|
||||
} else {
|
||||
// Offline entry.
|
||||
pageIndex = this.offset + entries.offlineEntries.length;
|
||||
}
|
||||
|
||||
this.entryId = entries.entries[pageOffset].id;
|
||||
this.previousOffset = offset > 0 ? offset - 1 : null;
|
||||
if (pageOffset + 1 < entries.entries.length) {
|
||||
this.entry = pageEntries[pageIndex];
|
||||
this.entryId = this.entry.id;
|
||||
|
||||
this.previousOffset = page > 0 || pageIndex > 0 ? this.offset - 1 : null;
|
||||
|
||||
let promise;
|
||||
|
||||
if (pageIndex + 1 < pageEntries.length) {
|
||||
// Not the last entry on the page;
|
||||
this.nextOffset = offset + 1;
|
||||
} else if (entries.entries.length < perPage) {
|
||||
this.nextOffset = this.offset + 1;
|
||||
} else if (pageEntries.length < perPage) {
|
||||
// Last entry of the last page.
|
||||
this.nextOffset = null;
|
||||
} else {
|
||||
// Last entry of the page, check if there are more pages.
|
||||
return this.dataProvider.getEntries(dataId, groupId, undefined, undefined, page + 1, perPage).then((entries) => {
|
||||
this.nextOffset = entries && entries.entries && entries.entries.length > 0 ? offset + 1 : null;
|
||||
promise = this.dataProvider.getEntries(this.data.id, this.selectedGroup, '0', 'DESC', page + 1, perPage)
|
||||
.then((entries) => {
|
||||
this.nextOffset = entries && entries.entries && entries.entries.length > 0 ? this.offset + 1 : null;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(promise).then(() => {
|
||||
if (this.entryId > 0) {
|
||||
// Online entry, we need to fetch the the rating info.
|
||||
return this.dataProvider.getEntry(this.data.id, this.entryId).then((entry) => {
|
||||
this.ratingInfo = entry.ratinginfo;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,7 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||
import { AddonModDataProvider } from './data';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { AddonModDataHelperProvider } from './helper';
|
||||
|
||||
/**
|
||||
* Content links handler for database approve/disapprove entry.
|
||||
|
@ -30,29 +28,10 @@ export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase
|
|||
featureName = 'CoreCourseModuleDelegate_AddonModData';
|
||||
pattern = /\/mod\/data\/view\.php.*([\?\&](d|approve|disapprove)=\d+)/;
|
||||
|
||||
constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider) {
|
||||
constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to help get courseId.
|
||||
*
|
||||
* @param {number} dataId Database Id.
|
||||
* @param {string} siteId Site Id, if not set, current site will be used.
|
||||
* @param {number} courseId Course Id if already set.
|
||||
* @return {Promise<number>} Resolved with course Id when done.
|
||||
*/
|
||||
protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise<number> {
|
||||
if (courseId) {
|
||||
return Promise.resolve(courseId);
|
||||
}
|
||||
|
||||
return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
|
||||
return module.course;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
|
@ -66,34 +45,11 @@ export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase
|
|||
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: (siteId, navCtrl?): void => {
|
||||
const modal = this.domUtils.showModalLoading(),
|
||||
dataId = parseInt(params.d, 10),
|
||||
const dataId = parseInt(params.d, 10),
|
||||
entryId = parseInt(params.approve, 10) || parseInt(params.disapprove, 10),
|
||||
approve = parseInt(params.approve, 10) ? true : false;
|
||||
|
||||
this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
|
||||
courseId = cId;
|
||||
|
||||
// Approve/disapprove entry.
|
||||
return this.dataProvider.approveEntry(dataId, entryId, approve, courseId, siteId).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errorapproving', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}).then(() => {
|
||||
const promises = [];
|
||||
promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
|
||||
promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
|
||||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, siteId);
|
||||
|
||||
this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true,
|
||||
3000);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
this.dataHelper.approveOrDisapproveEntry(dataId, entryId, approve, courseId, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -21,6 +21,67 @@ import { CoreFilepoolProvider } from '@providers/filepool';
|
|||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||
import { AddonModDataOfflineProvider } from './offline';
|
||||
import { AddonModDataFieldsDelegate } from './fields-delegate';
|
||||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||
|
||||
/**
|
||||
* Database entry (online or offline).
|
||||
*/
|
||||
export interface AddonModDataEntry {
|
||||
id: number; // Negative for offline entries.
|
||||
userid: number;
|
||||
groupid: number;
|
||||
dataid: number;
|
||||
timecreated: number;
|
||||
timemodified: number;
|
||||
approved: boolean;
|
||||
canmanageentry: boolean;
|
||||
fullname: string;
|
||||
contents: AddonModDataEntryFields;
|
||||
deleted?: boolean; // Entry is deleted offline.
|
||||
hasOffline?: boolean; // Entry has offline actions.
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry field content.
|
||||
*/
|
||||
export interface AddonModDataEntryField {
|
||||
fieldid: number;
|
||||
content: string;
|
||||
content1: string;
|
||||
content2: string;
|
||||
content3: string;
|
||||
content4: string;
|
||||
files: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry contents indexed by field id.
|
||||
*/
|
||||
export interface AddonModDataEntryFields {
|
||||
[fieldid: number]: AddonModDataEntryField;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of entries returned by web service and helper functions.
|
||||
*/
|
||||
export interface AddonModDataEntries {
|
||||
entries: AddonModDataEntry[]; // Online entries.
|
||||
totalcount: number; // Total count of online entries or found entries.
|
||||
maxcount?: number; // Total count of online entries. Only returned when searching.
|
||||
offlineEntries?: AddonModDataEntry[]; // Offline entries.
|
||||
hasOfflineActions?: boolean; // Whether the database has offline data.
|
||||
hasOfflineRatings?: boolean; // Whether the database has offline ratings.
|
||||
}
|
||||
|
||||
/**
|
||||
* Subfield form data.
|
||||
*/
|
||||
export interface AddonModDataSubfieldData {
|
||||
fieldid: number;
|
||||
subfield?: string;
|
||||
value?: string; // Value encoded in JSON.
|
||||
files?: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Service that provides some features for databases.
|
||||
|
@ -49,13 +110,13 @@ export class AddonModDataProvider {
|
|||
* @param {number} courseId Course ID.
|
||||
* @param {any} contents The fields data to be created.
|
||||
* @param {number} [groupId] Group id, 0 means that the function will determine the user group.
|
||||
* @param {any} fields The fields that define the contents.
|
||||
* @param {any[]} fields The fields that define the contents.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [forceOffline] Force editing entry in offline.
|
||||
* @return {Promise<any>} Promise resolved when the action is done.
|
||||
*/
|
||||
addEntry(dataId: number, entryId: number, courseId: number, contents: any, groupId: number = 0, fields: any, siteId?: string,
|
||||
forceOffline: boolean = false): Promise<any> {
|
||||
addEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], groupId: number = 0,
|
||||
fields: any, siteId?: string, forceOffline: boolean = false): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a data to be synchronized later.
|
||||
|
@ -76,6 +137,8 @@ export class AddonModDataProvider {
|
|||
fieldnotifications: notifications
|
||||
});
|
||||
}
|
||||
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.addEntryOnline(dataId, contents, groupId, siteId).catch((error) => {
|
||||
|
@ -93,12 +156,12 @@ export class AddonModDataProvider {
|
|||
* Adds a new entry to a database. It does not cache calls. It will fail if offline or cannot connect.
|
||||
*
|
||||
* @param {number} dataId Database ID.
|
||||
* @param {any} data The fields data to be created.
|
||||
* @param {any[]} data The fields data to be created.
|
||||
* @param {number} [groupId] Group id, 0 means that the function will determine the user group.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the action is done.
|
||||
*/
|
||||
addEntryOnline(dataId: number, data: any, groupId?: number, siteId?: string): Promise<any> {
|
||||
addEntryOnline(dataId: number, data: AddonModDataSubfieldData[], groupId?: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
databaseid: dataId,
|
||||
|
@ -184,7 +247,7 @@ export class AddonModDataProvider {
|
|||
* @param {any} contents The contents data of the fields.
|
||||
* @return {any} Array of notifications if any or false.
|
||||
*/
|
||||
protected checkFields(fields: any, contents: any): any {
|
||||
protected checkFields(fields: any, contents: AddonModDataSubfieldData[]): any[] | false {
|
||||
const notifications = [],
|
||||
contentsIndexed = {};
|
||||
|
||||
|
@ -289,13 +352,13 @@ export class AddonModDataProvider {
|
|||
* @param {number} dataId Database ID.
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {any} contents The contents data to be updated.
|
||||
* @param {any[]} contents The contents data to be updated.
|
||||
* @param {any} fields The fields that define the contents.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} forceOffline Force editing entry in offline.
|
||||
* @return {Promise<any>} Promise resolved when the action is done.
|
||||
*/
|
||||
editEntry(dataId: number, entryId: number, courseId: number, contents: any, fields: any, siteId?: string,
|
||||
editEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], fields: any, siteId?: string,
|
||||
forceOffline: boolean = false): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
|
@ -370,11 +433,11 @@ export class AddonModDataProvider {
|
|||
* Updates an existing entry. It does not cache calls. It will fail if offline or cannot connect.
|
||||
*
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {any} data The fields data to be updated.
|
||||
* @param {any[]} data The fields data to be updated.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the action is done.
|
||||
*/
|
||||
editEntryOnline(entryId: number, data: number, siteId?: string): Promise<any> {
|
||||
editEntryOnline(entryId: number, data: AddonModDataSubfieldData[], siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
entryid: entryId,
|
||||
|
@ -397,11 +460,11 @@ export class AddonModDataProvider {
|
|||
* @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.
|
||||
* @return {Promise<AddonModDataEntry[]>} 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?: string): Promise<AddonModDataEntry[]> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, [], 0, siteId);
|
||||
|
@ -420,10 +483,10 @@ export class AddonModDataProvider {
|
|||
* @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.
|
||||
* @return {Promise<AddonModDataEntry[]>} 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> {
|
||||
forceCache: boolean, ignoreCache: boolean, entries: any, page: number, siteId: string): Promise<AddonModDataEntry[]> {
|
||||
return this.getEntries(dataId, groupId, sort, order, page, perPage, forceCache, ignoreCache, siteId)
|
||||
.then((result) => {
|
||||
entries = entries.concat(result.entries);
|
||||
|
@ -595,11 +658,11 @@ export class AddonModDataProvider {
|
|||
* @param {boolean} [forceCache=false] True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it'll always fail in offline or server down).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the database is retrieved.
|
||||
* @return {Promise<AddonModDataEntries>} Promise resolved when the database is retrieved.
|
||||
*/
|
||||
getEntries(dataId: number, groupId: number = 0, sort: string = '0', order: string = 'DESC', page: number = 0,
|
||||
perPage: number = AddonModDataProvider.PER_PAGE, forceCache: boolean = false, ignoreCache: boolean = false,
|
||||
siteId?: string): Promise<any> {
|
||||
siteId?: string): Promise<AddonModDataEntries> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
// Always use sort and order params to improve cache usage (entries are identified by params).
|
||||
const params = {
|
||||
|
@ -622,7 +685,13 @@ export class AddonModDataProvider {
|
|||
preSets['emergencyCache'] = false;
|
||||
}
|
||||
|
||||
return site.read('mod_data_get_entries', params, preSets);
|
||||
return site.read('mod_data_get_entries', params, preSets).then((response) => {
|
||||
response.entries.forEach((entry) => {
|
||||
entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid');
|
||||
});
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -654,9 +723,10 @@ export class AddonModDataProvider {
|
|||
* @param {number} entryId Entry ID.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it'll always fail in offline or server down).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the database entry is retrieved.
|
||||
* @return {Promise<{entry: AddonModDataEntry, ratinginfo: CoreRatingInfo}>} Promise resolved when the entry is retrieved.
|
||||
*/
|
||||
getEntry(dataId: number, entryId: number, ignoreCache: boolean = false, siteId?: string): Promise<any> {
|
||||
getEntry(dataId: number, entryId: number, ignoreCache: boolean = false, siteId?: string):
|
||||
Promise<{entry: AddonModDataEntry, ratinginfo: CoreRatingInfo}> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
entryid: entryId,
|
||||
|
@ -671,7 +741,11 @@ export class AddonModDataProvider {
|
|||
preSets['emergencyCache'] = false;
|
||||
}
|
||||
|
||||
return site.read('mod_data_get_entry', params, preSets);
|
||||
return site.read('mod_data_get_entry', params, preSets).then((response) => {
|
||||
response.entry.contents = this.utils.arrayToObject(response.entry.contents, 'fieldid');
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -871,16 +945,16 @@ export class AddonModDataProvider {
|
|||
* @param {number} dataId The data instance id.
|
||||
* @param {number} [groupId=0] Group id, 0 means that the function will determine the user group.
|
||||
* @param {string} [search] Search text. It will be used if advSearch is not defined.
|
||||
* @param {any} [advSearch] Advanced search data.
|
||||
* @param {any[]} [advSearch] Advanced search data.
|
||||
* @param {string} [sort] Sort by this field.
|
||||
* @param {string} [order] The direction of the sorting.
|
||||
* @param {number} [page=0] Page of records to return.
|
||||
* @param {number} [perPage=PER_PAGE] Records per page to return. Default on AddonModDataProvider.PER_PAGE.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the action is done.
|
||||
* @return {Promise<AddonModDataEntries>} Promise resolved when the action is done.
|
||||
*/
|
||||
searchEntries(dataId: number, groupId: number = 0, search?: string, advSearch?: any, sort?: string, order?: string,
|
||||
page: number = 0, perPage: number = AddonModDataProvider.PER_PAGE, siteId?: string): Promise<any> {
|
||||
page: number = 0, perPage: number = AddonModDataProvider.PER_PAGE, siteId?: string): Promise<AddonModDataEntries> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
databaseid: dataId,
|
||||
|
@ -911,7 +985,13 @@ export class AddonModDataProvider {
|
|||
params['advsearch'] = advSearch;
|
||||
}
|
||||
|
||||
return site.read('mod_data_search_entries', params, preSets);
|
||||
return site.read('mod_data_search_entries', params, preSets).then((response) => {
|
||||
response.entries.forEach((entry) => {
|
||||
entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid');
|
||||
});
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,10 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||
import { AddonModDataProvider } from './data';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { AddonModDataHelperProvider } from './helper';
|
||||
|
||||
/**
|
||||
* Content links handler for database delete entry.
|
||||
|
@ -31,30 +28,10 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
|
|||
featureName = 'CoreCourseModuleDelegate_AddonModData';
|
||||
pattern = /\/mod\/data\/view\.php.*([\?\&](d|delete)=\d+)/;
|
||||
|
||||
constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
||||
private translate: TranslateService) {
|
||||
constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to help get courseId.
|
||||
*
|
||||
* @param {number} dataId Database Id.
|
||||
* @param {string} siteId Site Id, if not set, current site will be used.
|
||||
* @param {number} courseId Course Id if already set.
|
||||
* @return {Promise<number>} Resolved with course Id when done.
|
||||
*/
|
||||
protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise<number> {
|
||||
if (courseId) {
|
||||
return Promise.resolve(courseId);
|
||||
}
|
||||
|
||||
return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
|
||||
return module.course;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
|
@ -68,38 +45,10 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
|
|||
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: (siteId, navCtrl?): void => {
|
||||
const dataId = parseInt(params.d, 10);
|
||||
const entryId = parseInt(params.delete, 10);
|
||||
|
||||
this.domUtils.showConfirm(this.translate.instant('addon.mod_data.confirmdeleterecord')).then(() => {
|
||||
const modal = this.domUtils.showModalLoading(),
|
||||
dataId = parseInt(params.d, 10),
|
||||
entryId = parseInt(params.delete, 10);
|
||||
|
||||
return this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
|
||||
courseId = cId;
|
||||
|
||||
// Delete entry.
|
||||
return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}).then(() => {
|
||||
const promises = [];
|
||||
promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
|
||||
promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
|
||||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId,
|
||||
deleted: true}, siteId);
|
||||
|
||||
this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}).catch(() => {
|
||||
// Nothing to do.
|
||||
});
|
||||
this.dataHelper.showDeleteEntryModal(dataId, entryId, courseId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -14,12 +14,18 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||
import { AddonModDataFieldsDelegate } from './fields-delegate';
|
||||
import { AddonModDataOfflineProvider } from './offline';
|
||||
import { AddonModDataProvider } from './data';
|
||||
import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline';
|
||||
import { AddonModDataProvider, AddonModDataEntry, AddonModDataEntryFields, AddonModDataEntries } from './data';
|
||||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||
import { CoreRatingOfflineProvider } from '@core/rating/providers/offline';
|
||||
|
||||
/**
|
||||
* Service that provides helper functions for datas.
|
||||
|
@ -30,20 +36,26 @@ export class AddonModDataHelperProvider {
|
|||
constructor(private sitesProvider: CoreSitesProvider, protected dataProvider: AddonModDataProvider,
|
||||
private translate: TranslateService, private fieldsDelegate: AddonModDataFieldsDelegate,
|
||||
private dataOffline: AddonModDataOfflineProvider, private fileUploaderProvider: CoreFileUploaderProvider,
|
||||
private textUtils: CoreTextUtilsProvider) { }
|
||||
private textUtils: CoreTextUtilsProvider, private eventsProvider: CoreEventsProvider, private utils: CoreUtilsProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider,
|
||||
private ratingOffline: CoreRatingOfflineProvider) {}
|
||||
|
||||
/**
|
||||
* Returns the record with the offline actions applied.
|
||||
*
|
||||
* @param {any} record Entry to modify.
|
||||
* @param {any} offlineActions Offline data with the actions done.
|
||||
* @param {any} fields Entry defined fields indexed by fieldid.
|
||||
* @return {any} Modified entry.
|
||||
* @param {AddonModDataEntry} record Entry to modify.
|
||||
* @param {AddonModDataOfflineAction[]} offlineActions Offline data with the actions done.
|
||||
* @param {any[]} fields Entry defined fields indexed by fieldid.
|
||||
* @return {Promise<AddonModDataEntry>} Promise resolved when done.
|
||||
*/
|
||||
applyOfflineActions(record: any, offlineActions: any[], fields: any[]): any {
|
||||
applyOfflineActions(record: AddonModDataEntry, offlineActions: AddonModDataOfflineAction[], fields: any[]):
|
||||
Promise<AddonModDataEntry> {
|
||||
const promises = [];
|
||||
|
||||
offlineActions.forEach((action) => {
|
||||
record.timemodified = action.timemodified;
|
||||
record.hasOffline = true;
|
||||
|
||||
switch (action.action) {
|
||||
case 'approve':
|
||||
record.approved = true;
|
||||
|
@ -56,6 +68,8 @@ export class AddonModDataHelperProvider {
|
|||
break;
|
||||
case 'add':
|
||||
case 'edit':
|
||||
record.groupid = action.groupid;
|
||||
|
||||
const offlineContents = {};
|
||||
|
||||
action.fields.forEach((offlineContent) => {
|
||||
|
@ -77,10 +91,12 @@ export class AddonModDataHelperProvider {
|
|||
promises.push(this.getStoredFiles(record.dataid, record.id, field.id).then((offlineFiles) => {
|
||||
record.contents[field.id] = this.fieldsDelegate.overrideData(field, record.contents[field.id],
|
||||
offlineContents[field.id], offlineFiles);
|
||||
record.contents[field.id].fieldid = field.id;
|
||||
}));
|
||||
} else {
|
||||
record.contents[field.id] = this.fieldsDelegate.overrideData(field, record.contents[field.id],
|
||||
offlineContents[field.id]);
|
||||
record.contents[field.id].fieldid = field.id;
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -94,18 +110,59 @@ export class AddonModDataHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve or disapprove a database entry.
|
||||
*
|
||||
* @param {number} dataId Database ID.
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {boolaen} approve True to approve, false to disapprove.
|
||||
* @param {number} [courseId] Course ID. It not defined, it will be fetched.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
*/
|
||||
approveOrDisapproveEntry(dataId: number, entryId: number, approve: boolean, courseId?: number, siteId?: string): void {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
|
||||
this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => {
|
||||
// Approve/disapprove entry.
|
||||
return this.dataProvider.approveEntry(dataId, entryId, approve, courseId, siteId).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errorapproving', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}).then(() => {
|
||||
const promises = [];
|
||||
promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
|
||||
promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
|
||||
|
||||
return Promise.all(promises).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}).then(() => {
|
||||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, siteId);
|
||||
|
||||
this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true, 3000);
|
||||
}).catch(() => {
|
||||
// Ignore error, it was already displayed.
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays fields for being shown.
|
||||
*
|
||||
* @param {string} template Template HMTL.
|
||||
* @param {any[]} fields Fields that defines every content in the entry.
|
||||
* @param {any} entry Entry.
|
||||
* @param {number} offset Entry offset.
|
||||
* @param {string} mode Mode list or show.
|
||||
* @param {any} actions Actions that can be performed to the record.
|
||||
* @return {string} Generated HTML.
|
||||
* @param {string} template Template HMTL.
|
||||
* @param {any[]} fields Fields that defines every content in the entry.
|
||||
* @param {any} entry Entry.
|
||||
* @param {number} offset Entry offset.
|
||||
* @param {string} mode Mode list or show.
|
||||
* @param {AddonModDataOfflineAction[]} actions Actions that can be performed to the record.
|
||||
* @return {string} Generated HTML.
|
||||
*/
|
||||
displayShowFields(template: string, fields: any[], entry: any, offset: number, mode: string, actions: any): string {
|
||||
displayShowFields(template: string, fields: any[], entry: any, offset: number, mode: string,
|
||||
actions: AddonModDataOfflineAction[]): string {
|
||||
if (!template) {
|
||||
return '';
|
||||
}
|
||||
|
@ -135,8 +192,8 @@ export class AddonModDataHelperProvider {
|
|||
} else if (action == 'approvalstatus') {
|
||||
render = this.translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved'));
|
||||
} else {
|
||||
render = '<addon-mod-data-action action="' + action + '" [entry]="entries[' + entry.id +
|
||||
']" mode="' + mode + '" [database]="data" [offset]="' + offset + '"></addon-mod-data-action>';
|
||||
render = '<addon-mod-data-action action="' + action + '" [entry]="entries[' + entry.id + ']" mode="' + mode +
|
||||
'" [database]="data" [module]="module" [offset]="' + offset + '" [group]="group" ></addon-mod-data-action>';
|
||||
}
|
||||
template = template.replace(replace, render);
|
||||
} else {
|
||||
|
@ -147,6 +204,153 @@ export class AddonModDataHelperProvider {
|
|||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get online and offline entries, or search entries.
|
||||
*
|
||||
* @param {any} data Database object.
|
||||
* @param {any[]} fields The fields that define the contents.
|
||||
* @param {number} [groupId=0] Group ID.
|
||||
* @param {string} [search] Search text. It will be used if advSearch is not defined.
|
||||
* @param {any[]} [advSearch] Advanced search data.
|
||||
* @param {string} [sort=0] Sort the records by this field id, reserved ids are:
|
||||
* 0: timeadded
|
||||
* -1: firstname
|
||||
* -2: lastname
|
||||
* -3: approved
|
||||
* -4: timemodified.
|
||||
* Empty for using the default database setting.
|
||||
* @param {string} [order=DESC] The direction of the sorting: 'ASC' or 'DESC'.
|
||||
* Empty for using the default database setting.
|
||||
* @param {number} [page=0] Page of records to return.
|
||||
* @param {number} [perPage=PER_PAGE] Records per page to return. Default on PER_PAGE.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<AddonModDataEntries>} Promise resolved when the database is retrieved.
|
||||
*/
|
||||
fetchEntries(data: any, fields: any[], groupId: number = 0, search?: string, advSearch?: any[], sort: string = '0',
|
||||
order: string = 'DESC', page: number = 0, perPage: number = AddonModDataProvider.PER_PAGE, siteId?: string):
|
||||
Promise<AddonModDataEntries> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const offlineActions = {};
|
||||
const result: AddonModDataEntries = {
|
||||
entries: [],
|
||||
totalcount: 0,
|
||||
offlineEntries: []
|
||||
};
|
||||
|
||||
const offlinePromise = this.dataOffline.getDatabaseEntries(data.id, site.id).then((actions) => {
|
||||
result.hasOfflineActions = !!actions.length;
|
||||
|
||||
actions.forEach((action) => {
|
||||
if (typeof offlineActions[action.entryid] == 'undefined') {
|
||||
offlineActions[action.entryid] = [];
|
||||
}
|
||||
offlineActions[action.entryid].push(action);
|
||||
|
||||
// We only display new entries in the first page when not searching.
|
||||
if (action.action == 'add' && page == 0 && !search && !advSearch &&
|
||||
(!action.groupid || !groupId || action.groupid == groupId)) {
|
||||
result.offlineEntries.push({
|
||||
id: action.entryid,
|
||||
canmanageentry: true,
|
||||
approved: !data.approval || data.manageapproved,
|
||||
dataid: data.id,
|
||||
groupid: action.groupid,
|
||||
timecreated: -action.entryid,
|
||||
timemodified: -action.entryid,
|
||||
userid: site.getUserId(),
|
||||
fullname: site.getInfo().fullname,
|
||||
contents: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sort offline entries by creation time.
|
||||
result.offlineEntries.sort((entry1, entry2) => entry2.timecreated - entry1.timecreated);
|
||||
});
|
||||
|
||||
const ratingsPromise = this.ratingOffline.hasRatings('mod_data', 'entry', 'module', data.coursemodule)
|
||||
.then((hasRatings) => {
|
||||
result.hasOfflineRatings = hasRatings;
|
||||
});
|
||||
|
||||
let fetchPromise: Promise<void>;
|
||||
if (search || advSearch) {
|
||||
fetchPromise = this.dataProvider.searchEntries(data.id, groupId, search, advSearch, sort, order, page, perPage,
|
||||
site.id).then((fetchResult) => {
|
||||
result.entries = fetchResult.entries;
|
||||
result.totalcount = fetchResult.totalcount;
|
||||
result.maxcount = fetchResult.maxcount;
|
||||
});
|
||||
} else {
|
||||
fetchPromise = this.dataProvider.getEntries(data.id, groupId, sort, order, page, perPage, false, false, site.id)
|
||||
.then((fetchResult) => {
|
||||
result.entries = fetchResult.entries;
|
||||
result.totalcount = fetchResult.totalcount;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([offlinePromise, ratingsPromise, fetchPromise]).then(() => {
|
||||
// Apply offline actions to online and offline entries.
|
||||
const promises = [];
|
||||
result.entries.forEach((entry) => {
|
||||
promises.push(this.applyOfflineActions(entry, offlineActions[entry.id] || [], fields));
|
||||
});
|
||||
result.offlineEntries.forEach((entry) => {
|
||||
promises.push(this.applyOfflineActions(entry, offlineActions[entry.id] || [], fields));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an online or offline entry.
|
||||
*
|
||||
* @param {any} data Database.
|
||||
* @param {any[]} fields List of database fields.
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<{entry: AddonModDataEntry, ratinginfo?: CoreRatingInfo}>} Promise resolved with the entry.
|
||||
*/
|
||||
fetchEntry(data: any, fields: any[], entryId: number, siteId?: string):
|
||||
Promise<{entry: AddonModDataEntry, ratinginfo?: CoreRatingInfo}> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.dataOffline.getEntryActions(data.id, entryId, site.id).then((offlineActions) => {
|
||||
let promise: Promise<{entry: AddonModDataEntry, ratinginfo?: CoreRatingInfo}>;
|
||||
|
||||
if (entryId > 0) {
|
||||
// Online entry.
|
||||
promise = this.dataProvider.getEntry(data.id, entryId, false, site.id);
|
||||
} else {
|
||||
// Offline entry or new entry.
|
||||
promise = Promise.resolve({
|
||||
entry: {
|
||||
id: entryId,
|
||||
userid: site.getUserId(),
|
||||
groupid: 0,
|
||||
dataid: data.id,
|
||||
timecreated: -entryId,
|
||||
timemodified: -entryId,
|
||||
approved: !data.approval || data.manageapproved,
|
||||
canmanageentry: true,
|
||||
fullname: site.getInfo().fullname,
|
||||
contents: [],
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then((response) => {
|
||||
return this.applyOfflineActions(response.entry, offlineActions, fields).then(() => {
|
||||
return response;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with all the actions that the user can do over the record.
|
||||
*
|
||||
|
@ -179,6 +383,24 @@ export class AddonModDataHelperProvider {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to get the course id of the database.
|
||||
*
|
||||
* @param {number} dataId Database id.
|
||||
* @param {number} [courseId] Course id, if known.
|
||||
* @param {string} [siteId] Site id, if not set, current site will be used.
|
||||
* @return {Promise<number>} Resolved with course Id when done.
|
||||
*/
|
||||
protected getActivityCourseIdIfNotSet(dataId: number, courseId?: number, siteId?: string): Promise<number> {
|
||||
if (courseId) {
|
||||
return Promise.resolve(courseId);
|
||||
}
|
||||
|
||||
return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
|
||||
return module.course;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default template of a certain type.
|
||||
*
|
||||
|
@ -256,17 +478,17 @@ export class AddonModDataHelperProvider {
|
|||
* Retrieve the entered data in the edit form.
|
||||
* We don't use ng-model because it doesn't detect changes done by JavaScript.
|
||||
*
|
||||
* @param {any} inputData Array with the entered form values.
|
||||
* @param {Array} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, files will be uploaded and itemId set.
|
||||
* @param {number} entryId Entry Id.
|
||||
* @param {any} entryContents Original entry contents indexed by field id.
|
||||
* @param {boolean} offline True to prepare the data for an offline uploading, false otherwise.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} That contains object with the answers.
|
||||
* @param {any} inputData Array with the entered form values.
|
||||
* @param {Array} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, files will be uploaded and itemId set.
|
||||
* @param {number} entryId Entry Id.
|
||||
* @param {AddonModDataEntryFields} entryContents Original entry contents.
|
||||
* @param {boolean} offline True to prepare the data for an offline uploading, false otherwise.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} That contains object with the answers.
|
||||
*/
|
||||
getEditDataFromForm(inputData: any, fields: any, dataId: number, entryId: number, entryContents: any, offline: boolean = false,
|
||||
siteId?: string): Promise<any> {
|
||||
getEditDataFromForm(inputData: any, fields: any, dataId: number, entryId: number, entryContents: AddonModDataEntryFields,
|
||||
offline: boolean = false, siteId?: string): Promise<any> {
|
||||
if (!inputData) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
@ -322,13 +544,13 @@ export class AddonModDataHelperProvider {
|
|||
/**
|
||||
* Retrieve the temp files to be updated.
|
||||
*
|
||||
* @param {any} inputData Array with the entered form values.
|
||||
* @param {Array} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, fils will be uploaded and itemId set.
|
||||
* @param {any} entryContents Original entry contents indexed by field id.
|
||||
* @return {Promise<any>} That contains object with the files.
|
||||
* @param {any} inputData Array with the entered form values.
|
||||
* @param {any[]} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, fils will be uploaded and itemId set.
|
||||
* @param {AddonModDataEntryFields} entryContents Original entry contents indexed by field id.
|
||||
* @return {Promise<any>} That contains object with the files.
|
||||
*/
|
||||
getEditTmpFiles(inputData: any, fields: any, dataId: number, entryContents: any): Promise<any> {
|
||||
getEditTmpFiles(inputData: any, fields: any[], dataId: number, entryContents: AddonModDataEntryFields): Promise<any> {
|
||||
if (!inputData) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
@ -343,45 +565,6 @@ export class AddonModDataHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, false, siteId);
|
||||
}
|
||||
|
||||
// It's an offline entry, search it in the offline actions.
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const offlineEntry = offlineActions.find((offlineAction) => 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 a list of stored attachment files for a new entry. See $mmaModDataHelper#storeFiles.
|
||||
*
|
||||
|
@ -403,13 +586,13 @@ export class AddonModDataHelperProvider {
|
|||
/**
|
||||
* Check if data has been changed by the user.
|
||||
*
|
||||
* @param {any} inputData Array with the entered form values.
|
||||
* @param {any} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, fils will be uploaded and itemId set.
|
||||
* @param {any} entryContents Original entry contents indexed by field id.
|
||||
* @return {Promise<boolean>} True if changed, false if not.
|
||||
* @param {any} inputData Object with the entered form values.
|
||||
* @param {any[]} fields Fields that defines every content in the entry.
|
||||
* @param {number} [dataId] Database Id. If set, fils will be uploaded and itemId set.
|
||||
* @param {AddonModDataEntryFields} entryContents Original entry contents indexed by field id.
|
||||
* @return {Promise<boolean>} True if changed, false if not.
|
||||
*/
|
||||
hasEditDataChanged(inputData: any, fields: any, dataId: number, entryContents: any): Promise<boolean> {
|
||||
hasEditDataChanged(inputData: any, fields: any[], dataId: number, entryContents: AddonModDataEntryFields): Promise<boolean> {
|
||||
const promises = fields.map((field) => {
|
||||
return this.fieldsDelegate.hasFieldDataChanged(field, inputData, entryContents[field.id]);
|
||||
});
|
||||
|
@ -424,6 +607,45 @@ export class AddonModDataHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation modal for deleting an entry.
|
||||
*
|
||||
* @param {number} dataId Database ID.
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {number} [courseId] Course ID. It not defined, it will be fetched.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
*/
|
||||
showDeleteEntryModal(dataId: number, entryId: number, courseId?: number, siteId?: string): void {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
this.domUtils.showConfirm(this.translate.instant('addon.mod_data.confirmdeleterecord')).then(() => {
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
|
||||
return this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => {
|
||||
return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId);
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
}).then(() => {
|
||||
return this.utils.allPromises([
|
||||
this.dataProvider.invalidateEntryData(dataId, entryId, siteId),
|
||||
this.dataProvider.invalidateEntriesData(dataId, siteId)
|
||||
]).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}).then(() => {
|
||||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId, entryId, deleted: true}, siteId);
|
||||
|
||||
this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}).catch(() => {
|
||||
// Ignore error, it was already displayed.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of files (either online files or local files), store the local files in a local folder
|
||||
* to be submitted later.
|
||||
|
|
|
@ -16,9 +16,24 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreFileProvider } from '@providers/file';
|
||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
import { AddonModDataSubfieldData } from './data';
|
||||
|
||||
/**
|
||||
* Entry action stored offline.
|
||||
*/
|
||||
export interface AddonModDataOfflineAction {
|
||||
dataid: number;
|
||||
courseid: number;
|
||||
groupid: number;
|
||||
action: string;
|
||||
entryid: number; // Negative for offline entries.
|
||||
fields: AddonModDataSubfieldData[];
|
||||
timemodified: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to handle Offline data.
|
||||
|
@ -87,7 +102,8 @@ export class AddonModDataOfflineProvider {
|
|||
};
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider,
|
||||
private fileProvider: CoreFileProvider, private fileUploaderProvider: CoreFileUploaderProvider) {
|
||||
private fileProvider: CoreFileProvider, private fileUploaderProvider: CoreFileUploaderProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
this.logger = logger.getInstance('AddonModDataOfflineProvider');
|
||||
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||
}
|
||||
|
@ -175,10 +191,10 @@ export class AddonModDataOfflineProvider {
|
|||
/**
|
||||
* Get all the stored entry data from all the databases.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with entries.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<AddonModDataOfflineAction[]>} Promise resolved with entries.
|
||||
*/
|
||||
getAllEntries(siteId?: string): Promise<any> {
|
||||
getAllEntries(siteId?: string): Promise<AddonModDataOfflineAction[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getAllRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE);
|
||||
}).then((entries) => {
|
||||
|
@ -187,15 +203,15 @@ export class AddonModDataOfflineProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all the stored entry data from a certain database.
|
||||
* Get all the stored entry actions from a certain database, sorted by modification time.
|
||||
*
|
||||
* @param {number} dataId Database ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with entries.
|
||||
* @param {number} dataId Database ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<AddonModDataOfflineAction[]>} Promise resolved with entries.
|
||||
*/
|
||||
getDatabaseEntries(dataId: number, siteId?: string): Promise<any> {
|
||||
getDatabaseEntries(dataId: number, siteId?: string): Promise<AddonModDataOfflineAction[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId});
|
||||
return site.getDb().getRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId}, 'timemodified');
|
||||
}).then((entries) => {
|
||||
return entries.map(this.parseRecord.bind(this));
|
||||
});
|
||||
|
@ -208,9 +224,9 @@ export class AddonModDataOfflineProvider {
|
|||
* @param {number} entryId Database entry Id.
|
||||
* @param {string} action Action to be done
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with entry.
|
||||
* @return {Promise<AddonModDataOfflineAction>} Promise resolved with entry.
|
||||
*/
|
||||
getEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise<any> {
|
||||
getEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise<AddonModDataOfflineAction> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecord(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId,
|
||||
action: action});
|
||||
|
@ -225,9 +241,9 @@ export class AddonModDataOfflineProvider {
|
|||
* @param {number} dataId Database ID.
|
||||
* @param {number} entryId Database entry Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with entry actions.
|
||||
* @return {Promise<AddonModDataOfflineAction[]>} Promise resolved with entry actions.
|
||||
*/
|
||||
getEntryActions(dataId: number, entryId: number, siteId?: string): Promise<any> {
|
||||
getEntryActions(dataId: number, entryId: number, siteId?: string): Promise<AddonModDataOfflineAction[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId});
|
||||
}).then((entries) => {
|
||||
|
@ -243,11 +259,10 @@ export class AddonModDataOfflineProvider {
|
|||
* @return {Promise<any>} Promise resolved with boolean: true if has offline answers, false otherwise.
|
||||
*/
|
||||
hasOfflineData(dataId: number, siteId?: string): Promise<any> {
|
||||
return this.getDatabaseEntries(dataId, siteId).then((entries) => {
|
||||
return !!entries.length;
|
||||
}).catch(() => {
|
||||
// No offline data found, return false.
|
||||
return false;
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.utils.promiseWorks(
|
||||
site.getDb().recordExists(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -286,10 +301,10 @@ export class AddonModDataOfflineProvider {
|
|||
/**
|
||||
* Parse "fields" of an offline record.
|
||||
*
|
||||
* @param {any} record Record object
|
||||
* @return {any} Record object with columns parsed.
|
||||
* @param {any} record Record object
|
||||
* @return {AddonModDataOfflineAction} Record object with columns parsed.
|
||||
*/
|
||||
protected parseRecord(record: any): any {
|
||||
protected parseRecord(record: any): AddonModDataOfflineAction {
|
||||
record.fields = this.textUtils.parseJSON(record.fields);
|
||||
|
||||
return record;
|
||||
|
@ -308,8 +323,8 @@ export class AddonModDataOfflineProvider {
|
|||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if stored, rejected if failure.
|
||||
*/
|
||||
saveEntry(dataId: number, entryId: number, action: string, courseId: number, groupId?: number, fields?: any[],
|
||||
timemodified?: number, siteId?: string): Promise<any> {
|
||||
saveEntry(dataId: number, entryId: number, action: string, courseId: number, groupId?: number,
|
||||
fields?: AddonModDataSubfieldData[], timemodified?: number, siteId?: string): Promise<any> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
timemodified = timemodified || new Date().getTime();
|
||||
|
|
|
@ -25,7 +25,7 @@ import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
|||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
||||
import { CoreRatingProvider } from '@core/rating/providers/rating';
|
||||
import { AddonModDataProvider } from './data';
|
||||
import { AddonModDataProvider, AddonModDataEntry } from './data';
|
||||
import { AddonModDataSyncProvider } from './sync';
|
||||
import { AddonModDataHelperProvider } from './helper';
|
||||
|
||||
|
@ -57,10 +57,10 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
* @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.
|
||||
* @return {Promise<any>} All unique entries.
|
||||
* @return {Promise<AddonModDataEntry[]>} All unique entries.
|
||||
*/
|
||||
protected getAllUniqueEntries(dataId: number, groups: any[], forceCache: boolean = false, ignoreCache: boolean = false,
|
||||
siteId?: string): Promise<any> {
|
||||
siteId?: string): Promise<AddonModDataEntry[]> {
|
||||
const promises = groups.map((group) => {
|
||||
return this.dataProvider.fetchAllEntries(dataId, group.id, undefined, undefined, undefined, forceCache, ignoreCache,
|
||||
siteId);
|
||||
|
@ -139,14 +139,14 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
/**
|
||||
* Returns the file contained in the entries.
|
||||
*
|
||||
* @param {any[]} entries List of entries to get files from.
|
||||
* @return {any[]} List of files.
|
||||
* @param {AddonModDataEntry[]} entries List of entries to get files from.
|
||||
* @return {any[]} List of files.
|
||||
*/
|
||||
protected getEntriesFiles(entries: any[]): any[] {
|
||||
protected getEntriesFiles(entries: AddonModDataEntry[]): any[] {
|
||||
let files = [];
|
||||
|
||||
entries.forEach((entry) => {
|
||||
entry.contents.forEach((content) => {
|
||||
this.utils.objectToArray(entry.contents).forEach((content) => {
|
||||
files = files.concat(content.files);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CoreAppProvider } from '@providers/app';
|
|||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { AddonModDataOfflineProvider } from './offline';
|
||||
import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline';
|
||||
import { AddonModDataProvider } from './data';
|
||||
import { AddonModDataHelperProvider } from './helper';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
|
@ -174,7 +174,7 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider {
|
|||
// No offline data found, return empty object.
|
||||
return [];
|
||||
});
|
||||
}).then((offlineActions) => {
|
||||
}).then((offlineActions: AddonModDataOfflineAction[]) => {
|
||||
if (!offlineActions.length) {
|
||||
// Nothing to sync.
|
||||
return;
|
||||
|
@ -226,35 +226,41 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider {
|
|||
/**
|
||||
* Synchronize an entry.
|
||||
*
|
||||
* @param {any} data Database.
|
||||
* @param {any} entryActions Entry actions.
|
||||
* @param {any} result Object with the result of the sync.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if success, rejected otherwise.
|
||||
* @param {any} data Database.
|
||||
* @param {AddonModDataOfflineAction[]} entryActions Entry actions.
|
||||
* @param {any} result Object with the result of the sync.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if success, rejected otherwise.
|
||||
*/
|
||||
protected syncEntry(data: any, entryActions: any[], result: any, siteId?: string): Promise<any> {
|
||||
protected syncEntry(data: any, entryActions: AddonModDataOfflineAction[], result: any, siteId?: string): Promise<any> {
|
||||
let discardError,
|
||||
timePromise,
|
||||
entryId = 0,
|
||||
entryId = entryActions[0].entryid,
|
||||
offlineId,
|
||||
deleted = false;
|
||||
|
||||
const promises = [];
|
||||
|
||||
// Sort entries by timemodified.
|
||||
entryActions = entryActions.sort((a: any, b: any) => a.timemodified - b.timemodified);
|
||||
|
||||
entryId = entryActions[0].entryid;
|
||||
const editAction = entryActions.find((action) => action.action == 'add' || action.action == 'edit');
|
||||
const approveAction = entryActions.find((action) => action.action == 'approve' || action.action == 'disapprove');
|
||||
const deleteAction = entryActions.find((action) => action.action == 'delete');
|
||||
|
||||
if (entryId > 0) {
|
||||
timePromise = this.dataProvider.getEntry(data.id, entryId, false, siteId).then((entry) => {
|
||||
timePromise = this.dataProvider.getEntry(data.id, entryId, true, siteId).then((entry) => {
|
||||
return entry.entry.timemodified;
|
||||
}).catch(() => {
|
||||
return -1;
|
||||
}).catch((error) => {
|
||||
if (error && this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means the entry has been deleted.
|
||||
return Promise.resolve(-1);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
} else if (editAction) {
|
||||
// New entry.
|
||||
offlineId = entryId;
|
||||
timePromise = Promise.resolve(0);
|
||||
} else {
|
||||
// New entry but the add action is missing, discard.
|
||||
timePromise = Promise.resolve(-1);
|
||||
}
|
||||
|
||||
return timePromise.then((timemodified) => {
|
||||
|
@ -266,58 +272,11 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider {
|
|||
return this.dataOffline.deleteAllEntryActions(data.id, entryId, siteId);
|
||||
}
|
||||
|
||||
entryActions.forEach((action) => {
|
||||
let actionPromise;
|
||||
const proms = [];
|
||||
|
||||
entryId = action.entryid > 0 ? action.entryid : entryId;
|
||||
|
||||
if (action.fields) {
|
||||
action.fields.forEach((field) => {
|
||||
// Upload Files if asked.
|
||||
const value = this.textUtils.parseJSON(field.value);
|
||||
if (value.online || value.offline) {
|
||||
let files = value.online || [];
|
||||
const fileProm = value.offline ? this.dataHelper.getStoredFiles(action.dataid, entryId, field.fieldid) :
|
||||
Promise.resolve([]);
|
||||
|
||||
proms.push(fileProm.then((offlineFiles) => {
|
||||
files = files.concat(offlineFiles);
|
||||
|
||||
return this.dataHelper.uploadOrStoreFiles(action.dataid, 0, entryId, field.fieldid, files, false,
|
||||
siteId).then((filesResult) => {
|
||||
field.value = JSON.stringify(filesResult);
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
actionPromise = Promise.all(proms).then(() => {
|
||||
// Perform the action.
|
||||
switch (action.action) {
|
||||
case 'add':
|
||||
return this.dataProvider.addEntryOnline(action.dataid, action.fields, data.groupid, siteId)
|
||||
.then((result) => {
|
||||
entryId = result.newentryid;
|
||||
});
|
||||
case 'edit':
|
||||
return this.dataProvider.editEntryOnline(entryId, action.fields, siteId);
|
||||
case 'approve':
|
||||
return this.dataProvider.approveEntryOnline(entryId, true, siteId);
|
||||
case 'disapprove':
|
||||
return this.dataProvider.approveEntryOnline(entryId, false, siteId);
|
||||
case 'delete':
|
||||
return this.dataProvider.deleteEntryOnline(entryId, siteId).then(() => {
|
||||
deleted = true;
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
promises.push(actionPromise.catch((error) => {
|
||||
if (error && error.wserror) {
|
||||
if (deleteAction) {
|
||||
return this.dataProvider.deleteEntryOnline(entryId, siteId).then(() => {
|
||||
deleted = true;
|
||||
}).catch((error) => {
|
||||
if (error && this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means it cannot be performed. Discard.
|
||||
discardError = this.textUtils.getErrorMessageFromError(error);
|
||||
} else {
|
||||
|
@ -328,11 +287,79 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider {
|
|||
// Delete the offline data.
|
||||
result.updated = true;
|
||||
|
||||
return this.dataOffline.deleteEntry(action.dataid, action.entryid, action.action, siteId);
|
||||
}));
|
||||
});
|
||||
return this.dataOffline.deleteAllEntryActions(deleteAction.dataid, deleteAction.entryid, siteId);
|
||||
});
|
||||
}
|
||||
|
||||
let editPromise;
|
||||
|
||||
if (editAction) {
|
||||
editPromise = Promise.all(editAction.fields.map((field) => {
|
||||
// Upload Files if asked.
|
||||
const value = this.textUtils.parseJSON(field.value);
|
||||
if (value.online || value.offline) {
|
||||
let files = value.online || [];
|
||||
const fileProm = value.offline ?
|
||||
this.dataHelper.getStoredFiles(editAction.dataid, entryId, field.fieldid) :
|
||||
Promise.resolve([]);
|
||||
|
||||
return fileProm.then((offlineFiles) => {
|
||||
files = files.concat(offlineFiles);
|
||||
|
||||
return this.dataHelper.uploadOrStoreFiles(editAction.dataid, 0, entryId, field.fieldid, files,
|
||||
false, siteId).then((filesResult) => {
|
||||
field.value = JSON.stringify(filesResult);
|
||||
});
|
||||
});
|
||||
}
|
||||
})).then(() => {
|
||||
if (editAction.action == 'add') {
|
||||
return this.dataProvider.addEntryOnline(editAction.dataid, editAction.fields, editAction.groupid, siteId)
|
||||
.then((result) => {
|
||||
entryId = result.newentryid;
|
||||
});
|
||||
} else {
|
||||
return this.dataProvider.editEntryOnline(entryId, editAction.fields, siteId);
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error && this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means it cannot be performed. Discard.
|
||||
discardError = this.textUtils.getErrorMessageFromError(error);
|
||||
} else {
|
||||
// Couldn't connect to server, reject.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}).then(() => {
|
||||
// Delete the offline data.
|
||||
result.updated = true;
|
||||
|
||||
return this.dataOffline.deleteEntry(editAction.dataid, editAction.entryid, editAction.action, siteId);
|
||||
});
|
||||
} else {
|
||||
editPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
if (approveAction) {
|
||||
editPromise = editPromise.then(() => {
|
||||
return this.dataProvider.approveEntryOnline(entryId, approveAction.action == 'approve', siteId);
|
||||
}).catch((error) => {
|
||||
if (error && this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means it cannot be performed. Discard.
|
||||
discardError = this.textUtils.getErrorMessageFromError(error);
|
||||
} else {
|
||||
// Couldn't connect to server, reject.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}).then(() => {
|
||||
// Delete the offline data.
|
||||
result.updated = true;
|
||||
|
||||
return this.dataOffline.deleteEntry(approveAction.dataid, approveAction.entryid, approveAction.action, siteId);
|
||||
});
|
||||
}
|
||||
|
||||
return editPromise;
|
||||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
if (discardError) {
|
||||
// Submission was discarded, add a warning.
|
||||
|
|
Loading…
Reference in New Issue