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