You are here

workbench_access.module in Workbench Access 8

Same filename and directory in other branches
  1. 7 workbench_access.module

File

workbench_access.module
View source
<?php

/**
 * @file
 * Contains workbench_access.module.
 */
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\workbench_access\Entity\AccessSchemeInterface;
use Drupal\workbench_access\FormAlterHelper;

/**
 * Implements hook_help().
 */
function workbench_access_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {

    // Main module help for the workbench_access module.
    case 'help.page.workbench_access':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Hierarchical access control module.') . '</p>';
      return $output;
    default:
  }
}

/**
 * Implements hook_form_alter().
 *
 * Restricts selection options for the node form.
 */
function workbench_access_form_alter(&$form, FormStateInterface $form_state) {
  $form_object = $form_state
    ->getFormObject();
  if (!$form_object instanceof EntityForm) {
    return;
  }
  $entity = $form_object
    ->getEntity();
  if (!$entity instanceof ContentEntityInterface) {
    return;
  }
  \Drupal::classResolver()
    ->getInstanceFromDefinition(FormAlterHelper::class)
    ->alterForm($form, $form, $form_state, $entity);
}

/**
 * Implements hook_entity_access().
 */
function workbench_access_entity_access(EntityInterface $entity, $op, AccountInterface $account) {

  // Return net result of all enabled access schemes. If one scheme allows
  // access, then it is granted.
  // We don't care about the View operation right now.
  if ($op === 'view' || $op === 'view label' || $account
    ->hasPermission('bypass workbench access')) {

    // Return early.
    return AccessResult::neutral();
  }
  return array_reduce(\Drupal::entityTypeManager()
    ->getStorage('access_scheme')
    ->loadMultiple(), function (AccessResult $carry, AccessSchemeInterface $scheme) use ($entity, $op, $account) {
    $carry
      ->addCacheableDependency($scheme)
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
    return $carry
      ->orIf($scheme
      ->getAccessScheme()
      ->checkEntityAccess($scheme, $entity, $op, $account));
  }, AccessResult::neutral());
}

/**
 * Implements hook_entity_delete().
 */
function workbench_access_entity_delete(EntityInterface $entity) {
  foreach (\Drupal::entityTypeManager()
    ->getStorage('access_scheme')
    ->loadMultiple() as $scheme) {
    $entity_type = $scheme
      ->getAccessScheme()
      ->entityType();
    if ($entity_type === $entity
      ->getEntityTypeId()) {

      // Delete all associated storage.
      $section_storage = \Drupal::entityTypeManager()
        ->getStorage('section_association');
      $sections = $section_storage
        ->loadByProperties([
        'access_scheme' => $scheme
          ->id(),
        'section_id' => $entity
          ->id(),
      ]);
      $section_storage
        ->delete($sections);
      \Drupal::service('workbench_access.user_section_storage')
        ->resetCache($scheme);
    }
  }
}

/**
 * Implements hook_node_create_access().
 *
 * @link https://www.drupal.org/node/2348203
 */
function workbench_access_entity_create_access(AccountInterface $account, $context, $entity_bundle) {

  // @todo move this to the access schemes.
  $return = AccessResult::neutral();

  // User can bypass.
  if ($account
    ->hasPermission('bypass workbench access')) {
    return $return
      ->cachePerPermissions();
  }

  // Check that access control applies to this entity type.
  $entity_type_id = $context['entity_type_id'];
  $schemes = array_filter(\Drupal::entityTypeManager()
    ->getStorage('access_scheme')
    ->loadMultiple(), function (AccessSchemeInterface $scheme) use ($entity_type_id, $entity_bundle, $return) {
    $return
      ->addCacheableDependency($scheme);
    return $scheme
      ->getAccessScheme()
      ->applies($entity_type_id, $entity_bundle);
  });
  if (!$schemes) {
    return $return
      ->addCacheTags([
      'access_scheme_list',
    ]);
  }

  // Check that the user is able to assign content to a section.
  $user_section_storage = \Drupal::service('workbench_access.user_section_storage');
  $forbidden = AccessResult::forbidden();
  $invalid_schemes = array_reduce($schemes, function ($carry, AccessSchemeInterface $scheme) use ($user_section_storage, $account, $forbidden) {
    $sections = $user_section_storage
      ->getUserSections($scheme, $account);
    if (!$sections) {
      $carry[] = $scheme
        ->label();
    }
    $forbidden
      ->addCacheableDependency($scheme);
    return $carry;
  }, []);
  if ($invalid_schemes) {
    return $forbidden
      ->setReason(sprintf('User has no active sections for the following access scheme(s): %s', implode(', ', $invalid_schemes)));
  }
  return $return;
}

