1<?php 2/** 3 * Copyright 2007 Maintainable Software, LLC 4 * Copyright 2006-2016 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 * View helpers for text 17 * 18 * @author Mike Naberezny <mike@maintainable.com> 19 * @author Derek DeVries <derek@maintainable.com> 20 * @author Chuck Hagenbuch <chuck@horde.org> 21 * @license http://www.horde.org/licenses/bsd 22 * @category Horde 23 * @package View 24 * @subpackage Helper 25 */ 26class Horde_View_Helper_Text extends Horde_View_Helper_Base 27{ 28 /** 29 * @var array 30 */ 31 protected $_cycles = array(); 32 33 /** 34 * @var Horde_Support_Inflector 35 */ 36 protected $_inflector; 37 38 /** 39 * Escapes a value for output in a view template. 40 * 41 * <code> 42 * <p><?php echo $this->h($this->templateVar) ?></p> 43 * </code> 44 * 45 * @param mixed $var The output to escape. 46 * 47 * @return mixed The escaped value. 48 */ 49 public function h($var) 50 { 51 return htmlspecialchars($var, ENT_QUOTES, $this->_view->getEncoding()); 52 } 53 54 /** 55 * Pluralizes the $singular word unless $count is one. If $plural 56 * form is not supplied, inflector will be used. 57 * 58 * @param integer $count Count determines singular or plural. 59 * @param string $singular Singular form. 60 * @param string $plural Plural form (optional). 61 */ 62 public function pluralize($count, $singular, $plural = null) 63 { 64 if ($count == '1') { 65 $word = $singular; 66 } elseif ($plural) { 67 $word = $plural; 68 } else { 69 if (!$this->_inflector) { 70 $this->_inflector = new Horde_Support_Inflector(); 71 } 72 $word = $this->_inflector->pluralize($singular); 73 } 74 75 return "$count $word"; 76 } 77 78 /** 79 * Creates a Cycle object whose __toString() method cycles through elements 80 * of an array every time it is called. 81 * 82 * This can be used for example, to alternate classes for table rows: 83 * 84 * <code> 85 * <?php foreach($items as $item): ?> 86 * <tr class="<?php echo $this->cycle("even", "odd") ?>"> 87 * <td>item</td> 88 * </tr> 89 * <?php endforeach ?> 90 * </code> 91 * 92 * You can use named cycles to allow nesting in loops. Passing an array as 93 * the last parameter with a <tt>name</tt> key will create a named cycle. 94 * You can manually reset a cycle by calling resetCycle() and passing the 95 * name of the cycle: 96 * 97 * <code> 98 * <?php foreach($items as $item): ?> 99 * <tr class="<?php echo $this->cycle('even', 'odd', array('name' => 'row_class')) ?>"> 100 * <td> 101 * <?php foreach ($item->values as $value): ?> 102 * <span style="color:<?php echo $this->cycle('red', 'green', 'blue', array('name' => 'colors')) ?>"> 103 * <?php echo $value ?> 104 * </span> 105 * <?php endforeach ?> 106 * <?php $this->resetCycle('colors') ?> 107 * </td> 108 * </tr> 109 * <?php endforeach ?> 110 * </code> 111 */ 112 public function cycle($firstValue) 113 { 114 $values = func_get_args(); 115 116 $last = end($values); 117 if (is_array($last)) { 118 $options = array_pop($values); 119 $name = isset($options['name']) ? $options['name'] : 'default'; 120 } else { 121 $name = 'default'; 122 } 123 124 if (empty($this->_cycles[$name]) || 125 $this->_cycles[$name]->getValues() != $values) { 126 $this->_cycles[$name] = new Horde_View_Helper_Text_Cycle($values); 127 } 128 129 return $this->_cycles[$name]; 130 } 131 132 /** 133 * Resets a cycle so that it starts from the first element the next time 134 * it is called. 135 * 136 * Pass in $name to reset a named cycle. 137 * 138 * @param string $name Name of cycle to reset. 139 */ 140 public function resetCycle($name = 'default') 141 { 142 if (isset($this->_cycles[$name])) { 143 $this->_cycles[$name]->reset(); 144 } 145 } 146 147 /** 148 * Highlights a phrase where it is found in the text by surrounding it 149 * like <strong class="highlight">I'm highlighted</strong>. 150 * 151 * The Highlighter can be customized by passing $highlighter as a string 152 * containing $1 as a placeholder where the phrase is supposed to be 153 * inserted. 154 * 155 * @param string $text A text containing phrases to highlight. 156 * @param string $phrase A phrase to highlight in $text. 157 * @param string $highlighter A highlighting replacement. 158 * 159 * @return string The highlighted text. 160 */ 161 public function highlight($text, $phrase, $highlighter = null) 162 { 163 if (empty($highlighter)) { 164 $highlighter = '<strong class="highlight">$1</strong>'; 165 } 166 if (empty($phrase) || empty($text)) { 167 return $text; 168 } 169 return preg_replace('/(' . preg_quote($phrase, '/') . ')/', 170 $highlighter, 171 $text); 172 } 173 174 /** 175 * If $text is longer than $length, $text will be truncated to the length 176 * of $length and the last three characters will be replaced with the 177 * $truncateString. 178 * 179 * <code> 180 * $this->truncate('Once upon a time in a world far far away', 14); 181 * // => Once upon a... 182 * </code> 183 * 184 * @param string $text A text to truncate. 185 * @param integer $length The maximum length of the text 186 * @param string $truncateString Replacement string for the truncated 187 * text. 188 * 189 * @return string The truncated text. 190 */ 191 public function truncate($text, $length = 30, $truncateString = '...') 192 { 193 if (empty($text)) { 194 return $text; 195 } 196 return Horde_String::length($text) > $length 197 ? rtrim(Horde_String::substr($text, 0, $length - Horde_String::length($truncateString))) . $truncateString 198 : $text; 199 } 200 201 /** 202 * Limits a string to a given maximum length in a smarter way than just 203 * using substr(). 204 * 205 * Namely, cut from the MIDDLE instead of from the end so that if we're 206 * doing this on (for instance) a bunch of binder names that start off with 207 * the same verbose description, and then are different only at the very 208 * end, they'll still be different from one another after truncating. 209 * 210 * <code> 211 * $str = 'The quick brown fox jumps over the lazy dog tomorrow morning.'; 212 * $shortStr = $this->truncateMiddle($str, 40); 213 * // $shortStr == 'The quick brown fox... tomorrow morning.' 214 * </code> 215 * 216 * @param string $str A text to truncate. 217 * @param integer $maxLength The maximum length of the text 218 * @param string $joiner Replacement string for the truncated text. 219 * 220 * @return string The truncated text. 221 */ 222 public function truncateMiddle($str, $maxLength = 80, $joiner = '...') 223 { 224 if (Horde_String::length($str) <= $maxLength) { 225 return $str; 226 } 227 $maxLength = $maxLength - Horde_String::length($joiner); 228 if ($maxLength <= 0) { 229 return $str; 230 } 231 $startPieceLength = (int) ceil($maxLength / 2); 232 $endPieceLength = (int) floor($maxLength / 2); 233 $trimmedString = rtrim(Horde_String::substr($str, 0, $startPieceLength)) . $joiner; 234 if ($endPieceLength > 0) { 235 $trimmedString .= ltrim(Horde_String::substr($str, (-1 * $endPieceLength))); 236 } 237 return $trimmedString; 238 } 239 240 /** 241 * Inserts HTML code to allow linebreaks in a string after slashes or 242 * underscores. 243 * 244 * @param string $str A string to mark up with linebreak markers. 245 * 246 * @return string The marked-up string. 247 */ 248 public function makeBreakable($str) 249 { 250 return str_replace( 251 array('/', '_'), 252 array('/<wbr>', '_<wbr>'), 253 $str 254 ); 255 } 256 257 /** 258 * Removes smart quotes. 259 * 260 * @see http://shiflett.org/blog/2005/oct/convert-smart-quotes-with-php 261 * 262 * @param string $str A string with potential smart quotes. 263 * 264 * @return string The cleaned-up string. 265 */ 266 public function cleanSmartQuotes($str) 267 { 268 $search = array( 269 '/\x96/', 270 '/\xE2\x80\x93/', 271 '/\x97/', 272 '/\xE2\x80\x94/', 273 '/\x91/', 274 '/\xE2\x80\x98/', 275 '/\x92/', 276 '/\xE2\x80\x99/', 277 '/\x93/', 278 '/\xE2\x80\x9C/', 279 '/\x94/', 280 '/\xE2\x80\x9D/', 281 '/\x85/', 282 '/\xE2\x80\xA6/', 283 '/\x95/', 284 '/\xE2\x80\xA2/', 285 '/\x09/', 286 287 // The order of these is very important. 288 '/\xC2\xBC/', 289 '/\xBC/', 290 '/\xC2\xBD/', 291 '/\xBD/', 292 '/\xC2\xBE/', 293 '/\xBE/', 294 ); 295 296 $replace = array( 297 '-', 298 '-', 299 '--', 300 '--', 301 "'", 302 "'", 303 "'", 304 "'", 305 '"', 306 '"', 307 '"', 308 '"', 309 '...', 310 '...', 311 '*', 312 '*', 313 ' ', 314 315 '1/4', 316 '1/4', 317 '1/2', 318 '1/2', 319 '3/4', 320 '3/4', 321 ); 322 323 return preg_replace($search, $replace, $str); 324 } 325} 326