Your IP : 3.135.201.139
<?
namespace Bitrix\Bizproc;
use \Bitrix\Main\Loader;
use \Bitrix\Rest\AppLangTable;
use \Bitrix\Rest\AppTable;
use \Bitrix\Rest\RestException;
use \Bitrix\Rest\AccessException;
Loader::includeModule('rest');
class RestService extends \IRestService
{
const SCOPE = 'bizproc';
protected static $app;
private static $allowedOperations = array('', '!', '<', '<=', '>', '>=');//, '><', '!><', '?', '=', '!=', '%', '!%', ''); May be later?
const ERROR_UNSUPPORTED_PROTOCOL = 'ERROR_UNSUPPORTED_PROTOCOL';
const ERROR_WRONG_HANDLER_URL = 'ERROR_WRONG_HANDLER_URL';
const ERROR_HANDLER_URL_MATCH = 'ERROR_HANDLER_URL_MATCH';
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_WRONG_ACTIVITY_NAME = 'ERROR_WRONG_ACTIVITY_NAME';
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';
public static function onRestServiceBuildDescription()
{
$map = array();
if (\CBPRuntime::isFeatureEnabled())
{
$map = array(
//activity
'bizproc.activity.add' => array(__CLASS__, 'addActivity'),
'bizproc.activity.delete' => array(__CLASS__, 'deleteActivity'),
'bizproc.activity.log' => array(__CLASS__, 'writeActivityLog'),
'bizproc.activity.list' => array(__CLASS__, 'getActivityList'),
//event
'bizproc.event.send' => array(__CLASS__, 'sendEvent'),
//task
'bizproc.task.list' => array(__CLASS__, 'getTaskList'),
'bizproc.task.complete' => array(__CLASS__, 'completeTask'),
//workflow
'bizproc.workflow.terminate' => array(__CLASS__, 'terminateWorkflow'),
'bizproc.workflow.start' => array(__CLASS__, 'startWorkflow'),
//workflow.instance
'bizproc.workflow.instance.list' => array(__CLASS__, 'getWorkflowInstances'),
//workflow.template
'bizproc.workflow.template.list' => array(__CLASS__, 'getWorkflowTemplates'),
//aliases
'bizproc.workflow.instances' => array(__CLASS__, 'getWorkflowInstances'),
);
}
if (\CBPRuntime::isFeatureEnabled()
|| \CBPRuntime::isFeatureEnabled('crm_automation_lead')
|| \CBPRuntime::isFeatureEnabled('crm_automation_deal')
)
{
$map = array_merge($map, array(
//robot
'bizproc.robot.add' => array(__CLASS__, 'addRobot'),
'bizproc.robot.delete' => array(__CLASS__, 'deleteRobot'),
'bizproc.robot.list' => array(__CLASS__, 'getRobotList'),
//provider
'bizproc.provider.add' => array(__CLASS__, 'addProvider'),
'bizproc.provider.delete' => array(__CLASS__, 'deleteProvider'),
'bizproc.provider.list' => array(__CLASS__, 'getProviderList'),
));
}
return $map ? array(static::SCOPE => $map) : false;
}
/**
* 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']);
}
}
/**
* 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);
$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';
if ($isRobot)
$params['USE_SUBSCRIPTION'] = 'N';
$result = RestActivityTable::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 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 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']);
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);
list($workflowId, $activityName, $eventId) = self::extractEventToken($params['EVENT_TOKEN']);
\CBPRuntime::sendExternalEvent(
$workflowId,
$activityName,
array(
'EVENT_ID' => $eventId,
'RETURN_VALUES' => isset($params['RETURN_VALUES']) ? $params['RETURN_VALUES'] : array(),
'LOG_MESSAGE' => isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : '',
)
);
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);
list($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);
\CBPRuntime::sendExternalEvent(
$workflowId,
$activityName,
array(
'EVENT_ID' => $eventId,
'LOG_ACTION' => true,
'LOG_MESSAGE' => $logMessage
)
);
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 array
* @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 = array(
'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'], $fields, array('ID', 'MODIFIED', 'OWNED_UNTIL'));
$filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'OWNED_UNTIL'));
$order = static::getOrder($params['ORDER'], $fields, array('MODIFIED' => 'DESC'));
$iterator = WorkflowInstanceTable::getList(array(
'select' => $select,
'filter' => $filter,
'order' => $order,
'limit' => static::LIST_LIMIT,
'offset' => (int) $n,
'count_total' => true,
));
$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' => $iterator->getCount(), '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);
}
$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 string Workflow ID.
* @throws AccessException
* @throws RestException
*/
public static function startWorkflow($params, $n, $server)
{
self::checkAdminPermissions();
$params = array_change_key_case($params, CASE_UPPER);
if (empty($params['TEMPLATE_ID']))
{
throw new RestException('Empty TEMPLATE_ID', self::ERROR_WRONG_WORKFLOW_ID);
}
$documentId = self::validateDocumentId($params['DOCUMENT_ID']);
$templateId = (int)$params['TEMPLATE_ID'];
$workflowParameters = isset($params['PARAMETERS']) && is_array($params['PARAMETERS']) ? $params['PARAMETERS'] : [];
$workflowParameters[\CBPDocument::PARAM_TAGRET_USER] = self::getCurrentUserId();
$errors = [];
$workflowId = \CBPDocument::startWorkflow($templateId, $documentId, $workflowParameters, $errors);
if (!$workflowId)
{
throw new RestException($errors[0]['message']);
}
return $workflowId;
}
/**
* @param array $params Input params.
* @param int $n Offset.
* @param \CRestServer $server Rest server instance.
* @return mixed 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'], $fields, array('ID'));
$filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED'));
$filter['!AUTO_EXECUTE'] = \CBPDocumentEventType::Automation;
$order = static::getOrder($params['ORDER'], $fields, array('ID' => 'ASC'));
$iterator = WorkflowTemplateTable::getList(array(
'select' => $select,
'filter' => $filter,
'order' => $order,
'limit' => static::LIST_LIMIT,
'offset' => (int) $n,
'count_total' => true,
));
$countTotal = $iterator->getCount();
$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 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::filterTaskParameters($row['PARAMETERS']);
}
$result[] = $row;
}
return static::setNavData($result, $iterator);
}
private static function filterTaskParameters(array $parameters)
{
$whiteList = array(
array('CommentLabelMessage', 'CommentLabel'),
'CommentRequired', 'ShowComment',
array('TaskButtonMessage', 'StatusOkLabel'),
array('TaskButton1Message', 'StatusYesLabel'),
array('TaskButton2Message', 'StatusNoLabel'),
array('TaskButtonCancelMessage', 'StatusCancelLabel'),
);
$filtered = array();
foreach ($whiteList as $whiteKey)
{
$filterKey = $whiteKey;
if (is_array($whiteKey))
{
$filterKey = $whiteKey[1];
$whiteKey = $whiteKey[0];
}
if (isset($parameters[$whiteKey]))
{
$filtered[$filterKey] = $parameters[$whiteKey];
}
}
return $filtered;
}
/**
* @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 ($task['ACTIVITY'] !== 'ReviewActivity' && $task['ACTIVITY'] !== 'ApproveActivity')
{
throw new RestException('Incorrect task type', self::ERROR_TASK_TYPE);
}
$errors = array();
$request = array(
'INLINE_USER_STATUS' => \CBPTaskUserStatus::resolveStatus($params['STATUS']),
'task_comment' => !empty($params['COMMENT']) && is_string($params['COMMENT']) ? $params['COMMENT'] : 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)
{
$field = strtoupper($field);
if (isset($fields[$field]) && !in_array($field, $select))
$select[$field] = $fields[$field];
}
}
return $select ? $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 = strtoupper($field);
$ordering = 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[strtoupper($lang['LANGUAGE_ID'])] = $lang['MENU_NAME'];
}
return $result;
}
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'], true);
if (isset($data['RETURN_PROPERTIES']))
throw new RestException('Return properties is not supported in Robots!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
if (isset($data['USE_SUBSCRIPTION']) && $data['USE_SUBSCRIPTION'] !== 'N')
throw new RestException('USE_SUBSCRIPTION is not supported in Robots!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
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 (!preg_match('#^[a-z0-9\.\-_]+$#i', $code))
throw new RestException('Wrong activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
}
private static function validateActivityHandler($handler, $server)
{
$handlerData = parse_url($handler);
if (is_array($handlerData)
&& strlen($handlerData['host']) > 0
&& strpos($handlerData['host'], '.') > 0
)
{
if ($handlerData['scheme'] == 'http' || $handlerData['scheme'] == 'https')
{
$host = $handlerData['host'];
$app = self::getApp($server);
if (strlen($app['URL']) > 0)
{
$urls = array($app['URL']);
if (strlen($app['URL_DEMO']) > 0)
{
$urls[] = $app['URL_DEMO'];
}
if (strlen($app['URL_INSTALL']) > 0)
{
$urls[] = $app['URL_INSTALL'];
}
$found = false;
foreach($urls as $url)
{
$a = parse_url($url);
if ($host == $a['host'] || $a['host'] == 'localhost')
{
$found = true;
break;
}
}
if(!$found)
{
throw new RestException('Handler URL host doesn\'t match application url', self::ERROR_HANDLER_URL_MATCH);
}
}
}
else
{
throw new RestException('Unsupported event handler protocol', self::ERROR_UNSUPPORTED_PROTOCOL);
}
}
else
{
throw new RestException('Wrong handler URL', self::ERROR_WRONG_HANDLER_URL);
}
}
private static function validateActivityProperties($properties, $isRobot = false)
{
if (!is_array($properties))
throw new RestException('Wrong properties array!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
$map = array(
FieldType::BOOL => true,
FieldType::DATE => true,
FieldType::DATETIME => true,
FieldType::DOUBLE => true,
FieldType::INT => true,
FieldType::SELECT => true,
FieldType::STRING => true,
FieldType::TEXT => true,
FieldType::USER => true,
);
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);
if ($isRobot)
{
$type = isset($property['TYPE']) ? $property['TYPE'] : FieldType::STRING;
if (!array_key_exists($type, $map))
throw new RestException('Unsupported property type ('.$type.')!', 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 validateDocumentId($documentId)
{
$type = null;
if ($documentId && is_array($documentId))
{
try
{
$runtime = \CBPRuntime::getRuntime();
$runtime->startRuntime();
/** @var \CBPDocumentService $documentService */
$documentService = $runtime->getService('DocumentService');
$documentId = $documentService->normalizeDocumentId($documentId);
$type = $documentService->getDocumentType($documentId);
}
catch (\CBPArgumentNullException $e) {}
}
if (!$type)
{
throw new RestException('Wrong DOCUMENT_ID!');
}
return $documentId;
}
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;
}
}