]> source.dussan.org Git - nextcloud-server.git/commitdiff
preload shares for calendars when listing calendars
authorRobin Appelman <robin@icewind.nl>
Tue, 25 Jul 2023 15:08:50 +0000 (17:08 +0200)
committerRobin Appelman <robin@icewind.nl>
Mon, 14 Aug 2023 12:52:06 +0000 (14:52 +0200)
Signed-off-by: Robin Appelman <robin@icewind.nl>
apps/dav/lib/CalDAV/CalDavBackend.php
apps/dav/lib/CalDAV/CalendarHome.php
apps/dav/lib/DAV/Sharing/Backend.php
apps/dav/lib/DAV/Sharing/Plugin.php

index 8df8bbb7d03e07d18611b7defe7d4868149b6b73..1f83efabfea87d8765803b47fcba571ec0b55e88 100644 (file)
@@ -2824,6 +2824,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
                return $this->calendarSharingBackend->getShares($resourceId);
        }
 
+       public function preloadShares(array $resourceIds) {
+               $this->calendarSharingBackend->preloadShares($resourceIds);
+       }
+
        /**
         * @param boolean $value
         * @param \OCA\DAV\CalDAV\Calendar $calendar
index a59e76d121fb982da173d3fa6a20f4e00740b325..cbf5cebc9e74ff41ad93c4fd033fbe1814d25538 100644 (file)
@@ -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);
@@ -97,6 +98,9 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
         * @inheritdoc
         */
        public function getChildren() {
+               if ($this->cachedChildren) {
+                       return $this->cachedChildren;
+               }
                $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
                $objects = [];
                foreach ($calendars as $calendar) {
@@ -136,6 +140,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
                        }
                }
 
+               $this->cachedChildren = $objects;
                return $objects;
        }
 
index 7ba5ffd270004a477b3d3e6a6782e799ac6a6330..c20a580b642ed28e84f694695accd8c71a06cadb 100644 (file)
@@ -29,6 +29,7 @@
 namespace OCA\DAV\DAV\Sharing;
 
 use OCA\DAV\Connector\Sabre\Principal;
+use OCP\Cache\CappedMemoryCache;
 use OCP\IDBConnection;
 use OCP\IGroupManager;
 use OCP\IUserManager;
@@ -45,12 +46,15 @@ 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();
        }
 
        /**
@@ -58,6 +62,7 @@ class Backend {
         * @param list<string> $remove
         */
        public function updateShares(IShareable $shareable, array $add, array $remove): void {
+               $this->shareCache->clear();
                foreach ($add as $element) {
                        $principal = $this->principalBackend->findByUri($element['href'], '');
                        if ($principal !== '') {
@@ -76,6 +81,7 @@ class Backend {
         * @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') {
@@ -119,6 +125,7 @@ class Backend {
        }
 
        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)))
@@ -127,6 +134,7 @@ class Backend {
        }
 
        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)))
@@ -135,6 +143,7 @@ class Backend {
        }
 
        private function unshare(IShareable $shareable, string $element): void {
+               $this->shareCache->clear();
                $parts = explode(':', $element, 2);
                if ($parts[0] !== 'principal') {
                        return;
@@ -167,6 +176,10 @@ class Backend {
         * @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')
@@ -188,9 +201,44 @@ class Backend {
                        ];
                }
 
+               $this->shareCache->set($resourceId, $shares);
                return $shares;
        }
 
+       public function preloadShares(array $resourceIds) {
+               $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
         *
index a4b2cd3681cc73c2e60b18ac9b23bc01f801ac66..8ddcb664fd51f5b7e494a342db89b608c4e37e1c 100644 (file)
@@ -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;
@@ -201,6 +203,20 @@ class Plugin extends ServerPlugin {
         * @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(