403Webshell
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/ilovecveti.ru/bitrix/modules/bizproc/lib/automation/engine/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/ilovecveti.ru/bitrix/modules/bizproc/lib/automation/engine/template.php
<?php
namespace Bitrix\Bizproc\Automation\Engine;

use Bitrix\Bizproc;
use Bitrix\Bizproc\Workflow\Template\Tpl;
use Bitrix\Bizproc\WorkflowTemplateTable;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Error;
use Bitrix\Main\ErrorCollection;
use Bitrix\Main\Result;
use Bitrix\Bizproc\Automation;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Type\DateTime;

Loc::loadMessages(__FILE__);

/**
 * Class Template
 * @package Bitrix\Bizproc\Automation\Engine
 */
class Template
{
	protected static $parallelActivityType = 'ParallelActivity';
	protected static $sequenceActivityType = 'SequenceActivity';
	/** @deprecated @var string $delayActivityType  */
	protected static $delayActivityType = 'DelayActivity';
	protected static $robotDelayActivityType = 'RobotDelayActivity';
	protected static $conditionActivityType = 'IfElseActivity';
	protected static $availableActivities = [];
	protected static $availableActivityClasses = [];

	protected $template;
	protected $autoExecuteType = \CBPDocumentEventType::Automation;
	/** @var  null|Robot[] */
	protected $robots;
	protected $isExternalModified;
	protected $isConverted = false;
	private static array $cache = [];

	/**
	 * Template constructor.
	 * @param array $documentType
	 * @param null $documentStatus
	 * @throws ArgumentException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 */
	public function __construct(array $documentType, $documentStatus = null)
	{
		$this->template = $this->getDefaultTemplate($documentType, $documentStatus);

		if ($documentStatus)
		{
			$cacheKey = $this->getCacheKey($documentType, $documentStatus);
			if (!isset(self::$cache[$cacheKey]))
			{
				self::$cache[$cacheKey] = $this->loadTemplate($documentType, $documentStatus);
			}

			if (self::$cache[$cacheKey])
			{
				$this->template = self::$cache[$cacheKey];
				$this->autoExecuteType = (int)$this->template['AUTO_EXECUTE'];
			}
		}
	}

	private function getDefaultTemplate(array $documentType, $documentStatus): array
	{
		return [
			'ID' => 0,
			'MODULE_ID' => $documentType[0],
			'ENTITY' => $documentType[1],
			'DOCUMENT_TYPE' => $documentType[2],
			'DOCUMENT_STATUS' => $documentStatus,
			'AUTO_EXECUTE' => $this->autoExecuteType,
			'TEMPLATE' => [],
			'PARAMETERS' => [],
			'CONSTANTS' => [],
			'VARIABLES' => [],
		];
	}

	private function loadTemplate(array $documentType, $documentStatus)
	{
		return WorkflowTemplateTable::getRow([
			'filter' => [
				'=MODULE_ID' => $documentType[0],
				'=ENTITY' => $documentType[1],
				'=DOCUMENT_TYPE' => $documentType[2],
				'=DOCUMENT_STATUS' => $documentStatus,
			],
		]);
	}

	private function getCacheKey(array $documentType, $documentStatus)
	{
		return \CBPHelper::stringify([...$documentType, $documentStatus]);
	}

	public static function createByTpl(Tpl $tpl)
	{
		$instance = new static($tpl->getDocumentComplexType());
		$instance->template = $tpl->collectValues();
		$instance->autoExecuteType = (int) $instance->template['AUTO_EXECUTE'];

		return $instance;
	}

	public function deleteRobots(array $robots, int $userId): Result
	{
		$isSameRobot = function ($lhsRobot, $rhsRobot) {
			return strcmp($lhsRobot->getName(), $rhsRobot->getName());
		};

		$remainingRobots = array_udiff($this->getRobots(), $robots, $isSameRobot);

		return $this->save($remainingRobots, $userId);
	}

	public function getDocumentStatus()
	{
		return isset($this->template['DOCUMENT_STATUS']) ? $this->template['DOCUMENT_STATUS'] : null;
	}

