403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/calendar/lib/userfield/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/calendar/lib/userfield/resourcebooking.php
<?php
namespace Bitrix\Calendar\UserField;

use Bitrix\Bitrix24;
use Bitrix\Calendar\Internals;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Text\HtmlFilter;
use Bitrix\Main\Type;
use Bitrix\Main\Type\Date;
use Bitrix\Main\Type\DateTime;
use Bitrix\Main\UserTable;

Loc::loadMessages(__FILE__);


class ResourceBooking extends \Bitrix\Main\UserField\TypeBase
{
	const USER_TYPE_ID = 'resourcebooking';
	public const EVENT_LABEL = '#resourcebooking#';
	const RESOURCE_CALENDAR_TYPE = 'resource';
	const BITRIX24_RESTRICTION = 100;
	const BITRIX24_RESTRICTION_CODE = 'uf_resourcebooking';

	const CRM_LEAD_ENTITY_ID = 'CRM_LEAD';
	const CRM_SUSPENDED_LEAD_ENTITY_ID = 'CRM_LEAD_SPD';
	const CRM_DEAL_ENTITY_ID = 'CRM_DEAL';
	const CRM_SUSPENDED_DEAL_ENTITY_ID = 'CRM_DEAL_SPD';

	protected static $restrictionCount = null;

	public static function getUserTypeDescription()
	{
		return array(
			"USER_TYPE_ID" => static::USER_TYPE_ID,
			"CLASS_NAME" => __CLASS__,
			"DESCRIPTION" => Loc::getMessage("USER_TYPE_RESOURCEBOOKING_DESCRIPTION"),
			"BASE_TYPE" => \CUserTypeManager::BASE_TYPE_STRING,
			"EDIT_CALLBACK" => array(__CLASS__, 'getPublicEdit'),
			"VIEW_CALLBACK" => array(__CLASS__, 'getPublicView')
		);
	}

	public static function prepareSettings($userField = [])
	{
		$userField = [
			'SETTINGS' => [
				'SELECTED_RESOURCES' => $userField['SETTINGS']['SELECTED_RESOURCES'] ?? null,
				'SELECTED_USERS' => $userField['SETTINGS']['SELECTED_USERS'] ?? null,
				'USE_USERS' => $userField['SETTINGS']['USE_USERS'] ?? null,
				'USE_RESOURCES' => $userField['SETTINGS']['USE_RESOURCES'] ?? null,
				'FULL_DAY' => $userField['SETTINGS']['FULL_DAY'] ?? null,
				'ALLOW_OVERBOOKING' => $userField['SETTINGS']['ALLOW_OVERBOOKING'] ?? null,
				'USE_SERVICES' => $userField['SETTINGS']['USE_SERVICES'] ?? null,
				'SERVICE_LIST' => $userField['SETTINGS']['SERVICE_LIST'] ?? null,
				'TIMEZONE' => $userField['SETTINGS']['TIMEZONE'] ?? null,
				'USE_USER_TIMEZONE' => $userField['SETTINGS']['USE_USER_TIMEZONE'] ?? null,
			],
		];

		$selectedResources = [];

		if (is_array($userField["SETTINGS"]["SELECTED_RESOURCES"]))
		{
			$selectedResources = self::handleResourceList($userField["SETTINGS"]["SELECTED_RESOURCES"]);
		}
		$selectedUsers = [];
		if (is_array($userField["SETTINGS"]["SELECTED_USERS"]))
		{
			foreach($userField["SETTINGS"]["SELECTED_USERS"] as $user)
			{
				if (intval($user) > 0)
				{
					$selectedUsers[] = intval($user);
				}
			}
		}

		return array(
			"USE_USERS" => $userField["SETTINGS"]["USE_USERS"] === 'N' ? 'N' : 'Y',
			"USE_RESOURCES" => $userField["SETTINGS"]["USE_RESOURCES"] === 'N' ? 'N' : 'Y',
			"RESOURCES" => self::getDefaultResourcesList(),
			"SELECTED_RESOURCES" => $selectedResources,
			"SELECTED_USERS" => $selectedUsers,
			"FULL_DAY" => $userField["SETTINGS"]["FULL_DAY"] === 'Y' ? 'Y' : 'N',
			"ALLOW_OVERBOOKING" => $userField["SETTINGS"]["ALLOW_OVERBOOKING"] === 'N' ? 'N' : 'Y',
			"USE_SERVICES" => $userField["SETTINGS"]["USE_SERVICES"] === 'N' ? 'N' : 'Y',
			"SERVICE_LIST" => is_array($userField["SETTINGS"]["SERVICE_LIST"]) ? $userField["SETTINGS"]["SERVICE_LIST"] : self::getDefaultServiceList(),
			"RESOURCE_LIMIT" => self::getBitrx24Limitation(),
			"TIMEZONE" => $userField["SETTINGS"]["TIMEZONE"],
			"USE_USER_TIMEZONE" => $userField["SETTINGS"]["USE_USER_TIMEZONE"] === 'Y' ? 'Y' : 'N'
		);
	}

	public static function getDBColumnType()
	{
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();
		return $helper->getColumnTypeByField(new \Bitrix\Main\ORM\Fields\TextField('x'));
	}

	public static function checkFields($userField, $value)
	{
		if($userField["MANDATORY"] === "Y" && ($value === 'empty' || !$value))
		{
			return array(array(
				"id" => $userField['FIELD_NAME'],
				"text"=>str_replace("#FIELD_NAME#", $userField['EDIT_FORM_LABEL'],
					Loc::getMessage("USER_TYPE_FIELD_VALUE_IS_MISSING"))
			));
		}

		return [];
	}

