[!!!] Extbase: Reintegrating branch "dispatcher" to trunk. Resolves: #10605
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / MVC / Web / Routing / UriBuilder.php
1 <?php
2 /* *
3 * This script is part of the TYPO3 project - inspiring people to share! *
4 * *
5 * TYPO3 is free software; you can redistribute it and/or modify it under *
6 * the terms of the GNU General Public License version 2 as published by *
7 * the Free Software Foundation. *
8 * *
9 * This script is distributed in the hope that it will be useful, but *
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
11 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
12 * Public License for more details. *
13 * */
14
15 /**
16 * An URI Builder
17 *
18 * @package Extbase
19 * @subpackage MVC\Web\Routing
20 * @version $Id$
21 * @api
22 */
23 class Tx_Extbase_MVC_Web_Routing_UriBuilder {
24
25 /**
26 * An instance of tslib_cObj
27 *
28 * @var tslib_cObj
29 */
30 protected $contentObject;
31
32 /**
33 * @var Tx_Extbase_MVC_Web_Request
34 */
35 protected $request;
36
37 /**
38 * @var array
39 */
40 protected $arguments = array();
41
42 /**
43 * Arguments which have been used for building the last URI
44 * @var array
45 */
46 protected $lastArguments = array();
47
48 /**
49 * @var string
50 */
51 protected $section = '';
52
53 /**
54 * @var boolean
55 */
56 protected $createAbsoluteUri = FALSE;
57
58 /**
59 * @var boolean
60 */
61 protected $addQueryString = FALSE;
62
63 /**
64 * @var array
65 */
66 protected $argumentsToBeExcludedFromQueryString = array();
67
68 /**
69 * @var boolean
70 */
71 protected $linkAccessRestrictedPages = FALSE;
72
73 /**
74 * @var integer
75 */
76 protected $targetPageUid = NULL;
77
78 /**
79 * @var integer
80 */
81 protected $targetPageType = 0;
82
83 /**
84 * @var boolean
85 */
86 protected $noCache = FALSE;
87
88 /**
89 * @var boolean
90 */
91 protected $useCacheHash = TRUE;
92
93 /**
94 * @var string
95 */
96 protected $format = '';
97
98 /**
99 * @var string
100 */
101 protected $argumentPrefix = NULL;
102
103 /**
104 * Constructs this URI Helper
105 */
106 public function __construct() {
107 // TODO shouldn't we retrieve the current cObject from the request?
108 $this->contentObject = t3lib_div::makeInstance('tslib_cObj');
109 }
110
111 /**
112 * Sets the current request
113 *
114 * @param Tx_Extbase_MVC_Web_Request $request
115 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
116 */
117 public function setRequest(Tx_Extbase_MVC_Web_Request $request) {
118 $this->request = $request;
119 return $this;
120 }
121
122 /**
123 * @return Tx_Extbase_MVC_Web_Request
124 */
125 public function getRequest() {
126 return $this->request;
127 }
128
129 /**
130 * Additional query parameters.
131 * If you want to "prefix" arguments, you can pass in multidimensional arrays:
132 * array('prefix1' => array('foo' => 'bar')) gets "&prefix1[foo]=bar"
133 *
134 * @param array $arguments
135 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
136 * @api
137 */
138 public function setArguments(array $arguments) {
139 $this->arguments = $arguments;
140 return $this;
141 }
142
143 /**
144 * @return array
145 * @api
146 */
147 public function getArguments() {
148 return $this->arguments;
149 }
150
151 /**
152 * If specified, adds a given HTML anchor to the URI (#...)
153 *
154 * @param string $section
155 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
156 * @api
157 */
158 public function setSection($section) {
159 $this->section = $section;
160 return $this;
161 }
162
163 /**
164 * @return string
165 * @api
166 */
167 public function getSection() {
168 return $this->section;
169 }
170
171 /**
172 * Specifies the format of the target (e.g. "html" or "xml")
173 *
174 * @param string $section
175 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
176 * @api
177 */
178 public function setFormat($format) {
179 $this->format = $format;
180 return $this;
181 }
182
183 /**
184 * @return string
185 * @api
186 */
187 public function getFormat() {
188 return $this->format;
189 }
190
191 /**
192 * If set, the URI is prepended with the current base URI. Defaults to FALSE.
193 *
194 * @param boolean $createAbsoluteUri
195 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
196 * @api
197 */
198 public function setCreateAbsoluteUri($createAbsoluteUri) {
199 $this->createAbsoluteUri = $createAbsoluteUri;
200 return $this;
201 }
202
203 /**
204 * @return boolean
205 * @api
206 */
207 public function getCreateAbsoluteUri() {
208 return $this->createAbsoluteUri;
209 }
210
211 /**
212 * If set, the current query parameters will be merged with $this->arguments. Defaults to FALSE.
213 *
214 * @param boolean $addQueryString
215 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
216 * @api
217 * @see TSref/typolink.addQueryString
218 */
219 public function setAddQueryString($addQueryString) {
220 $this->addQueryString = (boolean)$addQueryString;
221 return $this;
222 }
223
224 /**
225 * @return boolean
226 * @api
227 */
228 public function getAddQueryString() {
229 return $this->addQueryString;
230 }
231
232 /**
233 * A list of arguments to be excluded from the query parameters
234 * Only active if addQueryString is set
235 *
236 * @param array $argumentsToBeExcludedFromQueryString
237 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
238 * @api
239 * @see TSref/typolink.addQueryString.exclude
240 * @see setAddQueryString()
241 */
242 public function setArgumentsToBeExcludedFromQueryString(array $argumentsToBeExcludedFromQueryString) {
243 $this->argumentsToBeExcludedFromQueryString = $argumentsToBeExcludedFromQueryString;
244 return $this;
245 }
246
247 /**
248 * @return array
249 * @api
250 */
251 public function getArgumentsToBeExcludedFromQueryString() {
252 return $this->argumentsToBeExcludedFromQueryString;
253 }
254
255 /**
256 * Specifies the prefix to be used for all arguments.
257 *
258 * @param string $argumentPrefix
259 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
260 */
261 public function setArgumentPrefix($argumentPrefix) {
262 $this->argumentPrefix = (string)$argumentPrefix;
263 return $this;
264 }
265
266 /**
267 * @return string
268 */
269 public function getArgumentPrefix() {
270 return $this->argumentPrefix;
271 }
272
273 /**
274 * If set, URIs for pages without access permissions will be created
275 *
276 * @param boolean $linkAccessRestrictedPages
277 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
278 * @api
279 */
280 public function setLinkAccessRestrictedPages($linkAccessRestrictedPages) {
281 $this->linkAccessRestrictedPages = (boolean)$linkAccessRestrictedPages;
282 return $this;
283 }
284
285 /**
286 * @return boolean
287 * @api
288 */
289 public function getLinkAccessRestrictedPages() {
290 return $this->linkAccessRestrictedPages;
291 }
292
293 /**
294 * Uid of the target page
295 *
296 * @param integer $pageUid
297 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
298 * @api
299 */
300 public function setTargetPageUid($targetPageUid) {
301 $this->targetPageUid = $targetPageUid;
302 return $this;
303 }
304
305 /**
306 * returns $this->targetPageUid.
307 *
308 * @return integer
309 * @api
310 */
311 public function getTargetPageUid() {
312 return $this->targetPageUid;
313 }
314
315 /**
316 * Sets the page type of the target URI. Defaults to 0
317 *
318 * @param integer $pageType
319 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
320 * @api
321 */
322 public function setTargetPageType($targetPageType) {
323 $this->targetPageType = (integer)$targetPageType;
324 return $this;
325 }
326
327 /**
328 * @return integer
329 * @api
330 */
331 public function getTargetPageType() {
332 return $this->targetPageType;
333 }
334
335 /**
336 * by default FALSE; if TRUE, &no_cache=1 will be appended to the URI
337 * This overrules the useCacheHash setting
338 *
339 * @param boolean $noCache
340 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
341 * @api
342 */
343 public function setNoCache($noCache) {
344 $this->noCache = (boolean)$noCache;
345 return $this;
346 }
347
348 /**
349 * @return boolean
350 * @api
351 */
352 public function getNoCache() {
353 return $this->noCache;
354 }
355
356 /**
357 * by default TRUE; if FALSE, no cHash parameter will be appended to the URI
358 * If noCache is set, this setting will be ignored.
359 *
360 * @param boolean $useCacheHash
361 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
362 * @api
363 */
364 public function setUseCacheHash($useCacheHash) {
365 $this->useCacheHash = (boolean)$useCacheHash;
366 return $this;
367 }
368
369 /**
370 * @return boolean
371 * @api
372 */
373 public function getUseCacheHash() {
374 return $this->useCacheHash;
375 }
376
377 /**
378 * Returns the arguments being used for the last URI being built.
379 * This is only set after build() / uriFor() has been called.
380 *
381 * @return array The last arguments
382 * @author Sebastian Kurf├╝rst <sebastian@typo3.org>
383 */
384 public function getLastArguments() {
385 return $this->lastArguments;
386 }
387
388 /**
389 * Resets all UriBuilder options to their default value
390 *
391 * @return Tx_Extbase_MVC_Web_Routing_UriBuilder the current UriBuilder to allow method chaining
392 * @api
393 */
394 public function reset() {
395 $this->arguments = array();
396 $this->section = '';
397 $this->format = '';
398 $this->createAbsoluteUri = FALSE;
399 $this->addQueryString = FALSE;
400 $this->argumentsToBeExcludedFromQueryString = array();
401 $this->linkAccessRestrictedPages = FALSE;
402 $this->targetPageUid = NULL;
403 $this->targetPageType = 0;
404 $this->noCache = FALSE;
405 $this->useCacheHash = TRUE;
406 $this->argumentPrefix = NULL;
407
408 return $this;
409 }
410
411 /**
412 * Creates an URI used for linking to an Extbase action.
413 * Works in Frondend and Backend mode of TYPO3.
414 *
415 * @param string $actionName Name of the action to be called
416 * @param array $controllerArguments Additional query parameters. Will be "namespaced" and merged with $this->arguments.
417 * @param string $controllerName Name of the target controller. If not set, current ControllerName is used.
418 * @param string $extensionName Name of the target extension, without underscores. If not set, current ExtensionName is used.
419 * @param string $pluginName Name of the target plugin. If not set, current PluginName is used.
420 * @return string the rendered URI
421 * @api
422 * @see build()
423 */
424 public function uriFor($actionName = NULL, $controllerArguments = array(), $controllerName = NULL, $extensionName = NULL, $pluginName = NULL) {
425 if ($actionName !== NULL) {
426 $controllerArguments['action'] = $actionName;
427 }
428 if ($controllerName !== NULL) {
429 $controllerArguments['controller'] = $controllerName;
430 } else {
431 $controllerArguments['controller'] = $this->request->getControllerName();
432 }
433 if ($extensionName === NULL) {
434 $extensionName = $this->request->getControllerExtensionName();
435 }
436 if ($pluginName === NULL && TYPO3_MODE === 'FE') {
437 $pluginName = Tx_Extbase_Utility_Extension::getPluginNameByAction($extensionName, $controllerArguments['controller'], $controllerArguments['action']);
438 }
439 if ($pluginName === NULL) {
440 $pluginName = $this->request->getPluginName();
441 }
442 if ($this->targetPageUid === NULL && TYPO3_MODE === 'FE') {
443 $this->targetPageUid = Tx_Extbase_Utility_Extension::getTargetPidByPlugin($extensionName, $pluginName);
444 }
445 if ($this->format !== '') {
446 $controllerArguments['format'] = $this->format;
447 }
448 $pluginNamespace = Tx_Extbase_Utility_Extension::getPluginNamespace($extensionName, $pluginName);
449 if ($this->argumentPrefix !== NULL) {
450 $prefixedControllerArguments = array($this->argumentPrefix => $controllerArguments);
451 } else {
452 $prefixedControllerArguments = array($pluginNamespace => $controllerArguments);
453 }
454 $this->arguments = t3lib_div::array_merge_recursive_overrule($this->arguments, $prefixedControllerArguments);
455
456 return $this->build();
457 }
458
459 /**
460 * Builds the URI
461 * Depending on the current context this calls buildBackendUri() or buildFrontendUri()
462 *
463 * @return string The URI
464 * @api
465 * @see buildBackendUri()
466 * @see buildFrontendUri()
467 */
468 public function build() {
469 if (TYPO3_MODE === 'BE') {
470 return $this->buildBackendUri();
471 } else {
472 return $this->buildFrontendUri();
473 }
474 }
475
476 /**
477 * Builds the URI, backend flavour
478 * The resulting URI is relative and starts with "mod.php".
479 * The settings pageUid, pageType, noCache, useCacheHash & linkAccessRestrictedPages
480 * will be ignored in the backend.
481 *
482 * @return string The URI
483 */
484 public function buildBackendUri() {
485 if ($this->addQueryString === TRUE) {
486 $arguments = t3lib_div::_GET();
487 foreach($this->argumentsToBeExcludedFromQueryString as $argumentToBeExcluded) {
488 unset($arguments[$argumentToBeExcluded]);
489 }
490 } else {
491 $arguments = array(
492 'M' => t3lib_div::_GET('M'),
493 'id' => t3lib_div::_GET('id')
494 );
495 }
496 $arguments = t3lib_div::array_merge_recursive_overrule($arguments, $this->arguments);
497 $arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
498 $this->lastArguments = $arguments;
499 $uri = 'mod.php?' . http_build_query($arguments, NULL, '&');
500 if ($this->section !== '') {
501 $uri .= '#' . $this->section;
502 }
503 if ($this->createAbsoluteUri === TRUE) {
504 $uri = $this->request->getBaseURI() . $uri;
505 }
506 return $uri;
507 }
508
509 /**
510 * Builds the URI, frontend flavour
511 *
512 * @return string The URI
513 * @see buildTypolinkConfiguration()
514 */
515 public function buildFrontendUri() {
516 $typolinkConfiguration = $this->buildTypolinkConfiguration();
517
518 if ($this->createAbsoluteUri === TRUE) {
519 $typolinkConfiguration['forceAbsoluteUrl'] = TRUE;
520 }
521
522 $uri = $this->contentObject->typoLink_URL($typolinkConfiguration);
523 return $uri;
524 }
525
526
527 /**
528 * Builds a TypoLink configuration array from the current settings
529 *
530 * @return array typolink configuration array
531 * @see TSref/typolink
532 */
533 protected function buildTypolinkConfiguration() {
534 $typolinkConfiguration = array();
535
536 $typolinkConfiguration['parameter'] = $this->targetPageUid !== NULL ? $this->targetPageUid : $GLOBALS['TSFE']->id;
537 if ($this->targetPageType !== 0) {
538 $typolinkConfiguration['parameter'] .= ',' . $this->targetPageType;
539 }
540
541 if (count($this->arguments) > 0) {
542 $arguments = $this->convertDomainObjectsToIdentityArrays($this->arguments);
543 $this->lastArguments = $arguments;
544 $typolinkConfiguration['additionalParams'] = t3lib_div::implodeArrayForUrl(NULL, $arguments);
545 }
546
547 if ($this->addQueryString === TRUE) {
548 $typolinkConfiguration['addQueryString'] = 1;
549 if (count($this->argumentsToBeExcludedFromQueryString) > 0) {
550 $typolinkConfiguration['addQueryString.'] = array(
551 'exclude' => implode(',', $this->argumentsToBeExcludedFromQueryString)
552 );
553 }
554 // TODO: Support for __hmac and addQueryString!
555 }
556
557 if ($this->noCache === TRUE) {
558 $typolinkConfiguration['no_cache'] = 1;
559 } elseif ($this->useCacheHash) {
560 $typolinkConfiguration['useCacheHash'] = 1;
561 }
562
563 if ($this->section !== '') {
564 $typolinkConfiguration['section'] = $this->section;
565 }
566
567 if ($this->linkAccessRestrictedPages === TRUE) {
568 $typolinkConfiguration['linkAccessRestrictedPages'] = 1;
569 }
570
571 return $typolinkConfiguration;
572 }
573
574 /**
575 * Recursively iterates through the specified arguments and turns instances of type Tx_Extbase_DomainObject_AbstractEntity
576 * into an arrays containing the uid of the domain object.
577 *
578 * @param array $arguments The arguments to be iterated
579 * @return array The modified arguments array
580 */
581 protected function convertDomainObjectsToIdentityArrays(array $arguments) {
582 foreach ($arguments as $argumentKey => $argumentValue) {
583 if ($argumentValue instanceof Tx_Extbase_DomainObject_AbstractDomainObject) {
584 if ($argumentValue->getUid() !== NULL) {
585 $arguments[$argumentKey] = $argumentValue->getUid();
586 } elseif ($argumentValue instanceof Tx_Extbase_DomainObject_AbstractValueObject) {
587 $arguments[$argumentKey] = $this->convertTransientObjectToArray($argumentValue);
588 } else {
589 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('Could not serialize Domain Object ' . get_class($argumentValue) . '. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688);
590 }
591 } elseif (is_array($argumentValue)) {
592 $arguments[$argumentKey] = $this->convertDomainObjectsToIdentityArrays($argumentValue);
593 }
594 }
595 return $arguments;
596 }
597
598 /**
599 * Converts a given object recursively into an array.
600 *
601 * @param Tx_Extbase_DomainObject_AbstractDomainObject $object
602 * @return void
603 */
604 // TODO Refactore this into convertDomainObjectsToIdentityArrays()
605 public function convertTransientObjectToArray(Tx_Extbase_DomainObject_AbstractDomainObject $object) {
606 $result = array();
607 foreach ($object->_getProperties() as $propertyName => $propertyValue) {
608 if ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractDomainObject) {
609 if ($propertyValue->getUid() !== NULL) {
610 $result[$propertyName] = $propertyValue->getUid();
611 } else {
612 $result[$propertyName] = $this->convertTransientObjectToArray($propertyValue);
613 }
614 } elseif (is_array($propertyValue)) {
615 $result[$propertyName] = $this->convertDomainObjectsToIdentityArrays($propertyValue);
616 } else {
617 $result[$propertyName] = $propertyValue;
618 }
619 }
620 return $result;
621 }
622
623 }
624 ?>