Skip to content

Commit

Permalink
Add symfony event for resolving private links by apps
Browse files Browse the repository at this point in the history
Apps can now resolve private links whenever the link was not resolvable
by the files app.

Currently only the trashbin is responding to this in case a file is in
trash.
  • Loading branch information
Vincent Petry committed Mar 15, 2018
1 parent 928e6ec commit d2a62ac
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 90 deletions.
38 changes: 23 additions & 15 deletions apps/files/lib/Controller/ViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use OCP\IUserSession;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use OCP\AppFramework\Http;
use Symfony\Component\EventDispatcher\GenericEvent;

/**
* Class ViewController
Expand Down Expand Up @@ -283,18 +284,20 @@ public function showFile($fileId) {
$files = $baseFolder->getById($fileId);
$params = [];

$isFilesView = true;
if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
// Access files_trashbin if it exists
if ( $this->rootFolder->nodeExists($uid . '/files_trashbin/files/')) {
$baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
$files = $baseFolder->getById($fileId);
$params['view'] = 'trashbin';
$isFilesView = false;
}
}
if (empty($files)) {
// probe apps to see if the file is in a different state and can be accessed
// through another URL
$event = new GenericEvent(null, [
'fileid' => $fileId,
'uid' => $uid,
'resolvedWebLink' => null,
'resolvedDavLink' => null,
]);
$this->eventDispatcher->dispatch('files.resolvePrivateLink', $event);

if (!empty($files)) {
$webUrl = $event->getArgument('resolvedWebLink');
$webdavUrl = $event->getArgument('resolvedDavLink');
} else {
$file = current($files);
if ($file instanceof Folder) {
// set the full path to enter the folder
Expand All @@ -305,10 +308,15 @@ public function showFile($fileId) {
// and scroll to the entry
$params['scrollto'] = $file->getName();
}
$response = new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
if ($isFilesView) {
$webdavUrl = $this->urlGenerator->linkTo('', 'remote.php') . '/dav/files/' . rawurlencode($uid) . '/';
$webdavUrl .= \OCP\Util::encodePath(ltrim($baseFolder->getRelativePath($file->getPath()), '/'));
$webUrl = $this->urlGenerator->linkToRoute('files.view.index', $params);

$webdavUrl = $this->urlGenerator->linkTo('', 'remote.php') . '/dav/files/' . rawurlencode($uid) . '/';
$webdavUrl .= \OCP\Util::encodePath(ltrim($baseFolder->getRelativePath($file->getPath()), '/'));
}

if ($webUrl) {
$response = new RedirectResponse($webUrl);
if ($webdavUrl !== null) {
$response->addHeader('Webdav-Location', $webdavUrl);
}
return $response;
Expand Down
62 changes: 14 additions & 48 deletions apps/files/tests/Controller/ViewControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use OCP\Template;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
use Symfony\Component\EventDispatcher\GenericEvent;

/**
* Class ViewControllerTest
Expand Down Expand Up @@ -468,63 +469,28 @@ public function testShowFileRouteWithInvalidFileIdLoggedIn() {
/**
* @dataProvider showFileMethodProvider
*/
public function testShowFileRouteWithTrashedFile($useShowFile) {
$this->appManager->expects($this->once())
->method('isEnabledForUser')
->with('files_trashbin')
->will($this->returnValue(true));

public function testShowFileRouteWithDispatcher($useShowFile) {
$baseFolder = $this->createMock('\OCP\Files\Folder');
$this->rootFolder->expects($this->once())
->method('nodeExists')
->will($this->returnValue(true));

$parentNode = $this->createMock('\OCP\Files\Folder');
$parentNode->expects($this->once())
->method('getPath')
->will($this->returnValue('test@#?%test/files_trashbin/files/test.d1462861890/sub'));

$baseFolderFiles = $this->createMock('\OCP\Files\Folder');
$baseFolderTrash = $this->createMock('\OCP\Files\Folder');

$this->rootFolder->expects($this->at(0))
->method('get')
->with('test@#?%test/files/')
->will($this->returnValue($baseFolderFiles));
//The index is pointing to 2, because nodeExists internally calls get method.
$this->rootFolder->expects($this->at(2))
->method('get')
->with('test@#?%test/files_trashbin/files/')
->will($this->returnValue($baseFolderTrash));
->will($this->returnValue($baseFolder));

$baseFolderFiles->expects($this->once())
$baseFolder->expects($this->at(0))
->method('getById')
->with(123)
->will($this->returnValue([]));

$node = $this->createMock('\OCP\Files\File');
$node->expects($this->once())
->method('getParent')
->will($this->returnValue($parentNode));
$node->expects($this->once())
->method('getName')
->will($this->returnValue('somefile.txt'));

$baseFolderTrash->expects($this->at(0))
->method('getById')
->with(123)
->will($this->returnValue([$node]));
$baseFolderTrash->expects($this->at(1))
->method('getRelativePath')
->with('test@#?%test/files_trashbin/files/test.d1462861890/sub')
->will($this->returnValue('/test.d1462861890/sub'));

$this->urlGenerator
->expects($this->once())
->method('linkToRoute')
->with('files.view.index', ['view' => 'trashbin', 'dir' => '/test.d1462861890/sub', 'scrollto' => 'somefile.txt'])
->will($this->returnValue('/owncloud/index.php/apps/files/?view=trashbin&dir=/test.d1462861890/sub&scrollto=somefile.txt'));
$this->eventDispatcher->expects($this->once())
->method('dispatch')
->with('files.resolvePrivateLink')
->will($this->returnCallback(function ($eventName, $event) {
$event->setArgument('resolvedWebLink', '/owncloud/weblink/' . $event->getArgument('uid') . '/' . $event->getArgument('fileid'));
$event->setArgument('resolvedDavLink', '/owncloud/davlink/' . $event->getArgument('uid') . '/' . $event->getArgument('fileid'));
}));

$expected = new Http\RedirectResponse('/owncloud/index.php/apps/files/?view=trashbin&dir=/test.d1462861890/sub&scrollto=somefile.txt');
$expected = new Http\RedirectResponse('/owncloud/weblink/test@#?%test/123');
$expected->addHeader('Webdav-Location', '/owncloud/davlink/test@#?%test/123');
if ($useShowFile) {
$this->assertEquals($expected, $this->viewController->showFile(123));
} else {
Expand Down
3 changes: 3 additions & 0 deletions apps/files_trashbin/appinfo/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@
'name' => $l->t('Deleted files'),
];
});

