From f4aaa14a015ef4da8d72199aeaa761cc5696c8a3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 8 Jun 2015 22:49:33 +0200 Subject: [PATCH] Introduced a new "disabled_actions" option --- Controller/AdminController.php | 45 ++---- DependencyInjection/Configuration.php | 9 ++ DependencyInjection/EasyAdminExtension.php | 7 + .../tutorials/customizing-backend-actions.md | 62 +++++++- Resources/doc/tutorials/tips-and-tricks.md | 5 +- Resources/views/default/layout.html.twig | 2 +- Resources/views/default/list.html.twig | 2 - Resources/views/default/show.html.twig | 2 +- .../views/error/forbidden_action.html.twig | 15 +- Tests/Controller/CustomizedBackendTest.php | 20 +-- Tests/Controller/DefaultBackendTest.php | 31 ++-- Tests/Controller/ForbiddenActionTest.php | 27 ---- Tests/Controller/ReadOnlyBackendTest.php | 123 +++++++++++++++ .../EasyAdminExtensionTest.php | 9 ++ .../configurations/input/admin_108.yml | 6 + .../configurations/input/admin_109.yml | 8 + .../configurations/input/admin_110.yml | 9 ++ .../configurations/input/admin_111.yml | 10 ++ .../configurations/input/admin_112.yml | 10 ++ .../configurations/output/config_001.yml | 2 + .../configurations/output/config_002.yml | 3 + .../configurations/output/config_003.yml | 4 + .../configurations/output/config_004.yml | 2 + .../configurations/output/config_005.yml | 2 + .../configurations/output/config_006.yml | 1 + .../configurations/output/config_007.yml | 2 + .../configurations/output/config_008.yml | 2 + .../configurations/output/config_009.yml | 2 + .../configurations/output/config_010.yml | 2 + .../configurations/output/config_011.yml | 1 + .../configurations/output/config_012.yml | 3 + .../configurations/output/config_013.yml | 2 + .../configurations/output/config_014.yml | 2 + .../configurations/output/config_015.yml | 1 + .../configurations/output/config_016.yml | 3 + .../configurations/output/config_017.yml | 2 + .../configurations/output/config_018.yml | 2 + .../configurations/output/config_019.yml | 2 + .../configurations/output/config_020.yml | 2 + .../configurations/output/config_021.yml | 2 + .../configurations/output/config_022.yml | 3 + .../configurations/output/config_023.yml | 3 + .../configurations/output/config_024.yml | 3 + .../configurations/output/config_025.yml | 3 + .../configurations/output/config_026.yml | 2 + .../configurations/output/config_027.yml | 3 + .../configurations/output/config_028.yml | 3 + .../configurations/output/config_029.yml | 1 + .../configurations/output/config_030.yml | 3 + .../configurations/output/config_031.yml | 2 + .../configurations/output/config_032.yml | 2 + .../configurations/output/config_033.yml | 2 + .../configurations/output/config_034.yml | 2 + .../configurations/output/config_035.yml | 2 + .../configurations/output/config_036.yml | 2 + .../configurations/output/config_037.yml | 2 + .../configurations/output/config_038.yml | 2 + .../configurations/output/config_039.yml | 2 + .../configurations/output/config_040.yml | 2 + .../configurations/output/config_041.yml | 2 + .../configurations/output/config_042.yml | 2 + .../configurations/output/config_043.yml | 2 + .../configurations/output/config_044.yml | 1 + .../configurations/output/config_045.yml | 2 + .../configurations/output/config_046.yml | 2 + .../configurations/output/config_047.yml | 2 + .../configurations/output/config_048.yml | 2 + .../configurations/output/config_049.yml | 2 + .../configurations/output/config_050.yml | 2 + .../configurations/output/config_051.yml | 2 + .../configurations/output/config_052.yml | 2 + .../configurations/output/config_053.yml | 2 + .../configurations/output/config_054.yml | 2 + .../configurations/output/config_055.yml | 2 + .../configurations/output/config_056.yml | 2 + .../configurations/output/config_057.yml | 2 + .../configurations/output/config_058.yml | 2 + .../configurations/output/config_059.yml | 2 + .../configurations/output/config_060.yml | 2 + .../configurations/output/config_061.yml | 2 + .../configurations/output/config_062.yml | 2 + .../configurations/output/config_063.yml | 2 + .../configurations/output/config_064.yml | 2 + .../configurations/output/config_065.yml | 2 + .../configurations/output/config_066.yml | 2 + .../configurations/output/config_067.yml | 1 + .../configurations/output/config_068.yml | 2 + .../configurations/output/config_069.yml | 2 + .../configurations/output/config_070.yml | 2 + .../configurations/output/config_071.yml | 2 + .../configurations/output/config_072.yml | 2 + .../configurations/output/config_073.yml | 2 + .../configurations/output/config_074.yml | 2 + .../configurations/output/config_075.yml | 2 + .../configurations/output/config_076.yml | 2 + .../configurations/output/config_077.yml | 2 + .../configurations/output/config_078.yml | 2 + .../configurations/output/config_079.yml | 2 + .../configurations/output/config_080.yml | 2 + .../configurations/output/config_081.yml | 2 + .../configurations/output/config_082.yml | 2 + .../configurations/output/config_083.yml | 2 + .../configurations/output/config_084.yml | 2 + .../configurations/output/config_085.yml | 2 + .../configurations/output/config_086.yml | 2 + .../configurations/output/config_087.yml | 2 + .../configurations/output/config_088.yml | 2 + .../configurations/output/config_089.yml | 2 + .../configurations/output/config_090.yml | 2 + .../configurations/output/config_091.yml | 2 + .../configurations/output/config_092.yml | 2 + .../configurations/output/config_093.yml | 2 + .../configurations/output/config_094.yml | 2 + .../configurations/output/config_095.yml | 2 + .../configurations/output/config_096.yml | 2 + .../configurations/output/config_097.yml | 2 + .../configurations/output/config_098.yml | 2 + .../configurations/output/config_099.yml | 2 + .../configurations/output/config_100.yml | 2 + .../configurations/output/config_101.yml | 2 + .../configurations/output/config_102.yml | 2 + .../configurations/output/config_103.yml | 2 + .../configurations/output/config_104.yml | 2 + .../configurations/output/config_105.yml | 2 + .../configurations/output/config_106.yml | 2 + .../configurations/output/config_107.yml | 2 + .../configurations/output/config_108.yml | 27 ++++ .../configurations/output/config_109.yml | 143 +++++++++++++++++ .../configurations/output/config_110.yml | 142 +++++++++++++++++ .../configurations/output/config_111.yml | 143 +++++++++++++++++ .../configurations/output/config_112.yml | 144 ++++++++++++++++++ .../deprecations/output/config_001.yml | 1 + .../deprecations/output/config_002.yml | 1 + .../deprecations/output/config_003.yml | 1 + .../deprecations/output/config_004.yml | 1 + .../deprecations/output/config_005.yml | 1 + .../deprecations/output/config_006.yml | 1 + .../deprecations/output/config_007.yml | 1 + .../deprecations/output/config_008.yml | 1 + .../deprecations/output/config_009.yml | 1 + .../deprecations/output/config_010.yml | 1 + .../deprecations/output/config_011.yml | 1 + .../deprecations/output/config_012.yml | 1 + .../disabled_actions_must_be_an_array.yml | 6 + .../output/config_001.yml | 2 + .../output/config_002.yml | 2 + .../output/config_003.yml | 2 + .../output/config_004.yml | 3 + .../output/config_005.yml | 3 + .../output/config_001.yml | 2 + .../output/config_002.yml | 2 + .../output/config_003.yml | 2 + .../output/config_004.yml | 3 + .../output/config_005.yml | 3 + .../App/config/config_forbidden_action.yml | 13 -- .../App/config/config_read_only_backend.yml | 7 + Twig/EasyAdminTwigExtension.php | 10 +- 157 files changed, 1162 insertions(+), 131 deletions(-) delete mode 100644 Tests/Controller/ForbiddenActionTest.php create mode 100644 Tests/Controller/ReadOnlyBackendTest.php create mode 100644 Tests/DependencyInjection/fixtures/configurations/input/admin_108.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/input/admin_109.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/input/admin_110.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/input/admin_111.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/input/admin_112.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/output/config_108.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/output/config_109.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/output/config_110.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/output/config_111.yml create mode 100644 Tests/DependencyInjection/fixtures/configurations/output/config_112.yml create mode 100644 Tests/DependencyInjection/fixtures/exceptions/disabled_actions_must_be_an_array.yml delete mode 100644 Tests/Fixtures/App/config/config_forbidden_action.yml create mode 100644 Tests/Fixtures/App/config/config_read_only_backend.yml diff --git a/Controller/AdminController.php b/Controller/AdminController.php index a9b0533d8a..a8e3466b39 100644 --- a/Controller/AdminController.php +++ b/Controller/AdminController.php @@ -47,8 +47,6 @@ class AdminController extends Controller /** @var EntityManager */ protected $em; - protected $view; - /** * @Route("/", name="admin") * @@ -62,21 +60,16 @@ public function indexAction(Request $request) $action = $request->query->get('action', 'list'); - // for now, the homepage redirects to the 'list' action and view of the first entity + // for now, the homepage redirects to the 'list' action of the first entity if (null === $request->query->get('entity')) { return $this->redirect($this->generateUrl('admin', array( 'action' => $action, 'entity' => $this->getNameOfTheFirstConfiguredEntity(), - 'view' => $this->view, ))); } - if (in_array($action, array('list', 'edit', 'show', 'new')) && !$this->isActionAllowed($action)) { - throw new ForbiddenActionException(array( - 'action' => $action, - 'allowed_actions' => array_keys($this->entity[$this->view]['actions']), - 'view' => $this->view, - )); + if (!$this->isActionAllowed($action)) { + throw new ForbiddenActionException(array('action' => $action)); } $customMethodName = $action.$this->entity['name'].'Action'; @@ -126,7 +119,6 @@ protected function initialize(Request $request) $this->em = $this->getDoctrine()->getManagerForClass($this->entity['class']); $this->request = $request; - $this->view = $this->request->query->get('view', 'list'); $this->dispatch(EasyAdminEvents::POST_INITIALIZE); } @@ -138,7 +130,6 @@ private function dispatch($eventName, array $arguments = array()) 'em' => $this->em, 'entity' => $this->entity, 'request' => $this->request, - 'view' => $this->view, ), $arguments); $subject = isset($arguments['paginator']) ? $arguments['paginator'] : $arguments['entity']; @@ -164,7 +155,6 @@ protected function listAction() return $this->render($this->entity['templates']['list'], array( 'paginator' => $paginator, 'fields' => $fields, - 'view' => 'list', )); } @@ -214,7 +204,7 @@ protected function editAction() return !empty($refererUrl) ? $this->redirect(urldecode($refererUrl)) - : $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'list', 'entity' => $this->entity['name']))); + : $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name']))); } $this->dispatch(EasyAdminEvents::POST_EDIT); @@ -224,7 +214,6 @@ protected function editAction() 'entity_fields' => $fields, 'entity' => $entity, 'delete_form' => $deleteForm->createView(), - 'view' => 'edit', )); } @@ -254,7 +243,6 @@ protected function showAction() return $this->render($this->entity['templates']['show'], array( 'entity' => $entity, 'fields' => $fields, - 'view' => 'show', 'delete_form' => $deleteForm->createView(), )); } @@ -299,7 +287,7 @@ protected function newAction() $refererUrl = $this->request->query->get('referer', ''); - return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'new', 'entity' => $this->entity['name']))); + return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name']))); } $this->dispatch(EasyAdminEvents::POST_NEW, array( @@ -312,7 +300,6 @@ protected function newAction() 'form' => $newForm->createView(), 'entity_fields' => $fields, 'entity' => $entity, - 'view' => 'new', )); } @@ -327,7 +314,7 @@ protected function deleteAction() $this->dispatch(EasyAdminEvents::PRE_DELETE); if ('DELETE' !== $this->request->getMethod()) { - return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'list', 'entity' => $this->entity['name']))); + return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name']))); } $id = $this->request->query->get('id'); @@ -359,7 +346,7 @@ protected function deleteAction() return !empty($refererUrl) ? $this->redirect(urldecode($refererUrl)) - : $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'list', 'entity' => $this->entity['name']))); + : $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name']))); } /** @@ -383,7 +370,6 @@ protected function searchAction() return $this->render($this->entity['templates']['list'], array( 'paginator' => $paginator, 'fields' => $fields, - 'view' => 'search', )); } @@ -662,21 +648,21 @@ protected function render404error($view, array $parameters = array()) } /** - * Utility method that checks if the given action is allowed for the current - * view of the current entity. + * Utility method that checks if the given action is allowed for + * the current entity. * - * @param string $action + * @param string $actionName * * @return bool */ - protected function isActionAllowed($action) + protected function isActionAllowed($actionName) { - return array_key_exists($action, $this->entity[$this->view]['actions']); + return false === in_array($actionName, $this->entity['disabled_actions'], true); } /** * Utility shortcut to render an error when the requested action is not allowed - * for the given view of the given entity. + * for the given entity. * * @param string $action * @@ -685,10 +671,7 @@ protected function isActionAllowed($action) */ protected function renderForbiddenActionError($action) { - $allowedActions = array_keys($this->entity[$this->view]['actions']); - $parameters = array('action' => $action, 'allowed_actions' => $allowedActions, 'view' => $this->view); - - return $this->render('@EasyAdmin/error/forbidden_action.html.twig', $parameters, new Response('', 403)); + return $this->render('@EasyAdmin/error/forbidden_action.html.twig', array('action' => $action), new Response('', 403)); } /** diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 66654ffa16..54b39d2b54 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -186,6 +186,15 @@ private function addGlobalOptionsSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->end() + + ->variableNode('disabled_actions') + ->info('The names of the actions disabled for all backend entities.') + ->defaultValue(array()) + ->validate() + ->ifTrue(function ($v) { return false === is_array($v); }) + ->thenInvalid('The disabled_actions option must be an array of action names.') + ->end() + ->end() ->end() ; } diff --git a/DependencyInjection/EasyAdminExtension.php b/DependencyInjection/EasyAdminExtension.php index b1fff9759a..7e42417424 100644 --- a/DependencyInjection/EasyAdminExtension.php +++ b/DependencyInjection/EasyAdminExtension.php @@ -194,6 +194,13 @@ public function processEntityActions(array $backendConfiguration) $entitiesConfiguration = array(); foreach ($backendConfiguration['entities'] as $entityName => $entityConfiguration) { + // first, define the disabled actions + $actionsDisabledByBackend = $backendConfiguration['disabled_actions']; + $actionsDisabledByEntity = isset($entityConfiguration['disabled_actions']) ? $entityConfiguration['disabled_actions'] : array(); + $disabledActions = array_unique(array_merge($actionsDisabledByBackend, $actionsDisabledByEntity)); + $entityConfiguration['disabled_actions'] = $disabledActions; + + // second, define the actions of each entity view foreach (array('edit', 'list', 'new', 'show') as $view) { $defaultActions = $this->getDefaultActions($view); $backendActions = isset($backendConfiguration[$view]['actions']) ? $backendConfiguration[$view]['actions'] : array(); diff --git a/Resources/doc/tutorials/customizing-backend-actions.md b/Resources/doc/tutorials/customizing-backend-actions.md index 7bd06796d0..f143c9dc88 100644 --- a/Resources/doc/tutorials/customizing-backend-actions.md +++ b/Resources/doc/tutorials/customizing-backend-actions.md @@ -4,11 +4,61 @@ Customizing Backend Actions In this article you'll learn how to disable actions, how to tweak their appearance and how to create your own custom actions. +Disable Actions for Some or All Entities +---------------------------------------- + +Use the `disabled_actions` option to define the name of the actions disabled +globally or for some entity. For example, to disable the `show` action for all +entities, define the following: + +```yaml +easy_admin: + disabled_actions: ['show'] + # ... +``` + +When an action is disabled, the backend no longer displays it in any of the +views. In this example, if you browse any entity listing, you'll no longer see +the `Show` link next to each item. Moreover, if you try to *hack* the URL to +access to the `Show` view of some entity, you'll see a *Forbidden Action Error* +page. + +The `disabled_actions` option can also be defined for each entity. If you want +to disable the `new` action just for the `User` entity, use this configuration: + +```yaml +easy_admin: + entities: + User: + # ... + disabled_actions: ['new'] +``` + +Reload the backend and you'll no longer see the `Add User` button in the `list` +view of the entity. Again, if you try to *hack* the URL to add a new user, +you'll see the *Forbidden Action Error* page. + +Beware that the values of the `disabled_actions` options are merged. If the +backend configuration is the following: + +```yaml +easy_admin: + disabled_actions: ['show'] + # ... + entities: + User: + # ... + disabled_actions: ['new'] +``` + +The `User` entity will have both the `new` and the `show` actions disabled. + Configure the Actions Displayed in Each View -------------------------------------------- -The actions displayed in each view can be configured globally for the entire -backend or on a per-entity basis. +Besides disabling actions, you can also configure which actions are displayed +in each view and how do they look like. Again this configuration can be done +globally or per-entity. ### Adding or Removing Actions Globally @@ -80,6 +130,14 @@ will be the following: * Actions removed by the entity: `show` * Resulting actions for this entity: `list`, `new`, `search` +> **NOTE** +> +> Beware that the `actions` option just defines if an action should be +> displayed or not, but it doesn't disable the action. In the example above, +> if you *hack* the URL and change the `action` parameter manually, you can +> access to the `edit` and `show` actions. Use the `disabled_actions` options +> to ban those actions entirely. + Customizing the Actions Displayed in Each View ---------------------------------------------- diff --git a/Resources/doc/tutorials/tips-and-tricks.md b/Resources/doc/tutorials/tips-and-tricks.md index fd68a04de8..d2a5468c20 100644 --- a/Resources/doc/tutorials/tips-and-tricks.md +++ b/Resources/doc/tutorials/tips-and-tricks.md @@ -110,10 +110,7 @@ won't be able to add, modify or remove any information: ```yaml easy_admin: - list: - actions: ['-edit', '-new'] - show: - actions: ['-delete', '-edit'] + disabled_actions: ['delete', 'edit', 'new'] ``` Unloading the Default JavaScript and Stylesheets diff --git a/Resources/views/default/layout.html.twig b/Resources/views/default/layout.html.twig index 291ee30b36..0d0362fdca 100644 --- a/Resources/views/default/layout.html.twig +++ b/Resources/views/default/layout.html.twig @@ -55,7 +55,7 @@ {% block navigation_items %} {% for item in easyadmin_config('entities') %}
  • - + {{- item.label|trans -}}
  • diff --git a/Resources/views/default/list.html.twig b/Resources/views/default/list.html.twig index 8b43023a66..022e2c9175 100644 --- a/Resources/views/default/list.html.twig +++ b/Resources/views/default/list.html.twig @@ -22,7 +22,6 @@ {% block content %} {% set _request_parameters = _request_parameters|default({})|merge({ - view: 'list', action: app.request.get('action'), entity: _entity_config.name, sortField: app.request.get('sortField', ''), @@ -61,7 +60,6 @@ {% block search_action %} {% set _action = easyadmin_get_action_for_list_view('search', _entity_config.name) %}