Your IP : 18.188.218.219


Current Path : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/landing/lib/node/
Upload File :
Current File : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/landing/lib/node/component.php

<?php
namespace Bitrix\Landing\Node;

use \Bitrix\Main\Localization\Loc;
use Bitrix\Main\Web\Json;

Loc::loadMessages(__FILE__);

class Component extends \Bitrix\Landing\Node
{
	/**
	 * Predefined values for some dynamic props.
	 * @var array
	 */
	protected static $predefineForDynamicProps = array();

	/**
	 * Get class - frontend handler.
	 * @return string
	 */
	public static function getHandlerJS()
	{
		return 'BX.Landing.Node.Component';
	}

	/**
	 * Fill predefined values for some dynamic props.
	 * @param array $additionalVals Additional vals.
	 * @return void
	 */
	public static function setPredefineForDynamicProps(array $additionalVals)
	{
		foreach ($additionalVals as $code => $val)
		{
			self::$predefineForDynamicProps[$code] = $val;
		}
	}

	/**
	 * Save component with new params.
	 * @param string $content Content of block.
	 * @param string $code Code of component.
	 * @param array $params Params for replace in component.
	 * @return string Modified content.
	 */
	protected static function saveComponent($content, $code, array $params)
	{
		$components = \PHPParser::parseScript($content);
		foreach ($components as $component)
		{
			// get first component with code = $code
			if ($component['DATA']['COMPONENT_NAME'] == $code)
			{
				$params = array_merge($component['DATA']['PARAMS'], $params);
				$componentCode = ($component['DATA']['VARIABLE'] ? $component['DATA']['VARIABLE'] . '=' : '') .
					'$APPLICATION->IncludeComponent(' . PHP_EOL .
					"\t" . '"' . $component['DATA']['COMPONENT_NAME'] . '", ' . PHP_EOL .
					"\t" . '"' . $component['DATA']['TEMPLATE_NAME'] . '", ' . PHP_EOL .
					"\t" . 'array(' . PHP_EOL .
					"\t" . "\t" . \PHPParser::returnPHPStr2($params) . PHP_EOL .
					"\t" . '),' . PHP_EOL .
					"\t" . ($component['DATA']['PARENT_COMP'] ? $component['DATA']['PARENT_COMP'] : 'false') .
							(!empty($component['DATA']['FUNCTION_PARAMS']) ? ',' . PHP_EOL .
					"\t" . 'array(' . PHP_EOL . "\t" . "\t" . \PHPParser::returnPHPStr2($component['DATA']['FUNCTION_PARAMS']) . PHP_EOL .
					"\t" . ')' : '') . PHP_EOL .
					');';
				$componentCode = str_replace(array('<?', '?>'), array('< ?', '? >'), $componentCode);
				$content = mb_substr($content, 0, $component['START']).$componentCode.mb_substr($content, $component['END']);
				break;
			}
		}

		return $content;
	}

	/**
	 * Check if part of array or string is php code (for component).
	 * @param mixed $code Some content.
	 * @return bool
	 */
	protected static function checkPhpCode($code)
	{
		if (is_array($code))
		{
			foreach ($code as $k => $v)
			{
				if (
					self::checkPhpCode($k) ||
					self::checkPhpCode($v)
				)
				{
					return true;
				}
			}
		}
		else{
			if (
				mb_substr($code, 0, 2) == '={' &&
				mb_substr($code, -1, 1) == '}' &&
				mb_strlen($code) > 3
			)
			{
				return true;
			}
		}

		return false;
	}