	public function setDocumentStatus($status)
	{
		$this->template['DOCUMENT_STATUS'] = (string) $status;
		return $this;
	}

	public function getTemplate(): ?array
	{
		return $this->template['TEMPLATE'] ?? null;
	}

	public function setTemplate(array $template): static
	{
		$this->template['TEMPLATE'] = $template;

		return $this;
	}

	public function setName(string $name)
	{
		$this->template['NAME'] = $name;
		return $this;
	}

	public function getExecuteType($autoExecuteType)
	{
		return $this->autoExecuteType;
	}

	public function setExecuteType($autoExecuteType)
	{
		if (\CBPDocumentEventType::Out($autoExecuteType) === '')
		{
			throw new ArgumentException('Incorrect DocumentEventType');
		}

		$this->autoExecuteType = $autoExecuteType;
	}

	public function getId()
	{
		return isset($this->template['ID']) ? (int)$this->template['ID'] : 0;
	}

	public function getParameters(): array
	{
		return $this->template['PARAMETERS'] ?? [];
	}

	public function getRobotSettingsDialog(array $robot, $request = null, ?array $contextRobots = null)
	{
		if (isset($robot['Properties']) && is_array($robot['Properties']))
		{
			$robot['Properties'] = Automation\Helper::convertProperties($robot['Properties'], $this->getDocumentType());
		}

		unset($robot['Delay'], $robot['Condition']);

		$copy = clone $this;
		$robots = $contextRobots ? [$robot, ...$contextRobots] : [$robot];
		$copy->setRobots($robots);

		return \CBPActivity::callStaticMethod(
			$robot['Type'],
			"GetPropertiesDialog",
			array(
				$this->getDocumentType(), //documentType
				$robot['Name'] ?? null, //activityName
				$copy->template['TEMPLATE'], //arWorkflowTemplate
				$copy->getParameters(), //arWorkflowParameters
				$copy->template['VARIABLES'] ?? [], //arWorkflowVariables
				$request, //arCurrentValues = null
				'bizproc_automation_robot_dialog', //formName = ""
				null, //popupWindow = null
				SITE_ID, //siteId = ''
				$copy->template['CONSTANTS'] ?? [], //$arWorkflowConstants
			)
		);
	}

	public function saveRobotSettings(array $robot, array $request)
	{
		$saveResult = new Result();
		$documentType = $this->getDocumentType();

		if (isset($robot['Properties']) && is_array($robot['Properties']))
		{
			$robot['Properties'] = Automation\Helper::unConvertProperties($robot['Properties'], $documentType);
		}

		$request = Automation\Helper::unConvertProperties($request, $documentType);

		$copy = clone $this;
		$copy->setRobots([$robot]);
		$raw = $copy->template['TEMPLATE'];

		$robotErrors = $v = $p = array();
		$result = \CBPActivity::callStaticMethod(
			$robot['Type'],
			"GetPropertiesDialogValues",
			[
				$documentType,
				$robot['Name'] ?? null,
				&$raw,
				&$v,
				&$p,
				$request,
				&$robotErrors,
			]
		);

		if ($result || (isset($robot['Activated']) && $robot['Activated'] === 'N'))
		{
			$templateActivity = \CBPWorkflowTemplateLoader::findActivityByName($raw, $robot['Name'] ?? null);

			$robotTitle = $robot['Properties']['Title'] ?? null;
			$robot['Properties'] = $templateActivity['Properties'];
			$robot['Properties']['Title'] = $robotTitle;

			$saveResult->setData(['robot' => $robot]);
		}
		else
		{
			foreach ($robotErrors as $i => $error)
			{
				$errorMessage = $error['message'] ?? null;
				$errorCode = $error['code'] ?? null;
				$errorParameter = $error['parameter'] ?? null;
				$saveResult->addError(new Error($errorMessage, $errorCode, ['parameter' => $errorParameter]));
			}
		}

		return $saveResult;
	}

	public function save(array $robots, $userId, array $additional = [])
	{
		$userId = (int)$userId;
		$result = new Result();
		$templateId = !empty($this->template['ID']) ? $this->template['ID'] : 0;

		if (isset($additional['PARAMETERS']) && is_array($additional['PARAMETERS']))
		{
			$this->template['PARAMETERS'] = $additional['PARAMETERS'];
		}
		if (isset($additional['CONSTANTS']) && is_array($additional['CONSTANTS']))
		{
			$this->template['CONSTANTS'] = $additional['CONSTANTS'];
		}

		if ($templateId)
		{
			$templateResult = $this->updateTemplateRobots($robots, $userId);
		}
		else
		{
			$this->setRobots($robots);
			$templateResult = $this->addBizprocTemplate($userId);
		}

		if ($templateResult->isSuccess())
		{
			$resultData = $templateResult->getData();
			if (isset($resultData['ID']))
			{
				$this->template['ID'] = $resultData['ID'];
			}
		}
		else
		{
			$result->addErrors($templateResult->getErrors());
		}

		return $result;
	}

	public function setRobots(array $robots)
	{
		$this->robots = array();
		$this->isExternalModified = null;
		foreach ($robots as $robot)
		{
			if (is_array($robot))
				$robot = new Robot($robot);

			if (!($robot instanceof Robot))
			{
				throw new ArgumentException('Robots array is incorrect', 'robots');
			}

			$this->robots[] = $robot;
		}

		$this->unConvertTemplate();// make bizproc template

		return $this;
	}

	/**
	 * Convert instance data to array.
	 * @return array
	 */
	public function toArray()
	{
		$result = [
			'ID' => $this->getId(),
			'DOCUMENT_TYPE' => $this->getDocumentType(),
			'DOCUMENT_STATUS' => $this->template['DOCUMENT_STATUS'],
			'PARAMETERS' => $this->template['PARAMETERS'],
			'CONSTANTS' => $this->template['CONSTANTS'],
			'VARIABLES' => $this->template['VARIABLES'] ?? [],
		];

		$result['IS_EXTERNAL_MODIFIED'] = $this->isExternalModified();
		$result['ROBOTS'] = array();

		foreach ($this->getRobots() as $robot)
		{
			$result['ROBOTS'][] = $robot->toArray();
		}

		return $result;
	}

	public static function getAvailableRobots(array $documentType)
	{
		$key = implode('@', $documentType);
		if (!isset(static::$availableActivities[$key]))
		{
			static::$availableActivities[$key] = \CBPRuntime::getRuntime()
				->searchActivitiesByType('robot_activity', $documentType);
		}
		return static::$availableActivities[$key];
	}

	protected static function getAvailableRobotClasses(array $documentType)
	{
		$key = implode('@', $documentType);
		if (!isset(static::$availableActivityClasses[$key]))
		{
			static::$availableActivityClasses[$key] = array();
			$activities = static::getAvailableRobots($documentType);
			foreach ($activities as $activity)
			{
				static::$availableActivityClasses[$key][] = $activity['CLASS'];
			}
		}
		return static::$availableActivityClasses[$key];
	}

	protected function addBizprocTemplate($userId)
	{
		$userId = (int)$userId;
		$documentType = $this->getDocumentType();

		$raw = $this->template;
		$raw['DOCUMENT_TYPE'] = $documentType;
		$raw['NAME'] = $raw['NAME'] ?? $this->makeTemplateName();
		$raw['USER_ID'] = $userId;
		$raw['MODIFIER_USER'] = new \CBPWorkflowTemplateUser($userId);

		$result = new Result();
		try
		{
			$raw['ID'] = \CBPWorkflowTemplateLoader::add($raw, $userId === 1);
			$result->setData(array('ID' => $raw['ID']));

			$raw['MODULE_ID'] = $documentType[0];
			$raw['ENTITY'] = $documentType[1];
			$raw['DOCUMENT_TYPE'] = $documentType[2];
			$raw['PARAMETERS'] = [];
			$raw['CONSTANTS'] = [];
			$this->template = $raw;
		}
		catch (\Exception $e)
		{
			$result->addError(new Error($e->getMessage()));
		}

		return $result;
	}

