MOBILE-3726 core: Display warning modal before open browser

main
Dani Palou 2021-10-07 08:58:05 +02:00
parent be8a7b35b7
commit 84354cade7
50 changed files with 177 additions and 80 deletions

View File

@ -1578,6 +1578,7 @@
"core.dismiss": "local_moodlemobileapp", "core.dismiss": "local_moodlemobileapp",
"core.displayoptions": "atto_media", "core.displayoptions": "atto_media",
"core.done": "survey", "core.done": "survey",
"core.dontshowagain": "local_moodlemobileapp",
"core.download": "moodle", "core.download": "moodle",
"core.downloaded": "local_moodlemobileapp", "core.downloaded": "local_moodlemobileapp",
"core.downloadfile": "moodle", "core.downloadfile": "moodle",
@ -2257,6 +2258,7 @@
"core.viewembeddedcontent": "local_moodlemobileapp", "core.viewembeddedcontent": "local_moodlemobileapp",
"core.viewprofile": "moodle", "core.viewprofile": "moodle",
"core.warningofflinedatadeleted": "local_moodlemobileapp", "core.warningofflinedatadeleted": "local_moodlemobileapp",
"core.warnopeninbrowser": "local_moodlemobileapp",
"core.whatisyourage": "moodle", "core.whatisyourage": "moodle",
"core.wheredoyoulive": "moodle", "core.wheredoyoulive": "moodle",
"core.whoissiteadmin": "local_moodlemobileapp", "core.whoissiteadmin": "local_moodlemobileapp",

View File

