Issue #3463142 by plopesc, m4olivei, larowlan, penyaskito: Allow modules to hook into top of content section of new core navigation
parent
4761995e1a
commit
17b0403efa
|
@ -70,6 +70,7 @@
|
||||||
} only %}
|
} only %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ content.content_top }}
|
||||||
{{ content.content }}
|
{{ content.content }}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Hooks related to the Navigation module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide content for Navigation content_top section.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An associative array of renderable elements.
|
||||||
|
*
|
||||||
|
* @see hook_navigation_content_top_alter()
|
||||||
|
*/
|
||||||
|
function hook_navigation_content_top(): array {
|
||||||
|
return [
|
||||||
|
'navigation_foo' => [
|
||||||
|
'#markup' => \Drupal::config('system.site')->get('name'),
|
||||||
|
'#cache' => [
|
||||||
|
'tags' => ['config:system.site'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'navigation_bar' => [
|
||||||
|
'#markup' => 'bar',
|
||||||
|
],
|
||||||
|
'navigation_baz' => [
|
||||||
|
'#markup' => 'baz',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alter replacement values for placeholder tokens.
|
||||||
|
*
|
||||||
|
* @param $content_top
|
||||||
|
* An associative array of content returned by hook_navigation_content_top().
|
||||||
|
*
|
||||||
|
* @see hook_navigation_content_top()
|
||||||
|
*/
|
||||||
|
function hook_navigation_content_top_alter(array &$content_top): void {
|
||||||
|
// Remove a specific element.
|
||||||
|
unset($content_top['navigation_foo']);
|
||||||
|
// Modify an element.
|
||||||
|
$content_top['navigation_bar']['#markup'] = 'new bar';
|
||||||
|
// Change weight.
|
||||||
|
$content_top['navigation_baz']['#weight'] = '-100';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
|
@ -99,6 +99,11 @@ class NavigationHooks {
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$items['menu_region__footer'] = ['variables' => ['items' => [], 'title' => NULL, 'menu_name' => NULL]];
|
$items['menu_region__footer'] = ['variables' => ['items' => [], 'title' => NULL, 'menu_name' => NULL]];
|
||||||
|
$items['navigation_content_top'] = [
|
||||||
|
'variables' => [
|
||||||
|
'items' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
return $items;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Drupal\navigation;
|
namespace Drupal\navigation;
|
||||||
|
|
||||||
use Drupal\Component\Utility\NestedArray;
|
use Drupal\Component\Utility\NestedArray;
|
||||||
|
use Drupal\Component\Utility\SortArray;
|
||||||
use Drupal\Core\Block\BlockPluginInterface;
|
use Drupal\Core\Block\BlockPluginInterface;
|
||||||
use Drupal\Core\Cache\CacheBackendInterface;
|
use Drupal\Core\Cache\CacheBackendInterface;
|
||||||
use Drupal\Core\Cache\CacheableMetadata;
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
|
@ -18,6 +19,7 @@ use Drupal\Core\Image\ImageFactory;
|
||||||
use Drupal\Core\Menu\LocalTaskManagerInterface;
|
use Drupal\Core\Menu\LocalTaskManagerInterface;
|
||||||
use Drupal\Core\Plugin\Context\Context;
|
use Drupal\Core\Plugin\Context\Context;
|
||||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||||
|
use Drupal\Core\Render\Element;
|
||||||
use Drupal\Core\Routing\RouteMatchInterface;
|
use Drupal\Core\Routing\RouteMatchInterface;
|
||||||
use Drupal\Core\Security\Attribute\TrustedCallback;
|
use Drupal\Core\Security\Attribute\TrustedCallback;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
@ -128,6 +130,7 @@ final class NavigationRenderer {
|
||||||
if ($storage) {
|
if ($storage) {
|
||||||
foreach ($storage->getSections() as $delta => $section) {
|
foreach ($storage->getSections() as $delta => $section) {
|
||||||
$build[$delta] = $section->toRenderArray([]);
|
$build[$delta] = $section->toRenderArray([]);
|
||||||
|
$build[$delta]['#cache']['contexts'] = ['user.permissions', 'theme', 'languages:language_interface'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The render array is built based on decisions made by SectionStorage
|
// The render array is built based on decisions made by SectionStorage
|
||||||
|
@ -157,6 +160,8 @@ final class NavigationRenderer {
|
||||||
];
|
];
|
||||||
$build[0] = NestedArray::mergeDeepArray([$build[0], $defaults]);
|
$build[0] = NestedArray::mergeDeepArray([$build[0], $defaults]);
|
||||||
|
|
||||||
|
$build[0]['content_top'] = $this->getContentTop();
|
||||||
|
|
||||||
if ($logo_provider === self::LOGO_PROVIDER_CUSTOM) {
|
if ($logo_provider === self::LOGO_PROVIDER_CUSTOM) {
|
||||||
$logo_path = $logo_settings->get('logo.path');
|
$logo_path = $logo_settings->get('logo.path');
|
||||||
if (!empty($logo_path) && is_file($logo_path)) {
|
if (!empty($logo_path) && is_file($logo_path)) {
|
||||||
|
@ -169,10 +174,36 @@ final class NavigationRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$build[0]['#cache']['contexts'] = ['user.permissions', 'theme', 'languages:language_interface'];
|
|
||||||
return $build;
|
return $build;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content for content_top section.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The content_top section content.
|
||||||
|
*/
|
||||||
|
protected function getContentTop(): array {
|
||||||
|
$content_top = [
|
||||||
|
'#theme' => 'navigation_content_top',
|
||||||
|
];
|
||||||
|
$content_top_items = $this->moduleHandler->invokeAll('navigation_content_top');
|
||||||
|
$this->moduleHandler->alter('navigation_content_top', $content_top_items);
|
||||||
|
uasort($content_top_items, [SortArray::class, 'sortByWeightElement']);
|
||||||
|
// Filter out empty items, taking care to merge any cacheability metadata.
|
||||||
|
$cacheability = new CacheableMetadata();
|
||||||
|
$content_top_items = array_filter($content_top_items, function ($item) use (&$cacheability) {
|
||||||
|
if (Element::isEmpty($item)) {
|
||||||
|
$cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($item));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
});
|
||||||
|
$cacheability->applyTo($content_top);
|
||||||
|
$content_top['#items'] = $content_top_items;
|
||||||
|
return $content_top;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the top bar for content entity pages.
|
* Build the top bar for content entity pages.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{#
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Default theme implementation to display the navigation content_top section.
|
||||||
|
*
|
||||||
|
* Available variables:
|
||||||
|
* - items: An associative array of renderable elements to display in the
|
||||||
|
* content_top section.
|
||||||
|
*
|
||||||
|
* @ingroup themeable
|
||||||
|
*/
|
||||||
|
#}
|
||||||
|
{% if items is not empty %}
|
||||||
|
<div class="admin-toolbar__content-top">
|
||||||
|
{{ items }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Drupal\navigation_test\Hook;
|
namespace Drupal\navigation_test\Hook;
|
||||||
|
|
||||||
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
use Drupal\Core\Hook\Attribute\Hook;
|
use Drupal\Core\Hook\Attribute\Hook;
|
||||||
use Drupal\Core\State\StateInterface;
|
use Drupal\Core\State\StateInterface;
|
||||||
|
|
||||||
|
@ -34,4 +35,53 @@ class NavigationTestHooks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_navigation_content_top().
|
||||||
|
*/
|
||||||
|
#[Hook('navigation_content_top')]
|
||||||
|
public function navigationContentTop(): array {
|
||||||
|
if (\Drupal::keyValue('navigation_test')->get('content_top')) {
|
||||||
|
$items = [
|
||||||
|
'navigation_foo' => [
|
||||||
|
'#markup' => 'foo',
|
||||||
|
],
|
||||||
|
'navigation_bar' => [
|
||||||
|
'#markup' => 'bar',
|
||||||
|
],
|
||||||
|
'navigation_baz' => [
|
||||||
|
'#markup' => 'baz',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$items = [
|
||||||
|
'navigation_foo' => [],
|
||||||
|
'navigation_bar' => [],
|
||||||
|
'navigation_baz' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Add cache tags to our items to express a made up dependency to test
|
||||||
|
// cacheability. Note that as we're always returning the same items,
|
||||||
|
// sometimes only with cacheability metadata. By doing this we're testing
|
||||||
|
// conditional rendering of content_top items.
|
||||||
|
foreach ($items as &$element) {
|
||||||
|
CacheableMetadata::createFromRenderArray($element)
|
||||||
|
->addCacheTags(['navigation_test'])
|
||||||
|
->applyTo($element);
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_navigation_content_top_alter().
|
||||||
|
*/
|
||||||
|
#[Hook('navigation_content_top_alter')]
|
||||||
|
public function navigationContentTopAlter(&$content_top): void {
|
||||||
|
if (\Drupal::keyValue('navigation_test')->get('content_top_alter')) {
|
||||||
|
unset($content_top['navigation_foo']);
|
||||||
|
$content_top['navigation_bar']['#markup'] = 'new bar';
|
||||||
|
$content_top['navigation_baz']['#weight'] = '-100';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\Tests\navigation\Functional;
|
||||||
|
|
||||||
|
use Drupal\Core\Cache\Cache;
|
||||||
|
use Drupal\Core\Url;
|
||||||
|
use Drupal\Tests\BrowserTestBase;
|
||||||
|
|
||||||
|
// cspell:ignore foobarbaz baznew
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for navigation content_top section.
|
||||||
|
*
|
||||||
|
* @group navigation
|
||||||
|
*/
|
||||||
|
class NavigationContentTopTest extends BrowserTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $modules = ['navigation', 'navigation_test', 'test_page_test'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $defaultTheme = 'stark';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->drupalLogin($this->createUser([
|
||||||
|
'access navigation',
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests behavior of content_top section hooks.
|
||||||
|
*/
|
||||||
|
public function testNavigationContentTop(): void {
|
||||||
|
$test_page_url = Url::fromRoute('test_page_test.test_page');
|
||||||
|
$this->drupalGet($test_page_url);
|
||||||
|
$this->assertSession()->elementNotExists('css', '.admin-toolbar__content-top');
|
||||||
|
\Drupal::keyValue('navigation_test')->set('content_top', 1);
|
||||||
|
Cache::invalidateTags(['navigation_test']);
|
||||||
|
$this->drupalGet($test_page_url);
|
||||||
|
$this->assertSession()->elementTextContains('css', '.admin-toolbar__content-top', 'foobarbaz');
|
||||||
|
\Drupal::keyValue('navigation_test')->set('content_top_alter', 1);
|
||||||
|
Cache::invalidateTags(['navigation_test']);
|
||||||
|
$this->drupalGet($test_page_url);
|
||||||
|
$this->assertSession()->elementTextContains('css', '.admin-toolbar__content-top', 'baznew bar');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue