diff --git a/app/controllers/materialien/files.php b/app/controllers/materialien/files.php index 6f457677483cb8a83ac6b568c29164884b55a7c9..429a98c5d94b2f01227ad845a74af9725dc7346d 100644 --- a/app/controllers/materialien/files.php +++ b/app/controllers/materialien/files.php @@ -362,29 +362,37 @@ class Materialien_FilesController extends MVVController public function upload_attachment_action() { - if ($GLOBALS['user']->id === "nobody") { + $user = User::findCurrent(); + if (!$user) { throw new AccessDeniedException(); } - $file = $_FILES['file']; - $output = [ - 'name' => $file['name'], - 'size' => $file['size']]; - $mvvfile_id = Request::option('mvvfile_id'); - $output['mvvfile_id'] = $mvvfile_id; - $range_id = Request::option('range_id', $mvvfile_id); - $output['range_id'] = $range_id; - $file_language = Request::option('file_language'); + $document_id = Request::option('document_id'); - $top_folder = $this->getTopFolder($mvvfile_id); - - $user = User::findCurrent(); - - $file = StandardFile::create($_FILES['file']); - $error = $top_folder->validateUpload($file, $GLOBALS['user']->id); - if ($error != null) { - $file->delete(); + $file = $_FILES['file']; + $output = [ + 'name' => $file['name'], + 'size' => $file['size'], + 'mvvfile_id' => $mvvfile_id, + 'range_id' => Request::option('range_id', $mvvfile_id), + ]; + + $top_folder = $this->getTopFolder($output['mvvfile_id']); + + if ($document_id) { + $file = File::find($document_id); + $file->mime_type = $_FILES['file']['type'] ?? get_mime_type($_FILES['file']['name']); + $file->size = $_FILES['file']['size'] ?? filesize($_FILES['file']['tmp_name']); + $file->connectWithDataFile($_FILES['file']['tmp_name']); + } else { + $file = StandardFile::create($_FILES['file']); + } + $error = $top_folder->validateUpload($file, $user->id); + if ($error !== null) { + if (!$document_id) { + $file->delete(); + } $this->response->set_status(400); $this->render_json(compact('error')); return; @@ -399,18 +407,15 @@ class Materialien_FilesController extends MVVController return; } - $mvv_file_fileref = new MvvFileFileref([$mvvfile_id, $file_language]); - $mvv_file_fileref->fileref_id = $file->getId(); + $mvv_file_fileref = new MvvFileFileref([ + $output['mvvfile_id'], + Request::option('file_language'), + ]); + $mvv_file_fileref->fileref_id = $file->id; $mvv_file_fileref->store(); - $output['document_id'] = $file->getId(); - - $output['icon'] = Icon::create( - FileManager::getIconNameForMimeType( - $file->getMimeType() - ), - 'clickable' - )->asImg(['class' => "text-bottom"]); + $output['document_id'] = $file->id; + $output['icon'] = $file->getIcon(Icon::ROLE_CLICKABLE)->asImg(['class' => 'text-bottom']); $this->render_json($output); } diff --git a/app/views/materialien/files/add_dokument.php b/app/views/materialien/files/add_dokument.php index 3b706e90b473d03664c54dcb639b47b872b3e788..a940e95a24cedb319ebf68b556947666652d151e 100644 --- a/app/views/materialien/files/add_dokument.php +++ b/app/views/materialien/files/add_dokument.php @@ -47,7 +47,18 @@ <span class="icon"></span> <span class="name"></span> <span class="size"></span> - <a class="remove_attachment"><?= Icon::create('trash')->asImg(['class' => 'text-bottom']) ?></a> + <button class="refresh_attachment as-link" data-language="<?= htmlReady($key) ?>"> + <?= Icon::create('refresh')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei aktualisieren'), + ]) ?> + </button> + <button class="remove_attachment as-link"> + <?= Icon::create('trash')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei löschen'), + ]) ?> + </button> </li> </ul> <div id="statusbar_container"> @@ -79,7 +90,18 @@ <span class="icon"><?= Icon::create('file', Icon::ROLE_INFO, ['class' => 'text-bottom']); ?></span> <span class="name"><?= htmlReady($documents[$key]->filename) ?></span> <span class="size"></span> - <a class="remove_attachment"><?= Icon::create('trash', 'clickable')->asImg(['class' => "text-bottom"]) ?></a> + <button class="refresh_attachment as-link" data-language="<?= htmlReady($key) ?>"> + <?= Icon::create('refresh')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei aktualisieren'), + ]) ?> + </button> + <button class="remove_attachment as-link"> + <?= Icon::create('trash')->asImg([ + 'class' => 'text-bottom', + 'title' => _('Datei löschen'), + ]) ?> + </button> </li> <? endif; ?> </ul> diff --git a/lib/filesystem/MVVFolder.php b/lib/filesystem/MVVFolder.php index c5373b97d514923c7d5a5c425f78d533cc75e2b3..2d153dc8090b75337c725f7fc3062314cbf52d15 100644 --- a/lib/filesystem/MVVFolder.php +++ b/lib/filesystem/MVVFolder.php @@ -82,7 +82,7 @@ class MVVFolder extends StandardFolder if ($folder) { $topfolder = $folder->getTypedFolder(); } - return $topfolder ?: null; + return $topfolder ?? null; } /** diff --git a/lib/filesystem/StandardFolder.php b/lib/filesystem/StandardFolder.php index bbbb1fcb3935f6e7c3ff210d82cdf7857f036e2b..c387bb9b812d23aa4372679ecd549754c19e519c 100644 --- a/lib/filesystem/StandardFolder.php +++ b/lib/filesystem/StandardFolder.php @@ -224,14 +224,13 @@ class StandardFolder implements FolderType } /** - * @param FileType $newfile - * @param string $user_id + * @param string $user_id * @return string */ - public function validateUpload(FileType $newfile, $user_id) + public function validateUpload(FileType $file, $user_id) { $upload_type = FileManager::getUploadTypeConfig($this->range_id, $user_id); - return $this->getValidationMessages($upload_type, $newfile); + return $this->getValidationMessages($upload_type, $file); } protected function getValidationMessages($upload_type, $newfile) @@ -256,6 +255,8 @@ class StandardFolder implements FolderType if (in_array($ext, $types) && $upload_type['type'] === 'allow') { return sprintf(_('Sie dürfen den Dateityp %s nicht hochladen!'), $ext); } + + return null; } /** diff --git a/resources/assets/javascripts/mvv.js b/resources/assets/javascripts/mvv.js index a339624b60864457817341cf5185f3cfe6c0dbc1..92dcd6878acd96395b8c64a6efa35efbfc00b760 100644 --- a/resources/assets/javascripts/mvv.js +++ b/resources/assets/javascripts/mvv.js @@ -67,10 +67,17 @@ jQuery(function ($) { }); $(document).on('click', '.stgfile .remove_attachment', function($event) { - STUDIP.MVV.Document.remove_attachment($(this)); + STUDIP.Dialog.confirm($gettext('Soll die Datei wirklich gelöscht werden?')).done(() => { + STUDIP.MVV.Document.remove_attachment(this); + }); return false; }); + $(document).on('click', '.stgfile .refresh_attachment', (event) => { + STUDIP.MVV.Document.refresh_attachment(event.target); + event.preventDefault(); + }); + STUDIP.dialogReady( function() { @@ -663,27 +670,55 @@ STUDIP.MVV.Document = { }) }, 100); }, - remove_attachment: function(item) { - jQuery.ajax({ - url: STUDIP.ABSOLUTE_URI_STUDIP + 'dispatch.php/materialien/files/delete_attachment', - data: { - mvvfile_id: jQuery('#mvvfile_id').val(), - fileref_id: item.closest('li') - .find('input[name=document_id]') - .val() - }, - type: 'POST' + refresh_attachment(item) { + const language = item.closest('button').dataset.language; + const document_id = item.closest('.stgfile').querySelector('[name="document_id"]').value; + + const input = document.createElement('input'); + input.type = 'file'; + input.hidden = true; + + input.addEventListener('cancel', () => { + input.remove(); }); - item.parents('td').find('.attachments').toggle(); - item.closest('li') - .fadeOut(300, function() { - jQuery(this).remove(); + input.addEventListener('change', () => { + const fd = new FormData(); + fd.append('file', input.files[0], input.files[0].name); + fd.append('mvvfile_id', jQuery('#mvvfile_id').val()); + fd.append('range_id', jQuery('#range_id').val()); + fd.append('document_id', document_id); + fd.append('file_language', language); + + const statusbar = $('#statusbar_container .statusbar') + .first() + .clone() + .show(); + statusbar.appendTo('#statusbar_container'); + + STUDIP.MVV.Document.upload_file(fd, statusbar, true).then(() => { + input.remove(); + }); + }); + + item.parentNode.after(input); + input.click(); + }, + remove_attachment(item) { + const url = STUDIP.URLHelper.getURL('dispatch.php/materialien/files/delete_attachment', { + mvvfile_id: document.getElementById('mvvfile_id').value, + fileref_id: item.closest('li').querySelector('input[name=document_id]').value, + }); + $.post(url).done(() => { + $(item).closest('td').find('.attachments').toggle(); + $(item).closest('li').fadeOut(300, function () { + this.remove(); jQuery('#upload_chooser').show(); }); + }); }, - upload_from_input: function(input, file_language) { + upload_from_input(input, file_language) { STUDIP.MVV.Document.upload_files(input.files, file_language); - jQuery(input).val(''); + input.value = ''; }, fileIDQueue: 1, upload_files: function(files, file_language) { @@ -701,82 +736,94 @@ STUDIP.MVV.Document = { STUDIP.MVV.Document.upload_file(fd, statusbar); } }, - upload_file: function(formdata, statusbar) { - $.ajax({ - xhr: function() { - var xhrobj = $.ajaxSettings.xhr(); - if (xhrobj.upload) { - xhrobj.upload.addEventListener( - 'progress', - function(event) { - var percent = 0; - var position = event.loaded || event.position; - var total = event.total; - if (event.lengthComputable) { - percent = Math.ceil((position / total) * 100); - } - //Set progress - statusbar.find('.progress').css({ 'min-width': percent + '%', 'max-width': percent + '%' }); - statusbar - .find('.progresstext') - .text(percent === 100 ? jQuery('#upload_finished').text() : percent + '%'); - }, - false - ); + upload_file(formdata, statusbar, update = false) { + return new Promise((resolve, reject) => { + $.ajax({ + xhr() { + var xhrobj = $.ajaxSettings.xhr(); + if (xhrobj.upload) { + xhrobj.upload.addEventListener( + 'progress', + function(event) { + var percent = 0; + var position = event.loaded || event.position; + var total = event.total; + if (event.lengthComputable) { + percent = Math.ceil((position / total) * 100); + } + //Set progress + statusbar.find('.progress').css({ 'min-width': percent + '%', 'max-width': percent + '%' }); + statusbar + .find('.progresstext') + .text(percent === 100 ? jQuery('#upload_finished').text() : percent + '%'); + }, + false + ); + } + return xhrobj; + }, + url: STUDIP.ABSOLUTE_URI_STUDIP + 'dispatch.php/materialien/files/upload_attachment', + type: 'POST', + contentType: false, + processData: false, + cache: false, + data: formdata, + dataType: 'json' + }).done((data) => { + const language = formdata.get('file_language'); + + statusbar.find('.progress').css({ 'min-width': '100%', 'max-width': '100%' }); + var file = jQuery('#fileselector_'+formdata.get('file_language')).find('.stgfiles > .stgfile') + .first() + .clone(); + file.find('.name').text(data.name); + if (data.size < 1024) { + file.find('.size').text(data.size + 'B'); } - return xhrobj; - }, - url: STUDIP.ABSOLUTE_URI_STUDIP + 'dispatch.php/materialien/files/upload_attachment', - type: 'POST', - contentType: false, - processData: false, - cache: false, - data: formdata, - dataType: 'json' - }) - .done(function(data) { - statusbar.find('.progress').css({ 'min-width': '100%', 'max-width': '100%' }); - var file = jQuery('#fileselector_'+formdata.get('file_language')).find('.stgfiles > .stgfile') - .first() - .clone(); - file.find('.name').text(data.name); - if (data.size < 1024) { - file.find('.size').text(data.size + 'B'); - } - if (data.size > 1024 && data.size < 1024 * 1024) { - file.find('.size').text(Math.floor(data.size / 1024) + 'KB'); - } - if (data.size > 1024 * 1024 && data.size < 1024 * 1024 * 1024) { - file.find('.size').text(Math.floor(data.size / 1024 / 1024) + 'MB'); - } - if (data.size > 1024 * 1024 * 1024) { - file.find('.size').text(Math.floor(data.size / 1024 / 1024 / 1024) + 'GB'); - } - file.find('.icon').html(data.icon); - file.find('input[name=document_id]').attr('value', data.document_id); - jQuery('#fileviewer_'+formdata.get('file_language')).find('.stgfiles').append(file); - jQuery('#fileselector_'+formdata.get('file_language')).toggle(); - jQuery('#fileselector_'+formdata.get('file_language')).parents('.attachments').toggle(); - jQuery('#fileselector_'+formdata.get('file_language')).parents('.attachments').find('span').toggle(); - file.fadeIn(300); - statusbar.find('.progresstext').text(jQuery('#upload_received_data').text()); - statusbar.delay(1000).fadeOut(300, function() { - jQuery('#upload_chooser').hide(); - jQuery(this).remove(); - }); - }) - .fail(function(jqxhr, status, errorThrown) { - var error = jqxhr.responseJSON.error; - - statusbar - .find('.progress') - .addClass('progress-error') - .attr('title', error); - statusbar.find('.progresstext').html(error); - statusbar.on('click', function() { - jQuery(this).fadeOut(300, function() { - jQuery(this).remove(); + if (data.size > 1024 && data.size < 1024 * 1024) { + file.find('.size').text(Math.floor(data.size / 1024) + 'KB'); + } + if (data.size > 1024 * 1024 && data.size < 1024 * 1024 * 1024) { + file.find('.size').text(Math.floor(data.size / 1024 / 1024) + 'MB'); + } + if (data.size > 1024 * 1024 * 1024) { + file.find('.size').text(Math.floor(data.size / 1024 / 1024 / 1024) + 'GB'); + } + file.find('.icon').html(data.icon); + file.find('input[name=document_id]').attr('value', data.document_id); + if (update) { + $(`#fileviewer_${language} .stgfiles`).empty().append(file); + file.show(); + } else { + $(`#fileviewer_${language}`).find('.stgfiles').append(file); + $(`#fileselector_${language}`) + .toggle() + .parents('.attachments').toggle() + .find('span').toggle(); + file.fadeIn(300); + } + statusbar.find('.progresstext').text(jQuery('#upload_received_data').text()); + statusbar.delay(1000).fadeOut(300, function() { + $('#upload_chooser').hide(); + this.remove(); }); + + resolve(); + }).fail(function(jqxhr, status, errorThrown) { + var error = jqxhr.responseJSON.error; + + statusbar + .find('.progress') + .addClass('progress-error') + .attr('title', error); + statusbar.find('.progresstext').html(error); + statusbar.on('click', function() { + jQuery(this).fadeOut(300, function() { + jQuery(this).remove(); + }); + }); + + reject(new Error(error)); }); }); }