From 2ca47d13f682c0786fadc57e827feed3f4d85e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Burnichon?= Date: Fri, 18 Mar 2016 00:52:15 +0100 Subject: [PATCH] WIP --- .../Controller/Prod/OrderController.php | 157 +++++++++--------- .../Phrasea/ControllerProvider/Prod/Order.php | 2 +- lib/Alchemy/Phrasea/Order/OrderValidator.php | 131 +++++++++++++++ lib/Alchemy/Phrasea/Order/PartialOrder.php | 76 +++++++++ .../Record/RecordReferenceCollection.php | 15 +- 5 files changed, 302 insertions(+), 79 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Order/PartialOrder.php diff --git a/lib/Alchemy/Phrasea/Controller/Prod/OrderController.php b/lib/Alchemy/Phrasea/Controller/Prod/OrderController.php index 699c390b5c..f6d2d192b1 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/OrderController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/OrderController.php @@ -22,9 +22,12 @@ use Alchemy\Phrasea\Model\Entities\BasketElement; use Alchemy\Phrasea\Model\Entities\Order as OrderEntity; use Alchemy\Phrasea\Model\Entities\Order; use Alchemy\Phrasea\Model\Entities\OrderElement; +use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Repositories\OrderElementRepository; use Alchemy\Phrasea\Model\Repositories\OrderRepository; use Alchemy\Phrasea\Order\OrderFiller; +use Alchemy\Phrasea\Order\OrderValidator; +use Alchemy\Phrasea\Order\PartialOrder; use Assert\Assertion; use Doctrine\Common\Collections\ArrayCollection; use Silex\Application; @@ -141,10 +144,7 @@ class OrderController extends Controller */ public function displayOneOrder($order_id) { - $order = $this->getOrderRepository()->find($order_id); - if (null === $order) { - throw new NotFoundHttpException('Order not found'); - } + $order = $this->findOr404($order_id); return $this->render('prod/orders/order_item.html.twig', [ 'order' => $order, @@ -161,71 +161,38 @@ class OrderController extends Controller public function sendOrder(Request $request, $order_id) { $elementIds = $request->request->get('elements', []); + $acceptor = $this->getAuthenticatedUser(); - try { - Assertion::isArray($elementIds); - } catch (\Exception $exception) { - throw new BadRequestHttpException('Improper request', $exception); - } + $elements = $this->findRequestedElements($order_id, $elementIds, $acceptor); + $order = $this->findOr404($order_id); - /** @var OrderElement[] $elements */ - $elements = $this->getOrderElementRepository()->findBy([ - 'id' => $elementIds, - 'order' => $order_id, - ]); - - if (count($elements) !== count($elementIds)) { - throw new NotFoundHttpException(sprintf('At least one requested element does not exists or does not belong to order "%s"', $order_id)); - } - - /** @var Order $order */ - if (null === $order = $this->getOrderRepository()->find($order_id)) { - throw new NotFoundHttpException('Order not found'); - } + $basket = $this->app['provider.order_basket']->provideBasketForOrderAndUser($order, $acceptor); + $orderValidator = $this->getOrderValidator(); + $partialOrder = new PartialOrder($order, $elements); + $basketElements = $orderValidator->createBasketElements($partialOrder); + $orderValidator->accept($acceptor, $partialOrder); + $orderValidator->grantHD($basket->getUser(), $basketElements); $success = false; - $acceptor = $this->getAuthenticatedUser(); - - if ($this->app['validator.order']->isGrantedValidation($acceptor, $elements)) { - throw new AccessDeniedHttpException('At least one element is in a collection you have no access to.'); - } - - $basket = $this->app['provider.order_basket']->provideBasketForOrderAndUser($order, $acceptor); - - $n = 0; - - foreach ($elements as $element) { - $sbas_id = \phrasea::sbasFromBas($this->app, $element->getBaseId()); - $record = new \record_adapter($this->app, $sbas_id, $element->getRecordId()); - - $basketElement = new BasketElement(); - $basketElement->setRecord($record); - $basketElement->setBasket($basket); - - $element->setOrderMaster($acceptor); - $element->setDeny(false); - $element->getOrder()->setBasket($basket); - - $basket->addElement($basketElement); - - $n++; - $this->getAclForUser($basket->getUser())->grant_hd_on($record, $acceptor, 'order'); - } - try { - if ($n > 0) { - $order->setTodo($order->getTodo() - $n); - $this->dispatch(PhraseaEvents::ORDER_DELIVER, new OrderDeliveryEvent($order, $acceptor, $n)); + $manager = $this->getEntityManager(); + if (!empty($basketElements)) { + foreach ($basketElements as $element) { + $basket->addElement($element); + $manager->persist($element); + } + + $order->setTodo($order->getTodo() - count($basketElements)); + $this->dispatch(PhraseaEvents::ORDER_DELIVER, new OrderDeliveryEvent($order, $acceptor, count($basketElements))); } $success = true; - $manager = $this->getEntityManager(); $manager->persist($basket); $manager->persist($order); $manager->flush(); } catch (\Exception $e) { - + // I don't know why only basket persistence is not checked } if ('json' === $request->getRequestFormat()) { @@ -254,32 +221,21 @@ class OrderController extends Controller public function denyOrder(Request $request, $order_id) { $success = false; - /** @var Order $order */ - $order = $this->getOrderRepository()->find($order_id); - if (null === $order) { - throw new NotFoundHttpException('Order not found'); - } + $elementIds = $request->request->get('elements', []); + $acceptor = $this->getAuthenticatedUser(); - $n = 0; - $elements = $request->request->get('elements', []); - $manager = $this->getEntityManager(); - foreach ($order->getElements() as $orderElement) { - if (in_array($orderElement->getId(),$elements)) { - $orderElement->setOrderMaster($this->getAuthenticatedUser()); - $orderElement->setDeny(true); + $elements = $this->findRequestedElements($order_id, $elementIds, $acceptor); + $order = $this->findOr404($order_id); - $manager->persist($orderElement); - $n++; - } - } + $this->getOrderValidator()->deny($acceptor, new PartialOrder($order, $elements)); try { - if ($n > 0) { - $order->setTodo($order->getTodo() - $n); - $this->dispatch(PhraseaEvents::ORDER_DENY, new OrderDeliveryEvent($order, $this->getAuthenticatedUser(), $n)); + if (!empty($elements)) { + $this->dispatch(PhraseaEvents::ORDER_DENY, new OrderDeliveryEvent($order, $acceptor, count($elements))); } $success = true; + $manager = $this->getEntityManager(); $manager->persist($order); $manager->flush(); } catch (\Exception $e) { @@ -317,4 +273,55 @@ class OrderController extends Controller { return $this->app['repo.order-elements']; } + + /** + * @param int $orderId + * @return Order + */ + private function findOr404($orderId) + { + if (null === $order = $this->getOrderRepository()->find($orderId)) { + throw new NotFoundHttpException('Order not found'); + } + + return $order; + } + + /** + * @param int $orderId + * @param array $elementIds + * @param User $acceptor + * @return OrderElement[] + */ + private function findRequestedElements($orderId, $elementIds, User $acceptor) + { + try { + Assertion::isArray($elementIds); + } catch (\Exception $exception) { + throw new BadRequestHttpException('Improper request', $exception); + } + + $elements = $this->getOrderElementRepository()->findBy([ + 'id' => $elementIds, + 'order' => $orderId, + ]); + + if (count($elements) !== count($elementIds)) { + throw new NotFoundHttpException(sprintf('At least one requested element does not exists or does not belong to order "%s"', $orderId)); + } + + if (!$this->getOrderValidator()->isGrantedValidation($acceptor, $elements)) { + throw new AccessDeniedHttpException('At least one element is in a collection you have no access to.'); + } + + return $elements; + } + + /** + * @return OrderValidator + */ + private function getOrderValidator() + { + return $this->app['validator.order']; + } } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Order.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Order.php index 872731d2ea..ce1408fe37 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Order.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Order.php @@ -32,7 +32,7 @@ class Order implements ControllerProviderInterface, ServiceProviderInterface }); $app['validator.order'] = $app->share(function (PhraseaApplication $app) { - $orderValidator = new OrderValidator(); + $orderValidator = new OrderValidator($app['phraseanet.appbox'], $app['repo.collection-references']); $orderValidator->setAclProvider($app['acl']); return $orderValidator; diff --git a/lib/Alchemy/Phrasea/Order/OrderValidator.php b/lib/Alchemy/Phrasea/Order/OrderValidator.php index 769d15f2d7..150353268a 100644 --- a/lib/Alchemy/Phrasea/Order/OrderValidator.php +++ b/lib/Alchemy/Phrasea/Order/OrderValidator.php @@ -11,13 +11,37 @@ namespace Alchemy\Phrasea\Order; use Alchemy\Phrasea\Application\Helper\AclAware; +use Alchemy\Phrasea\Collection\Reference\CollectionReferenceRepository; +use Alchemy\Phrasea\Model\Entities\BasketElement; use Alchemy\Phrasea\Model\Entities\OrderElement; use Alchemy\Phrasea\Model\Entities\User; +use Alchemy\Phrasea\Record\RecordReference; +use Alchemy\Phrasea\Record\RecordReferenceCollection; +use Assert\Assertion; class OrderValidator { + const VALIDATION_ACCEPT = false; + const VALIDATION_DENY = true; + use AclAware; + /** + * @var \appbox + */ + private $appbox; + + /** + * @var CollectionReferenceRepository + */ + private $repository; + + public function __construct(\appbox $appbox, CollectionReferenceRepository $repository) + { + $this->appbox = $appbox; + $this->repository = $repository; + } + /** * @param User $acceptor * @param OrderElement[] $elements @@ -35,4 +59,111 @@ class OrderValidator return empty(array_diff(array_keys($elementsCollections), $acceptableCollections)); } + + /** + * @param PartialOrder $order + * @return BasketElement[] + */ + public function createBasketElements(PartialOrder $order) + { + $basketElements = []; + + $references = $this->getRecordReferenceCollection($order); + + foreach ($references->toRecords($this->appbox) as $record) { + $basketElement = new BasketElement(); + $basketElement->setRecord($record); + + $basketElements[] = $basketElement; + } + + return $basketElements; + } + + /** + * @param User $acceptor + * @param PartialOrder $order + */ + public function accept(User $acceptor, PartialOrder $order) + { + $this->acceptOrDenyPartialOrder($acceptor, $order, self::VALIDATION_ACCEPT); + } + + /** + * @param User $acceptor + * @param PartialOrder $order + */ + public function deny(User $acceptor, PartialOrder $order) + { + $this->acceptOrDenyPartialOrder($acceptor, $order, self::VALIDATION_DENY); + } + + /** + * @param User $user + * @param BasketElement[] $elements + */ + public function grantHD(User $user, $elements) + { + Assertion::allIsInstanceOf($elements, BasketElement::class); + + $acl = $this->getAclForUser($user); + + foreach ($elements as $element) { + $recordReference = RecordReference::createFromDataboxIdAndRecordId( + $element->getSbasId(), + $element->getRecordId() + ); + + $acl->grant_hd_on($recordReference, $user, 'order'); + } + } + + /** + * @param PartialOrder $order + * @return RecordReferenceCollection + */ + private function getRecordReferenceCollection(PartialOrder $order) + { + $collections = []; + + foreach ($this->repository->findMany($order->getBaseIds()) as $collectionReference) { + $collections[$collectionReference->getBaseId()] = $collectionReference; + } + + $references = new RecordReferenceCollection(); + + foreach ($order->getElements() as $orderElement) { + if (!isset($collections[$orderElement->getBaseId()])) { + throw new \RuntimeException('At least one collection was not found.'); + } + + $references->addRecordReference(RecordReference::createFromDataboxIdAndRecordId( + $collections[$orderElement->getBaseId()], + $orderElement->getRecordId() + )); + } + + return $references; + } + + /** + * @param User $acceptor + * @param PartialOrder $order + * @param bool $deny + */ + private function acceptOrDenyPartialOrder(User $acceptor, PartialOrder $order, $deny) + { + $elements = $order->getElements(); + + if (empty($elements)) { + return; + } + + foreach ($elements as $element) { + $element->setOrderMaster($acceptor); + $element->setDeny($deny); + } + + $order->getOrder()->setTodo($order->getOrder()->getTodo() - count($elements)); + } } diff --git a/lib/Alchemy/Phrasea/Order/PartialOrder.php b/lib/Alchemy/Phrasea/Order/PartialOrder.php new file mode 100644 index 0000000000..4eb094bef2 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/PartialOrder.php @@ -0,0 +1,76 @@ +order = $order; + + $this->elements = []; + + foreach ($elements as $element) { + if (null === $element->getOrder() || $element->getOrder()->getId() !== $order->getId()) { + throw new \InvalidArgumentException('Elements should belong to same order'); + } + + $this->elements[$element->getId()] = $element; + } + } + + /** + * @return Order + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return OrderElement[] + */ + public function getElements() + { + return $this->elements; + } + + public function getBaseIds() + { + $baseIds = []; + + foreach ($this->elements as $element) { + $baseIds[$element->getBaseId()] = true; + } + + return array_keys($baseIds); + } +} diff --git a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php index 8335c4db90..802d4a9e67 100644 --- a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php +++ b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php @@ -15,8 +15,6 @@ use Assert\Assertion; class RecordReferenceCollection implements \IteratorAggregate { - private $groups; - /** * @param array $records * @return RecordReferenceCollection @@ -43,16 +41,27 @@ class RecordReferenceCollection implements \IteratorAggregate */ private $references = []; + /** + * @var null|array + */ + private $groups; + /** * @param RecordReferenceInterface[] $references */ - public function __construct($references) + public function __construct($references = []) { Assertion::allIsInstanceOf($references, RecordReferenceInterface::class); $this->references = $references instanceof \Traversable ? iterator_to_array($references) : $references; } + public function addRecordReference(RecordReferenceInterface $reference) + { + $this->references[] = $reference; + $this->groups = null; + } + public function getIterator() { return new \ArrayIterator($this->references);