diff --git a/VERSIONS_HOTLINE/90657 b/VERSIONS_HOTLINE/90657 new file mode 100644 index 0000000000000000000000000000000000000000..4ba1896f72794055563e6ced6fd8e979f97c5c9f --- /dev/null +++ b/VERSIONS_HOTLINE/90657 @@ -0,0 +1 @@ + - ticket #90657 : Administration : L'accès à l'explorateur de fichiers est désormais gérée par la permission de groupe "Explorateur de Fichier: accès en ecriture sur les répertoires de dépot de fichier" \ No newline at end of file diff --git a/cosmogramme/sql/patch/patch_371.php b/cosmogramme/sql/patch/patch_371.php new file mode 100644 index 0000000000000000000000000000000000000000..dd167fd542846b0b98521305d500369dd7d7e29a --- /dev/null +++ b/cosmogramme/sql/patch/patch_371.php @@ -0,0 +1,2 @@ +<?php +(new Class_Migration_FileManagerWriteAccess())->run(); diff --git a/library/Class/Migration/FileManagerWriteAccess.php b/library/Class/Migration/FileManagerWriteAccess.php new file mode 100644 index 0000000000000000000000000000000000000000..c592a3989aa464fe71e6f418c1f534ccd0973a22 --- /dev/null +++ b/library/Class/Migration/FileManagerWriteAccess.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright (c) 2012-2019, 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 Class_Migration_FileManagerWriteAccess { + public function run() { + foreach(Class_UserGroup::findAll() as $group) + $this->_runOne($group); + } + + + protected function _runOne($group) { + if (array_intersect( + [Class_UserGroup::RIGHT_USER_DOMAINES_TOTAL_ACCESS, + Class_UserGroup::RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT, + Class_UserGroup::RIGHT_USER_ACCES_ARTICLES], + $group->getRights())) + $group->addRight(Class_UserGroup::RIGHT_USER_FILE_ACCESS)->save(); + } +} diff --git a/library/Class/UserGroup.php b/library/Class/UserGroup.php index 3f1c906fa5c0c26478cd7ff4b9b9c0dfebddf5ed..2b8af9e595d63f527607643d733455dbb02fb903 100644 --- a/library/Class/UserGroup.php +++ b/library/Class/UserGroup.php @@ -212,7 +212,7 @@ class Class_UserGroup extends Storm_Model_Abstract { self::RIGHT_ACCES_PNB_DILICOM => $t->_('Bibliothèque numérique: autoriser le prêt numérique Dilicom'), self::RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT => $t->_('Domaines: accès, modification et suppression limitée au créateur'), self::RIGHT_USER_DOMAINES_TOTAL_ACCESS => $t->_('Domaines: accès total en modification et suppression'), - self::RIGHT_USER_FILE_ACCESS => $t->_('Articles: accès sur les répertoires images et file'), + self::RIGHT_USER_FILE_ACCESS => $t->_('Explorateur de fichiers: accès en ecriture sur les répertoires de dépot de fichier'), self::RIGHT_USER_SITOTHEQUE => $t->_('Sitothèque: accès sitothèque'), self::RIGHT_USER_MODO => $t->_('Modération: accès modération'), self::RIGHT_USER_INSCRIPTIONS => $t->_('Modération: accès demandes d\'inscriptions'), diff --git a/library/Class/Users.php b/library/Class/Users.php index c2b79e30760ec096105bdc94af85784ee2e9b9c0..9ca72c7a86b1a08d98f3390f73fc00f90a5b30c3 100644 --- a/library/Class/Users.php +++ b/library/Class/Users.php @@ -939,6 +939,11 @@ class Class_Users extends Storm_Model_Abstract { } + public function hasRightFileManagerAccess() { + return $this->isAdmin() || $this->hasRightToAccess(Class_UserGroup::RIGHT_USER_FILE_ACCESS); + } + + /** * @return bool */ @@ -1932,4 +1937,9 @@ class Class_Users extends Storm_Model_Abstract { return false; } + + + public function isAllowedToAccess($controller, $action) { + return (new ZendAfi_Acl_AdminControllerGroup)->isAllowed($this, $controller, $action); + } } diff --git a/library/ZendAfi/Acl/AdminControllerGroup.php b/library/ZendAfi/Acl/AdminControllerGroup.php index 31a136c3ff0d9887afeef98e12af88c89ad976d9..ba103b09e36bf4e0c5abc73459dd913a39f31729 100644 --- a/library/ZendAfi/Acl/AdminControllerGroup.php +++ b/library/ZendAfi/Acl/AdminControllerGroup.php @@ -44,6 +44,14 @@ class ZendAfi_Acl_AdminControllerGroup { 'users/settings' => null, 'usergroup-agenda' => Class_UserGroup::RIGHT_USER_RENDEZ_VOUS, 'rendez-vous' => Class_UserGroup::RIGHT_USER_RENDEZ_VOUS, + 'file-manager/create' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/copy' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/import' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/resize' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/rename' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/delete' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager/drop' => Class_UserGroup::RIGHT_USER_FILE_ACCESS, + 'file-manager' => null, ], $_activated = []; diff --git a/library/ZendAfi/Controller/Plugin/Manager/FileManager.php b/library/ZendAfi/Controller/Plugin/Manager/FileManager.php index 7c48f064746dce48ea031f761b1d6af54c4927e4..6e7e06b3983f5324e8612dac345b66c29b49d5ac 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/FileManager.php +++ b/library/ZendAfi/Controller/Plugin/Manager/FileManager.php @@ -23,12 +23,12 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_Plugin_Manager_Manager { public function getActions($model) { - return [['url' => $this->_getBrowseUrl($model), + return [ + ['url' => $this->_getBrowseUrl($model), 'icon' => '', - 'caption' => function($model) - { - return $this->_view->thumbnail($model) . $model->getName(); - }, + 'caption' => function($model) { + return $this->_view->thumbnail($model) . $model->getName(); + }, 'label' => $this->_('Voir le contenu de "%s"', $model->getPath()), 'anchorOptions' => array_filter(['title' => $this->_('Voir le contenu de "%s"', $model->getId()), 'data-path' => $model->getPath(), @@ -53,7 +53,9 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P 'caption' => function($model) { return $this->_view->tag('i', '', ['class' => 'fa icon-folder-plus']) . $this->_view->tag('span', $this->_('Créer')); }, - 'condition' => 'isDir', + 'condition' => function ($model) { + return $model->isDir() && $this->_isallowedaction($model,'create'); + }, 'label' => $this->_('Créer un nouveau dossier dans "%s".', $model->getPath()), 'anchorOptions' => array_filter(['title' => $this->_('Ajouter un nouveau dossier dans "%s".', $model->getPath()), 'data-popup' => 'true', @@ -65,7 +67,9 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P 'caption' => function($model) { return $this->_view->tag('i', '', ['class' => 'fa fa-upload']) . $this->_view->tag('span', $this->_('Téléverser')); }, - 'condition' => 'isDir', + 'condition' => function ($model) { + return $model->isDir() && $this->_isallowedaction($model,'import'); + }, 'label' => $this->_('Téléverser un nouveau fichier dans le dossier "%s".', $model->getPath()), 'anchorOptions' => array_filter(['title' => $this->_('Téléverser un nouveau fichier dans le dossier "%s".', $model->getPath()), 'data-popup' => 'true', @@ -77,7 +81,9 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P 'caption' => function($model) { return $this->_view->tag('i', '', ['class' => 'fa fa-compress']) . $this->_view->tag('span', $this->_('Redimensionner')); }, - 'condition' => 'isResizable', + 'condition' => function($model) { + return $model->isResizable() && $this->_isallowedaction($model, 'resize'); + }, 'label' => $this->_('Redimensionner le fichier "%s".', $model->getPath()), 'anchorOptions' => array_filter(['title' => $this->_('Redimensionner le fichier "%s".', $model->getPath()), 'data-popup' => 'true', @@ -90,6 +96,7 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P return $this->_view->tag('i', '', ['class' => 'fa fa-pencil-square-o']) . $this->_view->tag('span', $this->_('Renommer')); }, 'label' => $this->_('Renommer "%s"', $model->getName()), + 'condition' => function($model){ return $this->_isallowedaction($model,'rename'); }, 'anchorOptions' => array_filter(['title' => $this->_('Renommer "%s"', $model->getName()), 'data-popup' => 'true', 'class' => $model->isWritable() ? null : 'disabled'])], @@ -101,6 +108,7 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P return $this->_view->tag('i', '', ['class' => 'fa fa-trash']) . $this->_view->tag('span', $this->_('Supprimer')); }, 'label' => $this->_('Supprimer "%s"', $model->getName()), + 'condition' => function($model) { return $this->_isallowedaction($model,'delete'); }, 'anchorOptions' => array_filter(['title' => $this->_('Supprimer "%s"', $model->getName()), 'data-popup' => 'true', 'class' => $model->isWritable() ? null : 'disabled'])], @@ -128,6 +136,11 @@ class ZendAfi_Controller_Plugin_Manager_FileManager extends ZendAfi_Controller_P } + protected function _isallowedaction($model, $action) { + return Class_Users::getIdentity()->isAllowedToAccess('file-manager', $action); + } + + protected function _getBrowseUrl($model) { return $this->_view->url([$model->getBrowserParam() => $model->getPath(), 'search_' . $model->getBrowserParam() => null, diff --git a/library/ZendAfi/View/Helper/Admin/FileManager.php b/library/ZendAfi/View/Helper/Admin/FileManager.php index fcbdab68c61a49b99cf73fd5465be79e4bbd9313..96bbab4ef64faf12144e39fb2c0f53ca3fc24198 100644 --- a/library/ZendAfi/View/Helper/Admin/FileManager.php +++ b/library/ZendAfi/View/Helper/Admin/FileManager.php @@ -34,7 +34,11 @@ class ZendAfi_View_Helper_Admin_FileManager extends ZendAfi_View_Helper_BaseHelp ->addOPACStyleSheet('file-manager/style.css') ->addOPACPluginScript('file-manager/file-manager.js') ->addOPACPluginStyleSheet('file-manager/file-manager.css') - ->addJqueryReady('$("div.file-manager").fileManager();initializePopups();'); + ->addJqueryReady('$("div.file-manager").fileManager({dragndrop: ' + . ($user->hasRightFileManagerAccess() + ? 'true' + : 'false' ) + . '});initializePopups();'); $html = implode([$this->_renderTools($settings), $this->_renderManager($settings)]); @@ -46,7 +50,8 @@ class ZendAfi_View_Helper_Admin_FileManager extends ZendAfi_View_Helper_BaseHelp ['class' => implode(' ', array_filter(['file-manager', $settings->getFullScreen() ? 'fullscreen': ''])), 'data-drop-url' => $this->view->url(['action' => 'drop', - 'render' => 'popup'])]); + 'render' => 'popup']) + ]); } diff --git a/public/opac/java/file-manager/file-manager.js b/public/opac/java/file-manager/file-manager.js index 426b1c114f3af12b4421271f1d74df4c91d5814f..f4bec67af0a203a32a5a855cdd2cf1f6eef4cd67 100644 --- a/public/opac/java/file-manager/file-manager.js +++ b/public/opac/java/file-manager/file-manager.js @@ -19,7 +19,13 @@ */ (function ( $ ) { - $.fn.fileManager = function() { + $.fn.fileManager = function(options) { + if (!options) + options = {}; + + if (undefined == options.dragndrop) + options.dragndrop = true; + var manager = $(this); if(manager.hasClass('fullscreen')) @@ -41,35 +47,37 @@ manager.find(".file-manager-contextmenu").hide(); }); - manager.find(":not(.show_actions) > .actions a").draggable( - { - revert: 'invalid', - containment: '.file-manager', - helper: 'clone', - distance: '20' - } - ); - - manager.find(":not(.show_actions) > .actions a:not(.read_only, .file), .browser:not(.read_only)").droppable( - { - accept: ":not(.show_actions) > .actions a", - greedy: true, - tolerance: "pointer", - hoverClass: "could-recieve-droppable", - drop: function( event, ui ) { - var item = ui.draggable, - move = item.attr('data-path'), - into = $(this).attr('data-path'), - drop_url = manager.attr('data-drop-url'); - - var url = drop_url + - '&item=' + move + - '&into=' + into; - - opacDialogFromUrl(url); - } - } - ); + if (options.dragndrop){ + manager.find(":not(.show_actions) > .actions a").draggable( + { + revert: 'invalid', + containment: '.file-manager', + helper: 'clone', + distance: '20' + } + ); + + manager.find(":not(.show_actions) > .actions a:not(.read_only, .file), .browser:not(.read_only)").droppable( + { + accept: ":not(.show_actions) > .actions a", + greedy: true, + tolerance: "pointer", + hoverClass: "could-recieve-droppable", + drop: function( event, ui ) { + var item = ui.draggable, + move = item.attr('data-path'), + into = $(this).attr('data-path'), + drop_url = manager.attr('data-drop-url'); + + var url = drop_url + + '&item=' + move + + '&into=' + into; + + opacDialogFromUrl(url); + } + } + ); + } }; $.urlParam = function(name){ diff --git a/tests/application/modules/admin/controllers/FileManagerControllerTest.php b/tests/application/modules/admin/controllers/FileManagerControllerTest.php index 3eac9ac1b8ca392c5c8e43bb627f6c9013323dbf..afd4d470be06f0e5e6c3d82b0349cdebdaea5262 100644 --- a/tests/application/modules/admin/controllers/FileManagerControllerTest.php +++ b/tests/application/modules/admin/controllers/FileManagerControllerTest.php @@ -91,7 +91,6 @@ class FileManagerNoFileSystemMockTest extends Admin_AbstractControllerTestCase { - abstract class FileManagerControllerTestCase extends Admin_AbstractControllerTestCase { protected $_storm_default_to_volatile = true, @@ -183,6 +182,57 @@ abstract class FileManagerControllerTestCase extends Admin_AbstractControllerTes +class FileManagerControllerIndexAclTest extends FileManagerControllerTestCase { + public function setUp() { + parent::setUp(); + + $group = $this->fixture('Class_UserGroup', ['id' => 4, + 'libelle'=>"UtilisateurSimple"]); + + $group->addRight(Class_UserGroup::RIGHT_USER_ACCES_ARTICLES); + + + $user = $this->fixture('Class_Users', + ['id' => 34, + 'login' => 'modoportail', + 'password' => 's3cr3t \o/', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL, + 'user_groups' => [$group] + ]); + + ZendAfi_Auth::getInstance()->logUser($user); + + $this->dispatch('/admin/file-manager/index'); + } + + + /** @test */ + public function pageFileManagerIndexshouldNotRedirect() { + $this->assertNotRedirect(); + } + + + public function deniedactions(){ + return [['create'],['import'],['rename'],['delete']]; + } + + + /** + * @test + * @dataProvider deniedactions + */ + public function buttonCreateShouldNotBeDisplayed($action) { + $this->assertNotXPath("//a[contains(@href, 'file-manager/" . $action . "')]", + $this->_response->getBody()); + } + + /** @test */ + public function dragndropShouldBeDisabled() { + $this->assertXPathContentContains("//script","fileManager({dragndrop: false"); + } +} + + class FileManagerControllerDispatchTest extends FileManagerControllerTestCase { public function setUp() { @@ -190,6 +240,11 @@ class FileManagerControllerDispatchTest extends FileManagerControllerTestCase { $this->dispatch('/admin/file-manager', true); } + /** @test */ + public function dragndropShouldBeEnabled() { + $this->assertXPathContentContains("//script","fileManager({dragndrop: true"); + } + /** @test */ public function fileManagerTitleShouldBeExplorateurDeFichier() { diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index 21165b817f7cd68a551f4408b391af49a74076f9..1d8e5e8c24f62a9a88b2c5d39edb8392daa72e0a 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -2593,3 +2593,12 @@ class UpgradeDB_370_Test extends UpgradeDBTestCase { /** @test */ public function placeholderForArteVodUpdateAuthor() {} } + + + +class UpgradeDB_371_Test extends UpgradeDBTestCase { + public function prepare() {} + + /** @test */ + public function placeholderForFileManagerWriteAccess() {} +} diff --git a/tests/library/Class/Migration/FileManagerWriteAccessTest.php b/tests/library/Class/Migration/FileManagerWriteAccessTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cb858b372a17dd2251bd5b5d77bc31f69e129722 --- /dev/null +++ b/tests/library/Class/Migration/FileManagerWriteAccessTest.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright (c) 2012-2019, 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 Class_Migration_FileManagerWriteAccessTest extends ModelTestCase { + public function setUp() { + parent::setUp(); + $this->fixture('Class_UserGroup', + ['id' => 4, + 'libelle' => 'Gestionnaires domaines', + 'rights' => [Class_UserGroup::RIGHT_USER_DOMAINES_TOTAL_ACCESS]]); + + $this->fixture('Class_UserGroup', + ['id' => 5, + 'libelle' => 'Limité domaines', + 'rights' => [Class_UserGroup::RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT]]); + + $this->fixture('Class_UserGroup', + ['id' => 6, + 'libelle' => 'Gestionnaires articles', + 'rights' => [Class_UserGroup::RIGHT_USER_ACCES_ARTICLES] + ]); + + $all = Class_UserGroup::getRightDefinitionList(); + $all = array_keys($all); + $all_but_migrable = array_diff($all,[Class_UserGroup::RIGHT_USER_DOMAINES_TOTAL_ACCESS, + Class_UserGroup::RIGHT_USER_ACCES_ARTICLES, + Class_UserGroup::RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT + ] + ); + $this->fixture('Class_UserGroup', + ['id' => 7, + 'libelle' => 'Sans droits', + 'rights' => [$all_but_migrable] + ]); + + (new Class_Migration_FileManagerWriteAccess)->run(); + } + + + public function groups() { + return [[4], [5], [6]]; + } + + + /** @test @dataProvider groups */ + public function groupShouldHaveFileManagerAccess($group) { + $this->assertTrue(Class_UserGroup::find($group) + ->hasRight(Class_UserGroup::RIGHT_USER_FILE_ACCESS)); + } + + + /** @test */ + public function groupWithNoMigrableRightShouldNotHaveFileManagerAccess() { + $this->assertFalse(Class_UserGroup::find(7)->hasRight(Class_UserGroup::RIGHT_USER_FILE_ACCESS)); + } +}