From 7040aa612c951d808b22df302c71458a4eed3ee4 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 11:44:15 +0545 Subject: [PATCH 1/9] test: add unit test for PROPFIND with varying status values --- tests/Sabre/DAV/ClientTest.php | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index 33f960ace8..82f46ac588 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -185,6 +185,91 @@ public function testPropFindDepth1() ], $request->getHeaders()); } + /** + * A PROPFIND on a folder containing resources will filter out the meta-data + * for resources that have a status that is not 200. + * For example, resources that are "403" (access is forbidden to the user) + * or "425" (too early), the resource may have been recently uploaded and + * still has some processing happening in the server before being made + * available for regular access. + */ + public function testPropFindMixedErrors() + { + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = << + + + /folder1 + + + + Folder1 + + HTTP/1.1 200 OK + + + + /folder1/file1.txt + + + + File1 + + HTTP/1.1 200 OK + + + + /folder1/file2.txt + + + + File2 + + HTTP/1.1 403 Forbidden + + + + /folder1/file3.txt + + + + File3 + + HTTP/1.1 425 Too Early + + + +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propFind('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir'], 1); + + self::assertEquals([ + '/folder1' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + '/folder1/file1.txt' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File1', + ], + '/folder1/file2.txt' => [], + '/folder1/file3.txt' => [], + ], $result); + + $request = $client->request; + self::assertEquals('PROPFIND', $request->getMethod()); + self::assertEquals('/folder1', $request->getUrl()); + self::assertEquals([ + 'Depth' => ['1'], + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + } + public function testPropPatch() { $client = new ClientMock([ From d4bce5993d629fa8464ece20511db1f3120e170d Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 11:58:57 +0545 Subject: [PATCH 2/9] refactor: split out some code into doPropFind --- lib/DAV/Client.php | 54 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index a9de71cdbb..87fa11fbf0 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -195,6 +195,45 @@ public function __construct(array $settings) * @return array */ public function propFind($url, array $properties, $depth = 0) + { + $result = $this->doPropFind($url, $properties, $depth); + + // If depth was 0, we only return the top item + if (0 === $depth) { + reset($result); + $result = current($result); + + return isset($result[200]) ? $result[200] : []; + } + + $newResult = []; + foreach ($result as $href => $statusList) { + $newResult[$href] = isset($statusList[200]) ? $statusList[200] : []; + } + + return $newResult; + } + + /** + * Does a PROPFIND request. + * + * The list of requested properties must be specified as an array, in clark + * notation. + * + * The returned array will contain a list of filenames as keys, and + * properties as values. + * + * The properties array will contain the list of properties. + * + * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * made to the server to also return all child resources. + * + * @param string $url + * @param int $depth + * + * @return array + */ + private function doPropFind($url, array $properties, $depth = 0) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; @@ -234,20 +273,7 @@ public function propFind($url, array $properties, $depth = 0) $result = $this->parseMultiStatus($response->getBodyAsString()); - // If depth was 0, we only return the top item - if (0 === $depth) { - reset($result); - $result = current($result); - - return isset($result[200]) ? $result[200] : []; - } - - $newResult = []; - foreach ($result as $href => $statusList) { - $newResult[$href] = isset($statusList[200]) ? $statusList[200] : []; - } - - return $newResult; + return $result; } /** From 5b18b9faa8cbcd0d542e325324fd4de9cda873f4 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 12:38:07 +0545 Subject: [PATCH 3/9] feat: add propFindUnfiltered public method This returns all the properties received in the server response, even for resources that have a status that is not 200 (success). --- lib/DAV/Client.php | 46 ++++++++++- tests/Sabre/DAV/ClientTest.php | 138 +++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index 87fa11fbf0..19aa08ef33 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -174,7 +174,7 @@ public function __construct(array $settings) } /** - * Does a PROPFIND request. + * Does a PROPFIND request with filtered response. * * The list of requested properties must be specified as an array, in clark * notation. @@ -214,6 +214,50 @@ public function propFind($url, array $properties, $depth = 0) return $newResult; } + /** + * Does a PROPFIND request with unfiltered response. + * + * The list of requested properties must be specified as an array, in clark + * notation. + * + * The returned array will contain a list of filenames as keys, and + * properties as values. + * + * The properties array will contain the list of properties. All properties + * that are actually returned from the server are returned by this method. + * + * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * made to the server to also return all child resources. + * + * @param string $url + * @param int $depth + * + * @return array + */ + public function propFindUnfiltered($url, array $properties, $depth = 0) + { + $result = $this->doPropFind($url, $properties, $depth); + + // If depth was 0, we only return the top item + if (0 === $depth) { + reset($result); + $resourceStatusList = current($result); + reset($resourceStatusList); + // $resourceStatus = key($resourceStatusList); + return current($resourceStatusList); + } + + $newResult = []; + foreach ($result as $href => $statusList) { + reset($statusList); + // $resourceStatus = key($statusList); + $resourceProperties = current($statusList); + $newResult[$href] = $resourceProperties; + } + + return $newResult; + } + /** * Does a PROPFIND request. * diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index 82f46ac588..7fab0be931 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -270,6 +270,144 @@ public function testPropFindMixedErrors() ], $request->getHeaders()); } + /** + * An "unfiltered" PROPFIND on a folder containing resources will include the + * meta-data for resources that have a status that is not 200. + * For example, resources that are "403" (access is forbidden to the user) + * or "425" (too early), the resource may have been recently uploaded and + * still has some processing happening in the server before being made + * available for regular access. + */ + public function testPropFindUnfilteredDepth0() + { + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = << + + + /folder1 + + + + Folder1 + + HTTP/1.1 200 OK + + + +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir']); + + self::assertEquals([ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], $result); + + $request = $client->request; + self::assertEquals('PROPFIND', $request->getMethod()); + self::assertEquals('/folder1', $request->getUrl()); + self::assertEquals([ + 'Depth' => ['0'], + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + } + + /** + * An "unfiltered" PROPFIND on a folder containing resources will include the + * meta-data for resources that have a status that is not 200. + * For example, resources that are "403" (access is forbidden to the user) + * or "425" (too early), the resource may have been recently uploaded and + * still has some processing happening in the server before being made + * available for regular access. + */ + public function testPropFindUnfiltered() + { + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = << + + + /folder1 + + + + Folder1 + + HTTP/1.1 200 OK + + + + /folder1/file1.txt + + + + File1 + + HTTP/1.1 200 OK + + + + /folder1/file2.txt + + + + File2 + + HTTP/1.1 403 Forbidden + + + + /folder1/file3.txt + + + + File3 + + HTTP/1.1 425 Too Early + + + +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir'], 1); + + self::assertEquals([ + '/folder1' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + '/folder1/file1.txt' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File1', + ], + '/folder1/file2.txt' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File2', + ], + '/folder1/file3.txt' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File3', + ], + ], $result); + + $request = $client->request; + self::assertEquals('PROPFIND', $request->getMethod()); + self::assertEquals('/folder1', $request->getUrl()); + self::assertEquals([ + 'Depth' => ['1'], + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + } + public function testPropPatch() { $client = new ClientMock([ From a474f67752d22792d8e25c9e00c49b20798022c6 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 13:35:54 +0545 Subject: [PATCH 4/9] feat: return both properties and status in propFindUnfiltered --- lib/DAV/Client.php | 8 +++----- tests/Sabre/DAV/ClientTest.php | 35 ++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index 19aa08ef33..027aa0d6de 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -243,16 +243,14 @@ public function propFindUnfiltered($url, array $properties, $depth = 0) reset($result); $resourceStatusList = current($result); reset($resourceStatusList); - // $resourceStatus = key($resourceStatusList); - return current($resourceStatusList); + + return ['properties' => current($resourceStatusList), 'status' => key($resourceStatusList)]; } $newResult = []; foreach ($result as $href => $statusList) { reset($statusList); - // $resourceStatus = key($statusList); - $resourceProperties = current($statusList); - $newResult[$href] = $resourceProperties; + $newResult[$href] = ['properties' => current($statusList), 'status' => key($statusList)]; } return $newResult; diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index 7fab0be931..06fcd2ca4f 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -304,8 +304,11 @@ public function testPropFindUnfilteredDepth0() $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir']); self::assertEquals([ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + 'status' => 200, ], $result); $request = $client->request; @@ -382,20 +385,32 @@ public function testPropFindUnfiltered() self::assertEquals([ '/folder1' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + 'status' => 200, ], '/folder1/file1.txt' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File1', + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File1', + ], + 'status' => 200, ], '/folder1/file2.txt' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File2', + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File2', + ], + 'status' => 403, ], '/folder1/file3.txt' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File3', + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File3', + ], + 'status' => 425, ], ], $result); From 55a68501f53309c86697bda90f025d0821307826 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 16:30:30 +0545 Subject: [PATCH 5/9] test: adjust testPropFindUnfiltered to show multi-propstat behavior --- tests/Sabre/DAV/ClientTest.php | 37 +++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index 06fcd2ca4f..a63184fe65 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -346,6 +346,12 @@ public function testPropFindUnfiltered() HTTP/1.1 200 OK + + + + + HTTP/1.1 404 Not Found + /folder1/file1.txt @@ -353,6 +359,7 @@ public function testPropFindUnfiltered() File1 + 12 HTTP/1.1 200 OK @@ -363,6 +370,7 @@ public function testPropFindUnfiltered() File2 + 27 HTTP/1.1 403 Forbidden @@ -373,15 +381,32 @@ public function testPropFindUnfiltered() File3 + 42 HTTP/1.1 425 Too Early + + /folder1/subfolder + + + + SubFolder + + HTTP/1.1 200 OK + + + + + + HTTP/1.1 404 Not Found + + XML; $client->response = new Response(207, [], $responseBody); - $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir'], 1); + $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{DAV:}contentlength', '{urn:zim}gir'], 1); self::assertEquals([ '/folder1' => [ @@ -395,6 +420,7 @@ public function testPropFindUnfiltered() 'properties' => [ '{DAV:}resourcetype' => null, '{DAV:}displayname' => 'File1', + '{DAV:}contentlength' => 12, ], 'status' => 200, ], @@ -402,6 +428,7 @@ public function testPropFindUnfiltered() 'properties' => [ '{DAV:}resourcetype' => null, '{DAV:}displayname' => 'File2', + '{DAV:}contentlength' => 27, ], 'status' => 403, ], @@ -409,9 +436,17 @@ public function testPropFindUnfiltered() 'properties' => [ '{DAV:}resourcetype' => null, '{DAV:}displayname' => 'File3', + '{DAV:}contentlength' => 42, ], 'status' => 425, ], + '/folder1/subfolder' => [ + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'SubFolder', + ], + 'status' => 200, + ], ], $result); $request = $client->request; From 344e037b3625623712f62b9414e97af2fc6b01f8 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 24 Nov 2023 17:01:53 +0545 Subject: [PATCH 6/9] feat: return all property-status combinations from PROPFIND --- lib/DAV/Client.php | 21 ++++---- tests/Sabre/DAV/ClientTest.php | 92 +++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index 027aa0d6de..03b98b11d7 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -237,20 +237,21 @@ public function propFind($url, array $properties, $depth = 0) public function propFindUnfiltered($url, array $properties, $depth = 0) { $result = $this->doPropFind($url, $properties, $depth); + $newResult = []; // If depth was 0, we only return the top item if (0 === $depth) { reset($result); - $resourceStatusList = current($result); - reset($resourceStatusList); - - return ['properties' => current($resourceStatusList), 'status' => key($resourceStatusList)]; - } - - $newResult = []; - foreach ($result as $href => $statusList) { - reset($statusList); - $newResult[$href] = ['properties' => current($statusList), 'status' => key($statusList)]; + $statusList = current($result); + foreach ($statusList as $statusCode => $associatedProperties) { + $newResult[] = ['properties' => $associatedProperties, 'status' => $statusCode]; + } + } else { + foreach ($result as $href => $statusList) { + foreach ($statusList as $statusCode => $associatedProperties) { + $newResult[$href][] = ['properties' => $associatedProperties, 'status' => $statusCode]; + } + } } return $newResult; diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index a63184fe65..9353b691b7 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -296,19 +296,33 @@ public function testPropFindUnfilteredDepth0() HTTP/1.1 200 OK + + + + + HTTP/1.1 404 Not Found + XML; $client->response = new Response(207, [], $responseBody); - $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{urn:zim}gir']); + $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{DAV:}contentlength', '{urn:zim}gir']); self::assertEquals([ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', + [ + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + 'status' => 200, + ], + [ + 'properties' => [ + '{DAV:}contentlength' => null, + ], + 'status' => 404, ], - 'status' => 200, ], $result); $request = $client->request; @@ -410,42 +424,64 @@ public function testPropFindUnfiltered() self::assertEquals([ '/folder1' => [ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', + [ + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', + ], + 'status' => 200, + ], + [ + 'properties' => [ + '{DAV:}contentlength' => null, + ], + 'status' => 404, ], - 'status' => 200, ], '/folder1/file1.txt' => [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File1', - '{DAV:}contentlength' => 12, + [ + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File1', + '{DAV:}contentlength' => 12, + ], + 'status' => 200, ], - 'status' => 200, ], '/folder1/file2.txt' => [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File2', - '{DAV:}contentlength' => 27, + [ + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File2', + '{DAV:}contentlength' => 27, + ], + 'status' => 403, ], - 'status' => 403, ], '/folder1/file3.txt' => [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File3', - '{DAV:}contentlength' => 42, + [ + 'properties' => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File3', + '{DAV:}contentlength' => 42, + ], + 'status' => 425, ], - 'status' => 425, ], '/folder1/subfolder' => [ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'SubFolder', + [ + 'properties' => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'SubFolder', + ], + 'status' => 200, + ], + [ + 'properties' => [ + '{DAV:}contentlength' => null, + ], + 'status' => 404, ], - 'status' => 200, ], ], $result); From 1e58ddeb7145e4c39933df2f92cf9b50efc7bdbc Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Mon, 27 Nov 2023 10:05:38 +0545 Subject: [PATCH 7/9] feat: return PROPFIND properties indexed by status --- lib/DAV/Client.php | 14 ++---- tests/Sabre/DAV/ClientTest.php | 81 ++++++++++++---------------------- 2 files changed, 30 insertions(+), 65 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index 03b98b11d7..e600b55f48 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -242,19 +242,11 @@ public function propFindUnfiltered($url, array $properties, $depth = 0) // If depth was 0, we only return the top item if (0 === $depth) { reset($result); - $statusList = current($result); - foreach ($statusList as $statusCode => $associatedProperties) { - $newResult[] = ['properties' => $associatedProperties, 'status' => $statusCode]; - } + + return current($result); } else { - foreach ($result as $href => $statusList) { - foreach ($statusList as $statusCode => $associatedProperties) { - $newResult[$href][] = ['properties' => $associatedProperties, 'status' => $statusCode]; - } - } + return $result; } - - return $newResult; } /** diff --git a/tests/Sabre/DAV/ClientTest.php b/tests/Sabre/DAV/ClientTest.php index 9353b691b7..e19ec07b44 100644 --- a/tests/Sabre/DAV/ClientTest.php +++ b/tests/Sabre/DAV/ClientTest.php @@ -310,18 +310,12 @@ public function testPropFindUnfilteredDepth0() $result = $client->propFindUnfiltered('folder1', ['{DAV:}resourcetype', '{DAV:}displayname', '{DAV:}contentlength', '{urn:zim}gir']); self::assertEquals([ - [ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', - ], - 'status' => 200, + 200 => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', ], - [ - 'properties' => [ - '{DAV:}contentlength' => null, - ], - 'status' => 404, + 404 => [ + '{DAV:}contentlength' => null, ], ], $result); @@ -424,63 +418,42 @@ public function testPropFindUnfiltered() self::assertEquals([ '/folder1' => [ - [ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'Folder1', - ], - 'status' => 200, + 200 => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'Folder1', ], - [ - 'properties' => [ - '{DAV:}contentlength' => null, - ], - 'status' => 404, + 404 => [ + '{DAV:}contentlength' => null, ], ], '/folder1/file1.txt' => [ - [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File1', - '{DAV:}contentlength' => 12, - ], - 'status' => 200, + 200 => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File1', + '{DAV:}contentlength' => 12, ], ], '/folder1/file2.txt' => [ - [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File2', - '{DAV:}contentlength' => 27, - ], - 'status' => 403, + 403 => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File2', + '{DAV:}contentlength' => 27, ], ], '/folder1/file3.txt' => [ - [ - 'properties' => [ - '{DAV:}resourcetype' => null, - '{DAV:}displayname' => 'File3', - '{DAV:}contentlength' => 42, - ], - 'status' => 425, + 425 => [ + '{DAV:}resourcetype' => null, + '{DAV:}displayname' => 'File3', + '{DAV:}contentlength' => 42, ], ], '/folder1/subfolder' => [ - [ - 'properties' => [ - '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), - '{DAV:}displayname' => 'SubFolder', - ], - 'status' => 200, + 200 => [ + '{DAV:}resourcetype' => new Xml\Property\ResourceType('{DAV:}collection'), + '{DAV:}displayname' => 'SubFolder', ], - [ - 'properties' => [ - '{DAV:}contentlength' => null, - ], - 'status' => 404, + 404 => [ + '{DAV:}contentlength' => null, ], ], ], $result); From add4a148c7f4a5f005170fdefc55725c80f4250a Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Mon, 11 Dec 2023 12:38:34 +0545 Subject: [PATCH 8/9] feat: tidy up client PROPFIND code and comments --- lib/DAV/Client.php | 48 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index e600b55f48..60d2679b14 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -179,16 +179,18 @@ public function __construct(array $settings) * The list of requested properties must be specified as an array, in clark * notation. * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. Only properties - * that are actually returned from the server (without error) will be - * returned, anything else is discarded. - * * Depth should be either 0 or 1. A depth of 1 will cause a request to be * made to the server to also return all child resources. * + * For depth 0, just the array of properties for the resource is returned. + * + * For depth 1, the returned array will contain a list of resource names as keys, + * and an array of properties as values. + * + * The array of properties will contain the properties as keys with their values as the value. + * Only properties that are actually returned from the server without error will be + * returned, anything else is discarded. + * * @param string $url * @param int $depth * @@ -220,15 +222,19 @@ public function propFind($url, array $properties, $depth = 0) * The list of requested properties must be specified as an array, in clark * notation. * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. All properties - * that are actually returned from the server are returned by this method. - * * Depth should be either 0 or 1. A depth of 1 will cause a request to be * made to the server to also return all child resources. * + * For depth 0, just the multi-level array of status and properties for the resource is returned. + * + * For depth 1, the returned array will contain a list of resources as keys and + * a multi-level array containing status and properties as value. + * + * The multi-level array of status and properties is formatted the same as what is + * documented for parseMultiStatus. + * + * All properties that are actually returned from the server are returned by this method. + * * @param string $url * @param int $depth * @@ -237,7 +243,6 @@ public function propFind($url, array $properties, $depth = 0) public function propFindUnfiltered($url, array $properties, $depth = 0) { $result = $this->doPropFind($url, $properties, $depth); - $newResult = []; // If depth was 0, we only return the top item if (0 === $depth) { @@ -255,14 +260,15 @@ public function propFindUnfiltered($url, array $properties, $depth = 0) * The list of requested properties must be specified as an array, in clark * notation. * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. - * * Depth should be either 0 or 1. A depth of 1 will cause a request to be * made to the server to also return all child resources. * + * The returned array will contain a list of resources as keys and + * a multi-level array containing status and properties as value. + * + * The multi-level array of status and properties is formatted the same as what is + * documented for parseMultiStatus. + * * @param string $url * @param int $depth * @@ -306,9 +312,7 @@ private function doPropFind($url, array $properties, $depth = 0) throw new HTTP\ClientHttpException($response); } - $result = $this->parseMultiStatus($response->getBodyAsString()); - - return $result; + return $this->parseMultiStatus($response->getBodyAsString()); } /** From c08f202a4d7851973db93b31755a9433826aa378 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Mon, 11 Dec 2023 14:39:01 +0545 Subject: [PATCH 9/9] feat: adjust client PROPFIND methods to add types --- lib/DAV/Client.php | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/DAV/Client.php b/lib/DAV/Client.php index 60d2679b14..187b29ba37 100644 --- a/lib/DAV/Client.php +++ b/lib/DAV/Client.php @@ -174,7 +174,7 @@ public function __construct(array $settings) } /** - * Does a PROPFIND request with filtered response. + * Does a PROPFIND request with filtered response returning only available properties. * * The list of requested properties must be specified as an array, in clark * notation. @@ -191,12 +191,9 @@ public function __construct(array $settings) * Only properties that are actually returned from the server without error will be * returned, anything else is discarded. * - * @param string $url - * @param int $depth - * - * @return array + * @param 1|0 $depth */ - public function propFind($url, array $properties, $depth = 0) + public function propFind(string $url, array $properties, int $depth = 0): array { $result = $this->doPropFind($url, $properties, $depth); @@ -235,12 +232,9 @@ public function propFind($url, array $properties, $depth = 0) * * All properties that are actually returned from the server are returned by this method. * - * @param string $url - * @param int $depth - * - * @return array + * @param 1|0 $depth */ - public function propFindUnfiltered($url, array $properties, $depth = 0) + public function propFindUnfiltered(string $url, array $properties, int $depth = 0): array { $result = $this->doPropFind($url, $properties, $depth); @@ -269,12 +263,9 @@ public function propFindUnfiltered($url, array $properties, $depth = 0) * The multi-level array of status and properties is formatted the same as what is * documented for parseMultiStatus. * - * @param string $url - * @param int $depth - * - * @return array + * @param 1|0 $depth */ - private function doPropFind($url, array $properties, $depth = 0) + private function doPropFind(string $url, array $properties, int $depth = 0): array { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true;