MOBILE-3565 directives: Implement core-link directive
parent
0c3d5293d4
commit
1d794f42b4
|
@ -30,7 +30,7 @@
|
||||||
<ion-spinner slot="end" *ngIf="handler.showBadge && handler.loading"></ion-spinner>
|
<ion-spinner slot="end" *ngIf="handler.showBadge && handler.loading"></ion-spinner>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<div *ngFor="let item of customItems" class="core-moremenu-customitem">
|
<div *ngFor="let item of customItems" class="core-moremenu-customitem">
|
||||||
<ion-item *ngIf="item.type != 'embedded'" [href]="item.url" title="{{item.label}}"> <!-- @todo core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'" -->
|
<ion-item *ngIf="item.type != 'embedded'" [href]="item.url" title="{{item.label}}" core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'">
|
||||||
<core-icon [name]="item.icon" slot="start"></core-icon>
|
<core-icon [name]="item.icon" slot="start"></core-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>{{item.label}}</h2>
|
<h2>{{item.label}}</h2>
|
||||||
|
|
|
@ -16,16 +16,18 @@ import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { CoreAutoFocusDirective } from './auto-focus';
|
import { CoreAutoFocusDirective } from './auto-focus';
|
||||||
import { CoreExternalContentDirective } from './external-content';
|
import { CoreExternalContentDirective } from './external-content';
|
||||||
|
import { CoreFabDirective } from './fab';
|
||||||
import { CoreFormatTextDirective } from './format-text';
|
import { CoreFormatTextDirective } from './format-text';
|
||||||
|
import { CoreLinkDirective } from './link';
|
||||||
import { CoreLongPressDirective } from './long-press';
|
import { CoreLongPressDirective } from './long-press';
|
||||||
import { CoreSupressEventsDirective } from './supress-events';
|
import { CoreSupressEventsDirective } from './supress-events';
|
||||||
import { CoreFabDirective } from './fab';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreAutoFocusDirective,
|
CoreAutoFocusDirective,
|
||||||
CoreExternalContentDirective,
|
CoreExternalContentDirective,
|
||||||
CoreFormatTextDirective,
|
CoreFormatTextDirective,
|
||||||
|
CoreLinkDirective,
|
||||||
CoreLongPressDirective,
|
CoreLongPressDirective,
|
||||||
CoreSupressEventsDirective,
|
CoreSupressEventsDirective,
|
||||||
CoreFabDirective,
|
CoreFabDirective,
|
||||||
|
@ -35,6 +37,7 @@ import { CoreFabDirective } from './fab';
|
||||||
CoreAutoFocusDirective,
|
CoreAutoFocusDirective,
|
||||||
CoreExternalContentDirective,
|
CoreExternalContentDirective,
|
||||||
CoreFormatTextDirective,
|
CoreFormatTextDirective,
|
||||||
|
CoreLinkDirective,
|
||||||
CoreLongPressDirective,
|
CoreLongPressDirective,
|
||||||
CoreSupressEventsDirective,
|
CoreSupressEventsDirective,
|
||||||
CoreFabDirective,
|
CoreFabDirective,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
import { Translate } from '@singletons/core.singletons';
|
import { Translate } from '@singletons/core.singletons';
|
||||||
import { CoreExternalContentDirective } from './external-content';
|
import { CoreExternalContentDirective } from './external-content';
|
||||||
|
import { CoreLinkDirective } from './link';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||||
|
@ -454,7 +455,9 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
// Important: We need to look for links first because in 'img' we add new links without core-link.
|
// Important: We need to look for links first because in 'img' we add new links without core-link.
|
||||||
anchors.forEach((anchor) => {
|
anchors.forEach((anchor) => {
|
||||||
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
||||||
// @todo
|
const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content);
|
||||||
|
linkDir.capture = true;
|
||||||
|
linkDir.ngOnInit();
|
||||||
|
|
||||||
this.addExternalContent(anchor);
|
this.addExternalContent(anchor);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core';
|
||||||
|
import { IonContent } from '@ionic/angular';
|
||||||
|
|
||||||
|
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 { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreConstants } from '@core/constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive to open a link in external browser or in the app.
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[core-link]',
|
||||||
|
})
|
||||||
|
export class CoreLinkDirective implements OnInit {
|
||||||
|
|
||||||
|
@Input() capture?: boolean | string; // If the link needs to be captured by the app.
|
||||||
|
@Input() inApp?: boolean | string; // True to open in embedded browser, false to open in system browser.
|
||||||
|
/* Whether the link should be opened with auto-login. Accepts the following values:
|
||||||
|
"yes" -> Always auto-login.
|
||||||
|
"no" -> Never auto-login.
|
||||||
|
"check" -> Auto-login only if it points to the current site. Default value. */
|
||||||
|
@Input() autoLogin = 'check';
|
||||||
|
|
||||||
|
protected element: Element;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
element: ElementRef,
|
||||||
|
@Optional() protected content: IonContent,
|
||||||
|
) {
|
||||||
|
this.element = element.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function executed when the component is initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.inApp = typeof this.inApp == 'undefined' ? this.inApp : CoreUtils.instance.isTrueOrOne(this.inApp);
|
||||||
|
|
||||||
|
// @todo: Handle split view?
|
||||||
|
|
||||||
|
this.element.addEventListener('click', (event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return; // Link already treated, stop.
|
||||||
|
}
|
||||||
|
|
||||||
|
let href = this.element.getAttribute('href') || this.element.getAttribute('ng-reflect-href') ||
|
||||||
|
this.element.getAttribute('xlink:href');
|
||||||
|
|
||||||
|
if (!href || CoreUrlUtils.instance.getUrlScheme(href) == 'javascript') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const openIn = this.element.getAttribute('data-open-in');
|
||||||
|
|
||||||
|
if (CoreUtils.instance.isTrueOrOne(this.capture)) {
|
||||||
|
href = CoreTextUtils.instance.decodeURI(href);
|
||||||
|
|
||||||
|
// @todo: Handle link.
|
||||||
|
this.navigate(href, openIn);
|
||||||
|
} else {
|
||||||
|
this.navigate(href, openIn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to correctly navigate, open file or url in the browser.
|
||||||
|
*
|
||||||
|
* @param href HREF to be opened.
|
||||||
|
* @param openIn Open In App value coming from data-open-in attribute.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async navigate(href: string, openIn?: string | null): Promise<void> {
|
||||||
|
|
||||||
|
if (CoreUrlUtils.instance.isLocalFileUrl(href)) {
|
||||||
|
return this.openLocalFile(href);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (href.charAt(0) == '#') {
|
||||||
|
// Look for id or name.
|
||||||
|
href = href.substr(1);
|
||||||
|
CoreDomUtils.instance.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: Custom URL schemes.
|
||||||
|
|
||||||
|
return this.openExternalLink(href, openIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a local file.
|
||||||
|
*
|
||||||
|
* @param path Path to the file.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async openLocalFile(path: string): Promise<void> {
|
||||||
|
const filename = path.substr(path.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
if (!CoreFileHelper.instance.isOpenableInApp({ filename })) {
|
||||||
|
try {
|
||||||
|
await CoreFileHelper.instance.showConfirmOpenUnsupportedFile();
|
||||||
|
} catch (error) {
|
||||||
|
return; // Cancelled, stop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CoreUtils.instance.openFile(path);
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModal(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an external link in the app or in browser.
|
||||||
|
*
|
||||||
|
* @param href HREF to be opened.
|
||||||
|
* @param openIn Open In App value coming from data-open-in attribute.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async openExternalLink(href: string, openIn?: string | null): Promise<void> {
|
||||||
|
// It's an external link, we will open with browser. Check if we need to auto-login.
|
||||||
|
if (!CoreSites.instance.isLoggedIn()) {
|
||||||
|
// Not logged in, cannot auto-login.
|
||||||
|
if (this.inApp) {
|
||||||
|
CoreUtils.instance.openInApp(href);
|
||||||
|
} else {
|
||||||
|
CoreUtils.instance.openInBrowser(href);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if URL does not have any protocol, so it's a relative URL.
|
||||||
|
if (!CoreUrlUtils.instance.isAbsoluteURL(href)) {
|
||||||
|
// Add the site URL at the begining.
|
||||||
|
if (href.charAt(0) == '/') {
|
||||||
|
href = CoreSites.instance.getCurrentSite()!.getURL() + href;
|
||||||
|
} else {
|
||||||
|
href = CoreSites.instance.getCurrentSite()!.getURL() + '/' + href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.autoLogin == 'yes') {
|
||||||
|
if (this.inApp) {
|
||||||
|
await CoreSites.instance.getCurrentSite()!.openInAppWithAutoLogin(href);
|
||||||
|
} else {
|
||||||
|
await CoreSites.instance.getCurrentSite()!.openInBrowserWithAutoLogin(href);
|
||||||
|
}
|
||||||
|
} else if (this.autoLogin == 'no') {
|
||||||
|
if (this.inApp) {
|
||||||
|
CoreUtils.instance.openInApp(href);
|
||||||
|
} else {
|
||||||
|
CoreUtils.instance.openInBrowser(href);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Priority order is: core-link inApp attribute > forceOpenLinksIn setting > data-open-in HTML attribute.
|
||||||
|
let openInApp = this.inApp;
|
||||||
|
if (typeof this.inApp == 'undefined') {
|
||||||
|
if (CoreConstants.CONFIG.forceOpenLinksIn == 'browser') {
|
||||||
|
openInApp = false;
|
||||||
|
} else if (CoreConstants.CONFIG.forceOpenLinksIn == 'app' || openIn == 'app') {
|
||||||
|
openInApp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openInApp) {
|
||||||
|
await CoreSites.instance.getCurrentSite()!.openInAppWithAutoLoginIfSameSite(href);
|
||||||
|
} else {
|
||||||
|
await CoreSites.instance.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ declare global {
|
||||||
appstores: Record<string, string>;
|
appstores: Record<string, string>;
|
||||||
displayqroncredentialscreen?: boolean;
|
displayqroncredentialscreen?: boolean;
|
||||||
displayqronsitescreen?: boolean;
|
displayqronsitescreen?: boolean;
|
||||||
|
forceOpenLinksIn: 'app' | 'browser';
|
||||||
};
|
};
|
||||||
|
|
||||||
BUILD: {
|
BUILD: {
|
||||||
|
|
Loading…
Reference in New Issue