association-1.0.0-alpha2/src/Plugin/Block/AssociationBlock.php
src/Plugin/Block/AssociationBlock.php
<?php namespace Drupal\association\Plugin\Block; use Drupal\association\AssociationNegotiatorInterface; use Drupal\association\Entity\AssociationInterface; use Drupal\association_menu\AssociationMenuBuilderInterface; use Drupal\association_menu\AssociationMenuStorageInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Block\Attribute\Block; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** * Provides a block, based on the active association context. */ #[Block( id: 'association_block', admin_label: new TranslatableMarkup('Association display block'), category: new TranslatableMarkup("Entity association"), context_definitions: [ 'association' => new ContextDefinition("entity:association", new TranslatableMarkup('Association'), FALSE), 'entity' => new ContextDefinition("entity", new TranslatableMarkup('Entity'), FALSE), ], )] class AssociationBlock extends AssociationBlockBase implements ContainerFactoryPluginInterface { /** * The entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * Retrieves the bundle information for various entity types. * * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface */ protected $entityBundleInfo; /** * The entity field manager. * * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ protected $entityFieldManager; /** * The entity display repository manager. * * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface */ protected $entityDisplayRepo; /** * Association menu item storage manager. * * @var \Drupal\association_menu\AssociationMenuStorageInterface */ protected $menuStorage; /** * Association menu builder, if available. * * @var \Drupal\association_menu\AssociationMenuBuilderInterface */ protected $menuBuilder; /** * The menu storage data of the association menu of this block. * * @var array */ protected $menuData; /** * Create an instance of the AssociationBlock plugin. * * @param array $configuration * The block configuration. * @param string $plugin_id * The unique identifier for this plugin. * @param mixed $plugin_definition * The plugin definition from discovery or a deriver. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info * Retrieves the bundle information for various entity types. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager * The entity field manager. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository * The entity display repository manager. * @param \Drupal\association\AssociationNegotiatorInterface $association_negotiator * The negotiator to determine the active association context of the block. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_bundle_info, EntityFieldManagerInterface $entity_field_manager, EntityDisplayRepositoryInterface $entity_display_repository, AssociationNegotiatorInterface $association_negotiator) { parent::__construct($configuration, $plugin_id, $plugin_definition, $association_negotiator); $this->entityTypeManager = $entity_type_manager; $this->entityBundleInfo = $entity_bundle_info; $this->entityFieldManager = $entity_field_manager; $this->entityDisplayRepo = $entity_display_repository; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { $instance = new static( $configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'), $container->get('entity_type.bundle.info'), $container->get('entity_field.manager'), $container->get('entity_display.repository'), $container->get('association.negotiator') ); // Optionally add association menu options when the menu module is enabled. $moduleHandler = $container->get('module_handler'); if ($moduleHandler->moduleExists('association_menu')) { try { // Having these services present allows menu options to appear, and // the association specific menus to become available. $instance->setAssociationMenuStorage($container->get('association_menu.storage')); $instance->setAssociationMenuBuilder($container->get('association_menu.builder')); } catch (ServiceNotFoundException $e) { // Optional menu services can fail safety. } } return $instance; } /** * Sets the association menu item storage manager. * * Having this service available indicates that menus are available to the * block and unlocks the menu configurations. * * @param \Drupal\association_menu\AssociationMenuStorageInterface $menu_storage * The association menu item storage manager. */ public function setAssociationMenuStorage(AssociationMenuStorageInterface $menu_storage) { $this->menuStorage = $menu_storage; } /** * Sets the association menu builder service. * * Having this service available indicates that menus are available to the * block and unlocks the menu display. * * @param \Drupal\association_menu\AssociationMenuBuilderInterface $menu_builder * The association menu builder to use with this block. */ public function setAssociationMenuBuilder(AssociationMenuBuilderInterface $menu_builder) { $this->menuBuilder = $menu_builder; } /** * {@inheritdoc} */ protected function getAssociationContextNames(): array { return [ 'layout_builder' => 'entity', 'global' => 'association', ]; } /** * {@inheritdoc} */ public function getCacheTags() { $cacheTags = parent::getCacheTags(); // Apply association tags from the entity context. if ($association = $this->getAssociation()) { $viewMode = $this->configuration['view_mode'] ?? 'default'; $display = $this->entityDisplayRepo->getViewDisplay('association', $association->bundle(), $viewMode); if ($display) { $cacheTags = Cache::mergeTags($cacheTags, $display->getCacheTags()); } // Apply menu cache tags if menus are displayed. $menu = $this->getMenu($association); if (!empty($menu['cache'])) { $cacheTags = Cache::mergeTags($cacheTags, $menu['cache']->getCacheTags()); } } return $cacheTags; } /** * {@inheritdoc} */ public function getCacheContexts() { $cacheContexts = parent::getCacheContexts(); if ($association = $this->getAssociation()) { $viewMode = $this->configuration['view_mode'] ?? 'default'; $display = $this->entityDisplayRepo->getViewDisplay('association', $association->bundle(), $viewMode); if ($display) { $cacheContexts = Cache::mergeContexts($cacheContexts, $display->getCacheContexts()); } // Apply menu cache contexts if menus are displayed. $menu = $this->getMenu($association); if (!empty($menu['cache'])) { $cacheContexts = Cache::mergeContexts($cacheContexts, $menu['cache']->getCacheContexts()); } } return $cacheContexts; } /** * {@inheritdoc} */ public function blockAccess(AccountInterface $account) { $bundles = $this->configuration['bundles'] ?? []; if ($assoc = $this->getAssociation()) { // Treat empty bundles configuration as all bundles, or if bundle is in // the list of allowed bundles check for association access to view. return (!$bundles || in_array($assoc->bundle(), $bundles)) ? $assoc->access('view', $account, TRUE) : AccessResult::forbidden()->addCacheableDependency($assoc); } // No association value available, block is not available. return AccessResult::forbidden(); } /** * {@inheritdoc} */ public function defaultConfiguration() { return [ 'view_mode' => 'block', 'bundles' => [], 'menu_display' => 'none', 'menu_display_field' => NULL, ]; } /** * {@inheritdoc} */ public function build() { $content = []; if ($assoc = $this->getAssociation()) { $viewMode = $this->configuration['view_mode'] ?? 'default'; $content['association'] = $this->entityTypeManager ->getViewBuilder('association') ->view($assoc, $viewMode); // Optionally include the association menu, if association_menu services // are available and the block has been configured to display the menu. $menu = $this->getMenu($assoc); if ($menu && $this->menuBuilder) { $content['menu'] = $this->menuBuilder->buildMenu($menu); } } return $content; } /** * {@inheritdoc} */ public function blockForm($form, FormStateInterface $form_state) { $form['view_mode'] = [ '#type' => 'radios', '#title' => $this->t('View mode'), '#options' => $this->entityDisplayRepo->getViewModeOptions('association'), '#default_value' => $this->configuration['view_mode'] ?? 'default', '#description' => $this->t('View mode to render the Entity Association as.'), ]; $bundles = []; foreach ($this->entityBundleInfo->getBundleInfo('association') as $bundle => $bundleInfo) { $bundles[$bundle] = $bundleInfo['label']; } $form['bundles'] = [ '#type' => 'checkboxes', '#title' => $this->t('Allowed association types'), '#options' => $bundles, '#default_value' => $this->configuration['bundles'] ?? [], '#description' => $this->t('Leaving this configuration blank is the same as allowing any types.'), ]; // Optional menu display settings. Only available if association_menu // module is currently enabled. $form['menu_display'] = [ '#type' => 'select', '#title' => $this->t('Display association menu'), '#options' => [ 'none' => $this->t('Hide menu'), 'visible' => $this->t('Show menu'), 'field' => $this->t('By field value'), ], '#default_value' => $this->configuration['menu_display'] ?? 'none', ]; // Hide these options if the association menu module is not available. if (!$this->menuStorage || !$this->menuBuilder) { $form['menu_display']['#access'] = FALSE; $form['menu_display']['#default_value'] = 'none'; } else { $fieldOptions = []; $fieldDefs = $this->entityFieldManager->getFieldStorageDefinitions('association'); foreach ($fieldDefs as $fieldName => $definition) { $fieldOptions[$fieldName] = preg_replace('#^association\.#', '', $definition->getLabel()); } // If a field is used to determine menu visibility, allow the admin to // pick which field is checked. $form['menu_display_field'] = [ '#type' => 'select', '#title' => $this->t('Field which determines menu visibility'), '#options' => $fieldOptions, '#states' => [ 'visible' => [ 'select[name="settings[menu_display]"]' => ['value' => 'field'], ], 'required' => [ 'select[name="settings[menu_display]"]' => ['value' => 'field'], ], ], '#description' => $this->t('Display menu when this field is TRUE.'), ]; } return $form; } /** * {@inheritdoc} */ public function blockSubmit($form, FormStateInterface $form_state) { $config['view_mode'] = $form_state->getValue('view_mode') ?: 'default'; $config['bundles'] = array_filter($form_state->getValue('bundles')); $config['menu_display'] = $form_state->getValue('menu_display'); $config['menu_display_field'] = $form_state->getValue('menu_display_field'); $this->setConfiguration($config); } /** * Get an association menu if configured to appear and menu is available. * * Method checks for block configurations to determine if a menu should be * included in the block display, and if the menu services are available to * build and display the menu. * * @param \Drupal\association\Entity\AssociationInterface $association * The entity association to fetch the menu for. * * @return array|null * If menu options are configured, and menu storage manager is available, * returns an array of menu data for the association. NULL returned if * menu is configured not to appear, or is not available. */ protected function getMenu(AssociationInterface $association): ?array { if (!$this->menuStorage) { return NULL; } $menuDisplay = $this->configuration['menu_display'] ?? 'none'; if ('none' === $menuDisplay) { return NULL; } // If the menu display is based on a field value, check the field. If the // field display parameter is missing, or points to a field that doesn't // exist for the association, these all count "no menu". if ('field' === $menuDisplay) { $menuField = $this->configuration['menu_display_field'] ?? ''; // The field is missing, or computes to empty, don't show the menu. if (!$menuField || !$association->hasField($menuField) || !$association->get($menuField)->value) { return NULL; } } if (!isset($this->menuData)) { $this->menuData = $this->menuStorage->getMenu($association); } return $this->menuData; } }