	public static function onBeforeSaveAll($userField, $values, $userId = false)
	{
		$valuesToSave = [];
		$currentUserId = \CCalendar::getCurUserId();
		$dateFrom = false;
		$dateTo = false;
		$serviceName = '';
		$entityTitle = '';
		$fields = [];
		$entity = null;

		$resourseList = Internals\ResourceTable::query()
			->setSelect(['*'])
			->where('PARENT_TYPE', $userField['ENTITY_ID'])
			->where('PARENT_ID', $userField['VALUE_ID'])
			->where('UF_ID', $userField['ID'])
			->exec()
		;

		$currentEntriesIndex = [];
		while ($resourse = $resourseList->fetch())
		{
			$currentEntriesIndex[$resourse['CAL_TYPE'].$resourse['RESOURCE_ID']] = $resourse;
		}

		if (self::isCrmEntity($userField['ENTITY_ID']) && Loader::includeModule('crm'))
		{
			if ($userField['ENTITY_ID'] === self::CRM_DEAL_ENTITY_ID)
			{
				$dealResult = \CCrmDeal::GetListEx(
					[],
					['=ID' => $userField['VALUE_ID'], 'CHECK_PERMISSIONS' => 'N'],
					false,
					false,
					['TITLE'],
				);

				if (!empty($dealResult))
				{
					$entity = $dealResult->Fetch();
				}

				if (!empty($entity) && $entity['TITLE'])
				{
					$entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE").': '.$entity['TITLE'];
				}
			}
			elseif ($userField['ENTITY_ID'] === self::CRM_LEAD_ENTITY_ID)
			{
				$leadResult = \CCrmLead::GetListEx(
					[],
					['=ID' => $userField['VALUE_ID'], 'CHECK_PERMISSIONS' => 'N'],
					false,
					false,
					['TITLE'],
				);

				if (!empty($leadResult))
				{
					$entity = $leadResult->Fetch();
				}

				if (!empty($entity) && $entity['TITLE'])
				{
					$entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE").': '.$entity['TITLE'];
				}
			}

			if (
				$userField['ENTITY_ID'] === self::CRM_SUSPENDED_DEAL_ENTITY_ID
				|| $userField['ENTITY_ID'] === self::CRM_SUSPENDED_LEAD_ENTITY_ID
			)
			{
				$entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE")." (Deleted)";
			}
		}

		if (!$entityTitle)
		{
			$entityTitle = Loc::getMessage("USER_TYPE_RESOURCE_EVENT_TITLE");
		}

		$valuesToMerge = [];
		foreach ($values as $value)
		{
			if ((string)$value === (string)((int)$value))
			{
				$currentValue = static::fetchFieldValue($value);
				$skipTime = is_array($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
				$fromTs = \CCalendar::timestamp($currentValue['DATE_FROM'], true, !$skipTime);
				$toTs = \CCalendar::timestamp($currentValue['DATE_TO'], true, !$skipTime);

				foreach ($currentValue['ENTRIES'] as $entry)
				{
					$entryExist = false;

					foreach ($values as $iValue)
					{
						$str = $entry['TYPE'].'|'.$entry['RESOURCE_ID'];
						if (str_starts_with($iValue, $str))
						{
							$entryExist = true;
							break;
						}
					}

					if (!$entryExist)
					{
						$valuesToMerge[] = self::prepareValue(
							$entry['TYPE'],
							$entry['RESOURCE_ID'],
							$currentValue['DATE_FROM'],
							$toTs - $fromTs,
							$currentValue['SERVICE_NAME']
						);
					}
				}
			}
		}

		$values = array_merge($values, $valuesToMerge);

		foreach ($values as $value)
		{
			$value = self::parseValue($value);

			if (!is_array($value))
			{
				continue;
			}

			if (!$dateFrom || !$dateTo)
			{
				$dateFromTimestamp = \CCalendar::timestamp($value['from']);
				$skipTime = isset($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
				$dateFrom = \CCalendar::date($dateFromTimestamp, !$skipTime);
				$duration = (int)$value['duration'];
				$dateTo = \CCalendar::date($dateFromTimestamp + ($skipTime ? $duration - \CCalendar::DAY_LENGTH : $duration), !$skipTime);
				$serviceName = trim($value['serviceName']);

				$fields = [
					"DATE_FROM" => $dateFrom,
					"DATE_TO" => $dateTo,
					"SKIP_TIME" => $skipTime,
					"NAME" => $entityTitle
				];

				if (
					$userField['ENTITY_ID'] === self::CRM_SUSPENDED_DEAL_ENTITY_ID
					|| $userField['ENTITY_ID'] === self::CRM_SUSPENDED_LEAD_ENTITY_ID
				)
				{
					$fields["DELETED"] = 'Y';
				}

				if ($serviceName !== '')
				{
					$fields["DESCRIPTION"] = Loc::getMessage("USER_TYPE_RESOURCE_SERVICE_LABEL").': '.$serviceName;
				}

				if (!$skipTime)
				{
					if ($userField['SETTINGS']['USE_USER_TIMEZONE'] === 'Y')
					{
						$timezoneName = \CCalendar::getUserTimezoneName($currentUserId, true);
					}
					else if($userField['SETTINGS']['TIMEZONE'])
					{
						$timezoneName = $userField['SETTINGS']['TIMEZONE'];
					}
					else
					{
						$timezoneName = \CCalendar::GetGoodTimezoneForOffset((int)date("Z"));
					}

					if($timezoneName)
					{
						$fields['TZ_FROM'] = $timezoneName;
						$fields['TZ_TO'] = $timezoneName;
					}
				}
			}

			$entryId = false;
			if (isset($currentEntriesIndex[$value['type'] . $value['id']]))
			{
				$fields['ID'] = $currentEntriesIndex[$value['type'].$value['id']]['EVENT_ID'];
				$entryId = $currentEntriesIndex[$value['type'].$value['id']]['ID'];
			}
			else
			{
				unset($fields['ID']);
			}

			$resourceBookingId = self::saveResource(
				$entryId,
				$value['type'],
				$value['id'],
				$fields,
				[
					'userField' => $userField,
					'serviceName' => $serviceName
				]
			);

			if ($resourceBookingId)
			{
				$valuesToSave[] = $resourceBookingId;
			}
		}

		foreach ($currentEntriesIndex as $resourceEntry)
		{
			if (!in_array($resourceEntry['ID'], $valuesToSave))
			{
				self::releaseResource($resourceEntry);
			}
		}

		return  $valuesToSave;
	}

	public static function onDelete($userField, $values, $userId = false)
	{
		$resourseList = Internals\ResourceTable::getList(
			array(
				"filter" => array(
					"=PARENT_TYPE" => $userField['ENTITY_ID'],
					"=PARENT_ID" => $userField['ENTITY_VALUE_ID'],
					"=UF_ID" => $userField['ID'],
				)
			)
		);

		while ($resourse = $resourseList->fetch())
		{
			self::releaseResource($resourse);
		}
	}

	/**
	 * Saves resource of given type.
	 *
	 * @param integer $id id of current booking.
	 * @param string $resourceType resource type.
	 * @param integer $resourceId resource id.
	 * @param array $eventFields calendar event fields.
	 * @param array $params additional params.
	 *
	 * @return integer, id of resource booking or null
	 * @throws \Bitrix\Main\SystemException
	 */
	public static function saveResource($id, $resourceType, $resourceId, $eventFields = [], $params = [])
	{
		$valueToSave = null;
		$currentUserId = \CCalendar::getCurUserId();

		$eventFields["EVENT_TYPE"] = '#resourcebooking#';
		if ($resourceType === 'user')
		{
			$eventFields["CAL_TYPE"] = $resourceType;
			$eventFields["OWNER_ID"] = $resourceId;

			if ($params['userField']['ENTITY_ID'] === 'CRM_DEAL' || $params['userField']['ENTITY_ID'] === 'CRM_LEAD')
			{
				$sectionId = \CCalendar::getCrmSection($resourceId, true);
			}
			else
			{
				$sectionId = \CCalendar::getMeetingSection($resourceId, true);
			}
			if ($sectionId)
			{
				$eventFields['SECTIONS'] = [$sectionId];
			}

			// Userfields for event
			$userFields = [];
			if ($params['userField']['ENTITY_ID'] === 'CRM_DEAL')
			{
				$userFields['UF_CRM_CAL_EVENT'] = ["D_".$params['userField']['VALUE_ID']];
			}
			elseif ($params['userField']['ENTITY_ID'] === 'CRM_LEAD')
			{
				$userFields['UF_CRM_CAL_EVENT'] = ["L_".$params['userField']['VALUE_ID']];
			}

			$entryId = \CCalendar::saveEvent([
				'arFields' => $eventFields,
				'UF' => $userFields,
				'silentErrorMode' => false,
				'autoDetectSection' => true,
				'autoCreateSection' => true,
				'checkPermission' => false
			]);
		}
		else
		{
			$eventFields["CAL_TYPE"] = $resourceType;
			$eventFields["SECTIONS"] = $resourceId;
			$entryId = \CCalendarEvent::edit([
				'arFields' => $eventFields
			]);
		}

		if ($entryId)
		{
			if ($eventFields['TZ_FROM'] ?? null)
			{
				$from = new Type\DateTime($eventFields["DATE_FROM"]);
				$to = new Type\DateTime($eventFields["DATE_TO"]);
				$fromUtc = new Type\DateTime($eventFields["DATE_FROM"], null, new \DateTimeZone('UTC'));
				$toUtc = new Type\DateTime($eventFields["DATE_TO"], null, new \DateTimeZone('UTC'));
			}
			else
			{
				$from = new Type\DateTime($eventFields["DATE_FROM"]);
				$to = new Type\DateTime($eventFields["DATE_TO"]);
				$fromUtc = $from;
				$toUtc = $to;
			}

			\CTimeZone::Disable();

			$resourceTableFields = [
				'EVENT_ID' => $entryId,
				'CAL_TYPE' => $eventFields["CAL_TYPE"],
				'RESOURCE_ID' => $resourceId,
				'PARENT_TYPE' => $params['bindingEntityType'] ?? $params['userField']['ENTITY_ID'],
				'PARENT_ID' => $params['bindingEntityId'] ?? $params['userField']['VALUE_ID'],
				'UF_ID' => $params['bindingUserfieldId'] ?? $params['userField']['ID'],
				'DATE_FROM' => $from,
				'DATE_TO' => $to,
				'SKIP_TIME' => $eventFields['SKIP_TIME'] ?? null,
				'DATE_FROM_UTC' => $fromUtc,
				'DATE_TO_UTC' => $toUtc,
				'TZ_FROM' => $eventFields['TZ_FROM'] ?? null,
				'TZ_TO' => $eventFields['TZ_TO'] ?? null,
				'TZ_OFFSET_FROM' => \CCalendar::getTimezoneOffset(
					$eventFields['TZ_FROM'] ?? null,
					\CCalendar::timestamp($eventFields["DATE_FROM"])
				),
				'TZ_OFFSET_TO' => \CCalendar::getTimezoneOffset(
					$eventFields['TZ_TO'] ?? null,
					\CCalendar::timestamp($eventFields["DATE_TO"])
				),
				'CREATED_BY' => $currentUserId,
				'SERVICE_NAME' => $params['serviceName'] ?? null
			];

			if ($id)
			{
				$result = Internals\ResourceTable::update($id, $resourceTableFields);
			}
			else
			{
				$result = Internals\ResourceTable::add($resourceTableFields);
			}

			if ($result->isSuccess())
			{
				$valueToSave = $result->getId();
			}
			else
			{
				\CCalendar::deleteEvent((int)$entryId, false);
			}

			foreach(\Bitrix\Main\EventManager::getInstance()->findEventHandlers("calendar", "onAfterResourceBookingAdd") as $event)
			{
				ExecuteModuleEventEx($event, [[
					'userFieldValueId' => $valueToSave,
					'bookingEventId' => $entryId,
					'resourceType' => $resourceType,
					'resourceId' => $resourceId,
					'serviceName' => $params['serviceName'] ?? null,
					'dateFrom' => $from,
					'dateTo' => $to,
					'skipTime' => $eventFields['SKIP_TIME'],
					'timezoneFrom' => $eventFields['TZ_FROM'] ?? null,
					'timezoneTo' => $eventFields['TZ_TO'] ?? null,
				]]);
			}

			\CTimeZone::Enable();
		}

		return $valueToSave;
	}

	private static function isCrmEntity($entityId)
	{
		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]);
	}

