Skip to content
52 changes: 52 additions & 0 deletions public/modules/custom/helfi_group/helfi_group.install
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

declare(strict_types=1);

use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\node\Entity\Node;
use Drupal\pathauto\PathautoState;

/**
* Update group menu item's content translation status to match node's status.
Expand Down Expand Up @@ -52,3 +54,53 @@ function helfi_group_update_9001(): void {
}
}
}

/**
* Update path aliases for group news items.
*/
function helfi_group_update_9002(&$sandbox): void {
$query = \Drupal::entityQuery('node')
->condition('type', 'news_item')
->accessCheck(FALSE);
$result = $query->execute();

if (!$result) {
return;
}

// Use the sandbox to store the information needed to track progression.
if (!isset($sandbox['current'])) {
// The count of entities visited so far.
$sandbox['current'] = 0;
// Total entities that must be visited.
$sandbox['max'] = count($result);
}

// Process 50 entities at a time.
$limit = 50;
$nids = array_slice($result, $sandbox['current'], $limit);
$nodes = Node::loadMultiple($nids);
$languageManager = \Drupal::languageManager();
foreach ($nodes as $node) {
// Set current languages to match node's language for proper
// token replacement.
$languageManager->setCurrentLanguage($node->language(), LanguageInterface::TYPE_CONTENT);
$languageManager->setCurrentLanguage($node->language(), LanguageInterface::TYPE_URL);

// Update Entity URL alias.
$path_value = $node->get('path')->getValue();
$path_value = $path_value ?: [[]];
$path_value[0]['pathauto'] = PathautoState::CREATE;
$node->set('path', $path_value);
\Drupal::service('pathauto.generator')->updateEntityAlias($node, 'bulkupdate', ['message' => TRUE]);

// Update our progress information.
$sandbox['current']++;
}

$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);

if ($sandbox['#finished'] >= 1) {
\Drupal::messenger()->addMessage('Url alias updated for ' . $sandbox['current'] . ' news items.');
}
}
11 changes: 10 additions & 1 deletion public/modules/custom/helfi_group/helfi_group.tokens.inc
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,19 @@ function helfi_group_tokens($type, $tokens, array $data, array $options, Bubblea
break;
}

// Resolve the path to its alias so the token works in both normal
// requests and during update hooks. In update hooks the path_alias
// outbound processor is disabled (see UpdateServiceProvider), so
// toUrl('canonical')->toString() would return the internal path
// (e.g. node/123). Using the alias manager directly returns the
// alias in both contexts.
$internal_path = $newsParent->toUrl('canonical')->getInternalPath();
$path = \Drupal::service('path_alias.manager')->getAliasByPath("/$internal_path", $langcode);

