1: <?php
2: /**
3: * Copyright 2007-2008 Maintainable Software, LLC
4: * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
5: *
6: * @author Mike Naberezny <mike@maintainable.com>
7: * @author Derek DeVries <derek@maintainable.com>
8: * @author Chuck Hagenbuch <chuck@horde.org>
9: * @license http://www.horde.org/licenses/bsd
10: * @category Horde
11: * @package View
12: * @subpackage Helper
13: */
14:
15: /**
16: * Use these methods to generate HTML tags programmatically.
17: *
18: * By default, they output HTML 4.01 Strict compliant tags.
19: *
20: * @author Mike Naberezny <mike@maintainable.com>
21: * @author Derek DeVries <derek@maintainable.com>
22: * @author Chuck Hagenbuch <chuck@horde.org>
23: * @license http://www.horde.org/licenses/bsd
24: * @category Horde
25: * @package View
26: * @subpackage Helper
27: */
28: class Horde_View_Helper_Tag extends Horde_View_Helper_Base
29: {
30: /**
31: * Boolean HTML attributes.
32: *
33: * @var array
34: */
35: private $_booleanAttributes = array('checked', 'disabled', 'multiple', 'readonly', 'selected');
36:
37: /**
38: * Returns an empty HTML tag of type $name.
39: *
40: * Add HTML attributes by passing an attributes hash to $options. For
41: * attributes with no value (like disabled and readonly), give it a value
42: * of TRUE in the $options array.
43: *
44: * <code>
45: * $this->tag('br')
46: * // => <br>
47: * $this->tag('input', array('type' => 'text', 'disabled' => true))
48: * // => <input type="text" disabled="disabled">
49: * </code>
50: *
51: * @param string $name Tag name.
52: * @param string $options Tag attributes.
53: *
54: * @return string Generated HTML tag.
55: */
56: public function tag($name, $options = null)
57: {
58: return "<$name"
59: . ($options ? $this->tagOptions($options) : '')
60: . '>';
61: }
62:
63: /**
64: * Returns an HTML block tag of type $name surrounding the $content.
65: *
66: * Add HTML attributes by passing an attributes hash to $options. For
67: * attributes with no value (like disabled and readonly), give it a value
68: * of TRUE in the $options array.
69: *
70: * <code>
71: * $this->contentTag('p', 'Hello world!')
72: * // => <p>Hello world!</p>
73: * $this->contentTag('div',
74: * $this->contentTag('p', 'Hello world!'),
75: * array('class' => 'strong'))
76: * // => <div class="strong"><p>Hello world!</p></div>
77: * $this->contentTag('select', $options, array('multiple' => true))
78: * // => <select multiple="multiple">...options...</select>
79: * </code>
80: *
81: * @param string $name Tag name.
82: * @param string $content Content to place between the tags.
83: * @param array $options Tag attributes.
84: *
85: * @return string Generated HTML tags with content between.
86: */
87: public function contentTag($name, $content, $options = null)
88: {
89: $tagOptions = ($options ? $this->tagOptions($options) : '');
90: return "<$name$tagOptions>$content</$name>";
91: }
92:
93: /**
94: * Returns a CDATA section with the given $content.
95: *
96: * CDATA sections are used to escape blocks of text containing characters
97: * which would otherwise be recognized as markup. CDATA sections begin with
98: * the string "<![CDATA[" and end with (and may not contain) the string
99: * "]]>".
100: *
101: * <code>
102: * $this->cdataSection('<hello world>');
103: * // => <![CDATA[<hello world>]]>
104: *
105: * @param string $content Content for inside CDATA section.
106: *
107: * @return string CDATA section with content.
108: */
109: public function cdataSection($content)
110: {
111: return "<![CDATA[$content]]>";
112: }
113:
114: /**
115: * Escapes a value for output in a view template.
116: *
117: * <code>
118: * <p><?php echo $this->escape($this->templateVar) ?></p>
119: * </code>
120: *
121: * @param string $var The output to escape.
122: *
123: * @return string The escaped value.
124: */
125: public function escape($var)
126: {
127: return htmlspecialchars($var, ENT_QUOTES, $this->_view->getEncoding());
128: }
129:
130: /**
131: * Returns the escaped $html without affecting existing escaped entities.
132: *
133: * <code>
134: * $this->escapeOnce('1 > 2 & 3')
135: * // => '1 < 2 & 3'
136: *
137: * @param string $html HTML to be escaped.
138: *
139: * @return string Escaped HTML without affecting existing escaped entities.
140: */
141: public function escapeOnce($html)
142: {
143: return $this->_fixDoubleEscape($this->escape($html));
144: }
145:
146: /**
147: * Converts an associative array of $options into a string of HTML
148: * attributes.
149: *
150: * @param array $options Key/value pairs.
151: *
152: * @return string 'key1="value1" key2="value2"'
153: */
154: public function tagOptions($options)
155: {
156: foreach ($options as $k => $v) {
157: if ($v === null || $v === false) {
158: unset($options[$k]);
159: }
160: }
161:
162: if (!empty($options)) {
163: foreach ($options as $k => &$v) {
164: if (in_array($k, $this->_booleanAttributes)) {
165: $v = $k;
166: } else {
167: $v = $k . '="' . $this->escapeOnce($v) . '"';
168: }
169: }
170: sort($options);
171: return ' ' . implode(' ', $options);
172: } else {
173: return '';
174: }
175: }
176:
177: /**
178: * Fix double-escaped entities, such as &amp;, &#123;, etc.
179: *
180: * @param string $escaped Double-escaped entities.
181: *
182: * @return string Entities fixed.
183: */
184: private function _fixDoubleEscape($escaped)
185: {
186: return preg_replace('/&([a-z]+|(#\d+));/i', '&\\1;', $escaped);
187: }
188: }
189: