MOBILE-3411 h5pactivity: Add button to download the file

main
Dani Palou 2020-05-26 15:10:58 +02:00
parent 4bbd05bd55
commit 0196a738d7
14 changed files with 385 additions and 27 deletions

View File

@ -13,5 +13,21 @@
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description> <core-course-module-description [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description>
<!-- TODO --> <ion-list *ngIf="deployedFile && !playing">
<ion-item text-wrap *ngIf="stateMessage">
<p >{{ stateMessage | translate }}</p>
</ion-item>
<!-- Button to download the package. -->
<ion-item *ngIf="!downloading && needsDownload" text-wrap>
<a ion-button block (click)="downloadAndPlay($event)">{{ 'core.download' | translate }}</a>
</ion-item>
<!-- Download progress. -->
<ion-item text-center *ngIf="downloading">
<ion-spinner></ion-spinner>
<p *ngIf="progressMessage">{{ progressMessage | translate }}</p>
<p *ngIf="percentage <= 100">{{ 'core.percentagenumber' | translate:{$a: percentage} }}</p>
</ion-item>
</ion-list>
</core-loading> </core-loading>

View File

@ -14,8 +14,20 @@
import { Component, Optional, Injector } from '@angular/core'; import { Component, Optional, Injector } from '@angular/core';
import { Content } from 'ionic-angular'; import { Content } from 'ionic-angular';
import { CoreApp } from '@providers/app';
import { CoreFilepool } from '@providers/filepool';
import { CoreWSExternalFile } from '@providers/ws';
import { CoreDomUtils } from '@providers/utils/dom';
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
import { AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData } from '../../providers/h5pactivity'; import { CoreH5P } from '@core/h5p/providers/h5p';
import { CoreH5PDisplayOptions } from '@core/h5p/classes/core';
import { CoreH5PHelper } from '@core/h5p/classes/helper';
import { CoreConstants } from '@core/constants';
import {
AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData, AddonModH5PActivityAccessInfo
} from '../../providers/h5pactivity';
/** /**
* Component that displays an H5P activity entry page. * Component that displays an H5P activity entry page.
@ -29,8 +41,18 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
moduleName = 'h5pactivity'; moduleName = 'h5pactivity';
h5pActivity: AddonModH5PActivityData; // The H5P activity object. h5pActivity: AddonModH5PActivityData; // The H5P activity object.
accessInfo: AddonModH5PActivityAccessInfo; // Info about the user capabilities.
deployedFile: CoreWSExternalFile; // The H5P deployed file.
stateMessage: string; // Message about the file state.
downloading: boolean; // Whether the H5P file is being downloaded.
needsDownload: boolean; // Whether the file needs to be downloaded.
percentage: string; // Download/unzip percentage.
progressMessage: string; // Message about download/unzip.
playing: boolean; // Whether the package is being played.
protected fetchContentDefaultError = 'addon.mod_h5pactivity.errorgetactivity'; protected fetchContentDefaultError = 'addon.mod_h5pactivity.errorgetactivity';
protected displayOptions: CoreH5PDisplayOptions;
constructor(injector: Injector, constructor(injector: Injector,
@Optional() protected content: Content) { @Optional() protected content: Content) {
@ -66,12 +88,61 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id); this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id);
this.description = this.h5pActivity.intro; this.description = this.h5pActivity.intro;
this.displayOptions = CoreH5PHelper.decodeDisplayOptions(this.h5pActivity.displayoptions);
this.dataRetrieved.emit(this.h5pActivity); this.dataRetrieved.emit(this.h5pActivity);
await Promise.all([
this.fetchAccessInfo(),
this.fetchDeployedFileData(),
]);
} finally { } finally {
this.fillContextMenu(refresh); this.fillContextMenu(refresh);
} }
} }
/**
* Fetch the access info and store it in the right variables.
*
* @return Promise resolved when done.
*/
protected async fetchAccessInfo(): Promise<void> {
this.accessInfo = await AddonModH5PActivity.instance.getAccessInformation(this.h5pActivity.id);
}
/**
* Fetch the deployed file data if needed and store it in the right variables.
*
* @return Promise resolved when done.
*/
protected async fetchDeployedFileData(): Promise<void> {
if (this.h5pActivity.deployedfile) {
// File already deployed and still valid, use this one.
this.deployedFile = this.h5pActivity.deployedfile;
} else {
if (!this.h5pActivity.package || !this.h5pActivity.package[0]) {
// Shouldn't happen.
throw 'No H5P package found.';
}
// Deploy the file in the server.
this.deployedFile = await CoreH5P.instance.getTrustedH5PFile(this.h5pActivity.package[0].fileurl, this.displayOptions);
}
await this.calculateFileStatus();
}
/**
* Calculate the status of the deployed file.
*
* @return Promise resolved when done.
*/
protected async calculateFileStatus(): Promise<void> {
const state = await CoreFilepool.instance.getFileStateByUrl(this.siteId, this.deployedFile.fileurl,
this.deployedFile.timemodified);
this.showFileState(state);
}
/** /**
* Perform the invalidate content function. * Perform the invalidate content function.
* *
@ -80,4 +151,120 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
protected invalidateContent(): Promise<any> { protected invalidateContent(): Promise<any> {
return AddonModH5PActivity.instance.invalidateActivityData(this.courseId); return AddonModH5PActivity.instance.invalidateActivityData(this.courseId);
} }
/**
* Displays some data based on the state of the main file.
*
* @param state The state of the file.
*/
protected showFileState(state: string): void {
if (state == CoreConstants.OUTDATED) {
this.stateMessage = 'addon.mod_h5pactivity.filestateoutdated';
this.needsDownload = true;
} else if (state == CoreConstants.NOT_DOWNLOADED) {
this.stateMessage = 'addon.mod_h5pactivity.filestatenotdownloaded';
this.needsDownload = true;
} else if (state == CoreConstants.DOWNLOADING) {
this.stateMessage = '';
if (!this.downloading) {
// It's being downloaded right now but the view isn't tracking it. "Restore" the download.
this.downloadDeployedFile().then(() => {
this.play();
});
}
} else {
this.stateMessage = '';
this.needsDownload = false;
}
}
/**
* Download the file and play it.
*
* @param e Click event.
* @return Promise resolved when done.
*/
async downloadAndPlay(e: MouseEvent): Promise<void> {
e && e.preventDefault();
e && e.stopPropagation();
if (!CoreApp.instance.isOnline()) {
CoreDomUtils.instance.showErrorModal('core.networkerrormsg', true);
return;
}
try {
// Confirm the download if needed.
await CoreDomUtils.instance.confirmDownloadSize({ size: this.deployedFile.filesize, total: true });
await this.downloadDeployedFile();
if (!this.isDestroyed) {
this.play();
}
} catch (error) {
if (CoreDomUtils.instance.isCanceledError(error) || this.isDestroyed) {
// User cancelled or view destroyed, stop.
return;
}
CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordownloading', true);
}
}
/**
* Download athe H5P deployed file or restores an ongoing download.
*
* @return Promise resolved when done.
*/
protected async downloadDeployedFile(): Promise<void> {
this.downloading = true;
this.progressMessage = 'core.downloading';
try {
await CoreFilepool.instance.downloadUrl(this.siteId, this.deployedFile.fileurl, false, this.component, this.componentId,
this.deployedFile.timemodified, (data) => {
if (!data) {
return;
}
if (data.message) {
// Show a message.
this.progressMessage = data.message;
this.percentage = undefined;
} else if (typeof data.loaded != 'undefined') {
if (this.progressMessage == 'core.downloading') {
// Downloading package.
this.percentage = (Number(data.loaded / this.deployedFile.filesize) * 100).toFixed(1);
} else if (typeof data.total != 'undefined') {
// Unzipping package.
this.percentage = (Number(data.loaded / data.total) * 100).toFixed(1);
} else {
this.percentage = undefined;
}
} else {
this.percentage = undefined;
}
});
} finally {
this.progressMessage = undefined;
this.percentage = undefined;
this.downloading = false;
}
}
/**
* Play the package.
*/
play(): void {
this.playing = true;
// @TODO
}
} }

