diff --git a/localgov_subsites_extras.api.php b/localgov_subsites_extras.api.php new file mode 100644 index 0000000..ebb7f5c --- /dev/null +++ b/localgov_subsites_extras.api.php @@ -0,0 +1,44 @@ +hasField('field_parent')) { + $parent = $node->field_parent->entity; + if ($parent instanceof NodeInterface) { + $node = $parent; + } + } +} + +/** + * @} End of "addtogroup hooks". + */ diff --git a/localgov_subsites_extras.module b/localgov_subsites_extras.module index c869f7b..8444d0c 100644 --- a/localgov_subsites_extras.module +++ b/localgov_subsites_extras.module @@ -7,6 +7,8 @@ declare(strict_types=1); +include 'localgov_subsites_extras_hooks.inc'; + /** * Implements hook_preprocess_node(). * @@ -69,5 +71,4 @@ function localgov_subsites_extras_entity_bundle_create($entity_type_id, $bundle) $type->setThirdPartySetting('menu_ui', 'available_menus', $availableMenus); $type->save(); } - } diff --git a/localgov_subsites_extras.services.yml b/localgov_subsites_extras.services.yml index b245fe7..687c9ca 100644 --- a/localgov_subsites_extras.services.yml +++ b/localgov_subsites_extras.services.yml @@ -1,7 +1,7 @@ services: localgov_subsites_extras.service: class: Drupal\localgov_subsites_extras\Service\SubsiteService - arguments: ['@entity_type.manager', '@plugin.manager.menu.link', '@current_route_match', '@config.factory'] + arguments: ['@config.factory', '@entity_type.manager', '@plugin.manager.menu.link', '@module_handler', '@current_route_match'] cache_context.subsites: class: Drupal\localgov_subsites_extras\Cache\SubsitesCacheContext arguments: diff --git a/localgov_subsites_extras_hooks.inc b/localgov_subsites_extras_hooks.inc new file mode 100644 index 0000000..7456684 --- /dev/null +++ b/localgov_subsites_extras_hooks.inc @@ -0,0 +1,45 @@ +hasField('localgov_directory_channels')) { + $directoryChannel = $node->localgov_directory_channels->entity; + if ($directoryChannel instanceof NodeInterface) { + $node = $directoryChannel; + } + } + } + +} + +if (!function_exists('localgov_guides_localgov_subsites_extras_current_node_alter')) { + + /** + * Implements hook_localgov_subsites_extras_current_node_alter(). + */ + function localgov_guides_localgov_subsites_extras_current_node_alter(?NodeInterface &$node) { + if ($node instanceof NodeInterface && $node->hasField('localgov_guides_parent')) { + $guideParent = $node->localgov_guides_parent->entity; + if ($guideParent instanceof NodeInterface) { + $node = $guideParent; + } + } + } + +} diff --git a/src/Service/SubsiteService.php b/src/Service/SubsiteService.php index 63ccd71..f277817 100644 --- a/src/Service/SubsiteService.php +++ b/src/Service/SubsiteService.php @@ -6,6 +6,7 @@ use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Menu\MenuLinkManagerInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\node\NodeInterface; @@ -15,30 +16,34 @@ */ class SubsiteService implements SubsiteServiceInterface { - // Disable phpcs for a bit, so we don't have to add a load of stuff that's - // made redundant by type hints. - // phpcs:disable - private EntityTypeManagerInterface $entityTypeManager; - private MenuLinkManagerInterface $menuLinkService; - private RouteMatchInterface $routeMatch; - private ConfigFactory $configFactory; + /** + * Subsite homepage. + * + * @var \Drupal\node\NodeInterface|null + */ private ?NodeInterface $subsiteHomePage; - private bool $searched = false; + + /** + * Searched flag. + * + * @var bool + */ + private bool $searched = FALSE; + + /** + * Subsite content types. + * + * @var array|null + */ private ?array $subsiteTypes = []; - private ?string $themeField; public function __construct( - EntityTypeManagerInterface $entityTypeManager, - MenuLinkManagerInterface $menuLinkService, - RouteMatchInterface $routeMatch, - ConfigFactory $configFactory - ) { - $this->entityTypeManager = $entityTypeManager; - $this->menuLinkService = $menuLinkService; - $this->routeMatch = $routeMatch; - $this->configFactory = $configFactory; - } - // phpcs:enable + private ConfigFactory $configFactory, + private EntityTypeManagerInterface $entityTypeManager, + private MenuLinkManagerInterface $menuLinkService, + private ModuleHandlerInterface $moduleHandler, + private RouteMatchInterface $routeMatch, + ) {} /** * {@inheritDoc} @@ -58,13 +63,13 @@ public function getHomePage(): ?NodeInterface { */ public function getCurrentSubsiteTheme(): ?string { - $this->themeField = $this->configFactory->get('localgov_subsites_extras.settings')->get('theme_field'); + $themeField = $this->configFactory->get('localgov_subsites_extras.settings')->get('theme_field'); // If the current node is part of a subsite, $subsiteHomePage will be the // subsite's homepage node. If it's not, it'll be null. $subsiteHomePage = $this->getHomePage(); if ($subsiteHomePage) { - return $subsiteHomePage->get($this->themeField)->value; + return $subsiteHomePage->get($themeField)->value; } return NULL; @@ -80,7 +85,7 @@ private function isSubsiteType(NodeInterface $node): bool { /** * Walks up the menu tree to look for a subsite homepage node. */ - private function walkMenuTree(NodeInterface $node) { + private function walkMenuTree(NodeInterface $node): ?NodeInterface { if ($this->isSubsiteType($node)) { return $node; @@ -91,10 +96,9 @@ private function walkMenuTree(NodeInterface $node) { if (!empty($result)) { $menuLink = reset($result); $parentMenuLinkID = $menuLink->getParent(); - if ($parentMenuLinkID) { $parentNode = $this->loadNodeForMenuLink($parentMenuLinkID); - return $this->walkMenuTree($parentNode); + return $parentNode ? $this->walkMenuTree($parentNode) : NULL; } } return NULL; @@ -103,7 +107,7 @@ private function walkMenuTree(NodeInterface $node) { /** * Loads the node for the supplied menu link ID. */ - private function loadNodeForMenuLink($menuLinkContentID) { + private function loadNodeForMenuLink($menuLinkContentID): ?NodeInterface { $menuLink = $this->menuLinkService->createInstance($menuLinkContentID); $pluginDefinition = $menuLink->getPluginDefinition(); @@ -135,6 +139,8 @@ private function findHomePage(?NodeInterface $node = NULL): ?NodeInterface { } } + $this->moduleHandler->alter('localgov_subsites_extras_current_node', $node); + if (!$node instanceof NodeInterface) { return NULL; } @@ -144,16 +150,7 @@ private function findHomePage(?NodeInterface $node = NULL): ?NodeInterface { $this->subsiteTypes = $subsiteTypes; } - $subsiteHomePage = $this->walkMenuTree($node); - - // @todo Move this out to an event or hook or something. - if (empty($subsiteHomePage) && $node->getType() === 'localgov_directories_page') { - /** @var \Drupal\node\NodeInterface $directoryChannel */ - $directoryChannel = $node->localgov_directory_channels->entity; - $subsiteHomePage = $this->walkMenuTree($directoryChannel); - } - - return $subsiteHomePage; + return $this->walkMenuTree($node); } } diff --git a/tests/src/Functional/SubsiteTest.php b/tests/src/Functional/SubsiteTest.php index 215d5d4..52668cb 100644 --- a/tests/src/Functional/SubsiteTest.php +++ b/tests/src/Functional/SubsiteTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\localgov_subsites_extras\Functional; use Drupal\menu_link_content\Entity\MenuLinkContent; -use Drupal\node\Entity\Node; use Drupal\Tests\BrowserTestBase; /** @@ -22,49 +21,77 @@ class SubsiteTest extends BrowserTestBase { protected static $modules = [ 'localgov_subsites', 'localgov_subsites_extras', + 'localgov_guides', ]; /** - * Test that we can set up a subsite using this module. + * Creates a menu link to the given node in the subsites menu. */ - public function testLoadAdminView() { + protected function createMenuLinkForNode($node, $parentLink = NULL) { + $properties = [ + 'link' => [['uri' => 'entity:node/' . $node->id()]], + 'title' => $node->label(), + 'menu_name' => 'subsites', + ]; + if ($parentLink instanceof MenuLinkContent) { + $properties['parent'] = 'menu_link_content:' . $parentLink->uuid(); + } + $menuLink = MenuLinkContent::create($properties); + $menuLink->save(); + return $menuLink; + } - $user = $this->createUser([], 'admintestuser', TRUE); + /** + * Test that we can set up a subsite using this module. + * + * Structure is a single subsite page under a subsite overview. + */ + public function testSubsitePage() { - // "theme_a" is the only default value in a fresh install of - // localgov_subsites. - $parentNode = Node::create([ + // "theme_a" is the only value in a fresh install of localgov_subsites. + $parentNode = $this->createNode([ 'type' => 'localgov_subsites_overview', - 'title' => $this->randomMachineName(), - 'uid' => $user->id(), - 'status' => 1, 'localgov_subsites_theme' => 'theme_a', ]); - $parentNode->save(); + $parentMenuLink = $this->createMenuLinkForNode($parentNode); - $parentMenuLink = MenuLinkContent::create([ - 'link' => [['uri' => 'entity:node/' . $parentNode->id()]], - 'title' => $parentNode->label(), - 'menu_name' => 'subsites', + $childNode = $this->createNode([ + 'type' => 'localgov_subsites_page', ]); - $parentMenuLink->save(); + $this->createMenuLinkForNode($childNode, $parentMenuLink); - $childNode = Node::create([ - 'type' => 'localgov_subsites_page', - 'title' => $this->randomMachineName(), - 'uid' => $user->id(), - 'status' => 1, + $this->drupalGet('/node/' . $childNode->id()); + + // Check the class for the color scheme is on the body of the child node. + $this->assertSession()->elementAttributeContains('xpath', '/body', 'class', 'subsite-extra--color-theme_a'); + } + + /** + * Test that we can set up a guide in a subsite using this module. + * + * Structure is a single guide page under a guide overview under a subsite + * overview. + */ + public function testGuidePage() { + + // "theme_a" is the only value in a fresh install of localgov_subsites. + $subsiteNode = $this->createNode([ + 'type' => 'localgov_subsites_overview', + 'localgov_subsites_theme' => 'theme_a', ]); - $childNode->save(); + $subsiteMenuLink = $this->createMenuLinkForNode($subsiteNode); - MenuLinkContent::create([ - 'link' => [['uri' => 'entity:node/' . $childNode->id()]], - 'title' => $childNode->label(), - 'menu_name' => 'subsites', - 'parent' => 'menu_link_content:' . $parentMenuLink->uuid(), - ])->save(); + $guideOverviewNode = $this->createNode([ + 'type' => 'localgov_guides_overview', + ]); + $this->createMenuLinkForNode($guideOverviewNode, $subsiteMenuLink); - $this->drupalGet('/node/' . $childNode->id()); + $guidePageNode = $this->createNode([ + 'type' => 'localgov_guides_page', + 'localgov_guides_parent' => $guideOverviewNode->id(), + ]); + + $this->drupalGet('/node/' . $guidePageNode->id()); // Check the class for the color scheme is on the body of the child node. $this->assertSession()->elementAttributeContains('xpath', '/body', 'class', 'subsite-extra--color-theme_a');