Commit 3639d6a0 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Merge branch '6.0.x'

parents d45e23a1 88a27abe
Loading
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -3,19 +3,30 @@ CHANGELOG

6.1.3
-----

- Retrait de la dépendance à laminas-dependency-plugin

6.1.1
-----

- PHP8.0 réautorisé

6.1.0
-----

- Compatibilité PHP8.2

6.0.15
------
- Doctrine ORM : support de la fonction coalesce(), y compris dans un order by.
- AjaxModalListener (unicaen.js) : une erreur explicite est émise si le formulaire n'a pas l'attribut requis 'action'.
- AjaxModalListener (unicaen.js) : ajout de la classe CSS 'loading' sur le 'body' lors du clic sur le lien puis retrait lorsque la modale est ouverte.
- AjaxModalListener (unicaen.js) : déclenchement d'un événement lors du clic sur le lien et d'un autre lorsque la modale est ouverte
  (noms à transmettre via les attributs 'data-event-modal-loading' et 'data-event-modal-opened' du lien).
- [FIX] FormElementCollection : corrections complémentaires pour que SearchAndSelect fonctionne.

6.0.14
------
- [FIX] FormElementCollection : forçage d'id pour éviter les doubons provoqué par le clonage (SearchAndSelect ne le supporte pas) !
- [FIX] FormElementCollection : correction d'un auto-sabotage !

6.0.13
------

+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ use UnicaenApp\Message\View\Helper\MessageHelperFactory;
use UnicaenApp\Mvc\RedirectResponse;
use UnicaenApp\Mvc\RedirectResponseFactory;
use UnicaenApp\ORM\Query\Functions\Chr;
use UnicaenApp\ORM\Query\Functions\Coalesce;
use UnicaenApp\ORM\Query\Functions\CompriseEntre;
use UnicaenApp\ORM\Query\Functions\PasHistorise;
use UnicaenApp\ORM\Query\Functions\Replace;
@@ -467,6 +468,7 @@ return [
                    'pasHistorise' => PasHistorise::class,
                    'replace' => Replace::class,
                    'chr' => Chr::class,
                    'coalesce' => Coalesce::class,
                ],
            ],
        ],
+24 −0
Original line number Diff line number Diff line
@@ -493,16 +493,37 @@ function AjaxModalListener(dialogDivId)
        var modalDialog = this.getModalDialog();

        if (url && url !== "#") {
            $("body").addClass('loading');

            // transmet à la DIV le lien cliqué (car fournit l'événement à déclencher à la soumission du formulaire)
            modalDialog.data('a', anchor);
            this.modalEventName = anchor.data('event');

            // déclenchement d'un événement dont le nom est fourni par le lien cliqué (via un attribut "data-xxxx")
            var eventName = anchor.data('event-modal-loading');
            if (eventName) {
                var event = jQuery.Event(eventName, {div: modalDialog, a: modalDialog.data('a')});
                // console.log("Triggering '" + event.type + "' event...");
                // console.log("Event object : ", event);
                this.eventListener.trigger(event);
            }

            // requête AJAX pour obtenir le nouveau contenu de la fenêtre modale
            $.get(url, {modal: 1}, $.proxy(function (data)
            {
                // remplacement du contenu de la fenêtre modale
                $(".modal-content", modalDialog.modal('show')).html(this.extractNewModalContent(data));

                // déclenchement d'un événement dont le nom est fourni par le lien cliqué (via un attribut "data-xxxx")
                var eventName = anchor.data('event-modal-opened');
                if (eventName) {
                    var event = jQuery.Event(eventName, {div: modalDialog, a: modalDialog.data('a')});
                    console.log("Triggering '" + event.type + "' event...");
                    console.log("Event object : ", event);
                    this.eventListener.trigger(event);
                }

                $("body").removeClass('loading');
            }, this));
        }

@@ -569,6 +590,9 @@ function AjaxModalListener(dialogDivId)
        postData.append("modal", "1");

        var url = form.attr('action');
        if (!url) {
            throw new Error("Le formulaire dans la modale doit posséder un attribut 'action'");
        }
        var isRedirect = url.indexOf("redirect=") > -1 || $("input[name=redirect]").val();

        // requête AJAX de soumission du formulaire
