<template> <div> <div class="control"> <label class="control__label">{{ $t('tableXMLImporter.fileLabel') }}</label> <div class="control__description"> {{ $t('tableXMLImporter.fileDescription') }} <pre> <notes> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> <note> <heading>Reminder</heading> <heading2>Reminder2</heading2> <to>Tove</to> <from>Jani</from> <body>Don't forget me this weekend!</body> </note> </notes></pre > </div> <div class="control__elements"> <div class="file-upload"> <input v-show="false" ref="file" type="file" accept=".xml" @change="select($event)" /> <a class="button button--large button--ghost file-upload__button" :class="{ 'button--loading': state !== null }" @click.prevent="$refs.file.click($event)" > <i class="fas fa-cloud-upload-alt"></i> {{ $t('tableXMLImporter.chooseButton') }} </a> <div v-if="state === null" class="file-upload__file"> {{ filename }} </div> <template v-else> <ProgressBar :value="fileLoadingProgress" :show-value="state === 'loading'" :status=" state === 'loading' ? $t('importer.loading') : stateTitle " /> </template> </div> <div v-if="$v.filename.$error" class="error"> {{ $t('error.fieldRequired') }} </div> </div> </div> <Alert v-if="error !== ''" :title="$t('common.wrong')" type="error" icon="exclamation" > {{ error }} </Alert> <TableImporterPreview v-if="error === '' && Object.keys(preview).length !== 0" :preview="preview" ></TableImporterPreview> </div> </template> <script> import { required } from 'vuelidate/lib/validators' import form from '@baserow/modules/core/mixins/form' import importer from '@baserow/modules/database/mixins/importer' import TableImporterPreview from '@baserow/modules/database/components/table/TableImporterPreview' import { XMLParser } from '@baserow/modules/database/utils/xml' export default { name: 'TableXMLImporter', components: { TableImporterPreview }, mixins: [form, importer], data() { return { filename: '', rawData: null, } }, validations: { values: { getData: { required }, }, filename: { required }, }, methods: { /** * Method that is called when a file has been chosen. It will check if the file is * not larger than 15MB. Otherwise it will take a long time and possibly a crash * if so many entries have to be loaded into memory. If the file is valid, the * contents will be loaded into memory and the reload method will be called which * parses the content. */ select(event) { if (event.target.files.length === 0) { return } const file = event.target.files[0] const maxSize = parseInt(this.$env.BASEROW_MAX_IMPORT_FILE_SIZE_MB, 10) * 1024 * 1024 if (file.size > maxSize) { this.handleImporterError( this.$t('tableXMLImporter.limitFileSize', { limit: this.$env.BASEROW_MAX_IMPORT_FILE_SIZE_MB, }) ) this.filename = '' } else { this.resetImporterState() this.fileLoadingProgress = 0 this.$emit('changed') this.state = 'loading' this.filename = file.name const reader = new FileReader() reader.addEventListener('progress', (event) => { this.fileLoadingProgress = (event.loaded / event.total) * 100 }) reader.addEventListener('load', (event) => { this.rawData = event.target.result this.fileLoadingProgress = 100 this.reload() }) reader.readAsBinaryString(event.target.files[0]) } }, async reload() { this.resetImporterState() this.state = 'parsing' await this.$ensureRender() const xmlParser = new XMLParser() xmlParser.parse(this.rawData) await this.$ensureRender() xmlParser.loadXML(3) await this.$ensureRender() const [header, xmlData, errors] = xmlParser.transform() if (errors.length > 0) { this.handleImporterError( this.$t('tableXMLImporter.processingError', { errors: errors.join('\n'), }) ) return } if (xmlData.length === 0) { this.handleImporterError(this.$t('tableXMLImporter.emptyError')) return } let hasHeader = false if (header.length > 0) { xmlData.unshift(header) hasHeader = true } const limit = this.$env.INITIAL_TABLE_DATA_LIMIT if (limit !== null && xmlData.length > limit) { this.handleImporterError( this.$t('tableXMLImporter.limitError', { limit }) ) return } const dataWithHeader = this.ensureHeaderExistsAndIsValid( xmlData, hasHeader ) this.values.getData = async () => { await this.$ensureRender() xmlParser.loadXML() await this.$ensureRender() const [header, xmlData, errors] = xmlParser.transform() if (errors.length > 0) { throw new Error(errors) } let hasHeader = false if (header.length > 0) { xmlData.unshift(header) hasHeader = true } const dataWithHeader = this.ensureHeaderExistsAndIsValid( xmlData, hasHeader ) return dataWithHeader } this.state = null this.preview = this.getPreview(dataWithHeader) }, }, } </script>