PageRenderTime 72ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/im/lib/integration/ui/entityselector/botprovider.php

https://gitlab.com/alexprowars/bitrix
PHP | 393 lines | 334 code | 54 blank | 5 comment | 38 complexity | 7aa582ca753535573fa2941affd1637b MD5 | raw file
  1. <?php
  2. namespace Bitrix\Im\Integration\UI\EntitySelector;
  3. use Bitrix\Im\Bot;
  4. use Bitrix\Im\Integration\UI\EntitySelector\Helper;
  5. use Bitrix\Im\Model\BotTable;
  6. use Bitrix\Im\User;
  7. use Bitrix\Main\EO_User;
  8. use Bitrix\Main\EO_User_Collection;
  9. use Bitrix\Main\ModuleManager;
  10. use Bitrix\Main\ORM\Fields\Relations\Reference;
  11. use Bitrix\Main\ORM\Query\Join;
  12. use Bitrix\Main\ORM\Query\Filter;
  13. use Bitrix\Main\Search\Content;
  14. use Bitrix\Main\UserTable;
  15. use Bitrix\UI\EntitySelector\BaseProvider;
  16. use Bitrix\UI\EntitySelector\Dialog;
  17. use Bitrix\UI\EntitySelector\Item;
  18. use Bitrix\UI\EntitySelector\SearchQuery;
  19. class BotProvider extends BaseProvider
  20. {
  21. public function __construct(array $options = [])
  22. {
  23. parent::__construct();
  24. if (isset($options['searchableBotTypes']) && is_array($options['searchableBotTypes']))
  25. {
  26. $this->options['searchableBotTypes'] = $options['searchableBotTypes'];
  27. }
  28. $this->options['fillDialog'] = true;
  29. if (isset($options['fillDialog']) && is_bool($options['fillDialog']))
  30. {
  31. $this->options['fillDialog'] = $options['fillDialog'];
  32. }
  33. }
  34. public function isAvailable(): bool
  35. {
  36. return $GLOBALS['USER']->isAuthorized() && !User::getInstance()->isExtranet();
  37. }
  38. public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void
  39. {
  40. $limit = 100;
  41. $items = $this->getBotItems([
  42. 'searchQuery' => $searchQuery->getQuery(),
  43. 'limit' => $limit
  44. ]);
  45. $limitExceeded = $limit <= count($items);
  46. if ($limitExceeded)
  47. {
  48. $searchQuery->setCacheable(false);
  49. }
  50. $dialog->addItems($items);
  51. }
  52. public function shouldFillDialog(): bool
  53. {
  54. return $this->getOption('fillDialog', true);
  55. }
  56. public function getItems(array $ids): array
  57. {
  58. if (!$this->shouldFillDialog())
  59. {
  60. return [];
  61. }
  62. return $this->getBotItems([
  63. 'userId' => $ids,
  64. ]);
  65. }
  66. public function getSelectedItems(array $ids): array
  67. {
  68. return $this->getItems($ids);
  69. }
  70. public function getBotItems(array $options = []): array
  71. {
  72. return $this->makeBotItems($this->getBotCollection($options), $options);
  73. }
  74. public function makeBotItems(EO_User_Collection $bots, array $options = []): array
  75. {
  76. return self::makeItems($bots, array_merge($this->getOptions(), $options));
  77. }
  78. public function getBotCollection(array $options = []): EO_User_Collection
  79. {
  80. $options = array_merge($this->getOptions(), $options);
  81. return self::getBots($options);
  82. }
  83. public static function getBots(array $options = []): EO_User_Collection
  84. {
  85. $query = UserTable::query();
  86. $query->setSelect([
  87. 'ID',
  88. 'NAME',
  89. 'LAST_NAME',
  90. 'SECOND_NAME',
  91. 'PERSONAL_PHOTO',
  92. 'WORK_POSITION',
  93. 'BOT_TYPE' => 'im_bot.TYPE',
  94. 'BOT_COUNT_MESSAGE' => 'im_bot.COUNT_MESSAGE',
  95. ]);
  96. $query->registerRuntimeField(
  97. new Reference(
  98. 'im_bot',
  99. BotTable::class,
  100. Join::on('this.ID', 'ref.BOT_ID'),
  101. ['join_type' => Join::TYPE_INNER]
  102. )
  103. );
  104. if (!empty($options['searchQuery']) && is_string($options['searchQuery']))
  105. {
  106. $query->registerRuntimeField(
  107. new Reference(
  108. 'USER_INDEX',
  109. \Bitrix\Main\UserIndexTable::class,
  110. Join::on('this.ID', 'ref.USER_ID'),
  111. ['join_type' => 'INNER']
  112. )
  113. );
  114. $query->whereMatch(
  115. 'USER_INDEX.SEARCH_USER_CONTENT',
  116. Filter\Helper::matchAgainstWildcard(
  117. Content::prepareStringToken($options['searchQuery']), '*', 1
  118. )
  119. );
  120. }
  121. if (isset($options['searchableBotTypes']) && is_array($options['searchableBotTypes']))
  122. {
  123. $query->whereIn('BOT_TYPE', $options['searchableBotTypes']);
  124. }
  125. $userIds = [];
  126. $userFilter = isset($options['userId']) ? 'userId' : (isset($options['!userId']) ? '!userId' : null);
  127. if (isset($options[$userFilter]))
  128. {
  129. if (is_array($options[$userFilter]) && !empty($options[$userFilter]))
  130. {
  131. foreach ($options[$userFilter] as $id)
  132. {
  133. $id = (int)$id;
  134. if ($id > 0)
  135. {
  136. $userIds[] = $id;
  137. }
  138. }
  139. $userIds = array_unique($userIds);
  140. if (!empty($userIds))
  141. {
  142. if ($userFilter === 'userId')
  143. {
  144. $query->whereIn('ID', $userIds);
  145. }
  146. else
  147. {
  148. $query->whereNotIn('ID', $userIds);
  149. }
  150. }
  151. }
  152. else if (!is_array($options[$userFilter]) && (int)$options[$userFilter] > 0)
  153. {
  154. if ($userFilter === 'userId')
  155. {
  156. $query->where('ID', (int)$options[$userFilter]);
  157. }
  158. else
  159. {
  160. $query->whereNot('ID', (int)$options[$userFilter]);
  161. }
  162. }
  163. }
  164. if (isset($options['limit']) && is_int($options['limit']))
  165. {
  166. $query->setLimit($options['limit']);
  167. }
  168. else
  169. {
  170. $query->setLimit(100);
  171. }
  172. if (!empty($options['order']) && is_array($options['order']))
  173. {
  174. $query->setOrder($options['order']);
  175. }
  176. else
  177. {
  178. $query->setOrder([
  179. 'BOT_COUNT_MESSAGE' => 'DESC'
  180. ]);
  181. }
  182. $result = $query->exec();
  183. return $result->fetchCollection();
  184. }
  185. public static function makeItems(EO_User_Collection $bots, array $options = []): array
  186. {
  187. $result = [];
  188. $isBitrix24 = ModuleManager::isModuleInstalled('bitrix24');
  189. foreach ($bots as $bot)
  190. {
  191. $botData = Bot::getCache($bot->getId());
  192. if (
  193. $isBitrix24
  194. && $botData['TYPE'] === Bot::TYPE_NETWORK
  195. && $botData['CLASS'] === 'Bitrix\ImBot\Bot\Support24'
  196. )
  197. {
  198. continue;
  199. }
  200. $result[] = self::makeItem($bot, $options);
  201. }
  202. return $result;
  203. }
  204. public static function makeItem(EO_User $bot, array $options = []): Item
  205. {
  206. $defaultIcon =
  207. 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2228%22%20'
  208. . 'height%3D%2228%22%20viewBox%3D%220%200%2028%2028%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20'
  209. . 'fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Ccircle%20cx%3D%2214%22%20cy%3D%2214%22%20r%3D%2214%22%20'
  210. . 'fill%3D%22%232FC6F6%22%2F%3E%0A%20%20%20%20%3Cpath%20'
  211. . 'fill%3D%22%23FFFFFF%22%20d%3D%22M19.053132%2C10.0133936%20L19.9184066%2C7.09247624%20C19'
  212. . '.9937984%2C6.83851954%2019.930205%2C6.56296095%2019.7515811%2C6.36960075%20C19.5729573%2C6'
  213. . '.17624054%2019.3064404%2C6.09445472%2019.0524247%2C6.15505122%20C18.798409%2C6.21564772%2018'
  214. . '.5954856%2C6.40942049%2018.5200937%2C6.66337719%20L17.7789513%2C9.17078557%20C15.4748028%2C7'
  215. . '.94807693%2012.7275787%2C7.95098931%2010.4259431%2C9.17858062%20L9.68114981%2C6.66337719%20C9'
  216. . '.56460406%2C6.27079414%209.15710205%2C6.04859979%208.77096861%2C6.16709222%20C8.38483517%2C6'
  217. . '.28558465%208.16629117%2C6.69989319%208.28283693%2C7.09247624%20L9.15176243%2C10.0249005%20C7'
  218. . '.2004503%2C11.6106349%206.0672511%2C14.0147948%206.0740137%2C16.5545557%20C6.0740137%2C21.1380463%209'
  219. . '.67019697%2C20.0133316%2014.1097491%2C20.0133316%20C18.5493013%2C20.0133316%2022.1454845%2C21'
  220. . '.1380463%2022.1454845%2C16.5545557%20C22.1533008%2C14.0079881%2021.0139427%2C11.5979375%2019'
  221. . '.053132%2C10.0133936%20Z%20M14.1024472%2C15.9316939%20C10.9334248%2C15.9316939%208.36315777%2C16'
  222. . '.2657676%208.36315777%2C14.9001487%20C8.36315777%2C13.5345299%2010.9334248%2C12.4257765%2014'
  223. . '.1024472%2C12.4257765%20C17.2714696%2C12.4257765%2019.8453876%2C13.5334163%2019.8453876%2C14'
  224. . '.9001487%20C19.8453876%2C16.2668812%2017.2751206%2C15.9316939%2014.1024472%2C15.9316939%20Z%20M11'
  225. . '.477416%2C13.4487843%20C11.0249669%2C13.5328062%2010.7150974%2C13.9604811%2010.7703097%2C14'
  226. . '.4247164%20C10.825522%2C14.8889517%2011.2267231%2C15.229209%2011.6858298%2C15.201166%20C12'
  227. . '.1449365%2C15.1731231%2012.5031841%2C14.7864774%2012.5033322%2C14.3188606%20C12.4520761%2C13'
  228. . '.7928552%2011.9955831%2C13.4057049%2011.477416%2C13.4487843%20Z%20M16.0191544%2C14.4269902%20C16'
  229. . '.0754002%2C14.8911461%2016.4771659%2C15.230674%2016.9362856%2C15.2020479%20C17.3954053%2C15'
  230. . '.1734219%2017.7533545%2C14.7865259%2017.7533947%2C14.3188606%20C17.7021533%2C13.7912874%2017'
  231. . '.2433569%2C13.4035634%2016.7238275%2C13.4487843%20C16.2716033%2C13.5343137%2015.9629087%2C13'
  232. . '.9628342%2016.0191544%2C14.4269902%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A'
  233. ;
  234. $customData = [
  235. 'imUser' => User::getInstance($bot->getId())->getArray(),
  236. 'imBot' => Bot::getCache($bot->getId()),
  237. ];
  238. $avatar = Helper\User::makeAvatar($bot);
  239. if (!$avatar)
  240. {
  241. if ($customData['imUser']['COLOR'] !== '')
  242. {
  243. $avatar = str_replace(
  244. '2FC6F6',
  245. explode('#', $customData['imUser']['COLOR'])[1],
  246. $defaultIcon
  247. );
  248. }
  249. else
  250. {
  251. $avatar = $defaultIcon;
  252. }
  253. }
  254. return new Item([
  255. 'id' => $bot->getId(),
  256. 'entityId' => 'im-bot',
  257. 'entityType' => Bot::getListForJs()[$bot->getId()]['type'],
  258. 'title' => Helper\User::formatName($bot, $options),
  259. 'avatar' => $avatar,
  260. 'customData' => $customData,
  261. ]);
  262. }
  263. public function fillDialog(Dialog $dialog): void
  264. {
  265. if (!$this->shouldFillDialog())
  266. {
  267. return;
  268. }
  269. $maxBotsInRecentTab = 50;
  270. // Preload first 50 users ('doSearch' method has to have the same filter).
  271. $preloadedBots = $this->getBotCollection([
  272. 'order' => ['ID' => 'DESC'],
  273. 'limit' => $maxBotsInRecentTab
  274. ]);
  275. if (count($preloadedBots) < $maxBotsInRecentTab)
  276. {
  277. // Turn off the user search
  278. $entity = $dialog->getEntity('im-bot');
  279. if ($entity)
  280. {
  281. $entity->setDynamicSearch(false);
  282. }
  283. }
  284. $recentBots = new EO_User_Collection();
  285. // Recent Items
  286. $recentItems = $dialog->getRecentItems()->getEntityItems('im-bot');
  287. $recentIds = array_map('intval', array_keys($recentItems));
  288. $this->fillRecentBots($recentBots, $recentIds, $preloadedBots);
  289. // Global Recent Items
  290. if (count($recentBots) < $maxBotsInRecentTab)
  291. {
  292. $recentGlobalItems = $dialog->getGlobalRecentItems()->getEntityItems('im-bot');
  293. $recentGlobalIds = [];
  294. if (!empty($recentGlobalItems))
  295. {
  296. $recentGlobalIds = array_map('intval', array_keys($recentGlobalItems));
  297. $recentGlobalIds = array_values(array_diff($recentGlobalIds, $recentBots->getIdList()));
  298. $recentGlobalIds = array_slice($recentGlobalIds, 0, $maxBotsInRecentTab - $recentBots->count());
  299. }
  300. $this->fillRecentBots($recentBots, $recentGlobalIds, $preloadedBots);
  301. }
  302. // The rest of preloaded users
  303. foreach ($preloadedBots as $preloadedBot)
  304. {
  305. $recentBots->add($preloadedBot);
  306. }
  307. $dialog->addRecentItems($this->makeBotItems($recentBots));
  308. }
  309. private function fillRecentBots(
  310. EO_User_Collection $recentBots,
  311. array $recentIds,
  312. EO_User_Collection $preloadedBots
  313. ): void
  314. {
  315. if (count($recentIds) < 1)
  316. {
  317. return;
  318. }
  319. $ids = array_values(array_diff($recentIds, $preloadedBots->getIdList()));
  320. if (!empty($ids))
  321. {
  322. $bots = $this->getBotCollection([
  323. 'userId' => $ids,
  324. ]);
  325. foreach ($bots as $bot)
  326. {
  327. $preloadedBots->add($bot);
  328. }
  329. }
  330. foreach ($recentIds as $recentId)
  331. {
  332. $bot = $preloadedBots->getByPrimary($recentId);
  333. if ($bot)
  334. {
  335. $recentBots->add($bot);
  336. }
  337. }
  338. }
  339. }