diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php index f5ff897179..7068e6c7ac 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php @@ -56,6 +56,8 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface return (new ApiOrderController($app)) ->setDispatcher($app['dispatcher']) ->setEntityManagerLocator(new LazyLocator($app, 'orm.em')) + ->setDelivererLocator(new LazyLocator($app, 'phraseanet.file-serve')) + ->setFileSystemLocator(new LazyLocator($app, 'filesystem')) ->setJsonBodyHelper($app['json.body_helper']); } ); @@ -116,6 +118,10 @@ class V2 implements ControllerProviderInterface, ServiceProviderInterface ->assert('orderId', '\d+') ->bind('api_v2_orders_deny'); + $controllers->get('/orders/{orderId}/archive', 'controller.api.v2.orders:getArchiveAction') + ->assert('orderId', '\d+') + ->bind('api_v2_orders_archive'); + return $controllers; } diff --git a/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php index 534a68e048..59758c3836 100644 --- a/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php +++ b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php @@ -10,24 +10,23 @@ namespace Alchemy\Phrasea\Order\Controller; +use Alchemy\Phrasea\Application\Helper\DelivererAware; +use Alchemy\Phrasea\Application\Helper\FilesystemAware; use Alchemy\Phrasea\Application\Helper\JsonBodyAware; use Alchemy\Phrasea\Controller\Api\Result; use Alchemy\Phrasea\Controller\RecordsRequest; +use Alchemy\Phrasea\Core\Event\ExportEvent; use Alchemy\Phrasea\Core\Event\OrderEvent; use Alchemy\Phrasea\Core\PhraseaEvents; +use Alchemy\Phrasea\Http\DeliverDataInterface; +use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\BasketElement; use Alchemy\Phrasea\Model\Entities\Order; -use Alchemy\Phrasea\Model\Entities\OrderElement; -use Alchemy\Phrasea\Model\RecordReferenceInterface; use Alchemy\Phrasea\Order\OrderElementTransformer; -use Alchemy\Phrasea\Order\OrderElementView; use Alchemy\Phrasea\Order\OrderFiller; use Alchemy\Phrasea\Order\OrderTransformer; -use Alchemy\Phrasea\Order\OrderView; use Alchemy\Phrasea\Order\OrderViewBuilder; -use Alchemy\Phrasea\Record\RecordReference; use Alchemy\Phrasea\Record\RecordReferenceCollection; -use Assert\Assertion; use Doctrine\Common\Collections\ArrayCollection; use League\Fractal\Manager; use League\Fractal\Pagination\PagerfantaPaginatorAdapter; @@ -39,9 +38,12 @@ use Pagerfanta\Pagerfanta; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class ApiOrderController extends BaseOrderController { + use DelivererAware; + use FilesystemAware; use JsonBodyAware; public function createAction(Request $request) @@ -135,6 +137,49 @@ class ApiOrderController extends BaseOrderController return $this->returnResourceResponse($request, $fractal, $resource); } + /** + * @param Request $request + * @param int $orderId + * @return Response + */ + public function getArchiveAction(Request $request, $orderId) + { + $order = $this->findOr404($orderId); + + if (! $this->isOrderAccessible($order)) { + throw new AccessDeniedHttpException(sprintf('Cannot access order "%d"', $order->getId())); + } + + $basket = $order->getBasket(); + + if (null === $basket instanceof Basket) { + throw new NotFoundHttpException(sprintf('No archive could be downloaded for Order "%d"', $order->getId())); + } + + $export = new \set_export($this->app, '', $basket->getId()); + $exportName = sprintf('%s.zip', $export->getExportName()); + + $user = $this->getAuthenticatedUser(); + + $subdefs = $this->findDataboxSubdefNames(); + + $exportData = $export->prepare_export($user, $this->getFilesystem(), $subdefs, true, true); + $exportData['export_name'] = $exportName; + + $token = $this->app['manipulator.token']->createDownloadToken($user, serialize($exportData)); + + $this->dispatch( + PhraseaEvents::EXPORT_CREATE, + new ExportEvent($user, $basket->getId(), '', $subdefs, $exportName) + ); + + set_time_limit(0); + ignore_user_abort(true); + $file = \set_export::build_zip($this->app, $token, $exportData, $exportName); + + return $this->deliverFile($file, $exportName, DeliverDataInterface::DISPOSITION_INLINE, 'application/zip'); + } + public function acceptElementsAction(Request $request, $orderId) { $elementIds = $this->fetchElementIdsFromRequest($request); @@ -276,4 +321,25 @@ class ApiOrderController extends BaseOrderController return false; } + + /** + * @return string[] + */ + private function findDataboxSubdefNames() + { + $subdefNames = [ + 'document' => true, + ]; + + foreach ($this->getApplicationBox()->get_databoxes() as $databox) { + foreach ($databox->get_subdef_structure() as $subdefGroup) { + /** @var \databox_subdef $subdef */ + foreach ($subdefGroup as $subdef) { + $subdefNames[$subdef->get_name()] = true; + } + } + } + + return array_keys($subdefNames); + } } diff --git a/lib/Alchemy/Phrasea/Order/OrderTransformer.php b/lib/Alchemy/Phrasea/Order/OrderTransformer.php index 9838e9dafa..bdc1f23725 100644 --- a/lib/Alchemy/Phrasea/Order/OrderTransformer.php +++ b/lib/Alchemy/Phrasea/Order/OrderTransformer.php @@ -37,9 +37,13 @@ class OrderTransformer extends TransformerAbstract 'owner_id' => (int)$order->getUser()->getId(), 'created' => $order->getCreatedOn()->format(DATE_ATOM), 'usage' => $order->getOrderUsage(), - 'status' => 0 === $order->getTodo() ? 'finished' : 'pending' + 'status' => 0 === $order->getTodo() ? 'finished' : 'pending', ]; + if ($view->getArchiveUrl()) { + $data['archive_url'] = $view->getArchiveUrl(); + } + if ($order->getDeadline()) { $data['deadline'] = $order->getDeadline()->format(DATE_ATOM); } diff --git a/lib/Alchemy/Phrasea/Order/OrderView.php b/lib/Alchemy/Phrasea/Order/OrderView.php index cd43659ba1..a74b36595f 100644 --- a/lib/Alchemy/Phrasea/Order/OrderView.php +++ b/lib/Alchemy/Phrasea/Order/OrderView.php @@ -20,6 +20,11 @@ class OrderView */ private $order; + /** + * @var string + */ + private $archiveUrl; + /** * @var OrderElementView[] */ @@ -43,6 +48,14 @@ class OrderView $this->viewElements = $viewElements instanceof \Traversable ? iterator_to_array($viewElements) : $viewElements; } + /** + * @param string $archiveUrl + */ + public function setArchiveUrl($archiveUrl) + { + $this->archiveUrl = (string)$archiveUrl; + } + /** * @return Order */ @@ -51,6 +64,14 @@ class OrderView return $this->order; } + /** + * @return string + */ + public function getArchiveUrl() + { + return $this->archiveUrl; + } + /** * @return OrderElementView[] */ diff --git a/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php b/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php index bd31b48dbf..3a105ee423 100644 --- a/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php +++ b/lib/Alchemy/Phrasea/Order/OrderViewBuilder.php @@ -84,6 +84,21 @@ class OrderViewBuilder */ private function fillViews(array $views, array $includes) { + array_walk($views, function (OrderView $view) { + // Archive is only available when a Basket is associated with the order (at least one element was accepted) + if (null === $basket = $view->getOrder()->getBasket()) { + return; + } + + if ($basket->getElements()->isEmpty()) { + return; + } + + $view->setArchiveUrl($this->application->url('api_v2_orders_archive', [ + 'orderId' => $view->getOrder()->getId(), + ])); + }); + if (!in_array('elements', $includes, true)) { return; } diff --git a/lib/classes/databox.php b/lib/classes/databox.php index 3469811b86..4346a3da04 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -892,7 +892,7 @@ class databox extends base implements ThumbnailedElement } /** - * @return databox_subdefsStructure|databox_subdef[][] + * @return databox_subdefsStructure|\Alchemy\Phrasea\Databox\SubdefGroup[]|databox_subdef[][] */ public function get_subdef_structure() {