762a4159a82e5463018c592078f22d65a6657aed
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / View / Confirmation / Element / AbstractElementView.php
1 <?php
2 namespace TYPO3\CMS\Form\View\Confirmation\Element;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Form\Utility\FormUtility;
19
20 /**
21 * Abstract class for the form elements view
22 *
23 * @author Patrick Broens <patrick@patrickbroens.nl>
24 */
25 abstract class AbstractElementView {
26
27 /**
28 * The model for the current object
29 *
30 * @var \TYPO3\CMS\Form\Domain\Model\Element\AbstractElement
31 */
32 protected $model;
33
34 /**
35 * Wrap for elements
36 *
37 * @var string
38 */
39 protected $elementWrap = '
40 <li>
41 <element />
42 </li>
43 ';
44
45 /**
46 * True if element needs no element wrap
47 * like <li>element</li>
48 *
49 * @var bool
50 */
51 protected $noWrap = FALSE;
52
53 /**
54 * Constructor
55 *
56 * @param object $model Current elements model
57 */
58 public function __construct($model) {
59 $this->model = $model;
60 }
61
62 /**
63 * Parse the XML of a view object,
64 * check the node type and name
65 * and add the proper XML part of child tags
66 * to the DOMDocument of the current tag
67 *
68 * @param \DOMDocument $dom
69 * @param \DOMNode $reference Current XML structure
70 * @param bool $emptyElement
71 * @return bool
72 */
73 protected function parseXML(\DOMDocument $dom, \DOMNode $reference, $emptyElement = FALSE) {
74 $node = $reference->firstChild;
75 while (!is_null($node)) {
76 $deleteNode = FALSE;
77 $nodeType = $node->nodeType;
78 $nodeName = $node->nodeName;
79 switch ($nodeType) {
80 case XML_TEXT_NODE:
81 break;
82 case XML_ELEMENT_NODE:
83 switch ($nodeName) {
84 case 'containerWrap':
85 $containerWrap = $this->render('containerWrap');
86 if ($containerWrap) {
87 $this->replaceNodeWithFragment($dom, $node, $containerWrap);
88 } else {
89 $emptyElement = TRUE;
90 }
91 $deleteNode = TRUE;
92 break;
93 case 'elements':
94 $replaceNode = $this->getChildElements($dom);
95 if ($replaceNode) {
96 $node->parentNode->replaceChild($replaceNode, $node);
97 } else {
98 $emptyElement = TRUE;
99 }
100 break;
101 case 'label':
102 if (!strrchr(get_class($this), 'AdditionalElement')) {
103 if ($this->model->additionalIsSet($nodeName)) {
104 $this->replaceNodeWithFragment($dom, $node, $this->getAdditional('label'));
105 }
106 $deleteNode = TRUE;
107 } else {
108 if (!$this->model->additionalIsSet($nodeName)) {
109 $deleteNode = TRUE;
110 }
111 }
112 break;
113 case 'legend':
114 if (!strrchr(get_class($this), 'AdditionalElement')) {
115 if ($this->model->additionalIsSet($nodeName)) {
116 $this->replaceNodeWithFragment($dom, $node, $this->getAdditional('legend'));
117 }
118 $deleteNode = TRUE;
119 }
120 break;
121 case 'inputvalue':
122 if (array_key_exists('checked', $this->model->getAllowedAttributes())) {
123 if (!$this->model->hasAttribute('checked')) {
124 $emptyElement = TRUE;
125 }
126 } elseif (array_key_exists('selected', $this->model->getAllowedAttributes()) && !$this->model->hasAttribute('selected')) {
127 $emptyElement = TRUE;
128 } else {
129 $inputValue = $this->getInputValue();
130 if ($inputValue != '') {
131 $replaceNode = $dom->createTextNode($this->getInputValue());
132 $node->parentNode->insertBefore($replaceNode, $node);
133 } else {
134 $emptyElement = TRUE;
135 }
136 }
137 $deleteNode = TRUE;
138 break;
139 case 'labelvalue':
140
141 case 'legendvalue':
142 $replaceNode = $dom->createTextNode($this->getAdditionalValue());
143 $node->parentNode->insertBefore($replaceNode, $node);
144 $deleteNode = TRUE;
145 break;
146 }
147 break;
148 }
149 // Parse the child nodes of this node if available
150 if ($node->hasChildNodes()) {
151 $emptyElement = $this->parseXML($dom, $node, $emptyElement);
152 }
153 // Get the current node for deletion if replaced. We need this because nextSibling can be empty
154 $oldNode = $node;
155 // Go to next sibling to parse
156 $node = $node->nextSibling;
157 // Delete the old node. This can only be done after going to the next sibling
158 if ($deleteNode) {
159 $oldNode->parentNode->removeChild($oldNode);
160 }
161 }
162 return $emptyElement;
163 }
164
165 /**
166 * Get the content for the current object as DOMDocument
167 *
168 * @param string $type Type of element for layout
169 * @param bool $returnFirstChild If TRUE, the first child will be returned instead of the DOMDocument
170 * @return \DOMNode XML part of the view object
171 */
172 public function render($type = 'element', $returnFirstChild = TRUE) {
173 $useLayout = $this->getLayout((string)$type);
174 $dom = new \DOMDocument('1.0', 'utf-8');
175 $dom->formatOutput = TRUE;
176 $dom->preserveWhiteSpace = FALSE;
177 $dom->loadXML($useLayout);
178 $emptyElement = $this->parseXML($dom, $dom);
179 if ($emptyElement) {
180 return NULL;
181 } elseif ($returnFirstChild) {
182 return $dom->firstChild;
183 } else {
184 return $dom;
185 }
186 }
187
188 /**
189 * Ask the layoutHandler to get the layout for this object
190 *
191 * @param string $type Layout type
192 * @return string HTML string of the layout to use for this element
193 */
194 public function getLayout($type) {
195 /** @var $layoutHandler \TYPO3\CMS\Form\Layout */
196 $layoutHandler = GeneralUtility::makeInstance(\TYPO3\CMS\Form\Layout::class);
197 switch ($type) {
198 case 'element':
199 $layoutDefault = $this->layout;
200 $layout = $layoutHandler->getLayoutByObject(FormUtility::getInstance()->getLastPartOfClassName($this, TRUE), $layoutDefault);
201 break;
202 case 'elementWrap':
203 $layoutDefault = $this->elementWrap;
204 $elementWrap = $layoutHandler->getLayoutByObject($type, $layoutDefault);
205 $layout = str_replace('<element />', $this->getLayout('element'), $elementWrap);
206 break;
207 case 'containerWrap':
208 $layoutDefault = $this->containerWrap;
209 $layout = $layoutHandler->getLayoutByObject($type, $layoutDefault);
210 break;
211 }
212 return $layout;
213 }
214
215 /**
216 * Replace the current node with a document fragment
217 *
218 * @param \DOMDocument $dom
219 * @param \DOMNode $node Current Node
220 * @param \DOMNode $value Value to import
221 * @return void
222 */
223 public function replaceNodeWithFragment(\DOMDocument $dom, \DOMNode $node, \DOMNode $value) {
224 $replaceNode = $dom->createDocumentFragment();
225 $domNode = $dom->importNode($value, TRUE);
226 $replaceNode->appendChild($domNode);
227 $node->parentNode->insertBefore($replaceNode, $node);
228 }
229
230 /**
231 * Set the attributes on the html tags according to the attributes that are
232 * assigned in the model for a certain element
233 *
234 * @param \DOMElement $domElement DOM element of the specific HTML tag
235 * @return void
236 */
237 public function setAttributes(\DOMElement $domElement) {
238 $attributes = $this->model->getAttributes();
239 foreach ($attributes as $key => $attribute) {
240 if (!empty($attribute)) {
241 $value = $attribute->getValue();
242 if ($value !== '') {
243 $domElement->setAttribute($key, $value);
244 }
245 }
246 }
247 }
248
249 /**
250 * Set a single attribute of a HTML tag specified by key
251 *
252 * @param \DOMElement $domElement DOM element of the specific HTML tag
253 * @param string $key Attribute key
254 * @return void
255 */
256 public function setAttribute(\DOMElement $domElement, $key) {
257 $attribute = $this->model->getAttributeValue((string)$key);
258 if (!empty($attribute)) {
259 $domElement->setAttribute($key, $attribute);
260 }
261 }
262
263 /**
264 * Sets the value of an attribute with the value of another attribute,
265 * for instance equalizing the name and id attributes for the form tag
266 *
267 * @param \DOMElement $domElement DOM element of the specific HTML tag
268 * @param string $key Key of the attribute which needs to be changed
269 * @param string $other Key of the attribute to take the value from
270 * @return void
271 */
272 public function setAttributeWithValueofOtherAttribute(\DOMElement $domElement, $key, $other) {
273 $attribute = $this->model->getAttributeValue((string)$other);
274 if (!empty($attribute)) {
275 $domElement->setAttribute($key, $attribute);
276 }
277 }
278
279 /**
280 * Load and instantiate an additional object
281 *
282 * @param string $class Type of additional
283 * @return object
284 */
285 protected function createAdditional($class) {
286 $class = strtolower((string)$class);
287 $className = 'TYPO3\\CMS\\Form\\View\\Confirmation\\Additional\\' . ucfirst($class) . 'AdditionalElementView';
288 return GeneralUtility::makeInstance($className, $this->model);
289 }
290
291 /**
292 * Create additional object by key and render the content
293 *
294 * @param string $key Type of additional
295 * @return \DOMNode
296 */
297 public function getAdditional($key) {
298 $additional = $this->createAdditional($key);
299 return $additional->render();
300 }
301
302 /**
303 * @return string
304 */
305 public function getInputValue() {
306 if (method_exists($this->model, 'getData')) {
307 $inputValue = nl2br($this->model->getData(), TRUE);
308 } else {
309 $inputValue = $this->model->getAttributeValue('value');
310 }
311 return $inputValue;
312 }
313
314 /**
315 * Return the id for the element wraps,
316 * like <li id="csc-form-"> ... </li>
317 *
318 * @return string
319 */
320 public function getElementWrapId() {
321 $elementId = (int)$this->model->getElementId();
322 $wrapId = 'csc-form-' . $elementId;
323 return $wrapId;
324 }
325
326 /**
327 * Returns the type for the element wraps,
328 * like <li class="csc-form-element csc-form-element-abstract">...</li>
329 *
330 * @return string
331 */
332 public function getElementWrapType() {
333 $elementType = strtolower(FormUtility::getInstance()->getLastPartOfClassName($this->model));
334 $wrapType = 'csc-form-element csc-form-element-' . $elementType;
335 return $wrapType;
336 }
337
338 /**
339 * Returns all element wraps.
340 *
341 * @return string
342 */
343 public function getElementWraps() {
344 $wraps = array(
345 $this->getElementWrapId(),
346 $this->getElementWrapType()
347 );
348 return implode(' ', $wraps);
349 }
350
351 /**
352 * Read the noWrap value of an element
353 * if TRUE the element does not need a element wrap
354 * like <li>element</li>
355 *
356 * @return bool
357 */
358 public function noWrap() {
359 return $this->noWrap;
360 }
361
362 }