diff --git a/lib/ClustersBackend/FaceRecognitionBackend.php b/lib/ClustersBackend/FaceRecognitionBackend.php index 12b5ca23..222db480 100644 --- a/lib/ClustersBackend/FaceRecognitionBackend.php +++ b/lib/ClustersBackend/FaceRecognitionBackend.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace OCA\Memories\ClustersBackend; use OCA\Memories\Db\TimelineQuery; -use OCA\Memories\Db\TimelineRoot; use OCA\Memories\Util; use OCP\IConfig; @@ -32,7 +31,6 @@ class FaceRecognitionBackend extends Backend { use PeopleBackendUtils; - public TimelineRoot $root; protected TimelineQuery $timelineQuery; protected IConfig $config; @@ -58,14 +56,14 @@ class FaceRecognitionBackend extends Backend public function getClusters(): array { return array_merge( - $this->timelineQuery->getFaceRecognitionPersons($this->root, $this->model()), - $this->timelineQuery->getFaceRecognitionClusters($this->root, $this->model()) + $this->timelineQuery->getFaceRecognitionPersons($this->model()), + $this->timelineQuery->getFaceRecognitionClusters($this->model()) ); } public function getPhotos(string $name, ?int $limit = null): array { - return $this->timelineQuery->getFaceRecognitionPhotos($name, $this->model(), $this->root, $limit) ?? []; + return $this->timelineQuery->getFaceRecognitionPhotos($name, $this->model(), $limit) ?? []; } public function sortPhotosForPreview(array &$photos) diff --git a/lib/ClustersBackend/PlacesBackend.php b/lib/ClustersBackend/PlacesBackend.php index 1f75b4ff..0d038926 100644 --- a/lib/ClustersBackend/PlacesBackend.php +++ b/lib/ClustersBackend/PlacesBackend.php @@ -24,12 +24,10 @@ declare(strict_types=1); namespace OCA\Memories\ClustersBackend; use OCA\Memories\Db\TimelineQuery; -use OCA\Memories\Db\TimelineRoot; use OCA\Memories\Util; class PlacesBackend extends Backend { - public TimelineRoot $root; protected TimelineQuery $timelineQuery; public function __construct( @@ -50,11 +48,11 @@ class PlacesBackend extends Backend public function getClusters(): array { - return $this->timelineQuery->getPlaces($this->root); + return $this->timelineQuery->getPlaces(); } public function getPhotos(string $name, ?int $limit = null): array { - return $this->timelineQuery->getPlacePhotos((int) $name, $this->root, $limit) ?? []; + return $this->timelineQuery->getPlacePhotos((int) $name, $limit) ?? []; } } diff --git a/lib/ClustersBackend/RecognizeBackend.php b/lib/ClustersBackend/RecognizeBackend.php index efd8a4e5..cd8fb29e 100644 --- a/lib/ClustersBackend/RecognizeBackend.php +++ b/lib/ClustersBackend/RecognizeBackend.php @@ -24,14 +24,12 @@ declare(strict_types=1); namespace OCA\Memories\ClustersBackend; use OCA\Memories\Db\TimelineQuery; -use OCA\Memories\Db\TimelineRoot; use OCA\Memories\Util; class RecognizeBackend extends Backend { use PeopleBackendUtils; - public TimelineRoot $root; protected TimelineQuery $timelineQuery; public function __construct( @@ -52,12 +50,12 @@ class RecognizeBackend extends Backend public function getClusters(): array { - return $this->timelineQuery->getPeopleRecognize($this->root, Util::getUID()); + return $this->timelineQuery->getPeopleRecognize(Util::getUID()); } public function getPhotos(string $name, ?int $limit = null): array { - return $this->timelineQuery->getPeopleRecognizePhotos((int) $name, $this->root, $limit) ?? []; + return $this->timelineQuery->getPeopleRecognizePhotos((int) $name, $limit) ?? []; } public function sortPhotosForPreview(array &$photos) diff --git a/lib/ClustersBackend/TagsBackend.php b/lib/ClustersBackend/TagsBackend.php index 17fbd330..26853876 100644 --- a/lib/ClustersBackend/TagsBackend.php +++ b/lib/ClustersBackend/TagsBackend.php @@ -24,12 +24,10 @@ declare(strict_types=1); namespace OCA\Memories\ClustersBackend; use OCA\Memories\Db\TimelineQuery; -use OCA\Memories\Db\TimelineRoot; use OCA\Memories\Util; class TagsBackend extends Backend { - public TimelineRoot $root; protected TimelineQuery $timelineQuery; public function __construct( @@ -50,11 +48,11 @@ class TagsBackend extends Backend public function getClusters(): array { - return $this->timelineQuery->getTags($this->root); + return $this->timelineQuery->getTags(); } public function getPhotos(string $name, ?int $limit = null): array { - return $this->timelineQuery->getTagPhotos($name, $this->root, $limit) ?? []; + return $this->timelineQuery->getTagPhotos($name, $limit) ?? []; } } diff --git a/lib/Controller/ClustersController.php b/lib/Controller/ClustersController.php index 73a1f2f6..c81ece18 100644 --- a/lib/Controller/ClustersController.php +++ b/lib/Controller/ClustersController.php @@ -123,13 +123,6 @@ class ClustersController extends GenericApiController if (!$this->backend->isEnabled()) { throw Exceptions::NotEnabled($this->backend->appName()); } - - if (property_exists($this->backend, 'root')) { - $this->backend->root = $this->getRequestRoot(); - if ($this->backend->root->isEmpty()) { - throw Exceptions::NoRequestRoot(); - } - } } /** diff --git a/lib/Controller/DaysController.php b/lib/Controller/DaysController.php index be6f2e58..35020d08 100644 --- a/lib/Controller/DaysController.php +++ b/lib/Controller/DaysController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace OCA\Memories\Controller; -use OCA\Memories\Db\TimelineRoot; use OCA\Memories\Exceptions; use OCA\Memories\Util; use OCP\AppFramework\Http; @@ -42,10 +41,8 @@ class DaysController extends GenericApiController { return Util::guardEx(function () { $uid = $this->getShareToken() ? '' : Util::getUID(); - $root = $this->getRequestRoot(); $list = $this->timelineQuery->getDays( - $root, $uid, $this->isRecursive(), $this->isArchive(), @@ -57,7 +54,7 @@ class DaysController extends GenericApiController $list = $this->timelineQuery->daysToMonths($list); } else { // Preload some day responses - $this->preloadDays($list, $uid, $root); + $this->preloadDays($list, $uid); } // Reverse response if requested. Folders still stay at top. @@ -67,6 +64,7 @@ class DaysController extends GenericApiController // Add subfolder info if querying non-recursively if (!$this->isRecursive()) { + $root = $this->timelineQuery->root(); array_unshift($list, $this->getSubfoldersEntry($root->getFolder($root->getOneId()))); } @@ -98,9 +96,6 @@ class DaysController extends GenericApiController return new JSONResponse([], Http::STATUS_OK); } - // Get the folder to show - $root = $this->getRequestRoot(); - // Convert to actual dayIds if month view if ($this->isMonthView()) { $dayIds = $this->timelineQuery->monthIdToDayIds((int) $dayIds[0]); @@ -108,7 +103,6 @@ class DaysController extends GenericApiController // Run actual query $list = $this->timelineQuery->getDay( - $root, $uid, $dayIds, $this->isRecursive(), @@ -158,17 +152,9 @@ class DaysController extends GenericApiController { $transforms = []; - // Add extra information, basename and mimetype - if (!$aggregateOnly && ($fields = $this->request->getParam('fields'))) { - $fields = explode(',', $fields); - $transforms[] = [$this->timelineQuery, 'transformExtraFields', $fields]; - } - // Filter for one album - if (Util::albumsIsEnabled()) { - if ($albumId = $this->request->getParam('album')) { - $transforms[] = [$this->timelineQuery, 'transformAlbumFilter', $albumId]; - } + if (($albumId = $this->request->getParam('album')) && Util::albumsIsEnabled()) { + $transforms[] = [$this->timelineQuery, 'transformAlbumFilter', $albumId]; } // Other transforms not allowed for public shares @@ -235,11 +221,10 @@ class DaysController extends GenericApiController /** * Preload a few "day" at the start of "days" response. * - * @param array $days the days array - * @param string $uid User ID or blank for public shares - * @param TimelineRoot $root the root folder + * @param array $days the days array + * @param string $uid User ID or blank for public shares */ - private function preloadDays(array &$days, string $uid, TimelineRoot &$root) + private function preloadDays(array &$days, string $uid) { $transforms = $this->getTransformations(false); $preloaded = 0; @@ -261,7 +246,6 @@ class DaysController extends GenericApiController if (\count($preloadDayIds) > 0) { $allDetails = $this->timelineQuery->getDay( - $root, $uid, $preloadDayIds, $this->isRecursive(), diff --git a/lib/Controller/DownloadController.php b/lib/Controller/DownloadController.php index 70418601..46e0d4ad 100644 --- a/lib/Controller/DownloadController.php +++ b/lib/Controller/DownloadController.php @@ -128,7 +128,7 @@ class DownloadController extends GenericApiController public function one(int $fileid): Http\Response { return Util::guardEx(function () use ($fileid) { - $file = $this->getUserFile($fileid); + $file = $this->fs->getUserFile($fileid); if (null === $file) { return Exceptions::NotFoundFile($fileid); } @@ -215,7 +215,7 @@ class DownloadController extends GenericApiController try { // This checks permissions - $file = $this->getUserFile($fileId); + $file = $this->fs->getUserFile($fileId); if (null === $file) { throw new \Exception('File not found'); } diff --git a/lib/Controller/GenericApiController.php b/lib/Controller/GenericApiController.php index 702d6e90..58028956 100644 --- a/lib/Controller/GenericApiController.php +++ b/lib/Controller/GenericApiController.php @@ -25,6 +25,7 @@ namespace OCA\Memories\Controller; use OCA\Memories\AppInfo\Application; use OCA\Memories\Db\TimelineQuery; +use OCA\Memories\Manager\FsManager; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\Files\IRootFolder; @@ -36,16 +37,16 @@ use Psr\Log\LoggerInterface; abstract class GenericApiController extends Controller { - use GenericApiControllerFs; use GenericApiControllerParams; protected IConfig $config; protected IUserSession $userSession; protected IRootFolder $rootFolder; protected IAppManager $appManager; - protected TimelineQuery $timelineQuery; protected IDBConnection $connection; protected LoggerInterface $logger; + protected TimelineQuery $timelineQuery; + protected FsManager $fs; public function __construct( IRequest $request, @@ -55,7 +56,8 @@ abstract class GenericApiController extends Controller IRootFolder $rootFolder, IAppManager $appManager, LoggerInterface $logger, - TimelineQuery $timelineQuery + TimelineQuery $timelineQuery, + FsManager $fs ) { parent::__construct(Application::APPNAME, $request); @@ -66,5 +68,6 @@ abstract class GenericApiController extends Controller $this->appManager = $appManager; $this->logger = $logger; $this->timelineQuery = $timelineQuery; + $this->fs = $fs; } } diff --git a/lib/Controller/ImageController.php b/lib/Controller/ImageController.php index 45ab8074..5cb293a8 100644 --- a/lib/Controller/ImageController.php +++ b/lib/Controller/ImageController.php @@ -55,7 +55,7 @@ class ImageController extends GenericApiController throw Exceptions::MissingParameter('id, x, y'); } - $file = $this->getUserFile($id); + $file = $this->fs->getUserFile($id); if (!$file) { throw Exceptions::NotFoundFile($id); } @@ -113,7 +113,7 @@ class ImageController extends GenericApiController continue; } - $file = $this->getUserFile($fileid); + $file = $this->fs->getUserFile($fileid); if (!$file) { continue; } @@ -178,7 +178,7 @@ class ImageController extends GenericApiController bool $tags = false ): Http\Response { return Util::guardEx(function () use ($id, $basic, $current, $tags) { - $file = $this->getUserFile((int) $id); + $file = $this->fs->getUserFile((int) $id); if (!$file) { throw Exceptions::NotFoundFile($id); } @@ -222,7 +222,7 @@ class ImageController extends GenericApiController public function setExif(string $id, array $raw): Http\Response { return Util::guardEx(function () use ($id, $raw) { - $file = $this->getUserFile((int) $id); + $file = $this->fs->getUserFile((int) $id); if (!$file) { throw Exceptions::NotFoundFile($id); } @@ -261,7 +261,7 @@ class ImageController extends GenericApiController public function decodable(string $id): Http\Response { return Util::guardEx(function () use ($id) { - $file = $this->getUserFile((int) $id); + $file = $this->fs->getUserFile((int) $id); if (!$file) { throw Exceptions::NotFoundFile($id); } diff --git a/lib/Controller/MapController.php b/lib/Controller/MapController.php index 480b4271..999efeed 100644 --- a/lib/Controller/MapController.php +++ b/lib/Controller/MapController.php @@ -36,9 +36,6 @@ class MapController extends GenericApiController public function clusters(): Http\Response { return Util::guardEx(function () { - // Get the folder to show - $root = $this->getRequestRoot(); - // Make sure we have bounds and zoom level // Zoom level is used to determine the grid length $bounds = $this->request->getParam('bounds'); @@ -52,11 +49,11 @@ class MapController extends GenericApiController $clusterDensity = 1; $gridLen = 180.0 / (2 ** $zoomLevel * $clusterDensity); - $clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds, $root); + $clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds); // Get previews for each cluster $clusterIds = array_map(fn ($cluster) => (int) $cluster['id'], $clusters); - $previews = $this->timelineQuery->getMapClusterPreviews($clusterIds, $root); + $previews = $this->timelineQuery->getMapClusterPreviews($clusterIds); // Merge the responses $fileMap = []; diff --git a/lib/Controller/PublicController.php b/lib/Controller/PublicController.php index 98fe3427..9caedc5a 100644 --- a/lib/Controller/PublicController.php +++ b/lib/Controller/PublicController.php @@ -4,7 +4,6 @@ namespace OCA\Memories\Controller; use OCA\Memories\AppInfo\Application; use OCA\Memories\Db\TimelineQuery; -use OCP\App\IAppManager; use OCP\AppFramework\AuthPublicShareController; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\AppFramework\Http\TemplateResponse; @@ -16,7 +15,6 @@ use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; -use OCP\IUserManager; use OCP\IUserSession; use OCP\Share\IManager as IShareManager; use OCP\Share\IShare; @@ -30,8 +28,6 @@ class PublicController extends AuthPublicShareController protected IUserSession $userSession; protected IRootFolder $rootFolder; protected IShareManager $shareManager; - protected IUserManager $userManager; - protected IAppManager $appManager; protected IConfig $config; protected TimelineQuery $timelineQuery; @@ -47,8 +43,6 @@ class PublicController extends AuthPublicShareController IUserSession $userSession, IRootFolder $rootFolder, IShareManager $shareManager, - IUserManager $userManager, - IAppManager $appManager, IConfig $config, TimelineQuery $timelineQuery ) { @@ -58,8 +52,6 @@ class PublicController extends AuthPublicShareController $this->userSession = $userSession; $this->rootFolder = $rootFolder; $this->shareManager = $shareManager; - $this->userManager = $userManager; - $this->appManager = $appManager; $this->config = $config; $this->timelineQuery = $timelineQuery; } @@ -106,7 +98,7 @@ class PublicController extends AuthPublicShareController throw new NotFoundException(); } - if (!self::validateShare($share)) { + if (!\OCA\Memories\Manager\FsManager::validateShare($share)) { throw new NotFoundException(); } @@ -144,38 +136,6 @@ class PublicController extends AuthPublicShareController return $response; } - /** - * Validate the permissions of the share. - */ - public static function validateShare(?IShare $share): bool - { - if (null === $share) { - return false; - } - - // Get user manager - $userManager = \OC::$server->get(IUserManager::class); - - // Check if share read is allowed - if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) { - return false; - } - - // If the owner is disabled no access to the linke is granted - $owner = $userManager->get($share->getShareOwner()); - if (null === $owner || !$owner->isEnabled()) { - return false; - } - - // If the initiator of the share is disabled no access is granted - $initiator = $userManager->get($share->getSharedBy()); - if (null === $initiator || !$initiator->isEnabled()) { - return false; - } - - return $share->getNode()->isReadable() && $share->getNode()->isShareable(); - } - protected function showAuthFailed(): TemplateResponse { $templateParameters = ['share' => $this->share, 'wrongpw' => true]; diff --git a/lib/Controller/ShareController.php b/lib/Controller/ShareController.php index c452d139..77bd9322 100644 --- a/lib/Controller/ShareController.php +++ b/lib/Controller/ShareController.php @@ -118,7 +118,7 @@ class ShareController extends GenericApiController try { $file = null; if ($id) { - $file = $this->getUserFile($id); + $file = $this->fs->getUserFile($id); } elseif ($path) { $file = Util::getUserFolder($uid)->get($path); } diff --git a/lib/Controller/TagsController.php b/lib/Controller/TagsController.php index 2e1cfed5..e83db122 100644 --- a/lib/Controller/TagsController.php +++ b/lib/Controller/TagsController.php @@ -44,7 +44,7 @@ class TagsController extends GenericApiController } // Check the user is allowed to edit the file - $file = $this->getUserFile($id); + $file = $this->fs->getUserFile($id); if (null === $file) { throw Exceptions::NotFoundFile($id); } diff --git a/lib/Controller/VideoController.php b/lib/Controller/VideoController.php index d77439a6..00b67036 100644 --- a/lib/Controller/VideoController.php +++ b/lib/Controller/VideoController.php @@ -56,7 +56,7 @@ class VideoController extends GenericApiController } // Get file - $file = $this->getUserFile($fileid); + $file = $this->fs->getUserFile($fileid); if (!$file || !$file->isReadable()) { throw Exceptions::NotFoundFile($fileid); } @@ -116,7 +116,7 @@ class VideoController extends GenericApiController string $transcode = '' ) { return Util::guardEx(function () use ($fileid, $liveid, $format, $transcode) { - $file = $this->getUserFile($fileid); + $file = $this->fs->getUserFile($fileid); if (null === $file) { throw Exceptions::NotFoundFile($fileid); } diff --git a/lib/Db/TimelineQuery.php b/lib/Db/TimelineQuery.php index d2d5250c..00dc7ebe 100644 --- a/lib/Db/TimelineQuery.php +++ b/lib/Db/TimelineQuery.php @@ -27,12 +27,24 @@ class TimelineQuery ]; protected IDBConnection $connection; + private ?TimelineRoot $_root = null; public function __construct(IDBConnection $connection) { $this->connection = $connection; } + public function root(): TimelineRoot + { + if (null === $this->_root) { + $this->_root = new TimelineRoot(); + $fsManager = \OC::$server->get(\OCA\Memories\Manager\FsManager::class); + $fsManager->populateRoot($this->_root); + } + + return $this->_root; + } + public static function debugQuery(IQueryBuilder &$query, string $sql = '') { // Print the query and exit @@ -63,10 +75,6 @@ class TimelineQuery return $sql; } - public function transformExtraFields(IQueryBuilder &$query, string $uid, array &$fields) - { - } - public function getInfoById(int $id, bool $basic): array { $qb = $this->connection->getQueryBuilder(); diff --git a/lib/Db/TimelineQueryDays.php b/lib/Db/TimelineQueryDays.php index b04d7831..20484539 100644 --- a/lib/Db/TimelineQueryDays.php +++ b/lib/Db/TimelineQueryDays.php @@ -78,15 +78,13 @@ trait TimelineQueryDays /** * Get the days response from the database for the timeline. * - * @param TimelineRoot $root The root to get the days from - * @param bool $recursive Whether to get the days recursively - * @param bool $archive Whether to get the days only from the archive folder - * @param array $queryTransforms An array of query transforms to apply to the query + * @param bool $recursive Whether to get the days recursively + * @param bool $archive Whether to get the days only from the archive folder + * @param array $queryTransforms An array of query transforms to apply to the query * * @return array The days response */ public function getDays( - TimelineRoot &$root, string $uid, bool $recursive, bool $archive, @@ -99,7 +97,7 @@ trait TimelineQueryDays $query->select('m.dayid', $count) ->from('memories', 'm') ; - $query = $this->joinFilecache($query, $root, $recursive, $archive); + $query = $this->joinFilecache($query, null, $recursive, $archive); // Group and sort by dayid $query->groupBy('m.dayid') @@ -119,18 +117,16 @@ trait TimelineQueryDays /** * Get the day response from the database for the timeline. * - * @param TimelineRoot $root The root to get the day from - * @param string $uid The user id - * @param int[] $day_ids The day ids to fetch - * @param bool $recursive If the query should be recursive - * @param bool $archive If the query should include only the archive folder - * @param array $queryTransforms The query transformations to apply - * @param mixed $day_ids + * @param string $uid The user id + * @param int[] $day_ids The day ids to fetch + * @param bool $recursive If the query should be recursive + * @param bool $archive If the query should include only the archive folder + * @param array $queryTransforms The query transformations to apply + * @param mixed $day_ids * * @return array An array of day responses */ public function getDay( - TimelineRoot &$root, string $uid, ?array $day_ids, bool $recursive, @@ -150,7 +146,7 @@ trait TimelineQueryDays ; // JOIN with filecache for existing files - $query = $this->joinFilecache($query, $root, $recursive, $archive); + $query = $this->joinFilecache($query, null, $recursive, $archive); // JOIN with mimetypes to get the mimetype $query->join('f', 'mimetypes', 'mimetypes', $query->expr()->eq('f.mimetype', 'mimetypes.id')); @@ -271,11 +267,15 @@ trait TimelineQueryDays * @param bool $archive Whether to get the days only from the archive folder */ private function joinFilecache( - IQueryBuilder &$query, - TimelineRoot &$root, - bool $recursive, - bool $archive + IQueryBuilder $query, + ?TimelineRoot $root = null, + bool $recursive = true, + bool $archive = false ) { + if (null === $root) { + $root = $this->root(); + } + // Join with memories $baseOp = $query->expr()->eq('f.fileid', 'm.fileid'); if ($root->isEmpty()) { diff --git a/lib/Db/TimelineQueryMap.php b/lib/Db/TimelineQueryMap.php index 19ad420f..a09b22fb 100644 --- a/lib/Db/TimelineQueryMap.php +++ b/lib/Db/TimelineQueryMap.php @@ -33,8 +33,7 @@ trait TimelineQueryMap public function getMapClusters( float $gridLen, - string $bounds, - TimelineRoot &$root + string $bounds ): array { $query = $this->connection->getQueryBuilder(); @@ -57,7 +56,7 @@ trait TimelineQueryMap $query->innerJoin('c', 'memories', 'm', $query->expr()->eq('c.id', 'm.mapcluster')); // JOIN with filecache for existing files - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // Bound the query to the map bounds $this->transformMapBoundsFilter($query, '', $bounds, 'c'); @@ -86,7 +85,7 @@ trait TimelineQueryMap return $clusters; } - public function getMapClusterPreviews(array $clusterIds, TimelineRoot &$root) + public function getMapClusterPreviews(array $clusterIds) { $query = $this->connection->getQueryBuilder(); @@ -97,7 +96,7 @@ trait TimelineQueryMap ); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // GROUP BY the cluster $query->groupBy('m.mapcluster'); diff --git a/lib/Db/TimelineQueryPeopleFaceRecognition.php b/lib/Db/TimelineQueryPeopleFaceRecognition.php index 1a891bd1..1d5ce773 100644 --- a/lib/Db/TimelineQueryPeopleFaceRecognition.php +++ b/lib/Db/TimelineQueryPeopleFaceRecognition.php @@ -58,7 +58,7 @@ trait TimelineQueryPeopleFaceRecognition ); } - public function getFaceRecognitionPhotos(string $id, int $currentModel, TimelineRoot &$root, ?int $limit) + public function getFaceRecognitionPhotos(string $id, int $currentModel, ?int $limit) { $query = $this->connection->getQueryBuilder(); @@ -95,7 +95,7 @@ trait TimelineQueryPeopleFaceRecognition } // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // LIMIT results if (null !== $limit) { @@ -110,7 +110,7 @@ trait TimelineQueryPeopleFaceRecognition return $this->executeQueryWithCTEs($query)->fetchAll(); } - public function getFaceRecognitionClusters(TimelineRoot &$root, int $currentModel, bool $show_singles = false, bool $show_hidden = false) + public function getFaceRecognitionClusters(int $currentModel, bool $show_singles = false, bool $show_hidden = false) { $query = $this->connection->getQueryBuilder(); @@ -133,7 +133,7 @@ trait TimelineQueryPeopleFaceRecognition )); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // GROUP by ID of face cluster $query->groupBy('frp.id'); @@ -170,7 +170,7 @@ trait TimelineQueryPeopleFaceRecognition return $faces; } - public function getFaceRecognitionPersons(TimelineRoot &$root, int $currentModel) + public function getFaceRecognitionPersons(int $currentModel) { $query = $this->connection->getQueryBuilder(); @@ -193,7 +193,7 @@ trait TimelineQueryPeopleFaceRecognition )); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // GROUP by name of face clusters $query->where($query->expr()->isNotNull('frp.name')); diff --git a/lib/Db/TimelineQueryPeopleRecognize.php b/lib/Db/TimelineQueryPeopleRecognize.php index 754d7457..994d5d98 100644 --- a/lib/Db/TimelineQueryPeopleRecognize.php +++ b/lib/Db/TimelineQueryPeopleRecognize.php @@ -62,7 +62,7 @@ trait TimelineQueryPeopleRecognize ); } - public function getPeopleRecognize(TimelineRoot &$root, string $uid) + public function getPeopleRecognize(string $uid) { $query = $this->connection->getQueryBuilder(); @@ -77,7 +77,7 @@ trait TimelineQueryPeopleRecognize $query->innerJoin('rfd', 'memories', 'm', $query->expr()->eq('m.fileid', 'rfd.file_id')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // WHERE this cluster belongs to the user $query->where($query->expr()->eq('rfc.user_id', $query->createNamedParameter($uid))); @@ -105,7 +105,7 @@ trait TimelineQueryPeopleRecognize return $faces; } - public function getPeopleRecognizePhotos(int $id, TimelineRoot $root, ?int $limit): array + public function getPeopleRecognizePhotos(int $id, ?int $limit): array { $query = $this->connection->getQueryBuilder(); @@ -129,7 +129,7 @@ trait TimelineQueryPeopleRecognize $query->innerJoin('rfd', 'memories', 'm', $query->expr()->eq('m.fileid', 'rfd.file_id')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // LIMIT results if (null !== $limit) { diff --git a/lib/Db/TimelineQueryPlaces.php b/lib/Db/TimelineQueryPlaces.php index 1971fbb4..6c6d9873 100644 --- a/lib/Db/TimelineQueryPlaces.php +++ b/lib/Db/TimelineQueryPlaces.php @@ -19,7 +19,7 @@ trait TimelineQueryPlaces )); } - public function getPlaces(TimelineRoot &$root) + public function getPlaces() { $query = $this->connection->getQueryBuilder(); @@ -34,7 +34,7 @@ trait TimelineQueryPlaces $query->innerJoin('mp', 'memories', 'm', $query->expr()->eq('m.fileid', 'mp.fileid')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // GROUP and ORDER by tag name $query->groupBy('e.osm_id', 'e.name'); @@ -54,7 +54,7 @@ trait TimelineQueryPlaces return $places; } - public function getPlacePhotos(int $id, TimelineRoot $root, ?int $limit): array + public function getPlacePhotos(int $id, ?int $limit): array { $query = $this->connection->getQueryBuilder(); @@ -67,7 +67,7 @@ trait TimelineQueryPlaces $query->innerJoin('mp', 'memories', 'm', $query->expr()->eq('m.fileid', 'mp.fileid')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // MAX number of photos if (null !== $limit) { diff --git a/lib/Db/TimelineQueryTags.php b/lib/Db/TimelineQueryTags.php index 387e91c2..7e05d415 100644 --- a/lib/Db/TimelineQueryTags.php +++ b/lib/Db/TimelineQueryTags.php @@ -37,7 +37,7 @@ trait TimelineQueryTags )); } - public function getTags(TimelineRoot &$root) + public function getTags() { $query = $this->connection->getQueryBuilder(); @@ -57,7 +57,7 @@ trait TimelineQueryTags $query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.objectid', 'stom.objectid')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // GROUP and ORDER by tag name $query->groupBy('st.id'); @@ -77,7 +77,7 @@ trait TimelineQueryTags return $tags; } - public function getTagPhotos(string $tagName, TimelineRoot $root, ?int $limit) + public function getTagPhotos(string $tagName, ?int $limit) { $query = $this->connection->getQueryBuilder(); $tagId = $this->getSystemTagId($query, $tagName); @@ -98,7 +98,7 @@ trait TimelineQueryTags $query->innerJoin('stom', 'memories', 'm', $query->expr()->eq('m.objectid', 'stom.objectid')); // WHERE these photos are in the user's requested folder recursively - $query = $this->joinFilecache($query, $root, true, false); + $query = $this->joinFilecache($query); // MAX number of files if (null !== $limit) { diff --git a/lib/Exif.php b/lib/Exif.php index fc90a96d..37087382 100644 --- a/lib/Exif.php +++ b/lib/Exif.php @@ -68,7 +68,7 @@ class Exif /** * Get the path to the user's configured photos directory. */ - public static function getPhotosPath(IConfig &$config, string &$userId) + public static function getPhotosPath(IConfig $config, string &$userId) { $p = $config->getUserValue($userId, Application::APPNAME, 'timelinePath', ''); if (empty($p)) { diff --git a/lib/Controller/GenericApiControllerFs.php b/lib/Manager/FsManager.php similarity index 79% rename from lib/Controller/GenericApiControllerFs.php rename to lib/Manager/FsManager.php index bea8d074..752e94cc 100644 --- a/lib/Controller/GenericApiControllerFs.php +++ b/lib/Manager/FsManager.php @@ -21,7 +21,7 @@ declare(strict_types=1); * along with this program. If not, see . */ -namespace OCA\Memories\Controller; +namespace OCA\Memories\Manager; use OCA\Memories\Db\TimelineQuery; use OCA\Memories\Db\TimelineRoot; @@ -31,22 +31,37 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\IConfig; +use OCP\IRequest; +use OCP\IUserManager; use OCP\IUserSession; +use OCP\Share\IShare; -trait GenericApiControllerFs +class FsManager { - use GenericApiControllerParams; - protected IConfig $config; protected IUserSession $userSession; protected IRootFolder $rootFolder; protected TimelineQuery $timelineQuery; + protected IRequest $request; + + public function __construct( + IConfig $config, + IUserSession $userSession, + IRootFolder $rootFolder, + TimelineQuery $timelineQuery, + IRequest $request + ) { + $this->config = $config; + $this->userSession = $userSession; + $this->rootFolder = $rootFolder; + $this->timelineQuery = $timelineQuery; + $this->request = $request; + } /** Get the TimelineRoot object relevant to the request */ - protected function getRequestRoot() + public function populateRoot(TimelineRoot &$root) { $user = $this->userSession->getUser(); - $root = new TimelineRoot(); // Albums have no folder if ($this->request->getParam('album') && Util::albumsIsEnabled()) { @@ -71,7 +86,7 @@ trait GenericApiControllerFs // Anything else needs a user if (null === $user) { - throw new \Exception('User not logged in'); + throw new \Exception('User not logged in: no timeline root'); } $uid = $user->getUID(); @@ -107,7 +122,7 @@ trait GenericApiControllerFs /** * Get a file with ID for the current user. */ - protected function getUserFile(int $fileId): ?File + public function getUserFile(int $fileId): ?File { // Don't check self for share token if ($this->getShareToken()) { @@ -122,7 +137,7 @@ trait GenericApiControllerFs /** * Get a file with ID from user's folder. */ - protected function getUserFolderFile(int $id): ?File + public function getUserFolderFile(int $id): ?File { $user = $this->userSession->getUser(); if (null === $user) { @@ -138,8 +153,10 @@ trait GenericApiControllerFs /** * Get a file with ID from an album. + * + * @param int $id FileID */ - protected function getAlbumFile(int $id): ?File + public function getAlbumFile(int $id): ?File { $user = $this->userSession->getUser(); if (null === $user) { @@ -163,9 +180,9 @@ trait GenericApiControllerFs /** * Get a file with ID from a public share. * - * @param int $fileId + * @param int $id FileID */ - protected function getShareFile(int $id): ?File + public function getShareFile(int $id): ?File { try { // Album share @@ -205,7 +222,7 @@ trait GenericApiControllerFs return null; } - protected function getShareObject() + public function getShareObject() { // Get token from request $token = $this->getShareToken(); @@ -215,7 +232,7 @@ trait GenericApiControllerFs // Get share by token $share = \OC::$server->get(\OCP\Share\IManager::class)->getShareByToken($token); - if (!PublicController::validateShare($share)) { + if (!self::validateShare($share)) { return null; } @@ -235,14 +252,14 @@ trait GenericApiControllerFs return $share; } - protected function getShareNode() + public function getShareNode() { $share = $this->getShareObject(); if (null === $share) { return null; } - // Get node from share + // Get node from share $node = $share->getNode(); // throws exception if not found if (!$node->isReadable() || !$node->isShareable()) { throw new \Exception('Share not found or invalid'); @@ -254,6 +271,38 @@ trait GenericApiControllerFs return $node; } + /** + * Validate the permissions of the share. + */ + public static function validateShare(?IShare $share): bool + { + if (null === $share) { + return false; + } + + // Get user manager + $userManager = \OC::$server->get(IUserManager::class); + + // Check if share read is allowed + if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) { + return false; + } + + // If the owner is disabled no access to the linke is granted + $owner = $userManager->get($share->getShareOwner()); + if (null === $owner || !$owner->isEnabled()) { + return false; + } + + // If the initiator of the share is disabled no access is granted + $initiator = $userManager->get($share->getSharedBy()); + if (null === $initiator || !$initiator->isEnabled()) { + return false; + } + + return $share->getNode()->isReadable() && $share->getNode()->isShareable(); + } + /** * Helper to get one file or null from a fiolder. * @@ -287,4 +336,9 @@ trait GenericApiControllerFs return $file; } + + private function getShareToken() + { + return $this->request->getParam('token'); + } }