Commit | Line | Data |
---|---|---|
dcc2332d MW |
1 | <?php |
2 | namespace wcf\system\bbcode; | |
3 | use wcf\data\bbcode\attribute\BBCodeAttribute; | |
4 | use wcf\data\smiley\SmileyCache; | |
5 | use wcf\system\event\EventHandler; | |
6 | use wcf\util\StringUtil; | |
7 | ||
8 | /** | |
9 | * Parses bbcode tags, smilies etc. in messages. | |
10 | * | |
11 | * @author Marcel Werk | |
ca4ba303 | 12 | * @copyright 2001-2014 WoltLab GmbH |
dcc2332d | 13 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
f4f05aa5 | 14 | * @package com.woltlab.wcf |
dcc2332d MW |
15 | * @subpackage system.bbcode |
16 | * @category Community Framework | |
17 | */ | |
18 | class MessageParser extends BBCodeParser { | |
19 | /** | |
20 | * list of smilies | |
0ad90fc3 | 21 | * @var array<\wcf\data\smiley\Smiley> |
dcc2332d MW |
22 | */ |
23 | protected $smilies = array(); | |
24 | ||
25 | /** | |
26 | * cached bbcodes | |
27 | * @var array | |
28 | */ | |
29 | protected $cachedCodes = array(); | |
30 | ||
dcc2332d MW |
31 | /** |
32 | * currently parsed message | |
33 | * @var string | |
34 | */ | |
35 | public $message = ''; | |
36 | ||
37 | /** | |
0ad90fc3 | 38 | * @see \wcf\system\SingletonFactory::init() |
dcc2332d MW |
39 | */ |
40 | protected function init() { | |
41 | parent::init(); | |
42 | ||
dcc2332d MW |
43 | if (MODULE_SMILEY == 1) { |
44 | // get smilies | |
45 | $smilies = SmileyCache::getInstance()->getSmilies(); | |
46 | $categories = SmileyCache::getInstance()->getCategories(); | |
47 | foreach ($smilies as $categoryID => $categorySmilies) { | |
48 | if ($categories[$categoryID ?: null]->isDisabled) continue; | |
49 | ||
50 | foreach ($categorySmilies as $smiley) { | |
51 | foreach ($smiley->smileyCodes as $smileyCode) { | |
52 | $this->smilies[$smileyCode] = '<img src="'.$smiley->getURL().'" alt="'.StringUtil::encodeHTML($smiley->smileyCode).'" />'; | |
53 | } | |
54 | } | |
55 | } | |
56 | krsort($this->smilies); | |
57 | } | |
58 | } | |
59 | ||
60 | /** | |
61 | * Parses a message. | |
62 | * | |
63 | * @param string $message | |
64 | * @param boolean $enableSmilies | |
65 | * @param boolean $enableHtml | |
66 | * @param boolean $enableBBCodes | |
67 | * @param boolean $doKeywordHighlighting | |
68 | * @return string parsed message | |
69 | */ | |
70 | public function parse($message, $enableSmilies = true, $enableHtml = false, $enableBBCodes = true, $doKeywordHighlighting = true) { | |
71 | $this->cachedCodes = array(); | |
72 | $this->message = $message; | |
73 | ||
74 | // call event | |
75 | EventHandler::getInstance()->fireAction($this, 'beforeParsing'); | |
76 | ||
77 | if ($enableBBCodes) { | |
78 | // cache codes | |
79 | $this->message = $this->cacheCodes($this->message); | |
80 | } | |
81 | ||
82 | if (!$enableHtml) { | |
83 | // encode html | |
84 | $this->message = StringUtil::encodeHTML($this->message); | |
85 | ||
86 | // converts newlines to <br />'s | |
87 | if ($this->getOutputType() == 'text/html') { | |
88 | $this->message = nl2br($this->message); | |
89 | } | |
90 | } | |
e1d2822f MW |
91 | else { |
92 | if ($this->getOutputType() == 'text/simplified-html') { | |
93 | $this->message = StringUtil::stripHTML($this->message); | |
94 | } | |
95 | } | |
dcc2332d MW |
96 | |
97 | // parse bbcodes | |
98 | if ($enableBBCodes) { | |
99 | $this->message = parent::parse($this->message); | |
100 | } | |
101 | ||
102 | // parse smilies | |
103 | if ($enableSmilies) { | |
104 | $this->message = $this->parseSmilies($this->message, $enableHtml); | |
105 | } | |
106 | ||
107 | if ($enableBBCodes && !empty($this->cachedCodes)) { | |
108 | // insert cached codes | |
109 | $this->message = $this->insertCachedCodes($this->message); | |
110 | } | |
111 | ||
112 | // highlight search query | |
113 | if ($doKeywordHighlighting) { | |
114 | $this->message = KeywordHighlighter::getInstance()->doHighlight($this->message); | |
115 | } | |
116 | ||
117 | // replace bad html tags (script etc.) | |
118 | $badSearch = array('/(javascript):/i', '/(about):/i', '/(vbscript):/i'); | |
119 | $badReplace = array('$1<b></b>:', '$1<b></b>:', '$1<b></b>:'); | |
120 | $this->message = preg_replace($badSearch, $badReplace, $this->message); | |
121 | ||
122 | // call event | |
123 | EventHandler::getInstance()->fireAction($this, 'afterParsing'); | |
124 | ||
125 | return $this->message; | |
126 | } | |
127 | ||
128 | /** | |
129 | * Parses smiley codes. | |
130 | * | |
131 | * @param string $text | |
132 | * @return string text | |
133 | */ | |
134 | protected function parseSmilies($text, $enableHtml = false) { | |
135 | foreach ($this->smilies as $code => $html) { | |
136 | //$text = preg_replace('~(?<!&\w{2}|&\w{3}|&\w{4}|&\w{5}|&\w{6}|&#\d{2}|&#\d{3}|&#\d{4}|&#\d{5})'.preg_quote((!$enableHtml ? StringUtil::encodeHTML($code) : $code), '~').'(?![^<]*>)~', $html, $text); | |
137 | $text = preg_replace('~(?<=^|\s)'.preg_quote((!$enableHtml ? StringUtil::encodeHTML($code) : $code), '~').'(?=$|\s|</li>'.(!$enableHtml ? '|<br />' : '').')~', $html, $text); | |
138 | } | |
139 | ||
140 | return $text; | |
141 | } | |
142 | ||
143 | /** | |
144 | * Caches code bbcodes to avoid parsing of smileys and other bbcodes inside them. | |
145 | * | |
146 | * @param string $text | |
147 | * @return string | |
148 | */ | |
149 | protected function cacheCodes($text) { | |
150 | if (!empty($this->sourceCodeRegEx)) { | |
151 | $text = preg_replace_callback("~(\[(".$this->sourceCodeRegEx.") | |
152 | (?:= | |
153 | (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*) | |
154 | (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))* | |
155 | )?\]) | |
156 | (.*?) | |
157 | (?:\[/\\2\])~six", array($this, 'cacheCodesCallback'), $text); | |
158 | } | |
159 | return $text; | |
160 | } | |
161 | ||
162 | /** | |
163 | * Returns the hash for an matched code bbcode in the message. | |
164 | * | |
165 | * @param array $matches | |
166 | * @return string | |
167 | */ | |
168 | protected function cacheCodesCallback($matches) { | |
169 | // create hash | |
170 | $hash = '@@'.StringUtil::getHash(uniqid(microtime()).$matches[3]).'@@'; | |
171 | ||
172 | // build tag | |
173 | $tag = $this->buildTag($matches[1]); | |
174 | $tag['content'] = $matches[3]; | |
175 | ||
176 | // save tag | |
177 | $this->cachedCodes[$hash] = $tag; | |
178 | ||
179 | return $hash; | |
180 | } | |
181 | ||
182 | /** | |
183 | * Reinserts cached code bbcodes. | |
184 | * | |
185 | * @param string $text | |
186 | * @return string | |
187 | */ | |
188 | protected function insertCachedCodes($text) { | |
189 | foreach ($this->cachedCodes as $hash => $tag) { | |
190 | // build code and insert | |
191 | if ($this->bbcodes[$tag['name']]->className) { | |
192 | $replacement = $this->bbcodes[$tag['name']]->getProcessor()->getParsedTag($tag, $tag['content'], $tag, $this); | |
193 | } | |
194 | else { | |
195 | $replacement = $this->buildOpeningTag($tag) . StringUtil::encodeHTML($tag['content']) . $this->buildClosingTag($tag); | |
196 | } | |
197 | ||
198 | $text = str_replace($hash, $replacement, $text); | |
199 | } | |
200 | ||
201 | return $text; | |
202 | } | |
203 | ||
204 | /** | |
0ad90fc3 | 205 | * @see \wcf\system\bbcode\BBCodeParser::isValidTagAttribute() |
dcc2332d MW |
206 | */ |
207 | protected function isValidTagAttribute(array $tagAttributes, BBCodeAttribute $definedTagAttribute) { | |
208 | if (!parent::isValidTagAttribute($tagAttributes, $definedTagAttribute)) { | |
209 | return false; | |
210 | } | |
211 | ||
212 | // check for cached codes | |
213 | if (isset($tagAttributes[$definedTagAttribute->attributeNo]) && preg_match('/@@[a-f0-9]{40}@@/', $tagAttributes[$definedTagAttribute->attributeNo])) { | |
214 | return false; | |
215 | } | |
216 | ||
217 | return true; | |
218 | } | |
b86143fb MW |
219 | |
220 | /** | |
221 | * Returns a text-only version of given message. | |
1a6e8c52 | 222 | * |
b86143fb MW |
223 | * @param string $message |
224 | * @return string | |
225 | */ | |
226 | public function stripHTML($message) { | |
227 | // remove img tags (smilies) | |
228 | $message = preg_replace('~<img src="[^"]+" alt="([^"]+)" />~', '\\1', $message); | |
229 | ||
230 | // strip other HTML tags | |
231 | $message = StringUtil::stripHTML($message); | |
232 | ||
233 | // decode HTML entities | |
234 | $message = StringUtil::decodeHTML($message); | |
235 | ||
236 | return $message; | |
237 | } | |
dcc2332d | 238 | } |