1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
RecentProvider.php
См. документацию.
1<?php
2
3namespace Bitrix\Im\V2\Integration\UI\EntitySelector;
4
5use Bitrix\Im\Model\ChatTable;
6use Bitrix\Im\Model\MessageTable;
7use Bitrix\Im\Model\RecentTable;
8use Bitrix\Im\Model\RelationTable;
9use Bitrix\Im\Model\UserTable;
10use Bitrix\Im\V2\Application\Features;
11use Bitrix\Im\V2\Chat;
12use Bitrix\Im\V2\Chat\Background\Background;
13use Bitrix\Im\V2\Chat\TextField\TextFieldEnabled;
14use Bitrix\Im\V2\Common\ContextCustomer;
15use Bitrix\Im\V2\Entity\User\User;
16use Bitrix\Im\V2\Entity\User\UserBot;
17use Bitrix\Im\V2\Entity\User\UserCollection;
18use Bitrix\Im\V2\Integration\AI\RoleManager;
19use Bitrix\Im\V2\Integration\HumanResources\Department\Department;
20use Bitrix\Im\V2\Integration\Socialnetwork\Group;
21use Bitrix\Im\V2\Permission\ActionGroup;
22use Bitrix\Main\Loader;
23use Bitrix\Main\ORM\Entity;
24use Bitrix\Main\ORM\Fields\BooleanField;
25use Bitrix\Main\ORM\Fields\ExpressionField;
26use Bitrix\Main\ORM\Fields\Relations\Reference;
27use Bitrix\Main\ORM\Query\Filter\ConditionTree;
28use Bitrix\Main\ORM\Query\Filter\Helper;
29use Bitrix\Main\ORM\Query\Join;
30use Bitrix\Main\ORM\Query\Query;
31use Bitrix\Main\Search\Content;
32use Bitrix\Main\Type\DateTime;
33use Bitrix\Main\UserIndexTable;
34use Bitrix\UI\EntitySelector\BaseProvider;
35use Bitrix\UI\EntitySelector\Dialog;
36use Bitrix\UI\EntitySelector\Item;
37use Bitrix\UI\EntitySelector\RecentItem;
38use Bitrix\UI\EntitySelector\SearchQuery;
39
41{
42 use ContextCustomer;
43
44 protected const ENTITY_ID = 'im-recent-v2';
45
46 protected const LIMIT = 30;
47 protected const TECHNICAL_LIMIT = 1000;
48 private const ENTITY_TYPE_USER = 'im-user';
49 private const ENTITY_TYPE_CHAT = 'im-chat';
50 private const WITH_CHAT_BY_USERS_OPTION = 'withChatByUsers';
51 private const ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION = 'onlyWithManageMessagesRight';
52 protected const ONLY_WITH_MANAGE_USERS_ADD_RIGHT_OPTION = 'onlyWithManageUsersAddRight';
53 protected const ONLY_WITH_OWNER_RIGHT_OPTION = 'onlyWithOwnerRight';
54 protected const ONLY_WITH_NULL_ENTITY_TYPE_OPTION = 'onlyWithNullEntityType';
55 private const EXCLUDE_FROM_RECENT_OPTION = 'excludeFromRecent';
56 protected const INCLUDE_ONLY_OPTION = 'includeOnly';
57 private const EXCLUDE_OPTION = 'exclude';
58 private const SEARCH_FLAGS_OPTION = 'searchFlags';
59 protected const FLAG_USERS = 'users';
60 protected const FLAG_CHATS = 'chats';
61 protected const FLAG_BOTS = 'bots';
62 private const ALLOWED_SEARCH_FLAGS = [self::FLAG_USERS, self::FLAG_CHATS, self::FLAG_BOTS];
63 protected const SEARCH_CHAT_TYPES_OPTION = 'searchChatTypes';
64 protected const EXCLUDE_IDS_OPTION = 'excludeIds';
65 protected const ALLOWED_SEARCH_CHAT_TYPES = [
66 Chat::IM_TYPE_CHAT,
67 Chat::IM_TYPE_OPEN,
68 Chat::IM_TYPE_CHANNEL,
69 Chat::IM_TYPE_OPEN_CHANNEL,
70 Chat::IM_TYPE_COLLAB,
71 ];
72 private const WITH_CHAT_BY_USERS_DEFAULT = false;
73 private const ONLY_WITH_MANAGE_MESSAGE_RIGHT_DEFAULT = false;
74 private const ONLY_WITH_OWNER_RIGHT_DEFAULT = false;
75 private const ONLY_WITH_NULL_ENTITY_TYPE_DEFAULT = false;
76 private const SEARCH_FLAGS_DEFAULT = [
77 self::FLAG_USERS => true,
78 self::FLAG_CHATS => true,
79 ];
80 private string $preparedSearchString;
81 private string $originalSearchString;
82 private array $userIds;
83 private array $chatIds;
84 private bool $sortEnable = true;
85
86 public function __construct(array $options = [])
87 {
88 $this->options[self::WITH_CHAT_BY_USERS_OPTION] = self::WITH_CHAT_BY_USERS_DEFAULT;
89 $this->options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION] = self::ONLY_WITH_MANAGE_MESSAGE_RIGHT_DEFAULT;
90 $this->options[self::ONLY_WITH_OWNER_RIGHT_OPTION] = self::ONLY_WITH_OWNER_RIGHT_DEFAULT;
91 $this->options[self::ONLY_WITH_NULL_ENTITY_TYPE_OPTION] = self::ONLY_WITH_NULL_ENTITY_TYPE_DEFAULT;
92 if (isset($options[self::WITH_CHAT_BY_USERS_OPTION]) && is_bool($options[self::WITH_CHAT_BY_USERS_OPTION]))
93 {
94 $this->options[self::WITH_CHAT_BY_USERS_OPTION] = $options[self::WITH_CHAT_BY_USERS_OPTION];
95 }
96 if (isset($options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION]) && is_bool($options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION]))
97 {
98 $this->options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION] = $options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION];
99 }
100 if (isset($options[self::EXCLUDE_FROM_RECENT_OPTION]) && is_array($options[self::EXCLUDE_FROM_RECENT_OPTION]))
101 {
102 $this->options[self::EXCLUDE_FROM_RECENT_OPTION] = $options[self::EXCLUDE_FROM_RECENT_OPTION];
103 }
104 if (isset($options[self::SEARCH_CHAT_TYPES_OPTION]) && is_array($options[self::SEARCH_CHAT_TYPES_OPTION]))
105 {
106 $this->options[self::SEARCH_CHAT_TYPES_OPTION] = $options[self::SEARCH_CHAT_TYPES_OPTION];
107 }
108 else
109 {
110 $this->options[self::SEARCH_CHAT_TYPES_OPTION] = static::ALLOWED_SEARCH_CHAT_TYPES;
111 }
112 if (isset($options[self::EXCLUDE_IDS_OPTION]) && is_array($options[self::EXCLUDE_IDS_OPTION]))
113 {
114 $this->options[self::EXCLUDE_IDS_OPTION] = $options[self::EXCLUDE_IDS_OPTION];
115 }
116 $this->prepareSearchFlags($options);
117 parent::__construct();
118 }
119
120 public function isAvailable(): bool
121 {
122 global $USER;
123
124 return $USER->IsAuthorized();
125 }
126
127 public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void
128 {
129 $this->originalSearchString = $searchQuery->getQuery();
130 $this->preparedSearchString = $this->prepareSearchString($searchQuery->getQuery());
131 $searchQuery->setCacheable(false);
132 if (!Content::canUseFulltextSearch($this->preparedSearchString))
133 {
134 return;
135 }
136 $items = $this->getSortedLimitedBlankItems();
137 $this->fillItems($items);
138 $dialog->addItems($items);
139 }
140
141 public function fillDialog(Dialog $dialog): void
142 {
143 if (!Loader::includeModule('intranet'))
144 {
145 return;
146 }
147
148 $requiredCountToFill = self::LIMIT - $dialog->getRecentItems()->count();
149
150 if ($requiredCountToFill <= 0)
151 {
152 return;
153 }
154
155 $defaultItems = $this->getDefaultDialogItems();
156
157 rsort($defaultItems);
158 $defaultItems = array_slice($defaultItems, 0, $requiredCountToFill);
159
160 foreach ($defaultItems as $itemId)
161 {
162 $dialog->getRecentItems()->add(new RecentItem(['id' => $itemId, 'entityId' => static::ENTITY_ID]));
163 }
164 }
165
166 protected function getDefaultDialogItems(): array
167 {
168 if (!$this->getContext()->getUser()->isExtranet())
169 {
170 return Department::getInstance()->getColleagues();
171 }
172
173 return Group::getUsersInSameGroups($this->getContext()->getUserId());
174 }
175
176 public function getItems(array $ids): array
177 {
178 $this->sortEnable = false;
179 $this->setUserAndChatIds($ids);
180 $items = $this->getItemsWithDates();
181 $this->fillItems($items);
182
183 $this->chatIds = [];
184 $this->userIds = [];
185
186 return $items;
187 }
188
189 public function getPreselectedItems(array $ids): array
190 {
191 /*$this->sortEnable = false;
192 $ids = array_slice($ids, 0, self::LIMIT);
193 $this->setUserAndChatIds($ids);
194 $foundItems = $this->getItemsWithDates();
195 $foundItemsDialogId = array_keys($foundItems);
196 $otherItemsDialogId = array_diff($ids, $foundItemsDialogId);
197 $otherItems = $this->getBlankItems($otherItemsDialogId);
198 $items = $this->mergeByKey($foundItems, $otherItems);
199 $this->fillItems($items);*/
200
201 return $this->getItems($ids);
202 }
203
204 private function setUserAndChatIds(array $ids): void
205 {
206 $needExcludeChats = isset($this->options[self::EXCLUDE_FROM_RECENT_OPTION][self::FLAG_CHATS]);
207 $needExcludeUsers = isset($this->options[self::EXCLUDE_FROM_RECENT_OPTION][self::FLAG_USERS]);
208 foreach ($ids as $id)
209 {
210 if ($this->isChatId($id) && !$needExcludeChats)
211 {
212 $chatId = substr($id, 4);
213 $this->chatIds[$chatId] = $chatId;
214 }
215 elseif (!$needExcludeUsers)
216 {
217 $this->userIds[$id] = $id;
218 }
219 }
220 }
221
222 private function getBlankItem(string $dialogId, ?DateTime $dateMessage = null, ?DateTime $secondDate = null): Item
223 {
224 $id = $dialogId;
225 $entityType = self::ENTITY_TYPE_USER;
226 if ($this->isChatId($dialogId))
227 {
228 $id = substr($dialogId, 4);
229 $entityType = self::ENTITY_TYPE_CHAT;
230 }
231 $customData = ['id' => $id];
232 $sort = 0;
233 $customData['dateMessage'] = $dateMessage;
234 $customData['secondSort'] = $secondDate instanceof DateTime ? $secondDate->getTimestamp() : 0;
235 if (isset($dateMessage))
236 {
237 if ($this->sortEnable)
238 {
239 $sort = $dateMessage->getTimestamp();
240 }
241 }
242
243 return new Item([
244 'id' => $dialogId,
245 'entityId' => static::ENTITY_ID,
246 'entityType' => $entityType,
247 'sort' => $sort,
248 'customData' => $customData,
249 ]);
250 }
251
256 private function fillItems(array $items): void
257 {
258 $userIds = [];
259 $chats = [];
260 foreach ($items as $item)
261 {
262 $id = $item->getCustomData()->get('id');
263 if ($item->getEntityType() === self::ENTITY_TYPE_USER)
264 {
265 $userIds[] = $id;
266
267 continue;
268 }
269
270 $chats[$id] = Chat::getInstance($id);
271 }
272
273 $users = new UserCollection($userIds);
274 $users->fillOnlineData();
275 $privateChatIds = \Bitrix\Im\Dialog::getChatIds($userIds, $this->getContext()->getUserId());
276 $copilotRoles = $this->getCopilotRoles($this->filterCopilotChats($chats));
277 Chat::fillSelfRelations($chats);
278
279 foreach ($items as $item)
280 {
281 $customData = $item->getCustomData()->getValues();
282 if ($item->getEntityType() === self::ENTITY_TYPE_USER)
283 {
284 $user = $users->getById($customData['id']);
285 $customData['user'] = $user->toRestFormat();
286
287 $chatId = (int)$privateChatIds[$customData['id']];
288 $customData['chat']['textFieldEnabled'] = (new TextFieldEnabled($chatId))->get();
289 $customData['chat']['backgroundId'] = (new Background($chatId))->get();
290 $customData['copilot'] = null;
291
292 $item
293 ->setTitle($user->getName())
294 ->setAvatar($user->getAvatar())
295 ->setCustomData($customData)
296 ;
297 }
298 if ($item->getEntityType() === self::ENTITY_TYPE_CHAT)
299 {
300 $chat = $chats[$customData['id']] ?? null;
301 if ($chat === null)
302 {
303 continue;
304 }
305
306 $customData['chat'] = $chat->toRestFormat(['CHAT_SHORT_FORMAT' => true]);
307 $customData['copilot'] = $copilotRoles[$chat->getId()] ?? null;
308 $item
309 ->setTitle($chat->getTitle())
310 ->setAvatar($chat->getAvatar())
311 ->setCustomData($customData)
312 ;
313 }
314 }
315 }
316
321 private function getCopilotRoles(array $copilotChats): array
322 {
323 $roleManager = new RoleManager();
324
325 $roleCodes = [];
326 foreach ($copilotChats as $chat)
327 {
328 $chatId = (int)$chat->getId();
329 $roleCodes[$chatId] = $roleManager->getMainRole($chatId);
330 }
331
332 $roles = $roleManager->getRoles($roleCodes);
333
334 $result = [];
335 foreach ($roleCodes as $chatId => $code)
336 {
337 $result[$chatId] = $roles[$code] ?? $roles[RoleManager::getDefaultRoleCode()];
338 }
339
340 return $result;
341 }
342
347 private function filterCopilotChats(array $chats): array
348 {
349 return array_filter($chats, static fn($chat) => $chat instanceof Chat\CopilotChat);
350 }
351
352 private function getItemsWithDates(): array
353 {
354 $userItemsWithDate = $this->getUserItemsWithDate();
355 $chatItemsWithDate = $this->getChatItemsWithDate();
356
357 return $this->mergeByKey($userItemsWithDate, $chatItemsWithDate);
358 }
359
360 private function getSortedLimitedBlankItems(): array
361 {
362 $items = $this->getItemsWithDates();
363 usort($items, function(Item $a, Item $b) {
364 if ($b->getSort() === $a->getSort())
365 {
366 if (!$this->isChatId($b->getId()) && !$this->isChatId($a->getId()))
367 {
368 $bUser = User::getInstance($b->getId());
369 $aUser = User::getInstance($a->getId());
370 if ($aUser->isExtranet() === $bUser->isExtranet())
371 {
372 return $bUser->getId() <=> $aUser->getId();
373 }
374
375 return $aUser->isExtranet() <=> $bUser->isExtranet();
376 }
377 return (int)$b->getCustomData()->get('secondSort') <=> (int)$a->getCustomData()->get('secondSort');
378 }
379 return $b->getSort() <=> $a->getSort();
380 });
381
382 return array_slice($items, 0, self::LIMIT);
383 }
384
385 private function getChatItemsWithDate(): array
386 {
387 if (!$this->needSearch(self::FLAG_CHATS))
388 {
389 return [];
390 }
391
392 if (isset($this->preparedSearchString))
393 {
394 return $this->mergeByKey(
395 $this->getChatItemsWithDateByUsers(),
396 $this->getChatItemsWithDateByTitle()
397 );
398 }
399
400 if (isset($this->chatIds) && !empty($this->chatIds))
401 {
402 return $this->getChatItemsWithDateByIds();
403 }
404
405 return [];
406 }
407
408 private function getChatItemsWithDateByIds(): array
409 {
410 if (!isset($this->chatIds) || empty($this->chatIds))
411 {
412 return [];
413 }
414
415 $result = $this->getCommonChatQuery(limit: self::TECHNICAL_LIMIT)->whereIn('ID', $this->chatIds)->fetchAll();
416
417 return $this->getChatItemsByRawResult($result);
418 }
419
420 private function getChatItemsWithDateByTitle(): array
421 {
422 if (!isset($this->preparedSearchString))
423 {
424 return [];
425 }
426
427 $result = $this
428 ->getCommonChatQueryWithOrder()
429 ->whereMatch('INDEX.SEARCH_TITLE', $this->preparedSearchString)
430 ->fetchAll()
431 ;
432
433 return $this->getChatItemsByRawResult($result, ['byUser' => false]);
434 }
435
436 private function getChatItemsWithDateByUsers(): array
437 {
438 if (!isset($this->preparedSearchString) || !$this->withChatByUsers())
439 {
440 return [];
441 }
442
443 $result = $this
444 ->getCommonChatQueryWithOrder(Join::TYPE_INNER)
445 ->registerRuntimeField(
446 'CHAT_SEARCH',
447 (new Reference(
448 'CHAT_SEARCH',
449 Entity::getInstanceByQuery($this->getChatsByUserNameQuery()),
450 Join::on('this.ID', 'ref.CHAT_ID')
451 ))->configureJoinType(Join::TYPE_INNER)
452 )
453 ->fetchAll()
454 ;
455
456 return $this->getChatItemsByRawResult($result, ['byUser' => true]);
457 }
458
459 private function getChatsByUserNameQuery(): Query
460 {
461 return RelationTable::query()
462 ->setSelect(['CHAT_ID'])
463 ->registerRuntimeField(
464 'USER',
465 (new Reference(
466 'USER',
467 \Bitrix\Main\UserTable::class,
468 Join::on('this.USER_ID', 'ref.ID'),
469 ))->configureJoinType(Join::TYPE_INNER)
470 )
471 ->registerRuntimeField(
472 'USER_INDEX',
473 (new Reference(
474 'USER_INDEX',
475 UserIndexTable::class,
476 Join::on('this.USER_ID', 'ref.USER_ID'),
477 ))->configureJoinType(Join::TYPE_INNER)
478 )
479 ->whereIn('MESSAGE_TYPE', [Chat::IM_TYPE_CHAT, Chat::IM_TYPE_OPEN])
480 ->where('USER.IS_REAL_USER', 'Y')
481 ->whereMatch('USER_INDEX.SEARCH_USER_CONTENT', $this->preparedSearchString)
482 ->setGroup(['CHAT_ID'])
483 ;
484 }
485
486 protected function getChatItemsByRawResult(array $raw, array $additionalCustomData = []): array
487 {
488 $result = [];
489
490 foreach ($raw as $row)
491 {
492 $dialogId = 'chat' . $row['ID'];
493 $messageDate = $row['MESSAGE_DATE_CREATE'] ?? null;
494 $secondDate = $row['MESSAGE_DATE_CREATE'] ?? null;
495 if (($row['IS_MEMBER'] ?? 'Y') === 'N')
496 {
497 $messageDate = null;
498 }
499 $item = $this->getBlankItem($dialogId, $messageDate, $secondDate);
500 if (!empty($additionalCustomData))
501 {
502 $customData = $item->getCustomData()->getValues();
503 $item->setCustomData(array_merge($customData, $additionalCustomData));
504 }
505 $result[$dialogId] = $item;
506 }
507
508 return $result;
509 }
510
511 protected function getCommonChatQueryWithOrder(string $joinType = Join::TYPE_LEFT, int $limit = self::LIMIT): Query
512 {
513 return $this->getCommonChatQuery($joinType, $limit)
514 ->setOrder(['IS_MEMBER' => 'DESC', 'LAST_MESSAGE_ID' => 'DESC', 'DATE_CREATE' => 'DESC'])
515 ;
516 }
517
518 protected function getCommonChatQuery(string $joinType = Join::TYPE_LEFT, int $limit = self::LIMIT): Query
519 {
520 $query = ChatTable::query()
521 ->setSelect(['ID', 'IS_MEMBER', 'MESSAGE_DATE_CREATE' => 'MESSAGE.DATE_CREATE', 'DATE_CREATE'])
522 ->registerRuntimeField(new Reference(
523 'RELATION',
524 RelationTable::class,
525 Join::on('this.ID', 'ref.CHAT_ID')
526 ->where('ref.USER_ID', $this->getContext()->getUserId()),
527 ['join_type' => $joinType]
528 )
529 )
530 ->registerRuntimeField(
531 new Reference(
532 'MESSAGE',
533 MessageTable::class,
534 Join::on('this.LAST_MESSAGE_ID', 'ref.ID'),
535 ['join_type' => Join::TYPE_LEFT]
536 )
537 )
538 ->registerRuntimeField(
539 'IS_MEMBER',
540 (new ExpressionField(
541 'IS_MEMBER',
542 "CASE WHEN %s IS NULL THEN 'N' ELSE 'Y' END",
543 ['RELATION.ID']
544 ))->configureValueType(BooleanField::class)
545 )
546 ->setLimit($limit)
547 ->whereIn('TYPE', $this->getAllowedChatTypesForQuery())
548 ;
549 if ($joinType === Join::TYPE_LEFT)
550 {
551 $query->where($this->getRelationFilter());
552 }
553
554 if ($this->options[self::ONLY_WITH_MANAGE_MESSAGES_RIGHT_OPTION])
555 {
556 \Bitrix\Im\V2\Permission\Filter::getRoleOrmFilter($query, ActionGroup::ManageMessages, 'RELATION', '');
557 }
558
559 if ($this->options[self::ONLY_WITH_MANAGE_USERS_ADD_RIGHT_OPTION])
560 {
561 \Bitrix\Im\V2\Permission\Filter::getRoleOrmFilter($query, ActionGroup::ManageUsersAdd, 'RELATION', '');
562 }
563
564 if ($this->options[self::ONLY_WITH_OWNER_RIGHT_OPTION])
565 {
566 $query->where('AUTHOR_ID', $this->getContext()->getUserId());
567 }
568
569 if ($this->options[self::ONLY_WITH_NULL_ENTITY_TYPE_OPTION])
570 {
571 $query->where(Query::filter()
572 ->logic('or')
573 ->whereNull('ENTITY_TYPE')
574 ->where('ENTITY_TYPE', ''))
575 ;
576 }
577
578 if (isset($this->options[self::EXCLUDE_IDS_OPTION]) && is_array($this->options[self::EXCLUDE_IDS_OPTION]))
579 {
580 $query->whereNotIn('ID', $this->options[self::EXCLUDE_IDS_OPTION]);
581 }
582
583 return $query;
584 }
585
586 protected function getAllowedChatTypesForQuery(): array
587 {
588 $types = [
589 Chat::IM_TYPE_CHAT,
590 Chat::IM_TYPE_OPEN,
591 Chat::IM_TYPE_CHANNEL,
592 Chat::IM_TYPE_OPEN_CHANNEL,
593 Chat::IM_TYPE_COLLAB,
594 Chat::IM_TYPE_COPILOT,
595 Chat::IM_TYPE_AI_ASSISTANT,
596 ];
597
598 return $types;
599 }
600
601 private function getRelationFilter(): ConditionTree
602 {
603 if (User::getCurrent()->isExtranet())
604 {
605 return Query::filter()->whereNotNull('RELATION.USER_ID');
606 }
607
608 return Query::filter()
609 ->logic('or')
610 ->whereNotNull('RELATION.USER_ID')
611 ->whereIn('TYPE', [Chat::IM_TYPE_OPEN, Chat::IM_TYPE_OPEN_CHANNEL])
612 ;
613 }
614
615 private function getUserItemsWithDate(): array
616 {
617 $result = [];
618 if (!$this->needSearch(self::FLAG_USERS))
619 {
620 return $result;
621 }
622 $query = UserTable::query()
623 ->setSelect(['ID', 'DATE_MESSAGE' => 'RECENT.DATE_MESSAGE', 'IS_INTRANET_USER', 'DATE_CREATE' => 'DATE_REGISTER'])
624 ->where('ACTIVE', true)
625 ->registerRuntimeField(
626 'RECENT',
627 new Reference(
628 'RECENT',
629 RecentTable::class,
630 Join::on('this.ID', 'ref.ITEM_ID')
631 ->where('ref.USER_ID', $this->getContext()->getUserId())
632 ->where('ref.ITEM_TYPE', Chat::IM_TYPE_PRIVATE),
633 ['join_type' => Join::TYPE_LEFT]
634 )
635 )
636 ;
637
638 if (isset($this->preparedSearchString))
639 {
640 $query
641 ->whereMatch('INDEX.SEARCH_USER_CONTENT', $this->preparedSearchString)
642 ->setOrder(['RECENT.DATE_MESSAGE' => 'DESC', 'IS_INTRANET_USER' => 'DESC', 'DATE_CREATE' => 'DESC'])
643 ->setLimit(self::LIMIT)
644 ;
645 }
646 elseif (isset($this->userIds) && !empty($this->userIds))
647 {
648 $query->whereIn('ID', $this->userIds)->setLimit(self::TECHNICAL_LIMIT);
649 }
650 else
651 {
652 return [];
653 }
654
655 $query->where($this->getIntranetFilter());
656
657 $raw = $query->fetchAll();
658
659 foreach ($raw as $row)
660 {
661 if ($this->isHiddenBot((int)$row['ID']))
662 {
663 continue;
664 }
665
666 $result[(int)$row['ID']] = $this->getBlankItem((int)$row['ID'], $row['DATE_MESSAGE'], $row['DATE_CREATE']);
667 }
668
669 $result = $this->getAdditionalUsers($result);
670
671 return $result;
672 }
673
674 private function getAdditionalUsers(array $foundUserItems): array
675 {
676 if ($this->needAddFavoriteChat($foundUserItems))
677 {
678 $foundUserItems[$this->getContext()->getUserId()] = $this->getFavoriteChatUserItem();
679 }
680
681 return $foundUserItems;
682 }
683
684 private function getFavoriteChatUserItem(): Item
685 {
686 $userId = $this->getContext()->getUserId();
687 $row = ChatTable::query()
688 ->setSelect(['DATE_MESSAGE' => 'MESSAGE.DATE_CREATE', 'DATE_CREATE'])
689 ->registerRuntimeField(
690 new Reference(
691 'MESSAGE',
692 MessageTable::class,
693 Join::on('this.LAST_MESSAGE_ID', 'ref.ID'),
694 ['join_type' => Join::TYPE_LEFT]
695 )
696 )
697 ->where('ENTITY_TYPE', Chat::ENTITY_TYPE_FAVORITE)
698 ->where('ENTITY_ID', $userId)
699 ->fetch() ?: []
700 ;
701 $dateMessage = $row['DATE_MESSAGE'] ?? null;
702 $dateCreate = $row['DATE_CREATE'] ?? null;
703
704 return $this->getBlankItem($this->getContext()->getUserId(), $dateMessage, $dateCreate);
705 }
706
707 private function needAddFavoriteChat(array $foundUserItems): bool
708 {
709 return
710 !isset($foundUserItems[$this->getContext()->getUserId()])
711 && isset($this->originalSearchString)
712 && static::isPhraseFoundBySearchQuery(Chat\FavoriteChat::getTitlePhrase(), $this->originalSearchString)
713 ;
714 }
715
716 private static function isPhraseFoundBySearchQuery(string $phrase, string $searchQuery): bool
717 {
718 $searchWords = explode(' ', $searchQuery);
719 $phraseWords = explode(' ', $phrase);
720
721 foreach ($searchWords as $searchWord)
722 {
723 $searchWordLowerCase = mb_strtolower($searchWord);
724 $found = false;
725 foreach ($phraseWords as $phraseWord)
726 {
727 $phraseWordLowerCase = mb_strtolower($phraseWord);
728 if (str_starts_with($phraseWordLowerCase, $searchWordLowerCase))
729 {
730 $found = true;
731 break;
732 }
733 }
734 if (!$found)
735 {
736 return false;
737 }
738 }
739
740 return true;
741 }
742
743 private function isHiddenBot(int $userId): bool
744 {
746
747 if ($user instanceof UserBot && $user->isBot())
748 {
749 if (!$this->needSearch(self::FLAG_BOTS))
750 {
751 return true;
752 }
753
754 $botData = $user->getBotData()->toRestFormat();
755 if ($botData['isHidden'])
756 {
757 return true;
758 }
759 }
760
761 return false;
762 }
763
764 private function getIntranetFilter(): ConditionTree
765 {
766 $filter = Query::filter();
767 if (!Loader::includeModule('intranet'))
768 {
769 return $filter->where($this->getRealUserOrBotCondition());
770 }
771
772 $subQuery = Group::getExtranetAccessibleUsersQuery($this->getContext()->getUserId());
773 if (!User::getCurrent()->isExtranet())
774 {
775 $filter->logic('or');
776 $filter->where('IS_INTRANET_USER', true);
777 if ($subQuery !== null)
778 {
779 $filter->whereIn('ID', $subQuery);
780 }
781 return $filter;
782 }
783
784 $filter->where($this->getRealUserOrBotCondition());
785 if ($subQuery !== null)
786 {
787 $filter->whereIn('ID', $subQuery);
788 }
789 else
790 {
791 $filter->where(new ExpressionField('EMPTY_LIST', '1'), '!=', 1);
792 }
793
794 return $filter;
795 }
796
797 private function getRealUserOrBotCondition(): ConditionTree
798 {
799 return Query::filter()
800 ->logic('or')
801 ->whereNotIn('EXTERNAL_AUTH_ID', UserTable::filterExternalUserTypes(['bot']))
802 ->whereNull('EXTERNAL_AUTH_ID')
803 ;
804 }
805
806 private function prepareSearchFlags(array $options): void
807 {
808 $this->options[self::SEARCH_FLAGS_OPTION] = self::SEARCH_FLAGS_DEFAULT;
809
810 if (isset($options[self::INCLUDE_ONLY_OPTION]) && is_array($options[self::INCLUDE_ONLY_OPTION]))
811 {
812 foreach (self::ALLOWED_SEARCH_FLAGS as $searchFlag)
813 {
814 $this->options[self::SEARCH_FLAGS_OPTION][$searchFlag] = false;
815 }
816
817 foreach ($options[self::INCLUDE_ONLY_OPTION] as $searchFlag)
818 {
819 if ($this->isValidSearchFlag($searchFlag))
820 {
821 $this->options[self::SEARCH_FLAGS_OPTION][$searchFlag] = true;
822 }
823 }
824 }
825 elseif (isset($options[self::EXCLUDE_OPTION]) && is_array($options[self::EXCLUDE_OPTION]))
826 {
827 foreach ($options[self::EXCLUDE_OPTION] as $searchFlag)
828 {
829 if ($this->isValidSearchFlag($searchFlag))
830 {
831 $this->options[self::SEARCH_FLAGS_OPTION][$searchFlag] = false;
832 }
833 }
834 }
835 }
836
837 private function isValidSearchFlag(string $searchFlag): bool
838 {
839 return in_array($searchFlag, self::ALLOWED_SEARCH_FLAGS, true);
840 }
841
842 private function needSearch(string $flag): bool
843 {
844 return $this->options[self::SEARCH_FLAGS_OPTION][$flag] ?? true;
845 }
846
847 private function mergeByKey(array ...$arrays): array
848 {
849 $result = [];
850 foreach ($arrays as $array)
851 {
852 foreach ($array as $key => $value)
853 {
855 }
856 }
857
858 return $result;
859 }
860
861 private function isChatId(string $id): bool
862 {
863 return substr($id, 0, 4) === 'chat';
864 }
865
866 private function withChatByUsers(): bool
867 {
868 return $this->options[self::WITH_CHAT_BY_USERS_OPTION] ?? self::WITH_CHAT_BY_USERS_DEFAULT;
869 }
870
871 private function prepareSearchString(string $searchString): string
872 {
873 $searchString = trim($searchString);
874
875 return Helper::matchAgainstWildcard(Content::prepareStringToken($searchString));
876 }
877}
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
Определения dialog.php:11
static getInstance($userId=null)
Определения user.php:45
static getTitlePhrase()
Определения FavoriteChat.php:215
getCommonChatQueryWithOrder(string $joinType=Join::TYPE_LEFT, int $limit=self::LIMIT)
Определения RecentProvider.php:511
getChatItemsByRawResult(array $raw, array $additionalCustomData=[])
Определения RecentProvider.php:486
getCommonChatQuery(string $joinType=Join::TYPE_LEFT, int $limit=self::LIMIT)
Определения RecentProvider.php:518
doSearch(SearchQuery $searchQuery, Dialog $dialog)
Определения RecentProvider.php:127
static getRoleOrmFilter(Query $query, ActionGroup $action, string $relationTableAlias, string $chatTableAlias)
Определения Filter.php:32
static getInstance()
Определения application.php:98
addItems(array $items)
Определения dialog.php:135
setCacheable(bool $flag=true)
Определения searchquery.php:72
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$filter
Определения iblock_catalog_list.php:54
global $USER
Определения csv_new_run.php:40
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$value
Определения Param.php:39
int $chatId
Определения Param.php:36
Определения chain.php:3
$user
Определения mysql_to_pgsql.php:33
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$items
Определения template.php:224
else $a
Определения template.php:137