Skip to content

Commit 6664801

Browse files
authored
Merge pull request #33697 from nextcloud/fix/improve-bulk-upload
Improve bulk upload and add a config switch for it
2 parents 5f0c21c + 30bfd5f commit 6664801

9 files changed

Lines changed: 152 additions & 48 deletions

File tree

apps/dav/lib/BulkUpload/BulkUploadPlugin.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* @copyright Copyright (c) 2021, Louis Chemineau <louis@chmn.me>
44
*
55
* @author Louis Chemineau <louis@chmn.me>
6+
* @author Côme Chilliet <come.chilliet@nextcloud.com>
67
*
78
* @license AGPL-3.0
89
*
@@ -27,19 +28,19 @@
2728
use Sabre\DAV\ServerPlugin;
2829
use Sabre\HTTP\RequestInterface;
2930
use Sabre\HTTP\ResponseInterface;
31+
use OCP\Files\DavUtil;
3032
use OCP\Files\Folder;
3133
use OCP\AppFramework\Http;
3234
use OCA\DAV\Connector\Sabre\MtimeSanitizer;
3335

3436
class BulkUploadPlugin extends ServerPlugin {
37+
private Folder $userFolder;
38+
private LoggerInterface $logger;
3539

36-
/** @var Folder */
37-
private $userFolder;
38-
39-
/** @var LoggerInterface */
40-
private $logger;
41-
42-
public function __construct(Folder $userFolder, LoggerInterface $logger) {
40+
public function __construct(
41+
Folder $userFolder,
42+
LoggerInterface $logger
43+
) {
4344
$this->userFolder = $userFolder;
4445
$this->logger = $logger;
4546
}
@@ -95,6 +96,8 @@ public function httpPost(RequestInterface $request, ResponseInterface $response)
9596
$writtenFiles[$headers['x-file-path']] = [
9697
"error" => false,
9798
"etag" => $node->getETag(),
99+
"fileid" => DavUtil::getDavFileId($node->getId()),
100+
"permissions" => DavUtil::getDavPermissions($node),
98101
];
99102
} catch (\Exception $e) {
100103
$this->logger->error($e->getMessage(), ['path' => $headers['x-file-path']]);

apps/dav/lib/Capabilities.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*
55
* @author Thomas Müller <thomas.mueller@tmit.eu>
66
* @author Louis Chemineau <louis@chmn.me>
7+
* @author Côme Chilliet <come.chilliet@nextcloud.com>
78
*
89
* @license AGPL-3.0
910
*
@@ -23,15 +24,24 @@
2324
namespace OCA\DAV;
2425

2526
use OCP\Capabilities\ICapability;
27+
use OCP\IConfig;
2628

2729
class Capabilities implements ICapability {
30+
private IConfig $config;
31+
32+
public function __construct(IConfig $config) {
33+
$this->config = $config;
34+
}
35+
2836
public function getCapabilities() {
29-
return [
37+
$capabilities = [
3038
'dav' => [
3139
'chunking' => '1.0',
32-
// disabled because of https://github.com/nextcloud/desktop/issues/4243
33-
// 'bulkupload' => '1.0',
3440
]
3541
];
42+
if ($this->config->getSystemValueBool('bulkupload.enabled', true)) {
43+
$capabilities['dav']['bulkupload'] = '1.0';
44+
}
45+
return $capabilities;
3646
}
3747
}

apps/dav/lib/Connector/Sabre/Node.php

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
use OC\Files\Mount\MoveableMount;
3939
use OC\Files\Node\File;
4040
use OC\Files\Node\Folder;
41-
use OC\Files\Storage\Wrapper\Wrapper;
4241
use OC\Files\View;
4342
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
43+
use OCP\Files\DavUtil;
4444
use OCP\Files\FileInfo;
4545
use OCP\Files\IRootFolder;
4646
use OCP\Files\StorageNotAvailableException;
@@ -252,10 +252,8 @@ public function getId() {
252252
* @return string|null
253253
*/
254254
public function getFileId() {
255-
if ($this->info->getId()) {
256-
$instanceId = \OC_Util::getInstanceId();
257-
$id = sprintf('%08d', $this->info->getId());
258-
return $id . $instanceId;
255+
if ($id = $this->info->getId()) {
256+
return DavUtil::getDavFileId($id);
259257
}
260258

261259
return null;
@@ -381,35 +379,7 @@ public function getNoteFromShare($user) {
381379
* @return string
382380
*/
383381
public function getDavPermissions() {
384-
$p = '';
385-
if ($this->info->isShared()) {
386-
$p .= 'S';
387-
}
388-
if ($this->info->isShareable()) {
389-
$p .= 'R';
390-
}
391-
if ($this->info->isMounted()) {
392-
$p .= 'M';
393-
}
394-
if ($this->info->isReadable()) {
395-
$p .= 'G';
396-
}
397-
if ($this->info->isDeletable()) {
398-
$p .= 'D';
399-
}
400-
if ($this->info->isUpdateable()) {
401-
$p .= 'NV'; // Renameable, Moveable
402-
}
403-
if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
404-
if ($this->info->isUpdateable()) {
405-
$p .= 'W';
406-
}
407-
} else {
408-
if ($this->info->isCreatable()) {
409-
$p .= 'CK';
410-
}
411-
}
412-
return $p;
382+
return DavUtil::getDavPermissions($this->info);
413383
}
414384

415385
public function getOwner() {

apps/dav/lib/Server.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,10 @@ public function __construct(IRequest $request, string $baseUri) {
313313
$view
314314
));
315315
$this->server->addPlugin(
316-
new BulkUploadPlugin($userFolder, $logger)
316+
new BulkUploadPlugin(
317+
$userFolder,
318+
$logger
319+
)
317320
);
318321
}
319322
$this->server->addPlugin(new \OCA\DAV\CalDAV\BirthdayCalendar\EnablePlugin(

apps/dav/tests/unit/CapabilitiesTest.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,39 @@
2424
namespace OCA\DAV\Tests\unit;
2525

2626
use OCA\DAV\Capabilities;
27+
use OCP\IConfig;
2728
use Test\TestCase;
2829

2930
/**
3031
* @package OCA\DAV\Tests\unit
3132
*/
3233
class CapabilitiesTest extends TestCase {
3334
public function testGetCapabilities() {
34-
$capabilities = new Capabilities();
35+
$config = $this->createMock(IConfig::class);
36+
$config->expects($this->once())
37+
->method('getSystemValueBool')
38+
->with('bulkupload.enabled', $this->isType('bool'))
39+
->willReturn(false);
40+
$capabilities = new Capabilities($config);
3541
$expected = [
3642
'dav' => [
3743
'chunking' => '1.0',
38-
// disabled because of https://github.com/nextcloud/desktop/issues/4243
39-
// 'bulkupload' => '1.0',
44+
],
45+
];
46+
$this->assertSame($expected, $capabilities->getCapabilities());
47+
}
48+
49+
public function testGetCapabilitiesWithBulkUpload() {
50+
$config = $this->createMock(IConfig::class);
51+
$config->expects($this->once())
52+
->method('getSystemValueBool')
53+
->with('bulkupload.enabled', $this->isType('bool'))
54+
->willReturn(true);
55+
$capabilities = new Capabilities($config);
56+
$expected = [
57+
'dav' => [
58+
'chunking' => '1.0',
59+
'bulkupload' => '1.0',
4060
],
4161
];
4262
$this->assertSame($expected, $capabilities->getCapabilities());

config/config.sample.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,4 +2238,11 @@
22382238
* Defaults to ``false``
22392239
*/
22402240
'projects.enabled' => false,
2241+
2242+
/**
2243+
* Enable the bulk upload feature.
2244+
*
2245+
* Defaults to ``true``
2246+
*/
2247+
'bulkupload.enabled' => true,
22412248
];

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@
262262
'OCP\\Files\\Config\\IMountProviderCollection' => $baseDir . '/lib/public/Files/Config/IMountProviderCollection.php',
263263
'OCP\\Files\\Config\\IRootMountProvider' => $baseDir . '/lib/public/Files/Config/IRootMountProvider.php',
264264
'OCP\\Files\\Config\\IUserMountCache' => $baseDir . '/lib/public/Files/Config/IUserMountCache.php',
265+
'OCP\\Files\\DavUtil' => $baseDir . '/lib/public/Files/DavUtil.php',
265266
'OCP\\Files\\EmptyFileNameException' => $baseDir . '/lib/public/Files/EmptyFileNameException.php',
266267
'OCP\\Files\\EntityTooLargeException' => $baseDir . '/lib/public/Files/EntityTooLargeException.php',
267268
'OCP\\Files\\Events\\BeforeDirectFileDownloadEvent' => $baseDir . '/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
295295
'OCP\\Files\\Config\\IMountProviderCollection' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IMountProviderCollection.php',
296296
'OCP\\Files\\Config\\IRootMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IRootMountProvider.php',
297297
'OCP\\Files\\Config\\IUserMountCache' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IUserMountCache.php',
298+
'OCP\\Files\\DavUtil' => __DIR__ . '/../../..' . '/lib/public/Files/DavUtil.php',
298299
'OCP\\Files\\EmptyFileNameException' => __DIR__ . '/../../..' . '/lib/public/Files/EmptyFileNameException.php',
299300
'OCP\\Files\\EntityTooLargeException' => __DIR__ . '/../../..' . '/lib/public/Files/EntityTooLargeException.php',
300301
'OCP\\Files\\Events\\BeforeDirectFileDownloadEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php',

lib/public/Files/DavUtil.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
4+
*
5+
* @author Arthur Schiwon <blizzz@owncloud.com>
6+
* @author Bart Visscher <bartv@thisnet.nl>
7+
* @author Jakob Sack <mail@jakobsack.de>
8+
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
9+
* @author Klaas Freitag <freitag@owncloud.com>
10+
* @author Markus Goetz <markus@woboq.com>
11+
* @author Morris Jobke <hey@morrisjobke.de>
12+
* @author Robin Appelman <icewind@owncloud.com>
13+
* @author Thomas Müller <thomas.mueller@tmit.eu>
14+
* @author Vincent Petry <pvince81@owncloud.com>
15+
* @author Côme Chilliet <come.chilliet@nextcloud.com>
16+
*
17+
* @license AGPL-3.0
18+
*
19+
* This code is free software: you can redistribute it and/or modify
20+
* it under the terms of the GNU Affero General Public License, version 3,
21+
* as published by the Free Software Foundation.
22+
*
23+
* This program is distributed in the hope that it will be useful,
24+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
25+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26+
* GNU Affero General Public License for more details.
27+
*
28+
* You should have received a copy of the GNU Affero General Public License, version 3,
29+
* along with this program. If not, see <http://www.gnu.org/licenses/>
30+
*
31+
*/
32+
33+
namespace OCP\Files;
34+
35+
/**
36+
* This class provides different helper functions related to WebDAV protocol
37+
*
38+
* @since 25.0.0
39+
*/
40+
class DavUtil {
41+
/**
42+
* Compute the fileId to use for dav responses
43+
*
44+
* @param int $id Id of the file returned by FileInfo::getId
45+
* @since 25.0.0
46+
*/
47+
public static function getDavFileId(int $id): string {
48+
$instanceId = \OC_Util::getInstanceId();
49+
$id = sprintf('%08d', $id);
50+
return $id . $instanceId;
51+
}
52+
53+
/**
54+
* Compute the format needed for returning permissions for dav
55+
*
56+
* @since 25.0.0
57+
*/
58+
public static function getDavPermissions(FileInfo $info): string {
59+
$p = '';
60+
if ($info->isShared()) {
61+
$p .= 'S';
62+
}
63+
if ($info->isShareable()) {
64+
$p .= 'R';
65+
}
66+
if ($info->isMounted()) {
67+
$p .= 'M';
68+
}
69+
if ($info->isReadable()) {
70+
$p .= 'G';
71+
}
72+
if ($info->isDeletable()) {
73+
$p .= 'D';
74+
}
75+
if ($info->isUpdateable()) {
76+
$p .= 'NV'; // Renameable, Moveable
77+
}
78+
if ($info->getType() === FileInfo::TYPE_FILE) {
79+
if ($info->isUpdateable()) {
80+
$p .= 'W';
81+
}
82+
} else {
83+
if ($info->isCreatable()) {
84+
$p .= 'CK';
85+
}
86+
}
87+
return $p;
88+
}
89+
}

0 commit comments

Comments
 (0)