Chris@0: entityManager = $entity_manager; Chris@0: $this->paths = new \SplObjectStorage(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { Chris@0: return new static($container->get('entity.manager')); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLangcode(Request $request = NULL) { Chris@0: $langcode = $request->query->get(static::QUERY_PARAMETER); Chris@0: Chris@0: $language_enabled = array_key_exists($langcode, $this->languageManager->getLanguages()); Chris@0: return $language_enabled ? $langcode : NULL; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) { Chris@0: // If appropriate, process outbound to add a query parameter to the url and Chris@0: // remove the language option, so that url negotiator does not rewrite the Chris@0: // url. Chris@0: Chris@0: // First, check if processing conditions are met. Chris@0: if (!($request && !empty($options['route']) && $this->hasLowerLanguageNegotiationWeight() && $this->meetsContentEntityRoutesCondition($options['route'], $request))) { Chris@0: return $path; Chris@0: } Chris@0: Chris@0: if (isset($options['language']) || $langcode = $this->getLangcode($request)) { Chris@0: // If the language option is set, unset it, so that the url language Chris@0: // negotiator does not rewrite the url. Chris@0: if (isset($options['language'])) { Chris@0: $langcode = $options['language']->getId(); Chris@0: unset($options['language']); Chris@0: } Chris@0: Chris@0: if (!isset($options['query'][static::QUERY_PARAMETER])) { Chris@0: $options['query'][static::QUERY_PARAMETER] = $langcode; Chris@0: } Chris@0: Chris@0: if ($bubbleable_metadata) { Chris@0: // Cached URLs that have been processed by this outbound path Chris@0: // processor must be: Chris@0: $bubbleable_metadata Chris@0: // - varied by the content language query parameter. Chris@0: ->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]); Chris@0: } Chris@0: } Chris@0: Chris@0: return $path; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguageSwitchLinks(Request $request, $type, Url $url) { Chris@0: $links = []; Chris@0: $query = []; Chris@0: parse_str($request->getQueryString(), $query); Chris@0: Chris@0: foreach ($this->languageManager->getNativeLanguages() as $language) { Chris@0: $langcode = $language->getId(); Chris@0: $query[static::QUERY_PARAMETER] = $langcode; Chris@0: $links[$langcode] = [ Chris@0: 'url' => $url, Chris@0: 'title' => $language->getName(), Chris@0: 'attributes' => ['class' => ['language-link']], Chris@0: 'query' => $query, Chris@0: ]; Chris@0: } Chris@0: Chris@0: return $links; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determines if content entity language negotiator has higher priority. Chris@0: * Chris@0: * The content entity language negotiator having higher priority than the url Chris@0: * language negotiator, is a criteria in Chris@0: * \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::processOutbound(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if the the content entity language negotiator has higher priority Chris@0: * than the url language negotiator, FALSE otherwise. Chris@0: */ Chris@0: protected function hasLowerLanguageNegotiationWeight() { Chris@0: if (!isset($this->hasLowerLanguageNegotiationWeightResult)) { Chris@0: // Only run if the LanguageNegotiationContentEntity outbound function is Chris@0: // being executed before the outbound function of LanguageNegotiationUrl. Chris@0: $content_method_weights = $this->config->get('language.types')->get('negotiation.language_content.enabled') ?: []; Chris@0: Chris@0: // Check if the content language is configured to be dependent on the Chris@0: // url negotiator directly or indirectly over the interface negotiator. Chris@0: if (isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) && ($content_method_weights[static::METHOD_ID] > $content_method_weights[LanguageNegotiationUrl::METHOD_ID])) { Chris@0: $this->hasLowerLanguageNegotiationWeightResult = FALSE; Chris@0: } Chris@0: else { Chris@0: $check_interface_method = FALSE; Chris@0: if (isset($content_method_weights[LanguageNegotiationUI::METHOD_ID])) { Chris@0: $interface_method_weights = $this->config->get('language.types')->get('negotiation.language_interface.enabled') ?: []; Chris@0: $check_interface_method = isset($interface_method_weights[LanguageNegotiationUrl::METHOD_ID]); Chris@0: } Chris@0: if ($check_interface_method) { Chris@0: $max_weight = $content_method_weights[LanguageNegotiationUI::METHOD_ID]; Chris@0: $max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? max($max_weight, $content_method_weights[LanguageNegotiationUrl::METHOD_ID]) : $max_weight; Chris@0: } Chris@0: else { Chris@0: $max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? $content_method_weights[LanguageNegotiationUrl::METHOD_ID] : PHP_INT_MAX; Chris@0: } Chris@0: Chris@0: $this->hasLowerLanguageNegotiationWeightResult = $content_method_weights[static::METHOD_ID] < $max_weight; Chris@0: } Chris@0: } Chris@0: Chris@0: return $this->hasLowerLanguageNegotiationWeightResult; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determines if content entity route condition is met. Chris@0: * Chris@0: * Requirements: currently being on an content entity route and processing Chris@0: * outbound url pointing to the same content entity. Chris@0: * Chris@0: * @param \Symfony\Component\Routing\Route $outbound_route Chris@0: * The route object for the current outbound url being processed. Chris@0: * @param \Symfony\Component\HttpFoundation\Request $request Chris@0: * The HttpRequest object representing the current request. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if the content entity route condition is met, FALSE otherwise. Chris@0: */ Chris@0: protected function meetsContentEntityRoutesCondition(Route $outbound_route, Request $request) { Chris@0: $outbound_path_pattern = $outbound_route->getPath(); Chris@0: $storage = isset($this->paths[$request]) ? $this->paths[$request] : []; Chris@0: if (!isset($storage[$outbound_path_pattern])) { Chris@0: $storage[$outbound_path_pattern] = FALSE; Chris@0: Chris@0: // Check if the outbound route points to the current entity. Chris@0: if ($content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request)) { Chris@0: if (!empty($this->getContentEntityPaths()[$outbound_path_pattern]) && $content_entity_type_id_for_current_route == $this->getContentEntityPaths()[$outbound_path_pattern]) { Chris@0: $storage[$outbound_path_pattern] = TRUE; Chris@0: } Chris@0: } Chris@0: Chris@0: $this->paths[$request] = $storage; Chris@0: } Chris@0: Chris@0: return $storage[$outbound_path_pattern]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the content entity type ID from the current request for the route. Chris@0: * Chris@0: * @param \Symfony\Component\HttpFoundation\Request $request Chris@0: * The HttpRequest object representing the current request. Chris@0: * Chris@0: * @return string Chris@0: * The entity type ID for the route from the request. Chris@0: */ Chris@0: protected function getContentEntityTypeIdForCurrentRequest(Request $request) { Chris@0: $content_entity_type_id_for_current_route = ''; Chris@0: Chris@0: if ($current_route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) { Chris@0: $current_route_path = $current_route->getPath(); Chris@0: $content_entity_type_id_for_current_route = isset($this->getContentEntityPaths()[$current_route_path]) ? $this->getContentEntityPaths()[$current_route_path] : ''; Chris@0: } Chris@0: Chris@0: return $content_entity_type_id_for_current_route; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the paths for the link templates of all content entities. Chris@0: * Chris@0: * @return array Chris@0: * An array of all content entity type IDs, keyed by the corresponding link Chris@0: * template paths. Chris@0: */ Chris@0: protected function getContentEntityPaths() { Chris@0: if (!isset($this->contentEntityPaths)) { Chris@0: $this->contentEntityPaths = []; Chris@0: $entity_types = $this->entityManager->getDefinitions(); Chris@0: foreach ($entity_types as $entity_type_id => $entity_type) { Chris@0: if ($entity_type->entityClassImplements(ContentEntityInterface::class)) { Chris@0: $entity_paths = array_fill_keys($entity_type->getLinkTemplates(), $entity_type_id); Chris@0: $this->contentEntityPaths = array_merge($this->contentEntityPaths, $entity_paths); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $this->contentEntityPaths; Chris@0: } Chris@0: Chris@0: }