diff --git a/entity_mesh.services.yml b/entity_mesh.services.yml index 2b042efbdcc8bb959ddc403af20f9ba078194a36..b7765743705d9d49e4d86adb10d9f815a854a978 100644 --- a/entity_mesh.services.yml +++ b/entity_mesh.services.yml @@ -5,13 +5,27 @@ services: arguments: [ 'entity_mesh' ] entity_mesh.repository: class: Drupal\entity_mesh\Repository - arguments: [ '@database', '@entity_mesh.logger', '@request_stack', '@entity_type.manager', '@entity_field.manager' ] + arguments: + - '@database' + - '@entity_mesh.logger' + - '@request_stack' + - '@entity_type.manager' + - '@entity_field.manager' + - '@config.factory' entity_mesh.menu: class: Drupal\entity_mesh\Menu arguments: ['@entity_mesh.repository', '@entity_type.manager', '@language_manager', '@config.factory'] entity_mesh.entity_render: class: Drupal\entity_mesh\EntityRender - arguments: ['@entity_mesh.repository', '@entity_type.manager', '@language_manager', '@config.factory', '@renderer', '@account_switcher', '@entity_mesh.language_negotiator_switcher'] + arguments: + - '@entity_mesh.repository' + - '@entity_type.manager' + - '@language_manager' + - '@config.factory' + - '@renderer' + - '@account_switcher' + - '@entity_mesh.language_negotiator_switcher' + - '@module_handler' entity_mesh.language_negotiator_switcher: class: Drupal\entity_mesh\Language\LanguageNegotiatorSwitcher arguments: ['@language_manager', '@module_handler', '@string_translation', '@entity_mesh.static_language_negotiator'] diff --git a/src/EntityRender.php b/src/EntityRender.php index 2b231329a08baeef9af0bb9db8953e055c66d798..8c784393b2d2ff5fcd9d001aa42170babd0889ed 100644 --- a/src/EntityRender.php +++ b/src/EntityRender.php @@ -7,6 +7,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Database\StatementInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountSwitcherInterface; @@ -40,12 +41,13 @@ class EntityRender extends Entity { */ protected LanguageNegotiatorSwitcher $languageNegotiatorSwitcher; + /** - * Url language prefixes. + * Module handler. * - * @var array + * @var \Drupal\Core\Extension\ModuleHandlerInterface */ - protected array $prefixes; + protected ModuleHandlerInterface $moduleHandler; /** * Constructs a Menu object. @@ -57,13 +59,15 @@ class EntityRender extends Entity { * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The language manager. + * The config factory. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer manager. * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher * The AccountSwitcher manager. * @param \Drupal\entity_mesh\Language\LanguageNegotiatorSwitcher $language_negotiator_switcher * The language negotiator switcher service. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * Module handler. */ public function __construct( RepositoryInterface $entity_mesh_repository, @@ -73,13 +77,14 @@ class EntityRender extends Entity { RendererInterface $renderer, AccountSwitcherInterface $account_switcher, LanguageNegotiatorSwitcher $language_negotiator_switcher, + ModuleHandlerInterface $module_handler, ) { - parent::__construct($entity_mesh_repository, $entity_type_manager, $language_manager, $config_factory); + parent::__construct($entity_mesh_repository, $entity_type_manager, $language_manager, $config_factory, $module_handler); $this->renderer = $renderer; $this->accountSwitcher = $account_switcher; $this->type = 'entity_render'; $this->languageNegotiatorSwitcher = $language_negotiator_switcher; - $this->prefixes = $config_factory->get('language.negotiation')->get('url.prefixes'); + $this->moduleHandler = $module_handler; } /** @@ -194,6 +199,7 @@ class EntityRender extends Entity { $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId()); $pre_render = $view_builder->view($entity, $view_mode, $langcode); + // @todo Review render plain is deprecated. $render_output = $this->renderer->renderPlain($pre_render); // Switches back to the current language: @@ -243,7 +249,7 @@ class EntityRender extends Entity { $target->setCategory('link'); if ($target->getLinkType() == 'internal' && $this->ifProcessInternalTarget()) { - $this->processInternalHref($target, $source->getSourceEntityLangcode() ?? ''); + $this->processInternalHref($target); // Check that the target is not the source to avoid circular references. if ($source->getSourceEntityType() == $target->getEntityType() @@ -263,34 +269,29 @@ class EntityRender extends Entity { * * @param \Drupal\entity_mesh\TargetInterface $target * The target. - * @param string $langcode - * The language code. */ - protected function processInternalHref(TargetInterface $target, string $langcode) { - $found_data = FALSE; - // Remove the langcode to find the alias. - // @todo take into account other languages also to support cross-language paths. - // @todo this is the default URL creation, change with the real one. - $prefix = $this->prefixes[$langcode] ?? $langcode; - $alias = str_replace('/' . $prefix, '', $target->getPath() ?? ''); + protected function processInternalHref(TargetInterface $target) { + + $alias = $this->entityMeshRepository->getPathWithoutLangPrefix($target->getPath()); + $target->setEntityLangcode($this->entityMeshRepository->getLangcodeFromPath($target->getPath())); - $found_data = $this->setDataIfRedirection($alias, $langcode, $target); + $found_data = $this->setDataIfRedirection($alias, $target); // Get the info from alias. // This method is quicker than using the router service, // so firstly apply this system. if (!$found_data) { - $found_data = $this->setDataTargetFromAliasIfExists($alias, $langcode, $target); + $found_data = $this->setDataTargetFromAliasIfExists($alias, $target); } // If this method not found the data, use the router service. if (!$found_data) { - $this->setDataTargetFromRoute($langcode, $target); + $this->setDataTargetFromRoute($target); } // If the target is set as link broken maybe is because is a file url. if ($target->getSubcategory() === 'broken-link') { - $this->setDataTargetIfFileUrl($langcode, $target); + $this->setDataTargetIfFileUrl($target); } $this->setBundleInTarget($target); @@ -328,15 +329,14 @@ class EntityRender extends Entity { * * @param string $alias * The alias. - * @param string $langcode - * The language code. * @param TargetInterface $target * The target. * * @return bool * If is a redirection. */ - protected function setDataIfRedirection(string &$alias, string $langcode, TargetInterface $target): bool { + protected function setDataIfRedirection(string &$alias, TargetInterface $target): bool { + $langcode = $target->getEntityLangcode() ?? ''; // Check if is a redirected link. $uri = $this->entityMeshRepository->ifRedirectionForPath($alias, $langcode); if ($uri === NULL) { @@ -361,9 +361,7 @@ class EntityRender extends Entity { return TRUE; } - if (str_starts_with($uri, "/$langcode")) { - $uri = substr($uri, strlen("/$langcode")); - } + $uri = $this->entityMeshRepository->getPathWithoutLangPrefix($uri); if ($type === '') { $alias = $uri; @@ -375,7 +373,7 @@ class EntityRender extends Entity { $uri = $possible_alias->path; } - return $this->processInternalPaths($uri, $langcode, $target); + return $this->processInternalPaths($uri, $target); } /** @@ -383,21 +381,23 @@ class EntityRender extends Entity { * * @param string $alias * Alias. - * @param string $langcode - * Langcode. * @param TargetInterface $target * Target. * * @return bool * If this method found the data. */ - protected function setDataTargetFromAliasIfExists(string $alias, string $langcode, TargetInterface $target): bool { + protected function setDataTargetFromAliasIfExists(string $alias, TargetInterface $target): bool { // Get the info from alias. - $record = $this->getPathFromAliasTables($alias, $langcode); - if (!$record) { - return FALSE; + $langcode = $target->getEntityLangcode(); + if ($this->moduleHandler->moduleExists('path_alias')) { + $record = $this->getPathFromAliasTables($alias, $langcode); + if ($record) { + return $this->processInternalPaths($record->path, $target); + } } - return $this->processInternalPaths($record->path, $langcode, $target); + return $this->processInternalPaths($alias, $target); + } /** @@ -405,15 +405,13 @@ class EntityRender extends Entity { * * @param string $path * The path. - * @param string $langcode - * The language code. * @param TargetInterface $target * The target. * * @return bool * If the path is processed. */ - protected function processInternalPaths(string $path, string $langcode, TargetInterface $target) { + protected function processInternalPaths(string $path, TargetInterface $target) { $path = explode('/', ltrim($path, '/')); if (count($path) < 2) { return FALSE; @@ -425,25 +423,21 @@ class EntityRender extends Entity { if (isset($path[2]) && is_numeric($path[2])) { $target->setEntityType($path[0]); $target->setEntityId((string) $path[2]); - $target->setEntityLangcode($langcode); return TRUE; } $target->setEntityType($path[0]); $target->setEntityId((string) ($path[1] ?? '')); - $target->setEntityLangcode($langcode); return TRUE; } /** * Set data about the target from route. * - * @param string $langcode - * The language code. * @param TargetInterface $target * The target. */ - protected function setDataTargetFromRoute($langcode, $target) { + protected function setDataTargetFromRoute($target) { if (empty($target->getPath())) { return; } @@ -461,7 +455,6 @@ class EntityRender extends Entity { if (isset($route_match['view_id'])) { $target->setEntityType('view'); $target->setEntityId($route_match['view_id'] . '.' . $route_match['display_id']); - $target->setEntityLangcode($langcode); return; } @@ -475,19 +468,16 @@ class EntityRender extends Entity { } $target->setEntityType($entity); $target->setEntityId((string) $entity_id); - $target->setEntityLangcode($langcode); } } /** * Set data target if file url. * - * @param string $langcode - * The language code. * @param TargetInterface $target * The target. */ - protected function setDataTargetIfFileUrl($langcode, $target) { + protected function setDataTargetIfFileUrl($target) { // Check if the path is a file. $path = $this->entityMeshRepository->getPathFromFileUrl($target->getPath() ?? ''); if (!$path) { @@ -509,14 +499,12 @@ class EntityRender extends Entity { if (!$media) { $target->setEntityType($file->getEntityTypeId()); $target->setEntityId((string) $file->id()); - $target->setEntityLangcode($langcode); return; } $target->setEntityType($media->getEntityTypeId()); $target->setEntityBundle($media->bundle()); $target->setEntityId((string) $media->id()); - $target->setEntityLangcode($langcode); } /** @@ -537,7 +525,9 @@ class EntityRender extends Entity { ->condition('alias', $alias) ->condition('path', $alias); $query->condition($or); - $query->condition('langcode', $langcode); + if (!empty($langcode)) { + $query->condition('langcode', $langcode); + } $result = $query->execute(); if (!$result instanceof StatementInterface) { return NULL; diff --git a/src/Repository.php b/src/Repository.php index 6f78a514106c835232294d2e5cad1f92a3cc9d16..8e601054764c376fced853701b20891fdfef8799 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -3,6 +3,7 @@ namespace Drupal\entity_mesh; use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; @@ -14,7 +15,6 @@ use Drupal\redirect\Exception\RedirectLoopException; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RequestStack; - /** * Service to perform database operations. */ @@ -55,6 +55,13 @@ class Repository implements RepositoryInterface { */ protected $entityFieldManager; + /** + * Url language prefixes. + * + * @var array + */ + protected array $prefixes; + /** * Constructs a new Repository object. * @@ -68,13 +75,23 @@ class Repository implements RepositoryInterface { * The entity type manager. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager * The entity field manager. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * Config factory. */ - public function __construct(Connection $database, LoggerInterface $logger, RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) { + public function __construct( + Connection $database, + LoggerInterface $logger, + RequestStack $request_stack, + EntityTypeManagerInterface $entity_type_manager, + EntityFieldManagerInterface $entity_field_manager, + ConfigFactoryInterface $config_factory, + ) { $this->database = $database; $this->logger = $logger; $this->requestStack = $request_stack; $this->entityTypeManager = $entity_type_manager; $this->entityFieldManager = $entity_field_manager; + $this->prefixes = $config_factory->get('language.negotiation')->get('url.prefixes'); } /** @@ -398,7 +415,8 @@ class Repository implements RepositoryInterface { try { /** @var \Drupal\redirect\Entity\Redirect $redirect_object */ $redirect_object = $redirect_repository->findMatchingRedirect($path, [], $langcode); - } catch (RedirectLoopException $e) { + } + catch (RedirectLoopException $e) { $this->logger->error($e->getMessage()); return NULL; } @@ -442,4 +460,66 @@ class Repository implements RepositoryInterface { return $uri; } + /** + * {@inheritdoc} + */ + public function getPathWithoutLangPrefix($path) { + $prefix = $this->getLangPrefixFromPath($path); + if (empty($prefix)) { + return $path; + } + $path = ltrim($path, '/'); + return str_replace($prefix, '', $path ?? ''); + } + + /** + * {@inheritdoc} + */ + public function getLangcodeFromPath($path): ?string { + $result = NULL; + if ($langcode_and_prefix = $this->getLangcodeAndPrefixFromPath($path)) { + $result = $langcode_and_prefix['langcode'] ?? NULL; + } + return $result; + } + + /** + * {@inheritdoc} + */ + public function getLangPrefixFromPath($path): ?string { + $result = NULL; + if ($langcode_and_prefix = $this->getLangcodeAndPrefixFromPath($path)) { + $result = $langcode_and_prefix['prefix'] ?? NULL; + } + return $result; + } + + /** + * Get the langcode and prefix from the path. + * + * @param string $path + * The path. + * + * @return array|null + * The langcode and prefix or NULL. + */ + protected function getLangcodeAndPrefixFromPath($path): ?array { + // If the prefixes ares empty, it is not a multilanguage site, so + // there are not langcode to return. + if (empty($this->prefixes)) { + return NULL; + } + $path = '/' . ltrim($path, '/'); + foreach ($this->prefixes as $langcode => $prefix) { + if ($prefix && str_starts_with($path, '/' . $prefix . '/')) { + return [ + 'langcode' => $langcode, + 'prefix' => $prefix, + ]; + } + } + + return NULL; + } + } diff --git a/src/RepositoryInterface.php b/src/RepositoryInterface.php index 5d56a2d8d2531d1563a8d1d6121bf9ac4508c0e2..b2d57577d1dbcbf6bd2007ab1ca6a1cdd8044440 100644 --- a/src/RepositoryInterface.php +++ b/src/RepositoryInterface.php @@ -143,4 +143,37 @@ interface RepositoryInterface { */ public function ifRedirectionForPath(string $path, string $langcode, int $count = 0); + /** + * Get the path without the lang prefix. + * + * @param string $path + * The path. + * + * @return string + * The path without the lang prefix. + */ + public function getPathWithoutLangPrefix($path); + + /** + * Get the langcode from the path. + * + * @param string $path + * The path. + * + * @return string|null + * The langcode or NULL. + */ + public function getLangcodeFromPath($path): ?string; + + /** + * Get the lang prefix from the path. + * + * @param string $path + * The path. + * + * @return string|null + * The langcode or NULL. + */ + public function getLangPrefixFromPath($path): ?string; + }