// Set the token replacement using the news parent path, but filtering
// out langcode and site prefix sections.
$replacements[$original] = _helfi_group_filter_path(
$newsParent->toUrl('canonical')->toString(),
$path,
[
$langcode,
_helfi_group_get_site_prefix($langcode),
Expand Down
28 changes: 28 additions & 0 deletions public/modules/custom/helfi_group/src/Hook/BlockAlter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Drupal\helfi_group\Hook;

use Drupal\Core\Hook\Attribute\Hook;
use Drupal\helfi_group\Plugin\Block\HelfiGroupMenuBlock;

/**
* Alter hooks for blocks.
*/
class BlockAlter {

/**
* Alter block definitions.
*
* @param array $definitions
* A block definitions array.
*/
#[Hook(hook: 'block_alter')]
public function alter(array &$definitions): void {
if (isset($definitions['group_content_menu:kasko_group_menu'])) {
$definitions['group_content_menu:kasko_group_menu']['class'] = HelfiGroupMenuBlock::class;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<?php

declare(strict_types=1);

namespace Drupal\helfi_group\Plugin\Block;

use Drupal\group_content_menu\GroupContentMenuInterface;
use Drupal\group_content_menu\Plugin\Block\GroupMenuBlock;
use Drupal\Core\Menu\MenuActiveTrailInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\node\NodeInterface;
use Drupal\group\Entity\GroupInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Overrides the generic Menu block.
*/
final class HelfiGroupMenuBlock extends GroupMenuBlock implements ContainerFactoryPluginInterface {

/**
* The route match service.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;

/**
* The menu link manager service.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;

/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MenuLinkTreeInterface $menu_tree,
MenuActiveTrailInterface $menu_active_trail,
EntityTypeManagerInterface $entity_type_manager,
RouteMatchInterface $route_match,
MenuLinkManagerInterface $menu_link_manager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $menu_tree, $menu_active_trail, $entity_type_manager);
$this->routeMatch = $route_match;
$this->menuLinkManager = $menu_link_manager;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('menu.link_tree'),
$container->get('menu.active_trail'),
$container->get('entity_type.manager'),
$container->get('current_route_match'),
$container->get('plugin.manager.menu.link'),
);
}

/**
* {@inheritdoc}
*/
public function build() {
$menu_name = $this->getMenuName();
// If unable to determine the menu, prevent the block from rendering.
if (!$menu_name) {
return [];
}

// Get block configuration and menu tree parameters.
$level = $this->configuration['level'];
$depth = $this->configuration['depth'];
$relative_visibility = $this->configuration['relative_visibility'] ?? FALSE;
$parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name);

// If active trail is empty, we might be on a group news item, that
// is not included in the menu tree. This fallback handling for empty
// active trail is the only abstraction from the parent class build method.
if (empty(array_filter($parameters->activeTrail))) {
$parameters->setActiveTrail($this->getActiveTrailForNewsItem($menu_name));
}

// Adjust the menu tree parameters based on the block's configuration.
$parameters->setMinDepth($level);

// Adjust menu root in cases where the active menu item is below root and
// only a subset of the full menu is to be shown, or possibly not at all.
$relative_level = count($parameters->activeTrail);
if ($level > 1 || ($relative_visibility && $relative_level > $level)) {
$menu_trail_ids = array_reverse(array_values($parameters->activeTrail));
// For relative visibility we reset the root relative to the active
// menu item.
if ($relative_visibility) {
$menu_root = $menu_trail_ids[$relative_level - 2];
$parameters->setRoot($menu_root);
}
// For absolute visibility, we reset root relative to the original and
// adjust the minimum depth.
elseif ($relative_level >= $level) {
$menu_root = $menu_trail_ids[$level - 1];
$parameters->setRoot($menu_root)->setMinDepth(1);
}
// If the active menu item is not at or above the visibility level, and
// relative visibility is not in play, then do not show the menu.
else {
return [];
}
}

// When the depth is configured to zero, there is no depth limit. When depth
// is non-zero, it indicates the number of levels that must be displayed.
// Hence, this is a relative depth that we must convert to an actual
// (absolute) depth, that may never exceed the maximum depth.
if ($depth > 0) {
$relative_depth = $relative_visibility ? $level + $depth - 1 : $depth;
$parameters->setMaxDepth(\min($relative_depth, $this->menuTree->maxDepth()));
}

// If expandedParents is empty, the whole menu tree is built.
if ($this->configuration['expand_all_items']) {
$parameters->expandedParents = [];
}

$tree = $this->menuTree->load($menu_name, $parameters);
$tree = $this->menuTree->transform($tree, $this->getMenuManipulators());
$build = $this->menuTree->build($tree);
$menu_instance = $this->getMenuInstance();
$build['#group_content_menu'] = [
'menu_name' => $menu_name,
'theme_hook_suggestion' => $this->configuration['theme_hook_suggestion'],
];
if ($menu_instance instanceof GroupContentMenuInterface) {
$build['#group_content_menu']['group_content_menu_type'] = $menu_instance->bundle();
$build['#contextual_links']['group_menu'] = [
'route_parameters' => [
'group' => $this->getContext('group')->getContextData()->getValue()->id(),
'group_content_menu' => $menu_instance->id(),
],
];

}
$build['#theme'] = 'menu';
return $build;
}

/**
* Get the active trail for a news item node.
*
* @param string $menu_name
* The menu name.
*
* @return array
* The active trail.
*/
private function getActiveTrailForNewsItem(string $menu_name): array {
$active_trail = ['' => ''];

// Fetch current node and only continue if it's a news_item.
$node = $this->routeMatch->getParameter('node');
if (!$node || !($node instanceof NodeInterface) || $node->bundle() !== 'news_item') {
return $active_trail;
}

// Fetch current group from plugin context.
$group = $this->getContextValue('group');
if (!$group || !($group instanceof GroupInterface)) {
return $active_trail;
}

// Fetch news parent from group.
$newsParent = NULL;
if ($group->hasField('field_group_news_parent')) {
$newsParent = $group->get('field_group_news_parent')->entity;
}
if (!$newsParent || !($newsParent instanceof NodeInterface)) {
return $active_trail;
}

// Build active trail from news parent.
$found = NULL;
$links = [];
$route_name = $newsParent->toUrl()->getRouteName();
if ($route_name) {
$route_parameters = $newsParent->toUrl()->getRouteParameters();

// Load links matching this route.
$links = $this->menuLinkManager->loadLinksByRoute($route_name, $route_parameters, $menu_name);
}
if ($links) {
$found = reset($links);
}
if ($found && $parents = $this->menuLinkManager->getParentIds($found->getPluginId())) {
$active_trail = $parents + $active_trail;
}

return $active_trail;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ class GroupMenuFilterByLanguageTest extends EntityKernelTestBase {
'content_translation',
'link',
'helfi_group',
'helfi_tpr',
'menu_block_current_language',
'language',
'locale',
'flexible_permissions',
'group',
];

/**
Expand Down
Loading