memories/lib/Db/AlbumsQuery.php

273 lines
9.3 KiB
PHP
Raw Normal View History

2022-10-26 22:12:46 +00:00
<?php
declare(strict_types=1);
namespace OCA\Memories\Db;
2022-10-26 23:20:28 +00:00
use OCP\DB\QueryBuilder\IQueryBuilder;
2022-10-26 22:12:46 +00:00
use OCP\IDBConnection;
class AlbumsQuery
2022-10-26 22:12:46 +00:00
{
protected IDBConnection $connection;
public function __construct(IDBConnection $connection)
2022-10-26 23:20:28 +00:00
{
$this->connection = $connection;
2022-10-26 23:20:28 +00:00
}
/** Get list of albums */
public function getList(string $uid, bool $shared = false)
2022-10-26 22:12:46 +00:00
{
$query = $this->connection->getQueryBuilder();
// SELECT everything from albums
2022-10-26 22:48:46 +00:00
$count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
2022-10-27 09:36:20 +00:00
$query->select('pa.*', $count)->from('photos_albums', 'pa');
if ($shared) {
$ids = $this->getSelfCollaborators($uid);
2022-11-29 16:57:03 +00:00
$query->innerJoin('pa', $this->collaboratorsTable(), 'pc', $query->expr()->andX(
2022-10-27 09:36:20 +00:00
$query->expr()->eq('pa.album_id', 'pc.album_id'),
$query->expr()->in('pc.collaborator_id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_STR_ARRAY)),
2022-10-27 09:36:20 +00:00
));
} else {
$query->where(
$query->expr()->eq('user', $query->createNamedParameter($uid)),
);
}
2022-10-26 22:12:46 +00:00
2022-10-26 23:20:28 +00:00
// WHERE these are items with this album
2022-10-27 06:37:56 +00:00
$query->leftJoin('pa', 'photos_albums_files', 'paf', $query->expr()->andX(
2022-10-26 22:48:46 +00:00
$query->expr()->eq('paf.album_id', 'pa.album_id'),
));
// WHERE these items are memories indexed photos
2022-10-27 06:37:56 +00:00
$query->leftJoin('paf', 'memories', 'm', $query->expr()->eq('m.fileid', 'paf.file_id'));
2022-10-26 22:48:46 +00:00
// WHERE these photos are in the filecache
2022-10-27 06:37:56 +00:00
$query->leftJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'));
2022-10-26 22:48:46 +00:00
2022-10-26 22:12:46 +00:00
// GROUP and ORDER by
2022-10-26 22:48:46 +00:00
$query->groupBy('pa.album_id');
2022-10-26 22:12:46 +00:00
$query->orderBy('pa.created', 'DESC');
$query->addOrderBy('pa.album_id', 'DESC'); // tie-breaker
// FETCH all albums
$albums = $query->executeQuery()->fetchAll();
// Post process
foreach ($albums as &$row) {
$row['cluster_id'] = $row['user'].'/'.$row['name'];
2022-10-26 22:48:46 +00:00
$row['album_id'] = (int) $row['album_id'];
$row['created'] = (int) $row['created'];
2022-10-26 22:12:46 +00:00
$row['last_added_photo'] = (int) $row['last_added_photo'];
}
return $albums;
}
2022-10-26 23:20:28 +00:00
/**
* Check if an album has a file.
*
* @return bool|string owner of file
*/
public function hasFile(int $albumId, int $fileId)
{
$query = $this->connection->getQueryBuilder();
$query->select('owner')->from('photos_albums_files')->where(
$query->expr()->andX(
$query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)),
$query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)),
)
);
return $query->executeQuery()->fetchOne();
}
2022-12-03 04:07:06 +00:00
/**
* Check if a file belongs to a user through an album.
2022-12-03 05:42:52 +00:00
*
* @return bool|string owner of file
2022-12-03 04:07:06 +00:00
*/
public function userHasFile(string $uid, int $fileId)
2022-12-03 04:07:06 +00:00
{
$query = $this->connection->getQueryBuilder();
2022-12-03 05:42:52 +00:00
$query->select('paf.owner')->from('photos_albums_files', 'paf')->where(
2022-12-03 04:07:06 +00:00
$query->expr()->andX(
$query->expr()->eq('paf.file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)),
$query->expr()->orX(
$query->expr()->eq('pa.album_id', 'paf.album_id'),
$query->expr()->eq('pc.album_id', 'paf.album_id'),
),
)
);
// Check if user-owned album or shared album
$query->leftJoin('paf', 'photos_albums', 'pa', $query->expr()->andX(
$query->expr()->eq('pa.album_id', 'paf.album_id'),
$query->expr()->eq('pa.user', $query->createNamedParameter($uid)),
));
// Join to shared album
$ids = $this->getSelfCollaborators($uid);
2022-12-03 04:07:06 +00:00
$query->leftJoin('paf', $this->collaboratorsTable(), 'pc', $query->expr()->andX(
$query->expr()->eq('pc.album_id', 'paf.album_id'),
$query->expr()->in('pc.collaborator_id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_STR_ARRAY)),
2022-12-03 04:07:06 +00:00
));
2022-12-03 05:42:52 +00:00
return $query->executeQuery()->fetchOne();
2022-12-03 04:07:06 +00:00
}
2022-10-27 07:11:35 +00:00
/**
* Get album if allowed. Also check if album is shared with user.
*
2022-12-03 04:07:06 +00:00
* @param string $uid UID of CURRENT user
* @param string $albumId $user/$name where $user is the OWNER of the album
2022-10-27 07:11:35 +00:00
*/
public function getIfAllowed(string $uid, string $albumId): ?array
2022-10-26 23:20:28 +00:00
{
$album = null;
2022-10-27 07:11:35 +00:00
// Split name and uid
$parts = explode('/', $albumId);
if (2 === \count($parts)) {
$albumUid = $parts[0];
$albumName = $parts[1];
// Check if owner
$query = $this->connection->getQueryBuilder();
$query->select('*')->from('photos_albums')->where(
$query->expr()->andX(
$query->expr()->eq('name', $query->createNamedParameter($albumName)),
$query->expr()->eq('user', $query->createNamedParameter($albumUid)),
)
);
$album = $query->executeQuery()->fetch();
2022-10-27 07:11:35 +00:00
}
// Album not found: it could be a link token at best
2022-10-27 07:11:35 +00:00
if (!$album) {
return $this->getAlbumByLink($albumId);
2022-10-26 23:20:28 +00:00
}
2022-10-27 07:11:35 +00:00
// Check if user is owner
if ($albumUid === $uid) {
return $album;
}
// Check in collaborators instead
2023-03-10 02:13:29 +00:00
$albumNumId = (int) $album['album_id'];
if ($this->userIsCollaborator($uid, $albumNumId)) {
2023-03-10 02:13:29 +00:00
return $album;
}
return null;
}
/**
* Check if user is a collaborator by numeric ID.
* Also checks if a group is a collaborator.
* Does not check if the user is the owner.
*
* @param string $uid User ID
* @param int $albumId Album ID (numeric)
*/
public function userIsCollaborator(string $uid, int $albumId): bool
2023-03-10 02:13:29 +00:00
{
$query = $this->connection->getQueryBuilder();
$ids = $this->getSelfCollaborators($uid);
2022-11-29 16:57:03 +00:00
$query->select('album_id')->from($this->collaboratorsTable())->where(
2022-10-26 23:20:28 +00:00
$query->expr()->andX(
2023-03-10 02:13:29 +00:00
$query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)),
$query->expr()->in('collaborator_id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_STR_ARRAY)),
2022-10-26 23:20:28 +00:00
)
);
2023-03-10 02:13:29 +00:00
return false !== $query->executeQuery()->fetchOne();
2022-10-26 23:20:28 +00:00
}
2022-11-29 16:57:03 +00:00
/**
* Get album object by token.
* Returns false if album link does not exist.
*/
public function getAlbumByLink(string $token)
{
$query = $this->connection->getQueryBuilder();
$query->select('*')->from('photos_albums', 'pa')
->innerJoin('pa', $this->collaboratorsTable(), 'pc', $query->expr()->andX(
$query->expr()->eq('pc.album_id', 'pa.album_id'),
$query->expr()->eq('collaborator_id', $query->createNamedParameter($token)),
$query->expr()->eq('collaborator_type', $query->createNamedParameter(3)), // = TYPE_LINK
))
;
return $query->executeQuery()->fetch() ?: null;
}
2022-12-06 19:38:57 +00:00
/**
* Get list of photos in album.
2022-12-06 19:38:57 +00:00
*/
public function getAlbumPhotos(int $albumId, ?int $limit)
2022-12-06 19:38:57 +00:00
{
$query = $this->connection->getQueryBuilder();
// SELECT all files
$query->select('file_id')->from('photos_albums_files', 'paf');
// WHERE they are in this album
$query->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)));
// AND in the filecache
2022-12-06 19:38:57 +00:00
$query->innerJoin('paf', 'filecache', 'fc', $query->expr()->eq('fc.fileid', 'paf.file_id'));
// Do not check if these files are indexed in memories
// This is since this function is called for downloads
// so funky things might happen if non-indexed files were
// added throught the Photos app
// ORDER by the id of the paf i.e. the order in which they were added
$query->orderBy('paf.album_file_id', 'DESC');
// LIMIT the results
if (null !== $limit) {
$query->setMaxResults($limit);
2022-12-06 19:38:57 +00:00
}
$result = $query->executeQuery()->fetchAll();
foreach ($result as &$row) {
$row['fileid'] = (int) $row['file_id'];
}
return $result;
2022-12-06 19:38:57 +00:00
}
/** Get list of collaborator ids including user id and groups */
private function getSelfCollaborators(string $uid)
{
// Get groups for the user
$groupManager = \OC::$server->get(\OCP\IGroupManager::class);
$user = \OC::$server->get(\OCP\IUserManager::class)->get($uid);
$groups = $groupManager->getUserGroupIds($user);
// And albums shared with user
$groups[] = $uid;
return $groups;
}
2022-11-29 16:57:03 +00:00
/** Get the name of the collaborators table */
2022-11-29 17:04:09 +00:00
private function collaboratorsTable()
{
2022-11-29 16:57:03 +00:00
// https://github.com/nextcloud/photos/commit/20e3e61ad577014e5f092a292c90a8476f630355
$appManager = \OC::$server->get(\OCP\App\IAppManager::class);
2022-11-29 16:57:03 +00:00
$photosVersion = $appManager->getAppVersion('photos');
2022-12-03 07:54:09 +00:00
if (version_compare($photosVersion, '2.0.1', '>=')) {
2022-11-29 16:57:03 +00:00
return 'photos_albums_collabs';
}
return 'photos_collaborators';
}
2022-10-26 22:12:46 +00:00
}