Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
GroupChat.php
1<?php
2
3namespace Bitrix\Im\V2\Chat;
4
28use CPullWatch;
29
30class GroupChat extends Chat implements PopupDataAggregatable
31{
32 protected function getDefaultType(): string
33 {
34 return self::IM_TYPE_CHAT;
35 }
36
37 public function setType(string $type): Chat
38 {
39 $this->type = $type;
40
41 return $this;
42 }
43
48 public function allowMention(): bool
49 {
50 return true;
51 }
52
53 protected function needToSendGreetingMessages(): bool
54 {
55 return !$this->getEntityType();
56 }
57
58 protected function checkAccessWithoutCaching(int $userId): bool
59 {
60 $options = [
61 'SELECT' => ['ID', 'CHAT_ID', 'USER_ID', 'START_ID'],
62 'FILTER' => ['USER_ID' => $userId, 'CHAT_ID' => $this->getChatId()],
63 'LIMIT' => 1,
64 ];
65
66 return $this->getRelations($options)->hasUser($userId, $this->getChatId());
67 }
68
69 public function add(array $params, ?Context $context = null): Result
70 {
71 $result = new Result;
72 $skipAddMessage = ($params['SKIP_ADD_MESSAGE'] ?? 'N') === 'Y';
73 $skipMessageUsersAdd = ($params['SKIP_MESSAGE_USERS_ADD'] ?? 'N') === 'Y';
74 $paramsResult = $this->prepareParams($params);
75 if ($paramsResult->isSuccess())
76 {
77 $params = $paramsResult->getResult();
78 }
79 else
80 {
81 return $result->addErrors($paramsResult->getErrors());
82 }
83
84 $chat = new static($params);
85 $chat->setExtranet($chat->checkIsExtranet())->setContext($context);
86 $chat->save();
87
88 if (!$chat->getChatId())
89 {
90 return $result->addError(new ChatError(ChatError::CREATION_ERROR));
91 }
92
93 $chat->addUsersToRelation([$chat->getAuthorId()], $params['MANAGERS'] ?? [], false);
94 if (!$skipAddMessage && $chat->needToSendGreetingMessages())
95 {
96 $chat->sendGreetingMessage($this->getContext()->getUserId());
97 $chat->sendBanner($this->getContext()->getUserId());
98 }
99
100 $usersToInvite = $chat->filterUsersToAdd($chat->getUserIds());
101 $addedUsers = $usersToInvite;
102 $addedUsers[$chat->getAuthorId()] = $chat->getAuthorId();
103
104 $chat->addUsersToRelation($usersToInvite, $params['MANAGERS'] ?? [], false);
105 if (!$skipAddMessage && !$skipMessageUsersAdd)
106 {
107 $chat->sendMessageUsersAdd($usersToInvite);
108 }
109 $chat->sendEventUsersAdd($addedUsers);
110 $chat->sendDescriptionMessage($this->getContext()->getUserId());
111 $chat->addIndex();
112
113 $result->setResult([
114 'CHAT_ID' => $chat->getChatId(),
115 'CHAT' => $chat,
116 ]);
117
118 self::cleanCache($chat->getChatId());
119
120 return $result;
121 }
122
123 protected function filterParams(array $params): array
124 {
125 if (isset($params['USER_ID']))
126 {
127 $params['USER_ID'] = (int)$params['USER_ID'];
128 }
129 else
130 {
131 $params['USER_ID'] = $this->getContext()->getUserId();
132 }
133
134 if (isset($params['AUTHOR_ID']))
135 {
136 $params['AUTHOR_ID'] = (int)$params['AUTHOR_ID'];
137 }
138 elseif (isset($params['OWNER_ID']))
139 {
140 $params['AUTHOR_ID'] = (int)$params['OWNER_ID'];
141 }
142
143 foreach (['USERS', 'MANAGERS'] as $paramName)
144 {
145 if (!isset($params[$paramName]) || !is_array($params[$paramName]))
146 {
147 $params[$paramName] = [];
148 }
149 else
150 {
151 $params[$paramName] = filter_var(
152 $params[$paramName],
153 FILTER_VALIDATE_INT,
154 [
155 'flags' => FILTER_REQUIRE_ARRAY,
156 'options' => ['min_range' => 1]
157 ]
158 );
159
160 foreach ($params[$paramName] as $key => $paramValue)
161 {
162 if (!is_int($paramValue))
163 {
164 unset($params[$paramName][$key]);
165 }
166 }
167 }
168 }
169
170 $params['SKIP_ADD_MESSAGE'] = isset($params['SKIP_ADD_MESSAGE']) && $params['SKIP_ADD_MESSAGE'] === 'Y';
171
172 return $params;
173 }
174
175 protected function prepareParams(array $params = []): Result
176 {
177 $result = new Result();
178 $params = $this->filterParams($params);
179
180 if (!isset($params['AUTHOR_ID']))
181 {
182 $params['AUTHOR_ID'] = $this->getContext()->getUserId();
183 }
184
185 if (!isset($params['OWNER_ID']))
186 {
187 $params['OWNER_ID'] = $this->getContext()->getUserId();
188 }
189
190 $params['MANAGERS'] ??= [];
191 $params['MANAGERS'] = array_unique(array_merge($params['MANAGERS'], [$params['AUTHOR_ID']]));
192
193 $params['USERS'] = array_unique(array_merge($params['USERS'], $params['MANAGERS']));
194 $params['USER_COUNT'] = count($params['USERS']);
195
196 if (
197 isset($params['AVATAR'])
198 && $params['AVATAR']
199 && !is_numeric((string)$params['AVATAR'])
200 )
201 {
202 $params['AVATAR'] = \CRestUtil::saveFile($params['AVATAR']);
203 $imageCheck = (new \Bitrix\Main\File\Image($params['AVATAR']["tmp_name"]))->getInfo();
204 if(
205 !$imageCheck
206 || !$imageCheck->getWidth()
207 || $imageCheck->getWidth() > 5000
208 || !$imageCheck->getHeight()
209 || $imageCheck->getHeight() > 5000
210 )
211 {
212 $params['AVATAR'] = null;
213 }
214
215 if (!$params['AVATAR'] || mb_strpos($params['AVATAR']['type'], "image/") !== 0)
216 {
217 $params['AVATAR'] = null;
218 }
219 else
220 {
221 $params['AVATAR'] = \CFile::saveFile($params['AVATAR'], 'im');
222 }
223 }
224
225 return $result->setResult($params);
226 }
227
228 protected function addUsersToRelation(array $usersToAdd, array $managerIds = [], ?bool $hideHistory = null)
229 {
230 parent::addUsersToRelation($usersToAdd, $managerIds, $hideHistory ?? \CIMSettings::GetStartChatMessage() == \CIMSettings::START_MESSAGE_LAST);
231 }
232
233 public function checkTitle(): Result
234 {
235 if (!$this->getTitle())
236 {
237 $this->setTitle($this->generateTitle());
238 }
239
240 return new Result;
241 }
242
243 protected function generateTitle(): string
244 {
245 if (Color::isEnabled() && $this->getColor())
246 {
247 $colorCodeKey = 'im_chat_color_' . $this->getColor();
248 $colorCodeCount = \CGlobalCounter::GetValue($colorCodeKey, \CGlobalCounter::ALL_SITES);
249 if ($colorCodeCount >= Color::MAX_COLOR_COUNT)
250 {
251 $colorCodeCount = 0;
252 \CGlobalCounter::Set($colorCodeKey, 1, \CGlobalCounter::ALL_SITES, '', false);
253 }
254
255 $chatTitle = Loc::getMessage('IM_CHAT_NAME_FORMAT', [
256 '#COLOR#' => Color::getName($this->getColor()),
257 '#NUMBER#' => ++$colorCodeCount,
258 ]);
259 \CGlobalCounter::Set($colorCodeKey, $colorCodeCount, \CGlobalCounter::ALL_SITES, '', false);
260 }
261 else
262 {
263 $userIds = [];
264 if ($this->getUserIds() && count($this->getUserIds()))
265 {
266 $userIds = $this->getUserIds();
267 }
268 $userIds = \CIMContactList::PrepareUserIds($userIds);
269 $users = \CIMContactList::GetUserData([
270 'ID' => array_values($userIds),
271 'DEPARTMENT' => 'N',
272 'USE_CACHE' => 'N'
273 ]);
274
275 $usersNames = [];
276 foreach ($users['users'] as $user)
277 {
278 $usersNames[] = htmlspecialcharsback($user['name']);
279 }
280
281 $chatTitle = Loc::getMessage('IM_CHAT_NAME_FORMAT_USERS', [
282 '#USERS_NAMES#' => implode(', ', $usersNames),
283 ]);
284 }
285
286 return mb_substr($chatTitle, 0, 255);
287 }
288
289 public function sendPushUpdateMessage(Message $message): void
290 {
291 $pushFormat = new Message\PushFormat();
292 $push = $pushFormat->formatMessageUpdate($message);
293 $push['params']['dialogId'] = $this->getDialogId();
294 Event::add($this->getUsersForPush(true, false), $push);
295 }
296
297 protected function sendPushReadOpponent(MessageCollection $messages, int $lastId): array
298 {
299 $pushMessage = parent::sendPushReadOpponent($messages, $lastId);
300 CPullWatch::AddToStack("IM_PUBLIC_{$this->chatId}", $pushMessage);
301
302 return $pushMessage;
303 }
304
305 protected function sendGreetingMessage(?int $authorId = null)
306 {
307 if (!$authorId)
308 {
309 $authorId = $this->getAuthorId();
310 }
311 $author = \Bitrix\Im\V2\Entity\User\User::getInstance($authorId);
312
313 $replace = ['#USER_NAME#' => htmlspecialcharsback($author->getName())];
314 $messageText = Loc::getMessage($this->getCodeGreetingMessage($author), $replace);
315
316 if ($messageText)
317 {
318 \CIMMessage::Add([
319 'MESSAGE_TYPE' => $this->getType(),
320 'TO_CHAT_ID' => $this->getChatId(),
321 'FROM_USER_ID' => $author->getId(),
322 'MESSAGE' => $messageText,
323 'SYSTEM' => 'Y',
324 'PUSH' => 'N'
325 ]);
326 }
327
328 if ($authorId !== $this->getAuthorId())
329 {
330 $this->sendMessageAuthorChange($author);
331 }
332 }
333
334 protected function getCodeGreetingMessage(\Bitrix\Im\V2\Entity\User\User $author): string
335 {
336 return 'IM_CHAT_CREATE_' . $author->getGender();
337 }
338
339 protected function sendMessageAuthorChange(\Bitrix\Im\V2\Entity\User\User $author): void
340 {
341 $messageText = Loc::getMessage(
342 'IM_CHAT_APPOINT_OWNER_' . $author->getGender(),
343 [
344 '#USER_1_NAME#' => htmlspecialcharsback($author->getName()),
345 '#USER_2_NAME#' => htmlspecialcharsback($this->getAuthor()->getName())
346 ]
347 );
348
349 \CIMMessage::Add([
350 'MESSAGE_TYPE' => $this->getType(),
351 'TO_CHAT_ID' => $this->getChatId(),
352 'FROM_USER_ID' => $author->getId(),
353 'MESSAGE' => $messageText,
354 'SYSTEM' => 'Y',
355 'PUSH' => 'N'
356 ]);
357 }
358
359 protected function sendBanner(?int $authorId = null): void
360 {
361 if (!$authorId)
362 {
363 $authorId = $this->getAuthorId();
364 }
365 $author = \Bitrix\Im\V2\Entity\User\User::getInstance($authorId);
366
367 if (
368 in_array($this->getType(), [self::IM_TYPE_CHAT, self::IM_TYPE_OPEN, self::IM_TYPE_COPILOT], true)
369 && empty($this->getEntityType())
370 )
371 {
372 \CIMMessage::Add([
373 'MESSAGE_TYPE' => $this->getType(),
374 'TO_CHAT_ID' => $this->getChatId(),
375 'FROM_USER_ID' => $author->getId(),
376 'MESSAGE' => Loc::getMessage('IM_CHAT_CREATE_WELCOME'),
377 'SYSTEM' => 'Y',
378 'PUSH' => 'N',
379 'PARAMS' => [
380 'COMPONENT_ID' => 'ChatCreationMessage',
381 ]
382 ]);
383 }
384 }
385
386 protected function sendInviteMessage(?int $authorId = null): void
387 {
388 if (!$authorId)
389 {
390 $authorId = $this->getAuthorId();
391 }
392 $author = \Bitrix\Im\V2\Entity\User\User::getInstance($authorId);
393
394 $userIds = array_unique($this->getUserIds());
395 if (count($userIds) < 2)
396 {
397 return;
398 }
399
400 $userIds = \CIMContactList::PrepareUserIds($userIds);
401 $users = \CIMContactList::GetUserData([
402 'ID' => array_values($userIds),
403 'DEPARTMENT' => 'N',
404 'USE_CACHE' => 'N'
405 ]);
406
407 if (!isset($users['users']) || count($users['users']) < 2)
408 {
409 return;
410 }
411
412 $usersNames = [];
413
414 if ($authorId !== $this->getAuthorId())
415 {
416 $usersNames[] = htmlspecialcharsback($this->getAuthor()->getName());
417 }
418
419 foreach ($users['users'] as $user)
420 {
421 if ($user['name'] !== $author->getName())
422 {
423 $usersNames[] = htmlspecialcharsback($user['name']);
424 }
425 }
426
427 $messageText = Loc::getMessage(
428 'IM_CHAT_JOIN_' . $author->getGender(),
429 [
430 '#USER_1_NAME#' => htmlspecialcharsback($author->getName()),
431 '#USER_2_NAME#' => implode(', ', array_unique($usersNames))
432 ]
433 );
434
435 \CIMMessage::Add([
436 'MESSAGE_TYPE' => $this->getType(),
437 'TO_CHAT_ID' => $this->getChatId(),
438 'FROM_USER_ID' => $author->getId(),
439 'MESSAGE' => $messageText,
440 'SYSTEM' => 'Y',
441 ]);
442 }
443
451 public function sendMessage($message, $sendingConfig = null): Result
452 {
453 $result = new Result;
454
455 if (!$this->getChatId())
456 {
457 return $result->addError(new ChatError(ChatError::WRONG_TARGET_CHAT));
458 }
459
460 if (is_string($message))
461 {
462 $message = (new Message)->setMessage($message);
463 }
464 elseif (!$message instanceof Message)
465 {
466 $message = new Message($message);
467 }
468
469 $message
470 ->setRegistry($this->messageRegistry)
471 ->setContext($this->context)
472 ->setChatId($this->getChatId())
473 ->setNotifyModule('im')
474 ->setNotifyEvent(Notify::EVENT_GROUP)
475 ;
476
477 // config for sending process
478 if ($sendingConfig instanceof SendingConfig)
479 {
480 $sendingServiceConfig = $sendingConfig;
481 }
482 else
483 {
484 $sendingServiceConfig = new SendingConfig();
485 if (is_array($sendingConfig))
486 {
487 $sendingServiceConfig->fill($sendingConfig);
488 }
489 }
490 // sending process
491 $sendService = new SendingService($sendingServiceConfig);
492 $sendService->setContext($this->context);
493
494
495 // check duplication by UUID
496 if (
497 !$message->isSystem()
498 && $message->getUuid()
499 )
500 {
501 $checkUuidResult = $sendService->checkDuplicateByUuid($message);
502 if (!$checkUuidResult->isSuccess())
503 {
504 return $result->addErrors($checkUuidResult->getErrors());
505 }
506 $data = $checkUuidResult->getResult();
507 if (!empty($data['messageId']))
508 {
509 return $result->setResult($checkUuidResult->getResult());
510 }
511 }
512
513 // author from current context
514 if (
515 !$message->getAuthorId()
516 && !$message->isSystem()
517 )
518 {
519 $message->setAuthorId($this->getContext()->getUserId());
520 }
521
522 // Extranet cannot send system
523 if (
524 $message->isSystem()
525 && $message->getAuthorId()
526 && User::getInstance($message->getAuthorId())->isExtranet()
527 )
528 {
529 $message->markAsSystem(false);
530 }
531
532 // permissions
533 if (
534 !$sendingServiceConfig->skipUserCheck()
535 && !$sendingServiceConfig->convertMode()
536 && !$message->isSystem()
537 )
538 {
539 if (!$this->hasAccess($message->getAuthorId()))
540 {
541 return $result->addError(new ChatError(ChatError::ACCESS_DENIED));
542 }
543 }
544
545 // fire event `im:OnBeforeChatMessageAdd` before message send
546 $eventResult = $sendService->fireEventBeforeMessageSend($this, $message);
547 if (!$eventResult->isSuccess())
548 {
549 // cancel sending by event
550 return $result->addErrors($eventResult->getErrors());
551 }
552
553 // check for empty message
554 if (
555 !$message->getMessage()
556 && !$message->hasFiles()
557 && !$message->getParams()->isSet(Params::ATTACH)
558 )
559 {
560 return $result->addError(new MessageError(MessageError::EMPTY_MESSAGE));
561 }
562
563 if ($sendingServiceConfig->keepConnectorSilence())
564 {
565 $message->getParams()->get(Params::STYLE_CLASS)->setValue('bx-messenger-content-item-system');
566 }
567
568
569 // Replacements / DateLink
570 if ($sendingServiceConfig->generateUrlPreview())
571 {
572 $message->parseDates();
573 }
574
575 // Emoji
576 $message->checkEmoji();
577
578 // BB codes with disk files
579 $message->uploadFileFromText();
580
581 // Format attached files
582 $message->formatFilesMessageOut();
583
584 // Save + Save Params
585 $saveResult = $message->save();
586 if (!$saveResult->isSuccess())
587 {
588 return $result->addErrors($saveResult->getErrors());
589 }
590
591 $sendService->updateMessageUuid($message);
592
593 // Unread
594 $readService = new ReadService($message->getAuthorId());
595 $readService->markMessageUnread($message, $this->getRelations());
596
597 // Chat message counter
598 $this
599 ->setLastMessageId($message->getMessageId())
600 ->incrementMessageCount()
601 ->save()
602 ;
603
604 // Recent
605 if ($sendingServiceConfig->addRecent())
606 {
607 $readService->markRecentUnread($message);
608 }
609
610 // Counters
611 $counters = [];
612 if ($sendingServiceConfig->sendPush())
613 {
614 $counters = $readService->getCountersForUsers($message, $this->getRelations());
615 }
616
617 // fire event `im:OnAfterMessagesAdd`
618 $sendService->fireEventAfterMessageSend($this, $message);
619
620 // Recent
621 if (
622 $sendingServiceConfig->addRecent()
623 && !$sendingServiceConfig->skipAuthorAddRecent()// Do not add author into recent list in case of self message chat.
624 )
625 {
626 $this->riseInRecent($message);
627 }
628
629 // Rich
630 if ($sendingServiceConfig->generateUrlPreview())
631 {
632 // generate preview or set bg job
633 $message->generateUrlPreview();
634 }
635
636 if ($this->getParentMessageId())
637 {
638 $this->updateParentMessageCount();
639 }
640
641 // send Push
642 if ($sendingServiceConfig->sendPush())
643 {
644 $pushService = new PushService($sendingServiceConfig);
645 $pushService->sendPushGroupChat($this, $message, $counters);
646 }
647
648 // Mentions
649 if (!$message->isSystem())
650 {
651 $mentionService = new MentionService($sendingServiceConfig);
652 $mentionService->setContext($this->context);
653 $mentionService->sendMentions($this, $message);
654 }
655
656 // Run message command
657 $botService = new BotService($sendingServiceConfig);
658 $botService->setContext($this->context);
659 $botService->runMessageCommand($this, $message);
660
661 // Links
662 (new UrlService())->saveUrlsFromMessage($message);
663
664 // search
665 $message->updateSearchIndex();
666
667 $result->setResult(['messageId' => $message->getMessageId()]);
668
669 return $result;
670 }
671
672 protected function sendDescriptionMessage(?int $authorId = null): void
673 {
674 if (!$this->getDescription())
675 {
676 return;
677 }
678
679 if (!$authorId)
680 {
681 $authorId = $this->getAuthorId();
682 }
683
684 \CIMMessage::Add([
685 'MESSAGE_TYPE' => $this->getType(),
686 'TO_CHAT_ID' => $this->getChatId(),
687 'FROM_USER_ID' => $authorId,
688 'MESSAGE' => htmlspecialcharsback($this->getDescription()),
689 ]);
690 }
691
692 public function getPopupData(array $excludedList = []): PopupData
693 {
694 $userIds = [$this->getContext()->getUserId()];
695
696 return new PopupData([new UserPopupItem($userIds)], $excludedList);
697 }
698}
static hasAccess($chatId)
Definition chat.php:445
static getType($chatData)
Definition chat.php:41
static getName($code)
Definition color.php:135
const MAX_COLOR_COUNT
Definition color.php:16
static isEnabled()
Definition color.php:50
const EVENT_GROUP
Definition notify.php:13
sendMessageAuthorChange(\Bitrix\Im\V2\Entity\User\User $author)
sendPushUpdateMessage(Message $message)
add(array $params, ?Context $context=null)
Definition GroupChat.php:69
getPopupData(array $excludedList=[])
sendBanner(?int $authorId=null)
sendMessage($message, $sendingConfig=null)
addUsersToRelation(array $usersToAdd, array $managerIds=[], ?bool $hideHistory=null)
sendPushReadOpponent(MessageCollection $messages, int $lastId)
prepareParams(array $params=[])
filterParams(array $params)
sendDescriptionMessage(?int $authorId=null)
sendGreetingMessage(?int $authorId=null)
sendInviteMessage(?int $authorId=null)
checkAccessWithoutCaching(int $userId)
Definition GroupChat.php:58
getCodeGreetingMessage(\Bitrix\Im\V2\Entity\User\User $author)
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
addError(Error $error)
Definition result.php:50