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:
-rw-r--r--appinfo/application.php75
-rw-r--r--appinfo/routes.php24
-rw-r--r--controller/filescontroller.php129
-rw-r--r--controller/previewcontroller.php269
-rw-r--r--controller/publicfilescontroller.php36
-rw-r--r--controller/publicpreviewcontroller.php89
-rw-r--r--controller/publicservicecontroller.php6
-rw-r--r--controller/servicecontroller.php67
-rw-r--r--environment/environment.php29
-rw-r--r--js/album.js2
-rw-r--r--js/gallery.js37
-rw-r--r--js/slideshow.js28
-rw-r--r--service/configservice.php183
-rw-r--r--service/downloadservice.php50
-rw-r--r--service/filesservice.php294
-rw-r--r--service/infoservice.php459
-rw-r--r--service/previewservice.php91
-rw-r--r--service/service.php83
-rw-r--r--service/thumbnailservice.php9
19 files changed, 1404 insertions, 556 deletions
diff --git a/appinfo/application.php b/appinfo/application.php
index 16235903..8313bec4 100644
--- a/appinfo/application.php
+++ b/appinfo/application.php
@@ -20,11 +20,14 @@ use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use OCA\GalleryPlus\Controller\PageController;
-use OCA\GalleryPlus\Controller\ServiceController;
-use OCA\GalleryPlus\Controller\PublicServiceController;
+use OCA\GalleryPlus\Controller\FilesController;
+use OCA\GalleryPlus\Controller\PreviewController;
+use OCA\GalleryPlus\Controller\PublicFilesController;
+use OCA\GalleryPlus\Controller\PublicPreviewController;
use OCA\GalleryPlus\Environment\Environment;
use OCA\GalleryPlus\Preview\Preview;
-use OCA\GalleryPlus\Service\InfoService;
+use OCA\GalleryPlus\Service\FilesService;
+use OCA\GalleryPlus\Service\ConfigService;
use OCA\GalleryPlus\Service\ThumbnailService;
use OCA\GalleryPlus\Service\PreviewService;
use OCA\GalleryPlus\Service\DownloadService;
@@ -64,32 +67,52 @@ class Application extends App {
}
);
$container->registerService(
- 'ServiceController', function (IContainer $c) {
- return new ServiceController(
+ 'FilesController', function (IContainer $c) {
+ return new FilesController(
$c->query('AppName'),
$c->query('Request'),
- $c->query('Environment'),
- $c->query('InfoService'),
+ $c->query('FilesService'),
+ $c->query('ConfigService'),
+ $c->query('SmarterLogger')
+ );
+ }
+ );
+ $container->registerService(
+ 'PublicFilesController', function (IContainer $c) {
+ return new PublicFilesController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('FilesService'),
+ $c->query('ConfigService'),
+ $c->query('SmarterLogger')
+ );
+ }
+ );
+ $container->registerService(
+ 'PreviewController', function (IContainer $c) {
+ return new PreviewController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('OCP\IURLGenerator'),
$c->query('ThumbnailService'),
$c->query('PreviewService'),
$c->query('DownloadService'),
- $c->query('OCP\IURLGenerator'),
- $c->query('OCP\IEventSource')
+ $c->query('OCP\IEventSource'),
+ $c->query('SmarterLogger')
);
}
);
$container->registerService(
- 'PublicServiceController', function (IContainer $c) {
- return new PublicServiceController(
+ 'PublicPreviewController', function (IContainer $c) {
+ return new PublicPreviewController(
$c->query('AppName'),
$c->query('Request'),
- $c->query('Environment'),
- $c->query('InfoService'),
+ $c->query('OCP\IURLGenerator'),
$c->query('ThumbnailService'),
$c->query('PreviewService'),
$c->query('DownloadService'),
- $c->query('OCP\IURLGenerator'),
- $c->query('OCP\IEventSource')
+ $c->query('OCP\IEventSource'),
+ $c->query('SmarterLogger')
);
}
);
@@ -98,12 +121,12 @@ class Application extends App {
* Core
*/
$container->registerService(
- 'OCP\IServerContainer', function ($c) {
+ 'OCP\IServerContainer', function (IAppContainer $c) {
return $c->getServer();
}
);
$container->registerService(
- 'OCP\IEventSource', function ($c) {
+ 'OCP\IEventSource', function (IAppContainer $c) {
return $c->getServer()
->createEventSource();
}
@@ -186,27 +209,31 @@ class Application extends App {
* Services
*/
$container->registerService(
- 'InfoService', function (IContainer $c) {
- return new InfoService(
+ 'FilesService', function (IContainer $c) {
+ return new FilesService(
$c->query('AppName'),
- $c->query('PreviewService'),
+ $c->query('Environment'),
$c->query('SmarterLogger')
);
}
);
$container->registerService(
- 'ThumbnailService', function (IAppContainer $c) {
- return new ThumbnailService(
+ 'ConfigService', function (IContainer $c) {
+ return new ConfigService(
$c->query('AppName'),
$c->query('Environment'),
- $c->query('CustomPreviewManager'),
$c->query('SmarterLogger')
);
}
);
$container->registerService(
+ 'ThumbnailService', function () {
+ return new ThumbnailService();
+ }
+ );
+ $container->registerService(
'PreviewService', function (IContainer $c) {
return new PreviewService(
$c->query('AppName'),
@@ -259,7 +286,7 @@ class Application extends App {
}
);
- // executed in the order that it is registered
+ // Executed in the order that it is registered
$container->registerMiddleware('SharingCheckMiddleware');
$container->registerMiddleware('EnvCheckMiddleware');
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 00edbfd0..579d61bf 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -61,31 +61,31 @@ return [
*/
// Supported media types. Only called by the slideshow
[
- 'name' => 'service#get_types',
- 'url' => '/mimetypes',
+ 'name' => 'preview#get_media_types',
+ 'url' => '/mediatypes',
'verb' => 'GET'
],
// All the images of which a preview can be generated
[
- 'name' => 'service#get_files',
+ 'name' => 'files#get_files',
'url' => '/files',
'verb' => 'GET'
],
// Batch creation of thumbnails
[
- 'name' => 'service#get_thumbnails',
+ 'name' => 'preview#get_thumbnails',
'url' => '/thumbnails',
'verb' => 'GET'
],
// Large preview of a file
[
- 'name' => 'service#show_preview',
+ 'name' => 'preview#show_preview',
'url' => '/preview',
'verb' => 'GET'
],
// Download the file
[
- 'name' => 'service#download_preview',
+ 'name' => 'preview#download_preview',
'url' => '/download',
'verb' => 'GET'
],
@@ -93,27 +93,27 @@ return [
* Public services
*/
[
- 'name' => 'public_service#get_types',
- 'url' => '/mimetypes.public',
+ 'name' => 'public_preview#get_media_types',
+ 'url' => '/mediatypes.public',
'verb' => 'GET'
],
[
- 'name' => 'public_service#get_files',
+ 'name' => 'public_files#get_files',
'url' => '/files.public',
'verb' => 'GET'
],
[
- 'name' => 'public_service#get_thumbnails',
+ 'name' => 'public_preview#get_thumbnails',
'url' => '/thumbnails.public',
'verb' => 'GET'
],
[
- 'name' => 'public_service#show_preview',
+ 'name' => 'public_preview#show_preview',
'url' => '/preview.public',
'verb' => 'GET'
],
[
- 'name' => 'public_service#download_preview',
+ 'name' => 'public_preview#download_preview',
'url' => '/download.public',
'verb' => 'GET'
],
diff --git a/controller/filescontroller.php b/controller/filescontroller.php
new file mode 100644
index 00000000..7e6c032b
--- /dev/null
+++ b/controller/filescontroller.php
@@ -0,0 +1,129 @@
+<?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 Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Olivier Paroz 2014-2015
+ * @copyright Robin Appelman 2012-2014
+ */
+
+namespace OCA\GalleryPlus\Controller;
+
+use OCP\IRequest;
+use OCP\Files\Folder;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+
+use OCA\GalleryPlus\Service\FilesService;
+use OCA\GalleryPlus\Service\ConfigService;
+use OCA\GalleryPlus\Utility\SmarterLogger;
+
+/**
+ * Class FilesController
+ *
+ * @package OCA\GalleryPlus\Controller
+ */
+class FilesController extends Controller {
+
+ use JsonHttpError;
+
+ /**
+ * @type FilesService
+ */
+ private $filesService;
+ /**
+ * @type ConfigService
+ */
+ private $configService;
+
+ /**
+ * Constructor
+ *
+ * @param string $appName
+ * @param IRequest $request
+ * @param FilesService $filesService
+ * @param ConfigService $configService
+ * @param SmarterLogger $logger
+ */
+ public function __construct(
+ $appName,
+ IRequest $request,
+ FilesService $filesService,
+ ConfigService $configService,
+ SmarterLogger $logger
+ ) {
+ parent::__construct($appName, $request);
+
+ $this->filesService = $filesService;
+ $this->configService = $configService;
+ //$this->logger = $logger;
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * Returns a list of all media files available to the authenticated user
+ *
+ * Authentication can be via a login/password or a token/(password)
+ *
+ * For private galleries, it returns all media files, with the full path
+ * from the root folder
+ * For public galleries, the path starts from the folder the link
+ * gives access to (virtual root)
+ *
+ * An exception is only caught in case something really wrong happens. As we don't test files
+ * before including them in the list, we may return some bad apples
+ *
+ * @param string $location a path representing the current album in the app
+ *
+ * @return array<string,array<string,string|int>>|Http\JSONResponse
+ */
+ public function getFiles($location) {
+ $mediaTypesArray = explode(';', $this->request->getParam('mediatypes'));
+ try {
+ /** @type Folder $folderNode */
+ list($folderPathFromRoot, $folderNode, $locationHasChanged) =
+ $this->filesService->getCurrentFolder(rawurldecode($location));
+ if (is_null($folderNode)) {
+ // Something very wrong has just happened
+ return new JSONResponse(['message' => 'Oh Nooooes!', 'success' => false], 500);
+ }
+ list($albumInfo, $privateAlbum) =
+ $this->configService->getAlbumInfo($folderNode, $folderPathFromRoot);
+ if ($privateAlbum) {
+ return new JSONResponse(['message' => 'Album is private', 'success' => false], 403);
+ }
+ $files = $this->filesService->getMediaFiles($folderNode, $mediaTypesArray);
+
+ return $this->formatResults($files, $albumInfo, $locationHasChanged);
+ } catch (\Exception $exception) {
+ return $this->error($exception);
+ }
+ }
+
+ /**
+ * Simply builds and returns an array containing the list of files, the album information and
+ * whether the location has changed or not
+ *
+ * @param $files
+ * @param $albumInfo
+ * @param $locationHasChanged
+ *
+ * @return array
+ */
+ private function formatResults($files, $albumInfo, $locationHasChanged) {
+ return [
+ 'files' => $files,
+ 'albuminfo' => $albumInfo,
+ 'locationhaschanged' => $locationHasChanged
+ ];
+ }
+
+}
diff --git a/controller/previewcontroller.php b/controller/previewcontroller.php
new file mode 100644
index 00000000..57f2afc7
--- /dev/null
+++ b/controller/previewcontroller.php
@@ -0,0 +1,269 @@
+<?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 Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Olivier Paroz 2014-2015
+ * @copyright Robin Appelman 2012-2014
+ */
+
+namespace OCA\GalleryPlus\Controller;
+
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IEventSource;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+
+use OCA\GalleryPlus\Http\ImageResponse;
+use OCA\GalleryPlus\Service\ServiceException;
+use OCA\GalleryPlus\Service\ThumbnailService;
+use OCA\GalleryPlus\Service\PreviewService;
+use OCA\GalleryPlus\Service\DownloadService;
+use OCA\GalleryPlus\Utility\SmarterLogger;
+
+/**
+ * Class PreviewController
+ *
+ * @package OCA\GalleryPlus\Controller
+ */
+class PreviewController extends Controller {
+
+ use JsonHttpError;
+
+ /**
+ * @type IURLGenerator
+ */
+ private $urlGenerator;
+ /**
+ * @type ThumbnailService
+ */
+ private $thumbnailService;
+ /**
+ * @type PreviewService
+ */
+ private $previewService;
+ /**
+ * @type DownloadService
+ */
+ private $downloadService;
+ /**
+ * @type IEventSource
+ */
+ private $eventSource;
+ /**
+ * @type SmarterLogger
+ */
+ private $logger;
+
+ /**
+ * Constructor
+ *
+ * @param string $appName
+ * @param IRequest $request
+ * @param IURLGenerator $urlGenerator
+ * @param ThumbnailService $thumbnailService
+ * @param PreviewService $previewService
+ * @param DownloadService $downloadService
+ * @param IEventSource $eventSource
+ * @param SmarterLogger $logger
+ */
+ public function __construct(
+ $appName,
+ IRequest $request,
+ IURLGenerator $urlGenerator,
+ ThumbnailService $thumbnailService,
+ PreviewService $previewService,
+ DownloadService $downloadService,
+ IEventSource $eventSource,
+ SmarterLogger $logger
+ ) {
+ parent::__construct($appName, $request);
+
+ $this->urlGenerator = $urlGenerator;
+ $this->thumbnailService = $thumbnailService;
+ $this->previewService = $previewService;
+ $this->downloadService = $downloadService;
+ $this->eventSource = $eventSource;
+ $this->logger = $logger;
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * Sends back a list of all media types supported by the system, as well as the name of their
+ * icon
+ *
+ * @param bool $slideshow
+ *
+ * @return array <string,string>|null
+ */
+ public function getMediaTypes($slideshow = false) {
+ return $this->previewService->getSupportedMediaTypes($slideshow);
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * Generates thumbnails
+ *
+ * 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 get rid of the problem
+ *
+ * @param string $images
+ * @param bool $square
+ * @param bool $scale
+ *
+ * @return array<string,array|string>
+ */
+ public function getThumbnails($images, $square, $scale) {
+ $imagesArray = explode(';', $images);
+
+ foreach ($imagesArray as $image) {
+ $thumbnail = $this->getThumbnail($image, $square, $scale);
+ $this->eventSource->send('preview', $thumbnail);
+ }
+ $this->eventSource->close();
+
+ exit();
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * 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 ImageResponse|Http\JSONResponse
+ */
+ public function showPreview($file, $x, $y) {
+ try {
+ $preview = $this->getPreview($file, $x, $y);
+
+ return new ImageResponse($preview['data'], $preview['status']);
+ } catch (ServiceException $exception) {
+ return $this->error($exception);
+ }
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * Downloads the file
+ *
+ * @param string $file
+ *
+ * @return \OCA\GalleryPlus\Http\ImageResponse|Http\JSONResponse
+ */
+ public function downloadPreview($file) {
+ try {
+ $download = $this->downloadService->downloadFile($file);
+
+ return new ImageResponse($download);
+ } catch (ServiceException $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
+ * Thumbnails are base64encoded before getting sent back
+ *
+ * @param string $image
+ * @param bool $square
+ * @param bool $scale
+ *
+ * @return array<string,array|string>
+ */
+ private function getThumbnail($image, $square, $scale) {
+ list($width, $height, $aspect, $animatedPreview, $base64Encode) =
+ $this->thumbnailService->getThumbnailSpecs($square, $scale);
+
+ try {
+ $preview = $this->getPreview(
+ $image, $width, $height, $aspect, $animatedPreview, $base64Encode
+ );
+ } catch (ServiceException $exception) {
+ $preview = ['data' => null, 'status' => 500, 'type' => 'error'];
+ }
+ $thumbnail = $preview['data'];
+ if ($preview['status'] === 200 && $preview['type'] === 'preview') {
+ $thumbnail['preview'] = $this->previewService->previewValidator($square, $base64Encode);
+ }
+ $thumbnail['status'] = $preview['status'];
+
+ return $thumbnail;
+ }
+
+ /**
+ * Returns either a generated preview (or the mime-icon when the preview generation fails)
+ * or the file as-is
+ *
+ * Sample logger
+ * We can't just send the preview array as it can contain quite a large data stream
+ * $this->logger->debug("[Batch] THUMBNAIL NAME : {image} / PATH : {path} /
+ * MIME : {mimetype} / DATA : {preview}", [
+ * 'image' => $preview['data']['image'],
+ * 'path' => $preview['data']['path'],
+ * 'mimetype' => $preview['data']['mimetype'],
+ * 'preview' => substr($preview['data']['preview'], 0, 20),
+ * ]
+ * );
+ *
+ * @param string $image
+ * @param int $width
+ * @param int $height
+ * @param bool $keepAspect
+ * @param bool $animatedPreview
+ * @param bool $base64Encode
+ *
+ * @return array<string,\OC_Image|string>
+ */
+ private function getPreview(
+ $image, $width, $height, $keepAspect = true, $animatedPreview = true, $base64Encode = false
+ ) {
+ $status = Http::STATUS_OK;
+ $previewRequired = $this->previewService->isPreviewRequired($image, $animatedPreview);
+ if ($previewRequired) {
+ $type = 'preview';
+ $preview = $this->previewService->createPreview(
+ $image, $width, $height, $keepAspect, $base64Encode
+ );
+ if (!$this->previewService->isPreviewValid()) {
+ $type = 'error';
+ $status = Http::STATUS_NOT_FOUND;
+ }
+ } else {
+ $type = 'download';
+ $preview = $this->downloadService->downloadFile($image, $base64Encode);
+ }
+
+ return [
+ 'data' => $preview,
+ 'status' => $status,
+ 'type' => $type
+ ];
+ }
+
+}
diff --git a/controller/publicfilescontroller.php b/controller/publicfilescontroller.php
new file mode 100644
index 00000000..ab48b2ee
--- /dev/null
+++ b/controller/publicfilescontroller.php
@@ -0,0 +1,36 @@
+<?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\Controller;
+
+/**
+ * Class PublicFilesController
+ *
+ * Note: Type casting only works if the "@param" parameters are also included in this class as
+ * their not yet inherited
+ *
+ * @package OCA\GalleryPlus\Controller
+ */
+class PublicFilesController extends FilesController {
+
+ /**
+ * @PublicPage
+ *
+ * Returns a list of all images from the folder the link gives access to
+ *
+ * @inheritDoc
+ */
+ public function getFiles($location) {
+ return parent::getFiles($location);
+ }
+
+}
diff --git a/controller/publicpreviewcontroller.php b/controller/publicpreviewcontroller.php
new file mode 100644
index 00000000..2a6d57f3
--- /dev/null
+++ b/controller/publicpreviewcontroller.php
@@ -0,0 +1,89 @@
+<?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\Controller;
+
+/**
+ * Class PublicPreviewController
+ *
+ * Note: Type casting only works if the "@param" parameters are also included in this class as
+ * their not yet inherited
+ *
+ * @package OCA\GalleryPlus\Controller
+ */
+class PublicPreviewController extends PreviewController {
+
+ /**
+ * @PublicPage
+ *
+ * @inheritDoc
+ *
+ * @param bool $slideshow
+ */
+ public function getMediaTypes($slideshow = false) {
+ return parent::getMediaTypes($slideshow);
+ }
+
+ /**
+ * @PublicPage
+ * @UseSession
+ *
+ * Generates thumbnails for public galleries
+ *
+ * The session needs to be maintained open or previews can't be generated
+ * for files located on encrypted storage
+ *
+ * @inheritDoc
+ *
+ * @param string $images
+ * @param bool $square
+ * @param bool $scale
+ */
+ public function getThumbnails($images, $square, $scale) {
+ return parent::getThumbnails($images, $square, $scale);
+ }
+
+ /**
+ * @PublicPage
+ * @UseSession
+ *
+ * Shows a large preview of a file
+ *
+ * The session needs to be maintained open or previews can't be generated
+ * for files located on encrypted storage
+ *
+ * @inheritDoc
+ *
+ * @param string $file
+ * @param int $x
+ * @param int $y
+ */
+ public function showPreview($file, $x, $y) {
+ return parent::showPreview($file, $x, $y);
+ }
+
+ /**
+ * @PublicPage
+ * @UseSession
+ *
+ * Downloads the file
+ *
+ * The session needs to be maintained open or previews can't be generated
+ * for files located on encrypted storage
+ *
+ * @inheritDoc
+ */
+ public function downloadPreview($file) {
+ return parent::downloadPreview($file);
+ }
+
+}
diff --git a/controller/publicservicecontroller.php b/controller/publicservicecontroller.php
index d5587fb3..c5142567 100644
--- a/controller/publicservicecontroller.php
+++ b/controller/publicservicecontroller.php
@@ -35,8 +35,8 @@ class PublicServiceController extends ServiceController {
*
* @inheritDoc
*/
- public function getFiles($location) {
- return parent::getFiles($location);
+ public function getImages() {
+ return parent::getImages();
}
/**
@@ -84,4 +84,4 @@ class PublicServiceController extends ServiceController {
return parent::downloadPreview($file);
}
-}
+} \ No newline at end of file
diff --git a/controller/servicecontroller.php b/controller/servicecontroller.php
index 6243cfbd..ebab195b 100644
--- a/controller/servicecontroller.php
+++ b/controller/servicecontroller.php
@@ -20,7 +20,6 @@ use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\JSONResponse;
use OCA\GalleryPlus\Environment\Environment;
use OCA\GalleryPlus\Environment\EnvironmentException;
@@ -107,6 +106,28 @@ class ServiceController extends Controller {
/**
* @NoAdminRequired
*
+ * Returns information about an album, based on its path
+ *
+ * Used to see if album thumbnails should be generated for a specific folder
+ *
+ * @param string $albumpath
+ *
+ * @return false|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 $nodeInfo;
+ } catch (EnvironmentException $exception) {
+ return $this->error($exception);
+ }
+ }
+
+ /**
+ * @NoAdminRequired
+ *
* Sends back a list of all media types supported by the system
*
* @return string[]
@@ -125,27 +146,19 @@ class ServiceController extends Controller {
* 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
*
- * @param string $location a path representing the current album in the app
- *
- * @return array<string,array<string,string|int>>|Http\JSONResponse
+ * @return array|Http\JSONResponse
*/
- public function getFiles($location) {
+ public function getImages() {
try {
- $imagesFolder = $this->environment->getResourceFromPath($location);
+ $imagesFolder = $this->environment->getResourceFromPath('');
$fromRootToFolder = $this->environment->getFromRootToFolder();
- list($albumInfo, $privateAlbum) =
- $this->infoService->getAlbumInfo($imagesFolder, $fromRootToFolder);
- if ($privateAlbum) {
- return new JSONResponse(['message' => 'Album is private', 'success' => false], 403);
- }
$folderData = [
'imagesFolder' => $imagesFolder,
'fromRootToFolder' => $fromRootToFolder,
];
- $files = $this->infoService->getImages($folderData);
- return ['files' => $files, 'albuminfo' => $albumInfo];
+ return $this->infoService->getImages($folderData);
} catch (EnvironmentException $exception) {
return $this->error($exception);
}
@@ -221,7 +234,7 @@ class ServiceController extends Controller {
$download = $this->downloadService->downloadFile($file);
return new ImageResponse($download);
- } catch (ServiceException $exception) {
+ } catch (EnvironmentException $exception) {
return $this->error($exception);
}
}
@@ -236,23 +249,19 @@ class ServiceController extends Controller {
* @param bool $square
* @param bool $scale
*
- * @return array<string,array|string>
+ * @return array|Http\JSONResponse
*/
private function getThumbnail($image, $square, $scale) {
- list($width, $height, $aspect, $animatedPreview, $base64Encode) =
- $this->thumbnailService->getThumbnailSpecs($square, $scale);
-
+ $thumbSpecs = $this->thumbnailService->getThumbnailSpecs($square, $scale);
try {
$preview = $this->getPreview(
- $image, $width, $height, $aspect, $animatedPreview, $base64Encode
+ $image, $thumbSpecs['width'], $thumbSpecs['height'],
+ $thumbSpecs['aspect'], $thumbSpecs['animatedPreview'], $thumbSpecs['base64Encode']
);
} catch (ServiceException $exception) {
- $preview = ['data' => null, 'status' => 500, 'type' => 'error'];
+ return $this->error($exception);
}
$thumbnail = $preview['data'];
- if ($preview['status'] === 200 && $preview['type'] === 'preview') {
- $thumbnail['preview'] = $this->previewService->previewValidator($square, $base64Encode);
- }
$thumbnail['status'] = $preview['status'];
return $thumbnail;
@@ -280,7 +289,7 @@ class ServiceController extends Controller {
* @param bool $animatedPreview
* @param bool $base64Encode
*
- * @return array<string,\OC_Image|string>
+ * @return mixed
*/
private function getPreview(
$image, $width, $height, $keepAspect = true, $animatedPreview = true, $base64Encode = false
@@ -288,24 +297,20 @@ class ServiceController extends Controller {
$status = Http::STATUS_OK;
$previewRequired = $this->previewService->isPreviewRequired($image, $animatedPreview);
if ($previewRequired) {
- $type = 'preview';
$preview = $this->previewService->createPreview(
$image, $width, $height, $keepAspect, $base64Encode
);
if (!$this->previewService->isPreviewValid()) {
- $type = 'error';
- $status = Http::STATUS_NOT_FOUND;
+ $status = Http::STATUS_UNSUPPORTED_MEDIA_TYPE;
}
} else {
- $type = 'download';
$preview = $this->downloadService->downloadFile($image, $base64Encode);
}
return [
'data' => $preview,
- 'status' => $status,
- 'type' => $type
+ 'status' => $status
];
}
-}
+} \ No newline at end of file
diff --git a/environment/environment.php b/environment/environment.php
index 776114da..bc2147eb 100644
--- a/environment/environment.php
+++ b/environment/environment.php
@@ -129,7 +129,7 @@ class Environment {
* 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
+ * so that the services can use one method to have access to resources
* without having to know whether they're private or public
*/
public function setStandardEnv() {
@@ -139,8 +139,8 @@ class Environment {
/**
* 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
+ * The path starts from the user's files folder because we'll query that folder to get the
+ * information we need. The resource is either a File or a Folder
*
* @param string $subPath
*
@@ -254,14 +254,27 @@ class Environment {
}
/**
- * Returns fromRootToFolder
+ * Returns the path which goes from the file, up to the root folder of the Gallery:
+ * current_folder/my_file
*
- * @see buildFromRootToFolder
+ * That root folder changes when folders are shared publicly
+ *
+ * @param Node $file
*
* @return string
*/
- public function getFromRootToFolder() {
- return $this->fromRootToFolder;
+ public function getPathFromVirtualRoot($file) {
+ $path = $file->getPath();
+
+ if ($file->getType() === 'dir') {
+ // Needed because fromRootToFolder always ends with a slash
+ $path .= '/';
+ }
+
+ $path = str_replace($this->fromRootToFolder, '', $path);
+ $path = rtrim($path, '/');
+
+ return $path;
}
/**
@@ -291,6 +304,8 @@ class Environment {
* Returns the path from the shared folder to the root folder in the original
* owner's filesystem: /userId/files/parent_folder/shared_folder
*
+ * This cannot be calculated with paths and IDs, the linkitem's file source is required
+ *
* @param string $fileSource
*
* @return string
diff --git a/js/album.js b/js/album.js
index 38a31c23..4c89f030 100644
--- a/js/album.js
+++ b/js/album.js
@@ -212,6 +212,7 @@ Album.prototype = {
* @returns {$.Deferred<Row>}
*/
getNextRow: function (width) {
+ var numberOfThumbnailsToPreload = 6;
/**
* Add images to the row until it's full
*
@@ -224,7 +225,6 @@ Album.prototype = {
* @returns {$.Deferred<Row>}
*/
var addRowElements = function (album, row, images) {
- var numberOfThumbnailsToPreload = 6;
if ((album.viewedItems + 5) > album.preloadOffset) {
album._preload(numberOfThumbnailsToPreload);
}
diff --git a/js/gallery.js b/js/gallery.js
index 76a108f8..6422d39e 100644
--- a/js/gallery.js
+++ b/js/gallery.js
@@ -1,5 +1,6 @@
/* global OC, $, _, t, Album, GalleryImage, SlideShow, oc_requesttoken, marked */
var Gallery = {};
+Gallery.mediaTypes = {};
Gallery.images = [];
Gallery.currentAlbum = null;
Gallery.users = [];
@@ -11,6 +12,20 @@ Gallery.token = undefined;
Gallery.currentSort = {};
/**
+ * Returns a list of supported media types
+ *
+ * @returns {string}
+ */
+Gallery.getMediaTypes = function () {
+ var types = '';
+ for (var i = 0, keys = Object.keys(Gallery.mediaTypes); i < keys.length; i++) {
+ types += keys[i] + ';';
+ }
+
+ return types.slice(0, -1);
+};
+
+/**
* Builds a map of the albums located in the current folder
*
* @param {string} path
@@ -63,8 +78,13 @@ Gallery.fillAlbums = function () {
Gallery.images = [];
Gallery.albumMap = {};
Gallery.imageMap = {};
- var currentFolder = decodeURI(window.location.href.split('#')[1] || '');
- var url = Gallery.buildUrl('files', '', {location: currentFolder});
+ var currentLocation = decodeURI(window.location.href.split('#')[1] || '');
+ var params = {
+ location: currentLocation,
+ mediatypes: Gallery.getMediaTypes()
+ };
+ // Only use the folder as a GET parameter and not as part of the URL
+ var url = Gallery.buildUrl('files', '', params);
return $.getJSON(url).then(function (data) {
var path = null;
var fileId = null;
@@ -738,9 +758,16 @@ $(document).ready(function () {
oc_requesttoken = Gallery.view.element.data('requesttoken');
}
- Gallery.fillAlbums().then(function () {
- window.onhashchange();
- });
+ $.getJSON(Gallery.buildUrl('mediatypes', '', {}))
+ .then(function (mediaTypes) {
+ //console.log('mediaTypes', mediaTypes);
+ Gallery.mediaTypes = mediaTypes;
+ })
+ .then(function () {
+ Gallery.fillAlbums().then(function () {
+ window.onhashchange();
+ });
+ });
$('#openAsFileListButton').click(function () {
var subUrl = '';
diff --git a/js/slideshow.js b/js/slideshow.js
index 2cd68225..9d9fda9c 100644
--- a/js/slideshow.js
+++ b/js/slideshow.js
@@ -29,6 +29,8 @@ var SlideShow = function (container, images, interval, maxScale) {
this.smallImageScale = 2;
};
+SlideShow.mediaTypes = {};
+
SlideShow.prototype = {
/**
*
@@ -338,7 +340,7 @@ SlideShow.prototype = {
}
}.bind(this);
if (mimeType === 'image/svg+xml') {
- image.src = this.getSVG(url);
+ image.src = this._getSVG(url);
} else {
image.src = url;
}
@@ -352,7 +354,7 @@ SlideShow.prototype = {
*
* @returns {*}
*/
- getSVG: function (source) {
+ _getSVG: function (source) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", source, false);
xmlHttp.send(null);
@@ -678,19 +680,19 @@ $(document).ready(function () {
});
};
- var url = SlideShow.buildUrl('mimetypes', {});
- // We're asking for a list of supported mimes. Images are given through the context
- $.getJSON(url).then(function (supportedMimes) {
-
- //console.log("enabledPreviewProviders: ", supportedMimes);
+ var url = SlideShow.buildUrl('mediatypes', '', {slideshow: 1});
+ // We're asking for a list of supported media types. Media files are retrieved through the
+ // context
+ $.getJSON(url).then(function (mediaTypes) {
+ //console.log("enabledPreviewProviders: ", mediaTypes);
+ SlideShow.mediaTypes = mediaTypes;
// We only want to create slideshows for supported media types
- for (var m = 0; m < supportedMimes.length; ++m) {
- var mime = supportedMimes[m];
- // Each click handler gets the same function and images array and is responsible to
- // load the slideshow
- prepareFileActions(mime);
- OCA.Files.fileActions.setDefault(mime, 'View');
+ for (var i = 0, keys = Object.keys(mediaTypes); i < keys.length; i++) {
+ // Each click handler gets the same function and images array and
+ // is responsible to load the slideshow
+ prepareFileActions(keys[i]);
+ OCA.Files.fileActions.setDefault(keys[i], 'View');
}
});
}
diff --git a/service/configservice.php b/service/configservice.php
new file mode 100644
index 00000000..34c167e5
--- /dev/null
+++ b/service/configservice.php
@@ -0,0 +1,183 @@
+<?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 Symfony\Component\Yaml\Yaml;
+
+use OCP\Files\Folder;
+use OCP\Files\File;
+
+/**
+ * Finds configurations files, parses them and returns a configuration array
+ *
+ * Checks the current and parent folders for configuration files
+ *
+ * @package OCA\GalleryPlus\Service
+ */
+class ConfigService extends Service {
+
+ /**
+ * Returns information about the currently selected folder
+ *
+ * * privacy setting
+ * * special configuration
+ * * permissions
+ * * ID
+ *
+ * @param Folder $folderNode
+ * @param string $folderPathFromRoot
+ *
+ * @return array<string,string|int>
+ */
+ public function getAlbumInfo($folderNode, $folderPathFromRoot) {
+ $configName = 'gallery.cnf';
+ $privacyChecker = '.nomedia';
+ $albumInfo = [];
+ list ($albumConfig, $privateAlbum) =
+ $this->getAlbumConfig($folderNode, $privacyChecker, $configName);
+
+ if (!$privateAlbum) {
+ $albumInfo = [
+ 'path' => $folderPathFromRoot,
+ 'fileid' => $folderNode->getID(),
+ 'permissions' => $folderNode->getPermissions()
+ ];
+ $albumInfo = array_merge($albumInfo, $albumConfig);
+ }
+
+ return [$albumInfo, $privateAlbum];
+ }
+
+ /**
+ * Returns an album configuration array
+ *
+ * @param Folder $folder
+ * @param string $privacyChecker
+ * @param string $configName
+ * @param int $level
+ * @param array $configArray
+ * @param bool $configComplete
+ *
+ * @return array <null|string,string>
+ */
+ private function getAlbumConfig(
+ $folder, $privacyChecker, $configName, $level = 0, $configArray = [],
+ $configComplete = false
+ ) {
+ if ($folder->nodeExists($privacyChecker)) {
+ // Cancel as soon as we find out that the folder is private
+ return [null, true];
+ }
+ list($configArray, $configComplete) =
+ $this->parseConfig($folder, $configName, $configArray, $configComplete, $level);
+ $parentFolder = $folder->getParent();
+ $path = $parentFolder->getPath();
+ if ($path !== '' && $path !== '/') {
+ $level++;
+
+ return $this->getAlbumConfig(
+ $parentFolder, $privacyChecker, $configName, $level, $configArray, $configComplete
+ );
+ }
+
+ // We have reached the root folder
+ return [$configArray, false];
+ }
+
+ /**
+ * Returns a parsed configuration if one was found in the current folder
+ *
+ * @param Folder $folder
+ * @param string $configName
+ * @param array $currentConfigArray
+ * @param bool $configComplete
+ * @param int $level
+ *
+ * @return array<array,bool>
+ */
+ private function parseConfig(
+ $folder, $configName, $currentConfigArray, $configComplete, $level
+ ) {
+ $configArray = $currentConfigArray;
+ // Let's try to find the missing information in the configuration located in this folder
+ if (!$configComplete && $folder->nodeExists($configName)) {
+ /** @type File $configFile */
+ $configFile = $folder->get($configName);
+ try {
+ $rawConfig = $configFile->getContent();
+ $saneConfig = $this->bomFixer($rawConfig);
+ $parsedConfigArray = Yaml::parse($saneConfig);
+ list($configArray, $configComplete) =
+ $this->validateConfig($currentConfigArray, $parsedConfigArray, $level);
+ } catch (\Exception $exception) {
+ $this->logger->error(
+ "Problem while parsing the configuration file : {path}",
+ ['path' => $folder->getPath() . '/' . $configFile->getPath()]
+ );
+ }
+ }
+
+ return [$configArray, $configComplete];
+ }
+
+ /**
+ * Removes the BOM from a file
+ *
+ * http://us.php.net/manual/en/function.pack.php#104151
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ private function bomFixer($file) {
+ $bom = pack("CCC", 0xef, 0xbb, 0xbf);
+ if (strncmp($file, $bom, 3) === 0) {
+ $file = substr($file, 3);
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns either the local config or one merged with a config containing sorting information
+ *
+ * @param array $currentConfigArray
+ * @param array $parsedConfigArray
+ * @param int $level
+ *
+ * @return array<array,bool>
+ */
+ private function validateConfig($currentConfigArray, $parsedConfigArray, $level) {
+ $configComplete = false;
+ $sorting = $parsedConfigArray['sorting'];
+ $sortOrder = $parsedConfigArray['sort_order'];
+ $configArray = $parsedConfigArray;
+ if ($sorting) {
+ $configComplete = true;
+ if ($level > 0) {
+ // We only need the sorting information
+ $currentConfigArray['sorting'] = $sorting;
+ $currentConfigArray['sort_order'] = $sortOrder;
+ $configArray = $currentConfigArray;
+ }
+ } else {
+ if ($level > 0) {
+ // Reset the array to what we had earlier since we didn't find any sorting information
+ $configArray = $currentConfigArray;
+ }
+ }
+
+ return [$configArray, $configComplete];
+ }
+
+}
diff --git a/service/downloadservice.php b/service/downloadservice.php
index 669fb061..0e0074ad 100644
--- a/service/downloadservice.php
+++ b/service/downloadservice.php
@@ -14,9 +14,6 @@ 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
@@ -28,53 +25,34 @@ 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
+ *
+ * @throws NotFoundServiceException
*/
public function downloadFile($image, $base64Encode = false) {
+ $this->logger->debug("[DownloadService] File to Download: $image");
$file = null;
+ $download = false;
try {
/** @type File $file */
$file = $this->environment->getResourceFromPath($image);
- } catch (NotFoundEnvException $exception) {
+ $download = [
+ 'path' => $image,
+ 'preview' => $file->getContent(),
+ 'mimetype' => $file->getMimeType()
+ ];
+
+ if ($base64Encode) {
+ $download['preview'] = $this->encode($download['preview']);
+ }
+ } catch (\Exception $exception) {
$this->logAndThrowNotFound($exception->getMessage());
}
- $this->logger->debug("[DownloadService] File to Download: $image");
- $download = [
- 'path' => $image,
- 'preview' => $file->getContent(),
- 'mimetype' => $file->getMimeType()
- ];
-
- if ($base64Encode) {
- $download['preview'] = $this->encode($download['preview']);
- }
return $download;
}
diff --git a/service/filesservice.php b/service/filesservice.php
new file mode 100644
index 00000000..56bfe327
--- /dev/null
+++ b/service/filesservice.php
@@ -0,0 +1,294 @@
+<?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;
+
+use OCP\Files\Folder;
+use OCP\Files\File;
+use OCP\Files\Node;
+
+/**
+ * Contains various methods which provide initial information about the
+ * supported media types, the folder permissions and the images contained in
+ * the system
+ *
+ * @package OCA\GalleryPlus\Service
+ */
+class FilesService extends Service {
+
+ /**
+ * @type null|array<string,string|int>
+ */
+ private $images = [];
+ /**
+ * @type string[]
+ */
+ private $supportedMediaTypes;
+
+ /**
+ * This returns the list of all media files which can be shown starting from the given folder
+ *
+ * @param Folder $folder
+ * @param string[] $supportedMediaTypes
+ *
+ * @return array<string,string|int> all the images we could find
+ */
+ public function getMediaFiles($folder, $supportedMediaTypes) {
+ $this->supportedMediaTypes = $supportedMediaTypes;
+
+ $this->searchFolder($folder);
+
+ return $this->images;
+ }
+
+ /**
+ * Look for media files and folders in the given folder
+ *
+ * @param Folder $folder
+ * @param int $subDepth
+ *
+ * @return int
+ */
+ private function searchFolder($folder, $subDepth = 0) {
+ $albumImageCounter = 0;
+ $subFolders = [];
+
+ $nodes = $this->getNodes($folder, $subDepth);
+ foreach ($nodes as $node) {
+ //$this->logger->debug("Sub-Node path : {path}", ['path' => $node->getPath()]);
+ $nodeType = $this->getNodeType($node);
+ $subFolders = array_merge($subFolders, $this->isFolder($node, $nodeType));
+
+ if ($nodeType === 'file') {
+ $albumImageCounter = $albumImageCounter + (int)$this->isPreviewAvailable($node);
+ if ($this->haveEnoughPictures($albumImageCounter, $subDepth)) {
+ break;
+ }
+ }
+ }
+ $this->searchSubFolders($subFolders, $subDepth, $albumImageCounter);
+
+ return $albumImageCounter;
+ }
+
+ /**
+ * Retrieves all files and sub-folders contained in a folder
+ *
+ * If we can't find anything in the current folder, we throw an exception as there is no point
+ * in doing any more work, but if we're looking at a sub-folder, we return an empty array so
+ * that it can be simply ignored
+ *
+ * @param Folder $folder
+ * @param int $subDepth
+ *
+ * @return array
+ *
+ * @throws NotFoundServiceException
+ */
+ private function getNodes($folder, $subDepth) {
+ $nodes = [];
+ try {
+ if ($folder->isReadable()
+ && $folder->getStorage()
+ ->isLocal()
+ ) {
+ $nodes = $folder->getDirectoryListing();
+ }
+ } catch (\Exception $exception) {
+ $nodes = $this->recoverFromGetNodesError($subDepth, $exception);
+ }
+
+ return $nodes;
+ }
+
+ /**
+ * Throws an exception if this problem occurs in the current folder, otherwise just ignores the
+ * sub-folder
+ *
+ * @param int $subDepth
+ * @param \Exception $exception
+ *
+ * @return array
+ * @throws NotFoundServiceException
+ */
+ private function recoverFromGetNodesError($subDepth, $exception) {
+ if ($subDepth === 0) {
+ $this->logAndThrowNotFound($exception->getMessage());
+ }
+
+ return [];
+ }
+
+ /**
+ * Returns the node type, either 'dir' or 'file'
+ *
+ * If there is a problem, we return an empty string so that the node can be ignored
+ *
+ * @param Node $node
+ *
+ * @return string
+ */
+ private function getNodeType($node) {
+ try {
+ $nodeType = $node->getType();
+ } catch (\Exception $exception) {
+ return '';
+ }
+
+ return $nodeType;
+ }
+
+ /**
+ * Returns the node if it's a folder we have access to
+ *
+ * @param Folder $node
+ * @param string $nodeType
+ *
+ * @return array|Folder
+ */
+ private function isFolder($node, $nodeType) {
+ if ($nodeType === 'dir') {
+ return [$node];
+ }
+
+ return [];
+ }
+
+ /**
+ * Checks if we've collected enough pictures to be able to build the view
+ *
+ * An album is full when we find max 4 pictures at the same level
+ *
+ * @param int $albumImageCounter
+ * @param int $subDepth
+ *
+ * @return bool
+ */
+ private function haveEnoughPictures($albumImageCounter, $subDepth) {
+ if ($subDepth === 0) {
+ return false;
+ }
+ if ($albumImageCounter === 4) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Looks for pictures in sub-folders
+ *
+ * If we're at level 0, we need to look for pictures in sub-folders no matter what
+ * If we're at deeper levels, we only need to go further if we haven't managed to find one
+ * picture in the current folder
+ *
+ * @param array<Folder> $subFolders
+ * @param int $subDepth
+ * @param int $albumImageCounter
+ */
+ private function searchSubFolders($subFolders, $subDepth, $albumImageCounter) {
+ if ($this->folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter)) {
+ $subDepth++;
+ foreach ($subFolders as $subFolder) {
+ $count = $this->searchFolder($subFolder, $subDepth);
+ if ($this->abortSearch($subDepth, $count)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if we need to look for media files in the specified folder
+ *
+ * @param array<Folder> $subFolders
+ * @param int $subDepth
+ * @param int $albumImageCounter
+ *
+ * @return bool
+ */
+ private function folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter) {
+ if (!empty($subFolders) && ($subDepth === 0 || $albumImageCounter === 0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if there is no need to check any other sub-folder at the same depth level
+ *
+ * @param int $subDepth
+ * @param int $count
+ *
+ * @return bool
+ */
+ private function abortSearch($subDepth, $count) {
+ if ($subDepth > 1 && $count > 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the file is of a supported media type and adds it to the array of items to
+ * return
+ *
+ * @todo We could potentially check if the file is readable ($file->stat() maybe) in order to
+ * only return valid files, but this may slow down operations
+ *
+ * @param File $file the file to test
+ *
+ * @return bool
+ */
+ private function isPreviewAvailable($file) {
+ try {
+ $mimeType = $file->getMimetype();
+ $isLocal = $file->getStorage()
+ ->isLocal();
+ if ($isLocal && in_array($mimeType, $this->supportedMediaTypes)) {
+ $this->addFileToResults($file);
+
+ return true;
+ }
+ } catch (\Exception $exception) {
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds various information about a file to the list of results
+ *
+ * @param File $file
+ */
+ private function addFileToResults($file) {
+ $imagePath = $this->environment->getPathFromVirtualRoot($file);
+ $imageId = $file->getId();
+ $mimeType = $file->getMimetype();
+ $mTime = $file->getMTime();
+
+ $imageData = [
+ 'path' => $imagePath,
+ 'fileid' => $imageId,
+ 'mimetype' => $mimeType,
+ 'mtime' => $mTime
+ ];
+
+ $this->images[] = $imageData;
+
+ //$this->logger->debug("Image path : {path}", ['path' => $imagePath]);
+ }
+
+}
diff --git a/service/infoservice.php b/service/infoservice.php
index f89545bb..0e8d82ee 100644
--- a/service/infoservice.php
+++ b/service/infoservice.php
@@ -12,11 +12,8 @@
namespace OCA\GalleryPlus\Service;
-use Symfony\Component\Yaml\Yaml;
-
use OCP\Files\Folder;
use OCP\Files\File;
-use OCP\Files\Node;
use OCA\GalleryPlus\Utility\SmarterLogger;
@@ -60,18 +57,6 @@ class InfoService extends Service {
'application/font-sfnt',
'application/x-font',
];
- /**
- * @type array<string, string|int>
- */
- private $images = [];
- /**
- * @type string[]
- */
- private $supportedMimes;
- /**
- * @type string
- */
- private $fromRootToFolder;
/**
* Constructor
@@ -122,423 +107,93 @@ class InfoService extends Service {
}
/**
- * Returns information about the currently selected folder
- *
- * * privacy setting
- * * special configuration
- * * permissions
- * * ID
- *
- * @param Folder $folderNode
- * @param string $folderPathFromRoot
- *
- * @return array<string,string|int>
- */
- public function getAlbumInfo($folderNode, $folderPathFromRoot) {
- $configName = 'gallery.cnf';
- $privacyChecker = '.nomedia';
- $albumInfo = [];
- list ($albumConfig, $privateAlbum) =
- $this->getAlbumConfig($folderNode, $privacyChecker, $configName);
-
- if (!$privateAlbum) {
- $path = str_replace($folderPathFromRoot, '', $folderNode->getPath());
- if (rtrim($folderPathFromRoot, '/') === $folderNode->getPath()) {
- $path = '';
- }
- $albumInfo = [
- 'path' => $path,
- 'fileid' => $folderNode->getID(),
- 'permissions' => $folderNode->getPermissions()
- ];
- $albumInfo = array_merge($albumInfo, $albumConfig);
- }
-
- return [$albumInfo, $privateAlbum];
- }
-
- /**
* This returns the list of all images which can be shown starting from the given folder
*
- * If the starting URL is one of a fullscreen preview, we'll return the images of the
- * containing folder
- *
- * @param array <Node, string> $folderData
+ * @param array $folderData
*
- * @return array<string,string|int> all the images we could find
+ * @return array all the images we could find
*/
public function getImages($folderData) {
- $this->supportedMimes = $this->getSupportedMimes(false);
- $this->fromRootToFolder = $folderData['fromRootToFolder'];
-
- /** @type Node $node */
- $node = $folderData['imagesFolder'];
- if ($node->getType() === 'dir') {
- $this->searchFolder($node);
- } else {
- $this->searchFolder($node->getParent());
- }
-
- return $this->images;
- }
-
- /**
- * Returns an album configuration array
- *
- * @param Folder $folder
- * @param string $privacyChecker
- * @param string $configName
- * @param int $level
- * @param array $configArray
- * @param bool $configComplete
- *
- * @return array <null|string,string>
- */
- private function getAlbumConfig(
- $folder, $privacyChecker, $configName, $level = 0, $configArray = [],
- $configComplete = false
- ) {
- if ($folder->nodeExists($privacyChecker)) {
- // Cancel as soon as we find out that the folder is private
- return [null, true];
- }
- list($configArray, $configComplete) =
- $this->parseConfig($folder, $configName, $configArray, $configComplete, $level);
- $parentFolder = $folder->getParent();
- $path = $parentFolder->getPath();
- if ($path !== '' && $path !== '/') {
- $level++;
-
- return $this->getAlbumConfig(
- $parentFolder, $privacyChecker, $configName, $level, $configArray, $configComplete
- );
- }
-
- // We have reached the root folder
- return [$configArray, false];
- }
-
- /**
- * Returns a parsed configuration if one was found in the current folder
- *
- * @param Folder $folder
- * @param string $configName
- * @param array $currentConfigArray
- * @param bool $configComplete
- * @param int $level
- *
- * @return array<array,bool>
- */
- private function parseConfig(
- $folder, $configName, $currentConfigArray, $configComplete, $level
- ) {
- $configArray = $currentConfigArray;
- // Let's try to find the missing information in the configuration located in this folder
- if (!$configComplete && $folder->nodeExists($configName)) {
- /** @type File $configFile */
- $configFile = $folder->get($configName);
- try {
- $rawConfig = $configFile->getContent();
- $saneConfig = $this->bomFixer($rawConfig);
- $parsedConfigArray = Yaml::parse($saneConfig);
- list($configArray, $configComplete) =
- $this->validateConfig($currentConfigArray, $parsedConfigArray, $level);
- } catch (\Exception $exception) {
- $this->logger->error(
- "Problem while parsing the configuration file : {path}",
- ['path' => $folder->getPath() . '/' . $configFile->getPath()]
- );
- }
- }
-
- return [$configArray, $configComplete];
- }
-
- /**
- * Removes the BOM from a file
- *
- * http://us.php.net/manual/en/function.pack.php#104151
- *
- * @param string $file
- *
- * @return string
- */
- private function bomFixer($file) {
- $bom = pack("CCC", 0xef, 0xbb, 0xbf);
- if (strncmp($file, $bom, 3) === 0) {
- $file = substr($file, 3);
- }
-
- return $file;
- }
-
- /**
- * Returns either the local config or one merged with a config containing sorting information
- *
- * @param array $currentConfigArray
- * @param array $parsedConfigArray
- * @param int $level
- *
- * @return array<array,bool>
- */
- private function validateConfig($currentConfigArray, $parsedConfigArray, $level) {
- $configComplete = false;
- $sorting = $parsedConfigArray['sorting'];
- $sortOrder = $parsedConfigArray['sort_order'];
- $configArray = $parsedConfigArray;
- if ($sorting) {
- $configComplete = true;
- if ($level > 0) {
- // We only need the sorting information
- $currentConfigArray['sorting'] = $sorting;
- $currentConfigArray['sort_order'] = $sortOrder;
- $configArray = $currentConfigArray;
- }
- } else {
- if ($level > 0) {
- // Reset the array to what we had earlier since we didn't find any sorting information
- $configArray = $currentConfigArray;
- }
- }
-
- return [$configArray, $configComplete];
- }
-
- /**
- * Look for media files and folders in the given folder
- *
- * @param Folder $folder
- * @param int $subDepth
- *
- * @return int
- */
- private function searchFolder($folder, $subDepth = 0) {
- $albumImageCounter = 0;
- $subFolders = [];
+ $images = $this->searchByMime($folderData['imagesFolder']);
+ $fromRootToFolder = $folderData['fromRootToFolder'];
- $nodes = $this->getNodes($folder, $subDepth);
- foreach ($nodes as $node) {
- //$this->logger->debug("Sub-Node path : {path}", ['path' => $node->getPath()]);
- $nodeType = $this->getNodeType($node);
- $subFolders = array_merge($subFolders, $this->allowedSubFolder($node, $nodeType));
+ $result = $this->prepareImagesArray($images, $fromRootToFolder);
- if ($nodeType === 'file') {
- $albumImageCounter = $albumImageCounter + (int)$this->isPreviewAvailable($node);
- if ($this->haveEnoughPictures($albumImageCounter, $subDepth)) {
- break;
- }
- }
- }
- $this->searchSubFolders($subFolders, $subDepth, $albumImageCounter);
-
- return $albumImageCounter;
- }
-
- /**
- * Retrieves all files and sub-folders contained in a folder
- *
- * If we can't find anything in the current folder, we throw an exception as there is no point
- * in doing any more work, but if we're looking at a sub-folder, we return an empty array so
- * that it can be simply ignored
- *
- * @param Folder $folder
- * @param int $subDepth
- *
- * @return array
- *
- * @throws NotFoundServiceException
- */
- private function getNodes($folder, $subDepth) {
- $nodes = [];
- try {
- if ($folder->isReadable()
- && $folder->getStorage()
- ->isLocal()
- ) {
- $nodes = $folder->getDirectoryListing();
- }
- } catch (\Exception $exception) {
- $nodes = $this->recoverFromGetNodesError($subDepth, $exception);
- }
+ //$this->logger->debug("Images array: {images}", ['images' => $result]);
- return $nodes;
+ return $result;
}
/**
- * Throws an exception if this problem occurs in the current folder, otherwise just ignores the
- * sub-folder
+ * Returns all the images of which we can generate a preview
*
- * @param int $subDepth
- * @param \Exception $exception
+ * @param Folder $imagesFolder
*
* @return array
- * @throws NotFoundServiceException
- */
- private function recoverFromGetNodesError($subDepth, $exception) {
- if ($subDepth === 0) {
- $this->logAndThrowNotFound($exception->getMessage());
- }
-
- return [];
- }
-
- /**
- * Returns the node type, either 'dir' or 'file'
- *
- * If there is a problem, we return an empty string so that the node can be ignored
- *
- * @param Node $node
- *
- * @return string
- */
- private function getNodeType($node) {
- try {
- $nodeType = $node->getType();
- } catch (\Exception $exception) {
- return '';
- }
-
- return $nodeType;
- }
-
- /**
- * Returns the node if it's a folder we have access to
- *
- * @param Folder $node
- * @param string $nodeType
- *
- * @return array|Folder
- */
- private function allowedSubFolder($node, $nodeType) {
- if ($nodeType === 'dir') {
- /** @type Folder $node */
- if (!$node->nodeExists('.nomedia')) {
- return [$node];
- }
- }
-
- return [];
- }
-
- /**
- * Checks if we've collected enough pictures to be able to build the view
- *
- * An album is full when we find max 4 pictures at the same level
- *
- * @param int $albumImageCounter
- * @param int $subDepth
- *
- * @return bool
*/
- private function haveEnoughPictures($albumImageCounter, $subDepth) {
- if ($subDepth === 0) {
- return false;
- }
- if ($albumImageCounter === 4) {
- return true;
- }
+ private function searchByMime($imagesFolder) {
+ $images = [];
+ $mimes = $this->getSupportedMimes(false);
- return false;
- }
+ foreach ($mimes as $mime) {
+ /**
+ * We look for images of this media type in the whole system.
+ * This can lead to performance issues
+ *
+ * @todo Use an internal Class to solve the performance issue
+ */
+ $mimeImages = $imagesFolder->searchByMime($mime);
- /**
- * Looks for pictures in sub-folders
- *
- * If we're at level 0, we need to look for pictures in sub-folders no matter what
- * If we're at deeper levels, we only need to go further if we haven't managed to find one
- * picture in the current folder
- *
- * @param array <Folder> $subFolders
- * @param int $subDepth
- * @param int $albumImageCounter
- */
- private function searchSubFolders($subFolders, $subDepth, $albumImageCounter) {
- if ($this->folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter)) {
- $subDepth++;
- foreach ($subFolders as $subFolder) {
- $count = $this->searchFolder($subFolder, $subDepth);
- if ($this->abortSearch($subDepth, $count)) {
- break;
- }
- }
+ $images = array_merge($images, $mimeImages);
}
- }
- /**
- * Checks if we need to look for media files in the specified folder
- *
- * @param array <Folder> $subFolders
- * @param int $subDepth
- * @param int $albumImageCounter
- *
- * @return bool
- */
- private function folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter) {
- if (!empty($subFolders) && ($subDepth === 0 || $albumImageCounter === 0)) {
- return true;
- }
-
- return false;
+ return $images;
}
/**
- * Returns true if there is no need to check any other sub-folder at the same depth level
- *
- * @param int $subDepth
- * @param int $count
- *
- * @return bool
- */
- private function abortSearch($subDepth, $count) {
- if ($subDepth > 1 && $count > 0) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Returns true if the file is of a supported media type and adds it to the array of items to
- * return
+ * Fixes the path of each image we've found
*
* 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 We could potentially check if the file is readable ($file->stat() maybe) in order to
- * only return valid files, but this may slow down operations
+ * @todo Test this on OC8
+ * On OC7, we fix searchByMime which returns images from the rubbish bin...
+ * https://github.com/owncloud/core/issues/4903
*
- * @param File $file the file to test
+ * Example logger
+ * $this->logger->debug(
+ * "folderPath: {folderPath} pathRelativeToFolder: {pathRelativeToFolder}
+ * imagePath: {imagePath} mime: {mime}", [
+ * 'folderPath' => $folderPath,
+ * 'pathRelativeToFolder' => $pathRelativeToFolder,
+ * 'imagePath' => $imagePath,
+ * 'mime' => $mimeType
+ * ]
+ * );
*
- * @return bool
+ * @param array $images
+ * @param string $fromRootToFolder
+ *
+ * @return array
*/
- private function isPreviewAvailable($file) {
- try {
- $mimeType = $file->getMimetype();
- $isLocal = $file->getStorage()
- ->isLocal();
- if ($isLocal && in_array($mimeType, $this->supportedMimes)) {
- $imagePath = $file->getPath();
- $fixedPath = str_replace($this->fromRootToFolder, '', $imagePath);
- $imageId = $file->getId();
- $mTime = $file->getMTime();
- $imageData = [
- 'path' => $fixedPath,
- 'fileid' => $imageId,
- 'mimetype' => $mimeType,
- 'mtime' => $mTime
- ];
- $this->images[] = $imageData;
-
- /*$this->logger->debug(
- "Image path : {path}", ['path' => $imagePath]
- );*/
-
- return true;
+ private function prepareImagesArray($images, $fromRootToFolder) {
+ $result = [];
+ /** @type File $image */
+ foreach ($images as $image) {
+ $imagePath = $image->getPath();
+ $mimeType = $image->getMimetype();
+ $fixedPath = str_replace($fromRootToFolder, '', $imagePath);
+ if (substr($fixedPath, 0, 9) === "_trashbin") {
+ continue;
}
- } catch (\Exception $exception) {
- return false;
+ $imageData = [
+ 'path' => $fixedPath,
+ 'mimetype' => $mimeType
+ ];
+ $result[] = $imageData;
}
- return false;
+ return $result;
}
-}
+
+} \ No newline at end of file
diff --git a/service/previewservice.php b/service/previewservice.php
index f9608827..0484e20d 100644
--- a/service/previewservice.php
+++ b/service/previewservice.php
@@ -13,6 +13,7 @@
namespace OCA\GalleryPlus\Service;
use OCP\Files\File;
+use OCP\Template;
use OCA\GalleryPlus\Environment\Environment;
use OCA\GalleryPlus\Environment\NotFoundEnvException;
@@ -29,13 +30,36 @@ class PreviewService extends Service {
use Base64Encode;
/**
- * @type Environment
- */
- private $environment;
- /**
* @type Preview
*/
private $previewManager;
+ /**
+ * @todo This hard-coded array could be replaced by admin settings
+ *
+ * @type string[]
+ */
+ private $baseMimeTypes = [
+ 'image/png',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/x-xbitmap',
+ 'image/bmp',
+ 'image/tiff',
+ 'image/x-dcraw',
+ 'application/x-photoshop',
+ 'application/illustrator',
+ 'application/postscript',
+ ];
+ /**
+ * These types are useful for files preview in the files app, but
+ * not for the gallery side
+ *
+ * @type string[]
+ */
+ private $slideshowMimeTypes = [
+ 'application/font-sfnt',
+ 'application/x-font',
+ ];
/**
* Constructor
@@ -51,21 +75,45 @@ class PreviewService extends Service {
Preview $previewManager,
SmarterLogger $logger
) {
- parent::__construct($appName, $logger);
+ parent::__construct($appName, $environment, $logger);
- $this->environment = $environment;
$this->previewManager = $previewManager;
}
/**
- * Returns true if the passed mime type is supported
+ * This builds and returns a list of all supported media types
*
- * @param string $mimeType
+ * @todo Native SVG could be disabled via admin settings
*
- * @return boolean
+ * @param bool $slideshow
+ *
+ * @return string[] all supported media types
*/
- public function isMimeSupported($mimeType = '*') {
- return $this->previewManager->isMimeSupported($mimeType);
+ public function getSupportedMediaTypes($slideshow) {
+ $supportedMimes = [];
+ $wantedMimes = $this->baseMimeTypes;
+
+ if ($slideshow) {
+ $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
+ if ($this->isMimeSupported($wantedMime)) {
+ $pathToIcon = Template::mimetype_icon($wantedMime);
+ $supportedMimes[$wantedMime] =
+ $pathToIcon; // We add it to the list of supported media types
+ }
+ }
+ // If it's enabled, but doesn't work, an exception will be raised.
+ // If it's disabled, we support it via the browser's native support
+ if (!$supportedMimes['image/svg+xml']) {
+ $supportedMimes['image/svg+xml'] = Template::mimetype_icon('image/svg+xml');
+ }
+
+ $this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]);
+
+ return $supportedMimes;
}
/**
@@ -75,6 +123,8 @@ class PreviewService extends Service {
* @param bool $animatedPreview
*
* @return bool
+ *
+ * @throws NotFoundServiceException
*/
public function isPreviewRequired($image, $animatedPreview) {
$file = null;
@@ -176,6 +226,25 @@ class PreviewService extends Service {
}
/**
+ * Returns true if the passed mime type is supported
+ *
+ * In case of a failure, we just return that the media type is not supported
+ *
+ * @param string $mimeType
+ *
+ * @return boolean
+ */
+ private function isMimeSupported($mimeType = '*') {
+ try {
+ return $this->previewManager->isMimeSupported($mimeType);
+ } catch (\Exception $exception) {
+ unset($exception);
+
+ return false;
+ }
+ }
+
+ /**
* Decides if we should download the SVG or generate a preview
*
* SVGs are downloaded if the SVG converter is disabled
diff --git a/service/service.php b/service/service.php
index bb9b229f..11d970c5 100644
--- a/service/service.php
+++ b/service/service.php
@@ -12,6 +12,10 @@
namespace OCA\GalleryPlus\Service;
+use OCP\Files\Folder;
+
+use OCA\GalleryPlus\Environment\Environment;
+use OCA\GalleryPlus\Environment\NotFoundEnvException;
use OCA\GalleryPlus\Utility\SmarterLogger;
/**
@@ -24,7 +28,11 @@ abstract class Service {
/**
* @type string
*/
- private $appName;
+ protected $appName;
+ /**
+ * @type Environment
+ */
+ protected $environment;
/**
* @type SmarterLogger
*/
@@ -34,26 +42,95 @@ abstract class Service {
* Constructor
*
* @param string $appName
+ * @param Environment $environment
* @param SmarterLogger $logger
*/
public function __construct(
$appName,
+ Environment $environment,
SmarterLogger $logger
) {
$this->appName = $appName;
+ $this->environment = $environment;
$this->logger = $logger;
}
/**
- * Logs the error and raises an exception
+ * This returns the current folder node based on a path
+ *
+ * If the path leads to a file, we'll return the node of the containing folder
+ *
+ * If we can't find anything, we try with the parent folder, up to the root or until we reach
+ * our recursive limit
+ *
+ * @param string $location
+ * @param int $depth
+ *
+ * @return array <Folder,string,bool>
+ */
+ public function getCurrentFolder($location, $depth = 0) {
+ $node = null;
+ $location = $this->validateLocation($location, $depth);
+ try {
+ $node = $this->environment->getResourceFromPath($location);
+ if ($node->getType() === 'file') {
+ $node = $node->getParent();
+ }
+ } catch (NotFoundEnvException $exception) {
+ // There might be a typo in the file or folder name
+ $folder = pathinfo($location, PATHINFO_DIRNAME);
+ $depth++;
+
+ return $this->getCurrentFolder($folder, $depth);
+ }
+ $path = $this->environment->getPathFromVirtualRoot($node);
+ $locationHasChanged = $this->hasLocationChanged($depth);
+
+ return [$path, $node, $locationHasChanged];
+ }
+
+ /**
+ * Logs the error and raises a "Not found" type exception
*
* @param string $message
*
- * @throws ServiceException
+ * @throws NotFoundServiceException
*/
protected function logAndThrowNotFound($message) {
$this->logger->error($message . ' (404)');
throw new NotFoundServiceException($message);
}
+
+ /**
+ * Makes sure we don't go too far up before giving up
+ *
+ * @param string $location
+ * @param int $depth
+ *
+ * @return string
+ */
+ private function validateLocation($location, $depth) {
+ if ($depth === 4) {
+ // We can't find anything, so we decide to return data for the root folder
+ $location = '';
+ }
+
+ return $location;
+ }
+
+ /**
+ * @param $depth
+ *
+ * @return bool
+ */
+ private function hasLocationChanged($depth) {
+ $locationHasChanged = false;
+ if ($depth > 0) {
+ $locationHasChanged = true;
+ }
+
+ return $locationHasChanged;
+ }
+
}
diff --git a/service/thumbnailservice.php b/service/thumbnailservice.php
index e2f4c4d1..b0103fe2 100644
--- a/service/thumbnailservice.php
+++ b/service/thumbnailservice.php
@@ -12,15 +12,8 @@
namespace OCA\GalleryPlus\Service;
-use OCA\GalleryPlus\Environment\Environment;
-use OCA\GalleryPlus\Preview\Preview;
-use OCA\GalleryPlus\Utility\SmarterLogger;
-
/**
- * 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
+ * Deals with any thumbnail specific requests
*
* @package OCA\GalleryPlus\Service
*/