From 48b1bec7eaae20c30d8d91f8a80226dd6fb2396b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 15 Jun 2020 15:18:19 +0200 Subject: [PATCH] Improve form behaviour to allow removing of document: * add button to remove document ; * fix error when document is removed ; --- CHANGELOG.md | 7 +- .../ChillDocStoreExtension.php | 1 + Form/StoredObjectType.php | 18 ++- Resources/config/services/form.yml | 6 + .../public/module/async_upload/index.scss | 17 ++- .../public/module/async_upload/uploader.js | 128 ++++++++++++++++-- Resources/translations/messages.fr.yml | 1 + Resources/views/Form/fields.html.twig | 2 + 8 files changed, 165 insertions(+), 15 deletions(-) create mode 100644 Resources/config/services/form.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9a4cd39..3ad4ff222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ Version 1.5.3 - the javascript for uploading a file now works within collections, listening to collection events. -Master branch +Version 1.5.4 ============= - replace default message on download button below dropzone ; @@ -27,3 +27,8 @@ Master branch - add privacy events to document edit / update - remove dump message +Version 1.5.5 +============= + +- add button to remove existing document in form, and improve UI in this part +- fix error when document is removed in form diff --git a/DependencyInjection/ChillDocStoreExtension.php b/DependencyInjection/ChillDocStoreExtension.php index 44141741e..ed36f1845 100644 --- a/DependencyInjection/ChillDocStoreExtension.php +++ b/DependencyInjection/ChillDocStoreExtension.php @@ -30,6 +30,7 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf $loader->load('services/controller.yml'); $loader->load('services/menu.yml'); $loader->load('services/fixtures.yml'); + $loader->load('services/form.yml'); } public function prepend(ContainerBuilder $container) diff --git a/Form/StoredObjectType.php b/Form/StoredObjectType.php index 824bd62ad..64cc32e83 100644 --- a/Form/StoredObjectType.php +++ b/Form/StoredObjectType.php @@ -10,14 +10,25 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Chill\DocStoreBundle\Entity\StoredObject; use Symfony\Component\Form\CallbackTransformer; +use Doctrine\ORM\EntityManagerInterface; /** - * + * Form type which allow to join a document * - * @author Julien Fastré */ class StoredObjectType extends AbstractType { + /** + * + * @var EntityManagerInterface + */ + protected $em; + + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -86,6 +97,9 @@ class StoredObjectType extends AbstractType } if (NULL === $object->getFilename()) { + // remove the original object + $this->em->remove($object); + return null; } diff --git a/Resources/config/services/form.yml b/Resources/config/services/form.yml new file mode 100644 index 000000000..c84507e51 --- /dev/null +++ b/Resources/config/services/form.yml @@ -0,0 +1,6 @@ +services: + Chill\DocStoreBundle\Form\StoredObjectType: + arguments: + $em: '@Doctrine\ORM\EntityManagerInterface' + tags: + - { name: form.type } diff --git a/Resources/public/module/async_upload/index.scss b/Resources/public/module/async_upload/index.scss index 9d42d88d9..54299efce 100644 --- a/Resources/public/module/async_upload/index.scss +++ b/Resources/public/module/async_upload/index.scss @@ -1,6 +1,7 @@ +// override dropzone from dropzoneJS .dropzone { margin-bottom: 0.5rem; - + .dz-preview { display: initial; margin-left: auto; @@ -19,7 +20,15 @@ } } -.sc-button.dz-bt-below-dropzone { - width: 100%; -} +.chill-dropzone__below-zone { + display: flex; + + & > *:not(:last-child) { + margin-right: 1rem; + } + + .sc-button.dz-bt-below-dropzone { + width: 100%; + } +} diff --git a/Resources/public/module/async_upload/uploader.js b/Resources/public/module/async_upload/uploader.js index 9ace4282c..9e96070bb 100644 --- a/Resources/public/module/async_upload/uploader.js +++ b/Resources/public/module/async_upload/uploader.js @@ -98,15 +98,26 @@ var encryptFile = function(originalFile, zoneData, done) { reader.readAsArrayBuffer(originalFile); }; +var addBelowButton = (btn, zone, zoneData) => { + let + belowZone = zone.querySelector('.chill-dropzone__below-zone'); + + if (belowZone === null) { + belowZone = document.createElement('div'); + belowZone.classList.add('chill-dropzone__below-zone'); + zone.appendChild(belowZone); + } + + belowZone.appendChild(btn); +}; -var initialize = function(zone) { +var createZone = (zone, zoneData) => { var created = document.createElement('div'), initMessage = document.createElement('div'), initContent = zone.dataset.labelInitMessage, - zoneData = { zone: zone, suffix: createFilename() }, dropzoneI; - + created.classList.add('dropzone'); initMessage.classList.add('dz-message'); initMessage.appendChild(document.createTextNode(initContent)); @@ -157,8 +168,6 @@ var initialize = function(zone) { zone.insertBefore(created, zone.firstChild); - insertDownloadButton(zone, zoneData); - let event = new CustomEvent("chill_dropzone_initialized", { detail: { dropzone: dropzoneI, @@ -168,6 +177,21 @@ var initialize = function(zone) { window.dispatchEvent(event); }; + +var initialize = function(zone) { + var + allowRemove = zone.dataset.allowRemove, + zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null } + ; + + if (hasDataInForm(zone, zoneData)) { + insertRemoveButton(zone, zoneData); + insertDownloadButton(zone, zoneData); + } else { + createZone(zone, zoneData); + } +}; + var createFilename = () => { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; @@ -195,6 +219,35 @@ var storeDataInForm = (zone, zoneData) => { insertDownloadButton(zone); }; +const restoreDataInForm = (zone, zoneData) => { + var + inputKey = zone.querySelector('input[data-stored-object-key]'), + inputIv = zone.querySelector('input[data-stored-object-iv]'), + inputObject = zone.querySelector('input[data-async-file-upload]'), + inputType = zone.querySelector('input[data-async-file-type]') + ; + + if (zoneData.old === null) { + console.log('should not have restored data'); + return; + } + + inputKey.value = zoneData.old.key; + inputIv.value = zoneData.old.iv; + inputType.value = zoneData.old.type; + inputObject.value = zoneData.old.obj; + + insertDownloadButton(zone); +}; + +const hasDataInForm = (zone, zoneData) => { + var + inputObject = zone.querySelector('input[data-async-file-upload]') + ; + + return inputObject.value.length > 0; +}; + var removeDataInForm = (zone, zoneData) => { var inputKey = zone.querySelector('input[data-stored-object-key]'), @@ -202,7 +255,15 @@ var removeDataInForm = (zone, zoneData) => { inputObject = zone.querySelector('input[data-async-file-upload]'), inputType = zone.querySelector('input[data-async-file-type]') ; - + + // store data for future usage + zoneData.old = { + key: inputKey.value, + iv: inputIv.value, + obj: inputObject.value, + type: inputType.value + }; + // set blank values inputKey.value = ""; inputIv.value = ""; inputType.value = ""; @@ -211,7 +272,57 @@ var removeDataInForm = (zone, zoneData) => { insertDownloadButton(zone); }; -var insertDownloadButton = (zone) => { +var insertRemoveButton = (zone, zoneData) => { + var + removeButton = document.createElement('a'), + cancelButton = document.createElement('a'), + labelRemove = zone.dataset.dictRemove, + labelCancel = 'Restaurer' + ; + + removeButton.classList.add('sc-button', 'bt-delete'); + removeButton.textContent = labelRemove; + + cancelButton.classList.add('sc-button'); + cancelButton.textContent = labelCancel; + + removeButton.addEventListener('click', (e) => { + e.preventDefault(); + if (zoneData.allowRemove === 'true') { + removeDataInForm(zone, zoneData); + cancelButton.addEventListener('click', (e) => { + e.preventDefault(); + + restoreDataInForm(zone, zoneData); + + cancelButton.remove(); + zone.querySelector('.dropzone').remove(); + + initialize(zone); + }); + } + addBelowButton(cancelButton, zone, zoneData); + //zone.appendChild(cancelButton); + removeButton.remove(); + createZone(zone, zoneData); + }); + + addBelowButton(removeButton, zone, zoneData); + // zone.appendChild(removeButton); +}; + +const removeDownloadButton = (zone, zoneData) => { + var + existingButtons = zone.querySelectorAll('a[data-download-button]') + ; + + // remove existing + existingButtons.forEach(function(b) { + b.remove(); + }); +}; + +var insertDownloadButton = (zone, zoneData) => { var existingButtons = zone.querySelectorAll('a[data-download-button]'), newButton = document.createElement('a'), @@ -247,7 +358,8 @@ var insertDownloadButton = (zone) => { newButton.classList.add('sc-button', 'bt-download', 'dz-bt-below-dropzone'); newButton.textContent = labelQuietButton; - zone.appendChild(newButton); + addBelowButton(newButton, zone, zoneData); + //zone.appendChild(newButton); initializeDownload(zone); }; diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index adf017864..5e46490a6 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -24,3 +24,4 @@ Max files exceeded. Remove previous files: Nombre maximum de fichier atteint. Su Cancel upload: Annuler le téléversement Are you sure you want to cancel this upload ?: Êtes-vous sûrs de vouloir annuler ce téléversement ? Upload canceled: Téléversement annulé +Remove existing file: Supprimer le document existant \ No newline at end of file diff --git a/Resources/views/Form/fields.html.twig b/Resources/views/Form/fields.html.twig index f9253c267..3ab0ff255 100644 --- a/Resources/views/Form/fields.html.twig +++ b/Resources/views/Form/fields.html.twig @@ -11,6 +11,8 @@ data-dict-cancel-upload="{{ 'Cancel upload'|trans|escape('html_attr') }}" data-dict-cancel-upload-confirm="{{ 'Are you sure you want to cancel this upload ?'|trans|escape('html_attr') }}" data-dict-upload-canceled="{{ 'Upload canceled'|trans|escape('html_attr') }}" + data-dict-remove="{{ 'Remove existing file'|trans|escape('html_attr') }}" + data-allow-remove="{% if required %}false{% else %}true{% endif %}" data-temp-url-generator="{{ path('async_upload.generate_url', { 'method': 'GET' })|escape('html_attr') }}"> {{ form_widget(form.filename) }} {{ form_widget(form.keyInfos, { 'attr': { 'data-stored-object-key': 1 } }) }}