diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts index e98d6abf0..3f2ba6c32 100644 --- a/src/addon/mod/assign/pages/edit/edit.ts +++ b/src/addon/mod/assign/pages/edit/edit.ts @@ -303,6 +303,9 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy { } return promise.then(() => { + // Clear temporary data from plugins. + return this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); + }).then(() => { // Submission saved, trigger event. const params = { assignmentId: this.assign.id, diff --git a/src/addon/mod/data/providers/offline.ts b/src/addon/mod/data/providers/offline.ts index 76ca2556d..df54530ff 100644 --- a/src/addon/mod/data/providers/offline.ts +++ b/src/addon/mod/data/providers/offline.ts @@ -17,6 +17,7 @@ import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreFileProvider } from '@providers/file'; +import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; /** * Service to handle Offline data. @@ -66,7 +67,7 @@ export class AddonModDataOfflineProvider { ]; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private fileProvider: CoreFileProvider) { + private fileProvider: CoreFileProvider, private fileUploaderProvider: CoreFileUploaderProvider) { this.logger = logger.getInstance('AddonModDataOfflineProvider'); this.sitesProvider.createTablesFromSchema(this.tablesSchema); } @@ -102,8 +103,52 @@ export class AddonModDataOfflineProvider { */ deleteEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, + return this.deleteEntryFiles(dataId, entryId, action, site.id).then(() => { + return site.getDb().deleteRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, action: action}); + }); + }); + } + + /** + * Delete entry offline files. + * + * @param {number} dataId Database ID. + * @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} Promise resolved if deleted, rejected if failure. + */ + protected deleteEntryFiles(dataId: number, entryId: number, action: string, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return this.getEntry(dataId, entryId, action, site.id).then((entry) => { + if (!entry.fields) { + return; + } + + const promises = []; + + entry.fields.forEach((field) => { + const value = this.textUtils.parseJSON(field.value); + if (!value.offline) { + return; + } + + const promise = this.getEntryFieldFolder(dataId, entryId, field.fieldid, site.id).then((folderPath) => { + return this.fileUploaderProvider.getStoredFiles(folderPath); + }).then((files) => { + return this.fileUploaderProvider.clearTmpFiles(files); + }).catch(() => { + // Files not found, ignore. + }); + + promises.push(promise); + }); + + return Promise.all(promises); + }).catch(() => { + // Entry not found, ignore. + }); }); } diff --git a/src/addon/mod/glossary/pages/edit/edit.ts b/src/addon/mod/glossary/pages/edit/edit.ts index f685feefd..8520727b6 100644 --- a/src/addon/mod/glossary/pages/edit/edit.ts +++ b/src/addon/mod/glossary/pages/edit/edit.ts @@ -232,6 +232,9 @@ export class AddonModGlossaryEditPage implements OnInit { attach, timecreated, undefined, this.entry, !this.attachments.length, !this.glossary.allowduplicatedentries); } }).then((entryId) => { + // Delete the local files from the tmp folder. + this.uploaderProvider.clearTmpFiles(this.attachments); + if (entryId) { // Data sent to server, delete stored files (if any). this.glossaryHelper.deleteStoredFiles(this.glossary.id, this.entry.concept, timecreated); diff --git a/src/addon/mod/glossary/providers/helper.ts b/src/addon/mod/glossary/providers/helper.ts index d4cf592b1..4ec569c47 100644 --- a/src/addon/mod/glossary/providers/helper.ts +++ b/src/addon/mod/glossary/providers/helper.ts @@ -29,7 +29,7 @@ export class AddonModGlossaryHelperProvider { private glossaryOffline: AddonModGlossaryOfflineProvider) {} /** - * Delete stored attachment files for a new discussion. + * Delete stored attachment files for a new entry. * * @param {number} glossaryId Glossary ID. * @param {string} entryName The name of the entry. diff --git a/src/addon/mod/workshop/providers/sync.ts b/src/addon/mod/workshop/providers/sync.ts index f6693960f..9b1fc1c02 100644 --- a/src/addon/mod/workshop/providers/sync.ts +++ b/src/addon/mod/workshop/providers/sync.ts @@ -353,7 +353,15 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { result.updated = true; return this.workshopOffline.deleteSubmissionAction(action.workshopid, action.submissionid, action.action, - siteId); + siteId).then(() => { + // Delete stored files. + if (action.action == 'add' || action.action == 'update') { + const editing = action.action == 'update'; + + return this.workshopHelper.deleteSubmissionStoredFiles(action.workshopid, + action.submissionid, editing, siteId); + } + }); }); }); }); @@ -433,7 +441,9 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { // Delete the offline data. result.updated = true; - return this.workshopOffline.deleteAssessment(workshop.id, assessmentId, siteId); + return this.workshopOffline.deleteAssessment(workshop.id, assessmentId, siteId).then(() => { + this.workshopHelper.deleteAssessmentStoredFiles(workshop.id, assessmentId, siteId); + }); }); }).then(() => { if (discardError) { diff --git a/src/core/fileuploader/providers/fileuploader.ts b/src/core/fileuploader/providers/fileuploader.ts index 166982d34..753b4bf21 100644 --- a/src/core/fileuploader/providers/fileuploader.ts +++ b/src/core/fileuploader/providers/fileuploader.ts @@ -486,7 +486,10 @@ export class CoreFileUploaderProvider { } /** - * Upload a file to a draft area. If the file is an online file it will be downloaded and then re-uploaded. + * Upload a file to a draft area and return the draft ID. + * + * If the file is an online file it will be downloaded and then re-uploaded. + * If the file is a local file it will not be deleted from the device after upload. * * @param {any} file Online file or local FileEntry. * @param {number} [itemId] Draft ID to use. Undefined or 0 to create a new draft ID. @@ -502,7 +505,9 @@ export class CoreFileUploaderProvider { let promise, fileName; - if (file.filename && !file.name) { + const isOnline = file.filename && !file.name; + + if (isOnline) { // It's an online file. We need to download it and re-upload it. fileName = file.filename; promise = this.filepoolProvider.downloadUrl(siteId, file.url || file.fileurl, false, component, componentId, @@ -517,7 +522,7 @@ export class CoreFileUploaderProvider { return promise.then((fileEntry) => { // Now upload the file. - const options = this.getFileUploadOptions(fileEntry.toURL(), fileName, fileEntry.type, true, 'draft', itemId); + const options = this.getFileUploadOptions(fileEntry.toURL(), fileName, fileEntry.type, isOnline, 'draft', itemId); return this.uploadFile(fileEntry.toURL(), options, undefined, siteId).then((result) => { return result.itemid; @@ -527,7 +532,9 @@ export class CoreFileUploaderProvider { /** * Given a list of files (either online files or local files), upload them to a draft area and return the draft ID. + * * Online files will be downloaded and then re-uploaded. + * Local files are not deleted from the device after upload. * If there are no files to upload it will return a fake draft ID (1). * * @param {any[]} files List of files.