Your IP : 3.144.139.201


Current Path : /var/www/axolotl/data/www/nn.axolotls.ru/bitrix/modules/crm/lib/webform/
Upload File :
Current File : /var/www/axolotl/data/www/nn.axolotls.ru/bitrix/modules/crm/lib/webform/fieldsynchronizer.php

<?php
/**
 * Bitrix Framework
 * @package bitrix
 * @subpackage crm
 * @copyright 2001-2016 Bitrix
 */
namespace Bitrix\Crm\WebForm;

use Bitrix\Main\Localization\Loc;
use Bitrix\Crm\Synchronization\UserFieldSynchronizer;
use Bitrix\Main\Context;

Loc::loadMessages(__FILE__);

class FieldSynchronizer
{
	protected $isCreateMode = false;

	public function getSynchronizeFields($schemeId, $fieldNames, $invoicePayerEntityName = null)
	{
		$this->isCreateMode = false;

		$syncFieldCodes = array();
		$srcFieldMap = $this->getFieldMap($schemeId, $fieldNames);
		foreach($srcFieldMap as $entityTypeName => $entityFields)
		{
			foreach($entityFields as $fieldName => $entityField)
			{
				$oldFieldCode = $entityField['OLD_FIELD_CODE'];
				$newFieldCode = $entityField['NEW_FIELD_CODE'];

				if($oldFieldCode == $newFieldCode)
				{
					continue;
				}

				if($newFieldCode)
				{
					continue;
				}

				$syncFieldCodes[] = $oldFieldCode;
			}
		}

		return $syncFieldCodes;
	}

	public function replacePostFields($schemeId, &$fields, &$dependencies, $invoicePayerEntityName = null)
	{
		$this->isCreateMode = true;

		$srcFieldCodes = array_keys($fields);
		$srcFieldMap = $this->getFieldMap($schemeId, $srcFieldCodes);

		foreach($srcFieldMap as $entityTypeName => $entityFields)
		{
			foreach($entityFields as $fieldName => $entityField)
			{
				$oldFieldCode = $entityField['OLD_FIELD_CODE'];
				$newFieldCode = $entityField['NEW_FIELD_CODE'];

				if($oldFieldCode == $newFieldCode)
				{
					continue;
				}

				// replace field
				self::replaceField($fields, $entityField);

				// replace dependencies
				self::replaceFieldDependencies($dependencies, $entityField);

				unset($fields[$entityField['OLD_FIELD_CODE']]);
			}
		}
	}

	protected function getFieldMap($schemeId, $srcFieldCodes)
	{
		$entityTypeNames = Entity::getNames();
		$srcFieldMap = array();
		foreach($srcFieldCodes as $srcFieldCodeTmp)
		{
			foreach($entityTypeNames as $entityTypeName)
			{
				$prefix = $entityTypeName . '_';
				if(substr($srcFieldCodeTmp, 0, strlen($prefix)) != $prefix)
				{
					continue;
				}

				$srcEntity = Entity::getMap($entityTypeName);
				$srcEntityFields = EntityFieldProvider::getFieldsInternal($entityTypeName, $srcEntity);

				$fieldName = substr($srcFieldCodeTmp, strlen($prefix));
				$srcFieldMap[$entityTypeName][$fieldName] = array(
					'FIELD_NAME' => $fieldName,
					'OLD_FIELD_CODE' => $srcFieldCodeTmp,
					'OLD_FIELD' => self::findField($fieldName, $srcEntityFields),
					'NEW_FIELD_CODE' => '',
					'NEW_FIELD' => null
				);
				break;
			}
		}

		foreach($srcFieldMap as $entityTypeName => $entityFields)
		{
			$fieldNames = array();
			foreach($entityFields as $keyId => $entityField)
			{
				$fieldNames[] = $entityField['FIELD_NAME'];
			}
			if(count($fieldNames) == 0)
			{
				continue;
			}

			$synchronizedFields = $this->getReplacedSchemeFields(
				$entityTypeName,
				$schemeId,
				$fieldNames
			);

			foreach($synchronizedFields as $dstEntityTypeName => $syncFields)
			{
				$dstEntity = Entity::getMap($dstEntityTypeName);
				$dstEntityFields = EntityFieldProvider::getFieldsInternal($dstEntityTypeName, $dstEntity);

				$prefix = $dstEntityTypeName . '_';
				foreach($syncFields as $syncFieldOld => $syncFieldNew)
				{
					if(!$syncFieldNew)
					{
						continue;
					}

					$srcFieldMap[$entityTypeName][$syncFieldOld]['NEW_FIELD_CODE'] = $prefix . $syncFieldNew;
					$srcFieldMap[$entityTypeName][$syncFieldOld]['NEW_FIELD'] = self::findField($syncFieldNew, $dstEntityFields);
				}
			}

		}

		return $srcFieldMap;
	}