	protected function makeTemplateName()
	{
		$msg = Loc::getMessage('BIZPROC_AUTOMATION_TEMPLATE_NAME', [
			'#STATUS#' => $this->template['DOCUMENT_STATUS']
		]);

		if ($this->autoExecuteType === \CBPDocumentEventType::Script)
		{
			$msg = Loc::getMessage('BIZPROC_AUTOMATION_TEMPLATE_SCRIPT_NAME');
		}

		return $msg;
	}

	private function updateTemplateRobots(array $robots, int $userId): Result
	{
		$templateId = $this->template['ID'];
		$result = new Result();

		$errors = $this->validateUpdatedRobots($robots, new \CBPWorkflowTemplateUser($userId));
		if (!$errors->isEmpty())
		{
			$result->addErrors($errors->getValues());

			return $result;
		}

		$this->setRobots($robots);
		$updateFields = [
			'AUTO_EXECUTE' => $this->template['AUTO_EXECUTE'],
			'TYPE' => $this->isExternalModified(),
			'TEMPLATE' => $this->template['TEMPLATE'],
			'PARAMETERS' => $this->template['PARAMETERS'],
			'VARIABLES' => [],
			'CONSTANTS' => $this->template['CONSTANTS'],
			'USER_ID' => $userId,
			'MODIFIER_USER' => new \CBPWorkflowTemplateUser($userId),
		];

		if (isset($this->template['NAME']))
		{
			$updateFields['NAME'] = $this->template['NAME'];
		}

		if (
			isset($this->template['MODULE_ID'])
			&& isset($this->template['ENTITY'])
			&& isset($this->template['DOCUMENT_TYPE'])
		)
		{
			$updateFields['DOCUMENT_TYPE'] = [
				$this->template['MODULE_ID'],
				$this->template['ENTITY'],
				$this->template['DOCUMENT_TYPE']
			];
		}

		try
		{
			\CBPWorkflowTemplateLoader::update($templateId, $updateFields, false, false);
		}
		catch (\Exception $e)
		{
			$result->addError(new Error($e->getMessage()));
		}

		return $result;
	}

	private function validateUpdatedRobots(array $robots, \CBPWorkflowTemplateUser $user): ErrorCollection
	{
		$errors = new ErrorCollection();
		$loader = \CBPWorkflowTemplateLoader::GetLoader();
		$originalRobots = $this->getRobots();

		$isSameRobot = function ($lhsRobot, $rhsRobot) {
			return $lhsRobot->getName() === $rhsRobot->getName();
		};

		/**@var Robot $robot */
		foreach ($robots as $robot)
		{
			if (is_array($robot))
			{
				$robot = new Robot($robot);
			}
			if (!($robot instanceof Robot))
			{
				$errors->setError(new Error('Robots array is incorrect'));
			}
			if (!$errors->isEmpty())
			{
				break;
			}

			$indexOfFoundRobot = -1;
			foreach ($originalRobots as $index => $originalRobot)
			{
				if ($isSameRobot($robot, $originalRobot))
				{
					$indexOfFoundRobot = $index;
					break;
				}
			}

			if ($indexOfFoundRobot < 0 || !$this->areRobotsEqual($robot, $originalRobots[$indexOfFoundRobot]))
			{
				if ($robot->isActivated())
				{
					$sequence = $this->convertRobotToSequenceActivity($robot);
					foreach ($loader->ValidateTemplate($sequence, $user) as $rawError)
					{
						$errors->setError(new Error(trim($rawError['message'])));
					}
				}
				unset($originalRobots[$indexOfFoundRobot]);
			}
		}

		return $errors;
	}

	private function areRobotsEqual(Robot $lhsRobot, Robot $rhsRobot): bool
	{
		$lhsCondition = $lhsRobot->getCondition() ?? new ConditionGroup();
		$rhsCondition = $rhsRobot->getCondition() ?? new ConditionGroup();

		$lhsDelay = $lhsRobot->getDelayInterval();
		$rhsDelay = $rhsRobot->getDelayInterval();
		if (!isset($lhsDelay) || $lhsDelay->isNow())
		{
			$lhsDelay = new DelayInterval();
		}
		if (!isset($rhsDelay) || $rhsDelay->isNow())
		{
			$rhsDelay = new DelayInterval();
		}

		return
			$lhsCondition->toArray()['items'] === $rhsCondition->toArray()['items']
			&& $lhsDelay->toArray() === $rhsDelay->toArray()
			&& $lhsRobot->getBizprocActivity() === $rhsRobot->getBizprocActivity()
		;
	}

