Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add an event merger and use it for the files activities
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Nov 25, 2016
commit da9468522b9e846d10b6e91ad10fa0c1b3b99546
17 changes: 16 additions & 1 deletion apps/files/lib/Activity/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
namespace OCA\Files\Activity;

use OCP\Activity\IEvent;
use OCP\Activity\IEventMerger;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\IL10N;
Expand All @@ -43,6 +44,9 @@ class Provider implements IProvider {
/** @var IUserManager */
protected $userManager;

/** @var IEventMerger */
protected $eventMerger;

/** @var string[] cached displayNames - key is the UID and value the displayname */
protected $displayNames = [];

Expand All @@ -51,12 +55,14 @@ class Provider implements IProvider {
* @param IURLGenerator $url
* @param IManager $activityManager
* @param IUserManager $userManager
* @param IEventMerger $eventMerger
*/
public function __construct(IL10N $l, IURLGenerator $url, IManager $activityManager, IUserManager $userManager) {
public function __construct(IL10N $l, IURLGenerator $url, IManager $activityManager, IUserManager $userManager, IEventMerger $eventMerger) {
$this->l = $l;
$this->url = $url;
$this->activityManager = $activityManager;
$this->userManager = $userManager;
$this->eventMerger = $eventMerger;
}

/**
Expand Down Expand Up @@ -115,6 +121,8 @@ public function parseShortVersion(IEvent $event, IEvent $previousEvent = null) {

$this->setSubjects($event, $subject, $parsedParameters);

$event = $this->eventMerger->mergeEvents('user', $event, $previousEvent);

return $event;
}

Expand Down Expand Up @@ -171,6 +179,13 @@ public function parseLongVersion(IEvent $event, IEvent $previousEvent = null) {

$this->setSubjects($event, $subject, $parsedParameters);

$event = $this->eventMerger->mergeEvents('file', $event, $previousEvent);

if ($event->getChildEvent() === null) {
// Couldn't group by file, maybe we can group by user
$event = $this->eventMerger->mergeEvents('user', $event, $previousEvent);
}

return $event;
}

Expand Down
256 changes: 256 additions & 0 deletions lib/private/Activity/EventMerger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <[email protected]>
*
* @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 OC\Activity;

use OCP\Activity\IEvent;
use OCP\Activity\IEventMerger;
use OCP\IL10N;

class EventMerger implements IEventMerger {

/** @var IL10N */
protected $l10n;

/**
* @param IL10N $l10n
*/
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}

/**
* Combines two events when possible to have grouping:
*
* Example1: Two events with subject '{user} created {file}' and
* $mergeParameter file with different file and same user will be merged
* to '{user} created {file1} and {file2}' and the childEvent on the return
* will be set, if the events have been merged.
*
* Example2: Two events with subject '{user} created {file}' and
* $mergeParameter file with same file and same user will be merged to
* '{user} created {file1}' and the childEvent on the return will be set, if
* the events have been merged.
*
* The following requirements have to be met, in order to be merged:
* - Both events need to have the same `getApp()`
* - Both events must not have a message `getMessage()`
* - Both events need to have the same subject `getSubject()`
* - Both events need to have the same object type `getObjectType()`
* - The time difference between both events must not be bigger then 3 hours
* - Only up to 5 events can be merged.
* - All parameters apart from such starting with $mergeParameter must be
* the same for both events.
*
* @param string $mergeParameter
* @param IEvent $event
* @param IEvent|null $previousEvent
* @return IEvent
*/
public function mergeEvents($mergeParameter, IEvent $event, IEvent $previousEvent = null) {
// No second event => can not combine
if (!$previousEvent instanceof IEvent) {
return $event;
}

// Different app => can not combine
if ($event->getApp() !== $previousEvent->getApp()) {
return $event;
}

// Message is set => can not combine
if ($event->getMessage() !== '' || $previousEvent->getMessage() !== '') {
return $event;
}

// Different subject => can not combine
if ($event->getSubject() !== $previousEvent->getSubject()) {
return $event;
}

// Different object type => can not combine
if ($event->getObjectType() !== $previousEvent->getObjectType()) {
return $event;
}

// More than 3 hours difference => can not combine
if (abs($event->getTimestamp() - $previousEvent->getTimestamp()) > 3 * 60 * 60) {
return $event;
}

// Other parameters are not the same => can not combine
try {
list($combined, $parameters) = $this->combineParameters($mergeParameter, $event, $previousEvent);
} catch (\UnexpectedValueException $e) {
return $event;
}

try {
$newSubject = $this->getExtendedSubject($event->getRichSubject(), $mergeParameter, $combined);
$parsedSubject = $this->generateParsedSubject($newSubject, $parameters);

$event->setRichSubject($newSubject, $parameters)
->setParsedSubject($parsedSubject)
->setChildEvent($previousEvent);
} catch (\UnexpectedValueException $e) {
return $event;
}

return $event;
}

/**
* @param string $mergeParameter
* @param IEvent $event
* @param IEvent $previousEvent
* @return array
* @throws \UnexpectedValueException
*/
protected function combineParameters($mergeParameter, IEvent $event, IEvent $previousEvent) {
$params1 = $event->getRichSubjectParameters();
$params2 = $previousEvent->getRichSubjectParameters();
$params = [];

$combined = 0;

// Check that all parameters from $event exist in $previousEvent
foreach ($params1 as $key => $parameter) {
if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) {
$combined++;
$params[$mergeParameter . $combined] = $parameter;
}
continue;
}

if (!isset($params2[$key]) || $params2[$key] !== $parameter) {
// Parameter missing on $previousEvent or different => can not combine
throw new \UnexpectedValueException();
}

$params[$key] = $parameter;
}

// Check that all parameters from $previousEvent exist in $event
foreach ($params2 as $key => $parameter) {
if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) {
$combined++;
$params[$mergeParameter . $combined] = $parameter;
}
continue;
}

