commit
1b2f160d8b
|
@ -133,5 +133,6 @@
|
|||
"moodle"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"clearIABSessionWhenAutoLogin": "android"
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
<ion-list *ngIf="url && !shouldIframe && (!shouldEmbed || !isOther)">
|
||||
<ion-item *ngIf="shouldEmbed">
|
||||
<ion-label>
|
||||
<img *ngIf="isImage" [alt]="name" [src]="url">
|
||||
<img *ngIf="isImage" [alt]="name" [src]="embeddedUrl">
|
||||
<video *ngIf="isVideo" [title]="name" controls controlsList="nodownload">
|
||||
<source [src]="url" [type]="mimetype">
|
||||
<source [src]="embeddedUrl" [type]="mimetype">
|
||||
</video>
|
||||
<audio *ngIf="isAudio" [title]="name" controls>
|
||||
<source [src]="url" [type]="mimetype">
|
||||
<source [src]="embeddedUrl" [type]="mimetype">
|
||||
</audio>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -23,6 +23,7 @@ import { CoreTextUtils } from '@services/utils/text';
|
|||
import { AddonModUrl, AddonModUrlDisplayOptions, AddonModUrlUrl } from '../../services/url';
|
||||
import { AddonModUrlHelper } from '../../services/url-helper';
|
||||
import { ADDON_MOD_URL_COMPONENT } from '../../constants';
|
||||
import { CoreSites } from '@services/sites';
|
||||
|
||||
/**
|
||||
* Component that displays a url.
|
||||
|
@ -38,6 +39,7 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
pluginName = 'url';
|
||||
|
||||
url?: string;
|
||||
embeddedUrl?: string;
|
||||
name?: string;
|
||||
shouldEmbed = false;
|
||||
shouldIframe = false;
|
||||
|
@ -137,15 +139,22 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
this.shouldEmbed = displayType == CoreConstants.RESOURCELIB_DISPLAY_EMBED;
|
||||
this.shouldIframe = displayType == CoreConstants.RESOURCELIB_DISPLAY_FRAME;
|
||||
|
||||
if (this.shouldEmbed) {
|
||||
const extension = CoreMimetypeUtils.guessExtensionFromUrl(url.externalurl);
|
||||
|
||||
this.mimetype = CoreMimetypeUtils.getMimeType(extension);
|
||||
this.isImage = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_image']);
|
||||
this.isAudio = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_audio']);
|
||||
this.isVideo = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_video']);
|
||||
this.isOther = !this.isImage && !this.isAudio && !this.isVideo;
|
||||
if (!this.shouldEmbed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extension = CoreMimetypeUtils.guessExtensionFromUrl(url.externalurl);
|
||||
|
||||
this.mimetype = CoreMimetypeUtils.getMimeType(extension);
|
||||
this.isImage = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_image']);
|
||||
this.isAudio = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_audio']);
|
||||
this.isVideo = CoreMimetypeUtils.isExtensionInGroup(extension, ['web_video']);
|
||||
this.isOther = !this.isImage && !this.isAudio && !this.isVideo;
|
||||
|
||||
// Fix the URL if it uses pluginfile endpoint.
|
||||
const currentSite = CoreSites.getCurrentSite();
|
||||
this.embeddedUrl = currentSite && this.url ?
|
||||
await currentSite.checkAndFixPluginfileURL(this.url) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,6 +54,7 @@ import { CoreFilepool } from '@services/filepool';
|
|||
import { CoreSiteInfo } from './unauthenticated-site';
|
||||
import { CoreAuthenticatedSite, CoreAuthenticatedSiteOptionalData, CoreSiteWSPreSets, WSObservable } from './authenticated-site';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
|
||||
/**
|
||||
* Class that represents a site (combination of site + user).
|
||||
|
@ -557,6 +558,17 @@ export class CoreSite extends CoreAuthenticatedSite {
|
|||
|
||||
// Open the URL.
|
||||
if (inApp) {
|
||||
if (
|
||||
options.clearsessioncache === undefined && autoLoginUrl !== url &&
|
||||
(
|
||||
CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'all' ||
|
||||
(CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'android' && CorePlatform.isAndroid()) ||
|
||||
(CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'ios' && CorePlatform.isIOS())
|
||||
)
|
||||
) {
|
||||
options.clearsessioncache = 'yes';
|
||||
}
|
||||
|
||||
return CoreUtils.openInApp(autoLoginUrl, options);
|
||||
} else {
|
||||
return CoreUtils.openInBrowser(autoLoginUrl, options);
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
EventEmitter,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { CoreFile } from '@services/file';
|
||||
import { CoreFile, CoreFileProvider } from '@services/file';
|
||||
import { CoreFilepool, CoreFilepoolFileActions, CoreFilepoolFileEventData } from '@services/filepool';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
|
@ -41,6 +41,9 @@ import { CorePromisedValue } from '@classes/promised-value';
|
|||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
|
||||
import { CoreWS } from '@services/ws';
|
||||
|
||||
/**
|
||||
* Directive to handle external content.
|
||||
|
@ -142,13 +145,12 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
* Get the URL that should be handled and, if valid, handle it.
|
||||
*/
|
||||
protected async checkAndHandleExternalContent(): Promise<void> {
|
||||
const siteId = this.siteId || CoreSites.getRequiredCurrentSite().getId();
|
||||
const tagName = this.element.tagName.toUpperCase();
|
||||
let targetAttr: string;
|
||||
let url: string;
|
||||
|
||||
// Always handle inline styles (if any).
|
||||
this.handleInlineStyles(siteId);
|
||||
this.handleInlineStyles(this.siteId);
|
||||
|
||||
if (tagName === 'A' || tagName == 'IMAGE') {
|
||||
targetAttr = 'href';
|
||||
|
@ -165,7 +167,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
if (tagName === 'VIDEO' && (this.posterUrl || this.poster)) { // eslint-disable-line deprecation/deprecation
|
||||
// Handle poster.
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
this.handleExternalContent('poster', this.posterUrl ?? this.poster ?? '', siteId).catch(() => {
|
||||
this.handleExternalContent('poster', this.posterUrl ?? this.poster ?? '').catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}
|
||||
|
@ -178,7 +180,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
}
|
||||
|
||||
try {
|
||||
await this.handleExternalContent(targetAttr, url, siteId);
|
||||
await this.handleExternalContent(targetAttr, url);
|
||||
} catch (error) {
|
||||
// Error handling content. Make sure the original URL is set.
|
||||
this.setElementUrl(targetAttr, url);
|
||||
|
@ -192,28 +194,27 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
*
|
||||
* @param targetAttr Attribute to modify.
|
||||
* @param url Original URL to treat.
|
||||
* @param siteId Site ID.
|
||||
* @returns Promise resolved if the element is successfully treated.
|
||||
*/
|
||||
protected async handleExternalContent(targetAttr: string, url: string, siteId?: string): Promise<void> {
|
||||
protected async handleExternalContent(targetAttr: string, url: string): Promise<void> {
|
||||
|
||||
const tagName = this.element.tagName;
|
||||
if (tagName == 'VIDEO' && targetAttr != 'poster') {
|
||||
this.handleVideoSubtitles(<HTMLVideoElement> this.element);
|
||||
}
|
||||
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
const isSiteFile = site.isSitePluginFileUrl(url);
|
||||
const site = await CoreUtils.ignoreErrors(CoreSites.getSite(this.siteId));
|
||||
const isSiteFile = site?.isSitePluginFileUrl(url);
|
||||
|
||||
if (!url || !url.match(/^https?:\/\//i) || CoreUrlUtils.isLocalFileUrl(url) ||
|
||||
(tagName === 'A' && !(isSiteFile || site.isSiteThemeImageUrl(url) || CoreUrlUtils.isGravatarUrl(url)))) {
|
||||
(tagName === 'A' && !(isSiteFile || site?.isSiteThemeImageUrl(url) || CoreUrlUtils.isGravatarUrl(url)))) {
|
||||
|
||||
this.logger.debug('Ignoring non-downloadable URL: ' + url);
|
||||
|
||||
throw new CoreError('Non-downloadable URL');
|
||||
}
|
||||
|
||||
if (!site.canDownloadFiles() && isSiteFile) {
|
||||
if (site && !site.canDownloadFiles() && isSiteFile) {
|
||||
this.element.parentElement?.removeChild(this.element); // Remove element since it'll be broken.
|
||||
|
||||
throw new CoreError(Translate.instant('core.cannotdownloadfiles'));
|
||||
|
@ -225,7 +226,9 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
|
||||
this.setElementUrl(targetAttr, finalUrl);
|
||||
|
||||
this.setListeners(targetAttr, url, site);
|
||||
if (site) {
|
||||
this.setListeners(targetAttr, url, site);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,7 +353,11 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
* @param site Site.
|
||||
* @returns Promise resolved with the URL.
|
||||
*/
|
||||
protected async getUrlToUse(targetAttr: string, url: string, site: CoreSite): Promise<string> {
|
||||
protected async getUrlToUse(targetAttr: string, url: string, site?: CoreSite): Promise<string> {
|
||||
if (!site) {
|
||||
return this.getUrlForNoSite(url);
|
||||
}
|
||||
|
||||
const tagName = this.element.tagName;
|
||||
let finalUrl: string;
|
||||
|
||||
|
@ -397,6 +404,36 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
|
|||
return finalUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to use when there's no site (the user hasn't authenticated yet).
|
||||
* In Android, the file will always be downloaded and served from the local file system to avoid problems with cookies.
|
||||
* In other platforms the file will never be downloaded, we'll always use the online URL.
|
||||
*
|
||||
* @param url Original URL to treat.
|
||||
* @returns Promise resolved with the URL.
|
||||
*/
|
||||
protected async getUrlForNoSite(url: string): Promise<string> {
|
||||
if (!CorePlatform.isAndroid()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const fileId = CoreFilepool.getFileIdByUrl(url);
|
||||
const extension = CoreMimetypeUtils.guessExtensionFromUrl(url);
|
||||
|
||||
const filePath = CoreFileProvider.NO_SITE_FOLDER + '/' + fileId + (extension ? '.' + extension : '');
|
||||
let fileEntry: FileEntry;
|
||||
|
||||
try {
|
||||
// Check if the file is already downloaded.
|
||||
fileEntry = await CoreFile.getFile(filePath);
|
||||
} catch {
|
||||
// File not downloaded, download it first.
|
||||
fileEntry = await CoreWS.downloadFile(url, filePath, false);
|
||||
}
|
||||
|
||||
return CoreFile.convertFileSrc(CoreFile.getFileEntryURL(fileEntry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listeners if needed.
|
||||
*
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
</ion-badge>
|
||||
</ion-label>
|
||||
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="end" [attr.aria-label]="grade.iconAlt" />
|
||||
<img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image" slot="end" [alt]="grade.iconAlt" />
|
||||
<img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image" slot="end" [alt]="grade.iconAlt"
|
||||
core-external-content />
|
||||
<ion-icon *ngIf="grade.image && grade.itemmodule" name="fas-chart-bar" slot="end"
|
||||
[attr.aria-label]="grade.iconAlt" />
|
||||
</ion-item>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
class="expandable-status-icon" [class.expandable-status-icon-expanded]="row.expanded" />
|
||||
<ion-icon *ngIf="row.icon" name="{{row.icon}}" slot="start" [attr.aria-label]="row.iconAlt" />
|
||||
<img *ngIf="row.image && !row.itemmodule" [src]="row.image" slot="start" class="core-module-icon"
|
||||
[alt]="row.iconAlt" />
|
||||
[alt]="row.iconAlt" core-external-content />
|
||||
<core-mod-icon *ngIf="row.image && row.itemmodule" [modicon]="row.image" slot="start"
|
||||
[modname]="row.itemmodule" [colorize]="false" />
|
||||
<span [innerHTML]="row.gradeitem"></span>
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
<div class="core-login-site">
|
||||
<div class="core-login-site-logo">
|
||||
<!-- Show site logo or a default image. -->
|
||||
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation" alt="" onError="this.src='assets/img/login_logo.png'">
|
||||
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation" alt="" core-external-content
|
||||
onError="this.src='assets/img/login_logo.png'">
|
||||
<img *ngIf="!logoUrl" src="assets/img/login_logo.png" role="presentation" alt="">
|
||||
</div>
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
<div class="core-login-site">
|
||||
<div class="core-login-site-logo" *ngIf="!showUserAvatar">
|
||||
<!-- Show site logo or a default image. -->
|
||||
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation" onError="this.src='assets/img/login_logo.png'" alt="">
|
||||
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation" onError="this.src='assets/img/login_logo.png'" alt=""
|
||||
core-external-content [siteId]="siteId">
|
||||
<img *ngIf="!logoUrl" src="assets/img/login_logo.png" role="presentation" alt="">
|
||||
</div>
|
||||
|
||||
|
|
|
@ -115,7 +115,8 @@
|
|||
<ng-template #sitelisting let-site="site">
|
||||
<ion-item button (click)="connect(site.url, $event)" [ngClass]="site.className" [attr.aria-label]="site.name" [detail]="true">
|
||||
<ion-thumbnail *ngIf="siteFinderSettings.displayimage" slot="start">
|
||||
<img [src]="site.imageurl" *ngIf="site.imageurl" onError="this.src='assets/icon/icon.png'" alt="" role="presentation">
|
||||
<img [src]="site.imageurl" *ngIf="site.imageurl" core-external-content onError="this.src='assets/icon/icon.png'" alt=""
|
||||
role="presentation">
|
||||
<img src="assets/icon/icon.png" *ngIf="!site.imageurl" class="core-login-default-icon" alt="" role="presentation">
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<ion-label>
|
||||
<!-- Show site logo. -->
|
||||
<img class="core-usermenu-site-logo" *ngIf="siteLogo && siteLogoLoaded" [src]="siteLogo" role="presentation" alt=""
|
||||
onError="this.class='image-not-found'">
|
||||
core-external-content onError="this.class='image-not-found'">
|
||||
<p class="core-usermenu-sitename">
|
||||
<core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true" />
|
||||
</p>
|
||||
|
|
|
@ -75,6 +75,7 @@ export class CoreFileProvider {
|
|||
// Folders.
|
||||
static readonly SITESFOLDER = 'sites';
|
||||
static readonly TMPFOLDER = 'tmp';
|
||||
static readonly NO_SITE_FOLDER = 'nosite';
|
||||
|
||||
static readonly CHUNK_SIZE = 1048576; // 1 MB. Same chunk size as Ionic Native.
|
||||
|
||||
|
|
|
@ -1341,7 +1341,7 @@ export class CoreFilepoolProvider {
|
|||
* @param fileUrl The absolute URL to the file.
|
||||
* @returns The file ID.
|
||||
*/
|
||||
protected getFileIdByUrl(fileUrl: string): string {
|
||||
getFileIdByUrl(fileUrl: string): string {
|
||||
let url = fileUrl;
|
||||
|
||||
// If site supports it, since 3.8 we use tokenpluginfile instead of pluginfile.
|
||||
|
|
|
@ -76,4 +76,5 @@ export interface EnvironmentConfig {
|
|||
demoMode?: boolean; // Whether to run the app in "demo mode".
|
||||
hideInformativeLinks?: boolean; // Whether to hide informative links.
|
||||
iconsPrefixes?: Record<string, Record<string, string[]>>; // Prefixes for custom font icons (located in src/assets/fonts).
|
||||
clearIABSessionWhenAutoLogin?: 'android' | 'ios' | 'all'; // Clear the session every time a new IAB is opened with auto-login.
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue