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