View File

@ -1,4 +1,7 @@
{ {
"errorgetactivity": "Error getting H5P activity data.", "errorgetactivity": "Error getting H5P activity data.",
"modulenameplural": "H5P" "filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.",
"filestateoutdated": "The H5P package has been modified since the last download. You need to download it again to be able to use it.",
"modulenameplural": "H5P",
"storingfiles": "Storing files"
} }

View File

@ -17,6 +17,7 @@ import { Injectable } from '@angular/core';
import { CoreSites } from '@providers/sites'; import { CoreSites } from '@providers/sites';
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CoreCourseLogHelper } from '@core/course/providers/log-helper';
import { makeSingleton, Translate } from '@singletons/core.singletons'; import { makeSingleton, Translate } from '@singletons/core.singletons';
@ -29,6 +30,39 @@ export class AddonModH5PActivityProvider {
protected ROOT_CACHE_KEY = 'mmaModH5PActivity:'; protected ROOT_CACHE_KEY = 'mmaModH5PActivity:';
/**
* Get cache key for access information WS calls.
*
* @param id H5P activity ID.
* @return Cache key.
*/
protected getAccessInformationCacheKey(id: number): string {
return this.ROOT_CACHE_KEY + 'accessInfo:' + id;
}
/**
* Get access information for a given H5P activity.
*
* @param id H5P activity ID.
* @param forceCache True to always get the value from cache. false otherwise.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the data.
*/
async getAccessInformation(id: number, forceCache?: boolean, siteId?: string): Promise<AddonModH5PActivityAccessInfo> {
const site = await CoreSites.instance.getSite(siteId);
const params = {
h5pactivityid: id,
};
const preSets = {
cacheKey: this.getAccessInformationCacheKey(id),
omitExpires: forceCache,
};
return site.read('mod_h5pactivity_get_h5pactivity_access_information', params, preSets);
}
/** /**
* Get cache key for H5P activity data WS calls. * Get cache key for H5P activity data WS calls.
* *
@ -54,6 +88,7 @@ export class AddonModH5PActivityProvider {
: Promise<AddonModH5PActivityData> { : Promise<AddonModH5PActivityData> {
const site = await CoreSites.instance.getSite(siteId); const site = await CoreSites.instance.getSite(siteId);
const params = { const params = {
courseids: [courseId], courseids: [courseId],
}; };
@ -66,7 +101,7 @@ export class AddonModH5PActivityProvider {
preSets.omitExpires = true; preSets.omitExpires = true;
} }
const response: AddonModH5PActivityGetByCoursesRresult = const response: AddonModH5PActivityGetByCoursesResult =
await site.read('mod_h5pactivity_get_h5pactivities_by_courses', params, preSets); await site.read('mod_h5pactivity_get_h5pactivities_by_courses', params, preSets);
if (response && response.h5pactivities) { if (response && response.h5pactivities) {
@ -108,6 +143,20 @@ export class AddonModH5PActivityProvider {
return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId); return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId);
} }
/**
* Invalidates access information.
*
* @param id H5P activity ID.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the data is invalidated.
*/
async invalidateAccessInformation(id: number, siteId?: string): Promise<void> {
const site = await CoreSites.instance.getSite(siteId);
await site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(id));
}
/** /**
* Invalidates H5P activity data. * Invalidates H5P activity data.
* *
@ -115,10 +164,10 @@ export class AddonModH5PActivityProvider {
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the data is invalidated. * @return Promise resolved when the data is invalidated.
*/ */
async invalidateActivityData(courseId: number, siteId?: string): Promise<any> { async invalidateActivityData(courseId: number, siteId?: string): Promise<void> {
const site = await CoreSites.instance.getSite(siteId); const site = await CoreSites.instance.getSite(siteId);
return site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId)); await site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId));
} }
/** /**
@ -131,6 +180,35 @@ export class AddonModH5PActivityProvider {
return site.wsAvailable('mod_h5pactivity_get_h5pactivities_by_courses'); return site.wsAvailable('mod_h5pactivity_get_h5pactivities_by_courses');
} }
/**
* Report an H5P activity as being viewed.
*
* @param id H5P activity ID.
* @param name Name of the activity.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the WS call is successful.
*/
async logView(id: number, name?: string, siteId?: string): Promise<void> {
const params = {
h5pactivityid: id,
};
const result: AddonModH5PActivityViewResult = await CoreCourseLogHelper.instance.logSingle(
'mod_h5pactivity_view_h5pactivity',
params,
AddonModH5PActivityProvider.COMPONENT,
id,
name,
'h5pactivity',
{},
siteId
);
if (!result.status) {
throw result.warnings[0] || 'Error marking H5P activity as viewed.';
}
}
} }
export class AddonModH5PActivity extends makeSingleton(AddonModH5PActivityProvider) {} export class AddonModH5PActivity extends makeSingleton(AddonModH5PActivityProvider) {}
@ -167,7 +245,26 @@ export type AddonModH5PActivityData = {
/** /**
* Result of WS mod_h5pactivity_get_h5pactivities_by_courses. * Result of WS mod_h5pactivity_get_h5pactivities_by_courses.
*/ */
export type AddonModH5PActivityGetByCoursesRresult = { export type AddonModH5PActivityGetByCoursesResult = {
h5pactivities: AddonModH5PActivityData[]; h5pactivities: AddonModH5PActivityData[];
warnings?: CoreWSExternalWarning[]; warnings?: CoreWSExternalWarning[];
}; };
/**
* Result of WS mod_h5pactivity_get_h5pactivity_access_information.
*/
export type AddonModH5PActivityAccessInfo = {
warnings?: CoreWSExternalWarning[];
canview?: boolean; // Whether the user has the capability mod/h5pactivity:view allowed.
canaddinstance?: boolean; // Whether the user has the capability mod/h5pactivity:addinstance allowed.
cansubmit?: boolean; // Whether the user has the capability mod/h5pactivity:submit allowed.
canreviewattempts?: boolean; // Whether the user has the capability mod/h5pactivity:reviewattempts allowed.
};
/**
* Result of WS mod_h5pactivity_view_h5pactivity.
*/
export type AddonModH5PActivityViewResult = {
status: boolean; // Status: true if success.
warnings?: CoreWSExternalWarning[];
};

View File

@ -1257,7 +1257,7 @@ export class AddonModScormProvider {
/** /**
* Invalidates access information. * Invalidates access information.
* *
* @param forumId SCORM ID. * @param scormId SCORM ID.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the data is invalidated. * @return Promise resolved when the data is invalidated.
*/ */

View File

@ -660,7 +660,10 @@
"addon.mod_glossary.searchquery": "Search query", "addon.mod_glossary.searchquery": "Search query",
"addon.mod_glossary.tagarea_glossary_entries": "Glossary entries", "addon.mod_glossary.tagarea_glossary_entries": "Glossary entries",
"addon.mod_h5pactivity.errorgetactivity": "Error getting H5P activity data.", "addon.mod_h5pactivity.errorgetactivity": "Error getting H5P activity data.",
"addon.mod_h5pactivity.filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.",
"addon.mod_h5pactivity.filestateoutdated": "The H5P package has been modified since the last download. You need to download it again to be able to use it.",
"addon.mod_h5pactivity.modulenameplural": "H5P", "addon.mod_h5pactivity.modulenameplural": "H5P",
"addon.mod_h5pactivity.storingfiles": "Storing files",
"addon.mod_imscp.deploymenterror": "Content package error!", "addon.mod_imscp.deploymenterror": "Content package error!",
"addon.mod_imscp.modulenameplural": "IMS content packages", "addon.mod_imscp.modulenameplural": "IMS content packages",
"addon.mod_imscp.showmoduledescription": "Show description", "addon.mod_imscp.showmoduledescription": "Show description",

View File

@ -20,6 +20,8 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
import { makeSingleton } from '@singletons/core.singletons';
/** /**
* Helper to manage logging to Moodle. * Helper to manage logging to Moodle.
*/ */
@ -355,3 +357,5 @@ export class CoreCourseLogHelperProvider {
})); }));
} }
} }
export class CoreCourseLogHelper extends makeSingleton(CoreCourseLogHelperProvider) {}

