diff --git a/src/core/features/h5p/h5p.module.ts b/src/core/features/h5p/h5p.module.ts index e6fdab6e4..803599a5a 100644 --- a/src/core/features/h5p/h5p.module.ts +++ b/src/core/features/h5p/h5p.module.ts @@ -14,6 +14,7 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { CorePluginFileDelegate } from '@services/plugin-file-delegate'; import { CORE_SITE_SCHEMAS } from '@services/sites'; import { CONTENT_TABLE_NAME, @@ -22,6 +23,7 @@ import { CONTENTS_LIBRARIES_TABLE_NAME, LIBRARIES_CACHEDASSETS_TABLE_NAME, } from './services/database/h5p'; +import { CoreH5PPluginFileHandler } from './services/handlers/pluginfile'; @NgModule({ imports: [ @@ -43,7 +45,7 @@ import { multi: true, deps: [], useFactory: () => () => { - // @todo + CorePluginFileDelegate.instance.registerHandler(CoreH5PPluginFileHandler.instance); }, }, ], diff --git a/src/core/features/h5p/services/handlers/pluginfile.ts b/src/core/features/h5p/services/handlers/pluginfile.ts new file mode 100644 index 000000000..05b816b35 --- /dev/null +++ b/src/core/features/h5p/services/handlers/pluginfile.ts @@ -0,0 +1,173 @@ +// (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 { FileEntry } from '@ionic-native/file'; + +import { CoreFilepoolOnProgressCallback } from '@services/filepool'; +import { CorePluginFileDownloadableResult, CorePluginFileHandler } from '@services/plugin-file-delegate'; +import { CoreSites } from '@services/sites'; +import { CoreMimetypeUtils } from '@services/utils/mimetype'; +import { CoreUrlUtils } from '@services/utils/url'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreWSExternalFile } from '@services/ws'; +import { CoreH5P } from '../h5p'; +import { Translate, makeSingleton } from '@singletons'; +import { CoreH5PHelper } from '../../classes/helper'; + +/** + * Handler to treat H5P files. + */ +@Injectable({ providedIn: 'root' }) +export class CoreH5PPluginFileHandlerService implements CorePluginFileHandler { + + name = 'CoreH5PPluginFileHandler'; + + /** + * React to a file being deleted. + * + * @param fileUrl The file URL used to download the file. + * @param path The path of the deleted file. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved when done. + */ + async fileDeleted(fileUrl: string, path: string, siteId?: string): Promise { + // If an h5p file is deleted, remove the contents folder. + await CoreH5P.instance.h5pPlayer.deleteContentByUrl(fileUrl, siteId); + } + + /** + * Check whether a file can be downloaded. If so, return the file to download. + * + * @param file The file data. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with the file to use. Rejected if cannot download. + */ + async getDownloadableFile(file: CoreWSExternalFile, siteId?: string): Promise { + 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); + } + + /** + * Given an HTML element, get the URLs of the files that should be downloaded and weren't treated by + * CoreFilepoolProvider.extractDownloadableFilesFromHtml. + * + * @param container Container where to get the URLs from. + * @return List of URLs. + */ + getDownloadableFilesFromHTML(container: HTMLElement): string[] { + const iframes = Array.from(container.querySelectorAll('iframe.h5p-iframe')); + const urls: string[] = []; + + for (let i = 0; i < iframes.length; i++) { + const params = CoreUrlUtils.instance.extractUrlParams(iframes[i].src); + + if (params.url) { + urls.push(params.url); + } + } + + return urls; + } + + /** + * Get a file size. + * + * @param file The file data. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with the size. + */ + async getFileSize(file: CoreWSExternalFile, siteId?: string): Promise { + try { + const trustedFile = await this.getDownloadableFile(file, siteId); + + return trustedFile.filesize || 0; + } catch (error) { + if (CoreUtils.instance.isWebServiceError(error)) { + // WS returned an error, it means it cannot be downloaded. + return 0; + } + + throw error; + } + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return Whether or not the handler is enabled on a site level. + */ + async isEnabled(): Promise { + return CoreH5P.instance.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 { + const offlineDisabled = await CoreH5P.instance.isOfflineDisabled(siteId); + + if (offlineDisabled) { + return { + downloadable: false, + reason: Translate.instance.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. + * + * @param file The file data. + * @return Whether the file should be treated by this handler. + */ + shouldHandleFile(file: CoreWSExternalFile): boolean { + return CoreMimetypeUtils.instance.guessExtensionFromUrl(file.fileurl) == 'h5p'; + } + + /** + * Treat a downloaded file. + * + * @param fileUrl The file URL used to download the file. + * @param file The file entry of the downloaded file. + * @param siteId Site ID. If not defined, current site. + * @param onProgress Function to call on progress. + * @return Promise resolved when done. + */ + treatDownloadedFile( + fileUrl: string, + file: FileEntry, + siteId?: string, + onProgress?: CoreFilepoolOnProgressCallback, + ): Promise { + return CoreH5PHelper.saveH5P(fileUrl, file, siteId, onProgress); + } + +} + +export class CoreH5PPluginFileHandler extends makeSingleton(CoreH5PPluginFileHandlerService) {}