Your IP : 18.117.73.46


Current Path : /home/bitrix/ext_www/coffe.land/bitrix/modules/calendar/lib/sync/
Upload File :
Current File : /home/bitrix/ext_www/coffe.land/bitrix/modules/calendar/lib/sync/googleapipush.php

<?php

namespace Bitrix\Calendar\Sync;

use Bitrix\Main\Type;
use \Bitrix\Calendar\PushTable;

final class GoogleApiPush
{
	const RENEW_LIMIT = 3;
	const CREATE_LIMIT = 2;
	const PROCESS_LIMIT = 4;
	const CLEAR_LIMIT = 6;
	/**
	 * Checks connection and ability to create push channel
	 * Recommended agent interval = 4h
	 */
	public static function renewWatchChannels()
	{
		global $DB;
		$result =  $DB->query("SELECT * FROM b_calendar_push WHERE " .
			\CDatabaseMysql::dateFormatToDB(FORMAT_DATETIME, 'EXPIRES') .
			" <= '" .
			\Bitrix\Main\Type\DateTime::createFromTimestamp(strtotime('+1 day')) .
			"' ORDER BY EXPIRES ASC LIMIT " . self::RENEW_LIMIT);

		$pushRows = array();
		$connectionIds = array();
		$sectionIds = array();

		while ($row = $result->fetch())
		{
			$pushRows[] = $row;
			if ($row['ENTITY_TYPE'] == 'CONNECTION')
			{
				$connectionIds[] = $row['ENTITY_ID'];
			}
			if ($row['ENTITY_TYPE'] == 'SECTION')
			{
				$sectionIds[] = $row['ENTITY_ID'];
			}
		}

		if (!empty($pushRows))
		{
			global $DB;
			$sections = array();
			$connections = array();
			if (!empty($sectionIds))
			{
				$sectionResult =  $DB->query("SELECT * FROM b_calendar_section WHERE ID IN (" . implode(',', $sectionIds) . ")");
				while($row = $sectionResult->fetch())
				{
					$sections[$row['ID']] = $row;
				}
			}

			if (!empty($connectionIds))
			{
				$connectionResult =  $DB->query("SELECT * FROM b_dav_connections WHERE ID IN (" . implode(',', $connectionIds) . ")");
				while($row = $connectionResult->fetch())
				{
					$connections[$row['ID']] = $row;
				}
			}

			foreach ($pushRows as $row)
			{
				if ($row['ENTITY_TYPE'] == 'CONNECTION' && !empty($connections[$row['ENTITY_ID']]))
				{
					$connectionData = $connections[$row['ENTITY_ID']];
					$googleApiConnection = new GoogleApiSync($connectionData['ENTITY_ID']);
					$channelInfo = $googleApiConnection->startWatchCalendarList($connectionData['NAME']);
				}
				elseif ($row['ENTITY_TYPE'] == 'SECTION' && !empty($sections[$row['ENTITY_ID']]))
				{
					$section = $sections[$row['ENTITY_ID']];
					$googleApiConnection = new GoogleApiSync($section['OWNER_ID']);
					$channelInfo = $googleApiConnection->startWatchEventsChannel($section['GAPI_CALENDAR_ID']);
				}
				else
				{
					continue;
				}

				$googleApiConnection->stopChannel($row['CHANNEL_ID'], $row['RESOURCE_ID']);
				PushTable::delete(array('ENTITY_TYPE' => $row['ENTITY_TYPE'], 'ENTITY_ID' => $row['ENTITY_ID']));

				if ($channelInfo)
				{
					PushTable::add(array(
						'ENTITY_TYPE' => $row['ENTITY_TYPE'],
						'ENTITY_ID' => $row['ENTITY_ID'],
						'CHANNEL_ID' => $channelInfo['id'],
						'RESOURCE_ID' => $channelInfo['resourceId'],
						'EXPIRES' => $channelInfo['expiration'],
						'NOT_PROCESSED' => 'N'
					));
				}
			}
		}

		if (count($pushRows) < 4)
		{
			$result = PushTable::getList(array(
				'order'  => array('EXPIRES' => 'ASC'),
				'limit'	 => 1
			));

			$row = $result->fetch();
			if ($row)
			{
				$nextAgentDate = \Bitrix\Main\Type\DateTime::createFromTimestamp(strtotime($row['EXPIRES']) - (60*60*20))->format(\Bitrix\Main\Type\Date::convertFormatToPhp(FORMAT_DATETIME));
			}
			else
			{
				$nextAgentDate = \Bitrix\Main\Type\DateTime::createFromTimestamp(strtotime('now') + (60*60*20))->format(\Bitrix\Main\Type\Date::convertFormatToPhp(FORMAT_DATETIME));
			}

			\CAgent::removeAgent("\\Bitrix\\Calendar\\Sync\\GoogleApiPush::renewWatchChannels();", "calendar");
			\CAgent::addAgent("\\Bitrix\\Calendar\\Sync\\GoogleApiPush::renewWatchChannels();", "calendar", "N", 900,"", "Y", $nextAgentDate);
			return false;
		}

		return "\\Bitrix\\Calendar\\Sync\\GoogleApiPush::renewWatchChannels();";
	}