+40 −48
Original line number Diff line number Diff line
<?php
namespace UnicaenApp\Form\View\Helper;

use UnicaenApp\Form\Element\Collection;
use Laminas\Form\View\Helper\FormCollection;
use Laminas\Form\ElementInterface;
use Laminas\Form\Element\Collection as CollectionElement;
use Laminas\Form\ElementInterface;
use Laminas\Form\FieldsetInterface;
use Laminas\Form\LabelAwareInterface;
use Laminas\Form\Element\Button;
use Laminas\Form\View\Helper\FormCollection;
use UnicaenApp\Form\Element\Collection;

/**
 * Aide de vue dessinant une collection d'éléments.
@@ -37,18 +36,13 @@ class FormElementCollection extends FormCollection
    protected $defaultElementHelper = 'formControlGroup';

    /**
     * Nombre d'éléments à afficher
     *
     * @var int
     * Nombre d'éléments à afficher.
     */
    protected $countElements;
    protected int $countElements;


    /**
     * Render a collection by iterating through all fieldsets and elements
     *
     * @param  ElementInterface $element
     * @return string
     * Render a collection by iterating through all fieldsets and elements.
     */
    public function render(ElementInterface $element): string
    {
@@ -61,9 +55,13 @@ class FormElementCollection extends FormCollection
        $index = 0;
        $markup = '';
        $linkMarkup = '';
        $javascript = null;
        $css = null;
        $templateMarkup = '';
        $elementHelper = $this->getElementHelper();
        assert(is_callable($elementHelper));
        $fieldsetHelper = $this->getFieldsetHelper();
        assert(is_callable($fieldsetHelper));

        if ($element instanceof CollectionElement && $element->shouldCreateTemplate()) {
            $this->countElements = $element->getCount();
@@ -80,9 +78,19 @@ class FormElementCollection extends FormCollection
        foreach ($element->getIterator() as $elementOrFieldset) {
            if ($elementOrFieldset instanceof FieldsetInterface) {
                $elementOrFieldset->setAttribute('data-index', $index);
                $markup .= $fieldsetHelper($elementOrFieldset, $this->shouldWrap());
                $fieldsetHtml = $fieldsetHelper($elementOrFieldset, $this->shouldWrap());
                $fieldsetHtml = str_replace('__index__', $index, $fieldsetHtml);
                $index++;
                $markup .= $fieldsetHtml;
            } elseif ($elementOrFieldset instanceof ElementInterface) {
                /**
                 * NB : la valeur d'un id doit être unique dans un DOM. Or, en cas de collection autorisant l'ajout d'élément
                 * (par clonage), on garantit l'unicité en concaténant le pattern '__index__', pattern qui est remplacé/incrémenté
                 * par le moteur js de cette aide de vue (cf. plus bas).
                 * Par ex, {@see \UnicaenApp\Form\Element\SearchAndSelect} ne fonctionne plus dans une collection en cas de doublon d'id.
                 */
                $id = $elementOrFieldset->getAttribute('id') ?: uniqid('element-');
                $elementOrFieldset->setAttribute('id', $id . '-__index__');
                $markup .= $elementHelper($elementOrFieldset);
            }
        }
@@ -122,8 +130,8 @@ class FormElementCollection extends FormCollection
                $templateMarkup,
                $attributesString,
                $linkMarkup,
                $css = null,
                $javascript = null,
                $css,
                $javascript,
            );
        } else {
            $markup .= $templateMarkup . $linkMarkup;
@@ -132,7 +140,7 @@ class FormElementCollection extends FormCollection
        return $markup;
    }

    protected function getDelLink()
    protected function getDelLink(): string
    {
        return '<div class="collection-delete-link">' .
                    '<a class="btn btn-link" title="Supprimer cet élément de la collection">' .
@@ -141,12 +149,7 @@ class FormElementCollection extends FormCollection
                '</div>';
    }

    /**
     * Get add link
     *
     * @return string
     */
    protected function getAddLink()
    protected function getAddLink(): string
    {
        return '<div class="collection-add-link">' .
                    '<a class="btn btn-link" title="Ajouter un élément à la collection">' .
@@ -155,11 +158,7 @@ class FormElementCollection extends FormCollection
                '</div>';
    }

    /**
     * @param Collection $container
     * @return string
     */
    protected function getCss($container)
    protected function getCss(Collection $container): string
    {
        $fieldsetId = $container->getAttribute('id');

@@ -192,11 +191,7 @@ EOT;
        return $css;
    }

    /**
     * @param Collection $container
     * @return string
     */
    protected function getJavascript($container)
    protected function getJavascript(Collection $container): string
    {
        $maxEnabled = ($container->getMaxElements()) ? 'true' : 'false';
        $minEnabled = ($container->getMinElements()) ? 'true' : 'false';
@@ -281,24 +276,21 @@ $().ready(function updateButtons()
        });
    }
});


