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

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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\Files_Sharing\SharedStorage;
use OCA\Photos\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\StorageNotAvailableException;
use OCP\IPreview;
use OCP\IRequest;
class AlbumsController extends Controller {
private string $userId;
private IRootFolder $rootFolder;
private IPreview $previewManager;
public function __construct(
string $userId,
IRequest $request,
IRootFolder $rootFolder,
IPreview $previewManager
) {
parent::__construct(Application::APP_ID, $request);
$this->userId = $userId;
$this->rootFolder = $rootFolder;
$this->previewManager = $previewManager;
}
/**
* @NoAdminRequired
*/
public function myAlbums(string $path = ''): JSONResponse {
return $this->generate($path, false);
}
/**
* @NoAdminRequired
*/
public function sharedAlbums(string $path = ''): JSONResponse {
return $this->generate($path, true);
}
private function generate(string $path, bool $shared): JSONResponse {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$folder = $userFolder;
if ($path !== '') {
try {
$folder = $userFolder->get($path);
} catch (NotFoundException $e) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
}
$data = $this->scanCurrentFolder($folder, $shared);
$result = $this->formatData($data);
return new JSONResponse($result, Http::STATUS_OK);
}
private function formatData(iterable $nodes): array {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$result = [];
/** @var Node $node */
foreach ($nodes as $node) {
// properly format full path and make sure
// we're relative to the user home folder
$isRoot = $node === $userFolder;
$path = $userFolder->getRelativePath($node->getPath());
$result[] = [
'basename' => $isRoot ? '' : $node->getName(),
'etag' => $node->getEtag(),
'fileid' => $node->getId(),
'filename' => $path,
'lastmod' => $node->getMTime(),
'mime' => $node->getMimetype(),
'size' => $node->getSize(),
'type' => $node->getType(),
'permissions' => $node->getPermissions(),
'hasPreview' => $this->previewManager->isAvailable($node),
];
}
return $result;
}
private function scanCurrentFolder(Folder $folder, bool $shared): iterable {
$nodes = $folder->getDirectoryListing();
// add current folder to iterable set
yield $folder;
foreach ($nodes as $node) {
if ($node instanceof Folder && $this->scanFolder($node, 0, $shared)) {
yield $node;
} elseif ($node instanceof File) {
if ($this->validFile($node, $shared)) {
yield $node;
}
}
}
}
private function validFile(File $file, bool $shared): bool {
$allowed_mimes = array_merge(Application::IMAGE_MIMES, Application::VIDEO_MIMES);
if (in_array($file->getMimeType(), $allowed_mimes) && $this->isShared($file) === $shared) {
return true;
}
return false;
}
private function isShared(Node $node): bool {
return $node->getStorage()->instanceOfStorage(SharedStorage::class) ||
$node->getStorage()->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
}
private function scanFolder(Folder $folder, int $depth, bool $shared): bool {
if ($depth > 4) {
return false;
}
try {
// Ignore folder with a .noimage or .nomedia node
if ($folder->nodeExists('.noimage') || $folder->nodeExists('.nomedia')) {
return false;
}
$nodes = $folder->getDirectoryListing();
} catch (StorageNotAvailableException $e) {
return false;
}
foreach ($nodes as $node) {
if ($node instanceof File) {
if ($this->validFile($node, $shared)) {
return true;
}
}
}
foreach ($nodes as $node) {
if ($node instanceof Folder && $this->isShared($node) === $shared) {
if ($this->scanFolder($node, $depth + 1, $shared)) {
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 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/>.
*
*/
namespace OCA\Photos\Controller;
use OCA\Photos\AppInfo\Application;
use OCA\Photos\Service\UserConfigService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\StreamResponse;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
class ApiController extends Controller {
private IConfig $config;
private IUserSession $userSession;
public function __construct(
IRequest $request,
IConfig $config,
IUserSession $userSession
) {
parent::__construct(Application::APP_ID, $request);
$this->config = $config;
$this->userSession = $userSession;
}
/**
* @NoAdminRequired
*
* update preferences (user setting)
*
* @param string key the identifier to change
* @param string value the value to set
*
* @return JSONResponse an empty JSONResponse with respective http status code
*/
public function setUserConfig(string $key, string $value): JSONResponse {
$user = $this->userSession->getUser();
if (is_null($user)) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
if (!in_array($key, array_keys(UserConfigService::DEFAULT_CONFIGS))) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
$this->config->setUserValue($user->getUid(), Application::APP_ID, $key, $value);
return new JSONResponse([], Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function serviceWorker(): StreamResponse {
$response = new StreamResponse(__DIR__.'/../../js/photos-service-worker.js');
$response->setHeaders([
'Content-Type' => 'application/javascript',
'Service-Worker-Allowed' => '/'
]);
$policy = new ContentSecurityPolicy();
$policy->addAllowedWorkerSrcDomain("'self'");
$policy->addAllowedScriptDomain("'self'");
$policy->addAllowedConnectDomain("'self'");
$response->setContentSecurityPolicy($policy);
return $response;
}
}
@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
/**
* @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/>.
*
*/
namespace OCA\Photos\Controller;
use OC\Files\Search\SearchBinaryOperator;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchQuery;
use OC\User\NoUserException;
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\Controller;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\Search\ISearchBinaryOperator;
use OCP\Files\Search\ISearchComparison;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\Util;
use Psr\Log\LoggerInterface;
class PageController extends Controller {
private IAppManager $appManager;
private IEventDispatcher $eventDispatcher;
private UserConfigService $userConfig;
private IInitialState $initialState;
private IUserSession $userSession;
private IRootFolder $rootFolder;
private ICacheFactory $cacheFactory;
private IL10N $l10n;
private ICache $nomediaPathsCache;
private LoggerInterface $logger;
public function __construct(
IRequest $request,
IAppManager $appManager,
IEventDispatcher $eventDispatcher,
UserConfigService $userConfig,
IInitialState $initialState,
IUserSession $userSession,
IRootFolder $rootFolder,
ICacheFactory $cacheFactory,
LoggerInterface $logger,
private IConfig $config,
IL10N $l10n
) {
parent::__construct(Application::APP_ID, $request);
$this->appManager = $appManager;
$this->eventDispatcher = $eventDispatcher;
$this->userConfig = $userConfig;
$this->initialState = $initialState;
$this->userSession = $userSession;
$this->rootFolder = $rootFolder;
$this->cacheFactory = $cacheFactory;
$this->nomediaPathsCache = $this->cacheFactory->createLocal('photos:nomedia-paths');
$this->logger = $logger;
$this->l10n = $l10n;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* Render default index template
*
* @return TemplateResponse
*/
public function index(): TemplateResponse {
$user = $this->userSession->getUser();
$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);
$this->initialState->provideInitialState('showPeopleMenuEntry', $this->config->getAppValue('photos', 'showPeopleMenuEntry', 'true') === 'true');
// Provide user config
foreach (array_keys(UserConfigService::DEFAULT_CONFIGS) as $key) {
$this->initialState->provideInitialState($key, $this->userConfig->getUserConfig($key));
}
$paths = [];
try {
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
$key = $user->getUID() . ':' . $userFolder->getEtag();
$paths = $this->nomediaPathsCache->get($key);
if ($paths === null) {
$search = $userFolder->search(new SearchQuery(new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', '.nomedia'),
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', '.noimage')
]), 0, 0, [], $user));
$paths = array_map(function (Node $node) use ($userFolder) {
return substr(dirname($node->getPath()), strlen($userFolder->getPath()));
}, $search);
$this->nomediaPathsCache->set($key, $paths, 60 * 60 * 24 * 28); // 28 days
}
} catch (InvalidPathException | NotFoundException | NotPermittedException | NoUserException $e) {
$this->logger->error($e->getMessage());
}
$this->initialState->provideInitialState('nomedia-paths', $paths);
Util::addScript(Application::APP_ID, 'photos-main');
if ($this->appManager->isEnabledForUser('recognize') === true) {
// Allow auto-translation of tags
Util::addTranslations('recognize');
}
$response = new TemplateResponse(Application::APP_ID, 'main', [
'pageTitle' => $this->l10n->t('Photos')
]);
$policy = new ContentSecurityPolicy();
$policy->addAllowedWorkerSrcDomain("'self'");
$policy->addAllowedScriptDomain("'self'");
$response->setContentSecurityPolicy($policy);
return $response;
}
}
@@ -0,0 +1,174 @@
<?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 OCA\Photos\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IGroupManager;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IUserSession;
class PreviewController extends Controller {
private IUserSession $userSession;
private ?Folder $userFolder;
private IRootFolder $rootFolder;
protected AlbumMapper $albumMapper;
private IPreview $preview;
private IGroupManager $groupManager;
public function __construct(
IRequest $request,
IUserSession $userSession,
?Folder $userFolder,
IRootFolder $rootFolder,
AlbumMapper $albumMapper,
IPreview $preview,
IGroupManager $groupManager
) {
parent::__construct(Application::APP_ID, $request);
$this->userSession = $userSession;
$this->userFolder = $userFolder;
$this->rootFolder = $rootFolder;
$this->albumMapper = $albumMapper;
$this->preview = $preview;
$this->groupManager = $groupManager;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* Render default index template
*
* @return DataResponse|FileDisplayResponse
*/
public function index(
int $fileId = -1,
int $x = 32,
int $y = 32
) {
if ($fileId === -1 || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$user = $this->userSession->getUser();
if ($user === null || $this->userFolder === null) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$nodes = $this->userFolder->getById($fileId);
/** @var \OCA\Photos\Album\AlbumInfo[] */
$checkedAlbums = [];
if (\count($nodes) === 0) {
$albumsOfCurrentUser = $this->albumMapper->getForUserAndFile($user->getUID(), $fileId);
$nodes = $this->getFileIdForAlbums($fileId, $albumsOfCurrentUser);
$checkedAlbums = $albumsOfCurrentUser;
}
if (\count($nodes) === 0) {
$receivedAlbums = $this->albumMapper->getAlbumsForCollaboratorIdAndFileId($user->getUID(), AlbumMapper::TYPE_USER, $fileId);
$receivedAlbums = array_udiff($receivedAlbums, $checkedAlbums, fn ($a, $b) => ($a->getId() - $b->getId()));
$nodes = $this->getFileIdForAlbums($fileId, $receivedAlbums);
$checkedAlbums = array_merge($checkedAlbums, $receivedAlbums);
}
if (\count($nodes) === 0) {
$userGroups = $this->groupManager->getUserGroupIds($user);
foreach ($userGroups as $groupId) {
$albumsForGroup = $this->albumMapper->getAlbumsForCollaboratorIdAndFileId($groupId, AlbumMapper::TYPE_GROUP, $fileId);
$albumsForGroup = array_udiff($albumsForGroup, $checkedAlbums, fn ($a, $b) => ($a->getId() - $b->getId()));
$nodes = $this->getFileIdForAlbums($fileId, $albumsForGroup);
$checkedAlbums = array_merge($checkedAlbums, $receivedAlbums);
if (\count($nodes) !== 0) {
break;
}
}
}
if (\count($nodes) === 0) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
$node = array_pop($nodes);
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
*/
protected function fetchPreview(
Node $node,
int $x,
int $y
) : Http\Response {
if (!($node instanceof File) || !$this->preview->isAvailable($node)) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if (!$node->isReadable()) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
try {
$f = $this->preview->getPreview($node, $x, $y);
$response = new FileDisplayResponse($f, Http::STATUS_OK, [
'Content-Type' => $f->getMimeType(),
]);
$response->cacheFor(3600 * 24, false, true);
return $response;
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
} catch (\InvalidArgumentException $e) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
}
}
@@ -0,0 +1,76 @@
<?php
/**
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
*
* @author Louis Chemineau <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\AppInfo\Application;
use OCA\Viewer\Event\LoadViewer;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IRequest;
use OCP\Util;
class PublicAlbumController extends Controller {
private IEventDispatcher $eventDispatcher;
private IInitialState $initialState;
public function __construct(
IRequest $request,
IEventDispatcher $eventDispatcher,
IInitialState $initialState
) {
parent::__construct(Application::APP_ID, $request);
$this->eventDispatcher = $eventDispatcher;
$this->initialState = $initialState;
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function get(): PublicTemplateResponse {
$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', false);
$this->initialState->provideInitialState('recognize', false);
$this->initialState->provideInitialState('systemtags', false);
Util::addScript(Application::APP_ID, 'photos-public');
$response = new PublicTemplateResponse(Application::APP_ID, 'public');
$policy = new ContentSecurityPolicy();
$policy->addAllowedWorkerSrcDomain("'self'");
$policy->addAllowedScriptDomain("'self'");
$response->setContentSecurityPolicy($policy);
return $response;
}
}
@@ -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);
}
}