Skip to content

Commit 9dcd84c

Browse files
committed
MOBILE-3332 h5p: Allow disabling offline h5p
1 parent 67da1cd commit 9dcd84c

File tree

12 files changed

+242
-46
lines changed

12 files changed

+242
-46
lines changed

scripts/langindex.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,7 @@
16191619
"core.h5p.offlineDialogRetryButtonLabel": "h5p",
16201620
"core.h5p.offlineDialogRetryMessage": "h5p",
16211621
"core.h5p.offlineSuccessfulSubmit": "h5p",
1622+
"core.h5p.offlinedisabled": "local_moodlemobileapp",
16221623
"core.h5p.originator": "h5p",
16231624
"core.h5p.pd": "h5p",
16241625
"core.h5p.pddl": "h5p",

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import { Component, Injector } from '@angular/core';
1616
import { CoreAppProvider } from '@providers/app';
17+
import { CoreFilepoolProvider } from '@providers/filepool';
1718
import { CoreSitesProvider } from '@providers/sites';
1819
import { CoreUtilsProvider } from '@providers/utils/utils';
1920
import { CoreCourseProvider } from '@core/course/providers/course';
@@ -38,10 +39,15 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
3839
contentText: string;
3940
displayDescription = true;
4041

41-
constructor(injector: Injector, private resourceProvider: AddonModResourceProvider, private courseProvider: CoreCourseProvider,
42-
private appProvider: CoreAppProvider, private prefetchHandler: AddonModResourcePrefetchHandler,
43-
private resourceHelper: AddonModResourceHelperProvider, private sitesProvider: CoreSitesProvider,
44-
private utils: CoreUtilsProvider) {
42+
constructor(injector: Injector,
43+
protected resourceProvider: AddonModResourceProvider,
44+
protected courseProvider: CoreCourseProvider,
45+
protected appProvider: CoreAppProvider,
46+
protected prefetchHandler: AddonModResourcePrefetchHandler,
47+
protected resourceHelper: AddonModResourceHelperProvider,
48+
protected sitesProvider: CoreSitesProvider,
49+
protected utils: CoreUtilsProvider,
50+
protected filepoolProvider: CoreFilepoolProvider) {
4551
super(injector);
4652
}
4753

@@ -147,15 +153,23 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
147153

148154
/**
149155
* Opens a file.
156+
*
157+
* @return Promise resolved when done.
150158
*/
151-
open(): void {
152-
this.prefetchHandler.isDownloadable(this.module, this.courseId).then((downloadable) => {
159+
async open(): Promise<void> {
160+
let downloadable = await this.prefetchHandler.isDownloadable(this.module, this.courseId);
161+
162+
if (downloadable) {
163+
// Check if the main file is downloadle.
164+
// This isn't done in "isDownloadable" to prevent extra WS calls in the course page.
165+
downloadable = await this.resourceHelper.isMainFileDownloadable(this.module);
166+
153167
if (downloadable) {
154-
this.resourceHelper.openModuleFile(this.module, this.courseId);
155-
} else {
156-
// The resource cannot be downloaded, open the activity in browser.
157-
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
168+
return this.resourceHelper.openModuleFile(this.module, this.courseId);
158169
}
159-
});
170+
}
171+
172+
// The resource cannot be downloaded, open the activity in browser.
173+
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
160174
}
161175
}

src/addon/mod/resource/providers/helper.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { AddonModResourceProvider } from './resource';
2020
import { CoreSitesProvider } from '@providers/sites';
2121
import { CoreFilepoolProvider } from '@providers/filepool';
2222
import { CoreFileProvider } from '@providers/file';
23+
import { CoreFileHelperProvider } from '@providers/file-helper';
2324
import { CoreAppProvider } from '@providers/app';
2425
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
2526
import { CoreTextUtilsProvider } from '@providers/utils/text';
@@ -36,11 +37,17 @@ export class AddonModResourceHelperProvider {
3637
// Display using object tag.
3738
protected DISPLAY_EMBED = 1;
3839

39-
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
40-
private resourceProvider: AddonModResourceProvider, private courseHelper: CoreCourseHelperProvider,
41-
private textUtils: CoreTextUtilsProvider, private mimetypeUtils: CoreMimetypeUtilsProvider,
42-
private fileProvider: CoreFileProvider, private appProvider: CoreAppProvider,
43-
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider) {
40+
constructor(protected courseProvider: CoreCourseProvider,
41+
protected domUtils: CoreDomUtilsProvider,
42+
protected resourceProvider: AddonModResourceProvider,
43+
protected courseHelper: CoreCourseHelperProvider,
44+
protected textUtils: CoreTextUtilsProvider,
45+
protected mimetypeUtils: CoreMimetypeUtilsProvider,
46+
protected fileProvider: CoreFileProvider,
47+
protected appProvider: CoreAppProvider,
48+
protected filepoolProvider: CoreFilepoolProvider,
49+
protected sitesProvider: CoreSitesProvider,
50+
protected fileHelper: CoreFileHelperProvider) {
4451
}
4552

4653
/**
@@ -136,6 +143,23 @@ export class AddonModResourceHelperProvider {
136143
return mimetype == 'text/html';
137144
}
138145

146+
/**
147+
* Check if main file of resource is downloadable.
148+
*
149+
* @param module Module instance.
150+
* @param siteId Site ID. If not defined, current site.
151+
* @return Promise resolved with boolean: whether main file is downloadable.
152+
*/
153+
isMainFileDownloadable(module: any, siteId?: string): Promise<boolean> {
154+
siteId = siteId || this.sitesProvider.getCurrentSiteId();
155+
156+
const mainFile = module.contents[0];
157+
const fileUrl = this.fileHelper.getFileUrl(mainFile);
158+
const timemodified = this.fileHelper.getFileTimemodified(mainFile);
159+
160+
return this.filepoolProvider.isFileDownloadable(siteId, fileUrl, timemodified);
161+
}
162+
139163
/**
140164
* Check if the resource is a Nextcloud file.
141165
*

src/assets/lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,7 @@
16191619
"core.h5p.offlineDialogRetryButtonLabel": "Retry now",
16201620
"core.h5p.offlineDialogRetryMessage": "Retrying in :num....",
16211621
"core.h5p.offlineSuccessfulSubmit": "Successfully submitted results.",
1622+
"core.h5p.offlinedisabled": "The site doesn't allow downloading H5P packages.",
16221623
"core.h5p.originator": "Originator",
16231624
"core.h5p.pd": "Public Domain",
16241625
"core.h5p.pddl": "Public Domain Dedication and Licence",

src/components/file/file.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { CoreFileHelperProvider } from '@providers/file-helper';
2020
import { CoreSitesProvider } from '@providers/sites';
2121
import { CoreDomUtilsProvider } from '@providers/utils/dom';
2222
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
23+
import { CoreUrlUtilsProvider } from '@providers/utils/url';
2324
import { CoreUtilsProvider } from '@providers/utils/utils';
2425
import { CoreTextUtilsProvider } from '@providers/utils/text';
2526
import { CoreConstants } from '@core/constants';
@@ -57,16 +58,17 @@ export class CoreFileComponent implements OnInit, OnDestroy {
5758
protected timemodified: number;
5859
protected observer;
5960

60-
constructor(private sitesProvider: CoreSitesProvider,
61-
private utils: CoreUtilsProvider,
62-
private domUtils: CoreDomUtilsProvider,
63-
private filepoolProvider: CoreFilepoolProvider,
64-
private appProvider: CoreAppProvider,
65-
private fileHelper: CoreFileHelperProvider,
66-
private mimeUtils: CoreMimetypeUtilsProvider,
67-
private eventsProvider: CoreEventsProvider,
68-
private textUtils: CoreTextUtilsProvider,
69-
private pluginFileDelegate: CorePluginFileDelegate) {
61+
constructor(protected sitesProvider: CoreSitesProvider,
62+
protected utils: CoreUtilsProvider,
63+
protected domUtils: CoreDomUtilsProvider,
64+
protected filepoolProvider: CoreFilepoolProvider,
65+
protected appProvider: CoreAppProvider,
66+
protected fileHelper: CoreFileHelperProvider,
67+
protected mimeUtils: CoreMimetypeUtilsProvider,
68+
protected eventsProvider: CoreEventsProvider,
69+
protected textUtils: CoreTextUtilsProvider,
70+
protected pluginFileDelegate: CorePluginFileDelegate,
71+
protected urlUtils: CoreUrlUtilsProvider) {
7072
this.onDelete = new EventEmitter();
7173
}
7274

@@ -104,6 +106,8 @@ export class CoreFileComponent implements OnInit, OnDestroy {
104106
this.observer = this.eventsProvider.on(eventName, () => {
105107
this.calculateState();
106108
});
109+
}).catch(() => {
110+
// File not downloadable.
107111
});
108112
}
109113
}
@@ -152,14 +156,14 @@ export class CoreFileComponent implements OnInit, OnDestroy {
152156
return;
153157
}
154158

155-
if (!this.canDownload) {
159+
if (!this.canDownload || !this.state || this.state == CoreConstants.NOT_DOWNLOADABLE) {
156160
// File cannot be downloaded, just open it.
157161
if (this.file.toURL) {
158162
// Local file.
159163
this.utils.openFile(this.file.toURL());
160164
} else if (this.fileUrl) {
161165
if (this.fileUrl.indexOf('http') === 0) {
162-
this.utils.openOnlineFile(this.fileUrl);
166+
this.utils.openOnlineFile(this.urlUtils.unfixPluginfileURL(this.fileUrl));
163167
} else {
164168
this.utils.openFile(this.fileUrl);
165169
}

src/core/h5p/components/h5p-player/h5p-player.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
7373
this.logger = loggerProvider.getInstance('CoreH5PPlayerComponent');
7474
this.site = sitesProvider.getCurrentSite();
7575
this.siteId = this.site.getId();
76-
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
76+
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles() &&
77+
!this.h5pProvider.isOfflineDisabledInSite();
7778
}
7879

7980
/**

src/core/h5p/lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"offlineDialogRetryButtonLabel": "Retry now",
6565
"offlineDialogRetryMessage": "Retrying in :num....",
6666
"offlineSuccessfulSubmit": "Successfully submitted results.",
67+
"offlinedisabled": "The site doesn't allow downloading H5P packages.",
6768
"originator": "Originator",
6869
"pd": "Public Domain",
6970
"pddl": "Public Domain Dedication and Licence",

src/core/h5p/providers/h5p.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,6 +1876,30 @@ export class CoreH5PProvider {
18761876
});
18771877
}
18781878

1879+
/**
1880+
* Check whether H5P offline is disabled.
1881+
*
1882+
* @param siteId Site ID. If not defined, current site.
1883+
* @return Promise resolved with boolean: whether is disabled.
1884+
*/
1885+
async isOfflineDisabled(siteId?: string): Promise<boolean> {
1886+
const site = await this.sitesProvider.getSite(siteId);
1887+
1888+
return this.isOfflineDisabledInSite(site);
1889+
}
1890+
1891+
/**
1892+
* Check whether H5P offline is disabled.
1893+
*
1894+
* @param site Site instance. If not defined, current site.
1895+
* @return Whether is disabled.
1896+
*/
1897+
isOfflineDisabledInSite(site?: CoreSite): boolean {
1898+
site = site || this.sitesProvider.getCurrentSite();
1899+
1900+
return site.isFeatureDisabled('NoDelegate_H5POffline');
1901+
}
1902+
18791903
/**
18801904
* Performs actions required when a library has been installed.
18811905
*

src/core/h5p/providers/pluginfile-handler.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
2222
import { CoreH5PProvider } from './h5p';
2323
import { CoreWSExternalFile } from '@providers/ws';
2424
import { FileEntry } from '@ionic-native/file';
25+
import { TranslateService } from '@ngx-translate/core';
2526

2627
/**
2728
* Handler to treat H5P files.
@@ -35,7 +36,8 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
3536
protected textUtils: CoreTextUtilsProvider,
3637
protected utils: CoreUtilsProvider,
3738
protected fileProvider: CoreFileProvider,
38-
protected h5pProvider: CoreH5PProvider) { }
39+
protected h5pProvider: CoreH5PProvider,
40+
protected translate: TranslateService) { }
3941

4042
/**
4143
* React to a file being deleted.
@@ -112,6 +114,28 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
112114
return this.h5pProvider.canGetTrustedH5PFileInSite();
113115
}
114116

117+
/**
118+
* Check if a file is downloadable.
119+
*
120+
* @param file The file data.
121+
* @param siteId Site ID. If not defined, current site.
122+
* @return Promise resolved with a boolean and a reason why it isn't downloadable if needed.
123+
*/
124+
async isFileDownloadable(file: CoreWSExternalFile, siteId?: string): Promise<{downloadable: boolean, reason?: string}> {
125+
const offlineDisabled = await this.h5pProvider.isOfflineDisabled(siteId);
126+
127+
if (offlineDisabled) {
128+
return {
129+
downloadable: false,
130+
reason: this.translate.instant('core.h5p.offlinedisabled'),
131+
};
132+
} else {
133+
return {
134+
downloadable: true,
135+
};
136+
}
137+
}
138+
115139
/**
116140
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
117141
*

src/providers/filepool.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,23 @@ export class CoreFilepoolProvider {
23852385
});
23862386
}
23872387

2388+
/**
2389+
* Check whether a file is downloadable.
2390+
*
2391+
* @param siteId The site ID.
2392+
* @param fileUrl File URL.
2393+
* @param timemodified The time this file was modified.
2394+
* @param filePath Filepath to download the file to. If defined, no extension will be added.
2395+
* @param revision File revision. If not defined, it will be calculated using the URL.
2396+
* @return Promise resolved with a boolean: whether a file is downloadable.
2397+
*/
2398+
async isFileDownloadable(siteId: string, fileUrl: string, timemodified: number = 0, filePath?: string, revision?: number)
2399+
: Promise<boolean> {
2400+
const state = await this.getFileStateByUrl(siteId, fileUrl, timemodified, filePath, revision);
2401+
2402+
return state != CoreConstants.NOT_DOWNLOADABLE;
2403+
}
2404+
23882405
/**
23892406
* Check if a file is downloading.
23902407
*

0 commit comments

Comments
 (0)