@ -53,7 +53,9 @@
<ion-item class="ion-text-wrap" *ngIf="badge.issuercontact"> <ion-item class="ion-text-wrap" *ngIf="badge.issuercontact">
<ion-label> <ion-label>
<h2>{{ 'addon.badges.contact' | translate}}</h2> <h2>{{ 'addon.badges.contact' | translate}}</h2>
<p><a href="mailto:{{badge.issuercontact}}" core-link auto-login="no"> {{ badge.issuercontact }} </a></p> <p><a href="mailto:{{badge.issuercontact}}" core-link auto-login="no" [showBrowserWarning]="false">
{{ badge.issuercontact }}
</a></p>
</ion-label> </ion-label>
</ion-item> </ion-item>
</ion-item-group> </ion-item-group>
@ -97,7 +99,9 @@
<ion-item class="ion-text-wrap" *ngIf="badge.imageauthoremail"> <ion-item class="ion-text-wrap" *ngIf="badge.imageauthoremail">
<ion-label> <ion-label>
<h2>{{ 'addon.badges.imageauthoremail' | translate}}</h2> <h2>{{ 'addon.badges.imageauthoremail' | translate}}</h2>
<p><a href="mailto:{{badge.imageauthoremail}}" core-link auto-login="no"> {{ badge.imageauthoremail }} </a></p> <p><a href="mailto:{{badge.imageauthoremail}}" core-link auto-login="no" [showBrowserWarning]="false">
{{ badge.imageauthoremail }}
</a></p>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item class="ion-text-wrap" *ngIf="badge.imageauthorurl"> <ion-item class="ion-text-wrap" *ngIf="badge.imageauthorurl">
@ -165,7 +169,8 @@
<ion-label> <ion-label>
<h2>{{ 'addon.badges.issueremail' | translate}}</h2> <h2>{{ 'addon.badges.issueremail' | translate}}</h2>
<p> <p>
<a href="mailto:{{badge.endorsement.issueremail}}" core-link auto-login="no"> <a href="mailto:{{badge.endorsement.issueremail}}" core-link auto-login="no"
[showBrowserWarning]="false">
{{ badge.endorsement.issueremail }} {{ badge.endorsement.issueremail }}
</a> </a>
</p> </p>

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="assign && (description || (assign.introattachments && assign.introattachments.length))" <core-context-menu-item *ngIf="assign && (description || (assign.introattachments && assign.introattachments.length))"
[priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()"

View File

@ -353,7 +353,7 @@
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon> <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
<ion-label> <ion-label>
<p>{{ 'addon.mod_assign.cannotgradefromapp' | translate }}</p> <p>{{ 'addon.mod_assign.cannotgradefromapp' | translate }}</p>
<ion-button expand="block" *ngIf="gradeUrl" [href]="gradeUrl" core-link > <ion-button expand="block" *ngIf="gradeUrl" [href]="gradeUrl" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -5,7 +5,7 @@
</ion-button> </ion-button>
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"></core-context-menu-item> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"></core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item> (action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item>
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -5,7 +5,7 @@
</ion-button> </ion-button>
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt">> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -17,7 +17,8 @@
<p class="item-heading">{{ 'core.numwords' | translate: {'$a': wordCount} }}</p> <p class="item-heading">{{ 'core.numwords' | translate: {'$a': wordCount} }}</p>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item class="ion-text-wrap" [href]="url" *ngIf="url" core-link capture="false" button detail="false"> <ion-item class="ion-text-wrap" [href]="url" *ngIf="url" core-link capture="false" button detail="false"
[showBrowserWarning]="false">
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
<ion-label> <ion-label>
<p class="item-heading">{{ 'core.openinbrowser' | translate }}</p> <p class="item-heading">{{ 'core.openinbrowser' | translate }}</p>

View File

@ -11,7 +11,7 @@
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -10,7 +10,7 @@
iconAction="stats-chart"> iconAction="stats-chart">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -5,7 +5,7 @@
</ion-button> </ion-button>
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -74,7 +74,7 @@ export class AddonModLtiHelperProvider {
module, module,
}; };
return site.openInBrowserWithAutoLogin(module.url!); return site.openInBrowserWithAutoLogin(module.url || '');
} }
// Open in app. // Open in app.

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">
@ -218,7 +218,8 @@
<!-- Button to open in browser if it cannot be attempted in the app. --> <!-- Button to open in browser if it cannot be attempted in the app. -->
<ion-button class="ion-margin" *ngIf="!buttonText && ((!hasSupportedQuestions && unsupportedQuestions.length) || <ion-button class="ion-margin" *ngIf="!buttonText && ((!hasSupportedQuestions && unsupportedQuestions.length) ||
unsupportedRules.length || behaviourSupported === false)" expand="block" [href]="externalUrl" core-link> unsupportedRules.length || behaviourSupported === false)" expand="block" [href]="externalUrl" core-link
[showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -165,7 +165,8 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-button *ngIf="preventSubmitMessages.length" expand="block" [href]="moduleUrl" core-link> <ion-button *ngIf="preventSubmitMessages.length" expand="block" [href]="moduleUrl" core-link
[showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
@ -182,7 +183,7 @@
<ion-item class="ion-text-wrap"> <ion-item class="ion-text-wrap">
<ion-label>{{ 'addon.mod_quiz.errorparsequestions' | translate }}</ion-label> <ion-label>{{ 'addon.mod_quiz.errorparsequestions' | translate }}</ion-label>
</ion-item> </ion-item>
<ion-button expand="block" class="ion-margin" [href]="moduleUrl" core-link> <ion-button expand="block" class="ion-margin" [href]="moduleUrl" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"></core-context-menu-item> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"></core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item> (action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item>
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"

View File

@ -188,7 +188,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
} }
// The resource cannot be downloaded, open the activity in browser. // The resource cannot be downloaded, open the activity in browser.
await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module.url!); await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module.url || '');
} }
/** /**

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">
@ -185,7 +185,7 @@
<p class="text-danger">{{ errorMessage | translate }}</p> <p class="text-danger">{{ errorMessage | translate }}</p>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-button class="ion-margin ion-text-wrap" expand="block" [href]="externalUrl" core-link> <ion-button class="ion-margin ion-text-wrap" expand="block" [href]="externalUrl" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"></core-context-menu-item> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false"></core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item> (action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item>
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"

View File

@ -14,7 +14,7 @@
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -2,7 +2,7 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
[href]="externalUrl" iconAction="fas-external-link-alt"> [href]="externalUrl" iconAction="fas-external-link-alt" [showBrowserWarning]="false">
</core-context-menu-item> </core-context-menu-item>
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
(action)="expandDescription()" iconAction="fas-arrow-right"> (action)="expandDescription()" iconAction="fas-arrow-right">

View File

@ -355,7 +355,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity
* *
* @param task Task to be done. * @param task Task to be done.
*/ */
runTask(task: AddonModWorkshopPhaseTaskData): void { async runTask(task: AddonModWorkshopPhaseTaskData): Promise<void> {
if (task.code == 'submit') { if (task.code == 'submit') {
this.gotoSubmit(); this.gotoSubmit();
} else if (task.link) { } else if (task.link) {

View File

@ -57,7 +57,7 @@ export class AddonModWorkshopPhaseInfoComponent implements OnInit {
* *
* @param task Task to be done. * @param task Task to be done.
*/ */
runTask(task: AddonModWorkshopPhaseTaskData): void { async runTask(task: AddonModWorkshopPhaseTaskData): Promise<void> {
if (task.code == 'submit') { if (task.code == 'submit') {
// This will close the modal and go to the submit. // This will close the modal and go to the submit.
ModalController.dismiss(true); ModalController.dismiss(true);

View File

@ -104,17 +104,20 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
case 'browser': case 'browser':
return CoreUtils.openInBrowser(url); return CoreUtils.openInBrowser(url);
default: default: {
if (CoreContentLinksHelper.handleLink(url, undefined, undefined, true)) { const treated = await CoreContentLinksHelper.handleLink(url, undefined, undefined, true);
if (treated) {
// Link treated, stop. // Link treated, stop.
return; return;
} }
}
} }
} }
// No appurl or cannot be handled by the app. Try to handle the contexturl now. // No appurl or cannot be handled by the app. Try to handle the contexturl now.
if (notification.contexturl) { if (notification.contexturl) {
if (CoreContentLinksHelper.handleLink(notification.contexturl)) { const treated = await CoreContentLinksHelper.handleLink(notification.contexturl);
if (treated) {
// Link treated, stop. // Link treated, stop.
return; return;
} }

View File

@ -125,7 +125,7 @@ export class AppComponent implements OnInit, AfterViewInit {
const urlScheme = CoreUrlUtils.getUrlProtocol(url); const urlScheme = CoreUrlUtils.getUrlProtocol(url);
if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') { if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') {
// Open in browser should launch the right app if found and do nothing if not found. // 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. // 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. // Try to navigate to last loaded URL so this error message isn't found.

View File

@ -32,7 +32,7 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time'; import { CoreTimeUtils } from '@services/utils/time';
import { CoreUrlUtils, CoreUrlParams } from '@services/utils/url'; 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 { CoreConstants } from '@/core/constants';
import { SQLiteDB } from '@classes/sqlitedb'; import { SQLiteDB } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
@ -1381,10 +1381,15 @@ export class CoreSite {
* *
* @param url The URL to open. * @param url The URL to open.
* @param alertMessage If defined, an alert will be shown before opening the browser. * @param alertMessage If defined, an alert will be shown before opening the browser.
* @param options Other options.
* @return Promise resolved when done, rejected otherwise. * @return Promise resolved when done, rejected otherwise.
*/ */
async openInBrowserWithAutoLogin(url: string, alertMessage?: string): Promise<void> { async openInBrowserWithAutoLogin(
await this.openWithAutoLogin(false, url, undefined, alertMessage); url: string,
alertMessage?: string,
options: CoreUtilsOpenInBrowserOptions = {},
): Promise<void> {
await this.openWithAutoLogin(false, url, options, alertMessage);
} }
/** /**
@ -1392,10 +1397,15 @@ export class CoreSite {
* *
* @param url The URL to open. * @param url The URL to open.
* @param alertMessage If defined, an alert will be shown before opening the browser. * @param alertMessage If defined, an alert will be shown before opening the browser.
* @param options Other options.
* @return Promise resolved when done, rejected otherwise. * @return Promise resolved when done, rejected otherwise.
*/ */
async openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string): Promise<void> { async openInBrowserWithAutoLoginIfSameSite(
await this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage); url: string,
alertMessage?: string,
options: CoreUtilsOpenInBrowserOptions = {},
): Promise<void> {
await this.openWithAutoLoginIfSameSite(false, url, options, alertMessage);
} }
/** /**
@ -1442,7 +1452,7 @@ export class CoreSite {
async openWithAutoLogin( async openWithAutoLogin(
inApp: boolean, inApp: boolean,
url: string, url: string,
options?: InAppBrowserOptions, options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {},
alertMessage?: string, alertMessage?: string,
): Promise<InAppBrowserObject | void> { ): Promise<InAppBrowserObject | void> {
// Get the URL to open. // Get the URL to open.
@ -1458,13 +1468,14 @@ export class CoreSite {
); );
await alert.onDidDismiss(); await alert.onDidDismiss();
options.showBrowserWarning = false; // A warning already shown, no need to show another.
} }
// Open the URL. // Open the URL.
if (inApp) { if (inApp) {
return CoreUtils.openInApp(url, options); return CoreUtils.openInApp(url, options);
} else { } else {
return CoreUtils.openInBrowser(url); return CoreUtils.openInBrowser(url, options);
} }
} }
@ -1480,7 +1491,7 @@ export class CoreSite {
async openWithAutoLoginIfSameSite( async openWithAutoLoginIfSameSite(
inApp: boolean, inApp: boolean,
url: string, url: string,
options?: InAppBrowserOptions, options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {},
alertMessage?: string, alertMessage?: string,
): Promise<InAppBrowserObject | void> { ): Promise<InAppBrowserObject | void> {
if (this.containsUrl(url)) { if (this.containsUrl(url)) {
@ -1489,7 +1500,7 @@ export class CoreSite {
if (inApp) { if (inApp) {
return Promise.resolve(CoreUtils.openInApp(url, options)); return Promise.resolve(CoreUtils.openInApp(url, options));
} else { } else {
CoreUtils.openInBrowser(url); CoreUtils.openInBrowser(url, options);
} }
} }
} }

View File

@ -51,6 +51,7 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
@Input() badgeClass?: number; // A class to set in the badge. @Input() badgeClass?: number; // A class to set in the badge.
@Input() badgeA11yText?: string; // Description for the badge, if needed. @Input() badgeA11yText?: string; // Description for the badge, if needed.
@Input() hidden?: boolean; // Whether the item should be hidden. @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() 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. @Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked.

View File

@ -4,7 +4,8 @@
</ion-list-header> </ion-list-header>
<ion-item class="ion-text-wrap" *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin" <ion-item class="ion-text-wrap" *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin"
[href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden"
[detail]="(item.href && !item.iconAction) || null" role="menuitem" [button]="(item.href && !item.iconAction)"> [detail]="(item.href && !item.iconAction) || null" role="menuitem" [button]="(item.href && !item.iconAction)"
[showBrowserWarning]="item.showBrowserWarning">
<ion-icon *ngIf="item.iconDescription" [name]="item.iconDescription" aria-hidden="true" slot="start"> <ion-icon *ngIf="item.iconDescription" [name]="item.iconDescription" aria-hidden="true" slot="start">
</ion-icon> </ion-icon>
<ion-label> <ion-label>

View File

@ -63,6 +63,7 @@ export class CoreConstants {
static readonly SETTINGS_ZOOM_LEVEL = 'CoreSettingsZoomLevel'; static readonly SETTINGS_ZOOM_LEVEL = 'CoreSettingsZoomLevel';
static readonly SETTINGS_COLOR_SCHEME = 'CoreSettingsColorScheme'; static readonly SETTINGS_COLOR_SCHEME = 'CoreSettingsColorScheme';
static readonly SETTINGS_ANALYTICS_ENABLED = 'CoreSettingsAnalyticsEnabled'; static readonly SETTINGS_ANALYTICS_ENABLED = 'CoreSettingsAnalyticsEnabled';
static readonly SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN = 'CoreSettingsDontShowExtLinkWarn';
// WS constants. // WS constants.
static readonly WS_TIMEOUT = 30000; // Timeout when not in WiFi. static readonly WS_TIMEOUT = 30000; // Timeout when not in WiFi.

View File

@ -43,6 +43,7 @@ export class CoreLinkDirective implements OnInit {
"no" -> Never auto-login. "no" -> Never auto-login.
"check" -> Auto-login only if it points to the current site. Default value. */ "check" -> Auto-login only if it points to the current site. Default value. */
@Input() autoLogin = 'check'; @Input() autoLogin = 'check';
@Input() showBrowserWarning = true; // Whether to show a warning before opening browser. Defaults to true.
protected element: Element; protected element: Element;
@ -199,33 +200,35 @@ export class CoreLinkDirective implements OnInit {
if (this.inApp) { if (this.inApp) {
CoreUtils.openInApp(href); CoreUtils.openInApp(href);
} else { } else {
CoreUtils.openInBrowser(href); CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning });
} }
return; return;
} }
const currentSite = CoreSites.getRequiredCurrentSite();
// Check if URL does not have any protocol, so it's a relative URL. // Check if URL does not have any protocol, so it's a relative URL.
if (!CoreUrlUtils.isAbsoluteURL(href)) { if (!CoreUrlUtils.isAbsoluteURL(href)) {
// Add the site URL at the begining. // Add the site URL at the begining.
if (href.charAt(0) == '/') { if (href.charAt(0) == '/') {
href = CoreSites.getCurrentSite()!.getURL() + href; href = currentSite.getURL() + href;
} else { } else {
href = CoreSites.getCurrentSite()!.getURL() + '/' + href; href = currentSite.getURL() + '/' + href;
} }
} }
if (this.autoLogin == 'yes') { if (this.autoLogin == 'yes') {
if (this.inApp) { if (this.inApp) {
await CoreSites.getCurrentSite()!.openInAppWithAutoLogin(href); await currentSite.openInAppWithAutoLogin(href);
} else { } else {
await CoreSites.getCurrentSite()!.openInBrowserWithAutoLogin(href); await currentSite.openInBrowserWithAutoLogin(href, undefined, { showBrowserWarning: this.showBrowserWarning });
} }
} else if (this.autoLogin == 'no') { } else if (this.autoLogin == 'no') {
if (this.inApp) { if (this.inApp) {
CoreUtils.openInApp(href); CoreUtils.openInApp(href);
} else { } else {
CoreUtils.openInBrowser(href); CoreUtils.openInBrowser(href, { showBrowserWarning: this.showBrowserWarning });
} }
} else { } else {
// Priority order is: core-link inApp attribute > forceOpenLinksIn setting > data-open-in HTML attribute. // 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) { if (openInApp) {
await CoreSites.getCurrentSite()!.openInAppWithAutoLoginIfSameSite(href); await currentSite.openInAppWithAutoLoginIfSameSite(href);
} else { } else {
await CoreSites.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(href); await currentSite.openInBrowserWithAutoLoginIfSameSite(
href,
undefined,
{ showBrowserWarning: this.showBrowserWarning },
);
} }
} }
} }

