From dba4a129ff94b2f682ae7f297620ca1a8c9efc8a Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Tue, 22 Oct 2013 20:20:15 +0200 Subject: [PATCH] module access management --- core/lib/Thelia/Action/Profile.php | 28 ++++++ core/lib/Thelia/Config/Resources/config.xml | 1 + .../Thelia/Config/Resources/routing/admin.xml | 4 + .../Controller/Admin/ProfileController.php | 94 ++++++++++++++++++ .../Core/Event/Profile/ProfileEvent.php | 11 +++ core/lib/Thelia/Core/Event/TheliaEvents.php | 1 + core/lib/Thelia/Core/Template/Loop/Module.php | 32 ++++++ .../Form/ProfileUpdateModuleAccessForm.php | 99 +++++++++++++++++++ templates/admin/default/profile-edit.html | 94 +++++++++++++++++- 9 files changed, 359 insertions(+), 5 deletions(-) create mode 100644 core/lib/Thelia/Form/ProfileUpdateModuleAccessForm.php diff --git a/core/lib/Thelia/Action/Profile.php b/core/lib/Thelia/Action/Profile.php index 91aa51306..544882700 100644 --- a/core/lib/Thelia/Action/Profile.php +++ b/core/lib/Thelia/Action/Profile.php @@ -28,7 +28,10 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Thelia\Core\Event\Profile\ProfileEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Core\Security\AccessManager; +use Thelia\Model\ModuleQuery; use Thelia\Model\Profile as ProfileModel; +use Thelia\Model\ProfileModule; +use Thelia\Model\ProfileModuleQuery; use Thelia\Model\ProfileQuery; use Thelia\Model\ProfileResource; use Thelia\Model\ProfileResourceQuery; @@ -104,6 +107,30 @@ class Profile extends BaseAction implements EventSubscriberInterface } } + /** + * @param ProfileEvent $event + */ + public function updateModuleAccess(ProfileEvent $event) + { + if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) { + ProfileModuleQuery::create()->filterByProfileId($event->getId())->delete(); + foreach($event->getModuleAccess() as $moduleCode => $accesses) { + $manager = new AccessManager(0); + $manager->build($accesses); + + $profileModule = new ProfileModule(); + $profileModule->setProfileId($event->getId()) + ->setModule(ModuleQuery::create()->findOneByCode($moduleCode)) + ->setAccess( $manager->getAccessValue() ); + + $profileModule->save(); + + } + + $event->setProfile($profile); + } + } + /** * @param ProfileEvent $event */ @@ -129,6 +156,7 @@ class Profile extends BaseAction implements EventSubscriberInterface TheliaEvents::PROFILE_UPDATE => array("update", 128), TheliaEvents::PROFILE_DELETE => array("delete", 128), TheliaEvents::PROFILE_RESOURCE_ACCESS_UPDATE => array("updateResourceAccess", 128), + TheliaEvents::PROFILE_MODULE_ACCESS_UPDATE => array("updateModuleAccess", 128), ); } } diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index 5b145f89f..0ce26cf49 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -129,6 +129,7 @@
+ diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index e4b0611c9..7b377b48e 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -781,6 +781,10 @@ Thelia\Controller\Admin\ProfileController::processUpdateResourceAccess + + Thelia\Controller\Admin\ProfileController::processUpdateModuleAccess + + Thelia\Controller\Admin\ProfileController::deleteAction diff --git a/core/lib/Thelia/Controller/Admin/ProfileController.php b/core/lib/Thelia/Controller/Admin/ProfileController.php index 4261f5e3b..3cc5729f5 100644 --- a/core/lib/Thelia/Controller/Admin/ProfileController.php +++ b/core/lib/Thelia/Controller/Admin/ProfileController.php @@ -30,6 +30,7 @@ use Thelia\Core\Event\TheliaEvents; use Thelia\Form\ProfileCreationForm; use Thelia\Form\ProfileModificationForm; use Thelia\Form\ProfileProfileListUpdateForm; +use Thelia\Form\ProfileUpdateModuleAccessForm; use Thelia\Form\ProfileUpdateResourceAccessForm; use Thelia\Model\ProfileQuery; @@ -128,6 +129,16 @@ class ProfileController extends AbstractCrudController return new ProfileUpdateResourceAccessForm($this->getRequest(), "form", $data); } + protected function hydrateModuleUpdateForm($object) + { + $data = array( + 'id' => $object->getId(), + ); + + // Setup the object form + return new ProfileUpdateModuleAccessForm($this->getRequest(), "form", $data); + } + protected function getObjectFromEvent($event) { return $event->hasProfile() ? $event->getProfile() : null; @@ -246,9 +257,11 @@ class ProfileController extends AbstractCrudController // Hydrate the form and pass it to the parser $resourceAccessForm = $this->hydrateResourceUpdateForm($object); + $moduleAccessForm = $this->hydrateModuleUpdateForm($object); // Pass it to the parser $this->getParserContext()->addForm($resourceAccessForm); + $this->getParserContext()->addForm($moduleAccessForm); } return parent::updateAction(); @@ -264,6 +277,16 @@ class ProfileController extends AbstractCrudController return $event; } + protected function getUpdateModuleAccessEvent($formData) + { + $event = new ProfileEvent(); + + $event->setId($formData['id']); + $event->setModuleAccess($this->getModuleAccess($formData)); + + return $event; + } + protected function getResourceAccess($formData) { $requirements = array(); @@ -286,6 +309,28 @@ class ProfileController extends AbstractCrudController return $requirements; } + protected function getModuleAccess($formData) + { + $requirements = array(); + foreach($formData as $data => $value) { + if(!strstr($data, ':')) { + continue; + } + + $explosion = explode(':', $data); + + $prefix = array_shift ( $explosion ); + + if($prefix != ProfileUpdateModuleAccessForm::MODULE_ACCESS_FIELD_PREFIX) { + continue; + } + + $requirements[implode('.', $explosion)] = $value; + } + + return $requirements; + } + public function processUpdateResourceAccess() { // Check current user authorization @@ -334,4 +379,53 @@ class ProfileController extends AbstractCrudController // At this point, the form has errors, and should be redisplayed. return $this->renderEditionTemplate(); } + + public function processUpdateModuleAccess() + { + // Check current user authorization + if (null !== $response = $this->checkAuth($this->resourceCode, AccessManager::UPDATE)) return $response; + + $error_msg = false; + + // Create the form from the request + $changeForm = new ProfileUpdateModuleAccessForm($this->getRequest()); + + try { + // Check the form against constraints violations + $form = $this->validateForm($changeForm, "POST"); + + // Get the form field values + $data = $form->getData(); + + $changeEvent = $this->getUpdateModuleAccessEvent($data); + + $this->dispatch(TheliaEvents::PROFILE_MODULE_ACCESS_UPDATE, $changeEvent); + + if (! $this->eventContainsObject($changeEvent)) + throw new \LogicException( + $this->getTranslator()->trans("No %obj was updated.", array('%obj', $this->objectName))); + + // Log object modification + if (null !== $changedObject = $this->getObjectFromEvent($changeEvent)) { + $this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + } + + if ($response == null) { + $this->redirectToEditionTemplate($this->getRequest(), isset($data['country_list'][0]) ? $data['country_list'][0] : null); + } else { + return $response; + } + } catch (FormValidationException $ex) { + // Form cannot be validated + $error_msg = $this->createStandardFormValidationErrorMessage($ex); + } catch (\Exception $ex) { + // Any other error + $error_msg = $ex->getMessage(); + } + + $this->setupFormErrorContext($this->getTranslator()->trans("%obj modification", array('%obj' => 'taxrule')), $error_msg, $changeForm, $ex); + + // At this point, the form has errors, and should be redisplayed. + return $this->renderEditionTemplate(); + } } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/Profile/ProfileEvent.php b/core/lib/Thelia/Core/Event/Profile/ProfileEvent.php index 35f2f30b4..7935b00eb 100644 --- a/core/lib/Thelia/Core/Event/Profile/ProfileEvent.php +++ b/core/lib/Thelia/Core/Event/Profile/ProfileEvent.php @@ -37,6 +37,7 @@ class ProfileEvent extends ActionEvent protected $description = null; protected $postscriptum = null; protected $resourceAccess = null; + protected $moduleAccess = null; public function __construct(Profile $profile = null) { @@ -139,4 +140,14 @@ class ProfileEvent extends ActionEvent { return $this->resourceAccess; } + + public function setModuleAccess($moduleAccess) + { + $this->moduleAccess = $moduleAccess; + } + + public function getModuleAccess() + { + return $this->moduleAccess; + } } diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 18f2a75bd..bc16115f9 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -553,6 +553,7 @@ final class TheliaEvents const PROFILE_UPDATE = "action.updateProfile"; const PROFILE_DELETE = "action.deleteProfile"; const PROFILE_RESOURCE_ACCESS_UPDATE = "action.updateProfileResourceAccess"; + const PROFILE_MODULE_ACCESS_UPDATE = "action.updateProfileModuleAccess"; // -- Tax Rules management --------------------------------------------- diff --git a/core/lib/Thelia/Core/Template/Loop/Module.php b/core/lib/Thelia/Core/Template/Loop/Module.php index d780658c0..3c046f61d 100755 --- a/core/lib/Thelia/Core/Template/Loop/Module.php +++ b/core/lib/Thelia/Core/Template/Loop/Module.php @@ -24,6 +24,7 @@ namespace Thelia\Core\Template\Loop; use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Core\Security\AccessManager; use Thelia\Core\Template\Element\BaseI18nLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; @@ -56,6 +57,13 @@ class Module extends BaseI18nLoop { return new ArgumentCollection( Argument::createIntListTypeArgument('id'), + Argument::createIntTypeArgument('profile'), + new Argument( + 'code', + new Type\TypeCollection( + new Type\AlphaNumStringListType() + ) + ), new Argument( 'module_type', new Type\TypeCollection( @@ -89,6 +97,20 @@ class Module extends BaseI18nLoop $search->filterById($id, Criteria::IN); } + $profile = $this->getProfile(); + + if (null !== $profile) { + $search->leftJoinProfileModule('profile_module') + ->addJoinCondition('profile_module', 'profile_module.PROFILE_ID=?', $profile, null, \PDO::PARAM_INT) + ->withColumn('profile_module.access', 'access'); + } + + $code = $this->getCode(); + + if(null !== $code) { + $search->filterByCode($code, Criteria::IN); + } + $moduleType = $this->getModule_type(); if (null !== $moduleType) { @@ -129,6 +151,16 @@ class Module extends BaseI18nLoop ->set("CLASS", $module->getFullNamespace()) ->set("POSITION", $module->getPosition()); + if (null !== $profile) { + $accessValue = $module->getVirtualColumn('access'); + $manager = new AccessManager($accessValue); + + $loopResultRow->set("VIEWABLE", $manager->can(AccessManager::VIEW)? 1 : 0) + ->set("CREATABLE", $manager->can(AccessManager::CREATE) ? 1 : 0) + ->set("UPDATABLE", $manager->can(AccessManager::UPDATE)? 1 : 0) + ->set("DELETABLE", $manager->can(AccessManager::DELETE)? 1 : 0); + } + $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Form/ProfileUpdateModuleAccessForm.php b/core/lib/Thelia/Form/ProfileUpdateModuleAccessForm.php new file mode 100644 index 000000000..bbbc869ee --- /dev/null +++ b/core/lib/Thelia/Form/ProfileUpdateModuleAccessForm.php @@ -0,0 +1,99 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Form; + +use Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\ExecutionContextInterface; +use Thelia\Core\Security\AccessManager; +use Thelia\Core\Translation\Translator; +use Thelia\Model\ProfileQuery; +use Thelia\Model\ModuleQuery; + +/** + * Class ProfileUpdateModuleAccessForm + * @package Thelia\Form + * @author Etienne Roudeix + */ +class ProfileUpdateModuleAccessForm extends BaseForm +{ + const MODULE_ACCESS_FIELD_PREFIX = "module"; + + protected function buildForm($change_mode = false) + { + $this->formBuilder + ->add("id", "hidden", array( + "required" => true, + "constraints" => array( + new Constraints\NotBlank(), + new Constraints\Callback( + array( + "methods" => array( + array($this, "verifyProfileId"), + ), + ) + ), + ) + )) + ; + + foreach(ModuleQuery::create()->find() as $module) { + $this->formBuilder->add( + self::MODULE_ACCESS_FIELD_PREFIX . ':' . str_replace(".", ":", $module->getCode()), + "choice", + array( + "choices" => array( + AccessManager::VIEW => AccessManager::VIEW, + AccessManager::CREATE => AccessManager::CREATE, + AccessManager::UPDATE => AccessManager::UPDATE, + AccessManager::DELETE => AccessManager::DELETE, + ), + "attr" => array( + "tag" => "modules", + "module_code" => $module->getCode(), + "module_title" => $module->getTitle(), + ), + "multiple" => true, + "constraints" => array( + + ) + ) + ); + } + } + + public function getName() + { + return "thelia_profile_module_access_modification"; + } + + public function verifyProfileId($value, ExecutionContextInterface $context) + { + $profile = ProfileQuery::create() + ->findPk($value); + + if (null === $profile) { + $context->addViolation("Profile ID not found"); + } + } +} diff --git a/templates/admin/default/profile-edit.html b/templates/admin/default/profile-edit.html index 09863aa95..a7c2e0f9d 100644 --- a/templates/admin/default/profile-edit.html +++ b/templates/admin/default/profile-edit.html @@ -32,7 +32,8 @@
@@ -116,11 +117,11 @@
-
+
{form name="thelia.admin.profile.resource-access.modification"} - + {form_hidden_fields form=$form} @@ -131,7 +132,8 @@ @@ -184,7 +186,89 @@ - + + +
- {intl l="Manage permissions"} + {intl l="Manage resource rights"} +
+ + +
+ + + + {/form} + +
+ +
+ + {form name="thelia.admin.profile.module-access.modification"} + +
+ + {form_hidden_fields form=$form} + + {* Be sure to get the product ID, even if the form could not be validated *} + + + {if $form_error}
{$form_error_message}
{/if} + + + + + + + + + + + + + + + + + + + {form_tagged_fields form=$form tag='modules'} + + {loop type="module" name="module-list" code=$attr_list.module_code profile=$ID backend_context="1"} + + + + + + + + + + + {/loop} + + {/form_tagged_fields} + + + + +
+ {intl l="Manage module rights"} + +
{intl l="Module"}{intl l="Title"}{intl l="Rights"}
{intl l="View"}{intl l="Create"}{intl l="Update"}{intl l="Delete"}
{$CODE}{$TITLE} +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+