summaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/CalDAV/BirthdayService.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib/CalDAV/BirthdayService.php')
-rw-r--r--apps/dav/lib/CalDAV/BirthdayService.php226
1 files changed, 226 insertions, 0 deletions
diff --git a/apps/dav/lib/CalDAV/BirthdayService.php b/apps/dav/lib/CalDAV/BirthdayService.php
new file mode 100644
index 00000000000..b74116f4083
--- /dev/null
+++ b/apps/dav/lib/CalDAV/BirthdayService.php
@@ -0,0 +1,226 @@
+<?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\CalDAV;
+
+use Exception;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\DAV\GroupPrincipalBackend;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Reader;
+
+class BirthdayService {
+
+ const BIRTHDAY_CALENDAR_URI = 'contact_birthdays';
+
+ /** @var GroupPrincipalBackend */
+ private $principalBackend;
+
+ /**
+ * BirthdayService constructor.
+ *
+ * @param CalDavBackend $calDavBackEnd
+ * @param CardDavBackend $cardDavBackEnd
+ * @param GroupPrincipalBackend $principalBackend
+ */
+ public function __construct($calDavBackEnd, $cardDavBackEnd, $principalBackend) {
+ $this->calDavBackEnd = $calDavBackEnd;
+ $this->cardDavBackEnd = $cardDavBackEnd;
+ $this->principalBackend = $principalBackend;
+ }
+
+ /**
+ * @param int $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ */
+ public function onCardChanged($addressBookId, $cardUri, $cardData) {
+
+ $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
+
+ $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
+ $targetPrincipals[] = $book['principaluri'];
+ foreach ($targetPrincipals as $principalUri) {
+ $calendar = $this->ensureCalendarExists($principalUri);
+ $objectUri = $book['uri'] . '-' . $cardUri. '.ics';
+ $calendarData = $this->buildBirthdayFromContact($cardData);
+ $existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri);
+ if (is_null($calendarData)) {
+ if (!is_null($existing)) {
+ $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri);
+ }
+ } else {
+ if (is_null($existing)) {
+ $this->calDavBackEnd->createCalendarObject($calendar['id'], $objectUri, $calendarData->serialize());
+ } else {
+ if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) {
+ $this->calDavBackEnd->updateCalendarObject($calendar['id'], $objectUri, $calendarData->serialize());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param int $addressBookId
+ * @param string $cardUri
+ */
+ public function onCardDeleted($addressBookId, $cardUri) {
+ $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
+ $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
+ $targetPrincipals[] = $book['principaluri'];
+ foreach ($targetPrincipals as $principalUri) {
+ $calendar = $this->ensureCalendarExists($principalUri);
+ $objectUri = $book['uri'] . '-' . $cardUri . '.ics';
+ $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri);
+ }
+ }
+
+ /**
+ * @param string $principal
+ * @return array|null
+ * @throws \Sabre\DAV\Exception\BadRequest
+ */
+ public function ensureCalendarExists($principal) {
+ $book = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
+ if (!is_null($book)) {
+ return $book;
+ }
+ $this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [
+ '{DAV:}displayname' => 'Contact birthdays',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FFFFCA',
+ ]);
+
+ return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
+ }
+
+ /**
+ * @param string $cardData
+ * @return null|VCalendar
+ */
+ public function buildBirthdayFromContact($cardData) {
+ if (empty($cardData)) {
+ return null;
+ }
+ try {
+ $doc = Reader::read($cardData);
+ } catch (Exception $e) {
+ return null;
+ }
+
+ if (!isset($doc->BDAY)) {
+ return null;
+ }
+ $birthday = $doc->BDAY;
+ if (!(string)$birthday) {
+ return null;
+ }
+ $title = str_replace('{name}',
+ strtr((string)$doc->FN, array('\,' => ',', '\;' => ';')),
+ '{name}'
+ );
+ try {
+ $date = new \DateTime($birthday);
+ } catch (Exception $e) {
+ return null;
+ }
+ $vCal = new VCalendar();
+ $vCal->VERSION = '2.0';
+ $vEvent = $vCal->createComponent('VEVENT');
+ $vEvent->add('DTSTART');
+ $vEvent->DTSTART->setDateTime(
+ $date
+ );
+ $vEvent->DTSTART['VALUE'] = 'DATE';
+ $vEvent->add('DTEND');
+ $date->add(new \DateInterval('P1D'));
+ $vEvent->DTEND->setDateTime(
+ $date
+ );
+ $vEvent->DTEND['VALUE'] = 'DATE';
+ $vEvent->{'UID'} = $doc->UID;
+ $vEvent->{'RRULE'} = 'FREQ=YEARLY';
+ $vEvent->{'SUMMARY'} = $title . ' (*' . $date->format('Y') . ')';
+ $vEvent->{'TRANSP'} = 'TRANSPARENT';
+ $alarm = $vCal->createComponent('VALARM');
+ $alarm->add($vCal->createProperty('TRIGGER', '-PT0M', ['VALUE' => 'DURATION']));
+ $alarm->add($vCal->createProperty('ACTION', 'DISPLAY'));
+ $alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'}));
+ $vEvent->add($alarm);
+ $vCal->add($vEvent);
+ return $vCal;
+ }
+
+ /**
+ * @param string $user
+ */
+ public function syncUser($user) {
+ $principal = 'principals/users/'.$user;
+ $this->ensureCalendarExists($principal);
+ $books = $this->cardDavBackEnd->getAddressBooksForUser($principal);
+ 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;
+ }
+
+ /**
+ * @param integer $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));
+ }
+
+}