getManualWorkflow($id); $entity = $this->getEntityForManualWorkflow($workflow, $targetId); $this->processManualWorkflowAccess($workflow, $entity); $this->processCheckManualWorkflowConditions($workflow, $entity); try { $result = $this->triggerWorkflow($entity, $workflow->getId(), true); } catch (FormulaError $e) { throw new Error("Formula error.", 500, $e); } if (!$result) { throw new RuntimeException("No result."); } return $result; } /** * @throws FormulaError * @throws Error */ public function triggerWorkflow(Entity $entity, string $workflowId, bool $mandatory = false): ?TriggerResult { /** @var ?Workflow $workflow */ $workflow = $this->entityManager->getEntityById(Workflow::ENTITY_TYPE, $workflowId); if (!$workflow) { throw new Error("Workflow $workflowId does not exist."); } if (!$workflow->isActive()) { if (!$mandatory) { $this->log->debug("Workflow $workflowId not triggerred as it's not active."); return null; } throw new Error("Workflow $workflowId is not active."); } if (!$this->workflowManager->checkConditions($workflow, $entity)) { $this->log->debug("Workflow $workflowId not triggerred as conditions are not met."); return null; } $workflowLogRecord = $this->entityManager->getNewEntity(WorkflowLogRecord::ENTITY_TYPE); $workflowLogRecord->set([ 'workflowId' => $workflowId, 'targetId' => $entity->getId(), 'targetType' => $entity->getEntityType() ]); $this->entityManager->saveEntity($workflowLogRecord); $alertObject = new stdClass(); $variables = ['__alert' => $alertObject]; $this->workflowManager->runActions($workflow, $entity, $variables); return $this->prepareTriggerResult($alertObject); } /** * @throws Forbidden * @throws Error */ private function processCheckManualWorkflowConditions(Workflow $workflow, CoreEntity $entity): void { $conditionGroup = $workflow->getManualDynamicLogicConditionGroup(); if ( !$conditionGroup || !class_exists("Espo\\Tools\\DynamicLogic\\ConditionCheckerFactory") ) { return; } $conditionCheckerFactory = $this->injectableFactory->create(ConditionCheckerFactory::class); $checker = $conditionCheckerFactory->create($entity); try { $item = LogicItem::fromGroupDefinition($conditionGroup); $isTrue = $checker->check($item); } catch (BadCondition $e) { throw new Error($e->getMessage(), 500, $e); } if (!$isTrue) { throw new Forbidden("Workflow conditions are not met."); } } /** * @throws NotFound */ private function getEntityForManualWorkflow(Workflow $workflow, string $targetId): CoreEntity { $targetEntityType = $workflow->getTargetEntityType(); $entity = $this->entityManager->getRDBRepository($targetEntityType)->getById($targetId); if (!$entity) { throw new NotFound(); } $this->serviceContainer->get($targetEntityType)->loadAdditionalFields($entity); if (!$entity instanceof CoreEntity) { throw new RuntimeException(); } return $entity; } /** * @throws Forbidden * @throws NotFound */ private function getManualWorkflow(string $id): Workflow { $workflow = $this->entityManager->getRDBRepositoryByClass(Workflow::class)->getById($id); if (!$workflow) { throw new NotFound("Workflow $id not found."); } if ($workflow->getType() !== Workflow::TYPE_MANUAL) { throw new Forbidden(); } return $workflow; } /** * @throws Forbidden */ private function processManualWorkflowAccess(Workflow $workflow, CoreEntity $entity): void { if ($this->user->isPortal()) { throw new Forbidden(); } $accessRequired = $workflow->getManualAccessRequired(); if ($accessRequired === Workflow::MANUAL_ACCESS_ADMIN) { if (!$this->user->isAdmin()) { throw new Forbidden("No admin access."); } } else if ($accessRequired === Workflow::MANUAL_ACCESS_READ) { if (!$this->acl->checkEntityRead($entity)) { throw new Forbidden("No read access."); } } else if (!$this->acl->checkEntityEdit($entity)) { throw new Forbidden("No edit access."); } if (!$this->user->isAdmin()) { $teamIdList = $workflow->getLinkMultipleIdList('manualTeams'); if (array_intersect($teamIdList, $this->user->getTeamIdList()) === []) { throw new Forbidden("User is not from allowed team."); } } } private function prepareTriggerResult(stdClass $alertObject): TriggerResult { $alert = null; if (property_exists($alertObject, 'message') && is_string($alertObject->message)) { $alert = new Alert( message: $alertObject->message, type: $alertObject->type ?? null, autoClose: $alertObject->autoClose ?? false, ); } return new TriggerResult( alert: $alert, ); } }