	/**
	 * Save data for this node.
	 * @param \Bitrix\Landing\Block $block Block instance.
	 * @param string $selector Selector.
	 * @param array $data Data array.
	 * @return void
	 */
	public static function saveNode(\Bitrix\Landing\Block $block, $selector, array $data)
	{
		//$data = array_pop($data);// we allow one type of component per block
		$manifest = $block->getManifest();
		if (isset($manifest['nodes'][$selector]['extra']))
		{
			$updateProps = array();
			$allowedProps = $manifest['nodes'][$selector]['extra'];
			foreach ($data as $code => $val)
			{
				if (isset($allowedProps[$code]))
				{
					$updateProps[$code] = self::transformPropValue(
						$val,
						$allowedProps[$code]
					);
					if (self::checkPhpCode(array($code => $updateProps[$code])))
					{
						unset($updateProps[$code]);
					}
				}
			}
			if (!empty($updateProps))
			{
				// !tmp bugfix about set section id to null
				if (
					array_key_exists('SECTION_ID', $updateProps) &&
					!trim($updateProps['SECTION_ID'])
				)
				{
					$updateProps['SECTION_ID'] = '={$sectionId}';
				}
				$doc = $block->getDom();
				$newContent = self::saveComponent(
					$doc->saveHTML(),
					$selector,
					$updateProps
				);
				// first clear dom
				foreach ($doc->getChildNodesArray() as $node)
				{
					$node->getParentNode()->removeChild($node);
				}
				// and load new content
				$doc->loadHTML($newContent);
			}
		}
	}

	/**
	 * Prepare item-node of manifest.
	 * @param \Bitrix\Landing\Block $block Block instance.
	 * @param array $manifest Manifest of current node.
	 * @param array &$manifestFull Full manifest of block (by ref).
	 * @return array|null Return null for delete from manifest.
	 */
	public static function prepareManifest(\Bitrix\Landing\Block $block, array $manifest, array &$manifestFull = array())
	{
		if (
			!isset($manifest['extra']['editable']) ||
			!is_array($manifest['extra']['editable'])
		)
		{
			return null;
		}
		else
		{
			$editable = $manifest['extra']['editable'];
		}

		if (
			!isset($manifestFull['attrs']) ||
			!is_array($manifestFull['attrs'])
		)
		{
			$manifestFull['attrs'] = array();
		}

		if (
			!isset($manifestFull['style']) ||
			!is_array($manifestFull['style'])
		)
		{
			$manifestFull['style'] = array();
		}

		$manifestFull['disableCache'] = true;
		$manifest['allowInlineEdit'] = false;
		$newExtra = array();
		$originalStyleBlock = isset($manifestFull['style']['block'])
							? $manifestFull['style']['block']
							: array();

		// detect all components in text
		$components = \PHPParser::parseScript($block->getContent());
		$classBlock = $block->getBlockClass();
		foreach ($components as $component)
		{
			foreach ($component['DATA']['PARAMS'] as $key => $param)
			{
				if (
					is_string($param)
					&& stripos($param, '={$classBlock->get') !== false
					&& $value = $classBlock->get($key)
				)
				{
					$component['DATA']['PARAMS'][$key] = $value;
				}
			}
			$componentName = $manifest['code'];
			// when found what need, get actually params from text and props description from component
			if ($component['DATA']['COMPONENT_NAME'] == $componentName)
			{
				// collect props
				$componentDesc = \CComponentUtil::GetComponentDescr(
					$component['DATA']['COMPONENT_NAME']
				);
				$propsTemplate = @\CComponentUtil::GetTemplateProps(//@fixme
					$component['DATA']['COMPONENT_NAME'],
					$component['DATA']['TEMPLATE_NAME'],
					'',
					self::$predefineForDynamicProps
				);
				if (isset($propsTemplate['PARAMETERS']))
				{
					$propsTemplate = $propsTemplate['PARAMETERS'];
				}
				$props = @\CComponentUtil::getComponentProps(//@fixme
					$component['DATA']['COMPONENT_NAME'],
					self::$predefineForDynamicProps
				);
				if (isset($props['PARAMETERS']))
				{
					$props = $props['PARAMETERS'];
				}
				if (!empty($propsTemplate) && is_array($propsTemplate))
				{
					foreach ($propsTemplate as $code => $prop)
					{
						$props[$code] = $prop;
					}
				}
				// style block
				$styleAttrs = array();
				if (
					!isset($manifestFull['style']) ||
					!is_array($manifestFull['style'])
				)
				{
					$manifestFull['style'] = array(
						'block' => array(),
						'nodes' => array()
					);
				}
				else if (!isset($manifestFull['style']['nodes']))
				{
					$manifestFull['style'] = array(
						'nodes' => $manifestFull['style']
					);
				}
				$manifestFull['style']['block'] = array_merge(array(
					'name' => isset($componentDesc['NAME'])
							? $componentDesc['NAME']
							: '',
					'type' => 'box',
					'additional' => array(
						array(
							'name' => Loc::getMessage('LANDING_NODE_CMP_STYLE_BLOCK'),
							'attrs' => &$styleAttrs
						)
					)
				), $originalStyleBlock);
				foreach ($editable as $field => $fieldItem)
				{
					if (isset($props[$field]))
					{
						// change node manifest
						$newExtra[$field] = $props[$field];
						$newExtra[$field]['VALUE'] = $component['DATA']['PARAMS'][$field] ?? '';
						// add attr
						if (!isset($manifestFull['attrs'][$componentName]))
						{
							$manifestFull['attrs'][$componentName] = array();
						}
						$propType = self::transformPropType(array(
							'name' => $fieldItem['name'] ?? $newExtra[$field]['NAME'],
							'style' => isset($fieldItem['style'])
										&& $fieldItem['style'],
							'original_type' => 'component',
							'component_type' => $newExtra[$field]['TYPE'] ?? '',
							'attribute' => $field,
							'value' => self::preparePropValue(
								$newExtra[$field]['VALUE'],
								$fieldItem
							),
							//'original_value' => $newExtra[$field]['VALUE'],
							'allowInlineEdit' => false
						) + $fieldItem, $newExtra[$field]);
						$newExtra[$field]['ATTRIBUTE_TYPE'] = $propType['type'];
						if ($propType['style'])
						{
							$propType['selector'] = $componentName;
							$styleAttrs[] = $propType;
						}
						else
						{
							$manifestFull['attrs'][$componentName][] = $propType;
						}
					}
				}
				if (empty($styleAttrs))
				{
					if ($originalStyleBlock)
					{
						$manifestFull['style']['block'] = $originalStyleBlock;
					}
					else
					{
						unset($manifestFull['style']['block']);
					}
				}
				// all right
				if (!empty($newExtra))
				{
					$manifest['extra'] = $newExtra;
					return $manifest;
				}
			}
		}
		return null;
	}

