Server IP : 184.154.167.98 / Your IP : 3.144.18.59 Web Server : Apache System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : puertode ( 1767) PHP Version : 7.2.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/puertode/public_html/tampico1900/apps/photos/lib/Album/ |
Upload File : |
<?php declare(strict_types=1); /** * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Photos\Album; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCA\Photos\Exception\AlreadyInAlbumException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\IMimeTypeLoader; use OCP\Security\ISecureRandom; use OCP\IDBConnection; use OCP\IGroup; use OCP\IUser; use OCP\IUserManager; use OCP\IGroupManager; use OCP\IL10N; class AlbumMapper { private IDBConnection $connection; private IMimeTypeLoader $mimeTypeLoader; private ITimeFactory $timeFactory; private IUserManager $userManager; private IGroupManager $groupManager; protected IL10N $l; protected ISecureRandom $random; // Same mapping as IShare. public const TYPE_USER = 0; public const TYPE_GROUP = 1; public const TYPE_LINK = 3; public function __construct( IDBConnection $connection, IMimeTypeLoader $mimeTypeLoader, ITimeFactory $timeFactory, IUserManager $userManager, IGroupManager $groupManager, IL10N $l, ISecureRandom $random ) { $this->connection = $connection; $this->mimeTypeLoader = $mimeTypeLoader; $this->timeFactory = $timeFactory; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->l = $l; $this->random = $random; } public function create(string $userId, string $name, string $location = ""): AlbumInfo { $created = $this->timeFactory->getTime(); $query = $this->connection->getQueryBuilder(); $query->insert("photos_albums") ->values([ 'user' => $query->createNamedParameter($userId), 'name' => $query->createNamedParameter($name), 'location' => $query->createNamedParameter($location), 'created' => $query->createNamedParameter($created, IQueryBuilder::PARAM_INT), 'last_added_photo' => $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT), ]); $query->executeStatement(); $id = $query->getLastInsertId(); return new AlbumInfo($id, $userId, $name, $location, $created, -1); } public function get(int $id): ?AlbumInfo { $query = $this->connection->getQueryBuilder(); $query->select("name", "user", "location", "created", "last_added_photo") ->from("photos_albums") ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $row = $query->executeQuery()->fetch(); if ($row) { return new AlbumInfo($id, $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo']); } else { return null; } } /** * @param string $userId * @return AlbumInfo[] */ public function getForUser(string $userId): array { $query = $this->connection->getQueryBuilder(); $query->select("album_id", "name", "location", "created", "last_added_photo") ->from("photos_albums") ->where($query->expr()->eq('user', $query->createNamedParameter($userId))); $rows = $query->executeQuery()->fetchAll(); return array_map(function (array $row) use ($userId) { return new AlbumInfo((int)$row['album_id'], $userId, $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo']); }, $rows); } /** * @param int $fileId * @return AlbumInfo[] */ public function getForFile(int $fileId): array { $query = $this->connection->getQueryBuilder(); $query->select("a.album_id", "name", "user", "location", "created", "last_added_photo") ->from("photos_albums", "a") ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->where($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $rows = $query->executeQuery()->fetchAll(); return array_map(function (array $row) { return new AlbumInfo((int)$row['album_id'], $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo']); }, $rows); } /** * @param string $userId * @param int $fileId * @return AlbumInfo[] */ public function getForUserAndFile(string $userId, int $fileId): array { $query = $this->connection->getQueryBuilder(); $query->select("a.album_id", "name", "user", "location", "created", "last_added_photo") ->from("photos_albums", "a") ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->where($query->expr()->eq('user', $query->createNamedParameter($userId))) ->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $rows = $query->executeQuery()->fetchAll(); return array_map(function (array $row) { return new AlbumInfo((int)$row['album_id'], $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo']); }, $rows); } public function rename(int $id, string $newName): void { $query = $this->connection->getQueryBuilder(); $query->update("photos_albums") ->set("name", $query->createNamedParameter($newName)) ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->executeStatement(); } public function setLocation(int $id, string $newLocation): void { $query = $this->connection->getQueryBuilder(); $query->update("photos_albums") ->set("location", $query->createNamedParameter($newLocation)) ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->executeStatement(); } public function delete(int $id): void { $this->connection->beginTransaction(); $query = $this->connection->getQueryBuilder(); $query->delete("photos_albums") ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->executeStatement(); $query = $this->connection->getQueryBuilder(); $query->delete("photos_albums_files") ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->executeStatement(); $query = $this->connection->getQueryBuilder(); $query->delete("photos_albums_collabs") ->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->executeStatement(); $this->connection->commit(); } /** * @param int $albumId * @param string $userId * @return AlbumFile[] */ public function getForAlbumIdAndUserWithFiles(int $albumId, string $userId): array { $query = $this->connection->getQueryBuilder(); $query->select("fileid", "mimetype", "a.album_id", "size", "mtime", "etag", "added", "owner") ->selectAlias("f.name", "file_name") ->selectAlias("a.name", "album_name") ->from("photos_albums", "a") ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->leftJoin("p", "filecache", "f", $query->expr()->eq("p.file_id", "f.fileid")) ->where($query->expr()->eq('a.album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('user', $query->createNamedParameter($userId))); $rows = $query->executeQuery()->fetchAll(); $files = []; foreach ($rows as $row) { $albumId = (int)$row['album_id']; if ($row['fileid']) { $mimeId = $row['mimetype']; $mimeType = $this->mimeTypeLoader->getMimetypeById($mimeId); $files[] = new AlbumFile((int)$row['fileid'], $row['file_name'], $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner']); } } return $files; } /** * @param int $albumId * @param int $fileId * @return AlbumFile */ public function getForAlbumIdAndFileId(int $albumId, int $fileId): AlbumFile { $query = $this->connection->getQueryBuilder(); $query->select("fileid", "mimetype", "a.album_id", "user", "size", "mtime", "etag", "location", "created", "last_added_photo", "added", "owner") ->selectAlias("f.name", "file_name") ->selectAlias("a.name", "album_name") ->from("photos_albums", "a") ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->leftJoin("p", "filecache", "f", $query->expr()->eq("p.file_id", "f.fileid")) ->where($query->expr()->eq('a.album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $row = $query->executeQuery()->fetchAll()[0]; $mimeId = $row['mimetype']; $mimeType = $this->mimeTypeLoader->getMimetypeById($mimeId); return new AlbumFile((int)$row['fileid'], $row['file_name'], $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner']); } public function addFile(int $albumId, int $fileId, string $owner): void { $added = $this->timeFactory->getTime(); try { $query = $this->connection->getQueryBuilder(); $query->insert("photos_albums_files") ->values([ "album_id" => $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT), "file_id" => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT), "added" => $query->createNamedParameter($added, IQueryBuilder::PARAM_INT), "owner" => $query->createNamedParameter($owner), ]); $query->executeStatement(); } catch (UniqueConstraintViolationException $e) { throw new AlreadyInAlbumException("File already in album", 0, $e); } $query = $this->connection->getQueryBuilder(); $query->update("photos_albums") ->set('last_added_photo', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))); $query->executeStatement(); } public function removeFile(int $albumId, int $fileId): void { $query = $this->connection->getQueryBuilder(); $query->delete("photos_albums_files") ->where($query->expr()->eq("album_id", $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq("file_id", $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $query->executeStatement(); $query = $this->connection->getQueryBuilder(); $query->update("photos_albums") ->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))); $query->executeStatement(); } public function removeFilesForUser(int $albumId, string $userId) { // Remove all photos by this user from the album: $query = $this->connection->getQueryBuilder(); $query->delete('photos_albums_files') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('owner', $query->createNamedParameter($userId))) ->executeStatement(); // Update the last added photo: $query = $this->connection->getQueryBuilder(); $query->update("photos_albums") ->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->executeStatement(); } private function getLastAdded(int $albumId): int { $query = $this->connection->getQueryBuilder(); $query->select("file_id") ->from("photos_albums_files") ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->orderBy("added", "DESC") ->setMaxResults(1); $id = $query->executeQuery()->fetchOne(); if ($id === false) { return -1; } else { return (int)$id; } } /** * @param int $albumId * @return array<array{'id': string, 'label': string, 'type': int}> */ public function getCollaborators(int $albumId): array { $query = $this->connection->getQueryBuilder(); $query->select("collaborator_id", "collaborator_type") ->from("photos_albums_collabs") ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))); $rows = $query->executeQuery()->fetchAll(); $collaborators = array_map(function (array $row) { /** @var IUser|IGroup|null */ $displayName = null; switch ($row['collaborator_type']) { case self::TYPE_USER: $displayName = $this->userManager->get($row['collaborator_id'])->getDisplayName(); break; case self::TYPE_GROUP: $displayName = $this->groupManager->get($row['collaborator_id'])->getDisplayName(); break; case self::TYPE_LINK: $displayName = $this->l->t('Public link'); break; default: throw new \Exception('Invalid collaborator type: ' . $row['collaborator_type']); } if (is_null($displayName)) { return null; } return [ 'id' => $row['collaborator_id'], 'label' => $displayName, 'type' => (int)$row['collaborator_type'], ]; }, $rows); return array_values(array_filter($collaborators, fn ($c) => $c !== null)); } /** * @param int $albumId * @param string $userId * @return bool */ public function isCollaborator(int $albumId, string $userId): bool { $query = $this->connection->getQueryBuilder(); $query->select("collaborator_id", "collaborator_type") ->from("photos_albums_collabs") ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))); $rows = $query->executeQuery()->fetchAll(); foreach ($rows as $row) { switch ($row['collaborator_type']) { case self::TYPE_USER: if ($row['collaborator_id'] === $userId) { return true; } break; case self::TYPE_GROUP: if ($this->groupManager->isInGroup($userId, $row['collaborator_id'])) { return true; } break; default: break; } } return false; } /** * @param int $albumId * @param array{'id': string, 'type': int} $collaborators */ public function setCollaborators(int $albumId, array $collaborators): void { $existingCollaborators = $this->getCollaborators($albumId); // Different behavior if type is link to prevent creating multiple link. function computeKey($c) { return ($c['type'] === AlbumMapper::TYPE_LINK ? '' : $c['id']).$c['type']; } $collaboratorsToAdd = array_udiff($collaborators, $existingCollaborators, fn ($a, $b) => strcmp(computeKey($a), computeKey($b))); $collaboratorsToRemove = array_udiff($existingCollaborators, $collaborators, fn ($a, $b) => strcmp(computeKey($a), computeKey($b))); $this->connection->beginTransaction(); foreach ($collaboratorsToAdd as $collaborator) { switch ($collaborator['type']) { case self::TYPE_USER: if (is_null($this->userManager->get($collaborator['id']))) { throw new \Exception('Unknown collaborator: ' . $collaborator['id']); } break; case self::TYPE_GROUP: if (is_null($this->groupManager->get($collaborator['id']))) { throw new \Exception('Unknown collaborator: ' . $collaborator['id']); } break; case self::TYPE_LINK: $collaborator['id'] = $this->random->generate(32, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); break; default: throw new \Exception('Invalid collaborator type: ' . $collaborator['type']); } $query = $this->connection->getQueryBuilder(); $query->insert('photos_albums_collabs') ->setValue('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)) ->setValue('collaborator_id', $query->createNamedParameter($collaborator['id'])) ->setValue('collaborator_type', $query->createNamedParameter($collaborator['type'], IQueryBuilder::PARAM_INT)) ->executeStatement(); } foreach ($collaboratorsToRemove as $collaborator) { $query = $this->connection->getQueryBuilder(); $query->delete('photos_albums_collabs') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaborator['id']))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaborator['type'], IQueryBuilder::PARAM_INT))) ->executeStatement(); } $this->connection->commit(); } /** * @param string $collaboratorId * @param int $collaboratorType * @return AlbumInfo[] */ public function getSharedAlbumsForCollaborator(string $collaboratorId, int $collaboratorType): array { $query = $this->connection->getQueryBuilder(); $rows = $query ->select("a.album_id", "name", "user", "location", "created", "last_added_photo") ->from("photos_albums_collabs", "c") ->leftJoin("c", "photos_albums", "a", $query->expr()->eq("a.album_id", "c.album_id")) ->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT))) ->executeQuery() ->fetchAll(); return array_map(function (array $row) { return new AlbumInfo( (int)$row['album_id'], $row['user'], $row['name'].' ('.$row['user'].')', $row['location'], (int)$row['created'], (int)$row['last_added_photo'] ); }, $rows); } /** * @param string $collaboratorId * @param string $collaboratorsType - The type of the collaborator, either a user or a group. * @return AlbumWithFiles[] */ public function getSharedAlbumsForCollaboratorWithFiles(string $collaboratorId, int $collaboratorType): array { $query = $this->connection->getQueryBuilder(); $rows = $query ->select("fileid", "mimetype", "a.album_id", "size", "mtime", "etag", "location", "created", "last_added_photo", "added", 'owner') ->selectAlias("f.name", "file_name") ->selectAlias("a.name", "album_name") ->selectAlias("a.user", "album_user") ->from("photos_albums_collabs", "c") ->leftJoin("c", "photos_albums", "a", $query->expr()->eq("a.album_id", "c.album_id")) ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->leftJoin("p", "filecache", "f", $query->expr()->eq("p.file_id", "f.fileid")) ->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT))) ->executeQuery() ->fetchAll(); $filesByAlbum = []; $albumsById = []; foreach ($rows as $row) { $albumId = (int)$row['album_id']; if ($row['fileid']) { $mimeId = $row['mimetype']; $mimeType = $this->mimeTypeLoader->getMimetypeById($mimeId); $filesByAlbum[$albumId][] = new AlbumFile((int)$row['fileid'], $row['file_name'], $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner']); } if (!isset($albumsById[$albumId])) { $albumName = $row['album_name']; // Suffix album name with the album owner to prevent duplicates. // Not done for public link as it would like owner's uid. if ($collaboratorType !== self::TYPE_LINK) { $albumName = $row['album_name'].' ('.$row['album_user'].')'; } $albumsById[$albumId] = new AlbumInfo($albumId, $row['album_user'], $albumName, $row['location'], (int)$row['created'], (int)$row['last_added_photo']); } } $result = []; foreach ($albumsById as $id => $album) { $result[] = new AlbumWithFiles($album, $this, $filesByAlbum[$id] ?? []); } return $result; } /** * @param string $userId * @param int $albumId * @return void */ public function deleteUserFromAlbumCollaboratorsList(string $userId, int $albumId): void { $query = $this->connection->getQueryBuilder(); $query->delete('photos_albums_collabs') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($userId))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter(self::TYPE_USER, IQueryBuilder::PARAM_INT))) ->executeStatement(); // Remove all photos by this user from the album: $this->removeFilesForUser($albumId, $userId); } /** * @param string $groupId * @param int $albumId * @return void */ public function deleteGroupFromAlbumCollaboratorsList(string $groupId, int $albumId): void { $query = $this->connection->getQueryBuilder(); $query->delete('photos_albums_collabs') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($groupId))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter(self::TYPE_GROUP, IQueryBuilder::PARAM_INT))) ->executeStatement(); } /** * @param string $collaboratorId * @param int $collaboratorType * @param int $fileId * @return AlbumInfo[] */ public function getAlbumsForCollaboratorIdAndFileId(string $collaboratorId, int $collaboratorType, int $fileId): array { $query = $this->connection->getQueryBuilder(); $rows = $query ->select("a.album_id", "name", "user", "location", "created", "last_added_photo") ->from("photos_albums_collabs", "c") ->leftJoin("c", "photos_albums", "a", $query->expr()->eq("a.album_id", "c.album_id")) ->leftJoin("a", "photos_albums_files", "p", $query->expr()->eq("a.album_id", "p.album_id")) ->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId))) ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) ->groupBy('a.album_id') ->executeQuery() ->fetchAll(); return array_map(function (array $row) { return new AlbumInfo( (int)$row['album_id'], $row['user'], $row['name'].' ('.$row['user'].')', $row['location'], (int)$row['created'], (int)$row['last_added_photo'] ); }, $rows); } }