	public static function checkSectionsPush($localSections, $userId)
	{
		$googleApiConnection = new GoogleApiSync($userId);
		//Create new channels and refresh old push channels for sections of current connection
		$sectionIds = array();
		foreach ($localSections as $section)
		{
			//Skip virtual calendars, because they are not pushable.
			if (preg_match('/(holiday.calendar.google.com)/', $section['GAPI_CALENDAR_ID']) ||
				preg_match('/(group.v.calendar.google.com)/', $section['GAPI_CALENDAR_ID']) ||
				preg_match('/(@virtual)/', $section['GAPI_CALENDAR_ID']))
				continue;
			$sectionIds[] = $section['ID'];
		}

		$sectionsIn = implode(',', $sectionIds);
		$pushChannels = PushTable::getList(array(
			'filter' => array('=ENTITY_TYPE' => 'SECTION', '@ IN (' . $sectionsIn . ')'),
		));
		$inactiveSections = array_flip($sectionIds);

		while($row = $pushChannels->fetch())
		{
			$diff = strtotime($row['EXPIRES']) - strtotime('now');
			if ($diff > GoogleApiSync::ONE_DAY)
			{
				unset($inactiveSections[$row['ENTITY_ID']]);
				continue;
			}
			$googleApiConnection->stopChannel($row['CHANNEL_ID'], $row['RESOURCE_ID']);
			$localCalendarIndex = array_search($row['ENTITY_ID'], array_column($localSections, 'ID'));
			if ($localCalendarIndex !== false)
			{
				$channelInfo = $googleApiConnection->startWatchCalendarList($localSections[$localCalendarIndex]['GAPI_CALENDAR_ID']);

				if ($channelInfo)
				{
					PushTable::update(
						array(
							'ENTITY_TYPE' => $row['ENTITY_TYPE'],
							'ENTITY_ID' => $row['ENTITY_ID']
						),
						array(
							'CHANNEL_ID' => $channelInfo['id'],
							'RESOURCE_ID' => $channelInfo['resourceId'],
							'EXPIRES' => $channelInfo['expiration'],
							'NOT_PROCESSED' => 'N'
						)
					);
					continue;
				}
			}

			//If we can't create channel or section deleted - we should remove it from sync channels.
			//It will be recreated at daily agent, if possible.
			PushTable::delete(array('ENTITY_TYPE' => $row['ENTITY_TYPE'], 'ENTITY_ID' => $row['ENTITY_ID']));
			unset($inactiveSections[$row['ENTITY_ID']]);
		}

		foreach ($localSections as $section)
		{
			if (isset($inactiveSections[$section['ID']]))
			{
				$channelInfo = $googleApiConnection->startWatchEventsChannel($section['GAPI_CALENDAR_ID']);
				if ($channelInfo && isset($channelInfo['id'], $channelInfo['resourceId']))
				{
					PushTable::delete(array("ENTITY_TYPE" => 'SECTION', 'ENTITY_ID' => $section['ID']));
					PushTable::add(array(
						'ENTITY_TYPE' => 'SECTION',
						'ENTITY_ID' => $section['ID'],
						'CHANNEL_ID' => $channelInfo['id'],
						'RESOURCE_ID' => $channelInfo['resourceId'],
						'EXPIRES' => $channelInfo['expiration'],
						'NOT_PROCESSED' => 'N'
					));
				}
			}
		}
	}

