Skip to content

Commit a8e336f

Browse files
committed
MOBILE-3411 h5pactivity: Play package in activity
1 parent 0196a73 commit a8e336f

File tree

14 files changed

+311
-113
lines changed

14 files changed

+311
-113
lines changed

src/addon/mod/h5pactivity/components/components.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { TranslateModule } from '@ngx-translate/core';
1919
import { CoreComponentsModule } from '@components/components.module';
2020
import { CoreDirectivesModule } from '@directives/directives.module';
2121
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
22+
import { CoreH5PComponentsModule } from '@core/h5p/components/components.module';
2223
import { AddonModH5PActivityIndexComponent } from './index/index';
2324

2425
@NgModule({
@@ -31,7 +32,8 @@ import { AddonModH5PActivityIndexComponent } from './index/index';
3132
TranslateModule.forChild(),
3233
CoreComponentsModule,
3334
CoreDirectivesModule,
34-
CoreCourseComponentsModule
35+
CoreCourseComponentsModule,
36+
CoreH5PComponentsModule,
3537
],
3638
providers: [
3739
],

src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description>
1515

16+
<!-- Offline disabled. -->
17+
<ion-card class="core-warning-card" icon-start *ngIf="!siteCanDownload && playing">
18+
<ion-icon name="warning"></ion-icon> {{ 'core.h5p.offlinedisabled' | translate }} {{ 'addon.mod_h5pactivity.offlinedisabledwarning' | translate }}
19+
</ion-card>
20+
1621
<ion-list *ngIf="deployedFile && !playing">
1722
<ion-item text-wrap *ngIf="stateMessage">
1823
<p >{{ stateMessage | translate }}</p>
@@ -30,4 +35,6 @@
3035
<p *ngIf="percentage <= 100">{{ 'core.percentagenumber' | translate:{$a: percentage} }}</p>
3136
</ion-item>
3237
</ion-list>
38+
39+
<core-h5p-iframe *ngIf="playing" [fileUrl]="fileUrl" [displayOptions]="displayOptions" [onlinePlayerUrl]="onlinePlayerUrl"></core-h5p-iframe>
3340
</core-loading>

src/addon/mod/h5pactivity/components/index/index.ts

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Component, Optional, Injector } from '@angular/core';
1616
import { Content } from 'ionic-angular';
1717

1818
import { CoreApp } from '@providers/app';
19+
import { CoreEvents } from '@providers/events';
1920
import { CoreFilepool } from '@providers/filepool';
2021
import { CoreWSExternalFile } from '@providers/ws';
2122
import { CoreDomUtils } from '@providers/utils/dom';
@@ -24,6 +25,7 @@ import { CoreH5P } from '@core/h5p/providers/h5p';
2425
import { CoreH5PDisplayOptions } from '@core/h5p/classes/core';
2526
import { CoreH5PHelper } from '@core/h5p/classes/helper';
2627
import { CoreConstants } from '@core/constants';
28+
import { CoreSite } from '@classes/site';
2729