	protected function updateBizprocTemplate($id, $userId)
	{
		$raw = $this->template;
		$result = new Result();

		$updateFields = [
			'TEMPLATE'      => $raw['TEMPLATE'],
			'PARAMETERS'    => $raw['PARAMETERS'],
			'VARIABLES'     => [],
			'CONSTANTS'     => $raw['CONSTANTS'],
			'USER_ID' 		=> $userId,
			'MODIFIER_USER' => new \CBPWorkflowTemplateUser($userId),
		];

		if (isset($raw['NAME']))
		{
			$updateFields['NAME'] = $raw['NAME'];
		}

		try
		{
			\CBPWorkflowTemplateLoader::update($id, $updateFields);
		}
		catch (\Exception $e)
		{
			$result->addError(new Error($e->getMessage()));
		}

		return $result;
	}

	protected function convertTemplate()
	{
		$this->robots = array();

		$raw = $this->template;
		if (!is_array($raw) || !isset($raw['TEMPLATE']))
		{
			return false; // BP template is lost.
		}

		/*if (!empty($raw['PARAMETERS']) || !empty($raw['VARIABLES']) || !empty($raw['CONSTANTS']))
		{
			$this->isExternalModified = true;
			return false; // modified or incorrect.
		}*/

		if (empty($raw['TEMPLATE'][0]['Children']) || !is_array($raw['TEMPLATE'][0]['Children']))
			return true;

		if (count($raw['TEMPLATE'][0]['Children']) > 1)
		{
			$this->isExternalModified = true;
			return false; // modified or incorrect.
		}

		$parallelActivity = $raw['TEMPLATE'][0]['Children'][0];
		if (
			!$parallelActivity
			|| $parallelActivity['Type'] !== static::$parallelActivityType
			|| (isset($parallelActivity['Activated']) && $parallelActivity['Activated'] === 'N')
		)
		{
			$this->isExternalModified = true;
			return false; // modified or incorrect.
		}

		foreach ($parallelActivity['Children'] as $sequence)
		{
			$delay = $condition = null;
			$robotsCnt = 0;
			foreach ($sequence['Children'] as $activity)
			{
				if (
					$activity['Type'] === static::$delayActivityType
					|| $activity['Type'] === static::$robotDelayActivityType)
				{
					$delay = $activity;
					continue;
				}

				if ($activity['Type'] === static::$conditionActivityType)
				{
					$condition = ConditionGroup::convertBizprocActivity($activity, $this->getDocumentType(), $this);
					if ($condition === false)
					{
						$this->isExternalModified = true;
						$this->robots = array();
						return false; // modified or incorrect.
					}
				}

				if (!$this->isRobot($activity))
				{
					$this->isExternalModified = true;
					$this->robots = array();
					return false; // modified or incorrect.
				}

				$robotActivity = new Robot($activity);
				if ($delay !== null)
				{
					$delayInterval = DelayInterval::createFromActivityProperties($delay['Properties']);
					$delayInterval->setActivated(\CBPHelper::getBool($delay['Activated'] ?? true));

					$robotActivity->setDelayInterval($delayInterval);
					$robotActivity->setDelayName($delay['Name']);

					if($delayInterval->isActivated() !== $robotActivity->isActivated())
					{
						$this->isExternalModified = true;
						$this->robots = [];

						return false; // modified
					}

					$delay = null;
				}

				if ($condition !== null)
				{
					if ($condition->isActivated() !== $robotActivity->isActivated())
					{
						$this->isExternalModified = true;
						$this->robots = [];

						return false; // modified
					}

					$robotActivity->setCondition($condition);
					$condition = null;
				}

				if ($robotsCnt > 0)
				{
					$robotActivity->setExecuteAfterPrevious();
				}

				++$robotsCnt;
				$this->robots[] = $robotActivity;
			}
		}

		$this->isConverted = true;
		return $this->robots;
	}

