Your IP : 13.59.241.124


Current Path : /home/bitrix/ext_www/ballu.in.ua/bitrix/modules/bizproc/lib/automation/engine/
Upload File :
Current File : /home/bitrix/ext_www/ballu.in.ua/bitrix/modules/bizproc/lib/automation/engine/template.php

<?php
namespace Bitrix\Bizproc\Automation\Engine;

use Bitrix\Bizproc\WorkflowTemplateTable;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Error;
use Bitrix\Main\Result;
use Bitrix\Bizproc\Automation;
use Bitrix\Main\Localization\Loc;

Loc::loadMessages(__FILE__);

/**
 * Class Template
 * @package Bitrix\Bizproc\Automation\Engine
 */
class Template
{
	protected static $parallelActivityType = 'ParallelActivity';
	protected static $sequenceActivityType = 'SequenceActivity';
	protected static $delayActivityType = 'DelayActivity';
	protected static $conditionActivityType = 'IfElseActivity';

	protected static $availableActivities = array();
	protected static $availableActivityClasses = array();

	protected $template;
	/** @var  null|Robot[] */
	protected $robots;
	protected $isExternalModified;

	/**
	 * 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 = array(
			'ID' => 0,
			'MODULE_ID' => $documentType[0],
			'ENTITY' => $documentType[1],
			'DOCUMENT_TYPE' => $documentType[2],
			'DOCUMENT_STATUS' => $documentStatus,
			'AUTO_EXECUTE' => \CBPDocumentEventType::Automation
		);

		if ($documentStatus)
		{
			$row = WorkflowTemplateTable::getList([
				'filter' => [
					'=MODULE_ID' => $documentType[0],
					'=ENTITY' => $documentType[1],
					'=DOCUMENT_TYPE' => $documentType[2],
					'=DOCUMENT_STATUS' => $documentStatus,
					'=AUTO_EXECUTE' => \CBPDocumentEventType::Automation
				]
			])->fetch();
			if ($row)
			{
				$this->template = $row;
			}
		}
	}

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

	public function getRobotSettingsDialog(array $robot, $request = null)
	{
		if (isset($robot['Properties']) && is_array($robot['Properties']))
		{
			$robot['Properties'] = $this->convertRobotProperties($robot['Properties'], $this->getDocumentType());
		}

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

		$this->setRobots(array($robot));

		return \CBPActivity::callStaticMethod(
			$robot['Type'],
			"GetPropertiesDialog",
			array(
				$this->getDocumentType(),
				$robot['Name'],
				$this->template['TEMPLATE'],
				array(),
				array(),
				null,
				$request,
				null,
				SITE_ID
			)
		);
	}

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

		if (isset($robot['Properties']) && is_array($robot['Properties']))
		{
			$robot['Properties'] = $this->unConvertRobotProperties($robot['Properties'], $documentType);
		}

		$request = $this->unConvertRobotProperties($request, $documentType);

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

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

		if ($result)
		{
			$templateActivity = \CBPWorkflowTemplateLoader::findActivityByName($raw, $robot['Name']);

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

			$saveResult->setData(array('robot' => $robot));
		}
		else
		{
			foreach ($robotErrors as $i => $error)
			{
				$saveResult->addError(new Error($error['message']));
			}
		}

		return $saveResult;
	}

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

		$this->setRobots($robots);

		$templateResult = $templateId ?
			$this->updateBizprocTemplate($templateId, $userId) : $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']
		];

		$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'] = Loc::getMessage('BIZPROC_AUTOMATION_TEMPLATE_NAME', array(
			'#STATUS#' => $this->template['DOCUMENT_STATUS']
		));
		$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];
			$this->template = $raw;
		}
		catch (\Exception $e)
		{
			$result->addError(new Error($e->getMessage()));
		}

		return $result;
	}

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

		try
		{
			\CBPWorkflowTemplateLoader::update($id, array(
				'TEMPLATE'      => $raw['TEMPLATE'],
				'PARAMETERS'    => array(),
				'VARIABLES'     => array(),
				'CONSTANTS'     => array(),
				'USER_ID' 		=> $userId,
				'MODIFIER_USER' => new \CBPWorkflowTemplateUser($userId),
			));
		}
		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)
		{
			$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)
				{
					$delay = $activity;
					continue;
				}

				if ($activity['Type'] === static::$conditionActivityType)
				{
					$condition = ConditionGroup::convertBizprocActivity($activity, $this->getDocumentType());
					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']);
					$robotActivity->setDelayInterval($delayInterval);
					$robotActivity->setDelayName($delay['Name']);
					$delay = null;
				}

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

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

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

		return $this->robots;
	}

	protected function unConvertTemplate()
	{
		$documentType = $this->getDocumentType();
		$this->template = array(
			'ID' => $this->getId(),
			'MODULE_ID' => $documentType[0],
			'ENTITY' => $documentType[1],
			'DOCUMENT_TYPE' => $documentType[2],
			'DOCUMENT_STATUS' => $this->template['DOCUMENT_STATUS'],
			'AUTO_EXECUTE' => \CBPDocumentEventType::Automation,
			'TEMPLATE'     => array(array(
				'Type' => 'SequentialWorkflowActivity',
				'Name' => 'Template',
				'Properties' => array('Title' => 'Bizproc Automation template'),
				'Children' => array()
			)),
			'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->createSequenceActivity();
				}

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

					$sequence['Children'][] = $this->createDelayActivity(
						$delayInterval->toActivityProperties(),
						$delayName
					);
				}

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

				if ($condition && count($condition->getItems()) > 0)
				{
					$activity = $condition->createBizprocActivity($activity, $documentType);
				}

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

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

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

			$this->template['TEMPLATE'][0]['Children'][] = $parallelActivity;
		}
	}

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

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

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

		return $this->robots;
	}

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

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

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

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

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

		return array(
			'Type' => static::$delayActivityType,
			'Name' => $delayName,
			'Properties' => $delayProperties,
			'Children' => array()
		);
	}

	private function convertRobotProperties(array $properties, array $documentType)
	{
		foreach ($properties as $code => $property)
		{
			if (is_array($property))
			{
				$properties[$code] = self::convertRobotProperties($property, $documentType);
			}
			else
			{
				$properties[$code] = Automation\Helper::convertExpressions($property, $documentType);
			}
		}
		return $properties;
	}

	private function unConvertRobotProperties(array $properties, array $documentType)
	{
		foreach ($properties as $code => $property)
		{
			if (is_array($property))
			{
				$properties[$code] = self::unConvertRobotProperties($property, $documentType);
			}
			else
			{
				$properties[$code] = Automation\Helper::unConvertExpressions($property, $documentType);
			}
		}
		return $properties;
	}

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