Your IP : 18.118.208.127


Current Path : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/rest/lib/api/
Upload File :
Current File : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/rest/lib/api/placement.php

<?php
namespace Bitrix\Rest\Api;


use Bitrix\Main\ArgumentException;
use Bitrix\Main\ArgumentNullException;
use Bitrix\Main\Entity\ExpressionField;
use Bitrix\Main\Loader;
use Bitrix\Rest\AccessException;
use Bitrix\Rest\AppTable;
use Bitrix\Rest\AuthTypeException;
use Bitrix\Rest\HandlerHelper;
use Bitrix\Rest\OAuth\Auth;
use Bitrix\Rest\PlacementLangTable;
use Bitrix\Rest\PlacementTable;
use Bitrix\Rest\RestException;
use Bitrix\Rest\Exceptions;
use Bitrix\Rest\Lang;
use Bitrix\Main\ArgumentTypeException;

class Placement extends \IRestService
{
	const SCOPE_PLACEMENT = 'placement';

	public static function onRestServiceBuildDescription()
	{
		return array(
			static::SCOPE_PLACEMENT => array(
				'placement.list' => array(
					'callback' => array(__CLASS__, 'getList'),
					'options' => array()
				),
				'placement.bind' => array(
					'callback' => array(__CLASS__, 'bind'),
					'options' => array()
				),
				'placement.unbind' => array(
					'callback' => array(__CLASS__, 'unbind'),
					'options' => array()
				),
				'placement.get' => array(
					'callback' => array(__CLASS__, 'get'),
					'options' => array()
				)
			),
		);
	}


	public static function getList($query, $n, \CRestServer $server)
	{
		if($server->getAuthType() !== Auth::AUTH_TYPE)
		{
			throw new AuthTypeException("Application context required");
		}

		$result = array();

		$serviceDescription = $server->getServiceDescription();

		$scopeList = array(\CRestUtil::GLOBAL_SCOPE);

		$query = array_change_key_case($query, CASE_UPPER);

		if(isset($query['SCOPE']))
		{
			if($query['SCOPE'] != '')
			{
				$scopeList = array($query['SCOPE']);
			}
		}
		elseif($query['FULL'] == true)
		{
			$scopeList = array_keys($serviceDescription);
		}
		else
		{
			$scopeList = static::getScope($server);
			$scopeList[] = \CRestUtil::GLOBAL_SCOPE;
		}

		$placementList = static::getPlacementList($server, $scopeList);

		foreach($placementList as $placement => $placementInfo)
		{
			if(!$placementInfo['private'])
			{
				$result[] = $placement;
			}
		}

		return $result;
	}

