Browse Source

Workflow batch actions

Guillermo Espinoza 7 years ago
parent
commit
8ef6ecf68e

+ 35 - 0
Admin/WorkflowBaseAdmin.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace WorkflowBundle\Admin;
+
+use Base\AdminBundle\Admin\BaseAdmin;
+
+class WorkflowBaseAdmin extends BaseAdmin
+{
+
+    /**
+     * @return array
+     */
+    public function getBatchActions()
+    {
+        $actions = parent::getBatchActions();
+
+        $workflows = $this->getRepository('WorkflowBundle:Workflow')->findAllByClass($this->getClass());
+        foreach ($workflows as $workflow) {
+            $transitions = $workflow->getDefinition($workflow->getSubject())->getTransitions();
+            foreach ($transitions as $key => $transition) {
+                $actions[$key] = array(
+                    'label' => $this->trans('workflow.' . $workflow->getName() . '.transitions.' . $transition->getName(), array(), 'WorkflowLabel'),
+                    'ask_confirmation' => true,
+                    'workflow' => array(
+                        'name' => $workflow->getName(),
+                        'transition' => $transition->getName(),
+                    ),
+                );
+            }
+        }
+
+        return $actions;
+    }
+
+}

+ 184 - 0
Controller/CRUDController.php

@@ -0,0 +1,184 @@
+<?php
+
+namespace WorkflowBundle\Controller;
+
+use Base\AdminBundle\Controller\CRUDController as BaseCRUDController;
+use Doctrine\Common\Inflector\Inflector;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+class CRUDController extends BaseCRUDController
+{
+
+    /**
+     * Batch action.
+     *
+     * @return Response|RedirectResponse
+     *
+     * @throws NotFoundHttpException If the HTTP method is not POST
+     * @throws \RuntimeException     If the batch action is not defined
+     */
+    public function batchAction()
+    {
+        $request = $this->getRequest();
+        $restMethod = $this->getRestMethod();
+
+        if ('POST' !== $restMethod) {
+            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
+        }
+
+        // check the csrf token
+        $this->validateCsrfToken('sonata.batch');
+
+        $confirmation = $request->get('confirmation', false);
+
+        if ($data = json_decode($request->get('data'), true)) {
+            $action = $data['action'];
+            $idx = $data['idx'];
+            $allElements = $data['all_elements'];
+            $request->request->replace(array_merge($request->request->all(), $data));
+        } else {
+            $request->request->set('idx', $request->get('idx', array()));
+            $request->request->set('all_elements', $request->get('all_elements', false));
+
+            $action = $request->get('action');
+            $idx = $request->get('idx');
+            $allElements = $request->get('all_elements');
+            $data = $request->request->all();
+
+            unset($data['_sonata_csrf_token']);
+        }
+
+        // NEXT_MAJOR: Remove reflection check.
+        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
+        if ($reflector->getDeclaringClass()->getName() === get_class($this->admin)) {
+            @trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
+                            . ' is deprecated since version 3.2.'
+                            . ' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
+                            . ' The method will be final in 4.0.', E_USER_DEPRECATED
+            );
+        }
+        $batchActions = $this->admin->getBatchActions();
+        if (!array_key_exists($action, $batchActions)) {
+            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
+        }
+
+        $camelizedAction = Inflector::classify($action);
+        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
+
+        if (method_exists($this, $isRelevantAction)) {
+            $nonRelevantMessage = call_user_func(array($this, $isRelevantAction), $idx, $allElements, $request);
+        } else {
+            $nonRelevantMessage = count($idx) != 0 || $allElements; // at least one item is selected
+        }
+
+        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
+            $nonRelevantMessage = 'flash_batch_empty';
+        }
+
+        $datagrid = $this->admin->getDatagrid();
+        $datagrid->buildPager();
+
+        if (true !== $nonRelevantMessage) {
+            $this->addFlash('sonata_flash_info', $nonRelevantMessage);
+
+            return new RedirectResponse(
+                    $this->admin->generateUrl(
+                            'list', array('filter' => $this->admin->getFilterParameters())
+                    )
+            );
+        }
+
+        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
+                $batchActions[$action]['ask_confirmation'] :
+                true;
+
+        if ($askConfirmation && $confirmation != 'ok') {
+            $actionLabel = $batchActions[$action]['label'];
+            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
+                    $batchActions[$action]['translation_domain'] :
+                    $this->admin->getTranslationDomain();
+
+            $formView = $datagrid->getForm()->createView();
+            $this->setFormTheme($formView, $this->admin->getFilterTheme());
+
+            return $this->render($this->admin->getTemplate('batch_confirmation'), array(
+                        'action' => 'list',
+                        'action_label' => $actionLabel,
+                        'batch_translation_domain' => $batchTranslationDomain,
+                        'datagrid' => $datagrid,
+                        'form' => $formView,
+                        'data' => $data,
+                        'csrf_token' => $this->getCsrfToken('sonata.batch'),
+                            ), null);
+        }
+
+        $workflowBatchAction = isset($batchActions[$action]['workflow']) ?: false;
+
+        // execute the action, batchActionXxxxx
+        $finalAction = sprintf('batchAction%s', $camelizedAction);
+        if (!is_callable(array($this, $finalAction)) && !$workflowBatchAction) {
+            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
+        }
+
+        $query = $datagrid->getQuery();
+
+        $query->setFirstResult(null);
+        $query->setMaxResults(null);
+
+        $this->allElementsBatch = $allElements;
+        $this->admin->preBatchAction($action, $query, $idx, $allElements);
+
+        if (count($idx) > 0) {
+            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
+        } elseif (!$allElements) {
+            $query = null;
+        }
+
+        if ($workflowBatchAction) {
+            return $this->runWorkflowBatchAction($batchActions[$action]['workflow'], $query);
+        }
+
+        return call_user_func(array($this, $finalAction), $query, $request);
+    }
+
+    /**
+     * @param array $workflow
+     * @param ProxyQueryInterface $selectedModelQuery
+     * 
+     * @return RedirectResponse
+     */
+    public function runWorkflowBatchAction($workflow, ProxyQueryInterface $selectedModelQuery)
+    {
+        if ($this->admin->isGranted('EDIT') === false || $this->admin->isGranted('DELETE') === false) {
+            throw new AccessDeniedException();
+        }
+        $session = $this->get('session')->getFlashBag();
+        $translator = $this->get('translator');
+        try {
+            $workflowRegistry = $this->get('workflow.registry');
+            $em = $this->get('doctrine.orm.entity_manager');
+            
+            $selectedModels = $selectedModelQuery->execute();
+            foreach ($selectedModels as $selectedModel) {
+                if ($workflowRegistry->get($selectedModel, $workflow['name'])->can($selectedModel, $workflow['transition'])) {
+                    $workflowName = "{$selectedModel->getWorkflowType()}.{$selectedModel->getWorkflowName()}";
+                    $this->get($workflowName)->apply($selectedModel, $workflow['transition']);
+                    
+                    $em->persist($selectedModel);
+                    $em->flush($selectedModel);
+                }
+            }
+            $session->add('sonata_flash_success', $translator->trans('flash_workflow_batch_success', array(), 'WorkflowBundle'));
+        } catch (\Exception $e) {
+            $session->add('sonata_flash_error', $translator->trans('flash_workflow_batch_error', array(), 'WorkflowBundle'));
+            $session->add('sonata_flash_error', $e->getMessage());
+        }
+
+        return new RedirectResponse($this->admin->generateUrl('list', array('filter' => $this->admin->getFilterParameters())));
+    }
+
+}