View File

@ -16,8 +16,9 @@ import { CoreFile, CoreFileProvider } from '@providers/file';
import { CoreSites } from '@providers/sites'; import { CoreSites } from '@providers/sites';
import { CoreMimetypeUtils } from '@providers/utils/mimetype'; import { CoreMimetypeUtils } from '@providers/utils/mimetype';
import { CoreTextUtils } from '@providers/utils/text'; import { CoreTextUtils } from '@providers/utils/text';
import { CoreUtils } from '@providers/utils/utils';
import { CoreH5P } from '../providers/h5p'; import { CoreH5P } from '../providers/h5p';
import { CoreH5PCore } from './core'; import { CoreH5PCore, CoreH5PDisplayOptions } from './core';
import { FileEntry } from '@ionic-native/file'; import { FileEntry } from '@ionic-native/file';
/** /**
@ -25,6 +26,25 @@ import { FileEntry } from '@ionic-native/file';
*/ */
export class CoreH5PHelper { export class CoreH5PHelper {
/**
* Convert the number representation of display options into an object.
*
* @param displayOptions Number representing display options.
* @return Object with display options.
*/
static decodeDisplayOptions(displayOptions: number): CoreH5PDisplayOptions {
const config: any = {};
const displayOptionsObject = CoreH5P.instance.h5pCore.getDisplayOptionsAsObject(displayOptions);
config.export = 0; // Don't allow downloading in the app.
config.embed = CoreUtils.instance.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_EMBED]) ?
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_EMBED] : 0;
config.copyright = CoreUtils.instance.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT]) ?
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] : 0;
return config;
}
/** /**
* Get the core H5P assets, including all core H5P JavaScript and CSS. * Get the core H5P assets, including all core H5P JavaScript and CSS.
* *
@ -107,19 +127,25 @@ export class CoreH5PHelper {
* @param fileUrl The file URL used to download the file. * @param fileUrl The file URL used to download the file.
* @param file The file entry of the downloaded file. * @param file The file entry of the downloaded file.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @param onProgress Function to call on progress.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
static async saveH5P(fileUrl: string, file: FileEntry, siteId?: string): Promise<void> { static async saveH5P(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: (event: any) => any): Promise<void> {
siteId = siteId || CoreSites.instance.getCurrentSiteId(); siteId = siteId || CoreSites.instance.getCurrentSiteId();
// Unzip the file.
const folderName = CoreMimetypeUtils.instance.removeExtension(file.name); const folderName = CoreMimetypeUtils.instance.removeExtension(file.name);
const destFolder = CoreTextUtils.instance.concatenatePaths(CoreFileProvider.TMPFOLDER, 'h5p/' + folderName); const destFolder = CoreTextUtils.instance.concatenatePaths(CoreFileProvider.TMPFOLDER, 'h5p/' + folderName);
// Notify that the unzip is starting.
onProgress && onProgress({message: 'core.unzipping'});
// Unzip the file. // Unzip the file.
await CoreFile.instance.unzipFile(file.toURL(), destFolder); await CoreFile.instance.unzipFile(file.toURL(), destFolder, onProgress);
try { try {
// Notify that the unzip is starting.
onProgress && onProgress({message: 'addon.mod_h5pactivity.storingfiles'});
// Read the contents of the unzipped dir, process them and store them. // Read the contents of the unzipped dir, process them and store them.
const contents = await CoreFile.instance.getDirectoryContents(destFolder); const contents = await CoreFile.instance.getDirectoryContents(destFolder);

View File

@ -219,11 +219,11 @@ export class CoreH5PPlayer {
* Get the content index file. * Get the content index file.
* *
* @param fileUrl URL of the H5P package. * @param fileUrl URL of the H5P package.
* @param urlParams URL params. * @param displayOptions Display options.
* @param siteId The site ID. If not defined, current site. * @param siteId The site ID. If not defined, current site.
* @return Promise resolved with the file URL if exists, rejected otherwise. * @return Promise resolved with the file URL if exists, rejected otherwise.
*/ */
async getContentIndexFileUrl(fileUrl: string, urlParams?: {[name: string]: string}, siteId?: string): Promise<string> { async getContentIndexFileUrl(fileUrl: string, displayOptions?: CoreH5PDisplayOptions, siteId?: string): Promise<string> {
siteId = siteId || CoreSites.instance.getCurrentSiteId(); siteId = siteId || CoreSites.instance.getCurrentSiteId();
const path = await this.h5pCore.h5pFS.getContentIndexFileUrl(fileUrl, siteId); const path = await this.h5pCore.h5pFS.getContentIndexFileUrl(fileUrl, siteId);
@ -231,9 +231,9 @@ export class CoreH5PPlayer {
// Add display options to the URL. // Add display options to the URL.
const data = await this.h5pCore.h5pFramework.getContentDataByUrl(fileUrl, siteId); const data = await this.h5pCore.h5pFramework.getContentDataByUrl(fileUrl, siteId);
const options = this.h5pCore.fixDisplayOptions(this.getDisplayOptionsFromUrlParams(urlParams), data.id); displayOptions = this.h5pCore.fixDisplayOptions(displayOptions, data.id);
return CoreUrlUtils.instance.addParamsToUrl(path, options, undefined, true); return CoreUrlUtils.instance.addParamsToUrl(path, displayOptions, undefined, true);
} }
/** /**

View File

@ -93,11 +93,12 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
this.loading = true; this.loading = true;
let localUrl: string; let localUrl: string;
const displayOptions = CoreH5P.instance.h5pPlayer.getDisplayOptionsFromUrlParams(this.urlParams);
if (this.canDownload && CoreFileHelper.instance.isStateDownloaded(this.state)) { if (this.canDownload && CoreFileHelper.instance.isStateDownloaded(this.state)) {
// Package is downloaded, use the local URL. // Package is downloaded, use the local URL.
try { try {
localUrl = await CoreH5P.instance.h5pPlayer.getContentIndexFileUrl(this.urlParams.url, this.urlParams, this.siteId); localUrl = await CoreH5P.instance.h5pPlayer.getContentIndexFileUrl(this.urlParams.url, displayOptions, this.siteId);
} catch (error) { } catch (error) {
// Index file doesn't exist, probably deleted because a lib was updated. Try to create it again. // Index file doesn't exist, probably deleted because a lib was updated. Try to create it again.
try { try {
@ -108,7 +109,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
await CoreH5PHelper.saveH5P(this.urlParams.url, file, this.siteId); await CoreH5PHelper.saveH5P(this.urlParams.url, file, this.siteId);
// File treated. Try to get the index file URL again. // File treated. Try to get the index file URL again.
localUrl = await CoreH5P.instance.h5pPlayer.getContentIndexFileUrl(this.urlParams.url, this.urlParams, localUrl = await CoreH5P.instance.h5pPlayer.getContentIndexFileUrl(this.urlParams.url, displayOptions,
this.siteId); this.siteId);
} catch (error) { } catch (error) {
// Still failing. Delete the H5P package? // Still failing. Delete the H5P package?

View File

@ -18,6 +18,7 @@ import { CoreMimetypeUtils } from '@providers/utils/mimetype';
import { CoreUrlUtils } from '@providers/utils/url'; import { CoreUrlUtils } from '@providers/utils/url';
import { CoreUtils } from '@providers/utils/utils'; import { CoreUtils } from '@providers/utils/utils';
import { CoreH5P } from './h5p'; import { CoreH5P } from './h5p';
import { CoreSites } from '@providers/sites';
import { CoreWSExternalFile } from '@providers/ws'; import { CoreWSExternalFile } from '@providers/ws';
import { FileEntry } from '@ionic-native/file'; import { FileEntry } from '@ionic-native/file';
import { Translate } from '@singletons/core.singletons'; import { Translate } from '@singletons/core.singletons';
@ -50,7 +51,14 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the file to use. Rejected if cannot download. * @return Promise resolved with the file to use. Rejected if cannot download.
*/ */
getDownloadableFile(file: CoreWSExternalFile, siteId?: string): Promise<CoreWSExternalFile> { async getDownloadableFile(file: CoreWSExternalFile, siteId?: string): Promise<CoreWSExternalFile> {
const site = await CoreSites.instance.getSite(siteId);
if (site.containsUrl(file.fileurl) && file.fileurl.match(/pluginfile\.php\/[^\/]+\/core_h5p\/export\//i)) {
// It's already a deployed file, use it.
return file;
}
return CoreH5P.instance.getTrustedH5PFile(file.fileurl, {}, false, siteId); return CoreH5P.instance.getTrustedH5PFile(file.fileurl, {}, false, siteId);
} }
@ -85,7 +93,7 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
*/ */
async getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> { async getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
try { try {
const trustedFile = await CoreH5P.instance.getTrustedH5PFile(file.fileurl, {}, false, siteId); const trustedFile = await this.getDownloadableFile(file, siteId);
return trustedFile.filesize; return trustedFile.filesize;
} catch (error) { } catch (error) {
@ -145,9 +153,10 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
* @param fileUrl The file URL used to download the file. * @param fileUrl The file URL used to download the file.
* @param file The file entry of the downloaded file. * @param file The file entry of the downloaded file.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @param onProgress Function to call on progress.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string): Promise<void> { treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: (event: any) => any): Promise<void> {
return CoreH5PHelper.saveH5P(fileUrl, file, siteId); return CoreH5PHelper.saveH5P(fileUrl, file, siteId, onProgress);
} }
} }

View File

@ -1058,7 +1058,7 @@ export class CoreFilepoolProvider {
return this.wsProvider.downloadFile(fileUrl, filePath, addExtension, onProgress).then((entry) => { return this.wsProvider.downloadFile(fileUrl, filePath, addExtension, onProgress).then((entry) => {
fileEntry = entry; fileEntry = entry;
return this.pluginFileDelegate.treatDownloadedFile(fileUrl, fileEntry, siteId); return this.pluginFileDelegate.treatDownloadedFile(fileUrl, fileEntry, siteId, onProgress);
}).then(() => { }).then(() => {
const data: CoreFilepoolFileEntry = poolFileObject || {}; const data: CoreFilepoolFileEntry = poolFileObject || {};

View File

@ -108,9 +108,10 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
* @param fileUrl The file URL used to download the file. * @param fileUrl The file URL used to download the file.
* @param file The file entry of the downloaded file. * @param file The file entry of the downloaded file.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @param onProgress Function to call on progress.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string): Promise<any>; treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: (event: any) => any): Promise<any>;
} }
/** /**
@ -360,13 +361,14 @@ export class CorePluginFileDelegate extends CoreDelegate {
* @param fileUrl The file URL used to download the file. * @param fileUrl The file URL used to download the file.
* @param file The file entry of the downloaded file. * @param file The file entry of the downloaded file.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @param onProgress Function to call on progress.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string): Promise<any> { treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string, onProgress?: (event: any) => any): Promise<any> {
const handler = this.getHandlerForFile({fileurl: fileUrl}); const handler = this.getHandlerForFile({fileurl: fileUrl});
if (handler && handler.treatDownloadedFile) { if (handler && handler.treatDownloadedFile) {
return handler.treatDownloadedFile(fileUrl, file, siteId); return handler.treatDownloadedFile(fileUrl, file, siteId, onProgress);
} }
return Promise.resolve(); return Promise.resolve();

View File

@ -872,6 +872,16 @@ export class CoreUtilsProvider {
return this.uniqueArray(array1.concat(array2), key); return this.uniqueArray(array1.concat(array2), key);
} }
/**
* Check if a value isn't null or undefined.
*
* @param value Value to check.
* @return True if not null and not undefined.
*/
notNullOrUndefined(value: any): boolean {
return typeof value != 'undefined' && value !== null;
}
/** /**
* Open a file using platform specific method. * Open a file using platform specific method.
* *