You are here

class Menu in Workbench Access 8

Defines a hierarchy based on a Menu.

Plugin annotation


@AccessControlHierarchy(
  id = "menu",
  module = "menu_ui",
  entity = "menu_link_content",
  label = @Translation("Menu"),
  description = @Translation("Uses a menu as an access control hierarchy.")
)

Hierarchy

Expanded class hierarchy of Menu

2 string references to 'Menu'
workbench_access.access_scheme.menu.yml in tests/modules/workbench_access_test/config/install/workbench_access.access_scheme.menu.yml
tests/modules/workbench_access_test/config/install/workbench_access.access_scheme.menu.yml
workbench_access.schema.yml in config/schema/workbench_access.schema.yml
config/schema/workbench_access.schema.yml

File

src/Plugin/AccessControlHierarchy/Menu.php, line 29

Namespace

Drupal\workbench_access\Plugin\AccessControlHierarchy
View source
class Menu extends AccessControlHierarchyBase {

  /**
   * Menu link tree service.
   *
   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
   */
  protected $menuTree;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {

    /** @var self $instance */
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    return $instance
      ->setMenuTree($container
      ->get('menu.link_tree'));
  }

  /**
   * Sets menu tree service.
   *
   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menuTree
   *   Menu tree service.
   *
   * @return $this
   */
  public function setMenuTree(MenuLinkTreeInterface $menuTree) {
    $this->menuTree = $menuTree;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getTree() {
    if (!isset($this->tree)) {
      $tree = [];
      $menuStorage = $this->entityTypeManager
        ->getStorage('menu');
      foreach ($menuStorage
        ->loadMultiple($this->configuration['menus']) as $menu_id => $menu) {
        $tree[$menu_id][$menu_id] = [
          'label' => $menu
            ->label(),
          'depth' => 0,
          'parents' => [],
          'weight' => 0,
          'description' => $menu
            ->label(),
          'path' => $menu
            ->toUrl('edit-form')
            ->toString(),
        ];
        $params = new MenuTreeParameters();
        $data = $this->menuTree
          ->load($menu_id, $params);
        $this->tree = $this
          ->buildTree($menu_id, $data, $tree);
      }
    }
    return $this->tree;
  }

  /**
   * Traverses the menu link tree and builds parentage arrays.
   *
   * Note: this method is necessary because Menu does not auto-load parents.
   *
   * @param string $id
   *   The root id of the section tree.
   * @param array $data
   *   An array of menu tree or subtree data.
   * @param array &$tree
   *   The computed tree array to return.
   *
   * @return array
   *   The compiled tree data.
   */
  protected function buildTree($id, array $data, array &$tree) {
    foreach ($data as $link_id => $link) {
      $tree[$id][$link_id] = [
        'id' => $link_id,
        'label' => $link->link
          ->getTitle(),
        'depth' => $link->depth,
        'parents' => [],
        'weight' => $link->link
          ->getWeight(),
        'description' => $link->link
          ->getDescription(),
        'path' => $link->link
          ->getUrlObject()
          ->toString(),
      ];

      // Get the parents.
      if ($parent = $link->link
        ->getParent()) {
        $tree[$id][$link_id]['parents'] = array_unique(array_merge($tree[$id][$link_id]['parents'], [
          $parent,
        ]));
        $tree[$id][$link_id]['parents'] = array_unique(array_merge($tree[$id][$link_id]['parents'], $tree[$id][$parent]['parents']));
      }
      else {
        $tree[$id][$link_id]['parents'] = [
          $id,
        ];
      }
      if (isset($link->subtree)) {

        // The elements of the 'subtree' sub-array are not sorted by weight.
        uasort($link->subtree, [
          $this,
          'sortTree',
        ]);
        $this
          ->buildTree($id, $link->subtree, $tree);
      }
    }
    return $tree;
  }

  /**
   * Sorts the menu tree by weight.
   */
  protected function sortTree($a, $b) {
    if ($a->link
      ->getWeight() == $b->link
      ->getWeight()) {
      return $a->link
        ->getTitle() > $b->link
        ->getTitle() ? 1 : 0;
    }
    return $a->link
      ->getWeight() > $b->link
      ->getWeight() ? 1 : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function alterForm(AccessSchemeInterface $scheme, array &$form, FormStateInterface &$form_state, ContentEntityInterface $entity) {
    if (!isset($form['menu'])) {
      return;
    }
    $element =& $form['menu'];
    $menu_check = [];
    $user_sections = $this->userSectionStorage
      ->getUserSections($scheme);
    foreach ($element['link']['menu_parent']['#options'] as $id => $data) {

      // The menu value here prepends the menu name. Remove that.
      $parts = explode(':', $id);
      $menu = array_shift($parts);

      // If the second element is empty, this is the root element which is
      // checked by menu name.
      if (empty($parts)) {
        $sections = [
          $menu,
        ];
      }
      else {
        $sections = [
          implode(':', $parts),
        ];
      }
      $menu_parent = $menu . ':';

      // Remove unusable elements, except the existing parent.
      // Do not remove top-level menus, we check those separately.
      if (!empty($element['link']['menu_parent']['#default_value']) && $id != $menu_parent && empty(WorkbenchAccessManager::checkTree($scheme, $sections, $user_sections))) {
        unset($element['link']['menu_parent']['#options'][$id]);
        if ($id == $element['link']['menu_parent']['#default_value']) {
          unset($element['link']['menu_parent']['#default_value']);
        }
      }

      // Check for the root menu item.
      if (!isset($menu_check[$menu]) && isset($element['link']['menu_parent']['#options'][$menu . ':'])) {
        if (empty(WorkbenchAccessManager::checkTree($scheme, [
          $menu,
        ], $user_sections))) {
          unset($element['link']['menu_parent']['#options'][$menu . ':']);
        }
        $menu_check[$menu] = TRUE;
      }
    }

    // As long as there are menu options available, check that the default value
    // is still in the options, if not then default to the root item.
    if (!empty($element['link']['menu_parent']['#options']) && empty($element['link']['menu_parent']['#default_value'])) {
      $element['link']['menu_parent']['#default_value'] = current(array_keys($element['link']['menu_parent']['#options']));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getEntityValues(EntityInterface $entity) {
    $values = [];
    $defaults = menu_ui_get_menu_link_defaults($entity);
    if (!empty($defaults['id'])) {
      $values = [
        $defaults['id'],
      ];
    }
    return $values;
  }

  /**
   * {@inheritdoc}
   */
  public function disallowedOptions($field) {

    // On the menu form, we never remove an existing parent item, so there is
    // no concept of a disallowed option.
    return [];
  }

  /**
   * {@inheritdoc}
   *
   * @TODO: Refactor
   */
  public function getViewsJoin($entity_type, $key, $alias = NULL) {
    if ($entity_type == 'user') {
      $configuration['menu'] = [
        'table' => 'section_association__user_id',
        'field' => 'user_id_target_id',
        'left_table' => 'users',
        'left_field' => $key,
        'operator' => '=',
        'table_alias' => 'section_association__user_id',
        'real_field' => 'entity_id',
      ];
      return $configuration;
    }
    else {
      $configuration['menu'] = [
        'table' => 'menu_tree',
        'field' => 'route_param_key',
        'left_table' => 'node',
        'left_field' => $key,
        'left_query' => "CONCAT('node=', {$alias}.{$key})",
        'operator' => '=',
        'table_alias' => 'menu_tree',
        'real_field' => 'id',
      ];
    }
    return $configuration;
  }

  /**
   * {@inheritdoc}
   */
  public function viewsData(array &$data, AccessSchemeInterface $scheme) {
    $data['node']['workbench_access_section'] = [
      'title' => t('Workbench Section @name', [
        '@name' => $scheme
          ->label(),
      ]),
      'help' => t('The sections to which this content belongs in the @name scheme.', [
        '@name' => $scheme
          ->label(),
      ]),
      'field' => [
        'scheme' => $scheme
          ->id(),
        'id' => 'workbench_access_section',
      ],
      'filter' => [
        'scheme' => $scheme
          ->id(),
        'field' => 'nid',
        'id' => 'workbench_access_section',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function applies($entity_type_id, $bundle) {
    return $entity_type_id === 'node' && in_array($bundle, $this->configuration['bundles']);
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $this->configuration['menus'] = array_values(array_filter($form_state
      ->getValue('menus')));
    $this->configuration['bundles'] = array_values(array_filter($form_state
      ->getValue('bundles')));
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form['menus'] = [
      '#type' => 'checkboxes',
      '#title' => $this
        ->t('Menus'),
      '#description' => $this
        ->t('Select the menus to use.'),
      '#options' => array_map(function (MenuInterface $menu) {
        return $menu
          ->label();
      }, $this->entityTypeManager
        ->getStorage('menu')
        ->loadMultiple()),
      '#default_value' => $this->configuration['menus'],
    ];
    $form['bundles'] = [
      '#type' => 'checkboxes',
      '#title' => $this
        ->t('Content types'),
      '#description' => $this
        ->t('Select the content types to enable access control on.'),
      '#options' => array_map(function (NodeTypeInterface $node_type) {
        return $node_type
          ->label();
      }, $this->entityTypeManager
        ->getStorage('node_type')
        ->loadMultiple()),
      '#default_value' => $this->configuration['bundles'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'menus' => [],
      'bundles' => [],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    $entity_type_map = [
      'menu' => 'menus',
      'node_type' => 'bundles',
    ];
    $dependencies = [];
    foreach ($entity_type_map as $entity_type => $configuration_key) {
      $dependencies = array_merge($dependencies, $this->entityTypeManager
        ->getStorage($entity_type)
        ->loadMultiple($this->configuration[$configuration_key]));
    }
    return array_reduce($dependencies, function (array $carry, ConfigEntityInterface $entity) {
      $carry[$entity
        ->getConfigDependencyKey()][] = $entity
        ->getConfigDependencyName();
      return $carry;
    }, []);
  }

  /**
   * {@inheritdoc}
   */
  public function onDependencyRemoval(array $dependencies) {
    $bundles = array_diff($this->configuration['bundles'], array_reduce($dependencies['config'], function (array $carry, $item) {
      if (!$item instanceof NodeTypeInterface) {
        return $carry;
      }
      $carry[] = $item
        ->id();
      return $carry;
    }, []));
    $menus = array_diff($this->configuration['menus'], array_reduce($dependencies['config'], function (array $carry, $item) {
      if (!$item instanceof NodeTypeInterface) {
        return $carry;
      }
      $carry[] = $item
        ->id();
      return $carry;
    }, []));
    $changed = $menus != $this->configuration['menus'] || $bundles != $this->configuration['bundles'];
    $this->configuration['menus'] = $menus;
    $this->configuration['bundles'] = $bundles;
    return $changed;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AccessControlHierarchyBase::$config protected property Config for module.
AccessControlHierarchyBase::$configFactory protected property A configuration factory object to store configuration.
AccessControlHierarchyBase::$entityTypeManager protected property Entity type manager.
AccessControlHierarchyBase::$tree protected property The access tree array.
AccessControlHierarchyBase::$userSectionStorage protected property User section storage.
AccessControlHierarchyBase::addWhere public function Adds a where clause to a view when using a section filter. Overrides AccessControlHierarchyInterface::addWhere
AccessControlHierarchyBase::checkEntityAccess public function Responds to request for node access. Overrides AccessControlHierarchyInterface::checkEntityAccess
AccessControlHierarchyBase::entityType public function Gets the entity type id controlled by the scheme.
AccessControlHierarchyBase::getApplicableFields public function @inheritdoc Overrides AccessControlHierarchyInterface::getApplicableFields 1
AccessControlHierarchyBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
AccessControlHierarchyBase::id public function Returns the id for a hierarchy. Overrides AccessControlHierarchyInterface::id
AccessControlHierarchyBase::label public function Returns the label for a hierarchy. Overrides AccessControlHierarchyInterface::label
AccessControlHierarchyBase::load public function Loads a hierarchy definition for a single item in the tree. Overrides AccessControlHierarchyInterface::load
AccessControlHierarchyBase::massageFormValues public function Massage form values as appropriate during entity submit. Overrides AccessControlHierarchyInterface::massageFormValues 1
AccessControlHierarchyBase::resetTree public function Resets the internal cache of the tree. Overrides AccessControlHierarchyInterface::resetTree
AccessControlHierarchyBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration
AccessControlHierarchyBase::submitEntity public static function Responds to the submission of an entity form. Overrides AccessControlHierarchyInterface::submitEntity
AccessControlHierarchyBase::validateConfigurationForm public function Form validation handler. Overrides PluginFormInterface::validateConfigurationForm 1
AccessControlHierarchyBase::__construct public function Constructs a new AccessControlHierarchyBase object. Overrides PluginBase::__construct 1
Menu::$menuTree protected property Menu link tree service.
Menu::alterForm public function Alters the selection options provided for an access control field. Overrides AccessControlHierarchyBase::alterForm
Menu::applies public function Check if this access scheme applies to the given entity. Overrides AccessControlHierarchyInterface::applies
Menu::buildConfigurationForm public function Form constructor. Overrides AccessControlHierarchyBase::buildConfigurationForm
Menu::buildTree protected function Traverses the menu link tree and builds parentage arrays.
Menu::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides AccessControlHierarchyBase::calculateDependencies
Menu::create public static function Creates an instance of the plugin. Overrides AccessControlHierarchyBase::create
Menu::defaultConfiguration public function Gets default configuration for this plugin. Overrides AccessControlHierarchyBase::defaultConfiguration
Menu::disallowedOptions public function Gets any options that are set but cannot be changed by the editor. Overrides AccessControlHierarchyBase::disallowedOptions
Menu::getEntityValues public function Retrieves the access control values from an entity. Overrides AccessControlHierarchyInterface::getEntityValues
Menu::getTree public function Gets the entire hierarchy tree. Overrides AccessControlHierarchyBase::getTree
Menu::getViewsJoin public function @TODO: Refactor Overrides AccessControlHierarchyBase::getViewsJoin
Menu::onDependencyRemoval public function Informs the plugin that a dependency of the scheme will be deleted. Overrides AccessControlHierarchyBase::onDependencyRemoval
Menu::setMenuTree public function Sets menu tree service.
Menu::sortTree protected function Sorts the menu tree by weight.
Menu::submitConfigurationForm public function Form submission handler. Overrides AccessControlHierarchyBase::submitConfigurationForm
Menu::viewsData public function Adds views data for the plugin. Overrides AccessControlHierarchyBase::viewsData
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginWithFormsTrait::getFormClass public function
PluginWithFormsTrait::hasFormClass public function
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.