drupalorg-1.0.x-dev/src/DrupalOrgBreadcrumbBuilder.php
src/DrupalOrgBreadcrumbBuilder.php
<?php
namespace Drupal\drupalorg;
use Drupal\contribution_records\SourceLink;
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\ClassResolver;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Provides a breadcrumb builder for nodes in a book.
*/
class DrupalOrgBreadcrumbBuilder implements BreadcrumbBuilderInterface {
use StringTranslationTrait;
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $nodeStorage;
/**
* Constructs the BookBreadcrumbBuilder.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
* @param \Drupal\drupalorg\ProjectService $projectService
* The project service.
* @param \Drupal\Core\DependencyInjection\ClassResolver $classResolver
* The class resolver.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request stack.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
protected ProjectService $projectService,
protected ClassResolver $classResolver,
protected RequestStack $requestStack,
) {
$this->nodeStorage = $entity_type_manager->getStorage('node');
}
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL) {
// Non-node routes.
$route_name = $route_match->getRouteName();
if (in_array($route_name, [
'drupalorg.issue_fork_management',
])) {
return TRUE;
}
// Node routes.
$node = $route_match->getParameter('node');
return $node instanceof NodeInterface && in_array($node->getType(), [
// All content types for which we customise the breadcrumbs.
'documentation',
'guide',
'casestudy',
'contribution_record',
]);
}
/**
* Build breadcrumbs specific to Documentation pages and guides.
*
* @param \Drupal\node\NodeInterface $node
* Node to build breadcrumbs from.
*
* @return array
* Array of links.
*/
protected function buildDocumentationBreadcrumbs(NodeInterface $node): array {
$links = [];
if ($node->hasField('og_group_ref_documentation') && !$node->og_group_ref_documentation->isEmpty()) {
// Find the nearest parent with field_sponsor information, and then check
// the type.
$parent_guide = $node->og_group_ref_documentation->entity;
while ($parent_guide) {
$links[] = $parent_guide->toLink($parent_guide->getTitle());
$parent_guide = ($parent_guide->hasField('og_group_ref_documentation') && !$parent_guide->og_group_ref_documentation->isEmpty()) ?
$parent_guide->og_group_ref_documentation->entity :
NULL;
}
}
// We worked from bottom up, but we need them from top to bottom.
$links = array_reverse($links);
$links[] = Link::createFromRoute($node->getTitle(), '<none>');
return $links;
}
/**
* Build breadcrumbs specific to Case Studies.
*
* @param \Drupal\node\NodeInterface $node
* Node to build breadcrumbs from.
*
* @return array
* Array of links.
*/
protected function buildCasestudyBreadcrumbs(NodeInterface $node): array {
$links = [];
$caseStudyUrl = Url::fromUserInput('/case-studies');
$links[] = Link::fromTextAndUrl(t('Solutions'), $caseStudyUrl);
$links[] = Link::createFromRoute(t('Case Studies'), '<none>');
return $links;
}
/**
* Build breadcrumbs for project-related pages.
*
* @param \Drupal\node\NodeInterface $project
* Project node to build breadcrumbs from.
*
* @return array
* Array of links.
*/
protected function buildProjectBreadcrumbs(NodeInterface $project): array {
$links = [];
if ($project->bundle() !== 'project_core') {
$project_type = $this->projectService->getProjectType($project);
$links[] = Link::fromTextAndUrl($project_type['plural'], $project_type['url']);
}
$links[] = Link::fromTextAndUrl($project->getTitle(), $project->toUrl());
return $links;
}
/**
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {
$breadcrumb = new Breadcrumb();
$links = [];
$route_name = $route_match->getRouteName();
if ($route_name === 'entity.node.canonical') {
$node = $route_match->getParameter('node');
switch ($node->getType()) {
case 'documentation':
case 'guide':
$links = $this->buildDocumentationBreadcrumbs($node);
break;
case 'casestudy':
$links = $this->buildCasestudyBreadcrumbs($node);
break;
case 'contribution_record':
/** @var \Drupal\contribution_records\SourceLink $source_link_instance */
$source_link_instance = $this->classResolver->getInstanceFromDefinition(SourceLink::class);
$source_link = $source_link_instance->setLink($node->get('field_source_link')->getValue()[0]['uri']);
if ($project_node = $source_link->getProject()) {
$links = $this->buildProjectBreadcrumbs($project_node);
}
break;
}
}
elseif ($route_name === 'drupalorg.issue_fork_management') {
$source_link_param = $this->requestStack->getCurrentRequest()->query->get('source_link');
if ($source_link_param) {
/** @var \Drupal\contribution_records\SourceLink $source_link_instance */
$source_link_instance = $this->classResolver->getInstanceFromDefinition(SourceLink::class);
$source_link = $source_link_instance->setLink($source_link_param);
if ($project_node = $source_link->getProject()) {
$links = $this->buildProjectBreadcrumbs($project_node);
}
}
}
array_unshift($links, Link::createFromRoute($this->t('Home'), '<front>'));
$breadcrumb->setLinks($links);
$breadcrumb->addCacheContexts(['url']);
return $breadcrumb;
}
}
