Bitrix-D7 22.6
 
Загрузка...
Поиск...
Не найдено
restservice.php
1<?php
2
3namespace Bitrix\Bizproc;
4
13
14Loader::includeModule('rest');
15
16class RestService extends \IRestService
17{
18 public const SCOPE = 'bizproc';
19 public const PLACEMENT_ACTIVITY_PROPERTIES_DIALOG = 'BIZPROC_ACTIVITY_PROPERTIES_DIALOG';
20
21 protected static $app;
22 private static $allowedOperations = ['', '!', '<', '<=', '>', '>='];
23 //, '><', '!><', '?', '=', '!=', '%', '!%', ''); May be later?
24
25 const ERROR_ACTIVITY_ALREADY_INSTALLED = 'ERROR_ACTIVITY_ALREADY_INSTALLED';
26 const ERROR_ACTIVITY_ADD_FAILURE = 'ERROR_ACTIVITY_ADD_FAILURE';
27 const ERROR_ACTIVITY_VALIDATION_FAILURE = 'ERROR_ACTIVITY_VALIDATION_FAILURE';
28 const ERROR_ACTIVITY_NOT_FOUND = 'ERROR_ACTIVITY_NOT_FOUND';
29 const ERROR_EMPTY_LOG_MESSAGE = 'ERROR_EMPTY_LOG_MESSAGE';
30 const ERROR_WRONG_WORKFLOW_ID = 'ERROR_WRONG_WORKFLOW_ID';
31
32 const ERROR_TEMPLATE_VALIDATION_FAILURE = 'ERROR_TEMPLATE_VALIDATION_FAILURE';
33
34 const ERROR_TASK_VALIDATION = 'ERROR_TASK_VALIDATION';
35 const ERROR_TASK_NOT_FOUND = 'ERROR_TASK_NOT_FOUND';
36 const ERROR_TASK_TYPE = 'ERROR_TASK_TYPE';
37 const ERROR_TASK_COMPLETED = 'ERROR_TASK_COMPLETED';
38 const ERROR_TASK_EXECUTION = 'ERROR_TASK_EXECUTION';
39
40 private const ALLOWED_TASK_ACTIVITIES = [
41 'ReviewActivity',
42 'ApproveActivity',
43 'RequestInformationActivity',
44 'RequestInformationOptionalActivity'
45 ];
46
47 public static function onRestServiceBuildDescription()
48 {
49 $map = [];
50
51 if (\CBPRuntime::isFeatureEnabled())
52 {
53 $map = [
54 //activity
55 'bizproc.activity.add' => [__CLASS__, 'addActivity'],
56 'bizproc.activity.update' => [__CLASS__, 'updateActivity'],
57 'bizproc.activity.delete' => [__CLASS__, 'deleteActivity'],
58 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'],
59 'bizproc.activity.list' => [__CLASS__, 'getActivityList'],
60
61 //event
62 'bizproc.event.send' => [__CLASS__, 'sendEvent'],
63
64 //task
65 'bizproc.task.list' => [__CLASS__, 'getTaskList'],
66 'bizproc.task.complete' => [__CLASS__, 'completeTask'],
67
68 //workflow
69 'bizproc.workflow.terminate' => [__CLASS__, 'terminateWorkflow'],
70 'bizproc.workflow.kill' => [__CLASS__, 'killWorkflow'],
71 'bizproc.workflow.start' => [__CLASS__, 'startWorkflow'],
72
73 //workflow.instance
74 'bizproc.workflow.instance.list' => [__CLASS__, 'getWorkflowInstances'],
75
76 //workflow.template
77 'bizproc.workflow.template.list' => [__CLASS__, 'getWorkflowTemplates'],
78 'bizproc.workflow.template.add' => [__CLASS__, 'addWorkflowTemplate'],
79 'bizproc.workflow.template.update' => [__CLASS__, 'updateWorkflowTemplate'],
80 'bizproc.workflow.template.delete' => [__CLASS__, 'deleteWorkflowTemplate'],
81
82 //aliases
83 'bizproc.workflow.instances' => [__CLASS__, 'getWorkflowInstances'],
84 ];
85 }
86
87 if (
88 \CBPRuntime::isFeatureEnabled()
89 || \CBPRuntime::isFeatureEnabled('crm_automation_lead')
90 || \CBPRuntime::isFeatureEnabled('crm_automation_deal')
91 || \CBPRuntime::isFeatureEnabled('crm_automation_order')
92 || \CBPRuntime::isFeatureEnabled('tasks_automation')
93 )
94 {
95 $map = array_merge($map, array(
96 'bizproc.event.send' => [__CLASS__, 'sendEvent'],
97 'bizproc.activity.log' => [__CLASS__, 'writeActivityLog'],
98
99 //robot
100 'bizproc.robot.add' => array(__CLASS__, 'addRobot'),
101 'bizproc.robot.update' => array(__CLASS__, 'updateRobot'),
102 'bizproc.robot.delete' => array(__CLASS__, 'deleteRobot'),
103 'bizproc.robot.list' => array(__CLASS__, 'getRobotList'),
104
105 //provider
106 'bizproc.provider.add' => [__CLASS__, 'addProvider'],
107 'bizproc.provider.delete' => [__CLASS__, 'deleteProvider'],
108 'bizproc.provider.list' => [__CLASS__, 'getProviderList'],
109 ));
110 }
111
112 //placements
113 $map[\CRestUtil::PLACEMENTS] = [
114 static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG => ['private' => true],
115 ];
116
117 return [
118 static::SCOPE => $map,
119 ];
120 }
121
127 public static function onRestAppDelete(array $fields)
128 {
129 $fields = array_change_key_case($fields, CASE_UPPER);
130 if (empty($fields['APP_ID']))
131 return;
132
133 if (!Loader::includeModule('rest'))
134 return;
135
136 $dbRes = AppTable::getById($fields['APP_ID']);
137 $app = $dbRes->fetch();
138
139 if(!$app)
140 return;
141
142 $iterator = RestActivityTable::getList(array(
143 'select' => array('ID'),
144 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
145 ));
146
147 while ($activity = $iterator->fetch())
148 {
149 RestActivityTable::delete($activity['ID']);
150 }
151
152 $iterator = RestProviderTable::getList(array(
153 'select' => array('ID'),
154 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
155 ));
156
157 while ($activity = $iterator->fetch())
158 {
159 RestProviderTable::delete($activity['ID']);
160 }
161
162 self::deleteAppPlacement($app['ID']);
163 }
164
170 public static function onRestAppUpdate(array $fields)
171 {
172 static::onRestAppDelete($fields);
173 }
174
182 public static function addActivity($params, $n, $server)
183 {
184 return self::addActivityInternal($params, $server, false);
185 }
186
194 public static function addRobot($params, $n, $server)
195 {
196 return self::addActivityInternal($params, $server, true);
197 }
198
207 private static function addActivityInternal($params, $server, $isRobot = false)
208 {
209 if(!$server->getClientId())
210 {
211 throw new AccessException("Application context required");
212 }
213
214 self::checkAdminPermissions();
215 $params = self::prepareActivityData($params);
216
217 if ($isRobot)
218 self::validateRobot($params, $server);
219 else
220 self::validateActivity($params, $server);
221
222 $appId = self::getAppId($server->getClientId());
223 $params['APP_ID'] = $server->getClientId();
224 $params['INTERNAL_CODE'] = self::generateInternalCode($params);
225 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
226
227 $iterator = RestActivityTable::getList(array(
228 'select' => array('ID'),
229 'filter' => array('=INTERNAL_CODE' => $params['INTERNAL_CODE'])
230 ));
231 $result = $iterator->fetch();
232 if ($result)
233 {
234 throw new RestException('Activity or Robot already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
235 }
236
237 $params['AUTH_USER_ID'] = isset($params['AUTH_USER_ID'])? (int) $params['AUTH_USER_ID'] : 0;
238 $params['IS_ROBOT'] = $isRobot ? 'Y' : 'N';
239 $params['USE_PLACEMENT'] = (isset($params['USE_PLACEMENT']) && $params['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
240
241 if ($params['USE_PLACEMENT'] === 'Y')
242 {
243 self::validateActivityHandler($params['PLACEMENT_HANDLER'] ?? null, $server);
244 self::upsertAppPlacement($appId, $params['CODE'], $params['PLACEMENT_HANDLER'] ?? null);
245 }
246
247 $result = RestActivityTable::add($params);
248
249 if ($result->getErrors())
250 {
251 if ($params['USE_PLACEMENT'] === 'Y')
252 {
253 self::deleteAppPlacement($appId, $params['CODE']);
254 }
255
256 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
257 }
258
259 return true;
260 }
261
269 public static function updateActivity($params, $n, $server)
270 {
271 return self::updateActivityInternal($params, $server, false);
272 }
273
281 public static function deleteActivity($params, $n, $server)
282 {
283 return self::deleteActivityInternal($params, $server, false);
284 }
285
293 public static function updateRobot($params, $n, $server)
294 {
295 return self::updateActivityInternal($params, $server, true);
296 }
297
305 public static function deleteRobot($params, $n, $server)
306 {
307 return self::deleteActivityInternal($params, $server, true);
308 }
309
318 private static function deleteActivityInternal($params, $server, $isRobot = false)
319 {
320 if(!$server->getClientId())
321 {
322 throw new AccessException("Application context required");
323 }
324
325 $params = array_change_key_case($params, CASE_UPPER);
326 self::checkAdminPermissions();
327 self::validateActivityCode($params['CODE']);
328 $params['APP_ID'] = $server->getClientId();
329 $internalCode = self::generateInternalCode($params);
330
331 $iterator = RestActivityTable::getList(array(
332 'select' => array('ID'),
333 'filter' => array(
334 '=INTERNAL_CODE' => $internalCode,
335 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
336 )
337 ));
338 $result = $iterator->fetch();
339 if (!$result)
340 {
341 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
342 }
343 RestActivityTable::delete($result['ID']);
344 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
345
346 return true;
347 }
348
360 private static function updateActivityInternal($params, $server, $isRobot = false)
361 {
362 if(!$server->getClientId())
363 {
364 throw new AccessException("Application context required");
365 }
366
367 $params = self::prepareActivityData($params);
368 self::checkAdminPermissions();
369 self::validateActivityCode($params['CODE']);
370 $params['APP_ID'] = $server->getClientId();
371 $internalCode = self::generateInternalCode($params);
372
373 $iterator = RestActivityTable::getList(array(
374 'select' => array('ID'),
375 'filter' => array(
376 '=INTERNAL_CODE' => $internalCode,
377 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
378 )
379 ));
380 $result = $iterator->fetch();
381 if (!$result)
382 {
383 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
384 }
385
386 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
387
388 if (!$fields)
389 {
390 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
391 }
392
393 $toUpdate = [];
394
395 if (isset($fields['HANDLER']))
396 {
397 self::validateActivityHandler($fields['HANDLER'], $server);
398 $toUpdate['HANDLER'] = $fields['HANDLER'];
399 }
400
401 if (isset($fields['AUTH_USER_ID']))
402 {
403 $toUpdate['AUTH_USER_ID'] = (int) $fields['AUTH_USER_ID'];
404 }
405
406 if (isset($fields['USE_SUBSCRIPTION']))
407 {
408 $toUpdate['USE_SUBSCRIPTION'] = (string) $fields['USE_SUBSCRIPTION'];
409 }
410
411 if (isset($fields['USE_PLACEMENT']))
412 {
413 $toUpdate['USE_PLACEMENT'] = ($fields['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
414 }
415
416 if (!empty($fields['NAME']))
417 {
418 $toUpdate['NAME'] = $fields['NAME'];
419 }
420
421 if (isset($fields['DESCRIPTION']))
422 {
423 $toUpdate['DESCRIPTION'] = $fields['DESCRIPTION'];
424 }
425
426 if (isset($fields['PROPERTIES']))
427 {
428 self::validateActivityProperties($fields['PROPERTIES']);
429 $toUpdate['PROPERTIES'] = $fields['PROPERTIES'];
430 }
431
432 if (isset($fields['RETURN_PROPERTIES']))
433 {
434 self::validateActivityProperties($fields['RETURN_PROPERTIES']);
435 $toUpdate['RETURN_PROPERTIES'] = $fields['RETURN_PROPERTIES'];
436 }
437
438 if (isset($fields['DOCUMENT_TYPE']))
439 {
440 if (empty($fields['DOCUMENT_TYPE']))
441 {
442 $toUpdate['DOCUMENT_TYPE'] = null;
443 }
444 else
445 {
446 static::validateActivityDocumentType($fields['DOCUMENT_TYPE']);
447 $toUpdate['DOCUMENT_TYPE'] = $fields['DOCUMENT_TYPE'];
448 }
449 }
450
451 if (isset($fields['FILTER']))
452 {
453 if (empty($fields['FILTER']))
454 {
455 $toUpdate['FILTER'] = null;
456 }
457 else
458 {
459 if (!is_array($fields['FILTER']))
460 {
461 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
462 }
463 $toUpdate['FILTER'] = $fields['FILTER'];
464 }
465 }
466
467 if (!$toUpdate)
468 {
469 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
470 }
471
472 if (isset($fields['PLACEMENT_HANDLER']))
473 {
474 self::validateActivityHandler($fields['PLACEMENT_HANDLER'], $server);
475 self::upsertAppPlacement(self::getAppId($params['APP_ID']), $params['CODE'], $fields['PLACEMENT_HANDLER']);
476 }
477
478 if (isset($toUpdate['USE_PLACEMENT']) && $toUpdate['USE_PLACEMENT'] === 'N')
479 {
480 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
481 }
482
483 RestActivityTable::update($result['ID'], $toUpdate);
484
485 return true;
486 }
487
496 public static function sendEvent($params, $n, $server)
497 {
498 $params = array_change_key_case($params, CASE_UPPER);
499 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
500
501 \CBPRuntime::sendExternalEvent(
502 $workflowId,
503 $activityName,
504 array(
505 'EVENT_ID' => $eventId,
506 'RETURN_VALUES' => isset($params['RETURN_VALUES']) ? $params['RETURN_VALUES'] : array(),
507 'LOG_MESSAGE' => isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : '',
508 )
509 );
510
511 return true;
512 }
513
522 public static function writeActivityLog($params, $n, $server)
523 {
524 $params = array_change_key_case($params, CASE_UPPER);
525 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
526
527 $logMessage = isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : '';
528
529 if (empty($logMessage))
530 throw new RestException('Empty log message!', self::ERROR_EMPTY_LOG_MESSAGE);
531
532 \CBPRuntime::sendExternalEvent(
533 $workflowId,
534 $activityName,
535 array(
536 'EVENT_ID' => $eventId,
537 'LOG_ACTION' => true,
538 'LOG_MESSAGE' => $logMessage
539 )
540 );
541
542 return true;
543 }
544
553 public static function getActivityList($params, $n, $server)
554 {
555 return self::getActivityListInternal($params, $server, false);
556 }
557
566 public static function getRobotList($params, $n, $server)
567 {
568 return self::getActivityListInternal($params, $server, true);
569 }
570
578 private static function getActivityListInternal($params, $server, $isRobot = false)
579 {
580 if(!$server->getClientId())
581 {
582 throw new AccessException("Application context required");
583 }
584
585 self::checkAdminPermissions();
586 $iterator = RestActivityTable::getList(array(
587 'select' => array('CODE'),
588 'filter' => array(
589 '=APP_ID' => $server->getClientId(),
590 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
591 )
592 ));
593
594 $result = array();
595 while ($row = $iterator->fetch())
596 {
597 $result[] = $row['CODE'];
598 }
599 return $result;
600 }
601
611 public static function getWorkflowInstances($params, $n, $server)
612 {
613 self::checkAdminPermissions();
614 $params = array_change_key_case($params, CASE_UPPER);
615
616 $fields = array(
617 'ID' => 'ID',
618 'MODIFIED' => 'MODIFIED',
619 'OWNED_UNTIL' => 'OWNED_UNTIL',
620 'MODULE_ID' => 'MODULE_ID',
621 'ENTITY' => 'ENTITY',
622 'DOCUMENT_ID' => 'DOCUMENT_ID',
623 'STARTED' => 'STARTED',
624 'STARTED_BY' => 'STARTED_BY',
625 'TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
626 );
627
628 $select = static::getSelect($params['SELECT'], $fields, array('ID', 'MODIFIED', 'OWNED_UNTIL'));
629 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'OWNED_UNTIL'));
630 $order = static::getOrder($params['ORDER'], $fields, array('MODIFIED' => 'DESC'));
631
632 $iterator = WorkflowInstanceTable::getList(array(
633 'select' => $select,
634 'filter' => $filter,
635 'order' => $order,
636 'limit' => static::LIST_LIMIT,
637 'offset' => (int) $n,
638 'count_total' => true,
639 ));
640
641 $result = array();
642 while ($row = $iterator->fetch())
643 {
644 if (isset($row['MODIFIED']))
645 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
646 if (isset($row['STARTED']))
647 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
648 if (isset($row['OWNED_UNTIL']))
649 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
650 $result[] = $row;
651 }
652
653 return static::setNavData($result, ['count' => $iterator->getCount(), 'offset' => $n]);
654 }
655
664 public static function terminateWorkflow($params, $n, $server)
665 {
666 self::checkAdminPermissions();
667 $params = array_change_key_case($params, CASE_UPPER);
668
669 if (empty($params['ID']))
670 {
671 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
672 }
673
674 $id = $params['ID'];
675 $status = isset($params['STATUS']) ? (string)$params['STATUS'] : '';
676 $errors = [];
677
678 if (!\CBPDocument::terminateWorkflow($id, [], $errors, $status))
679 {
680 throw new RestException($errors[0]['message']);
681 }
682
683 return true;
684 }
685
694 public static function killWorkflow($params, $n, $server)
695 {
696 self::checkAdminPermissions();
697 $params = array_change_key_case($params, CASE_UPPER);
698
699 if (empty($params['ID']))
700 {
701 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
702 }
703
704 $id = $params['ID'];
705 $errors = \CBPDocument::killWorkflow($id);
706
707 if ($errors)
708 {
709 throw new RestException($errors[0]['message']);
710 }
711
712 return true;
713 }
714
723 public static function startWorkflow($params, $n, $server)
724 {
725 $params = array_change_key_case($params, CASE_UPPER);
726
727 if (empty($params['TEMPLATE_ID']))
728 {
729 throw new RestException('Empty TEMPLATE_ID', self::ERROR_WRONG_WORKFLOW_ID);
730 }
731 $templateId = (int)$params['TEMPLATE_ID'];
732 $tplDocumentType = self::getTemplateDocumentType($templateId);
733
734 if (!$tplDocumentType)
735 {
736 throw new RestException('Template not found', self::ERROR_WRONG_WORKFLOW_ID);
737 }
738
739 //hotfix #0120474
740 $getParams = array_change_key_case($_GET, CASE_UPPER);
741 if (isset($getParams['DOCUMENT_ID']) && is_array($getParams['DOCUMENT_ID']))
742 {
743 $params['DOCUMENT_ID'] = $getParams['DOCUMENT_ID'];
744 }
745
746 $documentId = self::getDocumentId($params['DOCUMENT_ID']);
747
748 if (!$documentId)
749 {
750 throw new RestException('Wrong DOCUMENT_ID!');
751 }
752
753 $documentType = self::getDocumentType($documentId);
754
755 if (!$documentType)
756 {
757 throw new RestException('Incorrect document type!');
758 }
759 if (!self::isEqualDocumentType($tplDocumentType, $documentType))
760 {
761 throw new RestException('Template type and DOCUMENT_ID mismatch!');
762 }
763
764 self::checkStartWorkflowPermissions($documentId, $templateId);
765
766 $workflowParameters = isset($params['PARAMETERS']) && is_array($params['PARAMETERS']) ? $params['PARAMETERS'] : [];
767
768 $workflowParameters[\CBPDocument::PARAM_TAGRET_USER] = self::getCurrentUserId();
769
770 $errors = [];
771 $workflowId = \CBPDocument::startWorkflow($templateId, $documentId, $workflowParameters, $errors);
772
773 if (!$workflowId)
774 {
775 throw new RestException($errors[0]['message']);
776 }
777
778 return $workflowId;
779 }
780
781 private static function checkStartWorkflowPermissions(array $documentId, $templateId)
782 {
783 if (static::isAdmin())
784 {
785 return true;
786 }
787
788 if (
789 \CBPDocument::CanUserOperateDocument(
790 \CBPCanUserOperateOperation::StartWorkflow,
791 static::getCurrentUserId(),
792 $documentId,
793 ['WorkflowTemplateId' => $templateId]
794 )
795 )
796 {
797 return true;
798 }
799
800 throw new AccessException();
801 }
802
813 public static function getWorkflowTemplates($params, $n, $server)
814 {
815 self::checkAdminPermissions();
816 $params = array_change_key_case($params, CASE_UPPER);
817
818 $fields = array(
819 'ID' => 'ID',
820 'MODULE_ID' => 'MODULE_ID',
821 'ENTITY' => 'ENTITY',
822 'DOCUMENT_TYPE' => 'DOCUMENT_TYPE',
823 'AUTO_EXECUTE' => 'AUTO_EXECUTE',
824 'NAME' => 'NAME',
825 'DESCRIPTION' => 'DESCRIPTION',
826 'TEMPLATE' => 'TEMPLATE',
827 'PARAMETERS' => 'PARAMETERS',
828 'VARIABLES' => 'VARIABLES',
829 'CONSTANTS' => 'CONSTANTS',
830 'MODIFIED' => 'MODIFIED',
831 'IS_MODIFIED' => 'IS_MODIFIED',
832 'USER_ID' => 'USER_ID',
833 'SYSTEM_CODE' => 'SYSTEM_CODE',
834 );
835
836 $select = static::getSelect($params['SELECT'], $fields, array('ID'));
837 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED'));
838 $filter['<AUTO_EXECUTE'] = \CBPDocumentEventType::Automation;
839
840 $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'ASC'));
841
842 $iterator = WorkflowTemplateTable::getList(array(
843 'select' => $select,
844 'filter' => $filter,
845 'order' => $order,
846 'limit' => static::LIST_LIMIT,
847 'offset' => (int) $n,
848 'count_total' => true,
849 ));
850
851 $countTotal = $iterator->getCount();
852
853 $iterator = new \CBPWorkflowTemplateResult($iterator, \CBPWorkflowTemplateLoader::useGZipCompression());
854
855 $result = array();
856 while ($row = $iterator->fetch())
857 {
858 if (isset($row['MODIFIED']))
859 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
860 if (isset($row['STARTED']))
861 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
862 if (isset($row['OWNED_UNTIL']))
863 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
864 $result[] = $row;
865 }
866
867 return static::setNavData($result, ['count' => $countTotal, 'offset' => $n]);
868 }
869
877 public static function addWorkflowTemplate($params, $n, $server)
878 {
879 if(!$server->getClientId())
880 {
881 throw new AccessException("Application context required");
882 }
883
884 self::checkAdminPermissions();
885 $params = array_change_key_case($params, CASE_UPPER);
886
887 self::validateTemplateDocumentType($params['DOCUMENT_TYPE']);
888 self::validateTemplateName($params['NAME']);
889
890 $autoExecute = \CBPDocumentEventType::None;
891 if (isset($params['AUTO_EXECUTE']))
892 {
893 self::validateTemplateAutoExecution($params['AUTO_EXECUTE']);
894 $autoExecute = (int) $params['AUTO_EXECUTE'];
895 }
896
897 $data = self::prepareTemplateData($params['TEMPLATE_DATA']);
898
899 return \CBPWorkflowTemplateLoader::ImportTemplate(
900 0,
901 $params['DOCUMENT_TYPE'],
902 $autoExecute,
903 $params['NAME'],
904 isset($params['DESCRIPTION']) ? (string) $params['DESCRIPTION'] : '',
905 $data,
906 self::generateTemplateSystemCode($server)
907 );
908 }
909
917 public static function updateWorkflowTemplate($params, $n, $server)
918 {
919 if(!$server->getClientId())
920 {
921 throw new AccessException("Application context required");
922 }
923
924 self::checkAdminPermissions();
925 $params = array_change_key_case($params, CASE_UPPER);
926
927 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
928
929 if (!$fields)
930 {
931 throw new RestException("No fields to update.");
932 }
933
934 $tpl = WorkflowTemplateTable::getList(array(
935 'select' => ['ID', 'SYSTEM_CODE', 'NAME', 'DESCRIPTION', 'AUTO_EXECUTE', 'MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
936 'filter' => ['=ID' => (int) $params['ID']],
937 ))->fetch();
938
939 if (!$tpl)
940 {
941 throw new RestException("Workflow template not found.");
942 }
943
944 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
945 {
946 throw new RestException("You can update ONLY templates created by current application");
947 }
948
949 if (isset($fields['NAME']))
950 {
951 self::validateTemplateName($fields['NAME']);
952 $tpl['NAME'] = $fields['NAME'];
953 }
954
955 if (isset($fields['DESCRIPTION']))
956 {
957 $tpl['DESCRIPTION'] = (string) $fields['DESCRIPTION'];
958 }
959
960 if (isset($fields['AUTO_EXECUTE']))
961 {
962 self::validateTemplateAutoExecution($fields['AUTO_EXECUTE']);
963 $tpl['AUTO_EXECUTE'] = (int) $fields['AUTO_EXECUTE'];
964 }
965
966 if (isset($fields['TEMPLATE_DATA']))
967 {
968 $data = self::prepareTemplateData($fields['TEMPLATE_DATA']);
969
970 return \CBPWorkflowTemplateLoader::ImportTemplate(
971 $tpl['ID'],
972 [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']],
973 $tpl['AUTO_EXECUTE'],
974 $tpl['NAME'],
975 $tpl['DESCRIPTION'],
976 $data,
977 $tpl['SYSTEM_CODE']
978 );
979 }
980 else
981 {
982 return \CBPWorkflowTemplateLoader::Update($tpl['ID'], [
983 'NAME' => $tpl['NAME'],
984 'DESCRIPTION' => $tpl['DESCRIPTION'],
985 'AUTO_EXECUTE' => $tpl['AUTO_EXECUTE'],
986 ]);
987 }
988 }
989
997 public static function deleteWorkflowTemplate($params, $n, $server)
998 {
999 if(!$server->getClientId())
1000 {
1001 throw new AccessException("Application context required");
1002 }
1003
1004 self::checkAdminPermissions();
1005 $params = array_change_key_case($params, CASE_UPPER);
1006
1007 $tpl = WorkflowTemplateTable::getList(array(
1008 'select' => ['ID', 'SYSTEM_CODE'],
1009 'filter' => ['=ID' => (int) $params['ID']],
1010 ))->fetch();
1011
1012 if (!$tpl)
1013 {
1014 throw new RestException("Workflow template not found.");
1015 }
1016
1017 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
1018 {
1019 throw new RestException("You can delete ONLY templates created by current application");
1020 }
1021
1022 \CBPWorkflowTemplateLoader::Delete($tpl['ID']);
1023 }
1024
1032 public static function getTaskList($params, $n, $server)
1033 {
1034 $params = array_change_key_case($params, CASE_UPPER);
1035
1036 $fields = array(
1037 'ID' => 'ID',
1038 'ACTIVITY' => 'ACTIVITY',
1039 'ACTIVITY_NAME' => 'ACTIVITY_NAME',
1040 'WORKFLOW_ID' => 'WORKFLOW_ID',
1041 'DOCUMENT_NAME' => 'DOCUMENT_NAME',
1042 'DESCRIPTION' => 'DESCRIPTION',
1043 'NAME' => 'NAME',
1044 'MODIFIED' => 'MODIFIED',
1045 'WORKFLOW_STARTED' => 'WORKFLOW_STARTED',
1046 'WORKFLOW_STARTED_BY' => 'WORKFLOW_STARTED_BY',
1047 'OVERDUE_DATE' => 'OVERDUE_DATE',
1048 'WORKFLOW_TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
1049 'WORKFLOW_TEMPLATE_NAME' => 'WORKFLOW_TEMPLATE_NAME',
1050 'WORKFLOW_STATE' => 'WORKFLOW_STATE',
1051 'STATUS' => 'STATUS',
1052 'USER_ID' => 'USER_ID',
1053 'USER_STATUS' => 'USER_STATUS',
1054 'MODULE_ID' => 'MODULE_ID',
1055 'ENTITY' => 'ENTITY',
1056 'DOCUMENT_ID' => 'DOCUMENT_ID',
1057 'PARAMETERS' => 'PARAMETERS',
1058 );
1059
1060 $select = static::getSelect($params['SELECT'], $fields, array('ID', 'WORKFLOW_ID', 'DOCUMENT_NAME', 'NAME'));
1061 $select = array_merge(array('MODULE', 'ENTITY', 'DOCUMENT_ID'), $select);
1062 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'WORKFLOW_STARTED', 'OVERDUE_DATE'));
1063 $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'DESC'));
1064
1065 $currentUserId = self::getCurrentUserId();
1066 $isAdmin = static::isAdmin();
1067
1068 if (!$isAdmin && !isset($filter['USER_ID']))
1069 {
1070 $filter['USER_ID'] = $currentUserId;
1071 }
1072
1073 $targetUserId = isset($filter['USER_ID'])? (int)$filter['USER_ID'] : 0;
1074 if ($targetUserId !== $currentUserId && !\CBPHelper::checkUserSubordination($currentUserId, $targetUserId))
1075 {
1076 self::checkAdminPermissions();
1077 }
1078
1079 $iterator = \CBPTaskService::getList(
1080 $order,
1081 $filter,
1082 false,
1083 static::getNavData($n),
1084 $select
1085 );
1086
1087 $result = array();
1088 while ($row = $iterator->fetch())
1089 {
1090 if (isset($row['MODIFIED']))
1091 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
1092 if (isset($row['WORKFLOW_STARTED']))
1093 $row['WORKFLOW_STARTED'] = \CRestUtil::convertDateTime($row['WORKFLOW_STARTED']);
1094 if (isset($row['OVERDUE_DATE']))
1095 $row['OVERDUE_DATE'] = \CRestUtil::convertDateTime($row['OVERDUE_DATE']);
1096 $row['DOCUMENT_URL'] = \CBPDocument::getDocumentAdminPage(array(
1097 $row['MODULE_ID'], $row['ENTITY'], $row['DOCUMENT_ID']
1098 ));
1099
1100 if (isset($row['PARAMETERS']))
1101 {
1102 $row['PARAMETERS'] = static::prepareTaskParameters($row['PARAMETERS'], $row);
1103 }
1104
1105 $result[] = $row;
1106 }
1107
1108 return static::setNavData($result, $iterator);
1109 }
1110
1111 private static function prepareTaskParameters(array $parameters, array $task)
1112 {
1113 $whiteList = [
1114 ['CommentLabelMessage', 'CommentLabel'],
1115 'CommentRequired', 'ShowComment',
1116 ['TaskButtonMessage', 'StatusOkLabel'],
1117 ['TaskButton1Message', 'StatusYesLabel'],
1118 ['TaskButton2Message', 'StatusNoLabel'],
1119 ['TaskButtonCancelMessage', 'StatusCancelLabel'],
1120 ['REQUEST', 'Fields'],
1121 ];
1122
1123 $filtered = [];
1124
1125 foreach ($whiteList as $whiteKey)
1126 {
1127 $filterKey = $whiteKey;
1128 if (is_array($whiteKey))
1129 {
1130 $filterKey = $whiteKey[1];
1131 $whiteKey = $whiteKey[0];
1132 }
1133 if (isset($parameters[$whiteKey]))
1134 {
1135 $filtered[$filterKey] = $parameters[$whiteKey];
1136 }
1137 }
1138
1139 if (isset($filtered['Fields']))
1140 {
1141 $filtered['Fields'] = self::externalizeRequestFields($task, $filtered['Fields']);
1142 }
1143
1144 return $filtered;
1145 }
1146
1147 private static function externalizeRequestFields($task, array $fields): array
1148 {
1149 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1150 $result = [];
1151 foreach ($fields as $requestField)
1152 {
1153 $id = $requestField['Name'];
1154 $requestField['Name'] = $requestField['Title'];
1155 $property = FieldType::normalizeProperty($requestField);
1156 $property['Id'] = $id;
1157
1158 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1159 if ($fieldTypeObject)
1160 {
1161 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1162 $property['Default'] = $fieldTypeObject->externalizeValue('rest', $property['Default']);
1163 }
1164
1165 $result[] = $property;
1166 }
1167 return $result;
1168 }
1169
1170 private static function internalizeRequestFields($task, array $values): array
1171 {
1172 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1173 $result = [];
1174
1175 foreach ($task['PARAMETERS']['REQUEST'] as $property)
1176 {
1177 if (!isset($values[$property['Name']]))
1178 {
1179 continue;
1180 }
1181
1182 $property = FieldType::normalizeProperty($property);
1183 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1184 if ($fieldTypeObject)
1185 {
1186 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1187 $result[$property['Name']] = $fieldTypeObject->internalizeValue('rest', $values[$property['Name']]);
1188 }
1189 }
1190 return $result;
1191 }
1192
1200 public static function completeTask($params, $n, $server)
1201 {
1202 $params = array_change_key_case($params, CASE_UPPER);
1203 self::validateTaskParameters($params);
1204
1205 $userId = self::getCurrentUserId();
1206 $task = static::getTask($params['TASK_ID'], $userId);
1207
1208 if (!in_array($task['ACTIVITY'], self::ALLOWED_TASK_ACTIVITIES))
1209 {
1210 throw new RestException('Incorrect task type', self::ERROR_TASK_TYPE);
1211 }
1212
1213 if (!empty($params['FIELDS']))
1214 {
1215 $params['FIELDS'] = self::internalizeRequestFields($task, $params['FIELDS']);
1216 }
1217
1218 $errors = array();
1219 $request = array(
1220 'INLINE_USER_STATUS' => \CBPTaskUserStatus::resolveStatus($params['STATUS']),
1221 'task_comment' => !empty($params['COMMENT']) && is_string($params['COMMENT']) ? $params['COMMENT'] : null,
1222 'fields' => $params['FIELDS'] ?? null,
1223 );
1224
1225 if (!\CBPDocument::postTaskForm($task, $userId, $request, $errors))
1226 {
1227 throw new RestException($errors[0]["message"], self::ERROR_TASK_EXECUTION);
1228 }
1229
1230 return true;
1231 }
1232
1233 private static function validateTaskParameters(array $params)
1234 {
1235 if (empty($params['TASK_ID']))
1236 {
1237 throw new RestException('empty TASK_ID', self::ERROR_TASK_VALIDATION);
1238 }
1239 if (empty($params['STATUS']) || \CBPTaskUserStatus::resolveStatus($params['STATUS']) === null)
1240 {
1241 throw new RestException('incorrect STATUS', self::ERROR_TASK_VALIDATION);
1242 }
1243 }
1244
1245 private static function getTask($id, $userId)
1246 {
1247 $dbTask = \CBPTaskService::getList(
1248 array(),
1249 array("ID" => (int)$id, "USER_ID" => $userId),
1250 false,
1251 false,
1252 array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "USER_STATUS")
1253 );
1254 $task = $dbTask->fetch();
1255
1256 if (!$task)
1257 {
1258 throw new RestException('Task not found', self::ERROR_TASK_NOT_FOUND);
1259 }
1260 elseif ((int)$task['USER_STATUS'] !== \CBPTaskUserStatus::Waiting)
1261 {
1262 throw new RestException('Task already completed', self::ERROR_TASK_COMPLETED);
1263 }
1264
1265 if ($task)
1266 {
1267 $task["PARAMETERS"]["DOCUMENT_ID"] = \CBPStateService::getStateDocumentId($task['WORKFLOW_ID']);
1268 $task["MODULE_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][0];
1269 $task["ENTITY"] = $task["PARAMETERS"]["DOCUMENT_ID"][1];
1270 $task["DOCUMENT_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][2];
1271 }
1272
1273 return $task;
1274 }
1275
1283 public static function addProvider($params, $n, $server)
1284 {
1285 if (Loader::includeModule('messageservice'))
1286 {
1287 return \Bitrix\MessageService\RestService::addSender($params, $n, $server);
1288 }
1289
1290 if(!$server->getClientId())
1291 {
1292 throw new AccessException("Application context required");
1293 }
1294
1295 self::checkAdminPermissions();
1296 $params = self::prepareActivityData($params);
1297
1298 self::validateProvider($params, $server);
1299
1300 $params['APP_ID'] = $server->getClientId();
1301 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
1302
1303 $iterator = RestProviderTable::getList(array(
1304 'select' => array('ID'),
1305 'filter' => array(
1306 '=APP_ID' => $params['APP_ID'],
1307 '=CODE' => $params['CODE']
1308 )
1309 ));
1310 $result = $iterator->fetch();
1311 if ($result)
1312 {
1313 throw new RestException('Provider already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
1314 }
1315
1316 $result = RestProviderTable::add($params);
1317
1318 if ($result->getErrors())
1319 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
1320
1321 return true;
1322 }
1323
1331 public static function deleteProvider($params, $n, $server)
1332 {
1333 if (Loader::includeModule('messageservice'))
1334 {
1335 return \Bitrix\MessageService\RestService::deleteSender($params, $n, $server);
1336 }
1337
1338 if(!$server->getClientId())
1339 {
1340 throw new AccessException("Application context required");
1341 }
1342
1343 $params = array_change_key_case($params, CASE_UPPER);
1344 self::checkAdminPermissions();
1345 self::validateActivityCode($params['CODE']);
1346 $params['APP_ID'] = $server->getClientId();
1347
1348 $iterator = RestProviderTable::getList(array(
1349 'select' => array('ID'),
1350 'filter' => array(
1351 '=APP_ID' => $params['APP_ID'],
1352 '=CODE' => $params['CODE']
1353 )
1354 ));
1355 $result = $iterator->fetch();
1356 if (!$result)
1357 {
1358 throw new RestException('Provider not found!', self::ERROR_ACTIVITY_NOT_FOUND);
1359 }
1360 RestProviderTable::delete($result['ID']);
1361
1362 return true;
1363 }
1364
1373 public static function getProviderList($params, $n, $server)
1374 {
1375 if (Loader::includeModule('messageservice'))
1376 {
1377 return \Bitrix\MessageService\RestService::getSenderList($params, $n, $server);
1378 }
1379
1380 if(!$server->getClientId())
1381 {
1382 throw new AccessException("Application context required");
1383 }
1384
1385 self::checkAdminPermissions();
1386 $iterator = RestProviderTable::getList(array(
1387 'select' => array('CODE'),
1388 'filter' => array(
1389 '=APP_ID' => $server->getClientId()
1390 )
1391 ));
1392
1393 $result = array();
1394 while ($row = $iterator->fetch())
1395 {
1396 $result[] = $row['CODE'];
1397 }
1398 return $result;
1399 }
1400
1401 private static function getSelect($rules, $fields, $default = array())
1402 {
1403 $select = array();
1404 if (!empty($rules) && is_array($rules))
1405 {
1406 foreach ($rules as $field)
1407 {
1408 $field = mb_strtoupper($field);
1409 if (isset($fields[$field]) && !in_array($field, $select))
1410 $select[$field] = $fields[$field];
1411 }
1412 }
1413
1414 return $select ? $select : $default;
1415 }
1416
1417 private static function getOrder($rules, $fields, array $default = array())
1418 {
1419 $order = array();
1420 if (!empty($rules) && is_array($rules))
1421 {
1422 foreach ($rules as $field => $ordering)
1423 {
1424 $field = mb_strtoupper($field);
1425 $ordering = mb_strtoupper($ordering);
1426 if (isset($fields[$field]))
1427 $order[$fields[$field]] = $ordering == 'DESC' ? 'DESC' : 'ASC';
1428 }
1429 }
1430
1431 return $order ? $order : $default;
1432 }
1433
1434 private static function getFilter($rules, $fields, array $datetimeFieldsList = array())
1435 {
1436 $filter = array();
1437 if (!empty($rules) && is_array($rules))
1438 {
1439 foreach ($rules as $key => $value)
1440 {
1441 if (preg_match('/^([^a-zA-Z]*)(.*)/', $key, $matches))
1442 {
1443 $operation = $matches[1];
1444 $field = $matches[2];
1445
1446 if (in_array($operation, static::$allowedOperations, true) && isset($fields[$field]))
1447 {
1448 if (in_array($field, $datetimeFieldsList))
1449 $value = \CRestUtil::unConvertDateTime($value);
1450
1451 $filter[$operation.$fields[$field]] = $value;
1452 }
1453 }
1454 }
1455 }
1456
1457 return $filter;
1458 }
1459
1460 private static function checkAdminPermissions()
1461 {
1462 if (!static::isAdmin())
1463 {
1464 throw new AccessException();
1465 }
1466 }
1467
1468 private static function isAdmin()
1469 {
1470 global $USER;
1471 return (
1472 isset($USER)
1473 && is_object($USER)
1474 && (
1475 $USER->isAdmin()
1476 || Loader::includeModule('bitrix24') && \CBitrix24::isPortalAdmin($USER->getID())
1477 )
1478 );
1479 }
1480
1481 private static function getCurrentUserId()
1482 {
1483 global $USER;
1484 return (isset($USER) && is_object($USER)) ? (int)$USER->getID() : 0;
1485 }
1486
1487 private static function generateInternalCode($data)
1488 {
1489 return md5($data['APP_ID'].'@'.$data['CODE']);
1490 }
1491
1492 private static function getAppName($appId)
1493 {
1494 if (!Loader::includeModule('rest'))
1495 return array('*' => 'No app');
1496
1497 $iterator = AppTable::getList(
1498 array(
1499 'filter' => array(
1500 '=CLIENT_ID' => $appId
1501 ),
1502 'select' => array('ID', 'APP_NAME', 'CODE'),
1503 )
1504 );
1505 $app = $iterator->fetch();
1506 $result = array('*' => $app['APP_NAME'] ? $app['APP_NAME'] : $app['CODE']);
1507
1508 $iterator = AppLangTable::getList(array(
1509 'filter' => array(
1510 '=APP_ID' => $app['ID'],
1511 ),
1512 'select' => array('LANGUAGE_ID', 'MENU_NAME')
1513 ));
1514 while($lang = $iterator->fetch())
1515 {
1516 $result[mb_strtoupper($lang['LANGUAGE_ID'])] = $lang['MENU_NAME'];
1517 }
1518
1519 return $result;
1520 }
1521
1522 private static function getAppId($clientId)
1523 {
1524 if (!Loader::includeModule('rest'))
1525 {
1526 return null;
1527 }
1528
1529 $iterator = AppTable::getList(
1530 array(
1531 'filter' => array(
1532 '=CLIENT_ID' => $clientId
1533 ),
1534 'select' => array('ID'),
1535 )
1536 );
1537 $app = $iterator->fetch();
1538
1539 return (int) $app['ID'];
1540 }
1541
1542 private static function prepareActivityData(array $data, $ignore = false)
1543 {
1544 if (!$ignore)
1545 $data = array_change_key_case($data, CASE_UPPER);
1546 foreach ($data as $key => &$field)
1547 {
1548 if (is_array($field))
1549 $field = self::prepareActivityData($field, $key == 'PROPERTIES' || $key == 'RETURN_PROPERTIES' || $key == 'OPTIONS');
1550 }
1551 return $data;
1552 }
1553
1554 private static function validateActivity($data, $server)
1555 {
1556 if (!is_array($data) || empty($data))
1557 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1558
1559 static::validateActivityCode($data['CODE']);
1560 static::validateActivityHandler($data['HANDLER'], $server);
1561 if (empty($data['NAME']))
1562 throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1563
1564 if (isset($data['PROPERTIES']))
1565 static::validateActivityProperties($data['PROPERTIES']);
1566
1567 if (isset($data['RETURN_PROPERTIES']))
1568 static::validateActivityProperties($data['RETURN_PROPERTIES']);
1569 if (isset($data['DOCUMENT_TYPE']))
1570 static::validateActivityDocumentType($data['DOCUMENT_TYPE']);
1571 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1572 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1573 }
1574
1575 private static function validateProvider($data, $server)
1576 {
1577 if (!is_array($data) || empty($data))
1578 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1579
1580 static::validateActivityCode($data['CODE']);
1581 static::validateActivityHandler($data['HANDLER'], $server);
1582 if (empty($data['NAME']))
1583 throw new RestException('Empty provider NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1584
1585 if (empty($data['TYPE']))
1586 throw new RestException('Empty provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1587
1588 if (!in_array($data['TYPE'], RestProviderTable::getTypesList(), true))
1589 throw new RestException('Unknown provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1590 }
1591
1592 private static function validateRobot($data, $server)
1593 {
1594 if (!is_array($data) || empty($data))
1595 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1596
1597 static::validateActivityCode($data['CODE']);
1598 static::validateActivityHandler($data['HANDLER'], $server);
1599 if (empty($data['NAME']))
1600 throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1601
1602 if (isset($data['PROPERTIES']))
1603 static::validateActivityProperties($data['PROPERTIES']);
1604
1605 if (isset($data['RETURN_PROPERTIES']))
1606 static::validateActivityProperties($data['RETURN_PROPERTIES']);
1607 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1608 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1609 }
1610
1611 private static function validateActivityCode($code)
1612 {
1613 if (empty($code))
1614 throw new RestException('Empty activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1615 if (!preg_match('#^[a-z0-9\.\-_]+$#i', $code))
1616 throw new RestException('Wrong activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1617 }
1618
1619 private static function validateActivityHandler($handler, $server)
1620 {
1622 }
1623
1624 private static function validateActivityProperties($properties)
1625 {
1626 if (!is_array($properties))
1627 throw new RestException('Wrong properties array!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1628
1629 foreach ($properties as $key => $property)
1630 {
1631 if (!preg_match('#^[a-z][a-z0-9_]*$#i', $key))
1632 throw new RestException('Wrong property key ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1633 if (empty($property['NAME']))
1634 throw new RestException('Empty property NAME ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1635 }
1636 }
1637
1638 private static function validateActivityDocumentType($documentType)
1639 {
1640 try
1641 {
1642 $runtime = \CBPRuntime::getRuntime();
1643 $runtime->startRuntime();
1645 $documentService = $runtime->getService('DocumentService');
1646 $documentService->getDocumentFieldTypes($documentType);
1647 }
1648 catch (\CBPArgumentNullException $e)
1649 {
1650 throw new RestException('Wrong activity DOCUMENT_TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1651 }
1652 }
1653
1654 private static function getDocumentId($documentId): ?array
1655 {
1656 $documentId = \CBPHelper::parseDocumentId($documentId);
1657 $documentService = \CBPRuntime::getRuntime(true)->getDocumentService();
1658 $documentId = $documentService->normalizeDocumentId($documentId);
1659
1660 if (!$documentService->getDocument($documentId))
1661 {
1662 return null;
1663 }
1664
1665 return $documentId;
1666 }
1667
1668 private static function getDocumentType(array $documentId): ?array
1669 {
1670 try
1671 {
1672 $documentId = \CBPHelper::parseDocumentId($documentId);
1673 $runtime = \CBPRuntime::getRuntime(true);
1674 $documentService = $runtime->getDocumentService();
1675
1676 return $documentService->getDocumentType($documentId);
1677 }
1678 catch (\CBPArgumentNullException $e) {}
1679
1680 return null;
1681 }
1682
1683 private static function getTemplateDocumentType(int $id): ?array
1684 {
1685 $tpl = WorkflowTemplateTable::getList([
1686 'select' => ['MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
1687 'filter' => ['=ID' => $id],
1688 ])->fetch();
1689
1690 if ($tpl)
1691 {
1692 return [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']];
1693 }
1694 return null;
1695 }
1696
1697 private static function isEqualDocumentType(array $a, array $b)
1698 {
1699 return (
1700 (string)$a[0] === (string)$b[0]
1701 && (string)$a[1] === (string)$b[1]
1702 && (string)$a[2] === (string)$b[2]
1703 );
1704 }
1705
1706 private static function validateTemplateName($name)
1707 {
1708 if (empty($name))
1709 {
1710 throw new RestException('Empty activity code!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1711 }
1712 }
1713
1714 private static function upsertAppPlacement(int $appId, string $code, string $handler)
1715 {
1716 $filter = [
1717 '=APP_ID' => $appId,
1718 '=ADDITIONAL' => $code,
1719 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1720 ];
1721
1722 $dbRes = PlacementTable::getList(array(
1723 'filter' => $filter
1724 ));
1725
1726 $placementHandler = $dbRes->fetch();
1727
1728 if ($placementHandler)
1729 {
1730 $result = PlacementTable::update($placementHandler['ID'], ['PLACEMENT_HANDLER' => $handler]);
1731 }
1732 else
1733 {
1734 $placementBind = array(
1735 'APP_ID' => $appId,
1736 'ADDITIONAL' => $code,
1737 'PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1738 'PLACEMENT_HANDLER' => $handler,
1739 );
1740
1741 $result = PlacementTable::add($placementBind);
1742 }
1743
1744 if(!$result->isSuccess())
1745 {
1746 $errorMessage = $result->getErrorMessages();
1747 throw new RestException(
1748 'Unable to set placement handler: '.implode(', ', $errorMessage),
1750 );
1751 }
1752 }
1753
1754 private static function deleteAppPlacement(int $appId, string $code = null)
1755 {
1756 $filter = [
1757 '=APP_ID' => $appId,
1758 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1759 ];
1760
1761 if ($code)
1762 {
1763 $filter['=ADDITIONAL'] = $code;
1764 }
1765
1766 $dbRes = PlacementTable::getList(array(
1767 'filter' => $filter
1768 ));
1769
1770 while($placementHandler = $dbRes->fetch())
1771 {
1772 PlacementTable::delete($placementHandler["ID"]);
1773 }
1774 }
1775
1776 private static function prepareTemplateData($data)
1777 {
1778 if (!empty($data))
1779 {
1780 $fileFields = \CRestUtil::saveFile($data);
1781
1782 if ($fileFields)
1783 {
1784 return file_get_contents($fileFields['tmp_name']);
1785 }
1786 }
1787 throw new RestException('Incorrect field TEMPLATE_DATA!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1788 }
1789
1790 private static function validateTemplateDocumentType($documentType)
1791 {
1792 try
1793 {
1794 $documentService = \CBPRuntime::getRuntime(true)->getDocumentService();
1795 $documentService->getDocumentFieldTypes($documentType);
1796 }
1797 catch (\CBPArgumentNullException $e)
1798 {
1799 throw new RestException('Incorrect field DOCUMENT_TYPE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1800 }
1801 }
1802
1803 private static function validateTemplateAutoExecution($flag)
1804 {
1805 if ($flag === (string) (int) $flag)
1806 {
1807 $flag = (int) $flag;
1808
1809 if (in_array(
1810 $flag,
1811 [
1812 \CBPDocumentEventType::None,
1813 \CBPDocumentEventType::Create,
1814 \CBPDocumentEventType::Edit,
1815 \CBPDocumentEventType::Create | \CBPDocumentEventType::Edit
1816 ],
1817 true
1818 )
1819 )
1820 {
1821 return true;
1822 }
1823 }
1824
1825 throw new RestException('Incorrect field AUTO_EXECUTE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1826 }
1827
1828 private static function generateTemplateSystemCode(\CRestServer $server)
1829 {
1830 $appId = self::getAppId($server->getClientId());
1831
1832 return 'rest_app_'.$appId;
1833 }
1834
1835 private static function extractEventToken($token)
1836 {
1837 $data = \CBPRestActivity::extractToken($token);
1838 if (!$data)
1839 throw new AccessException();
1840 return $data;
1841 }
1842
1849 private static function getApp($server)
1850 {
1851 if(self::$app == null)
1852 {
1853 if (Loader::includeModule('rest'))
1854 {
1855 $result = AppTable::getList(
1856 array(
1857 'filter' => array(
1858 '=CLIENT_ID' => $server->getClientId()
1859 )
1860 )
1861 );
1862 self::$app = $result->fetch();
1863 }
1864 }
1865
1866 return self::$app;
1867 }
1868}
static normalizeProperty($property)
Definition: fieldtype.php:539
static onRestAppUpdate(array $fields)
static onRestServiceBuildDescription()
Definition: restservice.php:47
static onRestAppDelete(array $fields)
static getTaskList($params, $n, $server)
static deleteProvider($params, $n, $server)
static writeActivityLog($params, $n, $server)
static getActivityList($params, $n, $server)
static getWorkflowInstances($params, $n, $server)
static updateActivity($params, $n, $server)
static getRobotList($params, $n, $server)
static deleteWorkflowTemplate($params, $n, $server)
static updateWorkflowTemplate($params, $n, $server)
static addWorkflowTemplate($params, $n, $server)
static addProvider($params, $n, $server)
static updateRobot($params, $n, $server)
static terminateWorkflow($params, $n, $server)
static startWorkflow($params, $n, $server)
static deleteActivity($params, $n, $server)
const PLACEMENT_ACTIVITY_PROPERTIES_DIALOG
Definition: restservice.php:19
static getProviderList($params, $n, $server)
static addActivity($params, $n, $server)
static completeTask($params, $n, $server)
static getWorkflowTemplates($params, $n, $server)
static deleteRobot($params, $n, $server)
static sendEvent($params, $n, $server)
static killWorkflow($params, $n, $server)
static addRobot($params, $n, $server)
static checkCallback($handlerUrl, $appInfo=array(), $checkInstallUrl=true)