diff --git a/scripts/langindex.json b/scripts/langindex.json index 7c5324cd9..174a16271 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1578,6 +1578,7 @@ "core.dismiss": "local_moodlemobileapp", "core.displayoptions": "atto_media", "core.done": "survey", + "core.dontshowagain": "local_moodlemobileapp", "core.download": "moodle", "core.downloaded": "local_moodlemobileapp", "core.downloadfile": "moodle", @@ -2257,6 +2258,7 @@ "core.viewembeddedcontent": "local_moodlemobileapp", "core.viewprofile": "moodle", "core.warningofflinedatadeleted": "local_moodlemobileapp", + "core.warnopeninbrowser": "local_moodlemobileapp", "core.whatisyourage": "moodle", "core.wheredoyoulive": "moodle", "core.whoissiteadmin": "local_moodlemobileapp", diff --git a/src/addons/badges/pages/issued-badge/issued-badge.html b/src/addons/badges/pages/issued-badge/issued-badge.html index cb131d0e3..0a80b561d 100644 --- a/src/addons/badges/pages/issued-badge/issued-badge.html +++ b/src/addons/badges/pages/issued-badge/issued-badge.html @@ -53,7 +53,9 @@

{{ 'addon.badges.contact' | translate}}

-

{{ badge.issuercontact }}

+

+ {{ badge.issuercontact }} +

@@ -97,7 +99,9 @@

{{ 'addon.badges.imageauthoremail' | translate}}

-

{{ badge.imageauthoremail }}

+

+ {{ badge.imageauthoremail }} +

@@ -165,7 +169,8 @@

{{ 'addon.badges.issueremail' | translate}}

- + {{ badge.endorsement.issueremail }}

diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index 304f53c41..ef0cd7d5d 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index 05b8999d7..60a41827e 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index b52b2562a..7f8633b5d 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -5,7 +5,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index bd6f3fa27..124241f10 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index e3b1799f4..4763418b3 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -2,7 +2,7 @@ > + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.html b/src/addons/mod/forum/components/post-options-menu/post-options-menu.html index 81d22d13e..e1fe50448 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.html +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.html @@ -17,7 +17,8 @@

{{ 'core.numwords' | translate: {'$a': wordCount} }}

- +

{{ 'core.openinbrowser' | translate }}

diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index 8b19195d1..02b9e0405 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -11,7 +11,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index f88b40b88..90158fa9a 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -10,7 +10,7 @@ iconAction="stats-chart"> + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index 0397e0b29..05c5b3cff 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -5,7 +5,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index 52ea9eed8..d8948ccbb 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 39bfcafab..c2756f087 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/lti/services/lti-helper.ts b/src/addons/mod/lti/services/lti-helper.ts index ae3d56fda..1dbf19578 100644 --- a/src/addons/mod/lti/services/lti-helper.ts +++ b/src/addons/mod/lti/services/lti-helper.ts @@ -74,7 +74,7 @@ export class AddonModLtiHelperProvider { module, }; - return site.openInBrowserWithAutoLogin(module.url!); + return site.openInBrowserWithAutoLogin(module.url || ''); } // Open in app. diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index 08b1cdd38..cce3a3faf 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index 40a465a46..dabe2a7a9 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> @@ -218,7 +218,8 @@ + unsupportedRules.length || behaviourSupported === false)" expand="block" [href]="externalUrl" core-link + [showBrowserWarning]="false"> {{ 'core.openinbrowser' | translate }} diff --git a/src/addons/mod/quiz/pages/player/player.html b/src/addons/mod/quiz/pages/player/player.html index 31fb22135..bd8d40246 100644 --- a/src/addons/mod/quiz/pages/player/player.html +++ b/src/addons/mod/quiz/pages/player/player.html @@ -165,7 +165,8 @@
- + {{ 'core.openinbrowser' | translate }} @@ -182,7 +183,7 @@ {{ 'addon.mod_quiz.errorparsequestions' | translate }} - + {{ 'core.openinbrowser' | translate }} diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index 411279e8b..fc449c7cf 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> @@ -185,7 +185,7 @@