	protected function getFieldItemMap($oldItems, $newItems)
	{
		$itemIdMap = array();
		foreach($oldItems as $oldItem)
		{
			foreach($newItems as $newItem)
			{
				if($oldItem['VALUE'] == $newItem['VALUE'])
				{
					$itemIdMap[$oldItem['ID']] = $newItem['ID'];
				}
			}
		}

		return $itemIdMap;
	}

	protected function replaceField(&$fields, $entityField)
	{
		$oldFieldCode = $entityField['OLD_FIELD_CODE'];
		$newFieldCode = $entityField['NEW_FIELD_CODE'];
		if(!$newFieldCode || isset($fields[$newFieldCode]))
		{
			return;
		}

		// replace field codes and items
		$fields[$newFieldCode] = $fields[$oldFieldCode];
		if(!$fields[$newFieldCode]['ITEMS'])
		{
			return;
		}
		if(!$entityField['OLD_FIELD']['items'])
		{
			return;
		}
		if(!$entityField['NEW_FIELD']['items'])
		{
			return;
		}

		self::replaceFieldItems(
			$fields[$newFieldCode],
			$entityField['OLD_FIELD']['items'],
			$entityField['NEW_FIELD']['items']
		);
	}

	protected function replaceFieldItems(&$field, $oldItems, $newItems)
	{
		$itemIdMap = self::getFieldItemMap($oldItems, $newItems);
		foreach($field['ITEMS'] as $keyId => $oldItem)
		{
			foreach($itemIdMap as $oldItemId => $newItemId)
			{
				if($oldItem['ID'] != $oldItemId)
				{
					continue;
				}

				$field['ITEMS'][$keyId]['ID'] = $newItemId;
			}
		}
	}

	protected function replaceFieldDependencies(&$dependencies, $entityField)
	{
		$oldFieldCode = $entityField['OLD_FIELD_CODE'];
		$newFieldCode = $entityField['NEW_FIELD_CODE'];

		if(!$newFieldCode)
		{
			return;
		}

		foreach($dependencies as $dependencyId => $dependency)
		{
			if($dependency['IF_FIELD_CODE'] == $oldFieldCode)
			{
				$dependency['IF_FIELD_CODE'] = $newFieldCode;
				if($dependency['IF_VALUE'] && $entityField['OLD_FIELD']['items'] && $entityField['NEW_FIELD']['items'])
				{
					$itemIdMap = self::getFieldItemMap(
						$entityField['OLD_FIELD']['items'],
						$entityField['NEW_FIELD']['items']
					);
					if(isset($itemIdMap[$dependency['IF_VALUE']]))
					{
						$dependency['IF_VALUE'] = $itemIdMap[$dependency['IF_VALUE']];
					}
				}
			}

			if($dependency['DO_FIELD_CODE'] == $oldFieldCode)
			{
				$dependency['DO_FIELD_CODE'] = $newFieldCode;
			}

			$dependencies[$dependencyId] = $dependency;
		}
	}

	protected function findField($fieldName, $entityFields)
	{
		foreach($entityFields as $entityField)
		{
			if($entityField['entity_field_name'] == $fieldName)
			{
				return $entityField;
			}
		}

		return null;
	}

