Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/gallery.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Paroz <github@oparoz.com>2015-01-18 04:26:24 +0300
committerOlivier Paroz <github@oparoz.com>2015-01-18 04:26:24 +0300
commitee5c0a94cb1571f5675fd9caf8a7ca0a77927d20 (patch)
tree28eec518e6e59d7b438caf327321844869ca9bd7
parentacfa4c237250cbd4c63cd6e75a1ea42574d34ece (diff)
Services and middleware refactoring
* HTTP responses have been moved back to the controllers * The environment service has been split in two * An environment Class has been created to hold the variables the services need to do their job * The tokencheckmiddleware is now the envcheckmiddleware and makes sure the token is valid before sending it to an environment object * Some exceptions have been created in order to bubble up problems * Services have now status codes compatible with HTTP codes * ThumbnailService is now a child of PrevewService
-rw-r--r--appinfo/application.php75
-rw-r--r--controller/jsonhttperror.php16
-rw-r--r--controller/pagecontroller.php18
-rw-r--r--controller/servicecontroller.php126
-rw-r--r--environment/environment.php334
-rw-r--r--environment/environmentexception.php28
-rw-r--r--environment/notfoundenvexception.php26
-rw-r--r--middleware/checkmiddleware.php13
-rw-r--r--middleware/envcheckmiddleware.php316
-rw-r--r--middleware/sharingcheckmiddleware.php8
-rw-r--r--middleware/tokencheckmiddleware.php172
-rw-r--r--preview/preview.php161
-rw-r--r--service/base64encode.php42
-rw-r--r--service/downloadservice.php83
-rw-r--r--service/environmentservice.php385
-rw-r--r--service/infoservice.php112
-rw-r--r--service/notfoundserviceexception.php28
-rw-r--r--service/previewservice.php235
-rw-r--r--service/service.php90
-rw-r--r--service/serviceexception.php9
-rw-r--r--service/thumbnailservice.php111
21 files changed, 1285 insertions, 1103 deletions
diff --git a/appinfo/application.php b/appinfo/application.php
index f1db99bc..00eb7939 100644
--- a/appinfo/application.php
+++ b/appinfo/application.php
@@ -12,7 +12,6 @@
namespace OCA\GalleryPlus\AppInfo;
-
use OCP\IContainer;
use OCP\AppFramework\App;
@@ -21,17 +20,17 @@ use OCP\AppFramework\IAppContainer;
use OCA\GalleryPlus\Controller\PageController;
use OCA\GalleryPlus\Controller\ServiceController;
use OCA\GalleryPlus\Controller\PublicServiceController;
+use OCA\GalleryPlus\Environment\Environment;
use OCA\GalleryPlus\Preview\Preview;
-use OCA\GalleryPlus\Service\EnvironmentService;
use OCA\GalleryPlus\Service\InfoService;
use OCA\GalleryPlus\Service\ThumbnailService;
use OCA\GalleryPlus\Service\PreviewService;
+use OCA\GalleryPlus\Service\DownloadService;
use OCA\GalleryPlus\Middleware\SharingCheckMiddleware;
-use OCA\GalleryPlus\Middleware\TokenCheckMiddleware;
+use OCA\GalleryPlus\Middleware\EnvCheckMiddleware;
use OCA\GalleryPlus\Utility\SmarterLogger;
use OCA\GalleryPlus\Utility\Normalizer;
-
/**
* Class Application
*
@@ -67,10 +66,14 @@ class Application extends App {
return new ServiceController(
$c->query('AppName'),
$c->query('Request'),
+ $c->query('Environment'),
$c->query('InfoService'),
$c->query('ThumbnailService'),
$c->query('PreviewService'),
- $c->query('URLGenerator')
+ $c->query('DownloadService'),
+ $c->query('URLGenerator'),
+ $c->getServer()
+ ->createEventSource()
);
}
);
@@ -79,10 +82,14 @@ class Application extends App {
return new PublicServiceController(
$c->query('AppName'),
$c->query('Request'),
+ $c->query('Environment'),
$c->query('InfoService'),
$c->query('ThumbnailService'),
$c->query('PreviewService'),
- $c->query('URLGenerator')
+ $c->query('DownloadService'),
+ $c->query('URLGenerator'),
+ $c->getServer()
+ ->createEventSource()
);
}
);
@@ -169,6 +176,7 @@ class Application extends App {
'CustomPreviewManager', function (IContainer $c) {
return new Preview(
$c->query('Config'),
+ $c->query('PreviewManager'),
$c->query('SmarterLogger')
);
}
@@ -191,26 +199,19 @@ class Application extends App {
->getConfig();
}
);
-
- /**
- * Services
- */
- // Everything we need to do to set up the environment before processing the request
$container->registerService(
- 'Environment', function (IAppContainer $c) {
- return new EnvironmentService(
+ 'Environment', function (IContainer $c) {
+ return new Environment(
$c->query('AppName'),
$c->query('UserId'),
$c->query('UserFolder'),
$c->query('UserManager'),
$c->getServer(),
- $c->query('Hasher'),
- $c->query('Session'),
$c->query('SmarterLogger')
);
}
);
- /*// The same thing as above, but in OC8, hopefully. See https://github.com/owncloud/core/issues/12676
+ /*// The same thing as above, but in OC9, hopefully. See https://github.com/owncloud/core/issues/12676
$container->registerService(
'Environment', function (IAppContainer $c) {
$token = $c->query('Token');
@@ -220,14 +221,17 @@ class Application extends App {
->getEnvironment($token);
}
);*/
+
+ /**
+ * Services
+ */
$container->registerService(
'InfoService', function (IContainer $c) {
return new InfoService(
$c->query('AppName'),
- $c->query('UserFolder'),
- $c->query('Environment'),
- $c->query('SmarterLogger'),
- $c->query('PreviewManager')
+ $c->query('PreviewService'),
+ $c->query('SmarterLogger')
+
);
}
);
@@ -235,10 +239,10 @@ class Application extends App {
'ThumbnailService', function (IAppContainer $c) {
return new ThumbnailService(
$c->query('AppName'),
- $c->query('SmarterLogger'),
- $c->getServer()
- ->createEventSource(),
- $c->query('PreviewService')
+ $c->query('Environment'),
+ $c->query('CustomPreviewManager'),
+ $c->query('SmarterLogger')
+
);
}
);
@@ -247,8 +251,18 @@ class Application extends App {
return new PreviewService(
$c->query('AppName'),
$c->query('Environment'),
- $c->query('SmarterLogger'),
- $c->query('CustomPreviewManager')
+ $c->query('CustomPreviewManager'),
+ $c->query('SmarterLogger')
+
+ );
+ }
+ );
+ $container->registerService(
+ 'DownloadService', function (IContainer $c) {
+ return new DownloadService(
+ $c->query('AppName'),
+ $c->query('Environment'),
+ $c->query('SmarterLogger')
);
}
);
@@ -270,11 +284,13 @@ class Application extends App {
}
);
$container->registerService(
- 'TokenCheckMiddleware',
+ 'EnvCheckMiddleware',
function (IContainer $c) {
- return new TokenCheckMiddleware(
+ return new EnvCheckMiddleware(
$c->query('AppName'),
$c->query('Request'),
+ $c->query('Hasher'),
+ $c->query('Session'),
$c->query('Environment'),
$c->query('ControllerMethodReflector'),
$c->query('URLGenerator'),
@@ -285,7 +301,8 @@ class Application extends App {
// executed in the order that it is registered
$container->registerMiddleware('SharingCheckMiddleware');
- $container->registerMiddleware('TokenCheckMiddleware');
+ $container->registerMiddleware('EnvCheckMiddleware');
+
}
} \ No newline at end of file
diff --git a/controller/jsonhttperror.php b/controller/jsonhttperror.php
index 23a88938..6f7fe2b9 100644
--- a/controller/jsonhttperror.php
+++ b/controller/jsonhttperror.php
@@ -14,8 +14,14 @@
namespace OCA\GalleryPlus\Controller;
+use Exception;
+
+use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
+use OCA\GalleryPlus\Environment\NotFoundEnvException;
+use OCA\GalleryPlus\Service\NotFoundServiceException;
+
/**
* Our classes extend both Controller and ApiController, so we need to use
* traits to add some common methods
@@ -27,14 +33,14 @@ trait JsonHttpError {
/**
* @param \Exception $exception the message that is returned taken from the exception
*
- * @param int $code the http error code
- *
* @return JSONResponse
*/
- public function error(\Exception $exception, $code = 0) {
+ public function error(Exception $exception) {
$message = $exception->getMessage();
- if ($code === 0) {
- $code = $exception->getCode();
+ $code = Http::STATUS_INTERNAL_SERVER_ERROR;
+
+ if ($exception instanceof NotFoundServiceException || $exception instanceof NotFoundEnvException) {
+ $code = Http::STATUS_NOT_FOUND;
}
return new JSONResponse(
diff --git a/controller/pagecontroller.php b/controller/pagecontroller.php
index 6de271c8..e0d04445 100644
--- a/controller/pagecontroller.php
+++ b/controller/pagecontroller.php
@@ -20,7 +20,7 @@ use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
-use OCA\GalleryPlus\Service\EnvironmentService;
+use OCA\GalleryPlus\Environment\Environment;
/**
* Generates templates for the landing page from within ownCloud, the public
@@ -31,9 +31,9 @@ use OCA\GalleryPlus\Service\EnvironmentService;
class PageController extends Controller {
/**
- * @type EnvironmentService
+ * @type Environment
*/
- private $environmentService;
+ private $environment;
/**
* @type IURLGenerator
*/
@@ -44,18 +44,18 @@ class PageController extends Controller {
*
* @param string $appName
* @param IRequest $request
- * @param EnvironmentService $environmentService
+ * @param Environment $environment
* @param IURLGenerator $urlGenerator
*/
public function __construct(
$appName,
IRequest $request,
- EnvironmentService $environmentService,
+ Environment $environment,
IURLGenerator $urlGenerator
) {
parent::__construct($appName, $request);
- $this->environmentService = $environmentService;
+ $this->environment = $environment;
$this->urlGenerator = $urlGenerator;
}
@@ -94,10 +94,8 @@ class PageController extends Controller {
public function publicIndex() {
$appName = $this->appName;
$token = $this->request->getParam('token');
-
- $env = $this->environmentService->getEnv();
- $displayName = $env['originalOwnerDisplayName'];
- $albumName = $env['albumName'];
+ $displayName = $this->environment->getDisplayName();
+ $albumName = $this->environment->getSharedFolderName();
// Parameters sent to the template
$params = [
diff --git a/controller/servicecontroller.php b/controller/servicecontroller.php
index e0b00be2..24959c58 100644
--- a/controller/servicecontroller.php
+++ b/controller/servicecontroller.php
@@ -14,16 +14,21 @@
namespace OCA\GalleryPlus\Controller;
+use OCP\IEventSource;
use OCP\IURLGenerator;
use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Environment\EnvironmentException;
+use OCA\GalleryPlus\Http\ImageResponse;
+use OCA\GalleryPlus\Service\ServiceException;
use OCA\GalleryPlus\Service\InfoService;
use OCA\GalleryPlus\Service\ThumbnailService;
use OCA\GalleryPlus\Service\PreviewService;
-use OCA\GalleryPlus\Service\ServiceException;
+use OCA\GalleryPlus\Service\DownloadService;
/**
* Class ServiceController
@@ -35,6 +40,10 @@ class ServiceController extends Controller {
use JsonHttpError;
/**
+ * @type Environment
+ */
+ private $environment;
+ /**
* @type InfoService
*/
private $infoService;
@@ -47,34 +56,51 @@ class ServiceController extends Controller {
*/
private $previewService;
/**
+ * @type DownloadService
+ */
+ private $downloadService;
+ /**
* @type IURLGenerator
*/
private $urlGenerator;
+ /**
+ * @type IEventSource
+ */
+ private $eventSource;
/**
* Constructor
*
* @param string $appName
* @param IRequest $request
+ * @param Environment $environment
* @param InfoService $infoService
* @param ThumbnailService $thumbnailService
* @param PreviewService $previewService
+ * @param DownloadService $downloadService
* @param IURLGenerator $urlGenerator
+ * @param IEventSource $eventSource
*/
public function __construct(
$appName,
IRequest $request,
+ Environment $environment,
InfoService $infoService,
ThumbnailService $thumbnailService,
PreviewService $previewService,
- IURLGenerator $urlGenerator
+ DownloadService $downloadService,
+ IURLGenerator $urlGenerator,
+ IEventSource $eventSource
) {
parent::__construct($appName, $request);
+ $this->environment = $environment;
$this->infoService = $infoService;
$this->thumbnailService = $thumbnailService;
$this->previewService = $previewService;
+ $this->downloadService = $downloadService;
$this->urlGenerator = $urlGenerator;
+ $this->eventSource = $eventSource;
}
/**
@@ -86,13 +112,15 @@ class ServiceController extends Controller {
*
* @param string $albumpath
*
- * @return array|Http\JSONResponse
+ * @return array<string,int>|Http\JSONResponse
*/
public function getAlbumInfo($albumpath) {
try {
+ $nodeInfo = $this->environment->getNodeInfo($albumpath);
+
// Thanks to the AppFramework, Arrays are automatically JSON encoded
- return $this->infoService->getAlbumInfo($albumpath);
- } catch (ServiceException $exception) {
+ return $nodeInfo;
+ } catch (EnvironmentException $exception) {
return $this->error($exception);
}
}
@@ -111,14 +139,27 @@ class ServiceController extends Controller {
/**
* @NoAdminRequired
*
- * Returns a list of all images available to the logged-in user
+ * Returns a list of all images available to the authenticated user
+ *
+ * Authentication can be via a login/password or a token/(password)
+ *
+ * For private galleries, it returns all images, with the full path from the root folder
+ * For public galleries, the path starts from the folder the link gives access to
*
* @return array|Http\JSONResponse
*/
public function getImages() {
try {
- return $this->infoService->getImages();
- } catch (ServiceException $exception) {
+ $imagesFolder = $this->environment->getResourceFromPath('');
+ $fromRootToFolder = $this->environment->getFromRootToFolder();
+
+ $folderData = [
+ 'imagesFolder' => $imagesFolder,
+ 'fromRootToFolder' => $fromRootToFolder,
+ ];
+
+ return $this->infoService->getImages($folderData);
+ } catch (EnvironmentException $exception) {
return $this->error($exception);
}
}
@@ -130,34 +171,56 @@ class ServiceController extends Controller {
*
* Uses EventSource to send thumbnails back as soon as they're created
*
+ * FIXME: @LukasReschke says: The exit is required here because
+ * otherwise the AppFramework is trying to add headers as well after
+ * dispatching the request which results in a "Cannot modify header
+ * information" notice.
+ *
+ * WARNING: Returning a JSON response does not work.
+ *
* @param string $images
* @param bool $square
* @param bool $scale
*
- * @return Http\JSONResponse|null
+ * @return null|Http\JSONResponse
*/
public function getThumbnails($images, $square, $scale) {
- try {
- $this->thumbnailService->getAlbumThumbnails($images, $square, $scale);
- } catch (ServiceException $exception) {
- return $this->error($exception);
+ $imagesArray = explode(';', $images);
+
+ foreach ($imagesArray as $image) {
+ $thumbnail = $this->getThumbnail($image, $square, $scale);
+ $this->eventSource->send('preview', $thumbnail);
}
+ $this->eventSource->close();
+ exit();
}
/**
* @NoAdminRequired
*
- * Shows a large preview of a file
+ * Sends either a large preview of the requested file or the
+ * original file itself
+ *
+ * If the browser can use the file as-is then we simply let
+ * the browser download the file, straight from the filesystem
*
* @param string $file
* @param int $x
* @param int $y
*
- * @return \OCA\GalleryPlus\Http\ImageResponse|Http\JSONResponse
+ * @return ImageResponse|Http\JSONResponse
*/
public function showPreview($file, $x, $y) {
try {
- return $this->previewService->showPreview($file, $x, $y);
+ $animatedPreview = true;
+ $previewRequired = $this->previewService->isPreviewRequired($file, $animatedPreview);
+ if ($previewRequired) {
+ $preview = $this->previewService->createPreview($file, $x, $y);
+ } else {
+ $preview = $this->downloadService->downloadFile($file);
+ }
+
+ return new ImageResponse($preview, $preview['status']);
} catch (ServiceException $exception) {
return $this->error($exception);
}
@@ -174,7 +237,36 @@ class ServiceController extends Controller {
*/
public function downloadPreview($file) {
try {
- return $this->previewService->downloadPreview($file);
+ $download = $this->downloadService->downloadFile($file);
+
+ return new ImageResponse($download, $download['status']);
+ } catch (EnvironmentException $exception) {
+ return $this->error($exception);
+ }
+ }
+
+ /**
+ * Retrieves the thumbnail to send back to the browser
+ *
+ * The thumbnail is either a resized preview of the file or the original file
+ *
+ * @param string $image
+ * @param bool $square
+ * @param bool $scale
+ *
+ * @return array
+ */
+ private function getThumbnail($image, $square, $scale) {
+ try {
+ $previewRequired =
+ $this->previewService->isPreviewRequired($image, $animatedPreview = false);
+ if ($previewRequired) {
+ $thumbnail = $this->thumbnailService->createThumbnail($image, $square, $scale);
+ } else {
+ $thumbnail = $this->downloadService->downloadFile($image, $base64Encode = true);
+ }
+
+ return $thumbnail;
} catch (ServiceException $exception) {
return $this->error($exception);
}
diff --git a/environment/environment.php b/environment/environment.php
new file mode 100644
index 00000000..480b9076
--- /dev/null
+++ b/environment/environment.php
@@ -0,0 +1,334 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ * @author Authors of \OCA\Files_Sharing\Helper
+ *
+ * @copyright Olivier Paroz 2015
+ * @copyright Authors of \OCA\Files_Sharing\Helper 2014-2015
+ */
+
+namespace OCA\GalleryPlus\Environment;
+
+use OCP\IServerContainer;
+use OCP\IUserManager;
+use OCP\Share;
+use OCP\Files\Folder;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+
+use OCA\GalleryPlus\Utility\SmarterLogger;
+
+/**
+ * Builds the environment so that the services have access to the files and folders' owner
+ *
+ * @todo remove the serverContainer once OCP\IUserManager has a getUserFolder() method
+ *
+ * @package OCA\GalleryPlus\Environment
+ */
+class Environment {
+
+ /**
+ * @type string
+ */
+ private $appName;
+ /**
+ * The userId of the logged-in user or the person sharing a folder publicly
+ *
+ * @type string
+ */
+ private $userId;
+ /**
+ * The userFolder of the logged-in user or the ORIGINAL owner of the files which are shared
+ * publicly
+ *
+ * A share needs to be tracked back to its original owner in order to be able to access the
+ * resource
+ *
+ * @type Folder|null
+ */
+ private $userFolder;
+ /**
+ * @type IUserManager
+ */
+ private $userManager;
+ /**
+ * @type IServerContainer
+ */
+ private $serverContainer;
+ /**
+ * @type SmarterLogger
+ */
+ private $logger;
+ /**
+ * The path to the userFolder for users with accounts: /userId/files
+ *
+ * For public folders, it's the path from the shared folder to the root folder in the original
+ * owner's filesystem: /userId/files/parent_folder/shared_folder
+ *
+ * @type string
+ */
+ private $fromRootToFolder;
+ /**
+ * The name of the shared folder
+ *
+ * @type string
+ */
+ private $folderName;
+
+ /***
+ * Constructor
+ *
+ * @param string $appName
+ * @param string|null $userId
+ * @param Folder|null $userFolder
+ * @param IUserManager $userManager
+ * @param IServerContainer $serverContainer
+ * @param SmarterLogger $logger
+ */
+ public function __construct(
+ $appName,
+ $userId,
+ $userFolder,
+ IUserManager $userManager,
+ IServerContainer $serverContainer,
+ SmarterLogger $logger
+ ) {
+ $this->appName = $appName;
+ $this->userId = $userId;
+ $this->userFolder = $userFolder;
+ $this->userManager = $userManager;
+ $this->serverContainer = $serverContainer;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Creates the environment based on the linkItem the token links to
+ *
+ * @param array $linkItem
+ */
+ public function setTokenBasedEnv($linkItem) {
+ // Resolves reshares down to the last real share
+ $rootLinkItem = Share::resolveReShare($linkItem);
+ $origShareOwner = $rootLinkItem['uid_owner'];
+ $this->userFolder = $this->setupFilesystem($origShareOwner);
+
+ $fileSource = $linkItem['file_source'];
+ $this->fromRootToFolder = $this->buildFromRootToFolder($fileSource);
+
+ $this->folderName = $linkItem['file_target'];
+ $this->userId = $linkItem['uid_owner'];
+ }
+
+ /**
+ * Creates the environment for a logged-in user
+ *
+ * userId and userFolder are already known, we define fromRootToFolder
+ * so that the services can use one method to have access resources
+ * without having to know whether they're private or public
+ */
+ public function setStandardEnv() {
+ $this->fromRootToFolder = $this->userFolder->getPath() . '/';
+ }
+
+ /**
+ * Returns the resource located at the given path
+ *
+ * The path starts from the user's files folder
+ * The resource is either a File or a Folder
+ *
+ * @param string $subPath
+ *
+ * @return Node
+ */
+ public function getResourceFromPath($subPath) {
+ $path = $this->getImagePathFromFolder($subPath);
+ $nodeInfo = $this->getNodeInfo($path);
+
+ return $this->getResourceFromId($nodeInfo['fileid']);
+ }
+
+ /**
+ * Returns the Node based on the current user's files folder and a given
+ * path
+ *
+ * @param string $path
+ *
+ * @return array<string,int>|false
+ *
+ * @throws EnvironmentException
+ */
+ public function getNodeInfo($path) {
+ $nodeInfo = false;
+ $folder = $this->userFolder;
+ if ($folder === null) {
+ $this->logAndThrowNotFound("Could not access the user's folder");
+ } else {
+ try {
+ $node = $folder->get($path);
+ $nodeInfo = [
+ 'fileid' => $node->getId(),
+ 'permissions' => $node->getPermissions()
+ ];
+ } catch (NotFoundException $exception) {
+ $message = 'Could not find anything at: ' . $exception->getMessage();
+ $this->logAndThrowNotFound($message);
+ }
+ }
+
+ return $nodeInfo;
+ }
+
+
+ /**
+ * Returns the userId of the currently logged-in user or the sharer
+ *
+ * @return string
+ */
+ public function getUserID() {
+ return $this->userId;
+ }
+
+ /**
+ * Returns the name of the user sharing files publicly
+ *
+ * @return string
+ */
+ public function getDisplayName() {
+ $user = null;
+ $userId = $this->userId;
+
+ if (isset($userId)) {
+ $user = $this->userManager->get($userId);
+ }
+ if ($user === null) {
+ $this->logAndThrowNotFound('Could not find user');
+ }
+
+ return $user->getDisplayName();
+ }
+
+ /**
+ * Returns the name of shared folder
+ *
+ * @return string
+ */
+ public function getSharedFolderName() {
+ return trim($this->folderName, '//');
+ }
+
+ /**
+ * Returns /parent_folder/current_folder/_my_file
+ *
+ * getPath() on the file produces a path like:
+ * '/userId/files/my_folder/my_sub_folder'
+ *
+ * So we substract the path to the folder, giving us a relative path
+ * '/my_folder/my_sub_folder'
+ *
+ * @param string $image
+ *
+ * @return string
+ */
+ public function getImagePathFromFolder($image) {
+ $origSharePath = $this->fromRootToFolder;
+ $folderPath = $this->userFolder->getPath();
+ $origShareRelPath = str_replace($folderPath, '', $origSharePath);
+ $relativePath = $origShareRelPath;
+
+ /*$this->logger->debug(
+ 'Full Path {origSharePath}, folder path {folderPath}, relative path {relativePath}',
+ [
+ 'origSharePath' => $origSharePath,
+ 'folderPath' => $folderPath,
+ 'relativePath' => $relativePath
+ ]
+ );*/
+
+ return $relativePath . '/' . $image;
+ }
+
+ /**
+ * Returns fromRootToFolder
+ *
+ * @see buildFromRootToFolder
+ *
+ * @return string
+ */
+ public function getFromRootToFolder() {
+ return $this->fromRootToFolder;
+ }
+
+ /**
+ * Sets up the filesystem for the original share owner so that we can
+ * retrieve the files and returns the userFolder for that user
+ *
+ * We can't use 'UserFolder' from Application as the user is not known
+ * at instantiation time
+ *
+ * @param $origShareOwner
+ *
+ * @return Folder
+ */
+ private function setupFilesystem($origShareOwner) {
+ \OC_Util::tearDownFS(); // FIXME: Private API
+ \OC_Util::setupFS($origShareOwner); // FIXME: Private API
+
+ $folder = $this->serverContainer->getUserFolder($origShareOwner);
+ /*// Alternative which does not exist yet
+ $user = $this->userManager->get($origShareOwner);
+ $folder = $user->getUserFolder();*/
+
+ return $folder;
+ }
+
+ /**
+ * Returns the path from the shared folder to the root folder in the original
+ * owner's filesystem: /userId/files/parent_folder/shared_folder
+ *
+ * @param string $fileSource
+ *
+ * @return string
+ */
+ private function buildFromRootToFolder($fileSource) {
+ $resource = $this->getResourceFromId($fileSource);
+ $fromRootToFolder = $resource->getPath() . '/';
+
+ return $fromRootToFolder;
+ }
+
+ /**
+ * Returns the resource identified by the given ID
+ *
+ * @param int $resourceId
+ *
+ * @return Node
+ *
+ * @throws EnvironmentException
+ */
+ private function getResourceFromId($resourceId) {
+ $resourcesArray = $this->userFolder->getById($resourceId);
+ if ($resourcesArray[0] === null) {
+ $this->logAndThrowNotFound('Could not resolve linkItem');
+ }
+
+ return $resourcesArray[0];
+ }
+
+ /**
+ * Logs the error and raises an exception
+ *
+ * @param string $message
+ *
+ * @throws NotFoundEnvException
+ */
+ private function logAndThrowNotFound($message) {
+ $this->logger->error($message . ' (404)');
+ throw new NotFoundEnvException($message);
+ }
+
+} \ No newline at end of file
diff --git a/environment/environmentexception.php b/environment/environmentexception.php
new file mode 100644
index 00000000..9481797f
--- /dev/null
+++ b/environment/environmentexception.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ *
+ * @copyright Olivier Paroz 2015
+ */
+
+namespace OCA\GalleryPlus\Environment;
+
+use Exception;
+
+/**
+ * Thrown when the service cannot reply to a request
+ */
+class EnvironmentException extends Exception {
+
+ /**
+ * Constructor
+ *
+ * @param string $msg the message contained in the exception
+ */
+ public function __construct($msg) {}
+} \ No newline at end of file
diff --git a/environment/notfoundenvexception.php b/environment/notfoundenvexception.php
new file mode 100644
index 00000000..9589fa9a
--- /dev/null
+++ b/environment/notfoundenvexception.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ *
+ * @copyright Olivier Paroz 2015
+ */
+
+namespace OCA\GalleryPlus\Environment;
+
+/**
+ * Thrown when the service cannot reply to a request
+ */
+class NotFoundEnvException extends EnvironmentException {
+
+ /**
+ * Constructor
+ *
+ * @param string $msg the message contained in the exception
+ */
+ public function __construct($msg) {}
+} \ No newline at end of file
diff --git a/middleware/checkmiddleware.php b/middleware/checkmiddleware.php
index 81e2a30c..e13b14e6 100644
--- a/middleware/checkmiddleware.php
+++ b/middleware/checkmiddleware.php
@@ -90,6 +90,19 @@ abstract class CheckMiddleware extends Middleware {
}
/**
+ * Logs the error and raises an exception
+ *
+ * @param string $message
+ * @param int $code
+ *
+ * @throws CheckException
+ */
+ protected function logAndThrow($message, $code) {
+ $this->logger->error($message . ' (' . $code . ')');
+ throw new CheckException($message, $code);
+ }
+
+ /**
* Decides which type of response to send
*
* @param string $message
diff --git a/middleware/envcheckmiddleware.php b/middleware/envcheckmiddleware.php
new file mode 100644
index 00000000..1e4de129
--- /dev/null
+++ b/middleware/envcheckmiddleware.php
@@ -0,0 +1,316 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Authors of \OCA\Files_Sharing\Helper
+ *
+ * @copyright Olivier Paroz 2014-2015
+ * @copyright Bernhard Posselt 2012-2015
+ * @copyright Authors of \OCA\Files_Sharing\Helper 2014-2015
+ */
+
+namespace OCA\GalleryPlus\Middleware;
+
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\ISession;
+use OCP\Share;
+use OCP\Security\IHasher;
+
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Utility\IControllerMethodReflector;
+
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Service\ServiceException;
+use OCA\GalleryPlus\Utility\SmarterLogger;
+
+/**
+ * Checks that we have a valid token linked to a valid resource and that the
+ * user is authorised to access it
+ *
+ * Once all checks have been passed, the environment is ready to use
+ *
+ * @package OCA\GalleryPlus\Middleware
+ */
+class EnvCheckMiddleware extends CheckMiddleware {
+
+ /**
+ * @type IHasher
+ * */
+ private $hasher;
+ /**
+ * @type ISession
+ * */
+ private $session;
+ /**
+ * @type Environment
+ */
+ private $environment;
+ /**
+ * @type IControllerMethodReflector
+ */
+ protected $reflector;
+
+ /***
+ * Constructor
+ *
+ * @param string $appName
+ * @param IHasher $hasher
+ * @param ISession $session
+ * @param IRequest $request
+ * @param IControllerMethodReflector $reflector
+ * @param IURLGenerator $urlGenerator
+ * @param SmarterLogger $logger
+ * @param Environment $environment
+ * @param SmarterLogger $logger
+ */
+ public function __construct(
+ $appName,
+ IRequest $request,
+ IHasher $hasher,
+ ISession $session,
+ Environment $environment,
+ IControllerMethodReflector $reflector,
+ IURLGenerator $urlGenerator,
+ SmarterLogger $logger
+ ) {
+ parent::__construct(
+ $appName,
+ $request,
+ $urlGenerator,
+ $logger
+ );
+
+ $this->hasher = $hasher;
+ $this->session = $session;
+ $this->environment = $environment;
+ $this->reflector = $reflector;
+ }
+
+ /**
+ * Checks that we have a valid token linked to a valid resource and that the
+ * user is authorised to access it
+ *
+ * Inspects the controller method annotations and if PublicPage is found
+ * it checks that we have a token and an optional password giving access to a valid resource.
+ * Once that's done, the environment is setup so that our services can find the resources they
+ * need.
+ *
+ * The checks are not performed on "guest" pages and the environment is not setup. Typical
+ * guest pages are anonymous error ages
+ *
+ * @inheritDoc
+ */
+ public function beforeController($controller, $methodName) {
+ if ($this->reflector->hasAnnotation('Guest')) {
+ return;
+ }
+ $isPublicPage = $this->reflector->hasAnnotation('PublicPage');
+ if ($isPublicPage) {
+ $this->validateAndSetTokenBasedEnv();
+ } else {
+ $this->environment->setStandardEnv();
+ }
+ }
+
+ /**
+ * Checks that we have a token and an optional password giving access to a
+ * valid resource. Sets the token based environment after that
+ */
+ private function validateAndSetTokenBasedEnv() {
+ $token = $this->request->getParam('token');
+ if (!$token) {
+ $this->noTokenFound();
+ } else { // We have a token
+ // Let's see if it's linked to a valid resource
+ $linkItem = $this->getLinkItem($token);
+ $password = $this->request->getParam('password');
+ // Let's see if the user needs to provide a password
+ $this->checkAuthorisation($linkItem, $password);
+
+ $this->environment->setTokenBasedEnv($linkItem);
+ }
+ }
+
+ /**
+ * Throws an exception because no token was provided
+ *
+ * @throws CheckException
+ */
+ private function noTokenFound() {
+ $this->logAndThrow(
+ "Can't access a public resource without a token", Http::STATUS_NOT_FOUND
+ );
+ }
+
+ /**
+ * Validates a token to make sure its linked to a valid resource
+ *
+ * Logic mostly duplicated from @see \OCA\Files_Sharing\Helper
+ *
+ * @fixme setIncognitoMode in 8.1 https://github.com/owncloud/core/pull/12912
+ *
+ * @param string $token
+ *
+ * @return array|bool
+ *
+ * @throws CheckException
+ */
+ private function getLinkItem($token) {
+ // Allows a logged in user to access public links
+ \OC_User::setIncognitoMode(true);
+
+ $linkItem = Share::getShareByToken($token, false);
+
+ $this->checkLinkItemExists($linkItem);
+ $this->checkLinkItemIsValid($linkItem, $token);
+ $this->checkItemType($linkItem);
+
+ // Checks passed, let's store the linkItem
+ return $linkItem;
+ }
+
+ /**
+ * Makes sure that the token exists
+ *
+ * @param array|bool $linkItem
+ */
+ private function checkLinkItemExists($linkItem) {
+ if ($linkItem === false
+ || ($linkItem['item_type'] !== 'file'
+ && $linkItem['item_type'] !== 'folder')
+ ) {
+ $message = 'Passed token parameter is not valid';
+ $this->logAndThrow($message, Http::STATUS_BAD_REQUEST);
+ }
+ }
+
+ /**
+ * Makes sure that the token contains all the information that we need
+ *
+ * @param array|bool $linkItem
+ * @param string $token
+ */
+ private function checkLinkItemIsValid($linkItem, $token) {
+ if (!isset($linkItem['uid_owner'])
+ || !isset($linkItem['file_source'])
+ ) {
+ $message =
+ 'Passed token seems to be valid, but it does not contain all necessary information . ("'
+ . $token . '")';
+ $this->logAndThrow($message, Http::STATUS_NOT_FOUND);
+ }
+ }
+
+ /**
+ * Makes sure an item type was set for that token
+ *
+ * @param array|bool $linkItem
+ */
+ private function checkItemType($linkItem) {
+ if (!isset($linkItem['item_type'])) {
+ $message = 'No item type set for share id: ' . $linkItem['id'];
+ $this->logAndThrow($message, Http::STATUS_NOT_FOUND);
+ }
+ }
+
+ /**
+ * Checks if a password is required or if the one supplied is working
+ *
+ * @param array|bool $linkItem
+ * @param string|null $password optional password
+ *
+ * @throws CheckException
+ */
+ private function checkAuthorisation($linkItem, $password) {
+ $passwordRequired = isset($linkItem['share_with']);
+
+ if ($passwordRequired) {
+ if ($password !== null) {
+ $this->authenticate($linkItem, $password);
+ } else {
+ $this->checkSession($linkItem);
+ }
+ }
+ }
+
+ /**
+ * Authenticate link item with the given password
+ * or with the session if no password was given.
+ *
+ * @fixme @LukasReschke says: Migrate old hashes to new hash format
+ * Due to the fact that there is no reasonable functionality to update the password
+ * of an existing share no migration is yet performed there.
+ * The only possibility is to update the existing share which will result in a new
+ * share ID and is a major hack.
+ *
+ * In the future the migration should be performed once there is a proper method
+ * to update the share's password. (for example `$share->updatePassword($password)`
+ *
+ * @link https://github.com/owncloud/core/issues/10671
+ *
+ * @param array|bool $linkItem
+ * @param string $password
+ *
+ * @return bool true if authorized, an exception is raised otherwise
+ *
+ * @throws ServiceException
+ */
+ private function authenticate($linkItem, $password) {
+ if ($linkItem['share_type'] == Share::SHARE_TYPE_LINK) {
+ $this->checkPassword($linkItem, $password);
+ } else {
+ $this->logAndThrow(
+ 'Unknown share type ' . $linkItem['share_type'] . ' for share id '
+ . $linkItem['id'], Http::STATUS_NOT_FOUND
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates the given password
+ *
+ * @param array|bool $linkItem
+ * @param string $password
+ *
+ * @throws ServiceException
+ */
+ private function checkPassword($linkItem, $password) {
+ $newHash = '';
+ if ($this->hasher->verify($password, $linkItem['share_with'], $newHash)) {
+
+ // Save item id in session for future requests
+ $this->session->set('public_link_authenticated', $linkItem['id']);
+ if (!empty($newHash)) {
+ // For future use
+ }
+ } else {
+ $this->logAndThrow("Wrong password", Http::STATUS_UNAUTHORIZED);
+ }
+ }
+
+ /**
+ * Makes sure the user is already properly authenticated when a password is required and none
+ * was provided
+ *
+ * @param array|bool $linkItem
+ *
+ * @throws ServiceException
+ */
+ private function checkSession($linkItem) {
+ // Not authenticated ?
+ if (!$this->session->exists('public_link_authenticated')
+ || $this->session->get('public_link_authenticated') !== $linkItem['id']
+ ) {
+ $this->logAndThrow("Missing password", Http::STATUS_UNAUTHORIZED);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/middleware/sharingcheckmiddleware.php b/middleware/sharingcheckmiddleware.php
index d147d427..b623c479 100644
--- a/middleware/sharingcheckmiddleware.php
+++ b/middleware/sharingcheckmiddleware.php
@@ -82,17 +82,11 @@ class SharingCheckMiddleware extends CheckMiddleware {
public function beforeController($controller, $methodName) {
$sharingEnabled = $this->isSharingEnabled();
- // This needs to be done here as the Dispatcher does not call our reflector
- //$this->reflector->reflect($controller, $methodName);
-
$isPublicPage = $this->reflector->hasAnnotation('PublicPage');
$isGuest = $this->reflector->hasAnnotation('Guest');
if ($isPublicPage && !$isGuest && !$sharingEnabled) {
- throw new CheckException(
- 'Sharing is disabled',
- Http::STATUS_SERVICE_UNAVAILABLE
- );
+ $this->logAndThrow("'Sharing is disabled'", Http::STATUS_SERVICE_UNAVAILABLE);
}
}
diff --git a/middleware/tokencheckmiddleware.php b/middleware/tokencheckmiddleware.php
deleted file mode 100644
index 5a025b88..00000000
--- a/middleware/tokencheckmiddleware.php
+++ /dev/null
@@ -1,172 +0,0 @@
-<?php
-/**
- * ownCloud - galleryplus
- *
- * This file is licensed under the Affero General Public License version 3 or
- * later. See the COPYING file.
- *
- * @author Olivier Paroz <owncloud@interfasys.ch>
- * @author Bernhard Posselt <dev@bernhard-posselt.com>
- *
- * @copyright Olivier Paroz 2014-2015
- * @copyright Bernhard Posselt 2012-2015
- */
-
-namespace OCA\GalleryPlus\Middleware;
-
-use OCP\IRequest;
-use OCP\IURLGenerator;
-
-use OCP\AppFramework\Http;
-use OCP\AppFramework\Utility\IControllerMethodReflector;
-
-use OCA\GalleryPlus\Service\EnvironmentService;
-use OCA\GalleryPlus\Service\ServiceException;
-use OCA\GalleryPlus\Utility\SmarterLogger;
-
-
-/**
- * Checks that we have a valid token linked to a valid resource and that the
- * user is authorised to access it
- *
- * @package OCA\GalleryPlus\Middleware
- */
-class TokenCheckMiddleware extends CheckMiddleware {
-
- /**
- * @type EnvironmentService
- */
- private $environmentService;
- /**
- * @type IControllerMethodReflector
- */
- protected $reflector;
-
- /***
- * Constructor
- *
- * @param string $appName
- * @param IRequest $request
- * @param EnvironmentService $environmentService
- * @param IControllerMethodReflector $reflector
- * @param IURLGenerator $urlGenerator
- * @param SmarterLogger $logger
- */
- public function __construct(
- $appName,
- IRequest $request,
- EnvironmentService $environmentService,
- IControllerMethodReflector $reflector,
- IURLGenerator $urlGenerator,
- SmarterLogger $logger
- ) {
- parent::__construct(
- $appName,
- $request,
- $urlGenerator,
- $logger
- );
-
- $this->environmentService = $environmentService;
- $this->reflector = $reflector;
- }
-
- /**
- * Checks that we have a valid token linked to a valid resource and that the
- * user is authorised to access it
- *
- * Inspects the controller method annotations and if PublicPage is found
- * it checks that we have token and an optional password giving access to a
- * valid resource
- *
- * The check is not performed on "guest" pages which don't require a token
- *
- * @inheritDoc
- */
- public function beforeController($controller, $methodName) {
- $token = $this->request->getParam('token');
- $password = $this->request->getParam('password');
- /*// This needs to be done here as the Dispatcher does not call our reflector
- $this->reflector->reflect($controller, $methodName);*/
- $isPublicPage = $this->reflector->hasAnnotation('PublicPage');
- $isGuest = $this->reflector->hasAnnotation('Guest');
-
- if ($isPublicPage && !$isGuest) {
- if (!$token) {
- $this->noTokenFound();
- } else { // We have a token
- // Let's see if it's linked to a valid resource
- $this->checkToken($token);
- // Let's see if the user needs to provide a password
- $this->checkAuthorisation($password);
- // Let's see if we can set up the environment for the controller
- $this->setupTokenBasedEnv();
- }
- }
- }
-
- /**
- * Throws an exception because no token was provided
- *
- * @throws CheckException
- */
- private function noTokenFound() {
- throw new CheckException(
- "Can't access a public resource without a token",
- Http::STATUS_NOT_FOUND
- );
- }
-
- /**
- * Makes sure we have a valid token, linked to a valid resource
- *
- * @param string $token
- *
- * @throws CheckException
- */
- private function checkToken($token) {
- try {
- $this->environmentService->checkToken($token);
- } catch (ServiceException $exception) {
- throw new CheckException(
- $exception->getMessage(),
- $exception->getCode()
- );
- }
- }
-
- /**
- * Checks if a password is required or if the one supplied is working
- *
- * @param $password
- *
- * @throws CheckException
- */
- private function checkAuthorisation($password) {
- try {
- $this->environmentService->checkAuthorisation($password);
- } catch (ServiceException $exception) {
- throw new CheckException(
- $exception->getMessage(),
- $exception->getCode()
- );
- }
- }
-
- /**
- * Sets up the environment based on the received token
- *
- * @throws CheckException
- */
- private function setupTokenBasedEnv() {
- try {
- $this->environmentService->setupTokenBasedEnv();
- } catch (ServiceException $exception) {
- throw new CheckException(
- $exception->getMessage(),
- $exception->getCode()
- );
- }
- }
-
-} \ No newline at end of file
diff --git a/preview/preview.php b/preview/preview.php
index 19fb83ed..feff4a45 100644
--- a/preview/preview.php
+++ b/preview/preview.php
@@ -15,16 +15,15 @@ namespace OCA\GalleryPlus\Preview;
use OCP\IConfig;
use OCP\Image;
use OCP\Files\File;
+use OCP\IPreview;
use OCP\Template;
-use OCP\AppFramework\Http;
-
use OCA\GalleryPlus\Utility\SmarterLogger;
/**
* Generates previews
*
- * @todo On OC8.1, replace \OC\Preview with OC::$server->getPreviewManager()
+ * @todo On OC8.1, replace \OC\Preview with IPreview
*
* @package OCA\GalleryPlus\Preview
*/
@@ -35,13 +34,17 @@ class Preview {
*/
private $dataDir;
/**
+ * @type mixed
+ */
+ private $previewManager;
+ /**
* @type SmarterLogger
*/
private $logger;
/**
* @type string
*/
- private $owner;
+ private $userId;
/**
* @type \OC\Preview
*/
@@ -54,131 +57,60 @@ class Preview {
* @type int[]
*/
private $dims;
-
+ /**
+ * @type bool
+ */
+ private $success = true;
/**
* Constructor
*
* @param IConfig $config
+ * @param IPreview $previewManager
* @param SmarterLogger $logger
*/
public function __construct(
IConfig $config,
+ IPreview $previewManager,
SmarterLogger $logger
) {
$this->dataDir = $config->getSystemValue('datadirectory');
+ $this->previewManager = $previewManager;
$this->logger = $logger;
}
/**
- * Initialises the object
- *
- * @fixme Private API, but can't use the PreviewManager yet as it's incomplete
- *
- * @param string $owner
- * @param File $file
- * @param string $imagePathFromFolder
- */
- public function setupView($owner, $file, $imagePathFromFolder) {
- $this->owner = $owner;
- $this->file = $file;
- $this->preview = new \OC\Preview($owner, 'files', $imagePathFromFolder);
- }
-
- /**
- * Decides if we should download the file instead of generating a preview
- *
- * @param bool $animatedPreview
- * @param bool $download
- *
- * @return bool
- */
- public function previewRequired($animatedPreview, $download) {
- $mime = $this->file->getMimeType();
-
- if ($mime === 'image/svg+xml') {
- return $this->isSvgPreviewRequired();
- }
- if ($mime === 'image/gif') {
- return $this->isGifPreviewRequired($animatedPreview);
- }
-
- return !$download;
- }
-
- /**
- * Decides if we should download the SVG or generate a preview
- *
- * SVGs are downloaded if the SVG converter is disabled
- * Files of any media type are downloaded if requested by the client
- *
- * @return bool
- */
- private function isSvgPreviewRequired() {
- if (!$this->preview->isMimeSupported('image/svg+xml')) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Decides if we should download the GIF or generate a preview
- *
- * GIFs are downloaded if they're animated and we want to show
- * animations
+ * Returns true if the passed mime type is supported
*
- * @param bool $animatedPreview
+ * @param string $mimeType
*
- * @return bool
+ * @return boolean
*/
- private function isGifPreviewRequired($animatedPreview) {
- $animatedGif = $this->isGifAnimated();
-
- if ($animatedPreview && $animatedGif) {
- return false;
- }
-
- return true;
+ public function isMimeSupported($mimeType = '*') {
+ return $this->previewManager->isMimeSupported($mimeType);
}
/**
- * Tests if a GIF is animated
+ * Initialises the view which will be used to access files and generate previews
*
- * An animated gif contains multiple "frames", with each frame having a
- * header made up of:
- * * a static 4-byte sequence (\x00\x21\xF9\x04)
- * * 4 variable bytes
- * * a static 2-byte sequence (\x00\x2C) (Photoshop uses \x00\x21)
- *
- * We read through the file until we reach the end of the file, or we've
- * found at least 2 frame headers
- *
- * @link http://php.net/manual/en/function.imagecreatefromgif.php#104473
+ * @fixme Private API, but can't use the PreviewManager yet as it's incomplete
*
- * @return bool
+ * @param string $userId
+ * @param File $file
+ * @param string $imagePathFromFolder
*/
- private function isGifAnimated() {
- $fileHandle = $this->file->fopen('rb');
- $count = 0;
- while (!feof($fileHandle) && $count < 2) {
- $chunk = fread($fileHandle, 1024 * 100); //read 100kb at a time
- $count += preg_match_all(
- '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches
- );
- }
-
- fclose($fileHandle);
-
- return $count > 1;
+ public function setupView($userId, $file, $imagePathFromFolder) {
+ $this->userId = $userId;
+ $this->file = $file;
+ $this->preview = new \OC\Preview($userId, 'files', $imagePathFromFolder);
}
/**
* Returns a preview based on OC's preview class and our custom methods
*
- * We don't throw an exception when the preview generator fails,
- * instead, until the Preview class is fixed, we send the mime
- * icon along with a 415 error code.
+ * We check that the preview returned by the Preview class can be used by
+ * the browser. If not, we send the mime icon and change the status code so
+ * that the client knows that the process has failed.
*
* @fixme setKeepAspect is missing from public interface.
* https://github.com/owncloud/core/issues/12772
@@ -196,13 +128,11 @@ class Preview {
if ($maxX === 200) { // Only fixing the square thumbnails
$previewData = $this->previewValidator();
}
- $perfectPreview = ['preview' => $previewData, 'status' => Http::STATUS_OK];
+ $perfectPreview = ['preview' => $previewData];
} else {
$this->logger->debug("[PreviewService] ERROR! Did not get a preview");
- $perfectPreview = [
- 'preview' => $this->getMimeIcon(),
- 'status' => Http::STATUS_UNSUPPORTED_MEDIA_TYPE
- ];
+ $perfectPreview = ['preview' => $this->getMimeIcon()];
+ $this->success = false;
}
$perfectPreview['mimetype'] = 'image/png'; // Previews are always sent as PNG
@@ -210,6 +140,15 @@ class Preview {
}
/**
+ * Returns true if the preview was successfully generated
+ *
+ * @return bool
+ */
+ public function isPreviewValid() {
+ return $this->success;
+ }
+
+ /**
* Asks core for a preview based on our criteria
*
* @todo Need to read scaling setting from settings
@@ -217,8 +156,6 @@ class Preview {
* @param bool $keepAspect
*
* @return \OC_Image
- *
- * @throws \Exception
*/
private function getPreviewFromCore($keepAspect) {
$this->logger->debug("[PreviewService] Generating a new preview");
@@ -228,6 +165,8 @@ class Preview {
$this->preview->setScalingUp(false);
$this->preview->setKeepAspect($keepAspect);
+ //$this->logger->debug("[PreviewService] preview {preview}", ['preview' => $this->preview]);
+
return $this->preview->getPreview();
}
@@ -235,9 +174,9 @@ class Preview {
* Makes sure we return previews of the asked dimensions and fix the cache
* if necessary
*
- * The Preview class of OC7 sometimes return previews which are either
- * wider or smaller than the asked dimensions. This happens when one of the
- * original dimension is smaller than what is asked for
+ * The Preview class sometimes return previews which are either wider or
+ * smaller than the asked dimensions. This happens when one of the original
+ * dimension is smaller than what is asked for
*
* @return resource
*/
@@ -327,7 +266,7 @@ class Preview {
* @return mixed
*/
private function fixPreviewCache($fixedPreview) {
- $owner = $this->owner;
+ $owner = $this->userId;
$file = $this->file;
$preview = $this->preview;
$fixedPreviewObject = new Image($fixedPreview);
@@ -360,7 +299,7 @@ class Preview {
$iconData = new Image();
$image = $this->dataDir . '/../' . Template::mimetype_icon($mime);
- // Alternative
+ // Alternative which does not exist yet
//$image = $this->serverRoot() . Template::mimetype_icon($mime);
$iconData->loadFromFile($image);
diff --git a/service/base64encode.php b/service/base64encode.php
new file mode 100644
index 00000000..c29f6220
--- /dev/null
+++ b/service/base64encode.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ *
+ * @copyright Olivier Paroz 2015
+ */
+
+namespace OCA\GalleryPlus\Service;
+
+/**
+ * Base64 encoding utility method
+ *
+ * @package OCA\GalleryPlus\Service
+ */
+trait Base64Encode {
+
+ /**
+ * Returns base64 encoded data of a preview
+ *
+ * Using base64_encode for files which are downloaded
+ * (cached Thumbnails, SVG, GIFs) and using __toStrings
+ * for the previews which are instances of \OC_Image
+ *
+ * @param \OC_Image|string $previewData
+ *
+ * @return \OC_Image|string
+ */
+ protected function encode($previewData) {
+ if ($previewData instanceof \OC_Image) {
+ $previewData = (string)$previewData;
+ } else {
+ $previewData = base64_encode($previewData);
+ }
+
+ return $previewData;
+ }
+} \ No newline at end of file
diff --git a/service/downloadservice.php b/service/downloadservice.php
new file mode 100644
index 00000000..77cf9219
--- /dev/null
+++ b/service/downloadservice.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ *
+ * @copyright Olivier Paroz 2015
+ */
+
+namespace OCA\GalleryPlus\Service;
+
+use OCP\Files\File;
+
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Environment\NotFoundEnvException;
+use OCA\GalleryPlus\Utility\SmarterLogger;
+
+/**
+ * Prepares the file to download
+ *
+ * @package OCA\GalleryPlus\Service
+ */
+class DownloadService extends Service {
+
+ use Base64Encode;
+
+ /**
+ * @type Environment
+ */
+ private $environment;
+
+ /**
+ * Constructor
+ *
+ * @param string $appName
+ * @param Environment $environment
+ * @param SmarterLogger $logger
+ */
+ public function __construct(
+ $appName,
+ Environment $environment,
+ SmarterLogger $logger
+ ) {
+ parent::__construct($appName, $logger);
+
+ $this->environment = $environment;
+ }
+
+ /**
+ * Downloads the requested file
+ *
+ * @param string $image
+ * @param bool $base64Encode
+ *
+ * @return array
+ */
+ public function downloadFile($image, $base64Encode = false) {
+ $file = null;
+ try {
+ /** @type File $file */
+ $file = $this->environment->getResourceFromPath($image);
+ } catch (NotFoundEnvException $exception) {
+ $this->logAndThrowNotFound($exception->getMessage());
+ }
+ $this->logger->debug("[DownloadService] File to Download: {file}");
+ $download = [
+ 'path' => $image,
+ 'preview' => $file->getContent(),
+ 'mimetype' => $file->getMimeType(),
+ 'status' => Service::STATUS_OK
+ ];
+
+ if ($base64Encode) {
+ $download['preview'] = $this->encode($download['preview']);
+ }
+
+ return $download;
+ }
+
+} \ No newline at end of file
diff --git a/service/environmentservice.php b/service/environmentservice.php
deleted file mode 100644
index fe264ae4..00000000
--- a/service/environmentservice.php
+++ /dev/null
@@ -1,385 +0,0 @@
-<?php
-/**
- * ownCloud - galleryplus
- *
- * This file is licensed under the Affero General Public License version 3 or
- * later. See the COPYING file.
- *
- * @author Olivier Paroz <owncloud@interfasys.ch>
- * @author Authors of \OCA\Files_Sharing\Helper
- *
- * @copyright Olivier Paroz 2014-2015
- * @copyright Authors of \OCA\Files_Sharing\Helper 2014-2015
- */
-
-namespace OCA\GalleryPlus\Service;
-
-use OCP\Files\Folder;
-use OCP\IServerContainer;
-use OCP\IUser;
-use OCP\ISession;
-use OCP\Share;
-use OCP\IUserManager;
-use OCP\Security\IHasher;
-
-use OCP\AppFramework\Http;
-
-use OCA\GalleryPlus\Utility\SmarterLogger;
-
-/**
- * Builds the environment so that the services have access to the proper user,
- * folder and files
- *
- * @package OCA\GalleryPlus\Service
- */
-class EnvironmentService extends Service {
-
- /**
- * @type string
- */
- private $userId;
- /**
- * @type Folder|null
- */
- private $userFolder;
- /**
- * @type IUserManager
- */
- private $userManager;
- /**
- * @type IServerContainer
- */
- private $serverContainer;
- /**
- * @type IHasher
- * */
- private $hasher;
- /**
- * @type ISession
- * */
- private $session;
- /**
- * @type array
- */
- private $linkItem;
- /**
- * @type array
- */
- private $origShareData = [];
-
- /**
- * @param string $appName
- * @param string|null $userId
- * @param Folder|null $userFolder
- * @param IUserManager $userManager
- * @param IServerContainer $serverContainer
- * @param IHasher $hasher
- * @param ISession $session
- * @param SmarterLogger $logger
- */
- public function __construct(
- $appName,
- $userId,
- $userFolder,
- IUserManager $userManager,
- IServerContainer $serverContainer,
- IHasher $hasher,
- ISession $session,
- SmarterLogger $logger
- ) {
- parent::__construct($appName, $logger);
-
- $this->userId = $userId;
- $this->userFolder = $userFolder;
- $this->userManager = $userManager;
- $this->serverContainer = $serverContainer;
- $this->hasher = $hasher;
- $this->session = $session;
- }
-
- /**
- * Validates a token to make sure its linked to a valid resource
- *
- * Logic mostly duplicated from @see \OCA\Files_Sharing\Helper
- * @fixme setIncognitoMode in 8.1 https://github.com/owncloud/core/pull/12912
- *
- * @param string $token
- */
- public function checkToken($token) {
- // Allows a logged in user to access public links
- \OC_User::setIncognitoMode(true);
-
- $linkItem = Share::getShareByToken($token, false);
-
- $this->checkLinkItemExists($linkItem);
- $this->checkLinkItemIsValid($linkItem, $token);
- $this->checkItemType($linkItem);
-
- // Checks passed, let's store the linkItem
- $this->linkItem = $linkItem;
- }
-
- /**
- * Checks if a password is required and validates it if it is provided in
- * the request
- *
- * @param string $password optional password
- */
- public function checkAuthorisation($password = null) {
- $passwordRequired = isset($this->linkItem['share_with']);
-
- if ($passwordRequired) {
- if ($password !== null) {
- $this->authenticate($password);
- } else {
- $this->checkSession();
- }
- }
- }
-
- /**
- * Sets up the environment based on a token
- *
- * The token has already been vetted by checkToken via the token checking
- * middleware
- */
- public function setupTokenBasedEnv() {
- $linkItem = $this->linkItem;
- // Resolves reshares down to the last real share
- $rootLinkItem = Share::resolveReShare($linkItem);
- $origShareOwner = $rootLinkItem['uid_owner'];
- $user = $this->getUser($origShareOwner);
- $origOwnerDisplayName = $user->getDisplayName();
- // Setup FS for user
- \OC_Util::tearDownFS(); // FIXME: Private API
- \OC_Util::setupFS($origShareOwner); // FIXME: Private API
- $fileSource = $linkItem['file_source'];
- $origShareRelPath = $this->getPath($origShareOwner, $fileSource);
-
- // Checks passed, let's store the data
- $this->origShareData = [
- 'origShareOwner' => $origShareOwner,
- 'origOwnerDisplayName' => $origOwnerDisplayName,
- 'origShareRelPath' => $origShareRelPath
- ];
- }
-
- /**
- * Returns an array with details about the environment
- *
- * @return array various environment variables
- */
- public function getEnv() {
- $linkItem = $this->linkItem;
-
- if (isset($linkItem)) {
- $env = $this->getTokenBasedEnv($linkItem);
- } else {
- $env = [
- 'owner' => $this->userId,
- 'relativePath' => '/',
- 'folder' => $this->userFolder,
- ];
- }
-
- return $env;
- }
-
- /**
- * Returns the environment for a token
- *
- * @param $linkItem
- *
- * @return array
- */
- private function getTokenBasedEnv($linkItem) {
- $origShareOwner = $this->origShareData['origShareOwner'];
- $origShareRelPath = $this->origShareData['origShareRelPath'];
- // Displayed in the top right corner of the gallery
- $origOwnerDisplayName = $this->origShareData['origOwnerDisplayName'];
-
- $shareOwner = $linkItem['uid_owner'];
- $folder = $this->serverContainer->getUserFolder($shareOwner);
-
- $albumName = trim($linkItem['file_target'], '//');
-
- return [
- 'owner' => $shareOwner,
- 'relativePath' => $origShareRelPath . '/',
- 'folder' => $folder,
- 'albumName' => $albumName,
- 'originalShareOwner' => $origShareOwner,
- 'originalOwnerDisplayName' => $origOwnerDisplayName,
- ];
- }
-
- /**
- * Makes sure that the token exists
- *
- * @param bool|array $linkItem
- */
- private function checkLinkItemExists($linkItem) {
- if ($linkItem === false
- || ($linkItem['item_type'] !== 'file'
- && $linkItem['item_type'] !== 'folder')
- ) {
- $message = 'Passed token parameter is not valid';
- $this->kaBoom($message, Http::STATUS_BAD_REQUEST);
- }
- }
-
- /**
- * Makes sure that the token contains all the information that we need
- *
- * @param array $linkItem
- * @param string $token
- */
- private function checkLinkItemIsValid($linkItem, $token) {
- if (!isset($linkItem['uid_owner'])
- || !isset($linkItem['file_source'])
- ) {
- $message =
- 'Passed token seems to be valid, but it does not contain all necessary information . ("'
- . $token . '")';
- $this->kaBoom($message, Http::STATUS_NOT_FOUND);
- }
- }
-
- /**
- * Makes sure an item type was set for that token
- *
- * @param array $linkItem
- */
- private function checkItemType($linkItem) {
- if (!isset($linkItem['item_type'])) {
- $message = 'No item type set for share id: ' . $linkItem['id'];
- $this->kaBoom($message, Http::STATUS_NOT_FOUND);
- }
- }
-
- /**
- * Authenticate link item with the given password
- * or with the session if no password was given.
- *
- * @fixme Migrate old hashes to new hash format
- * Due to the fact that there is no reasonable functionality to update the password
- * of an existing share no migration is yet performed there.
- * The only possibility is to update the existing share which will result in a new
- * share ID and is a major hack.
- *
- * In the future the migration should be performed once there is a proper method
- * to update the share's password. (for example `$share->updatePassword($password)`
- * @link https://github.com/owncloud/core/issues/10671
- *
- * @param string $password
- *
- * @return bool true if authorized, an exception is raised otherwise
- *
- * @throws ServiceException
- */
- private function authenticate($password) {
- $linkItem = $this->linkItem;
-
- if ($linkItem['share_type'] == Share::SHARE_TYPE_LINK) {
- $this->checkPassword($password);
- } else {
- $this->kaBoom(
- 'Unknown share type ' . $linkItem['share_type'] . ' for share id '
- . $linkItem['id'], Http::STATUS_NOT_FOUND
- );
- }
-
- return true;
- }
-
- /**
- * Validates the given password
- *
- * @param string $password
- *
- * @throws ServiceException
- */
- private function checkPassword($password) {
- $linkItem = $this->linkItem;
- $newHash = '';
- if ($this->hasher->verify($password, $linkItem['share_with'], $newHash)) {
-
- // Save item id in session for future requests
- $this->session->set('public_link_authenticated', $linkItem['id']);
- if (!empty($newHash)) {
- // For future use
- }
- } else {
- $this->kaBoom("Wrong password", Http::STATUS_UNAUTHORIZED);
- }
- }
-
- /**
- * Makes sure the user is already properly authenticated when a password is required and none
- * was provided
- *
- * @throws ServiceException
- */
- private function checkSession() {
- // not authenticated ?
- if (!$this->session->exists('public_link_authenticated')
- || $this->session->get('public_link_authenticated') !== $this->linkItem['id']
- ) {
- $this->kaBoom("Missing password", Http::STATUS_UNAUTHORIZED);
- }
- }
-
- /**
- * Returns an instance of the user
- *
- * @param string $origShareOwner the user the share belongs to
- *
- * @return IUser an instance of the user
- */
- private function getUser($origShareOwner) {
- $user = null;
-
- if (isset($origShareOwner)) {
- $user = $this->userManager->get($origShareOwner);
- }
- if ($user === null) {
- $this->kaBoom('Could not find user', Http::STATUS_NOT_FOUND);
- }
-
- return $user;
- }
-
- /**
- * Returns the path the token gives access to
- *
- * getPath() on the file produces a path like:
- * '/owner/files/my_folder/my_sub_folder'
- *
- * So we substract the path to the folder, giving us a relative path
- * '/my_folder/my_sub_folder'
- *
- * @param string $origShareOwner
- * @param int $fileSource
- *
- * @return string the path, relative to the folder
- */
- private function getPath($origShareOwner, $fileSource) {
- $folder = $this->serverContainer->getUserFolder($origShareOwner);
- $resource = $this->getResourceFromId($folder, $fileSource);
-
- $origSharePath = $resource->getPath();
- $folderPath = $folder->getPath();
- $origShareRelPath = str_replace($folderPath, '', $origSharePath);
-
- /*$this->logger->debug(
- 'Full Path {origSharePath}, relative path {origShareRelPath}',
- [
- 'origSharePath' => $origSharePath,
- 'origShareRelPath' => $origShareRelPath
- ]
- );*/
-
- return $origShareRelPath;
- }
-
-} \ No newline at end of file
diff --git a/service/infoservice.php b/service/infoservice.php
index 40b044d6..0e8d82ee 100644
--- a/service/infoservice.php
+++ b/service/infoservice.php
@@ -14,9 +14,6 @@ namespace OCA\GalleryPlus\Service;
use OCP\Files\Folder;
use OCP\Files\File;
-use OCP\IPreview;
-
-use OCP\AppFramework\Http;
use OCA\GalleryPlus\Utility\SmarterLogger;
@@ -30,23 +27,15 @@ use OCA\GalleryPlus\Utility\SmarterLogger;
class InfoService extends Service {
/**
- * @type Folder|null
- */
- private $userFolder;
- /**
- * @type EnvironmentService
- */
- private $environmentService;
- /**
* @type mixed
*/
- private $previewManager;
+ private $previewService;
/**
* @todo This hard-coded array could be replaced by admin settings
*
* @type string[]
*/
- private static $baseMimeTypes = [
+ private $baseMimeTypes = [
'image/png',
'image/jpeg',
'image/gif',
@@ -64,7 +53,7 @@ class InfoService extends Service {
*
* @type string[]
*/
- private static $slideshowMimeTypes = [
+ private $slideshowMimeTypes = [
'application/font-sfnt',
'application/x-font',
];
@@ -73,47 +62,18 @@ class InfoService extends Service {
* Constructor
*
* @param string $appName
- * @param Folder|null $userFolder
- * @param EnvironmentService $environmentService
* @param SmarterLogger $logger
- * @param IPreview $previewManager
+ * @param PreviewService $previewManager
*/
public function __construct(
$appName,
- $userFolder,
- EnvironmentService $environmentService,
- SmarterLogger $logger,
- IPreview $previewManager
+ PreviewService $previewManager,
+ SmarterLogger $logger
+
) {
parent::__construct($appName, $logger);
- $this->userFolder = $userFolder;
- $this->environmentService = $environmentService;
- $this->previewManager = $previewManager;
- }
-
- /**
- * Returns information about an album, based on its path
- *
- * Used to see if we have access to the folder or not
- *
- * @param string $albumpath
- *
- * @return array<string,int>|false information about the given path
- */
- public function getAlbumInfo($albumpath) {
- $userFolder = $this->userFolder;
- $nodeInfo = false;
-
- if ($userFolder !== null) {
- $nodeInfo = $this->getNodeInfo($userFolder, $albumpath);
- } else {
- $message = "Could not access the user's folder";
- $code = Http::STATUS_NOT_FOUND;
- $this->kaBoom($message, $code);
- }
-
- return $nodeInfo;
+ $this->previewService = $previewManager;
}
/**
@@ -127,16 +87,15 @@ class InfoService extends Service {
*/
public function getSupportedMimes($slideshow = true) {
$supportedMimes = [];
- $wantedMimes = self::$baseMimeTypes;
+ $wantedMimes = $this->baseMimeTypes;
if ($slideshow) {
- $wantedMimes = array_merge($wantedMimes, self::$slideshowMimeTypes);
+ $wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes);
}
foreach ($wantedMimes as $wantedMime) {
// Let's see if a preview of files of that media type can be generated
- $preview = $this->previewManager;
- if ($preview->isMimeSupported($wantedMime)) {
+ if ($this->previewService->isMimeSupported($wantedMime)) {
$supportedMimes[] = $wantedMime; // We add it to the list of supported media types
}
}
@@ -148,49 +107,21 @@ class InfoService extends Service {
}
/**
- * This returns the list of all images which can be shown
+ * This returns the list of all images which can be shown starting from the given folder
*
- * For private galleries, it returns all images
- * For public galleries, it starts from the folder the link gives access to
+ * @param array $folderData
*
* @return array all the images we could find
*/
- public function getImages() {
- $folderData = $this->getImagesFolder();
-
- $imagesFolder = $folderData['imagesFolder'];
- $images = $this->searchByMime($imagesFolder);
-
+ public function getImages($folderData) {
+ $images = $this->searchByMime($folderData['imagesFolder']);
$fromRootToFolder = $folderData['fromRootToFolder'];
- $result = $this->fixImagePath($images, $fromRootToFolder);
- /*$this->logger->debug("Images array: {images}",['images' => $result]);*/
+ $result = $this->prepareImagesArray($images, $fromRootToFolder);
- return $result;
- }
+ //$this->logger->debug("Images array: {images}", ['images' => $result]);
- /**
- * Returns the folder where we need to look for files, as well as the path
- * starting from it and going up to the user's root folder
- *
- * @return array<string,Folder|string>
- */
- private function getImagesFolder() {
- $env = $this->environmentService->getEnv();
- $pathRelativeToFolder = $env['relativePath'];
- /** @type Folder $folder */
- $folder = $env['folder'];
- $folderPath = $folder->getPath();
- /** @type Folder $imagesFolder */
- $imagesFolder = $this->getResourceFromPath($folder, $pathRelativeToFolder);
- $fromRootToFolder = $folderPath . $pathRelativeToFolder;
-
- $folderData = [
- 'imagesFolder' => $imagesFolder,
- 'fromRootToFolder' => $fromRootToFolder,
- ];
-
- return $folderData;
+ return $result;
}
/**
@@ -225,6 +156,7 @@ class InfoService extends Service {
* We remove the part which goes from the user's root to the current
* folder and we also remove the current folder for public galleries
*
+ * @todo Test this on OC8
* On OC7, we fix searchByMime which returns images from the rubbish bin...
* https://github.com/owncloud/core/issues/4903
*
@@ -244,15 +176,13 @@ class InfoService extends Service {
*
* @return array
*/
- private function fixImagePath($images, $fromRootToFolder) {
+ private function prepareImagesArray($images, $fromRootToFolder) {
$result = [];
/** @type File $image */
foreach ($images as $image) {
$imagePath = $image->getPath();
$mimeType = $image->getMimetype();
- $fixedPath = str_replace(
- $fromRootToFolder, '', $imagePath
- );
+ $fixedPath = str_replace($fromRootToFolder, '', $imagePath);
if (substr($fixedPath, 0, 9) === "_trashbin") {
continue;
}
diff --git a/service/notfoundserviceexception.php b/service/notfoundserviceexception.php
new file mode 100644
index 00000000..298f7bbd
--- /dev/null
+++ b/service/notfoundserviceexception.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * ownCloud - galleryplus
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Olivier Paroz <owncloud@interfasys.ch>
+ *
+ * @copyright Olivier Paroz 2014-2015
+ */
+
+namespace OCA\GalleryPlus\Service;
+
+/**
+ * Thrown when the service cannot reply to a request
+ */
+class NotFoundServiceException extends ServiceException {
+
+ /**
+ * Constructor
+ *
+ * @param string $msg the message contained in the exception
+ */
+ public function __construct($msg) {
+ parent::__construct($msg);
+ }
+} \ No newline at end of file
diff --git a/service/previewservice.php b/service/previewservice.php
index 23b1c9d4..b8d5ba85 100644
--- a/service/previewservice.php
+++ b/service/previewservice.php
@@ -6,19 +6,16 @@
* later. See the COPYING file.
*
* @author Olivier Paroz <owncloud@interfasys.ch>
- * @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Olivier Paroz 2014-2015
- * @copyright Robin Appelman 2012-2015
*/
namespace OCA\GalleryPlus\Service;
use OCP\Files\File;
-use OCP\AppFramework\Http;
-
-use OCA\GalleryPlus\Http\ImageResponse;
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Environment\NotFoundEnvException;
use OCA\GalleryPlus\Preview\Preview;
use OCA\GalleryPlus\Utility\SmarterLogger;
@@ -29,112 +26,83 @@ use OCA\GalleryPlus\Utility\SmarterLogger;
*/
class PreviewService extends Service {
+ use Base64Encode;
+
/**
- * @type EnvironmentService
+ * @type Environment
*/
- private $environmentService;
+ private $environment;
/**
* @type Preview
*/
private $previewManager;
- /**
- * @type bool
- */
- private $animatedPreview = true;
- /**
- * @type bool
- */
- private $keepAspect = true;
- /**
- * @type bool
- */
- private $base64Encode = false;
- /**
- * @type bool
- */
- private $download = false;
/**
* Constructor
*
* @param string $appName
- * @param EnvironmentService $environmentService
- * @param SmarterLogger $logger
+ * @param Environment $environment
* @param Preview $previewManager
+ * @param SmarterLogger $logger
*/
public function __construct(
$appName,
- EnvironmentService $environmentService,
- SmarterLogger $logger,
- Preview $previewManager
+ Environment $environment,
+ Preview $previewManager,
+ SmarterLogger $logger
) {
parent::__construct($appName, $logger);
- $this->environmentService = $environmentService;
+ $this->environment = $environment;
$this->previewManager = $previewManager;
}
/**
- * @param string $image
- * @param int $maxX
- * @param int $maxY
- * @param bool $keepAspect
- *
- * @return array<string,\OC_Image|string> preview data
- */
- public function createThumbnails($image, $maxX, $maxY, $keepAspect) {
- $this->animatedPreview = false;
- $this->base64Encode = true;
- $this->keepAspect = $keepAspect;
-
- return $this->createPreview($image, $maxX, $maxY);
- }
-
-
- /**
- * Sends either a large preview of the requested file or the original file
- * itself
+ * Returns true if the passed mime type is supported
*
- * @param string $image
- * @param int $maxX
- * @param int $maxY
+ * @param string $mimeType
*
- * @return ImageResponse
+ * @return boolean
*/
- public function showPreview($image, $maxX, $maxY) {
- $preview = $this->createPreview($image, $maxX, $maxY);
-
- return new ImageResponse($preview, $preview['status']);
+ public function isMimeSupported($mimeType = '*') {
+ return $this->previewManager->isMimeSupported($mimeType);
}
/**
- * Downloads the requested file
+ * Decides if we should download the file instead of generating a preview
*
* @param string $image
+ * @param bool $animatedPreview
*
- * @return ImageResponse
+ * @return bool
*/
- public function downloadPreview($image) {
- $this->download = true;
+ public function isPreviewRequired($image, $animatedPreview) {
+ $file = null;
+ try {
+ /** @type File $file */
+ $file = $this->environment->getResourceFromPath($image);
+
+ } catch (NotFoundEnvException $exception) {
+ $this->logAndThrowNotFound($exception->getMessage());
+ }
+ $mime = $file->getMimeType();
+ if ($mime === 'image/svg+xml') {
+ return $this->isSvgPreviewRequired();
+ }
+ if ($mime === 'image/gif') {
+ return $this->isGifPreviewRequired($file, $animatedPreview);
+ }
- return $this->showPreview($image, null, null);
+ return true;
}
/**
- * Creates an array containing everything needed to render a preview in the
- * browser
- *
- * If the browser can use the file as-is or if we're asked to send it
- * as-is, then we simply let the browser download the file, straight from
- * Files
+ * Returns an array containing everything needed by the client to be able to display a preview
*
- * Some files are base64 encoded. Explicitly for files which are downloaded
- * (cached Thumbnails, SVG, GIFs) and via __toStrings for the previews
- * which are instances of \OC_Image
- *
- * We check that the preview returned by the Preview class can be used by
- * the browser. If not, we send the mime icon and change the status code so
- * that the client knows that the process failed
+ * * path: the given path to the file
+ * * mimetype: the file's media type
+ * * preview: the preview's content
+ * * status: a code indicating whether the conversion process was successful or not
*
* Example logger
* $this->logger->debug(
@@ -151,69 +119,100 @@ class PreviewService extends Service {
* @param string $image path to the image, relative to the user folder
* @param int $maxX asked width for the preview
* @param int $maxY asked height for the preview
+ * @param bool $keepAspect
+ *
+ * @return array <string,\OC_Image|string> preview data
+ */
+ public function createPreview($image, $maxX = 0, $maxY = 0, $keepAspect = true) {
+ $file = null;
+ try {
+ /** @type File $file */
+ $file = $this->environment->getResourceFromPath($image);
+ } catch (NotFoundEnvException $exception) {
+ $this->logAndThrowNotFound($exception->getMessage());
+ }
+ $userId = $this->environment->getUserID();
+ $imagePathFromFolder = $this->environment->getImagePathFromFolder($image);
+ $this->previewManager->setupView($userId, $file, $imagePathFromFolder);
+
+ $preview = $this->previewManager->preparePreview($maxX, $maxY, $keepAspect);
+ $preview['path'] = $image;
+ $preview['status'] = Service::STATUS_OK;
+ if (!$this->previewManager->isPreviewValid()) {
+ $preview['status'] = Service::STATUS_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ return $preview;
+ }
+
+ /**
+ * Decides if we should download the SVG or generate a preview
+ *
+ * SVGs are downloaded if the SVG converter is disabled
+ * Files of any media type are downloaded if requested by the client
*
- * @return array<string,\OC_Image|string> preview data
+ * @return bool
*/
- private function createPreview($image, $maxX = 0, $maxY = 0) {
- $env = $this->environmentService->getEnv();
- $owner = $env['owner'];
- $folder = $env['folder'];
- $imagePathFromFolder = $env['relativePath'] . $image;
- /** @type File $file */
- $file = $this->getResourceFromPath($folder, $imagePathFromFolder);
- $this->previewManager->setupView($owner, $file, $imagePathFromFolder);
- $previewRequired =
- $this->previewManager->previewRequired($this->animatedPreview, $this->download);
-
- if ($previewRequired) {
- $perfectPreview =
- $this->previewManager->preparePreview($maxX, $maxY, $this->keepAspect);
- } else {
- $perfectPreview = $this->prepareDownload($file, $image);
+ private function isSvgPreviewRequired() {
+ if (!$this->isMimeSupported('image/svg+xml')) {
+ return false;
}
- $perfectPreview['preview'] = $this->base64EncodeCheck($perfectPreview['preview']);
- $perfectPreview['path'] = $image;
- return $perfectPreview;
+ return true;
}
/**
- * Returns the data needed to make a file available for download
+ * Decides if we should download the GIF or generate a preview
+ *
+ * GIFs are downloaded if they're animated and we want to show
+ * animations
*
* @param File $file
- * @param string $image
+ * @param bool $animatedPreview
*
- * @return array
+ * @return bool
*/
- private function prepareDownload($file, $image) {
- $this->logger->debug("[PreviewService] Downloading file {file} as-is", ['file' => $image]);
-
- return [
- 'preview' => $file->getContent(),
- 'mimetype' => $file->getMimeType(),
- 'status' => Http::STATUS_OK
- ];
+ private function isGifPreviewRequired($file, $animatedPreview) {
+ $animatedGif = $this->isGifAnimated($file);
+
+ if ($animatedPreview && $animatedGif) {
+ return false;
+ }
+
+ return true;
}
/**
- * Returns base64 encoded data of a preview
+ * Tests if a GIF is animated
+ *
+ * An animated gif contains multiple "frames", with each frame having a
+ * header made up of:
+ * * a static 4-byte sequence (\x00\x21\xF9\x04)
+ * * 4 variable bytes
+ * * a static 2-byte sequence (\x00\x2C) (Photoshop uses \x00\x21)
+ *
+ * We read through the file until we reach the end of the file, or we've
+ * found at least 2 frame headers
*
- * @param \OC_Image|string $previewData
+ * @link http://php.net/manual/en/function.imagecreatefromgif.php#104473
*
- * @return \OC_Image|string
+ * @param File $file
+ *
+ * @return bool
*/
- private function base64EncodeCheck($previewData) {
- $base64Encode = $this->base64Encode;
-
- if ($base64Encode === true) {
- if ($previewData instanceof \OC_Image) {
- $previewData = (string)$previewData;
- } else {
- $previewData = base64_encode($previewData);
- }
+ private function isGifAnimated($file) {
+ $fileHandle = $file->fopen('rb');
+ $count = 0;
+ while (!feof($fileHandle) && $count < 2) {
+ $chunk = fread($fileHandle, 1024 * 100); //read 100kb at a time
+ $count += preg_match_all(
+ '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches
+ );
}
- return $previewData;
+ fclose($fileHandle);
+
+ return $count > 1;
}
} \ No newline at end of file
diff --git a/service/service.php b/service/service.php
index 725cc015..de142dab 100644
--- a/service/service.php
+++ b/service/service.php
@@ -12,12 +12,6 @@
namespace OCA\GalleryPlus\Service;
-use OCP\Files\Folder;
-use OCP\Files\Node;
-use OCP\Files\NotFoundException;
-
-use OCP\AppFramework\Http;
-
use OCA\GalleryPlus\Utility\SmarterLogger;
/**
@@ -27,10 +21,16 @@ use OCA\GalleryPlus\Utility\SmarterLogger;
*/
abstract class Service {
+ const STATUS_OK = 200;
+ const STATUS_NOT_FOUND = 404;
+ const STATUS_UNSUPPORTED_MEDIA_TYPE = 415;
+ const STATUS_UNPROCESSABLE_ENTITY = 422;
+ const STATUS_INTERNAL_SERVER_ERROR = 500;
+
/**
* @type string
*/
- protected $appName;
+ private $appName;
/**
* @type SmarterLogger
*/
@@ -42,86 +42,24 @@ abstract class Service {
* @param string $appName
* @param SmarterLogger $logger
*/
- public function __construct($appName, SmarterLogger $logger) {
+ public function __construct(
+ $appName,
+ SmarterLogger $logger
+ ) {
$this->appName = $appName;
$this->logger = $logger;
}
/**
- * Returns the resource identified by the given ID
- *
- * @param Folder $folder
- * @param int $resourceId
- *
- * @return Node
- * @throws ServiceException
- */
- protected function getResourceFromId($folder, $resourceId) {
- $resourcesArray = $folder->getById($resourceId);
- if ($resourcesArray[0] === null) {
- $this->kaBoom('Could not resolve linkItem', Http::STATUS_NOT_FOUND);
- }
-
- return $resourcesArray[0];
- }
-
- /**
- * Returns the resource located at the given path
- *
- * The path starts from the user's files folder
- * The resource is either a File or a Folder
- *
- * @param Folder $folder
- * @param string $path
- *
- * @return Node
- */
- protected function getResourceFromPath($folder, $path) {
- $nodeInfo = $this->getNodeInfo($folder, $path);
-
- return $this->getResourceFromId($folder, $nodeInfo['fileid']);
- }
-
- /**
- * Returns the Node based on the current user's files folder and a given
- * path
- *
- * @param Folder $folder
- * @param string $path
- *
- * @return int[]|false
- */
- protected function getNodeInfo($folder, $path) {
- $nodeInfo = false;
- try {
- $node = $folder->get($path);
- $nodeInfo = [
- 'fileid' => $node->getId(),
- 'permissions' => $node->getPermissions()
- ];
- } catch (NotFoundException $exception) {
- $message = $exception->getMessage();
- $code = Http::STATUS_NOT_FOUND;
- $this->kaBoom($message, $code);
- }
-
- return $nodeInfo;
- }
-
- /**
* Logs the error and raises an exception
*
* @param string $message
- * @param int $code
*
* @throws ServiceException
*/
- protected function kaBoom($message, $code) {
- $this->logger->error($message . ' (' . $code . ')');
+ protected function logAndThrowNotFound($message) {
+ $this->logger->error($message . ' (404)');
- throw new ServiceException(
- $message,
- $code
- );
+ throw new NotFoundServiceException($message);
}
} \ No newline at end of file
diff --git a/service/serviceexception.php b/service/serviceexception.php
index be78273a..81f24079 100644
--- a/service/serviceexception.php
+++ b/service/serviceexception.php
@@ -12,18 +12,19 @@
namespace OCA\GalleryPlus\Service;
+use Exception;
+
/**
* Thrown when the service cannot reply to a request
*/
-class ServiceException extends \Exception {
+class ServiceException extends Exception {
/**
* Constructor
*
* @param string $msg the message contained in the exception
- * @param int $code the HTTP status code
*/
- public function __construct($msg, $code = 0) {
- parent::__construct($msg, $code);
+ public function __construct($msg) {
+ parent::__construct($msg);
}
} \ No newline at end of file
diff --git a/service/thumbnailservice.php b/service/thumbnailservice.php
index 4a4980db..86922927 100644
--- a/service/thumbnailservice.php
+++ b/service/thumbnailservice.php
@@ -6,101 +6,54 @@
* later. See the COPYING file.
*
* @author Olivier Paroz <owncloud@interfasys.ch>
- * @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Olivier Paroz 2014-2015
- * @copyright Robin Appelman 2012-2015
*/
namespace OCA\GalleryPlus\Service;
-use OCP\IEventSource;
-
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Preview\Preview;
use OCA\GalleryPlus\Utility\SmarterLogger;
/**
- * Collects and returns thumbnails for the list of images which is submitted to
+ * Creates thumbnails for the list of images which is submitted to
* the service
*
* Uses EventSource to send back thumbnails as soon as they're ready
*
* @package OCA\GalleryPlus\Service
*/
-class ThumbnailService {
-
- /**
- * @type string
- */
- private $appName;
- /**
- * @type SmarterLogger
- */
- private $logger;
- /**
- * @type IEventSource
- */
- private $eventSource;
- /**
- * @type PreviewService
- */
- private $previewService;
-
+class ThumbnailService extends PreviewService {
/**
* Constructor
*
* @param string $appName
+ * @param Environment $environment
+ * @param Preview $previewManager
* @param SmarterLogger $logger
- * @param IEventSource $eventSource
- * @param PreviewService $previewService
*/
public function __construct(
$appName,
- SmarterLogger $logger,
- IEventSource $eventSource,
- PreviewService $previewService
+ Environment $environment,
+ Preview $previewManager,
+ SmarterLogger $logger
) {
- $this->appName = $appName;
- $this->logger = $logger;
- $this->previewService = $previewService;
- $this->eventSource = $eventSource;
- }
-
- /**
- * Sends previews of each image contained in the $images array using
- * EventSource
- *
- * The data is generated by the PreviewService and is base64 encoded
- *
- * WARNING: We can't close the session here because public galleries
- * of encrypted files would not get their previews
- *
- * FIXME: @LukasReschke says: The exit is required here because
- * otherwise the AppFramework is trying to add headers as well after
- * dispatching the request which results in a "Cannot modify header
- * information" notice.
- * WARNING: Returning a JSON response does not work.
- *
- * @param string $images
- * @param bool $square
- * @param bool $scale
- */
- public function getAlbumThumbnails($images, $square, $scale) {
- $imagesArray = explode(';', $images);
-
- $this->createThumbnails($imagesArray, $square, $scale);
-
- $this->eventSource->close();
-
- exit();
+ parent::__construct(
+ $appName,
+ $environment,
+ $previewManager,
+ $logger
+ );
}
/**
- * Asks the Thumbnail service to send us thumbnails
+ * Creates thumbnails of asked dimensions and aspect
*
- * Album thumnails need to be 200x200 and some will be resized by the
- * browser to 200x100 or 100x100.
- * Standard thumbnails are 400x200.
+ * * Album thumbnails need to be 200x200 and some will be resized by the
+ * browser to 200x100 or 100x100.
+ * * Standard thumbnails are 400x200.
*
* Sample logger
* We can't just send previewData as it can be quite a large stream
@@ -113,21 +66,23 @@ class ThumbnailService {
* ]
* );
*
- * @param string[] $imagesArray
+ * @param string $image
* @param bool $square
* @param bool $scale
+ *
+ * @return array
*/
- private function createThumbnails($imagesArray, $square, $scale) {
- foreach ($imagesArray as $image) {
- $height = 200 * $scale;
- if ($square) {
- $width = $height;
- } else {
- $width = 2 * $height;
- }
-
- $preview = $this->previewService->createThumbnails($image, $width, $height, !$square);
- $this->eventSource->send('preview', $preview);
+ public function createThumbnail($image, $square, $scale) {
+ $height = 200 * $scale;
+ if ($square) {
+ $width = $height;
+ } else {
+ $width = 2 * $height;
}
+ $preview = $this->createPreview($image, $width, $height, !$square);
+ $preview['preview'] = $this->encode($preview['preview']);
+
+ return $preview;
}
+
} \ No newline at end of file