EOT;

        foreach($container->getAutocomplete() as $name => $options) {
            $javascript .= <<<EOT
$('#{$container->getAttribute('id')}').on('keyup', '.{$container->getAttribute('id')}-autocomplete', function() {
    $(this).autocompleteOctopus({
        source: '{$options['source']}',
        minLength: {$options['min-length']},
        delay: {$options['delay']}
    });
});


EOT;

        }
//        foreach($container->getAutocomplete() as $name => $options) {
//            $javascript .= <<<EOT
//$('#{$container->getAttribute('id')}').on('keyup', '.{$container->getAttribute('id')}-autocomplete', function() {
//    $(this).autocompleteOctopus({
//        source: '{$options['source']}',
//        minLength: {$options['min-length']},
//        delay: {$options['delay']}
//    });
//});
//
//
//EOT;
//        }

        return '<script type="text/javascript">' . $javascript . '</script>';
    }
+28 −94
Original line number Diff line number Diff line
<?php
namespace UnicaenApp\Form\View\Helper;

use UnicaenApp\Exception\LogicException;
use UnicaenApp\Form\Element\SearchAndSelect;
use Laminas\Form\Element\Text;
use Laminas\Form\ElementInterface;
use Laminas\Form\Exception\InvalidElementException;
use Laminas\Form\View\Helper\FormText;
use UnicaenApp\Form\Element\SearchAndSelect;

/**
 * Aide de vue générant le code HTML de l'élément de formulaire du même nom.
 *
 * @author <bertrand.gauthier@unicaen.fr>
 * @see \UnicaenApp\Form\Element\SearchAndSelect
 * Aide de vue générant le code HTML de l'élément de formulaire {@see \UnicaenApp\Form\Element\SearchAndSelect}.
 */
class FormSearchAndSelect extends FormText
{
    /**
     * @var SearchAndSelect
     */
    protected $element;
    
    /**
     * @var string
     */
    protected $autocompleteSource;
    
    /**
     * @var int
     */
    protected $autocompleteMinLength = 2;
    
    /**
     * @var string
     */
    protected $spinnerSource = "//gest.unicaen.fr/images/ajax-loader-r.gif";
    protected SearchAndSelect $element;
    protected Text $autocompleteElement;
    protected ?string $autocompleteSource = null;
    protected int $autocompleteMinLength = 2;
    protected string $spinnerSource = "//gest.unicaen.fr/images/ajax-loader-r.gif";
    
