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;
+ }
+
}