Merge pull request #15196 from owncloud/limit-file-activities-to-favorites
Limit file activities to favorites
This commit is contained in:
commit
9c76d068c3
11 changed files with 658 additions and 12 deletions
|
@ -59,6 +59,11 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadshe
|
||||||
\OC::$server->getActivityManager()->registerExtension(function() {
|
\OC::$server->getActivityManager()->registerExtension(function() {
|
||||||
return new \OCA\Files\Activity(
|
return new \OCA\Files\Activity(
|
||||||
\OC::$server->query('L10NFactory'),
|
\OC::$server->query('L10NFactory'),
|
||||||
\OC::$server->getURLGenerator()
|
\OC::$server->getURLGenerator(),
|
||||||
|
\OC::$server->getActivityManager(),
|
||||||
|
new \OCA\Files\ActivityHelper(
|
||||||
|
\OC::$server->getTagManager()
|
||||||
|
),
|
||||||
|
\OC::$server->getConfig()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,16 +24,20 @@ namespace OCA\Files;
|
||||||
|
|
||||||
use OC\L10N\Factory;
|
use OC\L10N\Factory;
|
||||||
use OCP\Activity\IExtension;
|
use OCP\Activity\IExtension;
|
||||||
|
use OCP\Activity\IManager;
|
||||||
|
use OCP\IConfig;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
class Activity implements IExtension {
|
class Activity implements IExtension {
|
||||||
const FILTER_FILES = 'files';
|
const FILTER_FILES = 'files';
|
||||||
|
const FILTER_FAVORITES = 'files_favorites';
|
||||||
|
|
||||||
const TYPE_SHARE_CREATED = 'file_created';
|
const TYPE_SHARE_CREATED = 'file_created';
|
||||||
const TYPE_SHARE_CHANGED = 'file_changed';
|
const TYPE_SHARE_CHANGED = 'file_changed';
|
||||||
const TYPE_SHARE_DELETED = 'file_deleted';
|
const TYPE_SHARE_DELETED = 'file_deleted';
|
||||||
const TYPE_SHARE_RESTORED = 'file_restored';
|
const TYPE_SHARE_RESTORED = 'file_restored';
|
||||||
|
const TYPE_FAVORITES = 'files_favorites';
|
||||||
|
|
||||||
/** @var IL10N */
|
/** @var IL10N */
|
||||||
protected $l;
|
protected $l;
|
||||||
|
@ -44,14 +48,29 @@ class Activity implements IExtension {
|
||||||
/** @var IURLGenerator */
|
/** @var IURLGenerator */
|
||||||
protected $URLGenerator;
|
protected $URLGenerator;
|
||||||
|
|
||||||
|
/** @var \OCP\Activity\IManager */
|
||||||
|
protected $activityManager;
|
||||||
|
|
||||||
|
/** @var \OCP\IConfig */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/** @var \OCA\Files\ActivityHelper */
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Factory $languageFactory
|
* @param Factory $languageFactory
|
||||||
* @param IURLGenerator $URLGenerator
|
* @param IURLGenerator $URLGenerator
|
||||||
|
* @param IManager $activityManager
|
||||||
|
* @param ActivityHelper $helper
|
||||||
|
* @param IConfig $config
|
||||||
*/
|
*/
|
||||||
public function __construct(Factory $languageFactory, IURLGenerator $URLGenerator) {
|
public function __construct(Factory $languageFactory, IURLGenerator $URLGenerator, IManager $activityManager, ActivityHelper $helper, IConfig $config) {
|
||||||
$this->languageFactory = $languageFactory;
|
$this->languageFactory = $languageFactory;
|
||||||
$this->URLGenerator = $URLGenerator;
|
$this->URLGenerator = $URLGenerator;
|
||||||
$this->l = $this->getL10N();
|
$this->l = $this->getL10N();
|
||||||
|
$this->activityManager = $activityManager;
|
||||||
|
$this->helper = $helper;
|
||||||
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,6 +93,7 @@ class Activity implements IExtension {
|
||||||
return [
|
return [
|
||||||
self::TYPE_SHARE_CREATED => (string) $l->t('A new file or folder has been <strong>created</strong>'),
|
self::TYPE_SHARE_CREATED => (string) $l->t('A new file or folder has been <strong>created</strong>'),
|
||||||
self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong>'),
|
self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong>'),
|
||||||
|
self::TYPE_FAVORITES => (string) $l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'),
|
||||||
self::TYPE_SHARE_DELETED => (string) $l->t('A file or folder has been <strong>deleted</strong>'),
|
self::TYPE_SHARE_DELETED => (string) $l->t('A file or folder has been <strong>deleted</strong>'),
|
||||||
self::TYPE_SHARE_RESTORED => (string) $l->t('A file or folder has been <strong>restored</strong>'),
|
self::TYPE_SHARE_RESTORED => (string) $l->t('A file or folder has been <strong>restored</strong>'),
|
||||||
];
|
];
|
||||||
|
@ -229,6 +249,13 @@ class Activity implements IExtension {
|
||||||
*/
|
*/
|
||||||
public function getNavigation() {
|
public function getNavigation() {
|
||||||
return [
|
return [
|
||||||
|
'top' => [
|
||||||
|
self::FILTER_FAVORITES => [
|
||||||
|
'id' => self::FILTER_FAVORITES,
|
||||||
|
'name' => (string) $this->l->t('Favorites'),
|
||||||
|
'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FAVORITES]),
|
||||||
|
],
|
||||||
|
],
|
||||||
'apps' => [
|
'apps' => [
|
||||||
self::FILTER_FILES => [
|
self::FILTER_FILES => [
|
||||||
'id' => self::FILTER_FILES,
|
'id' => self::FILTER_FILES,
|
||||||
|
@ -236,7 +263,6 @@ class Activity implements IExtension {
|
||||||
'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FILES]),
|
'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FILES]),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'top' => [],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +273,7 @@ class Activity implements IExtension {
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isFilterValid($filterValue) {
|
public function isFilterValid($filterValue) {
|
||||||
return $filterValue === self::FILTER_FILES;
|
return $filterValue === self::FILTER_FILES || $filterValue === self::FILTER_FAVORITES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,7 +285,7 @@ class Activity implements IExtension {
|
||||||
* @return array|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
public function filterNotificationTypes($types, $filter) {
|
public function filterNotificationTypes($types, $filter) {
|
||||||
if ($filter === self::FILTER_FILES) {
|
if ($filter === self::FILTER_FILES || $filter === self::FILTER_FAVORITES) {
|
||||||
return array_intersect([
|
return array_intersect([
|
||||||
self::TYPE_SHARE_CREATED,
|
self::TYPE_SHARE_CREATED,
|
||||||
self::TYPE_SHARE_CHANGED,
|
self::TYPE_SHARE_CHANGED,
|
||||||
|
@ -280,9 +306,63 @@ class Activity implements IExtension {
|
||||||
* @return array|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
public function getQueryForFilter($filter) {
|
public function getQueryForFilter($filter) {
|
||||||
|
$user = $this->activityManager->getCurrentUserId();
|
||||||
|
// Display actions from all files
|
||||||
if ($filter === self::FILTER_FILES) {
|
if ($filter === self::FILTER_FILES) {
|
||||||
return ['`app` = ?', ['files']];
|
return ['`app` = ?', ['files']];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
// Remaining filters only work with a user/token
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display actions from favorites only
|
||||||
|
if ($filter === self::FILTER_FAVORITES || in_array($filter, ['all', 'by', 'self']) && $this->userSettingFavoritesOnly($user)) {
|
||||||
|
try {
|
||||||
|
$favorites = $this->helper->getFavoriteFilePaths($user);
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
// Too many favorites, can not put them into one query anymore...
|
||||||
|
return ['`app` = ?', ['files']];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display activities only, when they are not `type` create/change
|
||||||
|
* or `file` is a favorite or in a favorite folder
|
||||||
|
*/
|
||||||
|
$parameters = $fileQueryList = [];
|
||||||
|
$parameters[] = 'files';
|
||||||
|
|
||||||
|
$fileQueryList[] = '(`type` <> ? AND `type` <> ?)';
|
||||||
|
$parameters[] = self::TYPE_SHARE_CREATED;
|
||||||
|
$parameters[] = self::TYPE_SHARE_CHANGED;
|
||||||
|
|
||||||
|
foreach ($favorites['items'] as $favorite) {
|
||||||
|
$fileQueryList[] = '`file` = ?';
|
||||||
|
$parameters[] = $favorite;
|
||||||
|
}
|
||||||
|
foreach ($favorites['folders'] as $favorite) {
|
||||||
|
$fileQueryList[] = '`file` LIKE ?';
|
||||||
|
$parameters[] = $favorite . '/%';
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters[] = 'files';
|
||||||
|
|
||||||
|
return [
|
||||||
|
' CASE WHEN `app` = ? THEN (' . implode(' OR ', $fileQueryList) . ') ELSE `app` <> ? END ',
|
||||||
|
$parameters,
|
||||||
|
];
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the file actions favorite limitation enabled?
|
||||||
|
*
|
||||||
|
* @param string $user
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function userSettingFavoritesOnly($user) {
|
||||||
|
return (bool) $this->config->getUserValue($user, 'activity', 'notify_stream_' . self::TYPE_FAVORITES, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
84
apps/files/lib/activityhelper.php
Normal file
84
apps/files/lib/activityhelper.php
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Joas Schilling <nickvergessen@owncloud.com>
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2015, ownCloud, Inc.
|
||||||
|
* @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\Files;
|
||||||
|
|
||||||
|
use OCP\Files\Folder;
|
||||||
|
use OCP\ITagManager;
|
||||||
|
|
||||||
|
class ActivityHelper {
|
||||||
|
/** If a user has a lot of favorites the query might get too slow and long */
|
||||||
|
const FAVORITE_LIMIT = 50;
|
||||||
|
|
||||||
|
/** @var \OCP\ITagManager */
|
||||||
|
protected $tagManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ITagManager $tagManager
|
||||||
|
*/
|
||||||
|
public function __construct(ITagManager $tagManager) {
|
||||||
|
$this->tagManager = $tagManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with the favorites
|
||||||
|
*
|
||||||
|
* @param string $user
|
||||||
|
* @return array
|
||||||
|
* @throws \RuntimeException when too many or no favorites where found
|
||||||
|
*/
|
||||||
|
public function getFavoriteFilePaths($user) {
|
||||||
|
$tags = $this->tagManager->load('files', [], false, $user);
|
||||||
|
$favorites = $tags->getFavorites();
|
||||||
|
|
||||||
|
if (empty($favorites)) {
|
||||||
|
throw new \RuntimeException('No favorites', 1);
|
||||||
|
} else if (isset($favorites[self::FAVORITE_LIMIT])) {
|
||||||
|
throw new \RuntimeException('Too many favorites', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can not DI because the user is not known on instantiation
|
||||||
|
$rootFolder = \OC::$server->getUserFolder($user);
|
||||||
|
$folders = $items = [];
|
||||||
|
foreach ($favorites as $favorite) {
|
||||||
|
$nodes = $rootFolder->getById($favorite);
|
||||||
|
if (!empty($nodes)) {
|
||||||
|
/** @var \OCP\Files\Node $node */
|
||||||
|
$node = array_shift($nodes);
|
||||||
|
$path = substr($node->getPath(), strlen($user . '/files/'));
|
||||||
|
|
||||||
|
$items[] = $path;
|
||||||
|
if ($node instanceof Folder) {
|
||||||
|
$folders[] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($items)) {
|
||||||
|
throw new \RuntimeException('No favorites', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'items' => $items,
|
||||||
|
'folders' => $folders,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
297
apps/files/tests/activitytest.php
Normal file
297
apps/files/tests/activitytest.php
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Joas Schilling <nickvergessen@owncloud.com>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Files\Tests;
|
||||||
|
|
||||||
|
use OCA\Files\Activity;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class ActivityTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var \OC\ActivityManager */
|
||||||
|
private $activityManager;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $session;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $activityHelper;
|
||||||
|
|
||||||
|
/** @var \OCA\Files\Activity */
|
||||||
|
protected $activityExtension;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->request = $this->getMockBuilder('OCP\IRequest')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->session = $this->getMockBuilder('OCP\IUserSession')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->config = $this->getMockBuilder('OCP\IConfig')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->activityHelper = $this->getMockBuilder('OCA\Files\ActivityHelper')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$this->activityManager = new \OC\ActivityManager(
|
||||||
|
$this->request,
|
||||||
|
$this->session,
|
||||||
|
$this->config
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->activityExtension = $activityExtension = new Activity(
|
||||||
|
new \OC\L10N\Factory(),
|
||||||
|
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
|
||||||
|
$this->activityManager,
|
||||||
|
$this->activityHelper,
|
||||||
|
$this->config
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->activityManager->registerExtension(function() use ($activityExtension) {
|
||||||
|
return $activityExtension;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNotificationTypes() {
|
||||||
|
$result = $this->activityExtension->getNotificationTypes('en');
|
||||||
|
$this->assertTrue(is_array($result), 'Asserting getNotificationTypes() returns an array');
|
||||||
|
$this->assertCount(5, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_CREATED, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_CHANGED, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_FAVORITES, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_DELETED, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_RESTORED, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefaultTypes() {
|
||||||
|
$result = $this->activityExtension->getDefaultTypes('stream');
|
||||||
|
$this->assertTrue(is_array($result), 'Asserting getDefaultTypes(stream) returns an array');
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$result = array_flip($result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_CREATED, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_CHANGED, $result);
|
||||||
|
$this->assertArrayNotHasKey(Activity::TYPE_FAVORITES, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_DELETED, $result);
|
||||||
|
$this->assertArrayHasKey(Activity::TYPE_SHARE_RESTORED, $result);
|
||||||
|
|
||||||
|
$result = $this->activityExtension->getDefaultTypes('email');
|
||||||
|
$this->assertFalse($result, 'Asserting getDefaultTypes(email) returns false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTranslate() {
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->activityExtension->translate('files_sharing', '', [], false, false, 'en'),
|
||||||
|
'Asserting that no translations are set for files_sharing'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSpecialParameterList() {
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->activityExtension->getSpecialParameterList('files_sharing', ''),
|
||||||
|
'Asserting that no special parameters are set for files_sharing'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function typeIconData() {
|
||||||
|
return [
|
||||||
|
[Activity::TYPE_SHARE_CHANGED, 'icon-change'],
|
||||||
|
[Activity::TYPE_SHARE_CREATED, 'icon-add-color'],
|
||||||
|
[Activity::TYPE_SHARE_DELETED, 'icon-delete-color'],
|
||||||
|
[Activity::TYPE_SHARE_RESTORED, false],
|
||||||
|
[Activity::TYPE_FAVORITES, false],
|
||||||
|
['unknown type', false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider typeIconData
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param mixed $expected
|
||||||
|
*/
|
||||||
|
public function testTypeIcon($type, $expected) {
|
||||||
|
$this->assertSame($expected, $this->activityExtension->getTypeIcon($type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGroupParameter() {
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->activityExtension->getGroupParameter(['app' => 'files_sharing']),
|
||||||
|
'Asserting that no group parameters are set for files_sharing'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNavigation() {
|
||||||
|
$result = $this->activityExtension->getNavigation();
|
||||||
|
$this->assertCount(1, $result['top']);
|
||||||
|
$this->assertArrayHasKey(Activity::FILTER_FAVORITES, $result['top']);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['apps']);
|
||||||
|
$this->assertArrayHasKey(Activity::FILTER_FILES, $result['apps']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsFilterValid() {
|
||||||
|
$this->assertTrue($this->activityExtension->isFilterValid(Activity::FILTER_FAVORITES));
|
||||||
|
$this->assertTrue($this->activityExtension->isFilterValid(Activity::FILTER_FILES));
|
||||||
|
$this->assertFalse($this->activityExtension->isFilterValid('unknown filter'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filterNotificationTypesData() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
Activity::FILTER_FILES,
|
||||||
|
[
|
||||||
|
'NT0',
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_SHARE_CHANGED,
|
||||||
|
Activity::TYPE_SHARE_DELETED,
|
||||||
|
Activity::TYPE_SHARE_RESTORED,
|
||||||
|
Activity::TYPE_FAVORITES,
|
||||||
|
], [
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_SHARE_CHANGED,
|
||||||
|
Activity::TYPE_SHARE_DELETED,
|
||||||
|
Activity::TYPE_SHARE_RESTORED,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Activity::FILTER_FILES,
|
||||||
|
[
|
||||||
|
'NT0',
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_FAVORITES,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Activity::FILTER_FAVORITES,
|
||||||
|
[
|
||||||
|
'NT0',
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_SHARE_CHANGED,
|
||||||
|
Activity::TYPE_SHARE_DELETED,
|
||||||
|
Activity::TYPE_SHARE_RESTORED,
|
||||||
|
Activity::TYPE_FAVORITES,
|
||||||
|
], [
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_SHARE_CHANGED,
|
||||||
|
Activity::TYPE_SHARE_DELETED,
|
||||||
|
Activity::TYPE_SHARE_RESTORED,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'unknown filter',
|
||||||
|
[
|
||||||
|
'NT0',
|
||||||
|
Activity::TYPE_SHARE_CREATED,
|
||||||
|
Activity::TYPE_SHARE_CHANGED,
|
||||||
|
Activity::TYPE_SHARE_DELETED,
|
||||||
|
Activity::TYPE_SHARE_RESTORED,
|
||||||
|
Activity::TYPE_FAVORITES,
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider filterNotificationTypesData
|
||||||
|
*
|
||||||
|
* @param string $filter
|
||||||
|
* @param array $types
|
||||||
|
* @param mixed $expected
|
||||||
|
*/
|
||||||
|
public function testFilterNotificationTypes($filter, $types, $expected) {
|
||||||
|
$result = $this->activityExtension->filterNotificationTypes($types, $filter);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queryForFilterData() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
new \RuntimeException(),
|
||||||
|
'`app` = ?',
|
||||||
|
['files']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'items' => [],
|
||||||
|
'folders' => [],
|
||||||
|
],
|
||||||
|
' CASE WHEN `app` = ? THEN ((`type` <> ? AND `type` <> ?)) ELSE `app` <> ? END ',
|
||||||
|
['files', Activity::TYPE_SHARE_CREATED, Activity::TYPE_SHARE_CHANGED, 'files']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'items' => ['file.txt', 'folder'],
|
||||||
|
'folders' => ['folder'],
|
||||||
|
],
|
||||||
|
' CASE WHEN `app` = ? THEN ((`type` <> ? AND `type` <> ?) OR `file` = ? OR `file` = ? OR `file` LIKE ?) ELSE `app` <> ? END ',
|
||||||
|
['files', Activity::TYPE_SHARE_CREATED, Activity::TYPE_SHARE_CHANGED, 'file.txt', 'folder', 'folder/%', 'files']
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider queryForFilterData
|
||||||
|
*
|
||||||
|
* @param mixed $will
|
||||||
|
* @param string $query
|
||||||
|
* @param array $parameters
|
||||||
|
*/
|
||||||
|
public function testQueryForFilter($will, $query, $parameters) {
|
||||||
|
$this->mockUserSession('test');
|
||||||
|
|
||||||
|
$this->config->expects($this->any())
|
||||||
|
->method('getUserValue')
|
||||||
|
->willReturnMap([
|
||||||
|
['test', 'activity', 'notify_stream_' . Activity::TYPE_FAVORITES, false, true],
|
||||||
|
]);
|
||||||
|
if (is_array($will)) {
|
||||||
|
$this->activityHelper->expects($this->any())
|
||||||
|
->method('getFavoriteFilePaths')
|
||||||
|
->with('test')
|
||||||
|
->willReturn($will);
|
||||||
|
} else {
|
||||||
|
$this->activityHelper->expects($this->any())
|
||||||
|
->method('getFavoriteFilePaths')
|
||||||
|
->with('test')
|
||||||
|
->willThrowException($will);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->activityExtension->getQueryForFilter('all');
|
||||||
|
$this->assertEquals([$query, $parameters], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mockUserSession($user) {
|
||||||
|
$mockUser = $this->getMockBuilder('\OCP\IUser')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$mockUser->expects($this->any())
|
||||||
|
->method('getUID')
|
||||||
|
->willReturn($user);
|
||||||
|
|
||||||
|
$this->session->expects($this->any())
|
||||||
|
->method('isLoggedIn')
|
||||||
|
->willReturn(true);
|
||||||
|
$this->session->expects($this->any())
|
||||||
|
->method('getUser')
|
||||||
|
->willReturn($mockUser);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,8 +28,34 @@ namespace OC;
|
||||||
use OCP\Activity\IConsumer;
|
use OCP\Activity\IConsumer;
|
||||||
use OCP\Activity\IExtension;
|
use OCP\Activity\IExtension;
|
||||||
use OCP\Activity\IManager;
|
use OCP\Activity\IManager;
|
||||||
|
use OCP\IConfig;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
|
||||||
class ActivityManager implements IManager {
|
class ActivityManager implements IManager {
|
||||||
|
/** @var IRequest */
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/** @var IUserSession */
|
||||||
|
protected $session;
|
||||||
|
|
||||||
|
/** @var IConfig */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor of the controller
|
||||||
|
*
|
||||||
|
* @param IRequest $request
|
||||||
|
* @param IUserSession $session
|
||||||
|
* @param IConfig $config
|
||||||
|
*/
|
||||||
|
public function __construct(IRequest $request,
|
||||||
|
IUserSession $session,
|
||||||
|
IConfig $config) {
|
||||||
|
$this->request = $request;
|
||||||
|
$this->session = $session;
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Closure[]
|
* @var \Closure[]
|
||||||
|
@ -348,4 +374,43 @@ class ActivityManager implements IManager {
|
||||||
|
|
||||||
return array(' and ((' . implode(') or (', $conditions) . '))', $parameters);
|
return array(' and ((' . implode(') or (', $conditions) . '))', $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user we need to use
|
||||||
|
*
|
||||||
|
* Either the user is logged in, or we try to get it from the token
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
|
||||||
|
*/
|
||||||
|
public function getCurrentUserId() {
|
||||||
|
if (!$this->session->isLoggedIn()) {
|
||||||
|
return $this->getUserFromToken();
|
||||||
|
} else {
|
||||||
|
return $this->session->getUser()->getUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user for the token
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
|
||||||
|
*/
|
||||||
|
protected function getUserFromToken() {
|
||||||
|
$token = (string) $this->request->getParam('token', '');
|
||||||
|
if (strlen($token) !== 30) {
|
||||||
|
throw new \UnexpectedValueException('The token is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $this->config->getUsersForUserValue('activity', 'rsstoken', $token);
|
||||||
|
|
||||||
|
if (sizeof($users) !== 1) {
|
||||||
|
// No unique user found
|
||||||
|
throw new \UnexpectedValueException('The token is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token found login as that user
|
||||||
|
return array_shift($users);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,7 @@ class AllConfig implements \OCP\IConfig {
|
||||||
* @param string $userId the userId of the user that we want to store the value under
|
* @param string $userId the userId of the user that we want to store the value under
|
||||||
* @param string $appName the appName that we stored the value under
|
* @param string $appName the appName that we stored the value under
|
||||||
* @param string $key the key under which the value is being stored
|
* @param string $key the key under which the value is being stored
|
||||||
* @param string $default the default value to be returned if the value isn't set
|
* @param mixed $default the default value to be returned if the value isn't set
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getUserValue($userId, $appName, $key, $default = '') {
|
public function getUserValue($userId, $appName, $key, $default = '') {
|
||||||
|
|
|
@ -228,8 +228,12 @@ class Server extends SimpleContainer implements IServerContainer {
|
||||||
new ArrayCache()
|
new ArrayCache()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
$this->registerService('ActivityManager', function ($c) {
|
$this->registerService('ActivityManager', function (Server $c) {
|
||||||
return new ActivityManager();
|
return new ActivityManager(
|
||||||
|
$c->getRequest(),
|
||||||
|
$c->getUserSession(),
|
||||||
|
$c->getConfig()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
$this->registerService('AvatarManager', function ($c) {
|
$this->registerService('AvatarManager', function ($c) {
|
||||||
return new AvatarManager();
|
return new AvatarManager();
|
||||||
|
@ -435,7 +439,7 @@ class Server extends SimpleContainer implements IServerContainer {
|
||||||
* currently being processed is returned from this method.
|
* currently being processed is returned from this method.
|
||||||
* In case the current execution was not initiated by a web request null is returned
|
* In case the current execution was not initiated by a web request null is returned
|
||||||
*
|
*
|
||||||
* @return \OCP\IRequest|null
|
* @return \OCP\IRequest
|
||||||
*/
|
*/
|
||||||
function getRequest() {
|
function getRequest() {
|
||||||
return $this->query('Request');
|
return $this->query('Request');
|
||||||
|
|
|
@ -136,4 +136,14 @@ interface IManager {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function getQueryForFilter($filter);
|
function getQueryForFilter($filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user we need to use
|
||||||
|
*
|
||||||
|
* Either the user is logged in, or we try to get it from the token
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
|
||||||
|
*/
|
||||||
|
public function getCurrentUserId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ interface IConfig {
|
||||||
* @param string $userId the userId of the user that we want to store the value under
|
* @param string $userId the userId of the user that we want to store the value under
|
||||||
* @param string $appName the appName that we stored the value under
|
* @param string $appName the appName that we stored the value under
|
||||||
* @param string $key the key under which the value is being stored
|
* @param string $key the key under which the value is being stored
|
||||||
* @param string $default the default value to be returned if the value isn't set
|
* @param mixed $default the default value to be returned if the value isn't set
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getUserValue($userId, $appName, $key, $default = '');
|
public function getUserValue($userId, $appName, $key, $default = '');
|
||||||
|
|
|
@ -60,7 +60,7 @@ interface IServerContainer {
|
||||||
* is returned from this method.
|
* is returned from this method.
|
||||||
* In case the current execution was not initiated by a web request null is returned
|
* In case the current execution was not initiated by a web request null is returned
|
||||||
*
|
*
|
||||||
* @return \OCP\IRequest|null
|
* @return \OCP\IRequest
|
||||||
*/
|
*/
|
||||||
function getRequest();
|
function getRequest();
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,34 @@ class Test_ActivityManager extends \Test\TestCase {
|
||||||
/** @var \OC\ActivityManager */
|
/** @var \OC\ActivityManager */
|
||||||
private $activityManager;
|
private $activityManager;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $session;
|
||||||
|
|
||||||
|
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->activityManager = new \OC\ActivityManager();
|
$this->request = $this->getMockBuilder('OCP\IRequest')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->session = $this->getMockBuilder('OCP\IUserSession')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->config = $this->getMockBuilder('OCP\IConfig')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$this->activityManager = new \OC\ActivityManager(
|
||||||
|
$this->request,
|
||||||
|
$this->session,
|
||||||
|
$this->config
|
||||||
|
);
|
||||||
|
|
||||||
$this->activityManager->registerExtension(function() {
|
$this->activityManager->registerExtension(function() {
|
||||||
return new NoOpExtension();
|
return new NoOpExtension();
|
||||||
});
|
});
|
||||||
|
@ -111,6 +135,83 @@ class Test_ActivityManager extends \Test\TestCase {
|
||||||
$result = $this->activityManager->getQueryForFilter('InvalidFilter');
|
$result = $this->activityManager->getQueryForFilter('InvalidFilter');
|
||||||
$this->assertEquals(array(null, null), $result);
|
$this->assertEquals(array(null, null), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserFromTokenThrowInvalidTokenData() {
|
||||||
|
return [
|
||||||
|
[null, []],
|
||||||
|
['', []],
|
||||||
|
['12345678901234567890123456789', []],
|
||||||
|
['1234567890123456789012345678901', []],
|
||||||
|
['123456789012345678901234567890', []],
|
||||||
|
['123456789012345678901234567890', ['user1', 'user2']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \UnexpectedValueException
|
||||||
|
* @dataProvider getUserFromTokenThrowInvalidTokenData
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
* @param array $users
|
||||||
|
*/
|
||||||
|
public function testGetUserFromTokenThrowInvalidToken($token, $users) {
|
||||||
|
$this->mockRSSToken($token, $token, $users);
|
||||||
|
\Test_Helper::invokePrivate($this->activityManager, 'getUserFromToken');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserFromTokenData() {
|
||||||
|
return [
|
||||||
|
[null, '123456789012345678901234567890', 'user1'],
|
||||||
|
['user2', null, 'user2'],
|
||||||
|
['user2', '123456789012345678901234567890', 'user2'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getUserFromTokenData
|
||||||
|
*
|
||||||
|
* @param string $userLoggedIn
|
||||||
|
* @param string $token
|
||||||
|
* @param string $expected
|
||||||
|
*/
|
||||||
|
public function testGetUserFromToken($userLoggedIn, $token, $expected) {
|
||||||
|
if ($userLoggedIn !== null) {
|
||||||
|
$this->mockUserSession($userLoggedIn);
|
||||||
|
}
|
||||||
|
$this->mockRSSToken($token, '123456789012345678901234567890', ['user1']);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $this->activityManager->getCurrentUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mockRSSToken($requestToken, $userToken, $users) {
|
||||||
|
if ($requestToken !== null) {
|
||||||
|
$this->request->expects($this->any())
|
||||||
|
->method('getParam')
|
||||||
|
->with('token', '')
|
||||||
|
->willReturn($requestToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->config->expects($this->any())
|
||||||
|
->method('getUsersForUserValue')
|
||||||
|
->with('activity', 'rsstoken', $userToken)
|
||||||
|
->willReturn($users);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mockUserSession($user) {
|
||||||
|
$mockUser = $this->getMockBuilder('\OCP\IUser')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$mockUser->expects($this->any())
|
||||||
|
->method('getUID')
|
||||||
|
->willReturn($user);
|
||||||
|
|
||||||
|
$this->session->expects($this->any())
|
||||||
|
->method('isLoggedIn')
|
||||||
|
->willReturn(true);
|
||||||
|
$this->session->expects($this->any())
|
||||||
|
->method('getUser')
|
||||||
|
->willReturn($mockUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleExtension implements \OCP\Activity\IExtension {
|
class SimpleExtension implements \OCP\Activity\IExtension {
|
||||||
|
|
Loading…
Reference in a new issue