From ff01726c6b1a735aace5e88da8387b2e11e4b6f8 Mon Sep 17 00:00:00 2001 From: Louis Chemineau Date: Wed, 21 Sep 2022 16:50:28 +0200 Subject: [PATCH] Add public link logic Signed-off-by: Louis Chemineau --- appinfo/routes.php | 12 +- lib/Album/AlbumMapper.php | 26 +- lib/Controller/PublicAlbumController.php | 123 +++++++ lib/Sabre/Album/AlbumPhoto.php | 6 +- lib/Sabre/Album/AlbumRoot.php | 11 +- lib/Sabre/Album/PropFindPlugin.php | 24 +- lib/Sabre/Album/PublicAlbumRoot.php | 95 +++++ lib/Sabre/Album/PublicAlbumsHome.php | 73 ++++ lib/Sabre/Album/SharedAlbumRoot.php | 5 + lib/Sabre/Album/SharedAlbumsHome.php | 4 +- lib/Sabre/PhotosHome.php | 5 +- lib/Service/UserConfigService.php | 10 +- src/PhotosPublic.vue | 102 ++++++ .../Albums/CollaboratorsSelectionForm.vue | 160 ++++++--- .../Collection/CollectionContent.vue | 6 +- src/components/HeaderNavigation.vue | 4 +- src/mixins/FetchAlbumsMixin.js | 75 +--- src/mixins/FetchSharedAlbumsMixin.js | 63 +--- src/public.js | 67 ++++ src/router/index.js | 10 + src/services/Albums.js | 168 ++++++++- src/store/collectionStoreFactory.js | 214 ++++++++++++ src/store/index.js | 2 + src/store/publicAlbums.js | 213 ++++++++++++ src/views/AlbumContent.vue | 4 +- src/views/Albums.vue | 6 +- src/views/PublicAlbumContent.vue | 324 ++++++++++++++++++ src/views/SharedAlbumContent.vue | 8 +- src/views/SharedAlbums.vue | 10 +- webpack.js | 7 +- 30 files changed, 1593 insertions(+), 244 deletions(-) create mode 100644 lib/Controller/PublicAlbumController.php create mode 100644 lib/Sabre/Album/PublicAlbumRoot.php create mode 100644 lib/Sabre/Album/PublicAlbumsHome.php create mode 100644 src/PhotosPublic.vue create mode 100644 src/public.js create mode 100644 src/store/collectionStoreFactory.js create mode 100644 src/store/publicAlbums.js create mode 100644 src/views/PublicAlbumContent.vue diff --git a/appinfo/routes.php b/appinfo/routes.php index d290fdb0..2389fc2d 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -46,6 +46,14 @@ return [ 'path' => '', ] ], + [ 'name' => 'publicAlbum#get', 'url' => '/public/{ownerId}/{token}', 'verb' => 'GET', + 'requirements' => [ + 'ownerId' => '.*', + ], + 'requirements' => [ + 'token' => '.*', + ], + ], ['name' => 'page#index', 'url' => '/folders/{path}', 'verb' => 'GET', 'postfix' => 'folders', 'requirements' => [ 'path' => '.*', @@ -74,10 +82,8 @@ return [ 'requirements' => [ 'path' => '.*', ], - 'defaults' => [ - 'path' => '', - ] ], + [ 'name' => 'public#get', 'url' => '/display/{token}', 'verb' => 'GET' ], ['name' => 'page#index', 'url' => '/tags/{path}', 'verb' => 'GET', 'postfix' => 'tags', 'requirements' => [ 'path' => '.*', diff --git a/lib/Album/AlbumMapper.php b/lib/Album/AlbumMapper.php index a38ee2a5..577e0064 100644 --- a/lib/Album/AlbumMapper.php +++ b/lib/Album/AlbumMapper.php @@ -33,6 +33,7 @@ use OCP\IGroup; use OCP\IUser; use OCP\IUserManager; use OCP\IGroupManager; +use OCP\IL10N; class AlbumMapper { private IDBConnection $connection; @@ -40,6 +41,7 @@ class AlbumMapper { private ITimeFactory $timeFactory; private IUserManager $userManager; private IGroupManager $groupManager; + protected IL10N $l; // Same mapping as IShare. public const TYPE_USER = 0; @@ -51,13 +53,15 @@ class AlbumMapper { IMimeTypeLoader $mimeTypeLoader, ITimeFactory $timeFactory, IUserManager $userManager, - IGroupManager $groupManager + IGroupManager $groupManager, + IL10N $l ) { $this->connection = $connection; $this->mimeTypeLoader = $mimeTypeLoader; $this->timeFactory = $timeFactory; $this->userManager = $userManager; $this->groupManager = $groupManager; + $this->l = $l; } public function create(string $userId, string $name, string $location = ""): AlbumInfo { @@ -147,12 +151,13 @@ class AlbumMapper { $query->executeStatement(); } - public function setLocation(int $id, string $newLocation): void { + public function setLocation(int $id, string $newLocation): string { $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(); + return $newLocation; } public function delete(int $id): void { @@ -294,26 +299,29 @@ class AlbumMapper { $collaborators = array_map(function (array $row) { /** @var IUser|IGroup|null */ - $collaborator = null; + $displayName = null; switch ($row['collaborator_type']) { case self::TYPE_USER: - $collaborator = $this->userManager->get($row['collaborator_id']); + $displayName = $this->userManager->get($row['collaborator_id'])->getDisplayName(); break; case self::TYPE_GROUP: - $collaborator = $this->groupManager->get($row['collaborator_id']); + $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($collaborator)) { + if (is_null($displayName)) { return null; } return [ 'id' => $row['collaborator_id'], - 'label' => $collaborator->getDisplayName(), + 'label' => $displayName, 'type' => $row['collaborator_type'], ]; }, $rows); @@ -345,6 +353,8 @@ class AlbumMapper { throw new \Exception('Unknown collaborator: ' . $collaborator['id']); } break; + case self::TYPE_LINK: + break; default: throw new \Exception('Invalid collaborator type: ' . $collaborator['type']); } @@ -397,7 +407,7 @@ class AlbumMapper { if ($row['fileid']) { $mimeId = $row['mimetype']; $mimeType = $this->mimeTypeLoader->getMimetypeById($mimeId); - $filesByAlbum[$albumId][] = new AlbumFile((int)$row['fileid'], $row['album_name'].' ('.$row['album_user'].')', $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner']); + $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])) { diff --git a/lib/Controller/PublicAlbumController.php b/lib/Controller/PublicAlbumController.php new file mode 100644 index 00000000..b9a9364e --- /dev/null +++ b/lib/Controller/PublicAlbumController.php @@ -0,0 +1,123 @@ + + * + * @author Louis Chemineau + * + * @license AGPL-3.0-or-later + * + * 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 . + * + */ + +namespace OCA\Photos\Controller; + +use OCP\AppFramework\PublicShareController; +use OCA\Files\Event\LoadSidebar; +use OCA\Photos\AppInfo\Application; +use OCA\Photos\Service\UserConfigService; +use OCA\Viewer\Event\LoadViewer; +use OCP\App\IAppManager; +use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\AppFramework\Services\IInitialState; +use OCP\IRequest; +use OCP\ISession; +use OCP\Util; + + +class PublicAlbumController extends PublicShareController { + private IAppManager $appManager; + private IEventDispatcher $eventDispatcher; + private UserConfigService $userConfig; + private IInitialState $initialState; + + public function __construct( + IRequest $request, + ISession $session, + IAppManager $appManager, + IEventDispatcher $eventDispatcher, + UserConfigService $userConfig, + IInitialState $initialState, + ) { + parent::__construct(Application::APP_ID, $request, $session); + + $this->appManager = $appManager; + $this->eventDispatcher = $eventDispatcher; + $this->userConfig = $userConfig; + $this->initialState = $initialState; + } + + /** + * Validate the token of this share. If the token is invalid this controller + * will return a 404. + */ + public function isValidToken(): bool { + // TODO: uncomment + // $album = $this->albumMapper->getAlbumForToken($this->getToken); + // return $album !== null; + return true; + } + + public function getPasswordHash(): string { + return ''; + } + + /** + * Allows you to specify if this share is password protected + */ + protected function isPasswordProtected(): bool { + return false; + } + + /** + * Your normal controller function. The following annotation will allow guests + * to open the page as well + * + * @PublicPage + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function get(): TemplateResponse { + $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); + $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); + + $this->initialState->provideInitialState('image-mimes', Application::IMAGE_MIMES); + $this->initialState->provideInitialState('video-mimes', Application::VIDEO_MIMES); + $this->initialState->provideInitialState('maps', $this->appManager->isEnabledForUser('maps') === true); + $this->initialState->provideInitialState('recognize', $this->appManager->isEnabledForUser('recognize') === true); + $this->initialState->provideInitialState('systemtags', $this->appManager->isEnabledForUser('systemtags') === true); + + // Provide user config + foreach (array_keys(UserConfigService::DEFAULT_CONFIGS) as $key) { + $this->initialState->provideInitialState($key, $this->userConfig->getUserConfig($key)); + } + + Util::addScript(Application::APP_ID, 'photos-public'); + Util::addStyle(Application::APP_ID, 'icons'); + + $response = new TemplateResponse(Application::APP_ID, 'main'); + + $policy = new ContentSecurityPolicy(); + $policy->addAllowedWorkerSrcDomain("'self'"); + $policy->addAllowedScriptDomain("'self'"); + $response->setContentSecurityPolicy($policy); + + return $response; + } +} \ No newline at end of file diff --git a/lib/Sabre/Album/AlbumPhoto.php b/lib/Sabre/Album/AlbumPhoto.php index a5e51bff..24ab0c41 100644 --- a/lib/Sabre/Album/AlbumPhoto.php +++ b/lib/Sabre/Album/AlbumPhoto.php @@ -152,9 +152,11 @@ class AlbumPhoto implements IFile { switch ($favoriteState) { case "0": - return $tagger->removeFromFavorites($this->albumFile->getFileId()); + $tagger->removeFromFavorites($this->albumFile->getFileId()); + return "0"; case "1": - return $tagger->addToFavorites($this->albumFile->getFileId()); + $tagger->addToFavorites($this->albumFile->getFileId()); + return "1"; default: new \Exception('Favorite state is invalide, should be 0 or 1.'); } diff --git a/lib/Sabre/Album/AlbumRoot.php b/lib/Sabre/Album/AlbumRoot.php index ab8b1496..eed0b507 100644 --- a/lib/Sabre/Album/AlbumRoot.php +++ b/lib/Sabre/Album/AlbumRoot.php @@ -212,7 +212,7 @@ class AlbumRoot implements ICollection, ICopyTarget { } /** - * @return array + * @return array{'id': string, 'label': string, 'type': int} */ public function getCollaborators() { return array_map( @@ -220,4 +220,13 @@ class AlbumRoot implements ICollection, ICopyTarget { $this->albumMapper->getCollaborators($this->album->getAlbum()->getId()), ); } + + /** + * @param array{'id': string, 'type': int} $collaborators + * @return array{'id': string, 'label': string, 'type': int} + */ + public function setCollaborators($collaborators) { + $this->albumMapper->setCollaborators($this->getAlbum()->getAlbum()->getId(), $collaborators); + return $this->getCollaborators(); + } } diff --git a/lib/Sabre/Album/PropFindPlugin.php b/lib/Sabre/Album/PropFindPlugin.php index 46d1c225..00fb8017 100644 --- a/lib/Sabre/Album/PropFindPlugin.php +++ b/lib/Sabre/Album/PropFindPlugin.php @@ -78,7 +78,7 @@ class PropFindPlugin extends ServerPlugin { public function propFind(PropFind $propFind, INode $node): void { if ($node instanceof AlbumPhoto) { - // Checking if the node is trulely available and ignoring if not + // Checking if the node is truly available and ignoring if not // Should be pre-emptively handled by the NodeDeletedEvent try { $fileInfo = $node->getFileInfo(); @@ -87,12 +87,10 @@ class PropFindPlugin extends ServerPlugin { } $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, function () use ($fileInfo) { - return json_encode($this->previewManager->isAvailable($fileInfo)); - }); + $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))); if ($this->metadataEnabled) { $propFind->handle(FilesPlugin::FILE_METADATA_SIZE, function () use ($node) { @@ -111,11 +109,11 @@ class PropFindPlugin extends ServerPlugin { } } - if ($node instanceof AlbumRoot || $node instanceof SharedAlbumRoot) { - $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())); + if ($node instanceof AlbumRoot) { + $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()); // TODO detect dynamically which metadata groups are requested and @@ -141,7 +139,7 @@ class PropFindPlugin extends ServerPlugin { return true; }); $propPatch->handle(self::COLLABORATORS_PROPERTYNAME, function ($collaborators) use ($node) { - $this->albumMapper->setCollaborators($node->getAlbum()->getAlbum()->getId(), json_decode($collaborators, true)); + $collaborators = $node->setCollaborators(json_decode($collaborators, true)); return true; }); } diff --git a/lib/Sabre/Album/PublicAlbumRoot.php b/lib/Sabre/Album/PublicAlbumRoot.php new file mode 100644 index 00000000..8ca2e1e6 --- /dev/null +++ b/lib/Sabre/Album/PublicAlbumRoot.php @@ -0,0 +1,95 @@ + + * + * @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 . + * + */ + +namespace OCA\Photos\Sabre\Album; + +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\Conflict; +use OCP\Files\Folder; + +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'); + } + + // TODO: uncomment else it is a security hole. + // public function copyInto($targetName, $sourcePath, INode $sourceNode): bool { + // throw new Forbidden('Not allowed to copy into a public album'); + // } + + /** + * We cannot create files in an Album + * We add the file to the default Photos folder and then link it there. + * + * @param [type] $name + * @param [type] $data + * @return void + */ + public function createFile($name, $data = null) { + try { + $albumOwner = $this->album->getAlbum()->getUserId(); + $photosLocation = $this->userConfigService->getConfigForUser($albumOwner, 'photosLocation'); + $photosFolder = $this->rootFolder->getUserFolder($albumOwner)->get($photosLocation); + 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'); + } + } + + + 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"); + } + + $this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID); + return true; + } + + // Do not reveal collaborators for public albums. + public function getCollaborators() { + return []; + } +} diff --git a/lib/Sabre/Album/PublicAlbumsHome.php b/lib/Sabre/Album/PublicAlbumsHome.php new file mode 100644 index 00000000..9d7d7362 --- /dev/null +++ b/lib/Sabre/Album/PublicAlbumsHome.php @@ -0,0 +1,73 @@ + + * + * @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 . + * + */ + +namespace OCA\Photos\Sabre\Album; + +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use OCP\Files\IRootFolder; +use OCP\IUser; +use OCA\Photos\Sabre\Album\PublicAlbumRoot; +use OCA\Photos\Service\UserConfigService; +use OCA\Photos\Album\AlbumMapper; + +class PublicAlbumsHome extends AlbumsHome { + public function __construct( + array $principalInfo, + AlbumMapper $albumMapper, + IUser $user, + IRootFolder $rootFolder, + UserConfigService $userConfigService, + ) { + parent::__construct( + $principalInfo, + $albumMapper, + $user, + $rootFolder, + $userConfigService, + ); + } + + public function getName(): string { + return 'public'; + } + + /** + * @return never + */ + public function createDirectory($name) { + throw new Forbidden('Not allowed to create folders in this folder'); + } + + public function getChild($name) { + $albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($name, AlbumMapper::TYPE_LINK); + + array_filter($albums, fn ($album) => $album->getAlbum()->getUserId() === $this->user->getUid()); + + if (count($albums) !== 1) { + throw new NotFound(); + } + + return new PublicAlbumRoot($this->albumMapper, $albums[0], $this->rootFolder, $this->userFolder, $this->user, $this->userConfigService); + } +} diff --git a/lib/Sabre/Album/SharedAlbumRoot.php b/lib/Sabre/Album/SharedAlbumRoot.php index 26a2272e..607a009f 100644 --- a/lib/Sabre/Album/SharedAlbumRoot.php +++ b/lib/Sabre/Album/SharedAlbumRoot.php @@ -59,4 +59,9 @@ class SharedAlbumRoot extends AlbumRoot { $this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID); return true; } + + // Do not reveal collaborators for shared albums. + public function getCollaborators() { + return []; + } } diff --git a/lib/Sabre/Album/SharedAlbumsHome.php b/lib/Sabre/Album/SharedAlbumsHome.php index 89a0dd51..799ea15d 100644 --- a/lib/Sabre/Album/SharedAlbumsHome.php +++ b/lib/Sabre/Album/SharedAlbumsHome.php @@ -41,7 +41,6 @@ class SharedAlbumsHome extends AlbumsHome { IRootFolder $rootFolder, IGroupManager $groupManager, UserConfigService $userConfigService - ) { parent::__construct( $principalInfo, @@ -66,7 +65,7 @@ class SharedAlbumsHome extends AlbumsHome { } /** - * @return AlbumRoot[] + * @return SharedAlbumRoot[] */ public function getChildren(): array { if ($this->children === null) { @@ -82,7 +81,6 @@ class SharedAlbumsHome extends AlbumsHome { $this->children = array_map(function (AlbumWithFiles $folder) { return new SharedAlbumRoot($this->albumMapper, $folder, $this->rootFolder, $this->userFolder, $this->user, $this->userConfigService); }, $albums); - ; } return $this->children; diff --git a/lib/Sabre/PhotosHome.php b/lib/Sabre/PhotosHome.php index 668983fb..ba4d266e 100644 --- a/lib/Sabre/PhotosHome.php +++ b/lib/Sabre/PhotosHome.php @@ -26,6 +26,7 @@ namespace OCA\Photos\Sabre; use OCA\Photos\Album\AlbumMapper; use OCA\Photos\Sabre\Album\AlbumsHome; use OCA\Photos\Sabre\Album\SharedAlbumsHome; +use OCA\Photos\Sabre\Album\PublicAlbumsHome; use OCA\Photos\Service\UserConfigService; use OCP\Files\IRootFolder; use OCP\IUser; @@ -93,13 +94,15 @@ class PhotosHome implements ICollection { return new AlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService); } elseif ($name === 'sharedalbums') { return new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->groupManager, $this->userConfigService); + } elseif ($name === 'public') { + return new PublicAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService); } throw new NotFound(); } /** - * @return (AlbumsHome|SharedAlbumsHome)[] + * @return (AlbumsHome|SharedAlbumsHome|PublicAlbumHome)[] */ public function getChildren(): array { return [new AlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService)]; diff --git a/lib/Service/UserConfigService.php b/lib/Service/UserConfigService.php index 5f4752de..c007806d 100644 --- a/lib/Service/UserConfigService.php +++ b/lib/Service/UserConfigService.php @@ -52,16 +52,20 @@ class UserConfigService { } public function getUserConfig(string $key) { + $user = $this->userSession->getUser(); + return $this->getConfigForUser($user->getUid(), $key); + } + + public function getConfigForUser(string $userId, string $key) { if (!in_array($key, array_keys(self::DEFAULT_CONFIGS))) { throw new Exception('Unknown user config key'); } - $user = $this->userSession->getUser(); $default = self::DEFAULT_CONFIGS[$key]; - $value = $this->config->getUserValue($user->getUid(), Application::APP_ID, $key, $default); + $value = $this->config->getUserValue($userId, Application::APP_ID, $key, $default); // If the config is a path, make sure it exists if (str_starts_with($default, '/')) { - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $userFolder = $this->rootFolder->getUserFolder($userId); // If the folder does not exists, create it if (!$userFolder->nodeExists($value)) { $userFolder->newFolder($value); diff --git a/src/PhotosPublic.vue b/src/PhotosPublic.vue new file mode 100644 index 00000000..18d008ef --- /dev/null +++ b/src/PhotosPublic.vue @@ -0,0 +1,102 @@ + + + + + + diff --git a/src/components/Albums/CollaboratorsSelectionForm.vue b/src/components/Albums/CollaboratorsSelectionForm.vue index 03ef7ddc..d55a3a83 100644 --- a/src/components/Albums/CollaboratorsSelectionForm.vue +++ b/src/components/Albums/CollaboratorsSelectionForm.vue @@ -46,16 +46,16 @@ @@ -69,7 +69,7 @@
    -