[+BUGFIX] Incorrect type of Error thrown (FLOW3 Error)
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / ViewHelpers / Form / AbstractFormFieldViewHelper.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 Lesser General Public License as published by the *
8 * Free 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 Lesser *
14 * General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with the script. *
18 * If not, see http://www.gnu.org/licenses/lgpl.html *
19 * *
20 * The TYPO3 project - inspiring people to share! *
21 * */
22
23 /**
24 * Abstract Form View Helper. Bundles functionality related to direct property access of objects in other Form ViewHelpers.
25 *
26 * If you set the "property" attribute to the name of the property to resolve from the object, this class will
27 * automatically set the name and value of a form element.
28 *
29 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
30 * @api
31 */
32 abstract class Tx_Fluid_ViewHelpers_Form_AbstractFormFieldViewHelper extends Tx_Fluid_ViewHelpers_Form_AbstractFormViewHelper {
33
34 /**
35 * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
36 */
37 protected $configurationManager;
38
39 /**
40 * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager
41 * @return void
42 */
43 public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
44 $this->configurationManager = $configurationManager;
45 }
46
47 /**
48 * Initialize arguments.
49 *
50 * @return void
51 * @author Sebastian Kurfürst <sebastian@typo3.org>
52 * @api
53 */
54 public function initializeArguments() {
55 parent::initializeArguments();
56 $this->registerArgument('name', 'string', 'Name of input tag');
57 $this->registerArgument('value', 'mixed', 'Value of input tag');
58 $this->registerArgument('property', 'string', 'Name of Object Property. If used in conjunction with <f:form object="...">, "name" and "value" properties will be ignored.');
59 }
60
61 /**
62 * Get the name of this form element.
63 * Either returns arguments['name'], or the correct name for Object Access.
64 *
65 * In case property is something like bla.blubb (hierarchical), then [bla][blubb] is generated.
66 *
67 * @return string Name
68 * @author Sebastian Kurfürst <sebastian@typo3.org>
69 * @author Robert Lemke <robert@typo3.org>
70 * @author Karsten Dambekalns <karsten@typo3.org>
71 * @author Bastian Waidelich <bastian@typo3.org>
72 */
73 protected function getName() {
74 $name = $this->getNameWithoutPrefix();
75 return $this->prefixFieldName($name);
76 }
77
78 /**
79 * Get the name of this form element, without prefix.
80 *
81 * @return string name
82 * @author Sebastian Kurfürst <sebastian@typo3.org>
83 * @author Robert Lemke <robert@typo3.org>
84 * @author Karsten Dambekalns <karsten@typo3.org>
85 * @author Bastian Waidelich <bastian@typo3.org>
86 */
87 protected function getNameWithoutPrefix() {
88 if ($this->isObjectAccessorMode()) {
89 $formObjectName = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObjectName');
90 if (!empty($formObjectName)) {
91 $propertySegments = explode('.', $this->arguments['property']);
92 $propertyPath = '';
93 foreach ($propertySegments as $segment) {
94 $propertyPath .= '[' . $segment . ']';
95 }
96 $name = $formObjectName . $propertyPath;
97 } else {
98 $name = $this->arguments['property'];
99 }
100 } else {
101 $name = $this->arguments['name'];
102 }
103 if ($this->hasArgument('value') && is_object($this->arguments['value'])) {
104 if (NULL !== $this->persistenceManager->getIdentifierByObject($this->arguments['value'])
105 && (!$this->persistenceManager->getBackend()->isNewObject($this->arguments['value']))) {
106 $name .= '[__identity]';
107 }
108 }
109
110 return $name;
111 }
112
113 /**
114 * Get the value of this form element.
115 * Either returns arguments['value'], or the correct value for Object Access.
116 *
117 * @return mixed Value
118 * @author Sebastian Kurfürst <sebastian@typo3.org>
119 * @author Robert Lemke <robert@typo3.org>
120 * @author Bastian Waidelich <bastian@typo3.org>
121 */
122 protected function getValue() {
123 $value = NULL;
124 if ($this->hasArgument('value')) {
125 $value = $this->arguments['value'];
126 } elseif ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper') && $this->hasMappingErrorOccured()) {
127 $value = $this->getLastSubmittedFormData();
128 } elseif ($this->isObjectAccessorMode() && $this->viewHelperVariableContainer->exists('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObject')) {
129 $this->addAdditionalIdentityPropertiesIfNeeded();
130 $value = $this->getPropertyValue();
131 }
132 if (is_object($value)) {
133 $identifier = $this->persistenceManager->getIdentifierByObject($value);
134 if ($identifier !== NULL) {
135 $value = $identifier;
136 }
137 }
138 return $value;
139 }
140
141 /**
142 * Checks if a property mapping error has occured in the last request.
143 *
144 * @return boolean TRUE if a mapping error occured, FALSE otherwise
145 */
146 protected function hasMappingErrorOccured() {
147 return ($this->controllerContext->getRequest()->getOriginalRequest() !== NULL);
148 }
149
150 /**
151 * Get the form data which has last been submitted; only returns valid data in case
152 * a property mapping error has occured. Check with hasMappingErrorOccured() before!
153 *
154 * @return mixed
155 */
156 protected function getLastSubmittedFormData() {
157 $propertyPath = rtrim(preg_replace('/(\]\[|\[|\])/', '.', $this->getNameWithoutPrefix()), '.');
158 $value = Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($this->controllerContext->getRequest()->getOriginalRequest()->getArguments(), $propertyPath);
159 return $value;
160 }
161
162 /**
163 * Add additional identity properties in case the current property is hierarchical (of the form "bla.blubb").
164 * Then, [bla][__identity] has to be generated as well.
165 *
166 * @author Sebastian Kurfuerst <sebastian@typo3.org>
167 * @return void
168 */
169 protected function addAdditionalIdentityPropertiesIfNeeded() {
170 $propertySegments = explode('.', $this->arguments['property']);
171 if (count($propertySegments) >= 2) {
172 // hierarchical property. If there is no "." inside (thus $propertySegments == 1), we do not need to do anything
173 $formObject = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObject');
174
175 $objectName = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObjectName');
176 // If Count == 2 -> we need to go through the for-loop exactly once
177 for ($i=1; $i < count($propertySegments); $i++) {
178 $object = Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($formObject, implode('.', array_slice($propertySegments, 0, $i)));
179 $objectName .= '[' . $propertySegments[$i-1] . ']';
180 $hiddenIdentityField = $this->renderHiddenIdentityField($object, $objectName);
181
182 // Add the hidden identity field to the ViewHelperVariableContainer
183 $additionalIdentityProperties = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties');
184 $additionalIdentityProperties[$objectName] = $hiddenIdentityField;
185 $this->viewHelperVariableContainer->addOrUpdate('Tx_Fluid_ViewHelpers_FormViewHelper', 'additionalIdentityProperties', $additionalIdentityProperties);
186 }
187 }
188 }
189
190 /**
191 * Get the current property of the object bound to this form.
192 *
193 * @return mixed Value
194 * @author Bastian Waidelich <bastian@typo3.org>
195 */
196 protected function getPropertyValue() {
197
198 $formObject = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObject');
199 $propertyName = $this->arguments['property'];
200
201 if (is_array($formObject)) {
202 return isset($formObject[$propertyName]) ? $formObject[$propertyName] : NULL;
203 }
204 return Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($formObject, $propertyName);
205 }
206
207 /**
208 * Internal method which checks if we should evaluate a domain object or just output arguments['name'] and arguments['value']
209 *
210 * @return boolean TRUE if we should evaluate the domain object, FALSE otherwise.
211 * @author Sebastian Kurfürst <sebastian@typo3.org>
212 */
213 protected function isObjectAccessorMode() {
214 return $this->hasArgument('property')
215 && $this->viewHelperVariableContainer->exists('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObjectName');
216 }
217
218 /**
219 * Add an CSS class if this view helper has errors
220 *
221 * @return void
222 * @author Christopher Hlubek <hlubek@networkteam.com>
223 * @author Bastian Waidelich <bastian@typo3.org>
224 */
225 protected function setErrorClassAttribute() {
226 if ($this->hasArgument('class')) {
227 $cssClass = $this->arguments['class'] . ' ';
228 } else {
229 $cssClass = '';
230 }
231
232 if ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper')) {
233 $mappingResultsForProperty = $this->getMappingResultsForProperty();
234 if ($mappingResultsForProperty->hasErrors()) {
235 if ($this->hasArgument('errorClass')) {
236 $cssClass .= $this->arguments['errorClass'];
237 } else {
238 $cssClass .= 'error';
239 }
240 $this->tag->addAttribute('class', $cssClass);
241 }
242 } else {
243 // @deprecated since Extbase 1.4.0, will be removed in Extbase 1.6.0.
244 $errors = $this->getErrorsForProperty();
245 if (count($errors) > 0) {
246 if ($this->hasArgument('errorClass')) {
247 $cssClass .= $this->arguments['errorClass'];
248 } else {
249 $cssClass .= 'error';
250 }
251 $this->tag->addAttribute('class', $cssClass);
252 }
253 }
254 }
255
256 /**
257 * Get errors for the property and form name of this view helper
258 *
259 * @return array<Tx_Extbase_Error_Result> Array of errors
260 * @author Christopher Hlubek <hlubek@networkteam.com>
261 * @author Bastian Waidelich <bastian@typo3.org>
262 * @author Sebastian Kurfürst <sebastian@typo3.org>
263 */
264 protected function getMappingResultsForProperty() {
265 if (!$this->isObjectAccessorMode()) {
266 return new Tx_Extbase_Error_Result();
267 }
268 $originalRequestMappingResults = $this->controllerContext->getRequest()->getOriginalRequestMappingResults();
269 $formObjectName = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObjectName');
270
271 return $originalRequestMappingResults->forProperty($formObjectName)->forProperty($this->arguments['property']);
272 }
273
274 /**
275 * Get errors for the property and form name of this view helper
276 *
277 * @return array An array of Tx_Fluid_Error_Error objects
278 * @author Christopher Hlubek <hlubek@networkteam.com>
279 * @author Bastian Waidelich <bastian@typo3.org>
280 * @deprecated since Extbase 1.4.0, will be removed in Extbase 1.6.0.
281 */
282 protected function getErrorsForProperty() {
283 if (!$this->isObjectAccessorMode()) {
284 return array();
285 }
286 $errors = $this->controllerContext->getRequest()->getErrors();
287 $formObjectName = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'formObjectName');
288 $propertyName = $this->arguments['property'];
289 $formErrors = array();
290 foreach ($errors as $error) {
291 if ($error instanceof Tx_Extbase_Validation_PropertyError && $error->getPropertyName() === $formObjectName) {
292 $formErrors = $error->getErrors();
293 foreach ($formErrors as $formError) {
294 if ($formError instanceof Tx_Extbase_Validation_PropertyError && $formError->getPropertyName() === $propertyName) {
295 return $formError->getErrors();
296 }
297 }
298 }
299 }
300 return array();
301 }
302
303 /**
304 * Renders a hidden field with the same name as the element, to make sure the empty value is submitted
305 * in case nothing is selected. This is needed for checkbox and multiple select fields
306 *
307 * @return string the hidden field.
308 * @author Sebastian Kurfürst <sebastian@typo3.org>
309 * @author Bastian Waidelich <bastian@typo3.org>
310 */
311 protected function renderHiddenFieldForEmptyValue() {
312 $hiddenFieldNames = array();
313 if ($this->viewHelperVariableContainer->exists('Tx_Fluid_ViewHelpers_FormViewHelper', 'renderedHiddenFields')) {
314 $hiddenFieldNames = $this->viewHelperVariableContainer->get('Tx_Fluid_ViewHelpers_FormViewHelper', 'renderedHiddenFields');
315 }
316
317 $fieldName = $this->getName();
318 if (substr($fieldName, -2) === '[]') {
319 $fieldName = substr($fieldName, 0, -2);
320 }
321 if (!in_array($fieldName, $hiddenFieldNames)) {
322 $hiddenFieldNames[] = $fieldName;
323 $this->viewHelperVariableContainer->addOrUpdate('Tx_Fluid_ViewHelpers_FormViewHelper', 'renderedHiddenFields', $hiddenFieldNames);
324
325 return '<input type="hidden" name="' . htmlspecialchars($fieldName) . '" value="" />';
326 }
327 return '';
328 }
329 }
330
331 ?>