1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
recent.php
См. документацию.
1<?php
2
3namespace Bitrix\Im;
4
5use Bitrix\Im\Model\MessageParamTable;
6use Bitrix\Im\Model\MessageUnreadTable;
7use Bitrix\Im\Model\RecentTable;
8use Bitrix\Im\V2\Chat\Background\Background;
9use Bitrix\Im\V2\Application\Features;
10use Bitrix\Im\V2\Chat\Copilot\CopilotPopupItem;
11use Bitrix\Im\V2\Chat\EntityLink;
12use Bitrix\Im\V2\Chat\Param\Params;
13use Bitrix\Im\V2\Chat\MessagesAutoDelete\MessagesAutoDeleteConfigs;
14use Bitrix\Im\V2\Chat\TextField\TextFieldEnabled;
15use Bitrix\Im\V2\Integration\Socialnetwork\Collab\Collab;
16use Bitrix\Im\V2\Message\Counter\CounterType;
17use Bitrix\Im\V2\MessageCollection;
18use Bitrix\Im\V2\Entity\User\NullUser;
19use Bitrix\Im\V2\Permission;
20use Bitrix\Im\V2\Integration\AI\RoleManager;
21use Bitrix\Im\V2\Integration\Socialnetwork\Group;
22use Bitrix\Im\V2\Message;
23use Bitrix\Im\V2\Message\CounterService;
24use Bitrix\Im\V2\Entity\File\FileCollection;
25use Bitrix\Im\V2\Entity\File\FileItem;
26use Bitrix\Im\V2\Message\Param;
27use Bitrix\Im\V2\Message\MessagePopupItem;
28use Bitrix\Im\V2\Message\ReadService;
29use Bitrix\Im\V2\Message\Send\PushService;
30use Bitrix\Im\V2\Recent\Config\RecentConfigManager;
31use Bitrix\Im\V2\RelationCollection;
32use Bitrix\Im\V2\Rest\RestAdapter;
33use Bitrix\Im\V2\Settings\UserConfiguration;
34use Bitrix\Im\V2\Sync;
35use Bitrix\Imbot\Bot\CopilotChatBot;
36use Bitrix\Main\Application, Bitrix\Main\Localization\Loc;
37use Bitrix\Main\Config\Option;
38use Bitrix\Main\DB\SqlExpression;
39use Bitrix\Main\Engine\Response\Converter;
40use Bitrix\Main\Loader;
41use Bitrix\Main\ORM\Fields\ExpressionField;
42use Bitrix\Main\Type\DateTime;
43use Bitrix\Pull\Event;
44
45Loc::loadMessages(__FILE__);
46
47class Recent
48{
49 private static array $unreadElementCache = [];
50 private const PINNED_CHATS_LIMIT = 45;
51
52 static private bool $limitError = false;
53
54 public static function get($userId = null, $options = [])
55 {
56 $onlyOpenlinesOption = $options['ONLY_OPENLINES'] ?? null;
57 $onlyCopilotOption = $options['ONLY_COPILOT'] ?? null;
58 $skipOpenlinesOption = $options['SKIP_OPENLINES'] ?? null;
59 $skipChat = $options['SKIP_CHAT'] ?? null;
60 $skipDialog = $options['SKIP_DIALOG'] ?? null;
61 $byChatIds = isset($options['CHAT_IDS']);
62
63 if (isset($options['FORCE_OPENLINES']) && $options['FORCE_OPENLINES'] === 'Y')
64 {
65 $forceOpenlines = 'Y';
66 }
67 else
68 {
69 $forceOpenlines = 'N';
70 }
71
73 if (!$userId)
74 {
75 return false;
76 }
77
78 $showOpenlines = (
80 && ($onlyOpenlinesOption === 'Y' || $skipOpenlinesOption !== 'Y')
81 );
82
83 if (
84 $showOpenlines
85 && $forceOpenlines !== 'Y'
86 && class_exists('\Bitrix\ImOpenLines\Recent')
87 )
88 {
89 return \Bitrix\ImOpenLines\Recent::getRecent($userId, $options);
90 }
91
92 $generalChatId = \CIMChat::GetGeneralChatId();
93
94 $ormParams = self::getOrmParams([
95 'USER_ID' => $userId,
96 'SHOW_OPENLINES' => $showOpenlines,
97 'WITHOUT_COMMON_USERS' => true,
98 'CHAT_IDS' => $options['CHAT_IDS'] ?? null,
99 ]);
100
101 $lastSyncDateOption = $options['LAST_SYNC_DATE'] ?? null;
102 if ($lastSyncDateOption)
103 {
104 $maxLimit = (new \Bitrix\Main\Type\DateTime())->add('-7 days');
105 if ($maxLimit > $options['LAST_SYNC_DATE'])
106 {
107 $options['LAST_SYNC_DATE'] = $maxLimit;
108 }
109 $ormParams['filter']['>=DATE_UPDATE'] = $options['LAST_SYNC_DATE'];
110 }
111 else if ($options['ONLY_OPENLINES'] !== 'Y' && !$byChatIds)
112 {
113 $ormParams['filter']['>=DATE_UPDATE'] = (new \Bitrix\Main\Type\DateTime())->add('-30 days');
114 }
115
116 $skipTypes = [];
117 if ($onlyCopilotOption === 'Y')
118 {
119 $ormParams['filter'][] = [
120 '=ITEM_TYPE' => \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT
121 ];
122 }
123 elseif ($onlyOpenlinesOption === 'Y')
124 {
125 $ormParams['filter'][] = [
126 '=ITEM_TYPE' => IM_MESSAGE_OPEN_LINE
127 ];
128 }
129 elseif (!$byChatIds)
130 {
131 if ($options['SKIP_OPENLINES'] === 'Y')
132 {
133 $skipTypes[] = IM_MESSAGE_OPEN_LINE;
134 }
135 if ($skipChat === 'Y')
136 {
137 $skipTypes[] = IM_MESSAGE_OPEN;
138 $skipTypes[] = IM_MESSAGE_CHAT;
139 }
140 if ($skipDialog === 'Y')
141 {
142 $skipTypes[] = IM_MESSAGE_PRIVATE;
143 }
144 if (!empty($skipTypes))
145 {
146 $ormParams['filter'][] = [
147 '!@ITEM_TYPE' => $skipTypes
148 ];
149 }
150 }
151
152 if (!isset($options['LAST_SYNC_DATE']))
153 {
154 if (isset($options['OFFSET']))
155 {
156 $ormParams['offset'] = $options['OFFSET'];
157 }
158 if (isset($options['LIMIT']))
159 {
160 $ormParams['limit'] = $options['LIMIT'];
161 }
162 if (isset($options['ORDER']))
163 {
164 $ormParams['order'] = $options['ORDER'];
165 }
166 }
167
168 $result = [];
169 $orm = \Bitrix\Im\Model\RecentTable::getList($ormParams);
170 $rows = $orm->fetchAll();
171 $rows = self::prepareRows($rows, $userId);
172 foreach ($rows as $row)
173 {
174 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
175 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
176
177 if ($isUser)
178 {
179 if (isset($result[$id]) && !$row['ITEM_MID'])
180 {
181 continue;
182 }
183 }
184 else if (isset($result[$id]))
185 {
186 continue;
187 }
188
189 $item = self::formatRow($row, [
190 'GENERAL_CHAT_ID' => $generalChatId,
191 'GET_ORIGINAL_TEXT' => $options['GET_ORIGINAL_TEXT'] ?? null,
192 ]);
193 if (!$item)
194 {
195 continue;
196 }
197
198 $result[$id] = $item;
199 }
200 $result = array_values($result);
201
202 if (
203 $showOpenlines
204 && !$options['ONLY_OPENLINES']
205 && class_exists('\Bitrix\ImOpenLines\Recent')
206 )
207 {
208 $options['ONLY_IN_QUEUE'] = true;
209 $chatsInQueue = \Bitrix\ImOpenLines\Recent::getRecent($userId, $options);
210 $result = array_merge($result, $chatsInQueue);
211 }
212
214 $result,
215 ['PINNED' => SORT_DESC, 'MESSAGE' => SORT_DESC, 'ID' => SORT_DESC],
216 [
217 'ID' => function($row) {
218 return $row;
219 },
220 'MESSAGE' => function($row) {
221 return $row['DATE'] instanceof \Bitrix\Main\Type\DateTime ? $row['DATE']->getTimeStamp() : 0;
222 },
223 ]
224 );
225
226 if ($options['JSON'])
227 {
228 foreach ($result as $index => $item)
229 {
230 $result[$index] = self::jsonRow($item);
231 }
232 }
233
234 return $result;
235 }
236
237 public static function getList($userId = null, $options = [])
238 {
240 if (!$userId)
241 {
242 return false;
243 }
244
245 $generalChatId = \CIMChat::GetGeneralChatId();
246
247 $viewCommonUsers = Option::get('im', 'view_common_users', 'Y') === 'N'
248 ? false
249 : (bool)\CIMSettings::GetSetting(\CIMSettings::SETTINGS, 'viewCommonUsers')
250 ;
251
252 $onlyOpenlinesOption = $options['ONLY_OPENLINES'] ?? null;
253 $onlyCopilotOption = $options['ONLY_COPILOT'] ?? null;
254 $onlyChannelOption = $options['ONLY_CHANNEL'] ?? null;
255 $canManageMessagesOption = $options['CAN_MANAGE_MESSAGES'] ?? null;
256 $skipChatOption = $options['SKIP_CHAT'] ?? null;
257 $skipDialogOption = $options['SKIP_DIALOG'] ?? null;
258 $skipCollabOption = Collab::isAvailable() ? ($options['SKIP_COLLAB'] ?? null) : 'Y';
259 $lastMessageDateOption = $options['LAST_MESSAGE_DATE'] ?? null;
260 $withoutCommonUsers = !$viewCommonUsers || $onlyOpenlinesOption === 'Y';
261 $unreadOnly = isset($options['UNREAD_ONLY']) && $options['UNREAD_ONLY'] === 'Y';
262 $shortInfo = isset($options['SHORT_INFO']) && $options['SHORT_INFO'] === 'Y';
263 $parseText = $options['PARSE_TEXT'] ?? null;
264
265 $showOpenlines = (
267 && (
268 $onlyOpenlinesOption === 'Y'
269 || $options['SKIP_OPENLINES'] !== 'Y'
270 )
271 );
272
273 $ormParams = self::getOrmParams([
274 'USER_ID' => $userId,
275 'SHOW_OPENLINES' => $showOpenlines,
276 'WITHOUT_COMMON_USERS' => $withoutCommonUsers,
277 'UNREAD_ONLY' => $unreadOnly,
278 'SHORT_INFO' => $shortInfo,
279 ]);
280
281 if ($onlyCopilotOption === 'Y')
282 {
283 $ormParams['filter'][] = [
284 '=ITEM_TYPE' => \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT
285 ];
286 }
287 elseif ($onlyOpenlinesOption === 'Y')
288 {
289 $ormParams['filter'][] = [
290 '=ITEM_TYPE' => IM_MESSAGE_OPEN_LINE
291 ];
292 }
293 elseif ($onlyChannelOption === 'Y')
294 {
295 $ormParams['filter'][] = [
296 '=ITEM_TYPE' => [\Bitrix\Im\V2\Chat::IM_TYPE_OPEN_CHANNEL, \Bitrix\Im\V2\Chat::IM_TYPE_CHANNEL],
297 ];
298 }
299 else
300 {
301 $skipTypes = [];
302 if ($options['SKIP_OPENLINES'] === 'Y')
303 {
304 $skipTypes[] = IM_MESSAGE_OPEN_LINE;
305 }
306 if ($skipChatOption === 'Y')
307 {
308 $skipTypes[] = IM_MESSAGE_OPEN;
309 $skipTypes[] = IM_MESSAGE_CHAT;
310 }
311 if ($skipDialogOption === 'Y')
312 {
313 $skipTypes[] = IM_MESSAGE_PRIVATE;
314 }
315 if ($skipCollabOption === 'Y')
316 {
317 $skipTypes[] = \Bitrix\Im\V2\Chat::IM_TYPE_COLLAB;
318 }
319 if (!RecentConfigManager::EXTERNAL_CHAT_USE_DEFAULT_RECENT_SECTION)
320 {
321 $skipTypes[] = \Bitrix\Im\V2\Chat::IM_TYPE_EXTERNAL;
322 }
323 if (!empty($skipTypes))
324 {
325 $ormParams['filter'][] = [
326 '!@ITEM_TYPE' => $skipTypes
327 ];
328 }
329 }
330
331 if ($lastMessageDateOption instanceof \Bitrix\Main\Type\DateTime)
332 {
333 $ormParams['filter']['<=DATE_LAST_ACTIVITY'] = $lastMessageDateOption;
334 }
335 else if (isset($options['OFFSET']))
336 {
337 $ormParams['offset'] = $options['OFFSET'];
338 }
339
340 if (isset($options['LIMIT']))
341 {
342 $ormParams['limit'] = (int)$options['LIMIT'];
343 }
344 else
345 {
346 $ormParams['limit'] = 50;
347 }
348
349 $sortOption = (new UserConfiguration((int)$userId))->getGeneralSettings()['pinnedChatSort'];
350 if ($sortOption === 'byCost')
351 {
352 $ormParams['order'] = [
353 'PINNED' => 'DESC',
354 'PIN_SORT' => 'ASC',
355 'DATE_LAST_ACTIVITY' => 'DESC',
356 ];
357 }
358 else
359 {
360 $ormParams['order'] = [
361 'PINNED' => 'DESC',
362 'DATE_LAST_ACTIVITY' => 'DESC',
363 ];
364 }
365
366 if ($canManageMessagesOption === 'Y')
367 {
368 $ormParams = Permission\Filter::getRoleGetListFilter($ormParams, Permission\ActionGroup::ManageMessages, 'RELATION', 'CHAT');
369 }
370
371 $orm = \Bitrix\Im\Model\RecentTable::getList($ormParams);
372
373 $counter = 0;
374 $result = [];
375 $messageIdsWithCopilotRole = [];
376 $copilotData = [];
377 $chatsIds = [];
378 $messagesAutoDeleteConfigs = [];
379
380 $rows = $orm->fetchAll();
381 $rows = self::prepareRows($rows, $userId, $shortInfo);
382 foreach ($rows as $row)
383 {
384 $counter++;
385 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
386 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
387
388 if ($isUser)
389 {
390 if (isset($result[$id]) && !$row['ITEM_MID'])
391 {
392 continue;
393 }
394 }
395 else if (isset($result[$id]))
396 {
397 continue;
398 }
399
400 $item = self::formatRow($row, [
401 'GENERAL_CHAT_ID' => $generalChatId,
402 'WITHOUT_COMMON_USERS' => $withoutCommonUsers,
403 'GET_ORIGINAL_TEXT' => $options['GET_ORIGINAL_TEXT'] ?? null,
404 'SHORT_INFO' => $shortInfo,
405 'PARSE_TEXT' => $parseText,
406 ]);
407 if (!$item)
408 {
409 continue;
410 }
411
412 if ($row['ITEM_TYPE'] === \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT)
413 {
414 $copilotChatRole = (new RoleManager())->getMainRole((int)$item['CHAT_ID']);
415 if (isset($copilotChatRole))
416 {
417 $copilotData['chats'][$item['ID']] = $copilotChatRole;
418 }
419 }
420
421 if (!$shortInfo)
422 {
423 $chatsIds[] = (int)$item['CHAT_ID'];
424 }
425
426 if (
427 !$shortInfo
428 && (isset($item['USER']['BOT']) && $item['USER']['BOT'] === true)
429 && Loader::includeModule('imbot')
430 && (int)$item['MESSAGE']['AUTHOR_ID'] === CopilotChatBot::getBotId()
431 )
432 {
433 $messageIdsWithCopilotRole[] = (int)$item['MESSAGE']['ID'];
434 }
435
436 if ($shortInfo && $options['JSON'])
437 {
438 $result[$id] = self::jsonRow($item);
439 }
440 else
441 {
442 $result[$id] = $item;
443 }
444 }
445
446 if (!$shortInfo && !empty($messageIdsWithCopilotRole))
447 {
448 $copilotMessageRoles = self::fillCopilotMessageRoles($messageIdsWithCopilotRole);
449
450 foreach ($result as $item)
451 {
452 if (in_array((int)$item['MESSAGE']['ID'], $messageIdsWithCopilotRole, true))
453 {
454 $copilotData['messages'][(int)$item['MESSAGE']['ID']] =
455 $copilotMessageRoles[(int)$item['MESSAGE']['ID']] ?? RoleManager::getDefaultRoleCode()
456 ;
457 }
458 }
459 }
460
461 $copilotData = self::prepareCopilotData($copilotData, $userId, $shortInfo);
462
463 if ($showOpenlines && !$onlyCopilotOption && Loader::includeModule('imopenlines'))
464 {
465 if (!isset($options['SKIP_UNDISTRIBUTED_OPENLINES']) || $options['SKIP_UNDISTRIBUTED_OPENLINES'] !== 'Y')
466 {
467 $recentOpenLines = \Bitrix\ImOpenLines\Recent::getRecent($userId, ['ONLY_IN_QUEUE' => true]);
468
469 if (is_array($recentOpenLines))
470 {
471 $result = array_merge($result, $recentOpenLines);
472 }
473 }
474 }
475
476 if (!$shortInfo)
477 {
478 $messagesAutoDeleteConfigs = (new MessagesAutoDeleteConfigs($chatsIds))->toRestFormat();
479 }
480
481 $result = array_values($result);
482
483 if ($options['JSON'])
484 {
485 if (!$shortInfo)
486 {
487 foreach ($result as $index => $item)
488 {
489 $result[$index] = self::jsonRow($item);
490 }
491 }
492
493 $objectToReturn = [
494 'items' => $result,
495 'hasMorePages' => $ormParams['limit'] == $counter, // TODO remove this later
496 'hasMore' => $ormParams['limit'] == $counter,
497 'copilot' => !empty($copilotData) ? $copilotData : null,
498 'messagesAutoDeleteConfigs' => $messagesAutoDeleteConfigs,
499 ];
500
501 if (!isset($options['LAST_MESSAGE_DATE']))
502 {
503 $objectToReturn['birthdayList'] = \Bitrix\Im\Integration\Intranet\User::getBirthdayForToday();
504 }
505
506 return $objectToReturn;
507 }
508
509 $converter = new Converter(Converter::TO_SNAKE | Converter::TO_UPPER | Converter::KEYS);
510
511 return [
512 'ITEMS' => $result,
513 'HAS_MORE_PAGES' => $ormParams['limit'] == $counter, // TODO remove this later
514 'HAS_MORE' => $ormParams['limit'] == $counter,
515 'COPILOT' => !empty($copilotData) ? $converter->process($copilotData) : null,
516 'MESSAGES_AUTO_DELETE_CONFIGS' => $converter->process($messagesAutoDeleteConfigs) ?? [],
517 ];
518 }
519
520 private static function fillCopilotMessageRoles(array $messageIdsWithCopilotRole): array
521 {
522 $copilotMessageRoles = [];
523
524 $collection = Param::getDataClass()::query()
525 ->setSelect(['MESSAGE_ID', 'PARAM_VALUE'])
526 ->whereIn('MESSAGE_ID', $messageIdsWithCopilotRole)
527 ->where('PARAM_NAME', \Bitrix\Im\V2\Message\Params::COPILOT_ROLE)
528 ->fetchCollection()
529 ;
530
531 foreach ($collection as $item)
532 {
533 $copilotMessageRoles[(int)$item->getMessageId()] = $item->getParamValue();
534 }
535
536 return $copilotMessageRoles;
537 }
538
539 private static function prepareCopilotData(array $copilotData, int $userId, bool $shortInfo): array
540 {
541 $roleManager = (new RoleManager())->setContextUser($userId);
542 $recentCopilotRoles = !$shortInfo ? $roleManager->getRecentKeyRoles() : [];
543 $copilotRoles = array_values(array_merge(
544 $copilotData['chats'] ?? [],
545 $copilotData['messages'] ?? [],
546 $recentCopilotRoles
547 ));
548
549 $chats = CopilotPopupItem::convertArrayDataForChats($copilotData['chats'] ?? []);
550 $messages = CopilotPopupItem::convertArrayDataForMessages($copilotData['messages'] ?? []);
551 $roles =
552 $shortInfo
553 ? $roleManager->getRolesShort($copilotRoles)
554 : $roleManager->getRoles($copilotRoles)
555 ;
556 return [
557 'chats' => !empty($chats) ? $chats : null,
558 'messages' => !empty($messages) ? $messages : null,
559 'roles' => !empty($roles) ? $roles : null,
560 'recommendedRoles' => !empty($recentCopilotRoles) ? $recentCopilotRoles : null,
561 ];
562 }
563
564 public static function getElement($itemType, $itemId, $userId = null, $options = [])
565 {
567 if (!$userId)
568 {
569 return false;
570 }
571
572 $generalChatId = \CIMChat::GetGeneralChatId();
573
574 $ormParams = self::getOrmParams([
575 'USER_ID' => $userId,
576 'SHOW_OPENLINES' => $itemType === IM_MESSAGE_OPEN_LINE,
577 'WITHOUT_COMMON_USERS' => true,
578 ]);
579
580 $ormParams['filter']['=ITEM_TYPE'] = $itemType;
581 $ormParams['filter']['=ITEM_ID'] = $itemId;
582
583 $orm = \Bitrix\Im\Model\RecentTable::getList([
584 'select' => $ormParams['select'],
585 'filter' => $ormParams['filter'],
586 'runtime' => $ormParams['runtime'],
587 ]);
588
589 $result = null;
590 $rows = $orm->fetchAll();
591 $rows = self::prepareRows($rows, $userId);
592 foreach ($rows as $row)
593 {
594 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
595 if ($isUser)
596 {
597 if ($result && !$row['ITEM_MID'])
598 {
599 continue;
600 }
601 }
602 else if ($result)
603 {
604 continue;
605 }
606
607 $item = self::formatRow($row, [
608 'GENERAL_CHAT_ID' => $generalChatId,
609 ]);
610 if (!$item)
611 {
612 continue;
613 }
614
615 $result = $item;
616 }
617 $result = self::prepareRows([$result], $userId)[0];
618
619 if ($options['JSON'])
620 {
621 $result = self::jsonRow($result);
622 }
623
624 return $result;
625 }
626
627 private static function getOrmParams($params)
628 {
629 $userId = (int)$params['USER_ID'];
630 $showOpenlines = \Bitrix\Main\Loader::includeModule('imopenlines') && $params['SHOW_OPENLINES'] !== false;
631 $isIntranetInstalled = \Bitrix\Main\Loader::includeModule('intranet');
632 $isIntranet = $isIntranetInstalled && \Bitrix\Intranet\Util::isIntranetUser($userId);
633 $withoutCommonUsers = $params['WITHOUT_COMMON_USERS'] === true || !$isIntranet;
634 $unreadOnly = isset($params['UNREAD_ONLY']) && $params['UNREAD_ONLY'] === true;
635 $shortInfo = isset($params['SHORT_INFO']) && $params['SHORT_INFO'] === true;
636 $chatIds = $params['CHAT_IDS'] ?? null;
637
638 $shortInfoFields = [
639 'USER_ID',
640 'ITEM_TYPE',
641 'ITEM_ID',
642 'ITEM_MID',
643 'ITEM_CID',
644 'PINNED',
645 'UNREAD',
646 'DATE_MESSAGE',
647 'DATE_LAST_ACTIVITY',
648 'PIN_SORT',
649 'RELATION_ID' => 'RELATION.ID',
650 'RELATION_NOTIFY_BLOCK' => 'RELATION.NOTIFY_BLOCK',
651 'RELATION_IS_MANAGER' => 'RELATION.MANAGER',
652 'CHAT_ID' => 'CHAT.ID',
653 'CHAT_TITLE' => 'CHAT.TITLE',
654 'CHAT_TYPE' => 'CHAT.TYPE',
655 'CHAT_AVATAR' => 'CHAT.AVATAR',
656 'CHAT_AUTHOR_ID' => 'CHAT.AUTHOR_ID',
657 'CHAT_COLOR' => 'CHAT.COLOR',
658 'CHAT_ENTITY_TYPE' => 'CHAT.ENTITY_TYPE',
659 'CHAT_CAN_POST' => 'CHAT.CAN_POST',
660 'CHAT_EXTRANET' => 'CHAT.EXTRANET',
661 'USER_LAST_ACTIVITY_DATE' => 'USER.LAST_ACTIVITY_DATE',
662 ];
663
664 $additionalInfoFields = [
665 'ITEM_OLID',
666 'DATE_UPDATE',
667 'MESSAGE_ID' => 'MESSAGE.ID',
668 'MESSAGE_AUTHOR_ID' => 'MESSAGE.AUTHOR_ID',
669 'MESSAGE_TEXT' => 'MESSAGE.MESSAGE',
670 'MESSAGE_USER_LAST_ACTIVITY_DATE' => 'MESSAGE.AUTHOR.LAST_ACTIVITY_DATE',
671 'USER_EMAIL' => 'USER.EMAIL',
672 'MESSAGE_UUID_VALUE' => 'MESSAGE_UUID.UUID',
673 'CHAT_MANAGE_USERS_ADD' => 'CHAT.MANAGE_USERS_ADD',
674 'CHAT_MANAGE_USERS_DELETE' => 'CHAT.MANAGE_USERS_DELETE',
675 'CHAT_MANAGE_UI' => 'CHAT.MANAGE_UI',
676 'CHAT_MANAGE_SETTINGS' => 'CHAT.MANAGE_SETTINGS',
677 'CHAT_LAST_MESSAGE_STATUS_BOOL' => 'MESSAGE.NOTIFY_READ',
678 'RELATION_LAST_ID' => 'RELATION.LAST_ID',
679 'CHAT_PARENT_ID' => 'CHAT.PARENT_ID',
680 'CHAT_PARENT_MID' => 'CHAT.PARENT_MID',
681 'CHAT_ENTITY_ID' => 'CHAT.ENTITY_ID',
682 'CHAT_ENTITY_DATA_1' => 'CHAT.ENTITY_DATA_1',
683 'CHAT_ENTITY_DATA_2' => 'CHAT.ENTITY_DATA_2',
684 'CHAT_ENTITY_DATA_3' => 'CHAT.ENTITY_DATA_3',
685 'CHAT_DATE_CREATE' => 'CHAT.DATE_CREATE',
686 'CHAT_USER_COUNT' => 'CHAT.USER_COUNT',
687 ];
688
689 $shortRuntime = [
690 new \Bitrix\Main\Entity\ReferenceField(
691 'USER',
692 '\Bitrix\Main\UserTable',
693 array("=this.ITEM_TYPE" => new \Bitrix\Main\DB\SqlExpression("?s", IM_MESSAGE_PRIVATE), "=ref.ID" => "this.ITEM_ID"),
694 array("join_type"=>"LEFT")
695 ),
696 ];
697
698 if ($shortInfo)
699 {
700 $shortRuntime[] = new \Bitrix\Main\Entity\ReferenceField(
701 'CODE',
702 '\Bitrix\Im\Model\MessageParamTable',
703 [
704 "=ref.MESSAGE_ID" => "this.ITEM_MID",
705 "=ref.PARAM_NAME" => new \Bitrix\Main\DB\SqlExpression("?s", "CODE")
706 ],
707 ["join_type" => "LEFT"]
708 );
709 $shortInfoFields['MESSAGE_CODE'] = 'CODE.PARAM_VALUE';
710 }
711
712 $unreadTable = MessageUnreadTable::getTableName();
713
714 $additionalRuntime = [
715 new ExpressionField(
716 'HAS_UNREAD_MESSAGE',
717 "EXISTS(SELECT 1 FROM {$unreadTable} WHERE CHAT_ID = %s AND USER_ID = %s)",
718 ['ITEM_CID', 'USER_ID']
719 )
720 ];
721
722 $select = $shortInfo ? $shortInfoFields : array_merge($shortInfoFields, $additionalInfoFields);
723 $runtime = $shortInfo ? $shortRuntime : array_merge($shortRuntime, $additionalRuntime);
724
725 if (!$withoutCommonUsers)
726 {
727 $select['INVITATION_ORIGINATOR_ID'] = 'INVITATION.ORIGINATOR_ID';
728 }
729 if ($showOpenlines)
730 {
731 $select['LINES_ID'] = 'LINES.ID';
732 $select['LINES_STATUS'] = 'LINES.STATUS';
733 $select['LINES_DATE_CREATE'] = 'LINES.DATE_CREATE';
734 }
735
736 if (!$withoutCommonUsers)
737 {
738 $runtime[] = new \Bitrix\Main\Entity\ReferenceField(
739 'INVITATION',
740 '\Bitrix\Intranet\Internals\InvitationTable',
741 array("=this.ITEM_TYPE" => new \Bitrix\Main\DB\SqlExpression("?s", IM_MESSAGE_PRIVATE), "=ref.USER_ID" => "this.ITEM_ID"),
742 array("join_type"=>"LEFT")
743 );
744 }
745 if ($showOpenlines && !$shortInfo)
746 {
747 $runtime[] = new \Bitrix\Main\Entity\ReferenceField(
748 'LINES',
749 '\Bitrix\ImOpenlines\Model\SessionTable',
750 [">this.ITEM_OLID" => new \Bitrix\Main\DB\SqlExpression("?i", 0), "=ref.ID" => "this.ITEM_OLID"],
751 ["join_type" => "LEFT"]
752 );
753 }
754
755 if ($withoutCommonUsers)
756 {
757 $filter = ['=USER_ID' => $userId];
758 }
759 else
760 {
761 $filter = ['@USER_ID' => [$userId, 0]];
762 }
763
764 if ($isIntranetInstalled && !$isIntranet)
765 {
766 $subQuery = Group::getExtranetAccessibleUsersQuery($userId);
767 if ($subQuery !== null)
768 {
769 $filter[] = [
770 'LOGIC' => 'OR',
771 ['!=ITEM_TYPE' => 'P'],
772 ['@USER.ID' => new SqlExpression($subQuery->getQuery())],
773 ];
774 }
775 }
776
777 if ($unreadOnly)
778 {
779 $filter[] = [
780 'LOGIC' => 'OR',
781 ['==HAS_UNREAD_MESSAGE' => true],
782 ['=UNREAD' => true],
783 ];
784 }
785
786 if ($chatIds)
787 {
788 $filter['@ITEM_CID'] = $chatIds; // todo: add index
789 }
790
791 return [
792 'select' => $select,
793 'filter' => $filter,
794 'runtime' => $runtime,
795 ];
796 }
797
798 private static function formatRow($row, $options = []): ?array
799 {
800 $generalChatId = (int)$options['GENERAL_CHAT_ID'];
801 $withoutCommonUsers = isset($options['WITHOUT_COMMON_USERS']) && $options['WITHOUT_COMMON_USERS'] === true;
802 $shortInfo = isset($options['SHORT_INFO']) && $options['SHORT_INFO'];
803
804 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
805 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
806 $row['MESSAGE_ID'] ??= null;
807
808 if (!$isUser && ((!$row['MESSAGE_ID'] && !$shortInfo) || !$row['RELATION_ID'] || !$row['CHAT_ID']))
809 {
810 return null;
811 }
812
813 $item = self::formatItem($row,$options, $shortInfo, $isUser, $id);
814
815 if ($isUser)
816 {
817 if (
818 $withoutCommonUsers
819 && ($row['USER_ID'] == 0 || $row['MESSAGE_CODE'] === 'USER_JOIN')
820 )
821 {
822 return null;
823 }
824
825 $row['INVITATION_ORIGINATOR_ID'] ??= null;
826 if (!$row['USER_LAST_ACTIVITY_DATE'] && $row['INVITATION_ORIGINATOR_ID'])
827 {
828 $item['INVITED'] = [
829 'ORIGINATOR_ID' => (int)$row['INVITATION_ORIGINATOR_ID'],
830 'CAN_RESEND' => !empty($row['USER_EMAIL'])
831 ];
832 }
833 $item['USER'] = [
834 'ID' => (int)$row['ITEM_ID'],
835 ];
836
837 $item['CHAT']['TEXT_FIELD_ENABLED'] = self::getTextFieldEnabled((int)$row['ITEM_CID']);
838 $item['CHAT']['BACKGROUND_ID'] = self::getBackgroundId((int)$row['ITEM_CID']);
839 }
840 else
841 {
842 $item['CHAT'] = self::formatChat($row, $shortInfo, $generalChatId);
843
844 if (!$shortInfo)
845 {
846 $item['AVATAR'] = [
847 'URL' => $item['CHAT']['AVATAR'],
848 'COLOR' => $item['CHAT']['COLOR'],
849 ];
850 $item['TITLE'] = $row['CHAT_TITLE'];
851 }
852
853 if ($row["CHAT_ENTITY_TYPE"] == 'LINES')
854 {
855 $item['LINES'] = [
856 'ID' => (int)$row['LINES_ID'],
857 'STATUS' => (int)$row['LINES_STATUS'],
858 'DATE_CREATE' => $row['LINES_DATE_CREATE'] ?? $row['DATE_UPDATE'],
859 ];
860 }
861 $item['USER'] = [
862 'ID' => (int)($row['MESSAGE_AUTHOR_ID'] ?? 0),
863 ];
864 }
865
866 if ($item['USER']['ID'] > 0)
867 {
868 $user = self::formatUser($row, $item, $shortInfo);
869
870 if ($user === null)
871 {
872 return null;
873 }
874
875 $item['USER'] = $user;
876
877 if (
878 !$shortInfo
879 && $item['TYPE'] == 'user'
880 && !empty($user)
881 )
882 {
883 $item['AVATAR'] = [
884 'URL' => $user['AVATAR'],
885 'COLOR' => $user['COLOR']
886 ];
887 $item['TITLE'] = $user['NAME'];
888 }
889 }
890
891 $item['OPTIONS'] = [];
892 if ($row['USER_ID'] == 0 || $row['MESSAGE_CODE'] === 'USER_JOIN')
893 {
894 $item['OPTIONS']['DEFAULT_USER_RECORD'] = true;
895 }
896
897 return $item;
898 }
899
900 private static function formatMessage(array $row, array $options, bool $shortInfo): array
901 {
902 $row['DATE_MESSAGE'] ??= null;
903
904 if ($shortInfo)
905 {
906 return [
907 'ID' => (int)($row['ITEM_MID'] ?? 0),
908 'DATE' => $row['DATE_MESSAGE'] ?: $row['DATE_LAST_ACTIVITY'],
909 ];
910 }
911
912 if (!$row['ITEM_MID'] || !$row['MESSAGE_ID'])
913 {
914 return [
915 'ID' => (int)($row['ITEM_MID'] ?? 0),
916 'TEXT' => "",
917 'FILE' => false,
918 'AUTHOR_ID' => 0,
919 'ATTACH' => false,
920 'DATE' => $row['DATE_MESSAGE']?: $row['DATE_UPDATE'],
921 'STATUS' => $row['CHAT_LAST_MESSAGE_STATUS'],
922 ];
923 }
924
925 $attach = false;
926 if ($row['MESSAGE_ATTACH'] || $row["MESSAGE_ATTACH_JSON"])
927 {
928 if (preg_match('/^(\d+)$/', $row['MESSAGE_ATTACH']))
929 {
930 $attach = true;
931 }
932 else if ($row['MESSAGE_ATTACH'] === \CIMMessageParamAttach::FIRST_MESSAGE)
933 {
934 try
935 {
936 $value = \Bitrix\Main\Web\Json::decode($row["MESSAGE_ATTACH_JSON"]);
937 $attachRestored = \CIMMessageParamAttach::PrepareAttach($value);
938 $attach = $attachRestored['DESCRIPTION'];
939 }
940 catch (\Bitrix\Main\SystemException $e)
941 {
942 $attach = true;
943 }
944 }
945 else if (!empty($row['MESSAGE_ATTACH']))
946 {
947 $attach = $row['MESSAGE_ATTACH'];
948 }
949 else
950 {
951 $attach = true;
952 }
953 }
954
955 $text = $row['MESSAGE_TEXT'] ?? '';
956
957 $getOriginalTextOption = $options['GET_ORIGINAL_TEXT'] ?? null;
958 $parseText = $options['PARSE_TEXT'] ?? null;
959 if ($parseText === 'Y')
960 {
962 }
963 elseif ($getOriginalTextOption === 'Y')
964 {
966 }
967 else
968 {
970 str_replace("\n", " ", $text),
971 $row['MESSAGE_FILE'] > 0,
972 $attach
973 );
974 }
975
976 return [
977 'ID' => (int)$row['ITEM_MID'],
978 'TEXT' => $text,
979 'FILE' => $row['MESSAGE_FILE'],
980 'AUTHOR_ID' => (int)$row['MESSAGE_AUTHOR_ID'],
981 'ATTACH' => $attach,
982 'DATE' => $row['DATE_MESSAGE']?: $row['DATE_UPDATE'],
983 'STATUS' => $row['CHAT_LAST_MESSAGE_STATUS'],
984 'UUID' => $row['MESSAGE_UUID_VALUE'],
985 ];
986 }
987
988 private static function formatItem(
989 array $row,
991 bool $shortInfo,
992 bool $isUser,
993 mixed $id
994 ): array
995 {
996 $message = self::formatMessage($row, $options, $shortInfo);
997
998 if ($shortInfo)
999 {
1000 return [
1001 'ID' => $id,
1002 'CHAT_ID' => (int)$row['CHAT_ID'],
1003 'TYPE' => $isUser ? 'user' : 'chat',
1004 'MESSAGE' => $message,
1005 'COUNTER' => (int)$row['COUNTER'],
1006 'PINNED' => $row['PINNED'] === 'Y',
1007 'UNREAD' => $row['UNREAD'] === 'Y',
1008 'DATE_LAST_ACTIVITY' => $row['DATE_LAST_ACTIVITY'],
1009 ];
1010 }
1011
1012 return [
1013 'ID' => $id,
1014 'CHAT_ID' => (int)$row['CHAT_ID'],
1015 'TYPE' => $isUser ? 'user' : 'chat',
1016 'AVATAR' => [],
1017 'TITLE' => [],
1018 'MESSAGE' => $message,
1019 'COUNTER' => (int)$row['COUNTER'],
1020 'LAST_ID' => (int)($row['RELATION_LAST_ID'] ?? 0),
1021 'PINNED' => $row['PINNED'] === 'Y',
1022 'UNREAD' => $row['UNREAD'] === 'Y',
1023 'HAS_REMINDER' => isset($row['HAS_REMINDER']) && $row['HAS_REMINDER'] === 'Y',
1024 'DATE_UPDATE' => $row['DATE_UPDATE'],
1025 'DATE_LAST_ACTIVITY' => $row['DATE_LAST_ACTIVITY'],
1026 ];
1027 }
1028
1029 private static function formatChat(array $row, bool $shortInfo, int $generalChatId): array
1030 {
1031 $avatar = \CIMChat::GetAvatarImage($row['CHAT_AVATAR'], 200, false);
1032 $color = $row['CHAT_COLOR'] <> ''
1033 ? Color::getColor($row['CHAT_COLOR'])
1034 : Color::getColorByNumber(
1035 $row['ITEM_ID']
1036 );
1037 $chatType = \Bitrix\Im\Chat::getType($row);
1038
1039 if ($generalChatId == $row['ITEM_ID'])
1040 {
1041 $row["CHAT_ENTITY_TYPE"] = 'GENERAL';
1042 }
1043
1044 $muteList = [];
1045 if ($row['RELATION_NOTIFY_BLOCK'] == 'Y')
1046 {
1047 $muteList = [$row['RELATION_USER_ID'] => true];
1048 }
1049
1050 if ($shortInfo)
1051 {
1052 return [
1053 'ID' => (int)$row['ITEM_CID'],
1054 'NAME' => $row['CHAT_TITLE'],
1055 'EXTRANET' => $row['CHAT_EXTRANET'] == 'Y',
1056 'CONTAINS_COLLABER' => self::containsCollaber((int)$row['ITEM_CID']),
1057 'AVATAR' => $avatar,
1058 'COLOR' => $color,
1059 'TYPE' => $chatType,
1060 'ENTITY_TYPE' => (string)$row['CHAT_ENTITY_TYPE'],
1061 'MUTE_LIST' => $muteList,
1062 'ROLE' => self::getRole($row),
1063 'TEXT_FIELD_ENABLED' => self::getTextFieldEnabled((int)$row['ITEM_CID']),
1064 'BACKGROUND_ID' => self::getBackgroundId((int)$row['ITEM_CID']),
1065 'PERMISSIONS' => [
1066 'MANAGE_MESSAGES' => mb_strtolower($row['CHAT_CAN_POST'] ?? ''),
1067 ],
1068 ];
1069 }
1070
1071 $publicOption = null;
1072 if ($row["CHAT_ENTITY_TYPE"] === \Bitrix\Im\V2\Chat::ENTITY_TYPE_VIDEOCONF)
1073 {
1074 $publicOption = \Bitrix\Im\V2\Chat::getInstance((int)$row['ITEM_CID'])->getPublicOption();
1075 }
1076
1077 $managerList = [];
1078 if ($row['CHAT_OWNER'] ?? null == $row['RELATION_USER_ID'] || $row['RELATION_IS_MANAGER'] == 'Y')
1079 {
1080 $managerList = [(int)$row['RELATION_USER_ID']];
1081 }
1082
1083 $chatOptions = \CIMChat::GetChatOptions();
1084 $restrictions = $chatOptions['DEFAULT'];
1085 if ($row['CHAT_ENTITY_TYPE'] && array_key_exists($row['CHAT_ENTITY_TYPE'], $chatOptions))
1086 {
1087 $restrictions = $chatOptions[$row['CHAT_ENTITY_TYPE']];
1088 }
1089
1090 return [
1091 'ID' => (int)$row['ITEM_CID'],
1092 'PARENT_CHAT_ID' => (int)$row['CHAT_PARENT_ID'],
1093 'PARENT_MESSAGE_ID' => (int)$row['CHAT_PARENT_MID'],
1094 'NAME' => $row['CHAT_TITLE'],
1095 'OWNER' => (int)$row['CHAT_AUTHOR_ID'],
1096 'EXTRANET' => $row['CHAT_EXTRANET'] == 'Y',
1097 'CONTAINS_COLLABER' => self::containsCollaber((int)$row['ITEM_CID']),
1098 'AVATAR' => $avatar,
1099 'COLOR' => $color,
1100 'TYPE' => $chatType,
1101 'ENTITY_TYPE' => (string)$row['CHAT_ENTITY_TYPE'],
1102 'ENTITY_ID' => (string)$row['CHAT_ENTITY_ID'],
1103 'ENTITY_DATA_1' => (string)$row['CHAT_ENTITY_DATA_1'],
1104 'ENTITY_DATA_2' => (string)$row['CHAT_ENTITY_DATA_2'],
1105 'ENTITY_DATA_3' => (string)$row['CHAT_ENTITY_DATA_3'],
1106 'MUTE_LIST' => $muteList,
1107 'MANAGER_LIST' => $managerList,
1108 'DATE_CREATE' => $row['CHAT_DATE_CREATE'],
1109 'MESSAGE_TYPE' => $row["CHAT_TYPE"],
1110 'USER_COUNTER' => (int)$row['CHAT_USER_COUNT'],
1111 'RESTRICTIONS' => $restrictions,
1112 'ROLE' => self::getRole($row),
1113 'TEXT_FIELD_ENABLED' => self::getTextFieldEnabled((int)$row['ITEM_CID']),
1114 'BACKGROUND_ID' => self::getBackgroundId((int)$row['ITEM_CID']),
1115 'ENTITY_LINK' => EntityLink::getInstance(\CIMChat::initChatByArray($row))->toArray(),
1116 'PERMISSIONS' => [
1117 'MANAGE_USERS_ADD' => mb_strtolower($row['CHAT_MANAGE_USERS_ADD'] ?? ''),
1118 'MANAGE_USERS_DELETE' => mb_strtolower($row['CHAT_MANAGE_USERS_DELETE'] ?? ''),
1119 'MANAGE_UI' => mb_strtolower($row['CHAT_MANAGE_UI'] ?? ''),
1120 'MANAGE_SETTINGS' => mb_strtolower($row['CHAT_MANAGE_SETTINGS'] ?? ''),
1121 'MANAGE_MESSAGES' => mb_strtolower($row['CHAT_CAN_POST'] ?? ''),
1122 'CAN_POST' => mb_strtolower($row['CHAT_CAN_POST'] ?? ''),
1123 ],
1124 'PUBLIC' => $publicOption ?? '',
1125 ];
1126 }
1127
1128 private static function containsCollaber(int $chatId): bool
1129 {
1130 if ($chatId <= 0)
1131 {
1132 return false;
1133 }
1134
1135 $paramsService = Params::getInstance($chatId);
1136
1137 return (bool)$paramsService->get(Params::CONTAINS_COLLABER)?->getValue();
1138 }
1139
1140 private static function getTextFieldEnabled(int $chatId): bool
1141 {
1142 return (new TextFieldEnabled($chatId))->get();
1143 }
1144
1145 private static function getBackgroundId(int $chatId): ?string
1146 {
1147 return (new Background($chatId))->get();
1148 }
1149
1150 private static function getChatMessagesAutoDeleteConfigs(int $chatId): array
1151 {
1152 $config = (new MessagesAutoDeleteConfigs([$chatId]))->toRestFormat();
1153
1154 return (new Converter(Converter::TO_SNAKE | Converter::TO_UPPER | Converter::KEYS))->process($config);
1155 }
1156
1157 private static function formatUser(array $row, array $item, bool $shortInfo): ?array
1158 {
1159 $userObject = \Bitrix\Im\V2\Entity\User\User::getInstance($item['USER']['ID']);
1160 if ($userObject instanceof NullUser)
1161 {
1162 return [];
1163 }
1164
1165 $user = $userObject->getArray(['WITHOUT_ONLINE' => true, 'USER_SHORT_FORMAT' => $shortInfo]);
1166
1167 if ($shortInfo)
1168 {
1169 if (!$userObject->isActive())
1170 {
1171 return null;
1172 }
1173
1174 return $user;
1175 }
1176
1177 if ($item['TYPE'] == 'user')
1178 {
1179 if (
1180 !empty($user['BOT_DATA'])
1181 && Loader::includeModule('imbot')
1182 && $user['BOT_DATA']['code'] === CopilotChatBot::BOT_CODE
1183 )
1184 {
1185 return null;
1186 }
1187
1188 if (
1189 (!$user['ACTIVE'] && $item['COUNTER'] <= 0)
1190 && !$user['BOT']
1191 && !$user['CONNECTOR']
1192 && !$user['NETWORK']
1193 )
1194 {
1195 return null;
1196 }
1197 }
1198
1199 if ($item['TYPE'] == 'user')
1200 {
1201 $lastActivityDate = $row['USER_LAST_ACTIVITY_DATE'] ?? null;
1202 }
1203 else
1204 {
1205 $lastActivityDate = $row['MESSAGE_USER_LAST_ACTIVITY_DATE'] ?? null;
1206 }
1207
1208 $user['LAST_ACTIVITY_DATE'] = $lastActivityDate ?: false;
1209 $user['DESKTOP_LAST_DATE'] = false;
1210 $user['MOBILE_LAST_DATE'] = false;
1211 $user['IDLE'] = false;
1212
1213 return $user;
1214 }
1215
1216 private static function jsonRow($item)
1217 {
1218 if (!is_array($item))
1219 {
1220 return $item;
1221 }
1222
1223 foreach ($item as $key => $value)
1224 {
1225 if ($value instanceof \Bitrix\Main\Type\DateTime)
1226 {
1227 $item[$key] = date('c', $value->getTimestamp());
1228 }
1229 else if (is_array($value))
1230 {
1231 foreach ($value as $subKey => $subValue)
1232 {
1233 if ($subValue instanceof \Bitrix\Main\Type\DateTime)
1234 {
1235 $value[$subKey] = date('c', $subValue->getTimestamp());
1236 }
1237 else if (
1238 is_string($subValue)
1239 && $subValue
1240 && in_array($subKey, ['URL', 'AVATAR'])
1241 && mb_strpos($subValue, 'http') !== 0
1242 )
1243 {
1244 $value[$subKey] = \Bitrix\Im\Common::getPublicDomain().$subValue;
1245 }
1246 else if (is_array($subValue))
1247 {
1248 $value[$subKey] = array_change_key_case($subValue, CASE_LOWER);
1249 }
1250 }
1251 $item[$key] = array_change_key_case($value, CASE_LOWER);
1252 }
1253 }
1254
1255 return array_change_key_case($item, CASE_LOWER);
1256 }
1257
1258 public static function pin($dialogId, $pin, $userId = null)
1259 {
1261 if (!$userId)
1262 {
1263 return false;
1264 }
1265
1266 $pinnedCount = \Bitrix\Im\Model\RecentTable::getCount(['=USER_ID' => $userId, '=PINNED' => 'Y']);
1267
1268 self::$limitError = false;
1269 if ($pin && (int)$pinnedCount >= self::PINNED_CHATS_LIMIT)
1270 {
1271 self::$limitError = true;
1272
1273 return false;
1274 }
1275
1276 $pin = $pin === true? 'Y': 'N';
1277
1278 $id = $dialogId;
1279 $chatId = 0;
1280 if (mb_substr($dialogId, 0, 4) == 'chat')
1281 {
1282 $itemTypes = \Bitrix\Im\Chat::getTypes();
1283 $id = mb_substr($dialogId, 4);
1284 $chatId = (int)$id;
1285 }
1286 else
1287 {
1288 $itemTypes = IM_MESSAGE_PRIVATE;
1289 $chatId = \Bitrix\Im\Dialog::getChatId($dialogId);
1290 }
1291
1292 $element = \Bitrix\Im\Model\RecentTable::getList(
1293 [
1294 'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'PINNED', 'PIN_SORT'],
1295 'filter' => [
1296 '=USER_ID' => $userId,
1297 '=ITEM_TYPE' => $itemTypes,
1298 '=ITEM_ID' => $id
1299 ]
1300 ]
1301 )->fetch();
1302 if (!$element)
1303 {
1304 return false;
1305// if (mb_substr($dialogId, 0, 4) == 'chat')
1306// {
1307// if (!\Bitrix\Im\Dialog::hasAccess($dialogId))
1308// {
1309// return false;
1310// }
1311//
1312// $missingChat = \Bitrix\Im\Model\ChatTable::getRowById($id);
1313// $itemTypes = $missingChat['TYPE'];
1314// }
1315
1316// $messageId = 0;
1317// $relationId = 0;
1318// if ($itemTypes !== IM_MESSAGE_OPEN)
1319// {
1320
1321// }
1322
1323 $relationData = \Bitrix\Im\Model\RelationTable::getList(
1324 [
1325 'select' => ['ID', 'LAST_MESSAGE_ID' => 'CHAT.LAST_MESSAGE_ID'],
1326 'filter' => [
1327 '=CHAT_ID' => $chatId,
1328 '=USER_ID' => $userId,
1329 ]
1330 ]
1331 )->fetchAll()[0];
1332
1333 $messageId = $relationData['LAST_MESSAGE_ID'];
1334 $relationId = $relationData['ID'];
1335
1336 $addResult = \Bitrix\Im\Model\RecentTable::add(
1337 [
1338 'USER_ID' => $userId,
1339 'ITEM_TYPE' => $itemTypes,
1340 'ITEM_ID' => $id,
1341 'ITEM_MID' => $messageId,
1342 'ITEM_RID' => $relationId,
1343 'ITEM_CID' => $chatId,
1344 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime()
1345 ]
1346 );
1347 if (!$addResult->isSuccess())
1348 {
1349 return false;
1350 }
1351
1352// self::show($id);
1353
1354 $element['USER_ID'] = $userId;
1355 $element['ITEM_TYPE'] = $itemTypes;
1356 $element['ITEM_ID'] = $id;
1357 }
1358
1359 if ($element['PINNED'] == $pin)
1360 {
1361 return true;
1362 }
1363
1364 $connection = Application::getConnection();
1365 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1366
1367 if ($pin === 'Y')
1368 {
1369 self::increasePinSortCost($userId);
1370 }
1371 else
1372 {
1373 $pinSort = $element['PIN_SORT'] ? (int)$element['PIN_SORT'] : null;
1374 self::decreasePinSortCost($userId, $pinSort);
1375 }
1376
1377
1378 \Bitrix\Im\Model\RecentTable::update(
1379 [
1380 'USER_ID' => $element['USER_ID'],
1381 'ITEM_TYPE' => $element['ITEM_TYPE'],
1382 'ITEM_ID' => $element['ITEM_ID'],
1383 ],
1384 [
1385 'PINNED' => $pin,
1386 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime(),
1387 'PIN_SORT' => ($pin === 'Y') ? 1 : null,
1388 ]
1389 );
1390
1391 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1392
1393 $chat = \Bitrix\Im\V2\Chat::getInstance($chatId);
1396 $userId,
1397 $chat
1398 );
1399
1400 self::clearCache($element['USER_ID']);
1401
1402 $pullInclude = \Bitrix\Main\Loader::includeModule("pull");
1403 if ($pullInclude)
1404 {
1406 $userId,
1407 [
1408 'module_id' => 'im',
1409 'command' => 'chatPin',
1410 'expiry' => 3600,
1411 'params' => [
1412 'dialogId' => $dialogId,
1413 'active' => $pin == 'Y'
1414 ],
1415 'extra' => \Bitrix\Im\Common::getPullExtra()
1416 ]
1417 );
1418 }
1419
1420 return true;
1421 }
1422
1423 private static function increasePinSortCost(int $userId): void
1424 {
1425 $caseField = new SqlExpression('?# + 1', 'PIN_SORT');
1426
1427 RecentTable::updateByFilter(
1428 [
1429 '=PINNED' => 'Y',
1430 '=USER_ID' => $userId,
1431 '>=PIN_SORT' => 1,
1432 ],
1433 ['PIN_SORT' => $caseField]
1434 );
1435 }
1436
1437 private static function decreasePinSortCost(int $userId, ?int $pinSort)
1438 {
1439 if (!isset($pinSort))
1440 {
1441 return;
1442 }
1443
1444 $caseField = new SqlExpression('?# - 1', 'PIN_SORT');
1445
1446 RecentTable::updateByFilter(
1447 [
1448 '=PINNED' => 'Y',
1449 '=USER_ID' => $userId,
1450 '>PIN_SORT' => $pinSort,
1451 ],
1452 ['PIN_SORT' => $caseField]
1453 );
1454 }
1455
1456 public static function sortPin(\Bitrix\Im\V2\Chat $chat, int $newPosition, int $userId): void
1457 {
1458 $connection = Application::getConnection();
1459 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1460
1461 $query = RecentTable::query()
1462 ->setSelect(['PIN_SORT'])
1463 ->setLimit(1)
1464 ->where('USER_ID', $userId)
1465 ->where('ITEM_CID', (int)$chat->getChatId())
1466 ->where('PINNED', 'Y')
1467 ->fetch()
1468 ;
1469
1470 if (!$query)
1471 {
1472 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1473
1474 return;
1475 }
1476 $currentCost = (int)$query['PIN_SORT'];
1477
1478 $query = RecentTable::query()
1479 ->setSelect(['PIN_SORT'])
1480 ->setOrder(['PIN_SORT'])
1481 ->setOffset($newPosition - 1)
1482 ->setLimit(1)
1483 ->where('PINNED', 'Y')
1484 ->where('USER_ID', $userId)
1485 ->fetch()
1486 ;
1487
1488 if (!$query)
1489 {
1490 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1491
1492 return;
1493 }
1494 $newCost = (int)$query['PIN_SORT'];
1495
1496 if ($currentCost === $newCost)
1497 {
1498 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1499
1500 return;
1501 }
1502
1503 if ($currentCost < $newCost)
1504 {
1505 $caseField = new SqlExpression(
1506 "CASE WHEN ?# = ?i THEN ?i WHEN ?# > ?i AND ?# <= ?i THEN ?# - 1 END",
1507 'PIN_SORT',
1508 $currentCost,
1509 $newCost,
1510 'PIN_SORT',
1511 $currentCost,
1512 'PIN_SORT',
1513 $newCost,
1514 'PIN_SORT'
1515 );
1516
1517 $filter = [
1518 '=PINNED' => 'Y',
1519 '=USER_ID' => $userId,
1520 '>=PIN_SORT' => $currentCost,
1521 '<=PIN_SORT' => $newCost,
1522 ];
1523 }
1524 else
1525 {
1526 $caseField = new SqlExpression(
1527 "CASE WHEN ?# = ?i THEN ?i WHEN ?# >= ?i AND ?# < ?i THEN ?# + 1 END",
1528 'PIN_SORT',
1529 $currentCost,
1530 $newCost,
1531 'PIN_SORT',
1532 $newCost,
1533 'PIN_SORT',
1534 $currentCost,
1535 'PIN_SORT'
1536 );
1537
1538 $filter = [
1539 '=PINNED' => 'Y',
1540 '=USER_ID' => $userId,
1541 '>=PIN_SORT' => $newCost,
1542 '<=PIN_SORT' => $currentCost,
1543 ];
1544 }
1545
1546 RecentTable::updateByFilter(
1547 $filter,
1548 ['PIN_SORT' => $caseField]
1549 );
1550
1551 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1552 }
1553
1554 public static function getPinLimit(): int
1555 {
1556 return self::PINNED_CHATS_LIMIT ?? 25;
1557 }
1558
1559 public static function updatePinSortCost(int $userId): void
1560 {
1561 $connection = Application::getConnection();
1562 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1563
1564 $caseField = new SqlExpression('?#', 'ITEM_MID');
1565
1566 RecentTable::updateByFilter(
1567 [
1568 '=PINNED' => 'Y',
1569 '=USER_ID' => $userId
1570 ],
1571 ['PIN_SORT' => $caseField]
1572 );
1573
1574 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1575 }
1576
1577 public static function updateByFilter(array $filter, array $fields): void
1578 {
1579 RecentTable::updateByFilter($filter, $fields);
1580 }
1581
1582 public static function raiseChat(\Bitrix\Im\V2\Chat $chat, RelationCollection $relations, ?DateTime $lastActivity = null): void
1583 {
1584 $userIds = $relations->getUserIds();
1585 if (empty($userIds))
1586 {
1587 return;
1588 }
1589 $message = new Message($chat->getLastMessageId());
1590 $dateMessage = $message->getDateCreate() ?? new DateTime();
1591 $dateCreate = $lastActivity ?? $dateMessage;
1592 $fields = [];
1593
1594 foreach ($relations as $relation)
1595 {
1596 $userId = $relation->getUserId();
1597 if ($userId)
1598 {
1599 $fields[] = [
1600 'USER_ID' => $userId,
1601 'ITEM_TYPE' => $chat->getType(),
1602 'ITEM_ID' => $chat->getId(),
1603 'ITEM_MID' => $chat->getLastMessageId(),
1604 'ITEM_CID' => $chat->getId(),
1605 'ITEM_RID' => $relation->getId(),
1606 'DATE_MESSAGE' => $dateMessage,
1607 'DATE_UPDATE' => $dateCreate,
1608 'DATE_LAST_ACTIVITY' => $dateCreate,
1609 ];
1610 }
1611 }
1612
1613 static::merge($fields, ['DATE_LAST_ACTIVITY' => $dateCreate, 'DATE_UPDATE' => $dateCreate]);
1615 new Sync\Event(Sync\Event::ADD_EVENT, Sync\Event::CHAT_ENTITY, $chat->getId()),
1616 $userIds,
1617 $chat
1618 );
1619
1620 static::sendPullRecentUpdate($chat, $userIds, $dateCreate);
1621 }
1622
1623 public static function sendPullRecentUpdate(\Bitrix\Im\V2\Chat $chat, array $userIds, ?DateTime $lastCommentDate): void
1624 {
1625 $messages = new MessagePopupItem([$chat->getLastMessageId()], true);
1626 $restAdapter = new RestAdapter($messages);
1627 $pull = $restAdapter->toRestFormat([
1628 'WITHOUT_OWN_REACTIONS' => true,
1629 'MESSAGE_ONLY_COMMON_FIELDS' => true,
1630 ]);
1631 $pull['chat'] = $chat->toPullFormat();
1632 $pull['lastActivityDate'] = $lastCommentDate;
1633 $pull['counterType'] = $chat->getCounterType()->value;
1634 $pull['recentConfig'] = $chat->getRecentConfig()->toPullFormat();
1635
1636 $event = [
1637 'module_id' => 'im',
1638 'command' => 'recentUpdate',
1639 'params' => $pull,
1640 'extra' => Common::getPullExtra()
1641 ];
1642 $events = PushService::getEventGroups($event, $userIds, $chat->getId());
1643
1644 foreach ($events as $event)
1645 {
1646 Event::add($event['users'], $event['event']);
1647 }
1648 }
1649
1650 public static function merge(array $fields, array $update): void
1651 {
1652 RecentTable::multiplyMerge($fields, $update, ['USER_ID', 'ITEM_TYPE', 'ITEM_ID']);
1653 }
1654
1655 public static function getUsersOutOfRecent(\Bitrix\Im\V2\Chat $chat): array
1656 {
1657 $relations = $chat->getRelations()->filterActive();
1658 $users = $relations->getUserIds();
1659 $usersAlreadyInRecentRows = RecentTable::query()
1660 ->setSelect(['USER_ID'])
1661 ->where('ITEM_CID', $chat->getId())
1662 ->whereIn('USER_ID', $users)
1663 ->fetchAll()
1664 ;
1665 foreach ($usersAlreadyInRecentRows as $row)
1666 {
1667 $userId = (int)$row['USER_ID'];
1668 unset($users[$userId]);
1669 }
1670
1671 return $users;
1672 }
1673
1674 public static function unread($dialogId, $unread, $userId = null, ?int $markedId = null, ?string $itemTypes = null)
1675 {
1677 if (!$userId)
1678 {
1679 return false;
1680 }
1681
1682 $unread = $unread === true? 'Y': 'N';
1683
1684 $element = self::getUnreadElement($userId, $itemTypes, $dialogId);
1685
1686 if (!$element)
1687 {
1688 return false;
1689 }
1690 if ($element['UNREAD'] === $unread && !isset($markedId))
1691 {
1692 return true;
1693 }
1694
1695 self::$unreadElementCache[$userId][$dialogId] = null;
1696
1697 $updatedFields = [
1698 'UNREAD' => $unread,
1699 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime(),
1700 ];
1701
1702 if ($unread === 'N')
1703 {
1704 $markedId = 0;
1705 }
1706 if (isset($markedId))
1707 {
1708 $updatedFields['MARKED_ID'] = $markedId;
1709 }
1710
1711 \Bitrix\Im\Model\RecentTable::update(
1712 [
1713 'USER_ID' => $element['USER_ID'],
1714 'ITEM_TYPE' => $element['ITEM_TYPE'],
1715 'ITEM_ID' => $element['ITEM_ID'],
1716 ],
1717 $updatedFields
1718 );
1719
1720 self::clearCache($element['USER_ID']);
1721 //\Bitrix\Im\Counter::clearCache($element['USER_ID']);
1722 CounterService::clearCache((int)$element['USER_ID']);
1723 $chatId = (int)$element['ITEM_CID'];
1724 $chat = \Bitrix\Im\V2\Chat::getInstance($chatId);
1727 $userId,
1728 $chat
1729 );
1730
1731 $pullInclude = \Bitrix\Main\Loader::includeModule("pull");
1732 if ($pullInclude)
1733 {
1734 $readService = new ReadService($userId);
1735 $counter = $readService->getCounterService()->getByChatWithOverflow($chatId);
1736
1738 $userId,
1739 [
1740 'module_id' => 'im',
1741 'command' => 'chatUnread',
1742 'expiry' => 3600,
1743 'params' => [
1744 'chatId' => $chatId,
1745 'dialogId' => $dialogId,
1746 'active' => $unread === 'Y',
1747 'muted' => $element['MUTED'] === 'Y',
1748 'counter' => $counter,
1749 'markedId' => $markedId ?? $element['MARKED_ID'],
1750 'lines' => $element['ITEM_TYPE'] === IM_MESSAGE_OPEN_LINE,
1751 'counterType' => $chat->getCounterType()->value,
1752 'recentConfig' => $chat->getRecentConfig()->toPullFormat(),
1753 ],
1754 'extra' => \Bitrix\Im\Common::getPullExtra()
1755 ]
1756 );
1757 }
1758
1759 return true;
1760 }
1761
1762 private static function getUnreadElement(int $userId, ?string $itemTypes, $dialogId): array|false
1763 {
1764 if (self::$unreadElementCache[$userId][$dialogId] !== null)
1765 {
1766 return self::$unreadElementCache[$userId][$dialogId];
1767 }
1768
1769 $id = $dialogId;
1770 if (mb_substr($dialogId, 0, 4) === 'chat')
1771 {
1772 if ($itemTypes === null)
1773 {
1774 $itemTypes = \Bitrix\Im\Chat::getTypes();
1775 }
1776
1777 $id = mb_substr($dialogId, 4);
1778 }
1779 else
1780 {
1781 $itemTypes = IM_MESSAGE_PRIVATE;
1782 }
1783
1784 self::$unreadElementCache[$userId][$dialogId] = \Bitrix\Im\Model\RecentTable::getList([
1785 'select' => [
1786 'USER_ID',
1787 'ITEM_TYPE',
1788 'ITEM_ID',
1789 'UNREAD',
1790 'MUTED' => 'RELATION.NOTIFY_BLOCK',
1791 'ITEM_CID',
1792 'MARKED_ID',
1793 'ENTITY_TYPE' => 'CHAT.ENTITY_TYPE',
1794 ],
1795 'filter' => [
1796 '=USER_ID' => $userId,
1797 '=ITEM_TYPE' => $itemTypes,
1798 '=ITEM_ID' => $id
1799 ]
1800 ])->fetch();
1801
1802 return self::$unreadElementCache[$userId][$dialogId];
1803 }
1804
1805 public static function readAll(int $userId): void
1806 {
1807 \Bitrix\Im\Model\RecentTable::updateByFilter(
1808 [
1809 '=UNREAD' => 'Y',
1810 '=USER_ID' => $userId,
1811 ],
1812 [
1813 'UNREAD' => 'N',
1814 'MARKED_ID' => 0,
1815 ]
1816 );
1817 }
1818
1819 public static function isUnread(int $userId, string $itemType, string $dialogId): bool
1820 {
1821 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1822 $element = \Bitrix\Im\Model\RecentTable::getList([
1823 'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'UNREAD', 'MUTED' => 'RELATION.NOTIFY_BLOCK', 'ITEM_CID'],
1824 'filter' => [
1825 '=USER_ID' => $userId,
1826 '=ITEM_TYPE' => $itemType,
1827 '=ITEM_ID' => $id
1828 ]
1829 ])->fetch();
1830 if (!$element)
1831 {
1832 return false;
1833 }
1834
1835 return ($element['UNREAD'] ?? 'N') === 'Y';
1836 }
1837
1838 public static function getUnread(string $itemType, string $dialogId): array
1839 {
1840 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1841 $queryResult = \Bitrix\Im\Model\RecentTable::getList([
1842 'select' => ['USER_ID', 'UNREAD',],
1843 'filter' => [
1844 '=ITEM_TYPE' => $itemType,
1845 '=ITEM_ID' => $id
1846 ]
1847 ])->fetchAll();
1848
1849 $result = [];
1850
1851 foreach ($queryResult as $row)
1852 {
1853 $result[(int)$row['USER_ID']] = ($row['UNREAD'] ?? 'N') === 'Y';
1854 }
1855
1856 return $result;
1857 }
1858
1859 public static function getMarkedId(int $userId, string $itemType, string $dialogId): int
1860 {
1861 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1862 $element = \Bitrix\Im\Model\RecentTable::getList([
1863 'select' => ['MARKED_ID'],
1864 'filter' => [
1865 '=USER_ID' => $userId,
1866 '=ITEM_TYPE' => $itemType,
1867 '=ITEM_ID' => $id
1868 ]
1869 ])->fetch();
1870 if (!$element)
1871 {
1872 return 0;
1873 }
1874
1875 return (int)($element['MARKED_ID'] ?? 0);
1876 }
1877
1878 public static function getMarkedIdByChatIds(int $userId, array $chatIds): array
1879 {
1880 if (empty($chatIds))
1881 {
1882 return [];
1883 }
1884
1885 $markedIdByChatIds = [];
1886
1887 $result = RecentTable::query()
1888 ->setSelect(['ITEM_CID', 'MARKED_ID'])
1889 ->where('USER_ID', $userId)
1890 ->whereIn('ITEM_CID', $chatIds)
1891 ->fetchAll()
1892 ;
1893
1894 foreach ($result as $row)
1895 {
1896 $markedIdByChatIds[(int)$row['ITEM_CID']] = (int)$row['MARKED_ID'];
1897 }
1898
1899 return $markedIdByChatIds;
1900 }
1901
1902 public static function hide($dialogId, $userId = null)
1903 {
1904 return \CIMContactList::DialogHide($dialogId, $userId);
1905 }
1906
1907 public static function show($dialogId, $options = [], $userId = null)
1908 {
1910 if (!$userId)
1911 {
1912 return false;
1913 }
1914
1915 $chatId = Dialog::getChatId($dialogId, $userId);
1916 if (Common::isChatId($dialogId))
1917 {
1918 $entityId = $chatId;
1919 }
1920 else
1921 {
1922 $entityId = (int)$dialogId;
1923 }
1924
1925 $relation = \Bitrix\Im\Model\RelationTable::getList([
1926 'select' => [
1927 'ID',
1928 'TYPE' => 'CHAT.TYPE',
1929 'LAST_MESSAGE_ID' => 'CHAT.LAST_MESSAGE_ID',
1930 'LAST_MESSAGE_DATE' => 'MESSAGE.DATE_CREATE'
1931 ],
1932 'filter' => [
1933 '=CHAT_ID' => $chatId,
1934 '=USER_ID' => $userId
1935 ],
1936 'runtime' => [
1937 new \Bitrix\Main\Entity\ReferenceField(
1938 'MESSAGE',
1939 '\Bitrix\Im\Model\MessageTable',
1940 ["=ref.ID" => "this.CHAT.LAST_MESSAGE_ID"],
1941 ["join_type" => "LEFT"]
1942 ),
1943 ]
1944 ])->fetch();
1945
1946 if ($relation)
1947 {
1948 $relationId = $relation['ID'];
1949 $entityType = $relation['TYPE'];
1950 $messageId = $relation['LAST_MESSAGE_ID'];
1951 $messageDate = $relation['LAST_MESSAGE_DATE'];
1952 }
1953 else if (
1954 isset($options['CHAT_DATA']['TYPE'])
1955 && isset($options['CHAT_DATA']['LAST_MESSAGE_ID'])
1956 )
1957 {
1958 $relationId = 0;
1959 $entityType = $options['CHAT_DATA']['TYPE'];
1960 $messageId = $options['CHAT_DATA']['LAST_MESSAGE_ID'];
1961 $messageDate = $options['CHAT_DATA']['LAST_MESSAGE_DATE'];
1962 }
1963 else
1964 {
1965 $chat = \Bitrix\Im\Model\ChatTable::getList([
1966 'select' => [
1967 'TYPE',
1968 'LAST_MESSAGE_ID',
1969 'LAST_MESSAGE_DATE' => 'MESSAGE.DATE_CREATE'
1970 ],
1971 'filter' => [
1972 '=ID' => $chatId,
1973 ],
1974 'runtime' => [
1975 new \Bitrix\Main\Entity\ReferenceField(
1976 'MESSAGE',
1977 '\Bitrix\Im\Model\MessageTable',
1978 ["=ref.ID" => "this.LAST_MESSAGE_ID"],
1979 ["join_type" => "LEFT"]
1980 ),
1981 ]
1982 ])->fetch();
1983 if (!$chat)
1984 {
1985 return false;
1986 }
1987
1988 $relationId = 0;
1989 $entityType = $chat['TYPE'];
1990 $messageId = $chat['LAST_MESSAGE_ID'];
1991 $messageDate = $chat['LAST_MESSAGE_DATE'];
1992 }
1993
1994 $sessionId = 0;
1995 if ($entityType == IM_MESSAGE_OPEN_LINE)
1996 {
1997 if (isset($options['SESSION_ID']))
1998 {
1999 $sessionId = (int)$options['SESSION_ID'];
2000 }
2001 else if (\Bitrix\Main\Loader::includeModule('imopenlines'))
2002 {
2003 $session = \Bitrix\ImOpenLines\Model\SessionTable::getList([
2004 'select' => ['ID'],
2005 'filter' => ['=CHAT_ID' => $chatId],
2006 'order' => ['ID' => 'DESC'],
2007 'limit' => 1,
2008 ])->fetch();
2009 if ($session)
2010 {
2011 $sessionId = $session['ID'];
2012 }
2013 }
2014 }
2015
2016 \CIMContactList::SetRecent($temp = [
2017 'ENTITY_TYPE' => $entityType,
2018 'ENTITY_ID' => $entityId,
2019 'MESSAGE_ID' => $messageId,
2020 'MESSAGE_DATE' => $messageDate,
2021 'CHAT_ID' => $chatId,
2022 'RELATION_ID' => $relationId,
2023 'SESSION_ID' => $sessionId,
2024 'USER_ID' => $userId,
2025 ]);
2026
2027 if (!\Bitrix\Main\Loader::includeModule("pull"))
2028 {
2029 return true;
2030 }
2031
2032 $data = \Bitrix\Im\Recent::getElement($entityType, $entityId, $userId, ['JSON' => true]);
2033 if ($data)
2034 {
2035 if (
2036 !isset($data['message'])
2037 && $entityType === Chat::TYPE_OPEN_LINE
2038 && class_exists('\Bitrix\ImOpenLines\Recent')
2039 )
2040 {
2041 $data = \Bitrix\ImOpenLines\Recent::getElement(
2042 (int)$entityId,
2043 (int)$userId,
2044 [
2045 'JSON' => true,
2046 'fakeCounter' => 1
2047 ]
2048 );
2049 }
2051 'module_id' => 'im',
2052 'command' => 'chatShow',
2053 'params' => $data,
2054 'extra' => \Bitrix\Im\Common::getPullExtra()
2055 ]);
2056 }
2057
2058 return true;
2059 }
2060
2061 public static function clearCache($userId = null)
2062 {
2063 $cache = Application::getInstance()->getCache();
2064 $cache->cleanDir('/bx/imc/recent'.($userId ? Common::getCacheUserPostfix($userId) : ''));
2065 }
2066
2067 protected static function prepareRows(array $rows, int $userId, bool $shortInfo = false): array
2068 {
2069 [$messageIds, $chatIds] = self::getKeysForFetchAdditionalEntities($rows);
2070 $counters = (new CounterService($userId))->getForEachChat($chatIds);
2071 $params = $shortInfo ? [] : self::getMessageParams($messageIds);
2072
2073 return self::fillRows($rows, $params, $counters, $userId);
2074 }
2075
2077 {
2078 $messageIds = [];
2079 $chatIds = [];
2080
2081 foreach ($rows as $row)
2082 {
2083 if (isset($row['ITEM_MID']) && $row['ITEM_MID'] > 0)
2084 {
2085 $messageIds[] = (int)$row['ITEM_MID'];
2086 }
2087
2088 if (isset($row['ITEM_CID']) && $row['ITEM_CID'] > 0)
2089 {
2090 $chatIds[] = (int)$row['ITEM_CID'];
2091 }
2092 }
2093
2094 return [$messageIds, $chatIds];
2095 }
2096
2097 protected static function getMessageParams(array $messageIds): array
2098 {
2099 $result = [];
2100 $fileIds = [];
2101
2102 if (empty($messageIds))
2103 {
2104 return $result;
2105 }
2106
2107 $rows = MessageParamTable::query()
2108 ->setSelect(['*'])
2109 ->whereIn('MESSAGE_ID', $messageIds)
2110 ->exec()
2111 ;
2112
2113 foreach ($rows as $item)
2114 {
2115 $messageId = (int)$item['MESSAGE_ID'];
2116 $paramName = $item['PARAM_NAME'];
2117
2118 if ($paramName === 'CODE')
2119 {
2120 $result[$messageId]['CODE'] = $item['PARAM_VALUE'];
2121 }
2122 elseif ($paramName === 'ATTACH')
2123 {
2124 $result[$messageId]['ATTACH'] = [
2125 'VALUE' => $item['PARAM_VALUE'],
2126 'JSON' => $item['PARAM_JSON'],
2127 ];
2128 }
2129 elseif ($paramName === 'URL_ID')
2130 {
2131 $result[$messageId]['ATTACH'] = [
2132 'VALUE' => "",
2133 'JSON' => true,
2134 ];
2135 }
2136 elseif ($paramName === 'FILE_ID')
2137 {
2138 $fileIds[$messageId] = (int)$item['PARAM_VALUE'];
2139 $result[$messageId]['MESSAGE_FILE'] = true;
2140 }
2141 }
2142
2143 return self::fillFiles($result, $fileIds);
2144 }
2145
2146 protected static function fillFiles(array $params, array $fileIds): array
2147 {
2148 if (empty($fileIds))
2149 {
2150 return $params;
2151 }
2152
2154 {
2155 return $params;
2156 }
2157
2159
2160 foreach ($fileIds as $messageId => $fileId)
2161 {
2162 $file = $files->getById($fileId);
2163 if (!$file instanceof FileItem)
2164 {
2165 $params[$messageId]['MESSAGE_FILE'] = false;
2166 }
2167 else
2168 {
2169 $params[$messageId]['MESSAGE_FILE'] = [
2170 'ID' => $file->getId(),
2171 'TYPE' => $file->getContentType(),
2172 'NAME' => $file->getDiskFile()->getName(),
2173 ];
2174 }
2175 }
2176
2177 return $params;
2178 }
2179
2180 protected static function fillRows(array $rows, array $params, array $counters, int $userId): array
2181 {
2182 foreach ($rows as $key => $row)
2183 {
2184 $chatId = (int)($row['ITEM_CID'] ?? 0);
2185 $messageId = (int)($row['ITEM_MID'] ?? 0);
2186 $boolStatus = $row['CHAT_LAST_MESSAGE_STATUS_BOOL'] ?? 'N';
2187
2188 $rows[$key]['COUNTER'] = $counters[$chatId] ?? 0;
2189 $rows[$key]['CHAT_LAST_MESSAGE_STATUS'] = $boolStatus === 'Y' ? \IM_MESSAGE_STATUS_DELIVERED : \IM_MESSAGE_STATUS_RECEIVED;
2190 $rows[$key]['MESSAGE_CODE'] = $rows[$key]['MESSAGE_CODE'] ?? $params[$messageId]['CODE'] ?? null;
2191 $rows[$key]['MESSAGE_ATTACH'] = $params[$messageId]['ATTACH']['VALUE'] ?? null;
2192 $rows[$key]['MESSAGE_ATTACH_JSON'] = $params[$messageId]['ATTACH']['JSON'] ?? null;
2193 $rows[$key]['MESSAGE_FILE'] = $params[$messageId]['MESSAGE_FILE'] ?? false;
2194 $rows[$key]['RELATION_USER_ID'] = $row['RELATION_ID'] ? $userId : null;
2195 }
2196
2197 return $rows;
2198 }
2199
2205 protected static function getRole(array $row): string
2206 {
2207 if (!isset($row['RELATION_USER_ID']))
2208 {
2209 return \Bitrix\Im\V2\Chat::ROLE_GUEST;
2210 }
2211 if ((int)$row['CHAT_AUTHOR_ID'] === (int)$row['RELATION_USER_ID'])
2212 {
2213 return \Bitrix\Im\V2\Chat::ROLE_OWNER;
2214 }
2215 if ($row['RELATION_IS_MANAGER'] === 'Y')
2216 {
2217 return \Bitrix\Im\V2\Chat::ROLE_MANAGER;
2218 }
2219
2220 return \Bitrix\Im\V2\Chat::ROLE_MEMBER;
2221 }
2222
2223 public static function isLimitError(): bool
2224 {
2225 return self::$limitError;
2226 }
2227}
$connection
Определения actionsdefinitions.php:38
if(! $messageFields||!isset($messageFields['message_id'])||!isset($messageFields['status'])||!CModule::IncludeModule("messageservice")) $messageId
Определения callback_ismscenter.php:26
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
const ENTITY_TYPE_VIDEOCONF
Определения alias.php:14
static getTypes()
Определения chat.php:40
const TYPE_OPEN_LINE
Определения chat.php:30
static getColor($code)
Определения color.php:121
static isChatId($id)
Определения common.php:58
static getPullExtra()
Определения common.php:127
static getCacheUserPostfix($id)
Определения common.php:53
static getUserId($userId=null)
Определения common.php:73
static getChatId($dialogId, $userId=null)
Определения dialog.php:93
static getBirthdayForToday()
Определения user.php:448
static readAll(int $userId)
Определения recent.php:1805
static show($dialogId, $options=[], $userId=null)
Определения recent.php:1907
static prepareRows(array $rows, int $userId, bool $shortInfo=false)
Определения recent.php:2067
static isUnread(int $userId, string $itemType, string $dialogId)
Определения recent.php:1819
static clearCache($userId=null)
Определения recent.php:2061
static getUnread(string $itemType, string $dialogId)
Определения recent.php:1838
static updateByFilter(array $filter, array $fields)
Определения recent.php:1577
static getMarkedIdByChatIds(int $userId, array $chatIds)
Определения recent.php:1878
static raiseChat(\Bitrix\Im\V2\Chat $chat, RelationCollection $relations, ?DateTime $lastActivity=null)
Определения recent.php:1582
static sortPin(\Bitrix\Im\V2\Chat $chat, int $newPosition, int $userId)
Определения recent.php:1456
static unread($dialogId, $unread, $userId=null, ?int $markedId=null, ?string $itemTypes=null)
Определения recent.php:1674
static updatePinSortCost(int $userId)
Определения recent.php:1559
static fillRows(array $rows, array $params, array $counters, int $userId)
Определения recent.php:2180
static getMarkedId(int $userId, string $itemType, string $dialogId)
Определения recent.php:1859
static getRole(array $row)
Определения recent.php:2205
static getList($userId=null, $options=[])
Определения recent.php:237
static hide($dialogId, $userId=null)
Определения recent.php:1902
static fillFiles(array $params, array $fileIds)
Определения recent.php:2146
static getUsersOutOfRecent(\Bitrix\Im\V2\Chat $chat)
Определения recent.php:1655
static getMessageParams(array $messageIds)
Определения recent.php:2097
static merge(array $fields, array $update)
Определения recent.php:1650
static getKeysForFetchAdditionalEntities(array $rows)
Определения recent.php:2076
static getElement($itemType, $itemId, $userId=null, $options=[])
Определения recent.php:564
static getPinLimit()
Определения recent.php:1554
static sendPullRecentUpdate(\Bitrix\Im\V2\Chat $chat, array $userIds, ?DateTime $lastCommentDate)
Определения recent.php:1623
static isLimitError()
Определения recent.php:2223
static pin($dialogId, $pin, $userId=null)
Определения recent.php:1258
static isLegacyChatActivated($userId=false)
Определения settings.php:88
static parse($text, $params=Array())
Определения text.php:28
static populateUserBbCode(string $text)
Определения text.php:350
static removeBbCodes($text, $withFile=false, $attachValue=false)
Определения text.php:299
static initByDiskFilesIds(array $diskFilesIds, ?int $chatId=null)
Определения FileCollection.php:57
static getRoleGetListFilter(array $ormParams, ActionGroup $action, string $relationTableAlias, string $chatTableAlias)
Определения Filter.php:16
const ADD_EVENT
Определения Event.php:12
const CHAT_ENTITY
Определения Event.php:14
static getInstance()
Определения Logger.php:37
static getInstance()
Определения application.php:98
static clearCache($moduleId)
Определения option.php:464
static getInstance()
Определения servicelocator.php:33
static includeModule($moduleName)
Определения loader.php:67
getDataClass()
Определения entity.php:696
static sortByColumn(array &$array, $columns, $callbacks='', $defaultValueIfNotSetValue=null, $preserveKeys=false)
Определения collection.php:24
static add($recipient, array $parameters, $channelType=\CPullChannel::TYPE_PRIVATE)
Определения event.php:22
static GetSetting($type, $value, $userId=false)
Определения im_settings.php:223
const SETTINGS
Определения im_settings.php:12
$options
Определения commerceml2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
const IM_MESSAGE_STATUS_DELIVERED
Определения include.php:46
const IM_MESSAGE_CHAT
Определения include.php:23
const IM_MESSAGE_OPEN
Определения include.php:24
const IM_MESSAGE_STATUS_RECEIVED
Определения include.php:44
const IM_MESSAGE_PRIVATE
Определения include.php:22
const IM_MESSAGE_OPEN_LINE
Определения include.php:26
Определения Uuid.php:3
$value
Определения Param.php:39
toRestFormat()
Определения Param.php:305
Определения ChatsSync.php:3
Определения ActionUuid.php:3
Определения arrayresult.php:2
Определения ufield.php:9
Определения collection.php:2
$user
Определения mysql_to_pgsql.php:33
$files
Определения mysql_to_pgsql.php:30
$entityId
Определения payment.php:4
$message
Определения payment.php:8
$counter
Определения options.php:5
$event
Определения prolog_after.php:141
return false
Определения prolog_main_admin.php:185
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$config
Определения quickway.php:69
if(empty($signedUserToken)) $key
Определения quickway.php:257
$text
Определения template_pdf.php:79
$messages
Определения template.php:8
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$counters
Определения options.php:100
$rows
Определения options.php:264
$fields
Определения yandex_run.php:501