Current Path : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/landing/lib/node/ |
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; } }