	/**
	 * Additional transform type of prop item to attr item.
	 * @param array $item One attr.
	 * @param mixed $prop One prop.
	 * @return array
	 */
	protected static function transformPropType(array $item, $prop)
	{
		if (isset($prop['TYPE']))
		{
			if (
				$prop['TYPE'] == 'CUSTOM' &&
				isset($prop['JS_EVENT'])
			)
			{
				$prop['TYPE'] = $prop['TYPE'] . '_' . $prop['JS_EVENT'];
			}

			switch ($prop['TYPE'])
			{
				case 'LIST':
				{
					$item['items'] = array();
					if (isset($prop['MULTIPLE']) && $prop['MULTIPLE'] == 'Y')
					{
						$item['type'] = 'multiselect';
						if (!is_array($item['value']))
						{
							$item['value'] = array($item['value']);
						}
					}
					else
					{
						$prop['MULTIPLE'] = 'N';
						$item['type'] = 'dropdown';
					}
					if (isset($prop['VALUES']) && is_array($prop['VALUES']))
					{
						foreach ($prop['VALUES'] as $code => $val)
						{
							$item['items'][] = array(
								'name' => $val,
								'value' => $code,
								'selected' => (
												$prop['MULTIPLE'] == 'Y' &&
												in_array($code, $item['value'])
											) || $code == $item['value']
							);
						}
					}
					break;
				}
				case 'CHECKBOX':
				{
					$item['type'] = 'checkbox';
					$item['items'] = array(
						array(
							'name' => $item['name'],
							'value' => 'Y',
							'checked' => $item['value'] == 'Y'
						)
					);
					$item['compact'] = true;
					unset($item['name']);
					break;
				}
				case 'CUSTOM_initDraggableAddControl':
				{
					$item['type'] = 'catalog-view';
					$item['items'] = array(
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-1.svg', 'value' => '0'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-2.svg', 'value' => '1'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-3.svg', 'value' => '2'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-4.svg', 'value' => '3'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-1-4.svg', 'value' => '4'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-4-1.svg', 'value' => '5'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-6.svg', 'value' => '6'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-1-6.svg', 'value' => '7'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-6-1.svg', 'value' => '8'),
						array('name' => '', 'image' => '/bitrix/images/landing/catalog_images/preset-line.svg', 'value' => '9')
					);
					$jsArray = \Cutil::jsObjectToPhp($item['value'], true);
					$item['value'] = array();
					if (is_array($jsArray))
					{
						foreach ($jsArray as $val)
						{
							if (isset($val['VARIANT']))
							{
								$item['value'][] = (int)$val['VARIANT'];
							}
						}
					}
					break;
				}
				case 'CUSTOM_initPositionControl':
				{
					$item['type'] = 'position';
					$item['items'] = array(
						'top-left' => array('content' => '', 'value' => 'top-left'),
						'top-center' => array('content' => '', 'value' => 'top-center'),
						'top-right' => array('content' => '', 'value' => 'top-right'),
						'middle-left' => array('content' => '', 'value' => 'middle-left'),
						'middle-center' => array('content' => '', 'value' => 'middle-center'),
						'middle-right' => array('content' => '', 'value' => 'middle-right'),
						'bottom-left' => array('content' => '', 'value' => 'bottom-left'),
						'bottom-center' => array('content' => '', 'value' => 'bottom-center'),
						'bottom-right' => array('content' => '', 'value' => 'bottom-right')
					);
					break;
				}
				case 'CUSTOM_initDraggableOrderControl':
				{
					$item['type'] = 'sortable-list';
					$item['items'] = array();
					if (!is_array($item['value']))
					{
						$item['value'] = explode(',', $item['value']);
					}
					$items = Json::decode($prop['JS_DATA']);
					if (is_array($items))
					{
						foreach ($items as $code => $val)
						{
							$item['items'][] = array(
								'name' => $val,
								'value' => $code,
								'preview' => '/bitrix/images/landing/catalog_images/preview/'.mb_strtolower($code) . '.svg?v3'
							);
						}
					}
					break;
				}
				default:
				{
					if (!isset($item['type']) || !$item['type'])
					{
						$item['type'] = 'text';
					}
					switch ($item['type'])
					{
						case 'url':
						{
							$item['disableBlocks'] = true;
							break;
						}
						case 'filter':
						{
							ob_start();
							$filterId = 'LANDING_FLT_' . $item['attribute'];
							\Bitrix\Landing\Manager::getApplication()->includeComponent(
								'bitrix:main.ui.filter',
								'',
								array(
									'THEME' => \Bitrix\Main\UI\Filter\Theme::BORDER,
									'FILTER_ID' => $filterId,
									'FILTER' => isset($item['fields'])
														? $item['fields']
														: array(),
									'DISABLE_SEARCH' => true,
									'ENABLE_LABEL' => true
								)
							);
							$item['html'] = ob_get_clean();
							$item['filterId'] = $filterId;
							break;
						}
						default:
						{
							$item['placeholder'] = '';
						}
					}
					break;
				}
			}
		}

		return $item;
	}

