1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
restservice.php
См. документацию.
1<?php
2
3namespace Bitrix\Bizproc;
4
5use Bitrix\Bizproc\Workflow\Entity\WorkflowInstanceTable;
6use Bitrix\Main\DB\SqlQueryException;
7use Bitrix\Main\Loader;
8use Bitrix\Rest\AppLangTable;
9use Bitrix\Rest\AppTable;
10use Bitrix\Rest\HandlerHelper;
11use Bitrix\Rest\PlacementTable;
12use Bitrix\Rest\RestException;
13use Bitrix\Rest\AccessException;
14
15Loader::includeModule('rest');
16
18{
19 public const SCOPE = 'bizproc';
20 public const PLACEMENT_ACTIVITY_PROPERTIES_DIALOG = 'BIZPROC_ACTIVITY_PROPERTIES_DIALOG';
21
22 protected static $app;
23 private static $allowedOperations = ['', '!', '<', '<=', '>', '>='];
24 //, '><', '!><', '?', '=', '!=', '%', '!%', ''); May be later?
25
26 const ERROR_ACTIVITY_ALREADY_INSTALLED = 'ERROR_ACTIVITY_ALREADY_INSTALLED';
27 const ERROR_ACTIVITY_ADD_FAILURE = 'ERROR_ACTIVITY_ADD_FAILURE';
28 const ERROR_ACTIVITY_VALIDATION_FAILURE = 'ERROR_ACTIVITY_VALIDATION_FAILURE';
29 const ERROR_ACTIVITY_NOT_FOUND = 'ERROR_ACTIVITY_NOT_FOUND';
30 const ERROR_EMPTY_LOG_MESSAGE = 'ERROR_EMPTY_LOG_MESSAGE';
31 const ERROR_WRONG_WORKFLOW_ID = 'ERROR_WRONG_WORKFLOW_ID';
32
33 const ERROR_TEMPLATE_VALIDATION_FAILURE = 'ERROR_TEMPLATE_VALIDATION_FAILURE';
34 const ERROR_TEMPLATE_NOT_FOUND = 'ERROR_TEMPLATE_NOT_FOUND';
35 const ERROR_TEMPLATE_NOT_OWNER = 'ERROR_TEMPLATE_NOT_OWNER';
36
37 const ERROR_TASK_VALIDATION = 'ERROR_TASK_VALIDATION';
38 const ERROR_TASK_NOT_FOUND = 'ERROR_TASK_NOT_FOUND';
39 const ERROR_TASK_TYPE = 'ERROR_TASK_TYPE';
40 const ERROR_TASK_COMPLETED = 'ERROR_TASK_COMPLETED';
41 const ERROR_TASK_EXECUTION = 'ERROR_TASK_EXECUTION';
42 const ERROR_SELECT_VALIDATION_FAILURE = 'ERROR_SELECT_VALIDATION_FAILURE';
43
44 const ERROR_INVALID_USER_ID = 'ERROR_INVALID_USER_ID';
45 const ERROR_DELEGATION_NOT_ALLOWED = 'ERROR_DELEGATION_NOT_ALLOWED';
46
47 private const ALLOWED_TASK_ACTIVITIES = [
48 'ReviewActivity',
49 'ApproveActivity',
50 'RequestInformationActivity',
51 'RequestInformationOptionalActivity'
52 ];
53
54 public static function onRestServiceBuildDescription()
55 {
56 $map = [];
57
58 if (self::isEnabled())
59 {
60 $map = [
61 //activity
62 'bizproc.activity.add' => [__CLASS__, 'addActivity'],
63 'bizproc.activity.update' => [__CLASS__, 'updateActivity'],
64 'bizproc.activity.delete' => [__CLASS__, 'deleteActivity'],
65 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'],
66 'bizproc.activity.list' => [__CLASS__, 'getActivityList'],
67
68 //event
69 'bizproc.event.send' => [__CLASS__, 'sendEvent'],
70
71 //task
72 'bizproc.task.list' => [__CLASS__, 'getTaskList'],
73 'bizproc.task.complete' => [__CLASS__, 'completeTask'],
74 'bizproc.task.delegate' => [__CLASS__, 'delegateTask'],
75
76 //workflow
77 'bizproc.workflow.terminate' => [__CLASS__, 'terminateWorkflow'],
78 'bizproc.workflow.kill' => [__CLASS__, 'killWorkflow'],
79 'bizproc.workflow.start' => [__CLASS__, 'startWorkflow'],
80
81 //workflow.instance
82 'bizproc.workflow.instance.list' => [__CLASS__, 'getWorkflowInstances'],
83
84 //workflow.template
85 'bizproc.workflow.template.list' => [__CLASS__, 'getWorkflowTemplates'],
86 'bizproc.workflow.template.add' => [__CLASS__, 'addWorkflowTemplate'],
87 'bizproc.workflow.template.update' => [__CLASS__, 'updateWorkflowTemplate'],
88 'bizproc.workflow.template.delete' => [__CLASS__, 'deleteWorkflowTemplate'],
89
90 //aliases
91 'bizproc.workflow.instances' => [__CLASS__, 'getWorkflowInstances'],
92 ];
93 }
94
95 if (
96 self::isEnabled()
97 || self::isEnabled('crm_automation_lead')
98 || self::isEnabled('crm_automation_deal')
99 || self::isEnabled('crm_automation_order')
100 || self::isEnabled('tasks_automation')
101 )
102 {
103 $map = array_merge($map, array(
104 'bizproc.event.send' => [__CLASS__, 'sendEvent'],
105 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'],
106
107 //robot
108 'bizproc.robot.add' => array(__CLASS__, 'addRobot'),
109 'bizproc.robot.update' => array(__CLASS__, 'updateRobot'),
110 'bizproc.robot.delete' => array(__CLASS__, 'deleteRobot'),
111 'bizproc.robot.list' => array(__CLASS__, 'getRobotList'),
112
113 //provider
114 'bizproc.provider.add' => [__CLASS__, 'addProvider'],
115 'bizproc.provider.delete' => [__CLASS__, 'deleteProvider'],
116 'bizproc.provider.list' => [__CLASS__, 'getProviderList'],
117 ));
118 }
119
120 //placements
121 $map[\CRestUtil::PLACEMENTS] = [
122 static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG => ['private' => true],
123 ];
124
125 return [
126 static::SCOPE => $map,
127 ];
128 }
129
130 private static function isEnabled(string $feature = 'bizproc'): bool
131 {
132 if (Loader::includeModule('bitrix24'))
133 {
134 return \Bitrix\Bitrix24\Feature::isFeatureEnabled($feature);
135 }
136
137 return true;
138 }
139
145 public static function onRestAppDelete(array $fields)
146 {
147 $fields = array_change_key_case($fields, CASE_UPPER);
148 if (empty($fields['APP_ID']))
149 return;
150
151 if (!Loader::includeModule('rest'))
152 return;
153
154 $dbRes = AppTable::getById($fields['APP_ID']);
155 $app = $dbRes->fetch();
156
157 if(!$app)
158 return;
159
160 $iterator = RestActivityTable::getList(array(
161 'select' => array('ID'),
162 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
163 ));
164
165 while ($activity = $iterator->fetch())
166 {
167 RestActivityTable::delete($activity['ID']);
168 }
169
170 $iterator = RestProviderTable::getList(array(
171 'select' => array('ID'),
172 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
173 ));
174
175 while ($activity = $iterator->fetch())
176 {
177 RestProviderTable::delete($activity['ID']);
178 }
179
180 self::deleteAppPlacement($app['ID']);
181 }
182
188 public static function onRestAppUpdate(array $fields)
189 {
190 static::onRestAppDelete($fields);
191 }
192
200 public static function addActivity($params, $n, $server)
201 {
202 return self::addActivityInternal($params, $server, false);
203 }
204
212 public static function addRobot($params, $n, $server)
213 {
214 return self::addActivityInternal($params, $server, true);
215 }
216
225 private static function addActivityInternal($params, $server, $isRobot = false)
226 {
227 if(!$server->getClientId())
228 {
229 throw new AccessException("Application context required");
230 }
231
232 self::checkAdminPermissions();
233 $params = self::prepareActivityData($params);
234
235 if ($isRobot)
236 self::validateRobot($params, $server);
237 else
238 self::validateActivity($params, $server);
239
240 $appId = self::getAppId($server->getClientId());
241 $params['APP_ID'] = $server->getClientId();
242 $params['INTERNAL_CODE'] = self::generateInternalCode($params);
243 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
244
245 $iterator = RestActivityTable::getList(array(
246 'select' => array('ID'),
247 'filter' => array('=INTERNAL_CODE' => $params['INTERNAL_CODE'])
248 ));
249 $result = $iterator->fetch();
250 if ($result)
251 {
252 throw new RestException('Activity or Robot already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
253 }
254
255 $params['AUTH_USER_ID'] = isset($params['AUTH_USER_ID'])? (int) $params['AUTH_USER_ID'] : 0;
256 $params['IS_ROBOT'] = $isRobot ? 'Y' : 'N';
257 $params['USE_PLACEMENT'] = (isset($params['USE_PLACEMENT']) && $params['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
258
259 if ($params['USE_PLACEMENT'] === 'Y')
260 {
261 self::validateActivityHandler($params['PLACEMENT_HANDLER'] ?? null, $server);
262 self::upsertAppPlacement($appId, $params['CODE'], $params['PLACEMENT_HANDLER'] ?? null);
263 }
264
265 try
266 {
267 $result = RestActivityTable::add($params);
268 }
269 catch (SqlQueryException $exception)
270 {
271 throw new RestException('Activity or Robot already added!', self::ERROR_ACTIVITY_ADD_FAILURE);
272 }
273
274 if ($result->getErrors())
275 {
276 if ($params['USE_PLACEMENT'] === 'Y')
277 {
278 self::deleteAppPlacement($appId, $params['CODE']);
279 }
280
281 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
282 }
283
284 return true;
285 }
286
294 public static function updateActivity($params, $n, $server)
295 {
296 return self::updateActivityInternal($params, $server, false);
297 }
298
306 public static function deleteActivity($params, $n, $server)
307 {
308 return self::deleteActivityInternal($params, $server, false);
309 }
310
318 public static function updateRobot($params, $n, $server)
319 {
320 return self::updateActivityInternal($params, $server, true);
321 }
322
330 public static function deleteRobot($params, $n, $server)
331 {
332 return self::deleteActivityInternal($params, $server, true);
333 }
334
343 private static function deleteActivityInternal($params, $server, $isRobot = false)
344 {
345 if(!$server->getClientId())
346 {
347 throw new AccessException("Application context required");
348 }
349
350 $params = array_change_key_case($params, CASE_UPPER);
351 self::checkAdminPermissions();
352 self::validateActivityCode($params['CODE']);
353 $params['APP_ID'] = $server->getClientId();
354 $internalCode = self::generateInternalCode($params);
355
356 $iterator = RestActivityTable::getList(array(
357 'select' => array('ID'),
358 'filter' => array(
359 '=INTERNAL_CODE' => $internalCode,
360 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
361 )
362 ));
363 $result = $iterator->fetch();
364 if (!$result)
365 {
366 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
367 }
368 RestActivityTable::delete($result['ID']);
369 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
370
371 return true;
372 }
373
385 private static function updateActivityInternal($params, $server, $isRobot = false)
386 {
387 if(!$server->getClientId())
388 {
389 throw new AccessException("Application context required");
390 }
391
392 $params = self::prepareActivityData($params);
393 self::checkAdminPermissions();
394 self::validateActivityCode($params['CODE']);
395 $params['APP_ID'] = $server->getClientId();
396 $internalCode = self::generateInternalCode($params);
397
398 $iterator = RestActivityTable::getList(array(
399 'select' => array('ID'),
400 'filter' => array(
401 '=INTERNAL_CODE' => $internalCode,
402 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
403 )
404 ));
405 $result = $iterator->fetch();
406 if (!$result)
407 {
408 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
409 }
410
411 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
412
413 if (!$fields)
414 {
415 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
416 }
417
418 $toUpdate = [];
419
420 if (isset($fields['HANDLER']))
421 {
422 self::validateActivityHandler($fields['HANDLER'], $server);
423 $toUpdate['HANDLER'] = $fields['HANDLER'];
424 }
425
426 if (isset($fields['AUTH_USER_ID']))
427 {
428 $toUpdate['AUTH_USER_ID'] = (int) $fields['AUTH_USER_ID'];
429 }
430
431 if (isset($fields['USE_SUBSCRIPTION']))
432 {
433 $toUpdate['USE_SUBSCRIPTION'] = (string) $fields['USE_SUBSCRIPTION'];
434 }
435
436 if (isset($fields['USE_PLACEMENT']))
437 {
438 $toUpdate['USE_PLACEMENT'] = ($fields['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
439 }
440
441 if (!empty($fields['NAME']))
442 {
443 $toUpdate['NAME'] = $fields['NAME'];
444 }
445
446 if (isset($fields['DESCRIPTION']))
447 {
448 $toUpdate['DESCRIPTION'] = $fields['DESCRIPTION'];
449 }
450
451 if (isset($fields['PROPERTIES']))
452 {
453 self::validateActivityProperties($fields['PROPERTIES']);
454 $toUpdate['PROPERTIES'] = $fields['PROPERTIES'];
455 }
456
457 if (isset($fields['RETURN_PROPERTIES']))
458 {
459 self::validateActivityProperties($fields['RETURN_PROPERTIES']);
460 $toUpdate['RETURN_PROPERTIES'] = $fields['RETURN_PROPERTIES'];
461 }
462
463 if (isset($fields['DOCUMENT_TYPE']))
464 {
465 if (empty($fields['DOCUMENT_TYPE']))
466 {
467 $toUpdate['DOCUMENT_TYPE'] = null;
468 }
469 else
470 {
471 static::validateActivityDocumentType($fields['DOCUMENT_TYPE']);
472 $toUpdate['DOCUMENT_TYPE'] = $fields['DOCUMENT_TYPE'];
473 }
474 }
475
476 if (isset($fields['FILTER']))
477 {
478 if (empty($fields['FILTER']))
479 {
480 $toUpdate['FILTER'] = null;
481 }
482 else
483 {
484 if (!is_array($fields['FILTER']))
485 {
486 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
487 }
488 $toUpdate['FILTER'] = $fields['FILTER'];
489 }
490 }
491
492 if (!$toUpdate)
493 {
494 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
495 }
496
497 if (isset($fields['PLACEMENT_HANDLER']))
498 {
499 self::validateActivityHandler($fields['PLACEMENT_HANDLER'], $server);
500 self::upsertAppPlacement(self::getAppId($params['APP_ID']), $params['CODE'], $fields['PLACEMENT_HANDLER']);
501 }
502
503 if (isset($toUpdate['USE_PLACEMENT']) && $toUpdate['USE_PLACEMENT'] === 'N')
504 {
505 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
506 }
507
508 $updateResult = RestActivityTable::update($result['ID'], $toUpdate);
509
510 if (!$updateResult->isSuccess())
511 {
512 throw new RestException(
513 implode('; ', $updateResult->getErrorMessages()),
514 self::ERROR_ACTIVITY_VALIDATION_FAILURE
515 );
516 }
517
518 return true;
519 }
520
529 public static function sendEvent($params, $n, $server)
530 {
531 $params = array_change_key_case($params, CASE_UPPER);
532 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
533
534 $errors = [];
535 \CBPDocument::sendExternalEvent(
536 $workflowId,
537 $activityName,
538 [
539 'EVENT_ID' => $eventId,
540 'RETURN_VALUES' => $params['RETURN_VALUES'] ?? [],
541 'LOG_MESSAGE' => $params['LOG_MESSAGE'] ?? '',
542 ],
543 $errors,
544 );
545
546 if ($errors)
547 {
548 $error = current($errors);
549 throw new RestException($error['message'], $error['code']);
550 }
551
552 return true;
553 }
554
563 public static function writeActivityLog($params, $n, $server)
564 {
565 $params = array_change_key_case($params, CASE_UPPER);
566 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
567
568 $logMessage = isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : '';
569
570 if (empty($logMessage))
571 throw new RestException('Empty log message!', self::ERROR_EMPTY_LOG_MESSAGE);
572
573 $errors = [];
574 \CBPDocument::sendExternalEvent(
575 $workflowId,
576 $activityName,
577 [
578 'EVENT_ID' => $eventId,
579 'LOG_ACTION' => true,
580 'LOG_MESSAGE' => $logMessage
581 ],
582 $errors,
583 );
584
585 if ($errors)
586 {
587 $error = current($errors);
588 throw new RestException($error['message'], $error['code']);
589 }
590
591 return true;
592 }
593
602 public static function getActivityList($params, $n, $server)
603 {
604 return self::getActivityListInternal($params, $server, false);
605 }
606
615 public static function getRobotList($params, $n, $server)
616 {
617 return self::getActivityListInternal($params, $server, true);
618 }
619
627 private static function getActivityListInternal($params, $server, $isRobot = false)
628 {
629 if(!$server->getClientId())
630 {
631 throw new AccessException("Application context required");
632 }
633
634 self::checkAdminPermissions();
635 $iterator = RestActivityTable::getList(array(
636 'select' => array('CODE'),
637 'filter' => array(
638 '=APP_ID' => $server->getClientId(),
639 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
640 )
641 ));
642
643 $result = array();
644 while ($row = $iterator->fetch())
645 {
646 $result[] = $row['CODE'];
647 }
648 return $result;
649 }
650
660 public static function getWorkflowInstances($params, $n, $server)
661 {
662 self::checkAdminPermissions();
663 $params = array_change_key_case($params, CASE_UPPER);
664
665 $fields = [
666 'ID' => 'ID',
667 'MODIFIED' => 'MODIFIED',
668 'OWNED_UNTIL' => 'OWNED_UNTIL',
669 'MODULE_ID' => 'MODULE_ID',
670 'ENTITY' => 'ENTITY',
671 'DOCUMENT_ID' => 'DOCUMENT_ID',
672 'STARTED' => 'STARTED',
673 'STARTED_BY' => 'STARTED_BY',
674 'TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
675 ];
676
677 $select = static::getSelect($params['SELECT'] ?? null, $fields, ['ID', 'MODIFIED', 'OWNED_UNTIL']);
678 $filter = static::getFilter($params['FILTER'] ?? null, $fields, ['MODIFIED', 'OWNED_UNTIL', 'STARTED']);
679 $order = static::getOrder($params['ORDER'] ?? null, $fields, ['MODIFIED' => 'DESC']);
680 $shouldCountTotal = ($n >= 0);
681
682 $iterator = WorkflowInstanceTable::getList([
683 'select' => $select,
684 'filter' => $filter,
685 'order' => $order,
686 'limit' => static::LIST_LIMIT,
687 'offset' => max(0, (int)$n),
688 'count_total' => $shouldCountTotal,
689 ]);
690
691 $result = [];
692 while ($row = $iterator->fetch())
693 {
694 if (isset($row['MODIFIED']))
695 {
696 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
697 }
698 if (isset($row['STARTED']))
699 {
700 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
701 }
702 if (isset($row['OWNED_UNTIL']))
703 {
704 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
705 }
706 $result[] = $row;
707 }
708
709 $count = $shouldCountTotal ? $iterator->getCount() : 0;
710
711 return static::setNavData($result, ['count' => $count, 'offset' => $n]);
712 }
713
722 public static function terminateWorkflow($params, $n, $server)
723 {
724 self::checkAdminPermissions();
725 $params = array_change_key_case($params, CASE_UPPER);
726
727 if (empty($params['ID']))
728 {
729 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
730 }
731
732 if (!is_string($params['ID']))
733 {
734 throw new RestException('Invalid workflow instance ID (string expected)', self::ERROR_WRONG_WORKFLOW_ID);
735 }
736
737 $id = $params['ID'];
738 $status = isset($params['STATUS']) ? (string)$params['STATUS'] : '';
739 $errors = [];
740
741 if (!\CBPDocument::terminateWorkflow($id, [], $errors, $status))
742 {
743 throw new RestException($errors[0]['message']);
744 }
745
746 return true;
747 }
748
757 public static function killWorkflow($params, $n, $server)
758 {
759 self::checkAdminPermissions();
760 $params = array_change_key_case($params, CASE_UPPER);
761
762 if (empty($params['ID']))
763 {
764 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
765 }
766
767 $id = $params['ID'];
768 $errors = \CBPDocument::killWorkflow($id);
769
770 if ($errors)
771 {
772 throw new RestException($errors[0]['message']);
773 }
774
775 return true;
776 }
777
786 public static function startWorkflow($params, $n, $server)
787 {
788 $params = array_change_key_case($params, CASE_UPPER);
789
790 if (empty($params['TEMPLATE_ID']))
791 {
792 throw new RestException('Empty TEMPLATE_ID', self::ERROR_WRONG_WORKFLOW_ID);
793 }
794 $templateId = (int)$params['TEMPLATE_ID'];
795 $tplDocumentType = self::getTemplateDocumentType($templateId);
796
797 if (!$tplDocumentType)
798 {
799 throw new RestException('Template not found', self::ERROR_WRONG_WORKFLOW_ID);
800 }
801
802 //hotfix #0120474
803 $getParams = array_change_key_case($_GET, CASE_UPPER);
804 if (isset($getParams['DOCUMENT_ID']) && is_array($getParams['DOCUMENT_ID']))
805 {
806 $params['DOCUMENT_ID'] = $getParams['DOCUMENT_ID'];
807 }
808
809 $documentId = self::getDocumentId($params['DOCUMENT_ID']);
810
811 if (!$documentId)
812 {
813 throw new RestException('Wrong DOCUMENT_ID!');
814 }
815
816 $documentType = self::getDocumentType($documentId);
817
818 if (!$documentType)
819 {
820 throw new RestException('Incorrect document type!');
821 }
822
823 if (!\CBPHelper::isEqualDocument($tplDocumentType, $documentType))
824 {
825 throw new RestException('Template type and DOCUMENT_ID mismatch!');
826 }
827
828 self::checkStartWorkflowPermissions($documentId, $templateId);
829
830 $workflowParameters = isset($params['PARAMETERS']) && is_array($params['PARAMETERS']) ? $params['PARAMETERS'] : [];
831
832 $workflowParameters[\CBPDocument::PARAM_TAGRET_USER] = 'user_' . self::getCurrentUserId();
833
834 $errors = [];
835 $workflowId = \CBPDocument::startWorkflow($templateId, $documentId, $workflowParameters, $errors);
836
837 if (!$workflowId)
838 {
839 throw new RestException($errors[0]['message']);
840 }
841
842 return $workflowId;
843 }
844
845 private static function checkStartWorkflowPermissions(array $documentId, $templateId)
846 {
847 if (static::isAdmin())
848 {
849 return true;
850 }
851
852 if (
853 \CBPDocument::CanUserOperateDocument(
855 static::getCurrentUserId(),
856 $documentId,
857 ['WorkflowTemplateId' => $templateId]
858 )
859 )
860 {
861 return true;
862 }
863
864 throw new AccessException();
865 }
866
877 public static function getWorkflowTemplates($params, $n, $server)
878 {
879 self::checkAdminPermissions();
880 $params = array_change_key_case($params, CASE_UPPER);
881
882 $fields = array(
883 'ID' => 'ID',
884 'MODULE_ID' => 'MODULE_ID',
885 'ENTITY' => 'ENTITY',
886 'DOCUMENT_TYPE' => 'DOCUMENT_TYPE',
887 'AUTO_EXECUTE' => 'AUTO_EXECUTE',
888 'NAME' => 'NAME',
889 'DESCRIPTION' => 'DESCRIPTION',
890 'TEMPLATE' => 'TEMPLATE',
891 'PARAMETERS' => 'PARAMETERS',
892 'VARIABLES' => 'VARIABLES',
893 'CONSTANTS' => 'CONSTANTS',
894 'MODIFIED' => 'MODIFIED',
895 'IS_MODIFIED' => 'IS_MODIFIED',
896 'USER_ID' => 'USER_ID',
897 'SYSTEM_CODE' => 'SYSTEM_CODE',
898 );
899
900 $select = static::getSelect($params['SELECT'] ?? null, $fields, ['ID']);
901 $filter = static::getFilter($params['FILTER'] ?? null, $fields, ['MODIFIED']);
902 $filter['<AUTO_EXECUTE'] = \CBPDocumentEventType::Automation;
903
904 $order = static::getOrder($params['ORDER'] ?? null, $fields, ['ID' => 'ASC']);
905 $shouldCountTotal = ($n >= 0);
906
907 $iterator = WorkflowTemplateTable::getList(array(
908 'select' => $select,
909 'filter' => $filter,
910 'order' => $order,
911 'limit' => static::LIST_LIMIT,
912 'offset' => max(0, (int)$n),
913 'count_total' => $shouldCountTotal,
914 ));
915
916 $countTotal = $shouldCountTotal ? $iterator->getCount() : 0;
917
918 $iterator = new \CBPWorkflowTemplateResult($iterator, \CBPWorkflowTemplateLoader::useGZipCompression());
919
920 $result = array();
921 while ($row = $iterator->fetch())
922 {
923 if (isset($row['MODIFIED']))
924 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
925 if (isset($row['STARTED']))
926 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
927 if (isset($row['OWNED_UNTIL']))
928 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
929 $result[] = $row;
930 }
931
932 return static::setNavData($result, ['count' => $countTotal, 'offset' => $n]);
933 }
934
942 public static function addWorkflowTemplate($params, $n, $server)
943 {
944 if(!$server->getClientId())
945 {
946 throw new AccessException("Application context required");
947 }
948
949 self::checkAdminPermissions();
950 $params = array_change_key_case($params, CASE_UPPER);
951
952 self::validateTemplateDocumentType($params['DOCUMENT_TYPE']);
953 self::validateTemplateName($params['NAME']);
954
955 $autoExecute = \CBPDocumentEventType::None;
956 if (isset($params['AUTO_EXECUTE']))
957 {
958 self::validateTemplateAutoExecution($params['AUTO_EXECUTE']);
959 $autoExecute = (int) $params['AUTO_EXECUTE'];
960 }
961
962 $data = self::prepareTemplateData($params['TEMPLATE_DATA']);
963
964 try
965 {
966 return \CBPWorkflowTemplateLoader::ImportTemplate(
967 0,
968 $params['DOCUMENT_TYPE'],
969 $autoExecute,
970 $params['NAME'],
971 isset($params['DESCRIPTION']) ? (string) $params['DESCRIPTION'] : '',
972 $data,
973 self::generateTemplateSystemCode($server)
974 );
975 }
976 catch (\Exception $e)
977 {
978 throw new RestException($e->getMessage());
979 }
980 }
981
989 public static function updateWorkflowTemplate($params, $n, $server)
990 {
991 if(!$server->getClientId())
992 {
993 throw new AccessException("Application context required");
994 }
995
996 self::checkAdminPermissions();
997 $params = array_change_key_case($params, CASE_UPPER);
998
999 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
1000
1001 if (!$fields)
1002 {
1003 throw new RestException("No fields to update.", self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1004 }
1005
1006 $tpl = WorkflowTemplateTable::getList(array(
1007 'select' => ['ID', 'SYSTEM_CODE', 'NAME', 'DESCRIPTION', 'AUTO_EXECUTE', 'MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
1008 'filter' => ['=ID' => (int) $params['ID']],
1009 ))->fetch();
1010
1011 if (!$tpl)
1012 {
1013 throw new RestException("Workflow template not found.", self::ERROR_TEMPLATE_NOT_FOUND);
1014 }
1015
1016 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
1017 {
1018 throw new RestException(
1019 "You can update ONLY templates created by current application",
1020 self::ERROR_TEMPLATE_NOT_OWNER,
1021 );
1022 }
1023
1024 if (isset($fields['NAME']))
1025 {
1026 self::validateTemplateName($fields['NAME']);
1027 $tpl['NAME'] = $fields['NAME'];
1028 }
1029
1030 if (isset($fields['DESCRIPTION']))
1031 {
1032 $tpl['DESCRIPTION'] = (string) $fields['DESCRIPTION'];
1033 }
1034
1035 if (isset($fields['AUTO_EXECUTE']))
1036 {
1037 self::validateTemplateAutoExecution($fields['AUTO_EXECUTE']);
1038 $tpl['AUTO_EXECUTE'] = (int) $fields['AUTO_EXECUTE'];
1039 }
1040
1041 if (isset($fields['TEMPLATE_DATA']))
1042 {
1043 $data = self::prepareTemplateData($fields['TEMPLATE_DATA']);
1044
1045 return \CBPWorkflowTemplateLoader::ImportTemplate(
1046 $tpl['ID'],
1047 [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']],
1048 $tpl['AUTO_EXECUTE'],
1049 $tpl['NAME'],
1050 $tpl['DESCRIPTION'],
1051 $data,
1052 $tpl['SYSTEM_CODE']
1053 );
1054 }
1055 else
1056 {
1057 return \CBPWorkflowTemplateLoader::Update($tpl['ID'], [
1058 'NAME' => $tpl['NAME'],
1059 'DESCRIPTION' => $tpl['DESCRIPTION'],
1060 'AUTO_EXECUTE' => $tpl['AUTO_EXECUTE'],
1061 ]);
1062 }
1063 }
1064
1072 public static function deleteWorkflowTemplate($params, $n, $server)
1073 {
1074 if(!$server->getClientId())
1075 {
1076 throw new AccessException("Application context required");
1077 }
1078
1079 self::checkAdminPermissions();
1080 $params = array_change_key_case($params, CASE_UPPER);
1081
1082 $tpl = WorkflowTemplateTable::getList(array(
1083 'select' => ['ID', 'SYSTEM_CODE'],
1084 'filter' => ['=ID' => (int) $params['ID']],
1085 ))->fetch();
1086
1087 if (!$tpl)
1088 {
1089 throw new RestException("Workflow template not found.", self::ERROR_TEMPLATE_NOT_FOUND);
1090 }
1091
1092 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
1093 {
1094 throw new RestException("You can delete ONLY templates created by current application");
1095 }
1096
1097 try
1098 {
1099 \CBPWorkflowTemplateLoader::Delete($tpl['ID']);
1100 }
1102 {
1103 throw new RestException($e->getMessage());
1104 }
1105 }
1106
1114 public static function getTaskList($params, $n, $server)
1115 {
1116 $params = array_change_key_case($params, CASE_UPPER);
1117
1118 $fields = array(
1119 'ID' => 'ID',
1120 'ACTIVITY' => 'ACTIVITY',
1121 'ACTIVITY_NAME' => 'ACTIVITY_NAME',
1122 'WORKFLOW_ID' => 'WORKFLOW_ID',
1123 'DOCUMENT_NAME' => 'DOCUMENT_NAME',
1124 'DESCRIPTION' => 'DESCRIPTION',
1125 'NAME' => 'NAME',
1126 'MODIFIED' => 'MODIFIED',
1127 'WORKFLOW_STARTED' => 'WORKFLOW_STARTED',
1128 'WORKFLOW_STARTED_BY' => 'WORKFLOW_STARTED_BY',
1129 'OVERDUE_DATE' => 'OVERDUE_DATE',
1130 'WORKFLOW_TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
1131 'WORKFLOW_TEMPLATE_NAME' => 'WORKFLOW_TEMPLATE_NAME',
1132 'WORKFLOW_STATE' => 'WORKFLOW_STATE',
1133 'STATUS' => 'STATUS',
1134 'USER_ID' => 'USER_ID',
1135 'USER_STATUS' => 'USER_STATUS',
1136 'MODULE_ID' => 'MODULE_ID',
1137 'ENTITY' => 'ENTITY',
1138 'DOCUMENT_ID' => 'DOCUMENT_ID',
1139 'PARAMETERS' => 'PARAMETERS',
1140 );
1141
1142 $select = static::getSelect($params['SELECT'], $fields, array('ID', 'WORKFLOW_ID', 'DOCUMENT_NAME', 'NAME'));
1143 $select = array_merge(array('MODULE', 'ENTITY', 'DOCUMENT_ID'), $select);
1144 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'WORKFLOW_STARTED', 'OVERDUE_DATE'));
1145 $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'DESC'));
1146
1147 $currentUserId = self::getCurrentUserId();
1148 $isAdmin = static::isAdmin();
1149
1150 if (!$isAdmin && !isset($filter['USER_ID']))
1151 {
1152 $filter['USER_ID'] = $currentUserId;
1153 }
1154
1155 $targetUserId = isset($filter['USER_ID'])? (int)$filter['USER_ID'] : 0;
1156 if ($targetUserId !== $currentUserId && !\CBPHelper::checkUserSubordination($currentUserId, $targetUserId))
1157 {
1158 self::checkAdminPermissions();
1159 }
1160
1162 $order,
1163 $filter,
1164 false,
1165 static::getNavData($n),
1166 $select
1167 );
1168
1169 $result = array();
1170 while ($row = $iterator->fetch())
1171 {
1172 if (isset($row['MODIFIED']))
1173 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
1174 if (isset($row['WORKFLOW_STARTED']))
1175 $row['WORKFLOW_STARTED'] = \CRestUtil::convertDateTime($row['WORKFLOW_STARTED']);
1176 if (isset($row['OVERDUE_DATE']))
1177 $row['OVERDUE_DATE'] = \CRestUtil::convertDateTime($row['OVERDUE_DATE']);
1178 $row['DOCUMENT_URL'] = \CBPDocument::getDocumentAdminPage(array(
1179 $row['MODULE_ID'], $row['ENTITY'], $row['DOCUMENT_ID']
1180 ));
1181
1182 if (isset($row['PARAMETERS']))
1183 {
1184 $row['PARAMETERS'] = static::prepareTaskParameters($row['PARAMETERS'], $row);
1185 }
1186
1187 $result[] = $row;
1188 }
1189
1190 return static::setNavData($result, $iterator);
1191 }
1192
1193 private static function prepareTaskParameters(array $parameters, array $task)
1194 {
1195 $whiteList = [
1196 ['CommentLabelMessage', 'CommentLabel'],
1197 'CommentRequired', 'ShowComment',
1198 ['TaskButtonMessage', 'StatusOkLabel'],
1199 ['TaskButton1Message', 'StatusYesLabel'],
1200 ['TaskButton2Message', 'StatusNoLabel'],
1201 ['TaskButtonCancelMessage', 'StatusCancelLabel'],
1202 ['REQUEST', 'Fields'],
1203 ];
1204
1205 $filtered = [];
1206
1207 foreach ($whiteList as $whiteKey)
1208 {
1209 $filterKey = $whiteKey;
1210 if (is_array($whiteKey))
1211 {
1212 $filterKey = $whiteKey[1];
1213 $whiteKey = $whiteKey[0];
1214 }
1215 if (isset($parameters[$whiteKey]))
1216 {
1217 $filtered[$filterKey] = $parameters[$whiteKey];
1218 }
1219 }
1220
1221 if (isset($filtered['Fields']))
1222 {
1223 $filtered['Fields'] = self::externalizeRequestFields($task, $filtered['Fields']);
1224 }
1225
1226 return $filtered;
1227 }
1228
1229 private static function externalizeRequestFields($task, array $fields): array
1230 {
1231 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1232 $result = [];
1233 foreach ($fields as $requestField)
1234 {
1235 $id = $requestField['Name'];
1236 $requestField['Name'] = $requestField['Title'];
1237 $property = FieldType::normalizeProperty($requestField);
1238 $property['Id'] = $id;
1239
1240 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1241 if ($fieldTypeObject)
1242 {
1243 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1244 $property['Default'] = $fieldTypeObject->externalizeValue(
1246 $property['Default']
1247 );
1248 }
1249
1250 $result[] = $property;
1251 }
1252 return $result;
1253 }
1254
1255 private static function internalizeRequestFields($task, array $values): array
1256 {
1257 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1258 $result = [];
1259
1260 foreach ($task['PARAMETERS']['REQUEST'] as $property)
1261 {
1262 if (!isset($values[$property['Name']]))
1263 {
1264 continue;
1265 }
1266
1267 $property = FieldType::normalizeProperty($property);
1268 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1269 if ($fieldTypeObject)
1270 {
1271 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1272 $result[$property['Name']] = $fieldTypeObject->internalizeValue(FieldType::VALUE_CONTEXT_REST, $values[$property['Name']]);
1273 }
1274 }
1275 return $result;
1276 }
1277
1285 public static function completeTask($params, $n, $server)
1286 {
1287 $params = array_change_key_case($params, CASE_UPPER);
1288 self::validateTaskParameters($params);
1289
1290 $userId = self::getCurrentUserId();
1291 $task = static::getTask($params['TASK_ID'], $userId);
1292
1293 if (!in_array($task['ACTIVITY'], self::ALLOWED_TASK_ACTIVITIES))
1294 {
1295 throw new RestException('Incorrect task type', self::ERROR_TASK_TYPE);
1296 }
1297
1298 if (!empty($params['FIELDS']))
1299 {
1300 $params['FIELDS'] = self::internalizeRequestFields($task, $params['FIELDS']);
1301 }
1302
1303 $errors = array();
1304 $request = array(
1305 'INLINE_USER_STATUS' => \CBPTaskUserStatus::resolveStatus($params['STATUS']),
1306 'task_comment' => !empty($params['COMMENT']) && is_string($params['COMMENT']) ? $params['COMMENT'] : null,
1307 'fields' => $params['FIELDS'] ?? null,
1308 );
1309
1310 if (!\CBPDocument::postTaskForm($task, $userId, $request, $errors))
1311 {
1312 throw new RestException($errors[0]["message"], self::ERROR_TASK_EXECUTION);
1313 }
1314
1315 return true;
1316 }
1317
1318 public static function delegateTask($params, $n, $server)
1319 {
1320 $params = array_change_key_case($params, CASE_UPPER);
1321 $currentUserId = self::getCurrentUserId();
1322
1323 if (!is_array($params['TASK_IDS']) || array_filter($params['TASK_IDS'], static fn($id) => !is_numeric($id)))
1324 {
1325 throw new RestException('Invalid TASK_IDS', self::ERROR_TASK_VALIDATION);
1326 }
1327 $taskIds = array_map('intval', $params['TASK_IDS']);
1328
1329 if (!isset($params['FROM_USER_ID']) || !is_numeric($params['FROM_USER_ID']) || $params['FROM_USER_ID'] <= 0)
1330 {
1331 throw new RestException('Invalid FROM_USER_ID', self::ERROR_INVALID_USER_ID);
1332 }
1333 $fromUserId = (int)$params['FROM_USER_ID'];
1334
1335 if (!isset($params['TO_USER_ID']) || !is_numeric($params['TO_USER_ID']) || $params['TO_USER_ID'] <= 0)
1336 {
1337 throw new RestException('Invalid TO_USER_ID', self::ERROR_INVALID_USER_ID);
1338 }
1339 $toUserId = (int)$params['TO_USER_ID'];
1340
1341 $taskService = new Api\Service\TaskService(new Api\Service\TaskAccessService($currentUserId));
1342 $tasksRequest = new Api\Request\TaskService\DelegateTasksRequest($taskIds, $fromUserId, $toUserId, $currentUserId);
1343 $delegateTaskResult = $taskService->delegateTasks($tasksRequest);
1344
1345 if (!$delegateTaskResult->isSuccess())
1346 {
1347 $errors = implode(';', $delegateTaskResult->getErrorMessages());
1348 throw new RestException($errors, self::ERROR_DELEGATION_NOT_ALLOWED);
1349 }
1350
1351 return true;
1352 }
1353
1354 private static function validateTaskParameters(array $params)
1355 {
1356 if (empty($params['TASK_ID']))
1357 {
1358 throw new RestException('empty TASK_ID', self::ERROR_TASK_VALIDATION);
1359 }
1360 if (empty($params['STATUS']) || \CBPTaskUserStatus::resolveStatus($params['STATUS']) === null)
1361 {
1362 throw new RestException('incorrect STATUS', self::ERROR_TASK_VALIDATION);
1363 }
1364 }
1365
1366 private static function getTask($id, $userId)
1367 {
1368 $dbTask = \CBPTaskService::getList(
1369 array(),
1370 array("ID" => (int)$id, "USER_ID" => $userId),
1371 false,
1372 false,
1373 array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "USER_STATUS")
1374 );
1375 $task = $dbTask->fetch();
1376
1377 if (!$task)
1378 {
1379 throw new RestException('Task not found', self::ERROR_TASK_NOT_FOUND);
1380 }
1381 elseif ((int)$task['USER_STATUS'] !== \CBPTaskUserStatus::Waiting)
1382 {
1383 throw new RestException('Task already completed', self::ERROR_TASK_COMPLETED);
1384 }
1385
1386 if ($task)
1387 {
1388 $task["PARAMETERS"]["DOCUMENT_ID"] = \CBPStateService::getStateDocumentId($task['WORKFLOW_ID']);
1389 $task["MODULE_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][0];
1390 $task["ENTITY"] = $task["PARAMETERS"]["DOCUMENT_ID"][1];
1391 $task["DOCUMENT_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][2];
1392 }
1393
1394 return $task;
1395 }
1396
1404 public static function addProvider($params, $n, $server)
1405 {
1406 if (Loader::includeModule('messageservice'))
1407 {
1408 return \Bitrix\MessageService\RestService::addSender($params, $n, $server);
1409 }
1410
1411 if(!$server->getClientId())
1412 {
1413 throw new AccessException("Application context required");
1414 }
1415
1416 self::checkAdminPermissions();
1417 $params = self::prepareActivityData($params);
1418
1419 self::validateProvider($params, $server);
1420
1421 $params['APP_ID'] = $server->getClientId();
1422 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
1423
1424 $iterator = RestProviderTable::getList(array(
1425 'select' => array('ID'),
1426 'filter' => array(
1427 '=APP_ID' => $params['APP_ID'],
1428 '=CODE' => $params['CODE']
1429 )
1430 ));
1431 $result = $iterator->fetch();
1432 if ($result)
1433 {
1434 throw new RestException('Provider already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
1435 }
1436
1437 $result = RestProviderTable::add($params);
1438
1439 if ($result->getErrors())
1440 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
1441
1442 return true;
1443 }
1444
1452 public static function deleteProvider($params, $n, $server)
1453 {
1454 if (Loader::includeModule('messageservice'))
1455 {
1456 return \Bitrix\MessageService\RestService::deleteSender($params, $n, $server);
1457 }
1458
1459 if(!$server->getClientId())
1460 {
1461 throw new AccessException("Application context required");
1462 }
1463
1464 $params = array_change_key_case($params, CASE_UPPER);
1465 self::checkAdminPermissions();
1466 self::validateActivityCode($params['CODE']);
1467 $params['APP_ID'] = $server->getClientId();
1468
1469 $iterator = RestProviderTable::getList(array(
1470 'select' => array('ID'),
1471 'filter' => array(
1472 '=APP_ID' => $params['APP_ID'],
1473 '=CODE' => $params['CODE']
1474 )
1475 ));
1476 $result = $iterator->fetch();
1477 if (!$result)
1478 {
1479 throw new RestException('Provider not found!', self::ERROR_ACTIVITY_NOT_FOUND);
1480 }
1481 RestProviderTable::delete($result['ID']);
1482
1483 return true;
1484 }
1485
1494 public static function getProviderList($params, $n, $server)
1495 {
1496 if (Loader::includeModule('messageservice'))
1497 {
1498 return \Bitrix\MessageService\RestService::getSenderList($params, $n, $server);
1499 }
1500
1501 if(!$server->getClientId())
1502 {
1503 throw new AccessException("Application context required");
1504 }
1505
1506 self::checkAdminPermissions();
1507 $iterator = RestProviderTable::getList(array(
1508 'select' => array('CODE'),
1509 'filter' => array(
1510 '=APP_ID' => $server->getClientId()
1511 )
1512 ));
1513
1514 $result = array();
1515 while ($row = $iterator->fetch())
1516 {
1517 $result[] = $row['CODE'];
1518 }
1519 return $result;
1520 }
1521
1522 private static function getSelect($rules, $fields, $default = array())
1523 {
1524 $select = array();
1525 if (!empty($rules) && is_array($rules))
1526 {
1527 foreach ($rules as $field)
1528 {
1529 if (!is_scalar($field))
1530 {
1531 throw new RestException(
1532 "Invalid data in SELECT parameter",
1533 self::ERROR_SELECT_VALIDATION_FAILURE,
1534 );
1535 }
1536
1537 $field = mb_strtoupper($field);
1538 if (isset($fields[$field]) && !in_array($field, $select))
1539 $select[$field] = $fields[$field];
1540 }
1541 }
1542
1543 return $select ?: $default;
1544 }
1545
1546 private static function getOrder($rules, $fields, array $default = array())
1547 {
1548 $order = array();
1549 if (!empty($rules) && is_array($rules))
1550 {
1551 foreach ($rules as $field => $ordering)
1552 {
1553 $field = mb_strtoupper($field);
1554 $ordering = mb_strtoupper($ordering);
1555 if (isset($fields[$field]))
1556 $order[$fields[$field]] = $ordering == 'DESC' ? 'DESC' : 'ASC';
1557 }
1558 }
1559
1560 return $order ? $order : $default;
1561 }
1562
1563 private static function getFilter($rules, $fields, array $datetimeFieldsList = array())
1564 {
1565 $filter = array();
1566 if (!empty($rules) && is_array($rules))
1567 {
1568 foreach ($rules as $key => $value)
1569 {
1570 if (preg_match('/^([^a-zA-Z]*)(.*)/', $key, $matches))
1571 {
1572 $operation = $matches[1];
1573 $field = $matches[2];
1574
1575 if (in_array($operation, static::$allowedOperations, true) && isset($fields[$field]))
1576 {
1577 if (in_array($field, $datetimeFieldsList))
1578 {
1579 $value = \CRestUtil::unConvertDateTime($value);
1580 }
1581
1582 $filter[$operation.$fields[$field]] = $value;
1583 }
1584 }
1585 }
1586 }
1587
1588 return $filter;
1589 }
1590
1591 private static function checkAdminPermissions()
1592 {
1593 if (!static::isAdmin())
1594 {
1595 throw new AccessException();
1596 }
1597 }
1598
1599 private static function isAdmin()
1600 {
1601 global $USER;
1602 return (
1603 isset($USER)
1604 && is_object($USER)
1605 && (
1606 $USER->isAdmin()
1607 || Loader::includeModule('bitrix24') && \CBitrix24::isPortalAdmin($USER->getID())
1608 )
1609 );
1610 }
1611
1612 private static function getCurrentUserId()
1613 {
1614 global $USER;
1615 return (isset($USER) && is_object($USER)) ? (int)$USER->getID() : 0;
1616 }
1617
1618 private static function generateInternalCode($data)
1619 {
1620 return md5($data['APP_ID'].'@'.$data['CODE']);
1621 }
1622
1623 private static function getAppName($appId)
1624 {
1625 if (!Loader::includeModule('rest'))
1626 return array('*' => 'No app');
1627
1628 $iterator = AppTable::getList(
1629 array(
1630 'filter' => array(
1631 '=CLIENT_ID' => $appId
1632 ),
1633 'select' => array('ID', 'APP_NAME', 'CODE'),
1634 )
1635 );
1636 $app = $iterator->fetch();
1637 $result = array('*' => $app['APP_NAME'] ? $app['APP_NAME'] : $app['CODE']);
1638
1639 $iterator = AppLangTable::getList(array(
1640 'filter' => array(
1641 '=APP_ID' => $app['ID'],
1642 ),
1643 'select' => array('LANGUAGE_ID', 'MENU_NAME')
1644 ));
1645 while($lang = $iterator->fetch())
1646 {
1647 $result[mb_strtoupper($lang['LANGUAGE_ID'])] = $lang['MENU_NAME'];
1648 }
1649
1650 return $result;
1651 }
1652
1653 private static function getAppId($clientId)
1654 {
1655 if (!Loader::includeModule('rest'))
1656 {
1657 return null;
1658 }
1659
1660 $iterator = AppTable::getList(
1661 array(
1662 'filter' => array(
1663 '=CLIENT_ID' => $clientId
1664 ),
1665 'select' => array('ID'),
1666 )
1667 );
1668 $app = $iterator->fetch();
1669
1670 return (int) $app['ID'];
1671 }
1672
1673 private static function prepareActivityData(array $data, $ignore = false)
1674 {
1675 if (!$ignore)
1676 $data = array_change_key_case($data, CASE_UPPER);
1677 foreach ($data as $key => &$field)
1678 {
1679 if (is_array($field))
1680 $field = self::prepareActivityData($field, $key == 'PROPERTIES' || $key == 'RETURN_PROPERTIES' || $key == 'OPTIONS');
1681 }
1682 return $data;
1683 }
1684
1685 private static function validateActivity($data, $server)
1686 {
1687 if (!is_array($data) || empty($data))
1688 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1689
1690 static::validateActivityCode($data['CODE']);
1691 static::validateActivityHandler($data['HANDLER'], $server);
1692 if (empty($data['NAME']))
1693 throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1694
1695 if (isset($data['PROPERTIES']))
1696 static::validateActivityProperties($data['PROPERTIES']);
1697
1698 if (isset($data['RETURN_PROPERTIES']))
1699 static::validateActivityProperties($data['RETURN_PROPERTIES']);
1700 if (isset($data['DOCUMENT_TYPE']))
1701 static::validateActivityDocumentType($data['DOCUMENT_TYPE']);
1702 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1703 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1704 }
1705
1706 private static function validateProvider($data, $server)
1707 {
1708 if (!is_array($data) || empty($data))
1709 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1710
1711 static::validateActivityCode($data['CODE']);
1712 static::validateActivityHandler($data['HANDLER'], $server);
1713 if (empty($data['NAME']))
1714 throw new RestException('Empty provider NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1715
1716 if (empty($data['TYPE']))
1717 throw new RestException('Empty provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1718
1719 if (!in_array($data['TYPE'], RestProviderTable::getTypesList(), true))
1720 throw new RestException('Unknown provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1721 }
1722
1723 private static function validateRobot($data, $server)
1724 {
1725 if (!is_array($data) || empty($data))
1726 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1727
1728 static::validateActivityCode($data['CODE']);
1729 static::validateActivityHandler($data['HANDLER'], $server);
1730 if (empty($data['NAME']))
1731 throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1732
1733 if (isset($data['PROPERTIES']))
1734 static::validateActivityProperties($data['PROPERTIES']);
1735
1736 if (isset($data['RETURN_PROPERTIES']))
1737 static::validateActivityProperties($data['RETURN_PROPERTIES']);
1738 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1739 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1740 }
1741
1742 private static function validateActivityCode($code)
1743 {
1744 if (empty($code))
1745 {
1746 throw new RestException('Empty activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1747 }
1748 if (!is_string($code) || !preg_match('#^[a-z0-9\.\-_]+$#i', $code))
1749 {
1750 throw new RestException('Wrong activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1751 }
1752 }
1753
1754 private static function validateActivityHandler($handler, $server)
1755 {
1757 }
1758
1759 private static function validateActivityProperties($properties)
1760 {
1761 if (!is_array($properties))
1762 throw new RestException('Wrong properties array!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1763
1764 foreach ($properties as $key => $property)
1765 {
1766 if (!preg_match('#^[a-z][a-z0-9_]*$#i', $key))
1767 throw new RestException('Wrong property key ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1768 if (empty($property['NAME']))
1769 throw new RestException('Empty property NAME ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1770 }
1771 }
1772
1773 private static function validateActivityDocumentType($documentType)
1774 {
1775 try
1776 {
1777 $runtime = \CBPRuntime::getRuntime();
1778 $runtime->startRuntime();
1780 $documentService = $runtime->getService('DocumentService');
1781 $documentService->getDocumentFieldTypes($documentType);
1782 }
1783 catch (\CBPArgumentNullException $e)
1784 {
1785 throw new RestException('Wrong activity DOCUMENT_TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1786 }
1787 }
1788
1789 private static function getDocumentId($documentId): ?array
1790 {
1791 try
1792 {
1793 $documentService = \CBPRuntime::getRuntime()->getDocumentService();
1794 $documentId = $documentService->normalizeDocumentId($documentId);
1795 if ($documentService->getDocument($documentId, select: ['ID']))
1796 {
1797 return $documentId;
1798 }
1799 }
1800 catch (\CBPArgumentException $exception) {}
1801
1802 return null;
1803 }
1804
1805 private static function getDocumentType(array $documentId): ?array
1806 {
1807 try
1808 {
1809 $documentId = \CBPHelper::parseDocumentId($documentId);
1810 $runtime = \CBPRuntime::getRuntime(true);
1811 $documentService = $runtime->getDocumentService();
1812
1813 return $documentService->getDocumentType($documentId);
1814 }
1815 catch (\Exception $e) {}
1816
1817 return null;
1818 }
1819
1820 private static function getTemplateDocumentType(int $id): ?array
1821 {
1822 $tpl = WorkflowTemplateTable::getList([
1823 'select' => ['MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
1824 'filter' => ['=ID' => $id],
1825 ])->fetch();
1826
1827 if ($tpl)
1828 {
1829 return [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']];
1830 }
1831 return null;
1832 }
1833
1834 private static function validateTemplateName($name)
1835 {
1836 if (empty($name))
1837 {
1838 throw new RestException('Empty template name!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1839 }
1840 }
1841
1842 private static function upsertAppPlacement(int $appId, string $code, string $handler)
1843 {
1844 $filter = [
1845 '=APP_ID' => $appId,
1846 '=ADDITIONAL' => $code,
1847 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1848 ];
1849
1850 $dbRes = PlacementTable::getList(array(
1851 'filter' => $filter
1852 ));
1853
1854 $placementHandler = $dbRes->fetch();
1855
1856 if ($placementHandler)
1857 {
1858 $result = PlacementTable::update($placementHandler['ID'], ['PLACEMENT_HANDLER' => $handler]);
1859 }
1860 else
1861 {
1862 $placementBind = array(
1863 'APP_ID' => $appId,
1864 'ADDITIONAL' => $code,
1865 'PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1866 'PLACEMENT_HANDLER' => $handler,
1867 );
1868
1869 $result = PlacementTable::add($placementBind);
1870 }
1871
1872 if(!$result->isSuccess())
1873 {
1874 $errorMessage = $result->getErrorMessages();
1875 throw new RestException(
1876 'Unable to set placement handler: '.implode(', ', $errorMessage),
1877 RestException::ERROR_CORE
1878 );
1879 }
1880 }
1881
1882 private static function deleteAppPlacement(int $appId, string $code = null)
1883 {
1884 $filter = [
1885 '=APP_ID' => $appId,
1886 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1887 ];
1888
1889 if ($code)
1890 {
1891 $filter['=ADDITIONAL'] = $code;
1892 }
1893
1894 $dbRes = PlacementTable::getList(array(
1895 'filter' => $filter
1896 ));
1897
1898 while($placementHandler = $dbRes->fetch())
1899 {
1900 PlacementTable::delete($placementHandler["ID"]);
1901 }
1902 }
1903
1904 private static function prepareTemplateData($data)
1905 {
1906 if (!empty($data))
1907 {
1908 $fileFields = \CRestUtil::saveFile($data);
1909
1910 if ($fileFields)
1911 {
1912 return \Bitrix\Main\IO\File::getFileContents($fileFields['tmp_name']);
1913 }
1914 }
1915 throw new RestException('Incorrect field TEMPLATE_DATA!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1916 }
1917
1918 private static function validateTemplateDocumentType($documentType)
1919 {
1920 try
1921 {
1922 $documentService = \CBPRuntime::getRuntime(true)->getDocumentService();
1923 $documentService->getDocumentFieldTypes($documentType);
1924 }
1925 catch (\CBPArgumentNullException $e)
1926 {
1927 throw new RestException('Incorrect field DOCUMENT_TYPE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1928 }
1929 }
1930
1931 private static function validateTemplateAutoExecution($flag)
1932 {
1933 if ($flag === (string) (int) $flag)
1934 {
1935 $flag = (int) $flag;
1936
1937 if (in_array(
1938 $flag,
1939 [
1944 ],
1945 true
1946 )
1947 )
1948 {
1949 return true;
1950 }
1951 }
1952
1953 throw new RestException('Incorrect field AUTO_EXECUTE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1954 }
1955
1956 private static function generateTemplateSystemCode(\CRestServer $server)
1957 {
1958 $appId = self::getAppId($server->getClientId());
1959
1960 return 'rest_app_'.$appId;
1961 }
1962
1963 private static function extractEventToken($token)
1964 {
1965 $data = \CBPRestActivity::extractToken($token);
1966 if (!$data)
1967 throw new AccessException();
1968 return $data;
1969 }
1970
1977 private static function getApp($server)
1978 {
1979 if(self::$app == null)
1980 {
1981 if (Loader::includeModule('rest'))
1982 {
1983 $result = AppTable::getList(
1984 array(
1985 'filter' => array(
1986 '=CLIENT_ID' => $server->getClientId()
1987 )
1988 )
1989 );
1990 self::$app = $result->fetch();
1991 }
1992 }
1993
1994 return self::$app;
1995 }
1996}
return select
Определения access_edit.php:440
$count
Определения admin_tab.php:4
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
const VALUE_CONTEXT_REST
Определения fieldtype.php:100
static normalizeProperty($property)
Определения fieldtype.php:548
static getTypesList()
Определения restprovider.php:196
static onRestAppUpdate(array $fields)
Определения restservice.php:188
static onRestServiceBuildDescription()
Определения restservice.php:54
static onRestAppDelete(array $fields)
Определения restservice.php:145
static getTaskList($params, $n, $server)
Определения restservice.php:1114
static deleteProvider($params, $n, $server)
Определения restservice.php:1452
const ERROR_ACTIVITY_NOT_FOUND
Определения restservice.php:29
static writeActivityLog($params, $n, $server)
Определения restservice.php:563
const ERROR_TASK_COMPLETED
Определения restservice.php:40
static getActivityList($params, $n, $server)
Определения restservice.php:602
const ERROR_ACTIVITY_ADD_FAILURE
Определения restservice.php:27
static getWorkflowInstances($params, $n, $server)
Определения restservice.php:660
const ERROR_TASK_EXECUTION
Определения restservice.php:41
const ERROR_TEMPLATE_NOT_OWNER
Определения restservice.php:35
static updateActivity($params, $n, $server)
Определения restservice.php:294
static getRobotList($params, $n, $server)
Определения restservice.php:615
static deleteWorkflowTemplate($params, $n, $server)
Определения restservice.php:1072
const ERROR_WRONG_WORKFLOW_ID
Определения restservice.php:31
static updateWorkflowTemplate($params, $n, $server)
Определения restservice.php:989
static addWorkflowTemplate($params, $n, $server)
Определения restservice.php:942
static addProvider($params, $n, $server)
Определения restservice.php:1404
static updateRobot($params, $n, $server)
Определения restservice.php:318
static terminateWorkflow($params, $n, $server)
Определения restservice.php:722
const ERROR_EMPTY_LOG_MESSAGE
Определения restservice.php:30
static startWorkflow($params, $n, $server)
Определения restservice.php:786
static deleteActivity($params, $n, $server)
Определения restservice.php:306
const PLACEMENT_ACTIVITY_PROPERTIES_DIALOG
Определения restservice.php:20
static getProviderList($params, $n, $server)
Определения restservice.php:1494
const ERROR_DELEGATION_NOT_ALLOWED
Определения restservice.php:45
const SCOPE
Определения restservice.php:19
static addActivity($params, $n, $server)
Определения restservice.php:200
const ERROR_TEMPLATE_VALIDATION_FAILURE
Определения restservice.php:33
const ERROR_ACTIVITY_ALREADY_INSTALLED
Определения restservice.php:26
const ERROR_TEMPLATE_NOT_FOUND
Определения restservice.php:34
const ERROR_INVALID_USER_ID
Определения restservice.php:44
const ERROR_TASK_TYPE
Определения restservice.php:39
const ERROR_TASK_VALIDATION
Определения restservice.php:37
static completeTask($params, $n, $server)
Определения restservice.php:1285
const ERROR_SELECT_VALIDATION_FAILURE
Определения restservice.php:42
const ERROR_TASK_NOT_FOUND
Определения restservice.php:38
static delegateTask($params, $n, $server)
Определения restservice.php:1318
static getWorkflowTemplates($params, $n, $server)
Определения restservice.php:877
static $app
Определения restservice.php:22
static deleteRobot($params, $n, $server)
Определения restservice.php:330
static sendEvent($params, $n, $server)
Определения restservice.php:529
static killWorkflow($params, $n, $server)
Определения restservice.php:757
static addRobot($params, $n, $server)
Определения restservice.php:212
const ERROR_ACTIVITY_VALIDATION_FAILURE
Определения restservice.php:28
static checkCallback($handlerUrl, $appInfo=array(), $checkInstallUrl=true)
Определения handlerhelper.php:31
const StartWorkflow
Определения constants.php:212
const Edit
Определения constants.php:154
const Automation
Определения constants.php:156
const Create
Определения constants.php:153
const None
Определения constants.php:152
static getList($arOrder=array("ID"=> "DESC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения taskservice.php:831
static resolveStatus($name)
Определения constants.php:279
const Waiting
Определения constants.php:273
getClientId()
Определения rest.php:362
Определения rest.php:896
$templateId
Определения component_props2.php:51
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$activity
Определения options.php:214
$errors
Определения iblock_catalog_edit.php:74
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
global $USER
Определения csv_new_run.php:40
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
if(!defined('SITE_ID')) $lang
Определения include.php:91
$status
Определения session.php:10
$name
Определения menu_edit.php:35
$map
Определения config.php:5
$value
Определения Param.php:39
$order
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$matches
Определения index.php:22
$clientId
Определения seo_client.php:18
$error
Определения subscription_card_product.php:20
$n
Определения update_log.php:107
$dbRes
Определения yandex_detail.php:168
$iterator
Определения yandex_run.php:610
$fields
Определения yandex_run.php:501