From 3cb278f77f43a3c9f3bbad74af0d65feffe81144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Sun, 16 Feb 2020 14:30:11 +0100 Subject: [PATCH] Add dav plugin to expose calendars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- appinfo/info.xml | 6 +- lib/DAV/Calendar.php | 241 +++++++++++++++++++++++++++++++++++++ lib/DAV/CalendarObject.php | 152 +++++++++++++++++++++++ lib/DAV/CalendarPlugin.php | 80 ++++++++++++ lib/Db/Card.php | 14 +++ 5 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 lib/DAV/Calendar.php create mode 100644 lib/DAV/CalendarObject.php create mode 100644 lib/DAV/CalendarPlugin.php diff --git a/appinfo/info.xml b/appinfo/info.xml index ec9226b9d..db43d3f4c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -64,7 +64,11 @@ OCA\Deck\Activity\DeckProvider - + + + OCA\Deck\DAV\CalendarPlugin + + OCA\Deck\Provider\DeckProvider diff --git a/lib/DAV/Calendar.php b/lib/DAV/Calendar.php new file mode 100644 index 000000000..5fb7ee652 --- /dev/null +++ b/lib/DAV/Calendar.php @@ -0,0 +1,241 @@ + + * + * @author Georg Ehrke + * + * @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 . + * + */ +namespace OCA\Deck\DAV; + +use OCA\DAV\CalDAV\Integration\ExternalCalendar; +use OCA\DAV\CalDAV\Plugin; +use OCA\DAV\DAV\Sharing\IShareable; +use OCA\Deck\Db\Board; +use OCA\Deck\Db\Card; +use OCA\Deck\Service\CardService; +use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; +use Sabre\DAV\PropPatch; + +class Calendar extends ExternalCalendar implements IShareable { + + /** @var string */ + private $principalUri; + + /** @var string */ + private $calendarUri; + + /** @var string[] */ + private $children; + /** + * @var \stdClass + */ + private $cardService; + + /** + * Calendar constructor. + * + * @param string $principalUri + * @param string $calendarUri + */ + public function __construct(string $principalUri, string $calendarUri, Board $board = null) { + parent::__construct('deck', $calendarUri); + + $this->board = $board; + + $this->principalUri = $principalUri; + $this->calendarUri = $calendarUri; + + + if ($board) { + /** @var CardService cardService */ + $cardService = \OC::$server->query(CardService::class); + $this->children = $cardService->findCalendarEntries($board->getId()); + } else { + $this->children = []; + } + } + + + /** + * @inheritDoc + */ + function getOwner() { + return $this->principalUri; + } + + /** + * @inheritDoc + */ + function getACL() { + return [ + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'protected' => true, + ], + ]; + } + + /** + * @inheritDoc + */ + function setACL(array $acl) { + throw new \Sabre\DAV\Exception\Forbidden('Setting ACL is not supported on this node'); + } + + /** + * @inheritDoc + */ + function getSupportedPrivilegeSet() { + return null; + } + + /** + * @inheritDoc + */ + function calendarQuery(array $filters) { + // In a real implementation this should actually filter + return array_map(function (Card $card) { + return $card->getId() . '.ics'; + }, $this->children); + } + + /** + * @inheritDoc + */ + function createFile($name, $data = null) { + return null; + } + + /** + * @inheritDoc + */ + function getChild($name) { + if ($this->childExists($name)) { + $card = array_values(array_filter( + $this->children, + function ($card) use (&$name) { + return $card->getId() . '.ics' === $name; + } + )); + if (count($card) > 0) { + return new CalendarObject($this, $name, $card[0]); + } + } + } + + /** + * @inheritDoc + */ + function getChildren() { + $childNames = array_map(function (Card $card) { + return $card->getId() . '.ics'; + }, $this->children); + + $children = []; + + foreach ($childNames as $name) { + $children[] = $this->getChild($name); + } + + return $children; + } + + /** + * @inheritDoc + */ + function childExists($name) { + return count(array_filter( + $this->children, + function ($card) use (&$name) { + return $card->getId() . '.ics' === $name; + } + )) > 0; + } + + /** + * @inheritDoc + */ + function delete() { + return null; + } + + /** + * @inheritDoc + */ + function getLastModified() { + return $this->board->getLastModified(); + } + + /** + * @inheritDoc + */ + function getGroup() { + return []; + } + + /** + * @inheritDoc + */ + function propPatch(PropPatch $propPatch) { + // We can just return here and let oc_properties handle everything + } + + /** + * @inheritDoc + */ + function getProperties($properties) { + // A backend should provide at least minimum properties + return [ + '{DAV:}displayname' => 'Deck: ' . ($this->board ? $this->board->getTitle() : 'no board object provided'), + '{http://apple.com/ns/ical/}calendar-color' => '#' . $this->board->getColor(), + '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO', 'VEVENT']), + ]; + } + + /** + * @inheritDoc + */ + function updateShares(array $add, array $remove) { + // TODO: Implement updateShares() method. + } + + /** + * @inheritDoc + */ + function getShares() { + return []; + } + + /** + * @inheritDoc + */ + public function getResourceId() { + // TODO: Implement getResourceId() method. + } +} diff --git a/lib/DAV/CalendarObject.php b/lib/DAV/CalendarObject.php new file mode 100644 index 000000000..0dc0c78aa --- /dev/null +++ b/lib/DAV/CalendarObject.php @@ -0,0 +1,152 @@ + + * + * @author Georg Ehrke + * + * @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 . + * + */ +namespace OCA\Deck\DAV; + +use OCA\Deck\Db\Card; +use OCA\Deck\Service\CardService; +use Sabre\VObject\Component\VCalendar; + +class CalendarObject implements \Sabre\CalDAV\ICalendarObject, \Sabre\DAVACL\IACL { + + /** @var Calendar */ + private $calendar; + + /** @var string */ + private $name; + /** + * @var Card + */ + private $card; + + /** + * CalendarObject constructor. + * + * @param Calendar $calendar + * @param string $name + */ + public function __construct(Calendar $calendar, string $name, Card $card = null) { + $this->calendar = $calendar; + $this->name = $name; + $this->card = $card; + } + + /** + * @inheritDoc + */ + function getOwner() { + return null; + } + + /** + * @inheritDoc + */ + function getGroup() { + return null; + } + + /** + * @inheritDoc + */ + function getACL() { + return $this->calendar->getACL(); + } + + /** + * @inheritDoc + */ + function setACL(array $acl) { + throw new \Sabre\DAV\Exception\Forbidden('Setting ACL is not supported on this node'); + } + + /** + * @inheritDoc + */ + function getSupportedPrivilegeSet() { + return null; + } + + /** + * @inheritDoc + */ + function put($data) { + throw new \Sabre\DAV\Exception\Forbidden('This calendar-object is read-only'); + } + + /** + * @inheritDoc + */ + function get() { + if ($this->card) { + return $this->card->getCalendarObject()->serialize(); + } + } + + /** + * @inheritDoc + */ + function getContentType() { + return 'text/calendar; charset=utf-8'; + } + + /** + * @inheritDoc + */ + function getETag() { + return '"' . md5($this->get()) . '"'; + } + + /** + * @inheritDoc + */ + function getSize() { + return strlen($this->get()); + } + + /** + * @inheritDoc + */ + function delete() { + throw new \Sabre\DAV\Exception\Forbidden('This calendar-object is read-only'); + } + + /** + * @inheritDoc + */ + function getName() { + return $this->name; + } + + /** + * @inheritDoc + */ + function setName($name) { + throw new \Sabre\DAV\Exception\Forbidden('This calendar-object is read-only'); + } + + /** + * @inheritDoc + */ + function getLastModified() { + return $this->card->getLastModified(); + } +} diff --git a/lib/DAV/CalendarPlugin.php b/lib/DAV/CalendarPlugin.php new file mode 100644 index 000000000..ba5226827 --- /dev/null +++ b/lib/DAV/CalendarPlugin.php @@ -0,0 +1,80 @@ + + * + * @author Georg Ehrke + * + * @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 . + * + */ + +namespace OCA\Deck\DAV; + +use OCA\DAV\CalDAV\Integration\ExternalCalendar; +use OCA\DAV\CalDAV\Integration\ICalendarProvider; +use OCA\Deck\Db\Board; +use OCA\Deck\Service\BoardService; + +class CalendarPlugin implements ICalendarProvider { + + /** + * @var BoardService + */ + private $boardService; + + public function __construct(BoardService $boardService) { + $this->boardService = $boardService; + } + + /** + * @inheritDoc + */ + public function getAppId(): string { + return 'deck'; + } + + /** + * @inheritDoc + */ + public function fetchAllForCalendarHome(string $principalUri): array { + $boards = $this->boardService->findAll(); + return array_map(function (Board $board) use ($principalUri) { + return new Calendar($principalUri, 'board-' . $board->getId(), $board); + }, $boards); + } + + /** + * @inheritDoc + */ + public function hasCalendarInCalendarHome(string $principalUri, string $calendarUri): bool { + $boards = array_map(function(Board $board) { + return 'board-' . $board->getId(); + }, $this->boardService->findAll()); + return in_array($calendarUri, $boards, true); + } + + /** + * @inheritDoc + */ + public function getCalendarInCalendarHome(string $principalUri, string $calendarUri): ?ExternalCalendar { + if ($this->hasCalendarInCalendarHome($principalUri, $calendarUri)) { + $board = $this->boardService->find(str_replace('board-', '', $calendarUri)); + return new Calendar($principalUri, $calendarUri, $board); + } + + return null; + } +} diff --git a/lib/Db/Card.php b/lib/Db/Card.php index 36faa3dd6..107c253fd 100644 --- a/lib/Db/Card.php +++ b/lib/Db/Card.php @@ -24,6 +24,7 @@ namespace OCA\Deck\Db; use DateTime; +use Sabre\VObject\Component\VCalendar; class Card extends RelationalEntity { protected $title; @@ -117,4 +118,17 @@ class Card extends RelationalEntity { unset($json['descriptionPrev']); return $json; } + + public function getCalendarObject(): VCalendar { + $calendar = new VCalendar(); + $event = $calendar->createComponent('VEVENT'); + $event->UID = 'deck-cardevent' . $this->getId() . '@example.com'; + $event->DTSTAMP = new \DateTime($this->getDuedate()); + $event->DTSTART = new \DateTime($this->getDuedate()); + $event->DTEND = new \DateTime($this->getDuedate()); + $event->SUMMARY = $this->getTitle(); + $calendar->add($event); + return $calendar; + } + }