From cb25643741b417dff422c9b8ab2e9551a83bcda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Thu, 5 Jul 2018 18:11:06 +0200 Subject: [PATCH] Fetch file from rootFolder instead of IAppData to use StreamResponse when displaying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- lib/Service/FileService.php | 39 +++++++++++++-- tests/unit/Service/FileServiceTest.php | 67 ++++++++++++++++++++------ 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 50b71f6fd..a122d76c5 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -29,12 +29,16 @@ use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\AppFramework\Http\StreamResponse; use OCP\Files\Cache\IScanner; +use OCP\Files\Folder; use OCP\Files\IAppData; +use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; @@ -45,17 +49,23 @@ class FileService implements IAttachmentService { private $appData; private $request; private $logger; + private $rootFolder; + private $config; public function __construct( IL10N $l10n, IAppData $appData, IRequest $request, - ILogger $logger + ILogger $logger, + IRootFolder $rootFolder, + IConfig $config ) { $this->l10n = $l10n; $this->appData = $appData; $this->request = $request; $this->logger = $logger; + $this->rootFolder = $rootFolder; + $this->config = $config; } /** @@ -174,9 +184,32 @@ public function delete(Attachment $attachment) { } } + /** + * Workaround until ISimpleFile can be fetched as a resource + * + * @throws \Exception + */ + private function getFileFromRootFolder(Attachment $attachment) { + $folderName = 'file-card-' . (int)$attachment->getCardId(); + $instanceId = $this->config->getSystemValue('instanceid', null); + if ($instanceId === null) { + throw new \Exception('no instance id!'); + } + $name = 'appdata_' . $instanceId; + $appDataFolder = $this->rootFolder->get($name); + $appDataFolder = $appDataFolder->get('deck'); + $cardFolder = $appDataFolder->get($folderName); + return $cardFolder->get($attachment->getData()); + } + public function display(Attachment $attachment) { - $file = $this->getFileForAttachment($attachment); - $response = new FileDisplayResponse($file); + $file = $this->getFileFromRootFolder($attachment); + if (method_exists($file, 'fopen')) { + $response = new StreamResponse($file->fopen('r')); + $response->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"'); + } else { + $response = new FileDisplayResponse($file); + } if ($file->getMimeType() === 'application/pdf') { // We need those since otherwise chrome won't show the PDF file with CSP rule object-src 'none' // https://bugs.chromium.org/p/chromium/issues/detail?id=271452 diff --git a/tests/unit/Service/FileServiceTest.php b/tests/unit/Service/FileServiceTest.php index 39dc9fcec..1e7aa0865 100644 --- a/tests/unit/Service/FileServiceTest.php +++ b/tests/unit/Service/FileServiceTest.php @@ -38,11 +38,15 @@ use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\StreamResponse; use OCP\AppFramework\IAppContainer; +use OCP\Files\Folder; use OCP\Files\IAppData; +use OCP\Files\IRootFolder; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; @@ -61,6 +65,10 @@ class FileServiceTest extends TestCase { private $logger; /** @var FileService */ private $fileService; + /** @var IRootFolder */ + private $rootFolder; + /** @var IConfig */ + private $config; public function setUp() { parent::setUp(); @@ -68,7 +76,9 @@ public function setUp() { $this->appData = $this->createMock(IAppData::class); $this->l10n = $this->createMock(IL10N::class); $this->logger = $this->createMock(ILogger::class); - $this->fileService = new FileService($this->l10n, $this->appData, $this->request, $this->logger); + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->config = $this->createMock(IConfig::class); + $this->fileService = new FileService($this->l10n, $this->appData, $this->request, $this->logger, $this->rootFolder, $this->config); } public function mockGetFolder($cardId) { @@ -253,33 +263,60 @@ public function testDelete() { } public function testDisplay() { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->willReturn('123'); + $appDataFolder = $this->createMock(Folder::class); + $deckAppDataFolder = $this->createMock(Folder::class); + $cardFolder = $this->createMock(Folder::class); + $this->rootFolder->expects($this->once())->method('get')->willReturn($appDataFolder); + $appDataFolder->expects($this->once())->method('get')->willReturn($deckAppDataFolder); + $deckAppDataFolder->expects($this->once())->method('get')->willReturn($cardFolder); $attachment = $this->getAttachment(); - $file = $this->createMock(ISimpleFile::class); - $folder = $this->mockGetFolder('123'); - $folder->expects($this->once()) - ->method('getFile') - ->willReturn($file); - $file->expects($this->exactly(2)) + $file = $this->createMock(\OCP\Files\File::class); + $cardFolder->expects($this->once())->method('get')->willReturn($file); + $file->expects($this->any()) ->method('getMimeType') ->willReturn('image/jpeg'); + $file->expects($this->any()) + ->method('getName') + ->willReturn('file1'); + $file->expects($this->any()) + ->method('fopen') + ->willReturn('fileresource'); $actual = $this->fileService->display($attachment); - $expected = new FileDisplayResponse($file); + $expected = new StreamResponse('fileresource'); $expected->addHeader('Content-Type', 'image/jpeg'); + $expected->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"'); + $this->assertEquals($expected, $actual); } public function testDisplayPdf() { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->willReturn('123'); + $appDataFolder = $this->createMock(Folder::class); + $deckAppDataFolder = $this->createMock(Folder::class); + $cardFolder = $this->createMock(Folder::class); + $this->rootFolder->expects($this->once())->method('get')->willReturn($appDataFolder); + $appDataFolder->expects($this->once())->method('get')->willReturn($deckAppDataFolder); + $deckAppDataFolder->expects($this->once())->method('get')->willReturn($cardFolder); $attachment = $this->getAttachment(); - $file = $this->createMock(ISimpleFile::class); - $folder = $this->mockGetFolder('123'); - $folder->expects($this->once()) - ->method('getFile') - ->willReturn($file); - $file->expects($this->exactly(2)) + $file = $this->createMock(\OCP\Files\File::class); + $cardFolder->expects($this->once())->method('get')->willReturn($file); + $file->expects($this->any()) ->method('getMimeType') ->willReturn('application/pdf'); + $file->expects($this->any()) + ->method('getName') + ->willReturn('file1'); + $file->expects($this->any()) + ->method('fopen') + ->willReturn('fileresource'); $actual = $this->fileService->display($attachment); - $expected = new FileDisplayResponse($file); + $expected = new StreamResponse('fileresource'); + $expected->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"'); $expected->addHeader('Content-Type', 'application/pdf'); $policy = new ContentSecurityPolicy(); $policy->addAllowedObjectDomain('\'self\'');