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