Fix code formatting
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / bbcode / MessageParser.class.php
CommitLineData
dcc2332d
MW
1<?php
2namespace wcf\system\bbcode;
3use wcf\data\bbcode\attribute\BBCodeAttribute;
4use wcf\data\smiley\SmileyCache;
5use wcf\system\event\EventHandler;
6use 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 */
18class 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}