Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
text.php
1<?php
2namespace Bitrix\Im;
3
5
6Loc::loadMessages(__FILE__);
7
8class Text
9{
10 private static $replacements = Array();
11 private static $parsers = Array();
12
13 private static $emojiList = [
14 "file" => ':f09f938e:',
15 "image" => ':f09f96bc:',
16 "audio" => ':f09f9488:',
17 "video" => ':f09f93ba:',
18 "code" => ':f09f9384:',
19 "call" => ':f09f939e:',
20 "attach" => ':f09fa7a9:',
21 "quote" => ':f09f92ac:',
22 ];
23
24 public static function parse($text, $params = Array())
25 {
26 $linkParam = $params['LINK'] ?? null;
27 $smilesParam = $params['SMILES'] ?? null;
28 $linkLimitParam = $params['LINK_LIMIT'] ?? null;
29 $textLimitParam = $params['TEXT_LIMIT'] ?? null;
30 $cutStrikeParam = $params['CUT_STRIKE'] ?? null;
31
32 $parseId = md5($linkParam.$smilesParam.$linkLimitParam.$textLimitParam);
33 if (isset(self::$parsers[$parseId]))
34 {
35 $parser = self::$parsers[$parseId];
36 }
37 else
38 {
39 $parser = new \CTextParser();
40 $parser->serverName = Common::getPublicDomain();
41 $parser->maxStringLen = intval($textLimitParam);
42
43 $parser->anchorType = 'bbcode';
44 $parser->maxAnchorLength = intval($linkLimitParam)? $linkLimitParam: 55;
45
46 foreach ($parser->allow as $tag => $value)
47 {
48 $parser->allow[$tag] = 'N';
49 }
50 $parser->allow['EMOJI'] = 'Y';
51 $parser->allow['HTML'] = 'Y';
52 $parser->allow['ANCHOR'] = 'Y';
53 $parser->allow['TEXT_ANCHOR'] = 'Y';
54
55 self::$parsers[$parseId] = $parser;
56 }
57
58 $text = preg_replace_callback("/\[CODE\](.*?)\[\/CODE\]/si", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
59 $text = preg_replace_callback("/\[PUT(?:=(.+?))?\](.+?)?\[\/PUT\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
60 $text = preg_replace_callback("/\[SEND(?:=(.+?))?\](.+?)?\[\/SEND\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
61 $text = preg_replace_callback("/\[USER=([0-9]{1,})\]\[\/USER\]/i", Array('\Bitrix\Im\Text', 'modifyShortUserTag'), $text);
62
63 if ($cutStrikeParam === 'Y')
64 {
65 $text = preg_replace("/\[s\].*?\[\/s\]/i", "", $text);
66 }
67
68 $text = $parser->convertText($text);
69
70 $text = str_replace(['<br />', '#BR#', '[br]'], "\n", $text);
71 $text = str_replace(["&#169;", "&#153;", "&#174;"], ["(c)", "(tm)", "(r)"], $text);
72 $text = str_replace("&nbsp;", " ", $text);
73 $text = str_replace("&quot;", "\"", $text);
74 $text = str_replace("&#092;", "\\", $text);
75 $text = str_replace("&#036;", "\$", $text);
76 $text = str_replace("&#33;", "!", $text);
77 $text = str_replace("&#91;", "[", $text);
78 $text = str_replace("&#93;", "]", $text);
79 $text = str_replace("&#39;", "'", $text);
80 $text = str_replace("&lt;", "<", $text);
81 $text = str_replace("&gt;", ">", $text);
82 $text = str_replace("&#124;", '|', $text);
83 $text = str_replace("&amp;", "&", $text);
84
85 $text = self::recoverReplacements($text);
86
87 return $text;
88 }
89
90 public static function parseLegacyFormat($text, $params = Array())
91 {
92 if (!$text)
93 {
94 return '';
95 }
96
97 $safeParam = $params['SAFE'] ?? null;
98 $linkParam = $params['LINK'] ?? null;
99 $fontParam = $params['FONT'] ?? null;
100 $smilesParam = $params['SMILES'] ?? null;
101 $textAnchorParam = $params['TEXT_ANCHOR'] ?? null;
102 $linkLimitParam = $params['LINK_LIMIT'] ?? null;
103 $textLimitParam = $params['TEXT_LIMIT'] ?? null;
104 $linkTargetSelfParam = $params['LINK_TARGET_SELF'] ?? null;
105 $cutStrikeParam = $params['CUT_STRIKE'] ?? null;
106
107 if (!$safeParam || $safeParam === 'Y')
108 {
109 $text = htmlspecialcharsbx($text);
110 }
111
112 $allowTags = [
113 'HTML' => 'N',
114 'USER' => 'N',
115 'ANCHOR' => $linkParam === 'N' ? 'N' : 'Y',
116 'BIU' => 'Y',
117 'IMG' => 'N',
118 'QUOTE' => 'N',
119 'CODE' => 'N',
120 'FONT' => $fontParam === 'Y' ? 'Y' : 'N',
121 'LIST' => 'N',
122 'SPOILER' => 'N',
123 'SMILES' => $smilesParam === 'N' ? 'N' : 'Y',
124 'EMOJI' => 'Y',
125 'NL2BR' => 'Y',
126 'VIDEO' => 'N',
127 'TABLE' => 'N',
128 'CUT_ANCHOR' => 'N',
129 'SHORT_ANCHOR' => 'N',
130 'ALIGN' => 'N',
131 'TEXT_ANCHOR' => $textAnchorParam === 'N' ? 'N' : 'Y',
132 ];
133
134 $parseId = md5('legacy'.$linkParam.$smilesParam.$linkLimitParam.$textLimitParam.$linkTargetSelfParam);
135 if (isset(self::$parsers[$parseId]))
136 {
137 $parser = self::$parsers[$parseId];
138 }
139 else
140 {
141 $parser = new \CTextParser();
142 $parser->serverName = Common::getPublicDomain();
143 $parser->maxAnchorLength = intval($linkLimitParam)? $linkLimitParam: 55;
144 $parser->maxStringLen = intval($textLimitParam);
145 $parser->allow = $allowTags;
146 if ($linkTargetSelfParam === 'Y')
147 {
148 $parser->link_target = "_self";
149 }
150
151 self::$parsers[$parseId] = $parser;
152 }
153
154 $text = preg_replace_callback("/\[PUT(?:=(.+?))?\](.+?)?\[\/PUT\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
155 $text = preg_replace_callback("/\[SEND(?:=(.+?))?\](.+?)?\[\/SEND\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
156 $text = preg_replace_callback("/\[CODE\](.*?)\[\/CODE\]/si", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
157 $text = preg_replace_callback("/\[USER=([0-9]{1,})\]\[\/USER\]/i", Array('\Bitrix\Im\Text', 'modifyShortUserTag'), $text);
158
159 if ($cutStrikeParam === 'Y')
160 {
161 $text = preg_replace("/\[s\].*?\[\/s\]/i", "", $text);
162 }
163
164 $text = $parser->convertText($text);
165
166 $text = str_replace(array('#BR#', '[br]', '[BR]'), '<br/>', $text);
167
168 $text = self::recoverReplacements($text);
169
170 return $text;
171 }
172
173 public static function getReplaceMap($text)
174 {
175 $replaces = [];
176
177 $dates = self::getDateConverterParams($text);
178 foreach ($dates as $result)
179 {
180 $replaces[] = [
181 'TYPE' => 'DATE',
182 'TEXT' => $result->getText(),
183 'VALUE' => $result->getDate(),
184 'START' => $result->getTextPosition(),
185 'END' => $result->getTextPosition()+$result->getTextLength(),
186 ];
187 }
188
189 return self::resolveIntersect($replaces);
190 }
191
192 private static function resolveIntersect(array $segments): array
193 {
194 usort($segments, fn(array $segmentA, array $segmentB) => $segmentA['START'] <=> $segmentB['START']);
195
196 $result = [];
197 $maxEnd = -1;
198
199 foreach ($segments as $segment)
200 {
201 if ($segment['START'] > $maxEnd)
202 {
203 $result[] = $segment;
204 $maxEnd = $segment['END'];
205 }
206 }
207
208 return array_reverse($result);
209 }
210
215 public static function getDateConverterParams($text)
216 {
217 if ($text == '')
218 return Array();
219
220 $text = preg_replace_callback("/\[PUT(?:=(.+?))?\](.+?)?\[\/PUT\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
221 $text = preg_replace_callback("/\[SEND(?:=(.+?))?\](.+?)?\[\/SEND\]/i", Array('\Bitrix\Im\Text', 'setReplacement'), $text);
222 $text = preg_replace_callback('/\[URL\=([^\]]*)\]([^\]]*)\[\/URL\]/i', Array('\Bitrix\Im\Text', 'setReplacement'), $text);
223 $text = preg_replace_callback('/(https?):\/\/(([a-z0-9$_\.\+!\*\'\‍(\‍),;\?&=-]|%[0-9a-f]{2})+(:([a-z0-9$_\.\+!\*\'\‍(\‍),;\?&=-]|%[0-9a-f]{2})+)?@)?(?#)((([a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z][a-z0-9-]*[a-z0-9]|((\d|[1-9]\d|1\d{2}|2[0-4][0-9]|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4][0-9]|25[0-5]))(:\d+)?)(((\/+([a-z0-9$_\.\+!\*\'\‍(\‍),;:@&=-]|%[0-9a-f]{2})*)*(\?([a-z0-9$_\.\+!\*\'\‍(\‍),;:@&=-]|%[0-9a-f]{2})*)?)?)?(#([a-z0-9$_\.\+!\*\'\‍(\‍),;:@&=-]|%[0-9a-f]{2})*)?/im', Array('\Bitrix\Im\Text', 'setReplacement'), $text);
224 $text = preg_replace_callback('#\-{54}(.+?)\-{54}#s', Array('\Bitrix\Im\Text', 'setReplacement'), $text);
225
226 return \Bitrix\Main\Text\DateConverter::decode($text, 1000);
227 }
228
229 public static function isOnlyEmoji($text)
230 {
231 $total = 0;
232 $count = 0;
233
234 $pattern = '%(?:
235 \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
236 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
237 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
238 )%xs';
239 $text = preg_replace_callback($pattern, function () {return "";}, $text, 4-$total, $count);
240 $total += $count;
241
242 if ($total > 3)
243 {
244 return false;
245 }
246
247 if ($total <= 0)
248 {
249 return false;
250 }
251
252 $text = trim($text);
253
254 return !$text;
255 }
256
257 public static function setReplacement($match)
258 {
259 $code = '####REPLACEMENT_MARK_'.count(self::$replacements).'####';
260
261 self::$replacements[$code] = $match[0];
262
263 return $code;
264 }
265
266 public static function recoverReplacements($text)
267 {
268 if (empty(self::$replacements))
269 {
270 return $text;
271 }
272
273 foreach(self::$replacements as $code => $value)
274 {
275 $text = str_replace($code, $value, $text);
276 }
277
278 if (mb_strpos($text, '####REPLACEMENT_MARK_') !== false)
279 {
280 $text = self::recoverReplacements($text);
281 }
282
283 self::$replacements = Array();
284
285 return $text;
286 }
287
288 public static function modifyShortUserTag($matches)
289 {
290 $userId = $matches[1];
291 $userName = \Bitrix\Im\User::getInstance($userId)->getFullName(false);
292 return '[USER='.$userId.' REPLACE]'.$userName.'[/USER]';
293 }
294
295 public static function removeBbCodes($text, $withFile = false, $attachValue = false)
296 {
297 if ($attachValue)
298 {
299 if ($attachValue === true || preg_match('/^(\d+)$/', $attachValue))
300 {
301 $text .= " [".Loc::getMessage('IM_MESSAGE_ATTACH')."]";
302 }
303 else
304 {
305 $text .= ' '. $attachValue;
306 }
307 }
308
309 $text = preg_replace("/\[s\](.*?)\[\/s\]/i", "", $text);
310 $text = preg_replace("/\[[buis]\](.*?)\[\/[buis]\]/i", "$1", $text);
311 $text = preg_replace("/\[url\](.*?)\[\/url\]/i".BX_UTF_PCRE_MODIFIER, "$1", $text);
312 $text = preg_replace("/\[url\\s*=\\s*((?:[^\\[\\]]++|\\[ (?: (?>[^\\[\\]]+) | (?:\\1) )* \\])+)\\s*\\](.*?)\\[\\/url\\]/ixs".BX_UTF_PCRE_MODIFIER, "$2", $text);
313 $text = preg_replace("/\[RATING=([1-5]{1})\]/i", " [".Loc::getMessage('IM_MESSAGE_RATING')."] ", $text);
314 $text = preg_replace("/\[ATTACH=([0-9]{1,})\]/i", " [".Loc::getMessage('IM_MESSAGE_ATTACH')."] ", $text);
315 $text = preg_replace_callback("/\[USER=([0-9]{1,})\]\[\/USER\]/i", Array('\Bitrix\Im\Text', 'modifyShortUserTag'), $text);
316 $text = preg_replace("/\[USER=([0-9]+)( REPLACE)?](.*?)\[\/USER]/i", "$3", $text);
317 $text = preg_replace("/\[dialog=(chat\d+|\d+:\d)(?: message=(\d+))?](.*?)\[\/dialog]/i", "$3", $text);
318 $text = preg_replace("/\[context=(chat\d+|\d+:\d+)\/(\d+)](.*?)\[\/context]/i", "$3", $text);
319 $text = preg_replace("/\[CHAT=([0-9]{1,})\](.*?)\[\/CHAT\]/i", "$2", $text);
320 $text = preg_replace("/\[SEND(?:=(.+?))?\](.+?)?\[\/SEND\]/i", "$2", $text);
321 $text = preg_replace("/\[PUT(?:=(.+?))?\](.+?)?\[\/PUT\]/i", "$2", $text);
322 $text = preg_replace("/\[CALL(?:=(.+?))?\](.+?)?\[\/CALL\]/i", "$2", $text);
323 $text = preg_replace("/\[PCH=([0-9]{1,})\](.*?)\[\/PCH\]/i", "$2", $text);
324 $text = preg_replace("/\[size=(\d+)](.*?)\[\/size]/i", "$2", $text);
325 $text = preg_replace("/\[color=#([0-9a-f]{3}|[0-9a-f]{6})](.*?)\[\/color]/i", "$2", $text);
326 $text = preg_replace_callback("/\[ICON\=([^\]]*)\]/i", Array('\Bitrix\Im\Text', 'modifyIcon'), $text);
327 $text = preg_replace('#\-{54}.+?\-{54}#s', " [".Loc::getMessage('IM_QUOTE')."] ", str_replace(array("#BR#"), Array(" "), $text));
328 $text = trim($text);
329
330 if ($withFile)
331 {
332 $text .= " [".Loc::getMessage('IM_MESSAGE_FILE')."]";
333 }
334
335 $text = trim($text);
336
337 if ($text == '')
338 {
339 $text = Loc::getMessage('IM_MESSAGE_DELETE');
340 }
341
342 return $text;
343 }
344
345 public static function populateUserBbCode(string $text): string
346 {
347 return preg_replace_callback("/\[USER=([0-9]{1,})\]\[\/USER\]/i", static function($matches){
348 $userId = $matches[1];
349 $userName = \Bitrix\Im\User::getInstance($userId)->getFullName(false);
350 return '[USER='.$userId.' REPLACE]'.$userName.'[/USER]';
351 }, $text);
352 }
353
354 public static function encodeEmoji($text)
355 {
356 return \Bitrix\Main\Text\Emoji::encode($text);
357 }
358
359 public static function decodeEmoji($text)
360 {
361 return \Bitrix\Main\Text\Emoji::decode($text);
362 }
363
364 public static function getEmoji($code, $fallbackText = '')
365 {
366 if (!\Bitrix\Main\Application::isUtfMode())
367 {
368 return $fallbackText;
369 }
370
371 if (!isset(self::$emojiList[$code]))
372 {
373 return $fallbackText;
374 }
375
376 return self::decodeEmoji(self::$emojiList[$code]);
377 }
378
379 public static function getEmojiList(): ?array
380 {
381 if (!\Bitrix\Main\Application::isUtfMode())
382 {
383 return null;
384 }
385
386 return array_map(fn ($element) => self::decodeEmoji($element), self::$emojiList);
387 }
388
389 public static function convertHtmlToBbCode($html)
390 {
391 if (!is_string($html))
392 {
393 return $html;
394 }
395
396 $html = str_replace('&nbsp;', ' ', $html);
397 $html = str_replace('<hr>', '------[BR]', $html);
398 $html = str_replace('#BR#', '[BR]', $html);
399
400 $replaced = 0;
401 do
402 {
403 $html = preg_replace(
404 "/<([busi])[^>a-z]*>(.+?)<\\/(\\1)[^>a-z]*>/is".BX_UTF_PCRE_MODIFIER,
405 "[\\1]\\2[/\\1]",
406 $html, -1, $replaced
407 );
408 }
409 while($replaced > 0);
410
411 $html = preg_replace("/\\<br\s*\\/*\\>/is".BX_UTF_PCRE_MODIFIER,"[br]", $html);
412 $html = preg_replace(
413 [
414 "#<a[^>]+href\\s*=\\s*('|\")(.+?)(?:\\1)[^>]*>(.*?)</a[^>]*>#is".BX_UTF_PCRE_MODIFIER,
415 "#<a[^>]+href(\\s*=\\s*)([^'\">]+)>(.*?)</a[^>]*>#is".BX_UTF_PCRE_MODIFIER
416 ],
417 "[url=\\2]\\3[/url]", $html
418 );
419 $html = preg_replace(
420 ["/<font[^>]+color\s*=[\s'\"]*#([0-9a-f]{3}|[0-9a-f]{6})[\s'\"]*>(.+?)<\/font[^>]*>/i".BX_UTF_PCRE_MODIFIER],
421 ["[color=#\\1]\\2[/color]"],
422 $html
423 );
424 $html = preg_replace(
425 ["/<span[^>]+color\s*=[\s'\"]*#([0-9a-f]{3}|[0-9a-f]{6})[\s'\"]*>(.+?)<\/span[^>]*>/i".BX_UTF_PCRE_MODIFIER],
426 ["[color=#\\1]\\2[/color]"],
427 $html
428 );
429 $html = preg_replace(
430 ["/<font[^>]+size\s*=[\s'\"]*(\d+)[\s'\"]*>(.+?)<\/font[^>]*>/i".BX_UTF_PCRE_MODIFIER],
431 ["[size=\\1]\\2[/size]"],
432 $html
433 );
434
435 $replaced = 0;
436 do
437 {
438 $html = preg_replace(
439 "/<div(?:.*?)>(.*?)<\/div>/i".BX_UTF_PCRE_MODIFIER,
440 "\\1",
441 $html, -1, $replaced
442 );
443 }
444 while($replaced > 0);
445
446 $replaced = 0;
447 do
448 {
449 $html = preg_replace(
450 "/<span(?:.*?)>(.*?)<\/span>/i".BX_UTF_PCRE_MODIFIER,
451 "\\1",
452 $html, -1, $replaced
453 );
454 }
455 while($replaced > 0);
456
457 $html = preg_replace(
458 "/<font(?:.*?)>(.*?)<\/font>/i".BX_UTF_PCRE_MODIFIER,
459 "\\1",
460 $html
461 );
462
463 return $html;
464 }
465
466 public static function modifyIcon($params)
467 {
468 $text = $params[1];
469
470 $title = Loc::getMessage('IM_MESSAGE_ICON');
471
472 preg_match('/title\=(.*[^\s\]])/i', $text, $match);
473 if ($match)
474 {
475 $title = $match[1];
476 if (mb_strpos($title, 'width=') !== false)
477 {
478 $title = mb_substr($title, 0, mb_strpos($title, 'width='));
479 }
480 if (mb_strpos($title, 'height=') !== false)
481 {
482 $title = mb_substr($title, 0, mb_strpos($title, 'height='));
483 }
484 if (mb_strpos($title, 'size=') !== false)
485 {
486 $title = mb_substr($title, 0, mb_strpos($title, 'size='));
487 }
488 $title = trim($title);
489 }
490
491 return '('.$title.')';
492 }
493
494 public static function modifySendPut($params)
495 {
496 $code = mb_strpos(mb_strtoupper($params[0]), '[SEND') === 0? 'SEND': 'PUT';
497 return preg_replace("/\[$code(?:=(.+))?\](.+?)?\[\/$code\]/i", "$2", $params[0]);
498 }
499}
static modifyIcon($params)
Definition text.php:466
static parse($text, $params=Array())
Definition text.php:24
static recoverReplacements($text)
Definition text.php:266
static modifySendPut($params)
Definition text.php:494
static populateUserBbCode(string $text)
Definition text.php:345
static encodeEmoji($text)
Definition text.php:354
static setReplacement($match)
Definition text.php:257
static getDateConverterParams($text)
Definition text.php:215
static convertHtmlToBbCode($html)
Definition text.php:389
static getEmojiList()
Definition text.php:379
static parseLegacyFormat($text, $params=Array())
Definition text.php:90
static getEmoji($code, $fallbackText='')
Definition text.php:364
static modifyShortUserTag($matches)
Definition text.php:288
static isOnlyEmoji($text)
Definition text.php:229
static removeBbCodes($text, $withFile=false, $attachValue=false)
Definition text.php:295
static getReplaceMap($text)
Definition text.php:173
static decodeEmoji($text)
Definition text.php:359
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29