/* eslint-disable no-await-in-loop */ import { mapActions, mapGetters } from 'vuex'; export default { data() { return { importFolder: null, file_mapping: {}, elementCounter: 0, importElementCounter: 0, currentImportErrors: [], unitId: null, }; }, computed: { ...mapGetters({ context: 'context', courseware: 'courseware-instances/all', instanceById: 'courseware-instances/byId', lastCreatedBlocks: 'courseware-blocks/lastCreated', lastCreatedContainers: 'courseware-containers/lastCreated', lastCreatedElements: 'courseware-structural-elements/lastCreated', structuralElementById: 'courseware-structural-elements/byId', }), instance() { if (this.unitId) { if (this.inCourseContext) { return this.instanceById({id: 'course_' + this.context.id + '_' + this.unitId}); } else { return this.instanceById({id: 'user_' + this.context.id + '_' + this.unitId}); } } else { return null; } }, inCourseContext() { return this.context.type === 'courses'; } }, methods: { ...mapActions({ loadStructuralElementById: 'courseware-structural-elements/loadById', updateStructuralElement: 'updateStructuralElement' }), async importCourseware(element, rootId, files, importBehavior, settings) { // import all files await this.uploadAllFiles(files); this.elementCounter = await this.countImportElements([element]); this.setImportStructuresState(''); this.importElementCounter = 0; if (importBehavior === 'default') { await this.importStructuralElement([element], rootId, files); } if (importBehavior === 'migrate') { await this.migrateCourseware(element, rootId, files, settings); } }, countImportElements(element) { let counter = 0; if (element.length) { for (var i = 0; i < element.length; i++) { counter++; if (element[i].children?.length > 0) { counter += this.countImportElements(element[i].children); } if (element[i].containers?.length > 0) { for (var j = 0; j < element[i].containers.length; j++) { counter++; let container = element[i].containers[j]; if (container.blocks?.length) { for (var k = 0; k < container.blocks.length; k++) { counter++; } } } } } } return counter; }, async migrateCourseware(element, rootId, files, settings) { let root = this.structuralElementById({ id: rootId }); if (settings) { this.unitId = root.relationships.unit.data.id; const context = {type: this.context.type, id: this.context.id, unit: this.unitId}; await this.loadInstance(context); let currentInstance = this.instance; currentInstance.attributes['editing-permission-level'] = settings['editing-permission-level']; currentInstance.attributes['sequential-progression'] = settings['sequential-progression']; this.storeCoursewareSettings({ instance: currentInstance, }); } // add containers and blocks if (element.containers?.length > 0) { await Promise.all(element.containers.map((container) => this.importContainer(container, root, files))); } //compare payload let changedData = false; if (root.attributes.title === 'neue Seite') { root.attributes.title = element.attributes.title; changedData = true; } if (root.attributes.purpose === '') { root.attributes.purpose = element.attributes.purpose; changedData = true; } if (element.attributes.payload) { if (!root.attributes.payload.color && element.attributes.payload.color) { root.attributes.payload.color = element.attributes.payload.color; changedData = true; } if (!root.attributes.payload.description && element.attributes.payload.description) { root.attributes.payload.description = element.attributes.payload.description; changedData = true; } if (!root.attributes.payload.license_type && element.attributes.payload.license_type) { root.attributes.payload.license_type = element.attributes.payload.license_type; changedData = true; } if (!root.attributes.payload.required_time && element.attributes.payload.required_time) { root.attributes.payload.required_time = element.attributes.payload.required_time; changedData = true; } if (!root.attributes.payload.difficulty_start && element.attributes.payload.difficulty_start) { root.attributes.payload.difficulty_start = element.attributes.payload.difficulty_start; changedData = true; } if (!root.attributes.payload.difficulty_end && element.attributes.payload.difficulty_end) { root.attributes.payload.difficulty_end = element.attributes.payload.difficulty_end; changedData = true; } } if (changedData) { await this.lockObject({ id: root.id, type: 'courseware-structural-elements' }); await this.updateStructuralElement({ element: root, id: root.id, }); await this.unlockObject({ id: root.id, type: 'courseware-structural-elements' }); } // compare image if (element.imageId && root.relationships.image.data === null) { await this.setStructuralElementImage(root, element.imageId, files); } // add children if (element.children) { await this.importStructuralElement(element.children, rootId, files); } this.setImportStructuresProgress(100); }, async importStructuralElement(element, parent_id, files) { if (element.length) { for (var i = 0; i < element.length; i++) { this.setImportStructuresState(this.$gettext('Lege Seite an:') + ' ' + element[i].attributes.title); let new_element = null; try { await this.createStructuralElement({ attributes: element[i].attributes, parentId: parent_id, currentId: parent_id, }); new_element = this.lastCreatedElements; } catch(error) { this.currentImportErrors.push(this.$gettext('Seite konnte nicht erstellt werden') + ': ' + element.attributes.title); } this.importElementCounter++; if (new_element === null) { continue; } if (element[i].imageId) { await this.setStructuralElementImage(new_element, element[i].imageId, files); } if (element[i].children?.length > 0) { await this.importStructuralElement(element[i].children, new_element.id, files); } if (element[i].containers?.length > 0) { for (var j = 0; j < element[i].containers.length; j++) { let container = element[i].containers[j]; await this.importContainer(container, new_element, files); } } } } }, async setStructuralElementImage(new_element, imageId, files) { let imageFile = files.find((file) => { return file.id === imageId}); let zip_filedata = await this.zip.file(imageFile.id).async('blob'); // create new blob with correct type let filedata = zip_filedata.slice(0, zip_filedata.size, imageFile.attributes['mime-type']); this.setImportStructuresState(this.$gettext('Lade Vorschaubild hoch')); this.uploadImageForStructuralElement({ structuralElement: new_element, file: filedata, }).catch((error) => { console.error(error); this.currentImportErrors.push(this.$gettext('Fehler beim Hochladen des Vorschaubildes.')); }); }, async importContainer(container, structuralElement, files) { this.setImportStructuresState(this.$gettext('Lege Abschnitt an:') + ' ' + container.attributes.title); let new_container = null; try { await this.createContainer({ attributes: container.attributes, structuralElementId: structuralElement.id, }); new_container = this.lastCreatedContainers; } catch(error) { this.currentImportErrors.push(this.$gettext('Abschnitt konnte nicht erstellt werden') + ': ' + structuralElement.attributes.title + '→' + container.attributes.title); } this.importElementCounter++; if (new_container === null) { return null; } await this.unlockObject({ id: new_container.id, type: 'courseware-containers' }); if (container.blocks?.length) { let new_block = null; for (var k = 0; k < container.blocks.length; k++) { new_block = await this.importBlock(container.blocks[k], new_container, files, structuralElement); this.importElementCounter++; if (new_block !== null) { try { await this.updateContainerPayload(new_container, structuralElement.id, container.blocks[k].id, new_block.id); } catch(error) { this.currentImportErrors.push(this.$gettext('Abschnittdaten sind beschädigt. Möglicherweise werden nicht alle Blöcke dargestellt') + ': ' + structuralElement.attributes.title + '→' + container.attributes.title); } } } } }, async importBlock(block, block_container, files, element) { this.setImportStructuresState('Lege neuen Block an: ' + block.attributes.title); try { await this.createBlockInContainer({ container: {type: block_container.type, id: block_container.id}, blockType: block.attributes['block-type'], }); } catch(error) { this.currentImportErrors.push(this.$gettext('Block konnte nicht erstellt werden') + ': ' + element.attributes.title + '→' + block_container.attributes.title + '→' + block.attributes.title); return null; } let new_block = this.lastCreatedBlocks; // update old id ids in payload part for (var i = 0; i < files.length; i++) { if (files[i].related_block_id === block.id) { let old_file = this.file_mapping[files[i].id].old; let new_file = this.file_mapping[files[i].id].new; let payload = JSON.stringify(block.attributes.payload); if (new_file) { payload = payload.replaceAll(old_file.id, new_file.id); payload = payload.replaceAll(old_file.folder.id, new_file.relationships.parent.data.id); } else { payload = payload.replaceAll(old_file.id, ''); payload = payload.replaceAll(old_file.folder.id, ''); } block.attributes.payload = JSON.parse(payload); } } this.setImportStructuresState('Aktualisiere neuen Block: ' + block.attributes.title); try { await this.updateBlockInContainer({ attributes: block.attributes, blockId: new_block.id, containerId: block_container.id, }); } catch(error) { this.currentImportErrors.push(this.$gettext('Blockdaten sind beschädigt. Es werden die Standardwerte eingesetzt') + ': ' + element.attributes.title + '→' + block_container.attributes.title + '→' + block.attributes.title); this.unlockObject({ id: new_block.id, type: 'courseware-blocks' }); } return new_block; }, async updateContainerPayload(container, structuralElementId, oldBlockId, newBlockId) { container.attributes.payload.sections.forEach((section, index) => { let blockIndex = section.blocks.findIndex(blockID => blockID === oldBlockId); if(blockIndex > -1) { container.attributes.payload.sections[index].blocks[blockIndex] = newBlockId; } }); await this.lockObject({ id: container.id, type: 'courseware-containers' }); await this.updateContainer({ container: container, structuralElementId: structuralElementId }); await this.unlockObject({ id: container.id, type: 'courseware-containers' }); }, async uploadAllFiles(files) { let createFolder = false; for (let i = 0; i < files.length; i++) { if (files[i].folder !== null) { createFolder = true; } } if (files.length === 0 || !createFolder) { this.setImportFilesProgress(100); this.setImportFilesState(''); return true; } // create folder for importing the files into this.setImportFilesProgress(0); this.setImportFilesState(''); let now = new Date(); this.setImportFilesState(this.$gettext('Lege Import Ordner an...')); let main_folder = null; try { main_folder = await this.createRootFolder({ context: this.context, folder: { type: 'StandardFolder', name: ' CoursewareImport ' + now.toLocaleString('de-DE', { timeZone: 'UTC' }) + ' ' + now.getMilliseconds(), } }); } catch(error) { this.currentImportErrors.push(this.$gettext('Anlegen des Import-Ordners fehlgeschlagen.')); } let folders = {}; // upload all files to the newly created folder if (main_folder) { for (var i = 0; i < files.length; i++) { // if the subfolder with the referenced id does not exist yet, create it if (!files[i].folder) { continue; } if (!folders[files[i].folder.id]) { this.setImportFilesState(this.$gettext('Lege Ordner an') + ': ' + files[i].folder.name); folders[files[i].folder.id] = await this.createFolder({ context: this.context, parent: { data: { id: main_folder.id, type: 'folders' } }, folder: { type: 'StandardFolder', name: files[i].folder.name } }); } // only upload files with the same id once if (this.file_mapping[files[i].id] === undefined) { let zip_filedata = await this.zip.file(files[i].id).async('blob'); // create new blob with correct type let filedata = zip_filedata.slice(0, zip_filedata.size, files[i].attributes['mime-type']); let file = null; try { file = await this.createFile({ file: files[i], filedata: filedata, folder: folders[files[i].folder.id] }); } catch (error) { this.currentImportErrors.push(this.$gettext('Import einer Datei fehlgeschlagen.')); this.setImportFilesState(this.$gettext('Fehler beim Anlegen der Datei')); } if (file !== null) { this.setImportFilesState(this.$gettext('Erzeuge Datei') + ': ' + files[i].attributes.name); } this.setImportFilesProgress(parseInt(i / files.length * 100)); //file mapping this.file_mapping[files[i].id] = { old: files[i], new: file }; } } } else { return false; } this.setImportFilesProgress(100); this.setImportFilesState(''); return true; }, ...mapActions([ 'createBlockInContainer', 'createContainer', 'createStructuralElement', 'updateContainer', 'updateBlockInContainer', 'createFolder', 'createRootFolder', 'createFile', 'loadInstance', 'lockObject', 'unlockObject', 'setImportFilesState', 'setImportFilesProgress', 'setImportStructuresState', 'setImportStructuresProgress', 'setImportErrors', 'storeCoursewareSettings', 'uploadImageForStructuralElement' ]), }, watch: { importElementCounter(counter) { if (this.elementCounter !== 0) { this.setImportStructuresProgress(parseInt(counter / this.elementCounter * 100)); } else { this.setImportStructuresProgress(100); } }, currentImportErrors(errors) { if(errors.length > 0) { this.setImportErrors(errors); } } }, };