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