	private static function isResourceAvailable()
	{

	}

	public static function releaseResource($entry)
	{
		\CCalendar::deleteEvent((int)$entry['EVENT_ID'], true, array('checkPermissions' => false));
		Internals\ResourceTable::delete($entry['ID']);
	}

	private static function handleResourceList($resources)
	{
		$result = [];
		foreach($resources as $resource)
		{
			if (is_array($resource))
			{
				if (($resource['id'] ?? null) && (($resource['deleted'] ?? null) || ($resource['title'] ?? null) == ''))
				{
					$sectionList = Internals\SectionTable::getList(
						array(
							"filter" => array("ID" => $resource['id'], "CAL_TYPE" => $resource['type'] ?? null),
							"select" => array("ID", "CAL_TYPE", "NAME")
						)
					);
					if ($sectionList->fetch())
					{
						Internals\SectionTable::delete(array('ID' => $resource['id']));
					}
				}
				else if ($resource['id'] ?? null)
				{
					$sectionList = Internals\SectionTable::getList(
						array(
							"filter" => array("ID" => $resource['id'], "CAL_TYPE" => $resource['type'] ?? null),
							"select" => array("ID", "CAL_TYPE", "NAME")
						)
					);
					if ($section = $sectionList->fetch())
					{
						if ($section['NAME'] != ($resource['title'] ?? null))
						{
							\CCalendarSect::edit(array(
								'arFields' => array(
									'ID' => $resource['id'],
									'CAL_TYPE' => $resource['type'] ?? null,
									'NAME' => $resource['title'] ?? null,
									'ACCESS' => []
								)
							));
						}
					}
					$result[] = $resource;
				}
				elseif (!($resource['id'] ?? null) && ($resource['title'] ?? null) !== '')
				{
					$resource['id'] = \CCalendarSect::edit(array(
						'arFields' => array(
							'CAL_TYPE' => $resource['type'] ?? null,
							'NAME' => $resource['title'] ?? null,
							'ACCESS' => []
						)
					));
					$result[] = $resource;
				}
			}
		}
		return $result;
	}

