mirror of
https://github.com/nextcloud/server.git
synced 2026-02-20 00:12:30 -05:00
Merge pull request #40290 from nextcloud/backport/39861/stable25
[stable25] Calendar optimizations
This commit is contained in:
commit
0134edf286
4 changed files with 103 additions and 5 deletions
|
|
@ -215,6 +215,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
private IConfig $config;
|
||||
private bool $legacyEndpoint;
|
||||
private string $dbObjectPropertiesTable = 'calendarobjects_props';
|
||||
private array $cachedObjects = [];
|
||||
|
||||
public function __construct(IDBConnection $db,
|
||||
Principal $principalBackend,
|
||||
|
|
@ -1117,6 +1118,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return array|null
|
||||
*/
|
||||
public function getCalendarObject($calendarId, $objectUri, int $calendarType = self::CALENDAR_TYPE_CALENDAR) {
|
||||
$key = $calendarId . '::' . $objectUri . '::' . $calendarType;
|
||||
if (isset($this->cachedObjects[$key])) {
|
||||
return $this->cachedObjects[$key];
|
||||
}
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification', 'deleted_at'])
|
||||
->from('calendarobjects')
|
||||
|
|
@ -1131,6 +1136,12 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
return null;
|
||||
}
|
||||
|
||||
$object = $this->rowToCalendarObject($row);
|
||||
$this->cachedObjects[$key] = $object;
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function rowToCalendarObject(array $row): array {
|
||||
return [
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
|
|
@ -1217,6 +1228,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return string
|
||||
*/
|
||||
public function createCalendarObject($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
|
||||
$this->cachedObjects = [];
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
|
||||
// Try to detect duplicates
|
||||
|
|
@ -1309,6 +1321,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return string
|
||||
*/
|
||||
public function updateCalendarObject($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
|
||||
$this->cachedObjects = [];
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->update('calendarobjects')
|
||||
|
|
@ -1359,6 +1372,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @throws Exception
|
||||
*/
|
||||
public function moveCalendarObject(int $sourceCalendarId, int $targetCalendarId, int $objectId, string $oldPrincipalUri, string $newPrincipalUri, int $calendarType = self::CALENDAR_TYPE_CALENDAR): bool {
|
||||
$this->cachedObjects = [];
|
||||
$object = $this->getCalendarObjectById($oldPrincipalUri, $objectId);
|
||||
if (empty($object)) {
|
||||
return false;
|
||||
|
|
@ -1404,6 +1418,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @param int $classification
|
||||
*/
|
||||
public function setClassification($calendarObjectId, $classification) {
|
||||
$this->cachedObjects = [];
|
||||
if (!in_array($classification, [
|
||||
self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL
|
||||
])) {
|
||||
|
|
@ -1428,6 +1443,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId, $objectUri, $calendarType = self::CALENDAR_TYPE_CALENDAR, bool $forceDeletePermanently = false) {
|
||||
$this->cachedObjects = [];
|
||||
$data = $this->getCalendarObject($calendarId, $objectUri, $calendarType);
|
||||
|
||||
if ($data === null) {
|
||||
|
|
@ -1507,6 +1523,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @throws Forbidden
|
||||
*/
|
||||
public function restoreCalendarObject(array $objectData): void {
|
||||
$this->cachedObjects = [];
|
||||
$id = (int) $objectData['id'];
|
||||
$restoreUri = str_replace("-deleted.ics", ".ics", $objectData['uri']);
|
||||
$targetObject = $this->getCalendarObject(
|
||||
|
|
@ -1632,12 +1649,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
}
|
||||
}
|
||||
}
|
||||
$columns = ['uri'];
|
||||
if ($requirePostFilter) {
|
||||
$columns = ['uri', 'calendardata'];
|
||||
}
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select($columns)
|
||||
$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification', 'deleted_at'])
|
||||
->from('calendarobjects')
|
||||
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
|
||||
->andWhere($query->expr()->eq('calendartype', $query->createNamedParameter($calendarType)))
|
||||
|
|
@ -1658,6 +1671,11 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
|
||||
$result = [];
|
||||
while ($row = $stmt->fetch()) {
|
||||
// if we leave it as a blob we can't read it both from the post filter and the rowToCalendarObject
|
||||
if (isset($row['calendardata'])) {
|
||||
$row['calendardata'] = $this->readBlob($row['calendardata']);
|
||||
}
|
||||
|
||||
if ($requirePostFilter) {
|
||||
// validateFilterForObject will parse the calendar data
|
||||
// catch parsing errors
|
||||
|
|
@ -1682,6 +1700,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
}
|
||||
}
|
||||
$result[] = $row['uri'];
|
||||
$key = $calendarId . '::' . $row['uri'] . '::' . $calendarType;
|
||||
$this->cachedObjects[$key] = $this->rowToCalendarObject($row);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
@ -2631,6 +2651,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return void
|
||||
*/
|
||||
public function deleteSchedulingObject($principalUri, $objectUri) {
|
||||
$this->cachedObjects = [];
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete('schedulingobjects')
|
||||
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
|
||||
|
|
@ -2647,6 +2668,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return void
|
||||
*/
|
||||
public function createSchedulingObject($principalUri, $objectUri, $objectData) {
|
||||
$this->cachedObjects = [];
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->insert('schedulingobjects')
|
||||
->values([
|
||||
|
|
@ -2670,6 +2692,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return void
|
||||
*/
|
||||
protected function addChange($calendarId, $objectUri, $operation, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
|
||||
$this->cachedObjects = [];
|
||||
$table = $calendarType === self::CALENDAR_TYPE_CALENDAR ? 'calendars': 'calendarsubscriptions';
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
|
@ -2836,6 +2859,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
return $this->calendarSharingBackend->getShares($resourceId);
|
||||
}
|
||||
|
||||
public function preloadShares(array $resourceIds): void {
|
||||
$this->calendarSharingBackend->preloadShares($resourceIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $value
|
||||
* @param \OCA\DAV\CalDAV\Calendar $calendar
|
||||
|
|
@ -2905,6 +2932,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @param int $calendarType
|
||||
*/
|
||||
public function updateProperties($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
|
||||
$this->cachedObjects = [];
|
||||
$objectId = $this->getCalendarObjectId($calendarId, $objectUri, $calendarType);
|
||||
|
||||
try {
|
||||
|
|
@ -3063,6 +3091,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @param int $objectId
|
||||
*/
|
||||
protected function purgeProperties($calendarId, $objectId) {
|
||||
$this->cachedObjects = [];
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->dbObjectPropertiesTable)
|
||||
->where($query->expr()->eq('objectid', $query->createNamedParameter($objectId)))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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((string) $resourceId, $shares);
|
||||
return $shares;
|
||||
}
|
||||
|
||||
public function preloadShares(array $resourceIds): void {
|
||||
$resourceIds = array_filter($resourceIds, function(int $resourceId) {
|
||||
return !isset($this->shareCache[(string) $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((string) $resourceId, $sharesByResource[$resourceId]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For shared resources the sharee is set in the ACL of the resource
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in a new issue