MOBILE-3332 h5p: Allow disabling offline h5p
parent
67da1cdeba
commit
9dcd84c903
|
@ -1619,6 +1619,7 @@
|
|||
"core.h5p.offlineDialogRetryButtonLabel": "h5p",
|
||||
"core.h5p.offlineDialogRetryMessage": "h5p",
|
||||
"core.h5p.offlineSuccessfulSubmit": "h5p",
|
||||
"core.h5p.offlinedisabled": "local_moodlemobileapp",
|
||||
"core.h5p.originator": "h5p",
|
||||
"core.h5p.pd": "h5p",
|
||||
"core.h5p.pddl": "h5p",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
|
@ -38,10 +39,15 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
contentText: string;
|
||||
displayDescription = true;
|
||||
|
||||
constructor(injector: Injector, private resourceProvider: AddonModResourceProvider, private courseProvider: CoreCourseProvider,
|
||||
private appProvider: CoreAppProvider, private prefetchHandler: AddonModResourcePrefetchHandler,
|
||||
private resourceHelper: AddonModResourceHelperProvider, private sitesProvider: CoreSitesProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
constructor(injector: Injector,
|
||||
protected resourceProvider: AddonModResourceProvider,
|
||||
protected courseProvider: CoreCourseProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected prefetchHandler: AddonModResourcePrefetchHandler,
|
||||
protected resourceHelper: AddonModResourceHelperProvider,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
|
@ -147,15 +153,23 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
|
||||
/**
|
||||
* Opens a file.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
open(): void {
|
||||
this.prefetchHandler.isDownloadable(this.module, this.courseId).then((downloadable) => {
|
||||
async open(): Promise<void> {
|
||||
let downloadable = await this.prefetchHandler.isDownloadable(this.module, this.courseId);
|
||||
|
||||
if (downloadable) {
|
||||
// Check if the main file is downloadle.
|
||||
// This isn't done in "isDownloadable" to prevent extra WS calls in the course page.
|
||||
downloadable = await this.resourceHelper.isMainFileDownloadable(this.module);
|
||||
|
||||
if (downloadable) {
|
||||
this.resourceHelper.openModuleFile(this.module, this.courseId);
|
||||
} else {
|
||||
// The resource cannot be downloaded, open the activity in browser.
|
||||
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
|
||||
return this.resourceHelper.openModuleFile(this.module, this.courseId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The resource cannot be downloaded, open the activity in browser.
|
||||
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { AddonModResourceProvider } from './resource';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreFileProvider } from '@providers/file';
|
||||
import { CoreFileHelperProvider } from '@providers/file-helper';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
|
@ -36,11 +37,17 @@ export class AddonModResourceHelperProvider {
|
|||
// Display using object tag.
|
||||
protected DISPLAY_EMBED = 1;
|
||||
|
||||
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private resourceProvider: AddonModResourceProvider, private courseHelper: CoreCourseHelperProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private mimetypeUtils: CoreMimetypeUtilsProvider,
|
||||
private fileProvider: CoreFileProvider, private appProvider: CoreAppProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider) {
|
||||
constructor(protected courseProvider: CoreCourseProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected resourceProvider: AddonModResourceProvider,
|
||||
protected courseHelper: CoreCourseHelperProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected mimetypeUtils: CoreMimetypeUtilsProvider,
|
||||
protected fileProvider: CoreFileProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected fileHelper: CoreFileHelperProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,6 +143,23 @@ export class AddonModResourceHelperProvider {
|
|||
return mimetype == 'text/html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if main file of resource is downloadable.
|
||||
*
|
||||
* @param module Module instance.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with boolean: whether main file is downloadable.
|
||||
*/
|
||||
isMainFileDownloadable(module: any, siteId?: string): Promise<boolean> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const mainFile = module.contents[0];
|
||||
const fileUrl = this.fileHelper.getFileUrl(mainFile);
|
||||
const timemodified = this.fileHelper.getFileTimemodified(mainFile);
|
||||
|
||||
return this.filepoolProvider.isFileDownloadable(siteId, fileUrl, timemodified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the resource is a Nextcloud file.
|
||||
*
|
||||
|
|
|
@ -1619,6 +1619,7 @@
|
|||
"core.h5p.offlineDialogRetryButtonLabel": "Retry now",
|
||||
"core.h5p.offlineDialogRetryMessage": "Retrying in :num....",
|
||||
"core.h5p.offlineSuccessfulSubmit": "Successfully submitted results.",
|
||||
"core.h5p.offlinedisabled": "The site doesn't allow downloading H5P packages.",
|
||||
"core.h5p.originator": "Originator",
|
||||
"core.h5p.pd": "Public Domain",
|
||||
"core.h5p.pddl": "Public Domain Dedication and Licence",
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CoreFileHelperProvider } from '@providers/file-helper';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
|
@ -57,16 +58,17 @@ export class CoreFileComponent implements OnInit, OnDestroy {
|
|||
protected timemodified: number;
|
||||
protected observer;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider,
|
||||
private utils: CoreUtilsProvider,
|
||||
private domUtils: CoreDomUtilsProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider,
|
||||
private appProvider: CoreAppProvider,
|
||||
private fileHelper: CoreFileHelperProvider,
|
||||
private mimeUtils: CoreMimetypeUtilsProvider,
|
||||
private eventsProvider: CoreEventsProvider,
|
||||
private textUtils: CoreTextUtilsProvider,
|
||||
private pluginFileDelegate: CorePluginFileDelegate) {
|
||||
constructor(protected sitesProvider: CoreSitesProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected fileHelper: CoreFileHelperProvider,
|
||||
protected mimeUtils: CoreMimetypeUtilsProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected pluginFileDelegate: CorePluginFileDelegate,
|
||||
protected urlUtils: CoreUrlUtilsProvider) {
|
||||
this.onDelete = new EventEmitter();
|
||||
}
|
||||
|
||||
|
@ -104,6 +106,8 @@ export class CoreFileComponent implements OnInit, OnDestroy {
|
|||
this.observer = this.eventsProvider.on(eventName, () => {
|
||||
this.calculateState();
|
||||
});
|
||||
}).catch(() => {
|
||||
// File not downloadable.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -152,14 +156,14 @@ export class CoreFileComponent implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.canDownload) {
|
||||
if (!this.canDownload || !this.state || this.state == CoreConstants.NOT_DOWNLOADABLE) {
|
||||
// File cannot be downloaded, just open it.
|
||||
if (this.file.toURL) {
|
||||
// Local file.
|
||||
this.utils.openFile(this.file.toURL());
|
||||
} else if (this.fileUrl) {
|
||||
if (this.fileUrl.indexOf('http') === 0) {
|
||||
this.utils.openOnlineFile(this.fileUrl);
|
||||
this.utils.openOnlineFile(this.urlUtils.unfixPluginfileURL(this.fileUrl));
|
||||
} else {
|
||||
this.utils.openFile(this.fileUrl);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
this.logger = loggerProvider.getInstance('CoreH5PPlayerComponent');
|
||||
this.site = sitesProvider.getCurrentSite();
|
||||
this.siteId = this.site.getId();
|
||||
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
|
||||
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles() &&
|
||||
!this.h5pProvider.isOfflineDisabledInSite();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"offlineDialogRetryButtonLabel": "Retry now",
|
||||
"offlineDialogRetryMessage": "Retrying in :num....",
|
||||
"offlineSuccessfulSubmit": "Successfully submitted results.",
|
||||
"offlinedisabled": "The site doesn't allow downloading H5P packages.",
|
||||
"originator": "Originator",
|
||||
"pd": "Public Domain",
|
||||
"pddl": "Public Domain Dedication and Licence",
|
||||
|
|
|
@ -1876,6 +1876,30 @@ export class CoreH5PProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether H5P offline is disabled.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with boolean: whether is disabled.
|
||||
*/
|
||||
async isOfflineDisabled(siteId?: string): Promise<boolean> {
|
||||
const site = await this.sitesProvider.getSite(siteId);
|
||||
|
||||
return this.isOfflineDisabledInSite(site);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether H5P offline is disabled.
|
||||
*
|
||||
* @param site Site instance. If not defined, current site.
|
||||
* @return Whether is disabled.
|
||||
*/
|
||||
isOfflineDisabledInSite(site?: CoreSite): boolean {
|
||||
site = site || this.sitesProvider.getCurrentSite();
|
||||
|
||||
return site.isFeatureDisabled('NoDelegate_H5POffline');
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actions required when a library has been installed.
|
||||
*
|
||||
|
|
|
@ -22,6 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
|||
import { CoreH5PProvider } from './h5p';
|
||||
import { CoreWSExternalFile } from '@providers/ws';
|
||||
import { FileEntry } from '@ionic-native/file';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Handler to treat H5P files.
|
||||
|
@ -35,7 +36,8 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
|
|||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected fileProvider: CoreFileProvider,
|
||||
protected h5pProvider: CoreH5PProvider) { }
|
||||
protected h5pProvider: CoreH5PProvider,
|
||||
protected translate: TranslateService) { }
|
||||
|
||||
/**
|
||||
* React to a file being deleted.
|
||||
|
@ -112,6 +114,28 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
|
|||
return this.h5pProvider.canGetTrustedH5PFileInSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is downloadable.
|
||||
*
|
||||
* @param file The file data.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with a boolean and a reason why it isn't downloadable if needed.
|
||||
*/
|
||||
async isFileDownloadable(file: CoreWSExternalFile, siteId?: string): Promise<{downloadable: boolean, reason?: string}> {
|
||||
const offlineDisabled = await this.h5pProvider.isOfflineDisabled(siteId);
|
||||
|
||||
if (offlineDisabled) {
|
||||
return {
|
||||
downloadable: false,
|
||||
reason: this.translate.instant('core.h5p.offlinedisabled'),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
downloadable: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
|
||||
*
|
||||
|
|
|
@ -2385,6 +2385,23 @@ export class CoreFilepoolProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file is downloadable.
|
||||
*
|
||||
* @param siteId The site ID.
|
||||
* @param fileUrl File URL.
|
||||
* @param timemodified The time this file was modified.
|
||||
* @param filePath Filepath to download the file to. If defined, no extension will be added.
|
||||
* @param revision File revision. If not defined, it will be calculated using the URL.
|
||||
* @return Promise resolved with a boolean: whether a file is downloadable.
|
||||
*/
|
||||
async isFileDownloadable(siteId: string, fileUrl: string, timemodified: number = 0, filePath?: string, revision?: number)
|
||||
: Promise<boolean> {
|
||||
const state = await this.getFileStateByUrl(siteId, fileUrl, timemodified, filePath, revision);
|
||||
|
||||
return state != CoreConstants.NOT_DOWNLOADABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is downloading.
|
||||
*
|
||||
|
|
|
@ -84,6 +84,15 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
|
|||
*/
|
||||
getFileSize?(file: CoreWSExternalFile, siteId?: string): Promise<number>;
|
||||
|
||||
/**
|
||||
* Check if a file is downloadable.
|
||||
*
|
||||
* @param file The file data.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with a boolean and a reason why it isn't downloadable if needed.
|
||||
*/
|
||||
isFileDownloadable?(file: CoreWSExternalFile, siteId?: string): Promise<CorePluginFileDownloadableResult>;
|
||||
|
||||
/**
|
||||
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
|
||||
*
|
||||
|
@ -103,6 +112,21 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
|
|||
treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string): Promise<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data about if a file is downloadable.
|
||||
*/
|
||||
export type CorePluginFileDownloadableResult = {
|
||||
/**
|
||||
* Whether it's downloadable.
|
||||
*/
|
||||
downloadable: boolean;
|
||||
|
||||
/**
|
||||
* If not downloadable, the reason why it isn't.
|
||||
*/
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate to register pluginfile information handlers.
|
||||
*/
|
||||
|
@ -155,16 +179,22 @@ export class CorePluginFileDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the file to use. Rejected if cannot download.
|
||||
*/
|
||||
protected getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string)
|
||||
protected async getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string)
|
||||
: Promise<CoreWSExternalFile> {
|
||||
|
||||
if (handler && handler.getDownloadableFile) {
|
||||
return handler.getDownloadableFile(file, siteId).then((newFile) => {
|
||||
return newFile || file;
|
||||
});
|
||||
const isDownloadable = await this.isFileDownloadable(file, siteId);
|
||||
|
||||
if (!isDownloadable.downloadable) {
|
||||
throw isDownloadable.reason;
|
||||
}
|
||||
|
||||
return Promise.resolve(file);
|
||||
if (handler && handler.getDownloadableFile) {
|
||||
const newFile = await handler.getDownloadableFile(file, siteId);
|
||||
|
||||
return newFile || file;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,23 +270,32 @@ export class CorePluginFileDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the size.
|
||||
*/
|
||||
getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
|
||||
async getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
|
||||
const isDownloadable = await this.isFileDownloadable(file, siteId);
|
||||
|
||||
if (!isDownloadable.downloadable) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const handler = this.getHandlerForFile(file);
|
||||
|
||||
// First of all check if file can be downloaded.
|
||||
return this.getHandlerDownloadableFile(file, handler, siteId).then((file) => {
|
||||
if (!file) {
|
||||
return 0;
|
||||
}
|
||||
const downloadableFile = await this.getHandlerDownloadableFile(file, handler, siteId);
|
||||
if (!downloadableFile) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (handler && handler.getFileSize) {
|
||||
return handler.getFileSize(file, siteId).catch(() => {
|
||||
return file.filesize;
|
||||
});
|
||||
}
|
||||
if (handler && handler.getFileSize) {
|
||||
try {
|
||||
const size = handler.getFileSize(downloadableFile, siteId);
|
||||
|
||||
return Promise.resolve(file.filesize);
|
||||
});
|
||||
return size;
|
||||
} catch (error) {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
return downloadableFile.filesize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,6 +314,24 @@ export class CorePluginFileDelegate extends CoreDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is downloadable.
|
||||
*
|
||||
* @param file The file data.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise with the data.
|
||||
*/
|
||||
isFileDownloadable(file: CoreWSExternalFile, siteId?: string): Promise<CorePluginFileDownloadableResult> {
|
||||
const handler = this.getHandlerForFile(file);
|
||||
|
||||
if (handler && handler.isFileDownloadable) {
|
||||
return handler.isFileDownloadable(file, siteId);
|
||||
}
|
||||
|
||||
// Default to true.
|
||||
return Promise.resolve({downloadable: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the revision number from a file URL.
|
||||
*
|
||||
|
|
|
@ -469,4 +469,32 @@ export class CoreUrlUtilsProvider {
|
|||
|
||||
return matches && matches[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a pluginfile URL to use the default pluginfile script instead of the webservice one.
|
||||
*
|
||||
* @param url The url to be fixed.
|
||||
* @param siteUrl The URL of the site the URL belongs to.
|
||||
* @return Modified URL.
|
||||
*/
|
||||
unfixPluginfileURL(url: string, siteUrl?: string): string {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
|
||||
url = url.replace(/&/g, '&');
|
||||
|
||||
// It site URL is supplied, check if the URL belongs to the site.
|
||||
if (siteUrl && url.indexOf(this.textUtils.addEndingSlash(siteUrl)) !== 0) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Not a pluginfile URL. Treat webservice/pluginfile case.
|
||||
url = url.replace(/\/webservice\/pluginfile\.php\//, '/pluginfile.php/');
|
||||
|
||||
// Make sure the URL doesn't contain the token.
|
||||
url.replace(/([?&])token=[^&]*&?/, '$1');
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue