mirror of https://github.com/nextcloud/photos
Make public pages work
Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
parent
a2890b03e7
commit
142fe83638
|
@ -10,9 +10,10 @@
|
||||||
<author mail="skjnldsv@protonmail.com">John Molakvoæ</author>
|
<author mail="skjnldsv@protonmail.com">John Molakvoæ</author>
|
||||||
<namespace>Photos</namespace>
|
<namespace>Photos</namespace>
|
||||||
<category>multimedia</category>
|
<category>multimedia</category>
|
||||||
<types>
|
<types>
|
||||||
<dav/>
|
<dav />
|
||||||
</types>
|
<authentication />
|
||||||
|
</types>
|
||||||
|
|
||||||
<website>https://github.com/nextcloud/photos</website>
|
<website>https://github.com/nextcloud/photos</website>
|
||||||
<bugs>https://github.com/nextcloud/photos/issues</bugs>
|
<bugs>https://github.com/nextcloud/photos/issues</bugs>
|
||||||
|
@ -29,12 +30,13 @@
|
||||||
</navigation>
|
</navigation>
|
||||||
</navigations>
|
</navigations>
|
||||||
|
|
||||||
<sabre>
|
<sabre>
|
||||||
<collections>
|
<collections>
|
||||||
<collection>OCA\Photos\Sabre\RootCollection</collection>
|
<collection>OCA\Photos\Sabre\RootCollection</collection>
|
||||||
</collections>
|
<collection>OCA\Photos\Sabre\PublicRootCollection</collection>
|
||||||
<plugins>
|
</collections>
|
||||||
<plugin>OCA\Photos\Sabre\Album\PropFindPlugin</plugin>
|
<plugins>
|
||||||
</plugins>
|
<plugin>OCA\Photos\Sabre\Album\PropFindPlugin</plugin>
|
||||||
</sabre>
|
</plugins>
|
||||||
|
</sabre>
|
||||||
</info>
|
</info>
|
||||||
|
|
|
@ -46,10 +46,7 @@ return [
|
||||||
'path' => '',
|
'path' => '',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ 'name' => 'publicAlbum#get', 'url' => '/public/{ownerId}/{token}', 'verb' => 'GET',
|
[ 'name' => 'publicAlbum#get', 'url' => '/public/{token}', 'verb' => 'GET',
|
||||||
'requirements' => [
|
|
||||||
'ownerId' => '.*',
|
|
||||||
],
|
|
||||||
'requirements' => [
|
'requirements' => [
|
||||||
'token' => '.*',
|
'token' => '.*',
|
||||||
],
|
],
|
||||||
|
@ -132,5 +129,14 @@ return [
|
||||||
'fileId' => '.*',
|
'fileId' => '.*',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
|
[
|
||||||
|
'name' => 'publicPreview#index',
|
||||||
|
'url' => '/api/v1/publicPreview/{fileId}',
|
||||||
|
'verb' => 'GET',
|
||||||
|
'requirements' => [
|
||||||
|
'fileId' => '.*',
|
||||||
|
]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
@ -362,7 +362,7 @@ class AlbumMapper {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case self::TYPE_LINK:
|
case self::TYPE_LINK:
|
||||||
$collaborator['id'] = $this->random->generate(15, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
|
$collaborator['id'] = $this->random->generate(32, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception('Invalid collaborator type: ' . $collaborator['type']);
|
throw new \Exception('Invalid collaborator type: ' . $collaborator['type']);
|
||||||
|
@ -420,7 +420,13 @@ class AlbumMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($albumsById[$albumId])) {
|
if (!isset($albumsById[$albumId])) {
|
||||||
$albumsById[$albumId] = new AlbumInfo($albumId, $row['album_user'], $row['album_name'].' ('.$row['album_user'].')', $row['location'], (int)$row['created'], (int)$row['last_added_photo']);
|
$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) {
|
||||||
|
$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']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +458,7 @@ class AlbumMapper {
|
||||||
* @param int $fileId
|
* @param int $fileId
|
||||||
* @return AlbumInfo[]
|
* @return AlbumInfo[]
|
||||||
*/
|
*/
|
||||||
public function getAlbumForCollaboratorIdAndFileId(string $collaboratorId, int $collaboratorType, int $fileId): array {
|
public function getAlbumsForCollaboratorIdAndFileId(string $collaboratorId, int $collaboratorType, int $fileId): array {
|
||||||
$query = $this->connection->getQueryBuilder();
|
$query = $this->connection->getQueryBuilder();
|
||||||
$rows = $query
|
$rows = $query
|
||||||
->select("a.album_id", "name", "user", "location", "created", "last_added_photo")
|
->select("a.album_id", "name", "user", "location", "created", "last_added_photo")
|
||||||
|
|
|
@ -25,6 +25,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\Photos\AppInfo;
|
namespace OCA\Photos\AppInfo;
|
||||||
|
|
||||||
|
use OCA\DAV\Events\SabrePluginAuthInitEvent;
|
||||||
|
use OCA\Photos\Listener\SabrePluginAuthInitListener;
|
||||||
use OCA\DAV\Connector\Sabre\Principal;
|
use OCA\DAV\Connector\Sabre\Principal;
|
||||||
use OCA\Photos\Listener\CacheEntryRemovedListener;
|
use OCA\Photos\Listener\CacheEntryRemovedListener;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
|
@ -64,6 +66,7 @@ class Application extends App implements IBootstrap {
|
||||||
/** Register $principalBackend for the DAV collection */
|
/** Register $principalBackend for the DAV collection */
|
||||||
$context->registerServiceAlias('principalBackend', Principal::class);
|
$context->registerServiceAlias('principalBackend', Principal::class);
|
||||||
$context->registerEventListener(CacheEntryRemovedEvent::class, CacheEntryRemovedListener::class);
|
$context->registerEventListener(CacheEntryRemovedEvent::class, CacheEntryRemovedListener::class);
|
||||||
|
$context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(IBootContext $context): void {
|
public function boot(IBootContext $context): void {
|
||||||
|
|
|
@ -43,16 +43,16 @@ use OCP\IUserSession;
|
||||||
|
|
||||||
class PreviewController extends Controller {
|
class PreviewController extends Controller {
|
||||||
private IUserSession $userSession;
|
private IUserSession $userSession;
|
||||||
private Folder $userFolder;
|
private ?Folder $userFolder;
|
||||||
private IRootFolder $rootFolder;
|
private IRootFolder $rootFolder;
|
||||||
private AlbumMapper $albumMapper;
|
protected AlbumMapper $albumMapper;
|
||||||
private IPreview $preview;
|
private IPreview $preview;
|
||||||
private IGroupManager $groupManager;
|
private IGroupManager $groupManager;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
IUserSession $userSession,
|
IUserSession $userSession,
|
||||||
Folder $userFolder,
|
?Folder $userFolder,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
AlbumMapper $albumMapper,
|
AlbumMapper $albumMapper,
|
||||||
IPreview $preview,
|
IPreview $preview,
|
||||||
|
@ -67,7 +67,6 @@ class PreviewController extends Controller {
|
||||||
$this->preview = $preview;
|
$this->preview = $preview;
|
||||||
$this->groupManager = $groupManager;
|
$this->groupManager = $groupManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
|
@ -85,25 +84,35 @@ class PreviewController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->userSession->getUser();
|
$user = $this->userSession->getUser();
|
||||||
|
|
||||||
|
if ($user === null || $this->userFolder === null) {
|
||||||
|
return new DataResponse([], Http::STATUS_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
$nodes = $this->userFolder->getById($fileId);
|
$nodes = $this->userFolder->getById($fileId);
|
||||||
|
|
||||||
|
/** @var \OCA\Photos\Album\AlbumInfo[] */
|
||||||
|
$checkedAlbums = [];
|
||||||
if (\count($nodes) === 0) {
|
if (\count($nodes) === 0) {
|
||||||
$albums = $this->albumMapper->getForUserAndFile($user->getUID(), $fileId);
|
$albumsOfCurrentUser = $this->albumMapper->getForUserAndFile($user->getUID(), $fileId);
|
||||||
$receivedAlbums = $this->albumMapper->getAlbumForCollaboratorIdAndFileId($user->getUID(), AlbumMapper::TYPE_USER, $fileId);
|
$nodes = $this->getFileIdForAlbums($fileId, $albumsOfCurrentUser);
|
||||||
$albums = array_merge($albums, $receivedAlbums);
|
$checkedAlbums = $albumsOfCurrentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\count($nodes) === 0) {
|
||||||
|
$receivedAlbums = $this->albumMapper->getAlbumsForCollaboratorIdAndFileId($user->getUID(), AlbumMapper::TYPE_USER, $fileId);
|
||||||
|
$receivedAlbums = array_udiff($checkedAlbums, $receivedAlbums, fn ($a, $b) => strcmp($a->getId(), $b->getId()));
|
||||||
|
$nodes = $this->getFileIdForAlbums($fileId, $receivedAlbums);
|
||||||
|
$checkedAlbums = array_merge($checkedAlbums, $receivedAlbums);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\count($nodes) === 0) {
|
||||||
$userGroups = $this->groupManager->getUserGroupIds($user);
|
$userGroups = $this->groupManager->getUserGroupIds($user);
|
||||||
foreach ($userGroups as $groupId) {
|
foreach ($userGroups as $groupId) {
|
||||||
$albumsForGroup = $this->albumMapper->getAlbumForCollaboratorIdAndFileId($groupId, AlbumMapper::TYPE_GROUP, $fileId);
|
$albumsForGroup = $this->albumMapper->getAlbumsForCollaboratorIdAndFileId($groupId, AlbumMapper::TYPE_GROUP, $fileId);
|
||||||
$albumsForGroup = array_udiff($albumsForGroup, $albums, fn ($a, $b) => $a->getId() - $b->getId());
|
$albumsForGroup = array_udiff($checkedAlbums, $albumsForGroup, fn ($a, $b) => strcmp($a->getId(), $b->getId()));
|
||||||
$albums = array_merge($albums, $albumsForGroup);
|
$nodes = $this->getFileIdForAlbums($fileId, $albumsForGroup);
|
||||||
}
|
$checkedAlbums = array_merge($checkedAlbums, $receivedAlbums);
|
||||||
|
|
||||||
foreach ($albums as $album) {
|
|
||||||
$albumFile = $this->albumMapper->getForAlbumIdAndFileId($album->getId(), $fileId);
|
|
||||||
$nodes = $this->rootFolder
|
|
||||||
->getUserFolder($albumFile->getOwner())
|
|
||||||
->getById($fileId);
|
|
||||||
if (\count($nodes) !== 0) {
|
if (\count($nodes) !== 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -119,10 +128,25 @@ class PreviewController extends Controller {
|
||||||
return $this->fetchPreview($node, $x, $y);
|
return $this->fetchPreview($node, $x, $y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function getFileIdForAlbums($fileId, $albums) {
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
$albumFile = $this->albumMapper->getForAlbumIdAndFileId($album->getId(), $fileId);
|
||||||
|
$nodes = $this->rootFolder
|
||||||
|
->getUserFolder($albumFile->getOwner())
|
||||||
|
->getById($fileId);
|
||||||
|
if (\count($nodes) !== 0) {
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return DataResponse|FileDisplayResponse
|
* @return DataResponse|FileDisplayResponse
|
||||||
*/
|
*/
|
||||||
private function fetchPreview(
|
protected function fetchPreview(
|
||||||
Node $node,
|
Node $node,
|
||||||
int $x,
|
int $x,
|
||||||
int $y
|
int $y
|
||||||
|
|
|
@ -25,36 +25,27 @@
|
||||||
namespace OCA\Photos\Controller;
|
namespace OCA\Photos\Controller;
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCA\Files\Event\LoadSidebar;
|
|
||||||
use OCA\Photos\AppInfo\Application;
|
use OCA\Photos\AppInfo\Application;
|
||||||
use OCA\Photos\Service\UserConfigService;
|
|
||||||
use OCA\Viewer\Event\LoadViewer;
|
use OCA\Viewer\Event\LoadViewer;
|
||||||
use OCP\App\IAppManager;
|
|
||||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\AppFramework\Services\IInitialState;
|
use OCP\AppFramework\Services\IInitialState;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\Util;
|
use OCP\Util;
|
||||||
|
|
||||||
class PublicAlbumController extends Controller {
|
class PublicAlbumController extends Controller {
|
||||||
private IAppManager $appManager;
|
|
||||||
private IEventDispatcher $eventDispatcher;
|
private IEventDispatcher $eventDispatcher;
|
||||||
private UserConfigService $userConfig;
|
|
||||||
private IInitialState $initialState;
|
private IInitialState $initialState;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
IAppManager $appManager,
|
|
||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
UserConfigService $userConfig,
|
IInitialState $initialState
|
||||||
IInitialState $initialState,
|
|
||||||
) {
|
) {
|
||||||
parent::__construct(Application::APP_ID, $request);
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
|
||||||
$this->appManager = $appManager;
|
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->userConfig = $userConfig;
|
|
||||||
$this->initialState = $initialState;
|
$this->initialState = $initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +53,7 @@ class PublicAlbumController extends Controller {
|
||||||
* @PublicPage
|
* @PublicPage
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*/
|
*/
|
||||||
public function get(): TemplateResponse {
|
public function get(): PublicTemplateResponse {
|
||||||
$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
|
$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
|
||||||
|
|
||||||
$this->initialState->provideInitialState('image-mimes', Application::IMAGE_MIMES);
|
$this->initialState->provideInitialState('image-mimes', Application::IMAGE_MIMES);
|
||||||
|
@ -74,7 +65,7 @@ class PublicAlbumController extends Controller {
|
||||||
Util::addScript(Application::APP_ID, 'photos-public');
|
Util::addScript(Application::APP_ID, 'photos-public');
|
||||||
Util::addStyle(Application::APP_ID, 'icons');
|
Util::addStyle(Application::APP_ID, 'icons');
|
||||||
|
|
||||||
$response = new TemplateResponse(Application::APP_ID, 'main');
|
$response = new PublicTemplateResponse(Application::APP_ID, 'public');
|
||||||
|
|
||||||
$policy = new ContentSecurityPolicy();
|
$policy = new ContentSecurityPolicy();
|
||||||
$policy->addAllowedWorkerSrcDomain("'self'");
|
$policy->addAllowedWorkerSrcDomain("'self'");
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2022, Louis Chmn <louis@chmn.me>
|
||||||
|
*
|
||||||
|
* @author Louis Chmn <louis@chmn.me>
|
||||||
|
*
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Photos\Controller;
|
||||||
|
|
||||||
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||||
|
|
||||||
|
class PublicPreviewController extends PreviewController {
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
* @PublicPage
|
||||||
|
* Render default index template
|
||||||
|
*
|
||||||
|
* @return DataResponse|FileDisplayResponse
|
||||||
|
*/
|
||||||
|
public function index(
|
||||||
|
int $fileId = -1,
|
||||||
|
int $x = 32,
|
||||||
|
int $y = 32,
|
||||||
|
string $token = null
|
||||||
|
) {
|
||||||
|
if ($fileId === -1 || $x === 0 || $y === 0) {
|
||||||
|
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($token === null) {
|
||||||
|
return new DataResponse([], Http::STATUS_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicAlbums = $this->albumMapper->getAlbumsForCollaboratorIdAndFileId($token, AlbumMapper::TYPE_LINK, $fileId);
|
||||||
|
$nodes = $this->getFileIdForAlbums($fileId, $publicAlbums);
|
||||||
|
|
||||||
|
if (\count($nodes) === 0) {
|
||||||
|
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node = array_pop($nodes);
|
||||||
|
|
||||||
|
return $this->fetchPreview($node, $x, $y);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2022, Louis Chmn <louis@chmn.me>
|
||||||
|
*
|
||||||
|
* @author Louis Chmn <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\Listener;
|
||||||
|
|
||||||
|
use OCA\DAV\Events\SabrePluginAuthInitEvent;
|
||||||
|
use OCA\Photos\Sabre\PublicAlbumAuthBackend;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
|
||||||
|
class SabrePluginAuthInitListener implements IEventListener {
|
||||||
|
private PublicAlbumAuthBackend $publicAlbumAuthBackend;
|
||||||
|
|
||||||
|
public function __construct(PublicAlbumAuthBackend $publicAlbumAuthBackend) {
|
||||||
|
$this->publicAlbumAuthBackend = $publicAlbumAuthBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!($event instanceof SabrePluginAuthInitEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$server = $event->getServer();
|
||||||
|
$authPlugin = $server->getPlugin('auth');
|
||||||
|
$authPlugin->addBackend($this->publicAlbumAuthBackend);
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,6 +137,9 @@ class AlbumPhoto implements IFile {
|
||||||
public function isFavorite(): bool {
|
public function isFavorite(): bool {
|
||||||
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
|
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
|
||||||
$tagger = $tagManager->load('files');
|
$tagger = $tagManager->load('files');
|
||||||
|
if ($tagger === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$tags = $tagger->getTagsForObjects([$this->getFileId()]);
|
$tags = $tagger->getTagsForObjects([$this->getFileId()]);
|
||||||
|
|
||||||
if ($tags === false || empty($tags)) {
|
if ($tags === false || empty($tags)) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ use OCA\Photos\Service\UserConfigService;
|
||||||
use OCP\Files\Folder;
|
use OCP\Files\Folder;
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\Files\NotFoundException;
|
use OCP\Files\NotFoundException;
|
||||||
use OCP\IUser;
|
|
||||||
use Sabre\DAV\Exception\Conflict;
|
use Sabre\DAV\Exception\Conflict;
|
||||||
use Sabre\DAV\Exception\Forbidden;
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
use Sabre\DAV\Exception\NotFound;
|
use Sabre\DAV\Exception\NotFound;
|
||||||
|
@ -43,22 +42,19 @@ class AlbumRoot implements ICollection, ICopyTarget {
|
||||||
protected AlbumMapper $albumMapper;
|
protected AlbumMapper $albumMapper;
|
||||||
protected AlbumWithFiles $album;
|
protected AlbumWithFiles $album;
|
||||||
protected IRootFolder $rootFolder;
|
protected IRootFolder $rootFolder;
|
||||||
protected Folder $userFolder;
|
protected string $userId;
|
||||||
protected IUser $user;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AlbumMapper $albumMapper,
|
AlbumMapper $albumMapper,
|
||||||
AlbumWithFiles $album,
|
AlbumWithFiles $album,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
Folder $userFolder,
|
string $userId,
|
||||||
IUser $user,
|
|
||||||
UserConfigService $userConfigService
|
UserConfigService $userConfigService
|
||||||
) {
|
) {
|
||||||
$this->albumMapper = $albumMapper;
|
$this->albumMapper = $albumMapper;
|
||||||
$this->album = $album;
|
$this->album = $album;
|
||||||
$this->rootFolder = $rootFolder;
|
$this->rootFolder = $rootFolder;
|
||||||
$this->userFolder = $userFolder;
|
$this->userId = $userId;
|
||||||
$this->user = $user;
|
|
||||||
$this->userConfigService = $userConfigService;
|
$this->userConfigService = $userConfigService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +78,7 @@ class AlbumRoot implements ICollection, ICopyTarget {
|
||||||
|
|
||||||
protected function getPhotosLocationInfo() {
|
protected function getPhotosLocationInfo() {
|
||||||
$photosLocation = $this->userConfigService->getUserConfig('photosLocation');
|
$photosLocation = $this->userConfigService->getUserConfig('photosLocation');
|
||||||
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
|
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||||
return [$photosLocation, $userFolder];
|
return [$photosLocation, $userFolder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +94,12 @@ class AlbumRoot implements ICollection, ICopyTarget {
|
||||||
try {
|
try {
|
||||||
[$photosLocation, $userFolder] = $this->getPhotosLocationInfo();
|
[$photosLocation, $userFolder] = $this->getPhotosLocationInfo();
|
||||||
|
|
||||||
// If the folder does not exists, create it.
|
// If the folder does not exists, create it.
|
||||||
if (!$userFolder->nodeExists($photosLocation)) {
|
if (!$userFolder->nodeExists($photosLocation)) {
|
||||||
return $userFolder->newFolder($photosLocation);
|
return $userFolder->newFolder($photosLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
$photosFolder = $this->userFolder->get($photosLocation);
|
$photosFolder = $userFolder->get($photosLocation);
|
||||||
|
|
||||||
if (!($photosFolder instanceof Folder)) {
|
if (!($photosFolder instanceof Folder)) {
|
||||||
throw new Conflict('The destination exists and is not a folder');
|
throw new Conflict('The destination exists and is not a folder');
|
||||||
|
@ -159,23 +155,22 @@ class AlbumRoot implements ICollection, ICopyTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function copyInto($targetName, $sourcePath, INode $sourceNode): bool {
|
public function copyInto($targetName, $sourcePath, INode $sourceNode): bool {
|
||||||
$uid = $this->user->getUID();
|
|
||||||
if ($sourceNode instanceof File) {
|
if ($sourceNode instanceof File) {
|
||||||
$sourceId = $sourceNode->getId();
|
$sourceId = $sourceNode->getId();
|
||||||
$ownerUID = $sourceNode->getFileInfo()->getOwner()->getUID();
|
$ownerUID = $sourceNode->getFileInfo()->getOwner()->getUID();
|
||||||
return $this->addFile($sourceId, $ownerUID);
|
return $this->addFile($sourceId, $ownerUID);
|
||||||
}
|
}
|
||||||
|
$uid = $this->userId;
|
||||||
throw new \Exception("Can't add file to album, only files from $uid can be added");
|
throw new \Exception("Can't add file to album, only files from $uid can be added");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addFile(int $sourceId, string $ownerUID): bool {
|
protected function addFile(int $sourceId, string $ownerUID): bool {
|
||||||
$uid = $this->user->getUID();
|
|
||||||
if (in_array($sourceId, $this->album->getFileIds())) {
|
if (in_array($sourceId, $this->album->getFileIds())) {
|
||||||
throw new Conflict("File $sourceId is already in the folder");
|
throw new Conflict("File $sourceId is already in the folder");
|
||||||
}
|
}
|
||||||
if ($ownerUID === $uid) {
|
if ($ownerUID === $this->userId) {
|
||||||
$this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID);
|
$this->albumMapper->addFile($this->album->getAlbum()->getId(), $sourceId, $ownerUID);
|
||||||
$node = current($this->userFolder->getById($sourceId));
|
$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));
|
$this->album->addFile(new AlbumFile($sourceId, $node->getName(), $node->getMimetype(), $node->getSize(), $node->getMTime(), $node->getEtag(), $node->getCreationTime(), $ownerUID));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,7 @@ use OCA\Photos\Album\AlbumInfo;
|
||||||
use OCA\Photos\Album\AlbumMapper;
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
use OCA\Photos\Album\AlbumWithFiles;
|
use OCA\Photos\Album\AlbumWithFiles;
|
||||||
use OCA\Photos\Service\UserConfigService;
|
use OCA\Photos\Service\UserConfigService;
|
||||||
use OCP\Files\Folder;
|
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\IUser;
|
|
||||||
use Sabre\DAV\Exception\Forbidden;
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
use Sabre\DAV\Exception\NotFound;
|
use Sabre\DAV\Exception\NotFound;
|
||||||
use Sabre\DAV\ICollection;
|
use Sabre\DAV\ICollection;
|
||||||
|
@ -37,9 +35,8 @@ use Sabre\DAV\ICollection;
|
||||||
class AlbumsHome implements ICollection {
|
class AlbumsHome implements ICollection {
|
||||||
protected AlbumMapper $albumMapper;
|
protected AlbumMapper $albumMapper;
|
||||||
protected array $principalInfo;
|
protected array $principalInfo;
|
||||||
protected IUser $user;
|
protected string $userId;
|
||||||
protected IRootFolder $rootFolder;
|
protected IRootFolder $rootFolder;
|
||||||
protected Folder $userFolder;
|
|
||||||
protected UserConfigService $userConfigService;
|
protected UserConfigService $userConfigService;
|
||||||
|
|
||||||
public const NAME = 'albums';
|
public const NAME = 'albums';
|
||||||
|
@ -52,15 +49,14 @@ class AlbumsHome implements ICollection {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $principalInfo,
|
array $principalInfo,
|
||||||
AlbumMapper $albumMapper,
|
AlbumMapper $albumMapper,
|
||||||
IUser $user,
|
string $userId,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
UserConfigService $userConfigService
|
UserConfigService $userConfigService
|
||||||
) {
|
) {
|
||||||
$this->principalInfo = $principalInfo;
|
$this->principalInfo = $principalInfo;
|
||||||
$this->albumMapper = $albumMapper;
|
$this->albumMapper = $albumMapper;
|
||||||
$this->user = $user;
|
$this->userId = $userId;
|
||||||
$this->rootFolder = $rootFolder;
|
$this->rootFolder = $rootFolder;
|
||||||
$this->userFolder = $rootFolder->getUserFolder($user->getUID());
|
|
||||||
$this->userConfigService = $userConfigService;
|
$this->userConfigService = $userConfigService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +86,7 @@ class AlbumsHome implements ICollection {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function createDirectory($name) {
|
public function createDirectory($name) {
|
||||||
$uid = $this->user->getUID();
|
$this->albumMapper->create($this->userId, $name);
|
||||||
$this->albumMapper->create($uid, $name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getChild($name) {
|
public function getChild($name) {
|
||||||
|
@ -109,9 +104,9 @@ class AlbumsHome implements ICollection {
|
||||||
*/
|
*/
|
||||||
public function getChildren(): array {
|
public function getChildren(): array {
|
||||||
if ($this->children === null) {
|
if ($this->children === null) {
|
||||||
$albumInfos = $this->albumMapper->getForUser($this->user->getUID());
|
$albumInfos = $this->albumMapper->getForUser($this->userId);
|
||||||
$this->children = array_map(function (AlbumInfo $albumInfo) {
|
$this->children = array_map(function (AlbumInfo $albumInfo) {
|
||||||
return new AlbumRoot($this->albumMapper, new AlbumWithFiles($albumInfo, $this->albumMapper), $this->rootFolder, $this->userFolder, $this->user, $this->userConfigService);
|
return new AlbumRoot($this->albumMapper, new AlbumWithFiles($albumInfo, $this->albumMapper), $this->rootFolder, $this->userId, $this->userConfigService);
|
||||||
}, $albumInfos);
|
}, $albumInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,13 @@ use OCA\DAV\Connector\Sabre\FilesPlugin;
|
||||||
use OCA\Photos\Album\AlbumMapper;
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IPreview;
|
use OCP\IPreview;
|
||||||
|
use OCP\Files\NotFoundException;
|
||||||
use Sabre\DAV\INode;
|
use Sabre\DAV\INode;
|
||||||
use Sabre\DAV\PropFind;
|
use Sabre\DAV\PropFind;
|
||||||
use Sabre\DAV\PropPatch;
|
use Sabre\DAV\PropPatch;
|
||||||
use Sabre\DAV\Server;
|
use Sabre\DAV\Server;
|
||||||
use Sabre\DAV\ServerPlugin;
|
use Sabre\DAV\ServerPlugin;
|
||||||
use Sabre\DAV\Tree;
|
use Sabre\DAV\Tree;
|
||||||
use OCP\Files\NotFoundException;
|
|
||||||
|
|
||||||
class PropFindPlugin extends ServerPlugin {
|
class PropFindPlugin extends ServerPlugin {
|
||||||
public const ORIGINAL_NAME_PROPERTYNAME = '{http://nextcloud.org/ns}original-name';
|
public const ORIGINAL_NAME_PROPERTYNAME = '{http://nextcloud.org/ns}original-name';
|
||||||
|
@ -68,6 +68,19 @@ class PropFindPlugin extends ServerPlugin {
|
||||||
$this->metadataEnabled = $this->config->getSystemValueBool('enable_file_metadata', true);
|
$this->metadataEnabled = $this->config->getSystemValueBool('enable_file_metadata', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* @return void
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,8 +24,6 @@ declare(strict_types=1);
|
||||||
namespace OCA\Photos\Sabre\Album;
|
namespace OCA\Photos\Sabre\Album;
|
||||||
|
|
||||||
use Sabre\DAV\Exception\Forbidden;
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
use Sabre\DAV\Exception\Conflict;
|
|
||||||
use OCP\Files\Folder;
|
|
||||||
use Sabre\DAV\INode;
|
use Sabre\DAV\INode;
|
||||||
|
|
||||||
class PublicAlbumRoot extends AlbumRoot {
|
class PublicAlbumRoot extends AlbumRoot {
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
<?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 Sabre\DAV\Exception\Forbidden;
|
|
||||||
use Sabre\DAV\Exception\NotFound;
|
|
||||||
use OCA\Photos\Album\AlbumMapper;
|
|
||||||
|
|
||||||
class PublicAlbumsHome extends AlbumsHome {
|
|
||||||
public const NAME = '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);
|
|
||||||
|
|
||||||
$albums = 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -32,7 +32,7 @@ class SharedAlbumRoot extends AlbumRoot {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function delete() {
|
public function delete() {
|
||||||
$this->albumMapper->deleteUserFromAlbumCollaboratorsList($this->user->getUID(), $this->album->getAlbum()->getId());
|
$this->albumMapper->deleteUserFromAlbumCollaboratorsList($this->userId, $this->album->getAlbum()->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,8 +51,7 @@ class SharedAlbumRoot extends AlbumRoot {
|
||||||
fn ($collaborator) => $collaborator['type'].':'.$collaborator['id'],
|
fn ($collaborator) => $collaborator['type'].':'.$collaborator['id'],
|
||||||
$this->albumMapper->getCollaborators($this->album->getAlbum()->getId()),
|
$this->albumMapper->getCollaborators($this->album->getAlbum()->getId()),
|
||||||
);
|
);
|
||||||
$uid = $this->user->getUID();
|
if (!in_array(AlbumMapper::TYPE_USER.':'.$this->userId, $collaboratorIds)) {
|
||||||
if (!in_array(AlbumMapper::TYPE_USER.':'.$uid, $collaboratorIds)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,13 @@ namespace OCA\Photos\Sabre\Album;
|
||||||
use OCA\Photos\Album\AlbumWithFiles;
|
use OCA\Photos\Album\AlbumWithFiles;
|
||||||
use OCA\Photos\Service\UserConfigService;
|
use OCA\Photos\Service\UserConfigService;
|
||||||
use Sabre\DAV\Exception\Forbidden;
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
|
use OCP\IUserManager;
|
||||||
use OCP\IGroupManager;
|
use OCP\IGroupManager;
|
||||||
use OCA\Photos\Album\AlbumMapper;
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\IUser;
|
|
||||||
|
|
||||||
class SharedAlbumsHome extends AlbumsHome {
|
class SharedAlbumsHome extends AlbumsHome {
|
||||||
|
private IUserManager $userManager;
|
||||||
private IGroupManager $groupManager;
|
private IGroupManager $groupManager;
|
||||||
|
|
||||||
public const NAME = 'sharedalbums';
|
public const NAME = 'sharedalbums';
|
||||||
|
@ -39,19 +40,21 @@ class SharedAlbumsHome extends AlbumsHome {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $principalInfo,
|
array $principalInfo,
|
||||||
AlbumMapper $albumMapper,
|
AlbumMapper $albumMapper,
|
||||||
IUser $user,
|
string $userId,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
|
IUserManager $userManager,
|
||||||
IGroupManager $groupManager,
|
IGroupManager $groupManager,
|
||||||
UserConfigService $userConfigService
|
UserConfigService $userConfigService
|
||||||
) {
|
) {
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
$principalInfo,
|
$principalInfo,
|
||||||
$albumMapper,
|
$albumMapper,
|
||||||
$user,
|
$userId,
|
||||||
$rootFolder,
|
$rootFolder,
|
||||||
$userConfigService
|
$userConfigService
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->userManager = $userManager;
|
||||||
$this->groupManager = $groupManager;
|
$this->groupManager = $groupManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,17 +70,18 @@ class SharedAlbumsHome extends AlbumsHome {
|
||||||
*/
|
*/
|
||||||
public function getChildren(): array {
|
public function getChildren(): array {
|
||||||
if ($this->children === null) {
|
if ($this->children === null) {
|
||||||
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($this->user->getUID(), AlbumMapper::TYPE_USER);
|
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($this->userId, AlbumMapper::TYPE_USER);
|
||||||
|
|
||||||
$userGroups = $this->groupManager->getUserGroupIds($this->user);
|
$user = $this->userManager->get($this->userId);
|
||||||
|
$userGroups = $this->groupManager->getUserGroupIds($user);
|
||||||
foreach ($userGroups as $groupId) {
|
foreach ($userGroups as $groupId) {
|
||||||
$albumsForGroup = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($groupId, AlbumMapper::TYPE_GROUP);
|
$albumsForGroup = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($groupId, AlbumMapper::TYPE_GROUP);
|
||||||
$albumsForGroup = array_udiff($albumsForGroup, $albums, fn ($a, $b) => $a->getAlbum()->getId() - $b->getAlbum()->getId());
|
$albumsForGroup = array_udiff($albumsForGroup, $albums, fn ($a, $b) => $a->getAlbum()->getId() - $b->getAlbum()->getId());
|
||||||
$albums = array_merge($albums, $albumsForGroup);
|
$albums = array_merge($albums, $albumsForGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->children = array_map(function (AlbumWithFiles $folder) {
|
$this->children = array_map(function (AlbumWithFiles $album) {
|
||||||
return new SharedAlbumRoot($this->albumMapper, $folder, $this->rootFolder, $this->userFolder, $this->user, $this->userConfigService);
|
return new SharedAlbumRoot($this->albumMapper, $album, $this->rootFolder, $this->userId, $this->userConfigService);
|
||||||
}, $albums);
|
}, $albums);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,9 @@ namespace OCA\Photos\Sabre;
|
||||||
use OCA\Photos\Album\AlbumMapper;
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
use OCA\Photos\Sabre\Album\AlbumsHome;
|
use OCA\Photos\Sabre\Album\AlbumsHome;
|
||||||
use OCA\Photos\Sabre\Album\SharedAlbumsHome;
|
use OCA\Photos\Sabre\Album\SharedAlbumsHome;
|
||||||
use OCA\Photos\Sabre\Album\PublicAlbumsHome;
|
|
||||||
use OCA\Photos\Service\UserConfigService;
|
use OCA\Photos\Service\UserConfigService;
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\IUser;
|
use OCP\IUserManager;
|
||||||
use OCP\IGroupManager;
|
use OCP\IGroupManager;
|
||||||
use Sabre\DAV\Exception\Forbidden;
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
use Sabre\DAV\Exception\NotFound;
|
use Sabre\DAV\Exception\NotFound;
|
||||||
|
@ -38,23 +37,26 @@ use Sabre\DAV\ICollection;
|
||||||
class PhotosHome implements ICollection {
|
class PhotosHome implements ICollection {
|
||||||
private AlbumMapper $albumMapper;
|
private AlbumMapper $albumMapper;
|
||||||
private array $principalInfo;
|
private array $principalInfo;
|
||||||
private IUser $user;
|
private string $userId;
|
||||||
private IRootFolder $rootFolder;
|
private IRootFolder $rootFolder;
|
||||||
|
private IUserManager $userManager;
|
||||||
private IGroupManager $groupManager;
|
private IGroupManager $groupManager;
|
||||||
private UserConfigService $userConfigService;
|
private UserConfigService $userConfigService;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $principalInfo,
|
array $principalInfo,
|
||||||
AlbumMapper $albumMapper,
|
AlbumMapper $albumMapper,
|
||||||
IUser $user,
|
string $userId,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
|
IUserManager $userManager,
|
||||||
IGroupManager $groupManager,
|
IGroupManager $groupManager,
|
||||||
UserConfigService $userConfigService
|
UserConfigService $userConfigService
|
||||||
) {
|
) {
|
||||||
$this->principalInfo = $principalInfo;
|
$this->principalInfo = $principalInfo;
|
||||||
$this->albumMapper = $albumMapper;
|
$this->albumMapper = $albumMapper;
|
||||||
$this->user = $user;
|
$this->userId = $userId;
|
||||||
$this->rootFolder = $rootFolder;
|
$this->rootFolder = $rootFolder;
|
||||||
|
$this->userManager = $userManager;
|
||||||
$this->groupManager = $groupManager;
|
$this->groupManager = $groupManager;
|
||||||
$this->userConfigService = $userConfigService;
|
$this->userConfigService = $userConfigService;
|
||||||
}
|
}
|
||||||
|
@ -92,11 +94,9 @@ class PhotosHome implements ICollection {
|
||||||
public function getChild($name) {
|
public function getChild($name) {
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case AlbumsHome::NAME:
|
case AlbumsHome::NAME:
|
||||||
return new AlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService);
|
return new AlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userConfigService);
|
||||||
case SharedAlbumsHome::NAME:
|
case SharedAlbumsHome::NAME:
|
||||||
return new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->groupManager, $this->userConfigService);
|
return new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService);
|
||||||
case PublicAlbumsHome::NAME:
|
|
||||||
return new PublicAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotFound();
|
throw new NotFound();
|
||||||
|
@ -107,14 +107,13 @@ class PhotosHome implements ICollection {
|
||||||
*/
|
*/
|
||||||
public function getChildren(): array {
|
public function getChildren(): array {
|
||||||
return [
|
return [
|
||||||
new AlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService),
|
new AlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userConfigService),
|
||||||
new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->groupManager, $this->userConfigService),
|
new SharedAlbumsHome($this->principalInfo, $this->albumMapper, $this->userId, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService),
|
||||||
new PublicAlbumsHome($this->principalInfo, $this->albumMapper, $this->user, $this->rootFolder, $this->userConfigService),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function childExists($name): bool {
|
public function childExists($name): bool {
|
||||||
return $name === AlbumsHome::NAME || $name === SharedAlbumsHome::NAME || $name === PublicAlbumsHome::NAME;
|
return $name === AlbumsHome::NAME || $name === SharedAlbumsHome::NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastModified(): int {
|
public function getLastModified(): int {
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?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 OC\Security\Bruteforce\Throttler;
|
||||||
|
use OCA\Photos\Album\AlbumMapper;
|
||||||
|
use OCA\Photos\Album\AlbumWithFiles;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use Sabre\DAV\Auth\Backend\AbstractBasic;
|
||||||
|
|
||||||
|
class PublicAlbumAuthBackend extends AbstractBasic {
|
||||||
|
private const BRUTEFORCE_ACTION = 'public_webdav_auth';
|
||||||
|
private ?AlbumWithFiles $album = null;
|
||||||
|
private IRequest $request;
|
||||||
|
private AlbumMapper $albumMapper;
|
||||||
|
private Throttler $throttler;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IRequest $request,
|
||||||
|
AlbumMapper $albumMapper,
|
||||||
|
Throttler $throttler
|
||||||
|
) {
|
||||||
|
$this->request = $request;
|
||||||
|
$this->albumMapper = $albumMapper;
|
||||||
|
$this->throttler = $throttler;
|
||||||
|
|
||||||
|
// setup realm
|
||||||
|
$defaults = new \OCP\Defaults();
|
||||||
|
$this->realm = $defaults->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the token.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @return bool
|
||||||
|
* @throws \Sabre\DAV\Exception\NotAuthenticated
|
||||||
|
*/
|
||||||
|
protected function validateUserPass($username, $password) {
|
||||||
|
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
|
||||||
|
|
||||||
|
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($username, AlbumMapper::TYPE_LINK);
|
||||||
|
|
||||||
|
|
||||||
|
if (count($albums) !== 1) {
|
||||||
|
$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->album = $albums[0];
|
||||||
|
|
||||||
|
\OC_User::setIncognitoMode(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getShare(): AlbumWithFiles {
|
||||||
|
assert($this->album !== null);
|
||||||
|
return $this->album;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?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 Sabre\DAVACL\AbstractPrincipalCollection;
|
||||||
|
use Sabre\DAVACL\PrincipalBackend;
|
||||||
|
use Sabre\DAV\Exception\NotFound;
|
||||||
|
|
||||||
|
class PublicRootCollection extends AbstractPrincipalCollection {
|
||||||
|
private AlbumMapper $albumMapper;
|
||||||
|
private IRootFolder $rootFolder;
|
||||||
|
private UserConfigService $userConfigService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
AlbumMapper $albumMapper,
|
||||||
|
IRootFolder $rootFolder,
|
||||||
|
PrincipalBackend\BackendInterface $principalBackend,
|
||||||
|
UserConfigService $userConfigService
|
||||||
|
) {
|
||||||
|
parent::__construct($principalBackend, 'principals/token');
|
||||||
|
|
||||||
|
$this->albumMapper = $albumMapper;
|
||||||
|
$this->rootFolder = $rootFolder;
|
||||||
|
$this->userConfigService = $userConfigService;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (is_null($token)) {
|
||||||
|
throw new \Sabre\DAV\Exception\Forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($token, AlbumMapper::TYPE_LINK);
|
||||||
|
|
||||||
|
if (count($albums) !== 1) {
|
||||||
|
throw new NotFound("Unable to find public album");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PublicAlbumRoot($this->albumMapper, $albums[0], $this->rootFolder, $albums[0]->getAlbum()->getUserId(), $this->userConfigService);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,12 +29,14 @@ use OCP\Files\IRootFolder;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use Sabre\DAVACL\AbstractPrincipalCollection;
|
use Sabre\DAVACL\AbstractPrincipalCollection;
|
||||||
use Sabre\DAVACL\PrincipalBackend;
|
use Sabre\DAVACL\PrincipalBackend;
|
||||||
|
use OCP\IUserManager;
|
||||||
use OCP\IGroupManager;
|
use OCP\IGroupManager;
|
||||||
|
|
||||||
class RootCollection extends AbstractPrincipalCollection {
|
class RootCollection extends AbstractPrincipalCollection {
|
||||||
private AlbumMapper $folderMapper;
|
private AlbumMapper $folderMapper;
|
||||||
private IUserSession $userSession;
|
private IUserSession $userSession;
|
||||||
private IRootFolder $rootFolder;
|
private IRootFolder $rootFolder;
|
||||||
|
private IUserManager $userManager;
|
||||||
private IGroupManager $groupManager;
|
private IGroupManager $groupManager;
|
||||||
private UserConfigService $userConfigService;
|
private UserConfigService $userConfigService;
|
||||||
|
|
||||||
|
@ -43,6 +45,7 @@ class RootCollection extends AbstractPrincipalCollection {
|
||||||
IUserSession $userSession,
|
IUserSession $userSession,
|
||||||
IRootFolder $rootFolder,
|
IRootFolder $rootFolder,
|
||||||
PrincipalBackend\BackendInterface $principalBackend,
|
PrincipalBackend\BackendInterface $principalBackend,
|
||||||
|
IUserManager $userManager,
|
||||||
IGroupManager $groupManager,
|
IGroupManager $groupManager,
|
||||||
UserConfigService $userConfigService
|
UserConfigService $userConfigService
|
||||||
) {
|
) {
|
||||||
|
@ -51,6 +54,7 @@ class RootCollection extends AbstractPrincipalCollection {
|
||||||
$this->folderMapper = $folderMapper;
|
$this->folderMapper = $folderMapper;
|
||||||
$this->userSession = $userSession;
|
$this->userSession = $userSession;
|
||||||
$this->rootFolder = $rootFolder;
|
$this->rootFolder = $rootFolder;
|
||||||
|
$this->userManager = $userManager;
|
||||||
$this->groupManager = $groupManager;
|
$this->groupManager = $groupManager;
|
||||||
$this->userConfigService = $userConfigService;
|
$this->userConfigService = $userConfigService;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +74,7 @@ class RootCollection extends AbstractPrincipalCollection {
|
||||||
if (is_null($user) || $name !== $user->getUID()) {
|
if (is_null($user) || $name !== $user->getUID()) {
|
||||||
throw new \Sabre\DAV\Exception\Forbidden();
|
throw new \Sabre\DAV\Exception\Forbidden();
|
||||||
}
|
}
|
||||||
return new PhotosHome($principalInfo, $this->folderMapper, $user, $this->rootFolder, $this->groupManager, $this->userConfigService);
|
return new PhotosHome($principalInfo, $this->folderMapper, $name, $this->rootFolder, $this->userManager, $this->groupManager, $this->userConfigService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string {
|
public function getName(): string {
|
||||||
|
|
13
psalm.xml
13
psalm.xml
|
@ -1,13 +1,7 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<psalm
|
<psalm errorLevel="4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorBaseline="tests/psalm-baseline.xml">
|
||||||
errorLevel="4"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="https://getpsalm.org/schema/config"
|
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
|
||||||
errorBaseline="tests/psalm-baseline.xml"
|
|
||||||
>
|
|
||||||
<stubs>
|
<stubs>
|
||||||
<file name="tests/stub.phpstub" preloadClasses="true"/>
|
<file name="tests/stub.phpstub" preloadClasses="true" />
|
||||||
</stubs>
|
</stubs>
|
||||||
<projectFiles>
|
<projectFiles>
|
||||||
<directory name="lib" />
|
<directory name="lib" />
|
||||||
|
@ -28,6 +22,9 @@
|
||||||
<referencedClass name="OCA\DAV\Connector\Sabre\Principal" />
|
<referencedClass name="OCA\DAV\Connector\Sabre\Principal" />
|
||||||
<referencedClass name="OCA\DAV\Connector\Sabre\File" />
|
<referencedClass name="OCA\DAV\Connector\Sabre\File" />
|
||||||
<referencedClass name="OCA\DAV\Connector\Sabre\FilesPlugin" />
|
<referencedClass name="OCA\DAV\Connector\Sabre\FilesPlugin" />
|
||||||
|
<referencedClass name="OCA\DAV\Events\SabrePluginAuthInitEvent" />
|
||||||
|
<referencedClass name="OC\Security\Bruteforce\Throttler" />
|
||||||
|
<referencedClass name="OC_User" />
|
||||||
</errorLevel>
|
</errorLevel>
|
||||||
</UndefinedClass>
|
</UndefinedClass>
|
||||||
<UndefinedDocblockClass>
|
<UndefinedDocblockClass>
|
||||||
|
|
|
@ -358,7 +358,7 @@ export default {
|
||||||
this.errorFetchingAlbum = error
|
this.errorFetchingAlbum = error
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error('[PublicAlbumContent] Error fetching album', {error})
|
logger.error('[PublicAlbumContent] Error fetching album', { error })
|
||||||
showError(this.t('photos', 'Failed to fetch album.'))
|
showError(this.t('photos', 'Failed to fetch album.'))
|
||||||
} finally {
|
} finally {
|
||||||
this.loadingAlbum = false
|
this.loadingAlbum = false
|
||||||
|
@ -385,7 +385,7 @@ export default {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[PublicAlbumContent] Error updating album', {error})
|
logger.error('[PublicAlbumContent] Error updating album', { error })
|
||||||
showError(this.t('photos', 'Failed to update album.'))
|
showError(this.t('photos', 'Failed to update album.'))
|
||||||
} finally {
|
} finally {
|
||||||
this.loadingAlbum = false
|
this.loadingAlbum = false
|
||||||
|
@ -393,7 +393,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async copyPublicLink() {
|
async copyPublicLink() {
|
||||||
await navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}${generateUrl(`apps/photos/public/${getCurrentUser().uid}/${this.publicLink.id}`)}`)
|
await navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}${generateUrl(`apps/photos/public/${this.publicLink.id}`)}`)
|
||||||
this.publicLinkCopied = true
|
this.publicLinkCopied = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.publicLinkCopied = false
|
this.publicLinkCopied = false
|
||||||
|
|
|
@ -201,7 +201,12 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemURL(size) {
|
getItemURL(size) {
|
||||||
return generateUrl(`/apps/photos/api/v1/preview/${this.file.fileid}?x=${size}&y=${size}`)
|
const token = this.$route.params.token
|
||||||
|
if (token) {
|
||||||
|
return generateUrl(`/apps/photos/api/v1/publicPreview/${this.file.fileid}?x=${size}&y=${size}&token=${token}`)
|
||||||
|
} else {
|
||||||
|
return generateUrl(`/apps/photos/api/v1/preview/${this.file.fileid}?x=${size}&y=${size}`)
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
croppedLayout: croppedLayoutLocalStorage !== null
|
croppedLayout: croppedLayoutLocalStorage !== null
|
||||||
? croppedLayoutLocalStorage === 'true'
|
? croppedLayoutLocalStorage === 'true'
|
||||||
: loadState('photos', 'croppedLayout') === 'true',
|
: loadState('photos', 'croppedLayout', 'false') === 'true',
|
||||||
photosLocation: loadState('photos', 'photosLocation'),
|
photosLocation: loadState('photos', 'photosLocation', ''),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -120,11 +120,10 @@ const router = new Router({
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/public/:userId/:token',
|
path: '/public/:token',
|
||||||
component: PublicAlbumContent,
|
component: PublicAlbumContent,
|
||||||
name: 'publicAlbums',
|
name: 'publicAlbums',
|
||||||
props: route => ({
|
props: route => ({
|
||||||
userId: route.params.userId,
|
|
||||||
token: route.params.token,
|
token: route.params.token,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
import moment from '@nextcloud/moment'
|
import moment from '@nextcloud/moment'
|
||||||
import { translate } from '@nextcloud/l10n'
|
import { translate } from '@nextcloud/l10n'
|
||||||
|
|
||||||
import client from '../services/DavClient.js'
|
import defaultClient from '../services/DavClient.js'
|
||||||
import logger from '../services/logger.js'
|
import logger from '../services/logger.js'
|
||||||
import DavRequest from '../services/DavRequest.js'
|
import DavRequest from '../services/DavRequest.js'
|
||||||
import { genFileInfo } from '../utils/fileUtils.js'
|
import { genFileInfo } from '../utils/fileUtils.js'
|
||||||
|
@ -65,9 +65,10 @@ function getDavRequest(extraProps = '') {
|
||||||
* @param {string} path - Albums' root path.
|
* @param {string} path - Albums' root path.
|
||||||
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
||||||
* @param {string} extraProps - Extra properties to add to the DAV request.
|
* @param {string} extraProps - Extra properties to add to the DAV request.
|
||||||
|
* @param {import('webdav').WebDAVClient} client - The DAV client to use.
|
||||||
* @return {Promise<Album|null>}
|
* @return {Promise<Album|null>}
|
||||||
*/
|
*/
|
||||||
export async function fetchAlbum(path, options, extraProps = '') {
|
export async function fetchAlbum(path, options, extraProps = '', client = defaultClient) {
|
||||||
try {
|
try {
|
||||||
const response = await client.stat(path, {
|
const response = await client.stat(path, {
|
||||||
data: getDavRequest(extraProps),
|
data: getDavRequest(extraProps),
|
||||||
|
@ -91,12 +92,14 @@ export async function fetchAlbum(path, options, extraProps = '') {
|
||||||
*
|
*
|
||||||
* @param {string} path - Albums' root path.
|
* @param {string} path - Albums' root path.
|
||||||
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
||||||
|
* @param {string} extraProps - Extra properties to add to the DAV request.
|
||||||
|
* @param {import('webdav').WebDAVClient} client - The DAV client to use.
|
||||||
* @return {Promise<Album[]>}
|
* @return {Promise<Album[]>}
|
||||||
*/
|
*/
|
||||||
export async function fetchAlbums(path, options) {
|
export async function fetchAlbums(path, options, extraProps = '', client = defaultClient) {
|
||||||
try {
|
try {
|
||||||
const response = await client.getDirectoryContents(path, {
|
const response = await client.getDirectoryContents(path, {
|
||||||
data: getDavRequest(),
|
data: getDavRequest(extraProps),
|
||||||
details: true,
|
details: true,
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
@ -158,9 +161,10 @@ function formatAlbum(album) {
|
||||||
*
|
*
|
||||||
* @param {string} path - Albums' root path.
|
* @param {string} path - Albums' root path.
|
||||||
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
|
||||||
|
* @param {import('webdav').WebDAVClient} client - The DAV client to use.
|
||||||
* @return {Promise<Array>}
|
* @return {Promise<Array>}
|
||||||
*/
|
*/
|
||||||
export async function fetchAlbumContent(path, options) {
|
export async function fetchAlbumContent(path, options, client = defaultClient) {
|
||||||
try {
|
try {
|
||||||
const response = await client.getDirectoryContents(path, {
|
const response = await client.getDirectoryContents(path, {
|
||||||
data: DavRequest,
|
data: DavRequest,
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
slot="header"
|
slot="header"
|
||||||
slot-scope="{selectedFileIds}"
|
slot-scope="{selectedFileIds}"
|
||||||
:loading="loadingAlbum || loadingFiles"
|
:loading="loadingAlbum || loadingFiles"
|
||||||
:params="{ userId, token }"
|
:params="{ token }"
|
||||||
path="/"
|
path="/"
|
||||||
:root-title="albumOriginalName"
|
:root-title="albumOriginalName"
|
||||||
:title="albumOriginalName"
|
:title="albumOriginalName"
|
||||||
|
@ -93,6 +93,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex'
|
import { mapActions, mapGetters } from 'vuex'
|
||||||
|
import { createClient, getPatcher } from 'webdav'
|
||||||
|
|
||||||
import MapMarker from 'vue-material-design-icons/MapMarker'
|
import MapMarker from 'vue-material-design-icons/MapMarker'
|
||||||
import Plus from 'vue-material-design-icons/Plus'
|
import Plus from 'vue-material-design-icons/Plus'
|
||||||
import ImagePlus from 'vue-material-design-icons/ImagePlus'
|
import ImagePlus from 'vue-material-design-icons/ImagePlus'
|
||||||
|
@ -100,8 +102,10 @@ import Close from 'vue-material-design-icons/Close'
|
||||||
// import Download from 'vue-material-design-icons/Download'
|
// import Download from 'vue-material-design-icons/Download'
|
||||||
// import DownloadMultiple from 'vue-material-design-icons/DownloadMultiple'
|
// import DownloadMultiple from 'vue-material-design-icons/DownloadMultiple'
|
||||||
|
|
||||||
import { NcActions, NcActionButton, NcButton, NcModal, NcEmptyContent, /** NcActionSeparator, */ isMobile } from '@nextcloud/vue'
|
import { NcActions, NcActionButton, NcButton, NcEmptyContent, /** NcActionSeparator, */ isMobile } from '@nextcloud/vue'
|
||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
import axios from '@nextcloud/axios'
|
||||||
|
import { generateRemoteUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
import FetchFilesMixin from '../mixins/FetchFilesMixin.js'
|
import FetchFilesMixin from '../mixins/FetchFilesMixin.js'
|
||||||
import AbortControllerMixin from '../mixins/AbortControllerMixin.js'
|
import AbortControllerMixin from '../mixins/AbortControllerMixin.js'
|
||||||
|
@ -111,6 +115,16 @@ import HeaderNavigation from '../components/HeaderNavigation.vue'
|
||||||
import { fetchAlbum, fetchAlbumContent } from '../services/Albums.js'
|
import { fetchAlbum, fetchAlbumContent } from '../services/Albums.js'
|
||||||
import logger from '../services/logger.js'
|
import logger from '../services/logger.js'
|
||||||
|
|
||||||
|
const publicRootPath = 'dav'
|
||||||
|
|
||||||
|
// force our axios
|
||||||
|
const patcher = getPatcher()
|
||||||
|
patcher.patch('request', axios)
|
||||||
|
|
||||||
|
// init webdav client on default dav endpoint
|
||||||
|
const remote = generateRemoteUrl(publicRootPath)
|
||||||
|
const publicRemote = remote
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PublicAlbumContent',
|
name: 'PublicAlbumContent',
|
||||||
components: {
|
components: {
|
||||||
|
@ -137,10 +151,6 @@ export default {
|
||||||
],
|
],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
userId: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
token: {
|
token: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -155,6 +165,10 @@ export default {
|
||||||
loadingCount: 0,
|
loadingCount: 0,
|
||||||
loadingAddFilesToAlbum: false,
|
loadingAddFilesToAlbum: false,
|
||||||
albumOriginalName: '',
|
albumOriginalName: '',
|
||||||
|
publicClient: createClient(publicRemote, {
|
||||||
|
username: this.token,
|
||||||
|
password: null,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,9 +224,10 @@ export default {
|
||||||
this.errorFetchingAlbum = null
|
this.errorFetchingAlbum = null
|
||||||
|
|
||||||
const album = await fetchAlbum(
|
const album = await fetchAlbum(
|
||||||
`/photos/${this.userId}/public/${this.token}`,
|
`/photospublic/${this.token}`,
|
||||||
this.abortController.signal,
|
this.abortController.signal,
|
||||||
'<nc:original-name />',
|
'<nc:original-name />',
|
||||||
|
this.publicClient,
|
||||||
)
|
)
|
||||||
this.addPublicAlbums({ collections: [album] })
|
this.addPublicAlbums({ collections: [album] })
|
||||||
this.albumOriginalName = album.originalName
|
this.albumOriginalName = album.originalName
|
||||||
|
@ -244,8 +259,9 @@ export default {
|
||||||
this.semaphoreSymbol = semaphoreSymbol
|
this.semaphoreSymbol = semaphoreSymbol
|
||||||
|
|
||||||
const fetchedFiles = await fetchAlbumContent(
|
const fetchedFiles = await fetchAlbumContent(
|
||||||
`/photos/${this.userId}/public/${this.token}`,
|
`/photospublic/${this.token}`,
|
||||||
this.abortController.signal,
|
this.abortController.signal,
|
||||||
|
this.publicClient,
|
||||||
)
|
)
|
||||||
|
|
||||||
const fileIds = fetchedFiles
|
const fileIds = fetchedFiles
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
|
||||||
|
<input type="hidden" id="isPublic" name="isPublic" value="1">
|
||||||
|
<div id="content"></div>
|
|
@ -20,4 +20,10 @@
|
||||||
<code>SearchQuery</code>
|
<code>SearchQuery</code>
|
||||||
</UndefinedClass>
|
</UndefinedClass>
|
||||||
</file>
|
</file>
|
||||||
</files>
|
<file src="lib/Controller/PublicAlbumController.php">
|
||||||
|
<UndefinedClass occurrences="10">
|
||||||
|
<code>LoadViewer</code>
|
||||||
|
<code>LoadViewer</code>
|
||||||
|
</UndefinedClass>
|
||||||
|
</file>
|
||||||
|
</files>
|
Loading…
Reference in New Issue