commit
1f1912bf98
|
@ -507,11 +507,13 @@
|
|||
"addon.mod_data.foundrecords": "data",
|
||||
"addon.mod_data.gettinglocation": "local_moodlemobileapp",
|
||||
"addon.mod_data.latlongboth": "data",
|
||||
"addon.mod_data.locationnotenabled": "local_moodlemobileapp",
|
||||
"addon.mod_data.locationpermissiondenied": "local_moodlemobileapp",
|
||||
"addon.mod_data.menuchoose": "data",
|
||||
"addon.mod_data.modulenameplural": "data",
|
||||
"addon.mod_data.more": "data",
|
||||
"addon.mod_data.mylocation": "local_moodlemobileapp",
|
||||
"addon.mod_data.noaccess": "data",
|
||||
"addon.mod_data.nomatch": "data",
|
||||
"addon.mod_data.norecords": "data",
|
||||
"addon.mod_data.notapproved": "data",
|
||||
|
|
|
@ -180,29 +180,34 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
* @param showErrors If show errors to the user of hide them.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||
protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||
let canAdd = false,
|
||||
canSearch = false;
|
||||
|
||||
return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => {
|
||||
this.data = data;
|
||||
this.hasComments = data.comments;
|
||||
this.data = await this.dataProvider.getDatabase(this.courseId, this.module.id);
|
||||
this.hasComments = this.data.comments;
|
||||
|
||||
this.description = data.intro || data.description;
|
||||
this.dataRetrieved.emit(data);
|
||||
this.description = this.data.intro || this.data.description;
|
||||
this.dataRetrieved.emit(this.data);
|
||||
|
||||
if (sync) {
|
||||
try {
|
||||
// Try to synchronize the data.
|
||||
return this.syncActivity(showErrors).catch(() => {
|
||||
await this.syncActivity(showErrors);
|
||||
} catch (error) {
|
||||
// Ignore errors.
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
return this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id});
|
||||
}).then((accessData) => {
|
||||
this.access = accessData;
|
||||
}
|
||||
|
||||
if (!accessData.timeavailable) {
|
||||
this.groupInfo = await this.groupsProvider.getActivityGroupInfo(this.data.coursemodule);
|
||||
this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, this.groupInfo);
|
||||
|
||||
this.access = await this.dataProvider.getDatabaseAccessInformation(this.data.id, {
|
||||
cmId: this.module.id,
|
||||
groupId: this.selectedGroup || undefined
|
||||
});
|
||||
|
||||
if (!this.access.timeavailable) {
|
||||
const time = this.timeUtils.timestamp();
|
||||
|
||||
this.timeAvailableFrom = this.data.timeavailablefrom && time < this.data.timeavailablefrom ?
|
||||
|
@ -214,35 +219,28 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
|
||||
this.isEmpty = true;
|
||||
this.groupInfo = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
canSearch = true;
|
||||
canAdd = accessData.canaddentry;
|
||||
|
||||
return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => {
|
||||
this.groupInfo = groupInfo;
|
||||
this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo);
|
||||
});
|
||||
}).then(() => {
|
||||
return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}).then((fields) => {
|
||||
if (fields.length == 0) {
|
||||
canSearch = false;
|
||||
canAdd = false;
|
||||
canAdd = this.access.canaddentry;
|
||||
}
|
||||
|
||||
const fields = await this.dataProvider.getFields(this.data.id, {cmId: this.module.id});
|
||||
this.search.advanced = [];
|
||||
|
||||
this.fields = this.utils.arrayToObject(fields, 'id');
|
||||
this.fieldsArray = this.utils.objectToArray(this.fields);
|
||||
if (this.fieldsArray.length == 0) {
|
||||
canSearch = false;
|
||||
canAdd = false;
|
||||
}
|
||||
|
||||
return this.fetchEntriesData();
|
||||
});
|
||||
}).finally(() => {
|
||||
try {
|
||||
await this.fetchEntriesData();
|
||||
} finally {
|
||||
this.canAdd = canAdd;
|
||||
this.canSearch = canSearch;
|
||||
this.fillContextMenu(refresh);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,13 +250,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
*/
|
||||
protected fetchEntriesData(): Promise<any> {
|
||||
|
||||
return this.dataProvider.getDatabaseAccessInformation(this.data.id, {
|
||||
groupId: this.selectedGroup,
|
||||
cmId: this.module.id,
|
||||
}).then((accessData) => {
|
||||
// Update values for current group.
|
||||
this.access.canaddentry = accessData.canaddentry;
|
||||
|
||||
const search = this.search.searching && !this.search.searchingAdvanced ? this.search.text : undefined;
|
||||
const advSearch = this.search.searching && this.search.searchingAdvanced ? this.search.advanced : undefined;
|
||||
|
||||
|
@ -269,7 +260,6 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
sort: Number(this.search.sortBy),
|
||||
order: this.search.sortDirection,
|
||||
page: this.search.page,
|
||||
});
|
||||
}).then((entries) => {
|
||||
const numEntries = entries.entries.length;
|
||||
const numOfflineEntries = entries.offlineEntries.length;
|
||||
|
@ -390,18 +380,29 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
* @param groupId Group ID.
|
||||
* @return Resolved when new group is selected or rejected if not.
|
||||
*/
|
||||
setGroup(groupId: number): Promise<any> {
|
||||
async setGroup(groupId: number): Promise<void> {
|
||||
this.selectedGroup = groupId;
|
||||
this.search.page = 0;
|
||||
|
||||
return this.fetchEntriesData().then(() => {
|
||||
// Only update canAdd if there's any field, otheerwise, canAdd will remain false.
|
||||
if (this.fieldsArray.length > 0) {
|
||||
// Update values for current group.
|
||||
this.access = await this.dataProvider.getDatabaseAccessInformation(this.data.id, {
|
||||
groupId: this.selectedGroup,
|
||||
cmId: this.module.id,
|
||||
});
|
||||
|
||||
this.canAdd = this.access.canaddentry;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.fetchEntriesData();
|
||||
|
||||
// Log activity view for coherence with Moodle web.
|
||||
return this.logView();
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
} catch (error) {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"modulenameplural": "Databases",
|
||||
"more": "More",
|
||||
"mylocation": "My location",
|
||||
"noaccess": "You do not have access to this page",
|
||||
"nomatch": "No matching entries found!",
|
||||
"norecords": "No entries in database",
|
||||
"notapproved": "Entry is not approved yet.",
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<div class="addon-data-contents addon-data-entries-{{data.id}}" *ngIf="data">
|
||||
<core-style [css]="data.csstemplate" prefix=".addon-data-entries-{{data.id}}"></core-style>
|
||||
<div class="addon-data-contents {{cssClass}}" *ngIf="data">
|
||||
<core-style [css]="data.csstemplate" prefix=".{{cssClass}}"></core-style>
|
||||
|
||||
<form (ngSubmit)="save($event)" [formGroup]="editForm" #editFormEl>
|
||||
<core-compile-html [text]="editFormRender" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
|
||||
|
|
|
@ -52,6 +52,8 @@ export class AddonModDataEditPage {
|
|||
protected siteId: string;
|
||||
protected offline: boolean;
|
||||
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
||||
protected initialSelectedGroup = null;
|
||||
protected isEditing = false;
|
||||
|
||||
title = '';
|
||||
component = AddonModDataProvider.COMPONENT;
|
||||
|
@ -75,7 +77,10 @@ export class AddonModDataEditPage {
|
|||
this.module = params.get('module') || {};
|
||||
this.entryId = params.get('entryId') || null;
|
||||
this.courseId = params.get('courseId');
|
||||
this.selectedGroup = params.get('group') || 0;
|
||||
this.selectedGroup = this.entryId ? null : (params.get('group') || 0);
|
||||
|
||||
// If entryId is lower than 0 or null, it is a new entry or an offline entry.
|
||||
this.isEditing = this.entryId && this.entryId > 0;
|
||||
|
||||
this.siteId = sitesProvider.getCurrentSiteId();
|
||||
|
||||
|
@ -88,7 +93,7 @@ export class AddonModDataEditPage {
|
|||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.fetchEntryData();
|
||||
this.fetchEntryData(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +108,8 @@ export class AddonModDataEditPage {
|
|||
|
||||
const inputData = this.editForm.value;
|
||||
|
||||
const changed = await this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id, this.entry.contents);
|
||||
let changed = await this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id, this.entry.contents);
|
||||
changed = changed || (!this.isEditing && this.initialSelectedGroup != this.selectedGroup);
|
||||
|
||||
if (changed) {
|
||||
// Show confirmation if some data has been modified.
|
||||
|
@ -120,38 +126,78 @@ export class AddonModDataEditPage {
|
|||
/**
|
||||
* Fetch the entry data.
|
||||
*
|
||||
* @param [refresh] To refresh all downloaded data.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
protected fetchEntryData(): Promise<any> {
|
||||
return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => {
|
||||
this.title = data.name || this.title;
|
||||
this.data = data;
|
||||
this.cssClass = 'addon-data-entries-' + data.id;
|
||||
protected async fetchEntryData(refresh: boolean = false): Promise<void> {
|
||||
try {
|
||||
this.data = await this.dataProvider.getDatabase(this.courseId, this.module.id);
|
||||
this.title = this.data.name || this.title;
|
||||
this.cssClass = 'addon-data-entries-' + this.data.id;
|
||||
|
||||
return this.dataProvider.getDatabaseAccessInformation(data.id, {cmId: this.module.id});
|
||||
}).then((accessData) => {
|
||||
if (this.entryId) {
|
||||
return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => {
|
||||
this.groupInfo = groupInfo;
|
||||
this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo);
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
return this.dataProvider.getFields(this.data.id, {cmId: this.module.id});
|
||||
}).then((fieldsData) => {
|
||||
this.fieldsArray = fieldsData;
|
||||
this.fields = this.utils.arrayToObject(fieldsData, 'id');
|
||||
this.fieldsArray = await this.dataProvider.getFields(this.data.id, {cmId: this.module.id});
|
||||
this.fields = this.utils.arrayToObject(this.fieldsArray, 'id');
|
||||
|
||||
const entry = await this.dataHelper.fetchEntry(this.data, this.fieldsArray, this.entryId);
|
||||
|
||||
return this.dataHelper.fetchEntry(this.data, fieldsData, this.entryId);
|
||||
}).then((entry) => {
|
||||
this.entry = entry.entry;
|
||||
|
||||
this.editFormRender = this.displayEditFields();
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
// Load correct group.
|
||||
this.selectedGroup = this.selectedGroup == null ? this.entry.groupid : this.selectedGroup;
|
||||
|
||||
// Check permissions when adding a new entry or offline entry.
|
||||
if (!this.isEditing) {
|
||||
let haveAccess = false;
|
||||
|
||||
if (refresh) {
|
||||
this.groupInfo = await this.groupsProvider.getActivityGroupInfo(this.data.coursemodule);
|
||||
this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, this.groupInfo);
|
||||
this.initialSelectedGroup = this.selectedGroup;
|
||||
}
|
||||
|
||||
if (this.groupInfo.groups.length > 0) {
|
||||
if (refresh) {
|
||||
const canAddGroup = {};
|
||||
|
||||
await Promise.all(this.groupInfo.groups.map(async (group) => {
|
||||
const accessData = await this.dataProvider.getDatabaseAccessInformation(this.data.id, {
|
||||
cmId: this.module.id, groupId: group.id});
|
||||
|
||||
canAddGroup[group.id] = accessData.canaddentry;
|
||||
}));
|
||||
|
||||
this.groupInfo.groups = this.groupInfo.groups.filter((group) => {
|
||||
return !!canAddGroup[group.id];
|
||||
});
|
||||
|
||||
haveAccess = canAddGroup[this.selectedGroup];
|
||||
} else {
|
||||
// Groups already filtered, so it have access.
|
||||
haveAccess = true;
|
||||
}
|
||||
} else {
|
||||
const accessData = await this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id});
|
||||
haveAccess = accessData.canaddentry;
|
||||
}
|
||||
|
||||
if (!haveAccess) {
|
||||
// You shall not pass, go back.
|
||||
this.domUtils.showErrorModal('addon.mod_data.noaccess', true);
|
||||
|
||||
// Go back to entry list.
|
||||
this.forceLeave = true;
|
||||
this.navCtrl.pop();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.editFormRender = this.displayEditFields();
|
||||
} catch (message) {
|
||||
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +206,7 @@ export class AddonModDataEditPage {
|
|||
* @param e Event.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
save(e: Event): Promise<any> {
|
||||
save(e: Event): Promise<void> {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -169,6 +215,7 @@ export class AddonModDataEditPage {
|
|||
return this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id,
|
||||
this.entry.contents).then((changed) => {
|
||||
|
||||
changed = changed || (!this.isEditing && this.initialSelectedGroup != this.selectedGroup);
|
||||
if (!changed) {
|
||||
if (this.entryId) {
|
||||
return this.returnToEntryList();
|
||||
|
@ -196,7 +243,7 @@ export class AddonModDataEditPage {
|
|||
return Promise.reject(e);
|
||||
}).then((editData) => {
|
||||
if (editData.length > 0) {
|
||||
if (this.entryId) {
|
||||
if (this.isEditing) {
|
||||
return this.dataProvider.editEntry(this.data.id, this.entryId, this.courseId, editData, this.fields,
|
||||
undefined, this.offline);
|
||||
}
|
||||
|
@ -213,20 +260,20 @@ export class AddonModDataEditPage {
|
|||
}
|
||||
|
||||
// This is done if entry is updated when editing or creating if not.
|
||||
if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) {
|
||||
if ((this.isEditing && result.updated) || (!this.isEditing && result.newentryid)) {
|
||||
|
||||
this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.siteId);
|
||||
|
||||
if (result.sent) {
|
||||
this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'data' });
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
|
||||
this.entryId = this.entryId || result.newentryid;
|
||||
if (result.sent) {
|
||||
this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'data' });
|
||||
|
||||
if (this.isEditing) {
|
||||
promises.push(this.dataProvider.invalidateEntryData(this.data.id, this.entryId, this.siteId));
|
||||
}
|
||||
promises.push(this.dataProvider.invalidateEntriesData(this.data.id, this.siteId));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED,
|
||||
|
@ -264,7 +311,7 @@ export class AddonModDataEditPage {
|
|||
* @param groupId Group identifier to set.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
setGroup(groupId: number): Promise<any> {
|
||||
setGroup(groupId: number): Promise<void> {
|
||||
this.selectedGroup = groupId;
|
||||
this.loaded = false;
|
||||
|
||||
|
@ -322,7 +369,7 @@ export class AddonModDataEditPage {
|
|||
*
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
protected returnToEntryList(): Promise<any> {
|
||||
protected returnToEntryList(): Promise<void> {
|
||||
const inputData = this.editForm.value;
|
||||
|
||||
return this.dataHelper.getEditTmpFiles(inputData, this.fieldsArray, this.data.id,
|
||||
|
|
|
@ -117,46 +117,51 @@ export class AddonModDataProvider {
|
|||
* @param forceOffline Force editing entry in offline.
|
||||
* @return Promise resolved when the action is done.
|
||||
*/
|
||||
addEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], groupId: number = 0,
|
||||
async 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.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.dataOffline.saveEntry(dataId, entryId, 'add', courseId, groupId, contents, undefined, siteId)
|
||||
.then((entry) => {
|
||||
const storeOffline = async (): Promise<any> => {
|
||||
const entry = await this.dataOffline.saveEntry(dataId, entryId, 'add', courseId, groupId, contents, undefined, siteId);
|
||||
|
||||
return {
|
||||
// Return provissional entry Id.
|
||||
newentryid: entry,
|
||||
sent: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Checks to store offline.
|
||||
if (!this.appProvider.isOnline() || forceOffline) {
|
||||
const notifications = this.checkFields(fields, contents);
|
||||
if (notifications) {
|
||||
return Promise.resolve({
|
||||
fieldnotifications: notifications
|
||||
});
|
||||
return { fieldnotifications: notifications };
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unnecessary not synced actions.
|
||||
await this.deleteEntryOfflineAction(dataId, entryId, 'add', siteId);
|
||||
|
||||
// App is offline, store the action.
|
||||
if (!this.appProvider.isOnline() || forceOffline) {
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.addEntryOnline(dataId, contents, groupId, siteId).then((result) => {
|
||||
try {
|
||||
const result = await this.addEntryOnline(dataId, contents, groupId, siteId);
|
||||
result.sent = true;
|
||||
|
||||
return result;
|
||||
}).catch((error) => {
|
||||
} catch (error) {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Couldn't connect to server, store in offline.
|
||||
return storeOffline();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -193,48 +198,49 @@ export class AddonModDataProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the action is done.
|
||||
*/
|
||||
approveEntry(dataId: number, entryId: number, approve: boolean, courseId: number, siteId?: string): Promise<any> {
|
||||
async approveEntry(dataId: number, entryId: number, approve: boolean, courseId: number, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a data to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
const storeOffline = async (): Promise<any> => {
|
||||
const action = approve ? 'approve' : 'disapprove';
|
||||
|
||||
return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId)
|
||||
.then(() => {
|
||||
await this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId);
|
||||
|
||||
return {
|
||||
sent: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Get if the opposite action is not synced.
|
||||
const oppositeAction = approve ? 'disapprove' : 'approve';
|
||||
|
||||
return this.dataOffline.getEntry(dataId, entryId, oppositeAction, siteId).then(() => {
|
||||
// Found. Just delete the action.
|
||||
return this.dataOffline.deleteEntry(dataId, entryId, oppositeAction, siteId);
|
||||
}).catch(() => {
|
||||
const found = await this.deleteEntryOfflineAction(dataId, entryId, oppositeAction, siteId);
|
||||
if (found) {
|
||||
// Offline action has been found and deleted. Stop here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.appProvider.isOnline()) {
|
||||
// App is offline, store the action.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.approveEntryOnline(entryId, approve, siteId).then(() => {
|
||||
try {
|
||||
await this.approveEntryOnline(entryId, approve, siteId);
|
||||
|
||||
return {
|
||||
sent: true,
|
||||
};
|
||||
}).catch((error) => {
|
||||
} catch (error) {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Couldn't connect to server, store in offline.
|
||||
return storeOffline();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,38 +304,22 @@ export class AddonModDataProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the action is done.
|
||||
*/
|
||||
deleteEntry(dataId: number, entryId: number, courseId: number, siteId?: string): Promise<any> {
|
||||
async deleteEntry(dataId: number, entryId: number, courseId: number, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a data to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId)
|
||||
.then(() => {
|
||||
const storeOffline = async (): Promise<any> => {
|
||||
await this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId);
|
||||
|
||||
return {
|
||||
sent: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
let justAdded = false;
|
||||
|
||||
// Check if the opposite action is not synced and just delete it.
|
||||
return this.dataOffline.getEntryActions(dataId, entryId, siteId).then((entries) => {
|
||||
if (entries && entries.length) {
|
||||
// Found. Delete other actions first.
|
||||
const proms = entries.map((entry) => {
|
||||
if (entry.action == 'add') {
|
||||
justAdded = true;
|
||||
}
|
||||
|
||||
return this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId);
|
||||
});
|
||||
|
||||
return Promise.all(proms);
|
||||
}
|
||||
}).then(() => {
|
||||
if (justAdded) {
|
||||
// The field was added offline, delete and stop.
|
||||
const addedOffline = await this.deleteEntryOfflineAction(dataId, entryId, 'add', siteId);
|
||||
if (addedOffline) {
|
||||
// Offline add action found and deleted. Stop here.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -338,20 +328,21 @@ export class AddonModDataProvider {
|
|||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.deleteEntryOnline(entryId, siteId).then(() => {
|
||||
try {
|
||||
await this.deleteEntryOnline(entryId, siteId);
|
||||
|
||||
return {
|
||||
sent: true,
|
||||
};
|
||||
}).catch((error) => {
|
||||
} catch (error) {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Couldn't connect to server, store in offline.
|
||||
return storeOffline();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,6 +362,29 @@ export class AddonModDataProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete entry offline action.
|
||||
*
|
||||
* @param dataId Database ID.
|
||||
* @param entryId Entry ID.
|
||||
* @param action Action name to delete.
|
||||
* @param siteId Site ID.
|
||||
* @return Resolved with true if the action has been found and deleted.
|
||||
*/
|
||||
protected async deleteEntryOfflineAction(dataId: number, entryId: number, action: string, siteId: string): Promise<boolean> {
|
||||
// Get other not not synced actions.
|
||||
try {
|
||||
await this.dataOffline.getEntry(dataId, entryId, action, siteId);
|
||||
|
||||
await this.dataOffline.deleteEntry(dataId, entryId, action, siteId);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
// Not found.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing entry.
|
||||
*
|
||||
|
@ -383,82 +397,50 @@ export class AddonModDataProvider {
|
|||
* @param forceOffline Force editing entry in offline.
|
||||
* @return Promise resolved when the action is done.
|
||||
*/
|
||||
editEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], fields: any, siteId?: string,
|
||||
forceOffline: boolean = false): Promise<any> {
|
||||
async editEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], fields: any,
|
||||
siteId?: string, forceOffline: boolean = false): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a data to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId)
|
||||
.then(() => {
|
||||
const storeOffline = async (): Promise<any> => {
|
||||
await this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId);
|
||||
|
||||
return {
|
||||
updated: true,
|
||||
sent: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
let justAdded = false,
|
||||
groupId;
|
||||
|
||||
if (!this.appProvider.isOnline() || forceOffline) {
|
||||
const notifications = this.checkFields(fields, contents);
|
||||
if (notifications) {
|
||||
return Promise.resolve({
|
||||
fieldnotifications: notifications
|
||||
});
|
||||
return { fieldnotifications: notifications };
|
||||
}
|
||||
}
|
||||
|
||||
// Get other not not synced actions.
|
||||
return this.dataOffline.getEntryActions(dataId, entryId, siteId).then((entries) => {
|
||||
if (entries && entries.length) {
|
||||
// Found. Delete add and edit actions first.
|
||||
const proms = [];
|
||||
entries.forEach((entry) => {
|
||||
if (entry.action == 'add') {
|
||||
justAdded = true;
|
||||
groupId = entry.groupid;
|
||||
proms.push(this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId));
|
||||
} else if (entry.action == 'edit') {
|
||||
proms.push(this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(proms);
|
||||
}
|
||||
}).then(() => {
|
||||
if (justAdded) {
|
||||
// The field was added offline, add again and stop.
|
||||
return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline)
|
||||
.then((result) => {
|
||||
result.updated = true;
|
||||
result.sent = true;
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
// Remove unnecessary not synced actions.
|
||||
await this.deleteEntryOfflineAction(dataId, entryId, 'edit', siteId);
|
||||
|
||||
if (!this.appProvider.isOnline() || forceOffline) {
|
||||
// App is offline, store the action.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.editEntryOnline(entryId, contents, siteId).then((result) => {
|
||||
try {
|
||||
const result = await this.editEntryOnline(entryId, contents, siteId);
|
||||
result.sent = true;
|
||||
|
||||
return result;
|
||||
}).catch((error) => {
|
||||
} catch (error) {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Couldn't connect to server, store in offline.
|
||||
return storeOffline();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing entry. It does not cache calls. It will fail if offline or cannot connect.
|
||||
|
|
|
@ -18,7 +18,6 @@ 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';
|
||||
|
@ -35,12 +34,19 @@ import { CoreRatingOfflineProvider } from '@core/rating/providers/offline';
|
|||
@Injectable()
|
||||
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 eventsProvider: CoreEventsProvider, private utils: CoreUtilsProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider,
|
||||
private ratingOffline: CoreRatingOfflineProvider) {}
|
||||
constructor(
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected dataProvider: AddonModDataProvider,
|
||||
protected translate: TranslateService,
|
||||
protected fieldsDelegate: AddonModDataFieldsDelegate,
|
||||
protected dataOffline: AddonModDataOfflineProvider,
|
||||
protected fileUploaderProvider: CoreFileUploaderProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected courseProvider: CoreCourseProvider,
|
||||
protected ratingOffline: CoreRatingOfflineProvider
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns the record with the offline actions applied.
|
||||
|
@ -632,35 +638,44 @@ export class AddonModDataHelperProvider {
|
|||
* @param courseId Course ID. It not defined, it will be fetched.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
*/
|
||||
showDeleteEntryModal(dataId: number, entryId: number, courseId?: number, siteId?: string): void {
|
||||
async showDeleteEntryModal(dataId: number, entryId: number, courseId?: number, siteId?: string): Promise<void> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
this.domUtils.showDeleteConfirm('addon.mod_data.confirmdeleterecord').then(() => {
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
let modal;
|
||||
try {
|
||||
await this.domUtils.showDeleteConfirm('addon.mod_data.confirmdeleterecord');
|
||||
|
||||
return this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => {
|
||||
return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId);
|
||||
}).catch((message) => {
|
||||
modal = this.domUtils.showModalLoading();
|
||||
|
||||
try {
|
||||
if (entryId > 0) {
|
||||
courseId = await this.getActivityCourseIdIfNotSet(dataId, courseId, siteId);
|
||||
}
|
||||
|
||||
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(() => {
|
||||
modal && modal.dismiss();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.dataProvider.invalidateEntryData(dataId, entryId, siteId);
|
||||
await this.dataProvider.invalidateEntriesData(dataId, siteId);
|
||||
} catch (error) {
|
||||
// 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(() => {
|
||||
} catch (error) {
|
||||
// Ignore error, it was already displayed.
|
||||
});
|
||||
}
|
||||
|
||||
modal && modal.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -513,6 +513,7 @@
|
|||
"addon.mod_data.modulenameplural": "Databases",
|
||||
"addon.mod_data.more": "More",
|
||||
"addon.mod_data.mylocation": "My location",
|
||||
"addon.mod_data.noaccess": "You do not have access to this page",
|
||||
"addon.mod_data.nomatch": "No matching entries found!",
|
||||
"addon.mod_data.norecords": "No entries in database",
|
||||
"addon.mod_data.notapproved": "Entry is not approved yet.",
|
||||
|
|
Loading…
Reference in New Issue