Skip to content

Commit 0be5ba8

Browse files
alperozturk96backportbot[bot]
authored andcommitted
webDavParentPath
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent 3f72ddf commit 0be5ba8

3 files changed

Lines changed: 115 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.utils
9+
10+
import com.nextcloud.utils.extensions.webDavParentPath
11+
import org.junit.Assert.assertEquals
12+
import org.junit.Test
13+
14+
class WebDavParentPathTests {
15+
16+
// ── Happy path ────────────────────────────────────────────────────────────
17+
18+
@Test
19+
fun testWebDavParentPathWhenGivenCorrectParentShouldReturnOneLevelAbove() {
20+
assertEquals("/Photos/Vacation/", "/Photos/Vacation/beach.jpg".webDavParentPath())
21+
assertEquals("/work/docs/", "/work/docs/notes.txt".webDavParentPath())
22+
}
23+
24+
@Test
25+
fun testWebDavParentPathWhenGivenDeepNestingShouldReturnDirectParent() {
26+
assertEquals("/a/b/c/d/", "/a/b/c/d/e.txt".webDavParentPath())
27+
}
28+
29+
// ── Root cases ────────────────────────────────────────────────────────────
30+
31+
@Test
32+
fun testWebDavParentPathWhenGivenRootFileShouldReturnRoot() {
33+
assertEquals("/", "/image.png".webDavParentPath())
34+
}
35+
36+
@Test
37+
fun testWebDavParentPathWhenGivenSlashShouldReturnRoot() {
38+
assertEquals("/", "/".webDavParentPath())
39+
}
40+
41+
@Test
42+
fun testWebDavParentPathWhenGivenEmptyStringShouldReturnRoot() {
43+
assertEquals("/", "".webDavParentPath())
44+
}
45+
46+
@Test
47+
fun testWebDavParentPathWhenGivenOnlySlashesShouldReturnRoot() {
48+
assertEquals("/", "///".webDavParentPath())
49+
}
50+
51+
// ── Relative paths ────────────────────────────────────────────────────────
52+
53+
@Test
54+
fun testWebDavParentPathWhenGivenRelativePathShouldReturnOneLevelAbove() {
55+
assertEquals("Documents/", "Documents/file.pdf".webDavParentPath())
56+
}
57+
58+
@Test
59+
fun testWebDavParentPathWhenGivenSingleWordPathShouldReturnRoot() {
60+
assertEquals("/", "readme.md".webDavParentPath())
61+
}
62+
63+
// ── Trailing slashes ──────────────────────────────────────────────────────
64+
65+
@Test
66+
fun testWebDavParentPathWhenGivenTrailingSlashShouldReturnOneLevelAbove() {
67+
assertEquals("/Photos/", "/Photos/Vacation/".webDavParentPath())
68+
}
69+
70+
@Test
71+
fun testWebDavParentPathWhenGivenMultipleTrailingSlashesShouldReturnOneLevelAbove() {
72+
assertEquals("/Photos/", "/Photos/Vacation///".webDavParentPath())
73+
}
74+
75+
// ── Encoded characters (WebDAV percent-encoding must be preserved) ────────
76+
77+
@Test
78+
fun testWebDavParentPathWhenGivenEncodedSpacesShouldPreserveEncoding() {
79+
assertEquals("/My%20Photos/", "/My%20Photos/beach%20photo.jpg".webDavParentPath())
80+
}
81+
82+
@Test
83+
fun testWebDavParentPathWhenGivenEncodedSpecialCharsShouldPreserveEncoding() {
84+
assertEquals("/files/%23reports/", "/files/%23reports/q1%262.pdf".webDavParentPath())
85+
}
86+
87+
// ── Unicode ───────────────────────────────────────────────────────────────
88+
89+
@Test
90+
fun testWebDavParentPathWhenGivenUnicodeCharsShouldReturnOneLevelAbove() {
91+
assertEquals("/照片/假期/", "/照片/假期/海滩.jpg".webDavParentPath())
92+
}
93+
94+
// ── Single character ──────────────────────────────────────────────────────
95+
96+
@Test
97+
fun testWebDavParentPathWhenGivenSingleCharFileAtRootShouldReturnRoot() {
98+
assertEquals("/", "/a".webDavParentPath())
99+
}
100+
101+
@Test
102+
fun testWebDavParentPathWhenGivenSingleCharDirShouldReturnOneLevelAbove() {
103+
assertEquals("/a/", "/a/b".webDavParentPath())
104+
}
105+
}

app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ fun String.removeFileExtension(): String {
2525
}
2626
}
2727

28+
fun String.webDavParentPath(): String {
29+
val normalized = this.trimEnd('/')
30+
if (normalized.isEmpty()) return "/"
31+
val parent = normalized.substringBeforeLast('/', "")
32+
return if (parent.isEmpty()) "/" else "$parent/"
33+
}
34+
2835
@Suppress("ComplexCondition")
2936
fun String?.eTagChanged(eTagOnServer: String?): Boolean {
3037
if (this == null || this.isEmpty() || eTagOnServer == null || eTagOnServer.isEmpty()) {

app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.nextcloud.client.device.PowerManagementService
2727
import com.nextcloud.client.jobs.upload.FileUploadEventBroadcaster
2828
import com.nextcloud.client.jobs.upload.FileUploadHelper
2929
import com.nextcloud.client.utils.Throttler
30+
import com.nextcloud.utils.extensions.webDavParentPath
3031
import com.owncloud.android.R
3132
import com.owncloud.android.databinding.UploadListLayoutBinding
3233
import com.owncloud.android.datamodel.OCFile
@@ -50,6 +51,7 @@ import com.owncloud.android.utils.FilesSyncHelper
5051
import kotlinx.coroutines.Dispatchers
5152
import kotlinx.coroutines.launch
5253
import kotlinx.coroutines.withContext
54+
import org.apache.commons.io.FilenameUtils
5355
import javax.inject.Inject
5456

5557
@Suppress("MagicNumber")
@@ -322,6 +324,7 @@ class UploadListActivity :
322324
val parentPath = storageManager
323325
.getFileByPath(upload.remotePath)
324326
.parentRemotePath
327+
?: upload.remotePath.webDavParentPath()
325328

326329
val checkOp = ExistenceCheckRemoteOperation(parentPath, false)
327330
val checkResult = checkOp.execute(client)

0 commit comments

Comments
 (0)