diff --git a/apps/dav/appinfo/application.php b/apps/dav/appinfo/application.php index df3bd34d47..7a201e1dd7 100644 --- a/apps/dav/appinfo/application.php +++ b/apps/dav/appinfo/application.php @@ -111,6 +111,15 @@ class Application extends App { $c->query('CalDavBackend') ); }); + + $container->registerService('BirthdayService', function($c) { + /** @var IAppContainer $c */ + return new BirthdayService( + $c->query('CalDavBackend'), + $c->query('CardDavBackend') + ); + + }); } /** @@ -130,10 +139,7 @@ class Application extends App { $listener = function($event) { if ($event instanceof GenericEvent) { - $b = new BirthdayService( - $this->getContainer()->query('CalDavBackend'), - $this->getContainer()->query('CardDavBackend') - ); + $b = $this->getContainer()->query('BirthdayService'); $b->onCardChanged( $event->getArgument('addressBookId'), $event->getArgument('cardUri'), @@ -147,10 +153,7 @@ class Application extends App { $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener); $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function($event) { if ($event instanceof GenericEvent) { - $b = new BirthdayService( - $this->getContainer()->query('CalDavBackend'), - $this->getContainer()->query('CardDavBackend') - ); + $b = $this->getContainer()->query('BirthdayService'); $b->onCardDeleted( $event->getArgument('addressBookId'), $event->getArgument('cardUri') diff --git a/apps/dav/appinfo/register_command.php b/apps/dav/appinfo/register_command.php index 963bea16ca..e07f6b4a25 100644 --- a/apps/dav/appinfo/register_command.php +++ b/apps/dav/appinfo/register_command.php @@ -24,6 +24,7 @@ use OCA\DAV\Command\CreateAddressBook; use OCA\DAV\Command\CreateCalendar; use OCA\Dav\Command\MigrateAddressbooks; use OCA\Dav\Command\MigrateCalendars; +use OCA\DAV\Command\SyncBirthdayCalendar; use OCA\DAV\Command\SyncSystemAddressBook; $dbConnection = \OC::$server->getDatabaseConnection(); @@ -37,6 +38,7 @@ $app = new Application(); $application->add(new CreateCalendar($userManager, $groupManager, $dbConnection)); $application->add(new CreateAddressBook($userManager, $app->getContainer()->query('CardDavBackend'))); $application->add(new SyncSystemAddressBook($app->getSyncService())); +$application->add(new SyncBirthdayCalendar($userManager, $app->getContainer()->query('BirthdayService'))); // the occ tool is *for now* only available in debug mode for developers to test if ($config->getSystemValue('debug', false)){ diff --git a/apps/dav/command/syncbirthdaycalendar.php b/apps/dav/command/syncbirthdaycalendar.php new file mode 100644 index 0000000000..66ab540b9a --- /dev/null +++ b/apps/dav/command/syncbirthdaycalendar.php @@ -0,0 +1,85 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCA\DAV\Command; + +use OCA\DAV\CalDAV\BirthdayService; +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class SyncBirthdayCalendar extends Command { + + /** @var BirthdayService */ + private $birthdayService; + + /** @var IUserManager */ + private $userManager; + + /** + * @param IUserManager $userManager + * @param BirthdayService $birthdayService + */ + function __construct(IUserManager $userManager, BirthdayService $birthdayService) { + parent::__construct(); + $this->birthdayService = $birthdayService; + $this->userManager = $userManager; + } + + protected function configure() { + $this + ->setName('dav:sync-birthday-calendar') + ->setDescription('Synchronizes the birthday calendar') + ->addArgument('user', + InputArgument::OPTIONAL, + 'User for whom the birthday calendar will be synchronized'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output) { + if ($input->hasArgument('user')) { + $user = $input->getArgument('user'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User <$user> in unknown."); + } + $output->writeln("Start birthday calendar sync for $user"); + $this->birthdayService->syncUser($user); + return; + } + $output->writeln("Start birthday calendar sync for all users ..."); + $p = new ProgressBar($output); + $p->start(); + $this->userManager->callForAllUsers(function($user) use ($p) { + $p->advance(); + /** @var IUser $user */ + $this->birthdayService->syncUser($user->getUID()); + }); + + $p->finish(); + $output->writeln(''); + } +} diff --git a/apps/dav/command/syncsystemaddressbook.php b/apps/dav/command/syncsystemaddressbook.php index b83a37131c..b62a42d7b9 100644 --- a/apps/dav/command/syncsystemaddressbook.php +++ b/apps/dav/command/syncsystemaddressbook.php @@ -34,7 +34,6 @@ class SyncSystemAddressBook extends Command { private $syncService; /** - * @param IUserManager $userManager * @param SyncService $syncService */ function __construct(SyncService $syncService) { diff --git a/apps/dav/lib/caldav/birthdayservice.php b/apps/dav/lib/caldav/birthdayservice.php index 5a0dfb6992..3b0a2a10e1 100644 --- a/apps/dav/lib/caldav/birthdayservice.php +++ b/apps/dav/lib/caldav/birthdayservice.php @@ -54,14 +54,18 @@ class BirthdayService { $calendar = $this->ensureCalendarExists($principalUri, $calendarUri, []); $objectUri = $book['uri'] . '-' . $cardUri. '.ics'; $calendarData = $this->buildBirthdayFromContact($cardData); + $existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri); if (is_null($calendarData)) { - $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); + if (!is_null($existing)) { + $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); + } } else { - $existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri); if (is_null($existing)) { $this->calDavBackEnd->createCalendarObject($calendar['id'], $objectUri, $calendarData->serialize()); } else { - $this->calDavBackEnd->updateCalendarObject($calendar['id'], $objectUri, $calendarData->serialize()); + if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) { + $this->calDavBackEnd->updateCalendarObject($calendar['id'], $objectUri, $calendarData->serialize()); + } } } } @@ -148,4 +152,36 @@ class BirthdayService { return $vCal; } + /** + * @param string $user + */ + public function syncUser($user) { + $books = $this->cardDavBackEnd->getAddressBooksForUser('principals/users/'.$user); + foreach($books as $book) { + $cards = $this->cardDavBackEnd->getCards($book['id']); + foreach($cards as $card) { + $this->onCardChanged($book['id'], $card['uri'], $card['carddata']); + } + } + } + + /** + * @param string $existingCalendarData + * @param VCalendar $newCalendarData + * @return bool + */ + public function birthdayEvenChanged($existingCalendarData, $newCalendarData) { + try { + $existingBirthday = Reader::read($existingCalendarData); + } catch (Exception $ex) { + return true; + } + if ($newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || + $newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue() + ) { + return true; + } + return false; + } + } diff --git a/apps/dav/tests/unit/carddav/birthdayservicetest.php b/apps/dav/tests/unit/carddav/birthdayservicetest.php index ab10ab26c1..faf42d9e1b 100644 --- a/apps/dav/tests/unit/carddav/birthdayservicetest.php +++ b/apps/dav/tests/unit/carddav/birthdayservicetest.php @@ -25,6 +25,7 @@ use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CardDAV\CardDavBackend; use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Reader; use Test\TestCase; class BirthdayServiceTest extends TestCase { @@ -98,9 +99,10 @@ class BirthdayServiceTest extends TestCase { /** @var BirthdayService | \PHPUnit_Framework_MockObject_MockObject $service */ $service = $this->getMock('\OCA\DAV\CalDAV\BirthdayService', - ['buildBirthdayFromContact'], [$this->calDav, $this->cardDav]); + ['buildBirthdayFromContact', 'birthdayEvenChanged'], [$this->calDav, $this->cardDav]); if ($expectedOp === 'delete') { + $this->calDav->expects($this->once())->method('getCalendarObject')->willReturn(''); $service->expects($this->once())->method('buildBirthdayFromContact')->willReturn(null); $this->calDav->expects($this->once())->method('deleteCalendarObject')->with(1234, 'default-gump.vcf.ics'); } @@ -110,13 +112,43 @@ class BirthdayServiceTest extends TestCase { } if ($expectedOp === 'update') { $service->expects($this->once())->method('buildBirthdayFromContact')->willReturn(new VCalendar()); - $this->calDav->expects($this->once())->method('getCalendarObject')->willReturn(''); + $service->expects($this->once())->method('birthdayEvenChanged')->willReturn(true); + $this->calDav->expects($this->once())->method('getCalendarObject')->willReturn([ + 'calendardata' => '']); $this->calDav->expects($this->once())->method('updateCalendarObject')->with(1234, 'default-gump.vcf.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nEND:VCALENDAR\r\n"); } $service->onCardChanged(666, 'gump.vcf', ''); } + /** + * @dataProvider providesBirthday + * @param $expected + * @param $old + * @param $new + */ + public function testBirthdayEvenChanged($expected, $old, $new) { + $new = Reader::read($new); + $this->assertEquals($expected, $this->service->birthdayEvenChanged($old, $new)); + } + + public function providesBirthday() { + return [ + [true, + '', + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], + [false, + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], + [true, + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:4567's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], + [true, + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000102\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"] + ]; + } + public function providesCardChanges(){ return[ ['delete'],