Compare commits

..

5 Commits

Author SHA1 Message Date
Julius Härtl
20b25e0108 Bump version to 0.4.0-beta5
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-07-05 18:53:47 +02:00
Julius Härtl
e959afb2de Merge pull request #519 from nextcloud/bugfix/noid/stream-attachment-download
Fetch file from rootFolder instead of IAppData to use StreamResponse
2018-07-05 18:52:45 +02:00
Julius Härtl
cb25643741 Fetch file from rootFolder instead of IAppData to use StreamResponse when displaying
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-07-05 18:42:33 +02:00
Nextcloud bot
bbfb9e713a [tx-robot] updated from transifex 2018-07-04 00:12:38 +00:00
Nextcloud bot
e71a38fe96 [tx-robot] updated from transifex 2018-07-03 00:16:32 +00:00
15 changed files with 103 additions and 19 deletions

View File

@@ -14,7 +14,7 @@
- 🚀 Get your project organized
</description>
<version>0.4.0-beta4</version>
<version>0.4.0-beta5</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>

View File

@@ -67,6 +67,7 @@ OC.L10N.register(
"Create new board" : "Neues Board erstellen",
"New board title" : "Neuer Board-Titel",
"Select an attachment" : "Anhang auswählen",
"Cancel upload" : "Hochladen abbrechen",
"by" : "von",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Dateilöschung rückgängig machen - Andernfalls wird die Datei beim nächsten CronJob-Lauf gelöscht.",
"Undo file deletion" : "Dateilöschung rückgängig machen",

View File

@@ -65,6 +65,7 @@
"Create new board" : "Neues Board erstellen",
"New board title" : "Neuer Board-Titel",
"Select an attachment" : "Anhang auswählen",
"Cancel upload" : "Hochladen abbrechen",
"by" : "von",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Dateilöschung rückgängig machen - Andernfalls wird die Datei beim nächsten CronJob-Lauf gelöscht.",
"Undo file deletion" : "Dateilöschung rückgängig machen",

View File

@@ -67,6 +67,7 @@ OC.L10N.register(
"Create new board" : "Neues Board erstellen",
"New board title" : "Neuer Board-Titel",
"Select an attachment" : "Wählen Sie einen Anhang",
"Cancel upload" : "Hochladen abbrechen",
"by" : "von",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Dateilöschung rückgängig machen - Andernfalls wird die Datei beim nächsten CronJob-Lauf gelöscht.",
"Undo file deletion" : "Dateilöschung rückgängig machen",

View File

@@ -65,6 +65,7 @@
"Create new board" : "Neues Board erstellen",
"New board title" : "Neuer Board-Titel",
"Select an attachment" : "Wählen Sie einen Anhang",
"Cancel upload" : "Hochladen abbrechen",
"by" : "von",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Dateilöschung rückgängig machen - Andernfalls wird die Datei beim nächsten CronJob-Lauf gelöscht.",
"Undo file deletion" : "Dateilöschung rückgängig machen",

View File

@@ -24,6 +24,7 @@ OC.L10N.register(
"Missing a temporary folder" : "Falta una carpeta temporal",
"Could not write file to disk" : "No se ha podido escribir el archivo al disco",
"A PHP extension stopped the file upload" : "Una extensión de PHP ha detenido la subida del archivo",
"No file uploaded or file size exceeds maximum of %s" : "No se ha subido ningún archivo, o el tamaño del archivo excede el máximo de %s",
"Deck" : "Deck",
"A kanban style project and personal management tool for Nextcloud" : "Una herramienta de manejo de proyectos y personal al estilo kanban para Nextcloud.",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 🚀 Get your project organized" : "Deckes una herramienta de organización al estilo kanban enfocada en la planificación personal y en la organización de proyectos para equipos, integrada en Nextcloud.\n\n\n- 📥 Añade tus tareas a tarjetas y ordénalas\n- 📄 Escribe notas adicionales en markdown\n- 🔖 Asigna etiquetas para una organización aún mejor\n- 👥 Comparte con tu equipo, amigos o familia\n- 🚀 Organiza tu proyecto",

View File

@@ -22,6 +22,7 @@
"Missing a temporary folder" : "Falta una carpeta temporal",
"Could not write file to disk" : "No se ha podido escribir el archivo al disco",
"A PHP extension stopped the file upload" : "Una extensión de PHP ha detenido la subida del archivo",
"No file uploaded or file size exceeds maximum of %s" : "No se ha subido ningún archivo, o el tamaño del archivo excede el máximo de %s",
"Deck" : "Deck",
"A kanban style project and personal management tool for Nextcloud" : "Una herramienta de manejo de proyectos y personal al estilo kanban para Nextcloud.",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 🚀 Get your project organized" : "Deckes una herramienta de organización al estilo kanban enfocada en la planificación personal y en la organización de proyectos para equipos, integrada en Nextcloud.\n\n\n- 📥 Añade tus tareas a tarjetas y ordénalas\n- 📄 Escribe notas adicionales en markdown\n- 🔖 Asigna etiquetas para una organización aún mejor\n- 👥 Comparte con tu equipo, amigos o familia\n- 🚀 Organiza tu proyecto",

View File

@@ -67,6 +67,7 @@ OC.L10N.register(
"Create new board" : "Crea una nuova lavagna",
"New board title" : "Titolo nuova lavagna",
"Select an attachment" : "Scegli un allegato",
"Cancel upload" : "Annulla caricamento",
"by" : "da",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Annulla eliminazione del file - Altrimenti il file sarà eliminato durante la prossima esecuzione del job di cron.",
"Undo file deletion" : "Annulla l'eliminazione della file",

View File

@@ -65,6 +65,7 @@
"Create new board" : "Crea una nuova lavagna",
"New board title" : "Titolo nuova lavagna",
"Select an attachment" : "Scegli un allegato",
"Cancel upload" : "Annulla caricamento",
"by" : "da",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Annulla eliminazione del file - Altrimenti il file sarà eliminato durante la prossima esecuzione del job di cron.",
"Undo file deletion" : "Annulla l'eliminazione della file",

View File

@@ -67,6 +67,7 @@ OC.L10N.register(
"Create new board" : "Criar novo painel",
"New board title" : "Título do novo painel",
"Select an attachment" : "Selecionar um anexo",
"Cancel upload" : "Cancelar envio",
"by" : "por",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Desfazer exclusão de arquivo - Caso contrário será excluído na próxima execução do cronjob.",
"Undo file deletion" : "Desfazer exclusão de arquivo",

View File

@@ -65,6 +65,7 @@
"Create new board" : "Criar novo painel",
"New board title" : "Título do novo painel",
"Select an attachment" : "Selecionar um anexo",
"Cancel upload" : "Cancelar envio",
"by" : "por",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Desfazer exclusão de arquivo - Caso contrário será excluído na próxima execução do cronjob.",
"Undo file deletion" : "Desfazer exclusão de arquivo",

View File

@@ -6,6 +6,7 @@ OC.L10N.register(
"Remove user from card" : "Убрать пользователя из карточки",
"Hours" : "Часы",
"Minutes" : "Минуты",
"Maximum file size of {size} exceeded" : "Максимальный размер файла {size} превышен",
"Are you sure you want to delete the stack with all of its data?" : "Вы действительно хотите удалить стек со всеми его данными?",
"The card \"%s\" on \"%s\" has reached its due date." : "Настал срок карточки «%s» в «%s» .",
"The board \"%s\" has been shared with you by %s." : "%s предоставил(а) Вам доступ к доске «%s».",
@@ -66,6 +67,7 @@ OC.L10N.register(
"Create new board" : "Создать новую доску",
"New board title" : "Заголовок новой доски",
"Select an attachment" : "Выберите вложение",
"Cancel upload" : "Прервать загрузку",
"by" : "автор",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Отменить удаление файла, иначе он будет автоматически удалена при следующей обработке cronjob.",
"Undo file deletion" : "Отменить удаление файла",

View File

@@ -4,6 +4,7 @@
"Remove user from card" : "Убрать пользователя из карточки",
"Hours" : "Часы",
"Minutes" : "Минуты",
"Maximum file size of {size} exceeded" : "Максимальный размер файла {size} превышен",
"Are you sure you want to delete the stack with all of its data?" : "Вы действительно хотите удалить стек со всеми его данными?",
"The card \"%s\" on \"%s\" has reached its due date." : "Настал срок карточки «%s» в «%s» .",
"The board \"%s\" has been shared with you by %s." : "%s предоставил(а) Вам доступ к доске «%s».",
@@ -64,6 +65,7 @@
"Create new board" : "Создать новую доску",
"New board title" : "Заголовок новой доски",
"Select an attachment" : "Выберите вложение",
"Cancel upload" : "Прервать загрузку",
"by" : "автор",
"Undo file deletion - Otherwise the file will be deleted during the next cronjob run." : "Отменить удаление файла, иначе он будет автоматически удалена при следующей обработке cronjob.",
"Undo file deletion" : "Отменить удаление файла",

View File

@@ -29,12 +29,16 @@ use OCA\Deck\StatusException;
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 @@ class FileService implements IAttachmentService {
}
}
/**
* 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

View File

@@ -38,11 +38,15 @@ use OCA\Deck\StatusException;
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 @@ class FileServiceTest extends TestCase {
$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 @@ class FileServiceTest extends TestCase {
}
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\'');