	protected function getXmlIdUserFieldBySystemField($entityTypeName, $fieldName)
	{
		return 'CRM_WEBFORM_' . $entityTypeName . '_' . $fieldName;
	}

	protected function getSystemFieldByUserField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName)
	{
		$srcEntityTypeId = \CCrmOwnerType::ResolveID($srcEntityTypeName);
		$entityId = \CCrmOwnerType::ResolveUserFieldEntityID($srcEntityTypeId);
		$userTypeEntity = new \CUserTypeEntity();

		$resultDb = $userTypeEntity->GetList(
			array(),
			array('ENTITY_ID' => $entityId, 'FIELD_NAME' => $srcFieldName)
		);

		if($dstField = $resultDb->Fetch())
		{
			$prefix = 'CRM_WEBFORM_' . $dstEntityTypeName . '_';

			if(substr($dstField['XML_ID'], 0, strlen($prefix)) == $prefix)
			{
				return substr($dstField['XML_ID'], strlen($prefix));
			}
		}

		return null;
	}

	protected function getUserFieldBySystemField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName)
	{
		$dstEntityTypeId = \CCrmOwnerType::ResolveID($dstEntityTypeName);
		$entityId = \CCrmOwnerType::ResolveUserFieldEntityID($dstEntityTypeId);
		$userTypeEntity = new \CUserTypeEntity();

		$xmlId = $this->getXmlIdUserFieldBySystemField($srcEntityTypeName, $srcFieldName);

		$resultDb = $userTypeEntity->GetList(
			array(),
			array('ENTITY_ID' => $entityId, 'XML_ID' => $xmlId)
		);
		if($dstField = $resultDb->Fetch())
		{
			return $dstField['FIELD_NAME'];
		}

		return null;
	}

	protected function createUserFieldBySystemField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName)
	{
		$userFieldId = null;

		$srcEntity = Entity::getMap($srcEntityTypeName);
		$srcEntityFields = EntityFieldProvider::getFieldsInternal($srcEntityTypeName, $srcEntity);
		$srcField = self::findField($srcFieldName, $srcEntityFields);

		if(!$srcField)
		{
			return null;
		}

		$dstFieldName = $this->getUserFieldBySystemField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName);
		if($dstFieldName || !$this->isCreateMode)
		{
			return $dstFieldName;
		}


		$typeId = null;
		$userFieldSettings = null;
		switch($srcField['type'])
		{
			case 'checkbox':
			case 'radio':
			case 'list':
				$typeId = 'enumeration';
				if($srcField['type'] == 'checkbox' || $srcField['type'] == 'radio')
				{
					$userFieldSettings['DISPLAY'] = 'CHECKBOX';
				}
				else
				{
					$userFieldSettings['DISPLAY'] = 'LIST';
				}
				break;

			case 'file':
			case 'string':
			case 'date':
				$typeId = $srcField['type'];
				break;

			default:
				$typeId = 'string';
		}

		$xmlId = $this->getXmlIdUserFieldBySystemField($srcEntityTypeName, $srcFieldName);

		$dstEntityTypeId = \CCrmOwnerType::ResolveID($dstEntityTypeName);
		$entityId = \CCrmOwnerType::ResolveUserFieldEntityID($dstEntityTypeId);
		$userTypeEntity = new \CUserTypeEntity();

		$resultDb = $userTypeEntity->GetList(
			array(),
			array('ENTITY_ID' => $entityId, 'XML_ID' => $xmlId)
		);
		if($dstField = $resultDb->Fetch())
		{
			return $dstField['FIELD_NAME'];
		}
		if(!$this->isCreateMode)
		{
			return null;
		}


		do
		{
			$dstFieldName = 'UF_CRM_'.strtoupper(uniqid());
			$resultDb = $userTypeEntity->GetList(
				array(),
				array('ENTITY_ID' => $entityId, 'FIELD_NAME' => $dstFieldName)
			);
		}
		while(is_array($resultDb->Fetch()));

		$dstField = array(
			'XML_ID' => $xmlId,
			'FIELD_NAME' => $dstFieldName,
			'ENTITY_ID' => $entityId,
			'USER_TYPE_ID' => $typeId,
			'SORT' =>100,
			'MULTIPLE' => $srcField['multiple'] ? 'Y' : 'N',
			'MANDATORY' => $srcField['required'] ? 'Y' : 'N',
			'SHOW_FILTER' => 'N',
			'SHOW_IN_LIST' => 'N',
			'EDIT_FORM_LABEL' => array(LANGUAGE_ID => $srcField['caption']),
			'LIST_COLUMN_LABEL' => array(LANGUAGE_ID => $srcField['caption']),
			'LIST_FILTER_LABEL' => array(LANGUAGE_ID => $srcField['caption']),
		);

		if($userFieldSettings)
		{
			$dstField['SETTINGS'] = $userFieldSettings;
		}

		$userFieldId = $userTypeEntity->Add($dstField);
		if($userFieldId && $typeId === 'enumeration' && $srcField['items'])
		{
			$enumList = array();
			$enumQty = 0;
			foreach($srcField['items'] as $item){
				$enum = array(
					'XML_ID' => $item['ID'],
					'VALUE' => $item['VALUE'],
					'SORT' => ($enumQty + 1) * 10,
				);
				$enumList["n{$enumQty}"] = $enum;
				$enumQty++;
			}

			$enumEntity = new \CUserFieldEnum();
			$enumEntity->SetEnumValues($userFieldId, $enumList);

			$GLOBALS['CACHE_MANAGER']->ClearByTag('crm_fields_list_' . $entityId);
		}

		return $dstFieldName;
	}

	protected function getSynchronizedSystemField($srcEntityTypeName, $srcFieldName, $dstEntityTypeName, $dstEntityFieldNames)
	{
		$interChangeableFields = array(
			array('NAME', 'TITLE', 'ORDER_TOPIC')
		);

		if(in_array($srcFieldName, $dstEntityFieldNames))
		{
			return $srcFieldName;
		}

		$dstFieldName = null;
		foreach($interChangeableFields as $icGroup)
		{
			// src field not in group
			if(!in_array($srcFieldName, $icGroup))
			{
				continue;
			}

			foreach($icGroup as $icFieldName)
			{
				// field does not existed in dst entity
				if(!in_array($icFieldName, $dstEntityFieldNames))
				{
					continue;
				}

				$dstFieldName = $icFieldName;
				break;
			}
		}

		// add userfield as a entity field
		if(!$dstFieldName)
		{
			$dstFieldName = $this->createUserFieldBySystemField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName);
		}

		return $dstFieldName;
	}

	protected function getSynchronizedUserFields($srcEntityTypeId, $dstEntityTypeId, $userFieldNames)
	{
		if(!$this->isCreateMode)
		{
			$synchronizedFieldNameMap = array();
			$newFields = UserFieldSynchronizer::getSynchronizationFields($srcEntityTypeId, $dstEntityTypeId, null, true);
			foreach($newFields as $newDstFieldName)
			{
				if(!in_array($newDstFieldName, $userFieldNames))
				{
					continue;
				}

				$synchronizedFieldNameMap[$newDstFieldName] = '';
			}
			foreach(UserFieldSynchronizer::$existedFieldNameMap as $existedSrcFieldName => $existedDstFieldName)
			{
				if(!in_array($existedSrcFieldName, $userFieldNames))
				{
					continue;
				}

				$synchronizedFieldNameMap[$existedSrcFieldName] = $existedDstFieldName;
			}

			return $synchronizedFieldNameMap;
		}
		else
		{
			return UserFieldSynchronizer::synchronize(
				$srcEntityTypeId,
				$dstEntityTypeId,
				Context::getCurrent()->getLanguage(),
				array('FIELD_NAME' => $userFieldNames)
			);
		}
	}

	/**
	 * Synchronize source type fields with destination type fields.
	 * Matches are searched by comparing field labels.
	 * If a source field is not found in the destination type, it will be created there.

	 * @param int $srcEntityTypeName Source Entity Type Name
	 * @param int $dstEntityTypeName Destination Entity Type Name
	 * @param array $srcFieldNames Field names for synchronizing
	 * @return array $synchronizedFieldMap
	 */
	protected function synchronizeFields($srcEntityTypeName, $dstEntityTypeName, $srcFieldNames)
	{
		$synchronizedFieldMap = array();
		$entityFieldNames = array();
		$userFieldNames = array();

		$dstEntityFieldNames = array();
		$dstEntity = Entity::getMap($dstEntityTypeName);

		//TODO: speed, refactor to fields without userfields
		$dstEntityFields = EntityFieldProvider::getFieldsInternal($dstEntityTypeName, $dstEntity);
		foreach($dstEntityFields as $dstEntityField)
		{
			$dstEntityFieldNames[] = $dstEntityField['entity_field_name'];
		}

		$srcEntityTypeId = \CCrmOwnerType::ResolveID($srcEntityTypeName);
		$dstEntityTypeId = \CCrmOwnerType::ResolveID($dstEntityTypeName);

		if(!$srcEntityTypeId)
		{
			return array();
		}

		if(!$dstEntityTypeId)
		{
			return array();
		}

		foreach($srcFieldNames as $fieldName)
		{
			if(substr($fieldName, 0, 3) == 'UF_')
			{
				$userFieldNames[] = $fieldName;
			}
			else
			{
				$dstFieldName = $this->getSynchronizedSystemField($srcEntityTypeName, $fieldName, $dstEntityTypeName, $dstEntityFieldNames);
				if(!$dstFieldName && $this->isCreateMode)
				{
					continue;
				}

				$entityFieldNames[$fieldName] = $dstFieldName;
			}
		}

		if(count($entityFieldNames) > 0)
		{
			$synchronizedFieldMap = array_merge($synchronizedFieldMap, $entityFieldNames);
		}

		if(count($userFieldNames) > 0)
		{
			$existedUserFieldToSystemFieldNameMap = array();
			foreach($userFieldNames as $srcFieldKey => $srcFieldName)
			{
				$dstFieldName = $this->getSystemFieldByUserField($dstEntityTypeName, $srcEntityTypeName, $srcFieldName);
				if($dstFieldName)
				{
					$existedUserFieldToSystemFieldNameMap[$srcFieldName] = $dstFieldName;
					unset($userFieldNames[$srcFieldKey]);
				}
			}

			$synchronizedUserFieldNameMap = $this->getSynchronizedUserFields($srcEntityTypeId, $dstEntityTypeId, $userFieldNames);
			$synchronizedFieldMap = array_merge($synchronizedFieldMap, $existedUserFieldToSystemFieldNameMap, $synchronizedUserFieldNameMap);
		}

		return $synchronizedFieldMap;
	}

	/**
	 * Synchronize source type fields with destination type fields.
	 * Matches are searched by comparing field labels.
	 * If a source field is not found in the destination type, it will be created there.
	 *
	 * @param int $srcEntityTypeName Source Entity Type Name
	 * @param int $dstSchemeId Destination Scheme ID
	 * @param array $fieldNames Field names for synchronizing
	 * @return array $synchronizedFieldMap
	 */
	protected function getReplacedSchemeFields($srcEntityTypeName, $dstSchemeId, $fieldNames)
	{
		$synchronizedFieldMap = array();
		$scheme = Entity::getSchemes($dstSchemeId);

		if(in_array($srcEntityTypeName, $scheme['ENTITIES']))
		{
			foreach($fieldNames as $fieldName)
			{
				$synchronizedFieldMap[$srcEntityTypeName][$fieldName] = $fieldName;
			}

			return $synchronizedFieldMap;
		}

		foreach($scheme['ENTITIES'] as $dstEntityTypeName)
		{

			if($dstEntityTypeName == \CCrmOwnerType::InvoiceName)
			{
				continue;
			}

			$synchronizedFieldMap[$dstEntityTypeName] = $this->synchronizeFields(
				$srcEntityTypeName,
				$dstEntityTypeName,
				$fieldNames
			);

			//TODO: remove for creating fields in all entities
			break;
		}

		return $synchronizedFieldMap;
	}
}