	public static function bind($params, $n, \CRestServer $server)
	{
		static::checkPermission($server);

		$params = array_change_key_case($params, CASE_UPPER);

		$placement = mb_strtoupper($params['PLACEMENT']);
		$placementHandler = $params['HANDLER'];

		if($placement == '')
		{
			throw new Exceptions\ArgumentNullException("PLACEMENT");
		}

		if($placement == PlacementTable::PLACEMENT_DEFAULT)
		{
			throw new Exceptions\ArgumentNullException("Wrong value", "PLACEMENT");
		}

		if($placementHandler == '')
		{
			throw new Exceptions\ArgumentNullException("HANDLER");
		}

		$appInfo = static::getApplicationInfo($server);
		HandlerHelper::checkCallback($placementHandler, $appInfo);

		$scopeList = static::getScope($server);
		$scopeList[] = \CRestUtil::GLOBAL_SCOPE;

		$placementList = static::getPlacementList($server, $scopeList);
		$placementInfo = $placementList[$placement];

		if (is_array($placementInfo) && (!isset($placementInfo['private']) || !$placementInfo['private']))
		{
			$placementLangList = [];
			$paramsOptions = $params['OPTIONS'] ?? [];
			$placementInfoOptions = $placementInfo['options'] ?? [];

			$placementBind = array(
				'APP_ID' => $appInfo['ID'],
				'USER_ID' => (isset($params['USER_ID']) && (int)$params['USER_ID'] > 0) ? (int)$params['USER_ID'] : PlacementTable::DEFAULT_USER_ID_VALUE,
				'PLACEMENT' => $placement,
				'PLACEMENT_HANDLER' => $placementHandler,
				'OPTIONS' => static::prepareOptions($paramsOptions, $placementInfoOptions),
			);

			if (
				$placementBind['USER_ID'] !== PlacementTable::DEFAULT_USER_ID_VALUE
				&& $placementInfo['user_mode'] !== true
			)
			{
				throw new RestException(
					'User mode is not available.',
					PlacementTable::ERROR_PLACEMENT_USER_MODE
				);
			}

			$langList = Lang::listLanguage();
			$langDefault = reset($langList);

			if (empty($params['LANG_ALL']))
			{
				if (!empty($params['TITLE']))
				{
					$placementLangList[$langDefault]['TITLE'] = trim($params['TITLE']);
				}

				if (!empty($params['DESCRIPTION']))
				{
					$placementLangList[$langDefault]['DESCRIPTION'] = trim($params['DESCRIPTION']);
				}

				if (!empty($params['GROUP_NAME']))
				{
					$placementLangList[$langDefault]['GROUP_NAME'] = trim($params['GROUP_NAME']);
				}
			}
			else
			{
				$fieldList = [
					'TITLE',
					'DESCRIPTION',
					'GROUP_NAME',
				];
				foreach ($params['LANG_ALL'] as $langCode => $langItem)
				{
					foreach ($fieldList as $field)
					{
						$placementLangList[$langCode][$field] = trim($langItem[$field] ?? '');
					}
				}
			}

			$placementBind['LANG_ALL'] = $placementLangList;
			$placementBind = Lang::mergeFromLangAll($placementBind);
			unset($placementBind['LANG_ALL']);

			if ($placementInfo['max_count'] > 0)
			{
				$filter = [
					'=APP_ID' => $placementBind['APP_ID'],
					'=PLACEMENT' => $placementBind['PLACEMENT'],
				];
				if ($placementInfo['user_mode'] === true)
				{
					$filter['=USER_ID'] = [
						PlacementTable::DEFAULT_USER_ID_VALUE,
						(int)$placementBind['USER_ID'],
					];
				}

				$res = PlacementTable::getList(
					[
						'filter' => $filter,
						'select' => [
							'COUNT',
						],
						'runtime' => [
							new ExpressionField('COUNT', 'COUNT(*)'),
						]
					]
				);

				if ($result = $res->fetch())
				{
					if ($result['COUNT'] >= $placementInfo['max_count'])
					{
						throw new RestException(
							'Placement max count: ' . $placementInfo['max_count'],
							PlacementTable::ERROR_PLACEMENT_MAX_COUNT
						);
					}
				}
			}

			if (
				array_key_exists('ICON', $params)
				&& is_array($params['ICON'])
				&& $params['ICON']['fileData']
				&& ($file = \CRestUtil::saveFile($params['ICON']['fileData']))
			)
			{
				$placementBind['ICON'] = $file;
			}
			if (!empty($placementInfo['registerCallback']['callback']))
			{
				if (
					$placementInfo['registerCallback']['moduleId']
					&& Loader::includeModule($placementInfo['registerCallback']['moduleId'])
					&& is_callable($placementInfo['registerCallback']['callback'])
				)
				{
					$resultCallback = call_user_func(
						$placementInfo['registerCallback']['callback'],
						$placementBind,
						$placementInfo
					);
					if (!empty($resultCallback['error']) && !empty($resultCallback['error_description']))
					{
						return $resultCallback;
					}
				}
			}

			$result = PlacementTable::add($placementBind);
			if ($result->isSuccess())
			{
				$placementId = $result->getId();
				if (empty($placementLangList))
				{
					$app = AppTable::getByClientId($placementBind['APP_ID']);
					if (!empty($app['APP_NAME']))
					{
						$placementLangList[$langDefault] = [
							'TITLE' => $app['APP_NAME']
						];
					}
				}
				foreach ($placementLangList as $langId => $data)
				{
					$data['PLACEMENT_ID'] = $placementId;
					$data['LANGUAGE_ID'] = $langId;
					$res = PlacementLangTable::add($data);
					if (!$res->isSuccess())
					{
						$errorMessage = $res->getErrorMessages();
						throw new RestException(
							'Unable to set placements language: ' . implode(', ', $errorMessage),
							RestException::ERROR_CORE
						);
					}
				}
			}
			else
			{
				$errorMessage = $result->getErrorMessages();
				throw new RestException(
					'Unable to set placement handler: ' . implode(', ', $errorMessage),
					RestException::ERROR_CORE
				);
			}

			return true;
		}

		throw new RestException(
			'Placement not found',
			PlacementTable::ERROR_PLACEMENT_NOT_FOUND
		);
	}

