MOBILE-3917 h5p: Check missing dependencies
parent
80f1b96f61
commit
1547b66ec9
|
@ -246,6 +246,7 @@ function build_lang($lang, $keys) {
|
|||
}
|
||||
|
||||
if ($value->file != 'local_moodlemobileapp') {
|
||||
$text = str_replace('$a->@', '$a.', $text);
|
||||
$text = str_replace('$a->', '$a.', $text);
|
||||
$text = str_replace('{$a', '{{$a', $text);
|
||||
$text = str_replace('}', '}}', $text);
|
||||
|
|
|
@ -1749,6 +1749,7 @@
|
|||
"core.h5p.licensee": "h5p",
|
||||
"core.h5p.licenseextras": "h5p",
|
||||
"core.h5p.licenseversion": "h5p",
|
||||
"core.h5p.missingdependency": "h5p",
|
||||
"core.h5p.nocopyright": "h5p",
|
||||
"core.h5p.offlineDialogBody": "h5p",
|
||||
"core.h5p.offlineDialogHeader": "h5p",
|
||||
|
|
|
@ -987,6 +987,13 @@ export type CoreH5PLibraryBasicData = {
|
|||
minorVersion: number; // Minor version.
|
||||
};
|
||||
|
||||
/**
|
||||
* Data about a missing library.
|
||||
*/
|
||||
export type CoreH5PMissingLibrary = CoreH5PLibraryBasicData & {
|
||||
libString: string; // Library that has the dependency.
|
||||
};
|
||||
|
||||
/**
|
||||
* Library basic data including patch version.
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,7 @@ import { CoreH5PSemantics } from './content-validator';
|
|||
import { CoreH5PContentBeingSaved, CoreH5PLibraryBeingSaved } from './storage';
|
||||
import { CoreH5PLibraryAddTo, CoreH5PLibraryMetadataSettings } from './validator';
|
||||
import { CoreH5PMetadata } from './metadata';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Equivalent to Moodle's implementation of H5PFrameworkInterface.
|
||||
|
@ -742,14 +743,14 @@ export class CoreH5PFramework {
|
|||
/**
|
||||
* Save what libraries a library is depending on.
|
||||
*
|
||||
* @param libraryId Library Id for the library we're saving dependencies for.
|
||||
* @param library Library data for the library we're saving dependencies for.
|
||||
* @param dependencies List of dependencies as associative arrays containing machineName, majorVersion, minorVersion.
|
||||
* @param dependencytype The type of dependency.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async saveLibraryDependencies(
|
||||
libraryId: number,
|
||||
library: CoreH5PLibraryBeingSaved,
|
||||
dependencies: CoreH5PLibraryBasicData[],
|
||||
dependencyType: string,
|
||||
siteId?: string,
|
||||
|
@ -761,9 +762,17 @@ export class CoreH5PFramework {
|
|||
// Get the ID of the library.
|
||||
const dependencyId = await this.getLibraryIdByData(dependency, siteId);
|
||||
|
||||
if (!dependencyId) {
|
||||
// Missing dependency. It should have been detected before installing the package.
|
||||
throw new CoreError(Translate.instant('core.h5p.missingdependency', { $a: {
|
||||
lib: CoreH5PCore.libraryToString(library),
|
||||
dep: CoreH5PCore.libraryToString(dependency),
|
||||
} }));
|
||||
}
|
||||
|
||||
// Create the relation.
|
||||
const entry: Partial<CoreH5PLibraryDependencyDBRecord> = {
|
||||
libraryid: libraryId,
|
||||
libraryid: library.libraryId,
|
||||
requiredlibraryid: dependencyId,
|
||||
dependencytype: dependencyType,
|
||||
};
|
||||
|
|
|
@ -211,7 +211,7 @@ export class CoreH5PHelper {
|
|||
// Read the contents of the unzipped dir, process them and store them.
|
||||
const contents = await CoreFile.getDirectoryContents(destFolder);
|
||||
|
||||
const filesData = await CoreH5P.h5pValidator.processH5PFiles(destFolder, contents);
|
||||
const filesData = await CoreH5P.h5pValidator.processH5PFiles(destFolder, contents, siteId);
|
||||
|
||||
const content = await CoreH5P.h5pStorage.savePackage(filesData, folderName, fileUrl, false, siteId);
|
||||
|
||||
|
|
|
@ -119,24 +119,26 @@ export class CoreH5PStorage {
|
|||
return;
|
||||
}
|
||||
|
||||
const libId = libraryData.libraryId;
|
||||
|
||||
libraryIds.push(libId);
|
||||
libraryIds.push(libraryData.libraryId);
|
||||
|
||||
// Remove any old dependencies.
|
||||
await this.h5pFramework.deleteLibraryDependencies(libId, siteId);
|
||||
await this.h5pFramework.deleteLibraryDependencies(libraryData.libraryId, siteId);
|
||||
|
||||
// Insert the different new ones.
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
if (typeof libraryData.preloadedDependencies != 'undefined') {
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(libId, libraryData.preloadedDependencies, 'preloaded'));
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(
|
||||
libraryData,
|
||||
libraryData.preloadedDependencies,
|
||||
'preloaded',
|
||||
));
|
||||
}
|
||||
if (typeof libraryData.dynamicDependencies != 'undefined') {
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(libId, libraryData.dynamicDependencies, 'dynamic'));
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(libraryData, libraryData.dynamicDependencies, 'dynamic'));
|
||||
}
|
||||
if (typeof libraryData.editorDependencies != 'undefined') {
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(libId, libraryData.editorDependencies, 'editor'));
|
||||
promises.push(this.h5pFramework.saveLibraryDependencies(libraryData, libraryData.editorDependencies, 'editor'));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
|
|
@ -12,17 +12,23 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { FileEntry, DirectoryEntry } from '@ionic-native/file/ngx';
|
||||
import { CoreFile, CoreFileFormat } from '@services/file';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreH5PSemantics } from './content-validator';
|
||||
import { CoreH5PCore, CoreH5PLibraryBasicData } from './core';
|
||||
import { CoreH5PCore, CoreH5PLibraryBasicData, CoreH5PMissingLibrary } from './core';
|
||||
import { CoreH5PFramework } from './framework';
|
||||
|
||||
/**
|
||||
* Equivalent to H5P's H5PValidator class.
|
||||
*/
|
||||
export class CoreH5PValidator {
|
||||
|
||||
constructor(public h5pFramework: CoreH5PFramework) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get library data.
|
||||
* This function won't validate most things because it should've been done by the server already.
|
||||
|
@ -49,6 +55,57 @@ export class CoreH5PValidator {
|
|||
return libraryData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the dependency declarations to find any missing libraries.
|
||||
*
|
||||
* @param libraries Libraries to check.
|
||||
* @return Promise resolved with the missing dependencies.
|
||||
*/
|
||||
protected getMissingLibraries(libraries: CoreH5PLibrariesJsonData): Record<string, CoreH5PMissingLibrary> {
|
||||
const missing: Record<string, CoreH5PMissingLibrary> = {};
|
||||
|
||||
Object.values(libraries).forEach((library) => {
|
||||
if (typeof library.preloadedDependencies !== 'undefined') {
|
||||
Object.assign(missing, this.getMissingDependencies(library.preloadedDependencies, library, libraries));
|
||||
}
|
||||
if (typeof library.dynamicDependencies !== 'undefined') {
|
||||
Object.assign(missing, this.getMissingDependencies(library.dynamicDependencies, library, libraries));
|
||||
}
|
||||
if (typeof library.editorDependencies !== 'undefined') {
|
||||
Object.assign(missing, this.getMissingDependencies(library.editorDependencies, library, libraries));
|
||||
}
|
||||
});
|
||||
|
||||
return missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getMissingLibraries, searches for dependency required libraries in the provided list of libraries.
|
||||
*
|
||||
* @param dependencies Dependencies to check.
|
||||
* @param library Library that has these dependencies.
|
||||
* @param libraries Libraries.
|
||||
* @return Promise resolved with missing dependencies.
|
||||
*/
|
||||
protected getMissingDependencies(
|
||||
dependencies: CoreH5PLibraryBasicData[],
|
||||
library: CoreH5PLibraryJsonData,
|
||||
libraries: CoreH5PLibrariesJsonData,
|
||||
): Record<string, CoreH5PLibraryBasicData> {
|
||||
const missing: Record<string, CoreH5PMissingLibrary> = {};
|
||||
|
||||
dependencies.forEach((dependency) => {
|
||||
const libString = CoreH5PCore.libraryToString(dependency);
|
||||
if (!libraries[libString]) {
|
||||
missing[libString] = Object.assign(dependency, {
|
||||
libString: CoreH5PCore.libraryToString(library),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get library data for all libraries in an H5P package.
|
||||
*
|
||||
|
@ -106,9 +163,14 @@ export class CoreH5PValidator {
|
|||
*
|
||||
* @param packagePath The path to the package folder.
|
||||
* @param entries List of files and directories in the root of the package folder.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async processH5PFiles(packagePath: string, entries: (DirectoryEntry | FileEntry)[]): Promise<CoreH5PMainJSONFilesData> {
|
||||
async processH5PFiles(
|
||||
packagePath: string,
|
||||
entries: (DirectoryEntry | FileEntry)[],
|
||||
siteId?: string,
|
||||
): Promise<CoreH5PMainJSONFilesData> {
|
||||
|
||||
// Read the needed files.
|
||||
const results = await Promise.all([
|
||||
|
@ -117,6 +179,31 @@ export class CoreH5PValidator {
|
|||
this.getPackageLibrariesData(packagePath, entries),
|
||||
]);
|
||||
|
||||
// Check if there are missing libraries.
|
||||
const missingLibraries = this.getMissingLibraries(results[2]);
|
||||
|
||||
// Check if the missing libraries are already installed in the app.
|
||||
await Promise.all(Object.keys(missingLibraries).map(async (libString) => {
|
||||
const dependency = missingLibraries[libString];
|
||||
const dependencyId = await this.h5pFramework.getLibraryIdByData(dependency, siteId);
|
||||
|
||||
if (dependencyId) {
|
||||
// Lib is installed.
|
||||
delete missingLibraries[libString];
|
||||
}
|
||||
}));
|
||||
|
||||
if (Object.keys(missingLibraries).length > 0) {
|
||||
// Missing library, throw error.
|
||||
const libString = Object.keys(missingLibraries)[0];
|
||||
const missingLibrary = missingLibraries[libString];
|
||||
|
||||
throw new CoreError(Translate.instant('core.h5p.missingdependency', { $a: {
|
||||
lib: missingLibrary.libString,
|
||||
dep: libString,
|
||||
} }));
|
||||
}
|
||||
|
||||
return {
|
||||
librariesJsonData: results[2],
|
||||
mainJsonData: results[0],
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"licensee": "Licensee",
|
||||
"licenseextras": "Licence extras",
|
||||
"licenseversion": "Licence version",
|
||||
"missingdependency": "Missing dependency {{$a.dep}} required by {{$a.lib}}.",
|
||||
"nocopyright": "No copyright information available for this content.",
|
||||
"offlineDialogBody": "We were unable to send information about your completion of this task. Please check your internet connection.",
|
||||
"offlineDialogHeader": "Your connection to the server was lost",
|
||||
|
@ -90,4 +91,4 @@
|
|||
"years": "Year(s)",
|
||||
"yearsfrom": "Years (from)",
|
||||
"yearsto": "Years (to)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ export class CoreH5PProvider {
|
|||
constructor() {
|
||||
this.queueRunner = new CoreQueueRunner(1);
|
||||
|
||||
this.h5pValidator = new CoreH5PValidator();
|
||||
this.h5pFramework = new CoreH5PFramework();
|
||||
this.h5pValidator = new CoreH5PValidator(this.h5pFramework);
|
||||
this.h5pCore = new CoreH5PCore(this.h5pFramework);
|
||||
this.h5pStorage = new CoreH5PStorage(this.h5pCore, this.h5pFramework);
|
||||
this.h5pPlayer = new CoreH5PPlayer(this.h5pCore, this.h5pStorage);
|
||||
|
|
Loading…
Reference in New Issue