From 90578327d57856e26f3a81813c5fdcb3db99617b Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 26 Sep 2016 13:50:39 +0200 Subject: [PATCH 01/13] Start working on calendar activities Signed-off-by: Joas Schilling --- apps/dav/lib/AppInfo/Application.php | 6 + apps/dav/lib/CalDAV/Activity.php | 250 ++++++++++++++++++++++++++ apps/dav/lib/CalDAV/CalDavBackend.php | 96 +++++++++- 3 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 apps/dav/lib/CalDAV/Activity.php diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 2bdcff9cf8..d073131f00 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -24,6 +24,7 @@ */ namespace OCA\DAV\AppInfo; +use OCA\DAV\CalDAV\Activity; use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\Capabilities; use OCA\DAV\CardDAV\ContactsManager; @@ -88,6 +89,11 @@ class Application extends App { ); } }); + + $aM = $this->getContainer()->getServer()->getActivityManager(); + $aM->registerExtension(function() { + return $this->getContainer()->query(Activity::class); + }); } public function getSyncService() { diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity.php new file mode 100644 index 0000000000..2f27a5bf07 --- /dev/null +++ b/apps/dav/lib/CalDAV/Activity.php @@ -0,0 +1,250 @@ + + * + * @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 . + * + */ + +namespace OCA\DAV\CalDAV; + +use OCP\Activity\IExtension; +use OCP\Activity\IManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\L10N\IFactory; + +class Activity implements IExtension { + const APP = 'dav'; + /** + * Filter with all sharing related activities + */ + const CALENDAR = 'calendar'; + + /** + * Activity types known to this extension + */ + const SUBJECT_ADD = 'calendar_add'; + const SUBJECT_UPDATE = 'calendar_update'; + const SUBJECT_DELETE = 'calendar_delete'; + + /** + * Subject keys for translation of the subjections + */ + + /** @var IFactory */ + protected $languageFactory; + + /** @var IURLGenerator */ + protected $URLGenerator; + + /** + * @param IFactory $languageFactory + * @param IURLGenerator $URLGenerator + */ + public function __construct(IFactory $languageFactory, IURLGenerator $URLGenerator) { + $this->languageFactory = $languageFactory; + $this->URLGenerator = $URLGenerator; + } + + protected function getL10N($languageCode = null) { + return $this->languageFactory->get(self::APP, $languageCode); + } + + /** + * The extension can return an array of additional notification types. + * If no additional types are to be added false is to be returned + * + * @param string $languageCode + * @return array|false + */ + public function getNotificationTypes($languageCode) { + $l = $this->getL10N($languageCode); + + return array( + self::CALENDAR => (string) $l->t('A calendar was modified'), + ); + } + + /** + * For a given method additional types to be displayed in the settings can be returned. + * In case no additional types are to be added false is to be returned. + * + * @param string $method + * @return array|false + */ + public function getDefaultTypes($method) { + $defaultTypes = []; + if ($method === self::METHOD_STREAM) { + $defaultTypes[] = self::CALENDAR; + } + + return $defaultTypes; + } + + /** + * A string naming the css class for the icon to be used can be returned. + * If no icon is known for the given type false is to be returned. + * + * @param string $type + * @return string|false + */ + public function getTypeIcon($type) { + switch ($type) { + case self::CALENDAR: + return 'icon-calendar-dark'; + } + + return false; + } + + /** + * The extension can translate a given message to the requested languages. + * If no translation is available false is to be returned. + * + * @param string $app + * @param string $text + * @param array $params + * @param boolean $stripPath + * @param boolean $highlightParams + * @param string $languageCode + * @return string|false + */ + public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { + if ($app !== self::APP) { + return false; + } + + $l = $this->getL10N($languageCode); + + switch ($text) { + case self::SUBJECT_ADD: + return (string) $l->t('%1$s created calendar %2$s', $params); + case self::SUBJECT_ADD . '_self': + return (string) $l->t('You created calendar %2$s', $params); + case self::SUBJECT_DELETE: + return (string) $l->t('%1$s deleted calendar %2$s', $params); + case self::SUBJECT_DELETE . '_self': + return (string) $l->t('You deleted calendar %2$s', $params); + case self::SUBJECT_UPDATE: + return (string) $l->t('%1$s updated calendar %2$s', $params); + case self::SUBJECT_UPDATE . '_self': + return (string) $l->t('You updated calendar %2$s', $params); + } + + return false; + } + + /** + * The extension can define the type of parameters for translation + * + * Currently known types are: + * * file => will strip away the path of the file and add a tooltip with it + * * username => will add the avatar of the user + * + * @param string $app + * @param string $text + * @return array|false + */ + public function getSpecialParameterList($app, $text) { + if ($app === self::APP) { + switch ($text) { + case self::SUBJECT_ADD: + case self::SUBJECT_DELETE: + case self::SUBJECT_UPDATE: + return [ + 0 => 'username', + //1 => 'calendar', + ]; + } + } + + return false; + } + + /** + * The extension can define the parameter grouping by returning the index as integer. + * In case no grouping is required false is to be returned. + * + * @param array $activity + * @return integer|false + */ + public function getGroupParameter($activity) { + return false; + } + + /** + * The extension can define additional navigation entries. The array returned has to contain two keys 'top' + * and 'apps' which hold arrays with the relevant entries. + * If no further entries are to be added false is no be returned. + * + * @return array|false + */ + public function getNavigation() { + $l = $this->getL10N(); + return [ + 'apps' => [ + self::CALENDAR => [ + 'id' => self::CALENDAR, + 'icon' => 'icon-calendar-dark', + 'name' => (string) $l->t('Calendar'), + 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::CALENDAR]), + ], + ], + 'top' => [], + ]; + } + + /** + * The extension can check if a custom filter (given by a query string like filter=abc) is valid or not. + * + * @param string $filterValue + * @return boolean + */ + public function isFilterValid($filterValue) { + return $filterValue === self::CALENDAR; + } + + /** + * The extension can filter the types based on the filter if required. + * In case no filter is to be applied false is to be returned unchanged. + * + * @param array $types + * @param string $filter + * @return array|false + */ + public function filterNotificationTypes($types, $filter) { + switch ($filter) { + case self::CALENDAR: + return array_intersect([self::CALENDAR], $types); + } + return false; + } + + /** + * For a given filter the extension can specify the sql query conditions including parameters for that query. + * In case the extension does not know the filter false is to be returned. + * The query condition and the parameters are to be returned as array with two elements. + * E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%')); + * + * @param string $filter + * @return array|false + */ + public function getQueryForFilter($filter) { + return false; + } + +} diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 07b61285ec..40488a2c75 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -31,6 +31,8 @@ use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; use OCP\IConfig; use OCP\IDBConnection; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; @@ -613,7 +615,11 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $query->setValue($column, $query->createNamedParameter($value)); } $query->execute(); - return $query->getLastInsertId(); + $calendarId = $query->getLastInsertId(); + + $this->triggerActivity(Activity::SUBJECT_ADD, $calendarId, $values); + + return $calendarId; } /** @@ -661,6 +667,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->addChange($calendarId, "", 2); + $this->triggerActivity(Activity::SUBJECT_UPDATE, $calendarId, $mutations); + return true; }); } @@ -672,6 +680,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @return void */ function deleteCalendar($calendarId) { + $this->triggerActivity(Activity::SUBJECT_DELETE, $calendarId); + $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?'); $stmt->execute([$calendarId]); @@ -1722,4 +1732,88 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription } return $principalUri; } + + protected function triggerActivity($action, $calendarId, array $changedProperties = []) { + $aM = \OC::$server->getActivityManager(); + $userSession = \OC::$server->getUserSession(); + + $properties = $this->getCalendarById($calendarId); + if (!isset($properties['principaluri'])) { + return; + } + + $principaluri = explode('/', $properties['principaluri']); + $owner = array_pop($principaluri); + + $currentUser = $userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $event = $aM->generateEvent(); + $event->setApp('dav') + ->setObject(Activity::CALENDAR, $calendarId) + ->setType(Activity::CALENDAR) + ->setAuthor($currentUser); + + $changedVisibleInformation = array_intersect([ + '{DAV:}displayname', + '{http://apple.com/ns/ical/}calendar-color' + ], array_keys($changedProperties)); + + if ($action === Activity::SUBJECT_UPDATE && empty($changedVisibleInformation)) { + $users = [$owner]; + } else { + $users = $this->getUsersForCalendar($calendarId); + $users[] = $owner; + } + + foreach ($users as $user) { + $event->setAffectedUser($user) + ->setSubject( + $user === $currentUser ? $action . '_self' : $action, + [ + $currentUser, + $properties['{DAV:}displayname'], + ] + ); + $aM->publish($event); + } + } + + /** + * Get all users that have access to a given calendar + * + * @param int $calendarId + * @return string[] + */ + protected function getUsersForCalendar($calendarId) { + $gM = \OC::$server->getGroupManager(); + + $users = $groups = []; + $shares = $this->getShares($calendarId); + foreach ($shares as $share) { + $prinical = explode('/', $share['{http://owncloud.org/ns}principal']); + if ($prinical[1] === 'users') { + $users[] = $prinical[2]; + } else if ($prinical[1] === 'groups') { + $groups[] = $prinical[2]; + } + } + + if (!empty($groups)) { + foreach ($groups as $gid) { + $group = $gM->get($gid); + if ($group instanceof IGroup) { + foreach ($group->getUsers() as $user) { + $users[] = $user->getUID(); + } + } + } + } + + return $users; + } } From 83d51afab1eabfdad50c6408aa6a030d8727227c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 7 Oct 2016 12:29:22 +0200 Subject: [PATCH 02/13] Unshare user activities Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity.php | 12 ++++- apps/dav/lib/CalDAV/CalDavBackend.php | 72 ++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity.php index 2f27a5bf07..fbdc7b054d 100644 --- a/apps/dav/lib/CalDAV/Activity.php +++ b/apps/dav/lib/CalDAV/Activity.php @@ -22,8 +22,6 @@ namespace OCA\DAV\CalDAV; use OCP\Activity\IExtension; -use OCP\Activity\IManager; -use OCP\IL10N; use OCP\IURLGenerator; use OCP\L10N\IFactory; @@ -40,6 +38,7 @@ class Activity implements IExtension { const SUBJECT_ADD = 'calendar_add'; const SUBJECT_UPDATE = 'calendar_update'; const SUBJECT_DELETE = 'calendar_delete'; + const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; /** * Subject keys for translation of the subjections @@ -143,6 +142,12 @@ class Activity implements IExtension { return (string) $l->t('%1$s updated calendar %2$s', $params); case self::SUBJECT_UPDATE . '_self': return (string) $l->t('You updated calendar %2$s', $params); + case self::SUBJECT_UNSHARE_USER: + return (string) $l->t('%1$s unshared calendar %2$s from you', $params); + case self::SUBJECT_UNSHARE_USER . '_you': + return (string) $l->t('You unshared calendar %2$s from %1$s', $params); + case self::SUBJECT_UNSHARE_USER . '_by': + return (string) $l->t('%1$s unshared calendar %2$s from themselves', $params); } return false; @@ -165,6 +170,9 @@ class Activity implements IExtension { case self::SUBJECT_ADD: case self::SUBJECT_DELETE: case self::SUBJECT_UPDATE: + case self::SUBJECT_UNSHARE_USER: + case self::SUBJECT_UNSHARE_USER . '_you': + case self::SUBJECT_UNSHARE_USER . '_by': return [ 0 => 'username', //1 => 'calendar', diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 40488a2c75..25cb3ce484 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -26,6 +26,7 @@ namespace OCA\DAV\CalDAV; use OCA\DAV\DAV\Sharing\IShareable; +use OCP\Activity\IEvent; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; @@ -1658,6 +1659,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param array $remove */ public function updateShares($shareable, $add, $remove) { + /** @var Calendar $shareable */ + $this->triggerActivitySharing($shareable, $add, $remove); + $this->sharingBackend->updateShares($shareable, $add, $remove); } @@ -1742,8 +1746,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription return; } - $principaluri = explode('/', $properties['principaluri']); - $owner = array_pop($principaluri); + $principal = explode('/', $properties['principaluri']); + $owner = $principal[2]; $currentUser = $userSession->getUser(); if ($currentUser instanceof IUser) { @@ -1783,6 +1787,70 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription } } + protected function triggerActivitySharing(Calendar $calendar, array $add, array $remove) { + $aM = \OC::$server->getActivityManager(); + $userSession = \OC::$server->getUserSession(); + + $calendarId = $calendar->getResourceId(); + //$shares = $this->sharingBackend->getShares($calendarId); + + $properties = $this->getCalendarById($calendarId); + $principal = explode('/', $properties['principaluri']); + $owner = $principal[2]; + + $currentUser = $userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $event = $aM->generateEvent(); + $event->setApp('dav') + ->setObject(Activity::CALENDAR, $calendarId) + ->setType(Activity::CALENDAR) + ->setAuthor($currentUser); + + foreach ($remove as $principal) { + // principal:principals/users/test + $parts = explode(':', $principal, 2); + if ($parts[0] !== 'principal') { + continue; + } + $principal = explode('/', $parts[1]); + + if ($principal[1] === 'users') { + $this->triggerActivityUnshareUser($principal[2], $event, $properties); + + if ($owner !== $principal[2]) { + $event->setAffectedUser($owner) + ->setSubject( + $owner === $event->getAuthor() ? Activity::SUBJECT_UNSHARE_USER . '_you' : Activity::SUBJECT_UNSHARE_USER . '_by', + [ + $principal[2], + $properties['{DAV:}displayname'], + ] + ); + $aM->publish($event); + } + } + } + } + + protected function triggerActivityUnshareUser($user, IEvent $event, array $properties) { + $aM = \OC::$server->getActivityManager(); + + $event->setAffectedUser($user) + ->setSubject( + $user === $event->getAuthor() ? Activity::SUBJECT_DELETE . '_self' : Activity::SUBJECT_UNSHARE_USER, + [ + $event->getAuthor(), + $properties['{DAV:}displayname'], + ] + ); + $aM->publish($event); + } + /** * Get all users that have access to a given calendar * From 9a7c522cc6c6b371f3e51ce92910740ceaa2c633 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 7 Oct 2016 13:44:02 +0200 Subject: [PATCH 03/13] Add activity for unshare from group Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity.php | 11 +++++++++++ apps/dav/lib/CalDAV/CalDavBackend.php | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity.php index fbdc7b054d..10945f887b 100644 --- a/apps/dav/lib/CalDAV/Activity.php +++ b/apps/dav/lib/CalDAV/Activity.php @@ -39,6 +39,7 @@ class Activity implements IExtension { const SUBJECT_UPDATE = 'calendar_update'; const SUBJECT_DELETE = 'calendar_delete'; const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; + const SUBJECT_UNSHARE_GROUP = 'calendar_group_unshare'; /** * Subject keys for translation of the subjections @@ -148,6 +149,8 @@ class Activity implements IExtension { return (string) $l->t('You unshared calendar %2$s from %1$s', $params); case self::SUBJECT_UNSHARE_USER . '_by': return (string) $l->t('%1$s unshared calendar %2$s from themselves', $params); + case self::SUBJECT_UNSHARE_GROUP . '_you': + return (string) $l->t('You unshared calendar %2$s from group %1$s', $params); } return false; @@ -168,8 +171,11 @@ class Activity implements IExtension { if ($app === self::APP) { switch ($text) { case self::SUBJECT_ADD: + case self::SUBJECT_ADD . '_self': case self::SUBJECT_DELETE: + case self::SUBJECT_DELETE . '_self': case self::SUBJECT_UPDATE: + case self::SUBJECT_UPDATE . '_self': case self::SUBJECT_UNSHARE_USER: case self::SUBJECT_UNSHARE_USER . '_you': case self::SUBJECT_UNSHARE_USER . '_by': @@ -177,6 +183,11 @@ class Activity implements IExtension { 0 => 'username', //1 => 'calendar', ]; + case self::SUBJECT_UNSHARE_GROUP . '_you': + return [ + //0 => 'group', + //1 => 'calendar', + ]; } } diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 25cb3ce484..a707b2c5af 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -1833,6 +1833,32 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ); $aM->publish($event); } + } else if ($principal[1] === 'groups') { + $this->triggerActivityUnshareGroup($principal[2], $event, $properties); + + $event->setAffectedUser($currentUser) + ->setSubject( + Activity::SUBJECT_UNSHARE_GROUP . '_you', + [ + $principal[2], + $properties['{DAV:}displayname'], + ] + ); + $aM->publish($event); + } + } + } + + protected function triggerActivityUnshareGroup($gid, IEvent $event, array $properties) { + $gM = \OC::$server->getGroupManager(); + + $group = $gM->get($gid); + if ($group instanceof IGroup) { + foreach ($group->getUsers() as $user) { + // Exclude current user + if ($user !== $event->getAuthor()) { + $this->triggerActivityUnshareUser($user->getUID(), $event, $properties); + } } } } From 35ce4c772c38a5a10ac18440734f0d38b96f73ae Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 09:19:14 +0200 Subject: [PATCH 04/13] Fix unshare actions for author != owner Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity.php | 16 ++++++++ apps/dav/lib/CalDAV/CalDavBackend.php | 54 +++++++++++++++++++-------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity.php index 10945f887b..d42e9704a8 100644 --- a/apps/dav/lib/CalDAV/Activity.php +++ b/apps/dav/lib/CalDAV/Activity.php @@ -148,9 +148,13 @@ class Activity implements IExtension { case self::SUBJECT_UNSHARE_USER . '_you': return (string) $l->t('You unshared calendar %2$s from %1$s', $params); case self::SUBJECT_UNSHARE_USER . '_by': + return (string) $l->t('%3$s unshared calendar %2$s from %1$s', $params); + case self::SUBJECT_UNSHARE_USER . '_self': return (string) $l->t('%1$s unshared calendar %2$s from themselves', $params); case self::SUBJECT_UNSHARE_GROUP . '_you': return (string) $l->t('You unshared calendar %2$s from group %1$s', $params); + case self::SUBJECT_UNSHARE_GROUP . '_by': + return (string) $l->t('%3$s unshared calendar %2$s from group %1$s', $params); } return false; @@ -178,16 +182,28 @@ class Activity implements IExtension { case self::SUBJECT_UPDATE . '_self': case self::SUBJECT_UNSHARE_USER: case self::SUBJECT_UNSHARE_USER . '_you': + case self::SUBJECT_UNSHARE_USER . '_self': + return [ + 0 => 'username', + //1 => 'calendar', + ]; case self::SUBJECT_UNSHARE_USER . '_by': return [ 0 => 'username', //1 => 'calendar', + 2 => 'username', ]; case self::SUBJECT_UNSHARE_GROUP . '_you': return [ //0 => 'group', //1 => 'calendar', ]; + case self::SUBJECT_UNSHARE_GROUP . '_by': + return [ + //0 => 'group', + //1 => 'calendar', + 2 => 'username', + ]; } } diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index a707b2c5af..3e1f720cd0 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -1823,27 +1823,49 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->triggerActivityUnshareUser($principal[2], $event, $properties); if ($owner !== $principal[2]) { + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Activity::SUBJECT_UNSHARE_USER . '_you'; + } else if ($principal[2] === $event->getAuthor()) { + $subject = Activity::SUBJECT_UNSHARE_USER . '_self'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Activity::SUBJECT_UNSHARE_USER . '_you', $parameters); + $aM->publish($event); + + $subject = Activity::SUBJECT_UNSHARE_USER . '_by'; + $parameters[] = $event->getAuthor(); + } + $event->setAffectedUser($owner) - ->setSubject( - $owner === $event->getAuthor() ? Activity::SUBJECT_UNSHARE_USER . '_you' : Activity::SUBJECT_UNSHARE_USER . '_by', - [ - $principal[2], - $properties['{DAV:}displayname'], - ] - ); + ->setSubject($subject, $parameters); $aM->publish($event); } } else if ($principal[1] === 'groups') { $this->triggerActivityUnshareGroup($principal[2], $event, $properties); - $event->setAffectedUser($currentUser) - ->setSubject( - Activity::SUBJECT_UNSHARE_GROUP . '_you', - [ - $principal[2], - $properties['{DAV:}displayname'], - ] - ); + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Activity::SUBJECT_UNSHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Activity::SUBJECT_UNSHARE_GROUP . '_you', $parameters); + $aM->publish($event); + + $subject = Activity::SUBJECT_UNSHARE_GROUP . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); $aM->publish($event); } } @@ -1856,7 +1878,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription if ($group instanceof IGroup) { foreach ($group->getUsers() as $user) { // Exclude current user - if ($user !== $event->getAuthor()) { + if ($user->getUID() !== $event->getAuthor()) { $this->triggerActivityUnshareUser($user->getUID(), $event, $properties); } } From 776622f3de8fab30add0c83bf0331c57615467df Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 09:44:16 +0200 Subject: [PATCH 05/13] Add activities for shares Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity.php | 17 +++++ apps/dav/lib/CalDAV/CalDavBackend.php | 90 ++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity.php index d42e9704a8..b551a5f930 100644 --- a/apps/dav/lib/CalDAV/Activity.php +++ b/apps/dav/lib/CalDAV/Activity.php @@ -38,6 +38,8 @@ class Activity implements IExtension { const SUBJECT_ADD = 'calendar_add'; const SUBJECT_UPDATE = 'calendar_update'; const SUBJECT_DELETE = 'calendar_delete'; + const SUBJECT_SHARE_USER = 'calendar_user_share'; + const SUBJECT_SHARE_GROUP = 'calendar_group_share'; const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; const SUBJECT_UNSHARE_GROUP = 'calendar_group_unshare'; @@ -143,6 +145,12 @@ class Activity implements IExtension { return (string) $l->t('%1$s updated calendar %2$s', $params); case self::SUBJECT_UPDATE . '_self': return (string) $l->t('You updated calendar %2$s', $params); + case self::SUBJECT_SHARE_USER: + return (string) $l->t('%1$s shared calendar %2$s with you', $params); + case self::SUBJECT_SHARE_USER . '_you': + return (string) $l->t('You shared calendar %2$s with %1$s', $params); + case self::SUBJECT_SHARE_USER . '_by': + return (string) $l->t('%3$s shared calendar %2$s with %1$s', $params); case self::SUBJECT_UNSHARE_USER: return (string) $l->t('%1$s unshared calendar %2$s from you', $params); case self::SUBJECT_UNSHARE_USER . '_you': @@ -151,6 +159,10 @@ class Activity implements IExtension { return (string) $l->t('%3$s unshared calendar %2$s from %1$s', $params); case self::SUBJECT_UNSHARE_USER . '_self': return (string) $l->t('%1$s unshared calendar %2$s from themselves', $params); + case self::SUBJECT_SHARE_GROUP . '_you': + return (string) $l->t('You shared calendar %2$s with group %1$s', $params); + case self::SUBJECT_SHARE_GROUP . '_by': + return (string) $l->t('%3$s shared calendar %2$s with group %1$s', $params); case self::SUBJECT_UNSHARE_GROUP . '_you': return (string) $l->t('You unshared calendar %2$s from group %1$s', $params); case self::SUBJECT_UNSHARE_GROUP . '_by': @@ -180,6 +192,8 @@ class Activity implements IExtension { case self::SUBJECT_DELETE . '_self': case self::SUBJECT_UPDATE: case self::SUBJECT_UPDATE . '_self': + case self::SUBJECT_SHARE_USER: + case self::SUBJECT_SHARE_USER . '_you': case self::SUBJECT_UNSHARE_USER: case self::SUBJECT_UNSHARE_USER . '_you': case self::SUBJECT_UNSHARE_USER . '_self': @@ -187,17 +201,20 @@ class Activity implements IExtension { 0 => 'username', //1 => 'calendar', ]; + case self::SUBJECT_SHARE_USER . '_by': case self::SUBJECT_UNSHARE_USER . '_by': return [ 0 => 'username', //1 => 'calendar', 2 => 'username', ]; + case self::SUBJECT_SHARE_GROUP . '_you': case self::SUBJECT_UNSHARE_GROUP . '_you': return [ //0 => 'group', //1 => 'calendar', ]; + case self::SUBJECT_SHARE_GROUP . '_by': case self::SUBJECT_UNSHARE_GROUP . '_by': return [ //0 => 'group', diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 3e1f720cd0..2d2a5062a0 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -1792,7 +1792,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $userSession = \OC::$server->getUserSession(); $calendarId = $calendar->getResourceId(); - //$shares = $this->sharingBackend->getShares($calendarId); + $shares = $this->sharingBackend->getShares($calendarId); $properties = $this->getCalendarById($calendarId); $principal = explode('/', $properties['principaluri']); @@ -1820,7 +1820,13 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $principal = explode('/', $parts[1]); if ($principal[1] === 'users') { - $this->triggerActivityUnshareUser($principal[2], $event, $properties); + $this->triggerActivityUnshareUser( + $principal[2], + $event, + $properties, + Activity::SUBJECT_UNSHARE_USER, + Activity::SUBJECT_DELETE . '_self' + ); if ($owner !== $principal[2]) { $parameters = [ @@ -1846,7 +1852,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $aM->publish($event); } } else if ($principal[1] === 'groups') { - $this->triggerActivityUnshareGroup($principal[2], $event, $properties); + $this->triggerActivityUnshareGroup($principal[2], $event, $properties, Activity::SUBJECT_UNSHARE_USER); $parameters = [ $principal[2], @@ -1869,9 +1875,79 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $aM->publish($event); } } + + foreach ($add as $share) { + if ($this->isAlreadyShared($share['href'], $shares)) { + continue; + } + + // principal:principals/users/test + $parts = explode(':', $share['href'], 2); + if ($parts[0] !== 'principal') { + continue; + } + $principal = explode('/', $parts[1]); + + if ($principal[1] === 'users') { + $this->triggerActivityUnshareUser($principal[2], $event, $properties, Activity::SUBJECT_SHARE_USER); + + if ($owner !== $principal[2]) { + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Activity::SUBJECT_SHARE_USER . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Activity::SUBJECT_SHARE_USER . '_you', $parameters); + $aM->publish($event); + + $subject = Activity::SUBJECT_SHARE_USER . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $aM->publish($event); + } + } else if ($principal[1] === 'groups') { + $this->triggerActivityUnshareGroup($principal[2], $event, $properties, Activity::SUBJECT_SHARE_USER); + + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Activity::SUBJECT_SHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Activity::SUBJECT_SHARE_GROUP . '_you', $parameters); + $aM->publish($event); + + $subject = Activity::SUBJECT_SHARE_GROUP . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $aM->publish($event); + } + } } - protected function triggerActivityUnshareGroup($gid, IEvent $event, array $properties) { + protected function isAlreadyShared($principal, $shares) { + foreach ($shares as $share) { + if ($principal === $share['href']) { + return true; + } + } + return false; + } + + protected function triggerActivityUnshareGroup($gid, IEvent $event, array $properties, $subject) { $gM = \OC::$server->getGroupManager(); $group = $gM->get($gid); @@ -1879,18 +1955,18 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription foreach ($group->getUsers() as $user) { // Exclude current user if ($user->getUID() !== $event->getAuthor()) { - $this->triggerActivityUnshareUser($user->getUID(), $event, $properties); + $this->triggerActivityUnshareUser($user->getUID(), $event, $properties, $subject); } } } } - protected function triggerActivityUnshareUser($user, IEvent $event, array $properties) { + protected function triggerActivityUnshareUser($user, IEvent $event, array $properties, $subject, $subjectSelf = '') { $aM = \OC::$server->getActivityManager(); $event->setAffectedUser($user) ->setSubject( - $user === $event->getAuthor() ? Activity::SUBJECT_DELETE . '_self' : Activity::SUBJECT_UNSHARE_USER, + $user === $event->getAuthor() && $subjectSelf ? $subjectSelf : $subject, [ $event->getAuthor(), $properties['{DAV:}displayname'], From 07f6747305d23dfb16ad3c63b968f974771aa145 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 11:04:22 +0200 Subject: [PATCH 06/13] Move the methods into a new class Signed-off-by: Joas Schilling --- apps/dav/appinfo/v1/caldav.php | 6 +- apps/dav/lib/AppInfo/Application.php | 4 +- apps/dav/lib/CalDAV/Activity/Backend.php | 396 ++++++++++++++++++ .../{Activity.php => Activity/Extension.php} | 11 +- apps/dav/lib/CalDAV/CalDavBackend.php | 306 +------------- apps/dav/lib/Command/CreateCalendar.php | 5 +- apps/dav/lib/RootCollection.php | 5 +- .../unit/CalDAV/AbstractCalDavBackendTest.php | 40 +- .../tests/unit/CalDAV/CalDavBackendTest.php | 4 +- .../unit/CalDAV/PublicCalendarRootTest.php | 57 ++- 10 files changed, 497 insertions(+), 337 deletions(-) create mode 100644 apps/dav/lib/CalDAV/Activity/Backend.php rename apps/dav/lib/CalDAV/{Activity.php => Activity/Extension.php} (98%) diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php index ba49f2961f..ecd6a17123 100644 --- a/apps/dav/appinfo/v1/caldav.php +++ b/apps/dav/appinfo/v1/caldav.php @@ -46,10 +46,12 @@ $principalBackend = new Principal( 'principals/' ); $db = \OC::$server->getDatabaseConnection(); -$config = \OC::$server->getConfig(); $userManager = \OC::$server->getUserManager(); $random = \OC::$server->getSecureRandom(); -$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, $config, $random); +$groupManager = \OC::$server->getGroupManager(); +$activityManager = \OC::$server->getActivityManager(); +$userSession = \OC::$server->getUserSession(); +$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, $groupManager, $random, $activityManager, $userSession); $debugging = \OC::$server->getConfig()->getSystemValue('debug', false); diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index d073131f00..32483e8fd8 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -24,7 +24,7 @@ */ namespace OCA\DAV\AppInfo; -use OCA\DAV\CalDAV\Activity; +use OCA\DAV\CalDAV\Activity\Extension; use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\Capabilities; use OCA\DAV\CardDAV\ContactsManager; @@ -92,7 +92,7 @@ class Application extends App { $aM = $this->getContainer()->getServer()->getActivityManager(); $aM->registerExtension(function() { - return $this->getContainer()->query(Activity::class); + return $this->getContainer()->query(Extension::class); }); } diff --git a/apps/dav/lib/CalDAV/Activity/Backend.php b/apps/dav/lib/CalDAV/Activity/Backend.php new file mode 100644 index 0000000000..9d931e0f55 --- /dev/null +++ b/apps/dav/lib/CalDAV/Activity/Backend.php @@ -0,0 +1,396 @@ + + * + * @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 . + * + */ + +namespace OCA\DAV\CalDAV\Activity; + + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; +use OCP\Activity\IEvent; +use OCP\Activity\IManager as IActivityManager; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; + +/** + * Class Backend + * + * @package OCA\DAV\CalDAV\Activity + */ +class Backend { + + /** @var CalDavBackend */ + protected $calDavBackend; + + /** @var IActivityManager */ + protected $activityManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserSession */ + protected $userSession; + + /** + * @param CalDavBackend $calDavBackend + * @param IActivityManager $activityManager + * @param IGroupManager $groupManager + * @param IUserSession $userSession + */ + public function __construct(CalDavBackend $calDavBackend, IActivityManager $activityManager, IGroupManager $groupManager, IUserSession $userSession) { + $this->calDavBackend = $calDavBackend; + $this->activityManager = $activityManager; + $this->groupManager = $groupManager; + $this->userSession = $userSession; + } + + /** + * Creates activities when a calendar was creates + * + * @param int $calendarId + * @param array $properties + */ + public function addCalendar($calendarId, array $properties) { + $this->triggerActivity(Extension::SUBJECT_ADD, $calendarId, $properties); + } + + /** + * Creates activities when a calendar was updated + * + * @param int $calendarId + * @param array $properties + */ + public function updateCalendar($calendarId, array $properties) { + $this->triggerActivity(Extension::SUBJECT_UPDATE, $calendarId, $properties); + } + + /** + * Creates activities when a calendar was deleted + * + * @param int $calendarId + */ + public function deleteCalendar($calendarId) { + $this->triggerActivity(Extension::SUBJECT_DELETE, $calendarId); + } + + + /** + * Creates activities for all related users when a calendar was touched + * + * @param string $action + * @param int $calendarId + * @param array $changedProperties + */ + protected function triggerActivity($action, $calendarId, array $changedProperties = []) { + $properties = $this->calDavBackend->getCalendarById($calendarId); + if (!isset($properties['principaluri'])) { + return; + } + + $principal = explode('/', $properties['principaluri']); + $owner = $principal[2]; + + $currentUser = $this->userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $event = $this->activityManager->generateEvent(); + $event->setApp('dav') + ->setObject(Extension::CALENDAR, $calendarId) + ->setType(Extension::CALENDAR) + ->setAuthor($currentUser); + + $changedVisibleInformation = array_intersect([ + '{DAV:}displayname', + '{http://apple.com/ns/ical/}calendar-color' + ], array_keys($changedProperties)); + + if ($action === Extension::SUBJECT_UPDATE && empty($changedVisibleInformation)) { + $users = [$owner]; + } else { + $users = $this->getUsersForCalendar($calendarId); + $users[] = $owner; + } + + foreach ($users as $user) { + $event->setAffectedUser($user) + ->setSubject( + $user === $currentUser ? $action . '_self' : $action, + [ + $currentUser, + $properties['{DAV:}displayname'], + ] + ); + $this->activityManager->publish($event); + } + } + + /** + * Creates activities for all related users when a calendar was (un-)shared + * + * @param Calendar $calendar + * @param array $add + * @param array $remove + */ + public function updateCalendarShares(Calendar $calendar, array $add, array $remove) { + $calendarId = $calendar->getResourceId(); + $shares = $this->calDavBackend->getShares($calendarId); + + $properties = $this->calDavBackend->getCalendarById($calendarId); + $principal = explode('/', $properties['principaluri']); + $owner = $principal[2]; + + $currentUser = $this->userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $event = $this->activityManager->generateEvent(); + $event->setApp('dav') + ->setObject(Extension::CALENDAR, $calendarId) + ->setType(Extension::CALENDAR) + ->setAuthor($currentUser); + + foreach ($remove as $principal) { + // principal:principals/users/test + $parts = explode(':', $principal, 2); + if ($parts[0] !== 'principal') { + continue; + } + $principal = explode('/', $parts[1]); + + if ($principal[1] === 'users') { + $this->triggerActivityUser( + $principal[2], + $event, + $properties, + Extension::SUBJECT_UNSHARE_USER, + Extension::SUBJECT_DELETE . '_self' + ); + + if ($owner !== $principal[2]) { + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Extension::SUBJECT_UNSHARE_USER . '_you'; + } else if ($principal[2] === $event->getAuthor()) { + $subject = Extension::SUBJECT_UNSHARE_USER . '_self'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Extension::SUBJECT_UNSHARE_USER . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Extension::SUBJECT_UNSHARE_USER . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } else if ($principal[1] === 'groups') { + $this->triggerActivityGroup($principal[2], $event, $properties, Extension::SUBJECT_UNSHARE_USER); + + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Extension::SUBJECT_UNSHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Extension::SUBJECT_UNSHARE_GROUP . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Extension::SUBJECT_UNSHARE_GROUP . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } + + foreach ($add as $share) { + if ($this->isAlreadyShared($share['href'], $shares)) { + continue; + } + + // principal:principals/users/test + $parts = explode(':', $share['href'], 2); + if ($parts[0] !== 'principal') { + continue; + } + $principal = explode('/', $parts[1]); + + if ($principal[1] === 'users') { + $this->triggerActivityUser($principal[2], $event, $properties, Extension::SUBJECT_SHARE_USER); + + if ($owner !== $principal[2]) { + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Extension::SUBJECT_SHARE_USER . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Extension::SUBJECT_SHARE_USER . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Extension::SUBJECT_SHARE_USER . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } else if ($principal[1] === 'groups') { + $this->triggerActivityGroup($principal[2], $event, $properties, Extension::SUBJECT_SHARE_USER); + + $parameters = [ + $principal[2], + $properties['{DAV:}displayname'], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Extension::SUBJECT_SHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Extension::SUBJECT_SHARE_GROUP . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Extension::SUBJECT_SHARE_GROUP . '_by'; + $parameters[] = $event->getAuthor(); + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } + } + + /** + * Checks if a calendar is already shared with a principal + * + * @param string $principal + * @param array[] $shares + * @return bool + */ + protected function isAlreadyShared($principal, $shares) { + foreach ($shares as $share) { + if ($principal === $share['href']) { + return true; + } + } + + return false; + } + + /** + * Creates the given activity for all members of the given group + * + * @param string $gid + * @param IEvent $event + * @param array $properties + * @param string $subject + */ + protected function triggerActivityGroup($gid, IEvent $event, array $properties, $subject) { + $group = $this->groupManager->get($gid); + + if ($group instanceof IGroup) { + foreach ($group->getUsers() as $user) { + // Exclude current user + if ($user->getUID() !== $event->getAuthor()) { + $this->triggerActivityUser($user->getUID(), $event, $properties, $subject); + } + } + } + } + + /** + * Creates the given activity for the given user + * + * @param string $user + * @param IEvent $event + * @param array $properties + * @param string $subject + * @param string $subjectSelf + */ + protected function triggerActivityUser($user, IEvent $event, array $properties, $subject, $subjectSelf = '') { + $event->setAffectedUser($user) + ->setSubject( + $user === $event->getAuthor() && $subjectSelf ? $subjectSelf : $subject, + [ + $event->getAuthor(), + $properties['{DAV:}displayname'], + ] + ); + + $this->activityManager->publish($event); + } + + /** + * Get all users that have access to a given calendar + * + * @param int $calendarId + * @return string[] + */ + protected function getUsersForCalendar($calendarId) { + $users = $groups = []; + $shares = $this->calDavBackend->getShares($calendarId); + foreach ($shares as $share) { + $prinical = explode('/', $share['{http://owncloud.org/ns}principal']); + if ($prinical[1] === 'users') { + $users[] = $prinical[2]; + } else if ($prinical[1] === 'groups') { + $groups[] = $prinical[2]; + } + } + + if (!empty($groups)) { + foreach ($groups as $gid) { + $group = $this->groupManager->get($gid); + if ($group instanceof IGroup) { + foreach ($group->getUsers() as $user) { + $users[] = $user->getUID(); + } + } + } + } + + return $users; + } +} diff --git a/apps/dav/lib/CalDAV/Activity.php b/apps/dav/lib/CalDAV/Activity/Extension.php similarity index 98% rename from apps/dav/lib/CalDAV/Activity.php rename to apps/dav/lib/CalDAV/Activity/Extension.php index b551a5f930..9100bb7e18 100644 --- a/apps/dav/lib/CalDAV/Activity.php +++ b/apps/dav/lib/CalDAV/Activity/Extension.php @@ -19,22 +19,19 @@ * */ -namespace OCA\DAV\CalDAV; +namespace OCA\DAV\CalDAV\Activity; use OCP\Activity\IExtension; use OCP\IURLGenerator; use OCP\L10N\IFactory; -class Activity implements IExtension { +class Extension implements IExtension { const APP = 'dav'; /** * Filter with all sharing related activities */ const CALENDAR = 'calendar'; - /** - * Activity types known to this extension - */ const SUBJECT_ADD = 'calendar_add'; const SUBJECT_UPDATE = 'calendar_update'; const SUBJECT_DELETE = 'calendar_delete'; @@ -43,10 +40,6 @@ class Activity implements IExtension { const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; const SUBJECT_UNSHARE_GROUP = 'calendar_group_unshare'; - /** - * Subject keys for translation of the subjections - */ - /** @var IFactory */ protected $languageFactory; diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 2d2a5062a0..fa01d27bef 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -26,16 +26,16 @@ namespace OCA\DAV\CalDAV; use OCA\DAV\DAV\Sharing\IShareable; -use OCP\Activity\IEvent; +use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; +use OCP\Activity\IManager as IActivityManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; -use OCP\IConfig; use OCP\IDBConnection; -use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; +use OCP\IUserSession; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Backend\AbstractBackend; use Sabre\CalDAV\Backend\SchedulingSupport; @@ -127,32 +127,37 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription /** @var IUserManager */ private $userManager; - - /** @var IConfig */ - private $config; /** @var ISecureRandom */ private $random; + /** @var ActivityBackend */ + private $activityBackend; + /** * CalDavBackend constructor. * * @param IDBConnection $db * @param Principal $principalBackend * @param IUserManager $userManager - * @param IConfig $config + * @param IGroupManager $groupManager * @param ISecureRandom $random + * @param IActivityManager $activityManager + * @param IUserSession $userSession */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, - IConfig $config, - ISecureRandom $random) { + IGroupManager $groupManager, + ISecureRandom $random, + IActivityManager $activityManager, + IUserSession $userSession) { $this->db = $db; $this->principalBackend = $principalBackend; $this->userManager = $userManager; + $this->userManager = $groupManager; $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar'); - $this->config = $config; + $this->activityBackend = new ActivityBackend($this, $activityManager, $groupManager, $userSession); $this->random = $random; } @@ -618,7 +623,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $query->execute(); $calendarId = $query->getLastInsertId(); - $this->triggerActivity(Activity::SUBJECT_ADD, $calendarId, $values); + $this->activityBackend->addCalendar($calendarId, $values); return $calendarId; } @@ -668,7 +673,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->addChange($calendarId, "", 2); - $this->triggerActivity(Activity::SUBJECT_UPDATE, $calendarId, $mutations); + $this->activityBackend->updateCalendar($calendarId, $mutations); return true; }); @@ -681,7 +686,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @return void */ function deleteCalendar($calendarId) { - $this->triggerActivity(Activity::SUBJECT_DELETE, $calendarId); + $this->activityBackend->deleteCalendar($calendarId); $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?'); $stmt->execute([$calendarId]); @@ -1660,8 +1665,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription */ public function updateShares($shareable, $add, $remove) { /** @var Calendar $shareable */ - $this->triggerActivitySharing($shareable, $add, $remove); - + $this->activityBackend->updateCalendarShares($shareable, $add, $remove); $this->sharingBackend->updateShares($shareable, $add, $remove); } @@ -1736,276 +1740,4 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription } return $principalUri; } - - protected function triggerActivity($action, $calendarId, array $changedProperties = []) { - $aM = \OC::$server->getActivityManager(); - $userSession = \OC::$server->getUserSession(); - - $properties = $this->getCalendarById($calendarId); - if (!isset($properties['principaluri'])) { - return; - } - - $principal = explode('/', $properties['principaluri']); - $owner = $principal[2]; - - $currentUser = $userSession->getUser(); - if ($currentUser instanceof IUser) { - $currentUser = $currentUser->getUID(); - } else { - $currentUser = $owner; - } - - $event = $aM->generateEvent(); - $event->setApp('dav') - ->setObject(Activity::CALENDAR, $calendarId) - ->setType(Activity::CALENDAR) - ->setAuthor($currentUser); - - $changedVisibleInformation = array_intersect([ - '{DAV:}displayname', - '{http://apple.com/ns/ical/}calendar-color' - ], array_keys($changedProperties)); - - if ($action === Activity::SUBJECT_UPDATE && empty($changedVisibleInformation)) { - $users = [$owner]; - } else { - $users = $this->getUsersForCalendar($calendarId); - $users[] = $owner; - } - - foreach ($users as $user) { - $event->setAffectedUser($user) - ->setSubject( - $user === $currentUser ? $action . '_self' : $action, - [ - $currentUser, - $properties['{DAV:}displayname'], - ] - ); - $aM->publish($event); - } - } - - protected function triggerActivitySharing(Calendar $calendar, array $add, array $remove) { - $aM = \OC::$server->getActivityManager(); - $userSession = \OC::$server->getUserSession(); - - $calendarId = $calendar->getResourceId(); - $shares = $this->sharingBackend->getShares($calendarId); - - $properties = $this->getCalendarById($calendarId); - $principal = explode('/', $properties['principaluri']); - $owner = $principal[2]; - - $currentUser = $userSession->getUser(); - if ($currentUser instanceof IUser) { - $currentUser = $currentUser->getUID(); - } else { - $currentUser = $owner; - } - - $event = $aM->generateEvent(); - $event->setApp('dav') - ->setObject(Activity::CALENDAR, $calendarId) - ->setType(Activity::CALENDAR) - ->setAuthor($currentUser); - - foreach ($remove as $principal) { - // principal:principals/users/test - $parts = explode(':', $principal, 2); - if ($parts[0] !== 'principal') { - continue; - } - $principal = explode('/', $parts[1]); - - if ($principal[1] === 'users') { - $this->triggerActivityUnshareUser( - $principal[2], - $event, - $properties, - Activity::SUBJECT_UNSHARE_USER, - Activity::SUBJECT_DELETE . '_self' - ); - - if ($owner !== $principal[2]) { - $parameters = [ - $principal[2], - $properties['{DAV:}displayname'], - ]; - - if ($owner === $event->getAuthor()) { - $subject = Activity::SUBJECT_UNSHARE_USER . '_you'; - } else if ($principal[2] === $event->getAuthor()) { - $subject = Activity::SUBJECT_UNSHARE_USER . '_self'; - } else { - $event->setAffectedUser($event->getAuthor()) - ->setSubject(Activity::SUBJECT_UNSHARE_USER . '_you', $parameters); - $aM->publish($event); - - $subject = Activity::SUBJECT_UNSHARE_USER . '_by'; - $parameters[] = $event->getAuthor(); - } - - $event->setAffectedUser($owner) - ->setSubject($subject, $parameters); - $aM->publish($event); - } - } else if ($principal[1] === 'groups') { - $this->triggerActivityUnshareGroup($principal[2], $event, $properties, Activity::SUBJECT_UNSHARE_USER); - - $parameters = [ - $principal[2], - $properties['{DAV:}displayname'], - ]; - - if ($owner === $event->getAuthor()) { - $subject = Activity::SUBJECT_UNSHARE_GROUP . '_you'; - } else { - $event->setAffectedUser($event->getAuthor()) - ->setSubject(Activity::SUBJECT_UNSHARE_GROUP . '_you', $parameters); - $aM->publish($event); - - $subject = Activity::SUBJECT_UNSHARE_GROUP . '_by'; - $parameters[] = $event->getAuthor(); - } - - $event->setAffectedUser($owner) - ->setSubject($subject, $parameters); - $aM->publish($event); - } - } - - foreach ($add as $share) { - if ($this->isAlreadyShared($share['href'], $shares)) { - continue; - } - - // principal:principals/users/test - $parts = explode(':', $share['href'], 2); - if ($parts[0] !== 'principal') { - continue; - } - $principal = explode('/', $parts[1]); - - if ($principal[1] === 'users') { - $this->triggerActivityUnshareUser($principal[2], $event, $properties, Activity::SUBJECT_SHARE_USER); - - if ($owner !== $principal[2]) { - $parameters = [ - $principal[2], - $properties['{DAV:}displayname'], - ]; - - if ($owner === $event->getAuthor()) { - $subject = Activity::SUBJECT_SHARE_USER . '_you'; - } else { - $event->setAffectedUser($event->getAuthor()) - ->setSubject(Activity::SUBJECT_SHARE_USER . '_you', $parameters); - $aM->publish($event); - - $subject = Activity::SUBJECT_SHARE_USER . '_by'; - $parameters[] = $event->getAuthor(); - } - - $event->setAffectedUser($owner) - ->setSubject($subject, $parameters); - $aM->publish($event); - } - } else if ($principal[1] === 'groups') { - $this->triggerActivityUnshareGroup($principal[2], $event, $properties, Activity::SUBJECT_SHARE_USER); - - $parameters = [ - $principal[2], - $properties['{DAV:}displayname'], - ]; - - if ($owner === $event->getAuthor()) { - $subject = Activity::SUBJECT_SHARE_GROUP . '_you'; - } else { - $event->setAffectedUser($event->getAuthor()) - ->setSubject(Activity::SUBJECT_SHARE_GROUP . '_you', $parameters); - $aM->publish($event); - - $subject = Activity::SUBJECT_SHARE_GROUP . '_by'; - $parameters[] = $event->getAuthor(); - } - - $event->setAffectedUser($owner) - ->setSubject($subject, $parameters); - $aM->publish($event); - } - } - } - - protected function isAlreadyShared($principal, $shares) { - foreach ($shares as $share) { - if ($principal === $share['href']) { - return true; - } - } - return false; - } - - protected function triggerActivityUnshareGroup($gid, IEvent $event, array $properties, $subject) { - $gM = \OC::$server->getGroupManager(); - - $group = $gM->get($gid); - if ($group instanceof IGroup) { - foreach ($group->getUsers() as $user) { - // Exclude current user - if ($user->getUID() !== $event->getAuthor()) { - $this->triggerActivityUnshareUser($user->getUID(), $event, $properties, $subject); - } - } - } - } - - protected function triggerActivityUnshareUser($user, IEvent $event, array $properties, $subject, $subjectSelf = '') { - $aM = \OC::$server->getActivityManager(); - - $event->setAffectedUser($user) - ->setSubject( - $user === $event->getAuthor() && $subjectSelf ? $subjectSelf : $subject, - [ - $event->getAuthor(), - $properties['{DAV:}displayname'], - ] - ); - $aM->publish($event); - } - - /** - * Get all users that have access to a given calendar - * - * @param int $calendarId - * @return string[] - */ - protected function getUsersForCalendar($calendarId) { - $gM = \OC::$server->getGroupManager(); - - $users = $groups = []; - $shares = $this->getShares($calendarId); - foreach ($shares as $share) { - $prinical = explode('/', $share['{http://owncloud.org/ns}principal']); - if ($prinical[1] === 'users') { - $users[] = $prinical[2]; - } else if ($prinical[1] === 'groups') { - $groups[] = $prinical[2]; - } - } - - if (!empty($groups)) { - foreach ($groups as $gid) { - $group = $gM->get($gid); - if ($group instanceof IGroup) { - foreach ($group->getUsers() as $user) { - $users[] = $user->getUID(); - } - } - } - } - - return $users; - } } diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php index da1f706a8b..6fea608244 100644 --- a/apps/dav/lib/Command/CreateCalendar.php +++ b/apps/dav/lib/Command/CreateCalendar.php @@ -75,11 +75,12 @@ class CreateCalendar extends Command { $this->userManager, $this->groupManager ); - $config = \OC::$server->getConfig(); $random = \OC::$server->getSecureRandom(); + $activityManager = \OC::$server->getActivityManager(); + $userSession = \OC::$server->getUserSession(); $name = $input->getArgument('name'); - $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $config, $random); + $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $this->groupManager, $random, $activityManager, $userSession); $caldav->createCalendar("principals/users/$user", $name, []); } } diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 4c76dc30c3..38b4ec795d 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -41,6 +41,9 @@ class RootCollection extends SimpleCollection { $config = \OC::$server->getConfig(); $random = \OC::$server->getSecureRandom(); $userManager = \OC::$server->getUserManager(); + $groupManager = \OC::$server->getGroupManager(); + $activityManager = \OC::$server->getActivityManager(); + $userSession = \OC::$server->getUserSession(); $db = \OC::$server->getDatabaseConnection(); $dispatcher = \OC::$server->getEventDispatcher(); $userPrincipalBackend = new Principal( @@ -62,7 +65,7 @@ class RootCollection extends SimpleCollection { $systemPrincipals->disableListing = $disableListing; $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); $filesCollection->disableListing = $disableListing; - $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $config, $random); + $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $activityManager, $userSession); $calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users'); $calendarRoot->disableListing = $disableListing; $publicCalendarRoot = new PublicCalendarRoot($caldavBackend); diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php index 2559ecbbf8..a674c65c28 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php @@ -22,18 +22,15 @@ namespace OCA\DAV\Tests\unit\CalDAV; -use DateTime; -use DateTimeZone; +use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; use OCA\DAV\CalDAV\CalDavBackend; -use OCA\DAV\CalDAV\Calendar; use OCA\DAV\Connector\Sabre\Principal; -use OCP\IL10N; -use OCP\IConfig; +use OCP\Activity\IManager as IActivityManager; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\IUserSession; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; -use Sabre\DAV\PropPatch; -use Sabre\DAV\Xml\Property\Href; -use Sabre\DAVACL\IACL; use Test\TestCase; /** @@ -50,12 +47,10 @@ abstract class AbstractCalDavBackendTest extends TestCase { /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */ protected $principal; - - /** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ protected $userManager; - - /** var OCP\IConfig */ - protected $config; + /** @var ActivityBackend|\PHPUnit_Framework_MockObject_MockObject */ + protected $activityBackend; /** @var ISecureRandom */ private $random; @@ -67,9 +62,10 @@ abstract class AbstractCalDavBackendTest extends TestCase { public function setUp() { parent::setUp(); - $this->userManager = $this->getMockBuilder('OCP\IUserManager') - ->disableOriginalConstructor() - ->getMock(); + $this->userManager = $this->createMock(IUserManager::class); + $groupManager = $this->createMock(IGroupManager::class); + $activityManager = $this->createMock(IActivityManager::class); + $userSession = $this->createMock(IUserSession::class); $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') ->disableOriginalConstructor() ->setMethods(['getPrincipalByPath', 'getGroupMembership']) @@ -83,15 +79,21 @@ abstract class AbstractCalDavBackendTest extends TestCase { ->willReturn([self::UNIT_TEST_GROUP]); $db = \OC::$server->getDatabaseConnection(); - $this->config = \OC::$server->getConfig(); $this->random = \OC::$server->getSecureRandom(); - $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->config, $this->random); - $this->tearDown(); + $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $groupManager, $this->random, $activityManager, $userSession); + + $this->activityBackend = $this->createMock(ActivityBackend::class); + $this->invokePrivate($this->backend, 'activityBackend', [$this->activityBackend]); + + $this->cleanUpBackend(); } public function tearDown() { + $this->cleanUpBackend(); parent::tearDown(); + } + public function cleanUpBackend() { if (is_null($this->backend)) { return; } diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 8349d98cd9..4d60e983b1 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -357,10 +357,8 @@ EOD; $calendar->setPublishStatus(false); $this->assertEquals(false, $calendar->getPublishStatus()); - $publicCalendarURI = md5($this->config->getSystemValue('secret', '') . $calendar->getResourceId()); $this->setExpectedException('Sabre\DAV\Exception\NotFound'); - $publicCalendar = $this->backend->getPublicCalendar($publicCalendarURI); - + $this->backend->getPublicCalendar($publicCalendarURI); } public function testSubscriptions() { diff --git a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php index 6dfec6d7e1..c3a3d783e1 100644 --- a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php +++ b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php @@ -2,12 +2,16 @@ namespace OCA\DAV\Tests\unit\CalDAV; +use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; use OCA\DAV\CalDAV\Calendar; use OCA\DAV\Connector\Sabre\Principal; +use OCP\Activity\IManager as IActivityManager; +use OCP\IGroupManager; use OCP\IL10N; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\PublicCalendarRoot; use OCP\IUserManager; +use OCP\IUserSession; use OCP\Security\ISecureRandom; use Test\TestCase; @@ -27,12 +31,19 @@ class PublicCalendarRootTest extends TestCase { private $publicCalendarRoot; /** @var IL10N */ private $l10n; - /** @var IUserManager */ - private $userManager; - /** @var Principal */ + /** @var Principal|\PHPUnit_Framework_MockObject_MockObject */ private $principal; - /** var IConfig */ - protected $config; + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $groupManager; + /** @var IActivityManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $activityManager; + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ + protected $userSession; + /** @var ActivityBackend|\PHPUnit_Framework_MockObject_MockObject */ + protected $activityBackend; + /** @var ISecureRandom */ private $random; @@ -40,21 +51,26 @@ class PublicCalendarRootTest extends TestCase { parent::setUp(); $db = \OC::$server->getDatabaseConnection(); - $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') - ->disableOriginalConstructor() - ->getMock(); - $this->config = \OC::$server->getConfig(); - $this->userManager = $this->getMockBuilder('\OCP\IUserManager')->getMock(); + $this->principal = $this->createMock('OCA\DAV\Connector\Sabre\Principal'); + $this->userManager = $this->createMock(IUserManager::class); + $groupManager = $this->createMock(IGroupManager::class); + $activityManager = $this->createMock(IActivityManager::class); + $userSession = $this->createMock(IUserSession::class); $this->random = \OC::$server->getSecureRandom(); $this->backend = new CalDavBackend( $db, $this->principal, $this->userManager, - $this->config, - $this->random + $groupManager, + $this->random, + $activityManager, + $userSession ); + $this->activityBackend = $this->createMock(ActivityBackend::class); + $this->invokePrivate($this->backend, 'activityBackend', [$this->activityBackend]); + $this->publicCalendarRoot = new PublicCalendarRoot($this->backend); $this->l10n = $this->getMockBuilder('\OCP\IL10N') @@ -79,6 +95,14 @@ class PublicCalendarRootTest extends TestCase { } public function testGetChild() { + $this->activityBackend->expects($this->exactly(1)) + ->method('addCalendar'); + $this->activityBackend->expects($this->never()) + ->method('updateCalendar'); + $this->activityBackend->expects($this->never()) + ->method('deleteCalendar'); + $this->activityBackend->expects($this->never()) + ->method('updateCalendarShares'); $calendar = $this->createPublicCalendar(); @@ -93,6 +117,15 @@ class PublicCalendarRootTest extends TestCase { } public function testGetChildren() { + $this->activityBackend->expects($this->exactly(1)) + ->method('addCalendar'); + $this->activityBackend->expects($this->never()) + ->method('updateCalendar'); + $this->activityBackend->expects($this->never()) + ->method('deleteCalendar'); + $this->activityBackend->expects($this->never()) + ->method('updateCalendarShares'); + $this->createPublicCalendar(); $publicCalendars = $this->backend->getPublicCalendars(); From 43b46bcc6aa491ffcaa8ee6d8214285136d250cc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 12:16:43 +0200 Subject: [PATCH 07/13] Activities for events and todos Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity/Backend.php | 125 ++++++++++++++++++++- apps/dav/lib/CalDAV/Activity/Extension.php | 66 +++++++++++ apps/dav/lib/CalDAV/CalDavBackend.php | 4 + 3 files changed, 189 insertions(+), 6 deletions(-) diff --git a/apps/dav/lib/CalDAV/Activity/Backend.php b/apps/dav/lib/CalDAV/Activity/Backend.php index 9d931e0f55..6e4da340c9 100644 --- a/apps/dav/lib/CalDAV/Activity/Backend.php +++ b/apps/dav/lib/CalDAV/Activity/Backend.php @@ -30,6 +30,7 @@ use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; +use Sabre\VObject\Reader; /** * Class Backend @@ -70,7 +71,7 @@ class Backend { * @param array $properties */ public function addCalendar($calendarId, array $properties) { - $this->triggerActivity(Extension::SUBJECT_ADD, $calendarId, $properties); + $this->triggerCalendarActivity(Extension::SUBJECT_ADD, $calendarId, $properties); } /** @@ -80,7 +81,7 @@ class Backend { * @param array $properties */ public function updateCalendar($calendarId, array $properties) { - $this->triggerActivity(Extension::SUBJECT_UPDATE, $calendarId, $properties); + $this->triggerCalendarActivity(Extension::SUBJECT_UPDATE, $calendarId, $properties); } /** @@ -89,10 +90,9 @@ class Backend { * @param int $calendarId */ public function deleteCalendar($calendarId) { - $this->triggerActivity(Extension::SUBJECT_DELETE, $calendarId); + $this->triggerCalendarActivity(Extension::SUBJECT_DELETE, $calendarId); } - /** * Creates activities for all related users when a calendar was touched * @@ -100,7 +100,7 @@ class Backend { * @param int $calendarId * @param array $changedProperties */ - protected function triggerActivity($action, $calendarId, array $changedProperties = []) { + protected function triggerCalendarActivity($action, $calendarId, array $changedProperties = []) { $properties = $this->calDavBackend->getCalendarById($calendarId); if (!isset($properties['principaluri'])) { return; @@ -362,6 +362,119 @@ class Backend { $this->activityManager->publish($event); } + /** + * Creates activities when a calendar object was created + * + * @param int $calendarId + * @param string $objectUri + */ + public function addCalendarObject($calendarId, $objectUri) { + $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_ADD, $calendarId, $objectUri); + } + + /** + * Creates activities when a calendar object was updated + * + * @param int $calendarId + * @param string $objectUri + */ + public function updateCalendarObject($calendarId, $objectUri) { + $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_UPDATE, $calendarId, $objectUri); + } + + /** + * Creates activities when a calendar object was deleted + * + * @param int $calendarId + * @param string $objectUri + */ + public function deleteCalendarObject($calendarId, $objectUri) { + $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_DELETE, $calendarId, $objectUri); + } + + /** + * Creates activities for all related users when a calendar was touched + * + * @param string $action + * @param int $calendarId + * @param string $objectUri + */ + protected function triggerCalendarObjectActivity($action, $calendarId, $objectUri) { + $properties = $this->calDavBackend->getCalendarById($calendarId); + if (!isset($properties['principaluri'])) { + return; + } + + $principal = explode('/', $properties['principaluri']); + $owner = $principal[2]; + + $currentUser = $this->userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $object = $this->getObjectNameAndType($calendarId, $objectUri); + $action = $action . '_' . $object['type']; + + if ($object['type'] === 'todo' && strpos($action, Extension::SUBJECT_OBJECT_UPDATE) === 0 && $object['status'] === 'COMPLETED') { + $action .= '_completed'; + } else if ($object['type'] === 'todo' && strpos($action, Extension::SUBJECT_OBJECT_UPDATE) === 0 && $object['status'] === 'NEEDS-ACTION') { + $action .= '_needs_action'; + } + + $event = $this->activityManager->generateEvent(); + $event->setApp('dav') + ->setObject(Extension::CALENDAR, $calendarId) + ->setType(Extension::CALENDAR) + ->setAuthor($currentUser); + + $users = $this->getUsersForCalendar($calendarId); + $users[] = $owner; + + foreach ($users as $user) { + $event->setAffectedUser($user) + ->setSubject( + $user === $currentUser ? $action . '_self' : $action, + [ + $currentUser, + $properties['{DAV:}displayname'], + $object['name'], + ] + ); + $this->activityManager->publish($event); + } + } + + /** + * @param int $calendarId + * @param string $objectUri + * @return string[]|bool + */ + protected function getObjectNameAndType($calendarId, $objectUri) { + $data = $this->calDavBackend->getCalendarObject($calendarId, $objectUri); + + $vObject = Reader::read($data['calendardata']); + $component = $componentType = null; + foreach($vObject->getComponents() as $component) { + if (in_array($component->name, ['VEVENT', 'VTODO'])) { + $componentType = $component->name; + break; + } + } + + if (!$componentType) { + // Calendar objects must have a VEVENT or VTODO component + return false; + } + + if ($componentType === 'VEVENT') { + return ['name' => (string) $component->SUMMARY, 'type' => 'event']; + } + return ['name' => (string) $component->SUMMARY, 'type' => 'todo', 'status' => (string) $component->STATUS]; + } + /** * Get all users that have access to a given calendar * @@ -391,6 +504,6 @@ class Backend { } } - return $users; + return array_unique($users); } } diff --git a/apps/dav/lib/CalDAV/Activity/Extension.php b/apps/dav/lib/CalDAV/Activity/Extension.php index 9100bb7e18..d3b0871363 100644 --- a/apps/dav/lib/CalDAV/Activity/Extension.php +++ b/apps/dav/lib/CalDAV/Activity/Extension.php @@ -40,6 +40,10 @@ class Extension implements IExtension { const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; const SUBJECT_UNSHARE_GROUP = 'calendar_group_unshare'; + const SUBJECT_OBJECT_ADD = 'object_add'; + const SUBJECT_OBJECT_UPDATE = 'object_update'; + const SUBJECT_OBJECT_DELETE = 'object_delete'; + /** @var IFactory */ protected $languageFactory; @@ -138,6 +142,7 @@ class Extension implements IExtension { return (string) $l->t('%1$s updated calendar %2$s', $params); case self::SUBJECT_UPDATE . '_self': return (string) $l->t('You updated calendar %2$s', $params); + case self::SUBJECT_SHARE_USER: return (string) $l->t('%1$s shared calendar %2$s with you', $params); case self::SUBJECT_SHARE_USER . '_you': @@ -152,6 +157,7 @@ class Extension implements IExtension { return (string) $l->t('%3$s unshared calendar %2$s from %1$s', $params); case self::SUBJECT_UNSHARE_USER . '_self': return (string) $l->t('%1$s unshared calendar %2$s from themselves', $params); + case self::SUBJECT_SHARE_GROUP . '_you': return (string) $l->t('You shared calendar %2$s with group %1$s', $params); case self::SUBJECT_SHARE_GROUP . '_by': @@ -160,6 +166,42 @@ class Extension implements IExtension { return (string) $l->t('You unshared calendar %2$s from group %1$s', $params); case self::SUBJECT_UNSHARE_GROUP . '_by': return (string) $l->t('%3$s unshared calendar %2$s from group %1$s', $params); + + case self::SUBJECT_OBJECT_ADD . '_event': + return (string) $l->t('%1$s created event %3$s in calendar %2$s', $params); + case self::SUBJECT_OBJECT_ADD . '_event_self': + return (string) $l->t('You created event %3$s in calendar %2$s', $params); + case self::SUBJECT_OBJECT_DELETE . '_event': + return (string) $l->t('%1$s deleted event %3$s from calendar %2$s', $params); + case self::SUBJECT_OBJECT_DELETE . '_event_self': + return (string) $l->t('You deleted event %3$s from calendar %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_event': + return (string) $l->t('%1$s updated event %3$s in calendar %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_event_self': + return (string) $l->t('You updated event %3$s in calendar %2$s', $params); + + case self::SUBJECT_OBJECT_ADD . '_todo': + return (string) $l->t('%1$s created todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_ADD . '_todo_self': + return (string) $l->t('You created todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_DELETE . '_todo': + return (string) $l->t('%1$s deleted todo %3$s from list %2$s', $params); + case self::SUBJECT_OBJECT_DELETE . '_todo_self': + return (string) $l->t('You deleted todo %3$s from list %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_todo': + return (string) $l->t('%1$s updated todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_todo_self': + return (string) $l->t('You updated todo %3$s in list %2$s', $params); + + case self::SUBJECT_OBJECT_UPDATE . '_todo_completed': + return (string) $l->t('%1$s solved todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_todo_completed_self': + return (string) $l->t('You solved todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action': + return (string) $l->t('%1$s reopened todo %3$s in list %2$s', $params); + case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action_self': + return (string) $l->t('You reopened todo %3$s in list %2$s', $params); + } return false; @@ -214,6 +256,30 @@ class Extension implements IExtension { //1 => 'calendar', 2 => 'username', ]; + + case self::SUBJECT_OBJECT_ADD . '_event': + case self::SUBJECT_OBJECT_ADD . '_event_self': + case self::SUBJECT_OBJECT_DELETE . '_event': + case self::SUBJECT_OBJECT_DELETE . '_event_self': + case self::SUBJECT_OBJECT_UPDATE . '_event': + case self::SUBJECT_OBJECT_UPDATE . '_event_self': + + case self::SUBJECT_OBJECT_ADD . '_todo': + case self::SUBJECT_OBJECT_ADD . '_todo_self': + case self::SUBJECT_OBJECT_DELETE . '_todo': + case self::SUBJECT_OBJECT_DELETE . '_todo_self': + case self::SUBJECT_OBJECT_UPDATE . '_todo': + case self::SUBJECT_OBJECT_UPDATE . '_todo_self': + + case self::SUBJECT_OBJECT_UPDATE . '_todo_completed': + case self::SUBJECT_OBJECT_UPDATE . '_todo_completed_self': + case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action': + case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action_self': + return [ + 0 => 'username', + //1 => 'calendar', + //2 => 'object', + ]; } } diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index fa01d27bef..660114e462 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -892,6 +892,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ]) ->execute(); + $this->activityBackend->addCalendarObject($calendarId, $objectUri); $this->addChange($calendarId, $objectUri, 1); return '"' . $extraData['etag'] . '"'; @@ -933,6 +934,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) ->execute(); + $this->activityBackend->updateCalendarObject($calendarId, $objectUri); $this->addChange($calendarId, $objectUri, 2); return '"' . $extraData['etag'] . '"'; @@ -965,6 +967,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @return void */ function deleteCalendarObject($calendarId, $objectUri) { + $this->activityBackend->deleteCalendarObject($calendarId, $objectUri); + $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `uri` = ?'); $stmt->execute([$calendarId, $objectUri]); From d3e8463de28197de2d4d61c35906b020ec85b432 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 13:20:53 +0200 Subject: [PATCH 08/13] Use a different type for events and todos Signed-off-by: Joas Schilling --- apps/dav/lib/CalDAV/Activity/Backend.php | 2 +- apps/dav/lib/CalDAV/Activity/Extension.php | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/dav/lib/CalDAV/Activity/Backend.php b/apps/dav/lib/CalDAV/Activity/Backend.php index 6e4da340c9..1c1637cd56 100644 --- a/apps/dav/lib/CalDAV/Activity/Backend.php +++ b/apps/dav/lib/CalDAV/Activity/Backend.php @@ -427,7 +427,7 @@ class Backend { $event = $this->activityManager->generateEvent(); $event->setApp('dav') ->setObject(Extension::CALENDAR, $calendarId) - ->setType(Extension::CALENDAR) + ->setType($object['type'] === 'event' ? Extension::CALENDAR_EVENT : Extension::CALENDAR_TODO) ->setAuthor($currentUser); $users = $this->getUsersForCalendar($calendarId); diff --git a/apps/dav/lib/CalDAV/Activity/Extension.php b/apps/dav/lib/CalDAV/Activity/Extension.php index d3b0871363..29222b90ce 100644 --- a/apps/dav/lib/CalDAV/Activity/Extension.php +++ b/apps/dav/lib/CalDAV/Activity/Extension.php @@ -31,6 +31,8 @@ class Extension implements IExtension { * Filter with all sharing related activities */ const CALENDAR = 'calendar'; + const CALENDAR_EVENT = 'calendar_event'; + const CALENDAR_TODO = 'calendar_todo'; const SUBJECT_ADD = 'calendar_add'; const SUBJECT_UPDATE = 'calendar_update'; @@ -75,6 +77,8 @@ class Extension implements IExtension { return array( self::CALENDAR => (string) $l->t('A calendar was modified'), + self::CALENDAR_EVENT => (string) $l->t('A calendar event was modified'), + self::CALENDAR_TODO => (string) $l->t('A calendar todo was modified'), ); } @@ -89,6 +93,8 @@ class Extension implements IExtension { $defaultTypes = []; if ($method === self::METHOD_STREAM) { $defaultTypes[] = self::CALENDAR; + $defaultTypes[] = self::CALENDAR_EVENT; + $defaultTypes[] = self::CALENDAR_TODO; } return $defaultTypes; @@ -104,7 +110,10 @@ class Extension implements IExtension { public function getTypeIcon($type) { switch ($type) { case self::CALENDAR: + case self::CALENDAR_EVENT: return 'icon-calendar-dark'; + case self::CALENDAR_TODO: + return 'icon-checkmark'; } return false; @@ -314,6 +323,12 @@ class Extension implements IExtension { 'name' => (string) $l->t('Calendar'), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::CALENDAR]), ], + self::CALENDAR_TODO => [ + 'id' => self::CALENDAR_TODO, + 'icon' => 'icon-checkmark', + 'name' => (string) $l->t('Todos'), + 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::CALENDAR_TODO]), + ], ], 'top' => [], ]; @@ -326,7 +341,7 @@ class Extension implements IExtension { * @return boolean */ public function isFilterValid($filterValue) { - return $filterValue === self::CALENDAR; + return in_array($filterValue, [self::CALENDAR, self::CALENDAR_TODO]); } /** @@ -340,7 +355,9 @@ class Extension implements IExtension { public function filterNotificationTypes($types, $filter) { switch ($filter) { case self::CALENDAR: - return array_intersect([self::CALENDAR], $types); + return array_intersect([self::CALENDAR, self::CALENDAR_EVENT], $types); + case self::CALENDAR_TODO: + return array_intersect([self::CALENDAR_TODO], $types); } return false; } From 52dd27892b80ab91d0776c45d05d8447ac24b76e Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 13 Oct 2016 15:34:26 +0200 Subject: [PATCH 09/13] Use the event dispatcher Signed-off-by: Joas Schilling --- apps/dav/appinfo/v1/caldav.php | 6 +- apps/dav/lib/AppInfo/Application.php | 57 +++++++ apps/dav/lib/CalDAV/Activity/Backend.php | 143 ++++++------------ apps/dav/lib/CalDAV/CalDavBackend.php | 93 +++++++++--- apps/dav/lib/Command/CreateCalendar.php | 5 +- apps/dav/lib/RootCollection.php | 5 +- .../unit/CalDAV/AbstractCalDavBackendTest.php | 16 +- .../unit/CalDAV/PublicCalendarRootTest.php | 41 +---- 8 files changed, 189 insertions(+), 177 deletions(-) diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php index ecd6a17123..9f12a92f4c 100644 --- a/apps/dav/appinfo/v1/caldav.php +++ b/apps/dav/appinfo/v1/caldav.php @@ -48,10 +48,8 @@ $principalBackend = new Principal( $db = \OC::$server->getDatabaseConnection(); $userManager = \OC::$server->getUserManager(); $random = \OC::$server->getSecureRandom(); -$groupManager = \OC::$server->getGroupManager(); -$activityManager = \OC::$server->getActivityManager(); -$userSession = \OC::$server->getUserSession(); -$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, $groupManager, $random, $activityManager, $userSession); +$dispatcher = \OC::$server->getEventDispatcher(); +$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, $random, $dispatcher); $debugging = \OC::$server->getConfig()->getSystemValue('debug', false); diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 32483e8fd8..c777f5e5a3 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -24,6 +24,7 @@ */ namespace OCA\DAV\AppInfo; +use OCA\DAV\CalDAV\Activity\Backend; use OCA\DAV\CalDAV\Activity\Extension; use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\Capabilities; @@ -90,6 +91,62 @@ class Application extends App { } }); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', function(GenericEvent $event) { + /** @var Backend $backend */ + $backend = $this->getContainer()->query(Backend::class); + $backend->onCalendarAdd( + $event->getArgument('calendarData') + ); + }); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', function(GenericEvent $event) { + /** @var Backend $backend */ + $backend = $this->getContainer()->query(Backend::class); + $backend->onCalendarUpdate( + $event->getArgument('calendarData'), + $event->getArgument('shares'), + $event->getArgument('propertyMutations') + ); + }); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', function(GenericEvent $event) { + /** @var Backend $backend */ + $backend = $this->getContainer()->query(Backend::class); + $backend->onCalendarDelete( + $event->getArgument('calendarData'), + $event->getArgument('shares') + ); + }); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function(GenericEvent $event) { + /** @var Backend $backend */ + $backend = $this->getContainer()->query(Backend::class); + $backend->onCalendarUpdateShares( + $event->getArgument('calendarData'), + $event->getArgument('shares'), + $event->getArgument('add'), + $event->getArgument('remove') + ); + }); + + $listener = function(GenericEvent $event, $eventName) { + /** @var Backend $backend */ + $backend = $this->getContainer()->query(Backend::class); + + $subject = Extension::SUBJECT_OBJECT_ADD; + if ($eventName === '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject') { + $subject = Extension::SUBJECT_OBJECT_UPDATE; + } else if ($eventName === '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') { + $subject = Extension::SUBJECT_OBJECT_DELETE; + } + $backend->onTouchCalendarObject( + $subject, + $event->getArgument('calendarData'), + $event->getArgument('shares'), + $event->getArgument('objectData') + ); + }; + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $listener); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener); + $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', $listener); + $aM = $this->getContainer()->getServer()->getActivityManager(); $aM->registerExtension(function() { return $this->getContainer()->query(Extension::class); diff --git a/apps/dav/lib/CalDAV/Activity/Backend.php b/apps/dav/lib/CalDAV/Activity/Backend.php index 1c1637cd56..6cf09f6de4 100644 --- a/apps/dav/lib/CalDAV/Activity/Backend.php +++ b/apps/dav/lib/CalDAV/Activity/Backend.php @@ -22,8 +22,6 @@ namespace OCA\DAV\CalDAV\Activity; -use OCA\DAV\CalDAV\CalDavBackend; -use OCA\DAV\CalDAV\Calendar; use OCP\Activity\IEvent; use OCP\Activity\IManager as IActivityManager; use OCP\IGroup; @@ -39,9 +37,6 @@ use Sabre\VObject\Reader; */ class Backend { - /** @var CalDavBackend */ - protected $calDavBackend; - /** @var IActivityManager */ protected $activityManager; @@ -52,13 +47,11 @@ class Backend { protected $userSession; /** - * @param CalDavBackend $calDavBackend * @param IActivityManager $activityManager * @param IGroupManager $groupManager * @param IUserSession $userSession */ - public function __construct(CalDavBackend $calDavBackend, IActivityManager $activityManager, IGroupManager $groupManager, IUserSession $userSession) { - $this->calDavBackend = $calDavBackend; + public function __construct(IActivityManager $activityManager, IGroupManager $groupManager, IUserSession $userSession) { $this->activityManager = $activityManager; $this->groupManager = $groupManager; $this->userSession = $userSession; @@ -67,46 +60,47 @@ class Backend { /** * Creates activities when a calendar was creates * - * @param int $calendarId - * @param array $properties + * @param array $calendarData */ - public function addCalendar($calendarId, array $properties) { - $this->triggerCalendarActivity(Extension::SUBJECT_ADD, $calendarId, $properties); + public function onCalendarAdd(array $calendarData) { + $this->triggerCalendarActivity(Extension::SUBJECT_ADD, $calendarData); } /** * Creates activities when a calendar was updated * - * @param int $calendarId + * @param array $calendarData + * @param array $shares * @param array $properties */ - public function updateCalendar($calendarId, array $properties) { - $this->triggerCalendarActivity(Extension::SUBJECT_UPDATE, $calendarId, $properties); + public function onCalendarUpdate(array $calendarData, array $shares, array $properties) { + $this->triggerCalendarActivity(Extension::SUBJECT_UPDATE, $calendarData, $shares, $properties); } /** * Creates activities when a calendar was deleted * - * @param int $calendarId + * @param array $calendarData + * @param array $shares */ - public function deleteCalendar($calendarId) { - $this->triggerCalendarActivity(Extension::SUBJECT_DELETE, $calendarId); + public function onCalendarDelete(array $calendarData, array $shares) { + $this->triggerCalendarActivity(Extension::SUBJECT_DELETE, $calendarData, $shares); } /** * Creates activities for all related users when a calendar was touched * * @param string $action - * @param int $calendarId + * @param array $calendarData + * @param array $shares * @param array $changedProperties */ - protected function triggerCalendarActivity($action, $calendarId, array $changedProperties = []) { - $properties = $this->calDavBackend->getCalendarById($calendarId); - if (!isset($properties['principaluri'])) { + protected function triggerCalendarActivity($action, array $calendarData, array $shares = [], array $changedProperties = []) { + if (!isset($calendarData['principaluri'])) { return; } - $principal = explode('/', $properties['principaluri']); + $principal = explode('/', $calendarData['principaluri']); $owner = $principal[2]; $currentUser = $this->userSession->getUser(); @@ -118,7 +112,7 @@ class Backend { $event = $this->activityManager->generateEvent(); $event->setApp('dav') - ->setObject(Extension::CALENDAR, $calendarId) + ->setObject(Extension::CALENDAR, $calendarData['id']) ->setType(Extension::CALENDAR) ->setAuthor($currentUser); @@ -127,10 +121,10 @@ class Backend { '{http://apple.com/ns/ical/}calendar-color' ], array_keys($changedProperties)); - if ($action === Extension::SUBJECT_UPDATE && empty($changedVisibleInformation)) { + if (empty($shares) || ($action === Extension::SUBJECT_UPDATE && empty($changedVisibleInformation))) { $users = [$owner]; } else { - $users = $this->getUsersForCalendar($calendarId); + $users = $this->getUsersForShares($shares); $users[] = $owner; } @@ -140,7 +134,7 @@ class Backend { $user === $currentUser ? $action . '_self' : $action, [ $currentUser, - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], ] ); $this->activityManager->publish($event); @@ -150,16 +144,13 @@ class Backend { /** * Creates activities for all related users when a calendar was (un-)shared * - * @param Calendar $calendar + * @param array $calendarData + * @param array $shares * @param array $add * @param array $remove */ - public function updateCalendarShares(Calendar $calendar, array $add, array $remove) { - $calendarId = $calendar->getResourceId(); - $shares = $this->calDavBackend->getShares($calendarId); - - $properties = $this->calDavBackend->getCalendarById($calendarId); - $principal = explode('/', $properties['principaluri']); + public function onCalendarUpdateShares(array $calendarData, array $shares, array $add, array $remove) { + $principal = explode('/', $calendarData['principaluri']); $owner = $principal[2]; $currentUser = $this->userSession->getUser(); @@ -171,7 +162,7 @@ class Backend { $event = $this->activityManager->generateEvent(); $event->setApp('dav') - ->setObject(Extension::CALENDAR, $calendarId) + ->setObject(Extension::CALENDAR, $calendarData['id']) ->setType(Extension::CALENDAR) ->setAuthor($currentUser); @@ -187,7 +178,7 @@ class Backend { $this->triggerActivityUser( $principal[2], $event, - $properties, + $calendarData, Extension::SUBJECT_UNSHARE_USER, Extension::SUBJECT_DELETE . '_self' ); @@ -195,7 +186,7 @@ class Backend { if ($owner !== $principal[2]) { $parameters = [ $principal[2], - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { @@ -216,11 +207,11 @@ class Backend { $this->activityManager->publish($event); } } else if ($principal[1] === 'groups') { - $this->triggerActivityGroup($principal[2], $event, $properties, Extension::SUBJECT_UNSHARE_USER); + $this->triggerActivityGroup($principal[2], $event, $calendarData, Extension::SUBJECT_UNSHARE_USER); $parameters = [ $principal[2], - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { @@ -253,12 +244,12 @@ class Backend { $principal = explode('/', $parts[1]); if ($principal[1] === 'users') { - $this->triggerActivityUser($principal[2], $event, $properties, Extension::SUBJECT_SHARE_USER); + $this->triggerActivityUser($principal[2], $event, $calendarData, Extension::SUBJECT_SHARE_USER); if ($owner !== $principal[2]) { $parameters = [ $principal[2], - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { @@ -277,11 +268,11 @@ class Backend { $this->activityManager->publish($event); } } else if ($principal[1] === 'groups') { - $this->triggerActivityGroup($principal[2], $event, $properties, Extension::SUBJECT_SHARE_USER); + $this->triggerActivityGroup($principal[2], $event, $calendarData, Extension::SUBJECT_SHARE_USER); $parameters = [ $principal[2], - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { @@ -363,49 +354,19 @@ class Backend { } /** - * Creates activities when a calendar object was created - * - * @param int $calendarId - * @param string $objectUri - */ - public function addCalendarObject($calendarId, $objectUri) { - $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_ADD, $calendarId, $objectUri); - } - - /** - * Creates activities when a calendar object was updated - * - * @param int $calendarId - * @param string $objectUri - */ - public function updateCalendarObject($calendarId, $objectUri) { - $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_UPDATE, $calendarId, $objectUri); - } - - /** - * Creates activities when a calendar object was deleted - * - * @param int $calendarId - * @param string $objectUri - */ - public function deleteCalendarObject($calendarId, $objectUri) { - $this->triggerCalendarObjectActivity(Extension::SUBJECT_OBJECT_DELETE, $calendarId, $objectUri); - } - - /** - * Creates activities for all related users when a calendar was touched + * Creates activities when a calendar object was created/updated/deleted * * @param string $action - * @param int $calendarId - * @param string $objectUri + * @param array $calendarData + * @param array $shares + * @param array $objectData */ - protected function triggerCalendarObjectActivity($action, $calendarId, $objectUri) { - $properties = $this->calDavBackend->getCalendarById($calendarId); - if (!isset($properties['principaluri'])) { + public function onTouchCalendarObject($action, array $calendarData, array $shares, array $objectData) { + if (!isset($calendarData['principaluri'])) { return; } - $principal = explode('/', $properties['principaluri']); + $principal = explode('/', $calendarData['principaluri']); $owner = $principal[2]; $currentUser = $this->userSession->getUser(); @@ -415,7 +376,7 @@ class Backend { $currentUser = $owner; } - $object = $this->getObjectNameAndType($calendarId, $objectUri); + $object = $this->getObjectNameAndType($objectData); $action = $action . '_' . $object['type']; if ($object['type'] === 'todo' && strpos($action, Extension::SUBJECT_OBJECT_UPDATE) === 0 && $object['status'] === 'COMPLETED') { @@ -426,11 +387,11 @@ class Backend { $event = $this->activityManager->generateEvent(); $event->setApp('dav') - ->setObject(Extension::CALENDAR, $calendarId) + ->setObject(Extension::CALENDAR, $calendarData['id']) ->setType($object['type'] === 'event' ? Extension::CALENDAR_EVENT : Extension::CALENDAR_TODO) ->setAuthor($currentUser); - $users = $this->getUsersForCalendar($calendarId); + $users = $this->getUsersForShares($shares); $users[] = $owner; foreach ($users as $user) { @@ -439,7 +400,7 @@ class Backend { $user === $currentUser ? $action . '_self' : $action, [ $currentUser, - $properties['{DAV:}displayname'], + $calendarData['{DAV:}displayname'], $object['name'], ] ); @@ -448,14 +409,11 @@ class Backend { } /** - * @param int $calendarId - * @param string $objectUri + * @param array $objectData * @return string[]|bool */ - protected function getObjectNameAndType($calendarId, $objectUri) { - $data = $this->calDavBackend->getCalendarObject($calendarId, $objectUri); - - $vObject = Reader::read($data['calendardata']); + protected function getObjectNameAndType(array $objectData) { + $vObject = Reader::read($objectData['calendardata']); $component = $componentType = null; foreach($vObject->getComponents() as $component) { if (in_array($component->name, ['VEVENT', 'VTODO'])) { @@ -478,12 +436,11 @@ class Backend { /** * Get all users that have access to a given calendar * - * @param int $calendarId + * @param array $shares * @return string[] */ - protected function getUsersForCalendar($calendarId) { + protected function getUsersForShares(array $shares) { $users = $groups = []; - $shares = $this->calDavBackend->getShares($calendarId); foreach ($shares as $share) { $prinical = explode('/', $share['{http://owncloud.org/ns}principal']); if ($prinical[1] === 'users') { diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 660114e462..fb608c2009 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -26,16 +26,12 @@ namespace OCA\DAV\CalDAV; use OCA\DAV\DAV\Sharing\IShareable; -use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; -use OCP\Activity\IManager as IActivityManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; use OCP\IDBConnection; -use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; -use OCP\IUserSession; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Backend\AbstractBackend; use Sabre\CalDAV\Backend\SchedulingSupport; @@ -52,6 +48,8 @@ use Sabre\HTTP\URLUtil; use Sabre\VObject\DateTimeParser; use Sabre\VObject\Reader; use Sabre\VObject\Recur\EventIterator; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Class CalDavBackend @@ -131,8 +129,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription /** @var ISecureRandom */ private $random; - /** @var ActivityBackend */ - private $activityBackend; + /** @var EventDispatcherInterface */ + private $dispatcher; /** * CalDavBackend constructor. @@ -140,25 +138,20 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param IDBConnection $db * @param Principal $principalBackend * @param IUserManager $userManager - * @param IGroupManager $groupManager * @param ISecureRandom $random - * @param IActivityManager $activityManager - * @param IUserSession $userSession + * @param EventDispatcherInterface $dispatcher */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, - IGroupManager $groupManager, ISecureRandom $random, - IActivityManager $activityManager, - IUserSession $userSession) { + EventDispatcherInterface $dispatcher) { $this->db = $db; $this->principalBackend = $principalBackend; $this->userManager = $userManager; - $this->userManager = $groupManager; $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar'); - $this->activityBackend = new ActivityBackend($this, $activityManager, $groupManager, $userSession); $this->random = $random; + $this->dispatcher = $dispatcher; } /** @@ -623,7 +616,12 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $query->execute(); $calendarId = $query->getLastInsertId(); - $this->activityBackend->addCalendar($calendarId, $values); + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::createCalendar', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + ])); return $calendarId; } @@ -673,7 +671,14 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->addChange($calendarId, "", 2); - $this->activityBackend->updateCalendar($calendarId, $mutations); + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + 'propertyMutations' => $mutations, + ])); return true; }); @@ -686,7 +691,13 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @return void */ function deleteCalendar($calendarId) { - $this->activityBackend->deleteCalendar($calendarId); + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + ])); $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?'); $stmt->execute([$calendarId]); @@ -892,7 +903,15 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ]) ->execute(); - $this->activityBackend->addCalendarObject($calendarId, $objectUri); + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + 'objectData' => $this->getCalendarObject($calendarId, $objectUri), + ] + )); $this->addChange($calendarId, $objectUri, 1); return '"' . $extraData['etag'] . '"'; @@ -934,7 +953,18 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) ->execute(); - $this->activityBackend->updateCalendarObject($calendarId, $objectUri); + $data = $this->getCalendarObject($calendarId, $objectUri); + if (is_array($data)) { + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + 'objectData' => $data, + ] + )); + } $this->addChange($calendarId, $objectUri, 2); return '"' . $extraData['etag'] . '"'; @@ -967,7 +997,18 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @return void */ function deleteCalendarObject($calendarId, $objectUri) { - $this->activityBackend->deleteCalendarObject($calendarId, $objectUri); + $data = $this->getCalendarObject($calendarId, $objectUri); + if (is_array($data)) { + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + 'objectData' => $data, + ] + )); + } $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `uri` = ?'); $stmt->execute([$calendarId, $objectUri]); @@ -1668,8 +1709,16 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param array $remove */ public function updateShares($shareable, $add, $remove) { - /** @var Calendar $shareable */ - $this->activityBackend->updateCalendarShares($shareable, $add, $remove); + $calendarId = $shareable->getResourceId(); + $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateShares', new GenericEvent( + '\OCA\DAV\CalDAV\CalDavBackend::updateShares', + [ + 'calendarId' => $calendarId, + 'calendarData' => $this->getCalendarById($calendarId), + 'shares' => $this->getShares($calendarId), + 'add' => $add, + 'remove' => $remove, + ])); $this->sharingBackend->updateShares($shareable, $add, $remove); } diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php index 6fea608244..24990352fa 100644 --- a/apps/dav/lib/Command/CreateCalendar.php +++ b/apps/dav/lib/Command/CreateCalendar.php @@ -76,11 +76,10 @@ class CreateCalendar extends Command { $this->groupManager ); $random = \OC::$server->getSecureRandom(); - $activityManager = \OC::$server->getActivityManager(); - $userSession = \OC::$server->getUserSession(); + $dispatcher = \OC::$server->getEventDispatcher(); $name = $input->getArgument('name'); - $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $this->groupManager, $random, $activityManager, $userSession); + $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $random, $dispatcher); $caldav->createCalendar("principals/users/$user", $name, []); } } diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 38b4ec795d..478f0929c2 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -41,9 +41,6 @@ class RootCollection extends SimpleCollection { $config = \OC::$server->getConfig(); $random = \OC::$server->getSecureRandom(); $userManager = \OC::$server->getUserManager(); - $groupManager = \OC::$server->getGroupManager(); - $activityManager = \OC::$server->getActivityManager(); - $userSession = \OC::$server->getUserSession(); $db = \OC::$server->getDatabaseConnection(); $dispatcher = \OC::$server->getEventDispatcher(); $userPrincipalBackend = new Principal( @@ -65,7 +62,7 @@ class RootCollection extends SimpleCollection { $systemPrincipals->disableListing = $disableListing; $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); $filesCollection->disableListing = $disableListing; - $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $activityManager, $userSession); + $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $random, $dispatcher); $calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users'); $calendarRoot->disableListing = $disableListing; $publicCalendarRoot = new PublicCalendarRoot($caldavBackend); diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php index a674c65c28..7d2e033e0b 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php @@ -22,15 +22,12 @@ namespace OCA\DAV\Tests\unit\CalDAV; -use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\Connector\Sabre\Principal; -use OCP\Activity\IManager as IActivityManager; -use OCP\IGroupManager; use OCP\IUserManager; -use OCP\IUserSession; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -49,8 +46,6 @@ abstract class AbstractCalDavBackendTest extends TestCase { protected $principal; /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ protected $userManager; - /** @var ActivityBackend|\PHPUnit_Framework_MockObject_MockObject */ - protected $activityBackend; /** @var ISecureRandom */ private $random; @@ -63,9 +58,7 @@ abstract class AbstractCalDavBackendTest extends TestCase { parent::setUp(); $this->userManager = $this->createMock(IUserManager::class); - $groupManager = $this->createMock(IGroupManager::class); - $activityManager = $this->createMock(IActivityManager::class); - $userSession = $this->createMock(IUserSession::class); + $dispatcher = $this->createMock(EventDispatcherInterface::class); $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') ->disableOriginalConstructor() ->setMethods(['getPrincipalByPath', 'getGroupMembership']) @@ -80,10 +73,7 @@ abstract class AbstractCalDavBackendTest extends TestCase { $db = \OC::$server->getDatabaseConnection(); $this->random = \OC::$server->getSecureRandom(); - $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $groupManager, $this->random, $activityManager, $userSession); - - $this->activityBackend = $this->createMock(ActivityBackend::class); - $this->invokePrivate($this->backend, 'activityBackend', [$this->activityBackend]); + $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->random, $dispatcher); $this->cleanUpBackend(); } diff --git a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php index c3a3d783e1..59fa4747a9 100644 --- a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php +++ b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php @@ -2,17 +2,14 @@ namespace OCA\DAV\Tests\unit\CalDAV; -use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; use OCA\DAV\CalDAV\Calendar; use OCA\DAV\Connector\Sabre\Principal; -use OCP\Activity\IManager as IActivityManager; -use OCP\IGroupManager; use OCP\IL10N; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\PublicCalendarRoot; use OCP\IUserManager; -use OCP\IUserSession; use OCP\Security\ISecureRandom; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -35,14 +32,6 @@ class PublicCalendarRootTest extends TestCase { private $principal; /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ protected $userManager; - /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $groupManager; - /** @var IActivityManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $activityManager; - /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ - protected $userSession; - /** @var ActivityBackend|\PHPUnit_Framework_MockObject_MockObject */ - protected $activityBackend; /** @var ISecureRandom */ private $random; @@ -53,24 +42,17 @@ class PublicCalendarRootTest extends TestCase { $db = \OC::$server->getDatabaseConnection(); $this->principal = $this->createMock('OCA\DAV\Connector\Sabre\Principal'); $this->userManager = $this->createMock(IUserManager::class); - $groupManager = $this->createMock(IGroupManager::class); - $activityManager = $this->createMock(IActivityManager::class); - $userSession = $this->createMock(IUserSession::class); $this->random = \OC::$server->getSecureRandom(); + $dispatcher = $this->createMock(EventDispatcherInterface::class); $this->backend = new CalDavBackend( $db, $this->principal, $this->userManager, - $groupManager, $this->random, - $activityManager, - $userSession + $dispatcher ); - $this->activityBackend = $this->createMock(ActivityBackend::class); - $this->invokePrivate($this->backend, 'activityBackend', [$this->activityBackend]); - $this->publicCalendarRoot = new PublicCalendarRoot($this->backend); $this->l10n = $this->getMockBuilder('\OCP\IL10N') @@ -95,14 +77,6 @@ class PublicCalendarRootTest extends TestCase { } public function testGetChild() { - $this->activityBackend->expects($this->exactly(1)) - ->method('addCalendar'); - $this->activityBackend->expects($this->never()) - ->method('updateCalendar'); - $this->activityBackend->expects($this->never()) - ->method('deleteCalendar'); - $this->activityBackend->expects($this->never()) - ->method('updateCalendarShares'); $calendar = $this->createPublicCalendar(); @@ -117,15 +91,6 @@ class PublicCalendarRootTest extends TestCase { } public function testGetChildren() { - $this->activityBackend->expects($this->exactly(1)) - ->method('addCalendar'); - $this->activityBackend->expects($this->never()) - ->method('updateCalendar'); - $this->activityBackend->expects($this->never()) - ->method('deleteCalendar'); - $this->activityBackend->expects($this->never()) - ->method('updateCalendarShares'); - $this->createPublicCalendar(); $publicCalendars = $this->backend->getPublicCalendars(); From e8f82c6b61ec293c354c417762d4279a877372a4 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 3 Nov 2016 12:28:12 +0100 Subject: [PATCH 10/13] Add tests for dispatching of the events Signed-off-by: Joas Schilling --- .../unit/CalDAV/AbstractCalDavBackendTest.php | 25 ++++++++-- .../tests/unit/CalDAV/CalDavBackendTest.php | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php index 7d2e033e0b..d15be72c77 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php @@ -46,6 +46,8 @@ abstract class AbstractCalDavBackendTest extends TestCase { protected $principal; /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ protected $userManager; + /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $dispatcher; /** @var ISecureRandom */ private $random; @@ -58,7 +60,7 @@ abstract class AbstractCalDavBackendTest extends TestCase { parent::setUp(); $this->userManager = $this->createMock(IUserManager::class); - $dispatcher = $this->createMock(EventDispatcherInterface::class); + $this->dispatcher = $this->createMock(EventDispatcherInterface::class); $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') ->disableOriginalConstructor() ->setMethods(['getPrincipalByPath', 'getGroupMembership']) @@ -73,7 +75,7 @@ abstract class AbstractCalDavBackendTest extends TestCase { $db = \OC::$server->getDatabaseConnection(); $this->random = \OC::$server->getSecureRandom(); - $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->random, $dispatcher); + $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->random, $this->dispatcher); $this->cleanUpBackend(); } @@ -87,9 +89,13 @@ abstract class AbstractCalDavBackendTest extends TestCase { if (is_null($this->backend)) { return; } - $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); - foreach ($books as $book) { - $this->backend->deleteCalendar($book['id']); + $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); + foreach ($calendars as $calendar) { + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); + + $this->backend->deleteCalendar($calendar['id']); } $subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER); foreach ($subscriptions as $subscription) { @@ -98,6 +104,10 @@ abstract class AbstractCalDavBackendTest extends TestCase { } protected function createTestCalendar() { + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendar'); + $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', [ '{http://apple.com/ns/ical/}calendar-color' => '#1C4587FF' ]); @@ -135,6 +145,11 @@ END:VEVENT END:VCALENDAR EOD; $uri0 = $this->getUniqueID('event'); + + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); + $this->backend->createCalendarObject($calendarId, $uri0, $calData); return $uri0; diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 4d60e983b1..b5e700e8bc 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -51,6 +51,9 @@ class CalDavBackendTest extends AbstractCalDavBackendTest { '{DAV:}displayname' => 'Unit test', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar used for unit testing' ]); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar'); $this->backend->updateCalendar($calendarId, $patch); $patch->commit(); $this->assertEquals(1, $this->backend->getCalendarsForUserCount(self::UNIT_TEST_USER)); @@ -60,6 +63,9 @@ class CalDavBackendTest extends AbstractCalDavBackendTest { $this->assertEquals('Calendar used for unit testing', $books[0]['{urn:ietf:params:xml:ns:caldav}calendar-description']); // delete the address book + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); $this->backend->deleteCalendar($books[0]['id']); $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); $this->assertEquals(0, count($books)); @@ -106,6 +112,9 @@ class CalDavBackendTest extends AbstractCalDavBackendTest { $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); $this->assertEquals(1, count($books)); $calendar = new Calendar($this->backend, $books[0], $l10n); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::updateShares'); $this->backend->updateShares($calendar, $add, []); $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER1); $this->assertEquals(1, count($books)); @@ -138,6 +147,9 @@ END:VEVENT END:VCALENDAR EOD; + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); $this->backend->createCalendarObject($calendarId, $uri, $calData); /** @var IACL $child */ @@ -151,6 +163,9 @@ EOD; $this->assertAccess($groupCanWrite, self::UNIT_TEST_GROUP, '{DAV:}write', $acl); // delete the address book + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); $this->backend->deleteCalendar($books[0]['id']); $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); $this->assertEquals(0, count($books)); @@ -179,6 +194,9 @@ END:VEVENT END:VCALENDAR EOD; + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); $this->backend->createCalendarObject($calendarId, $uri, $calData); // get all the cards @@ -214,11 +232,17 @@ DTEND;VALUE=DATE-TIME:20130912T140000Z END:VEVENT END:VCALENDAR EOD; + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject'); $this->backend->updateCalendarObject($calendarId, $uri, $calData); $calendarObject = $this->backend->getCalendarObject($calendarId, $uri); $this->assertEquals($calData, $calendarObject['calendardata']); // delete the card + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject'); $this->backend->deleteCalendarObject($calendarId, $uri); $calendarObjects = $this->backend->getCalendarObjects($calendarId); $this->assertEquals(0, count($calendarObjects)); @@ -246,10 +270,19 @@ END:VEVENT END:VCALENDAR EOD; $uri0 = $this->getUniqueID('card'); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); $this->backend->createCalendarObject($calendarId, $uri0, $calData); $uri1 = $this->getUniqueID('card'); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); $this->backend->createCalendarObject($calendarId, $uri1, $calData); $uri2 = $this->getUniqueID('card'); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject'); $this->backend->createCalendarObject($calendarId, $uri2, $calData); // get all the cards @@ -270,8 +303,17 @@ EOD; } // delete the card + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject'); $this->backend->deleteCalendarObject($calendarId, $uri0); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject'); $this->backend->deleteCalendarObject($calendarId, $uri1); + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject'); $this->backend->deleteCalendarObject($calendarId, $uri2); $calendarObjects = $this->backend->getCalendarObjects($calendarId); $this->assertEquals(0, count($calendarObjects)); @@ -335,6 +377,10 @@ EOD; } public function testPublications() { + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with('\OCA\DAV\CalDAV\CalDavBackend::createCalendar'); + $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', []); $calendarInfo = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER)[0]; From c4775a5bce4f2b804fa4bd15cdf77698d3831d7a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 3 Nov 2016 12:57:40 +0100 Subject: [PATCH 11/13] Start unit tests for the activity listener Signed-off-by: Joas Schilling --- .../unit/CalDAV/Activity/BackendTest.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 apps/dav/tests/unit/CalDAV/Activity/BackendTest.php diff --git a/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php new file mode 100644 index 0000000000..60afeee8c0 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php @@ -0,0 +1,100 @@ + + * + * @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 . + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV\Activity; + +use OCA\DAV\CalDAV\Activity\Backend; +use OCA\DAV\CalDAV\Activity\Extension; +use OCP\Activity\IManager; +use OCP\IGroupManager; +use OCP\IUserSession; +use Test\TestCase; + +class BackendTest extends TestCase { + + /** @var IManager */ + protected $activityManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserSession */ + protected $userSession; + + protected function setUp() { + parent::setUp(); + $this->activityManager = $this->createMock(IManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->userSession = $this->createMock(IUserSession::class); + } + + /** + * @param array $methods + * @return Backend|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getBackend(array $methods = []) { + if (empty($methods)) { + return new Backend( + $this->activityManager, + $this->groupManager, + $this->userSession + ); + } else { + return $this->getMockBuilder(Backend::class) + ->setConstructorArgs([ + $this->activityManager, + $this->groupManager, + $this->userSession, + ]) + ->setMethods($methods) + ->getMock(); + } + } + + public function dataCallTriggerCalendarActivity() { + return [ + ['onCalendarAdd', [['data']], Extension::SUBJECT_ADD, [['data'], [], []]], + ['onCalendarUpdate', [['data'], ['shares'], ['properties']], Extension::SUBJECT_UPDATE, [['data'], ['shares'], ['properties']]], + ['onCalendarDelete', [['data'], ['shares']], Extension::SUBJECT_DELETE, [['data'], ['shares'], []]], + ]; + } + + /** + * @dataProvider dataCallTriggerCalendarActivity + * + * @param string $method + * @param array $payload + * @param string $expectedSubject + * @param array $expectedPayload + */ + public function testCallTriggerCalendarActivity($method, array $payload, $expectedSubject, array $expectedPayload) { + $backend = $this->getBackend(['triggerCalendarActivity']); + $backend->expects($this->once()) + ->method('triggerCalendarActivity') + ->willReturnCallback(function() use($expectedPayload, $expectedSubject) { + $arguments = func_get_args(); + $this->assertSame($expectedSubject, array_shift($arguments)); + $this->assertEquals($expectedPayload, $arguments); + }); + + call_user_func_array([$backend, $method], $payload); + } +} From 36b656e2076484a979d0d00dc1df942960098898 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 3 Nov 2016 14:30:02 +0100 Subject: [PATCH 12/13] Add tests for triggerCalendarActivity Signed-off-by: Joas Schilling --- .../unit/CalDAV/Activity/BackendTest.php | 143 +++++++++++++++++- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php index 60afeee8c0..74a2bac584 100644 --- a/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php +++ b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php @@ -23,20 +23,22 @@ namespace OCA\DAV\Tests\unit\CalDAV\Activity; use OCA\DAV\CalDAV\Activity\Backend; use OCA\DAV\CalDAV\Activity\Extension; +use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\IGroupManager; +use OCP\IUser; use OCP\IUserSession; use Test\TestCase; class BackendTest extends TestCase { - /** @var IManager */ + /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ protected $activityManager; - /** @var IGroupManager */ + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ protected $groupManager; - /** @var IUserSession */ + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ protected $userSession; protected function setUp() { @@ -72,7 +74,7 @@ class BackendTest extends TestCase { public function dataCallTriggerCalendarActivity() { return [ ['onCalendarAdd', [['data']], Extension::SUBJECT_ADD, [['data'], [], []]], - ['onCalendarUpdate', [['data'], ['shares'], ['properties']], Extension::SUBJECT_UPDATE, [['data'], ['shares'], ['properties']]], + ['onCalendarUpdate', [['data'], ['shares'], ['changed-properties']], Extension::SUBJECT_UPDATE, [['data'], ['shares'], ['changed-properties']]], ['onCalendarDelete', [['data'], ['shares']], Extension::SUBJECT_DELETE, [['data'], ['shares'], []]], ]; } @@ -97,4 +99,137 @@ class BackendTest extends TestCase { call_user_func_array([$backend, $method], $payload); } + + public function dataTriggerCalendarActivity() { + return [ + // Add calendar + [Extension::SUBJECT_ADD, [], [], [], '', '', null, []], + [Extension::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], [], [], '', 'admin', null, ['admin']], + [Extension::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], [], [], 'test2', 'test2', null, ['admin']], + + // Update calendar + [Extension::SUBJECT_UPDATE, [], [], [], '', '', null, []], + // No visible change - owner only + [Extension::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], [], '', 'admin', null, ['admin']], + // Visible change + [Extension::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], ['{DAV:}displayname' => 'Name'], '', 'admin', ['user1'], ['user1', 'admin']], + [Extension::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], ['{DAV:}displayname' => 'Name'], 'test2', 'test2', ['user1'], ['user1', 'admin']], + + // Delete calendar + [Extension::SUBJECT_DELETE, [], [], [], '', '', null, []], + [Extension::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], [], '', 'admin', [], ['admin']], + [Extension::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], [], '', 'admin', ['user1'], ['user1', 'admin']], + [Extension::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + '{DAV:}displayname' => 'Name of calendar', + ], ['shares'], [], 'test2', 'test2', ['user1'], ['user1', 'admin']], + ]; + } + + /** + * @dataProvider dataTriggerCalendarActivity + * @param string $action + * @param array $data + * @param array $shares + * @param array $changedProperties + * @param string $currentUser + * @param string $author + * @param string[]|null $shareUsers + * @param string[] $users + */ + public function testTriggerCalendarActivity($action, array $data, array $shares, array $changedProperties, $currentUser, $author, $shareUsers, array $users) { + $backend = $this->getBackend(['getUsersForShares']); + + if ($shareUsers === null) { + $backend->expects($this->never()) + ->method('getUsersForShares'); + } else { + $backend->expects($this->once()) + ->method('getUsersForShares') + ->with($shares) + ->willReturn($shareUsers); + } + + if ($author !== '') { + if ($currentUser !== '') { + $user = $this->createMock(IUser::class); + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $user->expects($this->once()) + ->method('getUID') + ->willReturn($currentUser); + } else { + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn(null); + } + + $event = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('generateEvent') + ->willReturn($event); + + $event->expects($this->once()) + ->method('setApp') + ->with('dav') + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setObject') + ->with(Extension::CALENDAR, $data['id']) + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setType') + ->with(Extension::CALENDAR) + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setAuthor') + ->with($author) + ->willReturnSelf(); + + $event->expects($this->exactly(sizeof($users))) + ->method('setAffectedUser') + ->willReturnSelf(); + $event->expects($this->exactly(sizeof($users))) + ->method('setSubject') + ->willReturnSelf(); + $this->activityManager->expects($this->exactly(sizeof($users))) + ->method('publish') + ->with($event); + } else { + $this->activityManager->expects($this->never()) + ->method('generateEvent'); + } + + $this->invokePrivate($backend, 'triggerCalendarActivity', [$action, $data, $shares, $changedProperties]); + } } From 7a0e003adee4e42e1801e35485b731a543f820e6 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 3 Nov 2016 15:24:18 +0100 Subject: [PATCH 13/13] Add more tests Signed-off-by: Joas Schilling --- .../unit/CalDAV/Activity/BackendTest.php | 107 +++++++++++++++++- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php index 74a2bac584..3585d69bad 100644 --- a/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php +++ b/apps/dav/tests/unit/CalDAV/Activity/BackendTest.php @@ -25,6 +25,7 @@ use OCA\DAV\CalDAV\Activity\Backend; use OCA\DAV\CalDAV\Activity\Extension; use OCP\Activity\IEvent; use OCP\Activity\IManager; +use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; @@ -181,13 +182,9 @@ class BackendTest extends TestCase { if ($author !== '') { if ($currentUser !== '') { - $user = $this->createMock(IUser::class); $this->userSession->expects($this->once()) ->method('getUser') - ->willReturn($user); - $user->expects($this->once()) - ->method('getUID') - ->willReturn($currentUser); + ->willReturn($this->getUserMock($currentUser)); } else { $this->userSession->expects($this->once()) ->method('getUser') @@ -232,4 +229,104 @@ class BackendTest extends TestCase { $this->invokePrivate($backend, 'triggerCalendarActivity', [$action, $data, $shares, $changedProperties]); } + + public function dataGetUsersForShares() { + return [ + [ + [], + [], + [], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user3'], + ], + [], + ['user1', 'user2', 'user3'], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'], + ], + ['group2' => null, 'group3' => null], + ['user1', 'user2'], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'], + ], + ['group2' => ['user1', 'user2', 'user3'], 'group3' => ['user2', 'user3', 'user4']], + ['user1', 'user2', 'user3', 'user4'], + ], + ]; + } + + /** + * @dataProvider dataGetUsersForShares + * @param array $shares + * @param array $groups + * @param array $expected + */ + public function testGetUsersForShares(array $shares, array $groups, array $expected) { + $backend = $this->getBackend(); + + $getGroups = []; + foreach ($groups as $gid => $members) { + if ($members === null) { + $getGroups[] = [$gid, null]; + continue; + } + + $group = $this->createMock(IGroup::class); + $group->expects($this->once()) + ->method('getUsers') + ->willReturn($this->getUsers($members)); + + $getGroups[] = [$gid, $group]; + } + + $this->groupManager->expects($this->exactly(sizeof($getGroups))) + ->method('get') + ->willReturnMap($getGroups); + + $users = $this->invokePrivate($backend, 'getUsersForShares', [$shares]); + sort($users); + $this->assertEquals($expected, $users); + } + + /** + * @param string[] $users + * @return IUser[]|\PHPUnit_Framework_MockObject_MockObject[] + */ + protected function getUsers(array $users) { + $list = []; + foreach ($users as $user) { + $list[] = $this->getUserMock($user); + } + return $list; + } + + /** + * @param string $uid + * @return IUser|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getUserMock($uid) { + $user = $this->createMock(IUser::class); + $user->expects($this->once()) + ->method('getUID') + ->willReturn($uid); + return $user; + } }