Skip to content

Commit

Permalink
preload shares for calendars when listing calendars
Browse files Browse the repository at this point in the history
Signed-off-by: Robin Appelman <[email protected]>
  • Loading branch information
icewind1991 authored and backportbot-nextcloud[bot] committed Aug 14, 2023
1 parent 6e68b86 commit 234ae93
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 0 deletions.
4 changes: 4 additions & 0 deletions apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -2858,6 +2858,10 @@ public function getShares(int $resourceId): array {
return $this->calendarSharingBackend->getShares($resourceId);
}

public function preloadShares(array $resourceIds): void {
$this->calendarSharingBackend->preloadShares($resourceIds);
}

/**
* @param boolean $value
* @param \OCA\DAV\CalDAV\Calendar $calendar
Expand Down
5 changes: 5 additions & 0 deletions apps/dav/lib/CalDAV/CalendarHome.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {

/** @var LoggerInterface */
private $logger;
private ?array $cachedChildren = null;

public function __construct(BackendInterface $caldavBackend, $principalInfo, LoggerInterface $logger) {
parent::__construct($caldavBackend, $principalInfo);
Expand Down Expand Up @@ -97,6 +98,9 @@ public function createExtendedCollection($name, MkCol $mkCol): void {
* @inheritdoc
*/
public function getChildren() {
if ($this->cachedChildren) {
return $this->cachedChildren;
}
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
$objects = [];
foreach ($calendars as $calendar) {
Expand Down Expand Up @@ -136,6 +140,7 @@ public function getChildren() {
}
}

$this->cachedChildren = $objects;
return $objects;
}

Expand Down
48 changes: 48 additions & 0 deletions apps/dav/lib/DAV/Sharing/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Db\TTransactional;
use OCP\Cache\CappedMemoryCache;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
Expand All @@ -48,19 +49,23 @@ class Backend {
public const ACCESS_READ_WRITE = 2;
public const ACCESS_READ = 3;

private CappedMemoryCache $shareCache;

public function __construct(IDBConnection $db, IUserManager $userManager, IGroupManager $groupManager, Principal $principalBackend, string $resourceType) {
$this->db = $db;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->principalBackend = $principalBackend;
$this->resourceType = $resourceType;
$this->shareCache = new CappedMemoryCache();
}

/**
* @param list<array{href: string, commonName: string, readOnly: bool}> $add
* @param list<string> $remove
*/
public function updateShares(IShareable $shareable, array $add, array $remove): void {
$this->shareCache->clear();
$this->atomic(function () use ($shareable, $add, $remove) {
foreach ($add as $element) {
$principal = $this->principalBackend->findByUri($element['href'], '');
Expand All @@ -81,6 +86,7 @@ public function updateShares(IShareable $shareable, array $add, array $remove):
* @param array{href: string, commonName: string, readOnly: bool} $element
*/
private function shareWith(IShareable $shareable, array $element): void {
$this->shareCache->clear();
$user = $element['href'];
$parts = explode(':', $user, 2);
if ($parts[0] !== 'principal') {
Expand Down Expand Up @@ -124,6 +130,7 @@ private function shareWith(IShareable $shareable, array $element): void {
}

public function deleteAllShares(int $resourceId): void {
$this->shareCache->clear();
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
Expand All @@ -132,6 +139,7 @@ public function deleteAllShares(int $resourceId): void {
}

public function deleteAllSharesByUser(string $principaluri): void {
$this->shareCache->clear();
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri)))
Expand All @@ -140,6 +148,7 @@ public function deleteAllSharesByUser(string $principaluri): void {
}

private function unshare(IShareable $shareable, string $element): void {
$this->shareCache->clear();
$parts = explode(':', $element, 2);
if ($parts[0] !== 'principal') {
return;
Expand Down Expand Up @@ -172,6 +181,10 @@ private function unshare(IShareable $shareable, string $element): void {
* @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}>
*/
public function getShares(int $resourceId): array {
$cached = $this->shareCache->get($resourceId);
if ($cached) {
return $cached;
}
$query = $this->db->getQueryBuilder();
$result = $query->select(['principaluri', 'access'])
->from('dav_shares')
Expand All @@ -193,9 +206,44 @@ public function getShares(int $resourceId): array {
];
}

$this->shareCache->set((string)$resourceId, $shares);
return $shares;
}

public function preloadShares(array $resourceIds): void {
$resourceIds = array_filter($resourceIds, function(int $resourceId) {
return !isset($this->shareCache[$resourceId]);
});
if (count($resourceIds) === 0) {
return;
}
$query = $this->db->getQueryBuilder();
$result = $query->select(['resourceid', 'principaluri', 'access'])
->from('dav_shares')
->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
->groupBy(['principaluri', 'access', 'resourceid'])
->executeQuery();

$sharesByResource = array_fill_keys($resourceIds, []);
while ($row = $result->fetch()) {
$resourceId = (int)$row['resourceid'];
$p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
$sharesByResource[$resourceId][] = [
'href' => "principal:{$row['principaluri']}",
'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
'status' => 1,
'readOnly' => (int) $row['access'] === self::ACCESS_READ,
'{http://owncloud.org/ns}principal' => (string)$row['principaluri'],
'{http://owncloud.org/ns}group-share' => isset($p['uri']) ? str_starts_with($p['uri'], 'principals/groups') : false
];
}

foreach ($resourceIds as $resourceId) {
$this->shareCache->set($resourceId, $sharesByResource[$resourceId]);
}
}

/**
* For shared resources the sharee is set in the ACL of the resource
*
Expand Down
16 changes: 16 additions & 0 deletions apps/dav/lib/DAV/Sharing/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
namespace OCA\DAV\DAV\Sharing;

use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\Connector\Sabre\Auth;
use OCA\DAV\DAV\Sharing\Xml\Invite;
use OCA\DAV\DAV\Sharing\Xml\ShareRequest;
Expand Down Expand Up @@ -201,6 +203,20 @@ public function httpPost(RequestInterface $request, ResponseInterface $response)
* @return void
*/
public function propFind(PropFind $propFind, INode $node) {
if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
$backend = $node->getCalDAVBackend();
if ($backend instanceof CalDavBackend) {
$calendars = $node->getChildren();
$calendars = array_filter($calendars, function (INode $node) {
return $node instanceof IShareable;
});
/** @var int[] $resourceIds */
$resourceIds = array_map(function (IShareable $node) {
return $node->getResourceId();
}, $calendars);
$backend->preloadShares($resourceIds);
}
}
if ($node instanceof IShareable) {
$propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function () use ($node) {
return new Invite(
Expand Down

0 comments on commit 234ae93

Please sign in to comment.