	/**
	 * Creates and renew watch channels for connections
	 * Recommended interval = 1 hour, for push enable - recommended interval 3 minutes
	 *
	 * @param int $start
	 * @return string
	 */
	public static function createWatchChannels($start = 0)
	{
		$pushOptionEnabled = \COption::getOptionString('calendar', 'sync_by_push', false);
		if (!$pushOptionEnabled && !\CCalendar::isBitrix24())
		{
			return null;
		}

		$lastId = $start;
		\Bitrix\Main\Loader::includeModule('dav');
		$davConnections = \CDavConnection::getList(
			array("ID" => "ASC"),
			array(
				'ACCOUNT_TYPE' => 'google_api_oauth',
				'>ID' => $start
			),
			false,
			array('nTopCount' => self::CREATE_LIMIT)
		);

		$connections = array();
		$pushConnectionIds = array();

		while($row = $davConnections->fetch())
		{
			$lastId = $row['ID'];
			$connections[] = $row;
			$pushConnectionIds[] = $row['ID'];
		}

		if (!empty($connections))
		{
			$result = PushTable::getList(array(
				'filter' => array(
					'=ENTITY_TYPE' => 'CONNECTION',
					'=ENTITY_ID' => '@ IN (' . implode(',', $pushConnectionIds) . ')'
				),
			));

			$pushChannels = array();
			while($row = $result->fetch())
			{
				$pushChannels[$row['ENTITY_ID']] = $row;
			}

			foreach($connections as $davConnection)
			{
				$googleApiConnection = new GoogleApiSync($davConnection['ENTITY_ID']);
				if (empty($pushChannels[$davConnection['ID']]))
				{
					$channelInfo = $googleApiConnection->startWatchCalendarList($connections['NAME']);
					if ($channelInfo && isset($channelInfo['id'], $channelInfo['resourceId']))
					{
						PushTable::delete(array("ENTITY_TYPE" => 'CONNECTION', 'ENTITY_ID' => $davConnection['ID']));
						PushTable::add(array(
							'ENTITY_TYPE' => 'CONNECTION',
							'ENTITY_ID' => $davConnection['ID'],
							'CHANNEL_ID' => $channelInfo['id'],
							'RESOURCE_ID' => $channelInfo['resourceId'],
							'EXPIRES' => $channelInfo['expiration'],
							'NOT_PROCESSED' => 'N'
						));
					}
				}

				unset($googleApiConnection);
			}
		}
		if ($lastId == $start)
		{
			\CAgent::removeAgent("\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(" . $start . ");", "calendar");
			\CAgent::removeAgent("\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(0);", "calendar");
			\CAgent::addAgent("\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(0);", "calendar", "N", 3600, "", "Y", Type\DateTime::createFromTimestamp(strtotime('+1 hour'))->format(Type\Date::convertFormatToPhp(FORMAT_DATETIME)));
			return null;
		}
		else
		{
			return "\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(" . $lastId . ");";
		}
	}

	public static function stopChannel($row = array(), $ownerId = 0)
	{
		if ($row)
		{
			if ($row['ENTITY_TYPE'] == 'CONNECTION')
			{
				if ($ownerId == 0)
				{
					$connectionData = \CDavConnection::getById($row['ENTITY_ID']);
					$ownerId = $connectionData['ENTITY_ID'];
				}

				if ($ownerId > 0)
				{
					$googleApiConnection = new GoogleApiSync($ownerId);
					$googleApiConnection->stopChannel($row['CHANNEL_ID'], $row['RESOURCE_ID']);
				}
			}
			elseif ($row['ENTITY_TYPE'] == 'SECTION')
			{
				if ($ownerId == 0)
				{
					$section = \CCalendarSect::getById($row['ENTITY_ID']);
					$ownerId = $section['OWNER_ID'];
				}

				if ($ownerId > 0)
				{
					$googleApiConnection = new GoogleApiSync($ownerId);
					$googleApiConnection->stopChannel($row['CHANNEL_ID'], $row['RESOURCE_ID']);
				}
			}
			PushTable::delete(array("ENTITY_TYPE" => $row['ENTITY_TYPE'], 'ENTITY_ID' => $row['ENTITY_ID']));
		}
	}

	/**
	 * Stop all push channels agent
	 * Recommended interval - 60 sec. Response from google not required
	 *
	 * @return null|string
	 */
	public static function clearPushChannels()
	{
		\Bitrix\Main\Loader::includeModule('dav');
		$result = PushTable::getList(array(
			'limit'	 => self::CLEAR_LIMIT
		));
		$hasRows = false;
		while ($row = $result->fetch())
		{
			$hasRows = true;
			self::stopChannel($row);
		}
		if ($hasRows)
			return "\\Bitrix\\Calendar\\Sync\\GoogleApiPush::clearPushChannels();";
		else
			return null;
	}