	private static function prepareOptions($paramsOptions = [], $placementInfoOptions = []): array
	{
		$result = [];
		if (empty($placementInfoOptions))
		{
			return $result;
		}
		$requiredOptions = self::getRequiredOptions($placementInfoOptions);
		$defaultOptions = self::getDefaultOptions($placementInfoOptions);

		if (!is_array($paramsOptions))
		{
			if (!empty($requiredOptions))
			{
				throw new ArgumentTypeException('options', 'array');
			}

			return $defaultOptions;
		}

		self::checkRequiredOptionsInParamsOptions($paramsOptions, $requiredOptions);

		foreach ($placementInfoOptions as $optionName => $optionSetting)
		{
			$optionValue = $paramsOptions[$optionName] ?? $defaultOptions[$optionName] ?? null;
			$optionType = null;

			if (!is_array($optionSetting))
			{
				$optionType = $optionSetting;
			}
			else
			{
				$optionType = $optionSetting['type'];
			}

			switch($optionType)
			{
				case 'int':
					$result[$optionName] = (int)$optionValue;

					break;
				case 'string':
					$result[$optionName] = (string)$optionValue;

					break;
				case 'array':
					if (!is_array($optionValue))
					{
						throw new ArgumentTypeException($optionName, 'array');
					}
					$result[$optionName] = self::prepareCompositeOptions($optionValue, $optionSetting);

					break;
			}
		}

		return $result;
	}

	/**
	 * handling arrays in options
	 * @param array $paramOptionData
	 * @param array $optionSetting
	 * @return array
	 * @throws ArgumentTypeException
	 */
	private static function prepareCompositeOptions(array $paramOptionData, array $optionSetting): array
	{
		$result = [];
		if (!is_array($optionSetting['typeValue']))
		{
			throw new ArgumentTypeException('typeValue', 'array');
		}

		$allowedTypes = ['integer', 'string', 'array'];
		$optionSetting['typeValue'] = str_replace('int', 'integer', $optionSetting['typeValue']);
		$optionSetting['typeValue'] = array_intersect($optionSetting['typeValue'], $allowedTypes);

		//1. check transmitted placement data
		foreach ($paramOptionData as $keyOption => $valueOption)
		{
			$typeValue = gettype($valueOption);
			//do not take arrays, they are processed as a separate entity
			if (in_array($typeValue, $optionSetting['typeValue']) && $typeValue !== 'array')
			{
				$result[$keyOption] = $valueOption;
			}
		}

		//2. check default placement setting
		foreach ($optionSetting as $keySetting => $valueSetting)
		{
			//type and typeValue - service data
			if ($keySetting === 'type' || $keySetting === 'typeValue')
			{
				continue;
			}

			$typeValueSetting = gettype($valueSetting);

			if (
				$typeValueSetting === 'array'
				&& in_array('array', $optionSetting['typeValue'])
				&& isset($valueSetting['type'])
				&& isset($valueSetting['typeValue'])
				&& isset($paramOptionData[$keySetting])
			)
			{
				$result[$keySetting] = self::prepareCompositeOptions($paramOptionData[$keySetting], $valueSetting);
			}
		}

		return $result;
	}


	/**
	 * if the option configuration has the "default" key
	 * [
	 * 	optionName => [
	 * 		"default" => defaultValue
	 * 	],
	 * ]
	 * then return array in format
	 * [
	 * 	optionName => defaultValue
	 * ]
	 *
	 * @param array $placementInfoOptions
	 * @return array
	 */
	private static function getDefaultOptions(array $placementInfoOptions): array
	{
		$result = [];

		foreach ($placementInfoOptions as $optionName => $optionSetting)
		{
			if (isset($optionSetting['default']))
			{
				$result[$optionName] = $optionSetting['default'];
			}
		}

		return $result;
	}

	/**
	 * @param array $paramsOptions
	 * @param array $requiredOptions
	 * @return void
	 * @throws ArgumentNullException
	 */
	private static function checkRequiredOptionsInParamsOptions(array $paramsOptions, array $requiredOptions): void
	{
		foreach ($requiredOptions as $requiredOption)
		{
			if (!array_key_exists($requiredOption, $paramsOptions))
			{
				throw new ArgumentNullException($requiredOption);
			}
		}
	}


	/**
	 * get a list of names of all required options
	 * @param array $placementInfoOptions
	 * @return array
	 */
	private static function getRequiredOptions(array $placementInfoOptions): array
	{
		$result = [];
		foreach ($placementInfoOptions as $optionName => $optionSettings)
		{
			if (self::isRequiredOption($optionSettings))
			{
				$result[] = $optionName;
			}
		}

		return $result;
	}

