access_policy-1.0.x-dev/modules/access_policy_ui/src/Form/AccessPolicyOperationsForm.php
modules/access_policy_ui/src/Form/AccessPolicyOperationsForm.php
<?php namespace Drupal\access_policy_ui\Form; use Drupal\access_policy\AccessPolicyOperationPluginManager; use Drupal\access_policy\Entity\AccessPolicyInterface; use Drupal\access_policy\Plugin\access_policy\AccessPolicyOperation\AccessPolicyOperationInterface; use Drupal\access_policy_ui\OperationsTableUiBuilder; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\CloseDialogCommand; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\user\PermissionHandlerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a form for enabling/disabling operations. * * @package Drupal\access_policy\Form */ class AccessPolicyOperationsForm extends EntityForm { /** * The user permission handler. * * @var \Drupal\user\PermissionHandlerInterface */ protected $permissionHandler; /** * The operations table ui builder. * * @var \Drupal\access_policy_ui\OperationsTableUiBuilder */ protected $operationsTableUiBuilder; /** * The operation plugin manager. * * @var \Drupal\access_policy\AccessPolicyOperationPluginManager */ protected $operationPluginManager; /** * Constructs a new AccessPolicyPermissionsForm. * * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager service. * @param \Drupal\user\PermissionHandlerInterface $permission_handler * The user permission handler. * @param \Drupal\access_policy_ui\OperationsTableUiBuilder $ui_builder * The operations table ui builder. * @param \Drupal\access_policy\AccessPolicyOperationPluginManager $operation_plugin_manager * The operation plugin manager. */ public function __construct(ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, PermissionHandlerInterface $permission_handler, OperationsTableUiBuilder $ui_builder, AccessPolicyOperationPluginManager $operation_plugin_manager) { $this->moduleHandler = $module_handler; $this->entityTypeManager = $entity_type_manager; $this->permissionHandler = $permission_handler; $this->operationsTableUiBuilder = $ui_builder; $this->operationPluginManager = $operation_plugin_manager; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('module_handler'), $container->get('entity_type.manager'), $container->get('user.permissions'), $container->get('access_policy.operations_table_ui_builder'), $container->get('plugin.manager.access_policy_operation') ); } /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $form['help'] = [ '#type' => 'markup', '#markup' => '<p>' . $this->t('Choose which operations you want this access policy to control and how. Select permission to create a new a permission, select access rules for more fine-grained access control. Note that disabling a permission will automatically remove it from all roles.') . '</p>', ]; $form['operations'] = [ '#type' => 'table', '#header' => [ $this->t('Operations'), $this->t('Permission'), $this->t('Access rules'), $this->t('Show column'), ], ]; foreach ($this->getOperations() as $op => $operation) { $form['operations'][$op]['label'] = [ '#type' => 'markup', '#markup' => $operation->getLabel(), ]; $form['operations'][$op]['permission'] = [ '#type' => 'checkbox', '#default_value' => $this->entity->getOperationsHandler()->shouldValidatePermission($op), '#disabled' => !$operation->supportsPermissions() || $this->isPermissionDefinedElsewhere($operation, $this->entity), ]; $form['operations'][$op]['access_rules'] = [ '#type' => 'checkbox', '#default_value' => $this->entity->getOperationsHandler()->shouldValidateAccessRules($op), '#disabled' => !$operation->supportsAccessRules(), ]; $form['operations'][$op]['show_column'] = [ '#type' => 'checkbox', '#default_value' => $this->entity->getOperationsHandler()->showColumn($op), ]; } return $form; } /** * Determine whether operation supports permissions but does not define it. * * If the permission is defined elsewhere then it needs to be required * because we have no control over it. * * @param \Drupal\access_policy\Plugin\access_policy\AccessPolicyOperation\AccessPolicyOperationInterface $operation * The operation plugin. * @param \Drupal\access_policy\Entity\AccessPolicyInterface $access_policy * The access policy. * * @return bool * TRUE if the permission is defined elsewhere; FALSE otherwise. */ protected function isPermissionDefinedElsewhere(AccessPolicyOperationInterface $operation, AccessPolicyInterface $access_policy) { if ($operation->supportsPermissions() && !$operation->isPermissionOwner($access_policy)) { return TRUE; } return FALSE; } /** * {@inheritdoc} */ protected function actions(array $form, FormStateInterface $form_state) { $actions['actions'] = ['#type' => 'actions']; $actions['actions']['submit'] = [ '#type' => 'submit', '#value' => $this->t('Save operations'), '#button_type' => 'primary', '#submit' => ['::submitForm', '::save'], '#ajax' => [ 'callback' => [$this, 'ajaxSubmitCallback'], 'disable-refocus' => TRUE, ], ]; return $actions; } /** * Get the operations. * * @return \Drupal\access_policy\Plugin\access_policy\AccessPolicyOperation\AccessPolicyOperationInterface[] * Array of operations plugins. */ public function getOperations() { $plugins = $this->operationPluginManager->getApplicable($this->entity->getTargetEntityType()); $options = []; foreach ($plugins as $id => $plugin) { $options[$id] = $plugin; } return $options; } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { $entity = $this->entity; $values = $form_state->getValues(); $operations = $form_state->getValue('operations'); $handlers = $this->filterInvalidAccessRuleOperations($entity->getHandlers('access_rule'), $operations); $entity->setHandlers('access_rule', $handlers); $entity->setOperations($values['operations']); $status = $entity->save(); $this->removeInvalidPermissions(); return $status; } /** * Remove any invalid permissions on all user roles. */ protected function removeInvalidPermissions() { $permission_definitions = $this->permissionHandler->getPermissions(); $roles = $this->entityTypeManager->getStorage('user_role')->loadMultiple(); foreach ($roles as $role) { $permissions = $role->getPermissions(); $valid_permissions = array_intersect($permissions, array_keys($permission_definitions)); $invalid_permissions = array_diff($permissions, $valid_permissions); if (!empty($invalid_permissions)) { user_role_revoke_permissions($role->id(), $invalid_permissions); } } } /** * Remove any invalid access rule operation overrides. * * Remove any operations on access rules that are no longer supported. If no * access rules are supported then it will remove all the access rules. * * @return array * Array of handlers with operations filtered. */ protected function filterInvalidAccessRuleOperations(array $handlers, array $operations) { $allowed_operations = []; foreach ($operations as $op => $operation) { if ($operation['access_rules']) { $allowed_operations[] = $op; } } // If access rule are not supported then we should remove all the access // rules. if (empty($allowed_operations)) { return []; } foreach ($handlers as &$handler) { // If operations are defined then remove that operation from the array // If applicable. if (isset($handler['settings']['operations'])) { $handler['settings']['operations'] = array_values(array_intersect($allowed_operations, $handler['settings']['operations'])); } } return $handlers; } /** * Ajax callback to close the modal and update the selected rules. * * @param array $form * The form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current form state. * * @return \Drupal\Core\Ajax\AjaxResponse * An ajax response object. */ public function ajaxSubmitCallback(array $form, FormStateInterface $form_state) { $response = new AjaxResponse(); if (!$form_state::hasAnyErrors()) { // Make sure that the access policy is up-to-date before rebuilding. $access_policy = $this->entityTypeManager->getStorage('access_policy')->load($this->entity->id()); $role_operations = $this->operationsTableUiBuilder->build($access_policy); $response->addCommand(new CloseDialogCommand()); $response->addCommand(new HtmlCommand('#role-operations', $role_operations)); } return $response; } }