MOBILE-2235 h5p: Display download button in H5P placeholder
parent
690544afbf
commit
9b637fc496
|
@ -1,4 +1,4 @@
|
|||
<div [class.core-loading-container]="loading">
|
||||
<div [class.core-loading-container]="loading" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}">
|
||||
<iframe #iframe [hidden]="loading" class="core-iframe" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl"></iframe>
|
||||
<span class="core-loading-spinner">
|
||||
<ion-spinner *ngIf="loading"></ion-spinner>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
ion-app.app-root core-iframe {
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
iframe {
|
||||
border: 0;
|
||||
display: block;
|
||||
|
|
|
@ -30,6 +30,7 @@ import { CORE_COURSES_PROVIDERS } from '@core/courses/courses.module';
|
|||
import { CORE_FILEUPLOADER_PROVIDERS } from '@core/fileuploader/fileuploader.module';
|
||||
import { CORE_FILTER_PROVIDERS } from '@core/filter/filter.module';
|
||||
import { CORE_GRADES_PROVIDERS } from '@core/grades/grades.module';
|
||||
import { CORE_H5P_PROVIDERS } from '@core/h5p/h5p.module';
|
||||
import { CORE_LOGIN_PROVIDERS } from '@core/login/login.module';
|
||||
import { CORE_MAINMENU_PROVIDERS } from '@core/mainmenu/mainmenu.module';
|
||||
import { CORE_QUESTION_PROVIDERS } from '@core/question/question.module';
|
||||
|
@ -236,7 +237,7 @@ export class CoreCompileProvider {
|
|||
.concat(ADDON_MOD_SURVEY_PROVIDERS).concat(ADDON_MOD_URL_PROVIDERS).concat(ADDON_MOD_WIKI_PROVIDERS)
|
||||
.concat(ADDON_MOD_WORKSHOP_PROVIDERS).concat(ADDON_NOTES_PROVIDERS).concat(ADDON_NOTIFICATIONS_PROVIDERS)
|
||||
.concat(CORE_PUSHNOTIFICATIONS_PROVIDERS).concat(ADDON_REMOTETHEMES_PROVIDERS).concat(CORE_BLOCK_PROVIDERS)
|
||||
.concat(CORE_FILTER_PROVIDERS);
|
||||
.concat(CORE_FILTER_PROVIDERS).concat(CORE_H5P_PROVIDERS);
|
||||
|
||||
// We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
|
||||
for (const i in providers) {
|
||||
|
|
|
@ -402,7 +402,7 @@ export type CoreFilterFilter = {
|
|||
*/
|
||||
export type CoreFilterGetAvailableInContextResult = {
|
||||
filters: CoreFilterFilter[]; // Available filters.
|
||||
warning: CoreWSExternalWarning[]; // List of warnings.
|
||||
warnings: CoreWSExternalWarning[]; // List of warnings.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<div *ngIf="!showPackage" class="core-h5p-placeholder">
|
||||
<button *ngIf="!loading" class="core-h5p-placeholder-play-button" ion-button icon-only clear color="dark" (click)="play($event)">
|
||||
<button *ngIf="!loading && !errorMessage" class="core-h5p-placeholder-play-button" ion-button icon-only clear (click)="play($event)">
|
||||
<core-icon name="fa-play-circle"></core-icon>
|
||||
</button>
|
||||
|
||||
<ion-spinner *ngIf="loading" class="core-h5p-placeholder-spinner"></ion-spinner>
|
||||
<ion-spinner *ngIf="loading && !errorMessage" class="core-h5p-placeholder-spinner"></ion-spinner>
|
||||
|
||||
<div *ngIf="errorMessage" class="core-h5p-placeholder-error">{{ errorMessage }}</div>
|
||||
|
||||
<div class="core-h5p-placeholder-download-container">
|
||||
<core-download-refresh [status]="state" [enabled]="canDownload" [loading]="calculating" [canTrustDownload]="true" (action)="download()"></core-download-refresh>
|
||||
</div>
|
||||
</div>
|
||||
<core-iframe *ngIf="showPackage" [src]="src"></core-iframe>
|
||||
<core-iframe *ngIf="showPackage" [src]="playerSrc" iframeHeight="230px"></core-iframe>
|
||||
|
|
|
@ -1,23 +1,56 @@
|
|||
// H5P variables.
|
||||
$core-h5p-placeholder-bg-color: $gray !default;
|
||||
$core-h5p-placeholder-text-color: $text-color !default;
|
||||
|
||||
ion-app.app-root core-h5p-player {
|
||||
.core-h5p-placeholder {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 230px;
|
||||
background: url('../assets/img/icons/h5p.svg') center top 25px / 100px auto no-repeat $core-h5p-placeholder-bg-color;
|
||||
color: $core-h5p-placeholder-text-color;
|
||||
|
||||
.core-h5p-placeholder-play-button {
|
||||
.icon {
|
||||
color: $core-h5p-placeholder-text-color;
|
||||
}
|
||||
|
||||
.core-h5p-placeholder-play-button, .core-h5p-placeholder-spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.core-h5p-placeholder-play-button {
|
||||
font-size: 30px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.core-h5p-placeholder-download-container {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
font-size: 1.8em;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
ion-spinner {
|
||||
margin-right: 0.75em;
|
||||
}
|
||||
|
||||
core-download-refresh > ion-icon {
|
||||
margin: 0.4rem 0.2rem;
|
||||
padding: 0 0.5em;
|
||||
line-height: .67;
|
||||
}
|
||||
}
|
||||
|
||||
.core-h5p-placeholder-error {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
ion-spinner circle {
|
||||
stroke: $core-h5p-placeholder-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, ElementRef } from '@angular/core';
|
||||
import { Component, Input, ElementRef, OnInit, SimpleChange } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreH5PProvider } from '@core/h5p/providers/h5p';
|
||||
|
||||
/**
|
||||
* Component to render an H5P package.
|
||||
|
@ -22,17 +26,39 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
selector: 'core-h5p-player',
|
||||
templateUrl: 'core-h5p-player.html'
|
||||
})
|
||||
export class CoreH5PPlayerComponent {
|
||||
export class CoreH5PPlayerComponent implements OnInit {
|
||||
@Input() src: string; // The URL of the player to display the H5P package.
|
||||
|
||||
playerSrc: string;
|
||||
showPackage = false;
|
||||
loading = false;
|
||||
status: string;
|
||||
canDownload: boolean;
|
||||
calculating = true;
|
||||
errorMessage: string;
|
||||
|
||||
constructor(public elementRef: ElementRef,
|
||||
protected sitesProvider: CoreSitesProvider) {
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected h5pProvider: CoreH5PProvider) { }
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.checkCanDownload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||
// If it's already playing and the src changes, don't change the player src, the user could lose data.
|
||||
if (changes.src && !this.showPackage) {
|
||||
this.checkCanDownload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,11 +72,57 @@ export class CoreH5PPlayerComponent {
|
|||
|
||||
this.loading = true;
|
||||
|
||||
// @TODO: Check if package is downloaded and use the local player if so.
|
||||
|
||||
// Get auto-login URL so the user is automatically authenticated.
|
||||
this.sitesProvider.getCurrentSite().getAutoLoginUrl(this.src, false).then((url) => {
|
||||
this.src = url;
|
||||
this.playerSrc = url;
|
||||
this.loading = false;
|
||||
this.showPackage = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the package.
|
||||
*/
|
||||
download(): void {
|
||||
// @TODO: Implement package download.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the package can be downloaded.
|
||||
*/
|
||||
protected checkCanDownload(): void {
|
||||
if (this.src && this.h5pProvider.canGetTrustedH5PFileInSite()) {
|
||||
const params = this.urlUtils.extractUrlParams(this.src);
|
||||
|
||||
// @todo: Check if H5P offline is disabled in the site.
|
||||
|
||||
// Now check if the package can be played.
|
||||
this.calculating = true;
|
||||
|
||||
const options = {
|
||||
frame: this.utils.isTrueOrOne(params.frame),
|
||||
export: this.utils.isTrueOrOne(params.export),
|
||||
embed: this.utils.isTrueOrOne(params.embed),
|
||||
copyright: this.utils.isTrueOrOne(params.copyright),
|
||||
};
|
||||
|
||||
this.h5pProvider.getTrustedH5PFile(params.url, options).then((file) => {
|
||||
this.canDownload = true;
|
||||
this.errorMessage = undefined;
|
||||
}).catch((error) => {
|
||||
this.canDownload = false;
|
||||
this.errorMessage = this.textUtils.getErrorMessageFromError(error);
|
||||
}).finally(() => {
|
||||
this.calculating = false;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.calculating = false;
|
||||
this.canDownload = false;
|
||||
this.errorMessage = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreH5PComponentsModule } from './components/components.module';
|
||||
import { CoreH5PProvider } from './providers/h5p';
|
||||
|
||||
// List of providers (without handlers).
|
||||
export const CORE_H5P_PROVIDERS: any[] = [
|
||||
CoreH5PProvider
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
|
@ -21,6 +27,7 @@ import { CoreH5PComponentsModule } from './components/components.module';
|
|||
CoreH5PComponentsModule
|
||||
],
|
||||
providers: [
|
||||
CoreH5PProvider
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service to provide H5P functionalities.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreH5PProvider {
|
||||
|
||||
protected ROOT_CACHE_KEY = 'mmH5P:';
|
||||
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider,
|
||||
private sitesProvider: CoreSitesProvider) {
|
||||
|
||||
this.logger = logger.getInstance('CoreFilterProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not WS to get trusted H5P file is available.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with true if ws is available, false otherwise.
|
||||
* @since 3.8
|
||||
*/
|
||||
canGetTrustedH5PFile(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.canGetTrustedH5PFileInSite(site);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not WS to get trusted H5P file is available in a certain site.
|
||||
*
|
||||
* @param site Site. If not defined, current site.
|
||||
* @return Promise resolved with true if ws is available, false otherwise.
|
||||
* @since 3.4
|
||||
*/
|
||||
canGetTrustedH5PFileInSite(site?: CoreSite): boolean {
|
||||
site = site || this.sitesProvider.getCurrentSite();
|
||||
|
||||
return site.wsAvailable('core_h5p_get_trusted_h5p_file');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a trusted H5P file.
|
||||
*
|
||||
* @param url The file URL.
|
||||
* @param options Options.
|
||||
* @param ignoreCache Whether to ignore cache..
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the file data.
|
||||
*/
|
||||
getTrustedH5PFile(url: string, options: CoreH5PGetTrustedFileOptions, ignoreCache?: boolean, siteId?: string)
|
||||
: Promise<CoreWSExternalFile> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
||||
const data = {
|
||||
url: url,
|
||||
frame: options.frame ? 1 : 0,
|
||||
export: options.export ? 1 : 0,
|
||||
embed: options.embed ? 1 : 0,
|
||||
copyright: options.copyright ? 1 : 0,
|
||||
},
|
||||
preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getTrustedH5PFileCacheKey(url),
|
||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||
};
|
||||
|
||||
if (ignoreCache) {
|
||||
preSets.getFromCache = false;
|
||||
preSets.emergencyCache = false;
|
||||
}
|
||||
|
||||
return site.read('core_h5p_get_trusted_h5p_file', data, preSets).then((result: CoreH5PGetTrustedH5PFileResult): any => {
|
||||
if (result.warnings && result.warnings.length) {
|
||||
return Promise.reject(result.warnings[0]);
|
||||
}
|
||||
|
||||
if (result.files && result.files.length) {
|
||||
return result.files[0];
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for trusted H5P file WS calls.
|
||||
*
|
||||
* @param url The file URL.
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getTrustedH5PFileCacheKey(url: string): string {
|
||||
return this.getTrustedH5PFilePrefixCacheKey() + url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prefixed cache key for trusted H5P file WS calls.
|
||||
*
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getTrustedH5PFilePrefixCacheKey(): string {
|
||||
return this.ROOT_CACHE_KEY + 'trustedH5PFile:';
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all trusted H5P file WS calls.
|
||||
*
|
||||
* @param siteId Site ID (empty for current site).
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateAllGetTrustedH5PFile(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKeyStartingWith(this.getTrustedH5PFilePrefixCacheKey());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates get trusted H5P file WS call.
|
||||
*
|
||||
* @param url The URL of the file.
|
||||
* @param siteId Site ID (empty for current site).
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateAvailableInContexts(url: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getTrustedH5PFileCacheKey(url));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for core_h5p_get_trusted_h5p_file.
|
||||
*/
|
||||
export type CoreH5PGetTrustedFileOptions = {
|
||||
frame?: boolean; // Whether to show the bar options below the content.
|
||||
export?: boolean; // Whether to allow to download the package.
|
||||
embed?: boolean; // Whether to allow to copy the code to your site.
|
||||
copyright?: boolean; // The copyright option.
|
||||
};
|
||||
|
||||
/**
|
||||
* Result of core_h5p_get_trusted_h5p_file.
|
||||
*/
|
||||
export type CoreH5PGetTrustedH5PFileResult = {
|
||||
files: CoreWSExternalFile[]; // Files.
|
||||
warnings: CoreWSExternalWarning[]; // List of warnings.
|
||||
};
|
|
@ -369,9 +369,6 @@ $core-question-state-incorrect-color: $red-light !default;
|
|||
$core-dd-question-selected-shadow: 2px 2px 4px $gray-dark !default;
|
||||
$core-dd-question-colors: $white, $blue-light, #DCDCDC, #D8BFD8, #87CEFA, #DAA520, #FFD700, #F0E68C !default;
|
||||
|
||||
// H5P variables.
|
||||
$core-h5p-placeholder-bg-color: $gray-dark !default;
|
||||
|
||||
// Mixins
|
||||
// -------------------------
|
||||
@mixin core-transition($where: all, $time: 500ms) {
|
||||
|
|
Loading…
Reference in New Issue