View File

@ -17,7 +17,7 @@
<div *ngIf="module && module.url"> <div *ngIf="module && module.url">
<p><strong>{{ 'core.course.useactivityonbrowser' | translate }}</strong></p> <p><strong>{{ 'core.course.useactivityonbrowser' | translate }}</strong></p>
<ion-button expand="block" [href]="module.url" core-link> <ion-button expand="block" [href]="module.url" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -123,7 +123,8 @@
<ion-icon name="fas-briefcase" slot="start" aria-hidden="true"></ion-icon> <ion-icon name="fas-briefcase" slot="start" aria-hidden="true"></ion-icon>
<ion-label><h2>{{ 'core.course.contents' | translate }}</h2></ion-label> <ion-label><h2>{{ 'core.course.contents' | translate }}</h2></ion-label>
</ion-item> </ion-item>
<ion-item [href]="courseUrl" core-link [attr.aria-label]="course.fullname" button detail="false"> <ion-item [href]="courseUrl" core-link [attr.aria-label]="course.fullname" button detail="false"
[showBrowserWarning]="false">
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
<ion-label><h2>{{ 'core.openinbrowser' | translate }}</h2></ion-label> <ion-label><h2>{{ 'core.openinbrowser' | translate }}</h2></ion-label>
</ion-item> </ion-item>

