diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/dav/appinfo/info.xml | 4 | ||||
-rw-r--r-- | apps/dav/composer/composer/autoload_classmap.php | 8 | ||||
-rw-r--r-- | apps/dav/composer/composer/autoload_static.php | 8 | ||||
-rw-r--r-- | apps/dav/lib/AppInfo/Application.php | 18 | ||||
-rw-r--r-- | apps/dav/lib/CalDAV/Activity/Setting/CalDAVSetting.php | 2 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Backend.php | 485 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Filter.php | 87 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Provider/Addressbook.php | 191 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Provider/Base.php | 156 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Provider/Card.php | 144 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/Activity/Setting.php | 81 | ||||
-rw-r--r-- | apps/dav/lib/Listener/AddressbookListener.php | 120 | ||||
-rw-r--r-- | apps/dav/lib/Listener/CardListener.php | 108 |
13 files changed, 1411 insertions, 1 deletions
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index 60e38f55c69..ff99623e382 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -63,17 +63,21 @@ <setting>OCA\DAV\CalDAV\Activity\Setting\Calendar</setting> <setting>OCA\DAV\CalDAV\Activity\Setting\Event</setting> <setting>OCA\DAV\CalDAV\Activity\Setting\Todo</setting> + <setting>OCA\DAV\CardDAV\Activity\Setting</setting> </settings> <filters> <filter>OCA\DAV\CalDAV\Activity\Filter\Calendar</filter> <filter>OCA\DAV\CalDAV\Activity\Filter\Todo</filter> + <filter>OCA\DAV\CardDAV\Activity\Filter</filter> </filters> <providers> <provider>OCA\DAV\CalDAV\Activity\Provider\Calendar</provider> <provider>OCA\DAV\CalDAV\Activity\Provider\Event</provider> <provider>OCA\DAV\CalDAV\Activity\Provider\Todo</provider> + <provider>OCA\DAV\CardDAV\Activity\Provider\Addressbook</provider> + <provider>OCA\DAV\CardDAV\Activity\Provider\Card</provider> </providers> </activity> diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 5da3526c820..bedb7b6ec78 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -85,6 +85,12 @@ return array( 'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => $baseDir . '/../lib/CalDAV/WebcalCaching/Plugin.php', 'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => $baseDir . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php', 'OCA\\DAV\\Capabilities' => $baseDir . '/../lib/Capabilities.php', + 'OCA\\DAV\\CardDAV\\Activity\\Backend' => $baseDir . '/../lib/CardDAV/Activity/Backend.php', + 'OCA\\DAV\\CardDAV\\Activity\\Filter' => $baseDir . '/../lib/CardDAV/Activity/Filter.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Addressbook' => $baseDir . '/../lib/CardDAV/Activity/Provider/Addressbook.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Base' => $baseDir . '/../lib/CardDAV/Activity/Provider/Base.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Card' => $baseDir . '/../lib/CardDAV/Activity/Provider/Card.php', + 'OCA\\DAV\\CardDAV\\Activity\\Setting' => $baseDir . '/../lib/CardDAV/Activity/Setting.php', 'OCA\\DAV\\CardDAV\\AddressBook' => $baseDir . '/../lib/CardDAV/AddressBook.php', 'OCA\\DAV\\CardDAV\\AddressBookImpl' => $baseDir . '/../lib/CardDAV/AddressBookImpl.php', 'OCA\\DAV\\CardDAV\\AddressBookRoot' => $baseDir . '/../lib/CardDAV/AddressBookRoot.php', @@ -206,9 +212,11 @@ return array( 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', 'OCA\\DAV\\HookManager' => $baseDir . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => $baseDir . '/../lib/Listener/ActivityUpdaterListener.php', + 'OCA\\DAV\\Listener\\AddressbookListener' => $baseDir . '/../lib/Listener/AddressbookListener.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => $baseDir . '/../lib/Listener/CalendarContactInteractionListener.php', 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', 'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => $baseDir . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php', + 'OCA\\DAV\\Listener\\CardListener' => $baseDir . '/../lib/Listener/CardListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => $baseDir . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 60162ba5554..0a81ac8b4e9 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -100,6 +100,12 @@ class ComposerStaticInitDAV 'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/Plugin.php', 'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php', 'OCA\\DAV\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php', + 'OCA\\DAV\\CardDAV\\Activity\\Backend' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Backend.php', + 'OCA\\DAV\\CardDAV\\Activity\\Filter' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Filter.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Addressbook' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Provider/Addressbook.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Base' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Provider/Base.php', + 'OCA\\DAV\\CardDAV\\Activity\\Provider\\Card' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Provider/Card.php', + 'OCA\\DAV\\CardDAV\\Activity\\Setting' => __DIR__ . '/..' . '/../lib/CardDAV/Activity/Setting.php', 'OCA\\DAV\\CardDAV\\AddressBook' => __DIR__ . '/..' . '/../lib/CardDAV/AddressBook.php', 'OCA\\DAV\\CardDAV\\AddressBookImpl' => __DIR__ . '/..' . '/../lib/CardDAV/AddressBookImpl.php', 'OCA\\DAV\\CardDAV\\AddressBookRoot' => __DIR__ . '/..' . '/../lib/CardDAV/AddressBookRoot.php', @@ -221,9 +227,11 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', 'OCA\\DAV\\HookManager' => __DIR__ . '/..' . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/ActivityUpdaterListener.php', + 'OCA\\DAV\\Listener\\AddressbookListener' => __DIR__ . '/..' . '/../lib/Listener/AddressbookListener.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarContactInteractionListener.php', 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', 'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php', + 'OCA\\DAV\\Listener\\CardListener' => __DIR__ . '/..' . '/../lib/Listener/CardListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 8c1e4f77a12..d6c20f81d96 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -51,6 +51,10 @@ use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\Events\AddressBookCreatedEvent; +use OCA\DAV\Events\AddressBookDeletedEvent; +use OCA\DAV\Events\AddressBookShareUpdatedEvent; +use OCA\DAV\Events\AddressBookUpdatedEvent; use OCA\DAV\Events\CalendarCreatedEvent; use OCA\DAV\Events\CalendarDeletedEvent; use OCA\DAV\Events\CalendarObjectCreatedEvent; @@ -58,11 +62,16 @@ use OCA\DAV\Events\CalendarObjectDeletedEvent; use OCA\DAV\Events\CalendarObjectUpdatedEvent; use OCA\DAV\Events\CalendarShareUpdatedEvent; use OCA\DAV\Events\CalendarUpdatedEvent; +use OCA\DAV\Events\CardCreatedEvent; +use OCA\DAV\Events\CardDeletedEvent; +use OCA\DAV\Events\CardUpdatedEvent; use OCA\DAV\HookManager; use OCA\DAV\Listener\ActivityUpdaterListener; +use OCA\DAV\Listener\AddressbookListener; use OCA\DAV\Listener\CalendarContactInteractionListener; use OCA\DAV\Listener\CalendarDeletionDefaultUpdaterListener; use OCA\DAV\Listener\CalendarObjectReminderUpdaterListener; +use OCA\DAV\Listener\CardListener; use OCA\DAV\Search\ContactsSearchProvider; use OCA\DAV\Search\EventsSearchProvider; use OCA\DAV\Search\TasksSearchProvider; @@ -131,6 +140,15 @@ class Application extends App implements IBootstrap { $context->registerEventListener(CalendarObjectDeletedEvent::class, CalendarObjectReminderUpdaterListener::class); $context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarContactInteractionListener::class); + + $context->registerEventListener(AddressBookCreatedEvent::class, AddressbookListener::class); + $context->registerEventListener(AddressBookDeletedEvent::class, AddressbookListener::class); + $context->registerEventListener(AddressBookUpdatedEvent::class, AddressbookListener::class); + $context->registerEventListener(AddressBookShareUpdatedEvent::class, AddressbookListener::class); + $context->registerEventListener(CardCreatedEvent::class, CardListener::class); + $context->registerEventListener(CardDeletedEvent::class, CardListener::class); + $context->registerEventListener(CardUpdatedEvent::class, CardListener::class); + $context->registerNotifierService(Notifier::class); } diff --git a/apps/dav/lib/CalDAV/Activity/Setting/CalDAVSetting.php b/apps/dav/lib/CalDAV/Activity/Setting/CalDAVSetting.php index af036b7581b..a0b6b130489 100644 --- a/apps/dav/lib/CalDAV/Activity/Setting/CalDAVSetting.php +++ b/apps/dav/lib/CalDAV/Activity/Setting/CalDAVSetting.php @@ -45,6 +45,6 @@ abstract class CalDAVSetting extends ActivitySettings { } public function getGroupName() { - return $this->l->t('Calendar and tasks'); + return $this->l->t('Calendar, contacts and tasks'); } } diff --git a/apps/dav/lib/CardDAV/Activity/Backend.php b/apps/dav/lib/CardDAV/Activity/Backend.php new file mode 100644 index 00000000000..f23eee9d9ab --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Backend.php @@ -0,0 +1,485 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Joas Schilling <coding@schilljs.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity; + +use OCA\DAV\CardDAV\Activity\Provider\Addressbook; +use OCP\Activity\IEvent; +use OCP\Activity\IManager as IActivityManager; +use OCP\App\IAppManager; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use Sabre\CardDAV\Plugin; +use Sabre\VObject\Reader; + +class Backend { + + /** @var IActivityManager */ + protected $activityManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserSession */ + protected $userSession; + + /** @var IAppManager */ + protected $appManager; + + public function __construct(IActivityManager $activityManager, + IGroupManager $groupManager, + IUserSession $userSession, + IAppManager $appManager) { + $this->activityManager = $activityManager; + $this->groupManager = $groupManager; + $this->userSession = $userSession; + $this->appManager = $appManager; + } + + /** + * Creates activities when an addressbook was creates + * + * @param array $addressbookData + */ + public function onAddressbookCreate(array $addressbookData): void { + $this->triggerAddressbookActivity(Addressbook::SUBJECT_ADD, $addressbookData); + } + + /** + * Creates activities when a calendar was updated + * + * @param array $addressbookData + * @param array $shares + * @param array $properties + */ + public function onAddressbookUpdate(array $addressbookData, array $shares, array $properties): void { + $this->triggerAddressbookActivity(Addressbook::SUBJECT_UPDATE, $addressbookData, $shares, $properties); + } + + /** + * Creates activities when a calendar was deleted + * + * @param array $addressbookData + * @param array $shares + */ + public function onAddressbookDelete(array $addressbookData, array $shares): void { + $this->triggerAddressbookActivity(Addressbook::SUBJECT_DELETE, $addressbookData, $shares); + } + + /** + * Creates activities for all related users when a calendar was touched + * + * @param string $action + * @param array $addressbookData + * @param array $shares + * @param array $changedProperties + */ + protected function triggerAddressbookActivity(string $action, array $addressbookData, array $shares = [], array $changedProperties = []): void { + if (!isset($addressbookData['principaluri'])) { + return; + } + + $principal = explode('/', $addressbookData['principaluri']); + $owner = array_pop($principal); + + $currentUser = $this->userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $event = $this->activityManager->generateEvent(); + $event->setApp('dav') + ->setObject('addressbook', (int) $addressbookData['id']) + ->setType('addressbook') + ->setAuthor($currentUser); + + $changedVisibleInformation = array_intersect([ + '{DAV:}displayname', + '{' . Plugin::NS_CARDDAV . '}addressbook-description', + ], array_keys($changedProperties)); + + if (empty($shares) || ($action === Addressbook::SUBJECT_UPDATE && empty($changedVisibleInformation))) { + $users = [$owner]; + } else { + $users = $this->getUsersForShares($shares); + $users[] = $owner; + } + + foreach ($users as $user) { + $event->setAffectedUser($user) + ->setSubject( + $user === $currentUser ? $action . '_self' : $action, + [ + 'actor' => $currentUser, + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + ] + ); + $this->activityManager->publish($event); + } + } + + /** + * Creates activities for all related users when an addressbook was (un-)shared + * + * @param array $addressbookData + * @param array $shares + * @param array $add + * @param array $remove + */ + public function onAddressbookUpdateShares(array $addressbookData, array $shares, array $add, array $remove): void { + $principal = explode('/', $addressbookData['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('addressbook', (int) $addressbookData['id']) + ->setType('addressbook') + ->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, + $addressbookData, + Addressbook::SUBJECT_UNSHARE_USER, + Addressbook::SUBJECT_DELETE . '_self' + ); + + if ($owner !== $principal[2]) { + $parameters = [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + 'user' => $principal[2], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Addressbook::SUBJECT_UNSHARE_USER . '_you'; + } elseif ($principal[2] === $event->getAuthor()) { + $subject = Addressbook::SUBJECT_UNSHARE_USER . '_self'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Addressbook::SUBJECT_UNSHARE_USER . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Addressbook::SUBJECT_UNSHARE_USER . '_by'; + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } elseif ($principal[1] === 'groups') { + $this->triggerActivityGroup($principal[2], $event, $addressbookData, Addressbook::SUBJECT_UNSHARE_USER); + + $parameters = [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + 'group' => $principal[2], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Addressbook::SUBJECT_UNSHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Addressbook::SUBJECT_UNSHARE_GROUP . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Addressbook::SUBJECT_UNSHARE_GROUP . '_by'; + } + + $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, $addressbookData, Addressbook::SUBJECT_SHARE_USER); + + if ($owner !== $principal[2]) { + $parameters = [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + 'user' => $principal[2], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Addressbook::SUBJECT_SHARE_USER . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Addressbook::SUBJECT_SHARE_USER . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Addressbook::SUBJECT_SHARE_USER . '_by'; + } + + $event->setAffectedUser($owner) + ->setSubject($subject, $parameters); + $this->activityManager->publish($event); + } + } elseif ($principal[1] === 'groups') { + $this->triggerActivityGroup($principal[2], $event, $addressbookData, Addressbook::SUBJECT_SHARE_USER); + + $parameters = [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + 'group' => $principal[2], + ]; + + if ($owner === $event->getAuthor()) { + $subject = Addressbook::SUBJECT_SHARE_GROUP . '_you'; + } else { + $event->setAffectedUser($event->getAuthor()) + ->setSubject(Addressbook::SUBJECT_SHARE_GROUP . '_you', $parameters); + $this->activityManager->publish($event); + + $subject = Addressbook::SUBJECT_SHARE_GROUP . '_by'; + } + + $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(string $principal, array $shares): bool { + 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(string $gid, IEvent $event, array $properties, string $subject): void { + $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(string $user, IEvent $event, array $properties, string $subject, string $subjectSelf = ''): void { + $event->setAffectedUser($user) + ->setSubject( + $user === $event->getAuthor() && $subjectSelf ? $subjectSelf : $subject, + [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $properties['id'], + 'uri' => $properties['uri'], + 'name' => $properties['{DAV:}displayname'], + ], + ] + ); + + $this->activityManager->publish($event); + } + + /** + * Creates activities when a card was created/updated/deleted + * + * @param string $action + * @param array $addressbookData + * @param array $shares + * @param array $cardData + */ + public function triggerCardActivity(string $action, array $addressbookData, array $shares, array $cardData): void { + if (!isset($addressbookData['principaluri'])) { + return; + } + + $principal = explode('/', $addressbookData['principaluri']); + $owner = array_pop($principal); + + $currentUser = $this->userSession->getUser(); + if ($currentUser instanceof IUser) { + $currentUser = $currentUser->getUID(); + } else { + $currentUser = $owner; + } + + $card = $this->getCardNameAndId($cardData); + + $event = $this->activityManager->generateEvent(); + $event->setApp('dav') + ->setObject('addressbook', (int) $addressbookData['id']) + ->setType('card') + ->setAuthor($currentUser); + + $users = $this->getUsersForShares($shares); + $users[] = $owner; + + // Users for share can return the owner itself if the calendar is published + foreach (array_unique($users) as $user) { + $params = [ + 'actor' => $event->getAuthor(), + 'addressbook' => [ + 'id' => (int) $addressbookData['id'], + 'uri' => $addressbookData['uri'], + 'name' => $addressbookData['{DAV:}displayname'], + ], + 'card' => [ + 'id' => $card['id'], + 'name' => $card['name'], + ], + ]; + + + $event->setAffectedUser($user) + ->setSubject( + $user === $currentUser ? $action . '_self' : $action, + $params + ); + + $this->activityManager->publish($event); + } + } + + /** + * @param array $cardData + * @return string[] + */ + protected function getCardNameAndId(array $cardData): array { + $vObject = Reader::read($cardData['carddata']); + return ['id' => (string) $vObject->UID, 'name' => (string) ($vObject->FN ?? '')]; + } + + /** + * Get all users that have access to a given calendar + * + * @param array $shares + * @return string[] + */ + protected function getUsersForShares(array $shares): array { + $users = $groups = []; + foreach ($shares as $share) { + $principal = explode('/', $share['{http://owncloud.org/ns}principal']); + if ($principal[1] === 'users') { + $users[] = $principal[2]; + } elseif ($principal[1] === 'groups') { + $groups[] = $principal[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 array_unique($users); + } +} diff --git a/apps/dav/lib/CardDAV/Activity/Filter.php b/apps/dav/lib/CardDAV/Activity/Filter.php new file mode 100644 index 00000000000..9c9c0403d78 --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Filter.php @@ -0,0 +1,87 @@ +<?php +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity; + +use OCP\Activity\IFilter; +use OCP\IL10N; +use OCP\IURLGenerator; + +class Filter implements IFilter { + + /** @var IL10N */ + protected $l; + + /** @var IURLGenerator */ + protected $url; + + public function __construct(IL10N $l, IURLGenerator $url) { + $this->l = $l; + $this->url = $url; + } + + /** + * @return string Lowercase a-z and underscore only identifier + */ + public function getIdentifier(): string { + return 'contacts'; + } + + /** + * @return string A translated string + */ + public function getName(): string { + return $this->l->t('Contacts'); + } + + /** + * @return int whether the filter should be rather on the top or bottom of + * the admin section. The filters are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + */ + public function getPriority(): int { + return 40; + } + + /** + * @return string Full URL to an icon, empty string when none is given + */ + public function getIcon(): string { + return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts.svg')); + } + + /** + * @param string[] $types + * @return string[] An array of allowed apps from which activities should be displayed + */ + public function filterTypes(array $types): array { + return array_intersect(['contacts'], $types); + } + + /** + * @return string[] An array of allowed apps from which activities should be displayed + */ + public function allowedApps(): array { + return []; + } +} diff --git a/apps/dav/lib/CardDAV/Activity/Provider/Addressbook.php b/apps/dav/lib/CardDAV/Activity/Provider/Addressbook.php new file mode 100644 index 00000000000..26ee637fcbc --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Provider/Addressbook.php @@ -0,0 +1,191 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity\Provider; + +use OCP\Activity\IEvent; +use OCP\Activity\IEventMerger; +use OCP\Activity\IManager; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\L10N\IFactory; + +class Addressbook extends Base { + public const SUBJECT_ADD = 'addressbook_add'; + public const SUBJECT_UPDATE = 'addressbook_update'; + public const SUBJECT_DELETE = 'addressbook_delete'; + public const SUBJECT_SHARE_USER = 'addressbook_user_share'; + public const SUBJECT_SHARE_GROUP = 'addressbook_group_share'; + public const SUBJECT_UNSHARE_USER = 'addressbook_user_unshare'; + public const SUBJECT_UNSHARE_GROUP = 'addressbook_group_unshare'; + + /** @var IFactory */ + protected $languageFactory; + + /** @var IManager */ + protected $activityManager; + + /** @var IEventMerger */ + protected $eventMerger; + + public function __construct(IFactory $languageFactory, + IURLGenerator $url, + IManager $activityManager, + IUserManager $userManager, + IGroupManager $groupManager, + IEventMerger $eventMerger) { + parent::__construct($userManager, $groupManager, $url); + $this->languageFactory = $languageFactory; + $this->activityManager = $activityManager; + $this->eventMerger = $eventMerger; + } + + /** + * @param string $language + * @param IEvent $event + * @param IEvent|null $previousEvent + * @return IEvent + * @throws \InvalidArgumentException + */ + public function parse($language, IEvent $event, IEvent $previousEvent = null): IEvent { + if ($event->getApp() !== 'dav' || $event->getType() !== 'addressbook') { + throw new \InvalidArgumentException(); + } + + $l = $this->languageFactory->get('dav', $language); + + if ($this->activityManager->getRequirePNG()) { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts-dark.png'))); + } else { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts.svg'))); + } + + if ($event->getSubject() === self::SUBJECT_ADD) { + $subject = $l->t('{actor} created addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_ADD . '_self') { + $subject = $l->t('You created addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_DELETE) { + $subject = $l->t('{actor} deleted addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_DELETE . '_self') { + $subject = $l->t('You deleted addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_UPDATE) { + $subject = $l->t('{actor} updated addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_UPDATE . '_self') { + $subject = $l->t('You updated addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_SHARE_USER) { + $subject = $l->t('{actor} shared addressbook {addressbook} with you'); + } elseif ($event->getSubject() === self::SUBJECT_SHARE_USER . '_you') { + $subject = $l->t('You shared addressbook {addressbook} with {user}'); + } elseif ($event->getSubject() === self::SUBJECT_SHARE_USER . '_by') { + $subject = $l->t('{actor} shared addressbook {addressbook} with {user}'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_USER) { + $subject = $l->t('{actor} unshared addressbook {addressbook} from you'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_you') { + $subject = $l->t('You unshared addressbook {addressbook} from {user}'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_by') { + $subject = $l->t('{actor} unshared addressbook {addressbook} from {user}'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_self') { + $subject = $l->t('{actor} unshared addressbook {addressbook} from themselves'); + } elseif ($event->getSubject() === self::SUBJECT_SHARE_GROUP . '_you') { + $subject = $l->t('You shared addressbook {addressbook} with group {group}'); + } elseif ($event->getSubject() === self::SUBJECT_SHARE_GROUP . '_by') { + $subject = $l->t('{actor} shared addressbook {addressbook} with group {group}'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_GROUP . '_you') { + $subject = $l->t('You unshared addressbook {addressbook} from group {group}'); + } elseif ($event->getSubject() === self::SUBJECT_UNSHARE_GROUP . '_by') { + $subject = $l->t('{actor} unshared addressbook {addressbook} from group {group}'); + } else { + throw new \InvalidArgumentException(); + } + + $parsedParameters = $this->getParameters($event, $l); + $this->setSubjects($event, $subject, $parsedParameters); + + $event = $this->eventMerger->mergeEvents('addressbook', $event, $previousEvent); + + if ($event->getChildEvent() === null) { + if (isset($parsedParameters['user'])) { + // Couldn't group by calendar, maybe we can group by users + $event = $this->eventMerger->mergeEvents('user', $event, $previousEvent); + } elseif (isset($parsedParameters['group'])) { + // Couldn't group by calendar, maybe we can group by groups + $event = $this->eventMerger->mergeEvents('group', $event, $previousEvent); + } + } + + return $event; + } + + protected function getParameters(IEvent $event, IL10N $l): array { + $subject = $event->getSubject(); + $parameters = $event->getSubjectParameters(); + + switch ($subject) { + 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_SHARE_USER: + case self::SUBJECT_UNSHARE_USER: + case self::SUBJECT_UNSHARE_USER . '_self': + return [ + 'actor' => $this->generateUserParameter($parameters['actor']), + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + ]; + case self::SUBJECT_SHARE_USER . '_you': + case self::SUBJECT_UNSHARE_USER . '_you': + return [ + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'user' => $this->generateUserParameter($parameters['user']), + ]; + case self::SUBJECT_SHARE_USER . '_by': + case self::SUBJECT_UNSHARE_USER . '_by': + return [ + 'actor' => $this->generateUserParameter($parameters['actor']), + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'user' => $this->generateUserParameter($parameters['user']), + ]; + case self::SUBJECT_SHARE_GROUP . '_you': + case self::SUBJECT_UNSHARE_GROUP . '_you': + return [ + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'group' => $this->generateGroupParameter($parameters['group']), + ]; + case self::SUBJECT_SHARE_GROUP . '_by': + case self::SUBJECT_UNSHARE_GROUP . '_by': + return [ + 'actor' => $this->generateUserParameter($parameters['actor']), + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'group' => $this->generateGroupParameter($parameters['group']), + ]; + } + + throw new \InvalidArgumentException(); + } +} diff --git a/apps/dav/lib/CardDAV/Activity/Provider/Base.php b/apps/dav/lib/CardDAV/Activity/Provider/Base.php new file mode 100644 index 00000000000..9a9b58e6c6e --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Provider/Base.php @@ -0,0 +1,156 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity\Provider; + +use OCA\DAV\CardDAV\CardDavBackend; +use OCP\Activity\IEvent; +use OCP\Activity\IProvider; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUser; +use OCP\IUserManager; + +abstract class Base implements IProvider { + + /** @var IUserManager */ + protected $userManager; + + /** @var string[] */ + protected $userDisplayNames = []; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var string[] */ + protected $groupDisplayNames = []; + + /** @var IURLGenerator */ + protected $url; + + public function __construct(IUserManager $userManager, + IGroupManager $groupManager, + IURLGenerator $urlGenerator) { + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->url = $urlGenerator; + } + + /** + * @param IEvent $event + * @param string $subject + * @param array $parameters + */ + protected function setSubjects(IEvent $event, string $subject, array $parameters): void { + $placeholders = $replacements = []; + foreach ($parameters as $placeholder => $parameter) { + $placeholders[] = '{' . $placeholder . '}'; + $replacements[] = $parameter['name']; + } + + $event->setParsedSubject(str_replace($placeholders, $replacements, $subject)) + ->setRichSubject($subject, $parameters); + } + + /** + * @param array $data + * @param IL10N $l + * @return array + */ + protected function generateAddressbookParameter(array $data, IL10N $l): array { + if ($data['uri'] === CardDavBackend::PERSONAL_ADDRESSBOOK_URI && + $data['name'] === CardDavBackend::PERSONAL_ADDRESSBOOK_NAME) { + return [ + 'type' => 'addressbook', + 'id' => $data['id'], + 'name' => $l->t('Personal'), + ]; + } + + return [ + 'type' => 'addressbook', + 'id' => $data['id'], + 'name' => $data['name'], + ]; + } + + /** + * @param string $uid + * @return array + */ + protected function generateUserParameter(string $uid): array { + if (!isset($this->userDisplayNames[$uid])) { + $this->userDisplayNames[$uid] = $this->getUserDisplayName($uid); + } + + return [ + 'type' => 'user', + 'id' => $uid, + 'name' => $this->userDisplayNames[$uid], + ]; + } + + /** + * @param string $uid + * @return string + */ + protected function getUserDisplayName(string $uid): string { + $user = $this->userManager->get($uid); + if ($user instanceof IUser) { + return $user->getDisplayName(); + } + return $uid; + } + + /** + * @param string $gid + * @return array + */ + protected function generateGroupParameter(string $gid): array { + if (!isset($this->groupDisplayNames[$gid])) { + $this->groupDisplayNames[$gid] = $this->getGroupDisplayName($gid); + } + + return [ + 'type' => 'user-group', + 'id' => $gid, + 'name' => $this->groupDisplayNames[$gid], + ]; + } + + /** + * @param string $gid + * @return string + */ + protected function getGroupDisplayName(string $gid): string { + $group = $this->groupManager->get($gid); + if ($group instanceof IGroup) { + return $group->getDisplayName(); + } + return $gid; + } +} diff --git a/apps/dav/lib/CardDAV/Activity/Provider/Card.php b/apps/dav/lib/CardDAV/Activity/Provider/Card.php new file mode 100644 index 00000000000..a1b5ab97206 --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Provider/Card.php @@ -0,0 +1,144 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity\Provider; + +use OCP\Activity\IEvent; +use OCP\Activity\IEventMerger; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\L10N\IFactory; + +class Card extends Base { + public const SUBJECT_ADD = 'card_add'; + public const SUBJECT_UPDATE = 'card_update'; + public const SUBJECT_DELETE = 'card_delete'; + + /** @var IFactory */ + protected $languageFactory; + + /** @var IManager */ + protected $activityManager; + + /** @var IEventMerger */ + protected $eventMerger; + + /** @var IAppManager */ + protected $appManager; + + public function __construct(IFactory $languageFactory, + IURLGenerator $url, + IManager $activityManager, + IUserManager $userManager, + IGroupManager $groupManager, + IEventMerger $eventMerger, + IAppManager $appManager) { + parent::__construct($userManager, $groupManager, $url); + $this->languageFactory = $languageFactory; + $this->activityManager = $activityManager; + $this->eventMerger = $eventMerger; + $this->appManager = $appManager; + } + + /** + * @param string $language + * @param IEvent $event + * @param IEvent|null $previousEvent + * @return IEvent + * @throws \InvalidArgumentException + */ + public function parse($language, IEvent $event, IEvent $previousEvent = null): IEvent { + if ($event->getApp() !== 'dav' || $event->getType() !== 'card') { + throw new \InvalidArgumentException(); + } + + $l = $this->languageFactory->get('dav', $language); + + if ($this->activityManager->getRequirePNG()) { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts-dark.png'))); + } else { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts.svg'))); + } + + if ($event->getSubject() === self::SUBJECT_ADD) { + $subject = $l->t('{actor} created contact {card} in addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_ADD . '_self') { + $subject = $l->t('You created contact {card} in addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_DELETE) { + $subject = $l->t('{actor} deleted contact {card} from addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_DELETE . '_self') { + $subject = $l->t('You deleted contact {card} from addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_UPDATE) { + $subject = $l->t('{actor} updated contact {card} in addressbook {addressbook}'); + } elseif ($event->getSubject() === self::SUBJECT_UPDATE . '_self') { + $subject = $l->t('You updated contact {card} in addressbook {addressbook}'); + } else { + throw new \InvalidArgumentException(); + } + + $parsedParameters = $this->getParameters($event, $l); + $this->setSubjects($event, $subject, $parsedParameters); + + $event = $this->eventMerger->mergeEvents('card', $event, $previousEvent); + return $event; + } + + protected function getParameters(IEvent $event, IL10N $l): array { + $subject = $event->getSubject(); + $parameters = $event->getSubjectParameters(); + + switch ($subject) { + case self::SUBJECT_ADD: + case self::SUBJECT_DELETE: + case self::SUBJECT_UPDATE: + return [ + 'actor' => $this->generateUserParameter($parameters['actor']), + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'card' => $this->generateCardParameter($parameters['card']), + ]; + case self::SUBJECT_ADD . '_self': + case self::SUBJECT_DELETE . '_self': + case self::SUBJECT_UPDATE . '_self': + return [ + 'addressbook' => $this->generateAddressbookParameter($parameters['addressbook'], $l), + 'card' => $this->generateCardParameter($parameters['card']), + ]; + } + + throw new \InvalidArgumentException(); + } + + private function generateCardParameter(array $cardData): array { + return [ + 'type' => 'addressbook-contact', + 'id' => $cardData['id'], + 'name' => $cardData['name'], + ]; + } +} diff --git a/apps/dav/lib/CardDAV/Activity/Setting.php b/apps/dav/lib/CardDAV/Activity/Setting.php new file mode 100644 index 00000000000..2226d0ba5ce --- /dev/null +++ b/apps/dav/lib/CardDAV/Activity/Setting.php @@ -0,0 +1,81 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\CardDAV\Activity; + +use OCA\DAV\CalDAV\Activity\Setting\CalDAVSetting; + +class Setting extends CalDAVSetting { + /** + * @return string Lowercase a-z and underscore only identifier + */ + public function getIdentifier(): string { + return 'contacts'; + } + + /** + * @return string A translated string + */ + public function getName(): string { + return $this->l->t('A <strong>contact</strong> or <strong>addressbook</strong> was modified'); + } + + /** + * @return int whether the filter should be rather on the top or bottom of + * the admin section. The filters are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + */ + public function getPriority(): int { + return 50; + } + + /** + * @return bool True when the option can be changed for the stream + */ + public function canChangeStream(): bool { + return true; + } + + /** + * @return bool True when the option can be changed for the stream + */ + public function isDefaultEnabledStream(): bool { + return true; + } + + /** + * @return bool True when the option can be changed for the mail + */ + public function canChangeMail(): bool { + return true; + } + + /** + * @return bool True when the option can be changed for the stream + */ + public function isDefaultEnabledMail(): bool { + return false; + } +} diff --git a/apps/dav/lib/Listener/AddressbookListener.php b/apps/dav/lib/Listener/AddressbookListener.php new file mode 100644 index 00000000000..ff86fa55c62 --- /dev/null +++ b/apps/dav/lib/Listener/AddressbookListener.php @@ -0,0 +1,120 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCA\DAV\Listener; + +use OCA\DAV\CardDAV\Activity\Backend as ActivityBackend; +use OCA\DAV\Events\AddressBookCreatedEvent; +use OCA\DAV\Events\AddressBookDeletedEvent; +use OCA\DAV\Events\AddressBookShareUpdatedEvent; +use OCA\DAV\Events\AddressBookUpdatedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; +use Throwable; +use function sprintf; + +class AddressbookListener implements IEventListener { + /** @var ActivityBackend */ + private $activityBackend; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(ActivityBackend $activityBackend, + LoggerInterface $logger) { + $this->activityBackend = $activityBackend; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if ($event instanceof AddressBookCreatedEvent) { + try { + $this->activityBackend->onAddressbookCreate( + $event->getAddressBookData() + ); + + $this->logger->debug( + sprintf('Activity generated for new addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook creation, so we just log it + $this->logger->error('Error generating activities for a new addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } elseif ($event instanceof AddressBookUpdatedEvent) { + try { + $this->activityBackend->onAddressbookUpdate( + $event->getAddressBookData(), + $event->getShares(), + $event->getMutations() + ); + + $this->logger->debug( + sprintf('Activity generated for changed addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook update, so we just log it + $this->logger->error('Error generating activities for a changed addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } elseif ($event instanceof AddressBookDeletedEvent) { + try { + $this->activityBackend->onAddressbookDelete( + $event->getAddressBookData(), + $event->getShares() + ); + + $this->logger->debug( + sprintf('Activity generated for deleted addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook deletion, so we just log it + $this->logger->error('Error generating activities for a deleted addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } elseif ($event instanceof AddressBookShareUpdatedEvent) { + try { + $this->activityBackend->onAddressbookUpdateShares( + $event->getAddressBookData(), + $event->getOldShares(), + $event->getAdded(), + $event->getRemoved() + ); + + $this->logger->debug( + sprintf('Activity generated for (un)sharing addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook creation, so we just log it + $this->logger->error('Error generating activities for (un)sharing addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } + } +} diff --git a/apps/dav/lib/Listener/CardListener.php b/apps/dav/lib/Listener/CardListener.php new file mode 100644 index 00000000000..a73ca8191f7 --- /dev/null +++ b/apps/dav/lib/Listener/CardListener.php @@ -0,0 +1,108 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCA\DAV\Listener; + +use OCA\DAV\CardDAV\Activity\Provider\Card; +use OCA\DAV\CardDAV\Activity\Backend as ActivityBackend; +use OCA\DAV\Events\CardCreatedEvent; +use OCA\DAV\Events\CardDeletedEvent; +use OCA\DAV\Events\CardUpdatedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; +use Throwable; +use function sprintf; + +class CardListener implements IEventListener { + /** @var ActivityBackend */ + private $activityBackend; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(ActivityBackend $activityBackend, + LoggerInterface $logger) { + $this->activityBackend = $activityBackend; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if ($event instanceof CardCreatedEvent) { + try { + $this->activityBackend->triggerCardActivity( + Card::SUBJECT_ADD, + $event->getAddressBookData(), + $event->getShares(), + $event->getCardData() + ); + + $this->logger->debug( + sprintf('Activity generated for a new card in addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook creation, so we just log it + $this->logger->error('Error generating activities for a new card in addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } elseif ($event instanceof CardUpdatedEvent) { + try { + $this->activityBackend->triggerCardActivity( + Card::SUBJECT_UPDATE, + $event->getAddressBookData(), + $event->getShares(), + $event->getCardData() + ); + + $this->logger->debug( + sprintf('Activity generated for a changed card in addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook update, so we just log it + $this->logger->error('Error generating activities for a changed card in addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } elseif ($event instanceof CardDeletedEvent) { + try { + $this->activityBackend->triggerCardActivity( + Card::SUBJECT_DELETE, + $event->getAddressBookData(), + $event->getShares(), + $event->getCardData() + ); + + $this->logger->debug( + sprintf('Activity generated for a deleted card in addressbook %d', $event->getAddressBookId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the addressbook deletion, so we just log it + $this->logger->error('Error generating activities for a deleted card in addressbook: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } + } +} |