migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,79 @@
<?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\Sabre\Album;
use OCA\Photos\Album\AlbumFile;
use OCA\Photos\Album\AlbumInfo;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Sabre\CollectionPhoto;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use Sabre\DAV\IFile;
class AlbumPhoto extends CollectionPhoto implements IFile {
public function __construct(
private AlbumMapper $albumMapper,
private AlbumInfo $album,
private AlbumFile $albumFile,
private IRootFolder $rootFolder,
Folder $userFolder,
) {
parent::__construct($albumFile, $userFolder);
}
/**
* @return void
*/
public function delete() {
$this->albumMapper->removeFile($this->album->getId(), $this->file->getFileId());
}
private function getNode(): Node {
$nodes = $this->rootFolder
->getUserFolder($this->albumFile->getOwner() ?: $this->album->getUserId())
->getById($this->file->getFileId());
$node = current($nodes);
if ($node) {
return $node;
} else {
throw new NotFoundException("Photo not found for user");
}
}
public function get() {
$node = $this->getNode();
if ($node instanceof File) {
return $node->fopen('r');
} else {
throw new NotFoundException("Photo is a folder");
}
}
public function getFileInfo(): Node {
return $this->getNode();
}
}
@@ -0,0 +1,244 @@
<?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\Sabre\Album;
use OCA\DAV\Connector\Sabre\File;
use OCA\Photos\Album\AlbumFile;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Album\AlbumWithFiles;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use Sabre\DAV\Exception\Conflict;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
use Sabre\DAV\ICopyTarget;
use Sabre\DAV\INode;
class AlbumRoot implements ICollection, ICopyTarget {
protected AlbumMapper $albumMapper;
protected AlbumWithFiles $album;
protected IRootFolder $rootFolder;
protected string $userId;
public function __construct(
AlbumMapper $albumMapper,
AlbumWithFiles $album,
IRootFolder $rootFolder,
string $userId,
UserConfigService $userConfigService
) {
$this->albumMapper = $albumMapper;
$this->album = $album;
$this->rootFolder = $rootFolder;
$this->userId = $userId;
$this->userConfigService = $userConfigService;
}
/**
* @return void
*/
public function delete() {
$this->albumMapper->delete($this->album->getAlbum()->getId());
}
public function getName(): string {
return basename($this->album->getAlbum()->getTitle());
}
/**
* @return void
*/
public function setName($name) {
$this->albumMapper->rename($this->album->getAlbum()->getId(), $name);
}
protected function getPhotosLocationInfo() {
$photosLocation = $this->userConfigService->getUserConfig('photosLocation');
$userFolder = $this->rootFolder->getUserFolder($this->userId);
return [$photosLocation, $userFolder];
}
/**
* We cannot create files in an Album
* We add the file to the default Photos folder and then link it there.
*
* @param string $name
* @param null|resource|string $data
* @return void
*/
public function createFile($name, $data = null) {
try {
[$photosLocation, $userFolder] = $this->getPhotosLocationInfo();
try {
$photosFolder = $userFolder->get($photosLocation);
} catch (NotFoundException $e) {
// If the folder does not exists, create it
$photosFolder = $userFolder->newFolder($photosLocation);
}
// If the node is not a folder, we throw
if (!($photosFolder instanceof Folder)) {
throw new Conflict('The destination exists and is not a folder');
}
// Check for conflict and rename the file accordingly
$newName = \basename(\OC_Helper::buildNotExistingFileName($photosLocation, $name));
$node = $photosFolder->newFile($newName, $data);
$this->addFile($node->getId(), $node->getOwner()->getUID());
// Cheating with header because we are using fileID-fileName
// https://github.com/nextcloud/server/blob/af29b978078ffd9169a9bd9146feccbb7974c900/apps/dav/lib/Connector/Sabre/FilesPlugin.php#L564-L585
\header('OC-FileId: ' . $node->getId());
return '"' . $node->getEtag() . '"';
} catch (\Exception $e) {
throw new Forbidden('Could not create file');
}
}
/**
* @return never
*/
public function createDirectory($name) {
throw new Forbidden('Not allowed to create directories in this folder');
}
public function getChildren(): array {
return array_map(function (AlbumFile $file) {
return new AlbumPhoto($this->albumMapper, $this->album->getAlbum(), $file, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId));
}, $this->album->getFiles());
}
public function getChild($name): AlbumPhoto {
foreach ($this->album->getFiles() as $file) {
if ($file->getFileId() . "-" . $file->getName() === $name) {
return new AlbumPhoto($this->albumMapper, $this->album->getAlbum(), $file, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId));
}
}
throw new NotFound("$name not found");
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function getLastModified(): int {
return 0;
}
public function copyInto($targetName, $sourcePath, INode $sourceNode): bool {
if (!$sourceNode instanceof File) {
throw new Forbidden("The source is not a file");
}
$sourceId = $sourceNode->getId();
$ownerUID = $sourceNode->getFileInfo()->getOwner()->getUID();
$uid = $this->userId;
if ($ownerUID !== $uid) {
throw new Forbidden("Can't add file to album, only files from $uid can be added");
}
return $this->addFile($sourceId, $ownerUID);
}
protected function addFile(int $sourceId, string $ownerUID): bool {
if (in_array($sourceId, $this->album->getFileIds())) {
throw new Conflict("File $sourceId is already in the folder");
}
if ($ownerUID === $this->userId) {
$this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID);
$node = current($this->rootFolder->getUserFolder($ownerUID)->getById($sourceId));
$this->album->addFile(new AlbumFile($sourceId, $node->getName(), $node->getMimetype(), $node->getSize(), $node->getMTime(), $node->getEtag(), $node->getCreationTime(), $ownerUID));
return true;
}
return false;
}
public function getAlbum(): AlbumWithFiles {
return $this->album;
}
public function getDateRange(): array {
$earliestDate = null;
$latestDate = null;
foreach ($this->getChildren() as $child) {
try {
$childCreationDate = $child->getFileInfo()->getMtime();
} catch (NotFoundException $e) {
continue;
}
if ($childCreationDate < $earliestDate || $earliestDate === null) {
$earliestDate = $childCreationDate;
}
if ($childCreationDate > $earliestDate || $latestDate === null) {
$latestDate = $childCreationDate;
}
}
return ['start' => $earliestDate, 'end' => $latestDate];
}
/**
* @return int|null
*/
public function getCover() {
$children = $this->getChildren();
if (count($children) > 0) {
return $children[0]->getFileId();
} else {
return null;
}
}
/**
* @return array{array{'nc:collaborator': array{'id': string, 'label': string, 'type': int}}}
*/
public function getCollaborators(): array {
return array_map(
fn (array $collaborator) => [ 'nc:collaborator' => $collaborator ],
$this->albumMapper->getCollaborators($this->album->getAlbum()->getId()),
);
}
/**
* @param array{'id': string, 'type': int} $collaborators
* @return array{array{'nc:collaborator': array{'id': string, 'label': string, 'type': int}}}
*/
public function setCollaborators($collaborators): array {
$this->albumMapper->setCollaborators($this->getAlbum()->getAlbum()->getId(), $collaborators);
return $this->getCollaborators();
}
}
@@ -0,0 +1,128 @@
<?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\Sabre\Album;
use OCA\Photos\Album\AlbumInfo;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Album\AlbumWithFiles;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class AlbumsHome implements ICollection {
protected AlbumMapper $albumMapper;
protected array $principalInfo;
protected string $userId;
protected IRootFolder $rootFolder;
protected UserConfigService $userConfigService;
public const NAME = 'albums';
/**
* @var AlbumRoot[]
*/
protected ?array $children = null;
public function __construct(
array $principalInfo,
AlbumMapper $albumMapper,
string $userId,
IRootFolder $rootFolder,
UserConfigService $userConfigService
) {
$this->principalInfo = $principalInfo;
$this->albumMapper = $albumMapper;
$this->userId = $userId;
$this->rootFolder = $rootFolder;
$this->userConfigService = $userConfigService;
}
/**
* @return never
*/
public function delete() {
throw new Forbidden();
}
public function getName(): string {
return self::NAME;
}
/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Permission denied to rename this folder');
}
public function createFile($name, $data = null) {
throw new Forbidden('Not allowed to create files in this folder');
}
/**
* @return void
*/
public function createDirectory($name) {
$this->albumMapper->create($this->userId, $name);
}
public function getChild($name) {
foreach ($this->getChildren() as $child) {
if ($child->getName() === $name) {
return $child;
}
}
throw new NotFound();
}
/**
* @return AlbumRoot[]
*/
public function getChildren(): array {
if ($this->children === null) {
$albumInfos = $this->albumMapper->getForUser($this->userId);
$this->children = array_map(function (AlbumInfo $albumInfo) {
return new AlbumRoot($this->albumMapper, new AlbumWithFiles($albumInfo, $this->albumMapper), $this->rootFolder, $this->userId, $this->userConfigService);
}, $albumInfos);
}
return $this->children;
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function getLastModified(): int {
return 0;
}
}
@@ -0,0 +1,39 @@
<?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\Sabre\Album;
use OCP\Files\NotFoundException;
use Sabre\DAV\IFile;
class PublicAlbumPhoto extends AlbumPhoto implements IFile {
/** @return void */
public function delete() {
throw new NotFoundException("Deleting photos from a public album is not allowed.");
}
/** @return void */
public function put($data) {
throw new NotFoundException("Changing a photo from a public album is not allowed.");
}
}
@@ -0,0 +1,91 @@
<?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\Sabre\Album;
use OCA\Photos\Album\AlbumFile;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\INode;
class PublicAlbumRoot extends AlbumRoot {
/**
* @return void
*/
public function delete() {
throw new Forbidden('Not allowed to delete a public album');
}
/**
* @return void
*/
public function setName($name) {
throw new Forbidden('Not allowed to rename a public album');
}
public function copyInto($targetName, $sourcePath, INode $sourceNode): bool {
throw new Forbidden('Not allowed to copy into a public album');
}
protected function getPhotosLocationInfo() {
$albumOwner = $this->album->getAlbum()->getUserId();
$photosLocation = $this->userConfigService->getConfigForUser($albumOwner, 'photosLocation');
$userFolder = $this->rootFolder->getUserFolder($albumOwner);
return [$photosLocation, $userFolder];
}
public function createFile($name, $data = null) {
throw new Forbidden('Not allowed to create a file in a public album');
}
protected function addFile(int $sourceId, string $ownerUID): bool {
throw new Forbidden('Not allowed to add a file to a public album');
}
// Do not reveal collaborators for public albums.
public function getCollaborators(): array {
/** @var array{array{'nc:collaborator': array{'id': string, 'label': string, 'type': int}}} */
return [];
}
public function setCollaborators($collaborators): array {
throw new Forbidden('Not allowed to collaborators a public album');
}
/** @return never */
public function getChildren(): array {
return array_map(function (AlbumFile $file) {
return new PublicAlbumPhoto($this->albumMapper, $this->album->getAlbum(), $file, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId));
}, $this->album->getFiles());
}
public function getChild($name): PublicAlbumPhoto {
foreach ($this->album->getFiles() as $file) {
if ($file->getFileId() . "-" . $file->getName() === $name) {
return new PublicAlbumPhoto($this->albumMapper, $this->album->getAlbum(), $file, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId));
}
}
throw new NotFound("$name not found");
}
}
@@ -0,0 +1,100 @@
<?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\Sabre\Album;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Album\AlbumWithFiles;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use OCP\IUserManager;
use Sabre\DAV\Exception\Conflict;
use Sabre\DAV\Exception\Forbidden;
class SharedAlbumRoot extends AlbumRoot {
private IUserManager $userManager;
public function __construct(
AlbumMapper $albumMapper,
AlbumWithFiles $album,
IRootFolder $rootFolder,
string $userId,
UserConfigService $userConfigService,
IUserManager $userManager
) {
parent::__construct(
$albumMapper,
$album,
$rootFolder,
$userId,
$userConfigService,
$userManager
);
$this->userManager = $userManager;
}
/**
* @return void
*/
public function delete() {
$this->albumMapper->deleteUserFromAlbumCollaboratorsList($this->userId, $this->album->getAlbum()->getId());
}
/**
* @return void
*/
public function setName($name) {
throw new Forbidden('Not allowed to rename a shared album');
}
protected function addFile(int $sourceId, string $ownerUID): bool {
if (in_array($sourceId, $this->album->getFileIds())) {
throw new Conflict("File $sourceId is already in the folder");
}
if (!$this->albumMapper->isCollaborator($this->album->getAlbum()->getId(), $this->userId)) {
return false;
}
$this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID);
return true;
}
/**
* Return only the owner, and do not reveal other collaborators.
*/
public function getCollaborators(): array {
return [[
'nc:collaborator' => [
'id' => $this->album->getAlbum()->getUserId(),
'label' => $this->userManager->get($this->album->getAlbum()->getUserId())->getDisplayName(),
'type' => $this->album->getAlbum()->getReceivedFrom(),
],
]];
}
public function setCollaborators($collaborators): array {
throw new Forbidden('Not allowed to collaborators to a public album');
}
}
@@ -0,0 +1,90 @@
<?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\Sabre\Album;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Album\AlbumWithFiles;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUserManager;
use Sabre\DAV\Exception\Forbidden;
class SharedAlbumsHome extends AlbumsHome {
private IUserManager $userManager;
private IGroupManager $groupManager;
public const NAME = 'sharedalbums';
public function __construct(
array $principalInfo,
AlbumMapper $albumMapper,
string $userId,
IRootFolder $rootFolder,
IUserManager $userManager,
IGroupManager $groupManager,
UserConfigService $userConfigService
) {
parent::__construct(
$principalInfo,
$albumMapper,
$userId,
$rootFolder,
$userConfigService
);
$this->userManager = $userManager;
$this->groupManager = $groupManager;
}
/**
* @return never
*/
public function createDirectory($name) {
throw new Forbidden('Not allowed to create folders in this folder');
}
/**
* @return SharedAlbumRoot[]
*/
public function getChildren(): array {
if ($this->children === null) {
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($this->userId, AlbumMapper::TYPE_USER);
$user = $this->userManager->get($this->userId);
$userGroups = $this->groupManager->getUserGroupIds($user);
foreach ($userGroups as $groupId) {
$albumsForGroup = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($groupId, AlbumMapper::TYPE_GROUP);
$albumsForGroup = array_udiff($albumsForGroup, $albums, fn ($a, $b) => $a->getAlbum()->getId() - $b->getAlbum()->getId());
$albums = array_merge($albums, $albumsForGroup);
}
$this->children = array_map(function (AlbumWithFiles $album) {
return new SharedAlbumRoot($this->albumMapper, $album, $this->rootFolder, $this->userId, $this->userConfigService, $this->userManager);
}, $albums);
}
return $this->children;
}
}
@@ -0,0 +1,119 @@
<?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\Sabre;
use OCA\Photos\DB\PhotosFile;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\ITags;
use Sabre\DAV\Exception\Forbidden;
class CollectionPhoto {
public function __construct(
protected PhotosFile $file,
protected Folder $userFolder,
) {
}
public function getName() {
return $this->file->getFileId() . "-" . $this->file->getName();
}
/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Can\'t rename photos trough this api');
}
public function getLastModified() {
return $this->file->getMTime();
}
public function put($data) {
$nodes = $this->userFolder->getById($this->file->getFileId());
$node = current($nodes);
if ($node) {
/** @var Node $node */
if ($node instanceof File) {
return $node->putContent($data);
} else {
throw new NotFoundException("Photo is a folder");
}
} else {
throw new NotFoundException("Photo not found for user");
}
}
public function getFileId(): int {
return $this->file->getFileId();
}
public function getContentType() {
return $this->file->getMimeType();
}
public function getETag() {
return $this->file->getEtag();
}
public function getSize() {
return $this->file->getSize();
}
public function getFile(): PhotosFile {
return $this->file;
}
public function isFavorite(): bool {
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
$tagger = $tagManager->load('files');
if ($tagger === null) {
return false;
}
$tags = $tagger->getTagsForObjects([$this->getFileId()]);
if ($tags === false || empty($tags)) {
return false;
}
return array_search(ITags::TAG_FAVORITE, current($tags)) !== false;
}
public function setFavoriteState($favoriteState): bool {
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
$tagger = $tagManager->load('files');
switch ($favoriteState) {
case "0":
return $tagger->removeFromFavorites($this->file->getFileId());
case "1":
return $tagger->addToFavorites($this->file->getFileId());
default:
new \Exception('Favorite state is invalide, should be 0 or 1.');
}
}
}
@@ -0,0 +1,115 @@
<?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\Sabre;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\DB\Place\PlaceMapper;
use OCA\Photos\Sabre\Album\AlbumsHome;
use OCA\Photos\Sabre\Album\SharedAlbumsHome;
use OCA\Photos\Sabre\Place\PlacesHome;
use OCA\Photos\Service\ReverseGeoCoderService;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUserManager;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class PhotosHome implements ICollection {
public function __construct(
private array $principalInfo,
private AlbumMapper $albumMapper,
private PlaceMapper $placeMapper,
private ReverseGeoCoderService $reverseGeoCoderService,
private string $userId,
private IRootFolder $rootFolder,
private IUserManager $userManager,
private IGroupManager $groupManager,
private UserConfigService $userConfigService,
) {
}
/**
* @return never
*/
public function delete() {
throw new Forbidden();
}
public function getName(): string {
[, $name] = \Sabre\Uri\split($this->principalInfo['uri']);
return $name;
}
/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Permission denied to rename this folder');
}
public function createFile($name, $data = null) {
throw new Forbidden('Not allowed to create files in this folder');
}
/**
* @return never
*/
public function createDirectory($name) {
throw new Forbidden('Permission denied to create folders in this folder');
}
public function getChild($name) {
switch ($name) {
case AlbumsHome::NAME:
return new AlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userConfigService);
case SharedAlbumsHome::NAME:
return new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService);
case PlacesHome::NAME:
return new PlacesHome($this->userId, $this->rootFolder, $this->reverseGeoCoderService, $this->placeMapper);
}
throw new NotFound();
}
/**
* @return (AlbumsHome)[]
*/
public function getChildren(): array {
return [
new AlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userConfigService),
new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService),
new PlacesHome($this->userId, $this->rootFolder, $this->reverseGeoCoderService, $this->placeMapper),
];
}
public function childExists($name): bool {
return $name === AlbumsHome::NAME || $name === SharedAlbumsHome::NAME || $name === PlacesHome::NAME;
}
public function getLastModified(): int {
return 0;
}
}
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @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\Sabre\Place;
use OCA\Photos\DB\Place\PlaceFile;
use OCA\Photos\DB\Place\PlaceInfo;
use OCA\Photos\Sabre\CollectionPhoto;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\IFile;
class PlacePhoto extends CollectionPhoto implements IFile {
public function __construct(
private PlaceInfo $placeInfo,
PlaceFile $file,
private IRootFolder $rootFolder,
Folder $userFolder
) {
parent::__construct($file, $userFolder);
}
/**
* @return void
*/
public function delete() {
throw new Forbidden('Cannot remove from a place');
}
private function getNode(): Node {
$nodes = $this->rootFolder
->getUserFolder($this->placeInfo->getUserId())
->getById($this->file->getFileId());
$node = current($nodes);
if ($node) {
return $node;
} else {
throw new NotFoundException("Photo not found for user");
}
}
public function get() {
$node = $this->getNode();
if ($node instanceof File) {
return $node->fopen('r');
} else {
throw new NotFoundException("Photo is a folder");
}
}
public function getFileInfo(): Node {
return $this->getNode();
}
}
@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @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\Sabre\Place;
use OCA\Photos\DB\Place\PlaceFile;
use OCA\Photos\DB\Place\PlaceInfo;
use OCA\Photos\DB\Place\PlaceMapper;
use OCA\Photos\Service\ReverseGeoCoderService;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class PlaceRoot implements ICollection {
/** @var PlaceFile[]|null */
protected ?array $children = null;
public function __construct(
protected PlaceMapper $placeMapper,
protected ReverseGeoCoderService $reverseGeoCoderService,
protected PlaceInfo $placeInfo,
protected string $userId,
protected IRootFolder $rootFolder,
) {
}
/**
* @return never
*/
public function delete() {
throw new Forbidden('Not allowed to delete a place collection');
}
public function getName(): string {
return $this->placeInfo->getPlace();
}
/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Cannot change the place collection name');
}
/**
* @param string $name
* @param null|resource|string $data
* @return never
*/
public function createFile($name, $data = null) {
throw new Forbidden('Cannot create a file in a place collection');
}
/**
* @return never
*/
public function createDirectory($name) {
throw new Forbidden('Not allowed to create directories in this folder');
}
/**
* @return PlacePhoto[]
*/
public function getChildren(): array {
if ($this->children === null) {
$this->children = array_map(
fn (PlaceFile $file) => new PlacePhoto($this->placeInfo, $file, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId)),
$this->placeMapper->findFilesForUserAndPlace($this->placeInfo->getUserId(), $this->placeInfo->getPlace())
);
}
return $this->children;
}
public function getChild($name): PlacePhoto {
try {
[$fileId, $fileName] = explode('-', $name, 2);
$placeFile = $this->placeMapper->findFileForUserAndPlace($this->placeInfo->getUserId(), $this->placeInfo->getPlace(), $fileId, $fileName);
return new PlacePhoto($this->placeInfo, $placeFile, $this->rootFolder, $this->rootFolder->getUserFolder($this->userId));
} catch (NotFoundException $ex) {
throw new NotFound("File $name not found", 0, $ex);
}
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function getLastModified(): int {
return 0;
}
public function getFirstPhoto(): int {
$children = $this->getChildren();
if (count($children) === 0) {
throw new \Exception('No children found for place');
}
return $children[0]->getFileId();
}
/**
* @return int[]
*/
public function getFileIds(): array {
return array_map(function (PlacePhoto $file) {
return $file->getFileId();
}, $this->getChildren());
}
/**
* @return int|null
*/
public function getCover() {
$children = $this->getChildren();
if (count($children) > 0) {
return $children[0]->getFileId();
} else {
return null;
}
}
}
@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @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\Sabre\Place;
use OCA\Photos\DB\Place\PlaceInfo;
use OCA\Photos\DB\Place\PlaceMapper;
use OCA\Photos\Service\ReverseGeoCoderService;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class PlacesHome implements ICollection {
public const NAME = 'places';
/**
* @var PlaceRoot[]
*/
protected ?array $children = null;
public function __construct(
protected string $userId,
protected IRootFolder $rootFolder,
protected ReverseGeoCoderService $reverseGeoCoderService,
protected PlaceMapper $placeMapper,
) {
}
/**
* @return never
*/
public function delete() {
throw new Forbidden();
}
public function getName(): string {
return self::NAME;
}
/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Permission denied to rename this folder');
}
public function createFile($name, $data = null) {
throw new Forbidden('Not allowed to create files in this folder');
}
public function createDirectory($name) {
throw new Forbidden('Not allowed to create folder in this folder');
}
public function getChild($name): PlaceRoot {
try {
$placeInfo = $this->placeMapper->findPlaceForUser($this->userId, $name);
return new PlaceRoot($this->placeMapper, $this->reverseGeoCoderService, $placeInfo, $this->userId, $this->rootFolder);
} catch (NotFoundException $ex) {
throw new NotFound("Place $name does not exist", 0, $ex);
}
}
/**
* @return PlaceRoot[]
*/
public function getChildren(): array {
if ($this->children === null) {
$this->children = array_map(
fn (PlaceInfo $placeInfo) => new PlaceRoot($this->placeMapper, $this->reverseGeoCoderService, $placeInfo, $this->userId, $this->rootFolder),
$this->placeMapper->findPlacesForUser($this->userId)
);
}
return $this->children;
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function getLastModified(): int {
return 0;
}
}
@@ -0,0 +1,164 @@
<?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\Sabre;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Sabre\Album\AlbumPhoto;
use OCA\Photos\Sabre\Album\AlbumRoot;
use OCA\Photos\Sabre\Album\PublicAlbumPhoto;
use OCA\Photos\Sabre\Place\PlacePhoto;
use OCA\Photos\Sabre\Place\PlaceRoot;
use OCP\Files\DavUtil;
use OCP\Files\NotFoundException;
use OCP\FilesMetadata\IFilesMetadataManager;
use OCP\IPreview;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\DAV\Tree;
class PropFindPlugin extends ServerPlugin {
public const ORIGINAL_NAME_PROPERTYNAME = '{http://nextcloud.org/ns}original-name';
public const FILE_NAME_PROPERTYNAME = '{http://nextcloud.org/ns}file-name';
public const FAVORITE_PROPERTYNAME = '{http://owncloud.org/ns}favorite';
public const DATE_RANGE_PROPERTYNAME = '{http://nextcloud.org/ns}dateRange';
public const LOCATION_PROPERTYNAME = '{http://nextcloud.org/ns}location';
public const LAST_PHOTO_PROPERTYNAME = '{http://nextcloud.org/ns}last-photo';
public const NBITEMS_PROPERTYNAME = '{http://nextcloud.org/ns}nbItems';
public const COLLABORATORS_PROPERTYNAME = '{http://nextcloud.org/ns}collaborators';
public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
private IPreview $previewManager;
private ?Tree $tree;
private AlbumMapper $albumMapper;
public function __construct(
IPreview $previewManager,
AlbumMapper $albumMapper,
private IFilesMetadataManager $filesMetadataManager,
) {
$this->previewManager = $previewManager;
$this->albumMapper = $albumMapper;
}
/**
* Returns a plugin name.
*
* Using this name other plugins will be able to access other plugins
* using DAV\Server::getPlugin
*
* @return string
*/
public function getPluginName() {
return 'photosDavPlugin';
}
/**
* @return void
*/
public function initialize(Server $server) {
$this->tree = $server->tree;
$server->on('propFind', [$this, 'propFind']);
$server->on('propPatch', [$this, 'handleUpdateProperties']);
}
public function propFind(PropFind $propFind, INode $node): void {
if ($node instanceof AlbumPhoto || $node instanceof PlacePhoto) {
// Checking if the node is truly available and ignoring if not
// Should be pre-emptively handled by the NodeDeletedEvent
try {
$fileInfo = $node->getFileInfo();
} catch (NotFoundException $e) {
return;
}
$propFind->handle(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, fn () => $node->getFile()->getFileId());
$propFind->handle(FilesPlugin::GETETAG_PROPERTYNAME, fn () => $node->getETag());
$propFind->handle(self::FILE_NAME_PROPERTYNAME, fn () => $node->getFile()->getName());
$propFind->handle(self::FAVORITE_PROPERTYNAME, fn () => $node->isFavorite() ? 1 : 0);
$propFind->handle(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, fn () => json_encode($this->previewManager->isAvailable($fileInfo)));
$propFind->handle(FilesPlugin::PERMISSIONS_PROPERTYNAME, function () use ($node): string {
$permissions = DavUtil::getDavPermissions($node->getFileInfo());
$filteredPermissions = str_replace('R', '', $permissions);
if ($node instanceof PublicAlbumPhoto) {
$filteredPermissions = str_replace('D', '', $filteredPermissions);
$filteredPermissions = str_replace('NV', '', $filteredPermissions);
$filteredPermissions = str_replace('W', '', $filteredPermissions);
}
return $filteredPermissions;
});
foreach ($node->getFileInfo()->getMetadata() as $metadataKey => $metadataValue) {
$propFind->handle(FilesPlugin::FILE_METADATA_PREFIX.$metadataKey, $metadataValue);
}
$propFind->handle(FilesPlugin::HIDDEN_PROPERTYNAME, function () use ($node) {
$metadata = $this->filesMetadataManager->getMetadata((int)$node->getFileInfo()->getId(), true);
return $metadata->hasKey('files-live-photo') && $node->getFileInfo()->getMimetype() === 'video/quicktime' ? 'true' : 'false';
});
}
if ($node instanceof AlbumRoot) {
$propFind->handle(self::ORIGINAL_NAME_PROPERTYNAME, fn () => $node->getAlbum()->getAlbum()->getTitle());
$propFind->handle(self::LAST_PHOTO_PROPERTYNAME, fn () => $node->getAlbum()->getAlbum()->getLastAddedPhoto());
$propFind->handle(self::NBITEMS_PROPERTYNAME, fn () => count($node->getChildren()));
$propFind->handle(self::LOCATION_PROPERTYNAME, fn () => $node->getAlbum()->getAlbum()->getLocation());
$propFind->handle(self::DATE_RANGE_PROPERTYNAME, fn () => json_encode($node->getDateRange()));
$propFind->handle(self::COLLABORATORS_PROPERTYNAME, fn () => $node->getCollaborators());
}
if ($node instanceof PlaceRoot) {
$propFind->handle(self::LAST_PHOTO_PROPERTYNAME, fn () => $node->getFirstPhoto());
$propFind->handle(self::NBITEMS_PROPERTYNAME, fn () => count($node->getChildren()));
}
}
public function handleUpdateProperties($path, PropPatch $propPatch): void {
$node = $this->tree->getNodeForPath($path);
if ($node instanceof AlbumRoot) {
$propPatch->handle(self::LOCATION_PROPERTYNAME, function ($location) use ($node) {
$this->albumMapper->setLocation($node->getAlbum()->getAlbum()->getId(), $location);
return true;
});
$propPatch->handle(self::COLLABORATORS_PROPERTYNAME, function ($collaborators) use ($node) {
$collaborators = $node->setCollaborators(json_decode($collaborators, true));
return true;
});
}
if ($node instanceof AlbumPhoto) {
$propPatch->handle(self::FAVORITE_PROPERTYNAME, function ($favoriteState) use ($node) {
$node->setFavoriteState($favoriteState);
return true;
});
}
}
}
@@ -0,0 +1,78 @@
<?php
/**
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
*
* @author Louis Chmn <louis@chmn.me>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Photos\Sabre;
use Sabre\DAV\Auth\Backend\BackendInterface;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class PublicAlbumAuthBackend implements BackendInterface {
public function __construct() {
}
/**
* When this method is called, the backend must check if authentication was
* successful.
*
* The returned value must be one of the following
*
* [true, "principals/username"]
* [false, "reason for failure"]
*
* If authentication was successful, it's expected that the authentication
* backend returns a so-called principal url.
*
* Examples of a principal url:
*
* principals/admin
* principals/user1
* principals/users/joe
* principals/uid/123457
*
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
* return a string such as:
*
* principals/users/[username]
*
* @return array
*/
public function check(RequestInterface $request, ResponseInterface $response) {
\OC_User::setIncognitoMode(true);
return [true, "principals/token"];
}
/**
* This method is called when a user could not be authenticated, and
* authentication was required for the current request.
*
* This gives you the opportunity to set authentication headers. The 401
* status code will already be set.
*
* Keep in mind that in the case of multiple authentication backends, other
* WWW-Authenticate headers may already have been set, and you'll want to
* append your own WWW-Authenticate header instead of overwriting the
* existing one.
*/
public function challenge(RequestInterface $request, ResponseInterface $response) {
// This is intended to be public - there is no need to set WWW-Authenticate header
}
}
@@ -0,0 +1,99 @@
<?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\Sabre;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Sabre\Album\PublicAlbumRoot;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\Security\Bruteforce\IThrottler;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAVACL\PrincipalBackend;
class PublicRootCollection extends AbstractPrincipalCollection {
private const BRUTEFORCE_ACTION = 'publicphotos_webdav_auth';
private AlbumMapper $albumMapper;
private IRootFolder $rootFolder;
private UserConfigService $userConfigService;
private IRequest $request;
private IThrottler $throttler;
public function __construct(
AlbumMapper $albumMapper,
IRootFolder $rootFolder,
PrincipalBackend\BackendInterface $principalBackend,
UserConfigService $userConfigService,
IRequest $request,
IThrottler $throttler
) {
parent::__construct($principalBackend, 'principals/token');
$this->albumMapper = $albumMapper;
$this->rootFolder = $rootFolder;
$this->userConfigService = $userConfigService;
$this->request = $request;
$this->throttler = $throttler;
}
public function getName(): string {
return 'photospublic';
}
/**
* Child are retrieved directly by getChild.
* This should never be called.
* @param array $principalInfo
*/
public function getChildForPrincipal(array $principalInfo): PublicAlbumRoot {
throw new \Sabre\DAV\Exception\Forbidden();
}
/**
* Returns a child object, by its token.
*
* @param string $token
*
* @throws NotFound
*
* @return DAV\INode
*/
public function getChild($token) {
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
if (is_null($token)) {
throw new \Sabre\DAV\Exception\Forbidden();
}
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($token, AlbumMapper::TYPE_LINK);
if (count($albums) !== 1) {
$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
throw new NotFound("Unable to find public album");
}
return new PublicAlbumRoot($this->albumMapper, $albums[0], $this->rootFolder, $albums[0]->getAlbum()->getUserId(), $this->userConfigService);
}
}
@@ -0,0 +1,73 @@
<?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\Sabre;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\DB\Place\PlaceMapper;
use OCA\Photos\Service\ReverseGeoCoderService;
use OCA\Photos\Service\UserConfigService;
use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUserManager;
use OCP\IUserSession;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAVACL\PrincipalBackend;
class RootCollection extends AbstractPrincipalCollection {
public function __construct(
private AlbumMapper $albumMapper,
private PlaceMapper $placeMapper,
private ReverseGeoCoderService $reverseGeoCoderService,
private IUserSession $userSession,
private IRootFolder $rootFolder,
PrincipalBackend\BackendInterface $principalBackend,
private IUserManager $userManager,
private IGroupManager $groupManager,
private UserConfigService $userConfigService,
) {
parent::__construct($principalBackend, 'principals/users');
}
/**
* This method returns a node for a principal.
*
* The passed array contains principal information, and is guaranteed to
* at least contain a uri item. Other properties may or may not be
* supplied by the authentication backend.
*
* @param array $principalInfo
*/
public function getChildForPrincipal(array $principalInfo): PhotosHome {
[, $name] = \Sabre\Uri\split($principalInfo['uri']);
$user = $this->userSession->getUser();
if (is_null($user) || $name !== $user->getUID()) {
throw new \Sabre\DAV\Exception\Forbidden();
}
return new PhotosHome($principalInfo, $this->albumMapper, $this->placeMapper, $this->reverseGeoCoderService, $name, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService);
}
public function getName(): string {
return 'photos';
}
}