Skip to content

Commit

Permalink
Implement MediaDownloadAction
Browse files Browse the repository at this point in the history
  • Loading branch information
jordisala1991 committed Sep 30, 2021
1 parent f84f103 commit 12d483d
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 91 deletions.
73 changes: 73 additions & 0 deletions src/Action/MediaDownloadAction.php
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\MediaBundle\Action;

use Sonata\MediaBundle\Model\MediaManagerInterface;
use Sonata\MediaBundle\Provider\MediaProviderInterface;
use Sonata\MediaBundle\Provider\Pool;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

final class MediaDownloadAction
{
/**
* @var MediaManagerInterface
*/
private $mediaManager;

/**
* @var Pool
*/
private $pool;

public function __construct(MediaManagerInterface $mediaManager, Pool $pool)
{
$this->mediaManager = $mediaManager;
$this->pool = $pool;
}

/**
* @param int|string $id
*
* @throws NotFoundHttpException
* @throws AccessDeniedException
*/
public function __invoke(Request $request, $id, string $format = MediaProviderInterface::FORMAT_REFERENCE): Response
{
$media = $this->mediaManager->find($id);

if (null === $media) {
throw new NotFoundHttpException(sprintf('unable to find the media with the id : %s', $id));
}

if (!$this->pool->getDownloadStrategy($media)->isGranted($media, $request)) {
throw new AccessDeniedException();
}

$response = $this->pool->getProvider($media->getProviderName())->getDownloadResponse(
$media,
$format,
$this->pool->getDownloadMode($media)
);

if ($response instanceof BinaryFileResponse) {
$response->prepare($request);
}

return $response;
}
}
31 changes: 13 additions & 18 deletions src/Controller/MediaController.php
Expand Up @@ -13,17 +13,19 @@

namespace Sonata\MediaBundle\Controller;

use Sonata\MediaBundle\Action\MediaDownloadAction;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Provider\MediaProviderInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

/**
* @final since sonata-project/media-bundle 3.21.0
* NEXT_MAJOR: Remove this class.
*
* @deprecated since sonata-project/media-bundle 3.x, to be removed in 4.0.
*/
class MediaController extends Controller
{
Expand All @@ -46,32 +48,25 @@ public function getMedia($id)
}

/**
* NEXT_MAJOR: Remove this method.
*
* @param string $id
* @param string $format
*
* @throws NotFoundHttpException
*
* @return Response
*
* @deprecated since sonata-project/media-bundle 3.x, to be removed in 4.0.
*/
public function downloadAction($id, $format = MediaProviderInterface::FORMAT_REFERENCE)
{
$media = $this->getMedia($id);

if (!$media) {
throw new NotFoundHttpException(sprintf('unable to find the media with the id : %s', $id));
}

if (!$this->get('sonata.media.pool')->getDownloadSecurity($media)->isGranted($media, $this->getCurrentRequest())) {
throw new AccessDeniedException();
}

$response = $this->getProvider($media)->getDownloadResponse($media, $format, $this->get('sonata.media.pool')->getDownloadMode($media));

if ($response instanceof BinaryFileResponse) {
$response->prepare($this->getCurrentRequest());
}
$mediaDownloadAction = new MediaDownloadAction(
$this->get('sonata.media.manager.media'),
$this->get('sonata.media.pool')
);

return $response;
return $mediaDownloadAction($this->getCurrentRequest(), $id, $format);
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/SonataMediaExtension.php
Expand Up @@ -48,6 +48,7 @@ public function load(array $configs, ContainerBuilder $container)
$config = $processor->processConfiguration($configuration, $configs);

$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('actions.xml');
$loader->load('provider.xml');
$loader->load('media.xml');
$loader->load('twig.xml');
Expand Down
9 changes: 9 additions & 0 deletions src/Resources/config/actions.xml
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.media.action.media_download" class="Sonata\MediaBundle\Action\MediaDownloadAction" public="true">
<argument type="service" id="sonata.media.manager.media"/>
<argument type="service" id="sonata.media.pool"/>
</service>
</services>
</container>
2 changes: 1 addition & 1 deletion src/Resources/config/routing/media.xml
Expand Up @@ -5,7 +5,7 @@
<default key="format">reference</default>
</route>
<route id="sonata_media_download" path="/download/{id}/{format}">
<default key="_controller">Sonata\MediaBundle\Controller\MediaController::downloadAction</default>
<default key="_controller">sonata.media.action.media_download</default>
<default key="format">reference</default>
<requirement key="id">.*</requirement>
</route>
Expand Down
112 changes: 112 additions & 0 deletions tests/Action/MediaDownloadActionTest.php
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\MediaBundle\Tests\Controller;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sonata\MediaBundle\Action\MediaDownloadAction;
use Sonata\MediaBundle\Model\MediaManagerInterface;
use Sonata\MediaBundle\Provider\MediaProviderInterface;
use Sonata\MediaBundle\Provider\Pool;
use Sonata\MediaBundle\Security\DownloadStrategyInterface;
use Sonata\MediaBundle\Tests\App\Entity\Media;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

final class MediaDownloadActionTest extends TestCase
{
/**
* @var MockObject&MediaManagerInterface
*/
private $mediaManager;

/**
* @var Pool
*/
private $pool;

/**
* @var MediaDownloadAction
*/
private $mediaDownloadAction;

protected function setUp(): void
{
$this->mediaManager = $this->createMock(MediaManagerInterface::class);
$this->pool = new Pool('default_context');

$this->mediaDownloadAction = new MediaDownloadAction($this->mediaManager, $this->pool);
}

public function testDownloadActionWithNotFoundMedia(): void
{
$this->expectException(NotFoundHttpException::class);

$this->mediaDownloadAction->__invoke(new Request(), 1);
}

public function testDownloadActionAccessDenied(): void
{
$this->expectException(AccessDeniedException::class);

$request = new Request();
$media = new Media();
$media->setContext('default_context');

$this->configureDownloadSecurity($media, $request, false);
$this->mediaManager->method('find')->with(1)->willReturn($media);

$this->mediaDownloadAction->__invoke($request, 1);
}

public function testDownloadActionBinaryFile(): void
{
$media = new Media();
$media->setContext('default_context');
$media->setProviderName('provider');

$request = new Request();

$provider = $this->createMock(MediaProviderInterface::class);
$response = $this->createMock(BinaryFileResponse::class);

$this->configureDownloadSecurity($media, $request, true);

$this->pool->addProvider('provider', $provider);
$this->mediaManager->method('find')->with(1)->willReturn($media);
$provider->method('getDownloadResponse')->with($media, 'format', 'mode')->willReturn($response);
$response->expects(static::once())->method('prepare')->with($request);

$result = $this->mediaDownloadAction->__invoke($request, 1, 'format');

static::assertSame($response, $result);
}

private function configureDownloadSecurity(
Media $media,
Request $request,
bool $isGranted
): void {
$strategy = $this->createMock(DownloadStrategyInterface::class);
$strategy->method('isGranted')->with($media, $request)->willReturn($isGranted);

$this->pool->addContext('default_context', [], [], [
'mode' => 'mode',
'strategy' => 'download_strategy',
]);
$this->pool->addDownloadStrategy('download_strategy', $strategy);
}
}

0 comments on commit 12d483d

Please sign in to comment.