vendor/pimcore/portal-engine/src/Service/Workflow/WorkflowService.php line 256

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under following license:
  6.  * - Pimcore Commercial License (PCL)
  7.  *
  8.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  9.  *  @license    http://www.pimcore.org/license     PCL
  10.  */
  11. namespace Pimcore\Bundle\PortalEngineBundle\Service\Workflow;
  12. use Pimcore\Bundle\PortalEngineBundle\Enum\DataPool\TranslatorDomain;
  13. use Pimcore\Bundle\PortalEngineBundle\Enum\Permission;
  14. use Pimcore\Bundle\PortalEngineBundle\EventSubscriber\SaveUserSubscriber;
  15. use Pimcore\Bundle\PortalEngineBundle\Service\DataPool\TranslatorService;
  16. use Pimcore\Bundle\PortalEngineBundle\Service\Security\SecurityService;
  17. use Pimcore\Model\Asset;
  18. use Pimcore\Model\DataObject\Concrete;
  19. use Pimcore\Model\Document\PageSnippet;
  20. use Pimcore\Model\Element\AbstractElement;
  21. use Pimcore\Model\Element\ElementInterface;
  22. use Pimcore\Model\Element\Note;
  23. use Pimcore\Model\Element\Service;
  24. use Pimcore\Model\User;
  25. use Pimcore\Workflow\ActionsButtonService;
  26. use Pimcore\Workflow\EventSubscriber\NotesSubscriber;
  27. use Pimcore\Workflow\Manager;
  28. use Pimcore\Workflow\Place\StatusInfo;
  29. use Pimcore\Workflow\Transition;
  30. use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
  31. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  32. use Symfony\Component\Workflow\Registry;
  33. class WorkflowService
  34. {
  35.     /**
  36.      * @var Manager
  37.      */
  38.     protected $workflowManager;
  39.     /**
  40.      * @var Registry
  41.      */
  42.     protected $workflowRegistry;
  43.     /**
  44.      * @var ActionsButtonService
  45.      */
  46.     protected $actionsButtonService;
  47.     /**
  48.      * @var StatusInfo
  49.      */
  50.     protected $placeStatusInfo;
  51.     /**
  52.      * @var TranslatorService
  53.      */
  54.     protected $translatorService;
  55.     /**
  56.      * @var SecurityService
  57.      */
  58.     protected $securityService;
  59.     protected $tokenStorage;
  60.     /**
  61.      * WorkflowService constructor.
  62.      *
  63.      * @param Manager $workflowManager
  64.      * @param Registry $workflowRegistry
  65.      * @param ActionsButtonService $actionsButtonService
  66.      * @param StatusInfo $placeStatusInfo
  67.      * @param TranslatorService $translatorService
  68.      * @param SecurityService $securityService
  69.      * @param TokenStorageInterface $tokenStorage
  70.      */
  71.     public function __construct(
  72.         Manager $workflowManager,
  73.         Registry $workflowRegistry,
  74.         ActionsButtonService $actionsButtonService,
  75.         StatusInfo $placeStatusInfo,
  76.         TranslatorService $translatorService,
  77.         SecurityService $securityService,
  78.         TokenStorageInterface $tokenStorage
  79.     ) {
  80.         $this->workflowManager $workflowManager;
  81.         $this->workflowRegistry $workflowRegistry;
  82.         $this->actionsButtonService $actionsButtonService;
  83.         $this->placeStatusInfo $placeStatusInfo;
  84.         $this->translatorService $translatorService;
  85.         $this->securityService $securityService;
  86.         $this->tokenStorage $tokenStorage;
  87.     }
  88.     public function getWorkflowDetails(AbstractElement $element): array
  89.     {
  90.         $workflows = [];
  91.         foreach ($this->workflowManager->getAllWorkflows() as $workflowName) {
  92.             $workflow $this->workflowManager->getWorkflowIfExists($element$workflowName);
  93.             $workflowConfig $this->workflowManager->getWorkflowConfig($workflowName);
  94.             if (empty($workflow)) {
  95.                 continue;
  96.             }
  97.             $allowedTransitions $this->actionsButtonService->getAllowedTransitions($workflow$element);
  98.             $globalActions $this->actionsButtonService->getGlobalActions($workflow$element);
  99.             if (!sizeof($allowedTransitions) && !sizeof($globalActions)) {
  100.                 continue;
  101.             }
  102.             $workflows[] = [
  103.                 'name' => $workflow->getName(),
  104.                 'label' => $workflowConfig->getLabel(),
  105.                 'allowedTransitions' => $allowedTransitions,
  106.                 'globalActions' => $globalActions,
  107.             ];
  108.         }
  109.         return [
  110.             'workflow' => $workflows,
  111.             'statusInfo' => $this->placeStatusInfo->getAllPalacesHtml($element),
  112.             'history' => $this->getWorkflowHistory($element)
  113.         ];
  114.     }
  115.     /**
  116.      * @return array
  117.      */
  118.     protected function getWorkflowHistory(AbstractElement $element)
  119.     {
  120.         $notes = new Note\Listing;
  121.         $notes->setCondition('ctype=? and cid=? and type=? order by date desc', [
  122.             Service::getType($element),
  123.             $element->getId(),
  124.             'Status Update'
  125.         ])
  126.         ->setLimit(100);
  127.         $history = [];
  128.         foreach ($notes as $note) {
  129.             $user User::getById($note->getUser());
  130.             $history[] = [
  131.                 'title' => $note->getTitle(),
  132.                 'description' => $note->getDescription(),
  133.                 'user' => $user $user->getName() : '',
  134.                 'date' => $note->getDate(),
  135.             ];
  136.         }
  137.         return $history;
  138.     }
  139.     /**
  140.      * @param AbstractElement $element
  141.      *
  142.      * @return Note|null
  143.      */
  144.     protected function getLastWorkflowNote(AbstractElement $element)
  145.     {
  146.         $notes = new Note\Listing;
  147.         $notes->setCondition(
  148.             'ctype=? and cid=? and type=? order by date desc',
  149.             [
  150.                 Service::getType($element),
  151.                 $element->getId(),
  152.                 'Status Update'
  153.             ]
  154.         )->setLimit(1);
  155.         return $notes->current();
  156.     }
  157.     /**
  158.      * @param Asset|Concrete|PageSnippet $subject
  159.      * @param string $workflowName
  160.      * @param string $transitionName
  161.      *
  162.      * @return string|null potential error message
  163.      *
  164.      * @throws \Exception
  165.      */
  166.     public function applyTransition(AbstractElement $subjectstring $workflowNamestring $transitionName, array $data = []): ?string
  167.     {
  168.         $workflow $this->workflowRegistry->get($subject$workflowName);
  169.         $transitionLabel $transitionName;
  170.         $transition $this->workflowManager->getTransitionByName($workflowName$transitionName);
  171.         if ($transition instanceof Transition) {
  172.             $transitionLabel $this->translatorService->translate($transition->getLabel(), TranslatorDomain::DOMAIN_WORKFLOW_TRANSITION);
  173.         }
  174.         if ($workflow->can($subject$transitionName)) {
  175.             try {
  176.                 $this->workflowManager->applyWithAdditionalData($workflow$subject$transitionName$this->transformAddtionalData($data), true);
  177.                 if ($note $this->getLastWorkflowNote($subject)) {
  178.                     $note->setUser($this->securityService->getPimcoreUserId());
  179.                     $note->save();
  180.                 }
  181.             } catch (\Exception $e) {
  182.                 return $this->translatorService->translate('transition-failed'TranslatorDomain::DOMAIN_WORKFLOW) . ': ' $transitionLabel;
  183.             }
  184.         } else {
  185.             return $this->translatorService->translate('transition-not-allowed'TranslatorDomain::DOMAIN_WORKFLOW) . ': ' $transitionLabel;
  186.         }
  187.         return null;
  188.     }
  189.     /**
  190.      * @param Asset|Concrete|PageSnippet $subject
  191.      * @param string $workflowName
  192.      * @param string $globalActionName
  193.      *
  194.      * @return string|null potential error message
  195.      *
  196.      * @throws \Exception
  197.      */
  198.     public function applyGlobalAction(AbstractElement $subjectstring $workflowNamestring $globalActionName, array $data = []): ?string
  199.     {
  200.         $workflow $this->workflowRegistry->get($subject$workflowName);
  201.         $globalActionLabel $globalActionName;
  202.         $transition $this->workflowManager->getTransitionByName($workflowName$globalActionName);
  203.         if ($transition instanceof Transition) {
  204.             $globalActionLabel $this->translatorService->translate($transition->getLabel(), TranslatorDomain::DOMAIN_WORKFLOW_TRANSITION);
  205.         }
  206.         try {
  207.             $this->workflowManager->applyGlobalAction($workflow$subject$globalActionName$this->transformAddtionalData($data), true);
  208.         } catch (\Exception $e) {
  209.             return $this->translatorService->translate('transition-failed'TranslatorDomain::DOMAIN_WORKFLOW) . ': ' $globalActionLabel;
  210.         }
  211.         return null;
  212.     }
  213.     protected function transformAddtionalData(array $data): array
  214.     {
  215.         if (isset($data['comment'])) {
  216.             return [NotesSubscriber::ADDITIONAL_DATA_NOTES_COMMENT => $data['comment']];
  217.         }
  218.         return [];
  219.     }
  220.     public function hasWorkflowWithPermissions(ElementInterface $element): bool
  221.     {
  222.         $resetToken false;
  223.         if (!$this->tokenStorage->getToken()) {
  224.             $this->tokenStorage->setToken(new AnonymousToken('some_secret'SaveUserSubscriber::FALLBACK_USER_NAME));
  225.             $resetToken true;
  226.         }
  227.         $workflows $this->workflowManager->getAllWorkflowsForSubject($element);
  228.         if ($resetToken) {
  229.             $this->tokenStorage->setToken(null);
  230.         }
  231.         foreach ($workflows as $workflow) {
  232.             $places $this->workflowManager->getPlaceConfigsByWorkflowName($workflow->getName());
  233.             foreach ($places as $place) {
  234.                 $permissions $place->getPlaceConfigArray()['permissions'] ?? [];
  235.                 if (sizeof($permissions) > 0) {
  236.                     return true;
  237.                 }
  238.             }
  239.         }
  240.         return false;
  241.     }
  242.     public function isPermissionAllowedInWorkflows(ElementInterface $elementstring $permission): bool
  243.     {
  244.         // other portal engine permissions are not mapable to workflow permissions
  245.         if (!in_array($permission, [Permission::VIEWPermission::UPDATEPermission::DELETEPermission::EDIT])) {
  246.             return true;
  247.         }
  248.         $permissionMapping = [
  249.             Permission::VIEW => 'view',
  250.             Permission::UPDATE => 'publish',
  251.             Permission::DELETE => 'delete',
  252.             Permission::EDIT => 'publish',
  253.         ];
  254.         foreach ($this->workflowManager->getAllWorkflowsForSubject($element) as $workflow) {
  255.             $marking $workflow->getMarking($element);
  256.             if (!sizeof($marking->getPlaces())) {
  257.                 continue;
  258.             }
  259.             foreach ($this->workflowManager->getOrderedPlaceConfigs($workflow$marking) as $placeConfig) {
  260.                 if (!empty($placeConfig->getPermissions($workflow$element))) {
  261.                     $permissions $placeConfig->getUserPermissions($workflow$element);
  262.                     return isset($permissions[$permissionMapping[$permission]])
  263.                         ? $permissions[$permissionMapping[$permission]]
  264.                         : true;
  265.                 }
  266.             }
  267.         }
  268.         return true;
  269.     }
  270. }