[~TASK] Fluid (ViewHelpers): Fixed BE support of the ViewHelpers cObject, format...
[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 * @scope prototype
55 */
56 class Tx_Fluid_ViewHelpers_FormViewHelper extends Tx_Fluid_ViewHelpers_Form_AbstractFormViewHelper {
57
58 /**
59 * @var string
60 */
61 protected $tagName = 'form';
62
63 /**
64 * @var Tx_Extbase_Security_Channel_RequestHashService
65 */
66 protected $requestHashService;
67
68 /**
69 * We need the arguments of the formActionUri on requesthash calculation
70 * therefore we will store them in here right after calling uriBuilder
71 *
72 * @var array
73 */
74 protected $formActionUriArguments;
75
76 /**
77 * Inject a request hash service
78 *
79 * @param Tx_Extbase_Security_Channel_RequestHashService $requestHashService The request hash service
80 * @return void
81 * @author Sebastian Kurfürst <sebastian@typo3.org>
82 */
83 public function injectRequestHashService(Tx_Extbase_Security_Channel_RequestHashService $requestHashService) {
84 $this->requestHashService = $requestHashService;
85 }
86
87 /**
88 * Initialize arguments.
89 *
90 * @return void
91 */
92 public function initializeArguments() {
93 $this->registerTagAttribute('enctype', 'string', 'MIME type with which the form is submitted');
94 $this->registerTagAttribute('method', 'string', 'Transfer type (GET or POST)');
95 $this->registerTagAttribute('name', 'string', 'Name of form');
96 $this->registerTagAttribute('onreset', 'string', 'JavaScript: On reset of the form');
97 $this->registerTagAttribute('onsubmit', 'string', 'JavaScript: On submit of the form');
98
99 $this->registerUniversalTagAttributes();
100 }
101
102 /**
103 * Render the form.
104 *
105 * @param string $action Target action
106 * @param array $arguments Arguments
107 * @param string $controller Target controller
108 * @param string $extensionName Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used
109 * @param string $pluginName Target plugin. If empty, the current plugin name is used
110 * @param integer $pageUid Target page uid
111 * @param mixed $object Object to use for the form. Use in conjunction with the "property" attribute on the sub tags
112 * @param integer $pageType Target page type
113 * @param boolean $noCache set this to disable caching for the target page. You should not need this.
114 * @param boolean $noCacheHash set this to supress the cHash query parameter created by TypoLink. You should not need this.
115 * @param string $section The anchor to be added to the action URI (only active if $actionUri is not set)
116 * @param string $format The requested format (e.g. ".html") of the target page (only active if $actionUri is not set)
117 * @param array $additionalParams additional action URI query parameters that won't be prefixed like $arguments (overrule $arguments) (only active if $actionUri is not set)
118 * @param boolean $absolute If set, an absolute action URI is rendered (only active if $actionUri is not set)
119 * @param boolean $addQueryString If set, the current query parameters will be kept in the action URI (only active if $actionUri is not set)
120 * @param array $argumentsToBeExcludedFromQueryString arguments to be removed from the action URI. Only active if $addQueryString = TRUE and $actionUri is not set
121 * @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
122 * @param string $actionUri can be used to overwrite the "action" attribute of the form tag
123 * @return string rendered form
124 */
125 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) {
126 $this->setFormActionUri();
127
128 if (strtolower($this->arguments['method']) === 'get') {
129 $this->tag->addAttribute('method', 'get');
130 } else {
131 $this->tag->addAttribute('method', 'post');
132 }
133
134 $this->addFormNameToViewHelperVariableContainer();
135 $this->addFormObjectToViewHelperVariableContainer();
136 $this->addFieldNamePrefixToViewHelperVariableContainer();
137 $this->addFormFieldNamesToViewHelperVariableContainer();
138
139 $formContent = $this->renderChildren();
140
141 $content = $this->renderHiddenIdentityField($this->arguments['object'], $this->arguments['name']);
142 $content .= $this->renderAdditionalIdentityFields();
143 $content .= $this->renderHiddenReferrerFields();
144 $content .= $this->renderRequestHashField(); // Render hmac after everything else has been rendered
145 $content .= $formContent;
146
147 $this->tag->setContent($content);
148
149 $this->removeFieldNamePrefixFromViewHelperVariableContainer();
150 $this->removeFormObjectFromViewHelperVariableContainer();
151 $this->removeFormNameFromViewHelperVariableContainer();
152 $this->removeFormFieldNamesFromViewHelperVariableContainer();
153 $this->removeCheckboxFieldNamesFromViewHelperVariableContainer();
154
155 return $this->tag->render();
156 }
157
158 /**
159 * Sets the "action" attribute of the form tag
160 *
161 * @return void
162 */
163 protected function setFormActionUri() {
164 if ($this->arguments->hasArgument('actionUri')) {
165 $formActionUri = $this->arguments['actionUri'];
166 } else {
167 $uriBuilder = $this->controllerContext->getUriBuilder();
168 $formActionUri = $uriBuilder
169 ->reset()
170 ->setTargetPageUid($this->arguments['pageUid'])
171 ->setTargetPageType($this->arguments['pageType'])
172 ->setNoCache($this->arguments['noCache'])
173 ->setUseCacheHash(!$this->arguments['noCacheHash'])
174 ->setSection($this->arguments['section'])
175 ->setCreateAbsoluteUri($this->arguments['absolute'])
176 ->setArguments($this->arguments['additionalParams'])
177 ->setAddQueryString($this->arguments['addQueryString'])
178 ->setArgumentsToBeExcludedFromQueryString($this->arguments['argumentsToBeExcludedFromQueryString'])
179 ->setFormat($this->arguments['format'])
180 ->uriFor($this->arguments['action'], $this->arguments['arguments'], $this->arguments['controller'], $this->arguments['extensionName'], $this->arguments['pluginName']);
181 $this->formActionUriArguments = $uriBuilder->getArguments();
182 }
183 $this->tag->addAttribute('action', $formActionUri);
184 }
185
186 /**
187 * Render additional identity fields which were registered by form elements.
188 * This happens if a form field is defined like property="bla.blubb" - then we might need an identity property for the sub-object "bla".
189 *
190 * @return string HTML-string for the additional identity properties
191 * @author Sebastian Kurfürst <sebastian@typo3.org>
192 */
193 protected function renderAdditionalIdentityFields() {
194 if ($this->viewHelperVariableContainer->exists('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties')) {
195 $additionalIdentityProperties = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties');
196 $output = '';
197 foreach ($additionalIdentityProperties as $identity) {
198 $output .= chr(10) . $identity;
199 }
200 return $output;
201 }
202 return '';
203 }
204
205 /**
206 * Renders hidden form fields for referrer information about
207 * the current controller and action.
208 *
209 * @return string Hidden fields with referrer information
210 * @todo filter out referrer information that is equal to the target (e.g. same packageKey)
211 */
212 protected function renderHiddenReferrerFields() {
213 $request = $this->controllerContext->getRequest();
214 $extensionName = $request->getControllerExtensionName();
215 $controllerName = $request->getControllerName();
216 $actionName = $request->getControllerActionName();
217
218 $result = chr(10);
219 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[extensionName]') . '" value="' . $extensionName . '" />' . chr(10);
220 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[controllerName]') . '" value="' . $controllerName . '" />' . chr(10);
221 $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[actionName]') . '" value="' . $actionName . '" />' . chr(10);
222 return $result;
223 }
224
225 /**
226 * Adds the form name to the ViewHelperVariableContainer if the name attribute is specified.
227 *
228 * @return void
229 */
230 protected function addFormNameToViewHelperVariableContainer() {
231 if ($this->arguments->hasArgument('name')) {
232 $this->viewHelperVariableContainer->add('Tx_Fluid_ViewHelpers_FormViewHelper', 'formName', $this->arguments['name']);
233 }
234 }
235
236 /**
237 * Removes the form name from the ViewHelperVariableContainer.
238 *
239 * @return void
240 */
241 protected function removeFormNameFromViewHelperVariableContainer() {
242 if ($this->arguments->hasArgument('name')) {
243 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_FormViewHelper', 'formName');
244 }
245 }
246
247 /**
248 * Adds the object that is bound to this form to the ViewHelperVariableContainer if the formObject attribute is specified.
249 *
250 * @return void
251 */
252 protected function addFormObjectToViewHelperVariableContainer() {
253 if ($this->arguments->hasArgument('object')) {
254 $this->viewHelperVariableContainer->add('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObject', $this->arguments['object']);
255 $this->viewHelperVariableContainer->add('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties', array());
256 }
257 }
258
259 /**
260 * Removes the form object from the ViewHelperVariableContainer.
261 *
262 * @return void
263 */
264 protected function removeFormObjectFromViewHelperVariableContainer() {
265 if ($this->arguments->hasArgument('object')) {
266 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObject');
267 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties');
268 }
269 }
270
271 /**
272 * Adds the field name prefix to the ViewHelperVariableContainer
273 *
274 * @return void
275 */
276 protected function addFieldNamePrefixToViewHelperVariableContainer() {
277 $fieldNamePrefix = $this->getFieldNamePrefix();
278 $this->viewHelperVariableContainer->add('Tx_Fluid_ViewHelpers_FormViewHelper', 'fieldNamePrefix', $fieldNamePrefix);
279 }
280
281 /**
282 * Get the field name prefix
283 *
284 * @return string
285 */
286 protected function getFieldNamePrefix() {
287 if ($this->arguments->hasArgument('fieldNamePrefix')) {
288 return $this->arguments['fieldNamePrefix'];
289 } else {
290 return $this->getDefaultFieldNamePrefix();
291 }
292 }
293
294 /**
295 * Removes field name prefix from the ViewHelperVariableContainer
296 *
297 * @return void
298 */
299 protected function removeFieldNamePrefixFromViewHelperVariableContainer() {
300 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_FormViewHelper', 'fieldNamePrefix');
301 }
302
303 /**
304 * Adds a container for form field names to the ViewHelperVariableContainer
305 *
306 * @return void
307 * @author Sebastian Kurfürst <sebastian@typo3.org>
308 */
309 protected function addFormFieldNamesToViewHelperVariableContainer() {
310 $this->viewHelperVariableContainer->add('Tx_Fluid_ViewHelpers_FormViewHelper', 'formFieldNames', array());
311 }
312
313 /**
314 * Removes the container for form field names from the ViewHelperVariableContainer
315 *
316 * @return void
317 * @author Sebastian Kurfürst <sebastian@typo3.org>
318 */
319 protected function removeFormFieldNamesFromViewHelperVariableContainer() {
320 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_FormViewHelper', 'formFieldNames');
321 }
322
323 /**
324 * Render the request hash field
325 *
326 * @return string the hmac field
327 * @author Sebastian Kurfürst <sebastian@typo3.org>
328 */
329 protected function renderRequestHashField() {
330 $formFieldNames = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formFieldNames');
331 $this->postProcessUriArgumentsForRequesthash($this->formActionUriArguments, $formFieldNames);
332 $requestHash = $this->requestHashService->generateRequestHash($formFieldNames, $this->getFieldNamePrefix());
333 // in v4, we need to prefix __hmac as well to make it show up in the request object.
334 return '<input type="hidden" name="' . $this->prefixFieldName('__hmac') . '" value="' . htmlspecialchars($requestHash) . '" />';
335 }
336
337 /**
338 * Add the URI arguments after postprocessing to the request hash as well.
339 */
340 protected function postProcessUriArgumentsForRequestHash($arguments, &$results, $currentPrefix = '', $level = 0) {
341 if (!count($arguments)) return;
342 foreach ($arguments as $argumentName => $argumentValue) {
343 if (is_array($argumentValue)) {
344 $prefix = ($level==0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']');
345 $this->postProcessUriArgumentsForRequestHash($argumentValue, $results, $prefix, $level+1);
346 } else {
347 $results[] = ($level==0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']');
348 }
349 }
350 }
351
352 /**
353 * Retrieves the default field name prefix for this form
354 *
355 * @return string default field name prefix
356 */
357 protected function getDefaultFieldNamePrefix() {
358 $request = $this->controllerContext->getRequest();
359 if ($this->arguments->hasArgument('extensionName')) {
360 $extensionName = $this->arguments['extensionName'];
361 } else {
362 $extensionName = $request->getControllerExtensionName();
363 }
364 if ($this->arguments->hasArgument('pluginName')) {
365 $pluginName = $this->arguments['pluginName'];
366 } else {
367 $pluginName = $request->getPluginName();
368 }
369
370 return 'tx_' . strtolower($extensionName) . '_' . strtolower($pluginName);
371 }
372
373 /**
374 * Remove Checkbox field names from ViewHelper variable container, to start from scratch when a new form starts.
375 */
376 protected function removeCheckboxFieldNamesFromViewHelperVariableContainer() {
377 if ($this->viewHelperVariableContainer->exists('Tx_Fluid_ViewHelpers_Form_CheckboxViewHelper', 'checkboxFieldNames')) {
378 $this->viewHelperVariableContainer->remove('Tx_Fluid_ViewHelpers_Form_CheckboxViewHelper', 'checkboxFieldNames');
379 }
380 }
381 }
382
383 ?>