/** * 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); }); } }
/** * This method is called after most properties have been found * it allows us to add in any Lock-related properties * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $propFind->handle('{DAV:}supportedlock', function () { return new DAV\Property\SupportedLock(!!$this->locksBackend); }); $propFind->handle('{DAV:}lockdiscovery', function () use($propFind) { return new DAV\Property\LockDiscovery($this->getLocks($propFind->getPath())); }); }
/** * 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 File && !$node instanceof Directory) { return; } $propFind->handle(self::PROPERTY_NAME_COUNT, function () use($node) { return $this->commentsManager->getNumberOfCommentsForObject('files', strval($node->getId())); }); $propFind->handle(self::PROPERTY_NAME_HREF, function () use($node) { return $this->getCommentsLink($node); }); $propFind->handle(self::PROPERTY_NAME_UNREAD, function () use($node) { return $this->getUnreadCount($node); }); }
/** * Our PROPFIND handler * * Here we set a contenttype, if the node didn't already have one. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $propFind->handle('{DAV:}getcontenttype', function () use($propFind) { list(, $fileName) = URLUtil::splitPath($propFind->getPath()); return $this->getContentType($fileName); }); }
/** * Adds all CardDAV-specific properties * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $ns = '{http://owncloud.org/ns}'; if ($node instanceof AddressBook) { $propFind->handle($ns . 'groups', function () use($node) { return new Groups($node->getContactsGroups()); }); } }
function propFind(PropFind $propFind, INode $node) { /* Overload current-user-principal */ $propFind->handle('{DAV:}current-user-principal', function () { if ($url = parent::getCurrentUserPrincipal()) { return new Principal(Principal::HREF, $url . '/'); } else { return new Principal(Principal::UNAUTHENTICATED); } }); parent::propFind($propFind, $node); }
/** * PropFind * * @param PropFind $propFind * @param BaseINode $node * @return void */ function propFind(PropFind $propFind, BaseINode $node) { $caldavPlugin = $this->server->getPlugin('caldav'); if ($node instanceof DAVACL\IPrincipal) { $principalUrl = $node->getPrincipalUrl(); // notification-URL property $propFind->handle('{' . self::NS_CALENDARSERVER . '}notification-URL', function () use($principalUrl, $caldavPlugin) { $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/'; return new DAV\Xml\Property\Href($notificationPath); }); } if ($node instanceof INode) { $propFind->handle('{' . self::NS_CALENDARSERVER . '}notificationtype', [$node, 'getNotificationType']); } }
/** * Triggered before properties are looked up in specific nodes. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @param array $requestedProperties * @param array $returnedProperties * @TODO really should be broken into multiple methods, or even a class. * @return bool */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $path = $propFind->getPath(); // Checking the read permission if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) { // User is not allowed to read properties // Returning false causes the property-fetching system to pretend // that the node does not exist, and will cause it to be hidden // from listings such as PROPFIND or the browser plugin. if ($this->hideNodesFromListings) { return false; } // Otherwise we simply mark every property as 403. foreach ($propFind->getRequestedProperties() as $requestedProperty) { $propFind->set($requestedProperty, null, 403); } return; } /* Adding principal properties */ if ($node instanceof IPrincipal) { $propFind->handle('{DAV:}alternate-URI-set', function () use($node) { return new DAV\Property\HrefList($node->getAlternateUriSet()); }); $propFind->handle('{DAV:}principal-URL', function () use($node) { return new DAV\Property\Href($node->getPrincipalUrl() . '/'); }); $propFind->handle('{DAV:}group-member-set', function () use($node) { $members = $node->getGroupMemberSet(); foreach ($members as $k => $member) { $members[$k] = rtrim($member, '/') . '/'; } return new DAV\Property\HrefList($members); }); $propFind->handle('{DAV:}group-membership', function () use($node) { $members = $node->getGroupMembership(); foreach ($members as $k => $member) { $members[$k] = rtrim($member, '/') . '/'; } return new DAV\Property\HrefList($members); }); $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']); } $propFind->handle('{DAV:}principal-collection-set', function () { $val = $this->principalCollectionSet; // Ensuring all collections end with a slash foreach ($val as $k => $v) { $val[$k] = $v . '/'; } return new DAV\Property\HrefList($val); }); $propFind->handle('{DAV:}current-user-principal', function () { if ($url = $this->getCurrentUserPrincipal()) { return new Property\Principal(Property\Principal::HREF, $url . '/'); } else { return new Property\Principal(Property\Principal::UNAUTHENTICATED); } }); $propFind->handle('{DAV:}supported-privilege-set', function () use($node) { return new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); }); $propFind->handle('{DAV:}current-user-privilege-set', function () use($node, $propFind, $path) { if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { $propFind->set('{DAV:}current-user-privilege-set', null, 403); } else { $val = $this->getCurrentUserPrivilegeSet($node); if (!is_null($val)) { return new Property\CurrentUserPrivilegeSet($val); } } }); $propFind->handle('{DAV:}acl', function () use($node, $propFind, $path) { /* The ACL property contains all the permissions */ if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) { $propFind->set('{DAV:}acl', null, 403); } else { $acl = $this->getACL($node); if (!is_null($acl)) { return new Property\Acl($this->getACL($node)); } } }); $propFind->handle('{DAV:}acl-restrictions', function () { return new Property\AclRestrictions(); }); /* Adding ACL properties */ if ($node instanceof IACL) { $propFind->handle('{DAV:}owner', function () use($node) { return new DAV\Property\Href($node->getOwner() . '/'); }); } }
/** * This method handler is invoked during fetching of properties. * * We use this event to add calendar-auto-schedule-specific properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if (!$node instanceof DAVACL\IPrincipal) { return; } $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); // schedule-outbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $outboxPath = $calendarHomePath . '/outbox/'; return new Href($outboxPath); }); // schedule-inbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $inboxPath = $calendarHomePath . '/inbox/'; return new Href($inboxPath); }); $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function () use($principalUrl, $caldavPlugin) { // We don't support customizing this property yet, so in the // meantime we just grab the first calendar in the home-set. $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set'; $result = $this->server->getPropertiesForPath($calendarHomePath, ['{DAV:}resourcetype', $sccs], 1); foreach ($result as $child) { if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar') || $child[200]['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared')) { // Node is either not a calendar or a shared instance. continue; } if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) { // Either there is no supported-calendar-component-set // (which is fine) or we found one that supports VEVENT. return new Href($child['href']); } } }); // The server currently reports every principal to be of type // 'INDIVIDUAL' $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function () { return 'INDIVIDUAL'; }); }
/** * This method is triggered whenever properties are requested for a node. * We intercept this to see if we must return a {DAV:}sync-token. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $propFind->handle('{DAV:}sync-token', function () use($node) { if (!$node instanceof ISyncCollection || !($token = $node->getSyncToken())) { return; } return self::SYNCTOKEN_PREFIX . $token; }); }
/** * 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); }); }
/** * Adds all ownCloud-specific properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { $httpRequest = $this->server->httpRequest; if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { $propFind->handle(self::FILEID_PROPERTYNAME, function () use($node) { return $node->getFileId(); }); $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use($node) { return $node->getInternalFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use($node) { $perms = $node->getDavPermissions(); if ($this->isPublic) { // remove mount information $perms = str_replace(['S', 'M'], '', $perms); } return $perms; }); $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use($node, $httpRequest) { return $node->getSharePermissions($httpRequest->getRawServerValue('PHP_AUTH_USER')); }); $propFind->handle(self::GETETAG_PROPERTYNAME, function () use($node) { return $node->getETag(); }); $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use($node) { $owner = $node->getOwner(); return $owner->getUID(); }); $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use($node) { $owner = $node->getOwner(); $displayName = $owner->getDisplayName(); return $displayName; }); $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use($node) { if ($node->getPath() === '/') { return $this->config->getSystemValue('data-fingerprint', ''); } }); } if ($node instanceof \OCA\DAV\Files\FilesHome) { $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use($node) { return $this->config->getSystemValue('data-fingerprint', ''); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use($node) { /** @var $node \OCA\DAV\Connector\Sabre\File */ try { $directDownloadUrl = $node->getDirectDownload(); if (isset($directDownloadUrl['url'])) { return $directDownloadUrl['url']; } } catch (StorageNotAvailableException $e) { return false; } catch (ForbiddenException $e) { return false; } return false; }); $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use($node) { $checksum = $node->getChecksum(); if ($checksum === NULL || $checksum === '') { return null; } return new ChecksumList($checksum); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) { $propFind->handle(self::SIZE_PROPERTYNAME, function () use($node) { return $node->getSize(); }); } }
/** * This method is called when properties are retrieved. * * This specific handler is called very late in the process, because we * want other systems to first have a chance to handle the properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFindLate(PropFind $propFind, INode $node) { $propFind->handle('{http://calendarserver.org/ns/}getctag', function () use($propFind) { // If we already have a sync-token from the current propFind // request, we can re-use that. $val = $propFind->get('{http://sabredav.org/ns}sync-token'); if ($val) { return $val; } $val = $propFind->get('{DAV:}sync-token'); if ($val && is_scalar($val)) { return $val; } if ($val && $val instanceof Property\IHref) { return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); } // If we got here, the earlier two properties may simply not have // been part of the earlier request. We're going to fetch them. $result = $this->server->getProperties($propFind->getPath(), ['{http://sabredav.org/ns}sync-token', '{DAV:}sync-token']); if (isset($result['{http://sabredav.org/ns}sync-token'])) { return $result['{http://sabredav.org/ns}sync-token']; } if (isset($result['{DAV:}sync-token'])) { $val = $result['{DAV:}sync-token']; if (is_scalar($val)) { return $val; } elseif ($val instanceof Property\IHref) { return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); } } }); }
/** * Return some dummy response for PROPFIND requests with regard to locking * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $propFind->handle('{DAV:}supportedlock', function () { return new SupportedLock(true); }); $propFind->handle('{DAV:}lockdiscovery', function () use($propFind) { return new LockDiscovery([]); }); }
/** * Adds all ownCloud-specific properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { if ($node instanceof \OC\Connector\Sabre\Node) { $propFind->handle(self::FILEID_PROPERTYNAME, function () use($node) { return $node->getFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use($node) { return $node->getDavPermissions(); }); $propFind->handle(self::GETETAG_PROPERTYNAME, function () use($node) { return $node->getEtag(); }); } if ($node instanceof \OC\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use($node) { /** @var $node \OC\Connector\Sabre\File */ $directDownloadUrl = $node->getDirectDownload(); if (isset($directDownloadUrl['url'])) { return $directDownloadUrl['url']; } return false; }); } if ($node instanceof \OC\Connector\Sabre\Directory) { $propFind->handle(self::SIZE_PROPERTYNAME, function () use($node) { return $node->getSize(); }); } }
/** * This method is triggered *after* all properties have been retrieved. * This allows us to inject the correct resourcetype for calendars that * have been shared. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { if ($node instanceof ISharedCalendar) { $shareAccess = $node->getShareAccess(); if ($rt = $propFind->get('{DAV:}resourcetype')) { switch ($shareAccess) { case \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER: $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner'); break; case \Sabre\DAV\Sharing\Plugin::ACCESS_READ: case \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE: $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared'); break; } } $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function () { return new Xml\Property\AllowedSharingModes(true, false); }); } }
/** * Retrieves system tag properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { if (!$node instanceof SystemTagNode && !$node instanceof SystemTagMappingNode) { return; } $propFind->handle(self::ID_PROPERTYNAME, function () use($node) { return $node->getSystemTag()->getId(); }); $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use($node) { return $node->getSystemTag()->getName(); }); $propFind->handle(self::USERVISIBLE_PROPERTYNAME, function () use($node) { return $node->getSystemTag()->isUserVisible() ? 'true' : 'false'; }); $propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function () use($node) { // this is the tag's inherent property "is user assignable" return $node->getSystemTag()->isUserAssignable() ? 'true' : 'false'; }); $propFind->handle(self::CANASSIGN_PROPERTYNAME, function () use($node) { // this is the effective permission for the current user return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false'; }); $propFind->handle(self::GROUPS_PROPERTYNAME, function () use($node) { if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { // property only available for admins throw new Forbidden(); } $groups = []; // no need to retrieve groups for namespaces that don't qualify if ($node->getSystemTag()->isUserVisible() && !$node->getSystemTag()->isUserAssignable()) { $groups = $this->tagManager->getTagGroups($node->getSystemTag()); } return implode('|', $groups); }); }
/** * Triggered by a `PROPFIND` or `GET`. The goal is to set an appropriated * Content-Type. * * @param SabreDav\PropFind $propFind PropFind object. * @param SabreDav\INode $node Node. */ function propFind(SabreDav\PropFind $propFind, SabreDav\INode $node) { $propFind->handle('{DAV:}getcontenttype', function () use($propFind) { Mime::compute('katana://resource/mime.types'); $extension = pathinfo($propFind->getPath(), PATHINFO_EXTENSION); return Mime::getMimeFromExtension($extension); }); }
/** * This event is triggered when properties are requested for nodes. * * This allows us to inject any sharings-specific properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if ($node instanceof ISharedNode) { $propFind->handle('{DAV:}share-access', function () use($node) { return new Property\ShareAccess($node->getShareAccess()); }); $propFind->handle('{DAV:}invite', function () use($node) { return new Property\Invite($node->getInvites()); }); $propFind->handle('{DAV:}share-resource-uri', function () use($node) { return new Property\Href($node->getShareResourceUri()); }); } }
/** * Adds all ownCloud-specific properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { if ($node instanceof \OC\Connector\Sabre\Node) { $propFind->handle(self::FILEID_PROPERTYNAME, function () use($node) { return $node->getFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use($node) { $perms = $node->getDavPermissions(); if ($this->isPublic) { // remove mount information $perms = str_replace(['S', 'M'], '', $perms); } return $perms; }); $propFind->handle(self::GETETAG_PROPERTYNAME, function () use($node) { return $node->getEtag(); }); } if ($node instanceof \OC\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use($node) { /** @var $node \OC\Connector\Sabre\File */ try { $directDownloadUrl = $node->getDirectDownload(); if (isset($directDownloadUrl['url'])) { return $directDownloadUrl['url']; } } catch (StorageNotAvailableException $e) { // return empty download link when storage not available return false; } return false; }); } if ($node instanceof \OC\Connector\Sabre\Directory) { $propFind->handle(self::SIZE_PROPERTYNAME, function () use($node) { return $node->getSize(); }); } }
/** * 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; }); }
/** * This event is triggered when properties are requested for a certain * node. * * This allows us to inject any properties early. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if ($node instanceof IShareable) { $propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function () use($node) { return new Invite($node->getShares()); }); } }
/** * This method handler is invoked during fetching of properties. * * We use this event to add calendar-auto-schedule-specific properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if ($node instanceof DAVACL\IPrincipal) { $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); // schedule-outbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } $outboxPath = $calendarHomePath . '/outbox/'; return new LocalHref($outboxPath); }); // schedule-inbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } $inboxPath = $calendarHomePath . '/inbox/'; return new LocalHref($inboxPath); }); $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function () use($principalUrl, $caldavPlugin) { // We don't support customizing this property yet, so in the // meantime we just grab the first calendar in the home-set. $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set'; $result = $this->server->getPropertiesForPath($calendarHomePath, ['{DAV:}resourcetype', '{DAV:}share-access', $sccs], 1); foreach ($result as $child) { if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar')) { // Node is either not a calendar continue; } if (isset($child[200]['{DAV:}share-access'])) { $shareAccess = $child[200]['{DAV:}share-access']->getValue(); if ($shareAccess !== Sharing\Plugin::ACCESS_NOTSHARED && $shareAccess !== Sharing\Plugin::ACCESS_SHAREDOWNER) { // Node is a shared node, not owned by the relevant // user. continue; } } if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) { // Either there is no supported-calendar-component-set // (which is fine) or we found one that supports VEVENT. return new LocalHref($child['href']); } } }); // The server currently reports every principal to be of type // 'INDIVIDUAL' $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function () { return 'INDIVIDUAL'; }); } // Mapping the old property to the new property. $propFind->handle('{http://calendarserver.org/ns/}calendar-availability', function () use($propFind, $node) { // In case it wasn't clear, the only difference is that we map the // old property to a different namespace. $availProp = '{' . self::NS_CALDAV . '}calendar-availability'; $subPropFind = new PropFind($propFind->getPath(), [$availProp]); $this->server->getPropertiesByNode($subPropFind, $node); $propFind->set('{http://calendarserver.org/ns/}calendar-availability', $subPropFind->get($availProp), $subPropFind->getStatus($availProp)); }); }
/** * This method is triggered *after* all properties have been retrieved. * This allows us to inject the correct resourcetype for calendars that * have been shared. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { if ($node instanceof IShareableCalendar) { if ($rt = $propFind->get('{DAV:}resourcetype')) { if (count($node->getShares()) > 0) { $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner'); } } $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function () { return new Xml\Property\AllowedSharingModes(true, false); }); } }
/** * Retrieves system tag properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { if (!$node instanceof SystemTagNode) { return; } $propFind->handle(self::ID_PROPERTYNAME, function () use($node) { return $node->getSystemTag()->getId(); }); $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use($node) { return $node->getSystemTag()->getName(); }); $propFind->handle(self::USERVISIBLE_PROPERTYNAME, function () use($node) { return (int) $node->getSystemTag()->isUserVisible(); }); $propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function () use($node) { return (int) $node->getSystemTag()->isUserAssignable(); }); }
/** * Adds all ownCloud-specific properties * * @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) { $propFind->handle(self::FILEID_PROPERTYNAME, function () use($node) { return $node->getFileId(); }); $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use($node) { return $node->getInternalFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use($node) { $perms = $node->getDavPermissions(); if ($this->isPublic) { // remove mount information $perms = str_replace(['S', 'M'], '', $perms); } return $perms; }); $propFind->handle(self::GETETAG_PROPERTYNAME, function () use($node) { return $node->getEtag(); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use($node) { /** @var $node \OCA\DAV\Connector\Sabre\File */ $directDownloadUrl = $node->getDirectDownload(); if (isset($directDownloadUrl['url'])) { return $directDownloadUrl['url']; } return false; }); } if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) { $propFind->handle(self::SIZE_PROPERTYNAME, function () use($node) { return $node->getSize(); }); } $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use($node) { $owner = $node->getOwner(); return $owner->getUID(); }); $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use($node) { $owner = $node->getOwner(); $displayName = $owner->getDisplayName(); return $displayName; }); }
/** * Adds all CardDAV-specific properties * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { $ns = '{' . self::NS_CARDDAV . '}'; if ($node instanceof IAddressBook) { $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); $propFind->handle($ns . 'supported-address-data', function() { return new Xml\Property\SupportedAddressData(); }); $propFind->handle($ns . 'supported-collation-set', function() { return new Xml\Property\SupportedCollationSet(); }); } if ($node instanceof DAVACL\IPrincipal) { $path = $propFind->getPath(); $propFind->handle('{' . self::NS_CARDDAV . '}addressbook-home-set', function() use ($path) { return new Href($this->getAddressBookHomeForPrincipal($path) . '/'); }); if ($this->directories) $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function() { return new Href($this->directories); }); } if ($node instanceof ICard) { // The address-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_CARDDAV . '}address-data', function() use ($node) { $val = $node->get(); if (is_resource($val)) $val = stream_get_contents($val); return $val; }); } }
/** * Adds all CardDAV-specific properties * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { $ns = '{' . self::NS_CARDDAV . '}'; if ($node instanceof IAddressBook) { $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); $propFind->handle($ns . 'supported-address-data', function () { return new Property\SupportedAddressData(); }); $propFind->handle($ns . 'supported-collation-set', function () { return new Property\SupportedCollationSet(); }); } if ($node instanceof DAVACL\IPrincipal) { $path = $propFind->getPath(); $propFind->handle('{' . self::NS_CARDDAV . '}addressbook-home-set', function () use($path) { return new DAV\Property\Href($this->getAddressBookHomeForPrincipal($path) . '/'); }); if ($this->directories) { $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function () { return new DAV\Property\HrefList($this->directories); }); } } if ($node instanceof ICard) { // The address-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_CARDDAV . '}address-data', function () use($node) { $val = $node->get(); if (is_resource($val)) { $val = stream_get_contents($val); } return $val; }); } if ($node instanceof UserAddressBooks) { $propFind->handle('{http://calendarserver.org/ns/}me-card', function () use($node) { $props = $this->server->getProperties($node->getOwner(), ['{http://sabredav.org/ns}vcard-url']); if (isset($props['{http://sabredav.org/ns}vcard-url'])) { return new DAV\Property\Href($props['{http://sabredav.org/ns}vcard-url']); } }); } }
/** * This method handler is invoked during fetching of properties. * * We use this event to add calendar-auto-schedule-specific properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if (!$node instanceof DAVACL\IPrincipal) { return; } $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); // schedule-outbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $outboxPath = $calendarHomePath . '/outbox/'; return new Href($outboxPath); }); // schedule-inbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function () use($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $inboxPath = $calendarHomePath . '/inbox/'; return new Href($inboxPath); }); $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function () use($principalUrl, $caldavPlugin) { // We don't support customizing this property yet, so in the // meantime we just grab the first calendar in the home-set. $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $nodes = $this->server->tree->getNodeForPath($calendarHomePath)->getChildren(); foreach ($nodes as $node) { if ($node instanceof ICalendar) { return new Href($calendarHomePath . '/' . $node->getName()); } } }); // The server currently reports every principal to be of type // 'INDIVIDUAL' $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function () { return 'INDIVIDUAL'; }); }