Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
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 (self::isEnabled())
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 self::isEnabled()
89 || self::isEnabled('crm_automation_lead')
90 || self::isEnabled('crm_automation_deal')
91 || self::isEnabled('crm_automation_order')
92 || self::isEnabled('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
122 private static function isEnabled(string $feature = 'bizproc'): bool
123 {
124 if (Loader::includeModule('bitrix24'))
125 {
126 return \Bitrix\Bitrix24\Feature::isFeatureEnabled($feature);
127 }
128
129 return true;
130 }
131
137 public static function onRestAppDelete(array $fields)
138 {
139 $fields = array_change_key_case($fields, CASE_UPPER);
140 if (empty($fields['APP_ID']))
141 return;
142
143 if (!Loader::includeModule('rest'))
144 return;
145
146 $dbRes = AppTable::getById($fields['APP_ID']);
147 $app = $dbRes->fetch();
148
149 if(!$app)
150 return;
151
152 $iterator = RestActivityTable::getList(array(
153 'select' => array('ID'),
154 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
155 ));
156
157 while ($activity = $iterator->fetch())
158 {
159 RestActivityTable::delete($activity['ID']);
160 }
161
162 $iterator = RestProviderTable::getList(array(
163 'select' => array('ID'),
164 'filter' => array('=APP_ID' => $app['CLIENT_ID'])
165 ));
166
167 while ($activity = $iterator->fetch())
168 {
169 RestProviderTable::delete($activity['ID']);
170 }
171
172 self::deleteAppPlacement($app['ID']);
173 }
174
180 public static function onRestAppUpdate(array $fields)
181 {
182 static::onRestAppDelete($fields);
183 }
184
192 public static function addActivity($params, $n, $server)
193 {
194 return self::addActivityInternal($params, $server, false);
195 }
196
204 public static function addRobot($params, $n, $server)
205 {
206 return self::addActivityInternal($params, $server, true);
207 }
208
217 private static function addActivityInternal($params, $server, $isRobot = false)
218 {
219 if(!$server->getClientId())
220 {
221 throw new AccessException("Application context required");
222 }
223
224 self::checkAdminPermissions();
225 $params = self::prepareActivityData($params);
226
227 if ($isRobot)
228 self::validateRobot($params, $server);
229 else
230 self::validateActivity($params, $server);
231
232 $appId = self::getAppId($server->getClientId());
233 $params['APP_ID'] = $server->getClientId();
234 $params['INTERNAL_CODE'] = self::generateInternalCode($params);
235 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
236
237 $iterator = RestActivityTable::getList(array(
238 'select' => array('ID'),
239 'filter' => array('=INTERNAL_CODE' => $params['INTERNAL_CODE'])
240 ));
241 $result = $iterator->fetch();
242 if ($result)
243 {
244 throw new RestException('Activity or Robot already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
245 }
246
247 $params['AUTH_USER_ID'] = isset($params['AUTH_USER_ID'])? (int) $params['AUTH_USER_ID'] : 0;
248 $params['IS_ROBOT'] = $isRobot ? 'Y' : 'N';
249 $params['USE_PLACEMENT'] = (isset($params['USE_PLACEMENT']) && $params['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
250
251 if ($params['USE_PLACEMENT'] === 'Y')
252 {
253 self::validateActivityHandler($params['PLACEMENT_HANDLER'] ?? null, $server);
254 self::upsertAppPlacement($appId, $params['CODE'], $params['PLACEMENT_HANDLER'] ?? null);
255 }
256
257 $result = RestActivityTable::add($params);
258
259 if ($result->getErrors())
260 {
261 if ($params['USE_PLACEMENT'] === 'Y')
262 {
263 self::deleteAppPlacement($appId, $params['CODE']);
264 }
265
266 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
267 }
268
269 return true;
270 }
271
279 public static function updateActivity($params, $n, $server)
280 {
281 return self::updateActivityInternal($params, $server, false);
282 }
283
291 public static function deleteActivity($params, $n, $server)
292 {
293 return self::deleteActivityInternal($params, $server, false);
294 }
295
303 public static function updateRobot($params, $n, $server)
304 {
305 return self::updateActivityInternal($params, $server, true);
306 }
307
315 public static function deleteRobot($params, $n, $server)
316 {
317 return self::deleteActivityInternal($params, $server, true);
318 }
319
328 private static function deleteActivityInternal($params, $server, $isRobot = false)
329 {
330 if(!$server->getClientId())
331 {
332 throw new AccessException("Application context required");
333 }
334
335 $params = array_change_key_case($params, CASE_UPPER);
336 self::checkAdminPermissions();
337 self::validateActivityCode($params['CODE']);
338 $params['APP_ID'] = $server->getClientId();
339 $internalCode = self::generateInternalCode($params);
340
341 $iterator = RestActivityTable::getList(array(
342 'select' => array('ID'),
343 'filter' => array(
344 '=INTERNAL_CODE' => $internalCode,
345 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
346 )
347 ));
348 $result = $iterator->fetch();
349 if (!$result)
350 {
351 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
352 }
353 RestActivityTable::delete($result['ID']);
354 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
355
356 return true;
357 }
358
370 private static function updateActivityInternal($params, $server, $isRobot = false)
371 {
372 if(!$server->getClientId())
373 {
374 throw new AccessException("Application context required");
375 }
376
377 $params = self::prepareActivityData($params);
378 self::checkAdminPermissions();
379 self::validateActivityCode($params['CODE']);
380 $params['APP_ID'] = $server->getClientId();
381 $internalCode = self::generateInternalCode($params);
382
383 $iterator = RestActivityTable::getList(array(
384 'select' => array('ID'),
385 'filter' => array(
386 '=INTERNAL_CODE' => $internalCode,
387 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
388 )
389 ));
390 $result = $iterator->fetch();
391 if (!$result)
392 {
393 throw new RestException('Activity or Robot not found!', self::ERROR_ACTIVITY_NOT_FOUND);
394 }
395
396 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
397
398 if (!$fields)
399 {
400 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
401 }
402
403 $toUpdate = [];
404
405 if (isset($fields['HANDLER']))
406 {
407 self::validateActivityHandler($fields['HANDLER'], $server);
408 $toUpdate['HANDLER'] = $fields['HANDLER'];
409 }
410
411 if (isset($fields['AUTH_USER_ID']))
412 {
413 $toUpdate['AUTH_USER_ID'] = (int) $fields['AUTH_USER_ID'];
414 }
415
416 if (isset($fields['USE_SUBSCRIPTION']))
417 {
418 $toUpdate['USE_SUBSCRIPTION'] = (string) $fields['USE_SUBSCRIPTION'];
419 }
420
421 if (isset($fields['USE_PLACEMENT']))
422 {
423 $toUpdate['USE_PLACEMENT'] = ($fields['USE_PLACEMENT'] === 'Y') ? 'Y' : 'N';
424 }
425
426 if (!empty($fields['NAME']))
427 {
428 $toUpdate['NAME'] = $fields['NAME'];
429 }
430
431 if (isset($fields['DESCRIPTION']))
432 {
433 $toUpdate['DESCRIPTION'] = $fields['DESCRIPTION'];
434 }
435
436 if (isset($fields['PROPERTIES']))
437 {
438 self::validateActivityProperties($fields['PROPERTIES']);
439 $toUpdate['PROPERTIES'] = $fields['PROPERTIES'];
440 }
441
442 if (isset($fields['RETURN_PROPERTIES']))
443 {
444 self::validateActivityProperties($fields['RETURN_PROPERTIES']);
445 $toUpdate['RETURN_PROPERTIES'] = $fields['RETURN_PROPERTIES'];
446 }
447
448 if (isset($fields['DOCUMENT_TYPE']))
449 {
450 if (empty($fields['DOCUMENT_TYPE']))
451 {
452 $toUpdate['DOCUMENT_TYPE'] = null;
453 }
454 else
455 {
456 static::validateActivityDocumentType($fields['DOCUMENT_TYPE']);
457 $toUpdate['DOCUMENT_TYPE'] = $fields['DOCUMENT_TYPE'];
458 }
459 }
460
461 if (isset($fields['FILTER']))
462 {
463 if (empty($fields['FILTER']))
464 {
465 $toUpdate['FILTER'] = null;
466 }
467 else
468 {
469 if (!is_array($fields['FILTER']))
470 {
471 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
472 }
473 $toUpdate['FILTER'] = $fields['FILTER'];
474 }
475 }
476
477 if (!$toUpdate)
478 {
479 throw new RestException('No fields to update', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
480 }
481
482 if (isset($fields['PLACEMENT_HANDLER']))
483 {
484 self::validateActivityHandler($fields['PLACEMENT_HANDLER'], $server);
485 self::upsertAppPlacement(self::getAppId($params['APP_ID']), $params['CODE'], $fields['PLACEMENT_HANDLER']);
486 }
487
488 if (isset($toUpdate['USE_PLACEMENT']) && $toUpdate['USE_PLACEMENT'] === 'N')
489 {
490 self::deleteAppPlacement(self::getAppId($params['APP_ID']), $params['CODE']);
491 }
492
493 $updateResult = RestActivityTable::update($result['ID'], $toUpdate);
494
495 if (!$updateResult->isSuccess())
496 {
497 throw new RestException(
498 implode('; ', $updateResult->getErrorMessages()),
499 self::ERROR_ACTIVITY_VALIDATION_FAILURE
500 );
501 }
502
503 return true;
504 }
505
514 public static function sendEvent($params, $n, $server)
515 {
516 $params = array_change_key_case($params, CASE_UPPER);
517 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
518
519 $errors = [];
520 \CBPDocument::sendExternalEvent(
521 $workflowId,
522 $activityName,
523 [
524 'EVENT_ID' => $eventId,
525 'RETURN_VALUES' => $params['RETURN_VALUES'] ?? [],
526 'LOG_MESSAGE' => $params['LOG_MESSAGE'] ?? '',
527 ],
528 $errors,
529 );
530
531 if ($errors)
532 {
533 $error = current($errors);
534 throw new RestException($error['message'], $error['code']);
535 }
536
537 return true;
538 }
539
548 public static function writeActivityLog($params, $n, $server)
549 {
550 $params = array_change_key_case($params, CASE_UPPER);
551 [$workflowId, $activityName, $eventId] = self::extractEventToken($params['EVENT_TOKEN']);
552
553 $logMessage = isset($params['LOG_MESSAGE']) ? $params['LOG_MESSAGE'] : '';
554
555 if (empty($logMessage))
556 throw new RestException('Empty log message!', self::ERROR_EMPTY_LOG_MESSAGE);
557
558 $errors = [];
559 \CBPDocument::sendExternalEvent(
560 $workflowId,
561 $activityName,
562 [
563 'EVENT_ID' => $eventId,
564 'LOG_ACTION' => true,
565 'LOG_MESSAGE' => $logMessage
566 ],
567 $errors,
568 );
569
570 if ($errors)
571 {
572 $error = current($errors);
573 throw new RestException($error['message'], $error['code']);
574 }
575
576 return true;
577 }
578
587 public static function getActivityList($params, $n, $server)
588 {
589 return self::getActivityListInternal($params, $server, false);
590 }
591
600 public static function getRobotList($params, $n, $server)
601 {
602 return self::getActivityListInternal($params, $server, true);
603 }
604
612 private static function getActivityListInternal($params, $server, $isRobot = false)
613 {
614 if(!$server->getClientId())
615 {
616 throw new AccessException("Application context required");
617 }
618
619 self::checkAdminPermissions();
620 $iterator = RestActivityTable::getList(array(
621 'select' => array('CODE'),
622 'filter' => array(
623 '=APP_ID' => $server->getClientId(),
624 '=IS_ROBOT' => $isRobot ? 'Y' : 'N'
625 )
626 ));
627
628 $result = array();
629 while ($row = $iterator->fetch())
630 {
631 $result[] = $row['CODE'];
632 }
633 return $result;
634 }
635
645 public static function getWorkflowInstances($params, $n, $server)
646 {
647 self::checkAdminPermissions();
648 $params = array_change_key_case($params, CASE_UPPER);
649
650 $fields = array(
651 'ID' => 'ID',
652 'MODIFIED' => 'MODIFIED',
653 'OWNED_UNTIL' => 'OWNED_UNTIL',
654 'MODULE_ID' => 'MODULE_ID',
655 'ENTITY' => 'ENTITY',
656 'DOCUMENT_ID' => 'DOCUMENT_ID',
657 'STARTED' => 'STARTED',
658 'STARTED_BY' => 'STARTED_BY',
659 'TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
660 );
661
662 $select = static::getSelect($params['SELECT'], $fields, array('ID', 'MODIFIED', 'OWNED_UNTIL'));
663 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'OWNED_UNTIL'));
664 $order = static::getOrder($params['ORDER'], $fields, array('MODIFIED' => 'DESC'));
665
666 $iterator = WorkflowInstanceTable::getList(array(
667 'select' => $select,
668 'filter' => $filter,
669 'order' => $order,
670 'limit' => static::LIST_LIMIT,
671 'offset' => (int) $n,
672 'count_total' => true,
673 ));
674
675 $result = array();
676 while ($row = $iterator->fetch())
677 {
678 if (isset($row['MODIFIED']))
679 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
680 if (isset($row['STARTED']))
681 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
682 if (isset($row['OWNED_UNTIL']))
683 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
684 $result[] = $row;
685 }
686
687 return static::setNavData($result, ['count' => $iterator->getCount(), 'offset' => $n]);
688 }
689
698 public static function terminateWorkflow($params, $n, $server)
699 {
700 self::checkAdminPermissions();
701 $params = array_change_key_case($params, CASE_UPPER);
702
703 if (empty($params['ID']))
704 {
705 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
706 }
707
708 $id = $params['ID'];
709 $status = isset($params['STATUS']) ? (string)$params['STATUS'] : '';
710 $errors = [];
711
712 if (!\CBPDocument::terminateWorkflow($id, [], $errors, $status))
713 {
714 throw new RestException($errors[0]['message']);
715 }
716
717 return true;
718 }
719
728 public static function killWorkflow($params, $n, $server)
729 {
730 self::checkAdminPermissions();
731 $params = array_change_key_case($params, CASE_UPPER);
732
733 if (empty($params['ID']))
734 {
735 throw new RestException('Empty workflow instance ID', self::ERROR_WRONG_WORKFLOW_ID);
736 }
737
738 $id = $params['ID'];
739 $errors = \CBPDocument::killWorkflow($id);
740
741 if ($errors)
742 {
743 throw new RestException($errors[0]['message']);
744 }
745
746 return true;
747 }
748
757 public static function startWorkflow($params, $n, $server)
758 {
759 $params = array_change_key_case($params, CASE_UPPER);
760
761 if (empty($params['TEMPLATE_ID']))
762 {
763 throw new RestException('Empty TEMPLATE_ID', self::ERROR_WRONG_WORKFLOW_ID);
764 }
765 $templateId = (int)$params['TEMPLATE_ID'];
766 $tplDocumentType = self::getTemplateDocumentType($templateId);
767
768 if (!$tplDocumentType)
769 {
770 throw new RestException('Template not found', self::ERROR_WRONG_WORKFLOW_ID);
771 }
772
773 //hotfix #0120474
774 $getParams = array_change_key_case($_GET, CASE_UPPER);
775 if (isset($getParams['DOCUMENT_ID']) && is_array($getParams['DOCUMENT_ID']))
776 {
777 $params['DOCUMENT_ID'] = $getParams['DOCUMENT_ID'];
778 }
779
780 $documentId = self::getDocumentId($params['DOCUMENT_ID']);
781
782 if (!$documentId)
783 {
784 throw new RestException('Wrong DOCUMENT_ID!');
785 }
786
787 $documentType = self::getDocumentType($documentId);
788
789 if (!$documentType)
790 {
791 throw new RestException('Incorrect document type!');
792 }
793
794 if (!\CBPHelper::isEqualDocument($tplDocumentType, $documentType))
795 {
796 throw new RestException('Template type and DOCUMENT_ID mismatch!');
797 }
798
799 self::checkStartWorkflowPermissions($documentId, $templateId);
800
801 $workflowParameters = isset($params['PARAMETERS']) && is_array($params['PARAMETERS']) ? $params['PARAMETERS'] : [];
802
803 $workflowParameters[\CBPDocument::PARAM_TAGRET_USER] = self::getCurrentUserId();
804
805 $errors = [];
806 $workflowId = \CBPDocument::startWorkflow($templateId, $documentId, $workflowParameters, $errors);
807
808 if (!$workflowId)
809 {
810 throw new RestException($errors[0]['message']);
811 }
812
813 return $workflowId;
814 }
815
816 private static function checkStartWorkflowPermissions(array $documentId, $templateId)
817 {
818 if (static::isAdmin())
819 {
820 return true;
821 }
822
823 if (
824 \CBPDocument::CanUserOperateDocument(
825 \CBPCanUserOperateOperation::StartWorkflow,
826 static::getCurrentUserId(),
827 $documentId,
828 ['WorkflowTemplateId' => $templateId]
829 )
830 )
831 {
832 return true;
833 }
834
835 throw new AccessException();
836 }
837
848 public static function getWorkflowTemplates($params, $n, $server)
849 {
850 self::checkAdminPermissions();
851 $params = array_change_key_case($params, CASE_UPPER);
852
853 $fields = array(
854 'ID' => 'ID',
855 'MODULE_ID' => 'MODULE_ID',
856 'ENTITY' => 'ENTITY',
857 'DOCUMENT_TYPE' => 'DOCUMENT_TYPE',
858 'AUTO_EXECUTE' => 'AUTO_EXECUTE',
859 'NAME' => 'NAME',
860 'DESCRIPTION' => 'DESCRIPTION',
861 'TEMPLATE' => 'TEMPLATE',
862 'PARAMETERS' => 'PARAMETERS',
863 'VARIABLES' => 'VARIABLES',
864 'CONSTANTS' => 'CONSTANTS',
865 'MODIFIED' => 'MODIFIED',
866 'IS_MODIFIED' => 'IS_MODIFIED',
867 'USER_ID' => 'USER_ID',
868 'SYSTEM_CODE' => 'SYSTEM_CODE',
869 );
870
871 $select = static::getSelect($params['SELECT'], $fields, array('ID'));
872 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED'));
873 $filter['<AUTO_EXECUTE'] = \CBPDocumentEventType::Automation;
874
875 $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'ASC'));
876
877 $iterator = WorkflowTemplateTable::getList(array(
878 'select' => $select,
879 'filter' => $filter,
880 'order' => $order,
881 'limit' => static::LIST_LIMIT,
882 'offset' => (int) $n,
883 'count_total' => true,
884 ));
885
886 $countTotal = $iterator->getCount();
887
888 $iterator = new \CBPWorkflowTemplateResult($iterator, \CBPWorkflowTemplateLoader::useGZipCompression());
889
890 $result = array();
891 while ($row = $iterator->fetch())
892 {
893 if (isset($row['MODIFIED']))
894 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
895 if (isset($row['STARTED']))
896 $row['STARTED'] = \CRestUtil::convertDateTime($row['STARTED']);
897 if (isset($row['OWNED_UNTIL']))
898 $row['OWNED_UNTIL'] = \CRestUtil::convertDateTime($row['OWNED_UNTIL']);
899 $result[] = $row;
900 }
901
902 return static::setNavData($result, ['count' => $countTotal, 'offset' => $n]);
903 }
904
912 public static function addWorkflowTemplate($params, $n, $server)
913 {
914 if(!$server->getClientId())
915 {
916 throw new AccessException("Application context required");
917 }
918
919 self::checkAdminPermissions();
920 $params = array_change_key_case($params, CASE_UPPER);
921
922 self::validateTemplateDocumentType($params['DOCUMENT_TYPE']);
923 self::validateTemplateName($params['NAME']);
924
925 $autoExecute = \CBPDocumentEventType::None;
926 if (isset($params['AUTO_EXECUTE']))
927 {
928 self::validateTemplateAutoExecution($params['AUTO_EXECUTE']);
929 $autoExecute = (int) $params['AUTO_EXECUTE'];
930 }
931
932 $data = self::prepareTemplateData($params['TEMPLATE_DATA']);
933
934 return \CBPWorkflowTemplateLoader::ImportTemplate(
935 0,
936 $params['DOCUMENT_TYPE'],
937 $autoExecute,
938 $params['NAME'],
939 isset($params['DESCRIPTION']) ? (string) $params['DESCRIPTION'] : '',
940 $data,
941 self::generateTemplateSystemCode($server)
942 );
943 }
944
952 public static function updateWorkflowTemplate($params, $n, $server)
953 {
954 if(!$server->getClientId())
955 {
956 throw new AccessException("Application context required");
957 }
958
959 self::checkAdminPermissions();
960 $params = array_change_key_case($params, CASE_UPPER);
961
962 $fields = (isset($params['FIELDS']) && is_array($params['FIELDS'])) ? $params['FIELDS'] : null;
963
964 if (!$fields)
965 {
966 throw new RestException("No fields to update.");
967 }
968
969 $tpl = WorkflowTemplateTable::getList(array(
970 'select' => ['ID', 'SYSTEM_CODE', 'NAME', 'DESCRIPTION', 'AUTO_EXECUTE', 'MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
971 'filter' => ['=ID' => (int) $params['ID']],
972 ))->fetch();
973
974 if (!$tpl)
975 {
976 throw new RestException("Workflow template not found.");
977 }
978
979 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
980 {
981 throw new RestException("You can update ONLY templates created by current application");
982 }
983
984 if (isset($fields['NAME']))
985 {
986 self::validateTemplateName($fields['NAME']);
987 $tpl['NAME'] = $fields['NAME'];
988 }
989
990 if (isset($fields['DESCRIPTION']))
991 {
992 $tpl['DESCRIPTION'] = (string) $fields['DESCRIPTION'];
993 }
994
995 if (isset($fields['AUTO_EXECUTE']))
996 {
997 self::validateTemplateAutoExecution($fields['AUTO_EXECUTE']);
998 $tpl['AUTO_EXECUTE'] = (int) $fields['AUTO_EXECUTE'];
999 }
1000
1001 if (isset($fields['TEMPLATE_DATA']))
1002 {
1003 $data = self::prepareTemplateData($fields['TEMPLATE_DATA']);
1004
1005 return \CBPWorkflowTemplateLoader::ImportTemplate(
1006 $tpl['ID'],
1007 [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']],
1008 $tpl['AUTO_EXECUTE'],
1009 $tpl['NAME'],
1010 $tpl['DESCRIPTION'],
1011 $data,
1012 $tpl['SYSTEM_CODE']
1013 );
1014 }
1015 else
1016 {
1017 return \CBPWorkflowTemplateLoader::Update($tpl['ID'], [
1018 'NAME' => $tpl['NAME'],
1019 'DESCRIPTION' => $tpl['DESCRIPTION'],
1020 'AUTO_EXECUTE' => $tpl['AUTO_EXECUTE'],
1021 ]);
1022 }
1023 }
1024
1032 public static function deleteWorkflowTemplate($params, $n, $server)
1033 {
1034 if(!$server->getClientId())
1035 {
1036 throw new AccessException("Application context required");
1037 }
1038
1039 self::checkAdminPermissions();
1040 $params = array_change_key_case($params, CASE_UPPER);
1041
1042 $tpl = WorkflowTemplateTable::getList(array(
1043 'select' => ['ID', 'SYSTEM_CODE'],
1044 'filter' => ['=ID' => (int) $params['ID']],
1045 ))->fetch();
1046
1047 if (!$tpl)
1048 {
1049 throw new RestException("Workflow template not found.");
1050 }
1051
1052 if ($tpl['SYSTEM_CODE'] !== self::generateTemplateSystemCode($server))
1053 {
1054 throw new RestException("You can delete ONLY templates created by current application");
1055 }
1056
1057 \CBPWorkflowTemplateLoader::Delete($tpl['ID']);
1058 }
1059
1067 public static function getTaskList($params, $n, $server)
1068 {
1069 $params = array_change_key_case($params, CASE_UPPER);
1070
1071 $fields = array(
1072 'ID' => 'ID',
1073 'ACTIVITY' => 'ACTIVITY',
1074 'ACTIVITY_NAME' => 'ACTIVITY_NAME',
1075 'WORKFLOW_ID' => 'WORKFLOW_ID',
1076 'DOCUMENT_NAME' => 'DOCUMENT_NAME',
1077 'DESCRIPTION' => 'DESCRIPTION',
1078 'NAME' => 'NAME',
1079 'MODIFIED' => 'MODIFIED',
1080 'WORKFLOW_STARTED' => 'WORKFLOW_STARTED',
1081 'WORKFLOW_STARTED_BY' => 'WORKFLOW_STARTED_BY',
1082 'OVERDUE_DATE' => 'OVERDUE_DATE',
1083 'WORKFLOW_TEMPLATE_ID' => 'WORKFLOW_TEMPLATE_ID',
1084 'WORKFLOW_TEMPLATE_NAME' => 'WORKFLOW_TEMPLATE_NAME',
1085 'WORKFLOW_STATE' => 'WORKFLOW_STATE',
1086 'STATUS' => 'STATUS',
1087 'USER_ID' => 'USER_ID',
1088 'USER_STATUS' => 'USER_STATUS',
1089 'MODULE_ID' => 'MODULE_ID',
1090 'ENTITY' => 'ENTITY',
1091 'DOCUMENT_ID' => 'DOCUMENT_ID',
1092 'PARAMETERS' => 'PARAMETERS',
1093 );
1094
1095 $select = static::getSelect($params['SELECT'], $fields, array('ID', 'WORKFLOW_ID', 'DOCUMENT_NAME', 'NAME'));
1096 $select = array_merge(array('MODULE', 'ENTITY', 'DOCUMENT_ID'), $select);
1097 $filter = static::getFilter($params['FILTER'], $fields, array('MODIFIED', 'WORKFLOW_STARTED', 'OVERDUE_DATE'));
1098 $order = static::getOrder($params['ORDER'], $fields, array('ID' => 'DESC'));
1099
1100 $currentUserId = self::getCurrentUserId();
1101 $isAdmin = static::isAdmin();
1102
1103 if (!$isAdmin && !isset($filter['USER_ID']))
1104 {
1105 $filter['USER_ID'] = $currentUserId;
1106 }
1107
1108 $targetUserId = isset($filter['USER_ID'])? (int)$filter['USER_ID'] : 0;
1109 if ($targetUserId !== $currentUserId && !\CBPHelper::checkUserSubordination($currentUserId, $targetUserId))
1110 {
1111 self::checkAdminPermissions();
1112 }
1113
1114 $iterator = \CBPTaskService::getList(
1115 $order,
1116 $filter,
1117 false,
1118 static::getNavData($n),
1119 $select
1120 );
1121
1122 $result = array();
1123 while ($row = $iterator->fetch())
1124 {
1125 if (isset($row['MODIFIED']))
1126 $row['MODIFIED'] = \CRestUtil::convertDateTime($row['MODIFIED']);
1127 if (isset($row['WORKFLOW_STARTED']))
1128 $row['WORKFLOW_STARTED'] = \CRestUtil::convertDateTime($row['WORKFLOW_STARTED']);
1129 if (isset($row['OVERDUE_DATE']))
1130 $row['OVERDUE_DATE'] = \CRestUtil::convertDateTime($row['OVERDUE_DATE']);
1131 $row['DOCUMENT_URL'] = \CBPDocument::getDocumentAdminPage(array(
1132 $row['MODULE_ID'], $row['ENTITY'], $row['DOCUMENT_ID']
1133 ));
1134
1135 if (isset($row['PARAMETERS']))
1136 {
1137 $row['PARAMETERS'] = static::prepareTaskParameters($row['PARAMETERS'], $row);
1138 }
1139
1140 $result[] = $row;
1141 }
1142
1143 return static::setNavData($result, $iterator);
1144 }
1145
1146 private static function prepareTaskParameters(array $parameters, array $task)
1147 {
1148 $whiteList = [
1149 ['CommentLabelMessage', 'CommentLabel'],
1150 'CommentRequired', 'ShowComment',
1151 ['TaskButtonMessage', 'StatusOkLabel'],
1152 ['TaskButton1Message', 'StatusYesLabel'],
1153 ['TaskButton2Message', 'StatusNoLabel'],
1154 ['TaskButtonCancelMessage', 'StatusCancelLabel'],
1155 ['REQUEST', 'Fields'],
1156 ];
1157
1158 $filtered = [];
1159
1160 foreach ($whiteList as $whiteKey)
1161 {
1162 $filterKey = $whiteKey;
1163 if (is_array($whiteKey))
1164 {
1165 $filterKey = $whiteKey[1];
1166 $whiteKey = $whiteKey[0];
1167 }
1168 if (isset($parameters[$whiteKey]))
1169 {
1170 $filtered[$filterKey] = $parameters[$whiteKey];
1171 }
1172 }
1173
1174 if (isset($filtered['Fields']))
1175 {
1176 $filtered['Fields'] = self::externalizeRequestFields($task, $filtered['Fields']);
1177 }
1178
1179 return $filtered;
1180 }
1181
1182 private static function externalizeRequestFields($task, array $fields): array
1183 {
1184 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1185 $result = [];
1186 foreach ($fields as $requestField)
1187 {
1188 $id = $requestField['Name'];
1189 $requestField['Name'] = $requestField['Title'];
1190 $property = FieldType::normalizeProperty($requestField);
1191 $property['Id'] = $id;
1192
1193 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1194 if ($fieldTypeObject)
1195 {
1196 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1197 $property['Default'] = $fieldTypeObject->externalizeValue(
1199 $property['Default']
1200 );
1201 }
1202
1203 $result[] = $property;
1204 }
1205 return $result;
1206 }
1207
1208 private static function internalizeRequestFields($task, array $values): array
1209 {
1210 $documentService = \CBPRuntime::GetRuntime(true)->getDocumentService();
1211 $result = [];
1212
1213 foreach ($task['PARAMETERS']['REQUEST'] as $property)
1214 {
1215 if (!isset($values[$property['Name']]))
1216 {
1217 continue;
1218 }
1219
1220 $property = FieldType::normalizeProperty($property);
1221 $fieldTypeObject = $documentService->getFieldTypeObject($task["PARAMETERS"]["DOCUMENT_TYPE"], $property);
1222 if ($fieldTypeObject)
1223 {
1224 $fieldTypeObject->setDocumentId($task["PARAMETERS"]["DOCUMENT_ID"]);
1225 $result[$property['Name']] = $fieldTypeObject->internalizeValue(FieldType::VALUE_CONTEXT_REST, $values[$property['Name']]);
1226 }
1227 }
1228 return $result;
1229 }
1230
1238 public static function completeTask($params, $n, $server)
1239 {
1240 $params = array_change_key_case($params, CASE_UPPER);
1241 self::validateTaskParameters($params);
1242
1243 $userId = self::getCurrentUserId();
1244 $task = static::getTask($params['TASK_ID'], $userId);
1245
1246 if (!in_array($task['ACTIVITY'], self::ALLOWED_TASK_ACTIVITIES))
1247 {
1248 throw new RestException('Incorrect task type', self::ERROR_TASK_TYPE);
1249 }
1250
1251 if (!empty($params['FIELDS']))
1252 {
1253 $params['FIELDS'] = self::internalizeRequestFields($task, $params['FIELDS']);
1254 }
1255
1256 $errors = array();
1257 $request = array(
1258 'INLINE_USER_STATUS' => \CBPTaskUserStatus::resolveStatus($params['STATUS']),
1259 'task_comment' => !empty($params['COMMENT']) && is_string($params['COMMENT']) ? $params['COMMENT'] : null,
1260 'fields' => $params['FIELDS'] ?? null,
1261 );
1262
1263 if (!\CBPDocument::postTaskForm($task, $userId, $request, $errors))
1264 {
1265 throw new RestException($errors[0]["message"], self::ERROR_TASK_EXECUTION);
1266 }
1267
1268 return true;
1269 }
1270
1271 private static function validateTaskParameters(array $params)
1272 {
1273 if (empty($params['TASK_ID']))
1274 {
1275 throw new RestException('empty TASK_ID', self::ERROR_TASK_VALIDATION);
1276 }
1277 if (empty($params['STATUS']) || \CBPTaskUserStatus::resolveStatus($params['STATUS']) === null)
1278 {
1279 throw new RestException('incorrect STATUS', self::ERROR_TASK_VALIDATION);
1280 }
1281 }
1282
1283 private static function getTask($id, $userId)
1284 {
1285 $dbTask = \CBPTaskService::getList(
1286 array(),
1287 array("ID" => (int)$id, "USER_ID" => $userId),
1288 false,
1289 false,
1290 array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "USER_STATUS")
1291 );
1292 $task = $dbTask->fetch();
1293
1294 if (!$task)
1295 {
1296 throw new RestException('Task not found', self::ERROR_TASK_NOT_FOUND);
1297 }
1298 elseif ((int)$task['USER_STATUS'] !== \CBPTaskUserStatus::Waiting)
1299 {
1300 throw new RestException('Task already completed', self::ERROR_TASK_COMPLETED);
1301 }
1302
1303 if ($task)
1304 {
1305 $task["PARAMETERS"]["DOCUMENT_ID"] = \CBPStateService::getStateDocumentId($task['WORKFLOW_ID']);
1306 $task["MODULE_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][0];
1307 $task["ENTITY"] = $task["PARAMETERS"]["DOCUMENT_ID"][1];
1308 $task["DOCUMENT_ID"] = $task["PARAMETERS"]["DOCUMENT_ID"][2];
1309 }
1310
1311 return $task;
1312 }
1313
1321 public static function addProvider($params, $n, $server)
1322 {
1323 if (Loader::includeModule('messageservice'))
1324 {
1325 return \Bitrix\MessageService\RestService::addSender($params, $n, $server);
1326 }
1327
1328 if(!$server->getClientId())
1329 {
1330 throw new AccessException("Application context required");
1331 }
1332
1333 self::checkAdminPermissions();
1334 $params = self::prepareActivityData($params);
1335
1336 self::validateProvider($params, $server);
1337
1338 $params['APP_ID'] = $server->getClientId();
1339 $params['APP_NAME'] = self::getAppName($params['APP_ID']);
1340
1341 $iterator = RestProviderTable::getList(array(
1342 'select' => array('ID'),
1343 'filter' => array(
1344 '=APP_ID' => $params['APP_ID'],
1345 '=CODE' => $params['CODE']
1346 )
1347 ));
1348 $result = $iterator->fetch();
1349 if ($result)
1350 {
1351 throw new RestException('Provider already installed!', self::ERROR_ACTIVITY_ALREADY_INSTALLED);
1352 }
1353
1354 $result = RestProviderTable::add($params);
1355
1356 if ($result->getErrors())
1357 throw new RestException('Activity save error!', self::ERROR_ACTIVITY_ADD_FAILURE);
1358
1359 return true;
1360 }
1361
1369 public static function deleteProvider($params, $n, $server)
1370 {
1371 if (Loader::includeModule('messageservice'))
1372 {
1373 return \Bitrix\MessageService\RestService::deleteSender($params, $n, $server);
1374 }
1375
1376 if(!$server->getClientId())
1377 {
1378 throw new AccessException("Application context required");
1379 }
1380
1381 $params = array_change_key_case($params, CASE_UPPER);
1382 self::checkAdminPermissions();
1383 self::validateActivityCode($params['CODE']);
1384 $params['APP_ID'] = $server->getClientId();
1385
1386 $iterator = RestProviderTable::getList(array(
1387 'select' => array('ID'),
1388 'filter' => array(
1389 '=APP_ID' => $params['APP_ID'],
1390 '=CODE' => $params['CODE']
1391 )
1392 ));
1393 $result = $iterator->fetch();
1394 if (!$result)
1395 {
1396 throw new RestException('Provider not found!', self::ERROR_ACTIVITY_NOT_FOUND);
1397 }
1398 RestProviderTable::delete($result['ID']);
1399
1400 return true;
1401 }
1402
1411 public static function getProviderList($params, $n, $server)
1412 {
1413 if (Loader::includeModule('messageservice'))
1414 {
1415 return \Bitrix\MessageService\RestService::getSenderList($params, $n, $server);
1416 }
1417
1418 if(!$server->getClientId())
1419 {
1420 throw new AccessException("Application context required");
1421 }
1422
1423 self::checkAdminPermissions();
1424 $iterator = RestProviderTable::getList(array(
1425 'select' => array('CODE'),
1426 'filter' => array(
1427 '=APP_ID' => $server->getClientId()
1428 )
1429 ));
1430
1431 $result = array();
1432 while ($row = $iterator->fetch())
1433 {
1434 $result[] = $row['CODE'];
1435 }
1436 return $result;
1437 }
1438
1439 private static function getSelect($rules, $fields, $default = array())
1440 {
1441 $select = array();
1442 if (!empty($rules) && is_array($rules))
1443 {
1444 foreach ($rules as $field)
1445 {
1446 $field = mb_strtoupper($field);
1447 if (isset($fields[$field]) && !in_array($field, $select))
1448 $select[$field] = $fields[$field];
1449 }
1450 }
1451
1452 return $select ? $select : $default;
1453 }
1454
1455 private static function getOrder($rules, $fields, array $default = array())
1456 {
1457 $order = array();
1458 if (!empty($rules) && is_array($rules))
1459 {
1460 foreach ($rules as $field => $ordering)
1461 {
1462 $field = mb_strtoupper($field);
1463 $ordering = mb_strtoupper($ordering);
1464 if (isset($fields[$field]))
1465 $order[$fields[$field]] = $ordering == 'DESC' ? 'DESC' : 'ASC';
1466 }
1467 }
1468
1469 return $order ? $order : $default;
1470 }
1471
1472 private static function getFilter($rules, $fields, array $datetimeFieldsList = array())
1473 {
1474 $filter = array();
1475 if (!empty($rules) && is_array($rules))
1476 {
1477 foreach ($rules as $key => $value)
1478 {
1479 if (preg_match('/^([^a-zA-Z]*)(.*)/', $key, $matches))
1480 {
1481 $operation = $matches[1];
1482 $field = $matches[2];
1483
1484 if (in_array($operation, static::$allowedOperations, true) && isset($fields[$field]))
1485 {
1486 if (in_array($field, $datetimeFieldsList))
1487 $value = \CRestUtil::unConvertDateTime($value);
1488
1489 $filter[$operation.$fields[$field]] = $value;
1490 }
1491 }
1492 }
1493 }
1494
1495 return $filter;
1496 }
1497
1498 private static function checkAdminPermissions()
1499 {
1500 if (!static::isAdmin())
1501 {
1502 throw new AccessException();
1503 }
1504 }
1505
1506 private static function isAdmin()
1507 {
1508 global $USER;
1509 return (
1510 isset($USER)
1511 && is_object($USER)
1512 && (
1513 $USER->isAdmin()
1514 || Loader::includeModule('bitrix24') && \CBitrix24::isPortalAdmin($USER->getID())
1515 )
1516 );
1517 }
1518
1519 private static function getCurrentUserId()
1520 {
1521 global $USER;
1522 return (isset($USER) && is_object($USER)) ? (int)$USER->getID() : 0;
1523 }
1524
1525 private static function generateInternalCode($data)
1526 {
1527 return md5($data['APP_ID'].'@'.$data['CODE']);
1528 }
1529
1530 private static function getAppName($appId)
1531 {
1532 if (!Loader::includeModule('rest'))
1533 return array('*' => 'No app');
1534
1535 $iterator = AppTable::getList(
1536 array(
1537 'filter' => array(
1538 '=CLIENT_ID' => $appId
1539 ),
1540 'select' => array('ID', 'APP_NAME', 'CODE'),
1541 )
1542 );
1543 $app = $iterator->fetch();
1544 $result = array('*' => $app['APP_NAME'] ? $app['APP_NAME'] : $app['CODE']);
1545
1546 $iterator = AppLangTable::getList(array(
1547 'filter' => array(
1548 '=APP_ID' => $app['ID'],
1549 ),
1550 'select' => array('LANGUAGE_ID', 'MENU_NAME')
1551 ));
1552 while($lang = $iterator->fetch())
1553 {
1554 $result[mb_strtoupper($lang['LANGUAGE_ID'])] = $lang['MENU_NAME'];
1555 }
1556
1557 return $result;
1558 }
1559
1560 private static function getAppId($clientId)
1561 {
1562 if (!Loader::includeModule('rest'))
1563 {
1564 return null;
1565 }
1566
1567 $iterator = AppTable::getList(
1568 array(
1569 'filter' => array(
1570 '=CLIENT_ID' => $clientId
1571 ),
1572 'select' => array('ID'),
1573 )
1574 );
1575 $app = $iterator->fetch();
1576
1577 return (int) $app['ID'];
1578 }
1579
1580 private static function prepareActivityData(array $data, $ignore = false)
1581 {
1582 if (!$ignore)
1583 $data = array_change_key_case($data, CASE_UPPER);
1584 foreach ($data as $key => &$field)
1585 {
1586 if (is_array($field))
1587 $field = self::prepareActivityData($field, $key == 'PROPERTIES' || $key == 'RETURN_PROPERTIES' || $key == 'OPTIONS');
1588 }
1589 return $data;
1590 }
1591
1592 private static function validateActivity($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['DOCUMENT_TYPE']))
1608 static::validateActivityDocumentType($data['DOCUMENT_TYPE']);
1609 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1610 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1611 }
1612
1613 private static function validateProvider($data, $server)
1614 {
1615 if (!is_array($data) || empty($data))
1616 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1617
1618 static::validateActivityCode($data['CODE']);
1619 static::validateActivityHandler($data['HANDLER'], $server);
1620 if (empty($data['NAME']))
1621 throw new RestException('Empty provider NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1622
1623 if (empty($data['TYPE']))
1624 throw new RestException('Empty provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1625
1626 if (!in_array($data['TYPE'], RestProviderTable::getTypesList(), true))
1627 throw new RestException('Unknown provider TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1628 }
1629
1630 private static function validateRobot($data, $server)
1631 {
1632 if (!is_array($data) || empty($data))
1633 throw new RestException('Empty data!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1634
1635 static::validateActivityCode($data['CODE']);
1636 static::validateActivityHandler($data['HANDLER'], $server);
1637 if (empty($data['NAME']))
1638 throw new RestException('Empty activity NAME!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1639
1640 if (isset($data['PROPERTIES']))
1641 static::validateActivityProperties($data['PROPERTIES']);
1642
1643 if (isset($data['RETURN_PROPERTIES']))
1644 static::validateActivityProperties($data['RETURN_PROPERTIES']);
1645 if (isset($data['FILTER']) && !is_array($data['FILTER']))
1646 throw new RestException('Wrong activity FILTER!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1647 }
1648
1649 private static function validateActivityCode($code)
1650 {
1651 if (empty($code))
1652 {
1653 throw new RestException('Empty activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1654 }
1655 if (!is_string($code) || !preg_match('#^[a-z0-9\.\-_]+$#i', $code))
1656 {
1657 throw new RestException('Wrong activity code!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1658 }
1659 }
1660
1661 private static function validateActivityHandler($handler, $server)
1662 {
1664 }
1665
1666 private static function validateActivityProperties($properties)
1667 {
1668 if (!is_array($properties))
1669 throw new RestException('Wrong properties array!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1670
1671 foreach ($properties as $key => $property)
1672 {
1673 if (!preg_match('#^[a-z][a-z0-9_]*$#i', $key))
1674 throw new RestException('Wrong property key ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1675 if (empty($property['NAME']))
1676 throw new RestException('Empty property NAME ('.$key.')!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1677 }
1678 }
1679
1680 private static function validateActivityDocumentType($documentType)
1681 {
1682 try
1683 {
1684 $runtime = \CBPRuntime::getRuntime();
1685 $runtime->startRuntime();
1687 $documentService = $runtime->getService('DocumentService');
1688 $documentService->getDocumentFieldTypes($documentType);
1689 }
1690 catch (\CBPArgumentNullException $e)
1691 {
1692 throw new RestException('Wrong activity DOCUMENT_TYPE!', self::ERROR_ACTIVITY_VALIDATION_FAILURE);
1693 }
1694 }
1695
1696 private static function getDocumentId($documentId): ?array
1697 {
1698 try
1699 {
1700 $documentService = \CBPRuntime::getRuntime()->getDocumentService();
1701 $documentId = $documentService->normalizeDocumentId($documentId);
1702 if ($documentService->getDocument($documentId))
1703 {
1704 return $documentId;
1705 }
1706 }
1707 catch (\CBPArgumentException $exception) {}
1708
1709 return null;
1710 }
1711
1712 private static function getDocumentType(array $documentId): ?array
1713 {
1714 try
1715 {
1716 $documentId = \CBPHelper::parseDocumentId($documentId);
1717 $runtime = \CBPRuntime::getRuntime(true);
1718 $documentService = $runtime->getDocumentService();
1719
1720 return $documentService->getDocumentType($documentId);
1721 }
1722 catch (\CBPArgumentNullException $e) {}
1723
1724 return null;
1725 }
1726
1727 private static function getTemplateDocumentType(int $id): ?array
1728 {
1729 $tpl = WorkflowTemplateTable::getList([
1730 'select' => ['MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
1731 'filter' => ['=ID' => $id],
1732 ])->fetch();
1733
1734 if ($tpl)
1735 {
1736 return [$tpl['MODULE_ID'], $tpl['ENTITY'], $tpl['DOCUMENT_TYPE']];
1737 }
1738 return null;
1739 }
1740
1741 private static function validateTemplateName($name)
1742 {
1743 if (empty($name))
1744 {
1745 throw new RestException('Empty activity code!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1746 }
1747 }
1748
1749 private static function upsertAppPlacement(int $appId, string $code, string $handler)
1750 {
1751 $filter = [
1752 '=APP_ID' => $appId,
1753 '=ADDITIONAL' => $code,
1754 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1755 ];
1756
1757 $dbRes = PlacementTable::getList(array(
1758 'filter' => $filter
1759 ));
1760
1761 $placementHandler = $dbRes->fetch();
1762
1763 if ($placementHandler)
1764 {
1765 $result = PlacementTable::update($placementHandler['ID'], ['PLACEMENT_HANDLER' => $handler]);
1766 }
1767 else
1768 {
1769 $placementBind = array(
1770 'APP_ID' => $appId,
1771 'ADDITIONAL' => $code,
1772 'PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1773 'PLACEMENT_HANDLER' => $handler,
1774 );
1775
1776 $result = PlacementTable::add($placementBind);
1777 }
1778
1779 if(!$result->isSuccess())
1780 {
1781 $errorMessage = $result->getErrorMessages();
1782 throw new RestException(
1783 'Unable to set placement handler: '.implode(', ', $errorMessage),
1785 );
1786 }
1787 }
1788
1789 private static function deleteAppPlacement(int $appId, string $code = null)
1790 {
1791 $filter = [
1792 '=APP_ID' => $appId,
1793 '=PLACEMENT' => static::PLACEMENT_ACTIVITY_PROPERTIES_DIALOG,
1794 ];
1795
1796 if ($code)
1797 {
1798 $filter['=ADDITIONAL'] = $code;
1799 }
1800
1801 $dbRes = PlacementTable::getList(array(
1802 'filter' => $filter
1803 ));
1804
1805 while($placementHandler = $dbRes->fetch())
1806 {
1807 PlacementTable::delete($placementHandler["ID"]);
1808 }
1809 }
1810
1811 private static function prepareTemplateData($data)
1812 {
1813 if (!empty($data))
1814 {
1815 $fileFields = \CRestUtil::saveFile($data);
1816
1817 if ($fileFields)
1818 {
1819 return file_get_contents($fileFields['tmp_name']);
1820 }
1821 }
1822 throw new RestException('Incorrect field TEMPLATE_DATA!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1823 }
1824
1825 private static function validateTemplateDocumentType($documentType)
1826 {
1827 try
1828 {
1829 $documentService = \CBPRuntime::getRuntime(true)->getDocumentService();
1830 $documentService->getDocumentFieldTypes($documentType);
1831 }
1832 catch (\CBPArgumentNullException $e)
1833 {
1834 throw new RestException('Incorrect field DOCUMENT_TYPE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1835 }
1836 }
1837
1838 private static function validateTemplateAutoExecution($flag)
1839 {
1840 if ($flag === (string) (int) $flag)
1841 {
1842 $flag = (int) $flag;
1843
1844 if (in_array(
1845 $flag,
1846 [
1847 \CBPDocumentEventType::None,
1848 \CBPDocumentEventType::Create,
1849 \CBPDocumentEventType::Edit,
1850 \CBPDocumentEventType::Create | \CBPDocumentEventType::Edit
1851 ],
1852 true
1853 )
1854 )
1855 {
1856 return true;
1857 }
1858 }
1859
1860 throw new RestException('Incorrect field AUTO_EXECUTE!', self::ERROR_TEMPLATE_VALIDATION_FAILURE);
1861 }
1862
1863 private static function generateTemplateSystemCode(\CRestServer $server)
1864 {
1865 $appId = self::getAppId($server->getClientId());
1866
1867 return 'rest_app_'.$appId;
1868 }
1869
1870 private static function extractEventToken($token)
1871 {
1872 $data = \CBPRestActivity::extractToken($token);
1873 if (!$data)
1874 throw new AccessException();
1875 return $data;
1876 }
1877
1884 private static function getApp($server)
1885 {
1886 if(self::$app == null)
1887 {
1888 if (Loader::includeModule('rest'))
1889 {
1890 $result = AppTable::getList(
1891 array(
1892 'filter' => array(
1893 '=CLIENT_ID' => $server->getClientId()
1894 )
1895 )
1896 );
1897 self::$app = $result->fetch();
1898 }
1899 }
1900
1901 return self::$app;
1902 }
1903}
static normalizeProperty($property)
static onRestAppUpdate(array $fields)
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)
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)