From da799faf6e44e052a6d1f60353a0bb011890bae5 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 27 Nov 2018 03:39:04 +0100 Subject: [PATCH 1/3] MOBILE-2701 resource: Always show refresh for external files --- .../resource/providers/prefetch-handler.ts | 21 +++++++++++++++++++ src/providers/filepool.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/addon/mod/resource/providers/prefetch-handler.ts b/src/addon/mod/resource/providers/prefetch-handler.ts index 30b75932b..842687717 100644 --- a/src/addon/mod/resource/providers/prefetch-handler.ts +++ b/src/addon/mod/resource/providers/prefetch-handler.ts @@ -23,6 +23,7 @@ import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; import { AddonModResourceProvider } from './resource'; import { AddonModResourceHelperProvider } from './helper'; +import { CoreConstants } from '@core/constants'; /** * Handler to prefetch resources. @@ -41,6 +42,26 @@ export class AddonModResourcePrefetchHandler extends CoreCourseResourcePrefetchH super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } + /** + * Return the status to show based on current status. + * + * @param {any} module Module. + * @param {string} status The current status. + * @param {boolean} canCheck Whether the site allows checking for updates. + * @return {string} Status to display. + */ + determineStatus(module: any, status: string, canCheck: boolean): string { + if (status == CoreConstants.DOWNLOADED && module && module.contents) { + // If the first file is an external file, always display the module as outdated. + const mainFile = module.contents[0]; + if (mainFile && mainFile.isexternalfile) { + return CoreConstants.OUTDATED; + } + } + + return status; + } + /** * Download or prefetch the content. * diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index 3074fd91f..e01deba92 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -2501,7 +2501,7 @@ export class CoreFilepoolProvider { // File not in pool. }).then((entry: CoreFilepoolFileEntry) => { - if (entry && !this.isFileOutdated(entry, options.revision, options.timemodified)) { + if (entry && !options.isexternalfile && !this.isFileOutdated(entry, options.revision, options.timemodified)) { // We have the file, it is not stale, we can update links and remove from queue. this.logger.debug('Queued file already in store, ignoring...'); this.addFileLinks(siteId, fileId, links).catch(() => { From 146b0ec7d863b3bbe3352b23b95b8ae4c7f3f353 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 3 Dec 2018 15:54:07 +0100 Subject: [PATCH 2/3] MOBILE-2701 emulator: Handle errors when emulating file download --- src/core/emulator/providers/file-transfer.ts | 58 +++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/core/emulator/providers/file-transfer.ts b/src/core/emulator/providers/file-transfer.ts index 1ba92f175..cfef11512 100644 --- a/src/core/emulator/providers/file-transfer.ts +++ b/src/core/emulator/providers/file-transfer.ts @@ -130,7 +130,18 @@ export class FileTransferObjectMock extends FileTransferObject { xhr.onload = (): void => { // Finished dowloading the file. - let response = xhr.response; + let response = xhr.response || xhr.responseText; + + const status = Math.max(xhr.status === 1223 ? 204 : xhr.status, 0); + if (status < 200 || status >= 300) { + // Request failed. Try to get the error message. + this.parseResponse(response).then((response) => { + reject(new FileTransferErrorMock(-1, source, target, xhr.status, response || xhr.statusText, null)); + }); + + return; + } + if (!response) { reject(); } else { @@ -224,6 +235,51 @@ export class FileTransferObjectMock extends FileTransferObject { this.progressListener = listener; } + /** + * Same as Javascript's JSON.parse, but it will handle errors. + * + * @param {string} json JSON text. + * @return {any} JSON parsed as object or what it gets. + */ + protected parseJSON(json: string): any { + try { + return JSON.parse(json); + } catch (ex) { + // Error. + } + + return json; + } + + /** + * Parse a response, converting it into text and the into an object if needed. + * + * @param {any} response The response to parse. + * @return {Promise} Promise resolved with the parsed response. + */ + protected parseResponse(response: any): Promise { + return new Promise((resolve, reject): void => { + if (!response) { + resolve(''); + } else if (response.toString && response.toString() == '[object Blob]') { + // Convert the Blob into text. + const reader = new FileReader(); + reader.onloadend = (): void => { + resolve(reader.result); + }; + reader.readAsText(response); + + } else if (response.toString && response.toString() == '[object ArrayBuffer]') { + // Convert the ArrayBuffer into text. + resolve(String.fromCharCode.apply(null, new Uint8Array(response))); + } else { + resolve(response); + } + }).then((response: any) => { + return this.parseJSON(response); + }); + } + /** * Sends a file to a server. * From 85e57efcca9be6d23ecf8ad736abea2957f2b83e Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 3 Dec 2018 17:04:39 +0100 Subject: [PATCH 3/3] MOBILE-2701 resource: Don't allow downloading Nextcloud files --- src/addon/mod/resource/components/index/index.ts | 12 ++++++++++-- src/addon/mod/resource/providers/helper.ts | 12 +++++++++++- .../mod/resource/providers/prefetch-handler.ts | 14 ++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/addon/mod/resource/components/index/index.ts b/src/addon/mod/resource/components/index/index.ts index cd5b50e72..2304a319e 100644 --- a/src/addon/mod/resource/components/index/index.ts +++ b/src/addon/mod/resource/components/index/index.ts @@ -14,6 +14,7 @@ import { Component, Injector } from '@angular/core'; import { CoreAppProvider } from '@providers/app'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component'; import { AddonModResourceProvider } from '../../providers/resource'; @@ -37,7 +38,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource constructor(injector: Injector, private resourceProvider: AddonModResourceProvider, private courseProvider: CoreCourseProvider, private appProvider: CoreAppProvider, private prefetchHandler: AddonModResourcePrefetchHandler, - private resourceHelper: AddonModResourceHelperProvider) { + private resourceHelper: AddonModResourceHelperProvider, private sitesProvider: CoreSitesProvider) { super(injector); } @@ -142,6 +143,13 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource * Opens a file. */ open(): void { - this.resourceHelper.openModuleFile(this.module, this.courseId); + this.prefetchHandler.isDownloadable(this.module, this.courseId).then((downloadable) => { + if (downloadable) { + this.resourceHelper.openModuleFile(this.module, this.courseId); + } else { + // The resource cannot be downloaded, open the activity in browser. + return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url); + } + }); } } diff --git a/src/addon/mod/resource/providers/helper.ts b/src/addon/mod/resource/providers/helper.ts index 05c62f5aa..b6d0f3aa6 100644 --- a/src/addon/mod/resource/providers/helper.ts +++ b/src/addon/mod/resource/providers/helper.ts @@ -113,7 +113,7 @@ export class AddonModResourceHelperProvider { * @return {boolean} Whether the resource should be displayed embeded. */ isDisplayedEmbedded(module: any, display: number): boolean { - if (!module.contents.length || !this.fileProvider.isAvailable()) { + if (!module.contents.length || !this.fileProvider.isAvailable() || this.isNextcloudFile(module)) { return false; } @@ -139,6 +139,16 @@ export class AddonModResourceHelperProvider { return mimetype == 'text/html'; } + /** + * Check if the resource is a Nextcloud file. + * + * @param {any} module Module to check. + * @return {boolean} Whether it's a Nextcloud file. + */ + isNextcloudFile(module: any): boolean { + return module.contents && module.contents[0] && module.contents[0].repositorytype == 'nextcloud'; + } + /** * Opens a file of the resource activity. * diff --git a/src/addon/mod/resource/providers/prefetch-handler.ts b/src/addon/mod/resource/providers/prefetch-handler.ts index 842687717..7c05f8fbc 100644 --- a/src/addon/mod/resource/providers/prefetch-handler.ts +++ b/src/addon/mod/resource/providers/prefetch-handler.ts @@ -122,6 +122,20 @@ export class AddonModResourcePrefetchHandler extends CoreCourseResourcePrefetchH return Promise.all(promises); } + /** + * Check if a resource is downloadable. + * + * @param {any} module Module to check. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved with true if downloadable, resolved with false otherwise. + */ + isDownloadable(module: any, courseId: number): Promise { + // Don't allow downloading Nextcloud files for now. + return this.loadContents(module, courseId, false).then(() => { + return !this.resourceHelper.isNextcloudFile(module); + }); + } + /** * Whether or not the handler is enabled on a site level. *