ec02cc4253f385b9e4801e36abda6e218d9ad3ee
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / ViewHelpers / Form / DatePickerViewHelper.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Form\ViewHelpers\Form;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It originated from the Neos.Form package (www.neos.io)
9 *
10 * It is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License, either version 2
12 * of the License, or any later version.
13 *
14 * For the full copyright and license information, please read the
15 * LICENSE.txt file that was distributed with this source code.
16 *
17 * The TYPO3 project - inspiring people to share!
18 */
19
20 use TYPO3\CMS\Core\Page\PageRenderer;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
23 use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper;
24
25 /**
26 * Display a jQuery date picker.
27 *
28 * Note: Requires jQuery UI to be included on the page.
29 *
30 * Scope: frontend
31 * @api
32 */
33 class DatePickerViewHelper extends AbstractFormFieldViewHelper
34 {
35
36 /**
37 * @var string
38 */
39 protected $tagName = 'input';
40
41 /**
42 * @var \TYPO3\CMS\Extbase\Property\PropertyMapper
43 */
44 protected $propertyMapper;
45
46 /**
47 * @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper
48 * @internal
49 */
50 public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper)
51 {
52 $this->propertyMapper = $propertyMapper;
53 }
54
55 /**
56 * Initialize the arguments.
57 *
58 * @api
59 */
60 public function initializeArguments()
61 {
62 parent::initializeArguments();
63 $this->registerTagAttribute('size', 'int', 'The size of the input field');
64 $this->registerTagAttribute('placeholder', 'string', 'Specifies a short hint that describes the expected value of an input element');
65 $this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this view helper', false, 'f3-form-error');
66 $this->registerArgument('initialDate', 'string', 'Initial date (@see http://www.php.net/manual/en/datetime.formats.php for supported formats)');
67 $this->registerArgument('enableDatePicker', 'bool', 'Enable the Datepicker', false, true);
68 $this->registerArgument('previewMode', 'bool', 'Preview mde flag', true, false);
69 $this->registerArgument('dateFormat', 'string', 'The date format', false, 'Y-m-d');
70 $this->registerUniversalTagAttributes();
71 }
72
73 /**
74 * Renders the text field, hidden field and required javascript
75 *
76 * @return string
77 * @api
78 */
79 public function render()
80 {
81 $enableDatePicker = $this->arguments['enableDatePicker'];
82 $dateFormat = $this->arguments['dateFormat'];
83 $previewMode = (bool)$this->arguments['previewMode'];
84 $placeholder = $this->arguments['placeholder'];
85
86 $name = $this->getName();
87 $this->registerFieldNameForFormTokenGeneration($name);
88
89 $this->tag->addAttribute('type', 'text');
90 $this->tag->addAttribute('name', $name . '[date]');
91
92 if ($this->hasArgument('id')) {
93 $id = $this->arguments['id'];
94 } else {
95 $id = 'field' . md5(uniqid());
96 }
97
98 if (empty($placeholder)) {
99 $this->tag->addAttribute('placeholder', $dateFormat);
100 }
101
102 if ($enableDatePicker) {
103 $this->tag->addAttribute('readonly', 'readonly');
104 if (!$previewMode) {
105 $datePickerDateFormat = $this->convertDateFormatToDatePickerFormat($dateFormat);
106 $this->renderInlineJavascript($id, $datePickerDateFormat);
107 }
108 }
109 $date = $this->getSelectedDate();
110 if ($date !== null) {
111 $this->tag->addAttribute('value', $date->format($dateFormat));
112 }
113
114 $this->tag->addAttribute('id', $id);
115
116 $this->setErrorClassAttribute();
117 $content = '';
118 $content .= $this->tag->render();
119 $content .= '<input type="hidden" name="' . $name . '[dateFormat]" value="' . htmlspecialchars($dateFormat) . '" />';
120
121 return $content;
122 }
123
124 /**
125 * @return null|\DateTime
126 */
127 protected function getSelectedDate()
128 {
129 /** @var FormRuntime $formRuntime */
130 $formRuntime = $this->renderingContext
131 ->getViewHelperVariableContainer()
132 ->get(RenderRenderableViewHelper::class, 'formRuntime');
133
134 $formState = $formRuntime->getFormState();
135
136 $date = $formRuntime[$this->arguments['property']];
137 if ($date instanceof \DateTime) {
138 return $date;
139 }
140 if ($date !== null) {
141 $date = $this->propertyMapper->convert($date, 'DateTime');
142 if (!$date instanceof \DateTime) {
143 return null;
144 }
145 return $date;
146 }
147 if ($this->hasArgument('initialDate')) {
148 return new \DateTime($this->arguments['initialDate']);
149 }
150 }
151
152 /**
153 * @param string $dateFormat
154 * @return string
155 */
156 protected function convertDateFormatToDatePickerFormat(string $dateFormat): string
157 {
158 $replacements = [
159 'd' => 'dd',
160 'D' => 'D',
161 'j' => 'o',
162 'l' => 'DD',
163
164 'F' => 'MM',
165 'm' => 'mm',
166 'M' => 'M',
167 'n' => 'm',
168
169 'Y' => 'yy',
170 'y' => 'y'
171 ];
172 return strtr($dateFormat, $replacements);
173 }
174
175 /**
176 * @param string $uniqueIdentifier
177 * @param string $datePickerDateFormat
178 */
179 protected function renderInlineJavascript(string $uniqueIdentifier, string $datePickerDateFormat)
180 {
181 $this->getPageRenderer()->addJsFooterInlineCode(
182 'ext_form_datepicker-' . $uniqueIdentifier,
183 'if ("undefined" !== typeof $) {
184 $(function() {
185 $("#' . $uniqueIdentifier . '").datepicker({
186 dateFormat: "' . $datePickerDateFormat . '"
187 }).on("keydown", function(e) {
188 // By using "backspace" or "delete", you can clear the datepicker again.
189 if(e.keyCode == 8 || e.keyCode == 46) {
190 e.preventDefault();
191 $.datepicker._clearDate(this);
192 }
193 });
194 });
195 }
196 '
197 );
198 }
199
200 /**
201 * @return PageRenderer
202 */
203 protected function getPageRenderer(): PageRenderer
204 {
205 return GeneralUtility::makeInstance(PageRenderer::class);
206 }
207 }