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/lib/ |
Upload File : |
<?php namespace Bitrix\Bizproc; use Bitrix\Bizproc\Workflow\Entity\WorkflowInstanceTable; use Bitrix\Main\DB\SqlQueryException; use Bitrix\Main\Loader; use Bitrix\Rest\AppLangTable; use Bitrix\Rest\AppTable; use Bitrix\Rest\HandlerHelper; use Bitrix\Rest\PlacementTable; use Bitrix\Rest\RestException; use Bitrix\Rest\AccessException; Loader::includeModule('rest'); class RestService extends \IRestService { public const SCOPE = 'bizproc'; public const PLACEMENT_ACTIVITY_PROPERTIES_DIALOG = 'BIZPROC_ACTIVITY_PROPERTIES_DIALOG'; protected static $app; private static $allowedOperations = ['', '!', '<', '<=', '>', '>=']; //, '><', '!><', '?', '=', '!=', '%', '!%', ''); May be later? const ERROR_ACTIVITY_ALREADY_INSTALLED = 'ERROR_ACTIVITY_ALREADY_INSTALLED'; const ERROR_ACTIVITY_ADD_FAILURE = 'ERROR_ACTIVITY_ADD_FAILURE'; const ERROR_ACTIVITY_VALIDATION_FAILURE = 'ERROR_ACTIVITY_VALIDATION_FAILURE'; const ERROR_ACTIVITY_NOT_FOUND = 'ERROR_ACTIVITY_NOT_FOUND'; const ERROR_EMPTY_LOG_MESSAGE = 'ERROR_EMPTY_LOG_MESSAGE'; const ERROR_WRONG_WORKFLOW_ID = 'ERROR_WRONG_WORKFLOW_ID'; const ERROR_TEMPLATE_VALIDATION_FAILURE = 'ERROR_TEMPLATE_VALIDATION_FAILURE'; const ERROR_TEMPLATE_NOT_FOUND = 'ERROR_TEMPLATE_NOT_FOUND'; const ERROR_TEMPLATE_NOT_OWNER = 'ERROR_TEMPLATE_NOT_OWNER'; const ERROR_TASK_VALIDATION = 'ERROR_TASK_VALIDATION'; const ERROR_TASK_NOT_FOUND = 'ERROR_TASK_NOT_FOUND'; const ERROR_TASK_TYPE = 'ERROR_TASK_TYPE'; const ERROR_TASK_COMPLETED = 'ERROR_TASK_COMPLETED'; const ERROR_TASK_EXECUTION = 'ERROR_TASK_EXECUTION'; const ERROR_SELECT_VALIDATION_FAILURE = 'ERROR_SELECT_VALIDATION_FAILURE'; private const ALLOWED_TASK_ACTIVITIES = [ 'ReviewActivity', 'ApproveActivity', 'RequestInformationActivity', 'RequestInformationOptionalActivity' ]; public static function onRestServiceBuildDescription() { $map = []; if (self::isEnabled()) { $map = [ //activity 'bizproc.activity.add' => [__CLASS__, 'addActivity'], 'bizproc.activity.update' => [__CLASS__, 'updateActivity'], 'bizproc.activity.delete' => [__CLASS__, 'deleteActivity'], 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'], 'bizproc.activity.list' => [__CLASS__, 'getActivityList'], //event 'bizproc.event.send' => [__CLASS__, 'sendEvent'], //task 'bizproc.task.list' => [__CLASS__, 'getTaskList'], 'bizproc.task.complete' => [__CLASS__, 'completeTask'], //workflow 'bizproc.workflow.terminate' => [__CLASS__, 'terminateWorkflow'], 'bizproc.workflow.kill' => [__CLASS__, 'killWorkflow'], 'bizproc.workflow.start' => [__CLASS__, 'startWorkflow'], //workflow.instance 'bizproc.workflow.instance.list' => [__CLASS__, 'getWorkflowInstances'], //workflow.template 'bizproc.workflow.template.list' => [__CLASS__, 'getWorkflowTemplates'], 'bizproc.workflow.template.add' => [__CLASS__, 'addWorkflowTemplate'], 'bizproc.workflow.template.update' => [__CLASS__, 'updateWorkflowTemplate'], 'bizproc.workflow.template.delete' => [__CLASS__, 'deleteWorkflowTemplate'], //aliases 'bizproc.workflow.instances' => [__CLASS__, 'getWorkflowInstances'], ]; } if ( self::isEnabled() || self::isEnabled('crm_automation_lead') || self::isEnabled('crm_automation_deal') || self::isEnabled('crm_automation_order') || self::isEnabled('tasks_automation') ) { $map = array_merge($map, array( 'bizproc.event.send' => [__CLASS__, 'sendEvent'], 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'], //robot 'bizproc.robot.add' => array(__CLASS__, 'addRobot'), 'bizproc.robot.update' => array(__CLASS__, 'updateRobot'), 'bizproc.robot.delete' => array(__CLASS__, 'deleteRobot'), 'bizproc.robot.list' => array(__CLASS__, 'getRobotList'), //provider 'bizproc.provider.add' => [__CLASS__, 'addProvider'], 'bizproc.provider.delete' => [__CLASS__, 'deleteProvider'], 'bizproc.provider.list' => [__CLASS__, 'getProviderList'], )); } //placements $map[\CRestUtil::PLACEMENTS] = [ static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG => ['private' => true], ]; return [ static::SCOPE => $map, ]; } private static function isEnabled(string $feature = 'bizproc'): bool { if (Loader::includeModule('bitrix24')) { return \Bitrix\Bitrix24\Feature::isFeatureEnabled($feature); } return true; } /** * Deletes application activities. * @param array $fields Fields describes application. * @return void */ public static function onRestAppDelete(array $fields) { $fields = array_change_key_case($fields, CASE_UPPER); if (empty($fields['APP_ID'])) return; if (!Loader::includeModule('rest')) return; $dbRes = AppTable::getById($fields['APP_ID']); $app = $dbRes->fetch(); if(!$app) return; $iterator = RestActivityTable::getList(array( 'select' => array('ID'), 'filter' => array('=APP_ID' => $app['CLIENT_ID']) )); while ($activity = $iterator->fetch()) { RestActivityTable::delete($activity['ID']); } $iterator = RestProviderTable::getList(array( 'select' => array('ID'), 'filter' => array('=APP_ID' => $app['CLIENT_ID']) )); while ($activity = $iterator->fetch()) { RestProviderTable::delete($activity['ID']); } self::deleteAppPlacement($app['ID']); } /** * Deletes application activities. * @param array $fields Fields describes application. * @return void */ public static function onRestAppUpdate(array $fields) { static::onRestAppDelete($fields); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function addActivity($params, $n, $server) { return self::addActivityInternal($params, $server, false); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function addRobot($params, $n, $server) { return self::addActivityInternal($params, $server, true); } /** * @param array $params * @param \CRestServer $server * @param bool $isRobot * @return bool * @throws AccessException * @throws RestException */ private static function addActivityInternal($params, $server, $isRobot = false) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $params = self::prepareActivityData($params); if ($isRobot) self::validateRobot($params, $server); else self::validateActivity($params, $server); $appId = self::getAppId($server->getClientId()); $params['APP_ID'] = $server->getClientId(); $params['INTERNAL_CODE'] = self::generateInternalCode($params); $params['APP_NAME'] = self::getAppName($params['APP_ID']); $iterator = RestActivityTable::getList(array( 'select' => array('ID'), 'filter' => array('=INTERNAL_CODE' => $params['INTERNAL_CODE']) )); $result = $iterator->fetch(); if ($result) { throw new RestException('Activity or Robot already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED); } $params['AUTH_USER_ID'] = isset($params['AUTH_USER_ID'])? (int) $params['AUTH_USER_ID'] : 0; $params['IS_ROBOT'] = $isRobot ? 'Y' : 'N'; $params['USE_PLACEMENT'] = (isset($params['USE_PLACEMENT']) && $params['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N'; if ($params['USE_PLACEMENT'] === 'Y') { self::validateActivityHandler($params['PLACEMENT_HANDLER'] ?? null, $server); self::upsertAppPlacement($appId, $params['CODE'], $params['PLACEMENT_HANDLER'] ?? null); } try { $result = RestActivityTable::add($params); } catch (SqlQueryException $exception) { throw new RestException('Activity or Robot already added!', self::ERROR_ACTIVITY_ADD_FAILURE); } if ($result->getErrors()) { if ($params['USE_PLACEMENT'] === 'Y') { self::deleteAppPlacement($appId, $params['CODE']); } throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function updateActivity($params, $n, $server) { return self::updateActivityInternal($params, $server, false); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function deleteActivity($params, $n, $server) { return self::deleteActivityInternal($params, $server, false); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function updateRobot($params, $n, $server) { return self::updateActivityInternal($params, $server, true); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function deleteRobot($params, $n, $server) { return self::deleteActivityInternal($params, $server, true); } /** * @param array $params * @param \CRestServer $server * @param bool $isRobot * @return bool * @throws AccessException * @throws RestException */ private static function deleteActivityInternal($params, $server, $isRobot = false) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } $params = array_change_key_case($params, CASE_UPPER); self::checkAdminPermissions(); self::validateActivityCode($params['CODE']); $params['APP_ID'] = $server->getClientId(); $internalCode = self::generateInternalCode($params); $iterator = RestActivityTable::getList(array( 'select' => array('ID'), 'filter' => array( '=INTERNAL_CODE' => $internalCode, '=IS_ROBOT' => $isRobot ? 'Y' : 'N' ) )); $result = $iterator->fetch(); if (!$result) { throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND); } RestActivityTable::delete($result['ID']); self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']); return true; } /** * @param array $params * @param \CRestServer $server * @param bool $isRobot * @return bool * @throws AccessException * @throws RestException * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\ObjectPropertyException * @throws \Bitrix\Main\SystemException */ private static function updateActivityInternal($params, $server, $isRobot = false) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } $params = self::prepareActivityData($params); self::checkAdminPermissions(); self::validateActivityCode($params['CODE']); $params['APP_ID'] = $server->getClientId(); $internalCode = self::generateInternalCode($params); $iterator = RestActivityTable::getList(array( 'select' => array('ID'), 'filter' => array( '=INTERNAL_CODE' => $internalCode, '=IS_ROBOT' => $isRobot ? 'Y' : 'N' ) )); $result = $iterator->fetch(); if (!$result) { throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND); } $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null; if (!$fields) { throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } $toUpdate = []; if (isset($fields['HANDLER'])) { self::validateActivityHandler($fields['HANDLER'], $server); $toUpdate['HANDLER'] = $fields['HANDLER']; } if (isset($fields['AUTH_USER_ID'])) { $toUpdate['AUTH_USER_ID'] = (int) $fields['AUTH_USER_ID']; } if (isset($fields['USE_SUBSCRIPTION'])) { $toUpdate['USE_SUBSCRIPTION'] = (string) $fields['USE_SUBSCRIPTION']; } if (isset($fields['USE_PLACEMENT'])) { $toUpdate['USE_PLACEMENT'] = ($fields['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N'; } if (!empty($fields['NAME'])) { $toUpdate['NAME'] = $fields['NAME']; } if (isset($fields['DESCRIPTION'])) { $toUpdate['DESCRIPTION'] = $fields['DESCRIPTION']; } if (isset($fields['PROPERTIES'])) { self::validateActivityProperties($fields['PROPERTIES']); $toUpdate['PROPERTIES'] = $fields['PROPERTIES']; } if (isset($fields['RETURN_PROPERTIES'])) { self::validateActivityProperties($fields['RETURN_PROPERTIES']); $toUpdate['RETURN_PROPERTIES'] = $fields['RETURN_PROPERTIES']; } if (isset($fields['DOCUMENT_TYPE'])) { if (empty($fields['DOCUMENT_TYPE'])) { $toUpdate['DOCUMENT_TYPE'] = null; } else { static::validateActivityDocumentType($fields['DOCUMENT_TYPE']); $toUpdate['DOCUMENT_TYPE'] = $fields['DOCUMENT_TYPE']; } } if (isset($fields['FILTER'])) { if (empty($fields['FILTER'])) { $toUpdate['FILTER'] = null; } else { if (!is_array($fields['FILTER'])) { throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } $toUpdate['FILTER'] = $fields['FILTER']; } } if (!$toUpdate) { throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } if (isset($fields['PLACEMENT_HANDLER'])) { self::validateActivityHandler($fields['PLACEMENT_HANDLER'], $server); self::upsertAppPlacement(self::getAppId($params['APP_ID']), $params['CODE'], $fields['PLACEMENT_HANDLER']); } if (isset($toUpdate['USE_PLACEMENT']) && $toUpdate['USE_PLACEMENT'] === 'N') { self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']); } $updateResult = RestActivityTable::update($result['ID'], $toUpdate); if (!$updateResult->isSuccess()) { throw new RestException( implode('; ', $updateResult->getErrorMessages()), self::ERROR_ACTIVITY_VALIDATION_FAILURE ); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws AccessException * @throws RestException */ public static function sendEvent($params, $n, $server) { $params = array_change_key_case($params, CASE_UPPER); [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']); $errors = []; \CBPDocument::sendExternalEvent( $workflowId, $activityName, [ 'EVENT_ID' => $eventId, 'RETURN_VALUES' => $params['RETURN_VALUES'] ?? [], 'LOG_MESSAGE' => $params['LOG_MESSAGE'] ?? '', ], $errors, ); if ($errors) { $error = current($errors); throw new RestException($error['message'], $error['code']); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws AccessException * @throws RestException */ public static function writeActivityLog($params, $n, $server) { $params = array_change_key_case($params, CASE_UPPER); [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']); $logMessage = isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : ''; if (empty($logMessage)) throw new RestException('Empty log message!', self::ERROR_EMPTY_LOG_MESSAGE); $errors = []; \CBPDocument::sendExternalEvent( $workflowId, $activityName, [ 'EVENT_ID' => $eventId, 'LOG_ACTION' => true, 'LOG_MESSAGE' => $logMessage ], $errors, ); if ($errors) { $error = current($errors); throw new RestException($error['message'], $error['code']); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return array * @throws AccessException * @throws \Bitrix\Main\ArgumentException */ public static function getActivityList($params, $n, $server) { return self::getActivityListInternal($params, $server, false); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return array * @throws AccessException * @throws \Bitrix\Main\ArgumentException */ public static function getRobotList($params, $n, $server) { return self::getActivityListInternal($params, $server, true); } /** * @param array $params * @param \CRestServer $server * @param bool $isRobot * @return array * @throws AccessException */ private static function getActivityListInternal($params, $server, $isRobot = false) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $iterator = RestActivityTable::getList(array( 'select' => array('CODE'), 'filter' => array( '=APP_ID' => $server->getClientId(), '=IS_ROBOT' => $isRobot ? 'Y' : 'N' ) )); $result = array(); while ($row = $iterator->fetch()) { $result[] = $row['CODE']; } return $result; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return \Countable * @throws AccessException * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\SystemException */ public static function getWorkflowInstances($params, $n, $server) { self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); $fields = [ 'ID' => 'ID', 'MODIFIED' => 'MODIFIED', 'OWNED_UNTIL' => 'OWNED_UNTIL', 'MODULE_ID' => 'MODULE_ID', 'ENTITY' => 'ENTITY', 'DOCUMENT_ID' => 'DOCUMENT_ID', 'STARTED' => 'STARTED', 'STARTED_BY' => 'STARTED_BY', 'TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID', ]; $select = static::getSelect($params['SELECT'] ?? null, $fields, ['ID', 'MODIFIED', 'OWNED_UNTIL']); $filter = static::getFilter($params['FILTER'] ?? null, $fields, ['MODIFIED', 'OWNED_UNTIL', 'STARTED']); $order = static::getOrder($params['ORDER'] ?? null, $fields, ['MODIFIED' => 'DESC']); $shouldCountTotal = ($n >= 0); $iterator = WorkflowInstanceTable::getList([ 'select' => $select, 'filter' => $filter, 'order' => $order, 'limit' => static::LIST_LIMIT, 'offset' => max(0, (int)$n), 'count_total' => $shouldCountTotal, ]); $result = []; while ($row = $iterator->fetch()) { if (isset($row['MODIFIED'])) { $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']); } if (isset($row['STARTED'])) { $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']); } if (isset($row['OWNED_UNTIL'])) { $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']); } $result[] = $row; } $count = $shouldCountTotal ? $iterator->getCount() : 0; return static::setNavData($result, ['count' => $count, 'offset' => $n]); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool True on success. * @throws AccessException * @throws RestException */ public static function terminateWorkflow($params, $n, $server) { self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); if (empty($params['ID'])) { throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID); } if (!is_string($params['ID'])) { throw new RestException('Invalid workflow instance ID (string expected)', self::ERROR_WRONG_WORKFLOW_ID); } $id = $params['ID']; $status = isset($params['STATUS']) ? (string)$params['STATUS'] : ''; $errors = []; if (!\CBPDocument::terminateWorkflow($id, [], $errors, $status)) { throw new RestException($errors[0]['message']); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool True on success. * @throws AccessException * @throws RestException */ public static function killWorkflow($params, $n, $server) { self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); if (empty($params['ID'])) { throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID); } $id = $params['ID']; $errors = \CBPDocument::killWorkflow($id); if ($errors) { throw new RestException($errors[0]['message']); } return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return string Workflow ID. * @throws AccessException * @throws RestException */ public static function startWorkflow($params, $n, $server) { $params = array_change_key_case($params, CASE_UPPER); if (empty($params['TEMPLATE_ID'])) { throw new RestException('Empty TEMPLATE_ID', self::ERROR_WRONG_WORKFLOW_ID); } $templateId = (int)$params['TEMPLATE_ID']; $tplDocumentType = self::getTemplateDocumentType($templateId); if (!$tplDocumentType) { throw new RestException('Template not found', self::ERROR_WRONG_WORKFLOW_ID); } //hotfix #0120474 $getParams = array_change_key_case($_GET, CASE_UPPER); if (isset($getParams['DOCUMENT_ID']) && is_array($getParams['DOCUMENT_ID'])) { $params['DOCUMENT_ID'] = $getParams['DOCUMENT_ID']; } $documentId = self::getDocumentId($params['DOCUMENT_ID']); if (!$documentId) { throw new RestException('Wrong DOCUMENT_ID!'); } $documentType = self::getDocumentType($documentId); if (!$documentType) { throw new RestException('Incorrect document type!'); } if (!\CBPHelper::isEqualDocument($tplDocumentType, $documentType)) { throw new RestException('Template type and DOCUMENT_ID mismatch!'); } self::checkStartWorkflowPermissions($documentId, $templateId); $workflowParameters = isset($params['PARAMETERS']) && is_array($params['PARAMETERS']) ? $params['PARAMETERS'] : []; $workflowParameters[\CBPDocument::PARAM_TAGRET_USER] = 'user_' . self::getCurrentUserId(); $errors = []; $workflowId = \CBPDocument::startWorkflow($templateId, $documentId, $workflowParameters, $errors); if (!$workflowId) { throw new RestException($errors[0]['message']); } return $workflowId; } private static function checkStartWorkflowPermissions(array $documentId, $templateId) { if (static::isAdmin()) { return true; } if ( \CBPDocument::CanUserOperateDocument( \CBPCanUserOperateOperation::StartWorkflow, static::getCurrentUserId(), $documentId, ['WorkflowTemplateId' => $templateId] ) ) { return true; } throw new AccessException(); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return \Countable Templates collection. * @throws AccessException * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\ObjectPropertyException * @throws \Bitrix\Main\SystemException */ public static function getWorkflowTemplates($params, $n, $server) { self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); $fields = array( 'ID' => 'ID', 'MODULE_ID' => 'MODULE_ID', 'ENTITY' => 'ENTITY', 'DOCUMENT_TYPE' => 'DOCUMENT_TYPE', 'AUTO_EXECUTE' => 'AUTO_EXECUTE', 'NAME' => 'NAME', 'DESCRIPTION' => 'DESCRIPTION', 'TEMPLATE' => 'TEMPLATE', 'PARAMETERS' => 'PARAMETERS', 'VARIABLES' => 'VARIABLES', 'CONSTANTS' => 'CONSTANTS', 'MODIFIED' => 'MODIFIED', 'IS_MODIFIED' => 'IS_MODIFIED', 'USER_ID' => 'USER_ID', 'SYSTEM_CODE' => 'SYSTEM_CODE', ); $select = static::getSelect($params['SELECT'] ?? null, $fields, ['ID']); $filter = static::getFilter($params['FILTER'] ?? null, $fields, ['MODIFIED']); $filter['<AUTO_EXECUTE'] = \CBPDocumentEventType::Automation; $order = static::getOrder($params['ORDER'] ?? null, $fields, ['ID' => 'ASC']); $shouldCountTotal = ($n >= 0); $iterator = WorkflowTemplateTable::getList(array( 'select' => $select, 'filter' => $filter, 'order' => $order, 'limit' => static::LIST_LIMIT, 'offset' => max(0, (int)$n), 'count_total' => $shouldCountTotal, )); $countTotal = $shouldCountTotal ? $iterator->getCount() : 0; $iterator = new \CBPWorkflowTemplateResult($iterator, \CBPWorkflowTemplateLoader::useGZipCompression()); $result = array(); while ($row = $iterator->fetch()) { if (isset($row['MODIFIED'])) $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']); if (isset($row['STARTED'])) $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']); if (isset($row['OWNED_UNTIL'])) $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']); $result[] = $row; } return static::setNavData($result, ['count' => $countTotal, 'offset' => $n]); } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function addWorkflowTemplate($params, $n, $server) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); self::validateTemplateDocumentType($params['DOCUMENT_TYPE']); self::validateTemplateName($params['NAME']); $autoExecute = \CBPDocumentEventType::None; if (isset($params['AUTO_EXECUTE'])) { self::validateTemplateAutoExecution($params['AUTO_EXECUTE']); $autoExecute = (int) $params['AUTO_EXECUTE']; } $data = self::prepareTemplateData($params['TEMPLATE_DATA']); try { return \CBPWorkflowTemplateLoader::ImportTemplate( 0, $params['DOCUMENT_TYPE'], $autoExecute, $params['NAME'], isset($params['DESCRIPTION']) ? (string) $params['DESCRIPTION'] : '', $data, self::generateTemplateSystemCode($server) ); } catch (\Exception $e) { throw new RestException($e->getMessage()); } } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function updateWorkflowTemplate($params, $n, $server) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null; if (!$fields) { throw new RestException("No fields to update.", self::ERROR_TEMPLATE_VALIDATION_FAILURE); } $tpl = WorkflowTemplateTable::getList(array( 'select' => ['ID', 'SYSTEM_CODE', 'NAME', 'DESCRIPTION', 'AUTO_EXECUTE', 'MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'], 'filter' => ['=ID' => (int) $params['ID']], ))->fetch(); if (!$tpl) { throw new RestException("Workflow template not found.", self::ERROR_TEMPLATE_NOT_FOUND); } if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server)) { throw new RestException( "You can update ONLY templates created by current application", self::ERROR_TEMPLATE_NOT_OWNER, ); } if (isset($fields['NAME'])) { self::validateTemplateName($fields['NAME']); $tpl['NAME'] = $fields['NAME']; } if (isset($fields['DESCRIPTION'])) { $tpl['DESCRIPTION'] = (string) $fields['DESCRIPTION']; } if (isset($fields['AUTO_EXECUTE'])) { self::validateTemplateAutoExecution($fields['AUTO_EXECUTE']); $tpl['AUTO_EXECUTE'] = (int) $fields['AUTO_EXECUTE']; } if (isset($fields['TEMPLATE_DATA'])) { $data = self::prepareTemplateData($fields['TEMPLATE_DATA']); return \CBPWorkflowTemplateLoader::ImportTemplate( $tpl['ID'], [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']], $tpl['AUTO_EXECUTE'], $tpl['NAME'], $tpl['DESCRIPTION'], $data, $tpl['SYSTEM_CODE'] ); } else { return \CBPWorkflowTemplateLoader::Update($tpl['ID'], [ 'NAME' => $tpl['NAME'], 'DESCRIPTION' => $tpl['DESCRIPTION'], 'AUTO_EXECUTE' => $tpl['AUTO_EXECUTE'], ]); } } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function deleteWorkflowTemplate($params, $n, $server) { if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $params = array_change_key_case($params, CASE_UPPER); $tpl = WorkflowTemplateTable::getList(array( 'select' => ['ID', 'SYSTEM_CODE'], 'filter' => ['=ID' => (int) $params['ID']], ))->fetch(); if (!$tpl) { throw new RestException("Workflow template not found.", self::ERROR_TEMPLATE_NOT_FOUND); } if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server)) { throw new RestException("You can delete ONLY templates created by current application"); } try { \CBPWorkflowTemplateLoader::Delete($tpl['ID']); } catch (\CBPInvalidOperationException $e) { throw new RestException($e->getMessage()); } } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return array * @throws AccessException */ public static function getTaskList($params, $n, $server) { $params = array_change_key_case($params, CASE_UPPER); $fields = array( 'ID' => 'ID', 'ACTIVITY' => 'ACTIVITY', 'ACTIVITY_NAME' => 'ACTIVITY_NAME', 'WORKFLOW_ID' => 'WORKFLOW_ID', 'DOCUMENT_NAME' => 'DOCUMENT_NAME', 'DESCRIPTION' => 'DESCRIPTION', 'NAME' => 'NAME', 'MODIFIED' => 'MODIFIED', 'WORKFLOW_STARTED' => 'WORKFLOW_STARTED', 'WORKFLOW_STARTED_BY' => 'WORKFLOW_STARTED_BY', 'OVERDUE_DATE' => 'OVERDUE_DATE', 'WORKFLOW_TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID', 'WORKFLOW_TEMPLATE_NAME' => 'WORKFLOW_TEMPLATE_NAME', 'WORKFLOW_STATE' => 'WORKFLOW_STATE', 'STATUS' => 'STATUS', 'USER_ID' => 'USER_ID', 'USER_STATUS' => 'USER_STATUS', 'MODULE_ID' => 'MODULE_ID', 'ENTITY' => 'ENTITY', 'DOCUMENT_ID' => 'DOCUMENT_ID', 'PARAMETERS' => 'PARAMETERS', ); $select = static::getSelect($params['SELECT'], $fields, array('ID', 'WORKFLOW_ID', 'DOCUMENT_NAME', 'NAME')); $select = array_merge(array('MODULE', 'ENTITY', 'DOCUMENT_ID'), $select); $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'WORKFLOW_STARTED', 'OVERDUE_DATE')); $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'DESC')); $currentUserId = self::getCurrentUserId(); $isAdmin = static::isAdmin(); if (!$isAdmin && !isset($filter['USER_ID'])) { $filter['USER_ID'] = $currentUserId; } $targetUserId = isset($filter['USER_ID'])? (int)$filter['USER_ID'] : 0; if ($targetUserId !== $currentUserId && !\CBPHelper::checkUserSubordination($currentUserId, $targetUserId)) { self::checkAdminPermissions(); } $iterator = \CBPTaskService::getList( $order, $filter, false, static::getNavData($n), $select ); $result = array(); while ($row = $iterator->fetch()) { if (isset($row['MODIFIED'])) $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']); if (isset($row['WORKFLOW_STARTED'])) $row['WORKFLOW_STARTED'] = \CRestUtil::convertDateTime($row['WORKFLOW_STARTED']); if (isset($row['OVERDUE_DATE'])) $row['OVERDUE_DATE'] = \CRestUtil::convertDateTime($row['OVERDUE_DATE']); $row['DOCUMENT_URL'] = \CBPDocument::getDocumentAdminPage(array( $row['MODULE_ID'], $row['ENTITY'], $row['DOCUMENT_ID'] )); if (isset($row['PARAMETERS'])) { $row['PARAMETERS'] = static::prepareTaskParameters($row['PARAMETERS'], $row); } $result[] = $row; } return static::setNavData($result, $iterator); } private static function prepareTaskParameters(array $parameters, array $task) { $whiteList = [ ['CommentLabelMessage', 'CommentLabel'], 'CommentRequired', 'ShowComment', ['TaskButtonMessage', 'StatusOkLabel'], ['TaskButton1Message', 'StatusYesLabel'], ['TaskButton2Message', 'StatusNoLabel'], ['TaskButtonCancelMessage', 'StatusCancelLabel'], ['REQUEST', 'Fields'], ]; $filtered = []; foreach ($whiteList as $whiteKey) { $filterKey = $whiteKey; if (is_array($whiteKey)) { $filterKey = $whiteKey[1]; $whiteKey = $whiteKey[0]; } if (isset($parameters[$whiteKey])) { $filtered[$filterKey] = $parameters[$whiteKey]; } } if (isset($filtered['Fields'])) { $filtered['Fields'] = self::externalizeRequestFields($task, $filtered['Fields']); } return $filtered; } private static function externalizeRequestFields($task, array $fields): array { $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService(); $result = []; foreach ($fields as $requestField) { $id = $requestField['Name']; $requestField['Name'] = $requestField['Title']; $property = FieldType::normalizeProperty($requestField); $property['Id'] = $id; $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property); if ($fieldTypeObject) { $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]); $property['Default'] = $fieldTypeObject->externalizeValue( FieldType::VALUE_CONTEXT_REST, $property['Default'] ); } $result[] = $property; } return $result; } private static function internalizeRequestFields($task, array $values): array { $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService(); $result = []; foreach ($task['PARAMETERS']['REQUEST'] as $property) { if (!isset($values[$property['Name']])) { continue; } $property = FieldType::normalizeProperty($property); $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property); if ($fieldTypeObject) { $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]); $result[$property['Name']] = $fieldTypeObject->internalizeValue(FieldType::VALUE_CONTEXT_REST, $values[$property['Name']]); } } return $result; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws RestException */ public static function completeTask($params, $n, $server) { $params = array_change_key_case($params, CASE_UPPER); self::validateTaskParameters($params); $userId = self::getCurrentUserId(); $task = static::getTask($params['TASK_ID'], $userId); if (!in_array($task['ACTIVITY'], self::ALLOWED_TASK_ACTIVITIES)) { throw new RestException('Incorrect task type', self::ERROR_TASK_TYPE); } if (!empty($params['FIELDS'])) { $params['FIELDS'] = self::internalizeRequestFields($task, $params['FIELDS']); } $errors = array(); $request = array( 'INLINE_USER_STATUS' => \CBPTaskUserStatus::resolveStatus($params['STATUS']), 'task_comment' => !empty($params['COMMENT']) && is_string($params['COMMENT']) ? $params['COMMENT'] : null, 'fields' => $params['FIELDS'] ?? null, ); if (!\CBPDocument::postTaskForm($task, $userId, $request, $errors)) { throw new RestException($errors[0]["message"], self::ERROR_TASK_EXECUTION); } return true; } private static function validateTaskParameters(array $params) { if (empty($params['TASK_ID'])) { throw new RestException('empty TASK_ID', self::ERROR_TASK_VALIDATION); } if (empty($params['STATUS']) || \CBPTaskUserStatus::resolveStatus($params['STATUS']) === null) { throw new RestException('incorrect STATUS', self::ERROR_TASK_VALIDATION); } } private static function getTask($id, $userId) { $dbTask = \CBPTaskService::getList( array(), array("ID" => (int)$id, "USER_ID" => $userId), false, false, array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "USER_STATUS") ); $task = $dbTask->fetch(); if (!$task) { throw new RestException('Task not found', self::ERROR_TASK_NOT_FOUND); } elseif ((int)$task['USER_STATUS'] !== \CBPTaskUserStatus::Waiting) { throw new RestException('Task already completed', self::ERROR_TASK_COMPLETED); } if ($task) { $task["PARAMETERS"]["DOCUMENT_ID"] = \CBPStateService::getStateDocumentId($task['WORKFLOW_ID']); $task["MODULE_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][0]; $task["ENTITY"] = $task["PARAMETERS"]["DOCUMENT_ID"][1]; $task["DOCUMENT_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][2]; } return $task; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function addProvider($params, $n, $server) { if (Loader::includeModule('messageservice')) { return \Bitrix\MessageService\RestService::addSender($params, $n, $server); } if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $params = self::prepareActivityData($params); self::validateProvider($params, $server); $params['APP_ID'] = $server->getClientId(); $params['APP_NAME'] = self::getAppName($params['APP_ID']); $iterator = RestProviderTable::getList(array( 'select' => array('ID'), 'filter' => array( '=APP_ID' => $params['APP_ID'], '=CODE' => $params['CODE'] ) )); $result = $iterator->fetch(); if ($result) { throw new RestException('Provider already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED); } $result = RestProviderTable::add($params); if ($result->getErrors()) throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE); return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return bool * @throws \Exception */ public static function deleteProvider($params, $n, $server) { if (Loader::includeModule('messageservice')) { return \Bitrix\MessageService\RestService::deleteSender($params, $n, $server); } if(!$server->getClientId()) { throw new AccessException("Application context required"); } $params = array_change_key_case($params, CASE_UPPER); self::checkAdminPermissions(); self::validateActivityCode($params['CODE']); $params['APP_ID'] = $server->getClientId(); $iterator = RestProviderTable::getList(array( 'select' => array('ID'), 'filter' => array( '=APP_ID' => $params['APP_ID'], '=CODE' => $params['CODE'] ) )); $result = $iterator->fetch(); if (!$result) { throw new RestException('Provider not found!', self::ERROR_ACTIVITY_NOT_FOUND); } RestProviderTable::delete($result['ID']); return true; } /** * @param array $params Input params. * @param int $n Offset. * @param \CRestServer $server Rest server instance. * @return array * @throws AccessException * @throws \Bitrix\Main\ArgumentException */ public static function getProviderList($params, $n, $server) { if (Loader::includeModule('messageservice')) { return \Bitrix\MessageService\RestService::getSenderList($params, $n, $server); } if(!$server->getClientId()) { throw new AccessException("Application context required"); } self::checkAdminPermissions(); $iterator = RestProviderTable::getList(array( 'select' => array('CODE'), 'filter' => array( '=APP_ID' => $server->getClientId() ) )); $result = array(); while ($row = $iterator->fetch()) { $result[] = $row['CODE']; } return $result; } private static function getSelect($rules, $fields, $default = array()) { $select = array(); if (!empty($rules) && is_array($rules)) { foreach ($rules as $field) { if (!is_scalar($field)) { throw new RestException( "Invalid data in SELECT parameter", self::ERROR_SELECT_VALIDATION_FAILURE, ); } $field = mb_strtoupper($field); if (isset($fields[$field]) && !in_array($field, $select)) $select[$field] = $fields[$field]; } } return $select ?: $default; } private static function getOrder($rules, $fields, array $default = array()) { $order = array(); if (!empty($rules) && is_array($rules)) { foreach ($rules as $field => $ordering) { $field = mb_strtoupper($field); $ordering = mb_strtoupper($ordering); if (isset($fields[$field])) $order[$fields[$field]] = $ordering == 'DESC' ? 'DESC' : 'ASC'; } } return $order ? $order : $default; } private static function getFilter($rules, $fields, array $datetimeFieldsList = array()) { $filter = array(); if (!empty($rules) && is_array($rules)) { foreach ($rules as $key => $value) { if (preg_match('/^([^a-zA-Z]*)(.*)/', $key, $matches)) { $operation = $matches[1]; $field = $matches[2]; if (in_array($operation, static::$allowedOperations, true) && isset($fields[$field])) { if (in_array($field, $datetimeFieldsList)) { $value = \CRestUtil::unConvertDateTime($value); } $filter[$operation.$fields[$field]] = $value; } } } } return $filter; } private static function checkAdminPermissions() { if (!static::isAdmin()) { throw new AccessException(); } } private static function isAdmin() { global $USER; return ( isset($USER) && is_object($USER) && ( $USER->isAdmin() || Loader::includeModule('bitrix24') && \CBitrix24::isPortalAdmin($USER->getID()) ) ); } private static function getCurrentUserId() { global $USER; return (isset($USER) && is_object($USER)) ? (int)$USER->getID() : 0; } private static function generateInternalCode($data) { return md5($data['APP_ID'].'@'.$data['CODE']); } private static function getAppName($appId) { if (!Loader::includeModule('rest')) return array('*' => 'No app'); $iterator = AppTable::getList( array( 'filter' => array( '=CLIENT_ID' => $appId ), 'select' => array('ID', 'APP_NAME', 'CODE'), ) ); $app = $iterator->fetch(); $result = array('*' => $app['APP_NAME'] ? $app['APP_NAME'] : $app['CODE']); $iterator = AppLangTable::getList(array( 'filter' => array( '=APP_ID' => $app['ID'], ), 'select' => array('LANGUAGE_ID', 'MENU_NAME') )); while($lang = $iterator->fetch()) { $result[mb_strtoupper($lang['LANGUAGE_ID'])] = $lang['MENU_NAME']; } return $result; } private static function getAppId($clientId) { if (!Loader::includeModule('rest')) { return null; } $iterator = AppTable::getList( array( 'filter' => array( '=CLIENT_ID' => $clientId ), 'select' => array('ID'), ) ); $app = $iterator->fetch(); return (int) $app['ID']; } private static function prepareActivityData(array $data, $ignore = false) { if (!$ignore) $data = array_change_key_case($data, CASE_UPPER); foreach ($data as $key => &$field) { if (is_array($field)) $field = self::prepareActivityData($field, $key == 'PROPERTIES' || $key == 'RETURN_PROPERTIES' || $key == 'OPTIONS'); } return $data; } private static function validateActivity($data, $server) { if (!is_array($data) || empty($data)) throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); static::validateActivityCode($data['CODE']); static::validateActivityHandler($data['HANDLER'], $server); if (empty($data['NAME'])) throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); if (isset($data['PROPERTIES'])) static::validateActivityProperties($data['PROPERTIES']); if (isset($data['RETURN_PROPERTIES'])) static::validateActivityProperties($data['RETURN_PROPERTIES']); if (isset($data['DOCUMENT_TYPE'])) static::validateActivityDocumentType($data['DOCUMENT_TYPE']); if (isset($data['FILTER']) && !is_array($data['FILTER'])) throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } private static function validateProvider($data, $server) { if (!is_array($data) || empty($data)) throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); static::validateActivityCode($data['CODE']); static::validateActivityHandler($data['HANDLER'], $server); if (empty($data['NAME'])) throw new RestException('Empty provider NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); if (empty($data['TYPE'])) throw new RestException('Empty provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); if (!in_array($data['TYPE'], RestProviderTable::getTypesList(), true)) throw new RestException('Unknown provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } private static function validateRobot($data, $server) { if (!is_array($data) || empty($data)) throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); static::validateActivityCode($data['CODE']); static::validateActivityHandler($data['HANDLER'], $server); if (empty($data['NAME'])) throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); if (isset($data['PROPERTIES'])) static::validateActivityProperties($data['PROPERTIES']); if (isset($data['RETURN_PROPERTIES'])) static::validateActivityProperties($data['RETURN_PROPERTIES']); if (isset($data['FILTER']) && !is_array($data['FILTER'])) throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } private static function validateActivityCode($code) { if (empty($code)) { throw new RestException('Empty activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } if (!is_string($code) || !preg_match('#^[a-z0-9\.\-_]+$#i', $code)) { throw new RestException('Wrong activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } } private static function validateActivityHandler($handler, $server) { HandlerHelper::checkCallback($handler); } private static function validateActivityProperties($properties) { if (!is_array($properties)) throw new RestException('Wrong properties array!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); foreach ($properties as $key => $property) { if (!preg_match('#^[a-z][a-z0-9_]*$#i', $key)) throw new RestException('Wrong property key ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); if (empty($property['NAME'])) throw new RestException('Empty property NAME ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } } private static function validateActivityDocumentType($documentType) { try { $runtime = \CBPRuntime::getRuntime(); $runtime->startRuntime(); /** @var \CBPDocumentService $documentService */ $documentService = $runtime->getService('DocumentService'); $documentService->getDocumentFieldTypes($documentType); } catch (\CBPArgumentNullException $e) { throw new RestException('Wrong activity DOCUMENT_TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE); } } private static function getDocumentId($documentId): ?array { try { $documentService = \CBPRuntime::getRuntime()->getDocumentService(); $documentId = $documentService->normalizeDocumentId($documentId); if ($documentService->getDocument($documentId, select: ['ID'])) { return $documentId; } } catch (\CBPArgumentException $exception) {} return null; } private static function getDocumentType(array $documentId): ?array { try { $documentId = \CBPHelper::parseDocumentId($documentId); $runtime = \CBPRuntime::getRuntime(true); $documentService = $runtime->getDocumentService(); return $documentService->getDocumentType($documentId); } catch (\Exception $e) {} return null; } private static function getTemplateDocumentType(int $id): ?array { $tpl = WorkflowTemplateTable::getList([ 'select' => ['MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'], 'filter' => ['=ID' => $id], ])->fetch(); if ($tpl) { return [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']]; } return null; } private static function validateTemplateName($name) { if (empty($name)) { throw new RestException('Empty template name!', self::ERROR_TEMPLATE_VALIDATION_FAILURE); } } private static function upsertAppPlacement(int $appId, string $code, string $handler) { $filter = [ '=APP_ID' => $appId, '=ADDITIONAL' => $code, '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG, ]; $dbRes = PlacementTable::getList(array( 'filter' => $filter )); $placementHandler = $dbRes->fetch(); if ($placementHandler) { $result = PlacementTable::update($placementHandler['ID'], ['PLACEMENT_HANDLER' => $handler]); } else { $placementBind = array( 'APP_ID' => $appId, 'ADDITIONAL' => $code, 'PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG, 'PLACEMENT_HANDLER' => $handler, ); $result = PlacementTable::add($placementBind); } if(!$result->isSuccess()) { $errorMessage = $result->getErrorMessages(); throw new RestException( 'Unable to set placement handler: '.implode(', ', $errorMessage), RestException::ERROR_CORE ); } } private static function deleteAppPlacement(int $appId, string $code = null) { $filter = [ '=APP_ID' => $appId, '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG, ]; if ($code) { $filter['=ADDITIONAL'] = $code; } $dbRes = PlacementTable::getList(array( 'filter' => $filter )); while($placementHandler = $dbRes->fetch()) { PlacementTable::delete($placementHandler["ID"]); } } private static function prepareTemplateData($data) { if (!empty($data)) { $fileFields = \CRestUtil::saveFile($data); if ($fileFields) { return \Bitrix\Main\IO\File::getFileContents($fileFields['tmp_name']); } } throw new RestException('Incorrect field TEMPLATE_DATA!', self::ERROR_TEMPLATE_VALIDATION_FAILURE); } private static function validateTemplateDocumentType($documentType) { try { $documentService = \CBPRuntime::getRuntime(true)->getDocumentService(); $documentService->getDocumentFieldTypes($documentType); } catch (\CBPArgumentNullException $e) { throw new RestException('Incorrect field DOCUMENT_TYPE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE); } } private static function validateTemplateAutoExecution($flag) { if ($flag === (string) (int) $flag) { $flag = (int) $flag; if (in_array( $flag, [ \CBPDocumentEventType::None, \CBPDocumentEventType::Create, \CBPDocumentEventType::Edit, \CBPDocumentEventType::Create | \CBPDocumentEventType::Edit ], true ) ) { return true; } } throw new RestException('Incorrect field AUTO_EXECUTE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE); } private static function generateTemplateSystemCode(\CRestServer $server) { $appId = self::getAppId($server->getClientId()); return 'rest_app_'.$appId; } private static function extractEventToken($token) { $data = \CBPRestActivity::extractToken($token); if (!$data) throw new AccessException(); return $data; } /** * @param \CRestServer $server * @return array|bool|false|mixed|null * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\LoaderException */ private static function getApp($server) { if(self::$app == null) { if (Loader::includeModule('rest')) { $result = AppTable::getList( array( 'filter' => array( '=CLIENT_ID' => $server->getClientId() ) ) ); self::$app = $result->fetch(); } } return self::$app; } }