	/**
	 * @param array|string $optionSettings
	 * @return bool
	 */
	private static function isRequiredOption($optionSettings): bool
	{
		if (!isset($optionSettings['require']))
		{
			return false;
		}

		return (bool)$optionSettings['require'];
	}

	public static function unbind($params, $n, \CRestServer $server)
	{
		static::checkPermission($server);

		$params = array_change_key_case($params, CASE_UPPER);

		if (!is_string($params['PLACEMENT']))
		{
			throw new ArgumentTypeException('PLACEMENT', 'string');
		}

		$placement = mb_strtoupper($params['PLACEMENT']);
		$placementHandler = $params['HANDLER'];

		if ($placement == '')
		{
			throw new ArgumentNullException("PLACEMENT");
		}

		$cnt = 0;

		$placementList = static::getPlacementList($server);

		if (array_key_exists($placement, $placementList) && !$placementList[$placement]['private'])
		{
			$appInfo = static::getApplicationInfo($server);

			$filter = array(
				'=APP_ID' => $appInfo["ID"],
				'=PLACEMENT' => $placement,
			);

			if (array_key_exists('USER_ID', $params))
			{
				$filter['USER_ID'] = (int)$params['USER_ID'];
			}

			if($placementHandler <> '')
			{
				$filter['=PLACEMENT_HANDLER'] = $placementHandler;
			}

			$dbRes = PlacementTable::getList(array(
				'filter' => $filter
			));

			while($placementHandler = $dbRes->fetch())
			{
				$cnt++;
				$result = PlacementTable::delete($placementHandler["ID"]);
				if($result->isSuccess())
				{
					$cnt++;
				}
			}
		}

		return array('count' => $cnt);
	}


	public static function get($params, $n, \CRestServer $server)
	{
		static::checkPermission($server);

		$result = array();

		$appInfo = static::getApplicationInfo($server);

		$dbRes = PlacementTable::getList(array(
			"filter" => array(
				"=APP_ID" => $appInfo["ID"],
			),
			'order' => array(
				"ID" => "ASC",
			)
		));

		$placementList = static::getPlacementList($server);

		foreach ($dbRes->fetchCollection() as $placement)
		{
			if (
				array_key_exists($placement->getPlacement(), $placementList)
				&& !$placementList[$placement->getPlacement()]['private']
			)
			{
				$langList = [];
				$placement->fillLangAll();
				if (!is_null($placement->getLangAll()))
				{
					foreach ($placement->getLangAll() as $lang)
					{
						$langList[$lang->getLanguageId()] = [
							'TITLE' => $lang->getTitle(),
							'DESCRIPTION' => $lang->getDescription(),
							'GROUP_NAME' => $lang->getGroupName(),
						];
					}
				}
				$result[] = array(
					'placement' => $placement->getPlacement(),
					'userId' => $placement->getUserId(),
					'handler' => $placement->getPlacementHandler(),
					'options' => $placement->getOptions(),
					'title' => $placement->getTitle(),
					'description' => $placement->getComment(),
					'langAll' => $langList,
				);
			}
		}

		return $result;
	}

	protected static function checkPermission(\CRestServer $server)
	{
		if($server->getAuthType() !== Auth::AUTH_TYPE)
		{
			throw new AuthTypeException("Application context required");
		}

		if(!\CRestUtil::isAdmin())
		{
			throw new AccessException();
		}
	}

	protected static function getScope(\CRestServer $server)
	{
		$result = array();

		$authData = $server->getAuthData();

		$scopeList = explode(',', $authData['scope']);

		$serviceDescription = $server->getServiceDescription();
		foreach($scopeList as $scope)
		{
			if(array_key_exists($scope, $serviceDescription))
			{
				$result[] = $scope;
			}
		}

		return $result;
	}

	protected static function getApplicationInfo(\CRestServer $server)
	{
		if($server->getAuthType() !== Auth::AUTH_TYPE)
		{
			throw new AuthTypeException("Application context required");
		}

		return AppTable::getByClientId($server->getClientId());
	}

	protected static function getPlacementList(\CRestServer $server, $scopeList = null)
	{
		$serviceDescription = $server->getServiceDescription();

		if($scopeList === null)
		{
			$scopeList = array_keys($serviceDescription);
		}

		$result = array();

		foreach($scopeList as $scope)
		{
			if(
				isset($serviceDescription[$scope])
				&& is_array($serviceDescription[$scope][\CRestUtil::PLACEMENTS])
			)
			{
				$result = array_merge($result, $serviceDescription[$scope][\CRestUtil::PLACEMENTS]);
			}
		}

		return $result;
	}
}