PageRenderTime 26ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/calendar/lib/util.php

https://gitlab.com/alexprowars/bitrix
PHP | 672 lines | 504 code | 68 blank | 100 comment | 72 complexity | ec9a27adc9ebf9e6b27b1e1ad9928aa5 MD5 | raw file
  1. <?php
  2. namespace Bitrix\Calendar;
  3. use Bitrix\Calendar\Sync\Util\MsTimezoneConverter;
  4. use Bitrix\Main;
  5. use Bitrix\Main\Loader;
  6. use Bitrix\Main\Localization\LanguageTable;
  7. use Bitrix\Main\Type\Date;
  8. use Bitrix\Main\Type\DateTime;
  9. class Util
  10. {
  11. public const USER_SELECTOR_CONTEXT = "CALENDAR";
  12. public const LIMIT_NUMBER_BANNER_IMPRESSIONS = 3;
  13. public const DATETIME_PHP_FORMAT = 'Y-m-d H:i:sP';
  14. private static $requestUid = '';
  15. private static $userAccessCodes = [];
  16. private static $pathCache = [];
  17. /**
  18. * @param $managerId
  19. * @param $userId
  20. * @return bool
  21. */
  22. public static function isManagerForUser($managerId, $userId): bool
  23. {
  24. return in_array('IU'.$userId, self::getUserAccessCodes($managerId));
  25. }
  26. /**
  27. * @return bool
  28. * @throws Main\ArgumentNullException
  29. * @throws Main\ArgumentOutOfRangeException
  30. */
  31. public static function isSectionStructureConverted(): bool
  32. {
  33. return \Bitrix\Main\Config\Option::get('calendar', 'sectionStructureConverted', 'N') === 'Y';
  34. }
  35. /**
  36. * @param $date
  37. * @param bool $round
  38. * @param bool $getTime
  39. * @return false|float|int
  40. */
  41. public static function getTimestamp($date, $round = true, $getTime = true)
  42. {
  43. $timestamp = MakeTimeStamp($date, \CSite::getDateFormat($getTime ? "FULL" : "SHORT"));
  44. return $round ? (round($timestamp / 60) * 60) : $timestamp;
  45. }
  46. /**
  47. * @param string|null $timeZone
  48. * @return bool
  49. */
  50. public static function isTimezoneValid(?string $timeZone): bool
  51. {
  52. return (!is_null($timeZone) && $timeZone !== 'false' && in_array($timeZone, timezone_identifiers_list(), true));
  53. }
  54. /**
  55. * @param string|null $tz
  56. * @return \DateTimeZone
  57. */
  58. public static function prepareTimezone(?string $tz = null): \DateTimeZone
  59. {
  60. if (!$tz)
  61. {
  62. return new \DateTimeZone("UTC");
  63. }
  64. if (self::isTimezoneValid($tz))
  65. {
  66. return new \DateTimeZone($tz);
  67. }
  68. if ($timezones = MsTimezoneConverter::getValidateTimezones($tz))
  69. {
  70. return new \DateTimeZone($timezones[0]);
  71. }
  72. return new \DateTimeZone(self::getServerTimezoneName());
  73. }
  74. /**
  75. * @param string|null $date
  76. * @param bool $fullDay
  77. * @param string $tz
  78. * @return Date
  79. * @throws Main\ObjectException
  80. */
  81. public static function getDateObject(string $date = null, $fullDay = true, $tz = 'UTC'): Date
  82. {
  83. $preparedDate = $date;
  84. if ($date)
  85. {
  86. $timestamp = \CCalendar::Timestamp($date, false, !$fullDay);
  87. $preparedDate = \CCalendar::Date($timestamp, !$fullDay);
  88. }
  89. return $fullDay
  90. ? new Date($preparedDate, Date::convertFormatToPhp(FORMAT_DATE))
  91. : new DateTime($preparedDate, Date::convertFormatToPhp(FORMAT_DATETIME), Util::prepareTimezone($tz));
  92. }
  93. /**
  94. * @return string
  95. */
  96. public static function getUserSelectorContext(): string
  97. {
  98. return self::USER_SELECTOR_CONTEXT;
  99. }
  100. public static function checkRuZone(): bool
  101. {
  102. if (\Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24'))
  103. {
  104. $isRussian = (\CBitrix24::getPortalZone() === 'ru');
  105. }
  106. else
  107. {
  108. $iterator = LanguageTable::getList([
  109. 'select' => ['ID'],
  110. 'filter' => ['=ID' => 'ru', '=ACTIVE' => 'Y']
  111. ]);
  112. $row = $iterator->fetch();
  113. if (empty($row))
  114. {
  115. $isRussian = false;
  116. }
  117. else
  118. {
  119. $iterator = LanguageTable::getList([
  120. 'select' => ['ID'],
  121. 'filter' => ['@ID' => ['ua', 'by', 'kz'], '=ACTIVE' => 'Y'],
  122. 'limit' => 1
  123. ]);
  124. $row = $iterator->fetch();
  125. $isRussian = empty($row);
  126. }
  127. }
  128. return $isRussian;
  129. }
  130. public static function convertEntitiesToCodes($entityList = [])
  131. {
  132. $codeList = [];
  133. if (is_array($entityList))
  134. {
  135. foreach($entityList as $entity)
  136. {
  137. if ($entity['entityId'] === 'meta-user' && $entity['id'] === 'all-users')
  138. {
  139. $codeList[] = 'UA';
  140. }
  141. elseif ($entity['entityId'] === 'user')
  142. {
  143. $codeList[] = 'U'.$entity['id'];
  144. }
  145. elseif ($entity['entityId'] === 'project' || $entity['entityId'] === 'project-roles')
  146. {
  147. $codeList[] = 'SG'.$entity['id'];
  148. }
  149. elseif ($entity['entityId'] === 'department')
  150. {
  151. $codeList[] = 'DR'.$entity['id'];
  152. }
  153. }
  154. }
  155. return $codeList;
  156. }
  157. public static function convertCodesToEntities($codeList = [])
  158. {
  159. $entityList = [];
  160. if (is_array($codeList))
  161. {
  162. foreach($codeList as $code)
  163. {
  164. if ($code === 'UA')
  165. {
  166. $entityList[] = [
  167. 'entityId' => 'meta-user',
  168. 'id' => 'all-users'
  169. ];
  170. }
  171. elseif (mb_substr($code, 0, 1) == 'U')
  172. {
  173. $entityList[] = [
  174. 'entityId' => 'user',
  175. 'id' => intval(mb_substr($code, 1))
  176. ];
  177. }
  178. if (mb_substr($code, 0, 2) == 'DR')
  179. {
  180. $entityList[] = [
  181. 'entityId' => 'department',
  182. 'id' => intval(mb_substr($code, 2))
  183. ];
  184. }
  185. elseif (preg_match('/^SG([0-9]+)_?([AEKMO])?$/', $code, $match) && isset($match[2]))
  186. {
  187. // todo May need to be removed/rewrite after creating new roles in projects.
  188. $entityList[] = [
  189. 'entityId' => 'project-roles',
  190. 'id' => mb_substr($code, 2)
  191. ];
  192. }
  193. elseif (mb_substr($code, 0, 2) == 'SG')
  194. {
  195. $entityList[] = [
  196. 'entityId' => 'project',
  197. 'id' => intval(mb_substr($code, 2))
  198. ];
  199. }
  200. }
  201. }
  202. return $entityList;
  203. }
  204. public static function getUsersByEntityList($entityList, $fetchUsers = false)
  205. {
  206. if (!Main\Loader::includeModule('socialnetwork'))
  207. {
  208. return [];
  209. }
  210. $users = \CSocNetLogDestination::getDestinationUsers(self::convertEntitiesToCodes($entityList), $fetchUsers);
  211. if ($fetchUsers)
  212. {
  213. for ($i = 0, $l = count($users); $i < $l; $i++)
  214. {
  215. $users[$i]['FORMATTED_NAME'] = \CCalendar::getUserName($users[$i]);
  216. }
  217. }
  218. return $users;
  219. }
  220. public static function getDefaultEntityList($userId, $type, $ownerId)
  221. {
  222. $entityList = [['entityId' => 'user', 'id' => $userId]];
  223. if ($type === 'user' && $ownerId !== $userId)
  224. {
  225. $entityList[] = ['entityId' => 'user', 'id' => $ownerId];
  226. }
  227. else if($type === 'group')
  228. {
  229. $entityList[] = ['entityId' => 'project', 'id' => $ownerId];
  230. }
  231. return $entityList;
  232. }
  233. /**
  234. * @param array|null $codeAttendees
  235. * @param string $stringWrapper
  236. * @return array
  237. * @throws Main\ArgumentException
  238. * @throws Main\ObjectPropertyException
  239. * @throws Main\SystemException
  240. */
  241. public static function getAttendees(array $codeAttendees = null, string $stringWrapper = ''): array
  242. {
  243. if (empty($codeAttendees))
  244. {
  245. return [];
  246. }
  247. $userIdList = [];
  248. $userList = [];
  249. foreach ($codeAttendees as $codeAttend)
  250. {
  251. if (mb_strpos($codeAttend, 'U') === 0)
  252. {
  253. $userId = (int)(mb_substr($codeAttend, 1));
  254. $userIdList[] = $userId;
  255. }
  256. }
  257. if (!empty($userIdList))
  258. {
  259. $res = \Bitrix\Main\UserTable::getList(array(
  260. 'filter' => [
  261. '=ID' => $userIdList,
  262. ],
  263. 'select' => ['NAME', 'LAST_NAME'],
  264. ));
  265. while ($user = $res->fetch())
  266. {
  267. $userList[] = addcslashes($stringWrapper . $user['NAME'].' '.$user['LAST_NAME'] . $stringWrapper, "()");
  268. }
  269. }
  270. return $userList;
  271. }
  272. /**
  273. * @return bool
  274. */
  275. public static function isShowDailyBanner(): bool
  276. {
  277. $isInstallMobileApp = (bool)\CUserOptions::GetOption('mobile', 'iOsLastActivityDate', false)
  278. || (bool)\CUserOptions::GetOption('mobile', 'AndroidLastActivityDate', false)
  279. ;
  280. $isSyncCalendar = (bool)\CUserOptions::GetOption('calendar', 'last_sync_iphone', false)
  281. || (bool)\CUserOptions::GetOption('calendar', 'last_sync_android', false)
  282. ;
  283. if ($isInstallMobileApp && $isSyncCalendar)
  284. {
  285. return false;
  286. }
  287. $dailySyncBanner = \CUserOptions::GetOption('calendar', 'daily_sync_banner', []);
  288. if (!isset($dailySyncBanner['last_sync_day']) && !isset($dailySyncBanner['count']))
  289. {
  290. $dailySyncBanner['last_sync_day'] = '';
  291. $dailySyncBanner['count'] = 0;
  292. }
  293. $today = (new Main\Type\Date())->format('Y-m-d');
  294. $isShowToday = ($today === $dailySyncBanner['last_sync_day']);
  295. $isLimitExceeded = ($dailySyncBanner['count'] >= self::LIMIT_NUMBER_BANNER_IMPRESSIONS);
  296. if ($isLimitExceeded || $isShowToday)
  297. {
  298. return false;
  299. }
  300. else
  301. {
  302. ++$dailySyncBanner['count'];
  303. $dailySyncBanner['last_sync_day'] = (new Main\Type\Date())->format('Y-m-d');
  304. \CUserOptions::SetOption('calendar', 'daily_sync_banner', $dailySyncBanner);
  305. return true;
  306. }
  307. }
  308. /**
  309. * @param int $userId
  310. * @return bool
  311. * @throws Main\ArgumentException
  312. * @throws Main\LoaderException
  313. * @throws Main\ObjectPropertyException
  314. * @throws Main\SystemException
  315. */
  316. public static function isExtranetUser(int $userId): bool
  317. {
  318. if (Loader::includeModule('intranet'))
  319. {
  320. $userDb = \Bitrix\Intranet\UserTable::getList([
  321. 'filter' => [
  322. 'ID' => $userId,
  323. ],
  324. 'select' => [
  325. 'USER_TYPE',
  326. ]
  327. ]);
  328. $user = $userDb->fetch();
  329. return $user['USER_TYPE'] === 'extranet';
  330. }
  331. return false;
  332. }
  333. /**
  334. * @param int $eventId
  335. * @return array|null
  336. * @throws Main\ArgumentException
  337. * @throws Main\ObjectPropertyException
  338. * @throws Main\SystemException
  339. */
  340. public static function getEventById(int $eventId): ?array
  341. {
  342. $eventDb = Internals\EventTable::getList([
  343. 'filter' => [
  344. '=ID' => $eventId,
  345. ],
  346. ]);
  347. if ($event = $eventDb->fetch())
  348. {
  349. return $event;
  350. }
  351. return null;
  352. }
  353. /**
  354. * @param string $command
  355. * @param int $userId
  356. * @param array $params
  357. * @return bool
  358. */
  359. public static function addPullEvent(string $command, int $userId, array $params = []): bool
  360. {
  361. if (!Loader::includeModule("pull"))
  362. {
  363. return false;
  364. }
  365. if (
  366. in_array($command, [
  367. 'edit_event',
  368. 'delete_event',
  369. 'set_meeting_status',
  370. ])
  371. )
  372. {
  373. \CPullWatch::AddToStack(
  374. 'calendar-planner-'.$userId,
  375. [
  376. 'module_id' => 'calendar',
  377. 'command' => $command,
  378. 'params' => $params
  379. ]
  380. );
  381. }
  382. if (
  383. in_array($command, [
  384. 'edit_event',
  385. 'delete_event',
  386. 'set_meeting_status',
  387. ])
  388. && isset($params['fields'])
  389. && isset($params['fields']['SECTION_OWNER_ID'])
  390. && (int)$params['fields']['SECTION_OWNER_ID'] !== $userId
  391. )
  392. {
  393. \Bitrix\Pull\Event::add(
  394. (int)$params['fields']['SECTION_OWNER_ID'],
  395. [
  396. 'module_id' => 'calendar',
  397. 'command' => $command,
  398. 'params' => $params
  399. ]
  400. );
  401. }
  402. return \Bitrix\Pull\Event::add(
  403. $userId,
  404. [
  405. 'module_id' => 'calendar',
  406. 'command' => $command,
  407. 'params' => $params
  408. ]
  409. );
  410. }
  411. /**
  412. * @param int $currentUserId
  413. * @param array $userIdList
  414. *
  415. * @return void
  416. */
  417. public static function initPlannerPullWatches(int $currentUserId, array $userIdList = []): void
  418. {
  419. if (Loader::includeModule("pull"))
  420. {
  421. foreach($userIdList as $userId)
  422. {
  423. if ((int)$userId !== $currentUserId)
  424. {
  425. \CPullWatch::Add($currentUserId, 'calendar-planner-'.$userId);
  426. }
  427. }
  428. }
  429. }
  430. public static function getUserFieldsByEventId(int $eventId): array
  431. {
  432. global $DB;
  433. $result = [];
  434. $strSql = "SELECT * from b_uts_calendar_event WHERE VALUE_ID=" . $eventId;
  435. $ufDb = $DB->query($strSql);
  436. while ($uf = $ufDb->fetch())
  437. {
  438. $result[] = [
  439. 'crm' => unserialize($uf['UF_CRM_CAL_EVENT'], ['allowed_classes' => false]),
  440. 'webdav' => unserialize($uf['UF_WEBDAV_CAL_EVENT'], ['allowed_classes' => false]),
  441. ];
  442. }
  443. return $result;
  444. }
  445. /**
  446. * @return string
  447. */
  448. public static function getServerTimezoneName(): string
  449. {
  450. return (new \DateTime())->getTimezone()->getName();
  451. }
  452. /**
  453. * @return int
  454. */
  455. public static function getServerOffsetUTC(): int
  456. {
  457. return (new \DateTime())->getOffset();
  458. }
  459. /**
  460. * @param string|null $tz
  461. * @param null $date
  462. * @return int
  463. * @throws \Exception
  464. */
  465. public static function getTimezoneOffsetFromServer(?string $tz = 'UTC', $date = null): int
  466. {
  467. if ($date instanceof Date)
  468. {
  469. $timestamp = $date->format(self::DATETIME_PHP_FORMAT);
  470. }
  471. elseif ($date === null)
  472. {
  473. $timestamp = 'now';
  474. }
  475. else
  476. {
  477. $timestamp = "@".(int)$date;
  478. }
  479. $date = new \DateTime($timestamp, self::prepareTimezone($tz));
  480. return $date->getOffset() - self::getServerOffsetUTC();
  481. }
  482. /**
  483. * @param string $requestUid
  484. */
  485. public static function setRequestUid(string $requestUid = ''): void
  486. {
  487. self::$requestUid = $requestUid;
  488. }
  489. /**
  490. * @return string
  491. */
  492. public static function getRequestUid(): string
  493. {
  494. return self::$requestUid;
  495. }
  496. /**
  497. * @param int $userId
  498. * @return array
  499. */
  500. public static function getUserAccessCodes(int $userId): array
  501. {
  502. global $USER;
  503. $userId = (int)$userId;
  504. if (!$userId)
  505. {
  506. $userId = \CCalendar::GetCurUserId();
  507. }
  508. if (!isset(self::$userAccessCodes[$userId]))
  509. {
  510. $codes = [];
  511. $r = \CAccess::GetUserCodes($userId);
  512. while($code = $r->Fetch())
  513. {
  514. $codes[] = $code['ACCESS_CODE'];
  515. }
  516. if (!in_array('G2', $codes))
  517. {
  518. $codes[] = 'G2';
  519. }
  520. if (!in_array('AU', $codes) && $USER && (int)$USER->GetId() === $userId)
  521. {
  522. $codes[] = 'AU';
  523. }
  524. if(!in_array('UA', $codes) && $USER && (int)$USER->GetId() == $userId)
  525. {
  526. $codes[] = 'UA';
  527. }
  528. self::$userAccessCodes[$userId] = $codes;
  529. }
  530. return self::$userAccessCodes[$userId];
  531. }
  532. /**
  533. * @param int $ownerId
  534. * @param string $type
  535. * @return string
  536. */
  537. public static function getPathToCalendar(?int $ownerId, ?string $type): string
  538. {
  539. $key = $type . $ownerId;
  540. if (!isset(self::$pathCache[$key]) || !is_string(self::$pathCache[$key]))
  541. {
  542. if ($type === 'user')
  543. {
  544. $path = \COption::GetOptionString(
  545. 'calendar',
  546. 'path_to_user_calendar',
  547. \COption::getOptionString('socialnetwork', 'user_page', "/company/personal/")
  548. . "user/#user_id#/calendar/"
  549. );
  550. }
  551. elseif ($type === 'group')
  552. {
  553. $path = \COption::GetOptionString(
  554. 'calendar',
  555. 'path_to_group_calendar',
  556. \COption::getOptionString('socialnetwork', 'workgroups_page', "/workgroups/")
  557. . "group/#group_id#/calendar/"
  558. );
  559. }
  560. else
  561. {
  562. $settings = \CCalendar::GetSettings();
  563. $path = $settings['path_to_type_' . $type];
  564. }
  565. if (!\COption::GetOptionString('calendar', 'pathes_for_sites', true))
  566. {
  567. $siteId = \CCalendar::GetSiteId();
  568. $pathList = \CCalendar::GetPathes();
  569. if (isset($pathList[$siteId]))
  570. {
  571. if ($type === 'user' && isset($pathList[$siteId]['path_to_user_calendar']))
  572. {
  573. $path = $pathList[$siteId]['path_to_user_calendar'];
  574. }
  575. elseif ($type === 'group' && isset($pathList[$siteId]['path_to_group_calendar']))
  576. {
  577. $path = $pathList[$siteId]['path_to_group_calendar'];
  578. }
  579. else
  580. {
  581. $path = $pathList[$siteId]['path_to_type_' . $type];
  582. }
  583. }
  584. }
  585. if (!is_string($path))
  586. {
  587. $path = '';
  588. }
  589. if (!empty($path) && $ownerId > 0)
  590. {
  591. if ($type === 'user')
  592. {
  593. $path = str_replace(['#user_id#', '#USER_ID#'], $ownerId, $path);
  594. }
  595. elseif ($type === 'group')
  596. {
  597. $path = str_replace(['#group_id#', '#GROUP_ID#'], $ownerId, $path);
  598. }
  599. }
  600. self::$pathCache[$key] = $path;
  601. }
  602. return self::$pathCache[$key];
  603. }
  604. }