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 %}
|
||||
</div>
|
||||
|
||||
{{ content.content_top }}
|
||||
{{ content.content }}
|
||||
</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['navigation_content_top'] = [
|
||||
'variables' => [
|
||||
'items' => [],
|
||||
],
|
||||
];
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\navigation;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\SortArray;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
@ -18,6 +19,7 @@ use Drupal\Core\Image\ImageFactory;
|
|||
use Drupal\Core\Menu\LocalTaskManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Security\Attribute\TrustedCallback;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
@ -128,6 +130,7 @@ final class NavigationRenderer {
|
|||
if ($storage) {
|
||||
foreach ($storage->getSections() as $delta => $section) {
|
||||
$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
|
||||
|
@ -157,6 +160,8 @@ final class NavigationRenderer {
|
|||
];
|
||||
$build[0] = NestedArray::mergeDeepArray([$build[0], $defaults]);
|
||||
|
||||
$build[0]['content_top'] = $this->getContentTop();
|
||||
|
||||
if ($logo_provider === self::LOGO_PROVIDER_CUSTOM) {
|
||||
$logo_path = $logo_settings->get('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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
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