/** * */ 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; }
/** * {@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; }