Merge pull request #1741 from nextcloud/new_preview

Improve previews
This commit is contained in:
Lukas Reschke 2016-11-03 21:07:16 +01:00 committed by GitHub
commit c0bbae28f0
32 changed files with 2353 additions and 382 deletions

View file

@ -6,3 +6,10 @@ NC 11 (????-??-??)
* PHP 5.4 and 5.5 no longer supported
* PHP 7.1 support
* OC_L10N removed use \OCP\IL10N (#1948)
* Preview handling is improved by sharing previews:
* Preview sharing (shared files/external storages)
* Previews are stored in the AppData
* Previews are served faster by not first converting them to image objects
* Core preview route changed:
* Route for the urlgenerator changed from 'core_ajax_preview' to 'core.Preview.getPreview'
* $urlGenerator->linkToRoute('core_ajax_preview', ...) => $urlGenerator->linkToRoute('core.Preview.getPreview', ...)

View file

@ -31,11 +31,13 @@ namespace OCA\Files\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Controller;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\IConfig;
use OCP\IRequest;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\Response;
use OCA\Files\Service\TagService;
use OCP\IPreview;
@ -101,18 +103,27 @@ class ApiController extends Controller {
* @param int $x
* @param int $y
* @param string $file URL-encoded filename
* @return DataResponse|DataDisplayResponse
* @return DataResponse|FileDisplayResponse
*/
public function getThumbnail($x, $y, $file) {
if($x < 1 || $y < 1) {
return new DataResponse(['message' => 'Requested size must be numeric and a positive value.'], Http::STATUS_BAD_REQUEST);
}
$preview = $this->previewManager->createPreview('files/'.$file, $x, $y, true);
if ($preview->valid()) {
return new DataDisplayResponse($preview->data(), Http::STATUS_OK, ['Content-Type' => 'image/png']);
} else {
try {
$file = $this->userFolder->get($file);
if ($file instanceof Folder) {
throw new NotFoundException();
}
/** @var File $file */
$preview = $this->previewManager->getPreview($file, $x, $y, true);
return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
} catch (NotFoundException $e) {
return new DataResponse(['message' => 'File not found.'], Http::STATUS_NOT_FOUND);
} catch (\Exception $e) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
}

View file

@ -28,11 +28,15 @@ namespace OCA\Files\Controller;
use OC\Files\FileInfo;
use OCP\AppFramework\Http;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\StorageNotAvailableException;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Share\IManager;
use Test\TestCase;
use OCP\IRequest;
use OCA\Files\Service\TagService;
@ -54,7 +58,7 @@ class ApiControllerTest extends TestCase {
private $request;
/** @var TagService */
private $tagService;
/** @var IPreview */
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $preview;
/** @var ApiController */
private $apiController;
@ -62,11 +66,13 @@ class ApiControllerTest extends TestCase {
private $shareManager;
/** @var \OCP\IConfig */
private $config;
/** @var \OC\Files\Node\Folder */
/** @var Folder|\PHPUnit_Framework_MockObject_MockObject */
private $userFolder;
public function setUp() {
$this->request = $this->getMockBuilder('\OCP\IRequest')
parent::setUp();
$this->request = $this->getMockBuilder(IRequest::class)
->disableOriginalConstructor()
->getMock();
$this->user = $this->createMock(IUser::class);
@ -77,17 +83,17 @@ class ApiControllerTest extends TestCase {
$userSession->expects($this->any())
->method('getUser')
->will($this->returnValue($this->user));
$this->tagService = $this->getMockBuilder('\OCA\Files\Service\TagService')
$this->tagService = $this->getMockBuilder(TagService::class)
->disableOriginalConstructor()
->getMock();
$this->shareManager = $this->getMockBuilder('\OCP\Share\IManager')
$this->shareManager = $this->getMockBuilder(IManager::class)
->disableOriginalConstructor()
->getMock();
$this->preview = $this->getMockBuilder('\OCP\IPreview')
$this->preview = $this->getMockBuilder(IPreview::class)
->disableOriginalConstructor()
->getMock();
$this->config = $this->createMock(IConfig::class);
$this->userFolder = $this->getMockBuilder('\OC\Files\Node\Folder')
$this->userFolder = $this->getMockBuilder(Folder::class)
->disableOriginalConstructor()
->getMock();
@ -153,28 +159,41 @@ class ApiControllerTest extends TestCase {
}
public function testGetThumbnailInvalidSize() {
$this->userFolder->method('get')
->with($this->equalTo(''))
->willThrowException(new NotFoundException());
$expected = new DataResponse(['message' => 'Requested size must be numeric and a positive value.'], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $this->apiController->getThumbnail(0, 0, ''));
}
public function testGetThumbnailInvaidImage() {
$file = $this->createMock(File::class);
$this->userFolder->method('get')
->with($this->equalTo('unknown.jpg'))
->willReturn($file);
$this->preview->expects($this->once())
->method('createPreview')
->with('files/unknown.jpg', 10, 10, true)
->willReturn(new Image);
->method('getPreview')
->with($file, 10, 10, true)
->willThrowException(new NotFoundException());
$expected = new DataResponse(['message' => 'File not found.'], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $this->apiController->getThumbnail(10, 10, 'unknown.jpg'));
}
public function testGetThumbnail() {
$file = $this->createMock(File::class);
$this->userFolder->method('get')
->with($this->equalTo('known.jpg'))
->willReturn($file);
$preview = $this->createMock(ISimpleFile::class);
$this->preview->expects($this->once())
->method('createPreview')
->with('files/known.jpg', 10, 10, true)
->willReturn(new Image(\OC::$SERVERROOT.'/tests/data/testimage.jpg'));
->method('getPreview')
->with($this->equalTo($file), 10, 10, true)
->willReturn($preview);
$ret = $this->apiController->getThumbnail(10, 10, 'known.jpg');
$this->assertEquals(Http::STATUS_OK, $ret->getStatus());
$this->assertInstanceOf(Http\FileDisplayResponse::class, $ret);
}
public function testUpdateFileSorting() {

View file

@ -1,126 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Björn Schießle <bjoern@schiessle.org>
* @author Georg Ehrke <georg@owncloud.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
OCP\JSON::checkAppEnabled('files_sharing');
\OC_User::setIncognitoMode(true);
$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : '';
$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '32';
$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '32';
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true;
$token = array_key_exists('t', $_GET) ? (string) $_GET['t'] : '';
$keepAspect = array_key_exists('a', $_GET) ? true : false;
if($token === ''){
\OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
\OCP\Util::writeLog('core-preview', 'No token parameter was passed', \OCP\Util::DEBUG);
exit;
}
$linkedItem = \OCP\Share::getShareByToken($token);
$shareManager = \OC::$server->getShareManager();
$share = $shareManager->getShareByToken($token);
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
OCP\JSON::error(array('data' => 'Share is not readable.'));
exit();
}
if($linkedItem === false || ($linkedItem['item_type'] !== 'file' && $linkedItem['item_type'] !== 'folder')) {
\OC_Response::setStatus(\OC_Response::STATUS_NOT_FOUND);
\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
exit;
}
if(!isset($linkedItem['uid_owner']) || !isset($linkedItem['file_source'])) {
\OC_Response::setStatus(\OC_Response::STATUS_INTERNAL_SERVER_ERROR);
\OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OCP\Util::WARN);
exit;
}
$rootLinkItem = OCP\Share::resolveReShare($linkedItem);
$userId = $rootLinkItem['uid_owner'];
OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
\OC_Util::setupFS($userId);
\OC\Files\Filesystem::initMountPoints($userId);
$view = new \OC\Files\View('/' . $userId . '/files');
$pathId = $linkedItem['file_source'];
$path = $view->getPath($pathId);
if($path === null) {
\OC_Response::setStatus(\OC_Response::STATUS_NOT_FOUND);
\OCP\Util::writeLog('core-preview', 'Could not resolve file for shared item', \OCP\Util::WARN);
exit;
}
$pathInfo = $view->getFileInfo($path);
$sharedFile = null;
if($linkedItem['item_type'] === 'folder') {
$isValid = \OC\Files\Filesystem::isValidPath($file);
if(!$isValid) {
\OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
\OCP\Util::writeLog('core-preview', 'Passed filename is not valid, might be malicious (file:"' . $file . '";ip:"' . \OC::$server->getRequest()->getRemoteAddress() . '")', \OCP\Util::WARN);
exit;
}
$sharedFile = \OC\Files\Filesystem::normalizePath($file);
}
if($linkedItem['item_type'] === 'file') {
$parent = $pathInfo['parent'];
$path = $view->getPath($parent);
$sharedFile = $pathInfo['name'];
}
$path = \OC\Files\Filesystem::normalizePath($path, false);
if(substr($path, 0, 1) === '/') {
$path = substr($path, 1);
}
if($maxX === 0 || $maxY === 0) {
\OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
\OCP\Util::writeLog('core-preview', 'x and/or y set to 0', \OCP\Util::DEBUG);
exit;
}
$root = 'files/' . $path;
try{
$preview = new \OC\Preview($userId, $root);
$preview->setFile($sharedFile);
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
$preview->setScalingUp($scalingUp);
$preview->setKeepAspect($keepAspect);
$preview->showPreview();
} catch (\Exception $e) {
\OC_Response::setStatus(\OC_Response::STATUS_INTERNAL_SERVER_ERROR);
\OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::DEBUG);
}

View file

@ -39,6 +39,17 @@ $application->registerRoutes($this, [
'url' => '/testremote',
'verb' => 'GET'
],
[
'name' => 'PublicPreview#getPreview',
'url' => '/publicpreview',
'verb' => 'GET',
],
[
'name' => 'PublicPreview#getPreview',
'url' => '/ajax/publicpreview.php',
'verb' => 'GET',
],
],
'ocs' => [
/*
@ -114,15 +125,8 @@ $application->registerRoutes($this, [
]);
/** @var $this \OCP\Route\IRouter */
$this->create('core_ajax_public_preview', '/publicpreview')->action(
function() {
require_once __DIR__ . '/../ajax/publicpreview.php';
});
$this->create('files_sharing_ajax_list', 'ajax/list.php')
->actionInclude('files_sharing/ajax/list.php');
$this->create('files_sharing_ajax_publicpreview', 'ajax/publicpreview.php')
->actionInclude('files_sharing/ajax/publicpreview.php');
$this->create('sharing_external_shareinfo', '/shareinfo')
->actionInclude('files_sharing/ajax/shareinfo.php');

View file

@ -0,0 +1,102 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Sharing\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Constants;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as ShareManager;
class PublicPreviewController extends Controller {
/** @var ShareManager */
private $shareManager;
/** @var IPreview */
private $previewManager;
public function __construct($appName,
IRequest $request,
ShareManager $shareManger,
IPreview $previewManager) {
parent::__construct($appName, $request);
$this->shareManager = $shareManger;
$this->previewManager = $previewManager;
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $file
* @param int $x
* @param int $y
* @param string $t
* @param bool $a
* @return DataResponse|FileDisplayResponse
*/
public function getPreview(
$file = '',
$x = 32,
$y = 32,
$t = '',
$a = false
) {
if ($t === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
$share = $this->shareManager->getShareByToken($t);
} catch (ShareNotFound $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
try {
$node = $share->getNode();
if ($node instanceof Folder) {
$file = $node->get($file);
} else {
$file = $node;
}
$f = $this->previewManager->getPreview($file, $x, $y, !$a);
return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
}
}

View file

@ -0,0 +1,193 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Sharing\Tests\Controller;
use OCA\Files_Sharing\Controller\PublicPreviewController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Constants;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Punic\Data;
use Test\TestCase;
class PublicPreviewControllerTest extends TestCase {
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
/** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
private $shareManager;
/** @var PublicPreviewController */
private $controller;
public function setUp() {
parent::setUp();
$this->previewManager = $this->createMock(IPreview::class);
$this->shareManager = $this->createMock(IManager::class);
$this->controller = new PublicPreviewController(
'files_sharing',
$this->createMock(IRequest::class),
$this->shareManager,
$this->previewManager
);
}
public function testInvalidToken() {
$res = $this->controller->getPreview('file', 10, 10, '');
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidWidth() {
$res = $this->controller->getPreview('file', 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidHeight() {
$res = $this->controller->getPreview('file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidShare() {
$this->shareManager->method('getShareByToken')
->with($this->equalTo('token'))
->willThrowException(new ShareNotFound());
$res = $this->controller->getPreview('file', 10, 10, 'token');
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testShareNotAccessable() {
$share = $this->createMock(IShare::class);
$this->shareManager->method('getShareByToken')
->with($this->equalTo('token'))
->willReturn($share);
$share->method('getPermissions')
->willReturn(0);
$res = $this->controller->getPreview('file', 10, 10, 'token');
$expected = new DataResponse([], Http::STATUS_FORBIDDEN);
$this->assertEquals($expected, $res);
}
public function testPreviewFile() {
$share = $this->createMock(IShare::class);
$this->shareManager->method('getShareByToken')
->with($this->equalTo('token'))
->willReturn($share);
$share->method('getPermissions')
->willReturn(Constants::PERMISSION_READ);
$file = $this->createMock(File::class);
$share->method('getNode')
->willReturn($file);
$preview = $this->createMock(ISimpleFile::class);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, false)
->willReturn($preview);
$preview->method('getMimeType')
->willReturn('myMime');
$res = $this->controller->getPreview('file', 10, 10, 'token', true);
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'myMime']);
$this->assertEquals($expected, $res);
}
public function testPreviewFolderInvalidFile() {
$share = $this->createMock(IShare::class);
$this->shareManager->method('getShareByToken')
->with($this->equalTo('token'))
->willReturn($share);
$share->method('getPermissions')
->willReturn(Constants::PERMISSION_READ);
$folder = $this->createMock(Folder::class);
$share->method('getNode')
->willReturn($folder);
$folder->method('get')
->with($this->equalTo('file'))
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file', 10, 10, 'token', true);
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testPreviewFolderValidFile() {
$share = $this->createMock(IShare::class);
$this->shareManager->method('getShareByToken')
->with($this->equalTo('token'))
->willReturn($share);
$share->method('getPermissions')
->willReturn(Constants::PERMISSION_READ);
$folder = $this->createMock(Folder::class);
$share->method('getNode')
->willReturn($folder);
$file = $this->createMock(File::class);
$folder->method('get')
->with($this->equalTo('file'))
->willReturn($file);
$preview = $this->createMock(ISimpleFile::class);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, false)
->willReturn($preview);
$preview->method('getMimeType')
->willReturn('myMime');
$res = $this->controller->getPreview('file', 10, 10, 'token', true);
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'myMime']);
$this->assertEquals($expected, $res);
}
}

View file

@ -1,80 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Björn Schießle <bjoern@schiessle.org>
* @author Georg Ehrke <georg@owncloud.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
\OC_Util::checkLoggedIn();
\OC::$server->getSession()->close();
if(!\OC_App::isEnabled('files_trashbin')){
exit;
}
$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : '';
$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '44';
$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '44';
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true;
if($file === '') {
\OC_Response::setStatus(400); //400 Bad Request
\OCP\Util::writeLog('core-preview', 'No file parameter was passed', \OCP\Util::DEBUG);
exit;
}
if($maxX === 0 || $maxY === 0) {
\OC_Response::setStatus(400); //400 Bad Request
\OCP\Util::writeLog('core-preview', 'x and/or y set to 0', \OCP\Util::DEBUG);
exit;
}
try{
$preview = new \OC\Preview(\OC_User::getUser(), 'files_trashbin/files', $file);
$view = new \OC\Files\View('/'.\OC_User::getUser(). '/files_trashbin/files');
if ($view->is_dir($file)) {
$mimetype = 'httpd/unix-directory';
} else {
$pathInfo = pathinfo(ltrim($file, '/'));
$fileName = $pathInfo['basename'];
// if in root dir
if ($pathInfo['dirname'] === '.') {
// cut off the .d* suffix
$i = strrpos($fileName, '.');
if ($i !== false) {
$fileName = substr($fileName, 0, $i);
}
}
$mimetype = \OC::$server->getMimeTypeDetector()->detectPath($fileName);
}
$preview->setMimetype($mimetype);
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
$preview->setScalingUp($scalingUp);
$preview->showPreview();
} catch (\OC\PreviewNotAvailableException $e) {
\OC_Response::setStatus(404);
}catch(\Exception $e) {
\OC_Response::setStatus(500);
\OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::DEBUG);
}

View file

@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Roeland Jago Douma <roeland@famdouma.nl>
@ -25,9 +26,16 @@
namespace OCA\Files_Trashbin\AppInfo;
$application = new Application();
$application->registerRoutes($this, [
'routes' => [
[
'name' => 'Preview#getPreview',
'url' => '/ajax/preview.php',
'verb' => 'GET',
],
],
]);
$this->create('core_ajax_trashbin_preview', 'ajax/preview.php')
->actionInclude('files_trashbin/ajax/preview.php');
$this->create('files_trashbin_ajax_delete', 'ajax/delete.php')
->actionInclude('files_trashbin/ajax/delete.php');
$this->create('files_trashbin_ajax_isEmpty', 'ajax/isEmpty.php')

View file

@ -0,0 +1,119 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Trashbin\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
class PreviewController extends Controller {
/** @var IRootFolder */
private $rootFolder;
/** @var string */
private $userId;
/** @var IMimeTypeDetector */
private $mimeTypeDetector;
/** @var IPreview */
private $previewManager;
/**
* @param string $appName
* @param IRequest $request
* @param IRootFolder $rootFolder
* @param $userId
* @param IMimeTypeDetector $mimeTypeDetector
* @param IPreview $previewManager
*/
public function __construct($appName,
IRequest $request,
IRootFolder $rootFolder,
$userId,
IMimeTypeDetector $mimeTypeDetector,
IPreview $previewManager) {
parent::__construct($appName, $request);
$this->rootFolder = $rootFolder;
$this->userId = $userId;
$this->mimeTypeDetector = $mimeTypeDetector;
$this->previewManager = $previewManager;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $file
* @param int $x
* @param int $y
* @return DataResponse|Http\FileDisplayResponse
*/
public function getPreview(
$file = '',
$x = 44,
$y = 44
) {
if ($file === '') {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
if ($x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
/** @var Folder $trash */
$trash = $userFolder->getParent()->get('files_trashbin/files');
$trashFile = $trash->get($file);
if ($trashFile instanceof Folder) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
/** @var File $trashFile */
$fileName = $trashFile->getName();
$i = strrpos($fileName, '.');
if ($i !== false) {
$fileName = substr($fileName, 0, $i);
}
$mimeType = $this->mimeTypeDetector->detectPath($fileName);
$f = $this->previewManager->getPreview($trashFile, $x, $y, true, IPreview::MODE_FILL, $mimeType);
return new Http\FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
}
}

View file

@ -0,0 +1,181 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Trashbin\Tests\Controller;
use OCA\Files_Trashbin\Controller\PreviewController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
use Test\TestCase;
class PreviewControllerTest extends TestCase {
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
private $rootFolder;
/** @var string */
private $userId;
/** @var IMimeTypeDetector|\PHPUnit_Framework_MockObject_MockObject */
private $mimeTypeDetector;
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
/** @var PreviewController */
private $controller;
public function setUp() {
parent::setUp();
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->userId = 'user';
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
$this->previewManager = $this->createMock(IPreview::class);
$this->controller = new PreviewController(
'files_versions',
$this->createMock(IRequest::class),
$this->rootFolder,
$this->userId,
$this->mimeTypeDetector,
$this->previewManager
);
}
public function testInvalidFile() {
$res = $this->controller->getPreview('');
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidWidth() {
$res = $this->controller->getPreview('file', 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidHeight() {
$res = $this->controller->getPreview('file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testValidPreview() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
$trash = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
$userRoot->method('get')
->with('files_trashbin/files')
->willReturn($trash);
$this->mimeTypeDetector->method('detectPath')
->with($this->equalTo('file'))
->willReturn('myMime');
$file = $this->createMock(File::class);
$trash->method('get')
->with($this->equalTo('file.1234'))
->willReturn($file);
$file->method('getName')
->willReturn('file.1234');
$preview = $this->createMock(ISimpleFile::class);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, true, IPreview::MODE_FILL, 'myMime')
->willReturn($preview);
$preview->method('getMimeType')
->willReturn('previewMime');
$res = $this->controller->getPreview('file.1234', 10, 10);
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'previewMime']);
$this->assertEquals($expected, $res);
}
public function testTrashFileNotFound() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
$trash = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
$userRoot->method('get')
->with('files_trashbin/files')
->willReturn($trash);
$trash->method('get')
->with($this->equalTo('file.1234'))
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file.1234', 10, 10);
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testTrashFolder() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
$trash = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
$userRoot->method('get')
->with('files_trashbin/files')
->willReturn($trash);
$folder = $this->createMock(Folder::class);
$trash->method('get')
->with($this->equalTo('folder'))
->willReturn($folder);
$res = $this->controller->getPreview('folder', 10, 10);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
}

View file

@ -1,66 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Björn Schießle <bjoern@schiessle.org>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
\OC_Util::checkLoggedIn();
if(!\OC_App::isEnabled('files_versions')){
exit;
}
$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : '';
$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : 44;
$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : 44;
$version = array_key_exists('version', $_GET) ? $_GET['version'] : '';
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true;
if($file === '' && $version === '') {
\OC_Response::setStatus(400); //400 Bad Request
\OCP\Util::writeLog('versions-preview', 'No file parameter was passed', \OCP\Util::DEBUG);
exit;
}
if($maxX === 0 || $maxY === 0) {
\OC_Response::setStatus(400); //400 Bad Request
\OCP\Util::writeLog('versions-preview', 'x and/or y set to 0', \OCP\Util::DEBUG);
exit;
}
try {
list($user, $file) = \OCA\Files_Versions\Storage::getUidAndFilename($file);
$preview = new \OC\Preview($user, 'files_versions', $file.'.v'.$version);
$mimetype = \OC::$server->getMimeTypeDetector()->detectPath($file);
$preview->setMimetype($mimetype);
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
$preview->setScalingUp($scalingUp);
$preview->showPreview();
} catch (\OCP\Files\NotFoundException $e) {
\OC_Response::setStatus(404);
\OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::DEBUG);
} catch (\Exception $e) {
\OC_Response::setStatus(500);
\OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::DEBUG);
}

View file

@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Björn Schießle <bjoern@schiessle.org>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
@ -29,13 +30,17 @@
namespace OCA\Files_Versions\AppInfo;
$application = new Application();
$application->registerRoutes($this, [
'routes' => [
[
'name' => 'Preview#getPreview',
'url' => '/preview',
'verb' => 'GET',
],
],
]);
/** @var $this \OCP\Route\IRouter */
$this->create('core_ajax_versions_preview', '/preview')->action(
function() {
require_once __DIR__ . '/../ajax/preview.php';
});
$this->create('files_versions_download', 'download.php')
->actionInclude('files_versions/download.php');
$this->create('files_versions_ajax_getVersions', 'ajax/getVersions.php')

View file

@ -0,0 +1,99 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
class PreviewController extends Controller {
/** @var IRootFolder */
private $rootFolder;
/** @var string */
private $userId;
/** @var IMimeTypeDetector */
private $mimeTypeDetector;
/** @var IPreview */
private $previewManager;
public function __construct($appName,
IRequest $request,
IRootFolder $rootFolder,
$userId,
IMimeTypeDetector $mimeTypeDetector,
IPreview $previewManager) {
parent::__construct($appName, $request);
$this->rootFolder = $rootFolder;
$this->userId = $userId;
$this->mimeTypeDetector = $mimeTypeDetector;
$this->previewManager = $previewManager;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $file
* @param int $x
* @param int $y
* @param string $version
* @return DataResponse|FileDisplayResponse
*/
public function getPreview(
$file = '',
$x = 44,
$y = 44,
$version = ''
) {
if($file === '' || $version === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
/** @var Folder $versionFolder */
$versionFolder = $userFolder->getParent()->get('files_versions');
$mimeType = $this->mimeTypeDetector->detectPath($file);
$file = $versionFolder->get($file.'.v'.$version);
/** @var File $file */
$f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
}
}

View file

@ -462,7 +462,7 @@ class Storage {
if (empty($userFullPath)) {
$versions[$key]['preview'] = '';
} else {
$versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $timestamp));
$versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
}
$versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
$versions[$key]['name'] = $versionedFile;

View file

@ -0,0 +1,167 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Tests\Controller;
use OCA\Files_Versions\Controller\PreviewController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
use Test\TestCase;
class PreviewControllerTest extends TestCase {
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
private $rootFolder;
/** @var string */
private $userId;
/** @var IMimeTypeDetector|\PHPUnit_Framework_MockObject_MockObject */
private $mimeTypeDetector;
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
/** @var PreviewController */
private $controller;
public function setUp() {
parent::setUp();
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->userId = 'user';
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
$this->previewManager = $this->createMock(IPreview::class);
$this->controller = new PreviewController(
'files_versions',
$this->createMock(IRequest::class),
$this->rootFolder,
$this->userId,
$this->mimeTypeDetector,
$this->previewManager
);
}
public function testInvalidFile() {
$res = $this->controller->getPreview('');
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidWidth() {
$res = $this->controller->getPreview('file', 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidHeight() {
$res = $this->controller->getPreview('file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidVersion() {
$res = $this->controller->getPreview('file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testValidPreview() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
$versions = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
$userRoot->method('get')
->with('files_versions')
->willReturn($versions);
$this->mimeTypeDetector->method('detectPath')
->with($this->equalTo('file'))
->willReturn('myMime');
$file = $this->createMock(File::class);
$versions->method('get')
->with($this->equalTo('file.v42'))
->willReturn($file);
$preview = $this->createMock(ISimpleFile::class);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, true, IPreview::MODE_FILL, 'myMime')
->willReturn($preview);
$preview->method('getMimeType')
->willReturn('previewMime');
$res = $this->controller->getPreview('file', 10, 10, '42');
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'previewMime']);
$this->assertEquals($expected, $res);
}
public function testVersionNotFound() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
$versions = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
$userRoot->method('get')
->with('files_versions')
->willReturn($versions);
$this->mimeTypeDetector->method('detectPath')
->with($this->equalTo('file'))
->willReturn('myMime');
$file = $this->createMock(File::class);
$versions->method('get')
->with($this->equalTo('file.v42'))
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file', 10, 10, '42');
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
}

View file

@ -0,0 +1,132 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\File;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
class PreviewController extends Controller {
/** @var string */
private $userId;
/** @var IRootFolder */
private $root;
/** @var IPreview */
private $preview;
/** @var ITimeFactory */
private $timeFactory;
/**
* PreviewController constructor.
*
* @param string $appName
* @param IRequest $request
* @param IPreview $preview
* @param IRootFolder $root
* @param string $userId
*/
public function __construct($appName,
IRequest $request,
IPreview $preview,
IRootFolder $root,
$userId,
ITimeFactory $timeFactory
) {
parent::__construct($appName, $request);
$this->preview = $preview;
$this->root = $root;
$this->userId = $userId;
$this->timeFactory = $timeFactory;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $file
* @param int $x
* @param int $y
* @param bool $a
* @param bool $forceIcon
* @param string $mode
* @return DataResponse|Http\FileDisplayResponse
*/
public function getPreview(
$file = '',
$x = 32,
$y = 32,
$a = false,
$forceIcon = true,
$mode = 'fill') {
if ($file === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
$userFolder = $this->root->getUserFolder($this->userId);
$file = $userFolder->get($file);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if (!($file instanceof File) || (!$forceIcon && !$this->preview->isAvailable($file))) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
} else if (!$file->isReadable()) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
try {
$f = $this->preview->getPreview($file, $x, $y, !$a, $mode);
$response = new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
// Let cache this!
$response->addHeader('Pragma', 'public');
// Cache previews for 24H
$response->cacheFor(3600 * 24);
$expires = new \DateTime();
$expires->setTimestamp($this->timeFactory->getTime());
$expires->add(new \DateInterval('P1D'));
$response->addHeader('Expires', $expires->format(\DateTime::RFC2822));
return $response;
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
}
}

View file

@ -1,67 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Georg Ehrke <georg@owncloud.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
\OC_Util::checkLoggedIn();
\OC::$server->getSession()->close();
$file = array_key_exists('file', $_GET) ? (string)$_GET['file'] : '';
$maxX = array_key_exists('x', $_GET) ? (int)$_GET['x'] : '32';
$maxY = array_key_exists('y', $_GET) ? (int)$_GET['y'] : '32';
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool)$_GET['scalingup'] : true;
$keepAspect = array_key_exists('a', $_GET) ? true : false;
$always = array_key_exists('forceIcon', $_GET) ? (bool)$_GET['forceIcon'] : true;
$mode = array_key_exists('mode', $_GET) ? $_GET['mode'] : 'fill';
if ($file === '') {
//400 Bad Request
\OC_Response::setStatus(400);
\OCP\Util::writeLog('core-preview', 'No file parameter was passed', \OCP\Util::DEBUG);
exit;
}
if ($maxX === 0 || $maxY === 0) {
//400 Bad Request
\OC_Response::setStatus(400);
\OCP\Util::writeLog('core-preview', 'x and/or y set to 0', \OCP\Util::DEBUG);
exit;
}
$info = \OC\Files\Filesystem::getFileInfo($file);
if (!$info instanceof OCP\Files\FileInfo || !$always && !\OC::$server->getPreviewManager()->isAvailable($info)) {
\OC_Response::setStatus(404);
} else if (!$info->isReadable()) {
\OC_Response::setStatus(403);
} else {
$preview = new \OC\Preview(\OC_User::getUser(), 'files');
$preview->setFile($file, $info);
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
$preview->setScalingUp($scalingUp);
$preview->setMode($mode);
$preview->setKeepAspect($keepAspect);
$preview->showPreview();
}

View file

@ -52,6 +52,8 @@ $application->registerRoutes($this, [
['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],
['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'],
['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'],
['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
],
'ocs' => [
['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
@ -68,10 +70,6 @@ $application->registerRoutes($this, [
$this->create('search_ajax_search', '/core/search')
->actionInclude('core/search/ajax/search.php');
// Routing
$this->create('core_ajax_preview', '/core/preview')
->actionInclude('core/ajax/preview.php');
$this->create('core_ajax_preview', '/core/preview.png')
->actionInclude('core/ajax/preview.php');
$this->create('core_ajax_update', '/core/ajax/update.php')
->actionInclude('core/ajax/update.php');

View file

@ -411,6 +411,7 @@ return array(
'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php',
'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php',
'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php',
'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php',
'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php',
'OC\\Core\\Controller\\TwoFactorChallengeController' => $baseDir . '/core/Controller/TwoFactorChallengeController.php',
'OC\\Core\\Controller\\UserController' => $baseDir . '/core/Controller/UserController.php',
@ -620,6 +621,8 @@ return array(
'OC\\Preview\\Bitmap' => $baseDir . '/lib/private/Preview/Bitmap.php',
'OC\\Preview\\Font' => $baseDir . '/lib/private/Preview/Font.php',
'OC\\Preview\\GIF' => $baseDir . '/lib/private/Preview/GIF.php',
'OC\\Preview\\Generator' => $baseDir . '/lib/private/Preview/Generator.php',
'OC\\Preview\\GeneratorHelper' => $baseDir . '/lib/private/Preview/GeneratorHelper.php',
'OC\\Preview\\Illustrator' => $baseDir . '/lib/private/Preview/Illustrator.php',
'OC\\Preview\\Image' => $baseDir . '/lib/private/Preview/Image.php',
'OC\\Preview\\JPEG' => $baseDir . '/lib/private/Preview/JPEG.php',
@ -640,6 +643,8 @@ return array(
'OC\\Preview\\StarOffice' => $baseDir . '/lib/private/Preview/StarOffice.php',
'OC\\Preview\\TIFF' => $baseDir . '/lib/private/Preview/TIFF.php',
'OC\\Preview\\TXT' => $baseDir . '/lib/private/Preview/TXT.php',
'OC\\Preview\\Watcher' => $baseDir . '/lib/private/Preview/Watcher.php',
'OC\\Preview\\WatcherConnector' => $baseDir . '/lib/private/Preview/WatcherConnector.php',
'OC\\Preview\\XBitmap' => $baseDir . '/lib/private/Preview/XBitmap.php',
'OC\\RedisFactory' => $baseDir . '/lib/private/RedisFactory.php',
'OC\\Repair' => $baseDir . '/lib/private/Repair.php',

View file

@ -441,6 +441,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php',
'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php',
'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php',
'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php',
'OC\\Core\\Controller\\SetupController' => __DIR__ . '/../../..' . '/core/Controller/SetupController.php',
'OC\\Core\\Controller\\TwoFactorChallengeController' => __DIR__ . '/../../..' . '/core/Controller/TwoFactorChallengeController.php',
'OC\\Core\\Controller\\UserController' => __DIR__ . '/../../..' . '/core/Controller/UserController.php',
@ -650,6 +651,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Preview\\Bitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/Bitmap.php',
'OC\\Preview\\Font' => __DIR__ . '/../../..' . '/lib/private/Preview/Font.php',
'OC\\Preview\\GIF' => __DIR__ . '/../../..' . '/lib/private/Preview/GIF.php',
'OC\\Preview\\Generator' => __DIR__ . '/../../..' . '/lib/private/Preview/Generator.php',
'OC\\Preview\\GeneratorHelper' => __DIR__ . '/../../..' . '/lib/private/Preview/GeneratorHelper.php',
'OC\\Preview\\Illustrator' => __DIR__ . '/../../..' . '/lib/private/Preview/Illustrator.php',
'OC\\Preview\\Image' => __DIR__ . '/../../..' . '/lib/private/Preview/Image.php',
'OC\\Preview\\JPEG' => __DIR__ . '/../../..' . '/lib/private/Preview/JPEG.php',
@ -670,6 +673,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Preview\\StarOffice' => __DIR__ . '/../../..' . '/lib/private/Preview/StarOffice.php',
'OC\\Preview\\TIFF' => __DIR__ . '/../../..' . '/lib/private/Preview/TIFF.php',
'OC\\Preview\\TXT' => __DIR__ . '/../../..' . '/lib/private/Preview/TXT.php',
'OC\\Preview\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Preview/Watcher.php',
'OC\\Preview\\WatcherConnector' => __DIR__ . '/../../..' . '/lib/private/Preview/WatcherConnector.php',
'OC\\Preview\\XBitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/XBitmap.php',
'OC\\RedisFactory' => __DIR__ . '/../../..' . '/lib/private/RedisFactory.php',
'OC\\Repair' => __DIR__ . '/../../..' . '/lib/private/Repair.php',

View file

@ -125,7 +125,7 @@ class Preview {
$sysConfig = \OC::$server->getConfig();
$this->configMaxWidth = $sysConfig->getSystemValue('preview_max_x', 2048);
$this->configMaxHeight = $sysConfig->getSystemValue('preview_max_y', 2048);
$this->maxScaleFactor = $sysConfig->getSystemValue('preview_max_scale_factor', 2);
$this->maxScaleFactor = $sysConfig->getSystemValue('preview_max_scale_factor', 1);
//save parameters
$this->setFile($file);

View file

@ -0,0 +1,338 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\IImage;
use OCP\IPreview;
use OCP\Preview\IProvider;
class Generator {
/** @var IPreview */
private $previewManager;
/** @var IConfig */
private $config;
/** @var IAppData */
private $appData;
/** @var GeneratorHelper */
private $helper;
/**
* @param IConfig $config
* @param IPreview $previewManager
* @param IAppData $appData
* @param GeneratorHelper $helper
*/
public function __construct(
IConfig $config,
IPreview $previewManager,
IAppData $appData,
GeneratorHelper $helper
) {
$this->config = $config;
$this->previewManager = $previewManager;
$this->appData = $appData;
$this->helper = $helper;
}
/**
* Returns a preview of a file
*
* The cache is searched first and if nothing usable was found then a preview is
* generated by one of the providers
*
* @param File $file
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param string $mimeType
* @return ISimpleFile
* @throws NotFoundException
*/
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
if ($mimeType === null) {
$mimeType = $file->getMimeType();
}
if (!$this->previewManager->isMimeSupported($mimeType)) {
throw new NotFoundException();
}
$previewFolder = $this->getPreviewFolder($file);
// Get the max preview and infer the max preview sizes from that
$maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType);
list($maxWidth, $maxHeight) = $this->getPreviewSize($maxPreview);
// Calculate the preview size
list($width, $height) = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight);
// Try to get a cached preview. Else generate (and store) one
try {
$file = $this->getCachedPreview($previewFolder, $width, $height, $crop);
} catch (NotFoundException $e) {
$file = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight);
}
return $file;
}
/**
* @param ISimpleFolder $previewFolder
* @param File $file
* @param string $mimeType
* @return ISimpleFile
* @throws NotFoundException
*/
private function getMaxPreview(ISimpleFolder $previewFolder, File $file, $mimeType) {
$nodes = $previewFolder->getDirectoryListing();
foreach ($nodes as $node) {
if (strpos($node->getName(), 'max')) {
return $node;
}
}
$previewProviders = $this->previewManager->getProviders();
foreach ($previewProviders as $supportedMimeType => $providers) {
if (!preg_match($supportedMimeType, $mimeType)) {
continue;
}
foreach ($providers as $provider) {
$provider = $this->helper->getProvider($provider);
if (!($provider instanceof IProvider)) {
continue;
}
$maxWidth = (int)$this->config->getSystemValue('preview_max_x', 2048);
$maxHeight = (int)$this->config->getSystemValue('preview_max_y', 2048);
$preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight);
if (!($preview instanceof IImage)) {
continue;
}
$path = strval($preview->width()) . '-' . strval($preview->height()) . '-max.png';
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
return $file;
}
}
throw new NotFoundException();
}
/**
* @param ISimpleFile $file
* @return int[]
*/
private function getPreviewSize(ISimpleFile $file) {
$size = explode('-', $file->getName());
return [(int)$size[0], (int)$size[1]];
}
/**
* @param int $width
* @param int $height
* @param bool $crop
* @return string
*/
private function generatePath($width, $height, $crop) {
$path = strval($width) . '-' . strval($height);
if ($crop) {
$path .= '-crop';
}
$path .= '.png';
return $path;
}
/**
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param int $maxWidth
* @param int $maxHeight
* @return int[]
*/
private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight) {
/*
* If we are not cropping we have to make sure the requested image
* respects the aspect ratio of the original.
*/
if (!$crop) {
$ratio = $maxHeight / $maxWidth;
if ($width === -1) {
$width = $height / $ratio;
}
if ($height === -1) {
$height = $width * $ratio;
}
$ratioH = $height / $maxHeight;
$ratioW = $width / $maxWidth;
/*
* Fill means that the $height and $width are the max
* Cover means min.
*/
if ($mode === IPreview::MODE_FILL) {
if ($ratioH > $ratioW) {
$height = $width * $ratio;
} else {
$width = $height / $ratio;
}
} else if ($mode === IPreview::MODE_COVER) {
if ($ratioH > $ratioW) {
$width = $height / $ratio;
} else {
$height = $width * $ratio;
}
}
}
if ($height !== $maxHeight && $width !== $maxWidth) {
/*
* Scale to the nearest power of two
*/
$pow2height = pow(2, ceil(log($height) / log(2)));
$pow2width = pow(2, ceil(log($width) / log(2)));
$ratioH = $height / $pow2height;
$ratioW = $width / $pow2width;
if ($ratioH < $ratioW) {
$width = $pow2width;
$height = $height / $ratioW;
} else {
$height = $pow2height;
$width = $width / $ratioH;
}
}
/*
* Make sure the requested height and width fall within the max
* of the preview.
*/
if ($height > $maxHeight) {
$ratio = $height / $maxHeight;
$height = $maxHeight;
$width = $width / $ratio;
}
if ($width > $maxWidth) {
$ratio = $width / $maxWidth;
$width = $maxWidth;
$height = $height / $ratio;
}
return [(int)round($width), (int)round($height)];
}
/**
* @param ISimpleFolder $previewFolder
* @param ISimpleFile $maxPreview
* @param int $width
* @param int $height
* @param bool $crop
* @param int $maxWidth
* @param int $maxHeight
* @return ISimpleFile
* @throws NotFoundException
*/
private function generatePreview(ISimpleFolder $previewFolder, ISimpleFile $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight) {
$preview = $this->helper->getImage($maxPreview);
if ($crop) {
if ($height !== $preview->height() && $width !== $preview->width()) {
//Resize
$widthR = $preview->width() / $width;
$heightR = $preview->height() / $height;
if ($widthR > $heightR) {
$scaleH = $height;
$scaleW = $maxWidth / $heightR;
} else {
$scaleH = $maxHeight / $widthR;
$scaleW = $width;
}
$preview->preciseResize(round($scaleW), round($scaleH));
}
$cropX = floor(abs($width - $preview->width()) * 0.5);
$cropY = 0;
$preview->crop($cropX, $cropY, $width, $height);
} else {
$preview->resize(max($width, $height));
}
$path = $this->generatePath($width, $height, $crop);
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
return $file;
}
/**
* @param ISimpleFolder $previewFolder
* @param int $width
* @param int $height
* @param bool $crop
* @return ISimpleFile
*
* @throws NotFoundException
*/
private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop) {
$path = $this->generatePath($width, $height, $crop);
return $previewFolder->getFile($path);
}
/**
* Get the specific preview folder for this file
*
* @param File $file
* @return ISimpleFolder
*/
private function getPreviewFolder(File $file) {
try {
$folder = $this->appData->getFolder($file->getId());
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($file->getId());
}
return $folder;
}
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\Files\View;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IImage;
use OCP\Image as img;
use OCP\Preview\IProvider;
/**
* Very small wrapper class to make the generator fully unit testable
*/
class GeneratorHelper {
/** @var IRootFolder */
private $rootFolder;
public function __construct(IRootFolder $rootFolder) {
$this->rootFolder = $rootFolder;
}
/**
* @param IProvider $provider
* @param File $file
* @param int $maxWidth
* @param int $maxHeight
* @return bool|IImage
*/
public function getThumbnail(IProvider $provider, File $file, $maxWidth, $maxHeight) {
list($view, $path) = $this->getViewAndPath($file);
return $provider->getThumbnail($path, $maxWidth, $maxHeight, false, $view);
}
/**
* @param File $file
* @return array
* This is required to create the old view and path
*/
private function getViewAndPath(File $file) {
$owner = $file->getOwner()->getUID();
$userFolder = $this->rootFolder->getUserFolder($owner)->getParent();
$nodes = $userFolder->getById($file->getId());
$file = $nodes[0];
$view = new View($userFolder->getPath());
$path = $userFolder->getRelativePath($file->getPath());
return [$view, $path];
}
/**
* @param ISimpleFile $maxPreview
* @return IImage
*/
public function getImage(ISimpleFile $maxPreview) {
return new img($maxPreview->getContent());
}
/**
* @param $provider
* @return IProvider
*/
public function getProvider($provider) {
return $provider();
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\Node;
use OCP\Files\Folder;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
/**
* Class Watcher
*
* @package OC\Preview
*
* Class that will watch filesystem activity and remove previews as needed.
*/
class Watcher {
/** @var IAppData */
private $appData;
/** @var int[] */
private $toDelete = [];
/**
* Watcher constructor.
*
* @param IAppData $appData
*/
public function __construct(IAppData $appData) {
$this->appData = $appData;
}
public function postWrite(Node $node) {
// We only handle files
if ($node instanceof Folder) {
return;
}
try {
$folder = $this->appData->getFolder($node->getId());
$folder->delete();
} catch (NotFoundException $e) {
//Nothing to do
}
}
public function preDelete(Node $node) {
// To avoid cycles
if ($this->toDelete !== []) {
return;
}
if ($node instanceof File) {
$this->toDelete[] = $node->getId();
return;
}
/** @var Folder $node */
$nodes = $node->search('');
foreach ($nodes as $node) {
if ($node instanceof File) {
$this->toDelete[] = $node->getId();
}
}
}
public function postDelete(Node $node) {
foreach ($this->toDelete as $fid) {
try {
$folder = $this->appData->getFolder($fid);
$folder->delete();
} catch (NotFoundException $e) {
// continue
}
}
}
}

View file

@ -0,0 +1,72 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\SystemConfig;
use OCP\Files\Node;
use OCP\Files\IRootFolder;
class WatcherConnector {
/** @var IRootFolder */
private $root;
/** @var SystemConfig */
private $config;
/**
* WatcherConnector constructor.
*
* @param IRootFolder $root
* @param SystemConfig $config
*/
public function __construct(IRootFolder $root,
SystemConfig $config) {
$this->root = $root;
$this->config = $config;
}
/**
* @return Watcher
*/
private function getWatcher() {
return \OC::$server->query(Watcher::class);
}
public function connectWatcher() {
// Do not connect if we are not setup yet!
if ($this->config->getValue('instanceid', null) !== null) {
$this->root->listen('\OC\Files', 'postWrite', function (Node $node) {
$this->getWatcher()->postWrite($node);
});
$this->root->listen('\OC\Files', 'preDelete', function (Node $node) {
$this->getWatcher()->preDelete($node);
});
$this->root->listen('\OC\Files', 'postDelete', function (Node $node) {
$this->getWatcher()->postDelete($node);
});
}
}
}

View file

@ -25,13 +25,30 @@
*/
namespace OC;
use OC\Preview\Generator;
use OC\Preview\GeneratorHelper;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IConfig;
use OCP\IPreview;
use OCP\Preview\IProvider;
class PreviewManager implements IPreview {
/** @var \OCP\IConfig */
/** @var IConfig */
protected $config;
/** @var IRootFolder */
protected $rootFolder;
/** @var IAppData */
protected $appData;
/** @var Generator */
private $generator;
/** @var bool */
protected $providerListDirty = false;
@ -52,8 +69,12 @@ class PreviewManager implements IPreview {
*
* @param \OCP\IConfig $config
*/
public function __construct(\OCP\IConfig $config) {
public function __construct(IConfig $config,
IRootFolder $rootFolder,
IAppData $appData) {
$this->config = $config;
$this->rootFolder = $rootFolder;
$this->appData = $appData;
}
/**
@ -120,6 +141,37 @@ class PreviewManager implements IPreview {
return $preview->getPreview();
}
/**
* Returns a preview of a file
*
* The cache is searched first and if nothing usable was found then a preview is
* generated by one of the providers
*
* @param File $file
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param string $mimeType
* @return ISimpleFile
* @throws NotFoundException
* @since 9.2.0
*/
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
if ($this->generator === null) {
$this->generator = new Generator(
$this->config,
$this,
$this->appData,
new GeneratorHelper(
$this->rootFolder
)
);
}
return $this->generator->getPreview($file, $width, $height, $crop, $mode, $mimeType);
}
/**
* returns true if the passed mime type is supported
*

View file

@ -117,7 +117,17 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerService('PreviewManager', function (Server $c) {
return new PreviewManager($c->getConfig());
return new PreviewManager(
$c->getConfig(),
$c->getRootFolder(),
$c->getAppDataDir('preview')
);
});
$this->registerService(\OC\Preview\Watcher::class, function (Server $c) {
return new \OC\Preview\Watcher(
$c->getAppDataDir('preview')
);
});
$this->registerService('EncryptionManager', function (Server $c) {
@ -192,6 +202,10 @@ class Server extends ServerContainer implements IServerContainer {
);
$connector = new HookConnector($root, $view);
$connector->viewToNode();
$previewConnector = new \OC\Preview\WatcherConnector($root, $c->getSystemConfig());
$previewConnector->connectWatcher();
return $root;
});
$this->registerService('LazyRootFolder', function(Server $c) {

View file

@ -33,11 +33,19 @@
// This means that they should be used by apps instead of the internal ownCloud classes
namespace OCP;
use OCP\Files\File;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\NotFoundException;
/**
* This class provides functions to render and show thumbnails and previews of files
* @since 6.0.0
*/
interface IPreview {
const MODE_FILL = 'fill';
const MODE_COVER = 'cover';
/**
* In order to improve lazy loading a closure can be registered which will be
* called in case preview providers are actually requested
@ -73,9 +81,27 @@ interface IPreview {
* @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly
* @return \OCP\IImage
* @since 6.0.0
* @deprecated 9.2.0 Use getPreview
*/
public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false);
/**
* Returns a preview of a file
*
* The cache is searched first and if nothing usable was found then a preview is
* generated by one of the providers
*
* @param File $file
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param string $mimeType To force a given mimetype for the file (files_versions needs this)
* @return ISimpleFile
* @throws NotFoundException
* @since 9.2.0
*/
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null);
/**
* Returns true if the passed mime type is supported

View file

@ -0,0 +1,226 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Tests\Core\Controller;
use OC\Core\Controller\PreviewController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
class PreviewControllerTest extends \Test\TestCase {
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
private $rootFolder;
/** @var string */
private $userId;
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
/** @var PreviewController|\PHPUnit_Framework_MockObject_MockObject */
private $controller;
public function setUp() {
parent::setUp();
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->userId = 'user';
$this->previewManager = $this->createMock(IPreview::class);
$this->controller = new PreviewController(
'core',
$this->createMock(IRequest::class),
$this->previewManager,
$this->rootFolder,
$this->userId,
$this->createMock(ITimeFactory::class)
);
}
public function testInvalidFile() {
$res = $this->controller->getPreview('');
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidWidth() {
$res = $this->controller->getPreview('file', 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidHeight() {
$res = $this->controller->getPreview('file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testFileNotFound() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$userFolder->method('get')
->with($this->equalTo('file'))
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file');
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testNotAFile() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$folder = $this->createMock(Folder::class);
$userFolder->method('get')
->with($this->equalTo('file'))
->willReturn($folder);
$res = $this->controller->getPreview('file');
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testNoPreviewAndNoIcon() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$file = $this->createMock(File::class);
$userFolder->method('get')
->with($this->equalTo('file'))
->willReturn($file);
$this->previewManager->method('isAvailable')
->with($this->equalTo($file))
->willReturn(false);
$res = $this->controller->getPreview('file', 10, 10, true, false);
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testForbiddenFile() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$file = $this->createMock(File::class);
$userFolder->method('get')
->with($this->equalTo('file'))
->willReturn($file);
$this->previewManager->method('isAvailable')
->with($this->equalTo($file))
->willReturn(true);
$file->method('isReadable')
->willReturn(false);
$res = $this->controller->getPreview('file', 10, 10, true, true);
$expected = new DataResponse([], Http::STATUS_FORBIDDEN);
$this->assertEquals($expected, $res);
}
public function testNoPreview() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$file = $this->createMock(File::class);
$userFolder->method('get')
->with($this->equalTo('file'))
->willReturn($file);
$this->previewManager->method('isAvailable')
->with($this->equalTo($file))
->willReturn(true);
$file->method('isReadable')
->willReturn(true);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
public function testValidPreview() {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->equalTo($this->userId))
->willReturn($userFolder);
$file = $this->createMock(File::class);
$userFolder->method('get')
->with($this->equalTo('file'))
->willReturn($file);
$this->previewManager->method('isAvailable')
->with($this->equalTo($file))
->willReturn(true);
$file->method('isReadable')
->willReturn(true);
$preview = $this->createMock(ISimpleFile::class);
$this->previewManager->method('getPreview')
->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
->willReturn($preview);
$preview->method('getMimeType')
->willReturn('myMime');
$res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
$this->assertEquals('myMime', $res->getHeaders()['Content-Type']);
$this->assertEquals(Http::STATUS_OK, $res->getStatus());
$this->assertEquals($preview, $this->invokePrivate($res, 'file'));
}
}

View file

@ -0,0 +1,338 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Preview;
use OC\Preview\Generator;
use OC\Preview\GeneratorHelper;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\IImage;
use OCP\IPreview;
use OCP\Preview\IProvider;
class GeneratorTest extends \Test\TestCase {
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
private $config;
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
/** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */
private $appData;
/** @var GeneratorHelper|\PHPUnit_Framework_MockObject_MockObject */
private $helper;
/** @var Generator */
private $generator;
public function setUp() {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->previewManager = $this->createMock(IPreview::class);
$this->appData = $this->createMock(IAppData::class);
$this->helper = $this->createMock(GeneratorHelper::class);
$this->generator = new Generator(
$this->config,
$this->previewManager,
$this->appData,
$this->helper
);
}
public function testGetCachedPreview() {
$file = $this->createMock(File::class);
$file->method('getMimeType')
->willReturn('myMimeType');
$file->method('getId')
->willReturn(42);
$this->previewManager->method('isMimeSupported')
->with($this->equalTo('myMimeType'))
->willReturn(true);
$previewFolder = $this->createMock(ISimpleFolder::class);
$this->appData->method('getFolder')
->with($this->equalTo(42))
->willReturn($previewFolder);
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')
->willReturn('1000-1000-max.png');
$previewFolder->method('getDirectoryListing')
->willReturn([$maxPreview]);
$previewFile = $this->createMock(ISimpleFile::class);
$previewFolder->method('getFile')
->with($this->equalTo('128-128.png'))
->willReturn($previewFile);
$result = $this->generator->getPreview($file, 100, 100);
$this->assertSame($previewFile, $result);
}
public function testGetNewPreview() {
$file = $this->createMock(File::class);
$file->method('getMimeType')
->willReturn('myMimeType');
$file->method('getId')
->willReturn(42);
$this->previewManager->method('isMimeSupported')
->with($this->equalTo('myMimeType'))
->willReturn(true);
$previewFolder = $this->createMock(ISimpleFolder::class);
$this->appData->method('getFolder')
->with($this->equalTo(42))
->willThrowException(new NotFoundException());
$this->appData->method('newFolder')
->with($this->equalTo(42))
->willReturn($previewFolder);
$this->config->method('getSystemValue')
->will($this->returnCallback(function($key, $defult) {
return $defult;
}));
$invalidProvider = $this->createMock(IProvider::class);
$validProvider = $this->createMock(IProvider::class);
$this->previewManager->method('getProviders')
->willReturn([
'/image\/png/' => ['wrongProvider'],
'/myMimeType/' => ['brokenProvider', 'invalidProvider', 'validProvider'],
]);
$this->helper->method('getProvider')
->will($this->returnCallback(function($provider) use ($invalidProvider, $validProvider) {
if ($provider === 'wrongProvider') {
$this->fail('Wrongprovider should not be constructed!');
} else if ($provider === 'brokenProvider') {
return false;
} else if ($provider === 'invalidProvider') {
return $invalidProvider;
} else if ($provider === 'validProvider') {
return $validProvider;
}
$this->fail('Unexpected provider requested');
}));
$image = $this->createMock(IImage::class);
$image->method('width')->willReturn(2048);
$image->method('height')->willReturn(2048);
$this->helper->method('getThumbnail')
->will($this->returnCallback(function ($provider, $file, $x, $y) use ($invalidProvider, $validProvider, $image) {
if ($provider === $validProvider) {
return $image;
} else {
return false;
}
}));
$image->method('data')
->willReturn('my data');
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')->willReturn('2048-2048-max.png');
$previewFile = $this->createMock(ISimpleFile::class);
$previewFolder->method('getDirectoryListing')
->willReturn([]);
$previewFolder->method('newFile')
->will($this->returnCallback(function($filename) use ($maxPreview, $previewFile) {
if ($filename === '2048-2048-max.png') {
return $maxPreview;
} else if ($filename === '128-128.png') {
return $previewFile;
}
$this->fail('Unexpected file');
}));
$maxPreview->expects($this->once())
->method('putContent')
->with($this->equalTo('my data'));
$previewFolder->method('getFile')
->with($this->equalTo('128-128.png'))
->willThrowException(new NotFoundException());
$image = $this->createMock(IImage::class);
$this->helper->method('getImage')
->with($this->equalTo($maxPreview))
->willReturn($image);
$image->expects($this->once())
->method('resize')
->with(128);
$image->method('data')
->willReturn('my resized data');
$previewFile->expects($this->once())
->method('putContent')
->with('my resized data');
$result = $this->generator->getPreview($file, 100, 100);
$this->assertSame($previewFile, $result);
}
public function testInvalidMimeType() {
$this->expectException(NotFoundException::class);
$file = $this->createMock(File::class);
$this->previewManager->method('isMimeSupported')
->with('invalidType')
->willReturn(false);
$this->generator->getPreview($file, 0, 0, true, IPreview::MODE_COVER, 'invalidType');
}
public function testNoProvider() {
$file = $this->createMock(File::class);
$file->method('getMimeType')
->willReturn('myMimeType');
$file->method('getId')
->willReturn(42);
$this->previewManager->method('isMimeSupported')
->with($this->equalTo('myMimeType'))
->willReturn(true);
$previewFolder = $this->createMock(ISimpleFolder::class);
$this->appData->method('getFolder')
->with($this->equalTo(42))
->willReturn($previewFolder);
$previewFolder->method('getDirectoryListing')
->willReturn([]);
$this->previewManager->method('getProviders')
->willReturn([]);
$this->expectException(NotFoundException::class);
$this->generator->getPreview($file, 100, 100);
}
public function dataSize() {
return [
[1024, 2048, 512, 512, false, IPreview::MODE_FILL, 256, 512],
[1024, 2048, 512, 512, false, IPreview::MODE_COVER, 512, 1024],
[1024, 2048, 512, 512, true, IPreview::MODE_FILL, 512, 512],
[1024, 2048, 512, 512, true, IPreview::MODE_COVER, 512, 512],
[1024, 2048, -1, 512, false, IPreview::MODE_COVER, 256, 512],
[1024, 2048, 512, -1, false, IPreview::MODE_FILL, 512, 1024],
[1024, 2048, 250, 1100, true, IPreview::MODE_COVER, 256, 1126],
[1024, 1100, 250, 1100, true, IPreview::MODE_COVER, 250, 1100],
[1024, 2048, 4096, 2048, false, IPreview::MODE_FILL, 1024, 2048],
[1024, 2048, 4096, 2048, false, IPreview::MODE_COVER, 1024, 2048],
[2048, 1024, 512, 512, false, IPreview::MODE_FILL, 512, 256],
[2048, 1024, 512, 512, false, IPreview::MODE_COVER, 1024, 512],
[2048, 1024, 512, 512, true, IPreview::MODE_FILL, 512, 512],
[2048, 1024, 512, 512, true, IPreview::MODE_COVER, 512, 512],
[2048, 1024, -1, 512, false, IPreview::MODE_FILL, 1024, 512],
[2048, 1024, 512, -1, false, IPreview::MODE_COVER, 512, 256],
[2048, 1024, 4096, 1024, true, IPreview::MODE_FILL, 2048, 512],
[2048, 1024, 4096, 1024, true, IPreview::MODE_COVER, 2048, 512],
];
}
/**
* @dataProvider dataSize
*
* @param int $maxX
* @param int $maxY
* @param int $reqX
* @param int $reqY
* @param bool $crop
* @param string $mode
* @param int $expectedX
* @param int $expectedY
*/
public function testCorrectSize($maxX, $maxY, $reqX, $reqY, $crop, $mode, $expectedX, $expectedY) {
$file = $this->createMock(File::class);
$file->method('getMimeType')
->willReturn('myMimeType');
$file->method('getId')
->willReturn(42);
$this->previewManager->method('isMimeSupported')
->with($this->equalTo('myMimeType'))
->willReturn(true);
$previewFolder = $this->createMock(ISimpleFolder::class);
$this->appData->method('getFolder')
->with($this->equalTo(42))
->willReturn($previewFolder);
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')
->willReturn($maxX . '-' . $maxY . '-max.png');
$previewFolder->method('getDirectoryListing')
->willReturn([$maxPreview]);
$filename = $expectedX . '-' . $expectedY;
if ($crop) {
$filename .= '-crop';
}
$filename .= '.png';
$previewFolder->method('getFile')
->with($this->equalTo($filename))
->willThrowException(new NotFoundException());
$image = $this->createMock(IImage::class);
$this->helper->method('getImage')
->with($this->equalTo($maxPreview))
->willReturn($image);
$image->method('height')->willReturn($maxY);
$image->method('width')->willReturn($maxX);
$preview = $this->createMock(ISimpleFile::class);
$previewFolder->method('newFile')
->with($this->equalTo($filename))
->willReturn($preview);
$result = $this->generator->getPreview($file, $reqX, $reqY, $crop, $mode);
$this->assertSame($preview, $result);
}
}

View file

@ -63,7 +63,7 @@ class UrlGeneratorTest extends \Test\TestCase {
public function provideRoutes() {
return array(
array('files_ajax_list', 'http://localhost/owncloud/index.php/apps/files/ajax/list.php'),
array('core_ajax_preview', 'http://localhost/owncloud/index.php/core/preview.png'),
array('core.Preview.getPreview', 'http://localhost/owncloud/index.php/core/preview.png'),
);
}