{{ errorMessage | translate }}

- + {{ 'core.openinbrowser' | translate }} diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index e17441410..80b973f89 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 7c13f1b46..722bdfafd 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index 169ed21b4..d06955e61 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -2,7 +2,7 @@ + [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"> diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index d47607361..ef11aedf7 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -355,7 +355,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity * * @param task Task to be done. */ - runTask(task: AddonModWorkshopPhaseTaskData): void { + async runTask(task: AddonModWorkshopPhaseTaskData): Promise { if (task.code == 'submit') { this.gotoSubmit(); } else if (task.link) { diff --git a/src/addons/mod/workshop/components/phase/phase.ts b/src/addons/mod/workshop/components/phase/phase.ts index cc5285630..fb0030a84 100644 --- a/src/addons/mod/workshop/components/phase/phase.ts +++ b/src/addons/mod/workshop/components/phase/phase.ts @@ -57,7 +57,7 @@ export class AddonModWorkshopPhaseInfoComponent implements OnInit { * * @param task Task to be done. */ - runTask(task: AddonModWorkshopPhaseTaskData): void { + async runTask(task: AddonModWorkshopPhaseTaskData): Promise { if (task.code == 'submit') { // This will close the modal and go to the submit. ModalController.dismiss(true); diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts index 1a4457874..c935f1ba6 100644 --- a/src/addons/notifications/services/handlers/push-click.ts +++ b/src/addons/notifications/services/handlers/push-click.ts @@ -104,17 +104,20 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi case 'browser': return CoreUtils.openInBrowser(url); - default: - if (CoreContentLinksHelper.handleLink(url, undefined, undefined, true)) { + default: { + const treated = await CoreContentLinksHelper.handleLink(url, undefined, undefined, true); + if (treated) { // Link treated, stop. return; } + } } } // No appurl or cannot be handled by the app. Try to handle the contexturl now. if (notification.contexturl) { - if (CoreContentLinksHelper.handleLink(notification.contexturl)) { + const treated = await CoreContentLinksHelper.handleLink(notification.contexturl); + if (treated) { // Link treated, stop. return; } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 038345fff..23cf1f6c5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -125,7 +125,7 @@ export class AppComponent implements OnInit, AfterViewInit { const urlScheme = CoreUrlUtils.getUrlProtocol(url); if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') { // Open in browser should launch the right app if found and do nothing if not found. - CoreUtils.openInBrowser(url); + CoreUtils.openInBrowser(url, { showBrowserWarning: false }); // At this point the InAppBrowser is showing a "Webpage not available" error message. // Try to navigate to last loaded URL so this error message isn't found. diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts index e11c0d587..b22ad2110 100644 --- a/src/core/classes/site.ts +++ b/src/core/classes/site.ts @@ -32,7 +32,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrlUtils, CoreUrlParams } from '@services/utils/url'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils, CoreUtilsOpenInBrowserOptions, PromiseDefer } from '@services/utils/utils'; import { CoreConstants } from '@/core/constants'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreError } from '@classes/errors/error'; @@ -1381,10 +1381,15 @@ export class CoreSite { * * @param url The URL to open. * @param alertMessage If defined, an alert will be shown before opening the browser. + * @param options Other options. * @return Promise resolved when done, rejected otherwise. */ - async openInBrowserWithAutoLogin(url: string, alertMessage?: string): Promise { - await this.openWithAutoLogin(false, url, undefined, alertMessage); + async openInBrowserWithAutoLogin( + url: string, + alertMessage?: string, + options: CoreUtilsOpenInBrowserOptions = {}, + ): Promise { + await this.openWithAutoLogin(false, url, options, alertMessage); } /** @@ -1392,10 +1397,15 @@ export class CoreSite { * * @param url The URL to open. * @param alertMessage If defined, an alert will be shown before opening the browser. + * @param options Other options. * @return Promise resolved when done, rejected otherwise. */ - async openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string): Promise { - await this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage); + async openInBrowserWithAutoLoginIfSameSite( + url: string, + alertMessage?: string, + options: CoreUtilsOpenInBrowserOptions = {}, + ): Promise { + await this.openWithAutoLoginIfSameSite(false, url, options, alertMessage); } /** @@ -1442,7 +1452,7 @@ export class CoreSite { async openWithAutoLogin( inApp: boolean, url: string, - options?: InAppBrowserOptions, + options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {}, alertMessage?: string, ): Promise { // Get the URL to open. @@ -1458,13 +1468,14 @@ export class CoreSite { ); await alert.onDidDismiss(); + options.showBrowserWarning = false; // A warning already shown, no need to show another. } // Open the URL. if (inApp) { return CoreUtils.openInApp(url, options); } else { - return CoreUtils.openInBrowser(url); + return CoreUtils.openInBrowser(url, options); } } @@ -1480,7 +1491,7 @@ export class CoreSite { async openWithAutoLoginIfSameSite( inApp: boolean, url: string, - options?: InAppBrowserOptions, + options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {}, alertMessage?: string, ): Promise { if (this.containsUrl(url)) { @@ -1489,7 +1500,7 @@ export class CoreSite { if (inApp) { return Promise.resolve(CoreUtils.openInApp(url, options)); } else { - CoreUtils.openInBrowser(url); + CoreUtils.openInBrowser(url, options); } } } diff --git a/src/core/components/context-menu/context-menu-item.ts b/src/core/components/context-menu/context-menu-item.ts index 4c104231d..fd1c2fc33 100644 --- a/src/core/components/context-menu/context-menu-item.ts +++ b/src/core/components/context-menu/context-menu-item.ts @@ -51,6 +51,7 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange @Input() badgeClass?: number; // A class to set in the badge. @Input() badgeA11yText?: string; // Description for the badge, if needed. @Input() hidden?: boolean; // Whether the item should be hidden. + @Input() showBrowserWarning = true; // Whether to show a warning before opening browser (for links). Defaults to true. @Output() action?: EventEmitter<() => void>; // Will emit an event when the item clicked. @Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked. diff --git a/src/core/components/context-menu/core-context-menu-popover.html b/src/core/components/context-menu/core-context-menu-popover.html index b52365b81..1c23ddabf 100644 --- a/src/core/components/context-menu/core-context-menu-popover.html +++ b/src/core/components/context-menu/core-context-menu-popover.html @@ -4,7 +4,8 @@ + [detail]="(item.href && !item.iconAction) || null" role="menuitem" [button]="(item.href && !item.iconAction)" + [showBrowserWarning]="item.showBrowserWarning"> diff --git a/src/core/constants.ts b/src/core/constants.ts index 37a8b370f..6df64e291 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -63,6 +63,7 @@ export class CoreConstants { static readonly SETTINGS_ZOOM_LEVEL = 'CoreSettingsZoomLevel'; static readonly SETTINGS_COLOR_SCHEME = 'CoreSettingsColorScheme'; static readonly SETTINGS_ANALYTICS_ENABLED = 'CoreSettingsAnalyticsEnabled'; + static readonly SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN = 'CoreSettingsDontShowExtLinkWarn'; // WS constants. static readonly WS_TIMEOUT = 30000; // Timeout when not in WiFi. diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 472b28a83..9687868e2 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -43,6 +43,7 @@ export class CoreLinkDirective implements OnInit { "no" -> Never auto-login. "check" -> Auto-login only if it points to the current site. Default value. */ @Input() autoLogin = 'check'; + @Input() showBrowserWarning = true; // Whether to show a warning before opening browser. Defaults to true. protected element: Element; @@ -199,33 +200,35 @@ export class CoreLinkDirective implements OnInit { if (this.inApp) { CoreUtils.openInApp(href); } else { - CoreUtils.openInBrowser(href); + CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); } return; } + const currentSite = CoreSites.getRequiredCurrentSite(); + // Check if URL does not have any protocol, so it's a relative URL. if (!CoreUrlUtils.isAbsoluteURL(href)) { // Add the site URL at the begining. if (href.charAt(0) == '/') { - href = CoreSites.getCurrentSite()!.getURL() + href; + href = currentSite.getURL() + href; } else { - href = CoreSites.getCurrentSite()!.getURL() + '/' + href; + href = currentSite.getURL() + '/' + href; } } if (this.autoLogin == 'yes') { if (this.inApp) { - await CoreSites.getCurrentSite()!.openInAppWithAutoLogin(href); + await currentSite.openInAppWithAutoLogin(href); } else { - await CoreSites.getCurrentSite()!.openInBrowserWithAutoLogin(href); + await currentSite.openInBrowserWithAutoLogin(href, undefined, { showBrowserWarning: this.showBrowserWarning }); } } else if (this.autoLogin == 'no') { if (this.inApp) { CoreUtils.openInApp(href); } else { - CoreUtils.openInBrowser(href); + CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning }); } } else { // Priority order is: core-link inApp attribute > forceOpenLinksIn setting > data-open-in HTML attribute. @@ -239,9 +242,13 @@ export class CoreLinkDirective implements OnInit { } if (openInApp) { - await CoreSites.getCurrentSite()!.openInAppWithAutoLoginIfSameSite(href); + await currentSite.openInAppWithAutoLoginIfSameSite(href); } else { - await CoreSites.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(href); + await currentSite.openInBrowserWithAutoLoginIfSameSite( + href, + undefined, + { showBrowserWarning: this.showBrowserWarning }, + ); } } } diff --git a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html index d9d9a6b4e..10fb79074 100644 --- a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html +++ b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html @@ -17,7 +17,7 @@

{{ 'core.course.useactivityonbrowser' | translate }}

- + {{ 'core.openinbrowser' | translate }} diff --git a/src/core/features/course/pages/preview/preview.html b/src/core/features/course/pages/preview/preview.html index 7a0e9317e..a69893d67 100644 --- a/src/core/features/course/pages/preview/preview.html +++ b/src/core/features/course/pages/preview/preview.html @@ -123,7 +123,8 @@

{{ 'core.course.contents' | translate }}

- +

{{ 'core.openinbrowser' | translate }}

diff --git a/src/core/features/courses/services/handlers/course-link.ts b/src/core/features/courses/services/handlers/course-link.ts index 9498eb7c2..018e11d6c 100644 --- a/src/core/features/courses/services/handlers/course-link.ts +++ b/src/core/features/courses/services/handlers/course-link.ts @@ -222,7 +222,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler try { await CoreDomUtils.showConfirm(body); - CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(url); + CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(url, undefined, { showBrowserWarning: false }); } catch { // User cancelled. }; diff --git a/src/core/features/login/pages/email-signup/email-signup.html b/src/core/features/login/pages/email-signup/email-signup.html index 928bdbce5..e630caba2 100644 --- a/src/core/features/login/pages/email-signup/email-signup.html +++ b/src/core/features/login/pages/email-signup/email-signup.html @@ -28,7 +28,7 @@ {{ 'core.login.signuprequiredfieldnotsupported' | translate }}
- + {{ 'core.openinbrowser' | translate }} diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts index 6e3c673ce..02812cc92 100644 --- a/src/core/features/login/pages/email-signup/email-signup.ts +++ b/src/core/features/login/pages/email-signup/email-signup.ts @@ -384,7 +384,10 @@ export class CoreLoginEmailSignupPage implements OnInit { * Show contact information on site (we have to display again the age verification form). */ showContactOnSite(): void { - CoreUtils.openInBrowser(CoreTextUtils.concatenatePaths(this.siteUrl, '/login/verify_age_location.php')); + CoreUtils.openInBrowser( + CoreTextUtils.concatenatePaths(this.siteUrl, '/login/verify_age_location.php'), + { showBrowserWarning: false }, + ); } /** diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 8f9780fc3..d41c0b742 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -660,7 +660,7 @@ export class CoreLoginHelperProvider { }); // Always open it in browser because the user might have the session stored in there. - CoreUtils.openInBrowser(loginUrl); + CoreUtils.openInBrowser(loginUrl, { showBrowserWarning: false }); CoreApp.closeApp(); return true; @@ -692,7 +692,7 @@ export class CoreLoginHelperProvider { closebuttoncaption: Translate.instant('core.login.cancel'), }); } else { - CoreUtils.openInBrowser(loginUrl); + CoreUtils.openInBrowser(loginUrl, { showBrowserWarning: false }); CoreApp.closeApp(); } } diff --git a/src/core/features/settings/pages/licenses/licenses.html b/src/core/features/settings/pages/licenses/licenses.html index 174475380..25ae4a491 100644 --- a/src/core/features/settings/pages/licenses/licenses.html +++ b/src/core/features/settings/pages/licenses/licenses.html @@ -30,8 +30,8 @@

{{ 'core.settings.license' | translate }}{{ 'core.labelsep' | translate }} {{ license.licenses }}

{{ license.url }}

-

{{ license.email }}

+

{{ license.email }}

{{ 'core.view' | translate }} diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index 2e21f945a..fb08137f1 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -17,7 +17,8 @@

{{ 'core.user.email' | translate }}

-

+

{{ user.email }}

@@ -25,7 +26,7 @@

{{ 'core.user.phone1' | translate}}

-

+

{{ user.phone1 }}

@@ -33,7 +34,7 @@

{{ 'core.user.phone2' | translate}}

-

+

{{ user.phone2 }}

@@ -41,7 +42,7 @@

{{ 'core.user.address' | translate}}

-

+

{{ formattedAddress }}

diff --git a/src/core/features/user/services/handlers/profile-mail.ts b/src/core/features/user/services/handlers/profile-mail.ts index d2c243643..0ba0d60e9 100644 --- a/src/core/features/user/services/handlers/profile-mail.ts +++ b/src/core/features/user/services/handlers/profile-mail.ts @@ -56,7 +56,7 @@ export class CoreUserProfileMailHandlerService implements CoreUserProfileHandler event.preventDefault(); event.stopPropagation(); - CoreUtils.openInBrowser('mailto:' + user.email); + CoreUtils.openInBrowser('mailto:' + user.email, { showBrowserWarning: false }); }, }; } diff --git a/src/core/lang.json b/src/core/lang.json index 1da713f7f..dff3a1052 100644 --- a/src/core/lang.json +++ b/src/core/lang.json @@ -86,6 +86,7 @@ "dismiss": "Dismiss", "displayoptions": "Display options", "done": "Done", + "dontshowagain": "Don't show again.", "download": "Download", "downloaded": "Downloaded", "downloadfile": "Download file", @@ -327,6 +328,7 @@ "viewembeddedcontent": "View embedded content", "viewprofile": "View profile", "warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}", + "warnopeninbrowser": "

You are about to leave the app to open the following URL in your device's browser. Do you want to continue?

\n

{{url}}

", "whatisyourage": "What is your age?", "wheredoyoulive": "In which country do you live?", "whoissiteadmin": "\"Site Administrators\" are the people who manage the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.", diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 291d3ca5e..fd3d4c85b 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -731,7 +731,7 @@ export class CoreSitesProvider { Translate.instant('core.updaterequired'), Translate.instant('core.download'), Translate.instant(siteId ? 'core.mainmenu.logout' : 'core.cancel'), - ).then(() => CoreUtils.openInBrowser(downloadUrl)).catch(() => { + ).then(() => CoreUtils.openInBrowser(downloadUrl, { showBrowserWarning: false })).catch(() => { // Do nothing. }); } else { diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 17048560b..093e072a9 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -1440,7 +1440,7 @@ export class CoreDomUtilsProvider { buttons.push({ text: Translate.instant('core.download'), handler: (): void => { - CoreUtils.openInBrowser(link); + CoreUtils.openInBrowser(link, { showBrowserWarning: false }); }, }); } @@ -1465,28 +1465,32 @@ export class CoreDomUtilsProvider { * * @param message Modal message. * @param header Modal header. - * @param placeholder Placeholder of the input element. By default, "Password". + * @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 options More options to pass to the alert. - * @return Promise resolved with the input data if the user clicks OK, rejected if cancels. + * @return Promise resolved with the input data (true for checkbox/radio) if the user clicks OK, rejected if cancels. */ showPrompt( message: string, header?: string, - placeholder?: string, + placeholderOrLabel?: string, type: TextFieldTypes | 'checkbox' | 'radio' | 'textarea' = 'password', ): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any return new Promise((resolve, reject) => { - placeholder = placeholder ?? Translate.instant('core.login.password'); + placeholderOrLabel = placeholderOrLabel ?? Translate.instant('core.login.password'); + const isCheckbox = type === 'checkbox'; + const isRadio = type === 'radio'; const options: AlertOptions = { header, message, inputs: [ { name: 'promptinput', - placeholder: placeholder, + placeholder: placeholderOrLabel, + label: placeholderOrLabel, type, + value: (isCheckbox || isRadio) ? true : undefined, }, ], buttons: [ @@ -1500,7 +1504,13 @@ export class CoreDomUtilsProvider { { text: Translate.instant('core.ok'), handler: (data) => { - resolve(data.promptinput); + if (isCheckbox) { + resolve(data[0]); + } else if (isRadio) { + resolve(data); + } else { + resolve(data.promptinput); + } }, }, ], diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index b654777f7..6d06cbf3d 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -32,6 +32,7 @@ import { CoreViewerQRScannerComponent } from '@features/viewer/components/qr-sca import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreFileEntry } from '@services/file-helper'; import { CoreConstants } from '@/core/constants'; +import { CoreWindow } from '@singletons/window'; type TreeNode = T & { children: TreeNode[] }; @@ -1047,8 +1048,17 @@ export class CoreUtilsProvider { * Do not use for files, refer to {@link openFile}. * * @param url The URL to open. + * @param options Options. */ - openInBrowser(url: string): void { + async openInBrowser(url: string, options: CoreUtilsOpenInBrowserOptions = {}): Promise { + if (options.showBrowserWarning || options.showBrowserWarning === undefined) { + try { + await CoreWindow.confirmOpenBrowserIfNeeded(url); + } catch (error) { + return; // Cancelled, stop. + } + } + window.open(url, '_system'); } @@ -1727,6 +1737,13 @@ export type CoreUtilsOpenFileOptions = { iOSOpenFileAction?: OpenFileAction; // Action to do when opening a file. }; +/** + * Options for opening in browser. + */ +export type CoreUtilsOpenInBrowserOptions = { + showBrowserWarning?: boolean; // Whether to display a warning before opening in browser. Defaults to true. +}; + /** * Possible default picker actions. */ diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index 2eee46bf7..5463c7521 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -14,11 +14,15 @@ import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { NavController } from '@ionic/angular'; +import { CoreConfig } from '@services/config'; import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrlUtils } from '@services/utils/url'; import { CoreUtils } from '@services/utils/utils'; +import { Translate } from '@singletons'; +import { CoreConstants } from '../constants'; /** * Options for the open function. @@ -44,6 +48,31 @@ export class CoreWindow { // Nothing to do. } + /** + * Show a confirm before opening a link in browser, unless the user previously marked to not show again. + * + * @param url URL to open. + * @return Promise resolved if confirmed, rejected if rejected. + */ + static async confirmOpenBrowserIfNeeded(url: string): Promise { + // Check if the user decided not to see the warning. + const dontShowWarning = await CoreConfig.get(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 0); + if (dontShowWarning) { + return; + } + + const dontShowAgain = await CoreDomUtils.showPrompt( + Translate.instant('core.warnopeninbrowser', { url }), + undefined, + Translate.instant('core.dontshowagain'), + 'checkbox', + ); + + if (dontShowAgain) { + CoreConfig.set(CoreConstants.SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN, 1); + } + } + /** * "Safe" implementation of window.open. It will open the URL without overriding the app. * @@ -73,12 +102,12 @@ export class CoreWindow { } if (!treated) { - // Not opened in the app, open with browser. Check if we need to auto-login + // Not opened in the app, open with browser. Check if we need to auto-login. if (!CoreSites.isLoggedIn()) { // Not logged in, cannot auto-login. CoreUtils.openInBrowser(url); } else { - await CoreSites.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(url); + await CoreSites.getRequiredCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); } } }