From 30b26adbd6401625b6febd61d006459aa6e67ebd Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 11 May 2022 14:36:46 +0200 Subject: [PATCH 1/2] MOBILE-4054 core: Allow not displaying again open file warning --- src/core/components/local-file/local-file.ts | 2 +- src/core/directives/link.ts | 2 +- .../features/course/services/course-helper.ts | 2 +- src/core/services/file-helper.ts | 37 +++++++++++++++++-- src/core/services/utils/dom.ts | 36 +++++++++--------- src/core/services/utils/iframe.ts | 2 +- src/core/singletons/window.ts | 2 +- 7 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/core/components/local-file/local-file.ts b/src/core/components/local-file/local-file.ts index 568b657df..7931d0e45 100644 --- a/src/core/components/local-file/local-file.ts +++ b/src/core/components/local-file/local-file.ts @@ -127,7 +127,7 @@ export class CoreLocalFileComponent implements OnInit { if (!CoreFileHelper.isOpenableInApp(this.file)) { try { - await CoreFileHelper.showConfirmOpenUnsupportedFile(); + await CoreFileHelper.showConfirmOpenUnsupportedFile(false, this.file); } catch (error) { return; // Cancelled, stop. } diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 8d11234af..f2fa04d86 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -163,7 +163,7 @@ export class CoreLinkDirective implements OnInit { if (!CoreFileHelper.isOpenableInApp({ filename })) { try { - await CoreFileHelper.showConfirmOpenUnsupportedFile(); + await CoreFileHelper.showConfirmOpenUnsupportedFile(false, { filename }); } catch (error) { return; // Cancelled, stop. } diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index ea53b8900..f4f9703cd 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -721,7 +721,7 @@ export class CoreCourseHelperProvider { const mainFile = files[0]; if (!CoreFileHelper.isOpenableInApp(mainFile)) { - await CoreFileHelper.showConfirmOpenUnsupportedFile(); + await CoreFileHelper.showConfirmOpenUnsupportedFile(false, mainFile); } const site = await CoreSites.getSite(siteId); diff --git a/src/core/services/file-helper.ts b/src/core/services/file-helper.ts index cb5da9e8d..7817c4d7b 100644 --- a/src/core/services/file-helper.ts +++ b/src/core/services/file-helper.ts @@ -28,6 +28,8 @@ import { CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { makeSingleton, Translate } from '@singletons'; import { CoreNetworkError } from '@classes/errors/network-error'; +import { CoreConfig } from './config'; +import { CoreCanceledError } from '@classes/errors/cancelederror'; /** * Provider to provide some helper functions regarding files and packages. @@ -71,7 +73,7 @@ export class CoreFileHelperProvider { const timemodified = this.getFileTimemodified(file); if (!this.isOpenableInApp(file)) { - await this.showConfirmOpenUnsupportedFile(); + await this.showConfirmOpenUnsupportedFile(false, file); } let url = await this.downloadFileIfNeeded( @@ -413,13 +415,42 @@ export class CoreFileHelperProvider { * Show a confirm asking the user if we wants to open the file. * * @param onlyDownload Whether the user is only downloading the file, not opening it. + * @param file The file that will be opened. * @return Promise resolved if confirmed, rejected otherwise. */ - showConfirmOpenUnsupportedFile(onlyDownload?: boolean): Promise { + async showConfirmOpenUnsupportedFile(onlyDownload = false, file: {filename?: string; name?: string}): Promise { + file = file || {}; // Just in case some plugin doesn't pass it. This can be removed in the future, @since app 4.1. + + // Check if the user decided not to see the warning. + const regex = /(?:\.([^.]+))?$/; + const regexResult = regex.exec(file.filename || file.name || ''); + + const configKey = 'CoreFileUnsupportedWarningDisabled-' + (regexResult?.[1] ?? 'unknown'); + const dontShowWarning = await CoreConfig.get(configKey, 0); + if (dontShowWarning) { + return; + } + const message = Translate.instant('core.cannotopeninapp' + (onlyDownload ? 'download' : '')); const okButton = Translate.instant(onlyDownload ? 'core.downloadfile' : 'core.openfile'); - return CoreDomUtils.showConfirm(message, undefined, okButton, undefined, { cssClass: 'core-modal-force-on-top' }); + try { + const dontShowAgain = await CoreDomUtils.showPrompt( + message, + undefined, + Translate.instant('core.dontshowagain'), + 'checkbox', + { okText: okButton }, + { cssClass: 'core-modal-force-on-top' }, + ); + + if (dontShowAgain) { + CoreConfig.set(configKey, 1); + } + } catch { + // User canceled. + throw new CoreCanceledError(''); + } } /** diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 014196466..7861e147a 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -1490,7 +1490,8 @@ export class CoreDomUtilsProvider { * @param header Modal header. * @param placeholderOrLabel Placeholder (for textual/numeric inputs) or label (for radio/checkbox). By default, "Password". * @param type Type of the input element. By default, password. - * @param buttons Buttons. If not provided, OK and Cancel buttons will be displayed. + * @param buttons Buttons. If not provided or it's an object with texts, OK and Cancel buttons will be displayed. + * @para options Other alert options. * @return Promise resolved with the input data (true for checkbox/radio) if the user clicks OK, rejected if cancels. */ showPrompt( @@ -1498,7 +1499,8 @@ export class CoreDomUtilsProvider { header?: string, placeholderOrLabel?: string, type: TextFieldTypes | 'checkbox' | 'radio' | 'textarea' = 'password', - buttons?: PromptButton[], + buttons?: PromptButton[] | { okText?: string; cancelText?: string }, + options: AlertOptions = {}, ): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any return new Promise((resolve, reject) => { placeholderOrLabel = placeholderOrLabel ?? Translate.instant('core.login.password'); @@ -1517,21 +1519,19 @@ export class CoreDomUtilsProvider { } }; - const options: AlertOptions = { - header, - message, - inputs: [ - { - name: 'promptinput', - placeholder: placeholderOrLabel, - label: placeholderOrLabel, - type, - value: (isCheckbox || isRadio) ? true : undefined, - }, - ], - }; + options.header = header; + options.message = message; + options.inputs = [ + { + name: 'promptinput', + placeholder: placeholderOrLabel, + label: placeholderOrLabel, + type, + value: (isCheckbox || isRadio) ? true : undefined, + }, + ]; - if (buttons?.length) { + if (Array.isArray(buttons) && buttons.length) { options.buttons = buttons.map((button) => ({ ...button, handler: (data) => { @@ -1549,14 +1549,14 @@ export class CoreDomUtilsProvider { // Default buttons. options.buttons = [ { - text: Translate.instant('core.cancel'), + text: buttons && 'cancelText' in buttons ? buttons.cancelText : Translate.instant('core.cancel'), role: 'cancel', handler: () => { reject(); }, }, { - text: Translate.instant('core.ok'), + text: buttons && 'okText' in buttons ? buttons.okText : Translate.instant('core.ok'), handler: resolvePromise, }, ]; diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts index 5b46db3c9..f9a5ccf11 100644 --- a/src/core/services/utils/iframe.ts +++ b/src/core/services/utils/iframe.ts @@ -520,7 +520,7 @@ export class CoreIframeUtilsProvider { if (!CoreFileHelper.isOpenableInApp({ filename })) { try { - await CoreFileHelper.showConfirmOpenUnsupportedFile(); + await CoreFileHelper.showConfirmOpenUnsupportedFile(false, { filename }); } catch (error) { return; // Cancelled, stop. } diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index 95b94247c..4e5d6891c 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -91,7 +91,7 @@ export class CoreWindow { if (!CoreFileHelper.isOpenableInApp({ filename })) { try { - await CoreFileHelper.showConfirmOpenUnsupportedFile(); + await CoreFileHelper.showConfirmOpenUnsupportedFile(false, { filename }); } catch (error) { return; // Cancelled, stop. } From bbe787cd00347bf41659d18753aef49aba24698f Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 18 May 2022 14:27:29 +0200 Subject: [PATCH 2/2] MOBILE-4054 behat: Add tests for open files --- src/tests/behat/open_files.feature | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/tests/behat/open_files.feature diff --git a/src/tests/behat/open_files.feature b/src/tests/behat/open_files.feature new file mode 100644 index 000000000..0f12b02da --- /dev/null +++ b/src/tests/behat/open_files.feature @@ -0,0 +1,58 @@ +@app @javascript +Feature: It opens files properly. + + Background: + Given the following "users" exist: + | username | + | student1 | + And the following "courses" exist: + | fullname | shortname | + | Course 1 | C1 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + And the following "activities" exist: + | activity | name | intro | display | course | defaultfilename | + | resource | Test TXT | Test TXT description | 5 | C1 | A txt.txt | + | resource | Test RTF | Test RTF description | 5 | C1 | A rtf.rtf | + | resource | Test DOC | Test DOC description | 5 | C1 | A doc.doc | + And the following config values are set as admin: + | filetypeexclusionlist | rtf,doc | tool_mobile | + + Scenario: Open a file + Given I entered the resource activity "Test TXT" on course "Course 1" as "student1" in the app + When I press "Open" near "Last modified" in the app + Then the app should have opened a browser tab with url "^blob:(first)?" + + When I switch to the browser tab opened by the app + Then I should see "Test resource A txt.txt file" + + When I close the browser tab opened by the app + And I press the back button in the app + And I press "Test RTF" in the app + And I press "Open" near "Last modified" in the app + Then I should find "This file may not work as expected on this device" in the app + + When I press "Open file" in the app + Then the app should have opened a browser tab with url "^blob:(second)?" + + When I switch to the browser tab opened by the app + Then I should see "Test resource A rtf.rtf file" + + When I close the browser tab opened by the app + And I press "Open" near "Last modified" in the app + Then I should find "This file may not work as expected on this device" in the app + + When I select "Don't show again." in the app + And I press "Open file" in the app + Then the app should have opened a browser tab with url "^blob:(third)?" + + When I close the browser tab opened by the app + And I press "Open" near "Last modified" in the app + Then the app should have opened a browser tab with url "^blob:(fourth)?" + + When I close the browser tab opened by the app + And I press the back button in the app + And I press "Test DOC" in the app + And I press "Open" in the app + Then I should find "This file may not work as expected on this device" in the app