[TASK] Merge submodule linkvalidator into core
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Factory / TypoScriptFactory.php
1 <?php
2 namespace TYPO3\CMS\Form\Domain\Factory;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2008-2013 Patrick Broens (patrick@patrickbroens.nl)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 /**
28 * Typoscript factory for form
29 *
30 * Takes the incoming Typoscipt and adds all the necessary form objects
31 * according to the configuration.
32 *
33 * @author Patrick Broens <patrick@patrickbroens.nl>
34 */
35 class TypoScriptFactory implements \TYPO3\CMS\Core\SingletonInterface {
36
37 /**
38 * @var string
39 */
40 const PROPERTY_DisableContentElement = 'disableContentElement';
41
42 /**
43 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
44 */
45 protected $localContentObject;
46
47 /**
48 * @var boolean
49 */
50 protected $disableContentElement = FALSE;
51
52 /**
53 * Build model from Typoscript
54 *
55 * @param array $typoscript Typoscript containing all configuration
56 * @return \TYPO3\CMS\Form\Domain\Model\Form The form object containing the child elements
57 */
58 public function buildModelFromTyposcript(array $typoscript) {
59 if (isset($typoscript[self::PROPERTY_DisableContentElement])) {
60 $this->setDisableContentElement($typoscript[self::PROPERTY_DisableContentElement]);
61 }
62 $this->setLayoutHandler($typoscript);
63 $form = $this->createElement('form', $typoscript);
64 return $form;
65 }
66
67 /**
68 * Disables the content element.
69 *
70 * @param boolean $disableContentElement
71 * @return void
72 */
73 public function setDisableContentElement($disableContentElement) {
74 $this->disableContentElement = (bool) $disableContentElement;
75 }
76
77 /**
78 * Rendering of a "numerical array" of Form objects from TypoScript
79 * Creates new object for each element found
80 *
81 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement Parent model object
82 * @param array $arguments Configuration array
83 * @throws \InvalidArgumentException
84 * @return void
85 */
86 public function getChildElementsByIntegerKey(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement, array $typoscript) {
87 if (is_array($typoscript)) {
88 $keys = \TYPO3\CMS\Core\TypoScript\TemplateService::sortedKeyList($typoscript);
89 foreach ($keys as $key) {
90 $class = $typoscript[$key];
91 if (intval($key) && !strstr($key, '.')) {
92 if (isset($typoscript[$key . '.'])) {
93 $elementArguments = $typoscript[$key . '.'];
94 } else {
95 $elementArguments = array();
96 }
97 $this->setElementType($parentElement, $class, $elementArguments);
98 }
99 }
100 } else {
101 throw new \InvalidArgumentException('Container element with id=' . $parentElement->getElementId() . ' has no configuration which means no children.', 1333754854);
102 }
103 }
104
105 /**
106 * Create and add element by type.
107 * This can be a derived Typoscript object by "<",
108 * a form element, or a regular Typoscript object.
109 *
110 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement The parent for the new element
111 * @param string $class Classname for the element
112 * @param array $arguments Configuration array
113 * @return void
114 */
115 public function setElementType(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement, $class, array $arguments) {
116 if (in_array($class, \TYPO3\CMS\Form\Utility\FormUtility::getInstance()->getFormObjects())) {
117 $this->addElement($parentElement, $class, $arguments);
118 } elseif ($this->disableContentElement === FALSE) {
119 if (substr($class, 0, 1) == '<') {
120 $key = trim(substr($class, 1));
121 /** @var $typoscriptParser \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser */
122 $typoscriptParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
123 $oldArguments = $arguments;
124 list($class, $arguments) = $typoscriptParser->getVal($key, $GLOBALS['TSFE']->tmpl->setup);
125 if (is_array($oldArguments) && count($oldArguments)) {
126 $arguments = $this->getLocalConentObject()->joinTSarrays($arguments, $oldArguments);
127 }
128 $GLOBALS['TT']->incStackPointer();
129 $contentObject = array(
130 'cObj' => $class,
131 'cObj.' => $arguments
132 );
133 $this->addElement($parentElement, 'content', $contentObject);
134 $GLOBALS['TT']->decStackPointer();
135 } else {
136 $contentObject = array(
137 'cObj' => $class,
138 'cObj.' => $arguments
139 );
140 $this->addElement($parentElement, 'content', $contentObject);
141 }
142 }
143 }
144
145 /**
146 * Add child object to this element
147 *
148 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement Parent model object
149 * @param string $class Type of element
150 * @param array $arguments Configuration array
151 * @return object
152 */
153 public function addElement(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $parentElement, $class, array $arguments = array()) {
154 $element = $this->createElement($class, $arguments);
155 $parentElement->addElement($element);
156 }
157
158 /**
159 * Create element by loading class
160 * and instantiating the object
161 *
162 * @param string $class Type of element
163 * @param array $arguments Configuration array
164 * @return \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement
165 */
166 public function createElement($class, array $arguments = array()) {
167 $class = strtolower((string) $class);
168 if ($class === 'form') {
169 $className = 'TYPO3\\CMS\\Form\\Domain\\Model\\' . ucfirst($class);
170 } else {
171 $className = 'TYPO3\\CMS\\Form\\Domain\\Model\\Element\\' . ucfirst($class) . 'Element';
172 }
173 /** @var $object \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement */
174 $object = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className);
175 if ($object->getElementType() === \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement::ELEMENT_TYPE_CONTENT) {
176 $object->setData($arguments['cObj'], $arguments['cObj.']);
177 } elseif ($object->getElementType() === \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement::ELEMENT_TYPE_PLAIN) {
178 $object->setProperties($arguments);
179 } elseif ($object->getElementType() === \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement::ELEMENT_TYPE_FORM) {
180 $object->setData($arguments['data']);
181 $this->reconstituteElement($object, $arguments);
182 } else {
183 throw new \InvalidArgumentException('Element type "' . $object->getElementType() . '" is not supported.', 1333754878);
184 }
185 return $object;
186 }
187
188 /**
189 * Reconstitutes the domain model of the accordant element.
190 *
191 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element
192 * @param array $arguments Configuration array
193 * @return void
194 */
195 protected function reconstituteElement(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element, array $arguments = array()) {
196 if (isset($arguments['value.'])) {
197 $cObj = $this->getLocalConentObject();
198 $arguments['value'] = $cObj->stdWrap($arguments['value'], $arguments['value.']);
199 }
200
201 $this->setAttributes($element, $arguments);
202 $this->setAdditionals($element, $arguments);
203 if (isset($arguments['filters.'])) {
204 $this->setFilters($element, $arguments['filters.']);
205 }
206 $element->setLayout($arguments['layout']);
207 $element->setValue($arguments['value']);
208 $element->setName($arguments['name']);
209 $element->setMessagesFromValidation();
210 $element->setErrorsFromValidation();
211 $element->checkFilterAndSetIncomingDataFromRequest();
212 $this->getChildElementsByIntegerKey($element, $arguments);
213 }
214
215 /**
216 * Set the attributes
217 *
218 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element Model object
219 * @param array $arguments Arguments
220 * @return void
221 */
222 public function setAttributes(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element, array $arguments) {
223 if ($element->hasAllowedAttributes()) {
224 $attributes = $element->getAllowedAttributes();
225 $mandatoryAttributes = $element->getMandatoryAttributes();
226 foreach ($attributes as $attribute => $value) {
227 if (isset($arguments[$attribute]) || isset($arguments[$attribute . '.']) || in_array($attribute, $mandatoryAttributes) || !empty($value)) {
228 if (!empty($arguments[$attribute])) {
229 $value = $arguments[$attribute];
230 } elseif (!empty($arguments[($attribute . '.')])) {
231 $value = $arguments[$attribute . '.'];
232 }
233 try {
234 $element->setAttribute($attribute, $value);
235 } catch (\Exception $exception) {
236 throw new \RuntimeException('Cannot call user function for attribute ' . ucfirst($attribute), 1333754904);
237 }
238 }
239 }
240 } else {
241 throw new \InvalidArgumentException('The element with id=' . $element->getElementId() . ' has no default attributes set.', 1333754925);
242 }
243 }
244
245 /**
246 * Set the additionals from Element Typoscript configuration
247 *
248 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element Model object
249 * @param array $arguments Arguments
250 * @return void
251 */
252 public function setAdditionals(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element, array $arguments) {
253 if (!empty($arguments)) {
254 if ($element->hasAllowedAdditionals()) {
255 $additionals = $element->getAllowedAdditionals();
256 foreach ($additionals as $additional) {
257 if (isset($arguments[$additional . '.']) || isset($arguments[$additional])) {
258 if (isset($arguments[$additional]) && isset($arguments[$additional . '.'])) {
259 $value = $arguments[$additional . '.'];
260 $type = $arguments[$additional];
261 } elseif (isset($arguments[$additional . '.'])) {
262 $value = $arguments[$additional . '.'];
263 $type = 'TEXT';
264 } else {
265 $value['value'] = $arguments[$additional];
266 $type = 'TEXT';
267 }
268 try {
269 $element->setAdditional($additional, $type, $value);
270 } catch (\Exception $exception) {
271 throw new \RuntimeException('Cannot call user function for additional ' . ucfirst($additional), 1333754941);
272 }
273 }
274 if (isset($arguments['layout.'][$additional]) && $element->additionalIsSet($additional)) {
275 $layout = $arguments['layout.'][$additional];
276 $element->setAdditionalLayout($additional, $layout);
277 }
278 }
279 } else {
280 throw new \InvalidArgumentException('The element with id=' . $element->getElementId() . ' has no additionals set.', 1333754962);
281 }
282 }
283 }
284
285 /**
286 * Add the filters according to the settings in the Typoscript array
287 *
288 * @param \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element Model object
289 * @param array $arguments TypoScript
290 * @return void
291 */
292 protected function setFilters(\TYPO3\CMS\Form\Domain\Model\Element\AbstractElement $element, array $arguments) {
293 $keys = \TYPO3\CMS\Core\TypoScript\TemplateService::sortedKeyList($arguments);
294 foreach ($keys as $key) {
295 $class = $arguments[$key];
296 if (intval($key) && !strstr($key, '.')) {
297 $filterArguments = $arguments[$key . '.'];
298 $filter = $element->makeFilter($class, $filterArguments);
299 $element->addFilter($filter);
300 }
301 }
302 }
303
304 /**
305 * Set the layout handler
306 *
307 * @param array $typoscript TypoScript
308 * @return \TYPO3\CMS\Form\Layout The layout handler
309 */
310 public function setLayoutHandler(array $typoscript) {
311 /** @var $layoutHandler \TYPO3\CMS\Form\Layout */
312 $layoutHandler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Form\\Layout');
313 // singleton
314 if (isset($typoscript['layout.'])) {
315 $layoutHandler->setLayout($typoscript['layout.']);
316 }
317 return $layoutHandler;
318 }
319
320 /**
321 * Set the request handler
322 *
323 * @param array $typoscript TypoScript
324 * @return \TYPO3\CMS\Form\Request The request handler
325 */
326 public function setRequestHandler($typoscript) {
327 $prefix = isset($typoscript['prefix']) ? $typoscript['prefix'] : '';
328 $method = isset($typoscript['method']) ? $typoscript['method'] : '';
329 /** @var $requestHandler \TYPO3\CMS\Form\Request */
330 $requestHandler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Form\\Request');
331 // singleton
332 $requestHandler->setPrefix($prefix);
333 $requestHandler->setMethod($method);
334 $requestHandler->storeFiles();
335 return $requestHandler;
336 }
337
338 /**
339 * Set the validation rules
340 *
341 * Makes the validation object and adds rules to it
342 *
343 * @param array $typoscript TypoScript
344 * @return \TYPO3\CMS\Form\Utility\ValidatorUtility The validation object
345 */
346 public function setRules(array $typoscript) {
347 $rulesTyposcript = isset($typoscript['rules.']) ? $typoscript['rules.'] : NULL;
348 /** @var $rulesClass \TYPO3\CMS\Form\Utility\ValidatorUtility */
349 $rulesClass = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Form\\Utility\\ValidatorUtility', $rulesTyposcript);
350 // singleton
351 if (is_array($rulesTyposcript)) {
352 $keys = \TYPO3\CMS\Core\TypoScript\TemplateService::sortedKeyList($rulesTyposcript);
353 foreach ($keys as $key) {
354 $class = $rulesTyposcript[$key];
355 if (intval($key) && !strstr($key, '.')) {
356 $elementArguments = $rulesTyposcript[$key . '.'];
357 $rule = $rulesClass->createRule($class, $elementArguments);
358 $rule->setFieldName($elementArguments['element']);
359 $breakOnError = isset($elementArguments['breakOnError']) ? $elementArguments['breakOnError'] : FALSE;
360 $rulesClass->addRule($rule, $elementArguments['element'], $breakOnError);
361 }
362 }
363 }
364 return $rulesClass;
365 }
366
367 /**
368 * Gets the local content object.
369 *
370 * @return \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
371 */
372 protected function getLocalConentObject() {
373 if (!isset($this->localContentObject)) {
374 $this->localContentObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
375 }
376 return $this->localContentObject;
377 }
378
379 }
380 ?>