/** * Build render arrays for each of the regions. * * @param array $regions * The render array representing regions. * @param array $contexts * The array of context objects. * * @return array * An associative array, keyed by region ID, containing the render arrays * representing the content of each region. */ protected function buildRegions(array $regions, array $contexts) { $build = []; foreach ($regions as $region => $blocks) { if (!$blocks) { continue; } $region_name = Html::getClass("block-region-{$region}"); $build[$region]['#prefix'] = '<div class="' . $region_name . '">'; $build[$region]['#suffix'] = '</div>'; /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ $weight = 0; foreach ($blocks as $block_id => $block) { if ($block instanceof ContextAwarePluginInterface) { $this->contextHandler->applyContextMapping($block, $contexts); } if ($block->access($this->account)) { $block_render_array = ['#theme' => 'block', '#attributes' => [], '#contextual_links' => [], '#weight' => $weight++, '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId()]; // Build the block and bubble its attributes up if possible. This // allows modules like Quickedit to function. // See \Drupal\block\BlockViewBuilder::preRender() for reference. $content = $block->build(); if ($content !== NULL && !Element::isEmpty($content)) { foreach (['#attributes', '#contextual_links'] as $property) { if (isset($content[$property])) { $block_render_array[$property] += $content[$property]; unset($content[$property]); } } } $block_render_array['content'] = $content; $build[$region][$block_id] = $block_render_array; } } } return $build; }
/** * */ protected function applyContexts(ConditionPluginCollection &$conditions, $logic) { $have_1_testable_condition = FALSE; foreach ($conditions as $id => $condition) { if ($condition instanceof ContextAwarePluginInterface) { try { $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping())); $this->contextHandler->applyContextMapping($condition, $contexts); $have_1_testable_condition = TRUE; } catch (ContextException $e) { if ($logic == 'and') { // Logic is all and found condition with contextException. return FALSE; } $conditions->removeInstanceId($id); } } else { $have_1_testable_condition = TRUE; } } if ($logic == 'or' && !$have_1_testable_condition) { return FALSE; } return TRUE; }
/** * @covers ::__construct */ public function setUp() { parent::setUp(); $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); $this->entityType = $this->prophesize(EntityTypeInterface::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); $module_handler->invokeAll(Argument::cetera())->willReturn([]); $this->pageAccess = new PageAccess($this->entityType->reveal(), $this->contextHandler->reveal()); $this->pageAccess->setModuleHandler($module_handler->reveal()); }
public function setUp() { $this->account = $this->prophesize(AccountInterface::class); $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); $this->uuidGenerator = $this->prophesize(UuidInterface::class); $this->token = $this->prophesize(Token::class); $this->builderManager = $this->prophesize(DisplayBuilderManagerInterface::class); $this->layoutManager = $this->prophesize(LayoutPluginManagerInterface::class); $this->layout = $this->prophesize(LayoutInterface::class); $this->layoutManager->createInstance(Argument::type('string'), Argument::type('array'))->willReturn($this->layout->reveal()); $this->variant = new PanelsDisplayVariant([], '', [], $this->contextHandler->reveal(), $this->account->reveal(), $this->uuidGenerator->reveal(), $this->token->reveal(), $this->builderManager->reveal(), $this->layoutManager->reveal()); }
/** * @covers ::__construct */ public function setUp() { parent::setUp(); $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); $this->entityType = $this->prophesize(EntityTypeInterface::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); $module_handler->invokeAll(Argument::cetera())->willReturn([]); $this->pageAccess = new PageAccess($this->entityType->reveal(), $this->contextHandler->reveal()); $this->pageAccess->setModuleHandler($module_handler->reveal()); $this->cacheContextsManager = $this->prophesize(CacheContextsManager::class); $container = new ContainerBuilder(); $container->set('cache_contexts_manager', $this->cacheContextsManager->reveal()); \Drupal::setContainer($container); }
/** * Builds the blocks in a layout region. * * @param \Drupal\layout\Plugin\LayoutRegion\LayoutRegionInterface $region * The layout region to render. * @param \Drupal\layout\Plugin\Layout\LayoutBlockAndContextProviderInterface $provider * The block and context provider needed to build the layout region. * * @return array * The render array. */ public function buildRegionBlocks(LayoutRegionInterface $region, LayoutBlockAndContextProviderInterface $provider) { $contexts = $provider->getContexts(); $blocksInRegion = $provider->getBlocksByRegion($region->id()); /** @var $blocksInRegion \Drupal\block\BlockPluginInterface[] */ $render_array = array(); foreach ($blocksInRegion as $id => $block) { if ($block instanceof ContextAwarePluginInterface) { $mapping = array(); if ($block instanceof ConfigurablePluginInterface) { $configuration = $block->getConfiguration(); if (isset($configuration['context_mapping'])) { $mapping = array_flip($configuration['context_mapping']); } } $this->contextHandler->applyContextMapping($block, $contexts, $mapping); } if ($block->access($this->account)) { $block_render_array = array('#theme' => 'block', '#attributes' => array(), '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId()); $block_render_array['#configuration']['label'] = String::checkPlain($block_render_array['#configuration']['label']); $block_render_array['content'] = $block->build(); $render_array[] = $block_render_array; } } return $render_array; }
/** * Build render arrays for each of the regions. * * @param array $regions * The render array representing regions. * @param array $contexts * The array of context objects. * * @return array * An associative array, keyed by region ID, containing the render arrays * representing the content of each region. */ protected function buildRegions(array $regions, array $contexts) { $build = []; foreach ($regions as $region => $blocks) { if (!$blocks) { continue; } $region_name = Html::getClass("block-region-{$region}"); $build[$region]['#prefix'] = '<div class="' . $region_name . '">'; $build[$region]['#suffix'] = '</div>'; /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ $weight = 0; foreach ($blocks as $block_id => $block) { if ($block instanceof ContextAwarePluginInterface) { $this->contextHandler->applyContextMapping($block, $contexts); } if ($block->access($this->account)) { $block_render_array = ['#theme' => 'block', '#attributes' => [], '#weight' => $weight++, '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId()]; $block_render_array['content'] = $block->build(); $build[$region][$block_id] = $block_render_array; } } } return $build; }
/** * Loads or creates a Block Plugin instance suitable for rendering or testing. * * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * * @return \Drupal\Core\Block\BlockBase * The Block Plugin instance. */ protected function getBlockInstance($form_state) { /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ $page_variant = PageVariant::load($form_state->getValue('variant_id')); // If a UUID is provided, the Block should already exist. if ($uuid = $form_state->getValue('uuid')) { /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $variant_plugin */ $variant_plugin = $page_variant->getVariantPlugin(); // If a temporary configuration for this variant exists, use it. $temp_store_key = 'variant.' . $page_variant->id(); if ($variant_config = $this->tempStore->get($temp_store_key)) { $variant_plugin->setConfiguration($variant_config); } // Load the existing Block instance. $block_instance = $variant_plugin->getBlock($uuid); } else { // Create an instance of this Block plugin. /** @var \Drupal\Core\Block\BlockBase $block_instance */ $block_instance = $this->blockManager->createInstance($form_state->getValue('plugin_id')); } // Add context to the block. if ($block_instance instanceof ContextAwarePluginInterface) { $this->contextHandler->applyContextMapping($block_instance, $page_variant->getContexts()); } return $block_instance; }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { /** @var \Drupal\block\BlockInterface $entity */ if ($operation != 'view') { return parent::checkAccess($entity, $operation, $account); } // Don't grant access to disabled blocks. if (!$entity->status()) { return AccessResult::forbidden()->addCacheableDependency($entity); } else { $conditions = []; $missing_context = FALSE; foreach ($entity->getVisibilityConditions() as $condition_id => $condition) { if ($condition instanceof ContextAwarePluginInterface) { try { $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping())); $this->contextHandler->applyContextMapping($condition, $contexts); } catch (ContextException $e) { $missing_context = TRUE; } } $conditions[$condition_id] = $condition; } if ($missing_context) { // If any context is missing then we might be missing cacheable // metadata, and don't know based on what conditions the block is // accessible or not. For example, blocks that have a node type // condition will have a missing context on any non-node route like the // frontpage. // @todo Avoid setting max-age 0 for some or all cases, for example by // treating available contexts without value differently in // https://www.drupal.org/node/2521956. $access = AccessResult::forbidden()->setCacheMaxAge(0); } elseif ($this->resolveConditions($conditions, 'and') !== FALSE) { // Delegate to the plugin. $block_plugin = $entity->getPlugin(); try { if ($block_plugin instanceof ContextAwarePluginInterface) { $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping())); $this->contextHandler->applyContextMapping($block_plugin, $contexts); } $access = $block_plugin->access($account, TRUE); } catch (ContextException $e) { // Setting access to forbidden if any context is missing for the same // reasons as with conditions (described in the comment above). // @todo Avoid setting max-age 0 for some or all cases, for example by // treating available contexts without value differently in // https://www.drupal.org/node/2521956. $access = AccessResult::forbidden()->setCacheMaxAge(0); } } else { $access = AccessResult::forbidden(); } $this->mergeCacheabilityFromConditions($access, $conditions); // Ensure that access is evaluated again when the block changes. return $access->addCacheableDependency($entity); } }
/** * Build the mega menu link/content tree. * * @param MegaMenuInterface $mega_menu * * @return array */ private function buildMegaMenuTree(MegaMenuInterface $mega_menu) { $tree = $this->loadMenuTree($mega_menu->getTargetMenu()); $build = $this->menuLinkTree->build($tree); $build['#mega_menu'] = $mega_menu; $cacheability = CacheableMetadata::createFromRenderArray($build); $cacheability->addCacheableDependency($mega_menu); // Add content from the mega menus to the link tree. foreach ($build['#items'] as $item_key => $item) { $safe_item_key = str_replace('.', '_', $item_key); $layout = $mega_menu->getLinkLayout($safe_item_key); if ($layout === MegaMenuInterface::NO_LAYOUT) { continue; } $build['#items'][$item_key]['attributes']['data-mega-menu-content-target'] = $item_key; /** @var LayoutInterface $layout_plugin */ $layout_plugin = $this->layoutPluginManager->createInstance($layout); $plugin_definition = $layout_plugin->getPluginDefinition(); // Build an array of the region names in the right order. $empty = array_fill_keys(array_keys($plugin_definition['region_names']), []); $full = $mega_menu->getBlocksByLink($safe_item_key)->getAllByRegion(); // Merge it with the actual values to maintain the ordering. $block_assignments = array_intersect_key(array_merge($empty, $full), $empty); $build['#items'][$item_key]['content'] = ['#prefix' => '<div data-mega-menu-content="' . $item_key . '" class="mega-menu-content">', '#suffix' => '</div>', '#theme' => $plugin_definition['theme'], '#settings' => [], '#layout' => $plugin_definition]; if (isset($plugin_definition['library'])) { $build['#items'][$item_key]['content']['#attached']['library'][] = $plugin_definition['library']; } foreach ($block_assignments as $region => $blocks) { $build['#items'][$item_key]['content'][$region] = []; /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ foreach ($blocks as $block_id => $block) { if ($block instanceof ContextAwarePluginInterface) { $contexts = $this->contextRepository->getRuntimeContexts($block->getContextMapping()); $this->contextHandler->applyContextMapping($block, $contexts); } // Make sure the user is allowed to view the block. $access = $block->access($this->account, TRUE); $cacheability->addCacheableDependency($access); // If the user is not allowed then do not render the block. if (!$access->isAllowed()) { continue; } $configuration = $block->getConfiguration(); // Create the render array for the block as a whole. // @see template_preprocess_block(). $block_build = ['#theme' => 'block', '#attributes' => [], '#weight' => $configuration['weight'], '#configuration' => $configuration, '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId(), '#block_plugin' => $block, '#pre_render' => [[$this, 'preRenderBlock']], '#cache' => ['keys' => ['mega_menu', $mega_menu->id(), 'block', $block_id], 'tags' => Cache::mergeTags($mega_menu->getCacheTags(), $block->getCacheTags()), 'contexts' => $block->getCacheContexts(), 'max-age' => $block->getCacheMaxAge()]]; $build['#items'][$item_key]['content'][$region][$block_id] = $block_build; $cacheability->addCacheableDependency($block); } } } $cacheability->applyTo($build); return $build; }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) { /** @var \Drupal\block\BlockInterface $entity */ if ($operation != 'view') { return parent::checkAccess($entity, $operation, $langcode, $account); } // Don't grant access to disabled blocks. if (!$entity->status()) { return AccessResult::forbidden()->cacheUntilEntityChanges($entity); } else { $contexts = $entity->getContexts(); $conditions = []; foreach ($entity->getVisibilityConditions() as $condition_id => $condition) { if ($condition instanceof ContextAwarePluginInterface) { try { $this->contextHandler->applyContextMapping($condition, $contexts); } catch (ContextException $e) { return AccessResult::forbidden()->setCacheable(FALSE); } } $conditions[$condition_id] = $condition; } if ($this->resolveConditions($conditions, 'and') !== FALSE) { // Delegate to the plugin. $access = $entity->getPlugin()->access($account, TRUE); } else { $access = AccessResult::forbidden(); } // This should not be hardcoded to an uncacheable access check result, but // in order to fix that, we need condition plugins to return cache contexts, // otherwise it will be impossible to determine by which cache contexts the // result should be varied. // @todo Change this to use $access->cacheUntilEntityChanges($entity) once // https://www.drupal.org/node/2375695 is resolved. return $access->setCacheable(FALSE); } }
/** * Compiles a render array for the given Block instance based on the form. * * @param \Drupal\Core\Block\BlockBase $block_instance * The Block instance you want to render. * * @return array $build * The Block render array. */ protected function buildBlockInstance($block_instance) { // Get the new block configuration. $configuration = $block_instance->getConfiguration(); // Add context to the block. if ($block_instance instanceof ContextAwarePluginInterface) { $this->contextHandler->applyContextMapping($block_instance, $this->panelsDisplay->getContexts()); } // Build the block content. $content = $block_instance->build(); // Disable any nested forms from the render array. $content = $this->removeFormWrapperRecursive($content); // Compile the render array. $build = ['#theme' => 'block', '#attributes' => [], '#contextual_links' => [], '#configuration' => $configuration, '#plugin_id' => $block_instance->getPluginId(), '#base_plugin_id' => $block_instance->getBaseId(), '#derivative_plugin_id' => $block_instance->getDerivativeId(), 'content' => $content]; return $build; }