	/**
	 * Prepare prop value before output in edit form.
	 * @param mixed $value Mixed value.
	 * @param array $prop Array of field from manifest.
	 * @return mixed
	 */
	protected static function preparePropValue($value, $prop)
	{
		if (isset($prop['type']))
		{
			switch ($prop['type'])
			{
				case 'url':
					{
						if ($value && isset($prop['entityType']))
						{
							// @todo: make this more universal
							if (
								$prop['entityType'] == 'element' &&
								$value != '={$elementCode}' &&
								$value != '={$elementId}'
							)
							{
								$value = '#catalogElement' . $value;
							}
							else if (
								$prop['entityType'] == 'section' &&
								$value != '={$sectionCode}' &&
								$value != '={$sectionId}'
							)
							{
								$value = '#catalogSection' . $value;
							}
						}
					}
			}
		}
		return $value;
	}

	/**
	 * Additional transform prop value before saving.
	 * @param mixed $value Mixed value.
	 * @param array $prop Array of prop.
	 * @return mixed
	 */
	protected static function transformPropValue($value, $prop)
	{
		if (!is_array($value))
		{
			$value = \CUtil::jsObjectToPhp($value, true);
		}

		if (isset($prop['TYPE']))
		{
			if (
				$prop['TYPE'] == 'CUSTOM' &&
				isset($prop['JS_EVENT'])
			)
			{
				$prop['TYPE'] = $prop['TYPE'] . '_' . $prop['JS_EVENT'];
			}
			if (
				isset($prop['MULTIPLE']) &&
				$prop['MULTIPLE'] == 'Y' &&
				!is_array($value)
			)
			{
				$value = array($value);
			}

			switch ($prop['TYPE'])
			{
				case 'CHECKBOX':
				{
					if (is_array($value))
					{
						$value = array_shift($value);
					}
					if ($value != 'Y')
					{
						$value = 'N';
					}
					break;
				}
				case 'CUSTOM_initDraggableAddControl':
				{
					$newValue = array();
					if (is_array($value))
					{
						foreach ($value as $val)
						{
							$newValue[] = array(
								'VARIANT' => $val,
								'BIG_DATA' => false
							);
						}
					}
					$value = \CUtil::phpToJsObject($newValue);
					break;
				}
				case 'CUSTOM_initDraggableOrderControl':
				{
					if (is_array($value))
					{
						$value = implode(',', $value);
					}
					break;
				}
				default:
				{
					if (isset($prop['ATTRIBUTE_TYPE']))
					{
						switch ($prop['ATTRIBUTE_TYPE'])
						{
							case 'url':
							{
								if (preg_match('/^#landing([\d]+)$/', $value, $matches))
								{
									$lansing = \Bitrix\Landing\Landing::createInstance($matches[1], [
										'skip_blocks' => true
									]);
									if ($lansing->exist())
									{
										$value = $lansing->getPublicUrl();
									}
								}
								else if (preg_match('/^#catalog(Element|Section)([\d]+)$/', $value, $matches))
								{
									$value = $matches[2];
								}
								break;
							}
						}
					}
				}
			}
		}

		return $value;
	}