View File

@ -222,7 +222,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler
try { try {
await CoreDomUtils.showConfirm(body); await CoreDomUtils.showConfirm(body);
CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(url); CoreSites.getCurrentSite()?.openInBrowserWithAutoLogin(url, undefined, { showBrowserWarning: false });
} catch { } catch {
// User cancelled. // User cancelled.
}; };

View File

@ -28,7 +28,7 @@
{{ 'core.login.signuprequiredfieldnotsupported' | translate }} {{ 'core.login.signuprequiredfieldnotsupported' | translate }}
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-button expand="block" class="ion-margin" [href]="signupUrl" core-link autoLogin="no"> <ion-button expand="block" class="ion-margin" [href]="signupUrl" core-link autoLogin="no" [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
</ion-button> </ion-button>
</ion-list> </ion-list>

View File

@ -384,7 +384,10 @@ export class CoreLoginEmailSignupPage implements OnInit {
* Show contact information on site (we have to display again the age verification form). * Show contact information on site (we have to display again the age verification form).
*/ */
showContactOnSite(): void { 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 },
);
} }
/** /**

View File

@ -660,7 +660,7 @@ export class CoreLoginHelperProvider {
}); });
// Always open it in browser because the user might have the session stored in there. // 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(); CoreApp.closeApp();
return true; return true;
@ -692,7 +692,7 @@ export class CoreLoginHelperProvider {
closebuttoncaption: Translate.instant('core.login.cancel'), closebuttoncaption: Translate.instant('core.login.cancel'),
}); });
} else { } else {
CoreUtils.openInBrowser(loginUrl); CoreUtils.openInBrowser(loginUrl, { showBrowserWarning: false });
CoreApp.closeApp(); CoreApp.closeApp();
} }
} }

View File

@ -30,8 +30,8 @@
</p> </p>
<p>{{ 'core.settings.license' | translate }}{{ 'core.labelsep' | translate }} {{ license.licenses }}</p> <p>{{ 'core.settings.license' | translate }}{{ 'core.labelsep' | translate }} {{ license.licenses }}</p>
<p><a *ngIf="license.url" [href]="license.url" core-link auto-login="no">{{ license.url }}</a></p> <p><a *ngIf="license.url" [href]="license.url" core-link auto-login="no">{{ license.url }}</a></p>
<p><a *ngIf="license.email" [href]="'mailto:' + license.email" core-link <p><a *ngIf="license.email" [href]="'mailto:' + license.email" core-link auto-login="no"
auto-login="no">{{ license.email }}</a></p> [showBrowserWarning]="false">{{ license.email }}</a></p>
</ion-label> </ion-label>
<ion-button *ngIf="license.licenseUrl" [href]="license.licenseUrl" target="_blank" <ion-button *ngIf="license.licenseUrl" [href]="license.licenseUrl" target="_blank"
fill="clear" slot="end" core-link auto-login="no">{{ 'core.view' | translate }}</ion-button> fill="clear" slot="end" core-link auto-login="no">{{ 'core.view' | translate }}</ion-button>

View File

@ -17,7 +17,8 @@
<ion-item class="ion-text-wrap" *ngIf="user.email"> <ion-item class="ion-text-wrap" *ngIf="user.email">
<ion-label> <ion-label>
<h2>{{ 'core.user.email' | translate }}</h2> <h2>{{ 'core.user.email' | translate }}</h2>
<p><a class="core-anchor" href="mailto:{{user.email}}" core-link auto-login="no"> <p><a class="core-anchor" href="mailto:{{user.email}}" core-link auto-login="no"
[showBrowserWarning]="false">
{{ user.email }} {{ user.email }}
</a></p> </a></p>
</ion-label> </ion-label>
@ -25,7 +26,7 @@
<ion-item class="ion-text-wrap" *ngIf="user.phone1"> <ion-item class="ion-text-wrap" *ngIf="user.phone1">
<ion-label> <ion-label>
<h2>{{ 'core.user.phone1' | translate}}</h2> <h2>{{ 'core.user.phone1' | translate}}</h2>
<p><a class="core-anchor" href="tel:{{user.phone1}}" core-link auto-login="no"> <p><a class="core-anchor" href="tel:{{user.phone1}}" core-link auto-login="no" [showBrowserWarning]="false">
{{ user.phone1 }} {{ user.phone1 }}
</a></p> </a></p>
</ion-label> </ion-label>
@ -33,7 +34,7 @@
<ion-item class="ion-text-wrap" *ngIf="user.phone2"> <ion-item class="ion-text-wrap" *ngIf="user.phone2">
<ion-label> <ion-label>
<h2>{{ 'core.user.phone2' | translate}}</h2> <h2>{{ 'core.user.phone2' | translate}}</h2>
<p><a class="core-anchor" href="tel:{{user.phone2}}" core-link auto-login="no"> <p><a class="core-anchor" href="tel:{{user.phone2}}" core-link auto-login="no" [showBrowserWarning]="false">
{{ user.phone2 }} {{ user.phone2 }}
</a></p> </a></p>
</ion-label> </ion-label>
@ -41,7 +42,7 @@
<ion-item class="ion-text-wrap" *ngIf="formattedAddress"> <ion-item class="ion-text-wrap" *ngIf="formattedAddress">
<ion-label> <ion-label>
<h2>{{ 'core.user.address' | translate}}</h2> <h2>{{ 'core.user.address' | translate}}</h2>
<p><a class="core-anchor" [href]="encodedAddress" core-link auto-login="no"> <p><a class="core-anchor" [href]="encodedAddress" core-link auto-login="no" [showBrowserWarning]="false">
{{ formattedAddress }} {{ formattedAddress }}
</a></p> </a></p>
</ion-label> </ion-label>

View File

@ -56,7 +56,7 @@ export class CoreUserProfileMailHandlerService implements CoreUserProfileHandler
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreUtils.openInBrowser('mailto:' + user.email); CoreUtils.openInBrowser('mailto:' + user.email, { showBrowserWarning: false });
}, },
}; };
} }

View File

@ -86,6 +86,7 @@
"dismiss": "Dismiss", "dismiss": "Dismiss",
"displayoptions": "Display options", "displayoptions": "Display options",
"done": "Done", "done": "Done",
"dontshowagain": "Don't show again.",
"download": "Download", "download": "Download",
"downloaded": "Downloaded", "downloaded": "Downloaded",
"downloadfile": "Download file", "downloadfile": "Download file",
@ -327,6 +328,7 @@
"viewembeddedcontent": "View embedded content", "viewembeddedcontent": "View embedded content",
"viewprofile": "View profile", "viewprofile": "View profile",
"warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}", "warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}",
"warnopeninbrowser": "<p>You are about to leave the app to open the following URL in your device's browser. Do you want to continue?</p>\n<p><b>{{url}}</b></p>",
"whatisyourage": "What is your age?", "whatisyourage": "What is your age?",
"wheredoyoulive": "In which country do you live?", "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.", "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.",

View File

@ -731,7 +731,7 @@ export class CoreSitesProvider {
Translate.instant('core.updaterequired'), Translate.instant('core.updaterequired'),
Translate.instant('core.download'), Translate.instant('core.download'),
Translate.instant(siteId ? 'core.mainmenu.logout' : 'core.cancel'), Translate.instant(siteId ? 'core.mainmenu.logout' : 'core.cancel'),
).then(() => CoreUtils.openInBrowser(downloadUrl)).catch(() => { ).then(() => CoreUtils.openInBrowser(downloadUrl, { showBrowserWarning: false })).catch(() => {
// Do nothing. // Do nothing.
}); });
} else { } else {

View File

@ -1440,7 +1440,7 @@ export class CoreDomUtilsProvider {
buttons.push({ buttons.push({
text: Translate.instant('core.download'), text: Translate.instant('core.download'),
handler: (): void => { handler: (): void => {
CoreUtils.openInBrowser(link); CoreUtils.openInBrowser(link, { showBrowserWarning: false });
}, },
}); });
} }
@ -1465,28 +1465,32 @@ export class CoreDomUtilsProvider {
* *
* @param message Modal message. * @param message Modal message.
* @param header Modal header. * @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 type Type of the input element. By default, password.
* @param options More options to pass to the alert. * @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( showPrompt(
message: string, message: string,
header?: string, header?: string,
placeholder?: string, placeholderOrLabel?: string,
type: TextFieldTypes | 'checkbox' | 'radio' | 'textarea' = 'password', type: TextFieldTypes | 'checkbox' | 'radio' | 'textarea' = 'password',
): Promise<any> { // eslint-disable-line @typescript-eslint/no-explicit-any ): Promise<any> { // eslint-disable-line @typescript-eslint/no-explicit-any
return new Promise((resolve, reject) => { 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 = { const options: AlertOptions = {
header, header,
message, message,
inputs: [ inputs: [
{ {
name: 'promptinput', name: 'promptinput',
placeholder: placeholder, placeholder: placeholderOrLabel,
label: placeholderOrLabel,
type, type,
value: (isCheckbox || isRadio) ? true : undefined,
}, },
], ],
buttons: [ buttons: [
@ -1500,7 +1504,13 @@ export class CoreDomUtilsProvider {
{ {
text: Translate.instant('core.ok'), text: Translate.instant('core.ok'),
handler: (data) => { handler: (data) => {
resolve(data.promptinput); if (isCheckbox) {
resolve(data[0]);
} else if (isRadio) {
resolve(data);
} else {
resolve(data.promptinput);
}
}, },
}, },
], ],

View File

@ -32,6 +32,7 @@ import { CoreViewerQRScannerComponent } from '@features/viewer/components/qr-sca
import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreCanceledError } from '@classes/errors/cancelederror';
import { CoreFileEntry } from '@services/file-helper'; import { CoreFileEntry } from '@services/file-helper';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreWindow } from '@singletons/window';
type TreeNode<T> = T & { children: TreeNode<T>[] }; type TreeNode<T> = T & { children: TreeNode<T>[] };
@ -1047,8 +1048,17 @@ export class CoreUtilsProvider {
* Do not use for files, refer to {@link openFile}. * Do not use for files, refer to {@link openFile}.
* *
* @param url The URL to open. * @param url The URL to open.
* @param options Options.
*/ */
openInBrowser(url: string): void { async openInBrowser(url: string, options: CoreUtilsOpenInBrowserOptions = {}): Promise<void> {
if (options.showBrowserWarning || options.showBrowserWarning === undefined) {
try {
await CoreWindow.confirmOpenBrowserIfNeeded(url);
} catch (error) {
return; // Cancelled, stop.
}
}
window.open(url, '_system'); window.open(url, '_system');
} }
@ -1727,6 +1737,13 @@ export type CoreUtilsOpenFileOptions = {
iOSOpenFileAction?: OpenFileAction; // Action to do when opening a file. 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. * Possible default picker actions.
*/ */

View File

@ -14,11 +14,15 @@
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
import { NavController } from '@ionic/angular'; import { NavController } from '@ionic/angular';
import { CoreConfig } from '@services/config';
import { CoreFileHelper } from '@services/file-helper'; import { CoreFileHelper } from '@services/file-helper';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url'; import { CoreUrlUtils } from '@services/utils/url';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { Translate } from '@singletons';
import { CoreConstants } from '../constants';
/** /**
* Options for the open function. * Options for the open function.
@ -44,6 +48,31 @@ export class CoreWindow {
// Nothing to do. // 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<void> {
// 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. * "Safe" implementation of window.open. It will open the URL without overriding the app.
* *
@ -73,12 +102,12 @@ export class CoreWindow {
} }
if (!treated) { 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()) { if (!CoreSites.isLoggedIn()) {
// Not logged in, cannot auto-login. // Not logged in, cannot auto-login.
CoreUtils.openInBrowser(url); CoreUtils.openInBrowser(url);
} else { } else {
await CoreSites.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(url); await CoreSites.getRequiredCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
} }
} }
} }