diff options
Diffstat (limited to 'apps/files/lib/Controller/ViewController.php')
-rw-r--r-- | apps/files/lib/Controller/ViewController.php | 265 |
1 files changed, 85 insertions, 180 deletions
diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php index 5c31ae8ffd4..ecf21cef313 100644 --- a/apps/files/lib/Controller/ViewController.php +++ b/apps/files/lib/Controller/ViewController.php @@ -1,41 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author fnuesse <felix.nuesse@t-online.de> - * @author fnuesse <fnuesse@techfak.uni-bielefeld.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Max Kovalenko <mxss1998@yandex.ru> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Nina Pypchenko <22447785+nina-py@users.noreply.github.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Files\Controller; -use OCA\Files\Activity\Helper; +use OC\Files\FilenameValidator; +use OC\Files\Filesystem; use OCA\Files\AppInfo\Application; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCA\Files\Event\LoadSearchPlugins; @@ -45,12 +18,15 @@ use OCA\Files\Service\ViewConfig; use OCA\Viewer\Event\LoadViewer; use OCP\App\IAppManager; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\Authentication\TwoFactorAuth\IRegistry; use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent as ResourcesLoadAdditionalScriptsEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Folder; @@ -62,197 +38,150 @@ use OCP\IL10N; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; -use OCP\Share\IManager; +use OCP\Util; /** * @package OCA\Files\Controller */ #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] class ViewController extends Controller { - private IURLGenerator $urlGenerator; - private IL10N $l10n; - private IConfig $config; - private IEventDispatcher $eventDispatcher; - private IUserSession $userSession; - private IAppManager $appManager; - private IRootFolder $rootFolder; - private Helper $activityHelper; - private IInitialState $initialState; - private ITemplateManager $templateManager; - private IManager $shareManager; - private UserConfig $userConfig; - private ViewConfig $viewConfig; - - public function __construct(string $appName, + + public function __construct( + string $appName, IRequest $request, - IURLGenerator $urlGenerator, - IL10N $l10n, - IConfig $config, - IEventDispatcher $eventDispatcher, - IUserSession $userSession, - IAppManager $appManager, - IRootFolder $rootFolder, - Helper $activityHelper, - IInitialState $initialState, - ITemplateManager $templateManager, - IManager $shareManager, - UserConfig $userConfig, - ViewConfig $viewConfig + private IURLGenerator $urlGenerator, + private IL10N $l10n, + private IConfig $config, + private IEventDispatcher $eventDispatcher, + private IUserSession $userSession, + private IAppManager $appManager, + private IRootFolder $rootFolder, + private IInitialState $initialState, + private ITemplateManager $templateManager, + private UserConfig $userConfig, + private ViewConfig $viewConfig, + private FilenameValidator $filenameValidator, + private IRegistry $twoFactorRegistry, ) { parent::__construct($appName, $request); - $this->urlGenerator = $urlGenerator; - $this->l10n = $l10n; - $this->config = $config; - $this->eventDispatcher = $eventDispatcher; - $this->userSession = $userSession; - $this->appManager = $appManager; - $this->rootFolder = $rootFolder; - $this->activityHelper = $activityHelper; - $this->initialState = $initialState; - $this->templateManager = $templateManager; - $this->shareManager = $shareManager; - $this->userConfig = $userConfig; - $this->viewConfig = $viewConfig; } /** * FIXME: Replace with non static code * * @return array - * @throws \OCP\Files\NotFoundException + * @throws NotFoundException */ protected function getStorageInfo(string $dir = '/') { - $rootInfo = \OC\Files\Filesystem::getFileInfo('/', false); + $rootInfo = Filesystem::getFileInfo('/', false); return \OC_Helper::getStorageInfo($dir, $rootInfo ?: null); } /** - * @NoCSRFRequired - * @NoAdminRequired - * * @param string $fileid * @return TemplateResponse|RedirectResponse */ - public function showFile(?string $fileid = null): Response { + #[NoAdminRequired] + #[NoCSRFRequired] + public function showFile(?string $fileid = null, ?string $opendetails = null, ?string $openfile = null): Response { if (!$fileid) { return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index')); } // This is the entry point from the `/f/{fileid}` URL which is hardcoded in the server. try { - return $this->redirectToFile((int) $fileid); + return $this->redirectToFile((int)$fileid, $opendetails, $openfile); } catch (NotFoundException $e) { - return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', ['fileNotFound' => true])); + // Keep the fileid even if not found, it will be used + // to detect the file could not be found and warn the user + return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.indexViewFileid', ['fileid' => $fileid, 'view' => 'files'])); } } /** - * @NoCSRFRequired - * @NoAdminRequired - * @UseSession - * * @param string $dir * @param string $view * @param string $fileid - * @param bool $fileNotFound * @return TemplateResponse|RedirectResponse */ - public function indexView($dir = '', $view = '', $fileid = null, $fileNotFound = false) { - return $this->index($dir, $view, $fileid, $fileNotFound); + #[NoAdminRequired] + #[NoCSRFRequired] + public function indexView($dir = '', $view = '', $fileid = null) { + return $this->index($dir, $view, $fileid); } /** - * @NoCSRFRequired - * @NoAdminRequired - * @UseSession - * * @param string $dir * @param string $view * @param string $fileid - * @param bool $fileNotFound * @return TemplateResponse|RedirectResponse */ - public function indexViewFileid($dir = '', $view = '', $fileid = null, $fileNotFound = false) { - return $this->index($dir, $view, $fileid, $fileNotFound); + #[NoAdminRequired] + #[NoCSRFRequired] + public function indexViewFileid($dir = '', $view = '', $fileid = null) { + return $this->index($dir, $view, $fileid); } /** - * @NoCSRFRequired - * @NoAdminRequired - * @UseSession - * * @param string $dir * @param string $view * @param string $fileid - * @param bool $fileNotFound * @return TemplateResponse|RedirectResponse */ - public function index($dir = '', $view = '', $fileid = null, $fileNotFound = false) { + #[NoAdminRequired] + #[NoCSRFRequired] + public function index($dir = '', $view = '', $fileid = null) { if ($fileid !== null && $view !== 'trashbin') { try { - return $this->redirectToFileIfInTrashbin((int) $fileid); + return $this->redirectToFileIfInTrashbin((int)$fileid); } catch (NotFoundException $e) { } } // Load the files we need - \OCP\Util::addInitScript('files', 'init'); - \OCP\Util::addStyle('files', 'merged'); - \OCP\Util::addScript('files', 'main'); - - $userId = $this->userSession->getUser()->getUID(); + Util::addInitScript('files', 'init'); + Util::addScript('files', 'main'); - // Get all the user favorites to create a submenu - try { - $userFolder = $this->rootFolder->getUserFolder($userId); - $favElements = $this->activityHelper->getFavoriteNodes($userId, true); - $favElements = array_map(fn (Folder $node) => [ - 'fileid' => $node->getId(), - 'path' => $userFolder->getRelativePath($node->getPath()), - ], $favElements); - } catch (\RuntimeException $e) { - $favElements = []; - } + $user = $this->userSession->getUser(); + $userId = $user->getUID(); // If the file doesn't exists in the folder and // exists in only one occurrence, redirect to that file // in the correct folder if ($fileid && $dir !== '') { $baseFolder = $this->rootFolder->getUserFolder($userId); - $nodes = $baseFolder->getById((int) $fileid); + $nodes = $baseFolder->getById((int)$fileid); if (!empty($nodes)) { $nodePath = $baseFolder->getRelativePath($nodes[0]->getPath()); $relativePath = $nodePath ? dirname($nodePath) : ''; // If the requested path does not contain the file id // or if the requested path is not the file id itself if (count($nodes) === 1 && $relativePath !== $dir && $nodePath !== $dir) { - return $this->redirectToFile((int) $fileid); + return $this->redirectToFile((int)$fileid); } - } else { // fileid does not exist anywhere - $fileNotFound = true; } } try { // If view is files, we use the directory, otherwise we use the root storage $storageInfo = $this->getStorageInfo(($view === 'files' && $dir) ? $dir : '/'); - } catch(\Exception $e) { + } catch (\Exception $e) { $storageInfo = $this->getStorageInfo(); } $this->initialState->provideInitialState('storageStats', $storageInfo); $this->initialState->provideInitialState('config', $this->userConfig->getConfigs()); $this->initialState->provideInitialState('viewConfigs', $this->viewConfig->getConfigs()); - $this->initialState->provideInitialState('favoriteFolders', $favElements); // File sorting user config $filesSortingConfig = json_decode($this->config->getUserValue($userId, 'files', 'files_sorting_configs', '{}'), true); $this->initialState->provideInitialState('filesSortingConfig', $filesSortingConfig); - // Forbidden file characters - $forbiddenCharacters = \OCP\Util::getForbiddenFileNameChars(); + // Forbidden file characters (deprecated use capabilities) + // TODO: Remove with next release of `@nextcloud/files` + $forbiddenCharacters = $this->filenameValidator->getForbiddenCharacters(); $this->initialState->provideInitialState('forbiddenCharacters', $forbiddenCharacters); $event = new LoadAdditionalScriptsEvent(); @@ -265,9 +194,19 @@ class ViewController extends Controller { $this->eventDispatcher->dispatchTyped(new LoadViewer()); } + $this->initialState->provideInitialState('templates_enabled', ($this->config->getSystemValueString('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton') !== '') || ($this->config->getSystemValueString('templatedirectory', \OC::$SERVERROOT . '/core/skeleton/Templates') !== '')); $this->initialState->provideInitialState('templates_path', $this->templateManager->hasTemplateDirectory() ? $this->templateManager->getTemplatePath() : false); $this->initialState->provideInitialState('templates', $this->templateManager->listCreators()); + $isTwoFactorEnabled = false; + foreach ($this->twoFactorRegistry->getProviderStates($user) as $providerId => $providerState) { + if ($providerId !== 'backup_codes' && $providerState === true) { + $isTwoFactorEnabled = true; + } + } + + $this->initialState->provideInitialState('isTwoFactorEnabled', $isTwoFactorEnabled); + $response = new TemplateResponse( Application::APP_ID, 'index', @@ -278,61 +217,10 @@ class ViewController extends Controller { $policy->addAllowedWorkerSrcDomain('\'self\''); $response->setContentSecurityPolicy($policy); - $this->provideInitialState($dir, $fileid); - return $response; } /** - * Add openFileInfo in initialState. - * @param string $dir - the ?dir= URL param - * @param string $fileid - the fileid URL param - * @return void - */ - private function provideInitialState(string $dir, ?string $fileid): void { - if ($fileid === null) { - return; - } - - $user = $this->userSession->getUser(); - - if ($user === null) { - return; - } - - $uid = $user->getUID(); - $userFolder = $this->rootFolder->getUserFolder($uid); - $node = $userFolder->getFirstNodeById((int) $fileid); - - if ($node === null) { - return; - } - - // properly format full path and make sure - // we're relative to the user home folder - $isRoot = $node === $userFolder; - $path = $userFolder->getRelativePath($node->getPath()); - $directory = $userFolder->getRelativePath($node->getParent()->getPath()); - - // Prevent opening a file from another folder. - if ($dir !== $directory) { - return; - } - - $this->initialState->provideInitialState( - 'openFileInfo', [ - 'id' => $node->getId(), - 'name' => $isRoot ? '' : $node->getName(), - 'path' => $path, - 'directory' => $directory, - 'mime' => $node->getMimetype(), - 'type' => $node->getType(), - 'permissions' => $node->getPermissions(), - ] - ); - } - - /** * Redirects to the trashbin file list and highlight the given file id * * @param int $fileId file id to show @@ -370,10 +258,12 @@ class ViewController extends Controller { * Redirects to the file list and highlight the given file id * * @param int $fileId file id to show + * @param string|null $openDetails open details parameter + * @param string|null $openFile open file parameter * @return RedirectResponse redirect response or not found response * @throws NotFoundException */ - private function redirectToFile(int $fileId) { + private function redirectToFile(int $fileId, ?string $openDetails = null, ?string $openFile = null): RedirectResponse { $uid = $this->userSession->getUser()->getUID(); $baseFolder = $this->rootFolder->getUserFolder($uid); $node = $baseFolder->getFirstNodeById($fileId); @@ -392,7 +282,22 @@ class ViewController extends Controller { } else { // set parent path as dir $params['dir'] = $baseFolder->getRelativePath($node->getParent()->getPath()); + // open the file by default (opening the viewer) + $params['openfile'] = 'true'; } + + // Forward open parameters if any. + // - openfile is true by default + // - opendetails is undefined by default + // - both will be evaluated as truthy + if ($openDetails !== null) { + $params['opendetails'] = $openDetails !== 'false' ? 'true' : 'false'; + } + + if ($openFile !== null) { + $params['openfile'] = $openFile !== 'false' ? 'true' : 'false'; + } + return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.indexViewFileid', $params)); } |