	public static function prepareValue($type, $id, $from, $duration, $serviceName = '')
	{
		$type = $type === 'user' || $type === 'resource' ? $type : 'user';
		$id = (int)$id > 0 ? $id : 1;
		$duration = (int)$duration > 0 ? $duration : 60;
		return $type.'|'.$id.'|'.$from.'|'.$duration.'|'.$serviceName;
	}

	public static function parseValue($value)
	{
		$res = false;
		if(mb_strpos($value, '|') >= 0)
		{
			$list = explode('|', $value);
			$type = $list[0] ?? null;
			$id = $list[1] ?? null;
			$from = $list[2] ?? null;
			$duration = $list[3] ?? null;
			$serviceName = $list[4] ?? null;
			if ($type === 'user' || ($type === 'resource' && (int)$id > 0))
			{
				$res = array(
					'type' => $type,
					'id' => $id,
					'from' => $from,
					'duration' => $duration,
					'serviceName' => $serviceName ? $serviceName : ''
				);
			}
		}
		return $res;
	}

	function getSettingsHTML($userField = false, $htmlControl = [], $varsFromForm = false)
	{
		\Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield', 'calendar_planner', 'socnetlogdest', 'helper', 'main', 'ui', 'ui.selector']);

		if($varsFromForm)
		{
			$settingsValue = $GLOBALS[$htmlControl["NAME"]];
		}
		elseif(is_array($userField))
		{
			$settingsValue = $userField["SETTINGS"];
		}
		else
		{
			$settingsValue = [];
		}

		$controlId = $userField['FIELD_NAME'].'_'.rand();
		$params = [
			'controlId' => $controlId,
			'settings' => $settingsValue,
			'userField' => $userField,
			'htmlControl' => $htmlControl,
			'outerWrapId' => $controlId.'-settings-outer-wrap',
			'formName' => 'post_form'
		];

		if ($settingsValue['USE_USERS'] === 'Y')
		{
			$params['socnetDestination'] = \CCalendar::getSocNetDestination(false, [], $settingsValue['SELECTED_USERS']);
		}

		$result = '<tr>
			<td></td>
			<td>
				<div id="'.HtmlFilter::encode($params['outerWrapId']).'"></div>
				<script>(function(){new BX.Calendar.AdminSettingsViewer('.
			\Bitrix\Main\Web\Json::encode($params).
			').showLayout();})();</script>
			</td>
		</tr>';

		return $result;
	}

	function getEditFormHTML($userField, $htmlControl)
	{
		return static::getPublicEdit($userField, $htmlControl);
	}

	public static function getPublicEdit($userField, $additionalParams = [])
	{
		\Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield', 'calendar_planner', 'socnetlogdest']);

		$fieldName = static::getFieldName($userField, $additionalParams);
		$userField['VALUE'] = static::getFieldValue($userField, $additionalParams);

		$value = static::fetchFieldValue($userField["VALUE"]);

		if (
			$userField['SETTINGS']['USE_RESOURCES'] === 'Y'
			&& (!is_array($userField['SETTINGS']['SELECTED_RESOURCES']) || empty($userField['SETTINGS']['SELECTED_RESOURCES']))
		)
		{
			$userField['SETTINGS']['USE_RESOURCES'] = 'N';
		}

		$controlId = $userField['FIELD_NAME'].'_'.rand();
		$params = [
			'controlId' => $controlId,
			'inputName' => $fieldName,
			'value' => $value,
			'plannerId' => $controlId.'_planner',
			'userSelectorId' => 'resource_booking_user_selector',
			'useUsers' => $userField['SETTINGS']['USE_USERS'] === 'Y',
			'useResources' => $userField['SETTINGS']['USE_RESOURCES'] === 'Y',
			'fullDay' => $userField['SETTINGS']['FULL_DAY'] === 'Y',
			'allowOverbooking' => $userField['SETTINGS']['ALLOW_OVERBOOKING'] !== 'N',
			'useServices' => $userField['SETTINGS']['USE_SERVICES'] === 'Y',
			'serviceList' => $userField['SETTINGS']['SERVICE_LIST'],
			'resourceList' => $userField['SETTINGS']['SELECTED_RESOURCES'],
			'userList' => $userField['SETTINGS']['SELECTED_USERS'],
			'userfieldId' => $userField['ID'],
			'resourceLimit' => self::getBitrx24Limitation(),
			'workTime' => [\COption::getOptionString('calendar', 'work_time_start', 9), \COption::getOptionString('calendar', 'work_time_end', 19)]
		];

		if ($params['useUsers'])
		{
			$params['socnetDestination'] = \CCalendar::getSocNetDestination(false, [], $userField['SETTINGS']['SELECTED_USERS']);
		}

		ob_start();
		?>

		<div id="<?= HtmlFilter::encode($params['controlId'])?>" class="crm-entity-widget-resourcebook-container"></div>
		<script>
			(function(){
				'use strict';
				BX.Runtime.loadExtension('calendar.resourcebookinguserfield').then(function(exports)
				{
					if (exports && BX.type.isFunction(exports.ResourcebookingUserfield))
					{
						exports.ResourcebookingUserfield.initEditFieldController(<?= \Bitrix\Main\Web\Json::encode($params)?>);
					}
				});
			})();
		</script>
		<?

		$html = ob_get_clean();

		return static::getHelper()->wrapDisplayResult($html);
	}

	public static function getPublicView($userField, $additionalParams = [])
	{
		$context = $additionalParams['CONTEXT'] ?? '';
		$value = static::fetchFieldValue($userField["VALUE"] ?? null);
		$skipTime = is_array($userField['SETTINGS'] ?? null) && ($userField['SETTINGS']['FULL_DAY'] ?? null) == 'Y';
		$fromTs = \CCalendar::timestamp($value['DATE_FROM'] ?? null, true, !$skipTime);
		$toTs = \CCalendar::timestamp($value['DATE_TO'] ?? null, true, !$skipTime);

		$users = [];
		$resources = [];
		$resourceNames = [];
		$userIdList = [];
		$resourceIdList = [];

		foreach($value['ENTRIES'] as $entry)
		{
			if ($entry['TYPE'] === 'user')
			{
				$userIdList[] = (int) $entry['RESOURCE_ID'];
			}
			else
			{
				$resourceIdList[] = (int) $entry['RESOURCE_ID'];
			}
		}

		$userIdList = array_unique($userIdList);
		$resourceIdList = array_unique($resourceIdList);

		if (!empty($userIdList))
		{
			$orm = UserTable::getList([
				'filter' => [
					'=ID' => $userIdList,
					'=ACTIVE' => 'Y'
				],
				'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'TITLE', 'PERSONAL_PHOTO']
			]);

			while ($user = $orm->fetch())
			{
				$user['URL'] = \CCalendar::getUserUrl($user["ID"]);
				$users[] = $user;
			}
		}

		if (!empty($resourceIdList))
		{
			$sectionList = Internals\SectionTable::getList(
				array(
					"filter" => array(
						"=ACTIVE" => 'Y',
						"!=CAL_TYPE" => ['user', 'group', 'company_calendar', 'company', 'calendar_company'],
						"ID" => $resourceIdList
					),
					"select" => array("ID", "CAL_TYPE", "NAME")
				)
			);

			while ($section = $sectionList->fetch())
			{
				$resources[$section['ID']] = $section;
				$resourceNames[] = HtmlFilter::encode($section['NAME']);
			}
		}

		if ($context === 'CRM_GRID')
		{
			\Bitrix\Main\UI\Extension::load([
				'ui.design-tokens',
				'ui.fonts.opensans',
			]);

			\Bitrix\Main\Page\Asset::getInstance()->addCss('/bitrix/js/calendar/userfield/resourcebooking.css');
			$resListItems = [];
			if (!empty($users))
			{
				foreach($users as $user)
				{
					$resListItems[] = '<span>'.HtmlFilter::encode(\CCalendar::getUserName($user)).'</span>';
				}
			}
			if (!empty($resourceNames))
			{
				foreach($resourceNames as $resourceName)
				{
					$resListItems[] = '<span>'.$resourceName.'</span>';
				}
			}

			if (!empty($resListItems))
			{
				$html = '<span>'.\CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs).'</span>: ';
				if(!empty($value['SERVICE_NAME']))
				{
					$html .= '<span>'.HtmlFilter::encode($value['SERVICE_NAME']).'</span>, ';
				}
				$html .= '<span>'.implode(', ', $resListItems).'</span>';
			}
			else
			{
				$html = '<span class="calendar-resbook-field-empty">'.Loc::getMessage("USER_TYPE_RESOURCE_EMPTY").'</span>';
			}
		}
		else
		{
			\Bitrix\Main\UI\Extension::load(['uf', 'calendar.resourcebookinguserfield']);
			if (empty($users) && empty($resourceNames))
			{
				$html = Loc::getMessage("USER_TYPE_RESOURCE_EMPTY");
			}
			else
			{
				ob_start();
				?>
				<div class="calendar-res-book-public-view-outer-wrap">
					<div class="calendar-res-book-public-view-inner-wrap">
						<div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
							<div class="crm-entity-widget-content-block-title">
								<span
									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>
							</div>
							<div
								class="crm-entity-widget-content-block-inner"><?= \CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs) ?></div>
						</div>

						<? if(!empty($value['SERVICE_NAME'])): ?>
							<div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
								<div class="crm-entity-widget-content-block-title">
									<span
										class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_SERVICE_PLACEHOLDER") ?></span>
								</div>
								<div
									class="crm-entity-widget-content-block-inner"><?= HtmlFilter::encode($value['SERVICE_NAME']) ?></div>
							</div>
						<?endif; ?>

						<? if (!empty($users)): ?>
							<div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
								<div class="crm-entity-widget-content-block-title">
									<span
										class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_USERS_CONTROL_DEFAULT_NAME") ?></span>
								</div>
								<? foreach($users as $user): ?>
									<div class="crm-widget-employee-container">
										<a class="crm-widget-employee-avatar-container" href="<?= $user['URL'] ?>" target="_blank" style="background-image: url('<?= \CCalendar::getUserAvatarSrc($user) ?>'); background-size: 30px;"></a>
										<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>
									</div>
								<? endforeach; ?>
							</div>
						<?endif; ?>

						<? if (!empty($resourceNames)): ?>
							<div class="crm-entity-widget-content-block crm-entity-widget-content-block-field-select">
								<div class="crm-entity-widget-content-block-title">
									<span
										class="crm-entity-widget-content-block-title-text"><?= Loc::getMessage("USER_TYPE_RESOURCE_RESOURCE_CONTROL_DEFAULT_NAME") ?></span>
								</div>
								<div class="crm-entity-widget-content-block-inner"><?= implode(', ', $resourceNames) ?></div>
							</div>
						<?endif; ?>
					</div>
				</div>
				<?
				$html = ob_get_clean();
			}
		}

		if ($context === 'UI_EDITOR')
		{
			$html = '<span class="field-item">' . $html . '</span>';
		}

		return static::getHelper()->wrapDisplayResult($html);
	}

	public static function getPublicText($userField)
	{
		$resultText = '';
		$value = static::fetchFieldValue($userField["VALUE"]);

		$users = [];
		$resources = [];
		$resourceNames = [];
		$userIdList = [];
		$resourseIdList = [];

		foreach($value['ENTRIES'] as $entry)
		{
			if ($entry['TYPE'] === 'user')
			{
				$userIdList[] = (int) $entry['RESOURCE_ID'];
			}
			else
			{
				$resourseIdList[] = (int) $entry['RESOURCE_ID'];
			}
		}

		$userIdList = array_unique($userIdList);
		$resourseIdList = array_unique($resourseIdList);

		if (!empty($userIdList))
		{
			$orm = UserTable::getList([
				'filter' => [
					'=ID' => $userIdList,
					'=ACTIVE' => 'Y'
				],
				'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'TITLE', 'PERSONAL_PHOTO']
			]);

			while ($user = $orm->fetch())
			{
				$user['URL'] = \CCalendar::getUserUrl($user["ID"]);
				$users[] = $user;
			}
		}

		if (!empty($resourseIdList))
		{
			$sectionList = Internals\SectionTable::getList(
				array(
					"filter" => array(
						"=ACTIVE" => 'Y',
						"!=CAL_TYPE" => ['user', 'group', 'company_calendar', 'company', 'calendar_company'],
						"ID" => $resourseIdList
					),
					"select" => array("ID", "CAL_TYPE", "NAME")
				)
			);

			while ($section = $sectionList->fetch())
			{
				$resources[$section['ID']] = $section;
				$resourceNames[] = $section['NAME'];
			}
		}

		$resListItems = [];
		if (!empty($users))
		{
			foreach($users as $user)
			{
				$resListItems[] = \CCalendar::getUserName($user);
			}
		}
		if (!empty($resourceNames))
		{
			foreach($resourceNames as $resourceName)
			{
				$resListItems[] = $resourceName;
			}
		}

		if (!empty($resListItems))
		{
			$skipTime = is_array($userField['SETTINGS']) && $userField['SETTINGS']['FULL_DAY'] === 'Y';
			$fromTs = isset($value['DATE_FROM']) ? \CCalendar::timestamp($value['DATE_FROM'], true, !$skipTime) : 0;
			$toTs = isset($value['DATE_TO']) ? \CCalendar::timestamp($value['DATE_TO'], true, !$skipTime) : 0;

			$resultText = \CCalendar::getFromToHtml($fromTs, $toTs, $skipTime, $toTs - $fromTs).': ';
			$resultText = str_replace("&ndash;", '-', $resultText);
			if(!empty($value['SERVICE_NAME']))
			{
				$resultText .= $value['SERVICE_NAME'].', ';
			}
			$resultText .= implode(', ', $resListItems);
		}
		return $resultText;
	}

	public static function getDefaultResourcesList()
	{
		$result = [];

		$typeList = Internals\TypeTable::getList(
			array(
				"filter" => array(
					"XML_ID" => self::RESOURCE_CALENDAR_TYPE
				),
				"select" => array("XML_ID", "NAME")
			)
		);

		while ($type = $typeList->fetch())
		{
			$type['SECTIONS'] = [];
			$result[$type['XML_ID']] = $type;
		}

		if (empty($result[self::RESOURCE_CALENDAR_TYPE] ?? null))
		{
			Internals\TypeTable::add([
				'XML_ID' => self::RESOURCE_CALENDAR_TYPE,
				'NAME' => self::RESOURCE_CALENDAR_TYPE,
				'ACTIVE' => 'Y'
			]);
			\CCalendar::ClearCache('type_list');

			$result[self::RESOURCE_CALENDAR_TYPE] = [
				'XML_ID' => self::RESOURCE_CALENDAR_TYPE,
				'NAME' =>  self::RESOURCE_CALENDAR_TYPE
			];
		}

		$sectionList = Internals\SectionTable::getList(
			array(
				"filter" => array(
					"=ACTIVE" => 'Y',
					"CAL_TYPE" => [self::RESOURCE_CALENDAR_TYPE],
					"!=NAME" => ''
				),
				"select" => array("ID", "CAL_TYPE", "NAME")
			)
		);

		while ($section = $sectionList->fetch())
		{
			if (is_array($result[$section['CAL_TYPE']]['SECTIONS']))
			{
				$result[$section['CAL_TYPE']]['SECTIONS'][] = $section;
			}
		}

		return $result;
	}

	protected static function fetchFieldValue($value)
	{
		$resourseList = Internals\ResourceTable::getList(
			array(
				"filter" => array(
					"=ID" => $value
				)
			)
		);

		$result = array(
			'ENTRIES' => []
		);

		while ($resourse = $resourseList->fetch())
		{
			if (!isset($result['DATE_FROM']))
			{
				\CTimeZone::Disable();
				$result['DATE_FROM'] = $resourse['DATE_FROM']->toString();
				$result['DATE_TO'] = $resourse['DATE_TO']->toString();
				$result['SERVICE_NAME'] = $resourse['SERVICE_NAME'];
				\CTimeZone::Enable();

				$fromTs = \CCalendar::timestamp($result['DATE_FROM']);
				$toTs = \CCalendar::timestamp($result['DATE_TO']);

				if (!$resourse['SKIP_TIME'])
				{
					$currentUserID = \CCalendar::getCurUserId();

					$userOffsetFrom = \CCalendar::getTimezoneOffset($resourse['TZ_FROM'], $fromTs) - \CCalendar::getCurrentOffsetUTC($currentUserID);
					$userOffsetTo = \CCalendar::getTimezoneOffset($resourse['TZ_TO'], $toTs) - \CCalendar::getCurrentOffsetUTC($currentUserID);

					$result['DATE_FROM'] = \CCalendar::date($fromTs - $userOffsetFrom);
					$result['DATE_TO'] = \CCalendar::date($toTs - $userOffsetTo);
				}
				else
				{
					$result['DATE_TO'] = \CCalendar::date($toTs + \CCalendar::DAY_LENGTH);
				}
			}

			$result['ENTRIES'][] = array(
				'ID' => $resourse['ID'],
				'EVENT_ID' => $resourse['EVENT_ID'],
				'TYPE' => $resourse['CAL_TYPE'],
				'RESOURCE_ID' => $resourse['RESOURCE_ID']
			);
		}

		return $result;
	}

	public static function getDefaultServiceList()
	{
		return [
			array('name' => '', 'duration' => 60)
		];
	}

	public static function getBitrx24Limitation()
	{
		$limit = -1;
		if (\Bitrix\Main\Loader::includeModule('bitrix24'))
		{
			$b24limit = Bitrix24\Feature::getVariable('calendar_resourcebooking_limit');
			if ($b24limit !== null)
			{
				return $b24limit;
			}

			//else: fallback
			$licenseType = \CBitrix24::getLicenseType();

			if ($licenseType === 'project' || $licenseType === 'self')
			{
				$limit = 6;
			}
			elseif ($licenseType === 'tf' || $licenseType === 'retail')
			{
				$limit = 12;
			}
			elseif ($licenseType === 'team' || $licenseType === 'start_2019')
			{
				$limit = 24;
			}
		}

		return $limit;
	}

	public static function getAvailableEntriesList()
	{
		return array('CRM_LEAD', 'CRM_DEAL');
	}

	public static function onBeforeUserTypeAdd(&$userTypeFields)
	{
		if ($userTypeFields['USER_TYPE_ID'] === 'resourcebooking')
		{
			$userTypeFields['MULTIPLE'] = 'Y';
		}
		return true;
	}

	public static function getResourceEntriesList($idList = [])
	{
		return self::fetchFieldValue($idList);
	}

	public static function getUserFieldByFieldName($fieldName = '', $selectedUsers = [])
	{
		$resultData = null;
		if ($fieldName)
		{
			$r = \CUserTypeEntity::getList(array("ID" => "ASC"), array("FIELD_NAME" => $fieldName));
			if ($r)
			{
				$resultData = $r->fetch();
			}
		}

		if (!is_array($selectedUsers))
		{
			$selectedUsers = [];
		}
		if (is_array($resultData) && isset($resultData['SETTINGS']['SELECTED_USERS']))
		{
			$selectedUsers = array_merge($selectedUsers, $resultData['SETTINGS']['SELECTED_USERS']);
		}

		array_walk($selectedUsers, 'intval');
		$selectedUsers = array_unique($selectedUsers);

		if (!empty($selectedUsers))
		{
			$orm = UserTable::getList([
				'filter' => [
					'=ID' => $selectedUsers,
					'=ACTIVE' => 'Y'
				],
				'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL']
			]);

			$resultData['SETTINGS']['USER_INDEX'] = [];
			while($user = $orm->fetch())
			{
				$resultData['SETTINGS']['USER_INDEX'][$user['ID']] = [
					'id' => $user['ID'],
					'displayName' => \CCalendar::getUserName($user)
				];
			}
		}

		return $resultData;
	}