$app = new \OCA\Files_Trashbin\AppInfo\Application();
$app->registerListeners();
}
16 changes: 16 additions & 0 deletions apps/files_trashbin/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OCA\Files_Trashbin\Expiration;
use OCA\Files_Trashbin\Quota;
use OCP\AppFramework\App;
use OCA\Files_Trashbin\Trashbin;

class Application extends App {
public function __construct (array $urlParams = []) {
Expand Down Expand Up @@ -55,5 +56,20 @@ public function __construct (array $urlParams = []) {
$c->query('ServerContainer')->getConfig()
);
});

/*
* Register trashbin service
*/
$container->registerService('Trashbin', function($c) {
return new Trashbin(
$c->getServer()->getLazyRootFolder(),
$c->getServer()->getUrlGenerator(),
$c->getServer()->getEventDispatcher()
);
});
}

public function registerListeners() {
$this->getContainer()->query('Trashbin')->registerListeners();
}
}
79 changes: 79 additions & 0 deletions apps/files_trashbin/lib/Trashbin.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,39 @@
use OCA\Files_Trashbin\Command\Expire;
use OCP\Files\NotFoundException;
use OCP\User;
use Symfony\Component\EventDispatcher\GenericEvent;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\IURLGenerator;
use Symfony\Component\EventDispatcher\EventDispatcher;

class Trashbin {

/**
* @var IURLGenerator
*/
private $urlGenerator;

/**
* @var IRootFolder
*/
private $rootFolder;

/**
* @var EventDispatcher
*/
private $eventDispatcher;

public function __construct(
IRootFolder $rootFolder,
IUrlGenerator $urlGenerator,
EventDispatcher $eventDispatcher
) {
$this->rootFolder = $rootFolder;
$this->urlGenerator = $urlGenerator;
$this->eventDispatcher = $eventDispatcher;
}

/**
* Whether versions have already be rescanned during this PHP request
*
Expand Down Expand Up @@ -974,6 +1004,25 @@ private static function getTrashbinSize($user) {
return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
}

/**
* Register listeners
*/
public function registerListeners() {
$this->eventDispatcher->addListener(
'files.resolvePrivateLink',
function(GenericEvent $event) {
$uid = $event->getArgument('uid');
$fileId = $event->getArgument('fileid');

$link = $this->resolvePrivateLink($uid, $fileId);

if ($link !== null) {
$event->setArgument('resolvedWebLink', $link);
}
}
);
}

/**
* register hooks
*/
Expand All @@ -990,6 +1039,36 @@ public static function registerHooks() {
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Trashbin\Storage', 'postRenameHook');
}

/**
* Resolves web URL that points to the trashbin view of the given file
*
* @param string $uid user id
* @param string $fileId file id
* @return string|null view URL or null if the file is not found or not accessible
*/
public function resolvePrivateLink($uid, $fileId) {
if ($this->rootFolder->nodeExists($uid . '/files_trashbin/files/')) {
$baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
$files = $baseFolder->getById($fileId);
if (!empty($files)) {
$params['view'] = 'trashbin';
$file = current($files);
if ($file instanceof Folder) {
// set the full path to enter the folder
$params['dir'] = $baseFolder->getRelativePath($file->getPath());
} else {
// set parent path as dir
$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
// and scroll to the entry
$params['scrollto'] = $file->getName();
}
return $this->urlGenerator->linkToRoute('files.view.index', $params);
}
}

return null;
}

/**
* check if trash bin is empty for a given user
*
Expand Down
Loading

0 comments on commit d2a62ac

Please sign in to comment.