Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi page types #16

Open
wants to merge 14 commits into
base: 1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions localgov_subsites_extras.api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* @file
* Hooks provided by the Locagov Subsites Extras module.
*/

use Drupal\node\NodeInterface;

/**
* @addtogroup hooks
* @{
*/

/**
* Alter the current node, before subsite determination.
*
* This alter hook is called before we walk the menu tree to determine if we're
* looking at a page in a subsite or not.
*
* It'll be passed the current node, acquired from routeMatch. If no node is
* found there, this hook will be passed null. This gives the opportunity to
* include non-node pages in a subsite if you wish.
*
* If you want to indicate that a subsite is not being viewed, set $node to
* NULL.
*
* Usually, this hook will be used to use a property of the node passed, such as
* a reference to another node (such as the parent node field for guides,
* directories, etc) to swop out the current node for a different node, which is
* in the menu and therefore part of a subsite.
*/
function hook_localgov_subsites_extras_current_node_alter(?NodeInterface &$node) {
if ($node instanceof NodeInterface && $node->hasField('field_parent')) {
$parent = $node->field_parent->entity;
if ($parent instanceof NodeInterface) {
$node = $parent;
}
}
}

/**
* @} End of "addtogroup hooks".
*/
3 changes: 2 additions & 1 deletion localgov_subsites_extras.module
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

declare(strict_types=1);

include 'localgov_subsites_extras_hooks.inc';

/**
* Implements hook_preprocess_node().
*
Expand Down Expand Up @@ -69,5 +71,4 @@ function localgov_subsites_extras_entity_bundle_create($entity_type_id, $bundle)
$type->setThirdPartySetting('menu_ui', 'available_menus', $availableMenus);
$type->save();
}

}
2 changes: 1 addition & 1 deletion localgov_subsites_extras.services.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
45 changes: 45 additions & 0 deletions localgov_subsites_extras_hooks.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* @file
* Hooks that we're temporarily implementing on behalf of other modules.
*
* Ideally, we want these hook implementations in their respective modules. For
* the moment, we've implemented them here, and when this module is heading
* towards a proper release, we'll open PRs to move them. Each hook is wrapped
* in function_exists, so that the hook can exist in both modules temporarily.
*/

use Drupal\node\NodeInterface;

if (!function_exists('localgov_directories_localgov_subsites_extras_current_node_alter')) {

/**
* Implements hook_localgov_subsites_extras_current_node_alter().
*/
function localgov_directories_localgov_subsites_extras_current_node_alter(?NodeInterface &$node) {
if ($node instanceof NodeInterface && $node->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;
}
}
}

}
69 changes: 33 additions & 36 deletions src/Service/SubsiteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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();

Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}

}
85 changes: 56 additions & 29 deletions tests/src/Functional/SubsiteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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');
Expand Down