From a45aca2a18003cc0dc27687d9c724ce0298de7ae Mon Sep 17 00:00:00 2001 From: Mathieu De Keyzer Date: Thu, 15 Feb 2024 12:00:51 +0100 Subject: [PATCH] refactor(bootstrap5): edit revision (#767) --- .../_to_be_ref/edit-revision.js | 247 ------------------ .../js/editRevisionEventListeners.js | 6 - EMS/admin-ui-bundle/assets/js/core/core.js | 18 ++ .../assets/js/core/events/changeEvent.js | 14 +- .../assets/js/core/events/ctrlSaveEvent.js | 15 ++ .../assets/js/core/plugins/form.js | 30 ++- .../assets/js/edit-revision.js | 179 ++++++++++++- .../MediaLibrary/MediaLibraryService.php | 2 +- 8 files changed, 240 insertions(+), 271 deletions(-) delete mode 100644 EMS/admin-ui-bundle/_to_be_ref/edit-revision.js create mode 100644 EMS/admin-ui-bundle/assets/js/core/events/ctrlSaveEvent.js diff --git a/EMS/admin-ui-bundle/_to_be_ref/edit-revision.js b/EMS/admin-ui-bundle/_to_be_ref/edit-revision.js deleted file mode 100644 index 2f8b7c6fe..000000000 --- a/EMS/admin-ui-bundle/_to_be_ref/edit-revision.js +++ /dev/null @@ -1,247 +0,0 @@ -'use strict'; - -import './js/core'; -import './css/app.scss'; -import './css/app.less'; -import IframePreview from "./js/module/iframePreview"; - - -import EmsListeners from './js/EmsListeners'; -import {editRevisionEventListeners} from "./js/editRevisionEventListeners"; -window.EmsListeners = EmsListeners; - -new IframePreview(); - - -let waitingResponse = false; -let synch = true; - -const primaryBox = $('#revision-primary-box'); -const updateMode = primaryBox.data('update-mode'); - -function onFormChange(event, allowAutoPublish){ - if (updateMode === 'disabled') { - // console.log('No way to save a finalized revision!'); - return; - } - else if (updateMode === 'autoPublish' && !allowAutoPublish) { - // console.log('The auto-save is disabled in auto-publish mode!'); - return; - } - - synch = false; - - updateChoiceFieldTypes(); - updateCollectionLabel(); - - - if(waitingResponse) { - return; - //abort the request might be an option, but it overloads the server - // waitingResponse.abort(); - } - - synch = true; - //update ckeditor's text areas - for (let i in CKEDITOR.instances) { - if(CKEDITOR.instances.hasOwnProperty(i)) { - CKEDITOR.instances[i].updateElement(); - } - } - - - waitingResponse = window.ajaxRequest.post( primaryBox.data('ajax-update'), $("form[name=revision]").serialize()) - .success(function(response) { - $('.has-error').removeClass('has-error'); - $('span.help-block').remove(); - - /** - * @param {{formErrors:array}} response - */ - $(response.formErrors).each(function(index, item){ - - /** - * @param {{propertyPath:string}} item - */ - let target = item.propertyPath; - const targetLabel = $('#' + target + '__label'); - const targetError = $('#' + target + '__error'); - - let propPath = $('#'+item.propertyPath+'_value'); - if(propPath.length && propPath.prop('nodeName') === 'TEXTAREA'){ - target = item.propertyPath+'_value'; - } - - const targetParent = $('#' + target); - if (targetLabel.length) { - targetLabel.closest('div.form-group').addClass('has-error'); - if (item.message && targetError.length > 0) { - targetError.addClass('has-error'); - if($('#'+target+'__error span.help-block').length === 0){ - targetError.append(''); - } - $('#'+target+'__error'+' span.help-block ul.list-unstyled').append('
  • '+item.message+'
  • '); - } - } - else { - $('#' + target).closest('div.form-group').addClass('has-error'); - targetParent.parents('.form-group').addClass('has-error'); - if(item.message) { - if(targetParent.parents('.form-group').find(' span.help-block').length === 0){ - targetParent.parent('.form-group').append(''); - } - else { - targetParent.parents('.form-group').find(' span.help-block ul.list-unstyled').append('
  • '+item.message+'
  • '); - } - } - } - - }); - }) - .always(function() { - waitingResponse = false; - if(!synch){ - onFormChange(); - } - }); -} - -$("form[name=revision]").submit(function( ) { - //disable all pending auto-save - waitingResponse = true; - synch = true; - $('#data-out-of-sync').remove(); -}); - -function updateCollectionLabel() -{ - $('.collection-panel').each(function() { - const collectionPanel = $(this); - const fieldLabel = collectionPanel.data('label-field'); - if (fieldLabel) { - $(this).children(':first').children(':first').children().each(function(){ - let val = $(this).find('input[name*='+fieldLabel+']').val(); - if (typeof val !== 'undefined') { - $(this).find('.collection-label-field').html(' | ' + val) - } - }); - } - }); -} - -function updateChoiceFieldTypes() -{ - $('.ems-choice-field-type').each(function(){ - const choice = $(this); - const collectionName = choice.data('linked-collection'); - if(collectionName) - { - - $('.collection-panel').each(function() - { - const collectionPanel = $(this); - if(collectionPanel.data('name') === collectionName) - { - const collectionLabelField = choice.data('collection-label-field'); - - collectionPanel.children('.panel-body').children('.collection-panel-container').children('.collection-item-panel').each(function(){ - - const collectionItem = $(this); - const index = collectionItem.data('index'); - const id = collectionItem.data('id'); - let label = ' #'+index; - - if(collectionLabelField) - { - label += ': '+$('#'+id+'_'+collectionLabelField).val(); - } - - const multiple = choice.data('multiple'); - const expanded = choice.data('expanded'); - - if(expanded) - { - const option = choice.find('input[value="'+index+'"]'); - if(option.length) - { - const parent = option.closest('.checkbox,.radio'); - if($('#'+id+'__ems_internal_deleted').val() === 'deleted'){ - parent.hide(); - option.addClass('input-to-hide'); - if(multiple) - { - option.attr('checked', false); - } - else - { - option.removeAttr("checked"); - } - } - else{ - option.removeClass('input-to-hide'); - parent.find('.checkbox-radio-label-text').text(label); - parent.show(); - } - } - } - else - { - const option = choice.find('option[value="'+index+'"]'); - if(option.length) - { - if($('#'+id+'__ems_internal_deleted').val() === 'deleted') - { - option.addClass('input-to-hide'); - } - else - { - option.removeClass('input-to-hide'); - option.show(); - option.text(label); - } - - } - } - - }) - } - - }); - - } - - $(this).find('option.input-to-hide').hide(); - $(this).find('.input-to-hide').each(function(){ - $(this).closest('.checkbox,.radio').hide(); - }) - }); -} - -$(window).ready(function() { - updateChoiceFieldTypes(); - updateCollectionLabel(); - editRevisionEventListeners($('form[name=revision]'), onFormChange); -}); - -if (null !== document.querySelector('form[name="revision"]')) { - $(document).keydown(function (e) { - let key = undefined; - /** - * @param {{keyIdentifier:string}} e - */ - const possible = [e.key, e.keyIdentifier, e.keyCode, e.which]; - - while (key === undefined && possible.length > 0) { - key = possible.pop(); - } - - if (typeof key === "number" && (115 === key || 83 === key) && (e.ctrlKey || e.metaKey) && !(e.altKey)) { - e.preventDefault(); - onFormChange(e, true); - return false; - } - return true; - - }); -} - diff --git a/EMS/admin-ui-bundle/_to_be_ref/js/editRevisionEventListeners.js b/EMS/admin-ui-bundle/_to_be_ref/js/editRevisionEventListeners.js index 293ceee56..841c0e5f2 100644 --- a/EMS/admin-ui-bundle/_to_be_ref/js/editRevisionEventListeners.js +++ b/EMS/admin-ui-bundle/_to_be_ref/js/editRevisionEventListeners.js @@ -23,12 +23,6 @@ function editRevisionEventListeners(target, onChangeCallback = null){ } }); - if (onChangeCallback) { - target.find("input").not(".ignore-ems-update,.datetime-picker,datetime-picker").on('input', onChangeCallback); - target.find("select").not(".ignore-ems-update").on('change', onChangeCallback); - target.find("textarea").not(".ignore-ems-update").on('input', onChangeCallback); - } - target.find('.add-content-button').on('click', function(e) { // prevent the link from creating a "#" on the URL e.preventDefault(); diff --git a/EMS/admin-ui-bundle/assets/js/core/core.js b/EMS/admin-ui-bundle/assets/js/core/core.js index ff7831f58..e2829b2b1 100644 --- a/EMS/admin-ui-bundle/assets/js/core/core.js +++ b/EMS/admin-ui-bundle/assets/js/core/core.js @@ -23,6 +23,8 @@ import RevisionTask from './components/revisionTask' import Sidebar from './components/sidebar' import { EMS_ADDED_DOM_EVENT } from './events/addedDomEvent' +import CtrlSaveEvent from './events/ctrlSaveEvent' +import $ from 'jquery' class Core { constructor () { @@ -68,6 +70,7 @@ class Core { document.addEventListener('DOMContentLoaded', this.load(document)) } this.initStatusRefresh() + this.initCtrlSaveEvent() this.components = [ new RevisionTask(), new Sidebar() @@ -103,6 +106,21 @@ class Core { xhr.send() } + + initCtrlSaveEvent () { + $(document).keydown(function (e) { + let key + const possible = [e.key, e.keyIdentifier, e.keyCode, e.which] + + while (key === undefined && possible.length > 0) { + key = possible.pop() + } + if (typeof key === 'number' && (key === 115 || key === 83) && (e.ctrlKey || e.metaKey) && !(e.altKey)) { + const event = new CtrlSaveEvent(e) + event.dispatch() + } + }) + } } const core = new Core() diff --git a/EMS/admin-ui-bundle/assets/js/core/events/changeEvent.js b/EMS/admin-ui-bundle/assets/js/core/events/changeEvent.js index bdcfbb13f..65d566740 100644 --- a/EMS/admin-ui-bundle/assets/js/core/events/changeEvent.js +++ b/EMS/admin-ui-bundle/assets/js/core/events/changeEvent.js @@ -1,12 +1,22 @@ export const EMS_CHANGE_EVENT = 'emsChangeEvent' export class ChangeEvent { constructor (input) { - this._event = new CustomEvent(EMS_CHANGE_EVENT, { input }) + this._form = input.closest('form') this._input = input + + this._event = new CustomEvent(EMS_CHANGE_EVENT, { + detail: { + form: this._form, + input + } + }) } dispatch () { - document.dispatchEvent(this._event) + if (undefined === this._form) { + return + } + this._form.dispatchEvent(this._event) } } export default ChangeEvent diff --git a/EMS/admin-ui-bundle/assets/js/core/events/ctrlSaveEvent.js b/EMS/admin-ui-bundle/assets/js/core/events/ctrlSaveEvent.js new file mode 100644 index 000000000..e5d07f7af --- /dev/null +++ b/EMS/admin-ui-bundle/assets/js/core/events/ctrlSaveEvent.js @@ -0,0 +1,15 @@ +export const EMS_CTRL_SAVE_EVENT = 'emsCtrlSaveEvent' +export class CtrlSaveEvent { + constructor (event) { + this._event = new CustomEvent(EMS_CTRL_SAVE_EVENT, { + detail: { + parentEvent: event + } + }) + } + + dispatch () { + document.dispatchEvent(this._event) + } +} +export default CtrlSaveEvent diff --git a/EMS/admin-ui-bundle/assets/js/core/plugins/form.js b/EMS/admin-ui-bundle/assets/js/core/plugins/form.js index 167bc7bdf..fd7362861 100644 --- a/EMS/admin-ui-bundle/assets/js/core/plugins/form.js +++ b/EMS/admin-ui-bundle/assets/js/core/plugins/form.js @@ -1,9 +1,12 @@ import $ from 'jquery' import ajaxRequest from '../components/ajaxRequest' +import ChangeEvent from '../events/changeEvent' +import { EMS_CTRL_SAVE_EVENT } from '../events/ctrlSaveEvent' class Form { load (target) { this.initAjaxSave(target) + this.initFormChangeEvent(target) } initAjaxSave (target) { @@ -31,22 +34,21 @@ class Form { } button.on('click', ajaxSave) + document.addEventListener(EMS_CTRL_SAVE_EVENT, (event) => ajaxSave(event.detail.parentEvent)) + }) + } - $(document).keydown(function (e) { - let key - const possible = [e.key, e.keyIdentifier, e.keyCode, e.which] - - while (key === undefined && possible.length > 0) { - key = possible.pop() - } - - if (typeof key === 'number' && (key === 115 || key === 83) && (e.ctrlKey || e.metaKey) && !(e.altKey)) { - ajaxSave(e) - return false - } - return true + initFormChangeEvent (target) { + const inputs = document.querySelectorAll('input,textarea,select') + for (let i = 0; i < inputs.length; ++i) { + if (inputs[i].classList.contains('ignore-ems-update') || inputs[i].classList.contains('datetime-picker')) { + continue + } + inputs[i].addEventListener('change', function () { + const event = new ChangeEvent(inputs[i]) + event.dispatch() }) - }) + } } } diff --git a/EMS/admin-ui-bundle/assets/js/edit-revision.js b/EMS/admin-ui-bundle/assets/js/edit-revision.js index 02e04858c..431a8a3ed 100644 --- a/EMS/admin-ui-bundle/assets/js/edit-revision.js +++ b/EMS/admin-ui-bundle/assets/js/edit-revision.js @@ -1,3 +1,180 @@ 'use strict' +import $ from 'jquery' +import ajaxRequest from './core/components/ajaxRequest' -console.log('edit revision') +import { EMS_CHANGE_EVENT } from './core/events/changeEvent' +import { EMS_CTRL_SAVE_EVENT } from './core/events/ctrlSaveEvent' + +let waitingResponse = false +let synch = true + +const primaryBox = $('#revision-primary-box') +const updateMode = primaryBox.data('update-mode') + +function updateCollectionLabel () { + $('.collection-panel').each(function () { + const collectionPanel = $(this) + const fieldLabel = collectionPanel.data('label-field') + if (fieldLabel) { + $(this).children(':first').children(':first').children().each(function () { + const val = $(this).find('input[name*=' + fieldLabel + ']').val() + if (typeof val !== 'undefined') { + $(this).find('.collection-label-field').html(' | ' + val) + } + }) + } + }) +} + +function updateChoiceFieldTypes () { + $('.ems-choice-field-type').each(function () { + const choice = $(this) + const collectionName = choice.data('linked-collection') + if (collectionName) { + $('.collection-panel').each(function () { + const collectionPanel = $(this) + if (collectionPanel.data('name') === collectionName) { + const collectionLabelField = choice.data('collection-label-field') + + collectionPanel.children('.panel-body').children('.collection-panel-container').children('.collection-item-panel').each(function () { + const collectionItem = $(this) + const index = collectionItem.data('index') + const id = collectionItem.data('id') + let label = ' #' + index + + if (collectionLabelField) { + label += ': ' + $('#' + id + '_' + collectionLabelField).val() + } + + const multiple = choice.data('multiple') + const expanded = choice.data('expanded') + + if (expanded) { + const option = choice.find('input[value="' + index + '"]') + if (option.length) { + const parent = option.closest('.checkbox,.radio') + if ($('#' + id + '__ems_internal_deleted').val() === 'deleted') { + parent.hide() + option.addClass('input-to-hide') + if (multiple) { + option.attr('checked', false) + } else { + option.removeAttr('checked') + } + } else { + option.removeClass('input-to-hide') + parent.find('.checkbox-radio-label-text').text(label) + parent.show() + } + } + } else { + const option = choice.find('option[value="' + index + '"]') + if (option.length) { + if ($('#' + id + '__ems_internal_deleted').val() === 'deleted') { + option.addClass('input-to-hide') + } else { + option.removeClass('input-to-hide') + option.show() + option.text(label) + } + } + } + }) + } + }) + } + + $(this).find('option.input-to-hide').hide() + $(this).find('.input-to-hide').each(function () { + $(this).closest('.checkbox,.radio').hide() + }) + }) +} + +function onChange (allowAutoPublish = false) { + if (updateMode === 'disabled') { + // console.log('No way to save a finalized revision!'); + return + } else if (updateMode === 'autoPublish' && !allowAutoPublish) { + // console.log('The auto-save is disabled in auto-publish mode!'); + return + } + + synch = false + + updateChoiceFieldTypes() + updateCollectionLabel() + + if (waitingResponse) { + return + // abort the request might be an option, but it overloads the server + // waitingResponse.abort(); + } + + synch = true + // update ckeditor's text areas + /* for (let i in CKEDITOR.instances) { + if(CKEDITOR.instances.hasOwnProperty(i)) { + CKEDITOR.instances[i].updateElement(); + } + } */ + + waitingResponse = ajaxRequest.post(primaryBox.data('ajax-update'), $('form[name=revision]').serialize()) + .success(function (response) { + $('.is-invalid').removeClass('is-invalid') + $('span.help-block').remove() + $(response.formErrors).each(function (index, item) { + let target = item.propertyPath + const targetLabel = $('#' + target + '__label') + const targetError = $('#' + target + '__error') + + const propPath = $('#' + item.propertyPath + '_value') + if (propPath.length && propPath.prop('nodeName') === 'TEXTAREA') { + target = item.propertyPath + '_value' + } + + const targetParent = $('#' + target) + if (targetLabel.length) { + targetLabel.closest('div.form-group').addClass('has-error') + if (item.message && targetError.length > 0) { + targetError.addClass('has-error') + if ($('#' + target + '__error span.help-block').length === 0) { + targetError.append('') + } + $('#' + target + '__error' + ' span.help-block ul.list-unstyled').append('
  • ' + item.message + '
  • ') + } + } else { + $('#' + target).closest('div.form-group').addClass('has-error') + targetParent.parents('.form-group').addClass('has-error') + if (item.message) { + if (targetParent.parents('.form-group').find(' span.help-block').length === 0) { + targetParent.parent('.form-group').append('') + } else { + targetParent.parents('.form-group').find(' span.help-block ul.list-unstyled').append('
  • ' + item.message + '
  • ') + } + } + } + }) + }) + .always(function () { + waitingResponse = false + if (!synch) { + onChange(allowAutoPublish) + } + }) +} + +$('form[name=revision]').submit(function () { + // disable all pending auto-save + waitingResponse = true + synch = true + $('#data-out-of-sync').remove() +}) + +window.onload = function () { + updateChoiceFieldTypes() + updateCollectionLabel() + const form = document.querySelector('form[name=revision]') + form.addEventListener(EMS_CHANGE_EVENT, () => onChange()) + document.addEventListener(EMS_CTRL_SAVE_EVENT, (event) => { event.detail.parentEvent.preventDefault(); onChange(true) }) +} diff --git a/EMS/core-bundle/src/Core/Component/MediaLibrary/MediaLibraryService.php b/EMS/core-bundle/src/Core/Component/MediaLibrary/MediaLibraryService.php index 39e2a34c4..efd263620 100644 --- a/EMS/core-bundle/src/Core/Component/MediaLibrary/MediaLibraryService.php +++ b/EMS/core-bundle/src/Core/Component/MediaLibrary/MediaLibraryService.php @@ -198,7 +198,7 @@ public function jobFolderRename(MediaLibraryConfig $config, UserInterface $user, return $this->jobService->createCommand($user, $command); } - public function renderHeader(MediaLibraryConfig $config, MediaLibraryFolder|string|null $folder = null, MediaLibraryFile|string|null $file = null, int $selectionFiles = 0): string + public function renderHeader(MediaLibraryConfig $config, MediaLibraryFolder|string $folder = null, MediaLibraryFile|string $file = null, int $selectionFiles = 0): string { $mediaFolder = \is_string($folder) ? $this->getFolder($config, $folder) : $folder; $mediaFile = \is_string($file) ? $this->getFile($config, $file) : $file;