[BUGFIX] Prevent XSS in ViewHelpers
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / ViewHelpers / Format / DateViewHelper.php
1 <?php
2 namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\MathUtility;
18 use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
19 use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
20 use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
21
22 /**
23 * Formats an object implementing \DateTimeInterface.
24 *
25 * = Examples =
26 *
27 * <code title="Defaults">
28 * <f:format.date>{dateObject}</f:format.date>
29 * </code>
30 * <output>
31 * 1980-12-13
32 * (depending on the current date)
33 * </output>
34 *
35 * <code title="Custom date format">
36 * <f:format.date format="H:i">{dateObject}</f:format.date>
37 * </code>
38 * <output>
39 * 01:23
40 * (depending on the current time)
41 * </output>
42 *
43 * <code title="Relative date with given time">
44 * <f:format.date format="Y" base="{dateObject}">-1 year</f:format.date>
45 * </code>
46 * <output>
47 * 2016
48 * (assuming dateObject is in 2017)
49 * </output>
50 *
51 * <code title="strtotime string">
52 * <f:format.date format="d.m.Y - H:i:s">+1 week 2 days 4 hours 2 seconds</f:format.date>
53 * </code>
54 * <output>
55 * 13.12.1980 - 21:03:42
56 * (depending on the current time, see http://www.php.net/manual/en/function.strtotime.php)
57 * </output>
58 *
59 * <code title="Localized dates using strftime date format">
60 * <f:format.date format="%d. %B %Y">{dateObject}</f:format.date>
61 * </code>
62 * <output>
63 * 13. Dezember 1980
64 * (depending on the current date and defined locale. In the example you see the 1980-12-13 in a german locale)
65 * </output>
66 *
67 * <code title="Inline notation">
68 * {f:format.date(date: dateObject)}
69 * </code>
70 * <output>
71 * 1980-12-13
72 * (depending on the value of {dateObject})
73 * </output>
74 *
75 * <code title="Inline notation (2nd variant)">
76 * {dateObject -> f:format.date()}
77 * </code>
78 * <output>
79 * 1980-12-13
80 * (depending on the value of {dateObject})
81 * </output>
82 *
83 * @api
84 */
85 class DateViewHelper extends AbstractViewHelper
86 {
87 /**
88 * Needed as child node's output can return a DateTime object which can't be escaped
89 *
90 * @var bool
91 */
92 protected $escapeChildren = false;
93
94 /**
95 * Render the supplied DateTime object as a formatted date.
96 *
97 * @param mixed $date either an object implementing DateTimeInterface or a string that is accepted by DateTime constructor
98 * @param string $format Format String which is taken to format the Date/Time
99 * @param mixed $base A base time (an object implementing DateTimeInterface or a string) used if $date is a relative date specification. Defaults to current time.
100 *
101 * @return string Formatted date
102 * @throws Exception
103 * @api
104 */
105 public function render($date = null, $format = '', $base = null)
106 {
107 return static::renderStatic(
108 array(
109 'date' => $date,
110 'format' => $format,
111 'base' => $base
112 ),
113 $this->buildRenderChildrenClosure(),
114 $this->renderingContext
115 );
116 }
117
118 /**
119 * @param array $arguments
120 * @param \Closure $renderChildrenClosure
121 * @param RenderingContextInterface $renderingContext
122 *
123 * @return string
124 * @throws Exception
125 */
126 public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
127 {
128 $date = $arguments['date'];
129 $format = $arguments['format'];
130 $base = $arguments['base'] === null ? time() : $arguments['base'];
131 if (is_string($base)) {
132 $base = trim($base);
133 }
134
135 if ($format === '') {
136 $format = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?: 'Y-m-d';
137 }
138
139 if ($date === null) {
140 $date = $renderChildrenClosure();
141 if ($date === null) {
142 return '';
143 }
144 }
145
146 if (is_string($date)) {
147 $date = trim($date);
148 }
149
150 if ($date === '') {
151 $date = 'now';
152 }
153
154 if (!$date instanceof \DateTimeInterface) {
155 try {
156 $base = $base instanceof \DateTimeInterface ? $base->format('U') : strtotime((MathUtility::canBeInterpretedAsInteger($base) ? '@' : '') . $base);
157 $dateTimestamp = strtotime((MathUtility::canBeInterpretedAsInteger($date) ? '@' : '') . $date, $base);
158 $date = new \DateTime('@' . $dateTimestamp);
159 $date->setTimezone(new \DateTimeZone(date_default_timezone_get()));
160 } catch (\Exception $exception) {
161 throw new Exception('"' . $date . '" could not be parsed by \DateTime constructor: ' . $exception->getMessage(), 1241722579);
162 }
163 }
164
165 if (strpos($format, '%') !== false) {
166 return strftime($format, $date->format('U'));
167 } else {
168 return $date->format($format);
169 }
170 }
171 }