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/cvetdv.ru/bitrix/modules/bizproc/classes/general/ |
Upload File : |
<?php use Bitrix\Bizproc; use Bitrix\Bizproc\Result\RenderedResult; use Bitrix\Main; abstract class CBPActivity { use Bizproc\Debugger\Mixins\WriterDebugTrack; public ?CBPActivity $parent = null; public int $executionStatus = CBPActivityExecutionStatus::Initialized; public int $executionResult = CBPActivityExecutionResult::None; private array $arStatusChangeHandlers = []; public const StatusChangedEvent = 0; public const ExecutingEvent = 1; public const CancelingEvent = 2; public const ClosedEvent = 3; public const FaultingEvent = 4; private const ValueSinglePattern = '\{=\s*(?<object>[a-z0-9_]+)\s*\:\s*(?<field>[a-z0-9_\.]+)(\s*>\s*(?<mod1>[a-z0-9_\:]+)(\s*,\s*(?<mod2>[a-z0-9_]+))?)?\s*\}'; public const ValuePattern = '#^\s*'.self::ValueSinglePattern.'\s*$#i'; private const ValueSimplePattern = '#^\s*\{\{(.*?)\}\}\s*$#i'; public const ValueInlinePattern = '#'.self::ValueSinglePattern.'#i'; /** Internal pattern used in calc.php */ public const ValueInternalPattern = '\{=\s*([a-z0-9_]+)\s*\:\s*([a-z0-9_\.]+)(\s*>\s*([a-z0-9_\:]+)(\s*,\s*([a-z0-9_]+))?)?\s*\}'; public const CalcPattern = '#^\s*(=\s*(.*)|\{\{=\s*(.*)\s*\}\})\s*$#is'; public const CalcInlinePattern = '#\{\{=\s*(.*?)\s*\}\}([^\}]|$)#is'; protected array $arProperties = []; protected array $arPropertiesTypes = []; protected string $name = ''; protected bool $activated = true; /** @var CBPWorkflow | \Bitrix\Bizproc\Debugger\Workflow\DebugWorkflow $workflow */ public $workflow = null; public array $arEventsMap = []; protected int $resultPriority = 0; /************************ PROPERTIES ************************************************/ /** * @return array */ public function getDocumentId() { return $this->getRootActivity()->getDocumentId(); } /** * @param array $documentId * @return void */ public function setDocumentId($documentId) { $this->getRootActivity()->setDocumentId($documentId); } /** * @return array */ public function getDocumentType() { $rootActivity = $this->getRootActivity(); if (empty($rootActivity->documentType)) { /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->getService('DocumentService'); $rootActivity->setDocumentType( $documentService->getDocumentType($rootActivity->getDocumentId()) ); } return $rootActivity->documentType; } public function setDocumentType(array $documentType): void { $this->getRootActivity()->documentType = $documentType; } public function getDocumentEventType(): int { return (int)$this->getRootActivity()->getRawProperty(CBPDocument::PARAM_DOCUMENT_EVENT_TYPE); } /** * @return int */ public function getWorkflowStatus() { return $this->getRootActivity()->getWorkflowStatus(); } public function setWorkflowStatus($status) { $this->getRootActivity()->setWorkflowStatus($status); } public function setFieldTypes(array $arFieldTypes = []): void { $rootActivity = $this->getRootActivity(); foreach ($arFieldTypes as $key => $value) { $rootActivity->arFieldTypes[$key] = $value; } } /** * @return int */ public function getWorkflowTemplateId() { $rootActivity = $this->getRootActivity(); //prevent recursion by checking setter if (method_exists($rootActivity, 'setWorkflowTemplateId')) { return $rootActivity->getWorkflowTemplateId(); } return 0; } /** * @return int */ public function getTemplateUserId() { $userId = 0; $rootActivity = $this->getRootActivity(); //prevent recursion by checking setter if (method_exists($rootActivity, 'setTemplateUserId')) { $userId = $rootActivity->getTemplateUserId(); } if (!$userId && $tplId = $this->getWorkflowTemplateId()) { $userId = CBPWorkflowTemplateLoader::getTemplateUserId($tplId); } return $userId; } protected static function getPropertiesMap(array $documentType, array $context = []): array { return []; } /**********************************************************/ protected function clearProperties() { $rootActivity = $this->GetRootActivity(); $documentId = $rootActivity->GetDocumentId(); $documentType = $this->GetDocumentType(); /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); if (is_array($rootActivity->arPropertiesTypes) && count($rootActivity->arPropertiesTypes) > 0 && is_array($rootActivity->arFieldTypes) && count($rootActivity->arFieldTypes) > 0) { foreach ($rootActivity->arPropertiesTypes as $key => $value) { if ($rootActivity->arFieldTypes[$value["Type"]]["BaseType"] == "file") { foreach ((array) $rootActivity->__get($key) as $v) { if (intval($v) > 0) { $iterator = \CFile::getByID($v); if ($file = $iterator->fetch()) { if ($file['MODULE_ID'] === 'bizproc') CFile::Delete($v); } } } } $fieldType = \Bitrix\Bizproc\FieldType::normalizeProperty($value); if ($fieldTypeObject = $documentService->getFieldTypeObject($documentType, $fieldType)) { $fieldTypeObject->setDocumentId($documentId) ->clearValue($rootActivity->arProperties[$key]); } } } } public function getPropertyBaseType($propertyName) { $rootActivity = $this->GetRootActivity(); return $rootActivity->arFieldTypes[$rootActivity->arPropertiesTypes[$propertyName]["Type"]]["BaseType"]; } public function getTemplatePropertyType($propertyName) { $rootActivity = $this->GetRootActivity(); if ($propertyName === 'TargetUser' && !isset($rootActivity->arPropertiesTypes[$propertyName])) { return ['Type' => 'user']; } return $rootActivity->arPropertiesTypes[$propertyName]; } public function setProperties($arProperties = array()) { if (count($arProperties) > 0) { foreach ($arProperties as $key => $value) { $this->arProperties[$key] = $value; } } } public function setPropertiesTypes($arPropertiesTypes = array()) { if (count($arPropertiesTypes) > 0) { foreach ($arPropertiesTypes as $key => $value) { $this->arPropertiesTypes[$key] = $value; } } } public function getPropertyType($propertyName): ?array { return $this->arPropertiesTypes[$propertyName] ?? null; } /**********************************************************/ protected function clearVariables() { $rootActivity = $this->GetRootActivity(); $documentId = $rootActivity->GetDocumentId(); $documentType = $this->GetDocumentType(); /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); if (is_array($rootActivity->arVariablesTypes) && count($rootActivity->arVariablesTypes) > 0 && is_array($rootActivity->arFieldTypes) && count($rootActivity->arFieldTypes) > 0) { foreach ($rootActivity->arVariablesTypes as $key => $value) { if ( isset($rootActivity->arFieldTypes[$value["Type"]]) && $rootActivity->arFieldTypes[$value["Type"]]["BaseType"] === "file" ) { foreach ((array) $rootActivity->arVariables[$key] as $v) { if (intval($v) > 0) { $iterator = \CFile::getByID($v); if ($file = $iterator->fetch()) { if ($file['MODULE_ID'] === 'bizproc') CFile::Delete($v); } } } } $fieldType = \Bitrix\Bizproc\FieldType::normalizeProperty($value); if ($fieldTypeObject = $documentService->getFieldTypeObject($documentType, $fieldType)) { $fieldTypeObject->setDocumentId($documentId) ->clearValue($rootActivity->arVariables[$key]); } } } } public function getVariableBaseType($variableName) { $rootActivity = $this->GetRootActivity(); return $rootActivity->arFieldTypes[$rootActivity->arVariablesTypes[$variableName]["Type"]]["BaseType"]; } public function setVariables($variables = []) { if (!is_array($variables)) { throw new CBPArgumentTypeException("variables", "array"); } if (count($variables) > 0) { $rootActivity = $this->GetRootActivity(); foreach ($variables as $key => $value) { $rootActivity->arVariables[$key] = $value; } } } public function setVariablesTypes($arVariablesTypes = array()) { if (count($arVariablesTypes) > 0) { $rootActivity = $this->GetRootActivity(); foreach ($arVariablesTypes as $key => $value) $rootActivity->arVariablesTypes[$key] = $value; } } public function setVariable($name, $value) { $rootActivity = $this->GetRootActivity(); $rootActivity->arVariables[$name] = $value; } public function getVariable($name) { $rootActivity = $this->GetRootActivity(); if (array_key_exists($name, $rootActivity->arVariables)) { return $rootActivity->arVariables[$name]; } return null; } public function getVariableType($name) { $rootActivity = $this->GetRootActivity(); return isset($rootActivity->arVariablesTypes[$name]) ? $rootActivity->arVariablesTypes[$name] : null; } private function getConstantTypes() { $rootActivity = $this->GetRootActivity(); if (method_exists($rootActivity, 'GetWorkflowTemplateId')) { $templateId = $rootActivity->GetWorkflowTemplateId(); if ($templateId > 0) { return CBPWorkflowTemplateLoader::getTemplateConstants($templateId); } } return null; } public function getConstant($name) { $constants = $this->GetConstantTypes(); if (isset($constants[$name]['Default'])) return $constants[$name]['Default']; return null; } public function getConstantType($name) { $constants = $this->GetConstantTypes(); if (isset($constants[$name])) return $constants[$name]; return array('Type' => null, 'Multiple' => false, 'Required' => false, 'Options' => null); } public function isVariableExists($name) { $rootActivity = $this->GetRootActivity(); $variables = $rootActivity->arVariables ?? []; $variablesTypes = $rootActivity->arVariablesTypes ?? []; return ( array_key_exists($name, $variables) || array_key_exists($name, $variablesTypes) ); } /************************************************/ public function getName(): string { return $this->name; } public function getRootActivity(): CBPActivity { if ($this->workflow) { return $this->workflow->getRootActivity(); } $p = $this; while ($p->parent !== null) { $p = $p->parent; } return $p; } public function setWorkflow(CBPWorkflow $workflow) { $this->workflow = $workflow; } public function unsetWorkflow() { $this->workflow = null; } public function getWorkflowInstanceId() { return $this->workflow->GetInstanceId(); } public function setStatusTitle($title = '') { $rootActivity = $this->GetRootActivity(); $stateService = $this->workflow->GetService("StateService"); if ($rootActivity instanceof CBPStateMachineWorkflowActivity) { $arState = $stateService->GetWorkflowState($this->GetWorkflowInstanceId()); $arActivities = $rootActivity->CollectNestedActivities(); /** @var CBPActivity $activity */ foreach ($arActivities as $activity) if ($activity->GetName() == $arState["STATE_NAME"]) break; $stateService->SetStateTitle( $this->GetWorkflowInstanceId(), $activity->Title.($title != '' ? ": ".$title : '') ); } else { if ($title != '') { $stateService->SetStateTitle( $this->GetWorkflowInstanceId(), $title ); } } } public function addStatusTitle($title = '') { if ($title == '') return; $stateService = $this->workflow->GetService("StateService"); $mainTitle = $stateService->GetStateTitle($this->GetWorkflowInstanceId()); $mainTitle .= ((mb_strpos($mainTitle, ": ") !== false) ? ", " : ": ").$title; $stateService->SetStateTitle($this->GetWorkflowInstanceId(), $mainTitle); } public function deleteStatusTitle($title = '') { if ($title == '') return; $stateService = $this->workflow->GetService("StateService"); $mainTitle = $stateService->GetStateTitle($this->GetWorkflowInstanceId()); $ar1 = explode(":", $mainTitle); if (count($ar1) <= 1) return; $newTitle = ""; $ar2 = explode(",", $ar1[1]); foreach ($ar2 as $a) { $a = trim($a); if ($a != $title) { if ($newTitle <> '') $newTitle .= ", "; $newTitle .= $a; } } $result = $ar1[0].($newTitle <> '' ? ": " : "").$newTitle; $stateService->SetStateTitle($this->GetWorkflowInstanceId(), $result); } private function getPropertyValueRecursive($val, $convertToType = null, ?callable $decorator = null) { // array(2, 5, array("SequentialWorkflowActivity1", "DocumentApprovers")) // array("Document", "IBLOCK_ID") // array("Workflow", "id") // "Hello, {=SequentialWorkflowActivity1:DocumentApprovers}, {=Document:IBLOCK_ID}!" $parsed = static::parseExpression($val); if ($parsed) { $result = null; if ($convertToType) $parsed['modifiers'][] = $convertToType; $this->getRealParameterValue( $parsed['object'], $parsed['field'], $result, $parsed['modifiers'], $decorator ); return array(1, $result); } elseif (is_array($val)) { $b = true; $r = array(); $keys = array_keys($val); $i = 0; foreach ($keys as $key) { if ($key."!" != $i."!") { $b = false; break; } $i++; } foreach ($keys as $key) { [$t, $a] = $this->GetPropertyValueRecursive($val[$key], $convertToType, $decorator); if ($b) { if ($t == 1 && is_array($a)) $r = array_merge($r, $a); else $r[] = $a; } else { $r[$key] = $a; } } if (count($r) == 2) { $keys = array_keys($r); if ($keys[0] == 0 && $keys[1] == 1 && is_string($r[0]) && is_string($r[1])) { $result = null; $modifiers = $convertToType ? array($convertToType) : array(); if ($this->GetRealParameterValue($r[0], $r[1], $result, $modifiers, $decorator)) return array(1, $result); } } return array(2, $r); } else { if (is_string($val)) { $typeClass = null; $fieldTypeObject = null; if ($convertToType) { /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); $documentType = $this->GetDocumentType(); $typesMap = $documentService->getTypesMap($documentType); $convertToType = mb_strtolower($convertToType); if (isset($typesMap[$convertToType])) { $typeClass = $typesMap[$convertToType]; $fieldTypeObject = $documentService->getFieldTypeObject( $documentType, array('Type' => \Bitrix\Bizproc\FieldType::STRING) ); } } $calc = new Bizproc\Calc\Parser($this); if (preg_match(self::CalcPattern, $val)) { $r = $calc->Calculate($val); if ($r !== null) { if ($typeClass && $fieldTypeObject) { if (is_array($r)) $fieldTypeObject->setMultiple(true); $r = $fieldTypeObject->convertValue($r, $typeClass); } return array(is_array($r)? 1 : 2, $r); } } //parse inline calculator $val = preg_replace_callback( static::CalcInlinePattern, function($matches) use ($calc) { $r = $calc->Calculate($matches[1]); if (is_array($r)) $r = implode(', ', CBPHelper::MakeArrayFlat($r)); return $r !== null? $r.$matches[2] : $matches[0]; }, $val ); //parse properties $val = preg_replace_callback( static::ValueInlinePattern, fn($matches) => $this->parseStringParameter($matches, $convertToType, $decorator), $val ); //converting... if ($typeClass && $fieldTypeObject) { $val = $fieldTypeObject->convertValue($val, $typeClass); } } return array(2, $val); } } private function getRealParameterValue( $objectName, $fieldName, &$result, array $modifiers = null, ?callable $decorator = null ) { $return = true; $property = null; /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); if ($objectName === "Document") { $rootActivity = $this->GetRootActivity(); $documentId = $rootActivity->GetDocumentId(); $documentType = $this->GetDocumentType(); $usedDocumentFields = $rootActivity->{CBPDocument::PARAM_USED_DOCUMENT_FIELDS} ?? []; $document = $documentService->GetDocument($documentId, $documentType, $usedDocumentFields); $documentFields = $documentService->GetDocumentFields($documentType); //check aliases $documentFieldsAliasesMap = CBPDocument::getDocumentFieldsAliasesMap($documentFields); if (!isset($document[$fieldName]) && mb_strtoupper(mb_substr($fieldName, -10)) === '_PRINTABLE') { $fieldName = mb_substr($fieldName, 0, -10); if (!in_array('printable', $modifiers)) { $modifiers[] = 'printable'; } } if (!isset($document[$fieldName]) && isset($documentFieldsAliasesMap[$fieldName])) { $fieldName = $documentFieldsAliasesMap[$fieldName]; } $result = ''; if (isset($document[$fieldName])) { $result = $document[$fieldName]; if (is_array($result) && mb_strtoupper(mb_substr($fieldName, -10)) === '_PRINTABLE') { $result = implode(", ", CBPHelper::MakeArrayFlat($result)); } $property = isset($documentFields[$fieldName]) ? $documentFields[$fieldName] : null; } } elseif (in_array($objectName, ['Template', 'Variable', 'Constant'])) { $rootActivity = $this->GetRootActivity(); if (mb_substr($fieldName, -10) == "_printable") { $fieldName = mb_substr($fieldName, 0, -10); $modifiers = ['printable']; } switch ($objectName) { case 'Variable': $result = $rootActivity->GetVariable($fieldName); $property = $rootActivity->getVariableType($fieldName); break; case 'Constant': $result = $rootActivity->GetConstant($fieldName); $property = $rootActivity->GetConstantType($fieldName); break; default: $result = $rootActivity->__get($fieldName); $property = $rootActivity->getTemplatePropertyType($fieldName); } } elseif ($objectName === 'GlobalConst') { $property = Bizproc\Workflow\Type\GlobalConst::getById($fieldName); if (!$property && mb_substr($fieldName, -10) == "_printable") { $fieldName = mb_substr($fieldName, 0, -10); $modifiers = ['printable']; $property = Bizproc\Workflow\Type\GlobalConst::getById($fieldName); } $result = Bizproc\Workflow\Type\GlobalConst::getValue($fieldName); } elseif ($objectName === 'GlobalVar') { $property = Bizproc\Workflow\Type\GlobalVar::getById($fieldName); if (!$property && mb_substr($fieldName, -10) == "_printable") { $fieldName = mb_substr($fieldName, 0, -10); $modifiers = ['printable']; $property = Bizproc\Workflow\Type\GlobalVar::getById($fieldName); } $result = Bizproc\Workflow\Type\GlobalVar::getValue($fieldName); } elseif ($objectName === "Workflow") { $result = $this->GetWorkflowInstanceId(); $property = array('Type' => 'string'); } elseif ($objectName === "User") { if (mb_substr($fieldName, -10) == "_printable") { $modifiers = ['printable']; } $result = 0; if (isset($GLOBALS["USER"]) && is_object($GLOBALS["USER"]) && $GLOBALS["USER"]->isAuthorized()) { $result = "user_".$GLOBALS["USER"]->GetID(); } $property = array('Type' => 'user'); } elseif ($objectName === "System") { if (mb_substr($fieldName, -10) === "_printable") { $fieldName = mb_substr($fieldName, 0, -10); $modifiers = ['printable']; } $result = null; $property = array('Type' => 'datetime'); $systemField = mb_strtolower($fieldName); if ($systemField === 'now') { $result = new Bizproc\BaseType\Value\DateTime(); } elseif ($systemField === 'nowlocal') { $result = new Bizproc\BaseType\Value\DateTime(time(), CTimeZone::GetOffset()); } elseif ($systemField === 'date') { $result = new Bizproc\BaseType\Value\Date(); $property = array('Type' => 'date'); } elseif ($systemField === 'eol') { $result = PHP_EOL; $property = ['Type' => 'string']; } elseif ($systemField === 'hosturl') { $result = Main\Engine\UrlManager::getInstance()->getHostUrl(); $property = ['Type' => 'string']; } if ($result === null) { $return = false; } } elseif ($objectName) { $activity = $this->workflow->GetActivityByName($objectName); if ($activity) { $result = $activity->__get($fieldName); $property = $activity->getPropertyType($fieldName); } else $return = false; } else $return = false; if ($property && $result) { $fieldTypeObject = $documentService->getFieldTypeObject($this->GetDocumentType(), $property); if ($fieldTypeObject) { $fieldTypeObject->setDocumentId($this->GetDocumentId()); $result = $fieldTypeObject->internalizeValue($objectName, $result); } } if ($return) { $result = $this->applyPropertyValueModifiers($fieldName, $property, $result, $modifiers); if ($decorator) { $result = $decorator($objectName, $fieldName, $property, $result); } } return $return; } public function getRuntimeProperty($object, $field, CBPActivity $ownerActivity): array { $rootActivity = $ownerActivity->getRootActivity(); $documentType = $rootActivity->getDocumentType(); $usedDocumentFields = $rootActivity->{CBPDocument::PARAM_USED_DOCUMENT_FIELDS} ?? []; $result = null; $property = null; if (CBPHelper::isEmptyValue($object)) { return [$property, $result]; } elseif ($object === 'Template' || $object === Bizproc\Workflow\Template\SourceType::Parameter) { $result = $rootActivity->__get($field); $property = $rootActivity->getTemplatePropertyType($field); } elseif ($object === Bizproc\Workflow\Template\SourceType::Variable) { $result = $rootActivity->getVariable($field); $property = $rootActivity->getVariableType($field); } elseif ($object === Bizproc\Workflow\Template\SourceType::Constant) { $result = $rootActivity->getConstant($field); $property = $rootActivity->getConstantType($field); } elseif ($object === Bizproc\Workflow\Template\SourceType::GlobalConstant) { $result = Bizproc\Workflow\Type\GlobalConst::getValue($field); $property = Bizproc\Workflow\Type\GlobalConst::getVisibleById($field, $documentType); } elseif ($object === Bizproc\Workflow\Template\SourceType::GlobalVariable) { $result = Bizproc\Workflow\Type\GlobalVar::getValue($field); $property = Bizproc\Workflow\Type\GlobalVar::getVisibleById($field, $documentType); } elseif ($object === Bizproc\Workflow\Template\SourceType::DocumentField) { $documentService = CBPRuntime::getRuntime()->getDocumentService(); $documentFields = $documentService->GetDocumentFields($documentType); $documentId = $rootActivity->getDocumentId(); $property = $documentFields[$field] ?? null; $result = $documentService->getFieldValue($documentId, $field, $documentType, $usedDocumentFields); } else { $activity = $rootActivity->workflow->getActivityByName($object); if ($activity) { $result = $activity->__get($field); $property = $activity->getPropertyType($field); } } if (!$property) { $property = ['Type' => 'string']; } return [$property, $result]; } private function applyPropertyValueModifiers($fieldName, $property, $value, array $modifiers) { if (empty($property) || empty($modifiers) || !is_array($property)) return $value; $typeName = null; $typeClass = null; $format = null; $modifiers = array_slice($modifiers, 0, 2); $rootActivity = $this->GetRootActivity(); $documentId = $rootActivity->GetDocumentId(); /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); $documentType = $this->GetDocumentType(); $typesMap = $documentService->getTypesMap($documentType); foreach ($modifiers as $m) { $m = mb_strtolower($m); if (isset($typesMap[$m])) { $typeName ??= $m; $typeClass ??= $typesMap[$m]; } else { $format = $m; } } $priority = $format && array_search($format, $modifiers) === 0 ? 'format' : 'type'; if ($typeName === \Bitrix\Bizproc\FieldType::STRING && $format === 'printable') { $typeClass = null; } if ($typeClass || $format) { $fieldTypeObject = $documentService->getFieldTypeObject($documentType, $property); if ($fieldTypeObject) { $fieldTypeObject->setDocumentId($documentId); if ($format && $priority === 'format') { $value = $fieldTypeObject->formatValue($value, $format); //$value becomes String $fieldTypeObject->setTypeClass(Bizproc\BaseType\StringType::class); } if ($typeClass) { $value = $fieldTypeObject->convertValue($value, $typeClass); } if ($format && $priority !== 'format') { $value = $fieldTypeObject->formatValue($value, $format); } } elseif ($format == 'printable') // compatibility: old printable style { $value = $documentService->GetFieldValuePrintable( $documentId, $fieldName, $property['Type'], $value, $property ); if (is_array($value)) $value = implode(", ", CBPHelper::MakeArrayFlat($value)); } } return $value; } private function parseStringParameter($matches, $convertToType = null, ?callable $decorator = null) { $result = ""; $modifiers = []; if (!empty($matches['mod1'])) { $modifiers[] = $matches['mod1']; } if (!empty($matches['mod2'])) { $modifiers[] = $matches['mod2']; } if ($convertToType) { $modifiers[] = $convertToType; } if (empty($modifiers)) { $modifiers[] = \Bitrix\Bizproc\FieldType::STRING; } if ($this->getRealParameterValue($matches['object'], $matches['field'], $result, $modifiers, $decorator)) { if (is_array($result)) { $result = implode(", ", CBPHelper::MakeArrayFlat($result)); } } else { $result = $matches[0]; } return $result; } public function parseValue($value, $convertToType = null, ?callable $decorator = null) { [$t, $r] = $this->getPropertyValueRecursive($value, $convertToType, $decorator); return $r; } protected function getRawProperty($name) { if (isset($this->arProperties[$name])) { return $this->arProperties[$name]; } else { $ro = $this->getRootActivity()->getReadOnlyData(); if (isset($ro[$this->getName()]) && isset($ro[$this->getName()][$name])) { return $ro[$this->getName()][$name]; } } return null; } public function __get($name) { $property = $this->getRawProperty($name); if ($property !== null) { [$t, $r] = $this->GetPropertyValueRecursive($property); return $r; } return null; } public function __isset($name) { return $this->isPropertyExists($name); } public function pullProperties(): array { $result = $this->arProperties; $this->arProperties = array_fill_keys(array_keys($this->arProperties), null); return [$this->getName() => $result]; } public function __set($name, $val) { if (array_key_exists($name, $this->arProperties)) { $this->arProperties[$name] = $val; } } public function isPropertyExists($name) { return array_key_exists($name, $this->arProperties); } public function collectNestedActivities() { return null; } public function collectUsages() { $usages = []; $this->collectUsagesRecursive($this->arProperties, $usages); return $usages; } public function collectPropertyUsages($propertyName): array { $usages = []; $this->collectUsagesRecursive($this->getRawProperty($propertyName), $usages); return $usages; } protected function collectUsagesRecursive($val, &$usages) { if (is_array($val)) { foreach ($val as $v) { $this->collectUsagesRecursive($v, $usages); } } elseif (is_string($val)) { $parsed = static::parseExpression($val); if ($parsed) { $usages[] = $this->getObjectSourceType($parsed['object'], $parsed['field']); } else { //TODO: check calc functions /*$calc = new CBPCalc($this); if (preg_match(self::CalcPattern, $val)) { $r = $calc->Calculate($val); } //parse inline calculator $val = preg_replace_callback( static::CalcInlinePattern, function($matches) use ($calc) { $r = $calc->Calculate($matches[1]); }, $val );*/ //parse properties $val = preg_replace_callback( static::ValueInlinePattern, function($matches) use (&$usages) { $usages[] = $this->getObjectSourceType($matches['object'], $matches['field']); }, $val ); } } } protected function getObjectSourceType($objectName, $fieldName) { return \Bitrix\Bizproc\Workflow\Template\SourceType::getObjectSourceType($objectName, $fieldName); } /************************ CONSTRUCTORS *****************************************************/ public function __construct($name) { $this->name = $name; } /************************ DEBUG ***********************************************************/ public function toString() { return $this->name. " [".get_class($this)."] (status=". CBPActivityExecutionStatus::Out($this->executionStatus). ", result=". CBPActivityExecutionResult::Out($this->executionResult). ", count(ClosedEvent)=". count($this->arStatusChangeHandlers[self::ClosedEvent]). ")"; } public function dump($level = 3) { $result = str_repeat(" ", $level).$this->ToString()."\n"; if (is_subclass_of($this, "CBPCompositeActivity")) { /** @var CBPActivity $activity */ foreach ($this->arActivities as $activity) $result .= $activity->Dump($level + 1); } return $result; } /************************ PROCESS ***********************************************************/ public function initialize() { } public function finalize() { } public function execute() { return CBPActivityExecutionStatus::Closed; } protected function reInitialize() { $this->executionStatus = CBPActivityExecutionStatus::Initialized; $this->executionResult = CBPActivityExecutionResult::None; } public function cancel() { return CBPActivityExecutionStatus::Closed; } public function handleFault(Exception $exception) { $status = $this->cancel(); if ($status == CBPActivityExecutionStatus::Canceling) { return CBPActivityExecutionStatus::Faulting; } return $status; } /************************ LOAD / SAVE *******************************************************/ public function fixUpParentChildRelationship(CBPActivity $nestedActivity) { $nestedActivity->parent = $this; } public static function load($stream) { if ($stream == '') { throw new CBPArgumentNullException("stream"); } return CBPRuntime::GetRuntime()->unserializeWorkflowStream($stream); } protected function getACNames() { return array(mb_substr(get_class($this), 3)); } private static function searchUsedActivities(CBPActivity $activity, &$arUsedActivities) { $arT = $activity->GetACNames(); foreach ($arT as $t) { if (!in_array($t, $arUsedActivities)) { $arUsedActivities[] = $t; } } if ($arNestedActivities = $activity->CollectNestedActivities()) { foreach ($arNestedActivities as $nestedActivity) { self::SearchUsedActivities($nestedActivity, $arUsedActivities); } } } public function save() { $usedActivities = []; self::SearchUsedActivities($this, $usedActivities); if ($children = $this->collectNestedActivities()) { /** @var CBPActivity $child */ foreach ($children as $child) { $child->unsetWorkflow(); } } $strUsedActivities = implode(",", $usedActivities); return $strUsedActivities.";".serialize($this); } /************************ STATUS CHANGE HANDLERS **********************************************/ public function addStatusChangeHandler($event, $eventHandler) { if (!is_array($this->arStatusChangeHandlers)) $this->arStatusChangeHandlers = array(); if (!array_key_exists($event, $this->arStatusChangeHandlers)) $this->arStatusChangeHandlers[$event] = array(); $this->arStatusChangeHandlers[$event][] = $eventHandler; } public function removeStatusChangeHandler($event, $eventHandler) { if (!is_array($this->arStatusChangeHandlers)) $this->arStatusChangeHandlers = array(); if (!array_key_exists($event, $this->arStatusChangeHandlers)) $this->arStatusChangeHandlers[$event] = array(); $index = array_search($eventHandler, $this->arStatusChangeHandlers[$event], true); if ($index !== false) unset($this->arStatusChangeHandlers[$event][$index]); } /************************ EVENTS **********************************************************************/ private function fireStatusChangedEvents($event, $arEventParameters = array()) { if (array_key_exists($event, $this->arStatusChangeHandlers) && is_array($this->arStatusChangeHandlers[$event])) { foreach ($this->arStatusChangeHandlers[$event] as $eventHandler) call_user_func_array(array($eventHandler, "OnEvent"), array($this, $arEventParameters)); } } public function setStatus($newStatus, $arEventParameters = array()) { $this->executionStatus = $newStatus; $this->fireStatusChangedEvents(self::StatusChangedEvent, $arEventParameters); switch ($newStatus) { case CBPActivityExecutionStatus::Executing: $this->fireStatusChangedEvents(self::ExecutingEvent, $arEventParameters); break; case CBPActivityExecutionStatus::Canceling: $this->fireStatusChangedEvents(self::CancelingEvent, $arEventParameters); break; case CBPActivityExecutionStatus::Closed: $this->fireStatusChangedEvents(self::ClosedEvent, $arEventParameters); break; case CBPActivityExecutionStatus::Faulting: $this->fireStatusChangedEvents(self::FaultingEvent, $arEventParameters); break; default: return; } } /************************ CREATE *****************************************************************/ public static function includeActivityFile($code) { return CBPRuntime::getRuntime()->includeActivityFile($code); } /** * @param string $code * @param string $name * @return CBPActivity|null * @throws CBPArgumentOutOfRangeException */ public static function createInstance($code, $name) { if (preg_match("#[^a-zA-Z0-9_]#", $code)) { throw new CBPArgumentOutOfRangeException("Activity '" . $code . "' is not valid"); } $classname = 'CBP' . $code; if (class_exists($classname)) { return new $classname($name); } return null; } public static function callStaticMethod($code, $method, $arParameters = array()) { $runtime = CBPRuntime::GetRuntime(); if (!$runtime->IncludeActivityFile($code)) { return [ [ "code" => "ActivityNotFound", "parameter" => $code, "message" => GetMessage("BPGA_ACTIVITY_NOT_FOUND_1", ['#ACTIVITY#' => htmlspecialcharsbx($code)]), ], ]; } if (preg_match("#[^a-zA-Z0-9_]#", $code)) { throw new CBPArgumentOutOfRangeException("Activity '".$code."' is not valid"); } if (strpos($code, 'CBP') === 0) { $code = mb_substr($code, 3); } $classname = 'CBP'.$code; if (method_exists($classname,$method)) { return call_user_func_array(array($classname, $method), $arParameters); } return false; } public function initializeFromArray($arParams) { if (is_array($arParams)) { foreach ($arParams as $key => $value) { if (array_key_exists($key, $this->arProperties)) { $this->arProperties[$key] = $value; } } } } /************************ MARK ****************************************************************/ public function markCanceled($arEventParameters = []) { if ($this->executionStatus != CBPActivityExecutionStatus::Closed) { if ($this->executionStatus != CBPActivityExecutionStatus::Canceling) { throw new CBPInvalidOperationException("InvalidCancelActivityState"); } $this->executionResult = CBPActivityExecutionResult::Canceled; $this->markClosed($arEventParameters); } } public function markCompleted($arEventParameters = []) { $this->executionResult = CBPActivityExecutionResult::Succeeded; $this->markClosed($arEventParameters); } public function markFaulted($arEventParameters = []) { $this->executionResult = CBPActivityExecutionResult::Faulted; $this->markClosed($arEventParameters); } private function markClosed($arEventParameters = []) { switch ($this->executionStatus) { case CBPActivityExecutionStatus::Executing: case CBPActivityExecutionStatus::Canceling: case CBPActivityExecutionStatus::Faulting: { if ($this instanceof \CBPCompositeActivity) { foreach ($this->arActivities as $activity) { if ( ($activity->executionStatus != CBPActivityExecutionStatus::Initialized) && ($activity->executionStatus != CBPActivityExecutionStatus::Closed) ) { throw new CBPInvalidOperationException('ActiveChildExist'); } } } if ($this->isActivated()) { /** @var CBPTrackingService $trackingService */ $trackingService = $this->workflow->getService('TrackingService'); $trackingService->write( $this->getWorkflowInstanceId(), CBPTrackingType::CloseActivity, $this->getName(), $this->executionStatus, $this->executionResult, $this->getTitle() ); } $this->setStatus(CBPActivityExecutionStatus::Closed, $arEventParameters); return; } } throw new CBPInvalidOperationException('InvalidCloseActivityState'); } protected function writeToTrackingService($message = "", $modifiedBy = 0, $trackingType = -1) { /** @var CBPTrackingService $trackingService */ $trackingService = $this->workflow->GetService("TrackingService"); if ($trackingType < 0) $trackingType = CBPTrackingType::Custom; $trackingService->Write($this->GetWorkflowInstanceId(), $trackingType, $this->name, $this->executionStatus, $this->executionResult, ($this->IsPropertyExists("Title") ? $this->Title : ""), $message, $modifiedBy); } protected function fixResult(Bitrix\Bizproc\Result\ResultDto $result): void { $workflowId = $this->getWorkflowInstanceId(); try { Bizproc\Result\Entity\ResultTable::upsert([ 'WORKFLOW_ID' => $workflowId, 'PRIORITY' => $this->resultPriority, 'ACTIVITY' => $result->activity, 'RESULT' => $result->data, ]); } catch (Throwable $e) { $this->trackError($e->getMessage()); } } public static function renderResult(array $result, string $workflowId, int $userId): RenderedResult { if (!self::checkResultViewRights($result, $workflowId, $userId)) { return RenderedResult::makeNoRights(); } $documentService = CBPRuntime::getRuntime()->getDocumentService(); if (isset($result['DOCUMENT_ID'])) { $url = $documentService->getDocumentDetailUrl($result['DOCUMENT_ID']); $name = $documentService->getDocumentName($result['DOCUMENT_ID']); if (isset($result['DOCUMENT_TYPE'])) { $type = (string)$documentService->getDocumentTypeCaption($result['DOCUMENT_TYPE']); $name = $type . ': ' . $name; } return new RenderedResult('[URL=' . $url . ']' . $name . '[/URL]', RenderedResult::BB_CODE_RESULT); } return RenderedResult::makeNoResult(); } protected static function checkResultViewRights(array $result, string $workflowId, int $userId): bool { $currentUser = new \CBPWorkflowTemplateUser($userId); $userCanReadDocument = false; if (isset($result['DOCUMENT_ID'])) { $userCanReadDocument = \CBPDocument::canUserOperateDocument( \CBPCanUserOperateOperation::ReadDocument, $currentUser->getId(), $result['DOCUMENT_ID'], ); } return $currentUser->isAdmin() || self::checkUserAccessWithSubordination($currentUser->getId(), $result['USERS'] ?? []) || $userCanReadDocument; } protected static function checkUserAccessWithSubordination(int $userId, array $users): bool { if (in_array($userId, $users, true)) { return true; } foreach ($users as $user) { if (\CBPHelper::checkUserSubordination($userId, $user)) { return true; } } return false; } protected function trackError(string $errorMsg) { $this->writeToTrackingService($errorMsg, 0, \CBPTrackingType::Error); } protected function getDebugInfo(array $values = [], array $map = []): array { if (count($map) <= 0) { $map = static::getPropertiesMap($this->getDocumentType()); } foreach ($map as $key => &$property) { if (is_string($property)) { $property = [ 'Name' => $property, 'Type' => 'string', ]; } if (!array_key_exists('TrackType', $property)) { $property['TrackType'] = CBPTrackingType::Debug; } if (array_key_exists('TrackValue', $property)) { continue; } if (!array_key_exists($key, $values)) { $property['TrackValue'] = $this->__get($key); continue; } $property['TrackValue'] = $values[$key]; } return $map; } protected function writeDebugInfo(array $map) { if (!$this->workflow->isDebug()) { return; } /** @var CBPDocumentService $documentService */ $documentService = $this->workflow->GetService("DocumentService"); foreach ($map as $property) { if (is_string($property)) { $property = [ 'Name' => $property, 'Type' => 'string', ]; } $fieldType = $documentService->getFieldTypeObject($this->getDocumentType(), $property); if (!$fieldType) { if (!array_key_exists('BaseType', $property)) { continue; } $property['Type'] = $property['BaseType']; $fieldType = $documentService->getFieldTypeObject($this->getDocumentType(), $property); if (!$fieldType) { continue; } } $value = $fieldType->formatValue($property['TrackValue']); $value = ($value !== '') ? $value : '[]'; $this->writeDebugTrack( $this->getWorkflowInstanceId(), $this->getName(), $this->executionStatus, $this->executionResult, $this->getTitle(), $this->preparePropertyForWritingToTrack($value, $property['Name'] ?? ''), $property['TrackType'] ?? \CBPTrackingType::Debug ); } } public function getTitle(): string { $activityTitle = $this->isPropertyExists('Title') ? $this->Title : ''; if (is_string($activityTitle)) { return $activityTitle; } return ''; } public function setActivated(bool $activated): void { $this->activated = $activated; } public function isActivated(): bool { return $this->activated; } public static function validateProperties($arTestProperties = array(), CBPWorkflowTemplateUser $user = null) { return array(); } public static function validateChild($childActivity, $bFirstChild = false) { return array(); } public static function &findActivityInTemplate(&$arWorkflowTemplate, $activityName) { return CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName); } public static function isExpression($text) { if (is_string($text)) { $text = trim($text); if ( preg_match(static::CalcPattern, $text) || preg_match(static::ValuePattern, $text) || preg_match(self::ValueSimplePattern, $text) ) { return true; } } return false; } public static function parseExpression($exp): ?array { $matches = null; if (is_string($exp) && preg_match(static::ValuePattern, $exp, $matches)) { $result = [ 'object' => $matches['object'], 'field' => $matches['field'], 'modifiers' => [], ]; if (!empty($matches['mod1'])) { $result['modifiers'][] = $matches['mod1']; } if (!empty($matches['mod2'])) { $result['modifiers'][] = $matches['mod2']; } return $result; } return null; } protected function getStorage(): Bizproc\Storage\ActivityStorage { return $this->getStorageFactory()->getActivityStorage($this); } private function getStorageFactory(): Bizproc\Storage\Factory { return Bizproc\Storage\Factory::getInstance(); } }