if (!isset($params1[$key]) || $params1[$key] !== $parameter) {
// Parameter missing on $event or different => can not combine
throw new \UnexpectedValueException();
}

$params[$key] = $parameter;
}

return [$combined, $params];
}

/**
* @param array[] $parameters
* @param string $mergeParameter
* @param array $parameter
* @return bool
*/
protected function checkParameterAlreadyExits($parameters, $mergeParameter, $parameter) {
foreach ($parameters as $key => $param) {
if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
if ($param === $parameter) {
return true;
}
}
}
return false;
}

/**
* @param string $subject
* @param string $parameter
* @param int $counter
* @return mixed
*/
protected function getExtendedSubject($subject, $parameter, $counter) {
switch ($counter) {
case 1:
$replacement = '{' . $parameter . '1}';
break;
case 2:
$replacement = $this->l10n->t(
'%1$s and %2$s',
['{' . $parameter . '1}', '{' . $parameter . '2}']
);
break;
case 3:
$replacement = $this->l10n->t(
'%1$s, %2$s and %3$s',
['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}']
);
break;
case 4:
$replacement = $this->l10n->t(
'%1$s, %2$s, %3$s and %4$s',
['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}', '{' . $parameter . '4}']
);
break;
case 5:
$replacement = $this->l10n->t(
'%1$s, %2$s, %3$s, %4$s and %5$s',
['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}', '{' . $parameter . '4}', '{' . $parameter . '5}']
);
break;
default:
throw new \UnexpectedValueException();
}

return str_replace(
'{' . $parameter . '}',
$replacement,
$subject
);
}

/**
* @param string $subject
* @param array[] $parameters
* @return string
*/
protected function generateParsedSubject($subject, $parameters) {
$placeholders = $replacements = [];
foreach ($parameters as $placeholder => $parameter) {
$placeholders[] = '{' . $placeholder . '}';
if ($parameter['type'] === 'file') {
$replacements[] = trim($parameter['path'], '/');
} else if (isset($parameter['name'])) {
$replacements[] = $parameter['name'];
} else {
$replacements[] = $parameter['id'];
}
}

return str_replace($placeholders, $replacements, $subject);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public function __construct($appName, $urlParams = array()){
$this->registerService('OCP\\Activity\\IManager', function($c) {
return $this->getServer()->getActivityManager();
});
$this->registerService(\OCP\Activity\IEventMerger::class, function($c) {
return $this->getServer()->query(\OCP\Activity\IEventMerger::class);
});

$this->registerService('OCP\\ICache', function($c) {
return $this->getServer()->getCache();
Expand Down
5 changes: 5 additions & 0 deletions lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ public function __construct($webRoot, \OC\Config $config) {
$c->query(IValidator::class)
);
});
$this->registerService(\OCP\Activity\IEventMerger::class, function (Server $c) {
return new \OC\Activity\EventMerger(
$c->getL10N('lib')
);
});
$this->registerAlias(IValidator::class, Validator::class);
$this->registerService('AvatarManager', function (Server $c) {
return new AvatarManager(
Expand Down
Loading