Browse Source

add resource / room principals

Signed-off-by: Georg Ehrke <developer@georgehrke.com>
Georg Ehrke 6 years ago
parent
commit
2da510af0b

+ 1 - 1
apps/dav/appinfo/info.xml

@@ -5,7 +5,7 @@
 	<name>WebDAV</name>
 	<summary>WebDAV endpoint</summary>
 	<description>WebDAV endpoint</description>
-	<version>1.5.3</version>
+	<version>1.5.4</version>
 	<licence>agpl</licence>
 	<author>owncloud.org</author>
 	<namespace>DAV</namespace>

+ 4 - 0
apps/dav/composer/composer/autoload_classmap.php

@@ -40,6 +40,9 @@ return array(
     'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => $baseDir . '/../lib/CalDAV/PublicCalendarRoot.php',
     'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => $baseDir . '/../lib/CalDAV/Publishing/PublishPlugin.php',
     'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => $baseDir . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+    'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
+    'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
+    'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
     'OCA\\DAV\\CalDAV\\Schedule\\IMipPlugin' => $baseDir . '/../lib/CalDAV/Schedule/IMipPlugin.php',
     'OCA\\DAV\\CalDAV\\Schedule\\Plugin' => $baseDir . '/../lib/CalDAV/Schedule/Plugin.php',
     'OCA\\DAV\\CalDAV\\Search\\SearchPlugin' => $baseDir . '/../lib/CalDAV/Search/SearchPlugin.php',
@@ -144,6 +147,7 @@ return array(
     'OCA\\DAV\\Migration\\Version1004Date20170924124212' => $baseDir . '/../lib/Migration/Version1004Date20170924124212.php',
     'OCA\\DAV\\Migration\\Version1004Date20170926103422' => $baseDir . '/../lib/Migration/Version1004Date20170926103422.php',
     'OCA\\DAV\\Migration\\Version1005Date20180413093149' => $baseDir . '/../lib/Migration/Version1005Date20180413093149.php',
+    'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
     'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
     'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
     'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',

+ 4 - 0
apps/dav/composer/composer/autoload_static.php

@@ -55,6 +55,9 @@ class ComposerStaticInitDAV
         'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/PublicCalendarRoot.php',
         'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/PublishPlugin.php',
         'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+        'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
+        'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
+        'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
         'OCA\\DAV\\CalDAV\\Schedule\\IMipPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Schedule/IMipPlugin.php',
         'OCA\\DAV\\CalDAV\\Schedule\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Schedule/Plugin.php',
         'OCA\\DAV\\CalDAV\\Search\\SearchPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Search/SearchPlugin.php',
@@ -159,6 +162,7 @@ class ComposerStaticInitDAV
         'OCA\\DAV\\Migration\\Version1004Date20170924124212' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170924124212.php',
         'OCA\\DAV\\Migration\\Version1004Date20170926103422' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170926103422.php',
         'OCA\\DAV\\Migration\\Version1005Date20180413093149' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180413093149.php',
+        'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
         'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
         'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
         'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',

+ 3 - 0
apps/dav/lib/CalDAV/CalDavBackend.php

@@ -76,6 +76,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
 	const PERSONAL_CALENDAR_URI = 'personal';
 	const PERSONAL_CALENDAR_NAME = 'Personal';
 
+	const RESOURCE_BOOKING_CALENDAR_URI = 'calendar';
+	const RESOURCE_BOOKING_CALENDAR_NAME = 'Calendar';
+
 	/**
 	 * We need to specify a max date, because we need to stop *somewhere*
 	 *

+ 11 - 0
apps/dav/lib/CalDAV/CalendarRoot.php

@@ -27,4 +27,15 @@ class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
 	function getChildForPrincipal(array $principal) {
 		return new CalendarHome($this->caldavBackend, $principal);
 	}
+
+	function getName() {
+		if ($this->principalPrefix === 'principals/calendar-resources' ||
+			$this->principalPrefix === 'principals/calendar-rooms') {
+			$parts = explode('/', $this->principalPrefix);
+
+			return $parts[1];
+		}
+
+		return parent::getName();
+	}
 }

+ 16 - 2
apps/dav/lib/CalDAV/Plugin.php

@@ -25,15 +25,29 @@ namespace OCA\DAV\CalDAV;
 
 class Plugin extends \Sabre\CalDAV\Plugin {
 
+	const SYSTEM_CALENDAR_ROOT = 'system-calendars';
+
 	/**
 	 * @inheritdoc
 	 */
-	function getCalendarHomeForPrincipal($principalUrl) {
+	function getCalendarHomeForPrincipal($principalUrl):string {
+
+		//TODO - debug this
 
 		if (strrpos($principalUrl, 'principals/users', -strlen($principalUrl)) !== false) {
 			list(, $principalId) = \Sabre\Uri\split($principalUrl);
-			return self::CALENDAR_ROOT .'/' . $principalId;
+			return self::CALENDAR_ROOT . '/' . $principalId;
+		}
+		if (strrpos($principalUrl, 'principals/calendar-resources', -strlen($principalUrl)) !== false) {
+			list(, $principalId) = \Sabre\Uri\split($principalUrl);
+			return self::SYSTEM_CALENDAR_ROOT . '/calendar-resources/' . $principalId;
 		}
+		if (strrpos($principalUrl, 'principals/calendar-rooms', -strlen($principalUrl)) !== false) {
+			list(, $principalId) = \Sabre\Uri\split($principalUrl);
+			return self::SYSTEM_CALENDAR_ROOT . '/calendar-rooms/' . $principalId;
+		}
+
+		throw new \LogicException('This is not supposed to happen');
 	}
 
 }

+ 299 - 0
apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php

@@ -0,0 +1,299 @@
+<?php
+/**
+ * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCA\DAV\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+use Sabre\DAVACL\PrincipalBackend\BackendInterface;
+use Sabre\DAV\Exception;
+use \Sabre\DAV\PropPatch;
+
+abstract class AbstractPrincipalBackend implements BackendInterface {
+
+	/** @var IDBConnection */
+	private $db;
+
+	/** @var IUserSession */
+	private $userSession;
+
+	/** @var IGroupManager */
+	private $groupManager;
+
+	/** @var string */
+	private $principalPrefix;
+
+	/** @var string */
+	private $dbTableName;
+
+	/**
+	 * @param IDBConnection $dbConnection
+	 * @param IUserSession $userSession
+	 * @param IGroupManager $groupManager
+	 * @param string $principalPrefix
+	 * @param string $dbPrefix
+	 */
+	public function __construct(IDBConnection $dbConnection,
+								IUserSession $userSession,
+								IGroupManager $groupManager,
+								$principalPrefix, $dbPrefix) {
+		$this->db = $dbConnection;
+		$this->userSession = $userSession;
+		$this->groupManager = $groupManager;
+		$this->principalPrefix = $principalPrefix;
+		$this->dbTableName = 'calendar_' . $dbPrefix . '_cache';
+	}
+
+	/**
+	 * Returns a list of principals based on a prefix.
+	 *
+	 * This prefix will often contain something like 'principals'. You are only
+	 * expected to return principals that are in this base path.
+	 *
+	 * You are expected to return at least a 'uri' for every user, you can
+	 * return any additional properties if you wish so. Common properties are:
+	 *   {DAV:}displayname
+	 *
+	 * @param string $prefixPath
+	 * @return string[]
+	 */
+	public function getPrincipalsByPrefix($prefixPath) {
+		$principals = [];
+
+		if ($prefixPath === $this->principalPrefix) {
+			$query = $this->db->getQueryBuilder();
+			$query->select(['backend_id', 'resource_id', 'email', 'displayname'])
+				->from($this->dbTableName);
+			$stmt = $query->execute();
+
+			while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+				$principals[] = $this->rowToPrincipal($row);
+			}
+
+			$stmt->closeCursor();
+		}
+
+		return $principals;
+	}
+
+	/**
+	 * Returns a specific principal, specified by it's path.
+	 * The returned structure should be the exact same as from
+	 * getPrincipalsByPrefix.
+	 *
+	 * @param string $path
+	 * @return array
+	 */
+	public function getPrincipalByPath($path) {
+		if (strpos($path, $this->principalPrefix) !== 0) {
+			return null;
+		}
+		list(, $name) = \Sabre\Uri\split($path);
+
+		list($backendId, $resourceId) = explode('-',  $name, 2);
+
+		$query = $this->db->getQueryBuilder();
+		$query->select(['backend_id', 'resource_id', 'email', 'displayname'])
+			->from($this->dbTableName)
+			->where($query->expr()->eq('backend_id', $query->createNamedParameter($backendId)))
+			->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resourceId)));
+		$stmt = $query->execute();
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+		if(!$row) {
+			return null;
+		}
+
+		return $this->rowToPrincipal($row);
+	}
+
+	/**
+	 * Returns the list of members for a group-principal
+	 *
+	 * @param string $principal
+	 * @return string[]
+	 * @throws Exception
+	 */
+	public function getGroupMemberSet($principal) {
+		return [];
+	}
+
+	/**
+	 * Returns the list of groups a principal is a member of
+	 *
+	 * @param string $principal
+	 * @return array
+	 * @throws Exception
+	 */
+	public function getGroupMembership($principal) {
+		return [];
+	}
+
+	/**
+	 * Updates the list of group members for a group principal.
+	 *
+	 * The principals should be passed as a list of uri's.
+	 *
+	 * @param string $principal
+	 * @param string[] $members
+	 * @throws Exception
+	 */
+	public function setGroupMemberSet($principal, array $members) {
+		throw new Exception('Setting members of the group is not supported yet');
+	}
+
+	/**
+	 * @param string $path
+	 * @param PropPatch $propPatch
+	 * @return int
+	 */
+	function updatePrincipal($path, PropPatch $propPatch) {
+		return 0;
+	}
+
+	/**
+	 * @param string $prefixPath
+	 * @param array $searchProperties
+	 * @param string $test
+	 * @return array
+	 */
+	function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+		$results = [];
+		if (\count($searchProperties) === 0) {
+			return [];
+		}
+		if ($prefixPath !== $this->principalPrefix) {
+			return [];
+		}
+
+		$user = $this->userSession->getUser();
+		if (!$user) {
+			return null;
+		}
+		$usersGroups = $this->groupManager->getUserGroupIds($user);
+
+		foreach ($searchProperties as $prop => $value) {
+			switch ($prop) {
+				case '{http://sabredav.org/ns}email-address':
+					$query = $this->db->getQueryBuilder();
+					$query->select(['backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+						->from($this->dbTableName)
+						->where($query->expr()->eq('email', $query->createNamedParameter($value)));
+
+					$stmt = $query->execute();
+					while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+						// TODO - check for group restrictions
+						$results[] = $this->rowToPrincipal($row)['uri'];
+					}
+
+					$stmt->closeCursor();
+					break;
+
+				case '{DAV:}displayname':
+					$query = $this->db->getQueryBuilder();
+					$query->select(['backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+						->from($this->dbTableName)
+						->where($query->expr()->eq('displayname', $query->createNamedParameter($value)));
+
+					$stmt = $query->execute();
+					while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+						// TODO - check for group restrictions
+						$results[] = $this->rowToPrincipal($row)['uri'];
+					}
+
+					$stmt->closeCursor();
+					break;
+
+				default:
+					$results[] = [];
+					break;
+			}
+		}
+
+		// results is an array of arrays, so this is not the first search result
+		// but the results of the first searchProperty
+		if (count($results) === 1) {
+			return $results;
+		}
+
+		switch ($test) {
+			case 'anyof':
+				return array_unique(array_merge(...$results));
+
+			case 'allof':
+			default:
+				return array_intersect(...$results);
+		}
+	}
+
+	/**
+	 * @param string $uri
+	 * @param string $principalPrefix
+	 * @return null|string
+	 */
+	function findByUri($uri, $principalPrefix) {
+		$user = $this->userSession->getUser();
+		if (!$user) {
+			return null;
+		}
+		$usersGroups = $this->groupManager->getUserGroupIds($user);
+
+		if (strpos($uri, 'mailto:') === 0) {
+			$email = substr($uri, 7);
+			$query = $this->db->getQueryBuilder();
+			$query->select(['backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+				->from($this->dbTableName)
+				->where($query->expr()->eq('email', $query->createNamedParameter($email)));
+
+			$stmt = $query->execute();
+			$row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+			if(!$row) {
+				return null;
+			}
+
+			return $this->rowToPrincipal($row)['uri'];
+		}
+		if (strpos($uri, 'principal:') === 0) {
+			$principal = substr($uri, 10);
+			$principal = $this->getPrincipalByPath($principal);
+
+			if ($principal !== null) {
+				return $principal['uri'];
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * convert database row to principal
+	 */
+	private function rowToPrincipal($row) {
+		return [
+			'uri' => $this->principalPrefix . '/' . $row['backend_id'] . '-' . $row['resource_id'],
+			'{DAV:}displayname' => $row['displayname'],
+			'{http://sabredav.org/ns}email-address' => $row['email']
+		];
+	}
+}

+ 42 - 0
apps/dav/lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCA\DAV\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+
+class ResourcePrincipalBackend extends AbstractPrincipalBackend {
+
+	/**
+	 * @param IDBConnection $dbConnection
+	 * @param IUserSession $userSession
+	 * @param IGroupManager $groupManager
+	 */
+	public function __construct(IDBConnection $dbConnection,
+								IUserSession $userSession,
+								IGroupManager $groupManager) {
+		parent::__construct($dbConnection, $userSession, $groupManager,
+			'principals/calendar-resources', 'resources');
+	}
+}

+ 42 - 0
apps/dav/lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCA\DAV\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+
+class RoomPrincipalBackend extends AbstractPrincipalBackend {
+
+	/**
+	 * @param IDBConnection $dbConnection
+	 * @param IUserSession $userSession
+	 * @param IGroupManager $groupManager
+	 */
+	public function __construct(IDBConnection $dbConnection,
+								IUserSession $userSession,
+								IGroupManager $groupManager) {
+		parent::__construct($dbConnection, $userSession, $groupManager,
+			'principals/calendar-rooms', 'rooms');
+	}
+}

+ 17 - 5
apps/dav/lib/CalDAV/Schedule/Plugin.php

@@ -80,20 +80,32 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
 				$principalUrl = $node->getPrincipalUrl();
 
 				$calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
-
 				if (!$calendarHomePath) {
 					return null;
 				}
 
+				if (strpos($principalUrl, 'principals/users') === 0) {
+					$uri = CalDavBackend::PERSONAL_CALENDAR_URI;
+					$displayname = CalDavBackend::PERSONAL_CALENDAR_NAME;
+				} elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
+						  strpos($principalUrl, 'principals/calendar-rooms') === 0) {
+					$uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
+					$displayname = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
+				} else {
+					// How did we end up here?
+					// TODO - throw exception or just ignore?
+					return null;
+				}
+
 				/** @var CalendarHome $calendarHome */
 				$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
-				if (!$calendarHome->childExists(CalDavBackend::PERSONAL_CALENDAR_URI)) {
-					$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, CalDavBackend::PERSONAL_CALENDAR_URI, [
-						'{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME,
+				if (!$calendarHome->childExists($uri)) {
+					$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
+						'{DAV:}displayname' => $displayname,
 					]);
 				}
 
-				$result = $this->server->getPropertiesForPath($calendarHomePath . '/' . CalDavBackend::PERSONAL_CALENDAR_URI, [], 1);
+				$result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
 				if (empty($result)) {
 					return null;
 				}

+ 87 - 0
apps/dav/lib/Migration/Version1005Date20180530124431.php

@@ -0,0 +1,87 @@
+<?php
+/**
+ * @copyright 2017 Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCA\DAV\Migration;
+
+use Doctrine\DBAL\Types\Type;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Migration\IOutput;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1005Date20180530124431 extends SimpleMigrationStep {
+
+	/**
+	 * @param IOutput $output
+	 * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+	 * @param array $options
+	 * @return null|ISchemaWrapper
+	 * @since 13.0.0
+	 */
+	public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+		/** @var ISchemaWrapper $schema */
+		$schema = $schemaClosure();
+
+		$types = ['resources', 'rooms'];
+		foreach($types as $type) {
+			if (!$schema->hasTable('calendar_' . $type . '_cache')) {
+				$table = $schema->createTable('calendar_' . $type . '_cache');
+
+				$table->addColumn('id', Type::BIGINT, [
+					'autoincrement' => true,
+					'notnull' => true,
+					'length' => 11,
+					'unsigned' => true,
+				]);
+				$table->addColumn('backend_id', Type::STRING, [
+					'notnull' => false,
+					'length' => 64,
+				]);
+				$table->addColumn('resource_id', Type::STRING, [
+					'notnull' => false,
+					'length' => 64,
+				]);
+				$table->addColumn('email', Type::STRING, [
+					'notnull' => false,
+					'length' => 255,
+				]);
+				$table->addColumn('displayname', Type::STRING, [
+					'notnull' => false,
+					'length' => 255,
+				]);
+				$table->addColumn('group_restrictions', Type::STRING, [
+					'notnull' => false,
+					'length' => 4000,
+				]);
+
+				$table->setPrimaryKey(['id'], 'calendar_' . $type . '_cache_id_idx');
+				$table->addIndex(['backend_id', 'resource_id'], 'calendar_' . $type . '_cache_backendresource_idx');
+				$table->addIndex(['email'], 'calendar_' . $type . '_cache_email_idx');
+				$table->addIndex(['displayname'], 'calendar_' . $type . '_cache_displayname_idx');
+			}
+		}
+
+		return $schema;
+	}
+}

+ 29 - 4
apps/dav/lib/RootCollection.php

@@ -27,6 +27,8 @@ namespace OCA\DAV;
 use OCA\DAV\CalDAV\CalDavBackend;
 use OCA\DAV\CalDAV\CalendarRoot;
 use OCA\DAV\CalDAV\PublicCalendarRoot;
+use OCA\DAV\CalDAV\ResourceBooking\ResourcePrincipalBackend;
+use OCA\DAV\CalDAV\ResourceBooking\RoomPrincipalBackend;
 use OCA\DAV\CardDAV\AddressBookRoot;
 use OCA\DAV\CardDAV\CardDavBackend;
 use OCA\DAV\Connector\Sabre\Principal;
@@ -43,6 +45,7 @@ class RootCollection extends SimpleCollection {
 		$random = \OC::$server->getSecureRandom();
 		$logger = \OC::$server->getLogger();
 		$userManager = \OC::$server->getUserManager();
+		$userSession = \OC::$server->getUserSession();
 		$groupManager = \OC::$server->getGroupManager();
 		$shareManager = \OC::$server->getShareManager();
 		$db = \OC::$server->getDatabaseConnection();
@@ -55,6 +58,8 @@ class RootCollection extends SimpleCollection {
 			$config
 		);
 		$groupPrincipalBackend = new GroupPrincipalBackend($groupManager);
+		$calendarResourcePrincipalBackend = new ResourcePrincipalBackend($db, $userSession, $groupManager);
+		$calendarRoomPrincipalBackend = new RoomPrincipalBackend($db, $userSession, $groupManager);
 		// as soon as debug mode is enabled we allow listing of principals
 		$disableListing = !$config->getSystemValue('debug', false);
 
@@ -65,11 +70,25 @@ class RootCollection extends SimpleCollection {
 		$groupPrincipals->disableListing = $disableListing;
 		$systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system');
 		$systemPrincipals->disableListing = $disableListing;
+		$calendarResourcePrincipals = new Collection($calendarResourcePrincipalBackend, 'principals/calendar-resources');
+		$calendarResourcePrincipals->disableListing = $disableListing;
+		$calendarRoomPrincipals = new Collection($calendarRoomPrincipalBackend, 'principals/calendar-rooms');
+		$calendarRoomPrincipals->disableListing = $disableListing;
+
+
 		$filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users');
 		$filesCollection->disableListing = $disableListing;
 		$caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
-		$calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users');
-		$calendarRoot->disableListing = $disableListing;
+		$userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users');
+		$userCalendarRoot->disableListing = $disableListing;
+
+		$resourceCalendarCaldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
+		$resourceCalendarRoot = new CalendarRoot($calendarResourcePrincipalBackend, $caldavBackend, 'principals/calendar-resources');
+		$resourceCalendarRoot->disableListing = $disableListing;
+		$roomCalendarCaldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
+		$roomCalendarRoot = new CalendarRoot($calendarRoomPrincipalBackend, $roomCalendarCaldavBackend, 'principals/calendar-rooms');
+		$roomCalendarRoot->disableListing = $disableListing;
+
 		$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config);
 		$publicCalendarRoot->disableListing = $disableListing;
 
@@ -111,9 +130,15 @@ class RootCollection extends SimpleCollection {
 				new SimpleCollection('principals', [
 						$userPrincipals,
 						$groupPrincipals,
-						$systemPrincipals]),
+						$systemPrincipals,
+						$calendarResourcePrincipals,
+						$calendarRoomPrincipals]),
 				$filesCollection,
-				$calendarRoot,
+				$userCalendarRoot,
+				new SimpleCollection('system-calendars', [
+					$resourceCalendarRoot,
+					$roomCalendarRoot,
+				]),
 				$publicCalendarRoot,
 				new SimpleCollection('addressbooks', [
 						$usersAddressBookRoot,

+ 4 - 2
apps/dav/lib/Server.php

@@ -132,13 +132,15 @@ class Server {
 		// acl
 		$acl = new DavAclPlugin();
 		$acl->principalCollectionSet = [
-			'principals/users', 'principals/groups'
+			'principals/users', 'principals/groups',
+			'principals/calendar-resources',
+			'principals/calendar-rooms',
 		];
 		$acl->defaultUsernamePath = 'principals/users';
 		$this->server->addPlugin($acl);
 
 		// calendar plugins
-		if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'principals'])) {
+		if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) {
 			$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
 			$this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
 			$this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin());