1: <?php
2: /**
3: * Turn text into HTML with varying levels of parsing. For no html
4: * whatsoever, use htmlspecialchars() instead.
5: *
6: * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
7: *
8: * See the enclosed file COPYING for license information (LGPL). If you
9: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
10: *
11: * @author Chuck Hagenbuch <chuck@horde.org>
12: * @author Jan Schneider <jan@horde.org>
13: * @author Michael Slusarz <slusarz@horde.org>
14: * @category Horde
15: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
16: * @package Text_Filter
17: */
18: class Horde_Text_Filter_Text2html extends Horde_Text_Filter_Base
19: {
20: const PASSTHRU = 0;
21: const SYNTAX = 1;
22: const MICRO = 2;
23: const MICRO_LINKURL = 3;
24: const NOHTML = 4;
25: const NOHTML_NOBREAK = 5;
26:
27: /**
28: * Filter parameters.
29: *
30: * @var array
31: */
32: protected $_params = array(
33: 'charset' => 'ISO-8859-1',
34: 'class' => 'fixed',
35: 'emails' => false,
36: 'flowed' => '<blockquote>',
37: 'linkurls' => false,
38: 'text2html' => false,
39: 'parselevel' => 0,
40: 'space2html' => false
41: );
42:
43: /**
44: * Constructor.
45: *
46: * @param array $params Parameters specific to this driver:
47: * <ul>
48: * <li>charset: (string) The charset to use for htmlspecialchars()
49: * calls.</li>
50: * <li>class: (string) See Horde_Text_Filter_Linkurls::.</li>
51: * <li>emails: (array) TODO</li>
52: * <li>flowed: (string) For flowed text, the HTML blockquote tag to
53: * insert before each level.
54: * <li>linkurls: (array) TODO</li>
55: * <li>parselevel: (integer) The parselevel of the output.
56: * <ul>
57: * <li>PASSTHRU: No action. Pass-through. Included for
58: * completeness.</li>
59: * <li>SYNTAX: Allow full html, also do line-breaks, in-lining,
60: * syntax-parsing.</li>
61: * <li>MICRO: Micro html (only line-breaks, in-line linking).</li>
62: * <li>MICRO_LINKURL: Micro html (only line-breaks, in-line linking of
63: * URLS; no email addresses are linked).</li>
64: * <li>NOHTML: No html (all stripped, only line-breaks).</li>
65: * <li>NOHTML_NOBREAK: No html whatsoever, no line breaks added.
66: * Included for completeness.</li>
67: * </ul>
68: * </li>
69: * <li>space2html: (array) TODO</li>
70: * </ul>
71: */
72: public function __construct($params = array())
73: {
74: parent::__construct($params);
75:
76: // Use ISO-8859-1 instead of US-ASCII
77: if (Horde_String::lower($this->_params['charset']) == 'us-ascii') {
78: $this->_params['charset'] = 'iso-8859-1';
79: }
80: }
81:
82: /**
83: * Executes any code necessary before applying the filter patterns.
84: *
85: * @param mixed $text The text before the filtering. Either a string or
86: * a Horde_Text_Flowed object (since 1.1.0).
87: *
88: * @return string The modified text.
89: */
90: public function preProcess($text)
91: {
92: if ($text instanceof Horde_Text_Flowed) {
93: $text->setMaxLength(0);
94: $lines = $text->toFixedArray();
95: $level = 0;
96: $out = $txt = '';
97:
98: foreach ($lines as $key => $val) {
99: $line = ltrim($val['text'], '>');
100:
101: if (!isset($lines[$key + 1])) {
102: $out .= $this->preProcess(ltrim($txt) . $line);
103: while (--$level > 0) {
104: $out .= '</blockquote>';
105: }
106: } elseif ($val['level'] > $level) {
107: $out .= $this->preProcess(ltrim($txt));
108: do {
109: $out .= $this->_params['flowed'];
110: } while (++$level != $val['level']);
111: $txt = $line;
112: } elseif ($val['level'] < $level) {
113: $out .= $this->preProcess(ltrim($txt));
114: do {
115: $out .= '</blockquote>';
116: } while (--$level != $val['level']);
117: $txt = $line;
118: } else {
119: $txt .= "\n" . $line;
120: }
121: }
122:
123: return $out;
124: }
125:
126: if (!strlen($text)) {
127: return '';
128: }
129:
130: /* Abort out on simple cases. */
131: if ($this->_params['parselevel'] == self::PASSTHRU) {
132: return $text;
133: }
134:
135: if ($this->_params['parselevel'] == self::NOHTML_NOBREAK) {
136: return @htmlspecialchars($text, ENT_COMPAT, $this->_params['charset']);
137: }
138:
139: if ($this->_params['parselevel'] < self::NOHTML) {
140: $filters = array();
141: if ($this->_params['linkurls']) {
142: reset($this->_params['linkurls']);
143: $this->_params['linkurls'][key($this->_params['linkurls'])]['encode'] = true;
144: $filters = $this->_params['linkurls'];
145: } else {
146: $filters['linkurls'] = array(
147: 'encode' => true
148: );
149: }
150:
151: if ($this->_params['parselevel'] < self::MICRO_LINKURL) {
152: if ($this->_params['emails']) {
153: reset($this->_params['emails']);
154: $this->_params['emails'][key($this->_params['emails'])]['encode'] = true;
155: $filters += $this->_params['emails'];
156: } else {
157: $filters['emails'] = array(
158: 'encode' => true
159: );
160: }
161: }
162:
163: $text = Horde_Text_Filter::filter($text, array_keys($filters), array_values($filters));
164: }
165:
166: /* For level MICRO or NOHTML, start with htmlspecialchars(). */
167: $text2 = @htmlspecialchars($text, ENT_COMPAT, $this->_params['charset']);
168:
169: /* Bad charset input in may result in an empty string. If so, try
170: * using the default charset encoding instead. */
171: if (!$text2) {
172: $text2 = @htmlspecialchars($text, ENT_COMPAT);
173: }
174: $text = $text2;
175:
176: /* Do in-lining of http://xxx.xxx to link, xxx@xxx.xxx to email. */
177: if ($this->_params['parselevel'] < self::NOHTML) {
178: $text = Horde_Text_Filter_Linkurls::decode($text);
179: if ($this->_params['parselevel'] < self::MICRO_LINKURL) {
180: $text = Horde_Text_Filter_Emails::decode($text);
181: }
182:
183: if ($this->_params['space2html']) {
184: $params = reset($this->_params['space2html']);
185: $driver = key($this->_params['space2html']);
186: } else {
187: $driver = 'space2html';
188: $params = array();
189: }
190:
191: $text = Horde_Text_Filter::filter($text, $driver, $params);
192: }
193:
194: /* Do the newline ---> <br /> substitution. Everybody gets this; if
195: * you don't want even that, just use htmlspecialchars(). */
196: return nl2br($text);
197: }
198:
199: }
200: