diff --git a/FEATURES/104332 b/FEATURES/104332 index c515f4dffe7c7f0a5d01c740ab37af248084c993..5b329a5eb2b027425da0a8c00574440c24815950 100644 --- a/FEATURES/104332 +++ b/FEATURES/104332 @@ -1,10 +1,10 @@ '104332' => - ['Label' => $this->_('[commande] St Cloud : ajout zone tableau PNB'), - 'Desc' => '', + ['Label' => $this->_('Tableau des prêts PNB : ajout des genres et sections, sélection multiple'), + 'Desc' => $this->_('Tableau des prêts PNB : possibilité d\'afficher les genres et sections. Sélection multiple possible d\'album'), 'Image' => '', 'Video' => '', 'Category' => '', 'Right' => function($feature_description, $user) {return true;}, - 'Wiki' => '', + 'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Configuration_du_PNB_Dilicom#Tableau_de_bord', 'Test' => '', 'Date' => '2021-01-14'], \ No newline at end of file diff --git a/VERSIONS_WIP/104332 b/VERSIONS_WIP/104332 index f80b4b1e11acccd5b2cf2b0c5fbf1b4d0ec67093..c285b1a43d58b5c56fdd656f448160ac64994407 100644 --- a/VERSIONS_WIP/104332 +++ b/VERSIONS_WIP/104332 @@ -1 +1 @@ - - ticket #104332 : PNB administration : ajout zone genre et sections dans le tableau \ No newline at end of file + - ticket #104332 : Tableau des prêts PNB : ajout des genres et sections, sélection multiple \ No newline at end of file diff --git a/library/Class/Album/Item.php b/library/Class/Album/Item.php index a8df06267be322a0f1d28d303e3903ddea480224..405267127439d458778814eab0235d3af2da3dd9 100644 --- a/library/Class/Album/Item.php +++ b/library/Class/Album/Item.php @@ -127,4 +127,18 @@ class Class_Album_Item extends Storm_Model_Abstract { $this->getUsageConstraints()->setLoanQuantity($quantity); return $this; } + + + public function getAlbumSectionIds() { + if ($album = $this->getAlbum()) + return explode(';',$album->getSections()); + return []; + } + + + public function getAlbumGenreIds() { + if ($album = $this->getAlbum()) + return explode(';',$album->getGenre()); + return []; + } } diff --git a/library/Class/MultiSelection/Abstract.php b/library/Class/MultiSelection/Abstract.php index 6902ec1359d5d37bd34c06c35a0129a8baaa6b34..120705ace2b72809ec63409afbce94c437f67fdb 100644 --- a/library/Class/MultiSelection/Abstract.php +++ b/library/Class/MultiSelection/Abstract.php @@ -59,10 +59,7 @@ abstract class Class_MultiSelection_Abstract { public function isFullWith($values, $limit) { if(count($values) > $limit) return true; - - $temp_storage = clone($this->_getStorage()); - $temp_storage->addValues($values); - return count($temp_storage->getValues()) > $limit; + return (count($values) + $this->countAlreadySelected()) > $limit; } @@ -78,6 +75,11 @@ abstract class Class_MultiSelection_Abstract { } + public function countAlreadySelected() { + return $this->_getStorage()->count(); + } + + public function contains($id) { return $this->_getStorage()->contains($id); } diff --git a/library/Class/MultiSelection/AlbumItem.php b/library/Class/MultiSelection/AlbumItem.php index 940a1e497d1342a6e1b297bf4cff2ec4501e1d35..a9fc7f38705d10c329ac40c8f45068cfb3c0bf25 100644 --- a/library/Class/MultiSelection/AlbumItem.php +++ b/library/Class/MultiSelection/AlbumItem.php @@ -21,25 +21,26 @@ class Class_MultiSelection_AlbumItem extends Class_MultiSelection_Album { - public function getModelIdsFromCategory($id) { - if(!$id) - return []; - - return (new Storm_Model_Collection(Class_Album_Item::getItemsOf($id))) - ->collect('id') - ->getArrayCopy(); - } + public function acceptActionsVisitor($visitor) { + $visitor + ->visitControllerName('album') - public function getModels() { - if(!$values = $this->getValues()) - return new Storm_Model_Collection([]); + ->visitLeafCondition(function($model) + { + return $this->isSelected($model->getAlbum()->getId()); + }) - return new Storm_Model_Collection(Class_Album_Item::findAll()); - } + ->visitAddLeaf(function($model) + { + return $this->_('Ajouter %s à la sélection', $model->getTitre()); + }) + ->visitRemoveLeaf(function($model) + { + return $this->_('Supprimer %s de la sélection', $model->getTitre()); + }); - protected function _getStorage() { - return new Class_MultiSelection_SessionStorage('albumitem'); + return $this; } } \ No newline at end of file diff --git a/library/Class/MultiSelection/SessionStorage.php b/library/Class/MultiSelection/SessionStorage.php index ae982ba321698722041b1c1134ae967476d503b8..3738bfceee05ae80de8b36563b060ef56b514341 100644 --- a/library/Class/MultiSelection/SessionStorage.php +++ b/library/Class/MultiSelection/SessionStorage.php @@ -46,6 +46,11 @@ class Class_MultiSelection_SessionStorage { } + public function count() { + return count($this->getValues()); + } + + public function addValues($values) { $call_back = function ($selected_models, $values) { $selected_models = array_merge($selected_models, $values); diff --git a/library/Class/TableDescription/PNBItems.php b/library/Class/TableDescription/PNBItems.php index 97b134fabb743d190f53fd3996c85c141fd57fe2..c630484111aa1ad0f16b4171930748793baf2481 100644 --- a/library/Class/TableDescription/PNBItems.php +++ b/library/Class/TableDescription/PNBItems.php @@ -47,7 +47,11 @@ class Class_TableDescription_PNBItems extends Class_TableDescription { 'anchorOptions' => ['data-popup' => 'true'], 'label' => $this->_('Voir l\'album'), 'icon' => 'view']); + return $this->_addMultiSelectionAction(); + } + + protected function _addMultiSelectionAction() { $multi_selection = new Class_MultiSelection_AlbumItem(); foreach( (new ZendAfi_Controller_Plugin_MultiSelection_LeafActions($multi_selection)) @@ -55,5 +59,6 @@ class Class_TableDescription_PNBItems extends Class_TableDescription { $action['id'] = function($model) { return $model->getAlbumId(); }; $this->addRowAction($action); } + return $this; } } diff --git a/library/Class/TableDescription/PNBItemsRenderer.php b/library/Class/TableDescription/PNBItemsRenderer.php index 3887e7a229ac2ae11a7d066f5cb8c5e76fd7209b..d66e6c085415d71e46ca2985379b6e9006186925 100644 --- a/library/Class/TableDescription/PNBItemsRenderer.php +++ b/library/Class/TableDescription/PNBItemsRenderer.php @@ -53,15 +53,15 @@ class Class_TableDescription_PNBItemsRenderer { public function getSection() { - if ($sections = $this->_item->getAlbum()->getSections()) - return implode(', ',Class_CodifSection::labelsOfIds(explode(';',$sections))->getArrayCopy()); + if ($sections = $this->_item->getAlbumSectionIds()) + return implode(', ',Class_CodifSection::labelsOfIds($sections)->getArrayCopy()); return ''; } public function getGenre() { - if ($genre = $this->_item->getAlbum()->getGenre()) - return implode(', ',Class_CodifGenre::labelsOfIds(explode(';',$genre))->getArrayCopy()); + if ($genre = $this->_item->getAlbumGenreIds()) + return implode(', ',Class_CodifGenre::labelsOfIds($genre)->getArrayCopy()); return ''; } diff --git a/library/ZendAfi/Controller/Plugin/MultiSelection/Abstract.php b/library/ZendAfi/Controller/Plugin/MultiSelection/Abstract.php index 9662b83c8a2cbe045ea6dff4988491f52d626900..9a57a23ad1e639b85f367a67064b095bd7e96f5e 100644 --- a/library/ZendAfi/Controller/Plugin/MultiSelection/Abstract.php +++ b/library/ZendAfi/Controller/Plugin/MultiSelection/Abstract.php @@ -40,6 +40,30 @@ abstract class ZendAfi_Controller_Plugin_MultiSelection_Abstract extends ZendAfi } + public function addModelToSelectionAjaxAction() { + $values = $this->_getValuesFromParams(); + $limit = (int) Class_AdminVar::getValueOrDefault('LIMIT_MULTIPLE_SELECTION'); + + if($this->_getMultiSelection()->isFullWith($values, $limit)) { + return $this->_helper->json(['selection' => $this->renderWidget(), + 'response' => $this->_('Il n\'est pas possible de sélectionner plus de %d éléments', + $limit)]); + } + + $this->_multi_selection = $this->_getMultiSelection()->addValues($values); + return $this->_helper->json(['selection' => $this->renderWidget(), + 'response' => 'ok']); + } + + + public function removeModelFromSelectionAjaxAction() { + $this->_getMultiSelection()->removeValues($this->_getValuesFromParams()); + + return $this->_helper->json(['selection' => $this->renderWidget(), + 'response' => 'ok']); + } + + public function removeModelFromSelectionAction() { $this->_getMultiSelection()->removeValues($this->_getValuesFromParams()); return $this->_redirectToReferer(); @@ -163,12 +187,17 @@ abstract class ZendAfi_Controller_Plugin_MultiSelection_Abstract extends ZendAfi if (('index' == $this->_request->getActionName()) || ('edit-multiple' == $this->_request->getActionName()) || ('dilicom' == $this->_request->getActionName())) - return $this->_view->Plugin_MultiSelection_Widget($this->_multi_selection); + return $this->renderWidget(); return ''; } + public function renderWidget() { + return $this->_view->Plugin_MultiSelection_Widget($this->_getMultiSelection()); + } + + public function visitGetForm($callback) { $this->_get_form = $callback; return $this; diff --git a/library/ZendAfi/Controller/Plugin/MultiSelection/LeafActions.php b/library/ZendAfi/Controller/Plugin/MultiSelection/LeafActions.php index 77cebd917db043f7430aaa7d61ebe4013f4ea762..f39a5f6c77855a4553ab00a93d9be2170660f8ef 100644 --- a/library/ZendAfi/Controller/Plugin/MultiSelection/LeafActions.php +++ b/library/ZendAfi/Controller/Plugin/MultiSelection/LeafActions.php @@ -26,6 +26,8 @@ class ZendAfi_Controller_Plugin_MultiSelection_LeafActions extends ZendAfi_Contr ['url' => ['controller' => $this->_controller_name, 'action' => 'remove-model-from-selection', 'select_id' => '%s'], + 'anchorOptions' => ['onclick' => 'addMultiSelection(\'selected-%s\');return false;', + 'class' => 'selected-%s'], 'icon' => 'cancel', 'label' => $this->_remove_leaf, 'condition' => $this->_leaf_condition], @@ -35,6 +37,10 @@ class ZendAfi_Controller_Plugin_MultiSelection_LeafActions extends ZendAfi_Contr 'select_id' => '%s'], 'icon' => 'basket', 'label' => $this->_add_leaf, + + 'anchorOptions' => ['onclick' => 'addMultiSelection(\'selected-%s\');return false;', + 'class' => 'selected-%s'], + 'condition' => function($model) { return !call_user_func($this->_leaf_condition, $model); diff --git a/library/ZendAfi/View/Helper/Plugin/MultiSelection/Widget.php b/library/ZendAfi/View/Helper/Plugin/MultiSelection/Widget.php index a2e18f5390059cc37c19784b8f9e339e572f1611..78db27f53d698252edeac70d1232d70e728a092a 100644 --- a/library/ZendAfi/View/Helper/Plugin/MultiSelection/Widget.php +++ b/library/ZendAfi/View/Helper/Plugin/MultiSelection/Widget.php @@ -35,11 +35,34 @@ class ZendAfi_View_Helper_Plugin_MultiSelection_Widget extends ZendAfi_View_Help $_category_label; public function Plugin_MultiSelection_Widget($multi_selection) { + $this->_current_skin = Class_Admin_Skin::current(); + + $icon_add = $this->_current_skin->renderActionIconOn('basket', + $this->view, + ['alt' => $this->_('Ajouter à la sélection'), + 'title' => $this->_('Ajouter'), + 'class' => 'ico']); + $icon_remove = $this->_current_skin->renderActionIconOn('cancel', + $this->view, + ['alt' => $this->_('Supprimer de la sélection'), + 'title' => $this->_('Supprimer'), + 'class' => 'ico']); + + + Class_ScriptLoader::getInstance() + ->addInlineScript(" + var icon_basket = '".$icon_add."'; + var icon_delete = '".$icon_remove."';") + + ->addAdminScript('multi_selection/multi_selection.js'); + if(!$multi_selection) return ''; if(0 == count($multi_selection->getModels())) - return ''; + return $this->_tag('div', + $this->_tag('div', '', + ['class' => 'selected_items_widget show'])); $multi_selection->acceptWidgetVisitor($this); return $this->_render(); diff --git a/library/ZendAfi/View/Helper/RenderModelAction.php b/library/ZendAfi/View/Helper/RenderModelAction.php index 8b35b1094267d11a48dfc2205416af64eef16d9f..1e4b08798ba992f3c168699a002673178c68ecb8 100644 --- a/library/ZendAfi/View/Helper/RenderModelAction.php +++ b/library/ZendAfi/View/Helper/RenderModelAction.php @@ -62,7 +62,7 @@ class ZendAfi_View_Helper_RenderModelAction extends ZendAfi_View_Helper_BaseHelp $url = $this->_initUrl($model); $icon = $this->_initIcon($model); - $anchorOptions = $this->_initAnchorOptions($model->getId(), $url); + $anchorOptions = $this->_initAnchorOptions($this->_getModelId($model), $url); $title = $this->_initTitle($model); @@ -131,6 +131,7 @@ class ZendAfi_View_Helper_RenderModelAction extends ZendAfi_View_Helper_BaseHelp && (false === strpos($url, $_SERVER['REQUEST_URI']))) return $attribs; + isset($attribs['class']) ? ($attribs['class'] .= ' selected') : ($attribs['class'] = 'selected'); diff --git a/public/admin/js/multi_selection/multi_selection.js b/public/admin/js/multi_selection/multi_selection.js new file mode 100644 index 0000000000000000000000000000000000000000..586046a6b56d63322fbc255042fbe6caad163d69 --- /dev/null +++ b/public/admin/js/multi_selection/multi_selection.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +function addMultiSelection(id) { + var url = $('a.'+id+':first').attr('href'); + $.getJSON(url.replace('-selection','-selection-ajax',url), function(data) { + $('.selected_items_widget:first').html(data['selection']); + if (data['response'] != 'ok' ) { + $('.selected_items_widget:first').html(data['selection']+"<div class=\"popup_notification\">"+data['response']+"</div>"); + $('.popup_notification:first').dialog(); + return ; + } + var is_basket = url.indexOf('add-model')>-1; + var icon = (is_basket ? icon_delete : icon_basket); + + var url_href = (is_basket + ? url.replace(/add-model-to/g,'remove-model-from',url) + : url.replace(/remove-model-from/g,'add-model-to',url)); + + $('a.'+id).each (function() { + $(this).attr('href', url_href); + $(this).html(icon); + }); + + }); + return false; +} diff --git a/public/admin/js/multi_selection/test.js b/public/admin/js/multi_selection/test.js new file mode 100644 index 0000000000000000000000000000000000000000..4ccafbea056667d84a6a24b93e10eecfd1981b8c --- /dev/null +++ b/public/admin/js/multi_selection/test.js @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +var ajax_calls = []; + +$.getJSON = function(url, callback, c) { + ajax_calls.push(url); + return callback({'selection':'my selection', 'response':'ok'}); +}; + + +test('call url with ajax', function() { + $('#first').click(); + equal(ajax_calls[0], '/admin/cms/add-model-to-selection-ajax/page/1/id_cat/7', 'URL has been called ' + ajax_calls[0]); +}); + + +test('change basket to remove all links and images', function() { + var first= $("#first"); + $('#first').click(); + $('#first').click(); + var second= $("#first"); + equal(first.attr("href"), '/admin/cms/remove-model-from-selection/page/1/id_cat/7' ); + equal(second.attr("href"), '/admin/cms/remove-model-from-selection/page/1/id_cat/7' ); + var img= $("#first> img"); + equal(img.attr("src"), "/public/admin/skins/bokeh74/icons/actions/cancel_24.png"); + + var img_two= $("#second> img"); + equal(img_two.attr("src"), "/public/admin/skins/bokeh74/icons/actions/cancel_24.png"); + +}); + + + +test('scenario with 3 clicks', function() { + $('#first').click(); + var myanchor= $("#first"); + equal(myanchor.attr("href"), '/admin/cms/add-model-to-selection/page/1/id_cat/7' ); + $('#first').click(); + equal(myanchor.attr("href"), '/admin/cms/remove-model-from-selection/page/1/id_cat/7' ); + $('#first').click(); + equal(myanchor.attr("href"), '/admin/cms/add-model-to-selection/page/1/id_cat/7' ); +}); + + diff --git a/public/admin/js/multi_selection/tests.html b/public/admin/js/multi_selection/tests.html new file mode 100644 index 0000000000000000000000000000000000000000..0d43492e1c14c53f5ac75238409f353a68adec27 --- /dev/null +++ b/public/admin/js/multi_selection/tests.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<!-- +/** + * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +--> +<html> +<head> + <meta charset="utf-8"> + <title>QUnit tests</title> + <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-git.css"> +</head> +<body> + <div id="qunit"></div> + <div id="qunit-fixture"></div> + <script type="text/javascript" src="../jquery-3.2.1.min.js"></script> + + <script src="multi_selection.js"></script> + <script src="http://code.jquery.com/qunit/qunit-1.13.0.js"></script> + <script src="test.js"></script> + <script> var icon_basket = '<img src="/public/admin/skins/bokeh74/icons/actions/panier_24.png" alt="Ajouter à la sélection" title="Ajouter" class="ico">'; + var icon_delete = '<img src="/public/admin/skins/bokeh74/icons/actions/cancel_24.png" alt="Supprimer de la sélection" title="Supprimer" class="ico">';</script> + <a class="select-111" id="first" href="/admin/cms/add-model-to-selection/page/1/id_cat/7" onclick="addMultiSelection('select-111');return false;"><img src="" alt="Ajouter"> </a> + <a class="select-111" id="second" href="/admin/cms/add-model-to-selection/page/1/id_cat/7" onclick="addMultiSelection('select-111');return false;"><img src="" alt="Ajouter"> </a> +</body> +</html> diff --git a/tests/js/MultiSelectionTest.php b/tests/js/MultiSelectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cff8067164ae26bc9707056f9912e4bd3f9263e7 --- /dev/null +++ b/tests/js/MultiSelectionTest.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class MultiSelectionTest extends PHPUnit_Framework_TestCase { + + /** @test */ + public function multiSelectJstestShouldSuccess() { + exec('phantomjs ' . ROOT_PATH . 'tests_js/lib/qunit-phantomjs-runner/runner.js ' . ROOT_PATH . 'public/admin/js/multi_selection/tests.html', $output, $result); + $this->assertEquals(0, $result, implode("\n", $output)); + } +} \ No newline at end of file