Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
Chat.php
1<?php
2
3namespace Bitrix\Im\V2;
4
5use Bitrix\Disk\Folder;
16use Bitrix\Im;
20use Bitrix\Im\Model\EO_Chat;
26use Bitrix\Im\V2\Common\ContextCustomer;
27use Bitrix\Im\V2\Common\ActiveRecordImplementation;
28use Bitrix\Im\V2\Common\RegistryEntryImplementation;
33use CFile;
34use CGlobalCounter;
35use CIMNotify;
36use CPushManager;
37use CRestUtil;
38
43{
44 use ContextCustomer
45 {
46 setContext as private defaultSaveContext;
47 }
48 use RegistryEntryImplementation;
49 use ActiveRecordImplementation
50 {
51 save as defaultSave;
52 }
53
54 public const
63 ;
64
65 public const IM_TYPES = [
66 self::IM_TYPE_PRIVATE,
67 self::IM_TYPE_CHAT,
68 self::IM_TYPE_COMMENT,
69 self::IM_TYPE_OPEN_LINE,
70 self::IM_TYPE_SYSTEM,
71 self::IM_TYPE_CHANNEL,
72 self::IM_TYPE_OPEN,
73 self::IM_TYPE_COPILOT,
74 ];
75
76 public const IM_TYPES_TRANSLATE = [
77 'PRIVATE' => self::IM_TYPE_PRIVATE,
78 'CHAT' => self::IM_TYPE_CHAT,
79 'COMMENT' => self::IM_TYPE_COMMENT,
80 'OPENLINE' => self::IM_TYPE_OPEN_LINE,
81 'SYSTEM' => self::IM_TYPE_SYSTEM,
82 'NOTIFY' => self::IM_TYPE_SYSTEM,
83 'CHANNEL' => self::IM_TYPE_CHANNEL,
84 'OPEN' => self::IM_TYPE_OPEN,
85 'COPILOT' => self::IM_TYPE_COPILOT,
86 ];
87
88 // Default entity types
89 public const
90 ENTITY_TYPE_VIDEOCONF = 'VIDEOCONF',
93 ;
94
95 //OPENLINES
96 public const
97 ENTITY_TYPE_LINE = 'LINES', //OPERATOR
98 ENTITY_TYPE_LIVECHAT = 'LIVECHAT'; //USER
99
100 protected const ENTITY_TYPES = [
101 self::ENTITY_TYPE_LINE,
102 self::ENTITY_TYPE_LIVECHAT,
103 self::ENTITY_TYPE_FAVORITE,
104 self::ENTITY_TYPE_VIDEOCONF,
105 ];
106
107 public const AVAILABLE_PARAMS = [
108 'type',
109 'entityType',
110 'entityId',
111 'entityData1',
112 'entityData2',
113 'entityData3',
114 'title',
115 'description',
116 'searchable',
117 'color',
118 'ownerId',
119 'users',
120 'managers',
121 'manageUsersAdd',
122 'manageUsersDelete',
123 'manageUi',
124 'manageSettings',
125 'disappearingTime',
126 'canPost',
127 'avatar',
128 'conferencePassword',
129 ];
130
131 public const
136 ;
137
138 public const ROLE_OWNER = 'OWNER';
139 public const ROLE_MANAGER = 'MANAGER';
140 public const ROLE_MEMBER = 'MEMBER';
141 public const ROLE_GUEST = 'GUEST';
142 public const ROLE_NONE = 'NONE';
143
144 private const CHUNK_SIZE = 1000;
145 protected const EXTRANET_CAN_SEE_HISTORY = false;
146
150 protected static array $chatStaticCache = [];
151
152 protected array $accessCache = [];
153
154 protected ?int $chatId = null;
155
163 protected ?string $dialogId = null;
164
173 protected ?string $type = null;
174
175 protected ?int $authorId = null;
176
177 protected ?string $title = null;
178
179 protected ?string $description = null;
180
181 protected ?string $color = null;
182
183 protected int $parentChatId = 0;
184
185 protected int $parentMessageId = 0;
186
187 protected ?bool $extranet = null;
188
189 protected ?int $avatarId = null;
190
191 protected ?int $pinMessageId = null;
192
193 protected ?int $callType = null;
194
195 protected ?string $callNumber = null;
196
197 protected ?string $entityType = null;
198
199 protected ?string $entityId = null;
200
201 protected ?string $entityData1 = null;
202
203 protected ?string $entityData2 = null;
204
206 protected ?string $entityData3 = null;
207
208 protected ?int $diskFolderId = null;
209
210 protected ?Folder $diskFolder = null;
211
212 protected ?int $messageCount = null;
213
214 protected ?int $userCount = null;
215
216 protected ?int $prevMessageId = null;
217
218 protected ?int $lastMessageId = null;
219 protected ?int $lastFileId = null;
220 protected ?DateTime $dateMessage = null;
221
222 protected ?int $markedId = null;
223 protected ?string $role = null;
224
225 protected ?string $aliasName = null;
226
227 protected ?string $lastMessageStatus = null;
228
229 protected ?DateTime $dateCreate = null;
230
231 protected ?string $manageUsersAdd = null;
232 protected ?string $manageUsersDelete = null;
233
234 protected ?string $manageUI = null;
235
236 protected ?string $manageSettings = null;
237
238 protected ?string $canPost = null;
239
240 protected ?array $usersIds = null;
241
242 protected ?int $disappearingTime = null;
243
246
250 protected ?array $relations = null;
251
252 protected ?ReadService $readService = null;
253
254 protected bool $isFilledNonCachedData = false;
255 protected bool $isDiskFolderFilled = false;
256
260 public function __construct($source = null)
261 {
262 $this->initByDefault();
263
264 if (!empty($source))
265 {
266 $this->load($source);
267 }
268
269 $this->messageRegistry = new Registry;
270 }
271
272 //region Users
273 //endregion
274
275 //region Relations
276 //endregion
277
282 public static function getInstance(?int $chatId): self
283 {
284 if (!isset($chatId))
285 {
286 return new Im\V2\Chat\NullChat();
287 }
288
289 if (isset(self::$chatStaticCache[$chatId]))
290 {
291 return self::$chatStaticCache[$chatId];
292 }
293
294 $chat = ChatFactory::getInstance()->getChatById($chatId);
295
296 if ($chat instanceof Im\V2\Chat\NullChat)
297 {
298 return $chat;
299 }
300
301 self::$chatStaticCache[$chatId] = $chat;
302
303 return self::$chatStaticCache[$chatId];
304 }
305
306 public static function cleanCache(int $id): void
307 {
308 unset(self::$chatStaticCache[$id]);
309
310 ChatFactory::getInstance()->cleanCache($id);
311 Im\V2\Chat\EntityLink::cleanCache($id);
312 }
313
314 public static function cleanAccessCache(int $chatId): void
315 {
316 if (isset(self::$chatStaticCache[$chatId]))
317 {
318 self::$chatStaticCache[$chatId]->accessCache = [];
319 }
320 }
321
322 public function save(): Result
323 {
324 $id = $this->getChatId();
325 $result = $this->defaultSave();
326
327 if (!$result->isSuccess())
328 {
329 return $result;
330 }
331
332 if ($id !== null)
333 {
334 self::cleanCache($id);
335 }
336
337 return $result;
338 }
339
340 public function getStartId(?int $userId = null): int
341 {
342 return RelationCollection::getStartId($userId ?? $this->getContext()->getUserId(), $this->getChatId());
343 }
344
345 public function isExist(): bool
346 {
347 return isset($this->chatId);
348 }
349
350 public function add(array $params): Result
351 {
352 return new Result();
353 }
354
355 protected function checkIsExtranet(): bool
356 {
357 if (
358 !count($this->usersIds ?? [])
359 || in_array($this->entityType, [self::ENTITY_TYPE_LINE, self::ENTITY_TYPE_LIVECHAT])
360 || in_array($this->type, [self::IM_TYPE_OPEN_LINE])
361 )
362 {
363 return false;
364 }
365
366 $userIds = \CIMContactList::PrepareUserIds($this->usersIds);
367 $users = \CIMContactList::GetUserData([
368 'ID' => array_values($userIds),
369 'DEPARTMENT' => 'N',
370 'USE_CACHE' => 'N'
371 ]);
372 foreach ($users['users'] as $user)
373 {
374 if ($user['extranet'])
375 {
376 return true;
377 }
378 }
379
380 return false;
381 }
382
383 protected function setUserIds(?array $userIds): self
384 {
385 if (is_array($userIds) && count($userIds))
386 {
387 $userIds = filter_var(
388 $userIds,
389 FILTER_VALIDATE_INT,
390 [
391 'flags' => FILTER_REQUIRE_ARRAY,
392 'options' => ['min_range' => 1]
393 ]
394 );
395
396 foreach ($userIds as $key => $userId)
397 {
398 if (!is_int($userId))
399 {
400 unset($userIds[$key]);
401 }
402 }
403 }
404
405 $this->usersIds = array_unique($userIds);
406
407 return $this;
408 }
409
410 public function getUserIds(): ?array
411 {
412 return $this->usersIds;
413 }
414
415 public function getAliasName(): ?string
416 {
417 $this->fillNonCachedData();
418
419 return $this->aliasName;
420 }
421
422 public function setAliasName(string $aliasName): self
423 {
424 $this->aliasName = $aliasName;
425
426 return $this;
427 }
428
429 public function prepareAliasToLoad($alias): ?string
430 {
431 if ($alias === null)
432 {
433 return null;
434 }
435
436 return $alias['ALIAS'] ?? null;
437 }
438
439 public function getMarkedId(): int
440 {
441 if (!isset($this->markedId))
442 {
443 $this->markedId = Im\Recent::getMarkedId($this->getContext()->getUserId(), $this->getType(), $this->getDialogId());
444 }
445
446 return $this->markedId;
447 }
448
449 public function getRole(): string
450 {
451 if (isset($this->role))
452 {
453 return $this->role;
454 }
455
456 if ($this->getContext()->getUserId() === (int)$this->getAuthorId())
457 {
458 $this->role = self::ROLE_OWNER;
459
460 return $this->role;
461 }
462
463 $selfRelation = $this->getSelfRelation();
464
465 if ($selfRelation === null)
466 {
467 $this->role = self::ROLE_GUEST;
468 }
469 elseif ($selfRelation->getManager())
470 {
471 $this->role = self::ROLE_MANAGER;
472 }
473 else
474 {
475 $this->role = self::ROLE_MEMBER;
476 }
477
478 return $this->role;
479 }
480
481 public function checkColor(): Result
482 {
483 if (!Color::isSafeColor($this->color))
484 {
485 CGlobalCounter::Increment('im_chat_color_id', CGlobalCounter::ALL_SITES, false);
486 $chatColorId = CGlobalCounter::GetValue('im_chat_color_id', CGlobalCounter::ALL_SITES);
487 $this->color = Color::getCodeByNumber($chatColorId);
488 }
489
490 return new Result();
491 }
492
493 //region Access & Permissions
494
499 public function hasAccess($user = null): bool
500 {
501 $userId = $this->getUserId($user);
502
503 if (isset($this->accessCache[$userId]))
504 {
505 return $this->accessCache[$userId];
506 }
507
508 if (!$userId || !$this->getChatId())
509 {
510 $this->accessCache[$userId] = false;
511
512 return false;
513 }
514
515 $this->accessCache[$userId] = $this->checkAccessWithoutCaching($userId);
516
517 return $this->accessCache[$userId];
518 }
519
520 protected function checkAccessWithoutCaching(int $userId): bool
521 {
522 return false;
523 }
524
525 protected function getUserId($user): int
526 {
527 $userId = 0;
528 if ($user === null)
529 {
530 $userId = $this->getContext()->getUserId();
531 }
532 elseif (is_numeric($user))
533 {
534 $userId = (int)$user;
535 }
536 elseif ($user instanceof User)
537 {
538 $userId = $user->getId();
539 }
540
541 return $userId;
542 }
543
544
545 //endregion
546
547 //region Message
548
552 public function getMessageRegistry(): Registry
553 {
554 return $this->messageRegistry;
555 }
556
561 public function getMessage(int $messageId): ?Message
562 {
563 if (isset($this->messageRegistry[$messageId]))
564 {
565 return $this->messageRegistry[$messageId];
566 }
567
568 $message = new Message;
569 $message->setRegistry($this->messageRegistry);
570
571 $loadResult = $message->load($messageId);
572 if ($loadResult->isSuccess())
573 {
574 return $message;
575 }
576
577 return null;
578 }
579
587 abstract public function sendMessage($message, $sendingConfig = null): Result;
588
593 public function updateMessage(Message $message): Result
594 {
595 $message->setRegistry($this->messageRegistry);
596
597 $result = new Result;
598
599 //todo: updating process here
600
601 return $result;
602 }
603
608 public function deleteMessage(Message $message): Result
609 {
610 //todo: drop process here
611 $result = new Result;
612
613 return $result;
614 }
615
620 public function riseInRecent(Message $message): void
621 {
623 foreach ($this->getRelations() as $relation)
624 {
625 if (!User::getInstance($relation->getUserId())->isActive())
626 {
627 continue;
628 }
629
630 if ($this->getEntityType() == self::ENTITY_TYPE_LINE)
631 {
632 if (User::getInstance($relation->getUserId())->getExternalAuthId() == 'imconnector')
633 {
634 continue;
635 }
636 }
637
638 \CIMContactList::SetRecent([
639 'ENTITY_ID' => $this->getChatId(),
640 'MESSAGE_ID' => $message->getMessageId(),
641 'CHAT_TYPE' => $this->getType(),
642 'USER_ID' => $relation->getUserId(),
643 'CHAT_ID' => $relation->getChatId(),
644 'RELATION_ID' => $relation->getId(),
645 ]);
646
647 if ($relation->getUserId() == $message->getAuthorId())
648 {
649 $relation
650 ->setLastId($message->getMessageId())
651 ->save();
652 }
653 }
654 }
655
661 public static function fillRole(array $chats, ?int $userId = null): void
662 {
663 //todo: replace to ChatCollection
664 $chatIdsToFill = [];
665 $userId ??= Im\V2\Entity\User\User::getCurrent()->getId();
666
667 foreach ($chats as $chat)
668 {
669 if ($chat->getAuthorId() === $userId)
670 {
671 $chat->role = self::ROLE_OWNER;
672 }
673 else
674 {
675 $id = $chat->getId();
676 $chatIdsToFill[$id] = $id;
677 }
678 }
679
680 if (empty($chatIdsToFill))
681 {
682 return;
683 }
684
685 $result = RelationTable::query()
686 ->setSelect(['CHAT_ID', 'MANAGER'])
687 ->where('USER_ID', $userId)
688 ->whereIn('CHAT_ID', $chatIdsToFill)
689 ->fetchAll()
690 ;
691 $isManager = [];
692
693 foreach ($result as $row)
694 {
695 $isManager[(int)$row['CHAT_ID']] = $row['MANAGER'] === 'Y';
696 }
697
698 foreach ($chats as $chat)
699 {
700 if (!isset($chatIdsToFill[$chat->getId()]))
701 {
702 continue;
703 }
704 if (!isset($isManager[$chat->getId()]))
705 {
706 $chat->role = self::ROLE_GUEST;
707 }
708 elseif ($isManager[$chat->getId()])
709 {
710 $chat->role = self::ROLE_MANAGER;
711 }
712 else
713 {
714 $chat->role = self::ROLE_MEMBER;
715 }
716 }
717 }
718
719 public static function readAllChats(int $userId): Result
720 {
721 $readService = new ReadService($userId);
722 $readService->readAll();
723
724 Im\Recent::readAll($userId);
725
726 if (Main\Loader::includeModule('pull'))
727 {
728 \Bitrix\Pull\Event::add($userId, [
729 'module_id' => 'im',
730 'command' => 'readAllChats',
731 'extra' => Im\Common::getPullExtra()
732 ]);
733 }
734
735 return new Result();
736 }
737
738 public function read(bool $onlyRecent = false, bool $byEvent = false): Result
739 {
740 Im\Recent::unread($this->getDialogId(), false, $this->getContext()->getUserId());
741
742 if ($onlyRecent)
743 {
744 $lastId = $this->getReadService()->getLastMessageIdInChat($this->chatId);
745
746 return (new Result())->setResult([
747 'CHAT_ID' => $this->chatId,
748 'LAST_ID' => $lastId,
749 'COUNTER' => $this->getReadService()->getCounterService()->getByChat($this->chatId),
750 'VIEWED_MESSAGES' => [],
751 ]);
752 }
753
754 return $this->readAllMessages($byEvent);
755 }
756
757 public function readAllMessages(bool $byEvent = false): Result
758 {
759 return $this->readMessages(null, $byEvent);
760 }
761
762 public function readMessages(?MessageCollection $messages, bool $byEvent = false): Result
763 {
764 $result = new Result();
765
766 if (isset($messages))
767 {
768 $messages = $messages->filterByChatId($this->chatId);
769
770 if ($messages->count() === 0)
771 {
772 return $result->addError(new MessageError(MessageError::MESSAGE_NOT_FOUND));
773 }
774 }
775
776 $readService = $this->getReadService();
777 $startId = $readService->getLastIdByChatId($this->chatId);
778 $readResult = isset($messages) ? $readService->read($messages, $this) : $readService->readAllInChat($this->chatId);
779 $counter = $readResult->getResult()['COUNTER'] ?? 0;
780 $viewedMessages = $readResult->getResult()['VIEWED_MESSAGES'] ?? new MessageCollection();
781
782 $lastId = $readService->getLastIdByChatId($this->chatId);
783
784 $notOwnMessages = $viewedMessages->filter(fn (Message $message) => $message->getAuthorId() !== $this->getContext()->getUserId());
785
786 if (Main\Loader::includeModule('pull'))
787 {
788 CIMNotify::DeleteBySubTag("IM_MESS_{$this->getChatId()}_{$this->getContext()->getUserId()}", false, false);
789 CPushManager::DeleteFromQueueBySubTag($this->getContext()->getUserId(), 'IM_MESS');
790 $this->sendPushRead($notOwnMessages, $lastId, $counter);
791 }
792
793 $this->sendEventRead($startId, $lastId, $counter, $byEvent);
794
795 return $result->setResult([
796 'CHAT_ID' => $this->chatId,
797 'LAST_ID' => $lastId,
798 'COUNTER' => $counter,
799 'VIEWED_MESSAGES' => $notOwnMessages->getIds(),
800 ]);
801 }
802
803 public function readTo(Message $message, bool $byEvent = false): Result
804 {
805 $readService = $this->getReadService();
806 $startId = $message->getMessageId();
807 $readResult = $readService->readTo($message);
808 $counter = $readResult->getResult()['COUNTER'] ?? 0;
809
810 $viewedMessages = $readResult->getResult()['VIEWED_MESSAGES'];
811 $messageCollection = new MessageCollection();
812 foreach ($viewedMessages as $messageId)
813 {
814 $viewedMessage = new Message();
815 $viewedMessage->setMessageId((int)$messageId);
816 $messageCollection->add($viewedMessage);
817 }
818
819 $lastId = $readService->getLastIdByChatId($this->chatId);
820
821 if (Main\Loader::includeModule('pull'))
822 {
823 CIMNotify::DeleteBySubTag("IM_MESS_{$this->getChatId()}_{$this->getContext()->getUserId()}", false, false);
824 CPushManager::DeleteFromQueueBySubTag($this->getContext()->getUserId(), 'IM_MESS');
825 $this->sendPushRead($messageCollection, $lastId, $counter);
826 }
827
828 $this->sendEventRead($startId, $lastId, $counter, $byEvent);
829
830 $result = new Result();
831 return $result->setResult([
832 'CHAT_ID' => $this->chatId,
833 'LAST_ID' => $lastId,
834 'COUNTER' => $counter,
835 'VIEWED_MESSAGES' => $viewedMessages,
836 ]);
837 }
838
839 public function unreadToMessage(Message $message): Result
840 {
841 $result = new Result();
842
843 if ($message->getChatId() !== $this->chatId)
844 {
845 return $result->addError(new MessageError(MessageError::MESSAGE_NOT_FOUND));
846 }
847
848 $readService = $this->getReadService();
849 $lastId = $readService->getLastMessageIdInChat($this->chatId);
850 $counter = $readService->unreadTo($message)->getResult()['COUNTER'];
851 $lastMessageIds = $this->getLastMessages($lastId, $message->getMessageId());
852 $lastMessageStatuses = $this->getReadService()->getViewedService()->getMessageStatuses($lastMessageIds);
853
854 /*if (Main\Loader::includeModule('pull'))
855 {
856 $this->sendPushUnreadSelf($message->getMessageId(), $lastId, $counter, $lastMessageStatuses);
857 $this->sendPushUnreadOpponent($lastMessageStatuses[$lastId] ?? \IM_MESSAGE_STATUS_RECEIVED, $lastId, $lastMessageStatuses);
858 }*/
859
860 return $result->setResult([
861 'CHAT_ID' => $this->chatId,
862 'LAST_ID' => $lastId,
863 'COUNTER' => $counter,
864 'UNREAD_TO' => $message->getId(),
865 'LAST_MESSAGE_STATUSES' => $lastMessageStatuses,
866 ]);
867 }
868
869 public function sendPushUpdateMessage(Message $message): void
870 {
871 return;
872 }
873
874 protected function sendPushRead(MessageCollection $messages, int $lastId, int $counter): void
875 {
876 if ($this->getType() === self::ENTITY_TYPE_LIVECHAT || !$this->getContext()->getUser()->isConnector())
877 {
878 $this->sendPushReadSelf($messages, $lastId, $counter);
879 }
880 $this->sendPushReadOpponent($messages, $lastId);
881 }
882
883 public function startRecordVoice(): void
884 {
885 if (!Main\Loader::includeModule('pull'))
886 {
887 return;
888 }
889
890 $pushFormatter = new Im\V2\Message\PushFormat();
891 Event::add($this->getUsersForPush(), $pushFormatter->formatStartRecordVoice($this));
892 }
893
894 protected function sendPushReadSelf(MessageCollection $messages, int $lastId, int $counter): void
895 {
896 $selfRelation = $this->getSelfRelation(['SELECT' => ['ID', 'CHAT_ID', 'USER_ID', 'NOTIFY_BLOCK']]);
897
898 $muted = isset($selfRelation) ? $selfRelation->getNotifyBlock() : false;
899 \Bitrix\Pull\Event::add($this->getContext()->getUserId(), [
900 'module_id' => 'im',
901 'command' => 'readMessageChat',
902 'params' => [
903 'dialogId' => $this->getDialogId(),
904 'chatId' => $this->getChatId(),
905 'lastId' => $lastId,
906 'counter' => $counter,
907 'muted' => $muted ?? false,
908 'unread' => Im\Recent::isUnread($this->getContext()->getUserId(), $this->getType(), $this->getDialogId()),
909 'lines' => $this->getType() === IM_MESSAGE_OPEN_LINE,
910 'viewedMessages' => $messages->getIds(),
911 ],
912 'extra' => \Bitrix\Im\Common::getPullExtra()
913 ]);
914 }
915
916 protected function sendPushReadOpponent(MessageCollection $messages, int $lastId): array
917 {
918 $pushMessage = [
919 'module_id' => 'im',
920 'command' => 'readMessageChatOpponent',
921 'expiry' => 600,
922 'params' => [
923 'dialogId' => $this->getDialogId(),
924 'chatId' => $this->chatId,
925 'userId' => $this->getContext()->getUserId(),
926 'userName' => $this->getContext()->getUser()->getName(),
927 'lastId' => $lastId,
928 'date' => (new DateTime())->format('c'),
929 'viewedMessages' => $messages->getIds(),
930 'chatMessageStatus' => $this->getReadService()->getChatMessageStatus($this->chatId),
931 ],
932 'extra' => \Bitrix\Im\Common::getPullExtra()
933 ];
934 \Bitrix\Pull\Event::add($this->getUsersForPush(), $pushMessage);
935
936 return $pushMessage;
937 }
938
939 protected function sendEventRead(int $startId, int $endId, int $counter, bool $byEvent): void
940 {
941 foreach (\GetModuleEvents("im", "OnAfterChatRead", true) as $arEvent)
942 {
943 \ExecuteModuleEventEx($arEvent, array(Array(
944 'CHAT_ID' => $this->chatId,
945 'CHAT_ENTITY_TYPE' => $this->getEntityType(),
946 'CHAT_ENTITY_ID' => $this->getEntityId(),
947 'START_ID' => $startId,
948 'END_ID' => $endId,
949 'COUNT' => $counter,
950 'USER_ID' => $this->getContext()->getUserId(),
951 'BY_EVENT' => $byEvent
952 )));
953 }
954 }
955
956 protected function sendPushUnreadSelf(int $unreadToId, int $lastId, int $counter, ?array $lastMessageStatuses): void
957 {
958 $selfRelation = $this->getSelfRelation();
959 $muted = isset($selfRelation) ? $selfRelation->getNotifyBlock() : false;
960 \Bitrix\Pull\Event::add($this->getContext()->getUserId(), [
961 'module_id' => 'im',
962 'command' => 'unreadMessageChat',
963 'params' => [
964 'dialogId' => $this->getDialogId(),
965 'chatId' => $this->chatId,
966 'lastId' => $lastId,
967 'date' => new DateTime(),
968 'counter' => $counter,
969 'muted' => $muted ?? false,
970 'unread' => Im\Recent::isUnread($this->getContext()->getUserId(), $this->getType(), $this->getDialogId()),
971 'lines' => $this->getType() === IM_MESSAGE_OPEN_LINE,
972 'unreadToId' => $unreadToId,
973 'lastMessageStatuses' => $lastMessageStatuses ?? [],
974 'lastMessageViews' => Im\Common::toJson($this->getLastMessageViews()),
975 ],
976 'push' => ['badge' => 'Y'],
977 'extra' => \Bitrix\Im\Common::getPullExtra()
978 ]);
979 }
980
981 public function getLastMessages(int $upperBound, int $lowerBound): array
982 {
983 $lastMessagesRaw = Im\Model\MessageTable::query()
984 ->setSelect(['ID'])
985 ->where('ID', '>=', $lowerBound)
986 ->where('ID', '<=', $upperBound)
987 ->where('CHAT_ID', $this->chatId)
988 ->setOrder(['DATE_CREATE' => 'DESC', 'ID' => 'DESC'])
989 ->setLimit(50)
990 ->fetchAll()
991 ;
992 $lastMessageIds = [];
993 foreach ($lastMessagesRaw as $row)
994 {
995 $lastMessageIds[] = (int)$row['ID'];
996 }
997
998 return $lastMessageIds;
999 }
1000
1001
1002 public function getLastMessageViews(): array
1003 {
1004 $lastMessageViewsByGroups = $this->getLastMessageViewsByGroups();
1005
1006 if (isset($lastMessageViewsByGroups['USERS'][$this->getContext()->getUserId()]))
1007 {
1008 return $lastMessageViewsByGroups['FOR_VIEWERS'];
1009 }
1010
1011 return $lastMessageViewsByGroups['FOR_NOT_VIEWERS'];
1012 }
1013
1014 public function getLastMessageViewsByGroups(): array
1015 {
1016 $defaultViewInfo = [
1017 'MESSAGE_ID' => 0,
1018 'FIRST_VIEWERS' => [],
1019 'COUNT_OF_VIEWERS' => 0,
1020 ];
1021 $defaultValue = [
1022 'USERS' => [],
1023 'FOR_VIEWERS' => $defaultViewInfo,
1024 'FOR_NOT_VIEWERS' => $defaultViewInfo,
1025 ];
1026
1027 $readService = $this->getReadService();
1028
1029 $lastMessageInChat = $this->getLastMessageId() ?? 0;
1030
1031 if ($lastMessageInChat === 0)
1032 {
1033 return $defaultValue;
1034 }
1035
1036 $messageViewers = $readService->getViewedService()->getMessageViewersIds($lastMessageInChat);
1037 $countOfView = count($messageViewers);
1038
1039 $firstViewers = [];
1040
1041 foreach ($messageViewers as $messageViewer)
1042 {
1043 if (count($firstViewers) >= 2)
1044 {
1045 break;
1046 }
1047
1048 $firstViewers[$messageViewer] = $messageViewer;
1049 }
1050
1051 $datesOfViews = $readService->getViewedService()->getDateViewedByMessageIdForEachUser($lastMessageInChat, $firstViewers);
1052
1053 $firstViewersWithDate = [];
1054
1055 foreach ($firstViewers as $viewer)
1056 {
1057 $firstViewersWithDate[] = [
1058 'USER_ID' => $viewer,
1059 'USER_NAME' => Im\V2\Entity\User\User::getInstance($viewer)->getName(),
1060 'DATE' => $datesOfViews[$viewer] ?? null
1061 ];
1062 }
1063
1064 $viewsInfoByGroups = ['USERS' => $messageViewers];
1065 $viewInfoForViewers = [
1066 'MESSAGE_ID' => $lastMessageInChat,
1067 'FIRST_VIEWERS' => $firstViewersWithDate,
1068 'COUNT_OF_VIEWERS' => $countOfView - 1,
1069 ];
1070 $viewInfoForNotViewers = $viewInfoForViewers;
1071 ++$viewInfoForNotViewers['COUNT_OF_VIEWERS'];
1072 $viewsInfoByGroups['FOR_VIEWERS'] = $viewInfoForViewers;
1073 $viewsInfoByGroups['FOR_NOT_VIEWERS'] = $viewInfoForNotViewers;
1074
1075 return $viewsInfoByGroups;
1076 }
1077
1078 protected function getUsersForPush(bool $skipBot = false, bool $skipSelf = true): array
1079 {
1080 $userId = $this->getContext()->getUserId();
1081 $isLineChat = $this->getEntityType() === self::ENTITY_TYPE_LINE;
1082 $relations = $this->getRelations();
1083 $userIds = [];
1084 foreach ($relations as $relation)
1085 {
1086 if ($skipSelf && $relation->getUserId() === $userId)
1087 {
1088 continue;
1089 }
1090 if ($skipBot && $relation->getUser()->isBot())
1091 {
1092 continue;
1093 }
1094 if ($isLineChat && $relation->getUser()->isConnector())
1095 {
1096 continue;
1097 }
1098 $userIds[] = $relation->getUserId();
1099 }
1100
1101 return $userIds;
1102 }
1103
1104 //endregion
1105
1106 //region Data storage
1107
1111 protected static function mirrorDataEntityFields(): array
1112 {
1113 return [
1114 'ID' => [
1115 'primary' => true,
1116 'field' => 'chatId',
1117 'set' => 'setChatId',
1118 'get' => 'getChatId',
1119 ],
1120 'CHAT_ID' => [
1121 'alias' => 'ID',
1122 ],
1123 'TYPE' => [
1124 'field' => 'type',
1125 'set' => 'setType',
1126 'get' => 'getType',
1127 'default' => 'getDefaultType',
1128 'beforeSave' => 'beforeSaveType',
1129 ],
1130 'AUTHOR_ID' => [
1131 'field' => 'authorId',
1132 'set' => 'setAuthorId',
1133 'get' => 'getAuthorId',
1134 ],
1135 'CHAT_AUTHOR_ID' => [
1136 'alias' => 'AUTHOR_ID',
1137 ],
1138 'COLOR' => [
1139 'field' => 'color',
1140 'get' => 'getColor',
1141 'set' => 'setColor',
1142 'beforeSave' => 'checkColor',
1143 // 'beforeSave' => 'validateColor', /** @see Chat::validateColor */
1144 //'default' => 'getDefaultColor', /** @see Chat::getDefaultColor */
1145 ],
1146 'TITLE' => [
1147 'field' => 'title',
1148 'set' => 'setTitle',
1149 'get' => 'getTitle',
1150 'beforeSave' => 'checkTitle',
1151 //'default' => 'getDefaultTitle', /** @see Chat::getDefaultTitle */
1152 ],
1153 'DESCRIPTION' => [
1154 'field' => 'description',
1155 'get' => 'getDescription',
1156 'set' => 'setDescription',
1157 ],
1158 'PARENT_ID' => [
1159 'field' => 'parentChatId',
1160 'get' => 'getParentId',
1161 'set' => 'setParentId',
1162 ],
1163 'PARENT_MID' => [
1164 'field' => 'parentMessageId',
1165 'get' => 'getParentMessageId',
1166 'set' => 'setParentMessageId',
1167 ],
1168 'EXTRANET' => [
1169 'field' => 'extranet',
1170 'get' => 'getExtranet',
1171 'set' => 'setExtranet',
1172 'default' => 'getDefaultExtranet',
1173 ],
1174 'AVATAR' => [
1175 'field' => 'avatarId',
1176 'get' => 'getAvatarId',
1177 'set' => 'setAvatarId',
1178 ],
1179 'PIN_MESSAGE_ID' => [
1180 'field' => 'pinMessageId',
1181 'get' => 'getPinMessageId',
1182 'set' => 'setPinMessageId',
1183 ],
1184 'CALL_TYPE' => [
1185 'field' => 'callType',
1186 'get' => 'getCallType',
1187 'set' => 'setCallType',
1188 ],
1189 'CALL_NUMBER' => [
1190 'field' => 'callNumber',
1191 'get' => 'getCallNumber',
1192 'set' => 'setCallNumber',
1193 ],
1194 'ENTITY_TYPE' => [
1195 'field' => 'entityType',
1196 'get' => 'getEntityType',
1197 'set' => 'setEntityType',
1198 'default' => 'getDefaultEntityType',
1199 ],
1200 'ENTITY_ID' => [
1201 'field' => 'entityId',
1202 'get' => 'getEntityId',
1203 'set' => 'setEntityId',
1204 ],
1205 'ENTITY_DATA_1' => [
1206 'field' => 'entityData1',
1207 'get' => 'getEntityData1',
1208 'set' => 'setEntityData1',
1209 ],
1210 'ENTITY_DATA_2' => [
1211 'field' => 'entityData2',
1212 'get' => 'getEntityData2',
1213 'set' => 'setEntityData2',
1214 ],
1215 'ENTITY_DATA_3' => [
1216 'field' => 'entityData3',
1217 'get' => 'getEntityData3',
1218 'set' => 'setEntityData3',
1219 ],
1220 'DISK_FOLDER_ID' => [
1221 'field' => 'diskFolderId',
1222 'get' => 'getDiskFolderId',
1223 'set' => 'setDiskFolderId',
1224 ],
1225 'MESSAGE_COUNT' => [
1226 'field' => 'messageCount',
1227 'get' => 'getMessageCount',
1228 'set' => 'setMessageCount',
1229 ],
1230 'USER_COUNT' => [
1231 'field' => 'userCount',
1232 'get' => 'getUserCount',
1233 'set' => 'setUserCount',
1234 ],
1235 'PREV_MESSAGE_ID' => [
1236 'field' => 'prevMessageId',
1237 'get' => 'getPrevMessageId',
1238 'set' => 'setPrevMessageId',
1239 ],
1240 'LAST_MESSAGE_ID' => [
1241 'field' => 'lastMessageId',
1242 'get' => 'getLastMessageId',
1243 'set' => 'setLastMessageId',
1244 ],
1245 'LAST_MESSAGE_STATUS' => [
1246 'field' => 'lastMessageStatus',
1247 'get' => 'getLastMessageStatus',
1248 'set' => 'setLastMessageStatus',
1249 'default' => 'getDefaultLastMessageStatus',
1250 ],
1251 'DATE_CREATE' => [
1252 'field' => 'dateCreate',
1253 'get' => 'getDateCreate',
1254 'set' => 'setDateCreate',
1255 'default' => 'getDefaultDateCreate',
1256 ],
1257 'MANAGE_USERS_ADD' => [
1258 'field' => 'manageUsersAdd',
1259 'get' => 'getManageUsersAdd',
1260 'set' => 'setManageUsersAdd',
1261 'default' => 'getDefaultManageUsersAdd',
1262 ],
1263 'MANAGE_USERS_DELETE' => [
1264 'field' => 'manageUsersDelete',
1265 'get' => 'getManageUsersDelete',
1266 'set' => 'setManageUsersDelete',
1267 'default' => 'getDefaultManageUsersDelete',
1268 ],
1269 'MANAGE_UI' => [
1270 'field' => 'manageUI',
1271 'get' => 'getManageUI',
1272 'set' => 'setManageUI',
1273 'default' => 'getDefaultManageUI',
1274 ],
1275 'MANAGE_SETTINGS' => [
1276 'field' => 'manageSettings',
1277 'get' => 'getManageSettings',
1278 'set' => 'setManageSettings',
1279 'default' => 'getDefaultManageSettings',
1280 ],
1281 'DISAPPEARING_TIME' => [
1282 'field' => 'disappearingTime',
1283 'get' => 'getDisappearingTime',
1284 'set' => 'setDisappearingTime',
1285 ],
1286 'CAN_POST' => [
1287 'field' => 'canPost',
1288 'get' => 'getCanPost',
1289 'set' => 'setCanPost',
1290 'default' => 'getDefaultCanPost',
1291 ],
1292 'USERS' => [
1293 'get' => 'getUserIds',
1294 'set' => 'setUserIds',
1295 ],
1296 'ALIAS' => [
1297 'field' => 'aliasName',
1298 'get' => 'getAliasName',
1299 'set' => 'setAliasName',
1300 'loadFilter' => 'prepareAliasToLoad',
1301 'skipSave' => true,
1302 ],
1303 'RELATIONS' => [
1304 'set' => 'setRelations',
1305 'skipSave' => true,
1306 ]
1307 ];
1308 }
1309
1313 public static function getDataClass(): string
1314 {
1315 return ChatTable::class;
1316 }
1317
1321 public function getPrimaryId(): ?int
1322 {
1323 return $this->getChatId();
1324 }
1325
1330 public function setPrimaryId(int $primaryId): self
1331 {
1332 return $this->setChatId($primaryId);
1333 }
1334
1335 //endregion
1336
1337 //region Search
1338
1346 public static function find(array $params, ?Context $context = null): Result
1347 {
1348 $result = new Result;
1349
1350 if ($params['CHAT_ID'] <= 0)
1351 {
1352 return $result->addError(new ChatError(ChatError::WRONG_PARAMETER));
1353 }
1354
1355 $connection = \Bitrix\Main\Application::getConnection();
1356
1357 $context = $context ?? Locator::getContext();
1358
1359 if ($context->getUserId() == 0)
1360 {
1361 $res = $connection->query("
1362 SELECT
1363 C.ID as CHAT_ID,
1364 C.PARENT_ID as CHAT_PARENT_ID,
1365 C.PARENT_MID as CHAT_PARENT_MID,
1366 C.TITLE as CHAT_TITLE,
1367 C.AUTHOR_ID as CHAT_AUTHOR_ID,
1368 C.TYPE as CHAT_TYPE,
1369 C.AVATAR as CHAT_AVATAR,
1370 C.COLOR as CHAT_COLOR,
1371 C.ENTITY_TYPE as CHAT_ENTITY_TYPE,
1372 C.ENTITY_ID as CHAT_ENTITY_ID,
1373 C.ENTITY_DATA_1 as CHAT_ENTITY_DATA_1,
1374 C.ENTITY_DATA_2 as CHAT_ENTITY_DATA_2,
1375 C.ENTITY_DATA_3 as CHAT_ENTITY_DATA_3,
1376 C.EXTRANET as CHAT_EXTRANET,
1377 C.PREV_MESSAGE_ID as CHAT_PREV_MESSAGE_ID,
1378 '1' as RID,
1379 'Y' as IS_MANAGER
1380 FROM b_im_chat C
1381 WHERE C.ID = ".(int)$params['CHAT_ID']."
1382 ");
1383 }
1384 else
1385 {
1386 if (empty($params['FROM_USER_ID']))
1387 {
1388 $params['FROM_USER_ID'] = $context->getUserId();
1389 }
1390
1391 $params['FROM_USER_ID'] = (int)$params['FROM_USER_ID'];
1392 if ($params['FROM_USER_ID'] <= 0)
1393 {
1394 return $result->addError(new ChatError(ChatError::WRONG_SENDER));
1395 }
1396
1397 $res = $connection->query("
1398 SELECT
1399 C.ID as CHAT_ID,
1400 C.PARENT_ID as CHAT_PARENT_ID,
1401 C.PARENT_MID as CHAT_PARENT_MID,
1402 C.TITLE as CHAT_TITLE,
1403 C.AUTHOR_ID as CHAT_AUTHOR_ID,
1404 C.TYPE as CHAT_TYPE,
1405 C.AVATAR as CHAT_AVATAR,
1406 C.COLOR as CHAT_COLOR,
1407 C.ENTITY_TYPE as CHAT_ENTITY_TYPE,
1408 C.ENTITY_ID as CHAT_ENTITY_ID,
1409 C.ENTITY_DATA_1 as CHAT_ENTITY_DATA_1,
1410 C.ENTITY_DATA_2 as CHAT_ENTITY_DATA_2,
1411 C.ENTITY_DATA_3 as CHAT_ENTITY_DATA_3,
1412 C.EXTRANET as CHAT_EXTRANET,
1413 C.PREV_MESSAGE_ID as CHAT_PREV_MESSAGE_ID,
1414 R.USER_ID as RID,
1415 R.MANAGER as IS_MANAGER
1416 FROM b_im_chat C
1417 LEFT JOIN b_im_relation R
1418 ON R.CHAT_ID = C.ID
1419 AND R.USER_ID = ".$params['FROM_USER_ID']."
1420 WHERE C.ID = ".(int)$params['CHAT_ID']."
1421 ");
1422 }
1423
1424 if ($row = $res->fetch())
1425 {
1426 $result->setResult([
1427 'ID' => (int)$row['CHAT_ID'],
1428 'TYPE' => $row['CHAT_TYPE'],
1429 'ENTITY_TYPE' => $row['CHAT_ENTITY_TYPE'],
1430 'ENTITY_ID' => $row['CHAT_ENTITY_ID'],
1431 /*'RELATIONS' => [
1432 (int)$row['RID'] => [
1433 'CHAT_ID' => (int)$row['CHAT_ID'],
1434 'USER_ID' => (int)$row['RID'],
1435 'IS_MANAGER' => $row['IS_MANAGER'],
1436 ]
1437 ]*/
1438 ]);
1439 }
1440
1441 return $result;
1442 }
1443
1451 public static function getSharedChatsWithUser(int $userId, int $limit = 50, int $offset = 0, ?int $currentUserId = null): array
1452 {
1453 $currentUserId ??= Im\V2\Entity\User\User::getCurrent()->getId();
1454 //todo: change with ChatCollection
1455 $chats = [];
1456
1457 $recentCollection = Im\Model\RecentTable::query()
1458 ->setSelect(['ITEM_ID', 'DATE_MESSAGE'])
1459 ->registerRuntimeField(
1460 new Reference(
1461 'RELATION',
1462 RelationTable::class,
1463 Join::on('this.ITEM_ID', 'ref.CHAT_ID')
1464 ->where('this.USER_ID', $currentUserId)
1465 ->where('ref.USER_ID', $userId)
1466 ->whereIn('this.ITEM_TYPE', [self::IM_TYPE_CHAT, self::IM_TYPE_OPEN]),
1467 ['join_type' => Join::TYPE_INNER]
1468 )
1469 )
1470 ->setOrder(['DATE_MESSAGE' => 'DESC'])
1471 ->setLimit($limit)
1472 ->setOffset($offset)
1473 ->fetchCollection()
1474 ;
1475
1476 foreach ($recentCollection as $recentItem)
1477 {
1478 $chat = self::getInstance($recentItem->getItemId());
1479 if ($chat instanceof Im\V2\Chat\NullChat)
1480 {
1481 continue;
1482 }
1483 $chat->dateMessage = $recentItem->getDateMessage();
1484 $chats[$chat->getId()] = $chat;
1485 }
1486
1487 return $chats;
1488 }
1489
1490 //endregion
1491
1492
1493 //region Setters & Getters
1494
1495 protected function setChatId(int $chatId): self
1496 {
1497 if (!$this->chatId)
1498 {
1499 $this->chatId = $chatId;
1500 }
1501 return $this;
1502 }
1503
1504 public function getChatId(): ?int
1505 {
1506 return $this->chatId;
1507 }
1508
1509 public function getId(): ?int
1510 {
1511 return $this->getChatId();
1512 }
1513
1518 public function setDialogId(string $dialogId): self
1519 {
1520 $this->dialogId = $dialogId;
1521
1522 if (\Bitrix\Im\Common::isChatId($dialogId))
1523 {
1524 $this->setChatId((int)\Bitrix\Im\Dialog::getChatId($dialogId));
1525 if (!$this->getType())
1526 {
1527 $this->setType(self::IM_TYPE_CHAT);
1528 }
1529 }
1530 else
1531 {
1532 if (!$this->getType())
1533 {
1534 $this->setType(self::IM_TYPE_PRIVATE);
1535 }
1536 }
1537
1538 return $this;
1539 }
1540
1545 abstract public function allowMention(): bool;
1546
1547 public function getDialogId(): ?string
1548 {
1549 if ($this->dialogId || !$this->getChatId())
1550 {
1551 return $this->dialogId;
1552 }
1553
1554 $this->dialogId = 'chat'. $this->getChatId();
1555
1556 return $this->dialogId;
1557 }
1558
1559 public function getDialogContextId(): ?string
1560 {
1561 return $this->getDialogId();
1562 }
1563
1568 public function setType(string $type): self
1569 {
1570 if (!in_array($type, self::IM_TYPES))
1571 {
1572 if (in_array($type, array_keys(self::IM_TYPES_TRANSLATE), true))
1573 {
1574 $type = self::IM_TYPES_TRANSLATE[$type];
1575 }
1576 else
1577 {
1578 $type = $this->getDefaultType();
1579 }
1580 }
1581
1582 $this->type = $type;
1583 return $this;
1584 }
1585
1586 public function getType(): string
1587 {
1588 if (!$this->type)
1589 {
1590 $this->type = $this->getDefaultType();
1591 }
1592
1593 return $this->type;
1594 }
1595
1596 abstract protected function getDefaultType(): string;
1597
1598 protected function beforeSaveType(): Result
1599 {
1600 $check = new Result;
1601
1602 if (!in_array($this->type, self::IM_TYPES, true))
1603 {
1604 $check->addError(new ChatError(ChatError::WRONG_TYPE,'Wrong chat type'));
1605 }
1606
1607 return $check;
1608 }
1609
1610 // Author
1611 public function setAuthorId(int $authorId): self
1612 {
1613 $this->authorId = $authorId;
1614 return $this;
1615 }
1616
1617 public function getAuthorId(): ?int
1618 {
1619 return $this->authorId;
1620 }
1621
1622 public function getAuthor(): Entity\User\User
1623 {
1624 return Im\V2\Entity\User\User::getInstance($this->getAuthorId());
1625 }
1626
1627 // Chat title
1628 public function setTitle(?string $title): self
1629 {
1630 $this->title = $title ? mb_substr(trim($title), 0, 255) : null;
1631 return $this;
1632 }
1633
1634 public function getTitle(): ?string
1635 {
1636 return $this->title;
1637 }
1638
1639 public function getDisplayedTitle(): ?string
1640 {
1641 return $this->title;
1642 }
1643
1644 // Chat description
1645 public function setDescription(?string $description): self
1646 {
1647 $this->description = $description ? trim($description) : null;
1648 return $this;
1649 }
1650
1651 public function getDescription(): ?string
1652 {
1653 return $this->description;
1654 }
1655
1656 // Chat color
1657 public function setColor(?string $color): self
1658 {
1659 $this->color = $color ? trim($color) : null;
1660 return $this;
1661 }
1662
1663 public function getColor(): ?string
1664 {
1665 return $this->color;
1666 }
1667
1668 public function validateColor(): Result
1669 {
1670 $check = new Result;
1671 if (!Color::isSafeColor($this->color))
1672 {
1673 $check->addError(new ChatError(ChatError::WRONG_COLOR,'Wrong chat color'));
1674 }
1675 return $check;
1676 }
1677
1678 public function getDefaultColor(): string
1679 {
1680 $color = '';
1681
1682 return $color;
1683 }
1684
1685 // parent chat
1686 public function setParentChatId(int $parentChatId): self
1687 {
1688 $this->parentChatId = $parentChatId > 0 ? $parentChatId : 0;
1689 return $this;
1690 }
1691
1692 public function getParentChatId(): ?int
1693 {
1694 return $this->parentChatId;
1695 }
1696
1697 // parent message
1698 public function setParentMessageId(int $messageId): self
1699 {
1700 $this->parentMessageId = $messageId > 0 ? $messageId : 0;
1701 return $this;
1702 }
1703
1704 public function getParentMessageId(): int
1705 {
1706 return $this->parentMessageId;
1707 }
1708
1709 // extranet
1710 public function setExtranet(?bool $extranet): self
1711 {
1712 $this->extranet = is_bool($extranet) ? $extranet : null;
1713 return $this;
1714 }
1715
1716 public function getExtranet(): ?bool
1717 {
1718 return $this->extranet;
1719 }
1720
1721 public function getDefaultExtranet(): bool
1722 {
1723 return false;
1724 }
1725
1726 // avatar's file Id
1727 public function setAvatarId(?int $avatarId): self
1728 {
1729 $this->avatarId = is_integer($avatarId) ? $avatarId : null;
1730 return $this;
1731 }
1732
1733 public function getAvatarId(): ?int
1734 {
1735 return $this->avatarId;
1736 }
1737
1738 public function getAvatar(int $size = 200, bool $addBlankPicture = false): string
1739 {
1740 $url = $addBlankPicture? '/bitrix/js/im/images/blank.gif': '';
1741
1742 if ((int)$this->getAvatarId() > 0 && $size > 0)
1743 {
1744 $arFileTmp = \CFile::ResizeImageGet(
1745 $this->getAvatarId(),
1746 ['width' => $size, 'height' => $size],
1747 BX_RESIZE_IMAGE_EXACT,
1748 false,
1749 false,
1750 true
1751 );
1752 if (!empty($arFileTmp['src']))
1753 {
1754 $url = $arFileTmp['src'];
1755 }
1756 }
1757 return $url;
1758 }
1759
1760 // pined message Id
1761 public function setPinMessageId(?int $pinMessageId): self
1762 {
1763 $this->pinMessageId = is_integer($pinMessageId) ? $pinMessageId : null;
1764 return $this;
1765 }
1766
1767 public function getPinMessageId(): ?int
1768 {
1769 return $this->pinMessageId;
1770 }
1771
1772 // callType
1773 public function setCallType(?int $callType): self
1774 {
1775 $this->callType = is_integer($callType) ? $callType : null;
1776 return $this;
1777 }
1778
1779 public function getCallType(): ?int
1780 {
1781 return $this->callType;
1782 }
1783
1784 // callNumber
1785 public function setCallNumber(?string $callNumber): self
1786 {
1787 $this->callNumber = $callNumber ? trim($callNumber) : null;
1788 return $this;
1789 }
1790
1791 public function getCallNumber(): ?string
1792 {
1793 return $this->callNumber;
1794 }
1795
1796 // entity Type
1797 public function setEntityType(?string $entityType): self
1798 {
1799 $this->entityType = $entityType ? trim($entityType) : null;
1800 return $this;
1801 }
1802
1803 public function getEntityType(): ?string
1804 {
1805 if ($this->entityType)
1806 {
1807 return $this->entityType;
1808 }
1809
1810 return $this->getDefaultEntityType();
1811 }
1812
1813 protected function getDefaultEntityType(): ?string
1814 {
1815 return null;
1816 }
1817
1818 // entity Id
1819 public function setEntityId(?string $entityId): self
1820 {
1821 $this->entityId = $entityId ? trim($entityId) : null;
1822 return $this;
1823 }
1824
1825 public function getEntityId(): ?string
1826 {
1827 return $this->entityId;
1828 }
1829
1830 // entity Data1
1831 public function setEntityData1(?string $entityData1): self
1832 {
1833 $this->entityData1 = $entityData1 ? trim($entityData1) : null;
1834 return $this;
1835 }
1836
1837 public function getEntityData1(): ?string
1838 {
1839 return $this->entityData1;
1840 }
1841
1842 // entity Data2
1843 public function setEntityData2(?string $entityData2): self
1844 {
1845 $this->entityData2 = $entityData2 ? trim($entityData2) : null;
1846 return $this;
1847 }
1848
1849 public function getEntityData2(): ?string
1850 {
1851 return $this->entityData2;
1852 }
1853
1854 // entityData3
1855 public function setEntityData3(?string $entityData3): self
1856 {
1857 $this->entityData3 = $entityData3 ? trim($entityData3) : null;
1858 return $this;
1859 }
1860
1861 public function getEntityData3(): ?string
1862 {
1863 return $this->entityData3;
1864 }
1865
1866 // disk Folder Id
1867 public function setDiskFolderId(?int $diskFolderId): self
1868 {
1869 $this->diskFolderId = is_integer($diskFolderId) ? $diskFolderId : null;
1870 return $this;
1871 }
1872
1873 public function getDiskFolderId(): ?int
1874 {
1875 return $this->diskFolderId;
1876 }
1877
1878 protected function setDiskFolder(?Folder $folder): void
1879 {
1880 $this->isDiskFolderFilled = true;
1881 $this->diskFolder = $folder;
1882 }
1883
1884 public function getOrCreateDiskFolder(): ?Folder
1885 {
1886 $folder = $this->getDiskFolder();
1887
1888 if ($folder === null)
1889 {
1890 $folder = $this->createDiskFolder();
1891 $this->setDiskFolder($folder);
1892 }
1893
1894 return $folder;
1895 }
1896
1897 public function getDiskFolder(): ?Folder
1898 {
1899 if (!Main\Loader::includeModule('disk'))
1900 {
1901 return null;
1902 }
1903
1904 if ($this->isDiskFolderFilled)
1905 {
1906 return $this->diskFolder;
1907 }
1908
1909 $diskFolderId = $this->getDiskFolderId();
1910 $folder = null;
1911
1912 if ($diskFolderId !== null && $diskFolderId !== 0)
1913 {
1914 $folder = \Bitrix\Disk\Folder::getById($diskFolderId);
1915 if (!($folder instanceof Folder) || (int)$folder->getStorageId() !== \CIMDisk::GetStorageId())
1916 {
1917 $folder = null;
1918 }
1919 }
1920
1921 $this->setDiskFolder($folder);
1922
1923 return $folder;
1924 }
1925
1926 protected function createDiskFolder(): ?Folder
1927 {
1928 $storage = \CIMDisk::GetStorage();
1929
1930 if (!$storage)
1931 {
1932 return null;
1933 }
1934
1935 $folderModel = $storage->addFolder(
1936 [
1937 'NAME' => "chat{$this->getId()}",
1938 'CREATED_BY' => $this->getContext()->getUserId(),
1939 ],
1941 true
1942 );
1943
1944 if ($folderModel)
1945 {
1946 $this->setDiskFolderId($folderModel->getId())->save();
1947 $accessProvider = new \Bitrix\Im\Access\ChatAuthProvider;
1948 $accessProvider->updateChatCodesByRelations($this->getId());
1949 }
1950
1951 return $folderModel;
1952 }
1953
1954 protected function getAccessCodesForDiskFolder(): array
1955 {
1956 $accessProvider = new \Bitrix\Im\Access\ChatAuthProvider;
1957 $driver = \Bitrix\Disk\Driver::getInstance();
1958 $rightsManager = $driver->getRightsManager();
1959 $accessCodes = [];
1960 // allow for access code `CHATxxx`
1961 $accessCodes[] = [
1962 'ACCESS_CODE' => $accessProvider->generateAccessCode($this->getId()),
1963 'TASK_ID' => $rightsManager->getTaskIdByName($rightsManager::TASK_EDIT)
1964 ];
1965
1966 return $accessCodes;
1967 }
1968
1969 protected function getStorageId(): int
1970 {
1971 return (int)\Bitrix\Main\Config\Option::get('im', 'disk_storage_id', 0);
1972 }
1973
1974 // message Count
1975 public function setMessageCount(int $messageCount): self
1976 {
1977 $this->messageCount = $messageCount > 0 ? $messageCount : 0;
1978 return $this;
1979 }
1980
1981 public function getMessageCount(): int
1982 {
1983 $this->fillNonCachedData();
1984
1985 return $this->messageCount;
1986 }
1987
1994 public function incrementMessageCount(int $increment = 1): self
1995 {
1996 $this->setMessageCount($this->getMessageCount() + $increment);
1997
1998 if ($this->getChatId())
1999 {
2000 ChatTable::update($this->getChatId(), [
2001 'MESSAGE_COUNT' => new Main\DB\SqlExpression('?# + ?i', 'MESSAGE_COUNT', $increment),
2002 'LAST_MESSAGE_ID' => $this->getLastMessageId(),
2003 ]);
2004 }
2005
2006 return $this;
2007 }
2008
2014 public function updateParentMessageCount(): self
2015 {
2016 if (
2017 $this->getChatId()
2018 && $this->getParentMessageId()
2019 && $this->getMessageCount()
2020 )
2021 {
2022 $message = new Message($this->getParentMessageId());
2023 $message->getParams()
2024 ->fill([
2025 Params::CHAT_MESSAGE => $this->getMessageCount(),
2026 Params::CHAT_LAST_DATE => new DateTime()
2027 ])
2028 ->save();
2029
2030 \CIMMessageParam::SendPull($this->getParentMessageId(), [Params::CHAT_MESSAGE, Params::CHAT_LAST_DATE]);
2031 }
2032
2033 return $this;
2034 }
2035
2036 // user Count
2037 public function setUserCount(int $userCount): self
2038 {
2039 $this->userCount = $userCount > 0 ? $userCount : 0;
2040 return $this;
2041 }
2042
2043 public function getUserCount(): int
2044 {
2045 $this->fillNonCachedData();
2046
2047 return $this->userCount;
2048 }
2049
2050 // prev Message Id
2051 public function setPrevMessageId(int $prevMessageId): self
2052 {
2053 $this->prevMessageId = $prevMessageId > 0 ? $prevMessageId : 0;
2054 return $this;
2055 }
2056
2057 public function getPrevMessageId(): int
2058 {
2059 return $this->prevMessageId;
2060 }
2061
2062 // last Message Id
2063 public function setLastMessageId(int $lastMessageId): self
2064 {
2065 $this->lastMessageId = $lastMessageId > 0 ? $lastMessageId : 0;
2066 return $this;
2067 }
2068
2069 public function getLastMessageId(): ?int
2070 {
2071 $this->fillNonCachedData();
2072
2073 return $this->lastMessageId;
2074 }
2075
2076 public function getLastFileId(): int
2077 {
2078 $this->lastFileId ??= \CIMDisk::GetMaxFileId($this->getId());
2079
2080 return $this->lastFileId;
2081 }
2082
2083 // last Message Status
2084 public function setLastMessageStatus(?string $lastMessageStatus): self
2085 {
2086 $this->lastMessageStatus = $lastMessageStatus ? trim($lastMessageStatus) : null;
2087 return $this;
2088 }
2089
2090 public function getLastMessageStatus(): ?string
2091 {
2092 return $this->lastMessageStatus;
2093 }
2094
2095 public function getDefaultLastMessageStatus(): string
2096 {
2097 return \IM_MESSAGE_STATUS_RECEIVED;
2098 }
2099
2100 // Create date
2101 public function setDateCreate(?DateTime $dateCreate): self
2102 {
2103 $this->dateCreate = $dateCreate ? $dateCreate : null;
2104 return $this;
2105 }
2106
2107 public function getDateCreate(): ?DateTime
2108 {
2109 return $this->dateCreate;
2110 }
2111
2113 {
2114 return new DateTime;
2115 }
2116
2117 protected function getReadService(): ReadService
2118 {
2119 if ($this->readService === null)
2120 {
2121 $this->readService = new ReadService();
2122 $this->readService->setContext($this->context);
2123 }
2124
2125 return $this->readService;
2126 }
2127
2132 public function getRelations(array $options = []): RelationCollection
2133 {
2134 $optionsHash = md5(serialize($options));
2135
2136 if (isset($this->relations[$optionsHash]))
2137 {
2138 return $this->relations[$optionsHash];
2139 }
2140
2141 $filter = $options['FILTER'] ?? [];
2142 $filter['CHAT_ID'] = $this->getChatId();
2143
2144 $relations = RelationCollection::find(
2145 $filter,
2146 $options['ORDER'] ?? [],
2147 $options['LIMIT'] ?? null,
2148 $this->context,
2149 $options['SELECT'] ?? RelationCollection::COMMON_FIELDS
2150 );
2151
2152 $this->relations[$optionsHash] = $relations;
2153
2154 return $this->relations[$optionsHash];
2155 }
2156
2157 public function setRelations(RelationCollection $relations): self
2158 {
2159 $emptyOptionsHash = md5(serialize([]));
2160 $this->relations[$emptyOptionsHash] = $relations;
2161
2162 return $this;
2163 }
2164
2165 public function getSelfRelation(array $options = []): ?Relation
2166 {
2167 $userId = $this->getContext()->getUserId();
2168
2169 $emptyOptionsHash = md5(serialize([]));
2170 if (isset($this->relations[$emptyOptionsHash]))
2171 {
2172 return $this->relations[$emptyOptionsHash]->getByUserId($userId, $this->getChatId() ?? 0);
2173 }
2174
2175 $options['FILTER']['USER_ID'] = $options['FILTER']['USER_ID'] ?? $userId;
2176
2177 return $this->getRelations($options)->getByUserId($userId, $this->getChatId() ?? 0);
2178 }
2179
2180 public function getBotInChat(): array
2181 {
2182 $botInChat = [];
2183 $relations = $this->getRelations();
2184 foreach ($relations as $relation)
2185 {
2186 if ($relation->getUser()->getExternalAuthId() === Im\Bot::EXTERNAL_AUTH_ID)
2187 {
2188 $botInChat[$relation->getUserId()] = $relation->getUserId();
2189 }
2190 }
2191
2192 return $botInChat;
2193 }
2194
2195 public function checkTitle(): Result
2196 {
2197 return new Result;
2198 }
2199
2204 public function setManageUsersAdd(string $manageUsersAdd): self
2205 {
2206 $manageUsersAdd = mb_strtoupper($manageUsersAdd);
2207 if (!in_array(
2208 $manageUsersAdd,
2209 [self::MANAGE_RIGHTS_MEMBER, self::MANAGE_RIGHTS_OWNER, self::MANAGE_RIGHTS_MANAGERS],
2210 true
2211 ))
2212 {
2213 $manageUsersAdd = $this->getDefaultManageUsersAdd();
2214 }
2215 $this->manageUsersAdd = $manageUsersAdd ;
2216
2217 return $this;
2218 }
2219
2220 public function getManageUsersAdd(): ?string
2221 {
2222 return $this->manageUsersAdd;
2223 }
2224
2225
2226 public function getDefaultManageUsersAdd(): string
2227 {
2228 return self::MANAGE_RIGHTS_MEMBER;
2229 }
2230
2235 public function setManageUsersDelete(string $manageUsersDelete): self
2236 {
2237 $manageUsersDelete = mb_strtoupper($manageUsersDelete);
2238 if (!in_array(
2239 $manageUsersDelete,
2240 [self::MANAGE_RIGHTS_MEMBER, self::MANAGE_RIGHTS_OWNER, self::MANAGE_RIGHTS_MANAGERS],
2241 true
2242 ))
2243 {
2244 $manageUsersDelete = $this->getDefaultManageUsersDelete();
2245 }
2246 $this->manageUsersDelete = $manageUsersDelete ;
2247
2248 return $this;
2249 }
2250
2251 public function getManageUsersDelete(): ?string
2252 {
2253 return $this->manageUsersDelete;
2254 }
2255
2256
2257 public function getDefaultManageUsersDelete(): string
2258 {
2259 return self::MANAGE_RIGHTS_MANAGERS;
2260 }
2261
2266 public function setManageUI(string $manageUI): self
2267 {
2268 $manageUI = mb_strtoupper($manageUI);
2269 if (!in_array(
2270 $manageUI,
2271 [self::MANAGE_RIGHTS_MEMBER, self::MANAGE_RIGHTS_OWNER, self::MANAGE_RIGHTS_MANAGERS],
2272 true
2273 ))
2274 {
2275 $manageUI = $this->getDefaultManageUI();
2276 }
2277 $this->manageUI = $manageUI;
2278
2279 return $this;
2280 }
2281
2282 public function getManageUI(): ?string
2283 {
2284 return $this->manageUI;
2285 }
2286
2287 public function getDefaultManageUI(): string
2288 {
2289 return self::MANAGE_RIGHTS_MEMBER;
2290 }
2291
2296 public function setManageSettings(string $manageSettings): self
2297 {
2298 $manageSettings = mb_strtoupper($manageSettings);
2299 if (!in_array($manageSettings, [self::MANAGE_RIGHTS_OWNER, self::MANAGE_RIGHTS_MANAGERS], true))
2300 {
2301 $manageSettings = $this->getDefaultManageSettings();
2302 }
2303 $this->manageSettings = $manageSettings ;
2304
2305 return $this;
2306 }
2307
2308 public function getManageSettings(): ?string
2309 {
2310 return $this->manageSettings;
2311 }
2312
2313 public function getDefaultManageSettings(): string
2314 {
2315 return self::MANAGE_RIGHTS_OWNER;
2316 }
2317
2318 public function setDisappearingTime(int $disappearingTime): self
2319 {
2320 if (is_numeric($disappearingTime) && (int)$disappearingTime > -1)
2321 {
2322 $this->disappearingTime = $disappearingTime;
2323 }
2324
2325 return $this;
2326 }
2327
2328 public function getDisappearingTime(): ?int
2329 {
2330 return $this->disappearingTime;
2331 }
2332
2337 public function setCanPost(string $canPost): self
2338 {
2339 $canPost = mb_strtoupper($canPost);
2340 if (!in_array(
2341 $canPost,
2342 [
2343 self::MANAGE_RIGHTS_NONE,
2344 self::MANAGE_RIGHTS_MEMBER,
2345 self::MANAGE_RIGHTS_OWNER,
2346 self::MANAGE_RIGHTS_MANAGERS
2347 ],
2348 true
2349 ))
2350 {
2351 $canPost = $this->getDefaultCanPost();
2352 }
2353 $this->canPost = $canPost;
2354
2355 return $this;
2356 }
2357
2358 public function getCanPost(): ?string
2359 {
2360 return $this->canPost;
2361 }
2362
2363 public function getDefaultCanPost(): string
2364 {
2365 return self::MANAGE_RIGHTS_MEMBER;
2366 }
2367
2368 public static function getCanPostList(): array
2369 {
2370 return [
2371 self::MANAGE_RIGHTS_NONE => Loc::getMessage('IM_CHAT_CAN_POST_NONE'),
2372 self::MANAGE_RIGHTS_MEMBER => Loc::getMessage('IM_CHAT_CAN_POST_ALL'),
2373 self::MANAGE_RIGHTS_OWNER => Loc::getMessage('IM_CHAT_CAN_POST_OWNER'),
2374 self::MANAGE_RIGHTS_MANAGERS => Loc::getMessage('IM_CHAT_CAN_POST_MANAGERS')
2375 ];
2376 }
2377 //endregion
2378
2379 public function hasPostAccess(?int $userId = null): bool
2380 {
2381 $canPost = $this->getCanPost();
2382
2383 if (!$userId)
2384 {
2385 $userId = $this->getContext()->getUserId();
2386 }
2387
2388 switch ($canPost)
2389 {
2390 case self::MANAGE_RIGHTS_MEMBER:
2391 return !!$this->getRelations([
2392 'FILTER' => [
2393 'USER_ID' => $userId,
2394 ],
2395 'LIMIT' => 1,
2396 ])->count();
2397 case self::MANAGE_RIGHTS_MANAGERS:
2398 return !!$this->getRelations([
2399 'FILTER' => [
2400 'USER_ID' => $userId,
2401 'MANAGER' => 'Y'
2402 ],
2403 'LIMIT' => 1,
2404 ])->count();
2405 case self::MANAGE_RIGHTS_OWNER:
2406 return $userId === $this->getAuthorId();
2407 default:
2408 return false;
2409 }
2410 }
2411
2412 public function createChatIfNotExists(array $params): self
2413 {
2414 return $this;
2415 }
2416
2417 public function join(): self
2418 {
2419 return $this->withContextUser(0)->addUsers([$this->getContext()->getUserId()], [], false);
2420 }
2421
2426 public function addUsers(array $userIds, array $managerIds = [], ?bool $hideHistory = null, bool $withMessage = true, bool $skipRecent = false): self
2427 {
2428 if (empty($userIds) || !$this->getChatId())
2429 {
2430 return $this;
2431 }
2432
2433 $usersToAdd = $this->filterUsersToAdd($userIds);
2434
2435 if (empty($usersToAdd))
2436 {
2437 return $this;
2438 }
2439
2440 $relations = $this->getRelations();
2441 $this->addUsersToRelation($usersToAdd, $managerIds, $hideHistory);
2442 $this->updateStateAfterUsersAdd($usersToAdd)->save();
2443 $this->sendPushUsersAdd($usersToAdd, $relations);
2444 $this->sendEventUsersAdd($usersToAdd);
2445 if ($withMessage)
2446 {
2447 $this->sendMessageUsersAdd($usersToAdd, $skipRecent);
2448 }
2449
2450 return $this;
2451 }
2452
2453 protected function sendMessageUsersAdd(array $usersToAdd, bool $skipRecent = false): void
2454 {
2455 if (empty($usersToAdd))
2456 {
2457 return;
2458 }
2459
2460 $currentUserId = $this->getContext()->getUserId();
2461 $userCodes = [];
2462 foreach ($usersToAdd as $userId)
2463 {
2464 $userCodes[] = "[USER={$userId}][/USER]";
2465 }
2466 $userCodesString = implode(', ', $userCodes);
2467
2468 $addsOnlyHimself = count($usersToAdd) === 1 && (isset($usersToAdd[$currentUserId]) || $currentUserId === 0);
2469 if ($addsOnlyHimself)
2470 {
2471 $userIdToAdd = current($usersToAdd);
2472 $userToAdd = Im\V2\Entity\User\User::getInstance($userIdToAdd);
2473 $messageText = Loc::getMessage("IM_CHAT_SELF_JOIN_{$userToAdd->getGender()}", ['#USER_NAME#' => $userCodesString]);
2474 }
2475 elseif ($currentUserId === 0 && count($usersToAdd) > 1)
2476 {
2477 $messageText = Loc::getMessage('IM_CHAT_SELF_JOIN', ['#USERS_NAME#' => $userCodesString]);
2478 }
2479 else
2480 {
2481 $currentUser = Im\V2\Entity\User\User::getInstance($currentUserId);
2482 $messageText = Loc::getMessage(
2483 "IM_CHAT_JOIN_{$currentUser->getGender()}",
2484 [
2485 '#USER_1_NAME#' => htmlspecialcharsback($currentUser->getName()),
2486 '#USER_2_NAME#' => $userCodesString
2487 ]
2488 );
2489 }
2490
2491 \CIMChat::AddMessage([
2492 "TO_CHAT_ID" => $this->getId(),
2493 "MESSAGE" => $messageText,
2494 "FROM_USER_ID" => $currentUserId,
2495 "SYSTEM" => 'Y',
2496 "RECENT_ADD" => $skipRecent ? 'N' : 'Y',
2497 "PARAMS" => [
2498 "CODE" => 'CHAT_JOIN',
2499 "NOTIFY" => $this->getEntityType() === self::ENTITY_TYPE_LINE? 'Y': 'N',
2500 ],
2501 "PUSH" => 'N',
2502 "SKIP_USER_CHECK" => 'Y',
2503 ]);
2504 }
2505
2506 protected function sendPushUsersAdd(array $usersToAdd, RelationCollection $oldRelations): array
2507 {
2508 if (!\Bitrix\Main\Loader::includeModule('pull'))
2509 {
2510 return [];
2511 }
2512
2513 $pushMessage = [
2514 'module_id' => 'im',
2515 'command' => 'chatUserAdd',
2516 'params' => [
2517 'chatId' => $this->getChatId(),
2518 'dialogId' => 'chat' . $this->getChatId(),
2519 'chatTitle' => \Bitrix\Im\Text::decodeEmoji($this->getTitle() ?? ''),
2520 'chatOwner' => $this->getAuthorId(),
2521 'chatExtranet' => $this->getExtranet() ?? false,
2522 'users' => (new Im\V2\Entity\User\UserCollection($usersToAdd))->toRestFormat(),
2523 'newUsers' => array_values($usersToAdd),
2524 'userCount' => $this->getUserCount()
2525 ],
2526 'extra' => \Bitrix\Im\Common::getPullExtra()
2527 ];
2528
2529 $allUsersIds = $oldRelations->getUserIds();
2530 if ($this->getEntityType() === self::ENTITY_TYPE_LINE) //todo: refactor this
2531 {
2532 foreach ($oldRelations as $relation)
2533 {
2534 if ($relation->getUser()->getExternalAuthId() === 'imconnector')
2535 {
2536 unset($allUsersIds[$relation->getUserId()]);
2537 }
2538 }
2539 }
2540 \Bitrix\Pull\Event::add(array_values($allUsersIds), $pushMessage);
2541
2542 return $pushMessage;
2543 }
2544
2545 protected function updateStateAfterUsersAdd(array $usersToAdd): self
2546 {
2547 if (!($this->getExtranet() ?? false))
2548 {
2549 foreach ($usersToAdd as $userId)
2550 {
2551 if (Im\V2\Entity\User\User::getInstance($userId)->isExtranet())
2552 {
2553 $this->setExtranet(true);
2554 break;
2555 }
2556 }
2557 }
2558
2559 $userCount = RelationTable::getCount(
2560 Main\ORM\Query\Query::filter()
2561 ->where('CHAT_ID', $this->getId())
2562 ->where('USER.ACTIVE', true)
2563 );
2564
2565 $this->setUserCount($userCount);
2566
2567 \CIMDisk::ChangeFolderMembers($this->getId(), $usersToAdd);
2568 self::cleanAccessCache($this->getId());
2569 $this->updateIndex();
2570
2571 return $this;
2572 }
2573
2574 protected function addUsersToRelation(array $usersToAdd, array $managerIds = [], ?bool $hideHistory = null)
2575 {
2576 if (empty($usersToAdd))
2577 {
2578 return;
2579 }
2580
2581 $hideHistory ??= false;
2582
2583 $managersMap = [];
2584 foreach ($managerIds as $managerId)
2585 {
2586 $managersMap[$managerId] = $managerId;
2587 }
2588
2589 $relations = $this->getRelations();
2590 foreach ($usersToAdd as $userId)
2591 {
2592 $user = Im\V2\Entity\User\User::getInstance($userId);
2593 $hideHistory = (!static::EXTRANET_CAN_SEE_HISTORY && $user->isExtranet()) ? true : $hideHistory;
2594 $relation = new Relation();
2595 $relation
2596 ->setChatId($this->getId())
2597 ->setMessageType($this->getType())
2598 ->setUserId($userId)
2599 ->setLastId($this->getLastMessageId())
2600 ->setStatus(\IM_STATUS_READ)
2601 ->fillRestriction($hideHistory, $this)
2602 ;
2603 if (isset($managersMap[$userId]))
2604 {
2605 $relation->setManager(true);
2606 }
2607 $relations->add($relation);
2608 }
2609 $relations->save(true);
2610 $this->relations = [];
2611 }
2612
2613 protected function filterUsersToAdd(array $userIds): array
2614 {
2615 $usersAlreadyInChat = $this->getRelations()->getUserIds();
2616 $usersToAdd = [];
2617
2618 foreach ($userIds as $userId)
2619 {
2620 $userId = (int)$userId;
2621 if (!isset($usersAlreadyInChat[$userId]) && $userId > 0)
2622 {
2623 $user = Im\V2\Entity\User\User::getInstance($userId);
2624 if ($user->isExist() && $user->isActive())
2625 {
2626 $usersToAdd[$userId] = $userId;
2627 }
2628 }
2629 }
2630
2631 return $usersToAdd;
2632 }
2633
2634 protected function sendEventUsersAdd(array $usersToAdd): void
2635 {
2636 if (empty($usersToAdd))
2637 {
2638 return;
2639 }
2640
2641 foreach ($usersToAdd as $userId)
2642 {
2643 $relation = $this->getRelations()->getByUserId($userId, $this->getId());
2644 if ($relation === null)
2645 {
2646 continue;
2647 }
2648 if ($relation->getUser()->isBot())
2649 {
2650 IM\Bot::changeChatMembers($this->getId(), $userId);
2651 IM\Bot::onJoinChat('chat'.$this->getId(), [
2652 'CHAT_TYPE' => $this->getType(),
2653 'MESSAGE_TYPE' => $this->getType(),
2654 'BOT_ID' => $userId,
2655 'USER_ID' => $this->getContext()->getUserId(),
2656 'CHAT_ID' => $this->getId(),
2657 "CHAT_AUTHOR_ID" => $this->getAuthorId(),
2658 "CHAT_ENTITY_TYPE" => $this->getEntityType(),
2659 "CHAT_ENTITY_ID" => $this->getEntityId(),
2660 "ACCESS_HISTORY" => (int)$relation->getStartCounter() === 0,
2661 ]);
2662 }
2663 }
2664
2665 if (!empty($this->getEntityType()))
2666 {
2667 $converter = new Main\Engine\Response\Converter(Main\Engine\Response\Converter::TO_CAMEL | Main\Engine\Response\Converter::UC_FIRST);
2668 $eventCode = $converter->process($this->getEntityType());
2669 //$eventCode = str_replace('_', '', ucfirst(ucwords(mb_strtolower($chatEntityType), '_')));
2670 foreach(GetModuleEvents("im", "OnChatUserAddEntityType".$eventCode, true) as $arEvent)
2671 {
2672 ExecuteModuleEventEx($arEvent, array([
2673 'CHAT_ID' => $this->getId(),
2674 'NEW_USERS' => $usersToAdd,
2675 ]));
2676 }
2677 }
2678 }
2679
2680 public function deleteUser(int $userId, bool $withMessage = true, bool $skipRecent = false, bool $withNotification = true): Result
2681 {
2682 $relations = clone $this->getRelations();
2683 $userRelation = $this->getRelations()->getByUserId($userId, $this->getId());
2684
2685 if ($userRelation === null)
2686 {
2687 return (new Result())->addError(new Im\V2\Entity\User\UserError(Im\V2\Entity\User\UserError::NOT_FOUND));
2688 }
2689
2690 if ($this->getAuthorId() === $userId)
2691 {
2692 $this->changeAuthor();
2693 }
2694
2695 \CIMContactList::DeleteRecent($this->getId(), true, $userId);
2696 \Bitrix\Im\LastSearch::delete('chat' . $this->getId(), $userId);
2697
2698 $userRelation->delete();
2699 $this->updateStateAfterUserDelete($userId)->save();
2700 $this->sendPushUserDelete($userId, $relations);
2701 $this->sendEventUserDelete($userId);
2702 if ($withMessage)
2703 {
2704 $this->sendMessageUserDelete($userId, $skipRecent);
2705 }
2706 if ($withNotification && $this->getContext()->getUserId() !== $userId)
2707 {
2708 $this->sendNotificationUserDelete($userId);
2709 }
2710
2711 return new Result();
2712 }
2713
2714 protected function sendMessageUserDelete(int $userId, bool $skipRecent = false): void
2715 {
2716 if ($this->getEntityType() === 'ANNOUNCEMENT')
2717 {
2718 return;
2719 }
2720
2721 $messageText = $this->getMessageUserDeleteText($userId);
2722
2723 if ($messageText === '')
2724 {
2725 return;
2726 }
2727
2728 \CIMChat::AddMessage([
2729 "TO_CHAT_ID" => $this->getId(),
2730 "MESSAGE" => $messageText,
2731 "FROM_USER_ID" => $this->getContext()->getUserId(),
2732 "SYSTEM" => 'Y',
2733 "RECENT_ADD" => $skipRecent ? 'N' : 'Y',
2734 "PARAMS" => [
2735 "CODE" => 'CHAT_LEAVE',
2736 "NOTIFY" => $this->getEntityType() === 'LINES'? 'Y': 'N',
2737 ],
2738 "PUSH" => 'N',
2739 "SKIP_USER_CHECK" => "Y",
2740 ]);
2741 }
2742
2743 protected function sendNotificationUserDelete(int $userId): void
2744 {
2745 if ($userId === $this->getContext()->getUserId())
2746 {
2747 return;
2748 }
2749 $gender = $this->getContext()->getUser()->getGender();
2750 $userName = $this->getContext()->getUser()->getName();
2751 $userName = "[USER={$this->getContext()->getUserId()}]{$userName}[/USER]";
2752 $notificationCallback = fn (?string $languageId = null) => Loc::getMessage(
2753 'IM_CHAT_KICK_NOTIFICATION_'. $gender,
2754 ["#USER_NAME#" => $userName],
2755 $languageId
2756 );
2757
2758 $notificationFields = [
2759 'TO_USER_ID' => $userId,
2760 'FROM_USER_ID' => 0,
2761 'NOTIFY_TYPE' => IM_NOTIFY_SYSTEM,
2762 'NOTIFY_MODULE' => 'im',
2763 'NOTIFY_TITLE' => htmlspecialcharsback(\Bitrix\Main\Text\Emoji::decode($this->getTitle())),
2764 'NOTIFY_MESSAGE' => $notificationCallback,
2765 ];
2766 CIMNotify::Add($notificationFields);
2767 }
2768
2769 protected function getMessageUserDeleteText(int $userId): string
2770 {
2771 $currentUser = $this->getContext()->getUser();
2772 if ($this->getContext()->getUserId() === $userId)
2773 {
2774 return Loc::getMessage("IM_CHAT_LEAVE_{$currentUser->getGender()}", ['#USER_NAME#' => htmlspecialcharsback($currentUser->getName())]);
2775 }
2776
2777 $user = Im\V2\Entity\User\User::getInstance($userId);
2778
2779 return Loc::getMessage("IM_CHAT_KICK_{$currentUser->getGender()}", ['#USER_1_NAME#' => htmlspecialcharsback($currentUser->getName()), '#USER_2_NAME#' => htmlspecialcharsback($user->getName())]);
2780 }
2781
2782 protected function updateStateAfterUserDelete(int $deletedUserId): self
2783 {
2784 $this->relations = [];
2785 if (
2786 ($this->getExtranet() ?? false)
2787 && $this->getRelations()->filter(fn (Relation $relation) => $relation->getUser()->isExtranet())->count() <= 0
2788 )
2789 {
2790 $this->setExtranet(false);
2791 }
2792
2793 $userCount = RelationTable::getCount(
2794 Main\ORM\Query\Query::filter()
2795 ->where('CHAT_ID', $this->getId())
2796 ->where('USER.ACTIVE', true)
2797 );
2798
2799 $this->setUserCount($userCount);
2800
2801 \CIMDisk::ChangeFolderMembers($this->getId(), $deletedUserId, false);
2802 self::cleanAccessCache($this->getId());
2803 $this->updateIndex();
2804
2805 return $this;
2806 }
2807
2808 protected function sendEventUserDelete(int $userId): void
2809 {
2810 $user = Im\V2\Entity\User\User::getInstance($userId);
2811 if ($user->isBot())
2812 {
2813 IM\Bot::changeChatMembers($this->getId(), $userId);
2814 IM\Bot::onLeaveChat('chat'.$this->getId(), [
2815 'CHAT_TYPE' => $this->getType(),
2816 'MESSAGE_TYPE' => $this->getType(),
2817 'BOT_ID' => $userId,
2818 'USER_ID' => $this->getContext()->getUserId(),
2819 "CHAT_AUTHOR_ID" => $this->getAuthorId(),
2820 "CHAT_ENTITY_TYPE" => $this->getEntityType(),
2821 "CHAT_ENTITY_ID" => $this->getEntityId(),
2822 ]);
2823 }
2824
2825 if (!empty($this->getEntityType()))
2826 {
2827 $converter = new Main\Engine\Response\Converter(Main\Engine\Response\Converter::TO_CAMEL | Main\Engine\Response\Converter::UC_FIRST);
2828 $eventCode = $converter->process($this->getEntityType());
2829 //$eventCode = str_replace('_', '', ucfirst(ucwords(mb_strtolower($chatEntityType), '_')));
2830 foreach(GetModuleEvents("im", "OnChatUserDeleteEntityType".$eventCode, true) as $arEvent)
2831 {
2832 ExecuteModuleEventEx($arEvent, array([
2833 'CHAT_ID' => $this->getId(),
2834 'USER_ID' => $userId,
2835 ]));
2836 }
2837 }
2838 }
2839
2840 protected function sendPushUserDelete(int $userId, RelationCollection $oldRelations): array
2841 {
2842 if (!\Bitrix\Main\Loader::includeModule('pull'))
2843 {
2844 return [];
2845 }
2846
2847 $pushMessage = [
2848 'module_id' => 'im',
2849 'command' => 'chatUserLeave',
2850 'params' => [
2851 'chatId' => $this->getChatId(),
2852 'dialogId' => 'chat' . $this->getChatId(),
2853 'chatTitle' => \Bitrix\Im\Text::decodeEmoji($this->getTitle() ?? ''),
2854 'userId' => $userId,
2855 'message' => $userId === $this->getContext()->getUserId() ? '' : $this->getMessageUserDeleteText($userId),
2856 'userCount' => $this->getUserCount()
2857 ],
2858 'extra' => \Bitrix\Im\Common::getPullExtra()
2859 ];
2860
2861 $allUsersIds = $oldRelations->getUserIds();
2862 if ($this->getEntityType() === self::ENTITY_TYPE_LINE) //todo: refactor this
2863 {
2864 foreach ($oldRelations as $relation)
2865 {
2866 if ($relation->getUser()->getExternalAuthId() === 'imconnector')
2867 {
2868 unset($allUsersIds[$relation->getUserId()]);
2869 }
2870 }
2871 }
2872 \Bitrix\Pull\Event::add(array_values($allUsersIds), $pushMessage);
2873
2874 return $pushMessage;
2875 }
2876
2877 public function changeAuthor(): void
2878 {
2879 $currentAuthorId = $this->getAuthorId();
2880 $relations = $this->getRelations();
2881 $authorRelation = $relations->getByUserId($currentAuthorId, $this->getId());
2882 if ($authorRelation !== null)
2883 {
2884 $authorRelation->setManager(false);
2885 }
2886 $otherRealUserRelation = $relations->filter(static function (Relation $relation) use ($currentAuthorId) {
2887 $user = $relation->getUser();
2888
2889 return $user->getId() !== $currentAuthorId
2890 && $user->isActive()
2891 && !$user->isBot()
2892 && !$user->isExtranet()
2893 && !$user->isConnector()
2894 ;
2895 })->getAny();
2896
2897 if (!$otherRealUserRelation instanceof Relation)
2898 {
2899 return;
2900 }
2901
2902 $this->setAuthorId($otherRealUserRelation->getUserId());
2903 $otherRealUserRelation->setManager(true);
2904 $relations->save(true);
2905 }
2906
2907 public function removeUsers(array $userIds): self
2908 {
2909 if (!$this->getChatId() || empty($userIds) || !count($userIds))
2910 {
2911 return $this;
2912 }
2913
2914 $userIds = filter_var(
2915 $userIds,
2916 FILTER_VALIDATE_INT,
2917 [
2918 'flags' => FILTER_REQUIRE_ARRAY,
2919 'options' => ['min_range' => 1],
2920 ]
2921 );
2922
2923 foreach ($userIds as $key => $userId)
2924 {
2925 if (!is_int($userId))
2926 {
2927 unset($userIds[$key]);
2928 }
2929 }
2930
2931 $relations = $this->getRelations([
2932 'CHAT_ID' => $this->getChatId(),
2933 ]);
2934
2935 $connection = Application::getConnection();
2936
2937 RelationTable::deleteByFilter([
2938 ['=USER_ID' => $userIds],
2939 ['=CHAT_ID' => $this->getChatId()]
2940 ]);
2941
2942 $removedCount = $connection->getAffectedRowsCount();
2943
2944 if (!$removedCount)
2945 {
2946 return $this;
2947 }
2948
2949 $relations = $this->getRelations();
2950 if ($extranetFlag = $this->getExtranet() ?? false)
2951 {
2952 $extranetFlag = false;
2953 foreach ($relations as $relation)
2954 {
2955 if ($extranetFlag = $relation->getUser()->isExtranet())
2956 {
2957 break;
2958 }
2959 }
2960 }
2961
2962 $this
2963 ->setExtranet($extranetFlag)
2964 ->setUserCount($relations->count())
2965 ->save();
2966
2967 if (\Bitrix\Main\Loader::includeModule('pull'))
2968 {
2969 $pushMessage = [
2970 'module_id' => 'im',
2971 'command' => 'chatUsersRemove',
2972 'params' => [
2973 'chatId' => $this->getChatId(),
2974 'dialogId' => 'chat' . $this->getChatId(),
2975 'chatExtranet' => $extranetFlag,
2976 'userCount' => $relations->count()
2977 ],
2978 'extra' => \Bitrix\Im\Common::getPullExtra()
2979 ];
2980
2981 $allUsersIds = $relations->getUserIds();
2982 if ($this->getEntityType() === self::ENTITY_TYPE_LINE)
2983 {
2984 foreach ($relations as $relation)
2985 {
2986 if ($relation->getUser()->getExternalAuthId() === 'imconnector')
2987 {
2988 unset($allUsersIds[$relation['userId']]);
2989 }
2990 }
2991 }
2992 \Bitrix\Pull\Event::add(array_values($allUsersIds), $pushMessage);
2993 if ($this->getType() === self::IM_TYPE_OPEN || $this->getType() === self::IM_TYPE_OPEN_LINE)
2994 {
2995 \CPullWatch::AddToStack('IM_PUBLIC_' . $this->getChatId(), $pushMessage);
2996 }
2997 }
2998
2999 return $this;
3000 }
3001
3002 public function setManagers(array $managerIds): self
3003 {
3004 if (!$this->getChatId() || empty($managerIds) || !count($managerIds))
3005 {
3006 return $this;
3007 }
3008
3009 $managerIds = filter_var(
3010 $managerIds,
3011 FILTER_VALIDATE_INT,
3012 [
3013 'flags' => FILTER_REQUIRE_ARRAY,
3014 'options' => ['min_range' => 1],
3015 ]
3016 );
3017
3018 foreach ($managerIds as $key => $managerId)
3019 {
3020 if (!is_int($managerId))
3021 {
3022 unset($managerIds[$key]);
3023 }
3024 }
3025
3026 $relations = $this->getRelations([
3027 'CHAT_ID' => $this->getChatId(),
3028 ]);
3029
3030 $relationIds = [];
3031 $unsetManagerIds = [];
3033 foreach ($relations as $relation)
3034 {
3035 if (in_array($relation->getUserId(), $managerIds, true))
3036 {
3037 $relationIds[] = $relation->getPrimaryId();
3038 }
3039 elseif ($relation->getManager())
3040 {
3041 $unsetManagerIds[] = $relation->getPrimaryId();
3042 }
3043
3044 }
3045
3046 if ($unsetManagerIds)
3047 {
3048 RelationTable::updateMulti(
3049 $unsetManagerIds,
3050 [
3051 'MANAGER' => 'N',
3052 ]
3053 );
3054 }
3055
3056 RelationTable::updateMulti(
3057 $relationIds,
3058 [
3059 'MANAGER' => 'Y',
3060 ]
3061 );
3062
3063 return $this;
3064 }
3065
3070 public static function loadPhrases(): void
3071 {
3072 Loc::loadMessages(__FILE__);
3073 }
3074
3075 public function setContext(?Context $context): self
3076 {
3077 $this->defaultSaveContext($context);
3078 $this->getReadService()->setContext($context);
3079 $this->role = null;
3080
3081 return $this;
3082 }
3083
3085 {
3086 $startMessageId = $this->getMarkedId() ?: $this->getLastId();
3087
3088 return (new \Bitrix\Im\V2\Message($startMessageId))->setChatId($this->getId())->setMessageId($startMessageId);
3089 }
3090
3091 public function fillNonCachedData(): self
3092 {
3093 if ($this->isFilledNonCachedData)
3094 {
3095 return $this;
3096 }
3097
3098 $this->fillActual(array_merge(ChatFactory::NON_CACHED_FIELDS, ['ALIAS.ALIAS']));
3099 $this->isFilledNonCachedData = true;
3100
3101 return $this;
3102 }
3103
3104 public static function getRestEntityName(): string
3105 {
3106 return 'chat';
3107 }
3108
3109 public function getEntityLink(): Im\V2\Chat\EntityLink
3110 {
3111 return Im\V2\Chat\EntityLink::getInstance($this);
3112 }
3113
3114 public function toRestFormat(array $option = []): array
3115 {
3116 $commonFields = [
3117 'avatar' => $this->getAvatar(),
3118 'color' => (string)$this->getColor() !== '' ? Color::getColor($this->getColor()) : Color::getColorByNumber($this->getChatId()),
3119 'description' => $this->getDescription() ?? '',
3120 'dialogId' => $this->getDialogId(),
3121 'diskFolderId' => $this->getDiskFolderId(),
3122 'entityData1' => $this->getEntityData1() ?? '',
3123 'entityData2' => $this->getEntityData2() ?? '',
3124 'entityData3' => $this->getEntityData3() ?? '',
3125 'entityId' => $this->getEntityId() ?? '',
3126 'entityType' => $this->getEntityType() ?? '',
3127 'extranet' => $this->getExtranet() ?? false,
3128 'id' => $this->getId(),
3129 'name' => $this->getTitle(),
3130 'owner' => (int)$this->getAuthorId(),
3131 'messageType' => $this->getType(),
3132 'role' => mb_strtolower($this->getRole()),
3133 'type' => $this->getTypeForRest(),
3134 'entityLink' => $this->getEntityLink()->toRestFormat($option),
3135 'permissions' => [
3136 'manageUsersAdd' => mb_strtolower($this->getManageUsersAdd()),
3137 'manageUsersDelete' => mb_strtolower($this->getManageUsersDelete()),
3138 'manageUi' => mb_strtolower($this->getManageUI()),
3139 'manageSettings' => mb_strtolower($this->getManageSettings()),
3140 'canPost' => mb_strtolower($this->getCanPost()),
3141 ],
3142 ];
3143 if ($option['CHAT_WITH_DATE_MESSAGE'] ?? false)
3144 {
3145 $commonFields['dateMessage'] = $this->dateMessage;
3146 }
3147 if ($option['CHAT_SHORT_FORMAT'] ?? false)
3148 {
3149 return $commonFields;
3150 }
3151
3152 $additionalFields = [
3153 'counter' => $this->getReadService()->getCounterService()->getByChat($this->getChatId()),
3154 'dateCreate' => $this->getDateCreate() === null ? null : $this->getDateCreate()->format('c'),
3155 'lastMessageId' => $this->getLastMessageId(),
3156 'lastMessageViews' => Im\Common::toJson($this->getLastMessageViews()),
3157 'lastId' => $this->getLastId(),
3158 'managerList' => $this->getManagerList(),
3159 'markedId' => $this->getMarkedId(),
3160 'messageCount' => $this->getMessageCount(),
3161 'muteList' => $this->getMuteList(),
3162 'public' => $this->getPublicOption() ?? '',
3163 'unreadId' => $this->getUnreadId(),
3164 'userCounter' => $this->getUserCount(),
3165 'aiProvider' => null,
3166 ];
3167
3168 return array_merge($commonFields, $additionalFields);
3169 }
3170
3171 protected function getManagerList(): array
3172 {
3173 $userIds = [];
3174 $relations = $this->getRelations();
3175
3176 foreach ($relations as $relation)
3177 {
3178 if ($relation->getManager() ?? false)
3179 {
3180 $userIds[] = $relation->getUserId();
3181 }
3182 }
3183
3184 return $userIds;
3185 }
3186
3187 protected function getMuteList(): array
3188 {
3189 $selfRelation = $this->getSelfRelation();
3190
3191 if ($selfRelation === null)
3192 {
3193 return [];
3194 }
3195
3196 if ($selfRelation->getNotifyBlock() ?? false)
3197 {
3198 return [$this->getContext()->getUserId()];
3199 }
3200
3201 return [];
3202 }
3203
3204 protected function getPublicOption(): ?array
3205 {
3206 if ($this->getAliasName() === null)
3207 {
3208 return null;
3209 }
3210
3211 return [
3212 'code' => $this->getAliasName(),
3213 'link' => Alias::getPublicLink($this->getEntityType(), $this->getAliasName())
3214 ];
3215 }
3216
3217 protected function getRestrictions(): array
3218 {
3219 $options = \CIMChat::GetChatOptions();
3220
3221 if ($this->getEntityType() && isset($options[$this->getEntityType()]))
3222 {
3223 return $options[$this->getEntityType()];
3224 }
3225
3226 return $options['DEFAULT'];
3227 }
3228
3229 public function getTypeForRest(): string
3230 {
3231 return Im\Chat::getType(['ID' => $this->getId(), 'TYPE' => $this->getType(), 'ENTITY_TYPE' => $this->getEntityType()]);
3232 }
3233
3234 protected function getUnreadId(): int
3235 {
3236 $selfRelation = $this->getSelfRelation();
3237 if ($selfRelation === null)
3238 {
3239 return 0;
3240 }
3241
3242 return $selfRelation->getUnreadId() ?? 0;
3243 }
3244
3245 protected function getLastId(): int
3246 {
3247 $selfRelation = $this->getSelfRelation();
3248 if ($selfRelation === null)
3249 {
3250 return 0;
3251 }
3252
3253 return $selfRelation->getLastId() ?? 0;
3254 }
3255
3256 protected function addIndex(): self
3257 {
3258 if (!$this->getChatId())
3259 {
3260 return $this;
3261 }
3262
3263 $index = \Bitrix\Im\Internals\ChatIndex::create()
3264 ->setChatId($this->getChatId())
3265 ->setTitle(mb_substr($this->getTitle() ?? '', 0, 255))
3266 ->setUserList($this->getUserNamesForIndex())
3267 ;
3268 \Bitrix\Im\Model\ChatTable::addIndexRecord($index);
3269
3270 return $this;
3271 }
3272
3273 protected function updateIndex(): self
3274 {
3275 if (!$this->getChatId())
3276 {
3277 return $this;
3278 }
3279
3280 $index = \Bitrix\Im\Internals\ChatIndex::create()
3281 ->setChatId($this->getChatId())
3282 ->setUserList($this->getUserNamesForIndex())
3283 ;
3284 \Bitrix\Im\Model\ChatTable::updateIndexRecord($index);
3285
3286 return $this;
3287 }
3288
3289 private function getUserNamesForIndex(): array
3290 {
3291 $relations = $this->getRelations(['LIMIT' => 100]);
3292
3293 $users = [];
3294 foreach ($relations as $relation)
3295 {
3296 $users[] = $relation->getUser()->getName() ?? '';
3297 }
3298
3299 return $users;
3300 }
3301
3302 public function deleteChat(): Result
3303 {
3304 $result = new Result();
3305
3306 if (!$this->getChatId())
3307 {
3308 return $result->addError(new ChatError(ChatError::NOT_FOUND));
3309 }
3310
3311 $this->hideChat();
3312
3313 $this->getRelations()->delete();
3314
3315 $chatId = $this->getChatId();
3316 $chatFolderId = $this->getDiskFolderId();
3317 $this->delete();
3318
3319 $messageCollection = MessageCollection::find(['CHAT_ID' => $chatId], []);
3320 $messageIds = $messageCollection->getIds();
3321 $messageCollection->delete();
3322
3323 foreach (array_chunk($messageIds, self::CHUNK_SIZE) as $messageIdsChunk)
3324 {
3325 Im\Model\MessageParamTable::deleteBatch([
3326 '=MESSAGE_ID' => $messageIdsChunk,
3327 ]);
3328 }
3329
3330 Im\V2\Link\Url\UrlCollection::deleteByChatsIds([$chatId]);
3331 Im\V2\Chat::cleanCache($chatId);
3332
3333 if ($chatFolderId)
3334 {
3335 $folderModel = \Bitrix\Disk\Folder::getById($chatFolderId);
3336 if ($folderModel)
3337 {
3338 $folderModel->deleteTree(\Bitrix\Disk\SystemUser::SYSTEM_USER_ID);
3339 }
3340 }
3341
3342 return $result;
3343 }
3344
3345 private function hideChat(): Result
3346 {
3347 $result = new Result();
3348
3349 if (!$this->getChatId())
3350 {
3351 return $result->addError(new ChatError(ChatError::NOT_FOUND));
3352 }
3353
3354 $pushList = [];
3355 foreach($this->getRelations() as $relation)
3356 {
3357 \CIMContactList::DeleteRecent($this->getChatId(), true, $relation->getUserId());
3358
3359 if (!Im\User::getInstance($relation->getUserId())->isConnector())
3360 {
3361 $pushList[] = $relation->getUserId();
3362 }
3363 }
3364
3365 if (
3366 !empty($pushList)
3367 && \Bitrix\Main\Loader::includeModule("pull")
3368 )
3369 {
3370 \Bitrix\Pull\Event::add($pushList, [
3371 'module_id' => 'im',
3372 'command' => 'chatHide',
3373 'expiry' => 3600,
3374 'params' => [
3375 'dialogId' => 'chat' . $this->getChatId(),
3376 'chatId' => $this->getId(),
3377 'lines' => $this->getType() === self::IM_TYPE_OPEN_LINE,
3378 ],
3379 'extra' => \Bitrix\Im\Common::getPullExtra()
3380 ]);
3381 }
3382
3383 return $result;
3384 }
3385
3386 public function updateAvatarId(int $avatarId, bool $withMessage = true, bool $skipRecent = false): Result
3387 {
3388 $oldAvatarId = $this->getAvatarId();
3389
3390 $this->setAvatarId($avatarId);
3391 $result = $this->save();
3392
3393 if (!$result->isSuccess())
3394 {
3395 return $result;
3396 }
3397
3398 if (isset($oldAvatarId) && $oldAvatarId > 0)
3399 {
3400 CFile::Delete($oldAvatarId);
3401 }
3402
3403 $avatarFile = CFile::ResizeImageGet(
3404 $avatarId,
3405 [],
3406 BX_RESIZE_IMAGE_EXACT,
3407 false,
3408 false,
3409 true
3410 );
3411 if (!empty($avatarFile['src']))
3412 {
3413 $imageUrl = $avatarFile['src'];
3414
3415 Event::add($this->getRelations()->getUserIds(), [
3416 'module_id' => 'im',
3417 'command' => 'chatAvatar',
3418 'params' => [
3419 'chatId' => $this->getChatId(),
3420 'avatar' => $imageUrl,
3421 ],
3422 'extra' => Common::getPullExtra()
3423 ]);
3424 }
3425
3426 if ($withMessage)
3427 {
3428 $this->sendMessageUpdateAvatar($skipRecent);
3429 }
3430
3431 return $result;
3432 }
3433
3434 public function updateAvatar(string $avatarBase64, bool $withMessage = true, bool $skipRecent = false): Result
3435 {
3436 if (isset($avatarBase64) && $avatarBase64)
3437 {
3438 $avatar = CRestUtil::saveFile($avatarBase64);
3439 $imageCheck = (new \Bitrix\Main\File\Image($avatar["tmp_name"]))->getInfo();
3440 if(
3441 !$imageCheck
3442 || !$imageCheck->getWidth()
3443 || $imageCheck->getWidth() > 5000
3444 || !$imageCheck->getHeight()
3445 || $imageCheck->getHeight() > 5000
3446 )
3447 {
3448 $avatar = null;
3449 }
3450 if (!$avatar || mb_strpos($avatar['type'], "image/") !== 0)
3451 {
3452 $avatarId = 0;
3453 }
3454 else
3455 {
3456 $avatar['MODULE_ID'] = 'im';
3457 $avatarId = CFile::saveFile($avatar, 'im');
3458 }
3459 }
3460 else
3461 {
3462 $avatarId = 0;
3463 }
3464
3465 $result = $this->updateAvatarId($avatarId, $withMessage, $skipRecent);
3466
3467 return $result->setResult($avatarId);
3468 }
3469
3470 protected function sendMessageUpdateAvatar(bool $skipRecent = false): void
3471 {
3472 $currentUser = $this->getContext()->getUser();
3473
3474 $messageText = Loc::getMessage(
3475 "IM_CHAT_AVATAR_CHANGE_{$currentUser->getGender()}",
3476 ['#USER_NAME#' => htmlspecialcharsback($currentUser->getName())]
3477 );
3478
3479 if ($messageText === '')
3480 {
3481 return;
3482 }
3483
3484 \CIMChat::AddMessage([
3485 "TO_CHAT_ID" => $this->getId(),
3486 "MESSAGE" => $messageText,
3487 "FROM_USER_ID" => $this->getContext()->getUserId(),
3488 "SYSTEM" => 'Y',
3489 "RECENT_ADD" => $skipRecent ? 'N' : 'Y',
3490 "PARAMS" => [
3491 "CODE" => 'CHAT_LEAVE',
3492 "NOTIFY" => $this->getEntityType() === 'LINES' ? 'Y': 'N',
3493 ],
3494 "PUSH" => 'N',
3495 "SKIP_USER_CHECK" => "Y",
3496 ]);
3497 }
3498
3499 public function checkAllowedAction(string $action): bool
3500 {
3501 $options = \CIMChat::GetChatOptions();
3502 $entityType = $this->getEntityType();
3503
3504 $defaultAllowed = (bool)($chatOptions['DEFAULT'][$action] ?? true);
3505
3506 if (isset($entityType, $options[$entityType]))
3507 {
3508 return (bool)($chatOptions[$entityType][$action] ?? $defaultAllowed);
3509 }
3510
3511 return $defaultAllowed;
3512 }
3513
3514 public function canDo(string $action): bool
3515 {
3516 $userRights = $this->getRole();
3517
3518 if ($this->getContext()->getUser()->isAdmin())
3519 {
3520 return true;
3521 }
3522
3523 $manageRights = '';
3524 if (in_array($action, Im\V2\Chat\Permission::ACTIONS_MANAGE_UI, true))
3525 {
3526 $manageRights = $this->getManageUI();
3527 }
3528
3529 if (in_array($action, Im\V2\Chat\Permission::ACTIONS_MANAGE_USERS_ADD, true))
3530 {
3531 $manageRights = $this->getManageUsersAdd();
3532 }
3533
3534 if (in_array($action, Im\V2\Chat\Permission::ACTIONS_MANAGE_USERS_DELETE, true))
3535 {
3536 $manageRights = $this->getManageUsersDelete();
3537 }
3538
3539 if (in_array($action, Im\V2\Chat\Permission::ACTIONS_MANAGE_SETTINGS, true))
3540 {
3541 $manageRights = $this->getManageSettings();
3542 }
3543
3544 if (in_array($action, Im\V2\Chat\Permission::ACTIONS_MANAGE_CAN_POST, true))
3545 {
3546 $manageRights = $this->getCanPost();
3547 }
3548
3549 return Im\V2\Chat\Permission::compareRole($userRights, $manageRights);
3550 }
3551}
static getPublicLink($type, $alias)
Definition alias.php:210
static getColorByNumber($number)
Definition color.php:144
static isSafeColor($code)
Definition color.php:186
static getColor($code)
Definition color.php:121
static getCodeByNumber($number)
Definition color.php:166
static toJson($array, $camelCase=true)
Definition common.php:90
static isChatId($id)
Definition common.php:64
static getPullExtra()
Definition common.php:128
static getChatId($dialogId, $userId=null)
Definition dialog.php:91
static isUnread(int $userId, string $itemType, string $dialogId)
Definition recent.php:1425
setLastMessageId(int $lastMessageId)
Definition Chat.php:2063
read(bool $onlyRecent=false, bool $byEvent=false)
Definition Chat.php:738
static getRestEntityName()
Definition Chat.php:3104
setPrimaryId(int $primaryId)
Definition Chat.php:1330
const ENTITY_TYPE_LIVECHAT
Definition Chat.php:98
setRelations(RelationCollection $relations)
Definition Chat.php:2157
string $aliasName
Definition Chat.php:225
static fillRole(array $chats, ?int $userId=null)
Definition Chat.php:661
Registry $messageRegistry
Definition Chat.php:245
const IM_TYPE_CHANNEL
Definition Chat.php:60
const AVAILABLE_PARAMS
Definition Chat.php:107
string $description
Definition Chat.php:179
setCanPost(string $canPost)
Definition Chat.php:2337
const IM_TYPE_PRIVATE
Definition Chat.php:55
sendMessageUsersAdd(array $usersToAdd, bool $skipRecent=false)
Definition Chat.php:2453
const ROLE_GUEST
Definition Chat.php:141
const IM_TYPES_TRANSLATE
Definition Chat.php:76
setDialogId(string $dialogId)
Definition Chat.php:1518
toRestFormat(array $option=[])
Definition Chat.php:3114
updateAvatarId(int $avatarId, bool $withMessage=true, bool $skipRecent=false)
Definition Chat.php:3386
bool $isDiskFolderFilled
Definition Chat.php:255
const MANAGE_RIGHTS_MEMBER
Definition Chat.php:133
getDefaultLastMessageStatus()
Definition Chat.php:2095
setUserIds(?array $userIds)
Definition Chat.php:383
setAvatarId(?int $avatarId)
Definition Chat.php:1727
const ENTITY_TYPE_GENERAL
Definition Chat.php:91
unreadToMessage(Message $message)
Definition Chat.php:839
updateMessage(Message $message)
Definition Chat.php:593
sendPushUserDelete(int $userId, RelationCollection $oldRelations)
Definition Chat.php:2840
static cleanAccessCache(int $chatId)
Definition Chat.php:314
setExtranet(?bool $extranet)
Definition Chat.php:1710
sendPushUpdateMessage(Message $message)
Definition Chat.php:869
setManageUsersDelete(string $manageUsersDelete)
Definition Chat.php:2235
const IM_TYPE_OPEN_LINE
Definition Chat.php:58
sendPushReadSelf(MessageCollection $messages, int $lastId, int $counter)
Definition Chat.php:894
static array $chatStaticCache
Definition Chat.php:150
setEntityData3(?string $entityData3)
Definition Chat.php:1855
static getSharedChatsWithUser(int $userId, int $limit=50, int $offset=0, ?int $currentUserId=null)
Definition Chat.php:1451
sendNotificationUserDelete(int $userId)
Definition Chat.php:2743
array $relations
Definition Chat.php:250
getUsersForPush(bool $skipBot=false, bool $skipSelf=true)
Definition Chat.php:1078
setEntityData1(?string $entityData1)
Definition Chat.php:1831
const ENTITY_TYPE_VIDEOCONF
Definition Chat.php:90
__construct($source=null)
Definition Chat.php:260
const IM_TYPE_COMMENT
Definition Chat.php:57
string $canPost
Definition Chat.php:238
const IM_TYPE_OPEN
Definition Chat.php:61
updateParentMessageCount()
Definition Chat.php:2014
checkAllowedAction(string $action)
Definition Chat.php:3499
setParentChatId(int $parentChatId)
Definition Chat.php:1686
string $entityData2
Definition Chat.php:203
sendMessage($message, $sendingConfig=null)
DateTime $dateMessage
Definition Chat.php:220
setPinMessageId(?int $pinMessageId)
Definition Chat.php:1761
prepareAliasToLoad($alias)
Definition Chat.php:429
const MANAGE_RIGHTS_MANAGERS
Definition Chat.php:135
string $entityId
Definition Chat.php:199
readTo(Message $message, bool $byEvent=false)
Definition Chat.php:803
incrementMessageCount(int $increment=1)
Definition Chat.php:1994
const MANAGE_RIGHTS_NONE
Definition Chat.php:132
string $title
Definition Chat.php:177
string $manageUsersDelete
Definition Chat.php:232
setDateCreate(?DateTime $dateCreate)
Definition Chat.php:2101
static loadPhrases()
Definition Chat.php:3070
const IM_TYPE_SYSTEM
Definition Chat.php:59
setPrevMessageId(int $prevMessageId)
Definition Chat.php:2051
const ROLE_MANAGER
Definition Chat.php:139
setMessageCount(int $messageCount)
Definition Chat.php:1975
bool $isFilledNonCachedData
Definition Chat.php:254
filterUsersToAdd(array $userIds)
Definition Chat.php:2613
sendEventRead(int $startId, int $endId, int $counter, bool $byEvent)
Definition Chat.php:939
array $accessCache
Definition Chat.php:152
sendEventUserDelete(int $userId)
Definition Chat.php:2808
addUsers(array $userIds, array $managerIds=[], ?bool $hideHistory=null, bool $withMessage=true, bool $skipRecent=false)
Definition Chat.php:2426
addUsersToRelation(array $usersToAdd, array $managerIds=[], ?bool $hideHistory=null)
Definition Chat.php:2574
readAllMessages(bool $byEvent=false)
Definition Chat.php:757
sendEventUsersAdd(array $usersToAdd)
Definition Chat.php:2634
setEntityData2(?string $entityData2)
Definition Chat.php:1843
int $disappearingTime
Definition Chat.php:242
string $dialogId
Definition Chat.php:163
getLastMessageViewsByGroups()
Definition Chat.php:1014
sendPushReadOpponent(MessageCollection $messages, int $lastId)
Definition Chat.php:916
static readAllChats(int $userId)
Definition Chat.php:719
const MANAGE_RIGHTS_OWNER
Definition Chat.php:134
string $manageUsersAdd
Definition Chat.php:231
string $manageUI
Definition Chat.php:234
setDiskFolderId(?int $diskFolderId)
Definition Chat.php:1867
string $entityType
Definition Chat.php:197
add(array $params)
Definition Chat.php:350
readMessages(?MessageCollection $messages, bool $byEvent=false)
Definition Chat.php:762
string $lastMessageStatus
Definition Chat.php:227
getAccessCodesForDiskFolder()
Definition Chat.php:1954
setManageUI(string $manageUI)
Definition Chat.php:2266
const EXTRANET_CAN_SEE_HISTORY
Definition Chat.php:145
sendPushRead(MessageCollection $messages, int $lastId, int $counter)
Definition Chat.php:874
removeUsers(array $userIds)
Definition Chat.php:2907
const IM_TYPE_CHAT
Definition Chat.php:56
const ROLE_MEMBER
Definition Chat.php:140
setManageSettings(string $manageSettings)
Definition Chat.php:2296
string $color
Definition Chat.php:181
getDefaultManageSettings()
Definition Chat.php:2313
setAliasName(string $aliasName)
Definition Chat.php:422
Folder $diskFolder
Definition Chat.php:210
canDo(string $action)
Definition Chat.php:3514
setLastMessageStatus(?string $lastMessageStatus)
Definition Chat.php:2084
getMessage(int $messageId)
Definition Chat.php:561
static cleanCache(int $id)
Definition Chat.php:306
static getCanPostList()
Definition Chat.php:2368
setEntityType(?string $entityType)
Definition Chat.php:1797
getStartId(?int $userId=null)
Definition Chat.php:340
setCallType(?int $callType)
Definition Chat.php:1773
const ROLE_NONE
Definition Chat.php:142
getAvatar(int $size=200, bool $addBlankPicture=false)
Definition Chat.php:1738
const ENTITY_TYPES
Definition Chat.php:100
setParentMessageId(int $messageId)
Definition Chat.php:1698
string $entityData1
Definition Chat.php:201
setEntityId(?string $entityId)
Definition Chat.php:1819
DateTime $dateCreate
Definition Chat.php:229
sendMessageUserDelete(int $userId, bool $skipRecent=false)
Definition Chat.php:2714
deleteMessage(Message $message)
Definition Chat.php:608
static getInstance(?int $chatId)
Definition Chat.php:282
getMessageUserDeleteText(int $userId)
Definition Chat.php:2769
setManageUsersAdd(string $manageUsersAdd)
Definition Chat.php:2204
setChatId(int $chatId)
Definition Chat.php:1495
const ENTITY_TYPE_LINE
Definition Chat.php:97
hasPostAccess(?int $userId=null)
Definition Chat.php:2379
sendPushUnreadSelf(int $unreadToId, int $lastId, int $counter, ?array $lastMessageStatuses)
Definition Chat.php:956
const IM_TYPES
Definition Chat.php:65
static getDataClass()
Definition Chat.php:1313
setTitle(?string $title)
Definition Chat.php:1628
sendPushUsersAdd(array $usersToAdd, RelationCollection $oldRelations)
Definition Chat.php:2506
string $entityData3
Definition Chat.php:206
string $callNumber
Definition Chat.php:195
int $parentMessageId
Definition Chat.php:185
hasAccess($user=null)
Definition Chat.php:499
setDescription(?string $description)
Definition Chat.php:1645
getDefaultManageUsersAdd()
Definition Chat.php:2226
getUserId($user)
Definition Chat.php:525
setContext(?Context $context)
Definition Chat.php:3075
sendMessageUpdateAvatar(bool $skipRecent=false)
Definition Chat.php:3470
setCallNumber(?string $callNumber)
Definition Chat.php:1785
setDiskFolder(?Folder $folder)
Definition Chat.php:1878
updateAvatar(string $avatarBase64, bool $withMessage=true, bool $skipRecent=false)
Definition Chat.php:3434
setUserCount(int $userCount)
Definition Chat.php:2037
setDisappearingTime(int $disappearingTime)
Definition Chat.php:2318
const IM_TYPE_COPILOT
Definition Chat.php:62
static find(array $params, ?Context $context=null)
Definition Chat.php:1346
getSelfRelation(array $options=[])
Definition Chat.php:2165
checkAccessWithoutCaching(int $userId)
Definition Chat.php:520
getRelations(array $options=[])
Definition Chat.php:2132
string $manageSettings
Definition Chat.php:236
createChatIfNotExists(array $params)
Definition Chat.php:2412
getDefaultManageUsersDelete()
Definition Chat.php:2257
setColor(?string $color)
Definition Chat.php:1657
const ENTITY_TYPE_FAVORITE
Definition Chat.php:92
array $usersIds
Definition Chat.php:240
setAuthorId(int $authorId)
Definition Chat.php:1611
deleteUser(int $userId, bool $withMessage=true, bool $skipRecent=false, bool $withNotification=true)
Definition Chat.php:2680
const ROLE_OWNER
Definition Chat.php:138
static mirrorDataEntityFields()
Definition Chat.php:1111
updateStateAfterUserDelete(int $deletedUserId)
Definition Chat.php:2782
updateStateAfterUsersAdd(array $usersToAdd)
Definition Chat.php:2545
ReadService $readService
Definition Chat.php:252
setType(string $type)
Definition Chat.php:1568
getLastMessages(int $upperBound, int $lowerBound)
Definition Chat.php:981
read(MessageCollection $messages, Chat $chat)
static find(array $filter, array $order, ?int $limit=null, ?Context $context=null, array $select=[])
getParams(bool $disallowLazyLoad=false)
Definition Message.php:313
static getStartId(int $userId, int $chatId)
static find(array $filter, array $order=[], ?int $limit=null, ?Context $context=null, array $select=self::COMMON_FIELDS)
static getConnection($name="")
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
addError(Error $error)
Definition result.php:50
static decode($text)
Definition emoji.php:24
setRegistry(Registry $registry)