Mercurial > hg > isophonics-drupal-site
comparison core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\language\Plugin\LanguageNegotiation; | |
4 | |
5 use Drupal\Core\Entity\ContentEntityInterface; | |
6 use Drupal\Core\Entity\EntityManagerInterface; | |
7 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; | |
8 use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
9 use Drupal\Core\Render\BubbleableMetadata; | |
10 use Drupal\Core\Url; | |
11 use Drupal\language\LanguageNegotiationMethodBase; | |
12 use Drupal\language\LanguageSwitcherInterface; | |
13 use Symfony\Cmf\Component\Routing\RouteObjectInterface; | |
14 use Symfony\Component\DependencyInjection\ContainerInterface; | |
15 use Symfony\Component\HttpFoundation\Request; | |
16 use Symfony\Component\Routing\Route; | |
17 | |
18 /** | |
19 * Class for identifying the content translation language. | |
20 * | |
21 * @LanguageNegotiation( | |
22 * id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::METHOD_ID, | |
23 * types = {Drupal\Core\Language\LanguageInterface::TYPE_CONTENT}, | |
24 * weight = -9, | |
25 * name = @Translation("Content language"), | |
26 * description = @Translation("Determines the content language from a request parameter."), | |
27 * ) | |
28 */ | |
29 class LanguageNegotiationContentEntity extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface, ContainerFactoryPluginInterface { | |
30 | |
31 /** | |
32 * The language negotiation method ID. | |
33 */ | |
34 const METHOD_ID = 'language-content-entity'; | |
35 | |
36 /** | |
37 * The query string parameter. | |
38 */ | |
39 const QUERY_PARAMETER = 'language_content_entity'; | |
40 | |
41 /** | |
42 * A list of all the link paths of enabled content entities. | |
43 * | |
44 * @var array | |
45 */ | |
46 protected $contentEntityPaths; | |
47 | |
48 /** | |
49 * Static cache for the language negotiation order check. | |
50 * | |
51 * @see \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::hasLowerLanguageNegotiationWeight() | |
52 * | |
53 * @var bool | |
54 */ | |
55 protected $hasLowerLanguageNegotiationWeightResult; | |
56 | |
57 /** | |
58 * Static cache of outbound route paths per request. | |
59 * | |
60 * @var \SplObjectStorage | |
61 */ | |
62 protected $paths; | |
63 | |
64 /** | |
65 * The entity manager. | |
66 * | |
67 * @var \Drupal\Core\Entity\EntityManagerInterface | |
68 */ | |
69 protected $entityManager; | |
70 | |
71 /** | |
72 * Constructs a new LanguageNegotiationContentEntity instance. | |
73 * | |
74 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
75 * The entity manager. | |
76 */ | |
77 public function __construct(EntityManagerInterface $entity_manager) { | |
78 $this->entityManager = $entity_manager; | |
79 $this->paths = new \SplObjectStorage(); | |
80 } | |
81 | |
82 /** | |
83 * {@inheritdoc} | |
84 */ | |
85 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
86 return new static($container->get('entity.manager')); | |
87 } | |
88 | |
89 /** | |
90 * {@inheritdoc} | |
91 */ | |
92 public function getLangcode(Request $request = NULL) { | |
93 $langcode = $request->query->get(static::QUERY_PARAMETER); | |
94 | |
95 $language_enabled = array_key_exists($langcode, $this->languageManager->getLanguages()); | |
96 return $language_enabled ? $langcode : NULL; | |
97 } | |
98 | |
99 /** | |
100 * {@inheritdoc} | |
101 */ | |
102 public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) { | |
103 // If appropriate, process outbound to add a query parameter to the url and | |
104 // remove the language option, so that url negotiator does not rewrite the | |
105 // url. | |
106 | |
107 // First, check if processing conditions are met. | |
108 if (!($request && !empty($options['route']) && $this->hasLowerLanguageNegotiationWeight() && $this->meetsContentEntityRoutesCondition($options['route'], $request))) { | |
109 return $path; | |
110 } | |
111 | |
112 if (isset($options['language']) || $langcode = $this->getLangcode($request)) { | |
113 // If the language option is set, unset it, so that the url language | |
114 // negotiator does not rewrite the url. | |
115 if (isset($options['language'])) { | |
116 $langcode = $options['language']->getId(); | |
117 unset($options['language']); | |
118 } | |
119 | |
120 if (!isset($options['query'][static::QUERY_PARAMETER])) { | |
121 $options['query'][static::QUERY_PARAMETER] = $langcode; | |
122 } | |
123 | |
124 if ($bubbleable_metadata) { | |
125 // Cached URLs that have been processed by this outbound path | |
126 // processor must be: | |
127 $bubbleable_metadata | |
128 // - varied by the content language query parameter. | |
129 ->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]); | |
130 } | |
131 } | |
132 | |
133 return $path; | |
134 } | |
135 | |
136 /** | |
137 * {@inheritdoc} | |
138 */ | |
139 public function getLanguageSwitchLinks(Request $request, $type, Url $url) { | |
140 $links = []; | |
141 $query = []; | |
142 parse_str($request->getQueryString(), $query); | |
143 | |
144 foreach ($this->languageManager->getNativeLanguages() as $language) { | |
145 $langcode = $language->getId(); | |
146 $query[static::QUERY_PARAMETER] = $langcode; | |
147 $links[$langcode] = [ | |
148 'url' => $url, | |
149 'title' => $language->getName(), | |
150 'attributes' => ['class' => ['language-link']], | |
151 'query' => $query, | |
152 ]; | |
153 } | |
154 | |
155 return $links; | |
156 } | |
157 | |
158 /** | |
159 * Determines if content entity language negotiator has higher priority. | |
160 * | |
161 * The content entity language negotiator having higher priority than the url | |
162 * language negotiator, is a criteria in | |
163 * \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::processOutbound(). | |
164 * | |
165 * @return bool | |
166 * TRUE if the the content entity language negotiator has higher priority | |
167 * than the url language negotiator, FALSE otherwise. | |
168 */ | |
169 protected function hasLowerLanguageNegotiationWeight() { | |
170 if (!isset($this->hasLowerLanguageNegotiationWeightResult)) { | |
171 // Only run if the LanguageNegotiationContentEntity outbound function is | |
172 // being executed before the outbound function of LanguageNegotiationUrl. | |
173 $content_method_weights = $this->config->get('language.types')->get('negotiation.language_content.enabled') ?: []; | |
174 | |
175 // Check if the content language is configured to be dependent on the | |
176 // url negotiator directly or indirectly over the interface negotiator. | |
177 if (isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) && ($content_method_weights[static::METHOD_ID] > $content_method_weights[LanguageNegotiationUrl::METHOD_ID])) { | |
178 $this->hasLowerLanguageNegotiationWeightResult = FALSE; | |
179 } | |
180 else { | |
181 $check_interface_method = FALSE; | |
182 if (isset($content_method_weights[LanguageNegotiationUI::METHOD_ID])) { | |
183 $interface_method_weights = $this->config->get('language.types')->get('negotiation.language_interface.enabled') ?: []; | |
184 $check_interface_method = isset($interface_method_weights[LanguageNegotiationUrl::METHOD_ID]); | |
185 } | |
186 if ($check_interface_method) { | |
187 $max_weight = $content_method_weights[LanguageNegotiationUI::METHOD_ID]; | |
188 $max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? max($max_weight, $content_method_weights[LanguageNegotiationUrl::METHOD_ID]) : $max_weight; | |
189 } | |
190 else { | |
191 $max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? $content_method_weights[LanguageNegotiationUrl::METHOD_ID] : PHP_INT_MAX; | |
192 } | |
193 | |
194 $this->hasLowerLanguageNegotiationWeightResult = $content_method_weights[static::METHOD_ID] < $max_weight; | |
195 } | |
196 } | |
197 | |
198 return $this->hasLowerLanguageNegotiationWeightResult; | |
199 } | |
200 | |
201 /** | |
202 * Determines if content entity route condition is met. | |
203 * | |
204 * Requirements: currently being on an content entity route and processing | |
205 * outbound url pointing to the same content entity. | |
206 * | |
207 * @param \Symfony\Component\Routing\Route $outbound_route | |
208 * The route object for the current outbound url being processed. | |
209 * @param \Symfony\Component\HttpFoundation\Request $request | |
210 * The HttpRequest object representing the current request. | |
211 * | |
212 * @return bool | |
213 * TRUE if the content entity route condition is met, FALSE otherwise. | |
214 */ | |
215 protected function meetsContentEntityRoutesCondition(Route $outbound_route, Request $request) { | |
216 $outbound_path_pattern = $outbound_route->getPath(); | |
217 $storage = isset($this->paths[$request]) ? $this->paths[$request] : []; | |
218 if (!isset($storage[$outbound_path_pattern])) { | |
219 $storage[$outbound_path_pattern] = FALSE; | |
220 | |
221 // Check if the outbound route points to the current entity. | |
222 if ($content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request)) { | |
223 if (!empty($this->getContentEntityPaths()[$outbound_path_pattern]) && $content_entity_type_id_for_current_route == $this->getContentEntityPaths()[$outbound_path_pattern]) { | |
224 $storage[$outbound_path_pattern] = TRUE; | |
225 } | |
226 } | |
227 | |
228 $this->paths[$request] = $storage; | |
229 } | |
230 | |
231 return $storage[$outbound_path_pattern]; | |
232 } | |
233 | |
234 /** | |
235 * Returns the content entity type ID from the current request for the route. | |
236 * | |
237 * @param \Symfony\Component\HttpFoundation\Request $request | |
238 * The HttpRequest object representing the current request. | |
239 * | |
240 * @return string | |
241 * The entity type ID for the route from the request. | |
242 */ | |
243 protected function getContentEntityTypeIdForCurrentRequest(Request $request) { | |
244 $content_entity_type_id_for_current_route = ''; | |
245 | |
246 if ($current_route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) { | |
247 $current_route_path = $current_route->getPath(); | |
248 $content_entity_type_id_for_current_route = isset($this->getContentEntityPaths()[$current_route_path]) ? $this->getContentEntityPaths()[$current_route_path] : ''; | |
249 } | |
250 | |
251 return $content_entity_type_id_for_current_route; | |
252 } | |
253 | |
254 /** | |
255 * Returns the paths for the link templates of all content entities. | |
256 * | |
257 * @return array | |
258 * An array of all content entity type IDs, keyed by the corresponding link | |
259 * template paths. | |
260 */ | |
261 protected function getContentEntityPaths() { | |
262 if (!isset($this->contentEntityPaths)) { | |
263 $this->contentEntityPaths = []; | |
264 $entity_types = $this->entityManager->getDefinitions(); | |
265 foreach ($entity_types as $entity_type_id => $entity_type) { | |
266 if ($entity_type->entityClassImplements(ContentEntityInterface::class)) { | |
267 $entity_paths = array_fill_keys($entity_type->getLinkTemplates(), $entity_type_id); | |
268 $this->contentEntityPaths = array_merge($this->contentEntityPaths, $entity_paths); | |
269 } | |
270 } | |
271 } | |
272 | |
273 return $this->contentEntityPaths; | |
274 } | |
275 | |
276 } |