    /**
     * {@inheritdoc}
     */
    public function __invoke(ElementInterface $element = null)
    {
        if ($element && !$element instanceof SearchAndSelect) {
            throw new InvalidElementException("L'élément spécifié n'est pas du type attendu.");
        if (! $element) {
            return $this;
        }

        $this->element = $element;
        return parent::__invoke($element);
        return $this->render($element);
    }

    /**
     * {@inheritdoc}
     */
    public function render(ElementInterface $element): string
    {
        if (!$element instanceof SearchAndSelect) {
@@ -74,20 +49,22 @@ class FormSearchAndSelect extends FormText
        //   'label' => libellé affiché (ex: nom complet de la personne)
        $this->element->setName($name . "[$id]");

        // render parent
        $markup = parent::render($this->element);
        
        $elementDomId      = $this->element->getAttribute('id');
        $autocompleteDomId = $elementDomId . '-autocomplete';

        $autocomplete = new Text();
        $autocomplete->setAttributes($this->element->getAttributes())
        $this->autocompleteElement = new Text();
        $this->autocompleteElement
            ->setAttributes($this->element->getAttributes())
            ->setName($name . "[$label]")
            ->setAttribute('id', $autocompleteDomId)
            ->setAttribute('class', 'form-control form-control-sm')
            ->setValue($this->element->getValueLabel() ?: $this->element->getValue());

        $markup .= $this->getView()->formText($autocomplete);
        $this->element->setAttribute('style', $this->element->getAttribute('style') . ' display:none;');

        $markup = parent::render($this->element);

        $markup .= $this->view->formText($this->autocompleteElement);
        
        $markup .= <<<EOS
<style>
@@ -100,34 +77,21 @@ class FormSearchAndSelect extends FormText
</style>
EOS;

//        $this->getView()->plugin('inlineScript')->appendScript($this->getJavascript());
        $markup .= PHP_EOL . '<script>' . $this->getJavascript() . '</script>' . PHP_EOL;
        
        return $markup;
    }

    /**
     * 
     * @return string
     * @throws LogicException
     */
    public function getJavascript()
    protected function getJavascript(): string
    {
        if (!$this->element) {
            throw new LogicException("Aucun élément spécifié, appelez render() auparavant.");
        }
        
        $elementDomId      = $this->element->getAttribute('id');
        $autocompleteDomId = $elementDomId . '-autocomplete';
        
        $js = <<<EOT
// cache l'élément contenant l'id de la sélection
$("#{$elementDomId}").css('display', 'none');
        $autocompleteDomId = $this->autocompleteElement->getAttribute('id');
        
        return <<<EOT
$(function() {
    // jQuery UI autocomplete
    var autocomp = $("#{$autocompleteDomId}");
    autocomp.autocompleteUnicaen({ // autocompleteUnicaen() définie dans "public/js/util.js"
    autocomp.autocompleteUnicaen({ // autocompleteUnicaen() définie dans "public/unicaen/app/js/unicaen.js"
        elementDomId: '$elementDomId',
        source: '{$this->getAutocompleteSource()}',
        minLength: {$this->getAutocompleteMinLength()},
@@ -135,27 +99,15 @@ $(function() {
    });
});
EOT;
        return $js;
    }
    
    /**
     * 
     * 
     * @param string|array $autocompleteSource
     * @return SearchAndSelect
     */
    public function setAutocompleteSource($autocompleteSource)
    public function setAutocompleteSource(string $autocompleteSource): self
    {
        $this->autocompleteSource = $autocompleteSource;
        return $this;
    }

    /**
     * 
     * 
     * @return string|array $autocompleteSource
     */
    public function getAutocompleteSource()
    public function getAutocompleteSource(): string
    {
        if (null !== $this->autocompleteSource) {
            return $this->autocompleteSource;
@@ -163,41 +115,23 @@ EOT;
        return $this->element->getAutocompleteSource();
    }

    /**
     * 
     * @return int
     */
    public function getAutocompleteMinLength()
    public function getAutocompleteMinLength(): int
    {
        return $this->autocompleteMinLength;
    }

    /**
     * 
     * @param int $autocompleteMinLength
     * @return SearchAndSelect
     */
    public function setAutocompleteMinLength($autocompleteMinLength)
    public function setAutocompleteMinLength(int $autocompleteMinLength): self
    {
        $this->autocompleteMinLength = $autocompleteMinLength;
        return $this;
    }

    /**
     * 
     * @return string
     */
    public function getSpinnerSource()
    public function getSpinnerSource(): string
    {
        return $this->spinnerSource;
    }

    /**
     * 
     * @param string $spinnerSource
     * @return SearchAndSelect
     */
    public function setSpinnerSource($spinnerSource)
    public function setSpinnerSource(string $spinnerSource): self
    {
        $this->spinnerSource = $spinnerSource;
        return $this;
Loading