Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
recent.php
1<?php
2
3namespace Bitrix\Im;
4
17use Bitrix\Imbot\Bot\CopilotChatBot;
22
23Loc::loadMessages(__FILE__);
24
25class Recent
26{
27 private const PINNED_CHATS_LIMIT = 25;
28
29 static private bool $limitError = false;
30
31 public static function get($userId = null, $options = [])
32 {
33 $onlyOpenlinesOption = $options['ONLY_OPENLINES'] ?? null;
34 $onlyCopilotOption = $options['ONLY_COPILOT'] ?? null;
35 $skipOpenlinesOption = $options['SKIP_OPENLINES'] ?? null;
36 $skipChat = $options['SKIP_CHAT'] ?? null;
37 $skipDialog = $options['SKIP_DIALOG'] ?? null;
38
39 if (isset($options['FORCE_OPENLINES']) && $options['FORCE_OPENLINES'] === 'Y')
40 {
41 $forceOpenlines = 'Y';
42 }
43 else
44 {
45 $forceOpenlines = 'N';
46 }
47
48 $userId = \Bitrix\Im\Common::getUserId($userId);
49 if (!$userId)
50 {
51 return false;
52 }
53
54 $showOpenlines = (
55 \Bitrix\Main\Loader::includeModule('imopenlines')
56 && ($onlyOpenlinesOption === 'Y' || $skipOpenlinesOption !== 'Y')
57 );
58
59 if (
60 $showOpenlines
61 && $forceOpenlines !== 'Y'
62 && class_exists('\Bitrix\ImOpenLines\Recent')
63 )
64 {
65 return \Bitrix\ImOpenLines\Recent::getRecent($userId, $options);
66 }
67
68 $generalChatId = \CIMChat::GetGeneralChatId();
69
70 $ormParams = self::getOrmParams([
71 'USER_ID' => $userId,
72 'SHOW_OPENLINES' => $showOpenlines,
73 'WITHOUT_COMMON_USERS' => true,
74 'CHAT_IDS' => $options['CHAT_IDS'] ?? null,
75 ]);
76
77 $lastSyncDateOption = $options['LAST_SYNC_DATE'] ?? null;
78 if ($lastSyncDateOption)
79 {
80 $maxLimit = (new \Bitrix\Main\Type\DateTime())->add('-7 days');
81 if ($maxLimit > $options['LAST_SYNC_DATE'])
82 {
83 $options['LAST_SYNC_DATE'] = $maxLimit;
84 }
85 $ormParams['filter']['>=DATE_UPDATE'] = $options['LAST_SYNC_DATE'];
86 }
87 else if ($options['ONLY_OPENLINES'] !== 'Y')
88 {
89 $ormParams['filter']['>=DATE_UPDATE'] = (new \Bitrix\Main\Type\DateTime())->add('-30 days');
90 }
91
92 $skipTypes = [];
93 if ($onlyCopilotOption === 'Y')
94 {
95 $ormParams['filter'][] = [
96 '=ITEM_TYPE' => \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT
97 ];
98 }
99 elseif ($onlyOpenlinesOption === 'Y')
100 {
101 $ormParams['filter'][] = [
102 '=ITEM_TYPE' => IM_MESSAGE_OPEN_LINE
103 ];
104 }
105 else
106 {
107 $skipTypes[] = \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT;
108 if ($options['SKIP_OPENLINES'] === 'Y')
109 {
110 $skipTypes[] = IM_MESSAGE_OPEN_LINE;
111 }
112 if ($skipChat === 'Y')
113 {
114 $skipTypes[] = IM_MESSAGE_OPEN;
115 $skipTypes[] = IM_MESSAGE_CHAT;
116 }
117 if ($skipDialog === 'Y')
118 {
119 $skipTypes[] = IM_MESSAGE_PRIVATE;
120 }
121 if (!empty($skipTypes))
122 {
123 $ormParams['filter'][] = [
124 '!@ITEM_TYPE' => $skipTypes
125 ];
126 }
127 }
128
129 if (!isset($options['LAST_SYNC_DATE']))
130 {
131 if (isset($options['OFFSET']))
132 {
133 $ormParams['offset'] = $options['OFFSET'];
134 }
135 if (isset($options['LIMIT']))
136 {
137 $ormParams['limit'] = $options['LIMIT'];
138 }
139 if (isset($options['ORDER']))
140 {
141 $ormParams['order'] = $options['ORDER'];
142 }
143 }
144
145 $result = [];
146 $orm = \Bitrix\Im\Model\RecentTable::getList($ormParams);
147 $rows = $orm->fetchAll();
148 $rows = self::prepareRows($rows, $userId);
149 foreach ($rows as $row)
150 {
151 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
152 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
153
154 if ($isUser)
155 {
156 if (isset($result[$id]) && !$row['ITEM_MID'])
157 {
158 continue;
159 }
160 }
161 else if (isset($result[$id]))
162 {
163 continue;
164 }
165
166 $item = self::formatRow($row, [
167 'GENERAL_CHAT_ID' => $generalChatId,
168 'GET_ORIGINAL_TEXT' => $options['GET_ORIGINAL_TEXT'] ?? null,
169 ]);
170 if (!$item)
171 {
172 continue;
173 }
174
175 $result[$id] = $item;
176 }
177 $result = array_values($result);
178
179 if (
180 $showOpenlines
181 && !$options['ONLY_OPENLINES']
182 && class_exists('\Bitrix\ImOpenLines\Recent')
183 )
184 {
185 $options['ONLY_IN_QUEUE'] = true;
186 $chatsInQueue = \Bitrix\ImOpenLines\Recent::getRecent($userId, $options);
187 $result = array_merge($result, $chatsInQueue);
188 }
189
190 \Bitrix\Main\Type\Collection::sortByColumn(
191 $result,
192 ['PINNED' => SORT_DESC, 'MESSAGE' => SORT_DESC, 'ID' => SORT_DESC],
193 [
194 'ID' => function($row) {
195 return $row;
196 },
197 'MESSAGE' => function($row) {
198 return $row['DATE'] instanceof \Bitrix\Main\Type\DateTime ? $row['DATE']->getTimeStamp() : 0;
199 },
200 ]
201 );
202
203 if ($options['JSON'])
204 {
205 foreach ($result as $index => $item)
206 {
207 $result[$index] = self::jsonRow($item);
208 }
209 }
210
211 return $result;
212 }
213
214 public static function getList($userId = null, $options = [])
215 {
216 $userId = \Bitrix\Im\Common::getUserId($userId);
217 if (!$userId)
218 {
219 return false;
220 }
221
222 $generalChatId = \CIMChat::GetGeneralChatId();
223
224 $viewCommonUsers = (bool)\CIMSettings::GetSetting(\CIMSettings::SETTINGS, 'viewCommonUsers');
225
226 $onlyOpenlinesOption = $options['ONLY_OPENLINES'] ?? null;
227 $onlyCopilotOption = $options['ONLY_COPILOT'] ?? null;
228 $skipChatOption = $options['SKIP_CHAT'] ?? null;
229 $skipDialogOption = $options['SKIP_DIALOG'] ?? null;
230 $lastMessageDateOption = $options['LAST_MESSAGE_DATE'] ?? null;
231 $withoutCommonUsers = !$viewCommonUsers || $onlyOpenlinesOption === 'Y';
232 $unreadOnly = isset($options['UNREAD_ONLY']) && $options['UNREAD_ONLY'] === 'Y';
233 $shortInfo = isset($options['SHORT_INFO']) && $options['SHORT_INFO'] === 'Y';
234
235 $showOpenlines = (
236 \Bitrix\Main\Loader::includeModule('imopenlines')
237 && (
238 $onlyOpenlinesOption === 'Y'
239 || $options['SKIP_OPENLINES'] !== 'Y'
240 )
241 );
242
243 $ormParams = self::getOrmParams([
244 'USER_ID' => $userId,
245 'SHOW_OPENLINES' => $showOpenlines,
246 'WITHOUT_COMMON_USERS' => $withoutCommonUsers,
247 'UNREAD_ONLY' => $unreadOnly,
248 'SHORT_INFO' => $shortInfo,
249 ]);
250
251 if ($onlyCopilotOption === 'Y')
252 {
253 $ormParams['filter'][] = [
254 '=ITEM_TYPE' => \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT
255 ];
256 }
257 elseif ($onlyOpenlinesOption === 'Y')
258 {
259 $ormParams['filter'][] = [
260 '=ITEM_TYPE' => IM_MESSAGE_OPEN_LINE
261 ];
262 }
263 else
264 {
265 $skipTypes = [];
266 $skipTypes[] = \Bitrix\Im\V2\Chat::IM_TYPE_COPILOT;
267 if ($options['SKIP_OPENLINES'] === 'Y')
268 {
269 $skipTypes[] = IM_MESSAGE_OPEN_LINE;
270 }
271 if ($skipChatOption === 'Y')
272 {
273 $skipTypes[] = IM_MESSAGE_OPEN;
274 $skipTypes[] = IM_MESSAGE_CHAT;
275 }
276 if ($skipDialogOption === 'Y')
277 {
278 $skipTypes[] = IM_MESSAGE_PRIVATE;
279 }
280 if (!empty($skipTypes))
281 {
282 $ormParams['filter'][] = [
283 '!@ITEM_TYPE' => $skipTypes
284 ];
285 }
286 }
287
288 if ($lastMessageDateOption instanceof \Bitrix\Main\Type\DateTime)
289 {
290 $ormParams['filter']['<=DATE_MESSAGE'] = $lastMessageDateOption;
291 }
292 else if (isset($options['OFFSET']))
293 {
294 $ormParams['offset'] = $options['OFFSET'];
295 }
296
297 if (isset($options['LIMIT']))
298 {
299 $ormParams['limit'] = (int)$options['LIMIT'];
300 }
301 else
302 {
303 $ormParams['limit'] = 50;
304 }
305
306 $sortOption = (new UserConfiguration((int)$userId))->getGeneralSettings()['pinnedChatSort'];
307 if ($sortOption === 'byCost')
308 {
309 $ormParams['order'] = [
310 'PINNED' => 'DESC',
311 'PIN_SORT' => 'ASC',
312 'DATE_MESSAGE' => 'DESC',
313 ];
314 }
315 else
316 {
317 $ormParams['order'] = [
318 'PINNED' => 'DESC',
319 'DATE_MESSAGE' => 'DESC',
320 ];
321 }
322
323 $orm = \Bitrix\Im\Model\RecentTable::getList($ormParams);
324
325 $counter = 0;
326 $result = [];
327 $files = [];
328
329 $rows = $orm->fetchAll();
330 $rows = self::prepareRows($rows, $userId);
331 foreach ($rows as $row)
332 {
333 $counter++;
334 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
335 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
336
337 if ($isUser)
338 {
339 if (isset($result[$id]) && !$row['ITEM_MID'])
340 {
341 continue;
342 }
343 }
344 else if (isset($result[$id]))
345 {
346 continue;
347 }
348
349 $item = self::formatRow($row, [
350 'GENERAL_CHAT_ID' => $generalChatId,
351 'WITHOUT_COMMON_USERS' => $withoutCommonUsers,
352 'GET_ORIGINAL_TEXT' => $options['GET_ORIGINAL_TEXT'] ?? null,
353 'SHORT_INFO' => $shortInfo,
354 ]);
355 if (!$item)
356 {
357 continue;
358 }
359
360 $result[$id] = $item;
361 }
362 if ($showOpenlines && !$onlyCopilotOption && Loader::includeModule('imopenlines'))
363 {
364 if (!isset($options['SKIP_UNDISTRIBUTED_OPENLINES']) || $options['SKIP_UNDISTRIBUTED_OPENLINES'] !== 'Y')
365 {
366 $recentOpenLines = \Bitrix\ImOpenLines\Recent::getRecent($userId, ['ONLY_IN_QUEUE' => true]);
367
368 if (is_array($recentOpenLines))
369 {
370 $result = array_merge($result, $recentOpenLines);
371 }
372 }
373 }
374
375 $result = array_values($result);
376
377 if ($options['JSON'])
378 {
379 foreach ($result as $index => $item)
380 {
381 $result[$index] = self::jsonRow($item);
382 }
383
384 $objectToReturn = [
385 'items' => $result,
386 'hasMorePages' => $ormParams['limit'] == $counter, // TODO remove this later
387 'hasMore' => $ormParams['limit'] == $counter
388 ];
389
390 if (!isset($options['LAST_MESSAGE_DATE']))
391 {
392 $objectToReturn['birthdayList'] = \Bitrix\Im\Integration\Intranet\User::getBirthdayForToday();
393 }
394
395 return $objectToReturn;
396 }
397
398 return [
399 'ITEMS' => $result,
400 'HAS_MORE_PAGES' => $ormParams['limit'] == $counter, // TODO remove this later
401 'HAS_MORE' => $ormParams['limit'] == $counter
402 ];
403 }
404
405 public static function getElement($itemType, $itemId, $userId = null, $options = [])
406 {
407 $userId = \Bitrix\Im\Common::getUserId($userId);
408 if (!$userId)
409 {
410 return false;
411 }
412
413 $generalChatId = \CIMChat::GetGeneralChatId();
414
415 $ormParams = self::getOrmParams([
416 'USER_ID' => $userId,
417 'SHOW_OPENLINES' => $itemType === IM_MESSAGE_OPEN_LINE,
418 'WITHOUT_COMMON_USERS' => true,
419 ]);
420
421 $ormParams['filter']['=ITEM_TYPE'] = $itemType;
422 $ormParams['filter']['=ITEM_ID'] = $itemId;
423
424 $orm = \Bitrix\Im\Model\RecentTable::getList([
425 'select' => $ormParams['select'],
426 'filter' => $ormParams['filter'],
427 'runtime' => $ormParams['runtime'],
428 ]);
429
430 $result = null;
431 $rows = $orm->fetchAll();
432 $rows = self::prepareRows($rows, $userId);
433 foreach ($rows as $row)
434 {
435 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
436 if ($isUser)
437 {
438 if ($result && !$row['ITEM_MID'])
439 {
440 continue;
441 }
442 }
443 else if ($result)
444 {
445 continue;
446 }
447
448 $item = self::formatRow($row, [
449 'GENERAL_CHAT_ID' => $generalChatId,
450 ]);
451 if (!$item)
452 {
453 continue;
454 }
455
456 $result = $item;
457 }
458 $result = self::prepareRows([$result], $userId)[0];
459
460 if ($options['JSON'])
461 {
462 $result = self::jsonRow($result);
463 }
464
465 return $result;
466 }
467
468 private static function getOrmParams($params)
469 {
470 $userId = (int)$params['USER_ID'];
471 $showOpenlines = \Bitrix\Main\Loader::includeModule('imopenlines') && $params['SHOW_OPENLINES'] !== false;
472 $isIntranet = \Bitrix\Main\Loader::includeModule('intranet') && \Bitrix\Intranet\Util::isIntranetUser($userId);
473 $withoutCommonUsers = $params['WITHOUT_COMMON_USERS'] === true || !$isIntranet;
474 $unreadOnly = isset($params['UNREAD_ONLY']) && $params['UNREAD_ONLY'] === true;
475 $shortInfo = isset($params['SHORT_INFO']) && $params['SHORT_INFO'] === true;
476 $chatIds = $params['CHAT_IDS'] ?? null;
477
478 $shortInfoFields = [
479 '*',
480 'RELATION_USER_ID' => 'RELATION.USER_ID',
481 'RELATION_NOTIFY_BLOCK' => 'RELATION.NOTIFY_BLOCK',
482 'RELATION_IS_MANAGER' => 'RELATION.MANAGER',
483 'CHAT_ID' => 'CHAT.ID',
484 'CHAT_TITLE' => 'CHAT.TITLE',
485 'CHAT_TYPE' => 'CHAT.TYPE',
486 'CHAT_AVATAR' => 'CHAT.AVATAR',
487 'CHAT_AUTHOR_ID' => 'CHAT.AUTHOR_ID',
488 'CHAT_EXTRANET' => 'CHAT.EXTRANET',
489 'CHAT_COLOR' => 'CHAT.COLOR',
490 'CHAT_ENTITY_TYPE' => 'CHAT.ENTITY_TYPE',
491 'CHAT_ENTITY_ID' => 'CHAT.ENTITY_ID',
492 'CHAT_ENTITY_DATA_1' => 'CHAT.ENTITY_DATA_1',
493 'CHAT_ENTITY_DATA_2' => 'CHAT.ENTITY_DATA_2',
494 'CHAT_ENTITY_DATA_3' => 'CHAT.ENTITY_DATA_3',
495 'CHAT_DATE_CREATE' => 'CHAT.DATE_CREATE',
496 'CHAT_USER_COUNT' => 'CHAT.USER_COUNT',
497 'MESSAGE_CODE' => 'CODE.PARAM_VALUE',
498 'USER_LAST_ACTIVITY_DATE' => 'USER.LAST_ACTIVITY_DATE',
499 'MESSAGE_DATE' => 'MESSAGE.DATE_CREATE',
500 ];
501
502 $additionalInfoFields = [
503 'MESSAGE_ID' => 'MESSAGE.ID',
504 'MESSAGE_AUTHOR_ID' => 'MESSAGE.AUTHOR_ID',
505 'MESSAGE_TEXT' => 'MESSAGE.MESSAGE',
506 'MESSAGE_FILE' => 'FILE.PARAM_VALUE',
507 'MESSAGE_ATTACH' => 'ATTACH.PARAM_VALUE',
508 'MESSAGE_ATTACH_JSON' => 'ATTACH.PARAM_JSON',
509 'MESSAGE_USER_LAST_ACTIVITY_DATE' => 'MESSAGE.AUTHOR.LAST_ACTIVITY_DATE',
510 /*'MESSAGE_USER_IDLE' => 'MESSAGE.STATUS.IDLE',
511 'MESSAGE_USER_MOBILE_LAST_DATE' => 'MESSAGE.STATUS.MOBILE_LAST_DATE',
512 'MESSAGE_USER_DESKTOP_LAST_DATE' => 'MESSAGE.STATUS.DESKTOP_LAST_DATE',*/
513 'USER_EMAIL' => 'USER.EMAIL',
514 /*'USER_IDLE' => 'STATUS.IDLE',
515 'USER_MOBILE_LAST_DATE' => 'STATUS.MOBILE_LAST_DATE',
516 'USER_DESKTOP_LAST_DATE' => 'STATUS.DESKTOP_LAST_DATE',*/
517 'MESSAGE_UUID_VALUE' => 'MESSAGE_UUID.UUID',
518 'HAS_REMINDER' => 'HAS_REMINDER',
519 'CHAT_MANAGE_USERS_ADD' => 'CHAT.MANAGE_USERS_ADD',
520 'CHAT_MANAGE_USERS_DELETE' => 'CHAT.MANAGE_USERS_DELETE',
521 'CHAT_MANAGE_UI' => 'CHAT.MANAGE_UI',
522 'CHAT_MANAGE_SETTINGS' => 'CHAT.MANAGE_SETTINGS',
523 'CHAT_CAN_POST' => 'CHAT.CAN_POST',
524 ];
525
526 $shortRuntime = [
527 new \Bitrix\Main\Entity\ReferenceField(
528 'CODE',
529 '\Bitrix\Im\Model\MessageParamTable',
530 [
531 "=ref.MESSAGE_ID" => "this.ITEM_MID",
532 "=ref.PARAM_NAME" => new \Bitrix\Main\DB\SqlExpression("?s", "CODE")
533 ],
534 ["join_type" => "LEFT"]
535 ),
536 new \Bitrix\Main\Entity\ReferenceField(
537 'USER',
538 '\Bitrix\Main\UserTable',
539 array("=this.ITEM_TYPE" => new \Bitrix\Main\DB\SqlExpression("?s", IM_MESSAGE_PRIVATE), "=ref.ID" => "this.ITEM_ID"),
540 array("join_type"=>"LEFT")
541 ),
542 ];
543
544 $reminderTable = LinkReminderTable::getTableName();
545 $unreadTable = MessageUnreadTable::getTableName();
546
547 $additionalRuntime = [
548 new \Bitrix\Main\Entity\ReferenceField(
549 'ATTACH',
550 '\Bitrix\Im\Model\MessageParamTable',
551 [
552 "=ref.MESSAGE_ID" => "this.ITEM_MID",
553 "=ref.PARAM_NAME" => new \Bitrix\Main\DB\SqlExpression("?s", "ATTACH")
554 ],
555 ["join_type" => "LEFT"]
556 ),
557 new \Bitrix\Main\Entity\ReferenceField(
558 'FILE',
559 '\Bitrix\Im\Model\MessageParamTable',
560 [
561 "=ref.MESSAGE_ID" => "this.ITEM_MID",
562 "=ref.PARAM_NAME" => new \Bitrix\Main\DB\SqlExpression("?s", "FILE_ID")
563 ],
564 ["join_type" => "LEFT"]
565 ),
566 /*new \Bitrix\Main\Entity\ReferenceField(
567 'STATUS',
568 '\Bitrix\Im\Model\StatusTable',
569 array("=this.ITEM_TYPE" => new \Bitrix\Main\DB\SqlExpression("?s", IM_MESSAGE_PRIVATE), "=ref.USER_ID" => "this.ITEM_ID"),
570 array("join_type"=>"LEFT")
571 ),*/
572 new ExpressionField(
573 'HAS_REMINDER',
574 "CASE WHEN EXISTS (
575 SELECT 1
576 FROM {$reminderTable}
577 WHERE CHAT_ID = %s AND AUTHOR_ID = %s AND IS_REMINDED = 'Y'
578 ) THEN 'Y' ELSE 'N' END",
579 ['ITEM_CID', 'USER_ID'],
580 ['data_type' => 'boolean', 'values' => ['N', 'Y']]
581 ),
582 new ExpressionField(
583 'HAS_UNREAD_MESSAGE',
584 "EXISTS(SELECT 1 FROM {$unreadTable} WHERE CHAT_ID = %s AND USER_ID = %s)",
585 ['ITEM_CID', 'USER_ID']
586 )
587 ];
588
589 $select = $shortInfo ? $shortInfoFields : array_merge($shortInfoFields, $additionalInfoFields);
590 $runtime = $shortInfo ? $shortRuntime : array_merge($shortRuntime, $additionalRuntime);
591
592 if (!$withoutCommonUsers)
593 {
594 $select['INVITATION_ORIGINATOR_ID'] = 'INVITATION.ORIGINATOR_ID';
595 }
596 if ($showOpenlines)
597 {
598 $select['LINES_ID'] = 'LINES.ID';
599 $select['LINES_STATUS'] = 'LINES.STATUS';
600 $select['LINES_DATE_CREATE'] = 'LINES.DATE_CREATE';
601 }
602
603 if (!$withoutCommonUsers)
604 {
605 $runtime[] = new \Bitrix\Main\Entity\ReferenceField(
606 'INVITATION',
607 '\Bitrix\Intranet\Internals\InvitationTable',
608 array("=this.ITEM_TYPE" => new \Bitrix\Main\DB\SqlExpression("?s", IM_MESSAGE_PRIVATE), "=ref.USER_ID" => "this.ITEM_ID"),
609 array("join_type"=>"LEFT")
610 );
611 }
612 if ($showOpenlines)
613 {
614 $runtime[] = new \Bitrix\Main\Entity\ReferenceField(
615 'LINES',
616 '\Bitrix\ImOpenlines\Model\SessionTable',
617 [">this.ITEM_OLID" => new \Bitrix\Main\DB\SqlExpression("?i", 0), "=ref.ID" => "this.ITEM_OLID"],
618 ["join_type" => "LEFT"]
619 );
620 }
621
622 if ($withoutCommonUsers)
623 {
624 $filter = ['=USER_ID' => $userId];
625 }
626 else
627 {
628 $filter = ['@USER_ID' => [$userId, 0]];
629 }
630
631 if ($unreadOnly)
632 {
633 $filter[] = [
634 'LOGIC' => 'OR',
635 ['==HAS_UNREAD_MESSAGE' => true],
636 ['=UNREAD' => true],
637 ];
638 }
639
640 if ($chatIds)
641 {
642 $filter['@ITEM_CID'] = $chatIds; // todo: add index
643 }
644
645 return [
646 'select' => $select,
647 'filter' => $filter,
648 'runtime' => $runtime,
649 ];
650 }
651
652 private static function formatRow($row, $options = []): ?array
653 {
654 $generalChatId = (int)$options['GENERAL_CHAT_ID'];
655 $withoutCommonUsers = isset($options['WITHOUT_COMMON_USERS']) && $options['WITHOUT_COMMON_USERS'] === true;
656 $shortInfo = isset($options['SHORT_INFO']) && $options['SHORT_INFO'];
657
658 $chatOwner = $row['CHAT_OWNER'] ?? null;
659
660 $isUser = $row['ITEM_TYPE'] == IM_MESSAGE_PRIVATE;
661 $id = $isUser? (int)$row['ITEM_ID']: 'chat'.$row['ITEM_ID'];
662 $row['MESSAGE_ID'] ??= null;
663
664 if (!$isUser && ((!$row['MESSAGE_ID'] && !$shortInfo) || !$row['RELATION_USER_ID'] || !$row['CHAT_ID']))
665 {
666 return null;
667 }
668
669 if ($row['ITEM_MID'] > 0 && $row['MESSAGE_ID'] > 0)
670 {
671 $attach = false;
672 if ($row['MESSAGE_ATTACH'] || $row["MESSAGE_ATTACH_JSON"])
673 {
674 if (preg_match('/^(\d+)$/', $row['MESSAGE_ATTACH']))
675 {
676 $attach = true;
677 }
678 else if ($row['MESSAGE_ATTACH'] === \CIMMessageParamAttach::FIRST_MESSAGE)
679 {
680 try
681 {
682 $value = \Bitrix\Main\Web\Json::decode($row["MESSAGE_ATTACH_JSON"]);
683 $attachRestored = \CIMMessageParamAttach::PrepareAttach($value);
684 $attach = $attachRestored['DESCRIPTION'];
685 }
686 catch (\Bitrix\Main\SystemException $e)
687 {
688 $attach = true;
689 }
690 }
691 else if (!empty($row['MESSAGE_ATTACH']))
692 {
693 $attach = $row['MESSAGE_ATTACH'];
694 }
695 else
696 {
697 $attach = true;
698 }
699 }
700
701 $text = $row['MESSAGE_TEXT'] ?? '';
702
703 $getOriginalTextOption = $options['GET_ORIGINAL_TEXT'] ?? null;
704 if ($getOriginalTextOption === 'Y')
705 {
706 $text = Text::populateUserBbCode($text);
707 }
708 else
709 {
710 $text = Text::removeBbCodes(
711 str_replace("\n", " ", $text),
712 $row['MESSAGE_FILE'] > 0,
713 $attach
714 );
715 }
716
717 $message = [
718 'ID' => (int)$row['ITEM_MID'],
719 'TEXT' => $text,
720 'FILE' => $row['MESSAGE_FILE'],
721 'AUTHOR_ID' => (int)$row['MESSAGE_AUTHOR_ID'],
722 'ATTACH' => $attach,
723 'DATE' => $row['MESSAGE_DATE']?: $row['DATE_UPDATE'],
724 'STATUS' => $row['CHAT_LAST_MESSAGE_STATUS'],
725 'UUID' => $row['MESSAGE_UUID_VALUE'],
726 ];
727 }
728 else
729 {
730 $row['MESSAGE_DATE'] ??= null;
731 $message = [
732 'ID' => 0,
733 'TEXT' => "",
734 'FILE' => false,
735 'AUTHOR_ID' => 0,
736 'ATTACH' => false,
737 'DATE' => $row['MESSAGE_DATE']?: $row['DATE_UPDATE'],
738 'STATUS' => $row['CHAT_LAST_MESSAGE_STATUS'],
739 ];
740 }
741
742 $item = [
743 'ID' => $id,
744 'CHAT_ID' => (int)$row['CHAT_ID'],
745 'TYPE' => $isUser ? 'user' : 'chat',
746 'AVATAR' => [],
747 'TITLE' => [],
748 'MESSAGE' => $message,
749 'COUNTER' => (int)$row['COUNTER'],
750 'PINNED' => $row['PINNED'] === 'Y',
751 'UNREAD' => $row['UNREAD'] === 'Y',
752 'HAS_REMINDER' => isset($row['HAS_REMINDER']) && $row['HAS_REMINDER'] === 'Y',
753 'DATE_UPDATE' => $row['DATE_UPDATE']
754 ];
755
756 if ($isUser)
757 {
758 if (
759 $withoutCommonUsers
760 && ($row['USER_ID'] == 0 || $row['MESSAGE_CODE'] === 'USER_JOIN')
761 )
762 {
763 return null;
764 }
765
766 $row['INVITATION_ORIGINATOR_ID'] ??= null;
767 if (!$row['USER_LAST_ACTIVITY_DATE'] && $row['INVITATION_ORIGINATOR_ID'])
768 {
769 $item['INVITED'] = [
770 'ORIGINATOR_ID' => (int)$row['INVITATION_ORIGINATOR_ID'],
771 'CAN_RESEND' => !empty($row['USER_EMAIL'])
772 ];
773 }
774 $item['USER'] = [
775 'ID' => (int)$row['ITEM_ID'],
776 ];
777 }
778 else
779 {
780 $avatar = \CIMChat::GetAvatarImage($row['CHAT_AVATAR'], 200, false);
781 $color = $row['CHAT_COLOR'] <> ''
782 ? Color::getColor($row['CHAT_COLOR'])
783 : Color::getColorByNumber(
784 $row['ITEM_ID']
785 );
786 $chatType = \Bitrix\Im\Chat::getType($row);
787
788 if ($generalChatId == $row['ITEM_ID'])
789 {
790 $row["CHAT_ENTITY_TYPE"] = 'GENERAL';
791 }
792
793 $muteList = [];
794 if ($row['RELATION_NOTIFY_BLOCK'] == 'Y')
795 {
796 $muteList = [$row['RELATION_USER_ID'] => true];
797 }
798
799 $managerList = [];
800 if (
801 $chatOwner == $row['RELATION_USER_ID']
802 || $row['RELATION_IS_MANAGER'] == 'Y'
803 )
804 {
805 $managerList = [(int)$row['RELATION_USER_ID']];
806 }
807
808 if ($row['RELATION_NOTIFY_BLOCK'] == 'Y')
809 {
810 $muteList = [$row['RELATION_USER_ID'] => true];
811 }
812
813 $chatOptions = \CIMChat::GetChatOptions();
814 $restrictions = $chatOptions['DEFAULT'];
815 if ($row['CHAT_ENTITY_TYPE'] && array_key_exists($row['CHAT_ENTITY_TYPE'], $chatOptions))
816 {
817 $restrictions = $chatOptions[$row['CHAT_ENTITY_TYPE']];
818 }
819
820 $item['AVATAR'] = [
821 'URL' => $avatar,
822 'COLOR' => $color
823 ];
824 $item['TITLE'] = $row['CHAT_TITLE'];
825 $item['CHAT'] = [
826 'ID' => (int)$row['ITEM_CID'],
827 'NAME' => $row['CHAT_TITLE'],
828 'OWNER' => (int)$row['CHAT_AUTHOR_ID'],
829 'EXTRANET' => $row['CHAT_EXTRANET'] == 'Y',
830 'AVATAR' => $avatar,
831 'COLOR' => $color,
832 'TYPE' => $chatType,
833 'ENTITY_TYPE' => (string)$row['CHAT_ENTITY_TYPE'],
834 'ENTITY_ID' => (string)$row['CHAT_ENTITY_ID'],
835 'ENTITY_DATA_1' => (string)$row['CHAT_ENTITY_DATA_1'],
836 'ENTITY_DATA_2' => (string)$row['CHAT_ENTITY_DATA_2'],
837 'ENTITY_DATA_3' => (string)$row['CHAT_ENTITY_DATA_3'],
838 'MUTE_LIST' => $muteList,
839 'MANAGER_LIST' => $managerList,
840 'DATE_CREATE' => $row['CHAT_DATE_CREATE'],
841 'MESSAGE_TYPE' => $row["CHAT_TYPE"],
842 'USER_COUNTER' => (int)$row['CHAT_USER_COUNT'],
843 'RESTRICTIONS' => $restrictions,
844 'ROLE' => self::getRole($row),
845 'ENTITY_LINK' => EntityLink::getInstance(\CIMChat::initChatByArray($row))->toArray(),
846 'AI_PROVIDER' => $chatType === 'copilot' ? AIHelper::getProviderName() : null,
847 'PERMISSIONS' => [
848 'MANAGE_USERS_ADD' => mb_strtolower($row['CHAT_MANAGE_USERS_ADD'] ?? ''),
849 'MANAGE_USERS_DELETE' => mb_strtolower($row['CHAT_MANAGE_USERS_DELETE'] ?? ''),
850 'MANAGE_UI' => mb_strtolower($row['CHAT_MANAGE_UI'] ?? ''),
851 'MANAGE_SETTINGS' => mb_strtolower($row['CHAT_MANAGE_SETTINGS'] ?? ''),
852 'CAN_POST' => mb_strtolower($row['CHAT_CAN_POST'] ?? ''),
853 ],
854 ];
855 if ($row["CHAT_ENTITY_TYPE"] == 'LINES')
856 {
857 $item['LINES'] = [
858 'ID' => (int)$row['LINES_ID'],
859 'STATUS' => (int)$row['LINES_STATUS'],
860 'DATE_CREATE' => $row['LINES_DATE_CREATE'] ?? $row['DATE_UPDATE'],
861 ];
862 }
863 $item['USER'] = [
864 'ID' => (int)($row['MESSAGE_AUTHOR_ID'] ?? 0),
865 ];
866 }
867
868 if ($item['USER']['ID'] > 0)
869 {
870 $user = \Bitrix\Im\V2\Entity\User\User::getInstance($item['USER']['ID'])
871 ->getArray(['WITHOUT_ONLINE' => true])
872 ;
873
874 if (!$user)
875 {
876 $user = ['ID' => 0];
877 }
878 else if ($item['TYPE'] == 'user')
879 {
880 if (
881 !empty($user['BOT_DATA'])
882 && Loader::includeModule('imbot')
883 && $user['BOT_DATA']['code'] === CopilotChatBot::BOT_CODE
884 )
885 {
886 return null;
887 }
888
889 if (
890 (!$user['ACTIVE'] && $item['COUNTER'] <= 0)
891 && !$user['BOT']
892 && !$user['CONNECTOR']
893 && !$user['NETWORK']
894 )
895 {
896 return null;
897 }
898
899 $item['AVATAR'] = [
900 'URL' => $user['AVATAR'],
901 'COLOR' => $user['COLOR']
902 ];
903
904 $item['TITLE'] = $user['NAME'];
905
906 $row['USER_LAST_ACTIVITY_DATE'] ??= null;
907 $row['USER_DESKTOP_LAST_DATE'] ??= null;
908 $row['USER_MOBILE_LAST_DATE'] ??= null;
909 $row['USER_IDLE'] ??= null;
910
911 $user['LAST_ACTIVITY_DATE'] = $row['USER_LAST_ACTIVITY_DATE']?: false;
912 $user['DESKTOP_LAST_DATE'] = $row['USER_DESKTOP_LAST_DATE']?: false;
913 $user['MOBILE_LAST_DATE'] = $row['USER_MOBILE_LAST_DATE']?: false;
914 $user['IDLE'] = $row['USER_IDLE']?: false;
915 }
916 else
917 {
918 $row['MESSAGE_USER_LAST_ACTIVITY_DATE'] ??= null;
919 $row['MESSAGE_USER_DESKTOP_LAST_DATE'] ??= null;
920 $row['MESSAGE_USER_MOBILE_LAST_DATE'] ??= null;
921 $row['MESSAGE_USER_IDLE'] ??= null;
922
923 $user['LAST_ACTIVITY_DATE'] = $row['MESSAGE_USER_LAST_ACTIVITY_DATE']?: false;
924 $user['DESKTOP_LAST_DATE'] = $row['MESSAGE_USER_DESKTOP_LAST_DATE']?: false;
925 $user['MOBILE_LAST_DATE'] = $row['MESSAGE_USER_MOBILE_LAST_DATE']?: false;
926 $user['IDLE'] = $row['MESSAGE_USER_IDLE']?: false;
927 }
928
929 $item['USER'] = $user;
930 }
931
932 $item['OPTIONS'] = [];
933 if ($row['USER_ID'] == 0 || $row['MESSAGE_CODE'] === 'USER_JOIN')
934 {
935 $item['OPTIONS']['DEFAULT_USER_RECORD'] = true;
936 }
937
938 return $item;
939 }
940
941 private static function jsonRow($item)
942 {
943 if (!is_array($item))
944 {
945 return $item;
946 }
947
948 foreach ($item as $key => $value)
949 {
950 if ($value instanceof \Bitrix\Main\Type\DateTime)
951 {
952 $item[$key] = date('c', $value->getTimestamp());
953 }
954 else if (is_array($value))
955 {
956 foreach ($value as $subKey => $subValue)
957 {
958 if ($subValue instanceof \Bitrix\Main\Type\DateTime)
959 {
960 $value[$subKey] = date('c', $subValue->getTimestamp());
961 }
962 else if (
963 is_string($subValue)
964 && $subValue
965 && in_array($subKey, ['URL', 'AVATAR'])
966 && mb_strpos($subValue, 'http') !== 0
967 )
968 {
969 $value[$subKey] = \Bitrix\Im\Common::getPublicDomain().$subValue;
970 }
971 else if (is_array($subValue))
972 {
973 $value[$subKey] = array_change_key_case($subValue, CASE_LOWER);
974 }
975 }
976 $item[$key] = array_change_key_case($value, CASE_LOWER);
977 }
978 }
979
980 return array_change_key_case($item, CASE_LOWER);
981 }
982
983 public static function pin($dialogId, $pin, $userId = null)
984 {
985 $userId = \Bitrix\Im\Common::getUserId($userId);
986 if (!$userId)
987 {
988 return false;
989 }
990
991 $pinnedCount = \Bitrix\Im\Model\RecentTable::getCount(['=USER_ID' => $userId, '=PINNED' => 'Y']);
992
993 self::$limitError = false;
994 if ($pin && (int)$pinnedCount >= self::PINNED_CHATS_LIMIT)
995 {
996 self::$limitError = true;
997
998 return false;
999 }
1000
1001 $pin = $pin === true? 'Y': 'N';
1002
1003 $id = $dialogId;
1004 $chatId = 0;
1005 if (mb_substr($dialogId, 0, 4) == 'chat')
1006 {
1007 $itemTypes = \Bitrix\Im\Chat::getTypes();
1008 $id = mb_substr($dialogId, 4);
1009 $chatId = (int)$id;
1010 }
1011 else
1012 {
1013 $itemTypes = IM_MESSAGE_PRIVATE;
1014 $chatId = \Bitrix\Im\Dialog::getChatId($dialogId);
1015 }
1016
1017 $element = \Bitrix\Im\Model\RecentTable::getList(
1018 [
1019 'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'PINNED', 'PIN_SORT'],
1020 'filter' => [
1021 '=USER_ID' => $userId,
1022 '=ITEM_TYPE' => $itemTypes,
1023 '=ITEM_ID' => $id
1024 ]
1025 ]
1026 )->fetch();
1027 if (!$element)
1028 {
1029 return false;
1030// if (mb_substr($dialogId, 0, 4) == 'chat')
1031// {
1032// if (!\Bitrix\Im\Dialog::hasAccess($dialogId))
1033// {
1034// return false;
1035// }
1036//
1037// $missingChat = \Bitrix\Im\Model\ChatTable::getRowById($id);
1038// $itemTypes = $missingChat['TYPE'];
1039// }
1040
1041// $messageId = 0;
1042// $relationId = 0;
1043// if ($itemTypes !== IM_MESSAGE_OPEN)
1044// {
1045
1046// }
1047
1048 $relationData = \Bitrix\Im\Model\RelationTable::getList(
1049 [
1050 'select' => ['ID', 'LAST_MESSAGE_ID' => 'CHAT.LAST_MESSAGE_ID'],
1051 'filter' => [
1052 '=CHAT_ID' => $chatId,
1053 '=USER_ID' => $userId,
1054 ]
1055 ]
1056 )->fetchAll()[0];
1057
1058 $messageId = $relationData['LAST_MESSAGE_ID'];
1059 $relationId = $relationData['ID'];
1060
1061 $addResult = \Bitrix\Im\Model\RecentTable::add(
1062 [
1063 'USER_ID' => $userId,
1064 'ITEM_TYPE' => $itemTypes,
1065 'ITEM_ID' => $id,
1066 'ITEM_MID' => $messageId,
1067 'ITEM_RID' => $relationId,
1068 'ITEM_CID' => $chatId,
1069 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime()
1070 ]
1071 );
1072 if (!$addResult->isSuccess())
1073 {
1074 return false;
1075 }
1076
1077// self::show($id);
1078
1079 $element['USER_ID'] = $userId;
1080 $element['ITEM_TYPE'] = $itemTypes;
1081 $element['ITEM_ID'] = $id;
1082 }
1083
1084 if ($element['PINNED'] == $pin)
1085 {
1086 return true;
1087 }
1088
1089 $connection = Application::getConnection();
1090 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1091
1092 if ($pin === 'Y')
1093 {
1094 self::increasePinSortCost($userId);
1095 }
1096 else
1097 {
1098 $pinSort = $element['PIN_SORT'] ? (int)$element['PIN_SORT'] : null;
1099 self::decreasePinSortCost($userId, $pinSort);
1100 }
1101
1102
1103 \Bitrix\Im\Model\RecentTable::update(
1104 [
1105 'USER_ID' => $element['USER_ID'],
1106 'ITEM_TYPE' => $element['ITEM_TYPE'],
1107 'ITEM_ID' => $element['ITEM_ID'],
1108 ],
1109 [
1110 'PINNED' => $pin,
1111 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime(),
1112 'PIN_SORT' => ($pin === 'Y') ? 1 : null,
1113 ]
1114 );
1115
1116 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1117
1118 if ($element['ITEM_TYPE'] !== \Bitrix\Im\V2\Chat::IM_TYPE_OPEN_LINE)
1119 {
1120 Sync\Logger::getInstance()->add(
1121 new Sync\Event(Sync\Event::ADD_EVENT, Sync\Event::CHAT_ENTITY, $chatId),
1122 $userId
1123 );
1124 }
1125
1126 self::clearCache($element['USER_ID']);
1127
1128 $pullInclude = \Bitrix\Main\Loader::includeModule("pull");
1129 if ($pullInclude)
1130 {
1131 \Bitrix\Pull\Event::add(
1132 $userId,
1133 [
1134 'module_id' => 'im',
1135 'command' => 'chatPin',
1136 'expiry' => 3600,
1137 'params' => [
1138 'dialogId' => $dialogId,
1139 'active' => $pin == 'Y'
1140 ],
1141 'extra' => \Bitrix\Im\Common::getPullExtra()
1142 ]
1143 );
1144 }
1145
1146 return true;
1147 }
1148
1149 private static function increasePinSortCost(int $userId): void
1150 {
1151 $caseField = new SqlExpression('?# + 1', 'PIN_SORT');
1152
1153 RecentTable::updateByFilter(
1154 [
1155 '=PINNED' => 'Y',
1156 '=USER_ID' => $userId,
1157 '>=PIN_SORT' => 1,
1158 ],
1159 ['PIN_SORT' => $caseField]
1160 );
1161 }
1162
1163 private static function decreasePinSortCost(int $userId, ?int $pinSort)
1164 {
1165 if (!isset($pinSort))
1166 {
1167 return;
1168 }
1169
1170 $caseField = new SqlExpression('?# - 1', 'PIN_SORT');
1171
1172 RecentTable::updateByFilter(
1173 [
1174 '=PINNED' => 'Y',
1175 '=USER_ID' => $userId,
1176 '>PIN_SORT' => $pinSort,
1177 ],
1178 ['PIN_SORT' => $caseField]
1179 );
1180 }
1181
1182 public static function sortPin(\Bitrix\Im\V2\Chat $chat, int $newPosition, int $userId): void
1183 {
1184 $connection = Application::getConnection();
1185 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1186
1187 $query = RecentTable::query()
1188 ->setSelect(['PIN_SORT'])
1189 ->setLimit(1)
1190 ->where('USER_ID', $userId)
1191 ->where('ITEM_CID', (int)$chat->getChatId())
1192 ->where('PINNED', 'Y')
1193 ->fetch()
1194 ;
1195
1196 if (!$query)
1197 {
1198 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1199
1200 return;
1201 }
1202 $currentCost = (int)$query['PIN_SORT'];
1203
1204 $query = RecentTable::query()
1205 ->setSelect(['PIN_SORT'])
1206 ->setOrder(['PIN_SORT'])
1207 ->setOffset($newPosition - 1)
1208 ->setLimit(1)
1209 ->where('PINNED', 'Y')
1210 ->where('USER_ID', $userId)
1211 ->fetch()
1212 ;
1213
1214 if (!$query)
1215 {
1216 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1217
1218 return;
1219 }
1220 $newCost = (int)$query['PIN_SORT'];
1221
1222 if ($currentCost === $newCost)
1223 {
1224 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1225
1226 return;
1227 }
1228
1229 if ($currentCost < $newCost)
1230 {
1231 $caseField = new SqlExpression(
1232 "CASE WHEN ?# = ?i THEN ?i WHEN ?# > ?i AND ?# <= ?i THEN ?# - 1 END",
1233 'PIN_SORT',
1234 $currentCost,
1235 $newCost,
1236 'PIN_SORT',
1237 $currentCost,
1238 'PIN_SORT',
1239 $newCost,
1240 'PIN_SORT'
1241 );
1242
1243 $filter = [
1244 '=PINNED' => 'Y',
1245 '=USER_ID' => $userId,
1246 '>=PIN_SORT' => $currentCost,
1247 '<=PIN_SORT' => $newCost,
1248 ];
1249 }
1250 else
1251 {
1252 $caseField = new SqlExpression(
1253 "CASE WHEN ?# = ?i THEN ?i WHEN ?# >= ?i AND ?# < ?i THEN ?# + 1 END",
1254 'PIN_SORT',
1255 $currentCost,
1256 $newCost,
1257 'PIN_SORT',
1258 $newCost,
1259 'PIN_SORT',
1260 $currentCost,
1261 'PIN_SORT'
1262 );
1263
1264 $filter = [
1265 '=PINNED' => 'Y',
1266 '=USER_ID' => $userId,
1267 '>=PIN_SORT' => $newCost,
1268 '<=PIN_SORT' => $currentCost,
1269 ];
1270 }
1271
1272 RecentTable::updateByFilter(
1273 $filter,
1274 ['PIN_SORT' => $caseField]
1275 );
1276
1277 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1278 }
1279
1280 public static function getPinLimit(): int
1281 {
1282 return self::PINNED_CHATS_LIMIT ?? 25;
1283 }
1284
1285 public static function updatePinSortCost(int $userId): void
1286 {
1287 $connection = Application::getConnection();
1288 $connection->lock("PIN_SORT_CHAT_{$userId}", 10);
1289
1290 $caseField = new SqlExpression('?#', 'ITEM_MID');
1291
1292 RecentTable::updateByFilter(
1293 [
1294 '=PINNED' => 'Y',
1295 '=USER_ID' => $userId
1296 ],
1297 ['PIN_SORT' => $caseField]
1298 );
1299
1300 $connection->unlock("PIN_SORT_CHAT_{$userId}");
1301 }
1302
1303 public static function updateByFilter(array $filter, array $fields): void
1304 {
1305 RecentTable::updateByFilter($filter, $fields);
1306 }
1307
1308 public static function unread($dialogId, $unread, $userId = null, ?int $markedId = null)
1309 {
1310 $userId = \Bitrix\Im\Common::getUserId($userId);
1311 if (!$userId)
1312 {
1313 return false;
1314 }
1315
1316 $unread = $unread === true? 'Y': 'N';
1317
1318 $id = $dialogId;
1319 if (mb_substr($dialogId, 0, 4) === 'chat')
1320 {
1321 $itemTypes = \Bitrix\Im\Chat::getTypes();
1322 $id = mb_substr($dialogId, 4);
1323 }
1324 else
1325 {
1326 $itemTypes = IM_MESSAGE_PRIVATE;
1327 }
1328
1329 $element = \Bitrix\Im\Model\RecentTable::getList([
1330 'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'UNREAD', 'MUTED' => 'RELATION.NOTIFY_BLOCK', 'ITEM_CID', 'MARKED_ID'],
1331 'filter' => [
1332 '=USER_ID' => $userId,
1333 '=ITEM_TYPE' => $itemTypes,
1334 '=ITEM_ID' => $id
1335 ]
1336 ])->fetch();
1337 if (!$element)
1338 {
1339 return false;
1340 }
1341 if ($element['UNREAD'] === $unread && !isset($markedId))
1342 {
1343 return true;
1344 }
1345
1346 $updatedFields = [
1347 'UNREAD' => $unread,
1348 'DATE_UPDATE' => new \Bitrix\Main\Type\DateTime(),
1349 ];
1350
1351 if ($unread === 'N')
1352 {
1353 $markedId = 0;
1354 }
1355 if (isset($markedId))
1356 {
1357 $updatedFields['MARKED_ID'] = $markedId;
1358 }
1359
1360 \Bitrix\Im\Model\RecentTable::update(
1361 [
1362 'USER_ID' => $element['USER_ID'],
1363 'ITEM_TYPE' => $element['ITEM_TYPE'],
1364 'ITEM_ID' => $element['ITEM_ID'],
1365 ],
1366 $updatedFields
1367 );
1368
1369 self::clearCache($element['USER_ID']);
1370 //\Bitrix\Im\Counter::clearCache($element['USER_ID']);
1371 CounterService::clearCache((int)$element['USER_ID']);
1372 $chatId = (int)$element['ITEM_CID'];
1373 if ($element['ITEM_TYPE'] !== \Bitrix\Im\V2\Chat::IM_TYPE_OPEN_LINE)
1374 {
1375 Sync\Logger::getInstance()->add(
1376 new Sync\Event(Sync\Event::ADD_EVENT, Sync\Event::CHAT_ENTITY, $chatId),
1377 $userId
1378 );
1379 }
1380
1381 $pullInclude = \Bitrix\Main\Loader::includeModule("pull");
1382 if ($pullInclude)
1383 {
1384 $readService = new ReadService($userId);
1385 $counter = $readService->getCounterService()->getByChat($chatId);
1386 //$readService->sendPush($chatId, [$userId], $counter, $time);
1387
1388 \Bitrix\Pull\Event::add(
1389 $userId,
1390 [
1391 'module_id' => 'im',
1392 'command' => 'chatUnread',
1393 'expiry' => 3600,
1394 'params' => [
1395 'chatId' => $chatId,
1396 'dialogId' => $dialogId,
1397 'active' => $unread === 'Y',
1398 'muted' => $element['MUTED'] === 'Y',
1399 'counter' => $counter,
1400 'markedId' => $markedId ?? $element['MARKED_ID'],
1401 'lines' => $element['ITEM_TYPE'] === IM_MESSAGE_OPEN_LINE,
1402 ],
1403 'extra' => \Bitrix\Im\Common::getPullExtra()
1404 ]
1405 );
1406 }
1407
1408 return true;
1409 }
1410
1411 public static function readAll(int $userId): void
1412 {
1413 \Bitrix\Im\Model\RecentTable::updateByFilter(
1414 [
1415 '=UNREAD' => 'Y',
1416 '=USER_ID' => $userId,
1417 ],
1418 [
1419 'UNREAD' => 'N',
1420 'MARKED_ID' => 0,
1421 ]
1422 );
1423 }
1424
1425 public static function isUnread(int $userId, string $itemType, string $dialogId): bool
1426 {
1427 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1428 $element = \Bitrix\Im\Model\RecentTable::getList([
1429 'select' => ['USER_ID', 'ITEM_TYPE', 'ITEM_ID', 'UNREAD', 'MUTED' => 'RELATION.NOTIFY_BLOCK', 'ITEM_CID'],
1430 'filter' => [
1431 '=USER_ID' => $userId,
1432 '=ITEM_TYPE' => $itemType,
1433 '=ITEM_ID' => $id
1434 ]
1435 ])->fetch();
1436 if (!$element)
1437 {
1438 return false;
1439 }
1440
1441 return ($element['UNREAD'] ?? 'N') === 'Y';
1442 }
1443
1444 public static function getUnread(string $itemType, string $dialogId): array
1445 {
1446 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1447 $queryResult = \Bitrix\Im\Model\RecentTable::getList([
1448 'select' => ['USER_ID', 'UNREAD',],
1449 'filter' => [
1450 '=ITEM_TYPE' => $itemType,
1451 '=ITEM_ID' => $id
1452 ]
1453 ])->fetchAll();
1454
1455 $result = [];
1456
1457 foreach ($queryResult as $row)
1458 {
1459 $result[(int)$row['USER_ID']] = ($row['UNREAD'] ?? 'N') === 'Y';
1460 }
1461
1462 return $result;
1463 }
1464
1465 public static function getMarkedId(int $userId, string $itemType, string $dialogId): int
1466 {
1467 $id = mb_strpos($dialogId, 'chat') === 0 ? mb_substr($dialogId, 4) : $dialogId;
1468 $element = \Bitrix\Im\Model\RecentTable::getList([
1469 'select' => ['MARKED_ID'],
1470 'filter' => [
1471 '=USER_ID' => $userId,
1472 '=ITEM_TYPE' => $itemType,
1473 '=ITEM_ID' => $id
1474 ]
1475 ])->fetch();
1476 if (!$element)
1477 {
1478 return 0;
1479 }
1480
1481 return (int)($element['MARKED_ID'] ?? 0);
1482 }
1483
1484 public static function getMarkedIdByChatIds(int $userId, array $chatIds): array
1485 {
1486 if (empty($chatIds))
1487 {
1488 return [];
1489 }
1490
1491 $markedIdByChatIds = [];
1492
1493 $result = RecentTable::query()
1494 ->setSelect(['ITEM_CID', 'MARKED_ID'])
1495 ->where('USER_ID', $userId)
1496 ->whereIn('ITEM_CID', $chatIds)
1497 ->fetchAll()
1498 ;
1499
1500 foreach ($result as $row)
1501 {
1502 $markedIdByChatIds[(int)$row['ITEM_CID']] = (int)$row['MARKED_ID'];
1503 }
1504
1505 return $markedIdByChatIds;
1506 }
1507
1508 public static function hide($dialogId, $userId = null)
1509 {
1510 return \CIMContactList::DialogHide($dialogId, $userId);
1511 }
1512
1513 public static function show($dialogId, $options = [], $userId = null)
1514 {
1515 $userId = Common::getUserId($userId);
1516 if (!$userId)
1517 {
1518 return false;
1519 }
1520
1521 $chatId = Dialog::getChatId($dialogId, $userId);
1522 if (Common::isChatId($dialogId))
1523 {
1524 $entityId = $chatId;
1525 }
1526 else
1527 {
1528 $entityId = (int)$dialogId;
1529 }
1530
1531 $relation = \Bitrix\Im\Model\RelationTable::getList([
1532 'select' => [
1533 'ID',
1534 'TYPE' => 'CHAT.TYPE',
1535 'LAST_MESSAGE_ID' => 'CHAT.LAST_MESSAGE_ID',
1536 'LAST_MESSAGE_DATE' => 'MESSAGE.DATE_CREATE'
1537 ],
1538 'filter' => [
1539 '=CHAT_ID' => $chatId,
1540 '=USER_ID' => $userId
1541 ],
1542 'runtime' => [
1543 new \Bitrix\Main\Entity\ReferenceField(
1544 'MESSAGE',
1545 '\Bitrix\Im\Model\MessageTable',
1546 ["=ref.ID" => "this.CHAT.LAST_MESSAGE_ID"],
1547 ["join_type" => "LEFT"]
1548 ),
1549 ]
1550 ])->fetch();
1551
1552 if ($relation)
1553 {
1554 $relationId = $relation['ID'];
1555 $entityType = $relation['TYPE'];
1556 $messageId = $relation['LAST_MESSAGE_ID'];
1557 $messageDate = $relation['LAST_MESSAGE_DATE'];
1558 }
1559 else if (
1560 isset($options['CHAT_DATA']['TYPE'])
1561 && isset($options['CHAT_DATA']['LAST_MESSAGE_ID'])
1562 )
1563 {
1564 $relationId = 0;
1565 $entityType = $options['CHAT_DATA']['TYPE'];
1566 $messageId = $options['CHAT_DATA']['LAST_MESSAGE_ID'];
1567 $messageDate = $options['CHAT_DATA']['LAST_MESSAGE_DATE'];
1568 }
1569 else
1570 {
1571 $chat = \Bitrix\Im\Model\ChatTable::getList([
1572 'select' => [
1573 'TYPE',
1574 'LAST_MESSAGE_ID',
1575 'LAST_MESSAGE_DATE' => 'MESSAGE.DATE_CREATE'
1576 ],
1577 'filter' => [
1578 '=ID' => $chatId,
1579 ],
1580 'runtime' => [
1581 new \Bitrix\Main\Entity\ReferenceField(
1582 'MESSAGE',
1583 '\Bitrix\Im\Model\MessageTable',
1584 ["=ref.ID" => "this.LAST_MESSAGE_ID"],
1585 ["join_type" => "LEFT"]
1586 ),
1587 ]
1588 ])->fetch();
1589 if (!$chat)
1590 {
1591 return false;
1592 }
1593
1594 $relationId = 0;
1595 $entityType = $chat['TYPE'];
1596 $messageId = $chat['LAST_MESSAGE_ID'];
1597 $messageDate = $chat['LAST_MESSAGE_DATE'];
1598 }
1599
1600 $sessionId = 0;
1601 if ($entityType == IM_MESSAGE_OPEN_LINE)
1602 {
1603 if (isset($options['SESSION_ID']))
1604 {
1605 $sessionId = (int)$options['SESSION_ID'];
1606 }
1607 else if (\Bitrix\Main\Loader::includeModule('imopenlines'))
1608 {
1609 $session = \Bitrix\ImOpenLines\Model\SessionTable::getList([
1610 'select' => ['ID'],
1611 'filter' => ['=CHAT_ID' => $chatId],
1612 'order' => ['ID' => 'DESC'],
1613 'limit' => 1,
1614 ])->fetch();
1615 if ($session)
1616 {
1617 $sessionId = $session['ID'];
1618 }
1619 }
1620 }
1621
1622 \CIMContactList::SetRecent($temp = [
1623 'ENTITY_TYPE' => $entityType,
1624 'ENTITY_ID' => $entityId,
1625 'MESSAGE_ID' => $messageId,
1626 'MESSAGE_DATE' => $messageDate,
1627 'CHAT_ID' => $chatId,
1628 'RELATION_ID' => $relationId,
1629 'SESSION_ID' => $sessionId,
1630 'USER_ID' => $userId,
1631 ]);
1632
1633 if (!\Bitrix\Main\Loader::includeModule("pull"))
1634 {
1635 return true;
1636 }
1637
1638 $data = \Bitrix\Im\Recent::getElement($entityType, $entityId, $userId, ['JSON' => true]);
1639 if ($data)
1640 {
1641 if (
1642 !isset($data['message'])
1643 && $entityType === Chat::TYPE_OPEN_LINE
1644 && class_exists('\Bitrix\ImOpenLines\Recent')
1645 )
1646 {
1647 $data = \Bitrix\ImOpenLines\Recent::getElement(
1648 (int)$entityId,
1649 (int)$userId,
1650 [
1651 'JSON' => true,
1652 'fakeCounter' => 1
1653 ]
1654 );
1655 }
1656 \Bitrix\Pull\Event::add($userId, [
1657 'module_id' => 'im',
1658 'command' => 'chatShow',
1659 'params' => $data,
1660 'extra' => \Bitrix\Im\Common::getPullExtra()
1661 ]);
1662 }
1663
1664 return true;
1665 }
1666
1667 public static function clearCache($userId = null)
1668 {
1669 $cache = Application::getInstance()->getCache();
1670 $cache->cleanDir('/bx/imc/recent'.($userId ? Common::getCacheUserPostfix($userId) : ''));
1671 }
1672
1673 protected static function prepareRows(array $rows, int $userId): array
1674 {
1675 $rows = static::fillCounters($rows, $userId);
1676 $rows = static::fillFiles($rows);
1677
1678 return static::fillLastMessageStatuses($rows, $userId);
1679 }
1680
1686 protected static function getRole(array $row): string
1687 {
1688 if (!isset($row['RELATION_USER_ID']))
1689 {
1690 return \Bitrix\Im\V2\Chat::ROLE_GUEST;
1691 }
1692 if ((int)$row['CHAT_AUTHOR_ID'] === (int)$row['RELATION_USER_ID'])
1693 {
1694 return \Bitrix\Im\V2\Chat::ROLE_OWNER;
1695 }
1696 if ($row['RELATION_IS_MANAGER'] === 'Y')
1697 {
1698 return \Bitrix\Im\V2\Chat::ROLE_MANAGER;
1699 }
1700
1701 return \Bitrix\Im\V2\Chat::ROLE_MEMBER;
1702 }
1703
1704 protected static function fillCounters(array $rows, int $userId): array
1705 {
1706 $chatIds = [];
1707
1708 foreach ($rows as $row)
1709 {
1710 $chatIds[] = (int)$row['CHAT_ID'];
1711 }
1712
1713 $counters = (new CounterService($userId))->getForEachChat($chatIds);
1714
1715 foreach ($rows as $key => $row)
1716 {
1717 $rows[$key]['COUNTER'] = (int)($counters[(int)$row['CHAT_ID']] ?? 0);
1718 }
1719
1720 return $rows;
1721 }
1722
1723 protected static function fillLastMessageStatuses(array $rows, int $userId): array
1724 {
1725 $messageIds = [];
1726
1727 foreach ($rows as $row)
1728 {
1729 if (isset($row['MESSAGE_AUTHOR_ID']) && (int)$row['MESSAGE_AUTHOR_ID'] === $userId)
1730 {
1731 $messageIds[] = (int)$row['MESSAGE_ID'];
1732 }
1733 }
1734
1735 $messageStatuses = (new ViewedService($userId))->getMessageStatuses($messageIds);
1736
1737 foreach ($rows as $key => $row)
1738 {
1739 $rows[$key]['CHAT_LAST_MESSAGE_STATUS'] = $messageStatuses[(int)($row['MESSAGE_ID'] ?? 0)] ?? \IM_MESSAGE_STATUS_RECEIVED;
1740 }
1741
1742 return $rows;
1743 }
1744
1745 protected static function fillFiles(array $rows): array
1746 {
1748 {
1749 foreach ($rows as $key => $row)
1750 {
1751 $rows[$key]['MESSAGE_FILE'] = (bool)($row['MESSAGE_FILE'] ?? false);
1752 }
1753
1754 return $rows;
1755 }
1756
1757 $fileIds = [];
1758
1759 foreach ($rows as $row)
1760 {
1761 if (isset($row['MESSAGE_FILE']) && $row['MESSAGE_FILE'] > 0)
1762 {
1763 $fileIds[] = (int)$row['MESSAGE_FILE'];
1764 }
1765 }
1766
1767 $files = FileCollection::initByDiskFilesIds($fileIds);
1768
1769 foreach ($rows as $key => $row)
1770 {
1771 $fileId = $row['MESSAGE_FILE'] ?? null;
1772 $rows[$key]['MESSAGE_FILE'] = false;
1773 if (isset($fileId) && $fileId > 0)
1774 {
1775 $file = $files->getById((int)$fileId);
1776 if ($file !== null)
1777 {
1779 $rows[$key]['MESSAGE_FILE'] = [
1780 'ID' => $file->getId(),
1781 'TYPE' => $file->getContentType(),
1782 'NAME' => $file->getDiskFile()->getName(),
1783 ];
1784 }
1785 }
1786 }
1787
1788 return $rows;
1789 }
1790
1791 public static function isLimitError(): bool
1792 {
1793 return self::$limitError;
1794 }
1795}
const TYPE_OPEN_LINE
Definition chat.php:26
static getColor($code)
Definition color.php:121
static isChatId($id)
Definition common.php:64
static getPullExtra()
Definition common.php:128
static getCacheUserPostfix($id)
Definition common.php:59
static getUserId($userId=null)
Definition common.php:74
static getChatId($dialogId, $userId=null)
Definition dialog.php:91
static readAll(int $userId)
Definition recent.php:1411
static show($dialogId, $options=[], $userId=null)
Definition recent.php:1513
static prepareRows(array $rows, int $userId)
Definition recent.php:1673
static isUnread(int $userId, string $itemType, string $dialogId)
Definition recent.php:1425
static clearCache($userId=null)
Definition recent.php:1667
static getUnread(string $itemType, string $dialogId)
Definition recent.php:1444
static updateByFilter(array $filter, array $fields)
Definition recent.php:1303
static getMarkedIdByChatIds(int $userId, array $chatIds)
Definition recent.php:1484
static sortPin(\Bitrix\Im\V2\Chat $chat, int $newPosition, int $userId)
Definition recent.php:1182
static updatePinSortCost(int $userId)
Definition recent.php:1285
static getMarkedId(int $userId, string $itemType, string $dialogId)
Definition recent.php:1465
static getRole(array $row)
Definition recent.php:1686
static getList($userId=null, $options=[])
Definition recent.php:214
static hide($dialogId, $userId=null)
Definition recent.php:1508
static fillLastMessageStatuses(array $rows, int $userId)
Definition recent.php:1723
static fillCounters(array $rows, int $userId)
Definition recent.php:1704
static unread($dialogId, $unread, $userId=null, ?int $markedId=null)
Definition recent.php:1308
static getElement($itemType, $itemId, $userId=null, $options=[])
Definition recent.php:405
static getPinLimit()
Definition recent.php:1280
static isLimitError()
Definition recent.php:1791
static pin($dialogId, $pin, $userId=null)
Definition recent.php:983
static isLegacyChatActivated($userId=false)
Definition settings.php:85
static populateUserBbCode(string $text)
Definition text.php:345
static removeBbCodes($text, $withFile=false, $attachValue=false)
Definition text.php:295
static initByDiskFilesIds(array $diskFilesIds, ?int $chatId=null)
static getConnection($name="")
static loadMessages($file)
Definition loc.php:64