	protected function unConvertTemplate()
	{
		$documentType = $this->getDocumentType();
		$this->template = [
			'ID' => $this->getId(),
			'MODULE_ID' => $documentType[0],
			'ENTITY' => $documentType[1],
			'DOCUMENT_TYPE' => $documentType[2],
			'DOCUMENT_STATUS' => $this->template['DOCUMENT_STATUS'],
			'NAME' => $this->template['NAME'] ?? $this->makeTemplateName(),
			'AUTO_EXECUTE' => $this->autoExecuteType,
			'TEMPLATE'     => [[
				'Type' => 'SequentialWorkflowActivity',
				'Name' => 'Template',
				'Properties' => ['Title' => 'Bizproc Automation template'],
				'Children' => [],
			]],
			'PARAMETERS' => $this->template['PARAMETERS'],
			'CONSTANTS' => $this->template['CONSTANTS'],
			'SYSTEM_CODE'  => 'bitrix_bizproc_automation',
		];

		if ($this->robots)
		{
			$parallelActivity = $this->createParallelActivity();
			$sequence = $this->createSequenceActivity();

			foreach ($this->robots as $i => $robot)
			{
				if ($i !== 0 && !$robot->isExecuteAfterPrevious())
				{
					$parallelActivity['Children'][] = $sequence;
					$sequence = $this->convertRobotToSequenceActivity($robot);
				}
				else
				{
					$sequence['Children'] = array_merge(
						$sequence['Children'],
						$this->convertRobotToSequenceActivity($robot)['Children']
					);
				}
			}

			$parallelActivity['Children'][] = $sequence;

			if (count($parallelActivity['Children']) < 2)
			{
				$parallelActivity['Children'][] = $this->createSequenceActivity();
			}

			$this->template['TEMPLATE'][0]['Children'][] = $parallelActivity;
		}
		$this->robots = null;
		$this->isConverted = false;
	}

	private function convertRobotToSequenceActivity(Robot $robot): array
	{
		$sequence = $this->createSequenceActivity();

		$delayInterval = $robot->getDelayInterval();
		if ($delayInterval && !$delayInterval->isNow())
		{
			$delayName = $robot->getDelayName();
			if (!$delayName)
			{
				$delayName = Robot::generateName();
				$robot->setDelayName($delayName);
			}

			$delayIntervalProperties = $delayInterval->toActivityProperties($this->getDocumentType());

			$sequence['Children'][] = $this->createDelayActivity(
				$delayIntervalProperties,
				$delayName,
				$robot->isActivated()
			);
		}

		$activity = $robot->getBizprocActivity();
		$condition = $robot->getCondition();

		if ($condition && $condition->getItems())
		{
			$activity = $condition->createBizprocActivity($activity, $this->getDocumentType(), $this);
		}

		$sequence['Children'][] = $activity;

		return $sequence;
	}

	protected function isRobot(array $activity)
	{
		if (!in_array($activity['Type'], static::getAvailableRobotClasses($this->getDocumentType())))
		{
			if ($this->isRestRobot($activity))
			{
				return true;
			}

			return false;
		}

		if (!empty($activity['Children']))
		{
			return false;
		}

		return true;
	}

	private function isRestRobot(array $activity): bool
	{
		if (!(mb_strpos($activity['Type'], \CBPRuntime::REST_ACTIVITY_PREFIX) === 0))
		{
			return false;
		}

		if (!empty($activity['Children']))
		{
			return false;
		}

		return true;
	}

	/**
	 * @return Robot[] Robot activities.
	 */
	public function getRobots()
	{
		if ($this->robots === null)
		{
			$this->convertTemplate();
		}

		return $this->robots;
	}

	public function getActivatedRobots(): array
	{
		if ($this->robots === null)
		{
			$this->convertTemplate();
		}

		$activatedRobots = [];
		foreach ($this->robots as $robot)
		{
			if ($robot->isActivated())
			{
				$activatedRobots[] = $robot;
			}
		}

		return $activatedRobots;
	}

	/**
	 * Returns Robot by it`s id.
	 * @param string $name Robot identificator.
	 * @return Robot|null Robot instance.
	 */
	public function getRobotByName(string $name): ?Robot
	{
		foreach ($this->getRobots() as $robot)
		{
			if ($name === $robot->getName())
			{
				return  $robot;
			}
		}

		return  null;
	}