/**
 * Implements hook_views_data_alter().
 */
function workbench_access_views_data_alter(array &$data) {
  $scheme_storage = \Drupal::entityTypeManager()
    ->getStorage('access_scheme');
  if ($schemes = $scheme_storage
    ->loadMultiple()) {

    /** @var \Drupal\workbench_access\Entity\AccessSchemeInterface $scheme */
    foreach ($schemes as $id => $scheme) {
      $scheme
        ->getAccessScheme()
        ->viewsData($data, $scheme);
      $data['users']['workbench_access_section__' . $scheme
        ->id()] = [
        'title' => t('Workbench Section: @name', [
          '@name' => $scheme
            ->label(),
        ]),
        'help' => t('The sections to which this user belongs for the @name scheme.', [
          '@name' => $scheme
            ->label(),
        ]),
        'field' => [
          'id' => 'workbench_access_user_section',
          'scheme' => $scheme
            ->id(),
        ],
        'filter' => [
          'field' => 'uid',
          'scheme' => $scheme
            ->id(),
          'id' => 'workbench_access_section',
        ],
      ];
    }
  }

  // Legacy support.
  // @todo Remove in 8.x-2.x.
  if ($default = \Drupal::state()
    ->get('workbench_access_upgraded_scheme_id')) {
    if ($scheme = $scheme_storage
      ->load($default)) {
      $data['users']['workbench_access_section'] = [
        'title' => t('Workbench Section: @name (legacy)', [
          '@name' => $scheme
            ->label(),
        ]),
        'help' => t('The sections to which this user belongs for the @name scheme.', [
          '@name' => $scheme
            ->label(),
        ]),
        'field' => [
          'id' => 'workbench_access_user_section',
          'scheme' => $scheme
            ->id(),
        ],
        'filter' => [
          'field' => 'uid',
          'scheme' => $scheme
            ->id(),
          'id' => 'workbench_access_section',
        ],
      ];
    }
    if (isset($data['node']['workbench_access_section__default'])) {
      $data['node']['workbench_access_section'] = $data['node']['workbench_access_section__default'];
      $data['node']['workbench_access_section']['title'] = t('Workbench Section: @name (legacy)', [
        '@name' => $scheme
          ->label(),
      ]);
    }
  }
}

/**
 * Implements hook_element_info_alter().
 */
function workbench_access_element_info_alter(array &$info) {
  if (isset($info['inline_entity_form'])) {
    $info['inline_entity_form']['#process'][] = 'workbench_access_process_inline_entity_form';
  }
}

/**
 * Process callback for inline entity form.
 */
function workbench_access_process_inline_entity_form(array &$element, FormStateInterface $form_state, &$complete_form) {
  $entity = $element['#entity'];
  if (!$entity instanceof ContentEntityInterface) {
    return $element;
  }
  return \Drupal::classResolver()
    ->getInstanceFromDefinition(FormAlterHelper::class)
    ->alterForm($element, $complete_form, $form_state, $entity);
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function workbench_access_user_delete($account) {
  $uid = $account
    ->id();
  $storage_handler = \Drupal::entityTypeManager()
    ->getStorage('section_association');

  // Retrieve all the section associations referencing this user.
  $section_user_associations = $storage_handler
    ->loadByProperties([
    'user_id' => $uid,
  ]);
  foreach ($section_user_associations as $section_association) {

    // For each retrived association, remove this user.
    if ($user_ids = $section_association
      ->getCurrentUserIds()) {
      $remaining_user_ids = array_diff($user_ids, [
        $uid,
      ]);
      $section_association
        ->set('user_id', $remaining_user_ids);
      $section_association
        ->save();
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function workbench_access_user_role_delete($role) {
  $role_section_storage = \Drupal::service('workbench_access.role_section_storage');
  $scheme_storage = \Drupal::entityTypeManager()
    ->getStorage('access_scheme');
  foreach ($scheme_storage
    ->loadMultiple() as $scheme) {
    $role_section_storage
      ->deleteRoleSections($scheme, $role
      ->id());
  }
}

/**
 * Implements hook_token_info().
 */
function workbench_access_token_info() {
  return \Drupal::service('workbench_access.tokens')
    ->getTokenInfo();
}

/**
 * Implements hook_tokens().
 */
function workbench_access_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  return \Drupal::service('workbench_access.tokens')
    ->getTokens($type, $tokens, $data, $options, $bubbleable_metadata);
}