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