	/**
	 * @param $channelId
	 * @return bool
	 */
	public static function receivePushSignal($channelId, $resourceId)
	{
		$result = PushTable::getList(array(
			'filter' => array(
				'=NOT_PROCESSED' => 'N',
				'=CHANNEL_ID' => $channelId,
				'=RESOURCE_ID' => $resourceId
			),
		));

		if ($row = $result->fetch())
		{
			PushTable::update(
				array(
					'ENTITY_TYPE' => $row['ENTITY_TYPE'],
					'ENTITY_ID' => $row['ENTITY_ID']
				),
				array(
					'NOT_PROCESSED' => 'Y',
					'FIRST_PUSH_DATE' => Type\DateTime::createFromTimestamp(strtotime('now'))
				)
			);
			return true;
		}
		return false;
	}

	/**
	 * Synchronize sections and connections on push signal receive
	 * Recommended agent interval = 3 minutes
	 *
	 * @return string
	 */
	public static function processPush()
	{
		\Bitrix\Main\Loader::includeModule('dav');
		$result = PushTable::getList(array(
			'filter' => array('=NOT_PROCESSED' => 'Y'),
			'order' => array('FIRST_PUSH_DATE' => 'ASC'),
			'limit' => self::PROCESS_LIMIT
		));
		$pushRows = array();

		while ($row = $result->fetch())
		{
			$pushRows[] = $row;
			if ($row['ENTITY_TYPE'] == 'CONNECTION')
			{
				$connectionIds[] = $row['ENTITY_ID'];
			}
			if ($row['ENTITY_TYPE'] == 'SECTION')
			{
				$sectionIds[] = $row['ENTITY_ID'];
			}
		}

		if (!empty($pushRows))
		{
			global $DB;
			$sections = array();
			$connections = array();
			if (!empty($sectionIds))
			{
				$sectionResult =  $DB->query("SELECT * FROM b_calendar_section WHERE ID IN (" . implode(',', $sectionIds) . ")");
				while ($row = $sectionResult->fetch())
				{
					$sections[$row['ID']] = $row;
				}
			}

			if (!empty($connectionIds))
			{
				$connectionResult =  $DB->query("SELECT * FROM b_dav_connections WHERE ID IN (" . implode(',', $connectionIds) . ")");
				while ($row = $connectionResult->fetch())
				{
					$connections[$row['ID']] = $row;
				}
			}

			foreach($pushRows as $row)
			{
				$resynced = false;
				$eventsSyncToken = false;
				if ($row['ENTITY_TYPE'] == 'CONNECTION')
				{
					if (!empty($connections[$row['ENTITY_ID']]))
					{
						$resynced = \CCalendarSync::syncConnection($connections[$row['ENTITY_ID']]);
					}
				}
				elseif ($row['ENTITY_TYPE'] == 'SECTION')
				{
					if (!empty($sections[$row['ENTITY_ID']]))
					{
						$eventsSyncToken = \CCalendarSync::syncCalendarEvents($sections[$row['ENTITY_ID']]);
						if ($eventsSyncToken)
						{
							\CCalendarSect::edit(array(
								'arFields' => array(
									'ID' => $sections[$row['ENTITY_ID']]['ID'],
									'SYNC_TOKEN' => $eventsSyncToken
								)
							));
						}
					}
				}

				if (($resynced && $row['ENTITY_TYPE'] == 'CONNECTION') ||
					($eventsSyncToken && $row['ENTITY_TYPE'] == 'SECTION'))
				{
					PushTable::update(
						array(
							'ENTITY_TYPE' => $row['ENTITY_TYPE'],
							'ENTITY_ID' => $row['ENTITY_ID']
						),
						array(
							'NOT_PROCESSED' => 'N',
							'FIRST_PUSH_DATE' => null
						)
					);
				}
				else
				{
					//PushTable::delete(array("ENTITY_TYPE" => $row['ENTITY_TYPE'], 'ENTITY_ID' => $row['ENTITY_ID']));
				}
			}
			\CCalendar::clearCache();
		}

		return "\\Bitrix\\Calendar\\Sync\\GoogleApiPush::processPush();";
	}
}