2830
import {
2931
AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData, AddonModH5PActivityAccessInfo
@@ -50,13 +52,22 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
5052
percentage: string; // Download/unzip percentage.
5153
progressMessage: string; // Message about download/unzip.
5254
playing: boolean; // Whether the package is being played.
55+
displayOptions: CoreH5PDisplayOptions; // Display options for the package.
56+
onlinePlayerUrl: string; // URL to play the package in online.
57+
fileUrl: string; // The fileUrl to use to play the package.
58+
state: string; // State of the file.
59+
siteCanDownload: boolean;
5360

5461
protected fetchContentDefaultError = 'addon.mod_h5pactivity.errorgetactivity';
55-
protected displayOptions: CoreH5PDisplayOptions;
62+
protected site: CoreSite;
63+
protected observer;
5664

5765
constructor(injector: Injector,
5866
@Optional() protected content: Content) {
5967
super(injector, content);
68+
69+
this.site = this.sitesProvider.getCurrentSite();
70+
this.siteCanDownload = this.site.canDownloadFiles() && !CoreH5P.instance.isOfflineDisabledInSite();
6071
}
6172

6273
/**
@@ -87,14 +98,25 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
8798
try {
8899
this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id);
89100

101+
this.dataRetrieved.emit(this.h5pActivity);
90102
this.description = this.h5pActivity.intro;
91103
this.displayOptions = CoreH5PHelper.decodeDisplayOptions(this.h5pActivity.displayoptions);
92-
this.dataRetrieved.emit(this.h5pActivity);
104+
105+
if (this.h5pActivity.package && this.h5pActivity.package[0]) {
106+
// The online player should use the original file, not the trusted one.
107+
this.onlinePlayerUrl = CoreH5P.instance.h5pPlayer.calculateOnlinePlayerUrl(
108+
this.site.getURL(), this.h5pActivity.package[0].fileurl, this.displayOptions);
109+
}
93110

94111
await Promise.all([
95112
this.fetchAccessInfo(),
96113
this.fetchDeployedFileData(),
97114
]);
115+
116+
if (!this.siteCanDownload || this.state == CoreConstants.DOWNLOADED) {
117+
// Cannot download the file or already downloaded, play the package directly.
118+
this.play();
119+
}
98120
} finally {
99121
this.fillContextMenu(refresh);
100122
}
@@ -115,6 +137,11 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
115137
* @return Promise resolved when done.
116138
*/
117139
protected async fetchDeployedFileData(): Promise<void> {
140+
if (!this.siteCanDownload) {
141+
// Cannot download the file, no need to fetch the file data.
142+
return;
143+
}
144+
118145
if (this.h5pActivity.deployedfile) {
119146
// File already deployed and still valid, use this one.
120147
this.deployedFile = this.h5pActivity.deployedfile;
@@ -128,19 +155,30 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
128155
this.deployedFile = await CoreH5P.instance.getTrustedH5PFile(this.h5pActivity.package[0].fileurl, this.displayOptions);
129156
}
130157

131-
await this.calculateFileStatus();
158+
this.fileUrl = this.deployedFile.fileurl;
159+
160+
// Listen for changes in the state.
161+
const eventName = await CoreFilepool.instance.getFileEventNameByUrl(this.siteId, this.deployedFile.fileurl);
162+
163+
if (!this.observer) {
164+
this.observer = CoreEvents.instance.on(eventName, () => {
165+
this.calculateFileState();
166+
});
167+
}
168+
169+
await this.calculateFileState();
132170
}
133171

134172
/**
135-
* Calculate the status of the deployed file.
173+
* Calculate the state of the deployed file.
136174
*
137175
* @return Promise resolved when done.
138176
*/
139-
protected async calculateFileStatus(): Promise<void> {
140-
const state = await CoreFilepool.instance.getFileStateByUrl(this.siteId, this.deployedFile.fileurl,
177+
protected async calculateFileState(): Promise<void> {
178+
this.state = await CoreFilepool.instance.getFileStateByUrl(this.siteId, this.deployedFile.fileurl,
141179
this.deployedFile.timemodified);
142180

143-
this.showFileState(state);
181+
this.showFileState();
144182
}
145183

146184
/**
@@ -154,18 +192,16 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
154192

155193
/**
156194
* Displays some data based on the state of the main file.
157-
*
158-
* @param state The state of the file.
159195
*/
160-
protected showFileState(state: string): void {
196+
protected showFileState(): void {
161197

162-
if (state == CoreConstants.OUTDATED) {
198+
if (this.state == CoreConstants.OUTDATED) {
163199
this.stateMessage = 'addon.mod_h5pactivity.filestateoutdated';
164200
this.needsDownload = true;
165-
} else if (state == CoreConstants.NOT_DOWNLOADED) {
201+
} else if (this.state == CoreConstants.NOT_DOWNLOADED) {
166202
this.stateMessage = 'addon.mod_h5pactivity.filestatenotdownloaded';
167203
this.needsDownload = true;
168-
} else if (state == CoreConstants.DOWNLOADING) {
204+
} else if (this.state == CoreConstants.DOWNLOADING) {
169205
this.stateMessage = '';
170206

171207
if (!this.downloading) {
@@ -264,7 +300,12 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
264300
*/
265301
play(): void {
266302
this.playing = true;
303+
}
267304

268-
// @TODO
305+
/**
306+
* Component destroyed.
307+
*/
308+
ngOnDestroy(): void {
309+
this.observer && this.observer.off();
269310
}
270311
}

src/addon/mod/h5pactivity/lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.",
44
"filestateoutdated": "The H5P package has been modified since the last download. You need to download it again to be able to use it.",
55
"modulenameplural": "H5P",
6+
"offlinedisabledwarning": "You will need to be online to view the H5P package.",
67
"storingfiles": "Storing files"
78
}

src/assets/lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@
663663
"addon.mod_h5pactivity.filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.",
664664
"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.",
665665
"addon.mod_h5pactivity.modulenameplural": "H5P",
666+
"addon.mod_h5pactivity.offlinedisabledwarning": "You will need to be online to view the H5P package.",
666667
"addon.mod_h5pactivity.storingfiles": "Storing files",
667668
"addon.mod_imscp.deploymenterror": "Content package error!",
668669
"addon.mod_imscp.modulenameplural": "IMS content packages",

src/core/h5p/classes/file-storage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export class CoreH5PFileStorage {
222222
await Array.from(result.rows).map(async (entry: {foldername: string}) => {
223223
try {
224224
// Delete the index.html.
225-
await CoreFile.instance.removeFile(this.getContentIndexPath(entry.foldername, site.getId()));
225+
await this.deleteContentIndex(entry.foldername, site.getId());
226226
} catch (error) {
227227
// Ignore errors.
228228
}

src/core/h5p/classes/helper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ export class CoreH5PHelper {
3636
const config: any = {};
3737
const displayOptionsObject = CoreH5P.instance.h5pCore.getDisplayOptionsAsObject(displayOptions);
3838

39-
config.export = 0; // Don't allow downloading in the app.
39+
config.export = false; // Don't allow downloading in the app.
4040
config.embed = CoreUtils.instance.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_EMBED]) ?
41-
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_EMBED] : 0;
41+
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_EMBED] : false;
4242
config.copyright = CoreUtils.instance.notNullOrUndefined(displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT]) ?
43-
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] : 0;
43+
displayOptionsObject[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] : false;
4444

4545
return config;
4646
}

src/core/h5p/classes/player.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ export class CoreH5PPlayer {
3030
constructor(protected h5pCore: CoreH5PCore,
3131
protected h5pStorage: CoreH5PStorage) { }
3232

33+
/**
34+
* Calculate the URL to the site H5P player.
35+
*
36+
* @param siteUrl Site URL.
37+
* @param fileUrl File URL.
38+
* @param displayOptions Display options.
39+
* @param component Component to send xAPI events to.
40+
* @return URL.
41+
*/
42+
calculateOnlinePlayerUrl(siteUrl: string, fileUrl: string, displayOptions?: CoreH5PDisplayOptions, component?: string): string {
43+
const params = this.getUrlParamsFromDisplayOptions(displayOptions);
44+
params.url = fileUrl;
45+
if (component) {
46+
params.component = component;
47+
}
48+
49+
return CoreUrlUtils.instance.addParamsToUrl(CoreTextUtils.instance.concatenatePaths(siteUrl, '/h5p/embed.php'), params);
50+
}
51+
3352
/**
3453
* Create the index.html to render an H5P package.
3554
* Part of the code of this function is equivalent to Moodle's add_assets_to_page function.
@@ -323,4 +342,25 @@ export class CoreH5PPlayer {
323342
getResizerScriptUrl(): string {
324343
return CoreTextUtils.instance.concatenatePaths(this.h5pCore.h5pFS.getCoreH5PPath(), 'js/h5p-resizer.js');
325344
}
345+
346+
/**
347+
* Get online player URL params from display options.
348+
*
349+
* @param options Display options.
350+
* @return Object with URL params.
351+
*/
352+
getUrlParamsFromDisplayOptions(options: CoreH5PDisplayOptions): {[name: string]: string} {
353+
const params: {[name: string]: string} = {};
354+
355+
if (!options) {
356+
return params;
357+
}
358+
359+
params[CoreH5PCore.DISPLAY_OPTION_FRAME] = options[CoreH5PCore.DISPLAY_OPTION_FRAME] ? '1' : '0';
360+
params[CoreH5PCore.DISPLAY_OPTION_DOWNLOAD] = options[CoreH5PCore.DISPLAY_OPTION_DOWNLOAD] ? '1' : '0';
361+
params[CoreH5PCore.DISPLAY_OPTION_EMBED] = options[CoreH5PCore.DISPLAY_OPTION_EMBED] ? '1' : '0';
362+
params[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] = options[CoreH5PCore.DISPLAY_OPTION_COPYRIGHT] ? '1' : '0';
363+
364+
return params;
365+
}
326366
}

src/core/h5p/classes/storage.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ export class CoreH5PStorage {
6262
// Library already installed.
6363
libraryData.libraryId = dbData.id;
6464

65-
if (!this.h5pFramework.isPatchedLibrary(libraryData, dbData)) {
65+
const isNewPatch = await this.h5pFramework.isPatchedLibrary(libraryData, dbData);
66+
67+
if (!isNewPatch) {
6668
// Same or older version, no need to save.
6769
libraryData.saveDependencies = false;
6870

src/core/h5p/components/components.module.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ import { CommonModule } from '@angular/common';
1717
import { IonicModule } from 'ionic-angular';
1818
import { TranslateModule } from '@ngx-translate/core';
1919
import { CoreDirectivesModule } from '@directives/directives.module';
20-
import { CoreH5PPlayerComponent } from './h5p-player/h5p-player';
2120
import { CoreComponentsModule } from '@components/components.module';
21+
import { CoreH5PPlayerComponent } from './h5p-player/h5p-player';
22+
import { CoreH5PIframeComponent } from './h5p-iframe/h5p-iframe';
2223

2324
@NgModule({
2425
declarations: [
25-
CoreH5PPlayerComponent
26+
CoreH5PPlayerComponent,
27+
CoreH5PIframeComponent,
2628
],
2729
imports: [
2830
CommonModule,
@@ -34,10 +36,12 @@ import { CoreComponentsModule } from '@components/components.module';
3436
providers: [
3537
],
3638
exports: [
37-
CoreH5PPlayerComponent
39+
CoreH5PPlayerComponent,
40+
CoreH5PIframeComponent,
3841
],
3942
entryComponents: [
40-
CoreH5PPlayerComponent
43+
CoreH5PPlayerComponent,
44+
CoreH5PIframeComponent,
4145
]
4246
})
4347
export class CoreH5PComponentsModule {}

0 commit comments

Comments
 (0)