	/**
	 * Build element/section url.
	 * @param int $elementId Element / section id.
	 * @param string $urlType Type of url (section / detail).
	 * @deprecated since 18.4.0
	 * @return string
	 */
	public static function getIblockURL($elementId, $urlType)
	{
		return \Bitrix\Landing\PublicAction\Utils::getIblockURL($elementId, $urlType);
	}

	/**
	 * Tmp function for gets iblock params.
	 * @param string $key If isset, return value for this key.
	 * @deprecated since 18.4.0
	 * @return array|string
	 */
	public static function getIblockParams($key = false)
	{
		static $params = array();

		if (empty($params))
		{
			$params['id'] = \Bitrix\Main\Config\Option::get('crm', 'default_product_catalog_id');
			$params['type'] = 'CRM_PRODUCT_CATALOG';
			$params['default_product'] = false;
		}

		if ($key === false)
		{
			return $params;
		}
		else
		{
			return isset($params[$key]) ? $params[$key] : null;
		}
	}

	/**
	 * Get data for this node.
	 * @param \Bitrix\Landing\Block $block Block instance.
	 * @param string $selector Selector.
	 * @return array
	 */
	public static function getNode(\Bitrix\Landing\Block $block, $selector)
	{
		$data = array();
		$manifest = $block->getManifest();

		// gets common attrs
		if (isset($manifest['attrs'][$selector]))
		{
			$allowedProps = $manifest['attrs'][$selector];
			foreach ($allowedProps as $attr)
			{
				if (!self::checkPhpCode($attr['value']))
				{
					$data[$attr['attribute']] = $attr['value'];
				}
			}
		}

		// gets attrs from style block
		if (
			isset($manifest['style']['block']['additional']) &&
			is_array($manifest['style']['block']['additional'])
		)
		{
			foreach ($manifest['style']['block']['additional'] as $item)
			{
				if (
					isset($item['attrs']) &&
					is_array($item['attrs'])
				)
				{
					foreach ($item['attrs'] as $attr)
					{
						if (!self::checkPhpCode($attr['value']))
						{
							$data[$attr['attribute']] = $attr['value'];
						}
					}
				}
			}
		}

		return $data;
	}
}