/** * Builds a form that configure an existing or new layout for the IPE. * * @param array $form * An associative array containing the structure of the form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param string $layout_id * The requested Layout ID. * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display * The current PageVariant ID. * * @return array * The form structure. */ public function buildForm(array $form, FormStateInterface $form_state, $layout_id = NULL, PanelsDisplayVariant $panels_display = NULL) { // We require these default arguments. if (!$layout_id || !$panels_display) { return FALSE; } // Save the panels display for later. $this->panelsDisplay = $panels_display; // Check if this is the current layout, and if not create an instance. $layout = $this->panelsDisplay->getLayout(); $current = $layout->getPluginId() == $layout_id; if (!$current) { // Create a new layout instance. $layout = $this->layoutManager->createInstance($layout_id, []); } // Save the layout for future use. $this->layout = $layout; $form['settings'] = $layout->buildConfigurationForm([], $form_state); $form['settings']['#tree'] = TRUE; // If the form is empty, inform the user. if (empty(Element::getVisibleChildren($form['settings']))) { $form['settings'][] = ['#markup' => $this->t('<h5>This layout does not provide any configuration.</h5>')]; } // Add an add button, which is only used by our App. $form['submit'] = ['#type' => 'button', '#value' => $current ? $this->t('Update') : $this->t('Change Layout'), '#ajax' => ['callback' => '::submitForm', 'wrapper' => 'panels-ipe-layout-form-wrapper', 'method' => 'replace', 'progress' => ['type' => 'throbber', 'message' => '']]]; return $form; }
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()); }
/** * Gets a given layout with empty regions and relevant metadata. * * @param \Drupal\page_manager\PageVariantInterface $page_variant * The page variant entity. * @param string $layout_id * The machine name of the requested layout. * * @return \Symfony\Component\HttpFoundation\JsonResponse */ public function getLayout(PageVariantInterface $page_variant, $layout_id) { /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $variant_plugin */ $variant_plugin = $page_variant->getVariantPlugin(); // Build the requested layout. $configuration = $variant_plugin->getConfiguration(); $configuration['layout'] = $layout_id; $variant_plugin->setConfiguration($configuration); // Inherit our PageVariant's contexts before rendering. $variant_plugin->setContexts($page_variant->getContexts()); $regions = $variant_plugin->getRegionNames(); $region_data = []; $region_content = []; // Compile region content and metadata. foreach ($regions as $id => $label) { // Wrap the region with a class/data attribute that our app can use. $region_name = Html::getClass("block-region-{$id}"); $region_content[$id] = ['#prefix' => '<div class="' . $region_name . '" data-region-name="' . $id . '">', '#suffix' => '</div>']; // Format region metadata. $region_data[] = ['name' => $id, 'label' => $label]; } $build = $variant_plugin->getLayout()->build($region_content); // Get the current layout. $current_layout = $variant_plugin->getLayout()->getPluginId(); // Get a list of all available layouts. $layouts = $this->layoutPluginManager->getLayoutOptions(); $data = ['id' => $layout_id, 'label' => $layouts[$layout_id], 'current' => $current_layout == $layout_id, 'html' => $this->renderer->render($build), 'regions' => $region_data]; // Update temp store. $this->savePageVariant($page_variant); // Return a structured JSON response for our Backbone App. return new JsonResponse($data); }
/** * 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; }
/** * Gets a list of available Layouts, without wrapping HTML. * * @param string $panels_storage_type * The id of the storage service. * @param string $panels_storage_id * The id within the storage service for the requested Panels display. * * @return \Symfony\Component\HttpFoundation\JsonResponse */ public function getLayouts($panels_storage_type, $panels_storage_id) { $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); // Get the current layout. $current_layout_id = $panels_display->getLayout()->getPluginId(); // Get a list of all available layouts. $layouts = $this->layoutPluginManager->getDefinitions(); $data = []; foreach ($layouts as $id => $layout) { $data[] = ['id' => $id, 'label' => $layout['label'], 'icon' => !empty($layout['icon']) ? $layout['icon'] : drupal_get_path('module', 'panels') . '/images/no-layout-preview.png', 'current' => $id == $current_layout_id, 'category' => $layout['category']]; } // Return a structured JSON response for our Backbone App. return new JsonResponse($data); }
/** * Get a list of regions for the specified layout. * * @param string $layout * * @return array */ protected function getLayoutRegions($layout) { $default_regions = $this->getDefaultRegions(); if ($layout === MegaMenuInterface::NO_LAYOUT) { return $default_regions; } $definition = $this->layoutPluginManager->getDefinition($layout, FALSE); if (!$definition) { return $default_regions; } if (!isset($definition['region_names']) || !count($definition['region_names'])) { return $default_regions; } return $definition['region_names'] + $default_regions; }