Overview

Packages

  • Text
    • Filter

Classes

  • Horde_Text_Filter
  • Horde_Text_Filter_Base
  • Horde_Text_Filter_Bbcode
  • Horde_Text_Filter_Cleanascii
  • Horde_Text_Filter_Cleanhtml
  • Horde_Text_Filter_Dimsignature
  • Horde_Text_Filter_Emails
  • Horde_Text_Filter_Emoticons
  • Horde_Text_Filter_Environment
  • Horde_Text_Filter_Exception
  • Horde_Text_Filter_Highlightquotes
  • Horde_Text_Filter_Html2text
  • Horde_Text_Filter_JavascriptMinify
  • Horde_Text_Filter_JavascriptMinify_JsMin
  • Horde_Text_Filter_Linkurls
  • Horde_Text_Filter_Simplemarkup
  • Horde_Text_Filter_Space2html
  • Horde_Text_Filter_Tabs2spaces
  • Horde_Text_Filter_Text2html
  • Horde_Text_Filter_Translation
  • Horde_Text_Filter_Words
  • Horde_Text_Filter_Xss
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Highlights quoted messages with different colors for the different quoting
  4:  * levels.
  5:  *
  6:  * CSS class names called "quoted1" ... "quoted{$cssLevels}" must be present.
  7:  *
  8:  * The text to be passed in must have already been passed through
  9:  * htmlspecialchars().
 10:  *
 11:  * Parameters:
 12:  * <pre>
 13:  * 'citeblock'  -- Display cite blocks?
 14:  *                 DEFAULT: true
 15:  * 'cssLevels'  -- Number of defined CSS class names.
 16:  *                 DEFAULT: 5
 17:  * 'hideBlocks' -- Hide large quoted text blocks by default?
 18:  *                 DEFAULT: false
 19:  * </pre>
 20:  *
 21:  * Copyright 2004-2012 Horde LLC (http://www.horde.org/)
 22:  *
 23:  * See the enclosed file COPYING for license information (LGPL). If you
 24:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 25:  *
 26:  * @author   Michael Slusarz <slusarz@horde.org>
 27:  * @author   Jan Schneider <jan@horde.org>
 28:  * @category Horde
 29:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 30:  * @package  Text_Filter
 31:  */
 32: class Horde_Text_Filter_Highlightquotes extends Horde_Text_Filter_Base
 33: {
 34:     /**
 35:      * Filter parameters.
 36:      *
 37:      * @var array
 38:      */
 39:     protected $_params = array(
 40:         'citeblock' => true,
 41:         'cssLevels' => 5,
 42:         'hideBlocks' => false
 43:     );
 44: 
 45:     /**
 46:      * The number of quoted lines to exceed to trigger large block
 47:      * processing.
 48:      *
 49:      * @var integer
 50:      */
 51:     protected $_qlimit = 8;
 52: 
 53:     /**
 54:      * Executes any code necessaray before applying the filter patterns.
 55:      *
 56:      * @param string $text  The text before the filtering.
 57:      *
 58:      * @return string  The modified text.
 59:      */
 60:     public function preProcess($text)
 61:     {
 62:         /* Tack a newline onto the beginning of the string so that we
 63:          * correctly highlight when the first character in the string is a
 64:          * quote character. */
 65:         return "\n$text";
 66:     }
 67: 
 68:     /**
 69:      * Returns a hash with replace patterns.
 70:      *
 71:      * @return array  Patterns hash.
 72:      */
 73:     public function getPatterns()
 74:     {
 75:         /* Remove extra spaces before quoted text as the CSS formatting will
 76:          * automatically add a bit of space for us. */
 77:         return ($this->_params['citeblock'])
 78:             ? array('regexp' => array("/<br \/>\s*\n\s*<br \/>\s*\n\s*((&gt;\s?)+)/m" => "<br />\n\\1"))
 79:             : array();
 80:     }
 81: 
 82:     /**
 83:      * Executes any code necessaray after applying the filter patterns.
 84:      *
 85:      * @param string $text  The text after the filtering.
 86:      *
 87:      * @return string  The modified text.
 88:      */
 89:     public function postProcess($text)
 90:     {
 91:         /* Use cite blocks to display the different quoting levels? */
 92:         $cb = $this->_params['citeblock'];
 93: 
 94:         /* Cite level before parsing the current line. */
 95:         $qlevel = 0;
 96: 
 97:         /* Other loop variables. */
 98:         $text_out = '';
 99:         $lines = array();
100:         $tmp = array('level' => 0, 'lines' => array());
101:         $qcount = 0;
102: 
103:         /* Parse text line by line. */
104:         foreach (explode("\n", $text) as $line) {
105:             /* Cite level of current line. */
106:             $clevel = 0;
107:             $matches = array();
108: 
109:             /* Do we have a citation line? */
110:             if (preg_match('/^\s*((&gt;\s?)+)/m', $line, $matches)) {
111:                 /* Count number of > characters => cite level */
112:                 $clevel = count(preg_split('/&gt;\s?/', $matches[1])) - 1;
113:             }
114: 
115:             if ($cb && isset($matches[1])) {
116:                 /* Strip all > characters. */
117:                 $line = substr($line, Horde_String::length($matches[1]));
118:             }
119: 
120:             /* Is this cite level lower than the current level? */
121:             if ($clevel < $qlevel) {
122:                 $lines[] = $tmp;
123:                 if ($clevel == 0) {
124:                     $text_out .= $this->_process($lines, $qcount);
125:                     $lines = array();
126:                     $qcount = 0;
127:                 }
128:                 $tmp = array('level' => $clevel, 'lines' => array());
129: 
130:             /* Is this cite level higher than the current level? */
131:             } elseif ($clevel > $qlevel) {
132:                 $lines[] = $tmp;
133:                 $tmp = array('level' => $clevel, 'lines' => array());
134:             }
135: 
136:             $tmp['lines'][] = $line;
137:             $qlevel = $clevel;
138: 
139:             if ($qlevel) {
140:                 ++$qcount;
141:             }
142:         }
143: 
144:         $lines[] = $tmp;
145:         $text_out .= $this->_process($lines, $qcount);
146: 
147:         /* Remove the leading newline we added above, if it's still there. */
148:         return ($text_out[0] == "\n")
149:             ? substr($text_out, 1)
150:             : $text_out;
151:     }
152: 
153:     /**
154:      * Process a batch of lines at the same quoted level.
155:      *
156:      * @param array $lines     Lines.
157:      * @param integer $qcount  Number of lines in quoted level.
158:      *
159:      * @return string  The rendered lines.
160:      */
161:     protected function _process($lines, $qcount)
162:     {
163:         $curr = reset($lines);
164:         $out = implode("\n", $this->_removeBr($curr['lines']));
165: 
166:         if ($qcount > $this->_qlimit) {
167:             $out .= $this->_beginLargeBlock($lines, $qcount);
168:         }
169: 
170:         $level = 0;
171: 
172:         next($lines);
173:         while (list(,$curr) = each($lines)) {
174:             if ($level > $curr['level']) {
175:                 for ($i = $level; $i > $curr['level']; --$i) {
176:                     $out .= $this->_params['citeblock'] ? '</div>' : '</font>';
177:                 }
178:             } else {
179:                 for ($i = $level; $i < $curr['level']; ++$i) {
180:                     /* Add quote block start tags for each cite level. */
181:                     $out .= ($this->_params['citeblock'] ? '<div class="citation ' : '<font class="') .
182:                         'quoted' . (($i % $this->_params['cssLevels']) + 1) . '"' .
183:                         ((($i == 0) && ($qcount > $this->_qlimit) && $this->_params['hideBlocks']) ? ' style="display:none"' : '') .
184:                         '>';
185:                 }
186:             }
187: 
188:             $out .= implode("\n", $this->_removeBr($curr['lines']));
189:             $level = $curr['level'];
190:         }
191: 
192:         for ($i = $level; $i > 0; --$i) {
193:             $out .= $this->_params['citeblock'] ? '</div>' : '</font>';
194:         }
195: 
196:         if ($qcount > $this->_qlimit) {
197:             $out .= $this->_endLargeBlock($lines, $qcount);
198:         }
199: 
200:         return $out;
201:     }
202: 
203:     /**
204:      * Add HTML code at the beginning of a large block of quoted lines.
205:      *
206:      * @param array $lines     Lines.
207:      * @param integer $qcount  Number of lines in quoted level.
208:      *
209:      * @return string  HTML code.
210:      */
211:     protected function _beginLargeBlock($lines, $qcount)
212:     {
213:         return '';
214:     }
215: 
216:     /**
217:      * Add HTML code at the end of a large block of quoted lines.
218:      *
219:      * @param array $lines     Lines.
220:      * @param integer $qcount  Number of lines in quoted level.
221:      *
222:      * @return string  HTML code.
223:      */
224:     protected function _endLargeBlock($lines, $qcount)
225:     {
226:         return '';
227:     }
228: 
229:     /**
230:      * Remove leading and trailing BR tags.
231:      *
232:      * @param array $lines  An array of text.
233:      *
234:      * @return array  The array with bare BR tags removed at the beginning and
235:      *                end.
236:      */
237:     protected function _removeBr($lines)
238:     {
239:         /* Remove leading/trailing line breaks. Spacing between quote blocks
240:          * will be handled by div CSS. */
241:         if (!$this->_params['citeblock']) {
242:             return $lines;
243:         }
244: 
245:         foreach (array_keys($lines) as $i) {
246:             if (!preg_match("/^\s*<br\s*\/>\s*$/i", $lines[$i])) {
247:                 break;
248:             }
249:             unset($lines[$i]);
250:         }
251: 
252:         foreach (array_reverse(array_keys($lines)) as $i) {
253:             if (!preg_match("/^\s*<br\s*\/>\s*$/i", $lines[$i])) {
254:                 break;
255:             }
256:             unset($lines[$i]);
257:         }
258: 
259:         return $lines;
260:     }
261: 
262: }
263: 
API documentation generated by ApiGen