	public function getRobotsByNames(array $names): array
	{
		return array_uintersect($this->getRobots(), $names, function ($lhs, $rhs) {
			$lhsName = is_string($lhs) ? $lhs : $lhs->getName();
			$rhsName = is_string($rhs) ? $rhs : $rhs->getName();

			return strcmp($lhsName, $rhsName);
		});
	}

	/**
	 * @return array Template activities.
	 */
	public function getActivities()
	{
		return $this->template['TEMPLATE'];
	}

	public function getModified(): ?DateTime
	{
		return $this->template['MODIFIED'] ?? null;
	}

	/**
	 * Checks is template was modified by external editor.
	 * @return bool
	 */
	public function isExternalModified()
	{
		if ($this->isExternalModified === null)
			$this->getRobots();

		return ($this->isExternalModified === true);
	}

	public function getDocumentType(): array
	{
		return [$this->template['MODULE_ID'], $this->template['ENTITY'], $this->template['DOCUMENT_TYPE']];
	}

	public function getProperty($object, $field): ?array
	{
		switch ($object)
		{
			case 'Template':
				return $this->template['PARAMETERS'][$field] ?? null;
			case 'Variable':
				return $this->template['VARIABLES'][$field] ?? null;
			case 'Constant':
				return $this->template['CONSTANTS'][$field] ?? null;
			case 'GlobalConst':
				return Bizproc\Workflow\Type\GlobalConst::getVisibleById($field, $this->getDocumentType());
			case 'GlobalVar':
				return Bizproc\Workflow\Type\GlobalVar::getVisibleById($field, $this->getDocumentType());
			case 'Document':
				static $fields;
				if (!$fields)
				{
					$documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
					$fields = $documentService->GetDocumentFields($this->getDocumentType());
				}

				return $fields[$field] ?? null;
			default:
				if ($this->isConverted)
				{
					return $this->findRobotProperty($object, $field);
				}
				else
				{
					return $this->findActivityProperty($object, $field);
				}
		}
	}

	private function findRobotProperty($object, $field): ?array
	{
		$robot = $this->getRobotByName($object);
		return $robot ? $robot->getReturnProperty($field) : null;
	}

	private function findActivityProperty($object, $field): ?array
	{
		$activity = self::findTemplateActivity($this->template['TEMPLATE'], $object);
		if (!$activity)
		{
			return null;
		}

		$props = \CBPRuntime::GetRuntime(true)->getActivityReturnProperties($activity['Type']);
		return $props[$field] ?? null;
	}

	private static function findTemplateActivity(array $template, $id)
	{
		foreach ($template as $activity)
		{
			if ($activity['Name'] === $id)
			{
				return $activity;
			}
			if (is_array($activity['Children']))
			{
				$found = self::findTemplateActivity($activity['Children'], $id);
				if ($found)
				{
					return $found;
				}
			}
		}
		return null;
	}

	private function createSequenceActivity()
	{
		return [
			'Type' => static::$sequenceActivityType,
			'Name' => Robot::generateName(),
			'Properties' => [
				'Title' => 'Automation sequence',
			],
			'Children' => [],
			'Activated' => 'Y',
		];
	}

	private function createParallelActivity()
	{
		return [
			'Type' => static::$parallelActivityType,
			'Name' => Robot::generateName(),
			'Properties' => [
				'Title' => Loc::getMessage('BIZPROC_AUTOMATION_PARALLEL_ACTIVITY'),
			],
			'Children' => [],
			'Activated' => 'Y',
		];
	}

	private function createDelayActivity(array $delayProperties, $delayName, $delayActived)
	{
		if (!isset($delayProperties['Title']))
		{
			$delayProperties['Title'] = Loc::getMessage('BIZPROC_AUTOMATION_ROBOT_DELAY_ACTIVITY');
		}

		return [
			'Type' => static::$robotDelayActivityType,
			'Name' => $delayName,
			'Activated' => $delayActived ? 'Y' : 'N',
			'Properties' => $delayProperties,
			'Children' => [],
		];
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit