You are here

protected function MenuLinkContentVisibilityLinkTreeManipulator::menuLinkCheckAccess in Custom Menu Links Visibility 2.x

Checks access for one menu link instance.

Parameters

\Drupal\Core\Menu\MenuLinkInterface $instance: The menu link instance.

Return value

\Drupal\Core\Access\AccessResultInterface The access result.

Overrides DefaultMenuLinkTreeManipulators::menuLinkCheckAccess

File

src/MenuLinkContentVisibilityLinkTreeManipulator.php, line 101

Class

MenuLinkContentVisibilityLinkTreeManipulator
Defines the access control handler for the menu item.

Namespace

Drupal\menu_link_content_visibility

Code

protected function menuLinkCheckAccess(MenuLinkInterface $instance) {
  $access_result = parent::menuLinkCheckAccess($instance);

  // Break out early if a menu administrator is running the menu access check,
  // or if we are not handling a content menu link.
  $is_menu_admin = $this
    ->getRequest()->attributes
    ->get('_menu_admin') ?? FALSE;
  if ($is_menu_admin || !$instance instanceof MenuLinkContent) {
    return $access_result;
  }

  // Load the menu link entity.
  // @todo replace with $instance->getEntity() when no longer protected.
  // @see https://www.drupal.org/project/drupal/issues/2997790
  $uuid = $instance
    ->getDerivativeId();

  /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
  $entity = $this->entityRepository
    ->loadEntityByUuid('menu_link_content', $uuid);

  // Load visibility conditions - if any.
  $visibility = unserialize($entity
    ->get('visibility')->value);
  if (empty($visibility)) {
    return $access_result;
  }
  $condition_plugin_collection = new ConditionPluginCollection($this->conditionManager, $visibility);
  $conditions = [];
  $missing_context = FALSE;
  $missing_value = FALSE;
  foreach ($condition_plugin_collection as $condition_id => $condition) {

    // gtag module does something special with contexts, ignore them for now
    if (in_array($condition_id, [
      'gtag_domain',
      'gtag_language',
    ])) {
      continue;
    }
    if ($condition instanceof ContextAwarePluginInterface) {
      try {
        $contexts = $this->contextRepository
          ->getRuntimeContexts(array_values($condition
          ->getContextMapping()));
        $this->contextHandler
          ->applyContextMapping($condition, $contexts);
        $conditions[$condition_id] = $condition;
      } catch (MissingValueContextException $e) {
        $missing_value = TRUE;
      } catch (ContextException $e) {
        $missing_context = TRUE;
      }
    }
  }
  if ($missing_context) {

    // If any context is missing then we might be missing cacheable
    // metadata, and don't know based on what conditions the menu link is
    // accessible or not. Make sure the result cannot be cached.
    $access_result = AccessResult::forbidden()
      ->setCacheMaxAge(0);
  }
  elseif ($missing_value) {

    // The contexts exist but have no value. Deny access without
    // disabling caching. For example the node type condition will have a
    // missing context on any non-node route like the frontpage.
    $access_result = AccessResult::forbidden();
  }
  elseif ($this
    ->resolveConditions($conditions, 'and') === FALSE) {
    $reason = count($conditions) > 1 ? "One of the menu link visibility conditions ('%s') denied access." : "The menu link visibility condition '%s' denied access.";
    $access_result = AccessResult::forbidden(sprintf($reason, implode("', '", array_keys($conditions))));
  }
  $this
    ->mergeCacheabilityFromConditions($access_result, $conditions);

  // Ensure that access is evaluated again when the menu link changes.
  return $access_result
    ->addCacheableDependency($entity);
}