[TASK] Remove closing PHP tags
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / ViewHelpers / FormViewHelper.php
1 <?php
2 namespace TYPO3\CMS\Fluid\ViewHelpers;
3
4 /* *
5 * This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU General Public License as published by the Free *
9 * Software Foundation, either version 3 of the License, or (at your *
10 * *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
15 * Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with the script. *
19 * If not, see http://www.gnu.org/licenses/gpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23 /**
24 * Form view helper. Generates a <form> Tag.
25 *
26 * = Basic usage =
27 *
28 * Use <f:form> to output an HTML <form> tag which is targeted at the specified action, in the current controller and package.
29 * It will submit the form data via a POST request. If you want to change this, use method="get" as an argument.
30 * <code title="Example">
31 * <f:form action="...">...</f:form>
32 * </code>
33 *
34 * = A complex form with a specified encoding type =
35 *
36 * <code title="Form with enctype set">
37 * <f:form action=".." controller="..." package="..." enctype="multipart/form-data">...</f:form>
38 * </code>
39 *
40 * = A Form which should render a domain object =
41 *
42 * <code title="Binding a domain object to a form">
43 * <f:form action="..." name="customer" object="{customer}">
44 * <f:form.hidden property="id" />
45 * <f:form.textbox property="name" />
46 * </f:form>
47 * </code>
48 * This automatically inserts the value of {customer.name} inside the textbox and adjusts the name of the textbox accordingly.
49 */
50 class FormViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormViewHelper {
51
52 /**
53 * @var string
54 */
55 protected $tagName = 'form';
56
57 /**
58 * @var \TYPO3\CMS\Extbase\Security\Channel\RequestHashService
59 */
60 protected $requestHashService;
61
62 /**
63 * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
64 */
65 protected $hashService;
66
67 /**
68 * @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
69 */
70 protected $mvcPropertyMappingConfigurationService;
71
72 /**
73 * @var \TYPO3\CMS\Extbase\Service\ExtensionService
74 */
75 protected $extensionService;
76
77 /**
78 * We need the arguments of the formActionUri on requesthash calculation
79 * therefore we will store them in here right after calling uriBuilder
80 *
81 * @var array
82 */
83 protected $formActionUriArguments;
84
85 /**
86 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
87 */
88 protected $configurationManager;
89
90 /**
91 * Inject a request hash service
92 *
93 * @param \TYPO3\CMS\Extbase\Security\Channel\RequestHashService $requestHashService The request hash service
94 * @return void
95 */
96 public function injectRequestHashService(\TYPO3\CMS\Extbase\Security\Channel\RequestHashService $requestHashService) {
97 $this->requestHashService = $requestHashService;
98 }
99
100 /**
101 * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
102 */
103 public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService) {
104 $this->hashService = $hashService;
105 }
106
107 /**
108 * @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
109 * @return void
110 */
111 public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService) {
112 $this->extensionService = $extensionService;
113 }
114
115 /**
116 * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
117 * @return void
118 */
119 public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
120 $this->configurationManager = $configurationManager;
121 }
122
123 /**
124 * @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMapperConfigurationService
125 * @return void
126 */
127 public function injectMvcPropertyMapperConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMapperConfigurationService) {
128 $this->mvcPropertyMappingConfigurationService = $mvcPropertyMapperConfigurationService;
129 }
130
131 /**
132 * Initialize arguments.
133 *
134 * @return void
135 */
136 public function initializeArguments() {
137 $this->registerTagAttribute('enctype', 'string', 'MIME type with which the form is submitted');
138 $this->registerTagAttribute('method', 'string', 'Transfer type (GET or POST)');
139 $this->registerTagAttribute('name', 'string', 'Name of form');
140 $this->registerTagAttribute('onreset', 'string', 'JavaScript: On reset of the form');
141 $this->registerTagAttribute('onsubmit', 'string', 'JavaScript: On submit of the form');
142 $this->registerUniversalTagAttributes();
143 }
144
145 /**
146 * Render the form.
147 *
148 * @param string $action Target action
149 * @param array $arguments Arguments
150 * @param string $controller Target controller
151 * @param string $extensionName Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used
152 * @param string $pluginName Target plugin. If empty, the current plugin name is used
153 * @param integer $pageUid Target page uid
154 * @param mixed $object Object to use for the form. Use in conjunction with the "property" attribute on the sub tags
155 * @param integer $pageType Target page type
156 * @param boolean $noCache set this to disable caching for the target page. You should not need this.
157 * @param boolean $noCacheHash set this to supress the cHash query parameter created by TypoLink. You should not need this.
158 * @param string $section The anchor to be added to the action URI (only active if $actionUri is not set)
159 * @param string $format The requested format (e.g. ".html") of the target page (only active if $actionUri is not set)
160 * @param array $additionalParams additional action URI query parameters that won't be prefixed like $arguments (overrule $arguments) (only active if $actionUri is not set)
161 * @param boolean $absolute If set, an absolute action URI is rendered (only active if $actionUri is not set)
162 * @param boolean $addQueryString If set, the current query parameters will be kept in the action URI (only active if $actionUri is not set)
163 * @param array $argumentsToBeExcludedFromQueryString arguments to be removed from the action URI. Only active if $addQueryString = TRUE and $actionUri is not set
164 * @param string $fieldNamePrefix Prefix that will be added to all field names within this form. If not set the prefix will be tx_yourExtension_plugin
165 * @param string $actionUri can be used to overwrite the "action" attribute of the form tag
166 * @param string $objectName name of the object that is bound to this form. If this argument is not specified, the name attribute of this form is used to determine the FormObjectName
167 * @param string $hiddenFieldClassName
168 * @return string rendered form
169 */
170 public function render($action = NULL, array $arguments = array(), $controller = NULL, $extensionName = NULL, $pluginName = NULL, $pageUid = NULL, $object = NULL, $pageType = 0, $noCache = FALSE, $noCacheHash = FALSE, $section = '', $format = '', array $additionalParams = array(), $absolute = FALSE, $addQueryString = FALSE, array $argumentsToBeExcludedFromQueryString = array(), $fieldNamePrefix = NULL, $actionUri = NULL, $objectName = NULL, $hiddenFieldClassName = NULL) {
171 $this->setFormActionUri();
172 if (strtolower($this->arguments['method']) === 'get') {
173 $this->tag->addAttribute('method', 'get');
174 } else {
175 $this->tag->addAttribute('method', 'post');
176 }
177 $this->addFormObjectNameToViewHelperVariableContainer();
178 $this->addFormObjectToViewHelperVariableContainer();
179 $this->addFieldNamePrefixToViewHelperVariableContainer();
180 $this->addFormFieldNamesToViewHelperVariableContainer();
181 $formContent = $this->renderChildren();
182
183 if ($this->arguments['hiddenFieldClassName'] !== NULL) {
184 $content = chr(10) . '<div class="' . htmlspecialchars($this->arguments['hiddenFieldClassName']) . '">';
185 } else {
186 $content = chr(10) . '<div>';
187 }
188
189 $content .= $this->renderHiddenIdentityField($this->arguments['object'], $this->getFormObjectName());
190 $content .= $this->renderAdditionalIdentityFields();
191 $content .= $this->renderHiddenReferrerFields();
192 if ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper') === FALSE) {
193 // Render hmac after everything else has been rendered
194 $content .= $this->renderRequestHashField();
195 } else {
196 // Render the trusted list of all properties after everything else has been rendered
197 $content .= $this->renderTrustedPropertiesField();
198 }
199 $content .= chr(10) . '</div>' . chr(10);
200 $content .= $formContent;
201 $this->tag->setContent($content);
202 $this->removeFieldNamePrefixFromViewHelperVariableContainer();
203 $this->removeFormObjectFromViewHelperVariableContainer();
204 $this->removeFormObjectNameFromViewHelperVariableContainer();
205 $this->removeFormFieldNamesFromViewHelperVariableContainer();
206 $this->removeCheckboxFieldNamesFromViewHelperVariableContainer();
207 return $this->tag->render();
208 }
209
210 /**
211 * Sets the "action" attribute of the form tag
212 *
213 * @return void
214 */
215 protected function setFormActionUri() {
216 if ($this->hasArgument('actionUri')) {
217 $formActionUri = $this->arguments['actionUri'];
218 } else {
219 $uriBuilder = $this->controllerContext->getUriBuilder();
220 $formActionUri = $uriBuilder->reset()->setTargetPageUid($this->arguments['pageUid'])->setTargetPageType($this->arguments['pageType'])->setNoCache($this->arguments['noCache'])->setUseCacheHash(!$this->arguments['noCacheHash'])->setSection($this->arguments['section'])->setCreateAbsoluteUri($this->arguments['absolute'])->setArguments((array) $this->arguments['additionalParams'])->setAddQueryString($this->arguments['addQueryString'])->setArgumentsToBeExcludedFromQueryString((array) $this->arguments['argumentsToBeExcludedFromQueryString'])->setFormat($this->arguments['format'])->uriFor($this->arguments['action'], $this->arguments['arguments'], $this->arguments['controller'], $this->arguments['extensionName'], $this->arguments['pluginName']);
221 $this->formActionUriArguments = $uriBuilder->getArguments();
222 }
223 $this->tag->addAttribute('action', $formActionUri);
224 }
225
226 /**
227 * Render additional identity fields which were registered by form elements.
228 * This happens if a form field is defined like property="bla.blubb" - then we might need an identity property for the sub-object "bla".
229 *
230 * @return string HTML-string for the additional identity properties
231 */
232 protected function renderAdditionalIdentityFields() {
233 if ($this->viewHelperVariableContainer->exists('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'additionalIdentityProperties')) {
234 $additionalIdentityProperties = $this->viewHelperVariableContainer->get('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'additionalIdentityProperties');
235 $output = '';
236 foreach ($additionalIdentityProperties as $identity) {
237 $output .= chr(10) . $identity;
238 }
239 return $output;
240 }
241 return '';
242 }
243
244 /**
245 * Renders hidden form fields for referrer information about
246 * the current controller and action.
247 *
248 * @return string Hidden fields with referrer information
249 * @todo filter out referrer information that is equal to the target (e.g. same packageKey)
250 */
251 protected function renderHiddenReferrerFields() {
252 $request = $this->controllerContext->getRequest();
253 $extensionName = $request->getControllerExtensionName();
254 $controllerName = $request->getControllerName();
255 $actionName = $request->getControllerActionName();
256 $result = chr(10);
257 if ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper')) {
258 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@extension]') . '" value="' . $extensionName . '" />' . chr(10);
259 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@controller]') . '" value="' . $controllerName . '" />' . chr(10);
260 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@action]') . '" value="' . $actionName . '" />' . chr(10);
261 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[arguments]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($request->getArguments())))) . '" />' . chr(10);
262 } else {
263 // @deprecated since Fluid 1.4.0, will be removed two versions after Fluid 6.1.
264 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[extensionName]') . '" value="' . $extensionName . '" />' . chr(10);
265 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[controllerName]') . '" value="' . $controllerName . '" />' . chr(10);
266 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[actionName]') . '" value="' . $actionName . '" />' . chr(10);
267 }
268 return $result;
269 }
270
271 /**
272 * Adds the form object name to the ViewHelperVariableContainer if "objectName" argument or "name" attribute is specified.
273 *
274 * @return void
275 */
276 protected function addFormObjectNameToViewHelperVariableContainer() {
277 $formObjectName = $this->getFormObjectName();
278 if ($formObjectName !== NULL) {
279 $this->viewHelperVariableContainer->add('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formObjectName', $formObjectName);
280 }
281 }
282
283 /**
284 * Removes the form name from the ViewHelperVariableContainer.
285 *
286 * @return void
287 */
288 protected function removeFormObjectNameFromViewHelperVariableContainer() {
289 $formObjectName = $this->getFormObjectName();
290 if ($formObjectName !== NULL) {
291 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formObjectName');
292 }
293 }
294
295 /**
296 * Returns the name of the object that is bound to this form.
297 * If the "objectName" argument has been specified, this is returned. Otherwise the name attribute of this form.
298 * If neither objectName nor name arguments have been set, NULL is returned.
299 *
300 * @return string specified Form name or NULL if neither $objectName nor $name arguments have been specified
301 */
302 protected function getFormObjectName() {
303 $formObjectName = NULL;
304 if ($this->hasArgument('objectName')) {
305 $formObjectName = $this->arguments['objectName'];
306 } elseif ($this->hasArgument('name')) {
307 $formObjectName = $this->arguments['name'];
308 }
309 return $formObjectName;
310 }
311
312 /**
313 * Adds the object that is bound to this form to the ViewHelperVariableContainer if the formObject attribute is specified.
314 *
315 * @return void
316 */
317 protected function addFormObjectToViewHelperVariableContainer() {
318 if ($this->hasArgument('object')) {
319 $this->viewHelperVariableContainer->add('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formObject', $this->arguments['object']);
320 $this->viewHelperVariableContainer->add('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'additionalIdentityProperties', array());
321 }
322 }
323
324 /**
325 * Removes the form object from the ViewHelperVariableContainer.
326 *
327 * @return void
328 */
329 protected function removeFormObjectFromViewHelperVariableContainer() {
330 if ($this->hasArgument('object')) {
331 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formObject');
332 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'additionalIdentityProperties');
333 }
334 }
335
336 /**
337 * Adds the field name prefix to the ViewHelperVariableContainer
338 *
339 * @return void
340 */
341 protected function addFieldNamePrefixToViewHelperVariableContainer() {
342 $fieldNamePrefix = $this->getFieldNamePrefix();
343 $this->viewHelperVariableContainer->add('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'fieldNamePrefix', $fieldNamePrefix);
344 }
345
346 /**
347 * Get the field name prefix
348 *
349 * @return string
350 */
351 protected function getFieldNamePrefix() {
352 if ($this->hasArgument('fieldNamePrefix')) {
353 return $this->arguments['fieldNamePrefix'];
354 } else {
355 return $this->getDefaultFieldNamePrefix();
356 }
357 }
358
359 /**
360 * Removes field name prefix from the ViewHelperVariableContainer
361 *
362 * @return void
363 */
364 protected function removeFieldNamePrefixFromViewHelperVariableContainer() {
365 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'fieldNamePrefix');
366 }
367
368 /**
369 * Adds a container for form field names to the ViewHelperVariableContainer
370 *
371 * @return void
372 */
373 protected function addFormFieldNamesToViewHelperVariableContainer() {
374 $this->viewHelperVariableContainer->add('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formFieldNames', array());
375 }
376
377 /**
378 * Removes the container for form field names from the ViewHelperVariableContainer
379 *
380 * @return void
381 */
382 protected function removeFormFieldNamesFromViewHelperVariableContainer() {
383 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formFieldNames');
384 }
385
386 /**
387 * Render the request hash field
388 *
389 * @return string the hmac field
390 */
391 protected function renderRequestHashField() {
392 $formFieldNames = $this->viewHelperVariableContainer->get('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formFieldNames');
393 $this->postProcessUriArgumentsForRequesthash($this->formActionUriArguments, $formFieldNames);
394 $requestHash = $this->requestHashService->generateRequestHash($formFieldNames, $this->getFieldNamePrefix());
395 // in v4, we need to prefix __hmac as well to make it show up in the request object.
396 return '<input type="hidden" name="' . $this->prefixFieldName('__hmac') . '" value="' . htmlspecialchars($requestHash) . '" />';
397 }
398
399 /**
400 * Add the URI arguments after postprocessing to the request hash as well.
401 */
402 protected function postProcessUriArgumentsForRequestHash($arguments, &$results, $currentPrefix = '', $level = 0) {
403 if (!count($arguments)) {
404 return;
405 }
406 foreach ($arguments as $argumentName => $argumentValue) {
407 if (is_array($argumentValue)) {
408 $prefix = $level == 0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']';
409 $this->postProcessUriArgumentsForRequestHash($argumentValue, $results, $prefix, $level + 1);
410 } else {
411 $results[] = $level == 0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']';
412 }
413 }
414 }
415
416 /**
417 * Retrieves the default field name prefix for this form
418 *
419 * @return string default field name prefix
420 */
421 protected function getDefaultFieldNamePrefix() {
422 $request = $this->controllerContext->getRequest();
423 if ($this->hasArgument('extensionName')) {
424 $extensionName = $this->arguments['extensionName'];
425 } else {
426 $extensionName = $request->getControllerExtensionName();
427 }
428 if ($this->hasArgument('pluginName')) {
429 $pluginName = $this->arguments['pluginName'];
430 } else {
431 $pluginName = $request->getPluginName();
432 }
433 if ($extensionName !== NULL && $pluginName != NULL) {
434 return $this->extensionService->getPluginNamespace($extensionName, $pluginName);
435 } else {
436 return '';
437 }
438 }
439
440 /**
441 * Remove Checkbox field names from ViewHelper variable container, to start from scratch when a new form starts.
442 */
443 protected function removeCheckboxFieldNamesFromViewHelperVariableContainer() {
444 if ($this->viewHelperVariableContainer->exists('TYPO3\\CMS\\Fluid\\ViewHelpers\\Form\\CheckboxViewHelper', 'checkboxFieldNames')) {
445 $this->viewHelperVariableContainer->remove('TYPO3\\CMS\\Fluid\\ViewHelpers\\Form\\CheckboxViewHelper', 'checkboxFieldNames');
446 }
447 }
448
449 /**
450 * Render the request hash field
451 *
452 * @return string The hmac field
453 */
454 protected function renderTrustedPropertiesField() {
455 $formFieldNames = $this->viewHelperVariableContainer->get('TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper', 'formFieldNames');
456 $requestHash = $this->mvcPropertyMappingConfigurationService->generateTrustedPropertiesToken($formFieldNames, $this->getFieldNamePrefix());
457 return '<input type="hidden" name="' . $this->prefixFieldName('__trustedProperties') . '" value="' . htmlspecialchars($requestHash) . '" />';
458 }
459 }