@@ -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') |
@@ -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)){ |
@@ -0,0 +1,85 @@ | |||
<?php | |||
/** | |||
* @author Thomas Müller <thomas.mueller@tmit.eu> | |||
* | |||
* @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 <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
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(''); | |||
} | |||
} |
@@ -34,7 +34,6 @@ class SyncSystemAddressBook extends Command { | |||
private $syncService; | |||
/** | |||
* @param IUserManager $userManager | |||
* @param SyncService $syncService | |||
*/ | |||
function __construct(SyncService $syncService) { |
@@ -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; | |||
} | |||
} |
@@ -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'], |