Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
resourcebooking.php
1<?php
3
4use Bitrix\Bitrix24;
13
14Loc::loadMessages(__FILE__);
15
16
18{
19 const USER_TYPE_ID = 'resourcebooking';
20 public const EVENT_LABEL = '#resourcebooking#';
21 const RESOURCE_CALENDAR_TYPE = 'resource';
23 const BITRIX24_RESTRICTION_CODE = 'uf_resourcebooking';
24
25 const CRM_LEAD_ENTITY_ID = 'CRM_LEAD';
26 const CRM_SUSPENDED_LEAD_ENTITY_ID = 'CRM_LEAD_SPD';
27 const CRM_DEAL_ENTITY_ID = 'CRM_DEAL';
28 const CRM_SUSPENDED_DEAL_ENTITY_ID = 'CRM_DEAL_SPD';
29
30 protected static $restrictionCount = null;
31
32 public static function getUserTypeDescription()
33 {
34 return array(
35 "USER_TYPE_ID" => static::USER_TYPE_ID,
36 "CLASS_NAME" => __CLASS__,
37 "DESCRIPTION" => Loc::getMessage("USER_TYPE_RESOURCEBOOKING_DESCRIPTION"),
38 "BASE_TYPE" => \CUserTypeManager::BASE_TYPE_STRING,
39 "EDIT_CALLBACK" => array(__CLASS__, 'getPublicEdit'),
40 "VIEW_CALLBACK" => array(__CLASS__, 'getPublicView')
41 );
42 }
43
44 public static function prepareSettings($userField = [])
45 {
46 $userField = [
47 'SETTINGS' => [
48 'SELECTED_RESOURCES' => $userField['SETTINGS']['SELECTED_RESOURCES'] ?? null,
49 'SELECTED_USERS' => $userField['SETTINGS']['SELECTED_USERS'] ?? null,
50 'USE_USERS' => $userField['SETTINGS']['USE_USERS'] ?? null,
51 'USE_RESOURCES' => $userField['SETTINGS']['USE_RESOURCES'] ?? null,
52 'FULL_DAY' => $userField['SETTINGS']['FULL_DAY'] ?? null,
53 'ALLOW_OVERBOOKING' => $userField['SETTINGS']['ALLOW_OVERBOOKING'] ?? null,
54 'USE_SERVICES' => $userField['SETTINGS']['USE_SERVICES'] ?? null,
55 'SERVICE_LIST' => $userField['SETTINGS']['SERVICE_LIST'] ?? null,
56 'TIMEZONE' => $userField['SETTINGS']['TIMEZONE'] ?? null,
57 'USE_USER_TIMEZONE' => $userField['SETTINGS']['USE_USER_TIMEZONE'] ?? null,
58 ],
59 ];
60
61 $selectedResources = [];
62
63 if (is_array($userField["SETTINGS"]["SELECTED_RESOURCES"]))
64 {
65 $selectedResources = self::handleResourceList($userField["SETTINGS"]["SELECTED_RESOURCES"]);
66 }
67 $selectedUsers = [];
68 if (is_array($userField["SETTINGS"]["SELECTED_USERS"]))
69 {
70 foreach($userField["SETTINGS"]["SELECTED_USERS"] as $user)
71 {
72 if (intval($user) > 0)
73 {
74 $selectedUsers[] = intval($user);
75 }
76 }
77 }
78
79 return array(
80 "USE_USERS" => $userField["SETTINGS"]["USE_USERS"] === 'N' ? 'N' : 'Y',
81 "USE_RESOURCES" => $userField["SETTINGS"]["USE_RESOURCES"] === 'N' ? 'N' : 'Y',
82 "RESOURCES" => self::getDefaultResourcesList(),
83 "SELECTED_RESOURCES" => $selectedResources,
84 "SELECTED_USERS" => $selectedUsers,
85 "FULL_DAY" => $userField["SETTINGS"]["FULL_DAY"] === 'Y' ? 'Y' : 'N',
86 "ALLOW_OVERBOOKING" => $userField["SETTINGS"]["ALLOW_OVERBOOKING"] === 'N' ? 'N' : 'Y',
87 "USE_SERVICES" => $userField["SETTINGS"]["USE_SERVICES"] === 'N' ? 'N' : 'Y',
88 "SERVICE_LIST" => is_array($userField["SETTINGS"]["SERVICE_LIST"]) ? $userField["SETTINGS"]["SERVICE_LIST"] : self::getDefaultServiceList(),
89 "RESOURCE_LIMIT" => self::getBitrx24Limitation(),
90 "TIMEZONE" => $userField["SETTINGS"]["TIMEZONE"],
91 "USE_USER_TIMEZONE" => $userField["SETTINGS"]["USE_USER_TIMEZONE"] === 'Y' ? 'Y' : 'N'
92 );
93 }
94
95 public static function getDBColumnType()
96 {
97 $connection = \Bitrix\Main\Application::getConnection();
98 $helper = $connection->getSqlHelper();
99 return $helper->getColumnTypeByField(new \Bitrix\Main\ORM\Fields\TextField('x'));
100 }
101
102 public static function checkFields($userField, $value)
103 {
104 if($userField["MANDATORY"] === "Y" && ($value === 'empty' || !$value))
105 {
106 return array(array(
107 "id" => $userField['FIELD_NAME'],
108 "text"=>str_replace("#FIELD_NAME#", $userField['EDIT_FORM_LABEL'],
109 Loc::getMessage("USER_TYPE_FIELD_VALUE_IS_MISSING"))
110 ));
111 }
112
113 return [];
114 }
115
116 public static function onBeforeSaveAll($userField, $values, $userId = false)
117 {
118 $valuesToSave = [];
119 $currentUserId = \CCalendar::getCurUserId();
120 $dateFrom = false;
121 $dateTo = false;
122 $serviceName = '';
123 $entityTitle = '';
124 $fields = [];
125 $entity = null;
126
127 $resourseList = Internals\ResourceTable::query()
128 ->setSelect(['*'])
129 ->where('PARENT_TYPE', $userField['ENTITY_ID'])
130 ->where('PARENT_ID', $userField['VALUE_ID'])
131 ->where('UF_ID', $userField['ID'])
132 ->exec()
133 ;
134
135 $currentEntriesIndex = [];
136 while ($resourse = $resourseList->fetch())
137 {
138 $currentEntriesIndex[$resourse['CAL_TYPE'].$resourse['RESOURCE_ID']] = $resourse;
139 }
140
141 if (self::isCrmEntity($userField['ENTITY_ID']) && Loader::includeModule('crm'))
142 {
143 if ($userField['ENTITY_ID'] === self::CRM_DEAL_ENTITY_ID)
144 {
145 $dealResult = \CCrmDeal::GetListEx(
146 [],
147 ['=ID' => $userField['VALUE_ID'], 'CHECK_PERMISSIONS' => 'N'],
148 false,
149 false,
150 ['TITLE'],
151 );
152
153 if (!empty($dealResult))
154 {
155 $entity = $dealResult->Fetch();
156 }
157
158 if (!empty($entity) && $entity['TITLE'])
159 {
160 $entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE").': '.$entity['TITLE'];
161 }
162 }
163 elseif ($userField['ENTITY_ID'] === self::CRM_LEAD_ENTITY_ID)
164 {
165 $leadResult = \CCrmLead::GetListEx(
166 [],
167 ['=ID' => $userField['VALUE_ID'], 'CHECK_PERMISSIONS' => 'N'],
168 false,
169 false,
170 ['TITLE'],
171 );
172
173 if (!empty($leadResult))
174 {
175 $entity = $leadResult->Fetch();
176 }
177
178 if (!empty($entity) && $entity['TITLE'])
179 {
180 $entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE").': '.$entity['TITLE'];
181 }
182 }
183
184 if (
185 $userField['ENTITY_ID'] === self::CRM_SUSPENDED_DEAL_ENTITY_ID
186 || $userField['ENTITY_ID'] === self::CRM_SUSPENDED_LEAD_ENTITY_ID
187 )
188 {
189 $entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE")." (Deleted)";
190 }
191 }
192
193 if (!$entityTitle)
194 {
195 $entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE");
196 }
197
198 $valuesToMerge = [];
199 foreach ($values as $value)
200 {
201 if ((string)$value === (string)((int)$value))
202 {
203 $currentValue = static::fetchFieldValue($value);
204 $skipTime = is_array($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
205 $fromTs = \CCalendar::timestamp($currentValue['DATE_FROM'], true, !$skipTime);
206 $toTs = \CCalendar::timestamp($currentValue['DATE_TO'], true, !$skipTime);
207
208 foreach ($currentValue['ENTRIES'] as $entry)
209 {
210 $entryExist = false;
211
212 foreach ($values as $iValue)
213 {
214 $str = $entry['TYPE'].'|'.$entry['RESOURCE_ID'];
215 if (str_starts_with($iValue, $str))
216 {
217 $entryExist = true;
218 break;
219 }
220 }
221
222 if (!$entryExist)
223 {
224 $valuesToMerge[] = self::prepareValue(
225 $entry['TYPE'],
226 $entry['RESOURCE_ID'],
227 $currentValue['DATE_FROM'],
228 $toTs - $fromTs,
229 $currentValue['SERVICE_NAME']
230 );
231 }
232 }
233 }
234 }
235
236 $values = array_merge($values, $valuesToMerge);
237
238 foreach ($values as $value)
239 {
240 $value = self::parseValue($value);
241
242 if (!is_array($value))
243 {
244 continue;
245 }
246
247 if (!$dateFrom || !$dateTo)
248 {
249 $dateFromTimestamp = \CCalendar::timestamp($value['from']);
250 $skipTime = isset($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
251 $dateFrom = \CCalendar::date($dateFromTimestamp, !$skipTime);
252 $duration = (int)$value['duration'];
253 $dateTo = \CCalendar::date($dateFromTimestamp + ($skipTime ? $duration - \CCalendar::DAY_LENGTH : $duration), !$skipTime);
254 $serviceName = trim($value['serviceName']);
255
256 $fields = [
257 "DATE_FROM" => $dateFrom,
258 "DATE_TO" => $dateTo,
259 "SKIP_TIME" => $skipTime,
260 "NAME" => $entityTitle
261 ];
262
263 if (
264 $userField['ENTITY_ID'] === self::CRM_SUSPENDED_DEAL_ENTITY_ID
265 || $userField['ENTITY_ID'] === self::CRM_SUSPENDED_LEAD_ENTITY_ID
266 )
267 {
268 $fields["DELETED"] = 'Y';
269 }
270
271 if ($serviceName !== '')
272 {
273 $fields["DESCRIPTION"] = Loc::getMessage("USER_TYPE_RESOURCE_SERVICE_LABEL").': '.$serviceName;
274 }
275
276 if (!$skipTime)
277 {
278 if ($userField['SETTINGS']['USE_USER_TIMEZONE'] === 'Y')
279 {
280 $timezoneName = \CCalendar::getUserTimezoneName($currentUserId, true);
281 }
282 else if($userField['SETTINGS']['TIMEZONE'])
283 {
284 $timezoneName = $userField['SETTINGS']['TIMEZONE'];
285 }
286 else
287 {
288 $timezoneName = \CCalendar::GetGoodTimezoneForOffset((int)date("Z"));
289 }
290
291 if($timezoneName)
292 {
293 $fields['TZ_FROM'] = $timezoneName;
294 $fields['TZ_TO'] = $timezoneName;
295 }
296 }
297 }
298
299 $entryId = false;
300 if (isset($currentEntriesIndex[$value['type'] . $value['id']]))
301 {
302 $fields['ID'] = $currentEntriesIndex[$value['type'].$value['id']]['EVENT_ID'];
303 $entryId = $currentEntriesIndex[$value['type'].$value['id']]['ID'];
304 }
305 else
306 {
307 unset($fields['ID']);
308 }
309
310 $resourceBookingId = self::saveResource(
311 $entryId,
312 $value['type'],
313 $value['id'],
314 $fields,
315 [
316 'userField' => $userField,
317 'serviceName' => $serviceName
318 ]
319 );
320
321 if ($resourceBookingId)
322 {
323 $valuesToSave[] = $resourceBookingId;
324 }
325 }
326
327 foreach ($currentEntriesIndex as $resourceEntry)
328 {
329 if (!in_array($resourceEntry['ID'], $valuesToSave))
330 {
331 self::releaseResource($resourceEntry);
332 }
333 }
334
335 return $valuesToSave;
336 }
337
338 public static function onDelete($userField, $values, $userId = false)
339 {
340 $resourseList = Internals\ResourceTable::getList(
341 array(
342 "filter" => array(
343 "PARENT_TYPE" => $userField['ENTITY_ID'],
344 "PARENT_ID" => $userField['ENTITY_VALUE_ID'],
345 "UF_ID" => $userField['ID']
346 )
347 )
348 );
349
350 while ($resourse = $resourseList->fetch())
351 {
352 self::releaseResource($resourse);
353 }
354 }
355
368 public static function saveResource($id, $resourceType, $resourceId, $eventFields = [], $params = [])
369 {
370 $valueToSave = null;
371 $currentUserId = \CCalendar::getCurUserId();
372
373 $eventFields["EVENT_TYPE"] = '#resourcebooking#';
374 if ($resourceType === 'user')
375 {
376 $eventFields["CAL_TYPE"] = $resourceType;
377 $eventFields["OWNER_ID"] = $resourceId;
378
379 if ($params['userField']['ENTITY_ID'] === 'CRM_DEAL' || $params['userField']['ENTITY_ID'] === 'CRM_LEAD')
380 {
381 $sectionId = \CCalendar::getCrmSection($resourceId, true);
382 }
383 else
384 {
385 $sectionId = \CCalendar::getMeetingSection($resourceId, true);
386 }
387 if ($sectionId)
388 {
389 $eventFields['SECTIONS'] = [$sectionId];
390 }
391
392 // Userfields for event
393 $userFields = [];
394 if ($params['userField']['ENTITY_ID'] === 'CRM_DEAL')
395 {
396 $userFields['UF_CRM_CAL_EVENT'] = ["D_".$params['userField']['VALUE_ID']];
397 }
398 elseif ($params['userField']['ENTITY_ID'] === 'CRM_LEAD')
399 {
400 $userFields['UF_CRM_CAL_EVENT'] = ["L_".$params['userField']['VALUE_ID']];
401 }
402
403 $entryId = \CCalendar::saveEvent([
404 'arFields' => $eventFields,
405 'UF' => $userFields,
406 'silentErrorMode' => false,
407 'autoDetectSection' => true,
408 'autoCreateSection' => true,
409 'checkPermission' => false
410 ]);
411 }
412 else
413 {
414 $eventFields["CAL_TYPE"] = $resourceType;
415 $eventFields["SECTIONS"] = $resourceId;
416 $entryId = \CCalendarEvent::edit([
417 'arFields' => $eventFields
418 ]);
419 }
420
421 if ($entryId)
422 {
423 if ($eventFields['TZ_FROM'] ?? null)
424 {
425 $from = new Type\DateTime($eventFields["DATE_FROM"]);
426 $to = new Type\DateTime($eventFields["DATE_TO"]);
427 $fromUtc = new Type\DateTime($eventFields["DATE_FROM"], null, new \DateTimeZone('UTC'));
428 $toUtc = new Type\DateTime($eventFields["DATE_TO"], null, new \DateTimeZone('UTC'));
429 }
430 else
431 {
432 $from = new Type\DateTime($eventFields["DATE_FROM"]);
433 $to = new Type\DateTime($eventFields["DATE_TO"]);
434 $fromUtc = $from;
435 $toUtc = $to;
436 }
437
438 \CTimeZone::Disable();
439
440 $resourceTableFields = [
441 'EVENT_ID' => $entryId,
442 'CAL_TYPE' => $eventFields["CAL_TYPE"],
443 'RESOURCE_ID' => $resourceId,
444 'PARENT_TYPE' => $params['bindingEntityType'] ?? $params['userField']['ENTITY_ID'],
445 'PARENT_ID' => $params['bindingEntityId'] ?? $params['userField']['VALUE_ID'],
446 'UF_ID' => $params['bindingUserfieldId'] ?? $params['userField']['ID'],
447 'DATE_FROM' => $from,
448 'DATE_TO' => $to,
449 'SKIP_TIME' => $eventFields['SKIP_TIME'] ?? null,
450 'DATE_FROM_UTC' => $fromUtc,
451 'DATE_TO_UTC' => $toUtc,
452 'TZ_FROM' => $eventFields['TZ_FROM'] ?? null,
453 'TZ_TO' => $eventFields['TZ_TO'] ?? null,
454 'TZ_OFFSET_FROM' => \CCalendar::getTimezoneOffset(
455 $eventFields['TZ_FROM'] ?? null,
456 \CCalendar::timestamp($eventFields["DATE_FROM"])
457 ),
458 'TZ_OFFSET_TO' => \CCalendar::getTimezoneOffset(
459 $eventFields['TZ_TO'] ?? null,
460 \CCalendar::timestamp($eventFields["DATE_TO"])
461 ),
462 'CREATED_BY' => $currentUserId,
463 'SERVICE_NAME' => $params['serviceName'] ?? null
464 ];
465
466 if ($id)
467 {
468 $result = Internals\ResourceTable::update($id, $resourceTableFields);
469 }
470 else
471 {
472 $result = Internals\ResourceTable::add($resourceTableFields);
473 }
474
475 if ($result->isSuccess())
476 {
477 $valueToSave = $result->getId();
478 }
479 else
480 {
481 \CCalendar::deleteEvent((int)$entryId, false);
482 }
483
484 foreach(\Bitrix\Main\EventManager::getInstance()->findEventHandlers("calendar", "onAfterResourceBookingAdd") as $event)
485 {
486 ExecuteModuleEventEx($event, [[
487 'userFieldValueId' => $valueToSave,
488 'bookingEventId' => $entryId,
489 'resourceType' => $resourceType,
490 'resourceId' => $resourceId,
491 'serviceName' => $params['serviceName'] ?? null,
492 'dateFrom' => $from,
493 'dateTo' => $to,
494 'skipTime' => $eventFields['SKIP_TIME'],
495 'timezoneFrom' => $eventFields['TZ_FROM'] ?? null,
496 'timezoneTo' => $eventFields['TZ_TO'] ?? null,
497 ]]);
498 }
499
500 \CTimeZone::Enable();
501 }
502
503 return $valueToSave;
504 }
505
506 private static function isCrmEntity($entityId)
507 {
508 return in_array($entityId, [self::CRM_LEAD_ENTITY_ID, self::CRM_SUSPENDED_LEAD_ENTITY_ID,self::CRM_DEAL_ENTITY_ID, self::CRM_SUSPENDED_DEAL_ENTITY_ID]);
509 }
510
511 private static function isResourceAvailable()
512 {
513
514 }
515
516 public static function releaseResource($entry)
517 {
518 \CCalendar::deleteEvent((int)$entry['EVENT_ID'], true, array('checkPermissions' => false));
519 Internals\ResourceTable::delete($entry['ID']);
520 }
521
522 private static function handleResourceList($resources)
523 {
524 $result = [];
525 foreach($resources as $resource)
526 {
527 if (is_array($resource))
528 {
529 if (($resource['id'] ?? null) && (($resource['deleted'] ?? null) || ($resource['title'] ?? null) == ''))
530 {
531 $sectionList = Internals\SectionTable::getList(
532 array(
533 "filter" => array("ID" => $resource['id'], "CAL_TYPE" => $resource['type'] ?? null),
534 "select" => array("ID", "CAL_TYPE", "NAME")
535 )
536 );
537 if ($sectionList->fetch())
538 {
539 Internals\SectionTable::delete(array('ID' => $resource['id']));
540 }
541 }
542 else if ($resource['id'] ?? null)
543 {
544 $sectionList = Internals\SectionTable::getList(
545 array(
546 "filter" => array("ID" => $resource['id'], "CAL_TYPE" => $resource['type'] ?? null),
547 "select" => array("ID", "CAL_TYPE", "NAME")
548 )
549 );
550 if ($section = $sectionList->fetch())
551 {
552 if ($section['NAME'] != ($resource['title'] ?? null))
553 {
554 \CCalendarSect::edit(array(
555 'arFields' => array(
556 'ID' => $resource['id'],
557 'CAL_TYPE' => $resource['type'] ?? null,
558 'NAME' => $resource['title'] ?? null,
559 'ACCESS' => []
560 )
561 ));
562 }
563 }
564 $result[] = $resource;
565 }
566 elseif (!($resource['id'] ?? null) && ($resource['title'] ?? null) !== '')
567 {
568 $resource['id'] = \CCalendarSect::edit(array(
569 'arFields' => array(
570 'CAL_TYPE' => $resource['type'] ?? null,
571 'NAME' => $resource['title'] ?? null,
572 'ACCESS' => []
573 )
574 ));
575 $result[] = $resource;
576 }
577 }
578 }
579 return $result;
580 }
581
582 public static function prepareValue($type, $id, $from, $duration, $serviceName = '')
583 {
584 $type = $type === 'user' || $type === 'resource' ? $type : 'user';
585 $id = (int)$id > 0 ? $id : 1;
586 $duration = (int)$duration > 0 ? $duration : 60;
587 return $type.'|'.$id.'|'.$from.'|'.$duration.'|'.$serviceName;
588 }
589
590 public static function parseValue($value)
591 {
592 $res = false;
593 if(mb_strpos($value, '|') >= 0)
594 {
595 $list = explode('|', $value);
596 $type = $list[0] ?? null;
597 $id = $list[1] ?? null;
598 $from = $list[2] ?? null;
599 $duration = $list[3] ?? null;
600 $serviceName = $list[4] ?? null;
601 if ($type === 'user' || ($type === 'resource' && (int)$id > 0))
602 {
603 $res = array(
604 'type' => $type,
605 'id' => $id,
606 'from' => $from,
607 'duration' => $duration,
608 'serviceName' => $serviceName ? $serviceName : ''
609 );
610 }
611 }
612 return $res;
613 }
614
615 function getSettingsHTML($userField = false, $htmlControl = [], $varsFromForm = false)
616 {
617 \Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield', 'calendar_planner', 'socnetlogdest', 'helper', 'main', 'ui', 'ui.selector']);
618
619 if($varsFromForm)
620 {
621 $settingsValue = $GLOBALS[$htmlControl["NAME"]];
622 }
623 elseif(is_array($userField))
624 {
625 $settingsValue = $userField["SETTINGS"];
626 }
627 else
628 {
629 $settingsValue = [];
630 }
631
632 $controlId = $userField['FIELD_NAME'].'_'.rand();
633 $params = [
634 'controlId' => $controlId,
635 'settings' => $settingsValue,
636 'userField' => $userField,
637 'htmlControl' => $htmlControl,
638 'outerWrapId' => $controlId.'-settings-outer-wrap',
639 'formName' => 'post_form'
640 ];
641
642 if ($settingsValue['USE_USERS'] === 'Y')
643 {
644 $params['socnetDestination'] = \CCalendar::getSocNetDestination(false, [], $settingsValue['SELECTED_USERS']);
645 }
646
647 $result = '<tr>
648 <td></td>
649 <td>
650 <div id="'.HtmlFilter::encode($params['outerWrapId']).'"></div>
651 <script>(function(){new BX.Calendar.AdminSettingsViewer('.
652 \Bitrix\Main\Web\Json::encode($params).
653 ').showLayout();})();</script>
654 </td>
655 </tr>';
656
657 return $result;
658 }
659
660 function getEditFormHTML($userField, $htmlControl)
661 {
662 return static::getPublicEdit($userField, $htmlControl);
663 }
664
665 public static function getPublicEdit($userField, $additionalParams = [])
666 {
667 \Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield', 'calendar_planner', 'socnetlogdest']);
668
669 $fieldName = static::getFieldName($userField, $additionalParams);
670 $userField['VALUE'] = static::getFieldValue($userField, $additionalParams);
671
672 $value = static::fetchFieldValue($userField["VALUE"]);
673
674 if (
675 $userField['SETTINGS']['USE_RESOURCES'] === 'Y'
676 && (!is_array($userField['SETTINGS']['SELECTED_RESOURCES']) || empty($userField['SETTINGS']['SELECTED_RESOURCES']))
677 )
678 {
679 $userField['SETTINGS']['USE_RESOURCES'] = 'N';
680 }
681
682 $controlId = $userField['FIELD_NAME'].'_'.rand();
683 $params = [
684 'controlId' => $controlId,
685 'inputName' => $fieldName,
686 'value' => $value,
687 'plannerId' => $controlId.'_planner',
688 'userSelectorId' => 'resource_booking_user_selector',
689 'useUsers' => $userField['SETTINGS']['USE_USERS'] === 'Y',
690 'useResources' => $userField['SETTINGS']['USE_RESOURCES'] === 'Y',
691 'fullDay' => $userField['SETTINGS']['FULL_DAY'] === 'Y',
692 'allowOverbooking' => $userField['SETTINGS']['ALLOW_OVERBOOKING'] !== 'N',
693 'useServices' => $userField['SETTINGS']['USE_SERVICES'] === 'Y',
694 'serviceList' => $userField['SETTINGS']['SERVICE_LIST'],
695 'resourceList' => $userField['SETTINGS']['SELECTED_RESOURCES'],
696 'userList' => $userField['SETTINGS']['SELECTED_USERS'],
697 'userfieldId' => $userField['ID'],
698 'resourceLimit' => self::getBitrx24Limitation(),
699 'workTime' => [\COption::getOptionString('calendar', 'work_time_start', 9), \COption::getOptionString('calendar', 'work_time_end', 19)]
700 ];
701
702 if ($params['useUsers'])
703 {
704 $params['socnetDestination'] = \CCalendar::getSocNetDestination(false, [], $userField['SETTINGS']['SELECTED_USERS']);
705 }
706
707 ob_start();
708 ?>
709
710 <div id="<?= HtmlFilter::encode($params['controlId'])?>" class="crm-entity-widget-resourcebook-container"></div>
711 <script>
712 (function(){
713 'use strict';
714 BX.Runtime.loadExtension('calendar.resourcebookinguserfield').then(function(exports)
715 {
716 if (exports && BX.type.isFunction(exports.ResourcebookingUserfield))
717 {
718 exports.ResourcebookingUserfield.initEditFieldController(<?= \Bitrix\Main\Web\Json::encode($params)?>);
719 }
720 });
721 })();
722 </script>
723 <?
724
725 $html = ob_get_clean();
726
727 return static::getHelper()->wrapDisplayResult($html);
728 }
729
730 public static function getPublicView($userField, $additionalParams = [])
731 {
732 $context = $additionalParams['CONTEXT'] ?? '';
733 $value = static::fetchFieldValue($userField["VALUE"] ?? null);
734 $skipTime = is_array($userField['SETTINGS'] ?? null) && ($userField['SETTINGS']['FULL_DAY'] ?? null) == 'Y';
735 $fromTs = \CCalendar::timestamp($value['DATE_FROM'] ?? null, true, !$skipTime);
736 $toTs = \CCalendar::timestamp($value['DATE_TO'] ?? null, true, !$skipTime);
737
738 $users = [];
739 $resources = [];
740 $resourceNames = [];
741 $userIdList = [];
742 $resourceIdList = [];
743
744 foreach($value['ENTRIES'] as $entry)
745 {
746 if ($entry['TYPE'] === 'user')
747 {
748 $userIdList[] = (int) $entry['RESOURCE_ID'];
749 }
750 else
751 {
752 $resourceIdList[] = (int) $entry['RESOURCE_ID'];
753 }
754 }
755
756 $userIdList = array_unique($userIdList);
757 $resourceIdList = array_unique($resourceIdList);
758
759 if (!empty($userIdList))
760 {
761 $orm = UserTable::getList([
762 'filter' => [
763 '=ID' => $userIdList,
764 '=ACTIVE' => 'Y'
765 ],
766 'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'TITLE', 'PERSONAL_PHOTO']
767 ]);
768
769 while ($user = $orm->fetch())
770 {
771 $user['URL'] = \CCalendar::getUserUrl($user["ID"]);
772 $users[] = $user;
773 }
774 }
775
776 if (!empty($resourceIdList))
777 {
778 $sectionList = Internals\SectionTable::getList(
779 array(
780 "filter" => array(
781 "=ACTIVE" => 'Y',
782 "!=CAL_TYPE" => ['user', 'group', 'company_calendar', 'company', 'calendar_company'],
783 "ID" => $resourceIdList
784 ),
785 "select" => array("ID", "CAL_TYPE", "NAME")
786 )
787 );
788
789 while ($section = $sectionList->fetch())
790 {
791 $resources[$section['ID']] = $section;
792 $resourceNames[] = HtmlFilter::encode($section['NAME']);
793 }
794 }
795
796 if ($context === 'CRM_GRID')
797 {
798 \Bitrix\Main\UI\Extension::load([
799 'ui.design-tokens',
800 'ui.fonts.opensans',
801 ]);
802
803 \Bitrix\Main\Page\Asset::getInstance()->addCss('/bitrix/js/calendar/userfield/resourcebooking.css');
804 $resListItems = [];
805 if (!empty($users))
806 {
807 foreach($users as $user)
808 {
809 $resListItems[] = '<span>'.HtmlFilter::encode(\CCalendar::getUserName($user)).'</span>';
810 }
811 }
812 if (!empty($resourceNames))
813 {
814 foreach($resourceNames as $resourceName)
815 {
816 $resListItems[] = '<span>'.$resourceName.'</span>';
817 }
818 }
819
820 if (!empty($resListItems))
821 {
822 $html = '<span>'.\CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs).'</span>: ';
823 if(!empty($value['SERVICE_NAME']))
824 {
825 $html .= '<span>'.HtmlFilter::encode($value['SERVICE_NAME']).'</span>, ';
826 }
827 $html .= '<span>'.implode(', ', $resListItems).'</span>';
828 }
829 else
830 {
831 $html = '<span class="calendar-resbook-field-empty">'.Loc::getMessage("USER_TYPE_RESOURCE_EMPTY").'</span>';
832 }
833 }
834 else
835 {
836 \Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield']);
837 if (empty($users) && empty($resourceNames))
838 {
839 $html = Loc::getMessage("USER_TYPE_RESOURCE_EMPTY");
840 }
841 else
842 {
843 ob_start();
844 ?>
845 <div class="calendar-res-book-public-view-outer-wrap">
846 <div class="calendar-res-book-public-view-inner-wrap">
847 <div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
848 <div class="crm-entity-widget-content-block-title">
849 <span
850 class="crm-entity-widget-content-block-title-text"><?= ($skipTime ? Loc::getMessage("USER_TYPE_RESOURCE_DATE_BLOCK_TITLE") : Loc::getMessage("USER_TYPE_RESOURCE_DATETIME_BLOCK_TITLE")) ?></span>
851 </div>
852 <div
853 class="crm-entity-widget-content-block-inner"><?= \CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs) ?></div>
854 </div>
855
856 <? if(!empty($value['SERVICE_NAME'])): ?>
857 <div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
858 <div class="crm-entity-widget-content-block-title">
859 <span
860 class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_SERVICE_PLACEHOLDER") ?></span>
861 </div>
862 <div
863 class="crm-entity-widget-content-block-inner"><?= HtmlFilter::encode($value['SERVICE_NAME']) ?></div>
864 </div>
865 <?endif; ?>
866
867 <? if (!empty($users)): ?>
868 <div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
869 <div class="crm-entity-widget-content-block-title">
870 <span
871 class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_USERS_CONTROL_DEFAULT_NAME") ?></span>
872 </div>
873 <? foreach($users as $user): ?>
874 <div class="crm-widget-employee-container">
875 <a class="crm-widget-employee-avatar-container" href="<?= $user['URL'] ?>" target="_blank" style="background-image: url('<?= \CCalendar::getUserAvatarSrc($user) ?>'); background-size: 30px;"></a>
876 <span class="crm-widget-employee-info"><a class="crm-widget-employee-name" href="<?= $user['URL']?>" target="_blank"><?= HtmlFilter::encode(\CCalendar::getUserName($user))?></a><span class="crm-widget-employee-position"></span></span>
877 </div>
878 <? endforeach; ?>
879 </div>
880 <?endif; ?>
881
882 <? if (!empty($resourceNames)): ?>
883 <div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
884 <div class="crm-entity-widget-content-block-title">
885 <span
886 class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_RESOURCE_CONTROL_DEFAULT_NAME") ?></span>
887 </div>
888 <div class="crm-entity-widget-content-block-inner"><?= implode(', ', $resourceNames) ?></div>
889 </div>
890 <?endif; ?>
891 </div>
892 </div>
893 <?
894 $html = ob_get_clean();
895 }
896 }
897
898 if ($context === 'UI_EDITOR')
899 {
900 $html = '<span class="field-item">' . $html . '</span>';
901 }
902
903 return static::getHelper()->wrapDisplayResult($html);
904 }
905
906 public static function getPublicText($userField)
907 {
908 $resultText = '';
909 $value = static::fetchFieldValue($userField["VALUE"]);
910
911 $users = [];
912 $resources = [];
913 $resourceNames = [];
914 $userIdList = [];
915 $resourseIdList = [];
916
917 foreach($value['ENTRIES'] as $entry)
918 {
919 if ($entry['TYPE'] === 'user')
920 {
921 $userIdList[] = (int) $entry['RESOURCE_ID'];
922 }
923 else
924 {
925 $resourseIdList[] = (int) $entry['RESOURCE_ID'];
926 }
927 }
928
929 $userIdList = array_unique($userIdList);
930 $resourseIdList = array_unique($resourseIdList);
931
932 if (!empty($userIdList))
933 {
934 $orm = UserTable::getList([
935 'filter' => [
936 '=ID' => $userIdList,
937 '=ACTIVE' => 'Y'
938 ],
939 'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'TITLE', 'PERSONAL_PHOTO']
940 ]);
941
942 while ($user = $orm->fetch())
943 {
944 $user['URL'] = \CCalendar::getUserUrl($user["ID"]);
945 $users[] = $user;
946 }
947 }
948
949 if (!empty($resourseIdList))
950 {
951 $sectionList = Internals\SectionTable::getList(
952 array(
953 "filter" => array(
954 "=ACTIVE" => 'Y',
955 "!=CAL_TYPE" => ['user', 'group', 'company_calendar', 'company', 'calendar_company'],
956 "ID" => $resourseIdList
957 ),
958 "select" => array("ID", "CAL_TYPE", "NAME")
959 )
960 );
961
962 while ($section = $sectionList->fetch())
963 {
964 $resources[$section['ID']] = $section;
965 $resourceNames[] = $section['NAME'];
966 }
967 }
968
969 $resListItems = [];
970 if (!empty($users))
971 {
972 foreach($users as $user)
973 {
974 $resListItems[] = \CCalendar::getUserName($user);
975 }
976 }
977 if (!empty($resourceNames))
978 {
979 foreach($resourceNames as $resourceName)
980 {
981 $resListItems[] = $resourceName;
982 }
983 }
984
985 if (!empty($resListItems))
986 {
987 $skipTime = is_array($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
988 $fromTs = isset($value['DATE_FROM']) ? \CCalendar::timestamp($value['DATE_FROM'], true, !$skipTime) : 0;
989 $toTs = isset($value['DATE_TO']) ? \CCalendar::timestamp($value['DATE_TO'], true, !$skipTime) : 0;
990
991 $resultText = \CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs).': ';
992 $resultText = str_replace("&ndash;", '-', $resultText);
993 if(!empty($value['SERVICE_NAME']))
994 {
995 $resultText .= $value['SERVICE_NAME'].', ';
996 }
997 $resultText .= implode(', ', $resListItems);
998 }
999 return $resultText;
1000 }
1001
1002 public static function getDefaultResourcesList()
1003 {
1004 $result = [];
1005
1006 $typeList = Internals\TypeTable::getList(
1007 array(
1008 "filter" => array(
1009 "XML_ID" => self::RESOURCE_CALENDAR_TYPE
1010 ),
1011 "select" => array("XML_ID", "NAME")
1012 )
1013 );
1014
1015 while ($type = $typeList->fetch())
1016 {
1017 $type['SECTIONS'] = [];
1018 $result[$type['XML_ID']] = $type;
1019 }
1020
1021 if (!$result[self::RESOURCE_CALENDAR_TYPE])
1022 {
1023 Internals\TypeTable::add([
1024 'XML_ID' => self::RESOURCE_CALENDAR_TYPE,
1025 'NAME' => self::RESOURCE_CALENDAR_TYPE,
1026 'ACTIVE' => 'Y'
1027 ]);
1028 \CCalendar::ClearCache('type_list');
1029
1030 $result[self::RESOURCE_CALENDAR_TYPE] = [
1031 'XML_ID' => self::RESOURCE_CALENDAR_TYPE,
1033 ];
1034 }
1035
1036 $sectionList = Internals\SectionTable::getList(
1037 array(
1038 "filter" => array(
1039 "=ACTIVE" => 'Y',
1040 "CAL_TYPE" => [self::RESOURCE_CALENDAR_TYPE],
1041 "!=NAME" => ''
1042 ),
1043 "select" => array("ID", "CAL_TYPE", "NAME")
1044 )
1045 );
1046
1047 while ($section = $sectionList->fetch())
1048 {
1049 if (is_array($result[$section['CAL_TYPE']]['SECTIONS']))
1050 {
1051 $result[$section['CAL_TYPE']]['SECTIONS'][] = $section;
1052 }
1053 }
1054
1055 return $result;
1056 }
1057
1058 protected static function fetchFieldValue($value)
1059 {
1060 $resourseList = Internals\ResourceTable::getList(
1061 array(
1062 "filter" => array(
1063 "=ID" => $value
1064 )
1065 )
1066 );
1067
1068 $result = array(
1069 'ENTRIES' => []
1070 );
1071
1072 while ($resourse = $resourseList->fetch())
1073 {
1074 if (!isset($result['DATE_FROM']))
1075 {
1076 \CTimeZone::Disable();
1077 $result['DATE_FROM'] = $resourse['DATE_FROM']->toString();
1078 $result['DATE_TO'] = $resourse['DATE_TO']->toString();
1079 $result['SERVICE_NAME'] = $resourse['SERVICE_NAME'];
1080 \CTimeZone::Enable();
1081
1082 $fromTs = \CCalendar::timestamp($result['DATE_FROM']);
1083 $toTs = \CCalendar::timestamp($result['DATE_TO']);
1084
1085 if (!$resourse['SKIP_TIME'])
1086 {
1087 $currentUserID = \CCalendar::getCurUserId();
1088
1089 $userOffsetFrom = \CCalendar::getTimezoneOffset($resourse['TZ_FROM'], $fromTs) - \CCalendar::getCurrentOffsetUTC($currentUserID);
1090 $userOffsetTo = \CCalendar::getTimezoneOffset($resourse['TZ_TO'], $toTs) - \CCalendar::getCurrentOffsetUTC($currentUserID);
1091
1092 $result['DATE_FROM'] = \CCalendar::date($fromTs - $userOffsetFrom);
1093 $result['DATE_TO'] = \CCalendar::date($toTs - $userOffsetTo);
1094 }
1095 else
1096 {
1097 $result['DATE_TO'] = \CCalendar::date($toTs + \CCalendar::DAY_LENGTH);
1098 }
1099 }
1100
1101 $result['ENTRIES'][] = array(
1102 'ID' => $resourse['ID'],
1103 'EVENT_ID' => $resourse['EVENT_ID'],
1104 'TYPE' => $resourse['CAL_TYPE'],
1105 'RESOURCE_ID' => $resourse['RESOURCE_ID']
1106 );
1107 }
1108
1109 return $result;
1110 }
1111
1112 public static function getDefaultServiceList()
1113 {
1114 return [
1115 array('name' => '', 'duration' => 60)
1116 ];
1117 }
1118
1119 public static function getBitrx24Limitation()
1120 {
1121 $limit = -1;
1122 if (\Bitrix\Main\Loader::includeModule('bitrix24'))
1123 {
1124 $b24limit = Bitrix24\Feature::getVariable('calendar_resourcebooking_limit');
1125 if ($b24limit !== null)
1126 {
1127 return $b24limit;
1128 }
1129
1130 //else: fallback
1131 $licenseType = \CBitrix24::getLicenseType();
1132
1133 if ($licenseType === 'project' || $licenseType === 'self')
1134 {
1135 $limit = 6;
1136 }
1137 elseif ($licenseType === 'tf' || $licenseType === 'retail')
1138 {
1139 $limit = 12;
1140 }
1141 elseif ($licenseType === 'team' || $licenseType === 'start_2019')
1142 {
1143 $limit = 24;
1144 }
1145 }
1146
1147 return $limit;
1148 }
1149
1150 public static function getAvailableEntriesList()
1151 {
1152 return array('CRM_LEAD', 'CRM_DEAL');
1153 }
1154
1155 public static function onBeforeUserTypeAdd(&$userTypeFields)
1156 {
1157 if ($userTypeFields['USER_TYPE_ID'] === 'resourcebooking')
1158 {
1159 $userTypeFields['MULTIPLE'] = 'Y';
1160 }
1161 return true;
1162 }
1163
1164 public static function getResourceEntriesList($idList = [])
1165 {
1166 return self::fetchFieldValue($idList);
1167 }
1168
1169 public static function getUserFieldByFieldName($fieldName = '', $selectedUsers = [])
1170 {
1171 $resultData = null;
1172 if ($fieldName)
1173 {
1174 $r = \CUserTypeEntity::getList(array("ID" => "ASC"), array("FIELD_NAME" => $fieldName));
1175 if ($r)
1176 {
1177 $resultData = $r->fetch();
1178 }
1179 }
1180
1181 if (!is_array($selectedUsers))
1182 {
1183 $selectedUsers = [];
1184 }
1185 if (is_array($resultData) && isset($resultData['SETTINGS']['SELECTED_USERS']))
1186 {
1187 $selectedUsers = array_merge($selectedUsers, $resultData['SETTINGS']['SELECTED_USERS']);
1188 }
1189
1190 array_walk($selectedUsers, 'intval');
1191 $selectedUsers = array_unique($selectedUsers);
1192
1193 if (!empty($selectedUsers))
1194 {
1195 $orm = UserTable::getList([
1196 'filter' => [
1197 '=ID' => $selectedUsers,
1198 '=ACTIVE' => 'Y'
1199 ],
1200 'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL']
1201 ]);
1202
1203 $resultData['SETTINGS']['USER_INDEX'] = [];
1204 while($user = $orm->fetch())
1205 {
1206 $resultData['SETTINGS']['USER_INDEX'][$user['ID']] = [
1207 'id' => $user['ID'],
1208 'displayName' => \CCalendar::getUserName($user)
1209 ];
1210 }
1211 }
1212
1213 return $resultData;
1214 }
1215
1216 public static function getFillFormData($data = [], $params = [])
1217 {
1218 global $USER;
1219 $resultData = [];
1220 $curUserId = $USER->GetID();
1221
1222 if (isset($params['from']) && $params['from'] instanceof Date
1223 && isset($params['to']) && $params['to'] instanceof Date
1224
1225 )
1226 {
1227 $from = $params['from']->toString();
1228 $to = $params['to']->toString();
1229 }
1230 else
1231 {
1232 $fromTs = (isset($params['from']) && $params['from']) ? \CCalendar::timestamp($params['from']) : time();
1233 $from = \CCalendar::date($fromTs, false);
1234 $to = (isset($params['to']) && $params['to'])
1235 ? \CCalendar::date(\CCalendar::timestamp($params['to']), false)
1236 : \CCalendar::date($fromTs + \CCalendar::DAY_LENGTH * 60, false);
1237 }
1238
1239 if (isset($params['timezone']))
1240 {
1241 $deltaOffset = \CCalendar::GetTimezoneOffset($params['timezone']) - \CCalendar::GetCurrentOffsetUTC($curUserId);
1242 }
1243 else
1244 {
1245 $deltaOffset = 0;
1246 }
1247 $resultData['timezoneOffset'] = 0;
1248
1249 // Fetch fetch UF properties
1250 if ($params['fieldName'])
1251 {
1252 $r = \CUserTypeEntity::getList(array("ID" => "ASC"), array("FIELD_NAME" => $params['fieldName']));
1253 if ($r)
1254 {
1255 $fieldProperties = $r->fetch();
1256 $resultData['fieldSettings'] = $fieldProperties['SETTINGS'];
1257
1258 if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
1259 {
1260 $resultData['timezoneOffset'] = $resultData['fieldSettings']['TIMEZONE'] ? \CCalendar::GetTimezoneOffset($resultData['fieldSettings']['TIMEZONE']) : intval(date("Z"));
1261 $resultData['timezoneOffsetLabel'] = 'UTC'.($resultData['timezoneOffset'] <> 0 ? ' '.($resultData['timezoneOffset'] < 0? '-':'+').sprintf("%02d", ($h = floor(abs($resultData['timezoneOffset'])/3600))).':'.sprintf("%02d", abs($resultData['timezoneOffset']) / 60 - $h * 60) : '');
1262 }
1263 }
1264 }
1265
1266 if (isset($data['users']))
1267 {
1268 $userIdList = is_array($data['users']['value'])
1269 ? $data['users']['value']
1270 : explode('|', $data['users']['value']);
1271 array_walk($userIdList, 'intval');
1272
1273 $resultData['usersAccessibility'] = [];
1274 $accessibility = \CCalendar::getAccessibilityForUsers(array(
1275 'users' => $userIdList,
1276 'from' => $from, // date or datetime in UTC
1277 'to' => $to, // date or datetime in UTC
1278 'getFromHR' => true,
1279 'checkPermissions' => false
1280 ));
1281
1282 foreach($accessibility as $userId => $entries)
1283 {
1284 $resultData['usersAccessibility'][$userId] = [];
1285
1286 foreach($entries as $entry)
1287 {
1288 if (isset($entry['DT_FROM']) && !isset($entry['DATE_FROM']))
1289 {
1290 $resultData['usersAccessibility'][$userId][] = array(
1291 'id' => $entry['ID'],
1292 'dateFrom' => $entry['DT_FROM'],
1293 'dateTo' => $entry['DT_TO'],
1294 );
1295 }
1296 else
1297 {
1298 $fromTs = \CCalendar::Timestamp($entry['DATE_FROM']);
1299 $toTs = \CCalendar::Timestamp($entry['DATE_TO']);
1300
1301 if ($entry['DT_SKIP_TIME'] !== "Y")
1302 {
1303 if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
1304 {
1305 $fromTs -= \CCalendar::GetTimezoneOffset($entry['TZ_FROM']) - $resultData['timezoneOffset'];
1306 $toTs -= \CCalendar::GetTimezoneOffset($entry['TZ_TO']) - $resultData['timezoneOffset'];
1307 }
1308 else
1309 {
1310 $fromTs -= $entry['~USER_OFFSET_FROM'];
1311 $toTs -= $entry['~USER_OFFSET_TO'];
1312 $fromTs += $deltaOffset;
1313 $toTs += $deltaOffset;
1314 }
1315 }
1316
1317 $resultData['usersAccessibility'][$userId][] = array(
1318 'id' => $entry['ID'],
1319 'dateFrom' => \CCalendar::Date($fromTs, $entry['DT_SKIP_TIME'] != 'Y'),
1320 'dateTo' => \CCalendar::Date($toTs, $entry['DT_SKIP_TIME'] != 'Y'),
1321 'fullDay' => $entry['DT_SKIP_TIME'] === "Y"
1322 );
1323 }
1324 }
1325 }
1326
1327 // User Index
1328 $orm = UserTable::getList(
1329 [
1330 'filter' => [
1331 '=ID' => $userIdList,
1332 '=ACTIVE' => 'Y'
1333 ],
1334 'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL']
1335 ]
1336 );
1337
1338 $resultData['SETTINGS']['USER_INDEX'] = [];
1339 while($user = $orm->fetch())
1340 {
1341 $resultData['userIndex'][$user['ID']] = [
1342 'id' => $user['ID'],
1343 'displayName' => \CCalendar::getUserName($user)
1344 ];
1345 }
1346 }
1347
1348 if (isset($data['resources']))
1349 {
1350 $resultData['resourcesAccessibility'] = [];
1351
1352 $resourceIdList = is_array($data['resources']['value'])
1353 ? $data['resources']['value']
1354 : explode('|', $data['resources']['value']);
1355
1356 array_walk($resourceIdList, 'intval');
1357
1358 $resEntries = \CCalendarEvent::getList(
1359 array(
1360 'arFilter' => array(
1361 "FROM_LIMIT" => $from,
1362 "TO_LIMIT" => $to,
1363 "CAL_TYPE" => 'resource',
1364 "ACTIVE_SECTION" => "Y",
1365 "SECTION" => $resourceIdList
1366 ),
1367 'parseRecursion' => true,
1368 'setDefaultLimit' => false
1369 )
1370 );
1371
1372 foreach($resEntries as $row)
1373 {
1374 $fromTs = \CCalendar::timestamp($row["DATE_FROM"]);
1375 $toTs = \CCalendar::timestamp($row['DATE_TO']);
1376
1377 if ($row['DT_SKIP_TIME'] !== "Y" && $resultData['fieldSettings']['USE_USER_TIMEZONE'] !== 'N')
1378 {
1379 if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
1380 {
1381 $fromTs -= \CCalendar::GetTimezoneOffset($entry['TZ_FROM']) - $resultData['timezoneOffset'];
1382 $toTs -= \CCalendar::GetTimezoneOffset($entry['TZ_TO']) - $resultData['timezoneOffset'];
1383 }
1384 else
1385 {
1386 $fromTs -= $row['~USER_OFFSET_FROM'];
1387 $toTs -= $row['~USER_OFFSET_TO'];
1388 $fromTs += $deltaOffset;
1389 $toTs += $deltaOffset;
1390 }
1391 }
1392
1393 $resultData['resourcesAccessibility'][$row['SECT_ID']][] = array(
1394 'id' => $row["ID"],
1395 'dateFrom' => \CCalendar::date($fromTs, $row['DT_SKIP_TIME'] !== 'Y'),
1396 'dateTo' => \CCalendar::date($toTs, $row['DT_SKIP_TIME'] !== 'Y'),
1397 'fullDay' => $row['DT_SKIP_TIME'] === "Y"
1398 );
1399 }
1400 }
1401
1402 $resultData['workTimeStart'] = floor(floatVal(\COption::GetOptionString('calendar', 'work_time_start', 9)));
1403 $resultData['workTimeEnd'] = ceil(floatVal(\COption::GetOptionString('calendar', 'work_time_end', 19)));
1404
1405 return $resultData;
1406 }
1407
1408 public static function getFormDateTimeSlots($fieldName = '', $options = [])
1409 {
1410 $from = (isset($options['from']) && $options['from'] instanceof Date) ? $options['from'] : new Date();
1411 if (isset($options['to']) && ($options['to'] instanceof Date))
1412 {
1413 $to = $options['to'];
1414 }
1415 else
1416 {
1417 $to = clone $from;
1418 $to->add($options['dateInterval'] ?? 'P5D');
1419 }
1420
1421 $formData = \Bitrix\Calendar\UserField\ResourceBooking::getFillFormData(
1422 $options['settingsData'],
1423 [
1424 'fieldName' => $fieldName,
1425 'from' => $from,
1426 'to' => $to
1427 ]
1428 );
1429
1430 // Merge Accessibility
1431 $accessibility = [];
1432 if ($formData['fieldSettings']['USE_USERS'] === 'Y'
1433 && isset($options['settingsData']['users']['value']))
1434 {
1435 $selectedUser = $options['settingsData']['users']['value'];
1436 if (
1437 isset($formData['usersAccessibility'])
1438 && isset($formData['usersAccessibility'][$selectedUser])
1439 )
1440 {
1441 $accessibility = array_merge($accessibility, $formData['usersAccessibility'][$selectedUser]);
1442 }
1443 }
1444
1445 if ($formData['fieldSettings']['USE_RESOURCES'] === 'Y'
1446 && isset($options['settingsData']['resources']['value']))
1447 {
1448 $selectedResource = $options['settingsData']['resources']['value'];
1449 if (
1450 isset($formData['resourcesAccessibility'])
1451 && isset($formData['resourcesAccessibility'][$selectedResource])
1452 )
1453 {
1454 $accessibility = array_merge($accessibility, $formData['resourcesAccessibility'][$selectedResource]);
1455 }
1456 }
1457
1458 $result = null;
1459 if ($selectedUser || $selectedResource)
1460 {
1461 $format = Date::convertFormatToPhp(FORMAT_DATETIME);
1462 foreach ($accessibility as $i => $item)
1463 {
1464 $accessibility[$i]['fromTs'] = (new DateTime($item['dateFrom'], $format))->getTimestamp();
1465 $accessibility[$i]['toTs'] = (new DateTime($item['dateTo'], $format))->getTimestamp();
1466 }
1467
1468 $result = self::getAvailableTimeSlots($accessibility, [
1469 'from' => $from,
1470 'to' => $to,
1471 'scale' => $options['settingsData']['time']['scale']
1472 ]);
1473 }
1474
1475 return $result;
1476 }
1477
1478 private static function getAvailableTimeSlots($accessibility, $options)
1479 {
1480 $from = (isset($options['from']) && $options['from'] instanceof Date) ? $options['from'] : new Date();
1481 $to = (isset($options['to']) && $options['to'] instanceof Date) ? $options['to'] : new Date();
1482 $to->add('P1D');
1483 $scale = (int)$options['scale'] > 0 ? (int)$options['scale'] : 60;
1484
1485 $workTimeStart = (int)\COption::getOptionString('calendar', 'work_time_start', 9);
1486 $workTimeEnd = (int)\COption::getOptionString('calendar', 'work_time_end', 19);
1487
1488 $step = 0;
1489 $currentDate = new DateTime($from->toString(), Date::convertFormatToPhp(FORMAT_DATETIME));
1490 $slots = [];
1491
1492 while ($currentDate->getTimestamp() < $to->getTimestamp())
1493 {
1494 $currentDate->setTime($workTimeStart, 0, 0);
1495 while ((int)$currentDate->format('H') < $workTimeEnd)
1496 {
1497 if ($currentDate->getTimestamp() > time())
1498 {
1499 $isFree = true;
1500 $slotStart = $currentDate->getTimestamp();
1501 $slotEnd = $slotStart + $scale * 60;
1502
1503 foreach ($accessibility as $i => $item)
1504 {
1505 if ($item['toTs'] > $slotStart && $item['fromTs'] < $slotEnd)
1506 {
1507 $isFree = false;
1508 break;
1509 }
1510 }
1511
1512 if ($isFree)
1513 {
1514 $slots[] = clone $currentDate;
1515 }
1516 }
1517 $currentDate->add('PT'.$scale.'M');
1518 $step++;
1519 }
1520
1521 if($step > 1000)
1522 {
1523 break;
1524 }
1525 $currentDate->add('P1D');
1526 }
1527
1528 return $slots;
1529 }
1530
1531 public static function prepareFormDateValues($dateFrom = null, $fieldName = '', $options = [])
1532 {
1533 $result = [];
1534 if (!isset($dateFrom) || !($dateFrom instanceof DateTime))
1535 {
1536 throw new \Bitrix\Main\SystemException('Wrong dateFrom value type. DateTime expected');
1537 }
1538 if (empty($fieldName))
1539 {
1540 throw new \Bitrix\Main\SystemException('Wrong fieldName given');
1541 }
1542
1543 $duration = 60;
1544 if (!empty($options['settingsData']['duration']['value']))
1545 {
1546 $duration = $options['settingsData']['duration']['value'];
1547 }
1548 else if (!empty($options['settingsData']['duration']['defaultValue']))
1549 {
1550 $duration = $options['settingsData']['duration']['defaultValue'];
1551 }
1552
1553 $r = \CUserTypeEntity::getList(["ID" => "ASC"], ["FIELD_NAME" => $fieldName]);
1554 if ($r)
1555 {
1556 $fieldProperties = $r->fetch();
1557 $fieldSettings = $fieldProperties['SETTINGS'];
1558
1559 if ($fieldSettings['USE_USERS'] === 'Y'
1560 && isset($options['settingsData']['users']['value']))
1561 {
1562 $result[] = self::prepareValue('user', $options['settingsData']['users']['value'], $dateFrom->toString(), $duration);
1563 }
1564
1565 if ($fieldSettings['USE_RESOURCES'] === 'Y'
1566 && isset($options['settingsData']['resources']['value']))
1567 {
1568 $result[] = self::prepareValue('resource', $options['settingsData']['resources']['value'], $dateFrom->toString(), $duration);
1569 }
1570 }
1571
1572 return $result;
1573 }
1574}
static saveResource($id, $resourceType, $resourceId, $eventFields=[], $params=[])
static getPublicEdit($userField, $additionalParams=[])
static prepareFormDateValues($dateFrom=null, $fieldName='', $options=[])
getSettingsHTML($userField=false, $htmlControl=[], $varsFromForm=false)
static getUserFieldByFieldName($fieldName='', $selectedUsers=[])
static onBeforeSaveAll($userField, $values, $userId=false)
static getFillFormData($data=[], $params=[])
static onDelete($userField, $values, $userId=false)
static getPublicView($userField, $additionalParams=[])
static prepareValue($type, $id, $from, $duration, $serviceName='')
static getFormDateTimeSlots($fieldName='', $options=[])
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static encode($string, $flags=ENT_COMPAT, $doubleEncode=true)
add($interval)
Definition date.php:145
$GLOBALS['____1444769544']
Definition license.php:1