+ 1 - 1
Entity/Workflow.php

@@ -17,7 +17,7 @@ use Symfony\Component\Workflow\Registry;
 /**
 /**
  * Workflow
  * Workflow
  *
  *
- * @ORM\Entity
+ * @ORM\Entity(repositoryClass="WorkflowBundle\Repository\WorkflowRepository")
  * @ORM\HasLifecycleCallbacks
  * @ORM\HasLifecycleCallbacks
  * @UniqueEntity(fields={"name", "tenancyId"}, message="errors.duplicate_key")
  * @UniqueEntity(fields={"name", "tenancyId"}, message="errors.duplicate_key")
  * @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="unique_idx", columns={"name", "tenancy_id"})})
  * @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="unique_idx", columns={"name", "tenancy_id"})})

+ 27 - 0
Repository/WorkflowRepository.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace WorkflowBundle\Repository;
+
+use \Doctrine\ORM\EntityRepository;
+
+class WorkflowRepository extends EntityRepository
+{
+
+    /**
+     * @param string $class
+     * 
+     * @return array
+     */
+    public function findAllByClass($class)
+    {
+        $results = $this->createQueryBuilder('Workflow')->getQuery()->getResult();
+        foreach ($results as $key => &$result) {
+            if (!in_array($class, $result->getSupport())) {
+                unset($results[$key]);
+            }
+        }
+        
+        return $results;
+    }
+
+}

+ 3 - 1
Resources/translations/WorkflowBundle.es.yml

@@ -109,4 +109,6 @@ Undefined Workflow: Sin workflow
 workflow_edit_create_alert: "Para realizar el template, tenga en cuenta:"
 workflow_edit_create_alert: "Para realizar el template, tenga en cuenta:"
 Workflow Documentation: Documentación sobre Workflows
 Workflow Documentation: Documentación sobre Workflows
 YAML Documentation: Sintaxis básica para YAML
 YAML Documentation: Sintaxis básica para YAML
-Actions Workflow: Acciones
+Actions Workflow: Acciones
+flash_workflow_batch_success: La acción se ejecutó correctamente
+flash_workflow_batch_error: Se produjo un error