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/rospirotorg.ru/bitrix/modules/landing/lib/node/ |
Upload File : |
<?php namespace Bitrix\Landing\Node; use Bitrix\Landing\Block; use Bitrix\Landing\History; 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); $params = array_filter($params, function ($param) { return $param !== null; }); $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 Block $block Block instance. * @param string $selector Selector. * @param array $data Data array. * @return void */ public static function saveNode(Block $block, $selector, array $data): void { if (empty($data)) { return; } $manifest = $block->getManifest(); if (isset ($manifest['nodes'][$selector]['extra'])) { $updateProps = []; $allowedProps = $manifest['nodes'][$selector]['extra']; $propsBefore = []; if (History::isActive()) { foreach ($allowedProps as $code => $prop) { $propsBefore[$code] = self::transformPropValue( $prop['VALUE'], $prop ); } } 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); if (History::isActive()) { $propsAfter = array_merge($propsBefore, $updateProps); $history = new History($block->getLandingId(), History::ENTITY_TYPE_LANDING); $history->push('EDIT_COMPONENT', [ 'block' => $block, 'selector' => $selector, 'position' => 0, 'valueBefore' => $propsBefore, 'valueAfter' => $propsAfter, ]); } } } } /** * Prepare item-node of manifest. * @param 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(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] ?? null; // 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 ), 'default_value' => $newExtra[$field]['DEFAULT'] ?? null, //'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'] = [ [ 'name' => $item['name'], 'value' => 'Y', 'checked' => ( (isset($item['value']) && $item['value'] !== '') ? $item['value'] : $prop['DEFAULT'] ) == '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; } case 'CUSTOM_initColorField': { $item['type'] = 'color'; $item['subtype'] = 'color'; break; } case 'CUSTOM_initIconField': { $item['type'] = 'icon'; $item['disableLink'] = 'true'; $item['value'] = is_array($item['value']) && !empty($item['value']) ? $item['value'] : $prop['DEFAULT'] ; break; } case 'CUSTOM_initImageField': { $item['type'] = 'image'; $item['disableLink'] = 'true'; $data = \Cutil::jsObjectToPhp($prop['JS_DATA'], true); $item['dimensions'] = $data['dimensions'] ?? 'false'; break; } case 'CUSTOM_initUserSelectField': { $item['type'] = 'user-select'; $item['value'] = (int)$item['value'] > 0 ? $item['value'] : $prop['DEFAULT']; $item['value'] = (int)$item['value']; break; } case 'CUSTOM_initDynamicSource': { $item['type'] = 'dynamic_source'; $item['hideSort'] = 'true'; $data = \Cutil::jsObjectToPhp($prop['JS_DATA'], true); if ($data['sources'] && is_array($data['sources'])) { $item['sources'] = $data['sources']; } if ($data['title'] && is_string($data['title'])) { $item['title'] = $data['title']; } if ($data['stubText'] && is_string($data['stubText'])) { $item['stubText'] = $data['stubText']; } if ($data['useLink'] && is_string($data['useLink'])) { $item['useLink'] = $data['useLink']; } if ($data['linkType'] && is_string($data['linkType'])) { $item['linkType'] = $data['linkType']; } 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 Block $block Block instance. * @param string $selector Selector. * @return array */ public static function getNode(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; } }