From 1e5cf961999963871b03b819fc16d62cefbfcb02 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 21 Sep 2022 10:33:46 +0000 Subject: [PATCH] fix #874 Closes #874 Merge request studip/studip!992 --- .../Routes/Files/FileRefsCreateByUpload.php | 7 + .../JsonApi/Schemas/ContentTermsOfUse.php | 1 + lib/models/Courseware/BlockTypes/Folder.php | 52 +++- .../assets/stylesheets/scss/courseware.scss | 81 +++++- .../courseware/CoursewareAudioBlock.vue | 38 +-- .../courseware/CoursewareCanvasBlock.vue | 24 +- .../courseware/CoursewareDownloadBlock.vue | 9 +- .../courseware/CoursewareFolderBlock.vue | 263 ++++++++++++++---- .../vue/store/courseware/courseware.module.js | 16 +- 9 files changed, 395 insertions(+), 96 deletions(-) diff --git a/lib/classes/JsonApi/Routes/Files/FileRefsCreateByUpload.php b/lib/classes/JsonApi/Routes/Files/FileRefsCreateByUpload.php index 3bb00d1b9ed..8c18ca1aaae 100644 --- a/lib/classes/JsonApi/Routes/Files/FileRefsCreateByUpload.php +++ b/lib/classes/JsonApi/Routes/Files/FileRefsCreateByUpload.php @@ -22,8 +22,15 @@ class FileRefsCreateByUpload extends NonJsonApiController throw new AuthorizationFailedException(); } + $term_id = $request->getParsedBody()['term-id']; + $fileRef = $this->handleUpload($request, $folder); + if ($term_id) { + $fileRef->content_terms_of_use_id = $term_id; + $fileRef->store(); + } + return $this->redirectToFileRef($response, $fileRef); } } diff --git a/lib/classes/JsonApi/Schemas/ContentTermsOfUse.php b/lib/classes/JsonApi/Schemas/ContentTermsOfUse.php index b827788e1fc..23c929437a9 100644 --- a/lib/classes/JsonApi/Schemas/ContentTermsOfUse.php +++ b/lib/classes/JsonApi/Schemas/ContentTermsOfUse.php @@ -19,6 +19,7 @@ class ContentTermsOfUse extends SchemaProvider 'name' => (string) $resource['name'], 'description' => mb_strlen($resource['description']) ? (string) $resource['description'] : null, 'icon' => $resource['icon'], + 'is-default' => (bool) $resource['is_default'], 'download-condition' => (int) $resource['download_condition'], 'mkdate' => date('c', $resource['mkdate']), 'chdate' => date('c', $resource['chdate']), diff --git a/lib/models/Courseware/BlockTypes/Folder.php b/lib/models/Courseware/BlockTypes/Folder.php index ef0bd2eb512..b3a0767b0d6 100644 --- a/lib/models/Courseware/BlockTypes/Folder.php +++ b/lib/models/Courseware/BlockTypes/Folder.php @@ -38,9 +38,59 @@ class Folder extends BlockType ]; } + /** + * Returns the decoded payload of the block associated with this instance. + * + * @return mixed the decoded payload + */ + public function getPayload() + { + $user = \User::findCurrent(); + $payload = $this->decodePayloadString($this->block['payload']); + + $folder = \Folder::find($payload['folder_id']); + $payload['folder-type'] = null; + $payload['files'] = []; + + if ($folder) { + $typedFolder = $folder->getTypedFolder(); + $payload['folder-type'] = $typedFolder->folder_type; + + foreach ($typedFolder->getFiles() as $folderFile) { + $file['id'] = $folderFile->id; + $file['attributes'] = [ + 'name' => $folderFile->name, + 'mime-type' => $folderFile->mime_type, + 'filesize' => (int) $folderFile->size, + 'mkdate' => date('c', $folderFile->mkdate), + ]; + $file['relationships'] = [ + 'owner' => [ + 'data' => ['type' => 'users', 'id' => $folderFile->user_id], + 'meta' => ['name' => $folderFile->getFileRef()->getAuthorName()] + ] + ]; + $file['meta'] = [ + 'download-url' => $folderFile->getDownloadURL(), + ]; + + if ($this->filePermission($typedFolder, $file, $user)) { + array_push($payload['files'], $file); + } + } + } + + return $payload; + } + + private function filePermission($typedFolder, $file, $user): bool + { + return $typedFolder->folder_type !== 'HomeworkFolder' || $user->id === $file['relationships']['owner']['data']['id'] || $typedFolder->isReadable($user->id); + } + /** - * get all files related to this bloc. + * get all files related to this block. * * @return \FileRef[] list of file references realted to this block */ diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 6f8aadba176..f7ee74ea7f6 100644 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -3674,16 +3674,22 @@ i f r a m e b l o c k e n d /* * * * * * * * * * * * f o l d e r b l o c k * * * * * * * * * * * */ +.cw-block-folder-info { + border: solid thin $content-color-40; + padding: 10px 10px 0 10px; + overflow: hidden; + border-bottom: none; +} .cw-block-folder-list { - border: solid thin #ccc; - padding: 4px; + border: solid thin $content-color-40; + padding: 0; list-style: none; .cw-block-folder-file-item { list-style: none; &:not(:last-child) { - border-bottom: solid thin #ccc; + border-bottom: solid thin $content-color-40; } a { display: block; @@ -3702,16 +3708,27 @@ f o l d e r b l o c k margin: 1em; } } - // for folder and download block +.cw-block-folder-upload { + border: solid thin $content-color-40; + padding: 1em 10px; + border-top: none; + + .cw-file-input { + width: calc(100% - 148px); + vertical-align: middle; + } +} +// for folder and download block .cw-block-file-info { @include background-icon(file, clickable, 24); background-repeat: no-repeat; - display: inline-block; - padding-left: 26px; - margin: 1em; - line-height: 24px; - color: $base-color; + display: block; + padding: 16px 16px 16px 40px; + background-position: 10px 16px; + width: calc(100% - 56px); + overflow: hidden; + text-overflow: ellipsis; &.cw-block-file-icon-empty { color: $black; @@ -3723,35 +3740,78 @@ f o l d e r b l o c k } &.cw-block-file-icon-audio { @include background-icon(file-audio, clickable, 24); + &.download-disabled { + @include background-icon(file-audio, info, 24); + } } &.cw-block-file-icon-pic { @include background-icon(file-pic, clickable, 24); + &.download-disabled { + @include background-icon(file-pic, info, 24); + } } &.cw-block-file-icon-video { @include background-icon(file-video, clickable, 24); + &.download-disabled { + @include background-icon(file-video, info, 24); + } } &.cw-block-file-icon-pdf { @include background-icon(file-pdf, clickable, 24); + &.download-disabled { + @include background-icon(file-pdf, info, 24); + } } &.cw-block-file-icon-word { @include background-icon(file-word, clickable, 24); + &.download-disabled { + @include background-icon(file-word, info, 24); + } } &.cw-block-file-icon-spreadsheet { @include background-icon(file-excel, clickable, 24); + &.download-disabled { + @include background-icon(file-excel, info, 24); + } } &.cw-block-file-icon-text { @include background-icon(file-text, clickable, 24); + &.download-disabled { + @include background-icon(file-text, info, 24); + } } &.cw-block-file-icon-ppt { @include background-icon(file-ppt, clickable, 24); + &.download-disabled { + @include background-icon(file-ppt, info, 24); + } } &.cw-block-file-icon-archive { @include background-icon(file-archive, clickable, 24); + &.download-disabled { + @include background-icon(file-archive, info, 24); + } } &.cw-block-file-icon-file { @include background-icon(file, clickable, 24); + &.download-disabled { + @include background-icon(file, info, 24); + } } } +.cw-block-file-details { + margin-top: -16px; + padding-left: 40px; + padding-bottom: 16px; + color: $dark-gray-color; +} +.cw-block-file-owner, +.cw-block-file-mkdate { + display: block; + width: calc(100% - 56px); + overflow: hidden; + text-overflow: ellipsis; +} /* * * * * * * * * * * * * * * f o l d e r b l o c k e n d * * * * * * * * * * * * * * */ @@ -3763,7 +3823,6 @@ d o w n l o a d b l o c k .cw-block-download { .cw-block-download-content { border: solid thin $content-color-40; - padding: 4px; .cw-block-download-file-item { a { display: block; @@ -4766,7 +4825,7 @@ cw tiles end .cw-file-input { width: stretch; border: solid thin $base-color; - font-size: 14px; + font-size: 13px; cursor: pointer; &::file-selector-button { diff --git a/resources/vue/components/courseware/CoursewareAudioBlock.vue b/resources/vue/components/courseware/CoursewareAudioBlock.vue index 7a946fa2088..218f11dc666 100644 --- a/resources/vue/components/courseware/CoursewareAudioBlock.vue +++ b/resources/vue/components/courseware/CoursewareAudioBlock.vue @@ -587,28 +587,32 @@ export default { async storeRecording() { let view = this; let user = this.usersById({id: this.userId}); - let file = {}; let blob = new Blob(view.chunks, {type: 'audio/webm; codecs:vp9' }); - file.attributes = {}; - file.attributes.name = (user.attributes["formatted-name"]).replace(/\s+/g, '_') + '.webm'; - let fileObj = false; - try { - fileObj = await this.createFile({ - file: file, - filedata: blob, - folder: {id: this.currentFolderId} - }); - } - catch(e) { - this.companionError({ - info: this.$gettext('Es ist ein Fehler aufgetreten! Die Aufnahme konnte nicht gespeichert werden.') - }); - console.debug(e); - } + let file = { + attributes: { + name: (user.attributes["formatted-name"]).replace(/\s+/g, '_') + '.webm' + }, + relationships: { + 'terms-of-use': { + data: { + id: 'SELFMADE_NONPUB' + } + } + } + }; + let fileObj = await this.createFile({ + file: file, + filedata: blob, + folder: {id: this.currentFolderId} + }); if(fileObj && fileObj.type === 'file-refs') { this.companionSuccess({ info: this.$gettext('Die Aufnahme wurde erfolgreich im Dateibereich abgelegt.') }); + } else { + this.companionError({ + info: this.$gettext('Es ist ein Fehler aufgetreten! Die Aufnahme konnte nicht gespeichert werden.') + }); } this.newRecording = false; this.getFolderFiles(); diff --git a/resources/vue/components/courseware/CoursewareCanvasBlock.vue b/resources/vue/components/courseware/CoursewareCanvasBlock.vue index 79363d51fc8..de474cee2f9 100644 --- a/resources/vue/components/courseware/CoursewareCanvasBlock.vue +++ b/resources/vue/components/courseware/CoursewareCanvasBlock.vue @@ -574,24 +574,20 @@ export default { file.attributes.name = (user.attributes["formatted-name"]).replace(/\s+/g, '_') + '_' + this.block.attributes.title + '_' + this.block.id; } - let img = false; - try { - img = await this.createFile({ - file: file, - filedata: imageBlob, - folder: {id: this.currentUploadFolderId} - }); - } - catch(e) { - this.companionError({ - info: this.$gettext('Es ist ein Fehler aufgetretten! Das Bild konnte nicht gespeichert werden.') - }); - console.log(e); - } + let img = await this.createFile({ + file: file, + filedata: imageBlob, + folder: {id: this.currentUploadFolderId} + }); + if(img && img.type === 'file-refs') { this.companionSuccess({ info: this.$gettext('Das Bild wurde erfolgreich im Dateibereich abgelegt.') }); + } else { + this.companionError({ + info: this.$gettext('Es ist ein Fehler aufgetretten! Das Bild konnte nicht gespeichert werden.') + }); } }, }, diff --git a/resources/vue/components/courseware/CoursewareDownloadBlock.vue b/resources/vue/components/courseware/CoursewareDownloadBlock.vue index 2094cfa7133..1f7a78beb90 100644 --- a/resources/vue/components/courseware/CoursewareDownloadBlock.vue +++ b/resources/vue/components/courseware/CoursewareDownloadBlock.vue @@ -19,11 +19,16 @@ {{ currentSuccess }} </div> <div class="cw-block-download-file-item"> - <a target="_blank" :download="currentFile.name" :href="currentFile.download_url" @click="handleDownload"> + <a + target="_blank" + :download="currentFile.name" + :title="$gettext('Datei herunterladen')" + :href="currentFile.download_url" + @click="handleDownload" + > <span class="cw-block-file-info" :class="['cw-block-file-icon-' + currentFile.icon]"> {{ currentFile.name }} </span> - <span class="cw-block-download-download-icon"></span> </a> </div> </div> diff --git a/resources/vue/components/courseware/CoursewareFolderBlock.vue b/resources/vue/components/courseware/CoursewareFolderBlock.vue index a1b97490d09..2d1f355f5b5 100644 --- a/resources/vue/components/courseware/CoursewareFolderBlock.vue +++ b/resources/vue/components/courseware/CoursewareFolderBlock.vue @@ -10,36 +10,127 @@ > <template #content> <div v-if="currentTitle !== ''" class="cw-block-title">{{ currentTitle }}</div> + <div v-if="isHomework" class="cw-block-folder-info"> + <p> + {{ $gettext('Dieser Ordner ist ein Hausaufgabenordner. Es können nur Dateien eingestellt werden.') }} + </p> + <p v-if="!isTeacher"> + {{ $gettext('Sie selbst haben folgende Dateien in diesen Ordner eingestellt') }}: + </p> + </div> <ul class="cw-block-folder-list"> <li v-for="file in files" :key="file.id" class="cw-block-folder-file-item"> - <a target="_blank" :download="file.name" :href="file.download_url"> - <span class="cw-block-file-info" :class="['cw-block-file-icon-' + file.icon]"> - {{ file.name }} + <a + v-if="downloadEnabled" + target="_blank" + :download="file.attributes.name" + :title="$gettext('Datei herunterladen')" + :href="file.meta['download-url']" + > + <span class="cw-block-file-info" :class="['cw-block-file-icon-' + getIcon(file.attributes['mime-type'])]"> + {{ file.attributes.name }} </span> - <span class="cw-block-folder-download-icon"></span> + <div v-if="isTeacher && isHomework" class="cw-block-file-details"> + <span class="cw-block-file-owner"> + {{ file.relationships.owner.meta.name }} + </span> + <span class="cw-block-file-mkdate"> + {{ getFormattedDate(file.attributes.mkdate) }} + </span> + </div> </a> + <template v-else> + <span class="cw-block-file-info download-disabled" :class="['cw-block-file-icon-' + getIcon(file.attributes['mime-type'])]"> + {{ file.attributes.name }} + </span> + <div class="cw-block-file-details"> + <span class="cw-block-file-mkdate"> + {{ getFormattedDate(file.attributes.mkdate) }} + </span> + </div> + </template> </li> <li v-if="files.length === 0"> <span class="cw-block-file-info cw-block-file-icon-empty"> - <translate>Dieser Ordner ist leer</translate> + {{ $gettext('Dieser Ordner ist leer') }} </span> </li> </ul> + <div v-if="uploadEnabled" class="cw-block-folder-upload"> + <form class="default" @submit.prevent=""> + <label> + {{ $gettext('Dateien zum Hochladen auswählen') }} + <input class="cw-file-input" ref="uploadFile" type="file" @change="displayTermSelector"/> + <button class="button" @click="uploadFile"> + {{ $gettext('Datei hochladen') }} + </button> + </label> + </form> + <studip-dialog + v-if="showTermSelector" + width="780" + height="510" + :title="$gettext('Lizenz auswählen')" + :confirmText="$gettext('Speichern')" + confirmClass="accept" + :closeText="$gettext('Lizenzauswahl abbrechen')" + closeClass="cancel" + @close="showTermSelector = false" + @confirm="selectTerm" + > + <template v-slot:dialogContent> + <form class="default" @submit.prevent=""> + <div style="margin-bottom: 1ex;"> + {{ $gettext('Bereitgestellte Dateien können heruntergeladen und ggf. weiterverbreitet werden. Dabei ist das Urheberrecht sowohl beim Hochladen der Datei als auch bei der Nutzung zu beachten. Bitte geben Sie daher an, um welche Art von Bereitstellung es sich handelt. Diese Angabe dient mehreren Zwecken: Beim Herunterladen wird ein Hinweis angezeigt, welche Nutzung der Datei zulässig ist. Beim Hochladen stellt die Angabe eine Entscheidungshilfe dar, damit Sie sichergehen können, dass die Datei tatsächlich bereitgestellt werden darf.') }} + </div> + <fieldset class="select_terms_of_use"> + <template v-for="term in termsOfUse"> + <input + type="radio" + name="content_terms_of_use_id" + :value="term.id" + v-model="selectedTerm" + :id="'content_terms_of_use-' + term.id" + :checked="selectedTerm === term.id" + :aria-description="term.description" + :key="term.id + '_input'" + /> + <label @click="selectedTerm = term.id" :key="term.id + 'label'"> + <div class="icon"> + <studip-icon :shape="term.attributes.icon" size="32"/> + </div> + <div class="text"> + {{ term.attributes.name }} + </div> + <studip-icon shape="arr_1down" size="24" class="arrow" /> + <studip-icon shape="check-circle" size="24" class="check" /> + </label> + <div class="terms_of_use_description" :key="term.id + '_description'"> + <div class="description"> + {{ term.attributes.description }} + </div> + </div> + </template> + </fieldset> + </form> + </template> + </studip-dialog> + </div> </template> <template v-if="canEdit" #edit> <form class="default" @submit.prevent=""> <label> - <translate>Überschrift</translate> + {{ $gettext('Überschrift') }} <input type="text" v-model="currentTitle" /> </label> <label> - <translate>Ordner</translate> - <courseware-folder-chooser v-model="currentFolderId" allowUserFolders /> + {{ $gettext('Ordner') }} + <courseware-folder-chooser v-model="currentFolderId" allowUserFolders allowHomeworkFolders /> </label> </form> </template> <template #info> - <p><translate>Informationen zum Dateiordner-Block</translate></p> + <p>{{ $gettext('Informationen zum Dateiordner-Block') }}</p> </template> </courseware-default-block> </div> @@ -48,6 +139,7 @@ <script> import CoursewareDefaultBlock from './CoursewareDefaultBlock.vue'; import CoursewareFolderChooser from './CoursewareFolderChooser.vue'; +import StudipDialog from '../StudipDialog.vue'; import { mapActions, mapGetters } from 'vuex'; @@ -56,6 +148,7 @@ export default { components: { CoursewareDefaultBlock, CoursewareFolderChooser, + StudipDialog }, props: { block: Object, @@ -66,66 +159,71 @@ export default { return { currentTitle: '', currentFolderId: '', - currentFileType: '', - files: [], + currentFolderType: '', + showTermSelector: false, + selectedTerm: null, }; }, computed: { ...mapGetters({ - relatedFileRefs: 'file-refs/related', - urlHelper: 'urlHelper', - relatedTermOfUse: 'terms-of-use/related', + folderById: 'folders/byId', + termsOfUse: 'terms-of-use/all' }), folderType() { return this.block?.attributes?.payload?.type; }, + storedFolderType() { + return this.block?.attributes?.payload?.folder_type; + }, + folderTypeHasChanged() { + return this.folderType === this.storedFolderType; + }, folderId() { return this.block?.attributes?.payload?.folder_id; }, title() { return this.block?.attributes?.payload?.title; }, + files() { + return this.block?.attributes?.payload?.files; + }, + isHomework() { + return this.folderType === 'HomeworkFolder'; + }, + uploadEnabled() { + return !this.isTeacher && this.isHomework; + }, + downloadEnabled() { + return this.isTeacher || !this.isHomework; + } }, - mounted() { + async mounted() { + await this.loadTermsOfUse(); this.initCurrentData(); }, methods: { ...mapActions({ - loadRelatedFileRefs: 'file-refs/loadRelated', + loadFolder: 'folders/loadById', + loadBlock: 'courseware-blocks/loadById', updateBlock: 'updateBlockInContainer', + createFile: 'createFile', + companionWarning: 'companionWarning', + companionSuccess: 'companionSuccess', + companionError: 'companionError', + loadTermsOfUse: 'terms-of-use/loadAll' }), - initCurrentData() { + async initCurrentData() { this.currentTitle = this.title; this.currentFolderId = this.folderId; - this.currentFolderType = this.folderType; - }, - async getFolderFiles() { - const parent = { type: 'folders', id: `${this.currentFolderId}` }; - const relationship = 'file-refs'; - const options = { include: 'terms-of-use' }; - await this.loadRelatedFileRefs({ parent, relationship, options }); - const fileRefs = this.relatedFileRefs({ parent, relationship }) ?? []; - this.processFiles(fileRefs); - }, - processFiles(files) { - this.files = files - .filter((file) => { - if (this.relatedTermOfUse({parent: file, relationship: 'terms-of-use'}).attributes['download-condition'] !== 0) { - return false; - } else { - return true; - } - }) - .map(({ id, attributes }) => ({ - id, - name: attributes.name, - icon: this.getIcon(attributes['mime-type']), - download_url: this.urlHelper.getURL( - `sendfile.php/`, - { type: 0, file_id: id, file_name: attributes.name }, - true - ), - })); + if (this.$refs?.uploadFile) { + this.$refs.uploadFile.value = null; + } + this.selectedTerm = this.getDefaultTerm(); + }, + async setCurrentFolderType() { + await this.loadFolder({ id: this.currentFolderId }); + const folder = this.folderById({ id: this.currentFolderId }); + this.currentFolderType = folder?.attributes['folder-type']; }, getIcon(mimeType) { let icon = 'file'; @@ -171,12 +269,22 @@ export default { return icon; }, + getFormattedDate(unformattedDate) { + const date = new Date(unformattedDate); + const localeDate = date.toLocaleDateString("de-DE", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }); + + return `${localeDate} ${date.getHours()}:${(date.getMinutes() < 10 ? '0' : '') + date.getMinutes()}:${(date.getSeconds() < 10 ? '0' : '') + date.getSeconds()}`; + }, storeBlock() { let attributes = {}; attributes.payload = {}; attributes.payload.title = this.currentTitle; attributes.payload.folder_id = this.currentFolderId; - attributes.payload.type = this.currentFileType; + attributes.payload.type = this.currentFolderType; this.updateBlock({ attributes: attributes, @@ -184,10 +292,69 @@ export default { containerId: this.block.relationships.container.data.id, }); }, + displayTermSelector() { + this.showTermSelector = true; + }, + selectTerm() { + this.showTermSelector = false; + this.uploadFile(); + }, + async uploadFile() { + const userFile = this.$refs?.uploadFile?.files[0]; + if (!userFile) { + this.companionWarning({ + info: this.$gettext('Bitte wählen Sie eine Datei aus.') + }); + return; + } + + let file = { + attributes: { + name: userFile.name.replace(/\s/g, '_') + }, + relationships: { + 'terms-of-use': { + data: { + id: this.selectedTerm + } + } + } + }; + let fileObj = await this.createFile({ + file: file, + filedata: userFile, + folder: { id: this.currentFolderId } + }); + if (fileObj && fileObj.type === 'file-refs') { + this.companionSuccess({ + info: this.$gettext('Die Datei wurde erfolgreich im Dateibereich abgelegt.') + }); + } else { + if (this.folderType !== 'HomeworkFolder') { + this.companionError({ + info: this.$gettext('Es ist ein Fehler aufgetretten.') + }); + } + } + this.reload(); + }, + async reload() { + await this.loadBlock({ id: this.block.id }); + this.initCurrentData(); + }, + getDefaultTerm() { + const defaultTerm = this.termsOfUse.filter(term => term.attributes['is-default'])[0]; + if (defaultTerm) { + return defaultTerm.id; + } + return null; + } }, watch: { currentFolderId() { - this.getFolderFiles(); + if (this.canEdit) { + this.setCurrentFolderType(); + } }, }, }; diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js index 85b7d11ca85..a211103ffcf 100644 --- a/resources/vue/store/courseware/courseware.module.js +++ b/resources/vue/store/courseware/courseware.module.js @@ -286,8 +286,12 @@ export const actions = { }, async createFile(context, { file, filedata, folder }) { + const termId = file.relationships['terms-of-use'].data.id; const formData = new FormData(); formData.append('file', filedata, file.attributes.name); + if (termId) { + formData.append('term-id', termId); + } const url = `folders/${folder.id}/file-refs`; let request = await state.httpClient.post(url, formData, { @@ -295,10 +299,16 @@ export const actions = { 'Content-Type': 'multipart/form-data', }, }); + let response = null; + try { + response = await state.httpClient.get(request.headers.location); + } + catch(e) { + console.debug(e); + response = null; + } - return state.httpClient.get(request.headers.location).then((response) => { - return response.data.data; - }); + return response ? response.data.data : response; }, async createRootFolder({ dispatch, rootGetters }, { context, folder }) { -- GitLab