/** * PropFind * * This method handler is invoked before any after properties for a * resource are fetched. This allows us to add in any CalDAV specific * properties. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $ns = '{' . self::NS_CALDAV . '}'; if ($node instanceof ICalendarObjectContainer) { $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); $propFind->handle($ns . 'supported-calendar-data', function () { return new Xml\Property\SupportedCalendarData(); }); $propFind->handle($ns . 'supported-collation-set', function () { return new Xml\Property\SupportedCollationSet(); }); } if ($node instanceof DAVACL\IPrincipal) { $principalUrl = $node->getPrincipalUrl(); $propFind->handle('{' . self::NS_CALDAV . '}calendar-home-set', function () use($principalUrl) { $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl) . '/'; return new Href($calendarHomePath); }); // The calendar-user-address-set property is basically mapped to // the {DAV:}alternate-URI-set property. $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-address-set', function () use($node) { $addresses = $node->getAlternateUriSet(); $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/'; return new Href($addresses, false); }); // For some reason somebody thought it was a good idea to add // another one of these properties. We're supporting it too. $propFind->handle('{' . self::NS_CALENDARSERVER . '}email-address-set', function () use($node) { $addresses = $node->getAlternateUriSet(); $emails = []; foreach ($addresses as $address) { if (substr($address, 0, 7) === 'mailto:') { $emails[] = substr($address, 7); } } return new Xml\Property\EmailAddressSet($emails); }); // These two properties are shortcuts for ical to easily find // other principals this principal has access to. $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; if ($propFind->getStatus($propRead) === 404 || $propFind->getStatus($propWrite) === 404) { $aclPlugin = $this->server->getPlugin('acl'); $membership = $aclPlugin->getPrincipalMembership($propFind->getPath()); $readList = []; $writeList = []; foreach ($membership as $group) { $groupNode = $this->server->tree->getNodeForPath($group); $listItem = Uri\split($group)[0] . '/'; // If the node is either ap proxy-read or proxy-write // group, we grab the parent principal and add it to the // list. if ($groupNode instanceof Principal\IProxyRead) { $readList[] = $listItem; } if ($groupNode instanceof Principal\IProxyWrite) { $writeList[] = $listItem; } } $propFind->set($propRead, new Href($readList)); $propFind->set($propWrite, new Href($writeList)); } } // instanceof IPrincipal if ($node instanceof ICalendarObject) { // The calendar-data property is not supposed to be a 'real' // property, but in large chunks of the spec it does act as such. // Therefore we simply expose it as a property. $propFind->handle('{' . self::NS_CALDAV . '}calendar-data', function () use($node) { $val = $node->get(); if (is_resource($val)) { $val = stream_get_contents($val); } // Taking out \r to not screw up the xml output return str_replace("\r", "", $val); }); } }
/** * Triggered after properties have been fetched. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { // There's a bunch of properties that must appear as a self-closing // xml-element. This event handler ensures that this will be the case. $props = ['{http://calendarserver.org/ns/}subscribed-strip-alarms', '{http://calendarserver.org/ns/}subscribed-strip-attachments', '{http://calendarserver.org/ns/}subscribed-strip-todos']; foreach ($props as $prop) { if ($propFind->getStatus($prop) === 200) { $propFind->set($prop, '', 200); } } }
/** * Adds tags and favorites properties to the response, * if requested. * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { if (!$node instanceof \OCA\DAV\Connector\Sabre\Node) { return; } // need prefetch ? if ($node instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME)) || !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME)))) { // note: pre-fetching only supported for depth <= 1 $folderContent = $node->getChildren(); $fileIds[] = (int) $node->getId(); foreach ($folderContent as $info) { $fileIds[] = (int) $info->getId(); } $tags = $this->getTagger()->getTagsForObjects($fileIds); if ($tags === false) { // the tags API returns false on error... $tags = array(); } $this->cachedTags = $this->cachedTags + $tags; $emptyFileIds = array_diff($fileIds, array_keys($tags)); // also cache the ones that were not found foreach ($emptyFileIds as $fileId) { $this->cachedTags[$fileId] = []; } } $tags = null; $isFav = null; $propFind->handle(self::TAGS_PROPERTYNAME, function () use($tags, &$isFav, $node) { list($tags, $isFav) = $this->getTagsAndFav($node->getId()); return new TagList($tags); }); $propFind->handle(self::FAVORITE_PROPERTYNAME, function () use($isFav, $node) { if (is_null($isFav)) { list(, $isFav) = $this->getTagsAndFav($node->getId()); } return $isFav; }); }
/** * Adds shares to propfind response * * @param PropFind $propFind propfind object * @param \Sabre\DAV\INode $sabreNode sabre node */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $sabreNode) { if (!$sabreNode instanceof \OCA\DAV\Connector\Sabre\Node) { return; } // need prefetch ? if ($sabreNode instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME))) { $folderNode = $this->userFolder->get($propFind->getPath()); $children = $folderNode->getDirectoryListing(); $this->cachedShareTypes[$folderNode->getId()] = $this->getShareTypes($folderNode); foreach ($children as $childNode) { $this->cachedShareTypes[$childNode->getId()] = $this->getShareTypes($childNode); } } $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use($sabreNode) { if (isset($this->cachedShareTypes[$sabreNode->getId()])) { $shareTypes = $this->cachedShareTypes[$sabreNode->getId()]; } else { $node = $this->userFolder->get($sabreNode->getPath()); $shareTypes = $this->getShareTypes($node); } return new ShareTypeList($shareTypes); }); }