Merge pull request #23510 from owncloud/birthdays-on-shared-addressbooks

Propagate birthdays of shared addressbooks to the sharee's birthday c…
This commit is contained in:
Thomas Müller 2016-04-08 15:19:38 +02:00
commit 6f3eeeeb36
5 changed files with 148 additions and 33 deletions

View File

@ -27,6 +27,8 @@ use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\SyncJob; use OCA\DAV\CardDAV\SyncJob;
use OCA\DAV\CardDAV\SyncService; use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\GroupPrincipalBackend;
use OCA\DAV\HookManager; use OCA\DAV\HookManager;
use OCA\Dav\Migration\AddressBookAdapter; use OCA\Dav\Migration\AddressBookAdapter;
use OCA\Dav\Migration\CalendarAdapter; use OCA\Dav\Migration\CalendarAdapter;
@ -79,7 +81,7 @@ class Application extends App {
/** @var IAppContainer $c */ /** @var IAppContainer $c */
$db = $c->getServer()->getDatabaseConnection(); $db = $c->getServer()->getDatabaseConnection();
$dispatcher = $c->getServer()->getEventDispatcher(); $dispatcher = $c->getServer()->getEventDispatcher();
$principal = new \OCA\DAV\Connector\Sabre\Principal( $principal = new Principal(
$c->getServer()->getUserManager(), $c->getServer()->getUserManager(),
$c->getServer()->getGroupManager() $c->getServer()->getGroupManager()
); );
@ -89,7 +91,7 @@ class Application extends App {
$container->registerService('CalDavBackend', function($c) { $container->registerService('CalDavBackend', function($c) {
/** @var IAppContainer $c */ /** @var IAppContainer $c */
$db = $c->getServer()->getDatabaseConnection(); $db = $c->getServer()->getDatabaseConnection();
$principal = new \OCA\DAV\Connector\Sabre\Principal( $principal = new Principal(
$c->getServer()->getUserManager(), $c->getServer()->getUserManager(),
$c->getServer()->getGroupManager() $c->getServer()->getGroupManager()
); );
@ -122,11 +124,14 @@ class Application extends App {
$container->registerService('BirthdayService', function($c) { $container->registerService('BirthdayService', function($c) {
/** @var IAppContainer $c */ /** @var IAppContainer $c */
$g = new GroupPrincipalBackend(
$c->getServer()->getGroupManager()
);
return new BirthdayService( return new BirthdayService(
$c->query('CalDavBackend'), $c->query('CalDavBackend'),
$c->query('CardDavBackend') $c->query('CardDavBackend'),
$g
); );
}); });
} }
@ -147,6 +152,7 @@ class Application extends App {
$listener = function($event) { $listener = function($event) {
if ($event instanceof GenericEvent) { if ($event instanceof GenericEvent) {
/** @var BirthdayService $b */
$b = $this->getContainer()->query('BirthdayService'); $b = $this->getContainer()->query('BirthdayService');
$b->onCardChanged( $b->onCardChanged(
$event->getArgument('addressBookId'), $event->getArgument('addressBookId'),
@ -161,6 +167,7 @@ class Application extends App {
$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener); $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener);
$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function($event) { $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function($event) {
if ($event instanceof GenericEvent) { if ($event instanceof GenericEvent) {
/** @var BirthdayService $b */
$b = $this->getContainer()->query('BirthdayService'); $b = $this->getContainer()->query('BirthdayService');
$b->onCardDeleted( $b->onCardDeleted(
$event->getArgument('addressBookId'), $event->getArgument('addressBookId'),

View File

@ -23,6 +23,7 @@ namespace OCA\DAV\CalDAV;
use Exception; use Exception;
use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\DAV\GroupPrincipalBackend;
use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Reader; use Sabre\VObject\Reader;
@ -30,15 +31,20 @@ class BirthdayService {
const BIRTHDAY_CALENDAR_URI = 'contact_birthdays'; const BIRTHDAY_CALENDAR_URI = 'contact_birthdays';
/** @var GroupPrincipalBackend */
private $principalBackend;
/** /**
* BirthdayService constructor. * BirthdayService constructor.
* *
* @param CalDavBackend $calDavBackEnd * @param CalDavBackend $calDavBackEnd
* @param CardDavBackend $cardDavBackEnd * @param CardDavBackend $cardDavBackEnd
* @param GroupPrincipalBackend $principalBackend
*/ */
public function __construct($calDavBackEnd, $cardDavBackEnd) { public function __construct($calDavBackEnd, $cardDavBackEnd, $principalBackend) {
$this->calDavBackEnd = $calDavBackEnd; $this->calDavBackEnd = $calDavBackEnd;
$this->cardDavBackEnd = $cardDavBackEnd; $this->cardDavBackEnd = $cardDavBackEnd;
$this->principalBackend = $principalBackend;
} }
/** /**
@ -48,22 +54,26 @@ class BirthdayService {
*/ */
public function onCardChanged($addressBookId, $cardUri, $cardData) { public function onCardChanged($addressBookId, $cardUri, $cardData) {
$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId); $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
$principalUri = $book['principaluri']; $targetPrincipals[] = $book['principaluri'];
$calendar = $this->ensureCalendarExists($principalUri); foreach ($targetPrincipals as $principalUri) {
$objectUri = $book['uri'] . '-' . $cardUri. '.ics'; $calendar = $this->ensureCalendarExists($principalUri);
$calendarData = $this->buildBirthdayFromContact($cardData); $objectUri = $book['uri'] . '-' . $cardUri. '.ics';
$existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri); $calendarData = $this->buildBirthdayFromContact($cardData);
if (is_null($calendarData)) { $existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri);
if (!is_null($existing)) { if (is_null($calendarData)) {
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); if (!is_null($existing)) {
} $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri);
} else { }
if (is_null($existing)) {
$this->calDavBackEnd->createCalendarObject($calendar['id'], $objectUri, $calendarData->serialize());
} else { } else {
if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) { if (is_null($existing)) {
$this->calDavBackEnd->updateCalendarObject($calendar['id'], $objectUri, $calendarData->serialize()); $this->calDavBackEnd->createCalendarObject($calendar['id'], $objectUri, $calendarData->serialize());
} else {
if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) {
$this->calDavBackEnd->updateCalendarObject($calendar['id'], $objectUri, $calendarData->serialize());
}
} }
} }
} }
@ -74,11 +84,14 @@ class BirthdayService {
* @param string $cardUri * @param string $cardUri
*/ */
public function onCardDeleted($addressBookId, $cardUri) { public function onCardDeleted($addressBookId, $cardUri) {
$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId); $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
$principalUri = $book['principaluri']; $targetPrincipals[] = $book['principaluri'];
$calendar = $this->ensureCalendarExists($principalUri); foreach ($targetPrincipals as $principalUri) {
$objectUri = $book['uri'] . '-' . $cardUri. '.ics'; $calendar = $this->ensureCalendarExists($principalUri);
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); $objectUri = $book['uri'] . '-' . $cardUri . '.ics';
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri);
}
} }
/** /**
@ -190,4 +203,24 @@ class BirthdayService {
return false; return false;
} }
/**
* @param $addressBookId
* @return mixed
*/
protected function getAllAffectedPrincipals($addressBookId) {
$targetPrincipals = [];
$shares = $this->cardDavBackEnd->getShares($addressBookId);
foreach ($shares as $share) {
if ($share['{http://owncloud.org/ns}group-share']) {
$users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']);
foreach ($users as $user) {
$targetPrincipals[] = $user['uri'];
}
} else {
$targetPrincipals[] = $share['{http://owncloud.org/ns}principal'];
}
}
return array_values(array_unique($targetPrincipals, SORT_STRING));
}
} }

View File

@ -22,6 +22,7 @@ namespace OCA\DAV\DAV;
use OCP\IGroup; use OCP\IGroup;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IUser;
use Sabre\DAV\Exception; use Sabre\DAV\Exception;
use \Sabre\DAV\PropPatch; use \Sabre\DAV\PropPatch;
use Sabre\DAVACL\PrincipalBackend\BackendInterface; use Sabre\DAVACL\PrincipalBackend\BackendInterface;
@ -82,10 +83,10 @@ class GroupPrincipalBackend implements BackendInterface {
return null; return null;
} }
$name = $elements[2]; $name = $elements[2];
$user = $this->groupManager->get($name); $group = $this->groupManager->get($name);
if (!is_null($user)) { if (!is_null($group)) {
return $this->groupToPrincipal($user); return $this->groupToPrincipal($group);
} }
return null; return null;
@ -99,8 +100,23 @@ class GroupPrincipalBackend implements BackendInterface {
* @throws Exception * @throws Exception
*/ */
public function getGroupMemberSet($principal) { public function getGroupMemberSet($principal) {
// TODO: implement if we want that $elements = explode('/', $principal);
return []; if ($elements[0] !== 'principals') {
return [];
}
if ($elements[1] !== 'groups') {
return [];
}
$name = $elements[2];
$group = $this->groupManager->get($name);
if (is_null($group)) {
return [];
}
return array_map(function($user) {
return $this->userToPrincipal($user);
}, $group->getUsers());
} }
/** /**
@ -162,8 +178,21 @@ class GroupPrincipalBackend implements BackendInterface {
protected function groupToPrincipal($group) { protected function groupToPrincipal($group) {
$groupId = $group->getGID(); $groupId = $group->getGID();
$principal = [ $principal = [
'uri' => "principals/groups/$groupId", 'uri' => "principals/groups/$groupId",
'{DAV:}displayname' => $groupId, '{DAV:}displayname' => $groupId,
];
return $principal;
}
/**
* @param IUser $user
* @return array
*/
protected function userToPrincipal($user) {
$principal = [
'uri' => 'principals/users/' . $user->getUID(),
'{DAV:}displayname' => $user->getDisplayName(),
]; ];
return $principal; return $principal;

View File

@ -161,7 +161,8 @@ class Backend {
'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '', 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '',
'status' => 1, 'status' => 1,
'readOnly' => ($row['access'] == self::ACCESS_READ), 'readOnly' => ($row['access'] == self::ACCESS_READ),
'{'.\OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $row['principaluri'] '{http://owncloud.org/ns}principal' => $row['principaluri'],
'{http://owncloud.org/ns}group-share' => is_null($p)
]; ];
} }

View File

@ -24,6 +24,7 @@ namespace OCA\DAV\Tests\Unit\CardDAV;
use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\CalDAV\BirthdayService;
use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\DAV\GroupPrincipalBackend;
use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Reader; use Sabre\VObject\Reader;
use Test\TestCase; use Test\TestCase;
@ -36,14 +37,17 @@ class BirthdayServiceTest extends TestCase {
private $calDav; private $calDav;
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject */ /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject */
private $cardDav; private $cardDav;
/** @var GroupPrincipalBackend | \PHPUnit_Framework_MockObject_MockObject */
private $groupPrincialBackend;
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->calDav = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock(); $this->calDav = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
$this->cardDav = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock(); $this->cardDav = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
$this->groupPrincialBackend = $this->getMockBuilder('OCA\DAV\DAV\GroupPrincipalBackend')->disableOriginalConstructor()->getMock();
$this->service = new BirthdayService($this->calDav, $this->cardDav); $this->service = new BirthdayService($this->calDav, $this->cardDav, $this->groupPrincialBackend);
} }
/** /**
@ -77,6 +81,7 @@ class BirthdayServiceTest extends TestCase {
'id' => 1234 'id' => 1234
]); ]);
$this->calDav->expects($this->once())->method('deleteCalendarObject')->with(1234, 'default-gump.vcf.ics'); $this->calDav->expects($this->once())->method('deleteCalendarObject')->with(1234, 'default-gump.vcf.ics');
$this->cardDav->expects($this->once())->method('getShares')->willReturn([]);
$this->service->onCardDeleted(666, 'gump.vcf'); $this->service->onCardDeleted(666, 'gump.vcf');
} }
@ -96,10 +101,11 @@ class BirthdayServiceTest extends TestCase {
->willReturn([ ->willReturn([
'id' => 1234 'id' => 1234
]); ]);
$this->cardDav->expects($this->once())->method('getShares')->willReturn([]);
/** @var BirthdayService | \PHPUnit_Framework_MockObject_MockObject $service */ /** @var BirthdayService | \PHPUnit_Framework_MockObject_MockObject $service */
$service = $this->getMock('\OCA\DAV\CalDAV\BirthdayService', $service = $this->getMock('\OCA\DAV\CalDAV\BirthdayService',
['buildBirthdayFromContact', 'birthdayEvenChanged'], [$this->calDav, $this->cardDav]); ['buildBirthdayFromContact', 'birthdayEvenChanged'], [$this->calDav, $this->cardDav, $this->groupPrincialBackend]);
if ($expectedOp === 'delete') { if ($expectedOp === 'delete') {
$this->calDav->expects($this->once())->method('getCalendarObject')->willReturn(''); $this->calDav->expects($this->once())->method('getCalendarObject')->willReturn('');
@ -132,6 +138,45 @@ class BirthdayServiceTest extends TestCase {
$this->assertEquals($expected, $this->service->birthdayEvenChanged($old, $new)); $this->assertEquals($expected, $this->service->birthdayEvenChanged($old, $new));
} }
public function testGetAllAffectedPrincipals() {
$this->cardDav->expects($this->once())->method('getShares')->willReturn([
[
'{http://owncloud.org/ns}group-share' => false,
'{http://owncloud.org/ns}principal' => 'principals/users/user01'
],
[
'{http://owncloud.org/ns}group-share' => false,
'{http://owncloud.org/ns}principal' => 'principals/users/user01'
],
[
'{http://owncloud.org/ns}group-share' => false,
'{http://owncloud.org/ns}principal' => 'principals/users/user02'
],
[
'{http://owncloud.org/ns}group-share' => true,
'{http://owncloud.org/ns}principal' => 'principals/groups/users'
],
]);
$this->groupPrincialBackend->expects($this->once())->method('getGroupMemberSet')
->willReturn([
[
'uri' => 'principals/users/user01',
],
[
'uri' => 'principals/users/user02',
],
[
'uri' => 'principals/users/user03',
],
]);
$users = $this->invokePrivate($this->service, 'getAllAffectedPrincipals', [6666]);
$this->assertEquals([
'principals/users/user01',
'principals/users/user02',
'principals/users/user03'
], $users);
}
public function providesBirthday() { public function providesBirthday() {
return [ return [
[true, [true,