mirror of
https://github.com/nextcloud/server.git
synced 2026-06-06 23:34:22 -04:00
Merge pull request #30368 from nextcloud/dav-allow-object-properties
Allow DAV Object properties
This commit is contained in:
commit
2ee948e8d1
6 changed files with 187 additions and 13 deletions
|
|
@ -5,7 +5,7 @@
|
|||
<name>WebDAV</name>
|
||||
<summary>WebDAV endpoint</summary>
|
||||
<description>WebDAV endpoint</description>
|
||||
<version>1.23.0</version>
|
||||
<version>1.24.0</version>
|
||||
<licence>agpl</licence>
|
||||
<author>owncloud.org</author>
|
||||
<namespace>DAV</namespace>
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
<step>OCA\DAV\Migration\RemoveOrphanEventsAndContacts</step>
|
||||
<step>OCA\DAV\Migration\RemoveClassifiedEventActivity</step>
|
||||
<step>OCA\DAV\Migration\RemoveDeletedUsersCalendarSubscriptions</step>
|
||||
<step>OCA\DAV\Migration\RemoveObjectProperties</step>
|
||||
</post-migration>
|
||||
<live-migration>
|
||||
<step>OCA\DAV\Migration\ChunkCleanup</step>
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ return array(
|
|||
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => $baseDir . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
|
||||
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => $baseDir . '/../lib/Migration/RemoveClassifiedEventActivity.php',
|
||||
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => $baseDir . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
|
||||
'OCA\\DAV\\Migration\\RemoveObjectProperties' => $baseDir . '/../lib/Migration/RemoveObjectProperties.php',
|
||||
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => $baseDir . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
|
||||
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => $baseDir . '/../lib/Migration/Version1004Date20170825134824.php',
|
||||
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => $baseDir . '/../lib/Migration/Version1004Date20170919104507.php',
|
||||
|
|
@ -278,6 +279,7 @@ return array(
|
|||
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => $baseDir . '/../lib/Migration/Version1016Date20201109085907.php',
|
||||
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php',
|
||||
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php',
|
||||
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => $baseDir . '/../lib/Migration/Version1024Date20211221144219.php',
|
||||
'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
|
||||
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => __DIR__ . '/..' . '/../lib/Migration/RemoveClassifiedEventActivity.php',
|
||||
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => __DIR__ . '/..' . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
|
||||
'OCA\\DAV\\Migration\\RemoveObjectProperties' => __DIR__ . '/..' . '/../lib/Migration/RemoveObjectProperties.php',
|
||||
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => __DIR__ . '/..' . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
|
||||
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170825134824.php',
|
||||
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170919104507.php',
|
||||
|
|
@ -293,6 +294,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => __DIR__ . '/..' . '/../lib/Migration/Version1016Date20201109085907.php',
|
||||
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php',
|
||||
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php',
|
||||
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => __DIR__ . '/..' . '/../lib/Migration/Version1024Date20211221144219.php',
|
||||
'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
|
|||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Sabre\DAV\Tree;
|
||||
use Sabre\DAV\Xml\Property\Complex;
|
||||
use function array_intersect;
|
||||
|
||||
class CustomPropertiesBackend implements BackendInterface {
|
||||
|
|
@ -38,6 +39,21 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
/** @var string */
|
||||
private const TABLE_NAME = 'properties';
|
||||
|
||||
/**
|
||||
* Value is stored as string.
|
||||
*/
|
||||
public const PROPERTY_TYPE_STRING = 1;
|
||||
|
||||
/**
|
||||
* Value is stored as XML fragment.
|
||||
*/
|
||||
public const PROPERTY_TYPE_XML = 2;
|
||||
|
||||
/**
|
||||
* Value is stored as a property object.
|
||||
*/
|
||||
public const PROPERTY_TYPE_OBJECT = 3;
|
||||
|
||||
/**
|
||||
* Ignored properties
|
||||
*
|
||||
|
|
@ -239,7 +255,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
$result = $qb->executeQuery();
|
||||
$props = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$props[$row['propertyname']] = $row['propertyvalue'];
|
||||
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
|
||||
}
|
||||
$result->closeCursor();
|
||||
return $props;
|
||||
|
|
@ -282,7 +298,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
|
||||
$props = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$props[$row['propertyname']] = $row['propertyvalue'];
|
||||
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
|
||||
}
|
||||
|
||||
$result->closeCursor();
|
||||
|
|
@ -304,9 +320,9 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
|
||||
|
||||
$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
|
||||
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
|
||||
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`, `valuetype`) VALUES(?,?,?,?,?)';
|
||||
|
||||
$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
|
||||
$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?, `valuetype` = ?' .
|
||||
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
|
||||
|
||||
// TODO: use "insert or update" strategy ?
|
||||
|
|
@ -325,24 +341,22 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
if ($propertyValue instanceOf \Sabre\DAV\Xml\Property\Complex) {
|
||||
$propertyValue = $propertyValue->getXml();
|
||||
} elseif (!is_string($propertyValue)) {
|
||||
$propertyValue = (string)$propertyValue;
|
||||
}
|
||||
[$value, $valueType] = $this->encodeValueForDatabase($propertyValue);
|
||||
if (!array_key_exists($propertyName, $existing)) {
|
||||
$this->connection->executeUpdate($insertStatement,
|
||||
[
|
||||
$this->user->getUID(),
|
||||
$this->formatPath($path),
|
||||
$propertyName,
|
||||
$propertyValue,
|
||||
$value,
|
||||
$valueType
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$this->connection->executeUpdate($updateStatement,
|
||||
[
|
||||
$propertyValue,
|
||||
$value,
|
||||
$valueType,
|
||||
$this->user->getUID(),
|
||||
$this->formatPath($path),
|
||||
$propertyName,
|
||||
|
|
@ -367,8 +381,40 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
private function formatPath(string $path): string {
|
||||
if (strlen($path) > 250) {
|
||||
return sha1($path);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
private function encodeValueForDatabase($value): array {
|
||||
if (is_scalar($value)) {
|
||||
$valueType = self::PROPERTY_TYPE_STRING;
|
||||
} elseif ($value instanceof Complex) {
|
||||
$valueType = self::PROPERTY_TYPE_XML;
|
||||
$value = $value->getXml();
|
||||
} else {
|
||||
return $path;
|
||||
$valueType = self::PROPERTY_TYPE_OBJECT;
|
||||
$value = serialize($value);
|
||||
}
|
||||
return [$value, $valueType];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|Complex|string
|
||||
*/
|
||||
private function decodeValueFromDatabase(string $value, int $valueType) {
|
||||
switch ($valueType) {
|
||||
case self::PROPERTY_TYPE_XML:
|
||||
return new Complex($value);
|
||||
case self::PROPERTY_TYPE_OBJECT:
|
||||
return unserialize($value);
|
||||
case self::PROPERTY_TYPE_STRING:
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
65
apps/dav/lib/Migration/RemoveObjectProperties.php
Normal file
65
apps/dav/lib/Migration/RemoveObjectProperties.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021, Thomas Citharel <nextcloud@tcit.fr>.
|
||||
*
|
||||
* @author Thomas Citharel <nextcloud@tcit.fr>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\Migration;
|
||||
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
|
||||
class RemoveObjectProperties implements IRepairStep {
|
||||
private const RESOURCE_TYPE_PROPERTY = '{DAV:}resourcetype';
|
||||
private const ME_CARD_PROPERTY = '{http://calendarserver.org/ns/}me-card';
|
||||
private const CALENDAR_TRANSP_PROPERTY = '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp';
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* RemoveObjectProperties constructor.
|
||||
*
|
||||
* @param IDBConnection $connection
|
||||
*/
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getName() {
|
||||
return 'Remove invalid object properties';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function run(IOutput $output) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$updated = $query->delete('properties')
|
||||
->where($query->expr()->in('propertyname', $query->createNamedParameter([self::RESOURCE_TYPE_PROPERTY, self::ME_CARD_PROPERTY, self::CALENDAR_TRANSP_PROPERTY], IQueryBuilder::PARAM_STR_ARRAY)))
|
||||
->andWhere($query->expr()->eq('propertyvalue', $query->createNamedParameter('Object')))
|
||||
->executeStatement();
|
||||
|
||||
$output->info("$updated invalid object properties removed.");
|
||||
}
|
||||
}
|
||||
58
apps/dav/lib/Migration/Version1024Date20211221144219.php
Normal file
58
apps/dav/lib/Migration/Version1024Date20211221144219.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\DAV\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use OCA\DAV\DAV\CustomPropertiesBackend;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\DB\Types;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version1024Date20211221144219 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
*/
|
||||
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
$propertiesTable = $schema->getTable('properties');
|
||||
|
||||
if ($propertiesTable->hasColumn('valuetype')) {
|
||||
return null;
|
||||
}
|
||||
$propertiesTable->addColumn('valuetype', Types::SMALLINT, [
|
||||
'notnull' => false,
|
||||
'default' => CustomPropertiesBackend::PROPERTY_TYPE_STRING
|
||||
]);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
*/
|
||||
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue