Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOBILE-4642 h5p: Prevent partially saved packages #4175

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/core/features/h5p/classes/file-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,43 @@ export class CoreH5PFileStorage {
}
}

/**
* Check that library is fully saved to the file system.
*
* @param libraryData Library data.
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with true if all library files are present.
*/
async checkLibrary(libraryData: CoreH5PLibraryBeingSaved, siteId?: string): Promise<boolean> {
const getFileNames = async (baseDir: string, dirName = ''): Promise<string[]> => {
const entries = await CoreFile.getDirectoryContents( baseDir + dirName);
const fileNames: string[] = [];

for (const entry of entries) {
const name = dirName + '/' + entry.name;
if (entry.isDirectory) {
fileNames.push(...(await getFileNames(baseDir, name)));
} else {
fileNames.push(name);
}
}

return fileNames;
};

if (!libraryData.uploadDirectory) {
return true;
}

siteId = siteId || CoreSites.getCurrentSiteId();
const folderPath = this.getLibraryFolderPath(libraryData, siteId);

const [sourceFiles, destFiles] = await Promise.all([
getFileNames(libraryData.uploadDirectory),
getFileNames(folderPath).catch(() => ([])).then(files => new Set(files)),
]);

return sourceFiles.every(name => destFiles.has(name));
}

}
33 changes: 10 additions & 23 deletions src/core/features/h5p/classes/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export class CoreH5PStorage {

const newerPatchVersion = existingLibrary.patchversion < libraryData.patchVersion;

if (!newerPatchVersion) {
// Make sure the library is fully saved to the file system if it is present in the DB.
// Some files might be missing if a previous library update was interrupted.
if (!newerPatchVersion && await this.h5pCore.h5pFS.checkLibrary(libraryData, siteId)) {
// Same or older version, no need to save.
libraryData.saveDependencies = false;

Expand All @@ -79,21 +81,12 @@ export class CoreH5PStorage {
libraryData.metadataSettings = libraryData.metadataSettings ?
CoreH5PMetadata.boolifyAndEncodeSettings(libraryData.metadataSettings) : undefined;

// Save the library files before saving to DB, in case the app is closed while copying the files.
await this.h5pCore.h5pFS.saveLibrary(libraryData, siteId);

// Save the library data in DB.
await this.h5pFramework.saveLibraryData(libraryData, siteId);

// Now save it in FS.
try {
await this.h5pCore.h5pFS.saveLibrary(libraryData, siteId);
} catch (error) {
if (libraryData.libraryId) {
// An error occurred, delete the DB data because the lib FS data has been deleted.
await this.h5pFramework.deleteLibrary(libraryData.libraryId, siteId);
}

throw error;
}

if (libraryData.libraryId !== undefined) {
const promises: Promise<void>[] = [];

Expand Down Expand Up @@ -196,21 +189,15 @@ export class CoreH5PStorage {

content.params = JSON.stringify(data.contentJsonData);

// Save the content data in DB.
await this.h5pCore.saveContent(content, folderName, fileUrl, siteId);

// Save the content files in their right place in FS.
const destFolder = CorePath.concatenatePaths(CoreFileProvider.TMPFOLDER, 'h5p/' + folderName);
const contentPath = CorePath.concatenatePaths(destFolder, 'content');

try {
await this.h5pCore.h5pFS.saveContent(contentPath, folderName, siteId);
} catch (error) {
// An error occurred, delete the DB data because the content files have been deleted.
await this.h5pFramework.deleteContentData(content.id!, siteId);
// Save the content files before saving to DB, in case the app is closed while copying the files.
await this.h5pCore.h5pFS.saveContent(contentPath, folderName, siteId);

throw error;
}
// Save the content data in DB.
await this.h5pCore.saveContent(content, folderName, fileUrl, siteId);
}

return content;
Expand Down