	public static function getFillFormData($data = [], $params = [])
	{
		global $USER;
		$resultData = [];
		$curUserId = $USER->GetID();

		if (isset($params['from']) && $params['from'] instanceof Date
			&& isset($params['to']) && $params['to'] instanceof Date

		)
		{
			$from = $params['from']->toString();
			$to = $params['to']->toString();
		}
		else
		{
			$fromTs = (isset($params['from']) && $params['from']) ? \CCalendar::timestamp($params['from']) : time();
			$from = \CCalendar::date($fromTs, false);
			$to = (isset($params['to']) && $params['to'])
				? \CCalendar::date(\CCalendar::timestamp($params['to']), false)
				: \CCalendar::date($fromTs + \CCalendar::DAY_LENGTH * 60, false);
		}

		if (isset($params['timezone']))
		{
			$deltaOffset = \CCalendar::GetTimezoneOffset($params['timezone']) - \CCalendar::GetCurrentOffsetUTC($curUserId);
		}
		else
		{
			$deltaOffset = 0;
		}
		$resultData['timezoneOffset'] = 0;

		// Fetch fetch UF properties
		if ($params['fieldName'])
		{
			$r = \CUserTypeEntity::getList(array("ID" => "ASC"), array("FIELD_NAME" => $params['fieldName']));
			if ($r)
			{
				$fieldProperties = $r->fetch();
				$resultData['fieldSettings'] = $fieldProperties['SETTINGS'];

				if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
				{
					$resultData['timezoneOffset'] = $resultData['fieldSettings']['TIMEZONE'] ? \CCalendar::GetTimezoneOffset($resultData['fieldSettings']['TIMEZONE']) : intval(date("Z"));
					$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) : '');
				}
			}
		}

		if (isset($data['users']))
		{
			$userIdList = is_array($data['users']['value'])
				? $data['users']['value']
				: explode('|', $data['users']['value']);
			array_walk($userIdList, 'intval');

			$resultData['usersAccessibility'] = [];
			$accessibility = \CCalendar::getAccessibilityForUsers(array(
				'users' => $userIdList,
				'from' => $from, // date or datetime in UTC
				'to' => $to, // date or datetime in UTC
				'getFromHR' => true,
				'checkPermissions' => false
			));

			foreach($accessibility as $userId => $entries)
			{
				$resultData['usersAccessibility'][$userId] = [];

				foreach($entries as $entry)
				{
					if (isset($entry['DT_FROM']) && !isset($entry['DATE_FROM']))
					{
						$resultData['usersAccessibility'][$userId][] = array(
							'id' => $entry['ID'],
							'dateFrom' => $entry['DT_FROM'],
							'dateTo' => $entry['DT_TO'],
						);
					}
					else
					{
						$fromTs = \CCalendar::Timestamp($entry['DATE_FROM']);
						$toTs = \CCalendar::Timestamp($entry['DATE_TO']);

						if ($entry['DT_SKIP_TIME'] !== "Y")
						{
							if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
							{
								$fromTs -= \CCalendar::GetTimezoneOffset($entry['TZ_FROM']) - $resultData['timezoneOffset'];
								$toTs -= \CCalendar::GetTimezoneOffset($entry['TZ_TO']) - $resultData['timezoneOffset'];
							}
							else
							{
								$fromTs -= $entry['~USER_OFFSET_FROM'];
								$toTs -= $entry['~USER_OFFSET_TO'];
								$fromTs += $deltaOffset;
								$toTs += $deltaOffset;
							}
						}

						$resultData['usersAccessibility'][$userId][] = array(
							'id' => $entry['ID'],
							'dateFrom' => \CCalendar::Date($fromTs, $entry['DT_SKIP_TIME'] != 'Y'),
							'dateTo' => \CCalendar::Date($toTs, $entry['DT_SKIP_TIME'] != 'Y'),
							'fullDay' => $entry['DT_SKIP_TIME'] === "Y"
						);
					}
				}
			}

			// User Index
			$orm = UserTable::getList(
				[
					'filter' => [
						'=ID' => $userIdList,
						'=ACTIVE' => 'Y'
					],
					'select' => ['ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL']
				]
			);

			$resultData['SETTINGS']['USER_INDEX'] = [];
			while($user = $orm->fetch())
			{
				$resultData['userIndex'][$user['ID']] = [
					'id' => $user['ID'],
					'displayName' => \CCalendar::getUserName($user)
				];
			}
		}

		if (isset($data['resources']))
		{
			$resultData['resourcesAccessibility'] = [];

			$resourceIdList = is_array($data['resources']['value'])
				? $data['resources']['value']
				: explode('|', $data['resources']['value']);

			array_walk($resourceIdList, 'intval');

			$resEntries = \CCalendarEvent::getList(
				array(
					'arFilter' => array(
						"FROM_LIMIT" => $from,
						"TO_LIMIT" => $to,
						"CAL_TYPE" => 'resource',
						"ACTIVE_SECTION" => "Y",
						"SECTION" => $resourceIdList
					),
					'parseRecursion' => true,
					'setDefaultLimit' => false
				)
			);

			foreach($resEntries as $row)
			{
				$fromTs = \CCalendar::timestamp($row["DATE_FROM"]);
				$toTs = \CCalendar::timestamp($row['DATE_TO']);

				if ($row['DT_SKIP_TIME'] !== "Y" && $resultData['fieldSettings']['USE_USER_TIMEZONE'] !== 'N')
				{
					if ($resultData['fieldSettings']['USE_USER_TIMEZONE'] === 'N')
					{
						$fromTs -= \CCalendar::GetTimezoneOffset($entry['TZ_FROM']) - $resultData['timezoneOffset'];
						$toTs -= \CCalendar::GetTimezoneOffset($entry['TZ_TO']) - $resultData['timezoneOffset'];
					}
					else
					{
						$fromTs -= $row['~USER_OFFSET_FROM'];
						$toTs -= $row['~USER_OFFSET_TO'];
						$fromTs += $deltaOffset;
						$toTs += $deltaOffset;
					}
				}

				$resultData['resourcesAccessibility'][$row['SECT_ID']][] = array(
					'id' => $row["ID"],
					'dateFrom' => \CCalendar::date($fromTs, $row['DT_SKIP_TIME'] !== 'Y'),
					'dateTo' => \CCalendar::date($toTs, $row['DT_SKIP_TIME'] !== 'Y'),
					'fullDay' => $row['DT_SKIP_TIME'] === "Y"
				);
			}
		}

		$resultData['workTimeStart'] = floor(floatVal(\COption::GetOptionString('calendar', 'work_time_start', 9)));
		$resultData['workTimeEnd'] = ceil(floatVal(\COption::GetOptionString('calendar', 'work_time_end', 19)));

		return $resultData;
	}

	public static function getFormDateTimeSlots($fieldName = '', $options = [])
	{
		$from = (isset($options['from']) && $options['from'] instanceof Date) ? $options['from'] : new Date();
		if (isset($options['to']) && ($options['to'] instanceof Date))
		{
			$to = $options['to'];
		}
		else
		{
			$to = clone $from;
			$to->add($options['dateInterval'] ?? 'P5D');
		}

		$formData = \Bitrix\Calendar\UserField\ResourceBooking::getFillFormData(
			$options['settingsData'],
			[
				'fieldName' => $fieldName,
				'from' => $from,
				'to' => $to
			]
		);

		// Merge Accessibility
		$accessibility = [];
		if ($formData['fieldSettings']['USE_USERS'] === 'Y'
			&& isset($options['settingsData']['users']['value']))
		{
			$selectedUser = $options['settingsData']['users']['value'];
			if (
				isset($formData['usersAccessibility'])
				&& isset($formData['usersAccessibility'][$selectedUser])
			)
			{
				$accessibility = array_merge($accessibility, $formData['usersAccessibility'][$selectedUser]);
			}
		}

		if ($formData['fieldSettings']['USE_RESOURCES'] === 'Y'
			&& isset($options['settingsData']['resources']['value']))
		{
			$selectedResource = $options['settingsData']['resources']['value'];
			if (
				isset($formData['resourcesAccessibility'])
				&& isset($formData['resourcesAccessibility'][$selectedResource])
			)
			{
				$accessibility = array_merge($accessibility, $formData['resourcesAccessibility'][$selectedResource]);
			}
		}

		$result = null;
		if ($selectedUser || $selectedResource)
		{
			$format = Date::convertFormatToPhp(FORMAT_DATETIME);
			foreach ($accessibility as $i => $item)
			{
				$accessibility[$i]['fromTs'] = (new DateTime($item['dateFrom'], $format))->getTimestamp();
				$accessibility[$i]['toTs'] = (new DateTime($item['dateTo'], $format))->getTimestamp();
			}

			$result = self::getAvailableTimeSlots($accessibility, [
				'from' => $from,
				'to' => $to,
				'scale' => $options['settingsData']['time']['scale']
			]);
		}

		return $result;
	}

	private static function getAvailableTimeSlots($accessibility, $options)
	{
		$from = (isset($options['from']) && $options['from'] instanceof Date) ? $options['from'] : new Date();
		$to = (isset($options['to']) && $options['to'] instanceof Date) ? $options['to'] : new Date();
		$to->add('P1D');
		$scale = (int)$options['scale'] > 0 ? (int)$options['scale'] : 60;

		$workTimeStart = (int)\COption::getOptionString('calendar', 'work_time_start', 9);
		$workTimeEnd = (int)\COption::getOptionString('calendar', 'work_time_end', 19);

		$step = 0;
		$currentDate = new DateTime($from->toString(), Date::convertFormatToPhp(FORMAT_DATETIME));
		$slots = [];

		while ($currentDate->getTimestamp() < $to->getTimestamp())
		{
			$currentDate->setTime($workTimeStart, 0, 0);
			while ((int)$currentDate->format('H') < $workTimeEnd)
			{
				if ($currentDate->getTimestamp() > time())
				{
					$isFree = true;
					$slotStart = $currentDate->getTimestamp();
					$slotEnd = $slotStart + $scale * 60;

					foreach ($accessibility as $i => $item)
					{
						if ($item['toTs'] > $slotStart && $item['fromTs'] < $slotEnd)
						{
							$isFree = false;
							break;
						}
					}

					if ($isFree)
					{
						$slots[] = clone $currentDate;
					}
				}
				$currentDate->add('PT'.$scale.'M');
				$step++;
			}

			if($step > 1000)
			{
				break;
			}
			$currentDate->add('P1D');
		}

		return $slots;
	}

	public static function prepareFormDateValues($dateFrom = null, $fieldName = '', $options = [])
	{
		$result = [];
		if (!isset($dateFrom) || !($dateFrom instanceof DateTime))
		{
			throw new \Bitrix\Main\SystemException('Wrong dateFrom value type. DateTime expected');
		}
		if (empty($fieldName))
		{
			throw new \Bitrix\Main\SystemException('Wrong fieldName given');
		}

		$duration = 60;
		if (!empty($options['settingsData']['duration']['value']))
		{
			$duration = $options['settingsData']['duration']['value'];
		}
		else if (!empty($options['settingsData']['duration']['defaultValue']))
		{
			$duration = $options['settingsData']['duration']['defaultValue'];
		}

		$r = \CUserTypeEntity::getList(["ID" => "ASC"], ["FIELD_NAME" => $fieldName]);
		if ($r)
		{
			$fieldProperties = $r->fetch();
			$fieldSettings = $fieldProperties['SETTINGS'];

			if ($fieldSettings['USE_USERS'] === 'Y'
				&& isset($options['settingsData']['users']['value']))
			{
				$result[] = self::prepareValue('user', $options['settingsData']['users']['value'], $dateFrom->toString(), $duration);
			}

			if ($fieldSettings['USE_RESOURCES'] === 'Y'
				&& isset($options['settingsData']['resources']['value']))
			{
				$result[] = self::prepareValue('resource', $options['settingsData']['resources']['value'], $dateFrom->toString(), $duration);
			}
		}

		return $result;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit