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/ilovecveti.ru/bitrix/modules/calendar/lib/sync/managers/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/ilovecveti.ru/bitrix/modules/calendar/lib/sync/managers/outgoingmanager.php
<?php

namespace Bitrix\Calendar\Sync\Managers;

use Bitrix\Calendar\Core\Event\Event;
use Bitrix\Calendar\Core;
use Bitrix\Calendar\Sync;
use Bitrix\Calendar\Internals\EO_Event;
use Bitrix\Calendar\Internals\EO_Event_Collection;
use Bitrix\Calendar\Internals\EO_EventConnection;
use Bitrix\Calendar\Internals\EventConnectionTable;
use Bitrix\Calendar\Internals\EventTable;
use Bitrix\Calendar\Internals\SectionTable;
use Bitrix\Calendar\Sync\Connection\Connection;
use Bitrix\Calendar\Sync\Connection\EventConnection;
use Bitrix\Calendar\Sync\Connection\SectionConnection;
use Bitrix\Calendar\Sync\Entities\InstanceMap;
use Bitrix\Calendar\Sync\Entities\SyncEvent;
use Bitrix\Calendar\Sync\Exceptions\ApiException;
use Bitrix\Calendar\Sync\Factories\FactoryBuilder;
use Bitrix\Calendar\Sync\Factories\FactoryInterface;
use Bitrix\Calendar\Internals\SectionConnectionTable;
use Bitrix\Calendar\Sync\Util\Context;
use Bitrix\Calendar\Sync\Util\EventContext;
use Bitrix\Calendar\Sync\Util\Result;
use Bitrix\Calendar\Sync\Util\SectionContext;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\DB\SqlQueryException;
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Entity\ReferenceField;
use Bitrix\Main\Error;
use Bitrix\Main\ObjectException;
use Bitrix\Main\ObjectNotFoundException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\SystemException;
use Exception;
use Generator;

class OutgoingManager
{
	const TIME_SLICE = 2600000;

	private Connection $connection;

	private FactoryInterface $factory;

	private VendorSynchronization $syncManager;
	/**
	 * @var Core\Mappers\Factory
	 */
	private Core\Mappers\Factory $mapperFactory;

	/**
	 * @throws ObjectNotFoundException
	 */
	public function __construct(Connection $connection)
	{
		$this->connection = $connection;
		$context = new Context(['connection' => $connection]);
		$this->factory = FactoryBuilder::create($connection->getVendor()->getCode(), $connection, $context);
		$this->syncManager = new VendorSynchronization($this->factory);
		$this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
	}

	/**
	 * @param array $params
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public function exportSections(array $params = []): Result
	{
		$mainResult = new Result();
		$resultData = [
			'links' => [
				'exported' => [],
				'updated' => [],
				'skipped' => [],
				'error' => [],
			],
			'exportErr' => [],
		];

		$excludeIds = $params['excludeIds'] ?? [];
		/** @var Core\Section\Section $section */
		foreach ($this->fetchSections($excludeIds) as $section)
		{
			$sectionResult = $this->exportSectionSimple($section);
			if ($sectionResult->isSuccess())
			{
				foreach ($sectionResult->getData() as $key => $link)
				{
					$resultData['links'][$key][] = $link;
				}
				$resultData['exported'][] = $section->getId();
			}
			else
			{
				$mainResult->addErrors($sectionResult->getErrors());
				$resultData['exportErr'][] = $section->getId();
			}
		}

		return $mainResult->setData($resultData);
	}

	/**
	 * @param SectionConnection $sectionLink
	 * @param array $excludeEventIds
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public function exportSectionEvents(SectionConnection $sectionLink, array $excludeEventIds = []): Result
	{
		$result = new Result();
		$resultData = [
			'events' => [
				'deleted' => [],
				'exported' => [],
				'updated' => [],
				'stripped' => [],
				'error' => [],
			]
		];

		$pushResult = static function(array $result) use (&$resultData)
		{
			if (empty($result['entityType']) || empty($result['entity']))
			{
				return;
			}
			if ($result['entityType'] === 'link')
			{
				$resultData['events'][$result['action']] = $result['entity']->getEvent()->getId();
			}
			elseif ($result['entityType'] === 'eventId')
			{
				$resultData['events'][$result['action']] = $result['entity'];
			}
		};

		foreach ($this->fetchSectionEvents($sectionLink->getSection(), $excludeEventIds) as $eventPack)
		{
			$exportResult = $this->exportEvent($eventPack, $sectionLink);
			if ($exportResult->isSuccess())
			{
				$pushResult($exportResult->getData());
			}
			else
			{
				$id = null;

				if ($eventPack['event'])
				{
					$id = $eventPack['event']->getId();
				}
				else if ($eventPack['master'])
				{
					$id = $eventPack['master']->getId();
				}
				$resultData['events']['error'] = $id;
			}
		}

		return $result->setData($resultData);
	}

	/**
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 *
	 */
	public function export(): Result
	{
		$mainResult = new Result();
		$resultData = [];

		foreach ($this->fetchSections() as $section)
		{
			$sectionResult = $this->exportSection($section);
			if (!$sectionResult->isSuccess())
			{
				continue;
			}

			/** @var SectionConnection $sectionLink */
			$sectionLink = $sectionResult->getData()['sectionConnection'] ?? null;

			if ($sectionLink)
			{
				foreach ($this->fetchSectionEvents($section) as $event)
				{
					$this->exportEvent($event, $sectionLink);
				}
			}
			else
			{
				throw new ObjectPropertyException('Context object das not have sectionLink information');
			}
		}

		return $mainResult->setData($resultData);
	}

	/**
	 * @param Core\Section\Section $section
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 * @throws Exception
	 *
	 */
	public function exportSection(Core\Section\Section $section): Result
	{
		$result = new Result();
		try
		{
			$sectionContext = new SectionContext();
			if ($link = $this->getSectionLink($section))
			{
				if ($link->isActive())
				{
					$result = $this->syncManager->updateSection($section, $sectionContext);
					if ($result->isSuccess())
					{
						$result->setData(array_merge($result->getData(), [
							'sectionConnection' => $link,
						]));
					}
					else if (
						!$result->isSuccess()
						&& $result->getData()['error'] === 404
						&& $section->getExternalType() === 'local'
					)
					{
						$this->deleteSectionConnection($link);
						$sectionContext->setSectionConnection(null);
						$result = $this->syncManager->createSection($section, $sectionContext);
					}
				}
			}
			else
			{
				$result = $this->syncManager->createSection($section, $sectionContext);
			}
		}
		catch (Core\Base\BaseException $exception)
		{
			$result->addError(new Error($exception->getMessage()));
		}
		finally
		{
			return $result;
		}
	}

	/**
	 * @param Core\Section\Section $section
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws Core\Base\BaseException
	 * @throws ObjectPropertyException //     * @throws SystemException
	 * @throws SystemException
	 * @throws ApiException
	 * @throws Exception
	 */
	public function exportSectionSimple(Core\Section\Section $section): Result
	{
		$resultData = [];
		$mainResult = new Result();
		$sectionContext = new SectionContext();

		try
		{
			if ($link = $this->getSectionLink($section))
			{
				$sectionContext->setSectionConnection($link);
				if ($link->isActive())
				{
					$result = $this->syncManager->updateSection($section, $sectionContext);
					if ($result->isSuccess())
					{
						$resultData['updated'] = $link;
					}
					else
					{
						$resultData['error'] = $link;
					}
				}
				else
				{
					$resultData['skipped'] = $link;
				}
			}
			elseif ($section->getExternalType() !== Core\Section\Section::LOCAL_EXTERNAL_TYPE)
			{
				$resultData['skipped'] = $section;
			}
			else
			{
				$result = $this->syncManager->createSection($section, $sectionContext);
				if (!empty($result->getData()['sectionConnection']))
				{
					$resultData['exported'] = $result->getData()['sectionConnection'];
				}
				else
				{
					$result->addError(new Error('Error of export section'));
					$resultData['section'] = $section;
				}
			}

			$mainResult->setData($resultData);
		}
		catch (Core\Base\BaseException $exception)
		{
			$mainResult->addError(new Error($exception->getMessage()));
		}
		finally
		{
			return $mainResult;
		}
	}

	/**
	 * @param array $exclude
	 *
	 * @return Generator|\Bitrix\Calendar\Core\Section\Section[]
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private function fetchSections(array $exclude = []): Generator
	{
		$sectionList = SectionTable::getList([
			'filter' => [
				'=CAL_TYPE' => $this->connection->getOwner()->getType(),
				'OWNER_ID' => $this->connection->getOwner()->getId(),
				'EXTERNAL_TYPE' => ['local', $this->connection->getVendor()->getCode()],
				'!ID' => $exclude
			],
			'select' => ['*'],
		]);

		$mapper = new Core\Mappers\Section();
		while ($sectionDM = $sectionList->fetchObject())
		{
			$section = $mapper->getByEntityObject($sectionDM);
			yield $section;
		}
	}

	/**
	 * @param \Bitrix\Calendar\Core\Section\Section $section
	 *
	 * @return SectionConnection|null
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private function getSectionLink(\Bitrix\Calendar\Core\Section\Section $section): ?SectionConnection
	{
		$mapper = new Core\Mappers\SectionConnection();
		$map = $mapper->getMap([
			'=SECTION_ID' => $section->getId(),
			'=CONNECTION_ID' => $this->connection->getId(),
		]);

		return match ($map->count())
		{
			0 => null,
			1 => $map->fetch(),
			default => throw new Core\Base\BaseException('More than 1 SectionConnections found.'),
		};
	}

	/**
	 * @return FactoryInterface
	 */
	private function getFactory(): FactoryInterface
	{
		return $this->factory;
	}

	/**
	 * @param Core\Section\Section $section
	 * @param array $excludeEventIds
	 *
	 * @return Generator|Event[]
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private function fetchSectionEvents(Core\Section\Section $section, array $excludeEventIds = []): Generator
	{
		$timestamp = time() - self::TIME_SLICE;
		$eventList = EventTable::getList([
			'select' => [
				'*',
				'LINK.*',
				'LINK.CONNECTION',
			],
			'filter' => [
				'=SECTION_ID' => $section->getId(),
				'=DELETED' => 'N',
				'!ID' => $excludeEventIds,
				'>DATE_TO_TS_UTC' => $timestamp,
				'!=MEETING_STATUS' => 'N',
			],
			'runtime' => [
				new ReferenceField(
					'LINK',
					EventConnectionTable::class,
					[
						'=this.ID' => 'ref.EVENT_ID',
						'ref.CONNECTION_ID' => ['?', $this->connection->getId()],
					],
					['join_type' => 'LEFT']
				),
			],
		])->fetchCollection();

		$eventList = $this->prepareEvents($eventList);

		foreach ($eventList as $eventPack)
		{
			yield $eventPack;
		}
	}

	private function prepareEvents(EO_Event_Collection $events): array
	{
		$result = [];

		foreach ($events as $event)
		{
			if ($event->getRrule())
			{
				$recId = $event->getParentId();
				$result[$recId]['type'] = 'recurrence';
				$result[$recId]['master'] = $event;
			}
			else if ($event->getRecurrenceId())
			{
				$result[$event->getRecurrenceId()]['instances'][] = $event;
			}
			else
			{
				$result[$event->getId()] = [
					'type' => 'single',
					'event' => $event,
				];
			}
		}

		return $result;
	}

	/**
	 * @param EO_Event $eventEntity
	 *
	 * @return EventConnection|null
	 * @throws ArgumentException
	 */
	private function getEventLink(EO_Event $eventEntity): ?EventConnection
	{
		/** @var EO_EventConnection $link */
		if ($link = $eventEntity->get('LINK'))
		{
			$link->setEvent($eventEntity);

			return $this->mapperFactory->getEventConnection()->getByEntityObject($link);
		}

		return null;
	}

	/**
	 * @param array $eventPack
	 * @param SectionConnection $sectionLink
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 * @throws Exception
	 */
	private function exportEvent(array $eventPack, SectionConnection $sectionLink): Result
	{
		if (!empty($eventPack['master']))
		{
			return $this->exportRecurrenceEvent($sectionLink, $eventPack);
		}

		if (!empty($eventPack['event']))
		{
			return $this->exportSimpleEvent($sectionLink, $eventPack['event']);
		}

		return (new Result())->addError(new Error('Unsupported eventpack format'));
	}

	/**
	 * @param SectionConnection $link
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws ObjectException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 *
	 * @todo move this logic to own separated class
	 */
	public function subscribeSection(SectionConnection $link): Result
	{
		$mainResult = new Result();
		if ($this->syncManager->canSubscribeSection())
		{
			$pushManager = new Sync\Managers\PushManager();
			$subscription = $pushManager->getPush(
				PushManager::TYPE_SECTION_CONNECTION,
				$link->getId()
			);
			if ($subscription && !$subscription->isExpired())
			{
				$result = $this->syncManager->renewPush($subscription);
				if ($result->isSuccess())
				{
					$mainResult = $pushManager->renewPush($subscription, $result->getData());
				}
				else
				{
					$mainResult->addError(new Error('Error of renew subscription.'));
					$mainResult->addErrors($result->getErrors());
				}
			}
			else
			{
				$subscribeResult = $this->syncManager->subscribeSection($link);
				if ($subscribeResult->isSuccess())
				{
					if ($subscription)
					{
						$pushManager->renewPush($subscription, $subscribeResult->getData());
					}
					else
					{
						try
						{
							$pushManager->addPush(
								'SECTION_CONNECTION',
								$link->getId(),
								$subscribeResult->getData()
							);
						}
						catch(SqlQueryException $e)
						{
							if (
								$e->getCode() === 400
								&& substr($e->getDatabaseMessage(), 0, 6) === '(1062)')
							{
								// there's a race situation
							}
							else
							{
								throw $e;
							}
						}

					}
				}
				else
				{
					$mainResult->addError(new Error('Error of add subscription.'));
					$mainResult->addErrors($subscribeResult->getErrors());
				}
			}
		}

		return $mainResult;
	}

	/**
	 * @throws ArgumentException
	 * @throws ObjectException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public function subscribeConnection(): Result
	{
		$mainResult = new Result();
		if ($this->syncManager->canSubscribeConnection())
		{
			$pushManager = new Sync\Managers\PushManager();
			$subscription = $pushManager->getPush(
				PushManager::TYPE_CONNECTION,
				$this->connection->getId()
			);

			if ($subscription && !$subscription->isExpired())
			{
				$result = $this->syncManager->renewPush($subscription);
				if ($result->isSuccess())
				{
					$mainResult = $pushManager->renewPush($subscription, $result->getData());
				}
				else
				{
					$mainResult->addError(new Error('Error of renew subscription.'));
					$mainResult->addErrors($result->getErrors());
				}
			}
			else
			{
				$subscribeResult = $this->syncManager->subscribeConnection();
				if ($subscribeResult->isSuccess())
				{
					if ($subscription !== null)
					{
						$pushManager->renewPush($subscription, $subscribeResult->getData());
					}
					else
					{
						$pushManager->addPush(
							'CONNECTION',
							$this->connection->getId(),
							$subscribeResult->getData()
						);
					}
				}
				else
				{
					$mainResult->addError(new Error('Error of add subscription.'));
					$mainResult->addErrors($subscribeResult->getErrors());
				}
			}
		}

		return $mainResult;
	}

	/**
	 * @param SectionConnection $link
	 *
	 * @return void
	 * @throws Exception
	 */
	private function deleteSectionConnection(SectionConnection $link): void
	{
		global $DB;

		if ($link->getConnection()->getId() && $link->getSection()->getId())
		{
			$DB->Query("
				DELETE FROM b_calendar_event_connection
				WHERE CONNECTION_ID = " . (int)$link->getConnection()->getId() ." 
				AND EVENT_ID IN (SELECT EV.ID FROM b_calendar_event EV
		        WHERE EV.SECTION_ID = " . (int)$link->getSection()->getId() . ");"
			);
		}

		if ($link->getId())
		{
			SectionConnectionTable::delete($link->getId());
		}
	}

	/**
	 * @param SectionConnection $sectionLink
	 * @param EO_Event $eventData
	 *
	 * @return Result
	 *
	 * @throws ArgumentException
	 * @throws Core\Base\BaseException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private function exportSimpleEvent(SectionConnection $sectionLink, EO_Event $eventData): Result
	{
		$result = new Result();
		$context = new Context([]);
		$context->add('sync', 'sectionLink', $sectionLink);

		$event = $this->mapperFactory->getEvent()->getByEntityObject($eventData);

		$eventLink = $this->getEventLink($eventData);
		$eventContext = (new EventContext())
			->merge($context)
			->setSectionConnection($sectionLink)
			->setEventConnection($eventLink)
		;

		if ($eventLink && !$eventLink->getVendorEventId())
		{
			$this->mapperFactory->getEventConnection()->delete($eventLink);
			$eventLink = null;
		}

		if ($event && $eventLink)
		{
			$resultUpdate = $this->syncManager->updateEvent($event, $eventContext);
			$resultData = [
				'entityType' => 'link',
				'entity' => $eventLink,
				'action' => 'updated',
			];
			if (!$resultUpdate->isSuccess())
			{
				$result->addErrors($resultUpdate->getErrors());
			}
		}
		else
		{
			$resultAdd = $this->syncManager->createEvent($event, $eventContext);
			$resultData = [
				'entityType' => 'link',
				'action' => 'exported',
			];
			if ($resultAdd->isSuccess())
			{
				if (!empty($resultAdd->getData()['eventConnectionId']))
				{
					$resultData['entity'] = $this->mapperFactory->getEventConnection()
						->getById($resultAdd->getData()['eventConnectionId'])
					;
				}
				else
				{
					$resultData['entity'] = $this->mapperFactory->getEventConnection()->getMap([
						'=EVENT_ID' => $event->getId(),
						'=CONNECTION_ID' =>  $sectionLink->getConnection()->getId(),
					])->fetch();
				}
			}
			else
			{
				$resultData = [
					'entityType' => 'errorLink',
					'action' => 'exported',
				];
				$result->addErrors($resultAdd->getErrors());
			}
		}

		return $result->setData($resultData);
	}

	/**
	 * @param SectionConnection $sectionLink
	 * @param array $eventData
	 *
	 * @return Result
	 * @throws Exception
	 */
	private function exportRecurrenceEvent(SectionConnection $sectionLink, array $eventData): Result
	{
		$context = new EventContext();
		$context->setSectionConnection($sectionLink);

		$recurrenceEvent = $this->buildRecurrenceEvent($eventData);

		if ($recurrenceEvent->getEventConnection())
		{
			$context->setEventConnection($recurrenceEvent->getEventConnection());
			$result = $this->syncManager->updateRecurrence($recurrenceEvent, $context);
		}
		else
		{
			$result = $this->syncManager->createRecurrence($recurrenceEvent, $context);
		}

		$resultData = [
			'entityType' => 'link',
			'action' => 'updated',
		];

		return $result->setData($resultData);
	}

	/**
	 * @param array $eventData
	 *
	 * @return SyncEvent
	 * @throws ArgumentException
	 */
	private function buildRecurrenceEvent(array $eventData): SyncEvent
	{
		$masterEvent = $this->mapperFactory->getEvent()->getByEntityObject($eventData['master']);
		$masterLink = $this->getEventLink($eventData['master']);
		$masterSyncEvent = (new SyncEvent())
			->setEvent($masterEvent)
			->setEventConnection($masterLink)
		;
		if ($masterSyncEvent->getEventConnection() && !$masterSyncEvent->getEventConnection()->getVendorEventId())
		{
			$this->mapperFactory->getEventConnection()->delete($masterSyncEvent->getEventConnection());
			$masterSyncEvent->setEventConnection(null);
		}

		$instancesCollection = new InstanceMap();
		$instances = $eventData['instances'] ?? [];
		foreach ($instances as $instance)
		{
			$instanceEvent = $this->mapperFactory->getEvent()->getByEntityObject($instance);
			$instanceLink = $this->getEventLink($instance);
			$instanceSyncEvent = (new SyncEvent())
				->setEvent($instanceEvent)
				->setEventConnection($instanceLink)
			;

			if ($instanceSyncEvent->getEventConnection() && !$instanceSyncEvent->getEventConnection()->getVendorEventId())
			{
				$this->mapperFactory->getEventConnection()->delete($instanceSyncEvent->getEventConnection());
				$instanceSyncEvent->setEventConnection(null);
			}
			$instancesCollection->add($instanceSyncEvent);
		}

		$masterSyncEvent->setInstanceMap($instancesCollection);

		return $masterSyncEvent;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit