Refactor all calls to use TimelineRoot
parent
4a5408b846
commit
d9afbbe710
|
@ -25,6 +25,7 @@ namespace OCA\Memories\Controller;
|
|||
|
||||
use OCA\Memories\AppInfo\Application;
|
||||
use OCA\Memories\Db\TimelineQuery;
|
||||
use OCA\Memories\Db\TimelineRoot;
|
||||
use OCA\Memories\Db\TimelineWrite;
|
||||
use OCA\Memories\Exif;
|
||||
use OCP\App\IAppManager;
|
||||
|
@ -88,12 +89,14 @@ class ApiBase extends Controller
|
|||
return $user ? $user->getUID() : '';
|
||||
}
|
||||
|
||||
/** Get the Folder object relevant to the request */
|
||||
protected function getRequestFolder()
|
||||
/** Get the TimelineRoot object relevant to the request */
|
||||
protected function getRequestRoot()
|
||||
{
|
||||
$root = new TimelineRoot();
|
||||
|
||||
// Albums have no folder
|
||||
if ($this->request->getParam('album')) {
|
||||
return null;
|
||||
return $root;
|
||||
}
|
||||
|
||||
// Public shared folder
|
||||
|
@ -103,7 +106,9 @@ class ApiBase extends Controller
|
|||
throw new \Exception('Share not found or invalid');
|
||||
}
|
||||
|
||||
return $share;
|
||||
$root->addFolder($share);
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
// Anything else needs a user
|
||||
|
@ -135,7 +140,13 @@ class ApiBase extends Controller
|
|||
throw new \Exception('Folder not readable');
|
||||
}
|
||||
|
||||
return $folder;
|
||||
// Don't add mount points for folder view
|
||||
$root->addFolder($folder);
|
||||
if (null === $folderPath) {
|
||||
$root->addMountPoints();
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Db\TimelineRoot;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\Files\Folder;
|
||||
|
||||
class DaysController extends ApiBase
|
||||
{
|
||||
|
@ -42,10 +42,10 @@ class DaysController extends ApiBase
|
|||
$uid = $this->getUid();
|
||||
|
||||
// Get the folder to show
|
||||
$folder = null;
|
||||
$root = null;
|
||||
|
||||
try {
|
||||
$folder = $this->getRequestFolder();
|
||||
$root = $this->getRequestRoot();
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class DaysController extends ApiBase
|
|||
// Run actual query
|
||||
try {
|
||||
$list = $this->timelineQuery->getDays(
|
||||
$folder,
|
||||
$root,
|
||||
$uid,
|
||||
$this->isRecursive(),
|
||||
$this->isArchive(),
|
||||
|
@ -65,7 +65,7 @@ class DaysController extends ApiBase
|
|||
$list = $this->timelineQuery->daysToMonths($list);
|
||||
} else {
|
||||
// Preload some day responses
|
||||
$this->preloadDays($list, $uid, $folder);
|
||||
$this->preloadDays($list, $uid, $root);
|
||||
}
|
||||
|
||||
// Reverse response if requested. Folders still stay at top.
|
||||
|
@ -75,7 +75,7 @@ class DaysController extends ApiBase
|
|||
|
||||
// Add subfolder info if querying non-recursively
|
||||
if (!$this->isRecursive()) {
|
||||
array_unshift($list, $this->getSubfoldersEntry($folder));
|
||||
array_unshift($list, $this->getSubfoldersEntry($root->getFolder($root->getOneId())));
|
||||
}
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
|
@ -111,10 +111,10 @@ class DaysController extends ApiBase
|
|||
}
|
||||
|
||||
// Get the folder to show
|
||||
$folder = null;
|
||||
$root = null;
|
||||
|
||||
try {
|
||||
$folder = $this->getRequestFolder();
|
||||
$root = $this->getRequestRoot();
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class DaysController extends ApiBase
|
|||
// Run actual query
|
||||
try {
|
||||
$list = $this->timelineQuery->getDay(
|
||||
$folder,
|
||||
$root,
|
||||
$uid,
|
||||
$dayIds,
|
||||
$this->isRecursive(),
|
||||
|
@ -239,9 +239,9 @@ class DaysController extends ApiBase
|
|||
*
|
||||
* @param array $days the days array
|
||||
* @param string $uid User ID or blank for public shares
|
||||
* @param null|Folder $folder the folder to search in
|
||||
* @param TimelineRoot $root the root folder
|
||||
*/
|
||||
private function preloadDays(array &$days, string $uid, &$folder)
|
||||
private function preloadDays(array &$days, string $uid, TimelineRoot &$root)
|
||||
{
|
||||
$transforms = $this->getTransformations(false);
|
||||
$preloaded = 0;
|
||||
|
@ -263,7 +263,7 @@ class DaysController extends ApiBase
|
|||
|
||||
if (\count($preloadDayIds) > 0) {
|
||||
$allDetails = $this->timelineQuery->getDay(
|
||||
$folder,
|
||||
$root,
|
||||
$uid,
|
||||
$preloadDayIds,
|
||||
$this->isRecursive(),
|
||||
|
|
|
@ -49,14 +49,14 @@ class FacesController extends ApiBase
|
|||
}
|
||||
|
||||
// If this isn't the timeline folder then things aren't going to work
|
||||
$folder = $this->getRequestFolder();
|
||||
if (null === $folder) {
|
||||
$root = $this->getRequestRoot();
|
||||
if ($root->isEmpty()) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Run actual query
|
||||
$list = $this->timelineQuery->getFaces(
|
||||
$folder,
|
||||
$root,
|
||||
);
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
|
@ -84,22 +84,23 @@ class FacesController extends ApiBase
|
|||
}
|
||||
|
||||
// Get folder to search for
|
||||
$folder = $this->getRequestFolder();
|
||||
if (null === $folder) {
|
||||
$root = $this->getRequestRoot();
|
||||
if ($root->isEmpty()) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Run actual query
|
||||
$detections = $this->timelineQuery->getFacePreviewDetection($folder, (int) $id);
|
||||
$detections = $this->timelineQuery->getFacePreviewDetection($root, (int) $id);
|
||||
if (null === $detections || 0 === \count($detections)) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Find the first detection that has a preview
|
||||
$preview = null;
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
foreach ($detections as &$detection) {
|
||||
// Get the file (also checks permissions)
|
||||
$files = $folder->getById($detection['file_id']);
|
||||
$files = $userFolder->getById($detection['file_id']);
|
||||
if (0 === \count($files) || FileInfo::TYPE_FILE !== $files[0]->getType()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -46,14 +46,14 @@ class TagsController extends ApiBase
|
|||
}
|
||||
|
||||
// If this isn't the timeline folder then things aren't going to work
|
||||
$folder = $this->getRequestFolder();
|
||||
if (null === $folder) {
|
||||
$root = $this->getRequestRoot();
|
||||
if ($root->isEmpty()) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Run actual query
|
||||
$list = $this->timelineQuery->getTags(
|
||||
$folder,
|
||||
$root,
|
||||
);
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
|
@ -77,8 +77,8 @@ class TagsController extends ApiBase
|
|||
}
|
||||
|
||||
// If this isn't the timeline folder then things aren't going to work
|
||||
$folder = $this->getRequestFolder();
|
||||
if (null === $folder) {
|
||||
$root = $this->getRequestRoot();
|
||||
if ($root->isEmpty()) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ class TagsController extends ApiBase
|
|||
// Run actual query
|
||||
$list = $this->timelineQuery->getTagPreviews(
|
||||
$tagName,
|
||||
$folder,
|
||||
$root,
|
||||
);
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
|
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Db;
|
||||
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
const CTE_FOLDERS = // CTE to get all folders recursively in the given top folders excluding archive
|
||||
|
@ -73,13 +72,10 @@ trait TimelineQueryDays
|
|||
{
|
||||
protected IDBConnection $connection;
|
||||
|
||||
/** Map of rootid => mount point */
|
||||
private $topFolderPaths = [];
|
||||
|
||||
/**
|
||||
* Get the days response from the database for the timeline.
|
||||
*
|
||||
* @param null|Folder $folder The folder to get the days from
|
||||
* @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
|
||||
|
@ -87,7 +83,7 @@ trait TimelineQueryDays
|
|||
* @return array The days response
|
||||
*/
|
||||
public function getDays(
|
||||
&$folder,
|
||||
TimelineRoot &$root,
|
||||
string $uid,
|
||||
bool $recursive,
|
||||
bool $archive,
|
||||
|
@ -100,7 +96,7 @@ trait TimelineQueryDays
|
|||
$query->select('m.dayid', $count)
|
||||
->from('memories', 'm')
|
||||
;
|
||||
$query = $this->joinFilecache($query, $folder, $recursive, $archive);
|
||||
$query = $this->joinFilecache($query, $root, $recursive, $archive);
|
||||
|
||||
// Group and sort by dayid
|
||||
$query->groupBy('m.dayid')
|
||||
|
@ -120,7 +116,7 @@ trait TimelineQueryDays
|
|||
/**
|
||||
* Get the day response from the database for the timeline.
|
||||
*
|
||||
* @param null|Folder $folder The folder to get the day from
|
||||
* @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
|
||||
|
@ -131,7 +127,7 @@ trait TimelineQueryDays
|
|||
* @return array An array of day responses
|
||||
*/
|
||||
public function getDay(
|
||||
&$folder,
|
||||
TimelineRoot &$root,
|
||||
string $uid,
|
||||
$day_ids,
|
||||
bool $recursive,
|
||||
|
@ -151,7 +147,7 @@ trait TimelineQueryDays
|
|||
;
|
||||
|
||||
// JOIN with filecache for existing files
|
||||
$query = $this->joinFilecache($query, $folder, $recursive, $archive);
|
||||
$query = $this->joinFilecache($query, $root, $recursive, $archive);
|
||||
$query->addSelect('f.etag', 'f.path', 'f.name AS basename');
|
||||
|
||||
// JOIN with mimetypes to get the mimetype
|
||||
|
@ -180,7 +176,7 @@ trait TimelineQueryDays
|
|||
$rows = $cursor->fetchAll();
|
||||
$cursor->closeCursor();
|
||||
|
||||
return $this->processDay($rows, $uid, $folder);
|
||||
return $this->processDay($rows, $uid, $root);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,12 +196,8 @@ trait TimelineQueryDays
|
|||
|
||||
/**
|
||||
* Process the single day response.
|
||||
*
|
||||
* @param array $day
|
||||
* @param string $uid User or blank if not logged in
|
||||
* @param null|Folder $folder
|
||||
*/
|
||||
private function processDay(&$day, $uid, $folder)
|
||||
private function processDay(array &$day, string $uid, TimelineRoot &$root)
|
||||
{
|
||||
/**
|
||||
* Path entry in database for folder.
|
||||
|
@ -225,15 +217,15 @@ trait TimelineQueryDays
|
|||
*/
|
||||
$defaultRootId = 0;
|
||||
|
||||
if (null !== $folder) {
|
||||
if (!$root->isEmpty()) {
|
||||
// Get root id of the top folder
|
||||
$defaultRootId = $folder->getId();
|
||||
$defaultRootId = $root->getOneId();
|
||||
|
||||
// No way to get the internal path from the folder
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('fileid', 'path')
|
||||
->from('filecache')
|
||||
->where($query->expr()->in('fileid', $query->createNamedParameter(array_keys($this->topFolderPaths), IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
->where($query->expr()->in('fileid', $query->createNamedParameter($root->getIds(), IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
;
|
||||
$paths = $query->executeQuery()->fetchAll();
|
||||
foreach ($paths as &$path) {
|
||||
|
@ -244,7 +236,7 @@ trait TimelineQueryDays
|
|||
// getPath looks like /user/files/... but we want /files/user/...
|
||||
// Split at / and swap these
|
||||
// For public shares, we just give the relative path
|
||||
if (!empty($uid) && ($actualPath = $this->topFolderPaths[$fileid])) {
|
||||
if (!empty($uid) && ($actualPath = $root->getFolderPath($fileid))) {
|
||||
$actualPath = explode('/', $actualPath);
|
||||
if (\count($actualPath) >= 3) {
|
||||
$tmp = $actualPath[1];
|
||||
|
@ -317,10 +309,11 @@ trait TimelineQueryDays
|
|||
*/
|
||||
private function addSubfolderJoinParams(
|
||||
IQueryBuilder &$query,
|
||||
TimelineRoot &$root,
|
||||
bool $archive
|
||||
) {
|
||||
// Add query parameters
|
||||
$query->setParameter('topFolderIds', array_keys($this->topFolderPaths), IQueryBuilder::PARAM_INT_ARRAY);
|
||||
$query->setParameter('topFolderIds', $root->getIds(), IQueryBuilder::PARAM_INT_ARRAY);
|
||||
$query->setParameter('cteFoldersArchive', $archive, IQueryBuilder::PARAM_BOOL);
|
||||
}
|
||||
|
||||
|
@ -328,44 +321,32 @@ trait TimelineQueryDays
|
|||
* Inner join with oc_filecache.
|
||||
*
|
||||
* @param IQueryBuilder $query Query builder
|
||||
* @param null|Folder $folder Either the top folder or null for all
|
||||
* @param TimelineRoot $root Either the top folder or null for all
|
||||
* @param bool $recursive Whether to get the days recursively
|
||||
* @param bool $archive Whether to get the days only from the archive folder
|
||||
*/
|
||||
private function joinFilecache(
|
||||
IQueryBuilder &$query,
|
||||
&$folder,
|
||||
TimelineRoot &$root,
|
||||
bool $recursive,
|
||||
bool $archive
|
||||
) {
|
||||
// Join with memories
|
||||
$baseOp = $query->expr()->eq('f.fileid', 'm.fileid');
|
||||
if (null === $folder) {
|
||||
if ($root->isEmpty()) {
|
||||
return $query->innerJoin('m', 'filecache', 'f', $baseOp);
|
||||
}
|
||||
|
||||
// Create top folders paths for later processing
|
||||
$this->topFolderPaths = [];
|
||||
$this->topFolderPaths[$folder->getId()] = $folder->getPath();
|
||||
|
||||
// Filter by folder (recursive or otherwise)
|
||||
$pathOp = null;
|
||||
if ($recursive) {
|
||||
// Add mountpoints recursively
|
||||
$this->mounts = \OC\Files\Filesystem::getMountManager()->findIn($folder->getPath());
|
||||
foreach ($this->mounts as &$mount) {
|
||||
$id = $mount->getStorageRootId();
|
||||
$path = $mount->getMountPoint();
|
||||
$this->topFolderPaths[$id] = $path;
|
||||
}
|
||||
|
||||
// Join with folders CTE
|
||||
$this->addSubfolderJoinParams($query, $archive);
|
||||
$this->addSubfolderJoinParams($query, $root, $archive);
|
||||
$query->innerJoin('f', 'cte_folders', 'cte_f', $query->expr()->eq('f.parent', 'cte_f.fileid'));
|
||||
$query->addSelect('cte_f.rootid');
|
||||
} else {
|
||||
// If getting non-recursively folder only check for parent
|
||||
$pathOp = $query->expr()->eq('f.parent', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT));
|
||||
$pathOp = $query->expr()->eq('f.parent', $query->createNamedParameter($root->getOneId(), IQueryBuilder::PARAM_INT));
|
||||
}
|
||||
|
||||
return $query->innerJoin('m', 'filecache', 'f', $query->expr()->andX(
|
||||
|
|
|
@ -47,7 +47,7 @@ trait TimelineQueryFaces
|
|||
);
|
||||
}
|
||||
|
||||
public function getFaces(Folder $folder)
|
||||
public function getFaces(TimelineRoot &$root)
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -62,7 +62,7 @@ trait TimelineQueryFaces
|
|||
$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, $folder, true, false);
|
||||
$query = $this->joinFilecache($query, $root, true, false);
|
||||
|
||||
// GROUP by ID of face cluster
|
||||
$query->groupBy('rfc.id');
|
||||
|
@ -87,7 +87,7 @@ trait TimelineQueryFaces
|
|||
return $faces;
|
||||
}
|
||||
|
||||
public function getFacePreviewDetection(Folder &$folder, int $id)
|
||||
public function getFacePreviewDetection(TimelineRoot &$root, int $id)
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -109,7 +109,7 @@ trait TimelineQueryFaces
|
|||
$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, $folder, true, false);
|
||||
$query = $this->joinFilecache($query, $root, true, false);
|
||||
|
||||
// LIMIT results
|
||||
$query->setMaxResults(15);
|
||||
|
|
|
@ -19,7 +19,9 @@ trait TimelineQueryFolders
|
|||
$query->select('f.fileid', 'f.etag')->from('memories', 'm');
|
||||
|
||||
// WHERE these photos are in the user's requested folder recursively
|
||||
$query = $this->joinFilecache($query, $folder, true, false);
|
||||
$root = new TimelineRoot();
|
||||
$root->addFolder($folder);
|
||||
$query = $this->joinFilecache($query, $root, true, false);
|
||||
|
||||
// ORDER descending by fileid
|
||||
$query->orderBy('f.fileid', 'DESC');
|
||||
|
|
|
@ -38,7 +38,7 @@ trait TimelineQueryTags
|
|||
));
|
||||
}
|
||||
|
||||
public function getTags(Folder $folder)
|
||||
public function getTags(TimelineRoot &$root)
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -58,7 +58,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, $folder, true, false);
|
||||
$query = $this->joinFilecache($query, $root, true, false);
|
||||
|
||||
// GROUP and ORDER by tag name
|
||||
$query->groupBy('st.id');
|
||||
|
@ -78,7 +78,7 @@ trait TimelineQueryTags
|
|||
return $tags;
|
||||
}
|
||||
|
||||
public function getTagPreviews(string $tagName, Folder &$folder)
|
||||
public function getTagPreviews(string $tagName, TimelineRoot &$root)
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$tagId = $this->getSystemTagId($query, $tagName);
|
||||
|
@ -99,7 +99,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, $folder, true, false);
|
||||
$query = $this->joinFilecache($query, $root, true, false);
|
||||
|
||||
// MAX 4
|
||||
$query->setMaxResults(4);
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Memories\Db;
|
||||
|
||||
use OCP\Files\Folder;
|
||||
|
||||
class TimelineRoot
|
||||
{
|
||||
protected array $folders;
|
||||
protected array $folderPaths;
|
||||
|
||||
/** Initialize */
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function addFolder(Folder &$folder)
|
||||
{
|
||||
// Add top level folder
|
||||
$id = $folder->getId();
|
||||
$folderPath = $folder->getPath();
|
||||
$this->folders[$id] = $folder;
|
||||
$this->folderPaths[$id] = $folderPath;
|
||||
}
|
||||
|
||||
// Add mountpoints recursively
|
||||
public function addMountPoints()
|
||||
{
|
||||
$mp = [];
|
||||
foreach ($this->folderPaths as $id => $folderPath) {
|
||||
$mounts = \OC\Files\Filesystem::getMountManager()->findIn($folderPath);
|
||||
foreach ($mounts as &$mount) {
|
||||
$id = $mount->getStorageRootId();
|
||||
$path = $mount->getMountPoint();
|
||||
$mp[$id] = $path;
|
||||
}
|
||||
}
|
||||
$this->folderPaths += $mp;
|
||||
}
|
||||
|
||||
public function getFolderPath(int $id)
|
||||
{
|
||||
return $this->folderPaths[$id];
|
||||
}
|
||||
|
||||
public function getIds()
|
||||
{
|
||||
return array_keys($this->folderPaths);
|
||||
}
|
||||
|
||||
public function getOneId()
|
||||
{
|
||||
return array_key_first($this->folders);
|
||||
}
|
||||
|
||||
public function getFolder(int $id)
|
||||
{
|
||||
return $this->folders[$id];
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->folderPaths);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue