-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13220 from nextcloud/feature/compatible-file-names
Feature Compatible File Names
- Loading branch information
Showing
37 changed files
with
2,532 additions
and
620 deletions.
There are no files selected for viewing
1,233 changes: 1,233 additions & 0 deletions
1,233
app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file modified
BIN
-1.71 KB
(78%)
...y/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
193 changes: 193 additions & 0 deletions
193
app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/* | ||
* Nextcloud - Android Client | ||
* | ||
* SPDX-FileCopyrightText: 2024 Alper Ozturk <[email protected]> | ||
* SPDX-License-Identifier: AGPL-3.0-or-later | ||
*/ | ||
|
||
package com.nextcloud.utils | ||
|
||
import com.nextcloud.utils.fileNameValidator.FileNameValidator | ||
import com.owncloud.android.AbstractIT | ||
import com.owncloud.android.R | ||
import com.owncloud.android.lib.resources.status.OCCapability | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Assert.assertFalse | ||
import org.junit.Assert.assertNull | ||
import org.junit.Assert.assertTrue | ||
import org.junit.Before | ||
import org.junit.Test | ||
|
||
@Suppress("TooManyFunctions") | ||
class FileNameValidatorTests : AbstractIT() { | ||
|
||
private var capability: OCCapability = fileDataStorageManager.getCapability(account.name) | ||
|
||
@Before | ||
fun setup() { | ||
capability = capability.apply { | ||
forbiddenFilenamesJson = """[".htaccess",".htaccess"]""" | ||
forbiddenFilenameBaseNamesJson = """ | ||
["con", "prn", "aux", "nul", "com0", "com1", "com2", "com3", "com4", | ||
"com5", "com6", "com7", "com8", "com9", "com¹", "com²", "com³", | ||
"lpt0", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", | ||
"lpt8", "lpt9", "lpt¹", "lpt²", "lpt³"] | ||
""" | ||
forbiddenFilenameExtensionJson = """[".filepart",".part"]""" | ||
forbiddenFilenameCharactersJson = """["<", ">", ":", "\\\\", "/", "|", "?", "*", "&"]""" | ||
} | ||
} | ||
|
||
@Test | ||
fun testInvalidCharacter() { | ||
val result = FileNameValidator.checkFileName("file<name", capability, targetContext) | ||
assertEquals( | ||
String.format(targetContext.getString(R.string.file_name_validator_error_invalid_character), "<"), | ||
result | ||
) | ||
} | ||
|
||
@Test | ||
fun testReservedName() { | ||
val result = FileNameValidator.checkFileName("CON", capability, targetContext) | ||
assertEquals(targetContext.getString(R.string.file_name_validator_error_reserved_names, "CON"), result) | ||
} | ||
|
||
@Test | ||
fun testForbiddenFilenameExtension() { | ||
val result = FileNameValidator.checkFileName("my_fav_file.filepart", capability, targetContext) | ||
assertEquals( | ||
targetContext.getString(R.string.file_name_validator_error_forbidden_file_extensions, "filepart"), | ||
result | ||
) | ||
} | ||
|
||
@Test | ||
fun testEndsWithSpaceOrPeriod() { | ||
val result = FileNameValidator.checkFileName("filename ", capability, targetContext) | ||
assertEquals(targetContext.getString(R.string.file_name_validator_error_ends_with_space_period), result) | ||
|
||
val result2 = FileNameValidator.checkFileName("filename.", capability, targetContext) | ||
assertEquals(targetContext.getString(R.string.file_name_validator_error_ends_with_space_period), result2) | ||
} | ||
|
||
@Test | ||
fun testEmptyFileName() { | ||
val result = FileNameValidator.checkFileName("", capability, targetContext) | ||
assertEquals(targetContext.getString(R.string.filename_empty), result) | ||
} | ||
|
||
@Test | ||
fun testFileAlreadyExists() { | ||
val existingFiles = mutableSetOf("existingFile") | ||
val result = FileNameValidator.checkFileName("existingFile", capability, targetContext, existingFiles) | ||
assertEquals(targetContext.getString(R.string.file_already_exists), result) | ||
} | ||
|
||
@Test | ||
fun testValidFileName() { | ||
val result = FileNameValidator.checkFileName("validFileName", capability, targetContext) | ||
assertNull(result) | ||
} | ||
|
||
@Test | ||
fun testIsFileHidden() { | ||
assertTrue(FileNameValidator.isFileHidden(".hiddenFile")) | ||
assertFalse(FileNameValidator.isFileHidden("visibleFile")) | ||
} | ||
|
||
@Test | ||
fun testIsFileNameAlreadyExist() { | ||
val existingFiles = mutableSetOf("existingFile") | ||
assertTrue(FileNameValidator.isFileNameAlreadyExist("existingFile", existingFiles)) | ||
assertFalse(FileNameValidator.isFileNameAlreadyExist("newFile", existingFiles)) | ||
} | ||
|
||
@Test | ||
fun testValidFolderAndFilePaths() { | ||
val folderPath = "validFolder" | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertTrue(result) | ||
} | ||
|
||
@Test | ||
fun testFolderPathWithReservedName() { | ||
val folderPath = "CON" | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFilePathWithReservedName() { | ||
val folderPath = "validFolder" | ||
val filePaths = listOf("file1.txt", "PRN.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFolderPathWithInvalidCharacter() { | ||
val folderPath = "invalid<Folder" | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFilePathWithInvalidCharacter() { | ||
val folderPath = "validFolder" | ||
val filePaths = listOf("file1.txt", "file|2.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFolderPathEndingWithSpace() { | ||
val folderPath = "folderWithSpace " | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFilePathEndingWithPeriod() { | ||
val folderPath = "validFolder" | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testFilePathWithNestedFolder() { | ||
val folderPath = "validFolder\\secondValidFolder\\CON" | ||
val filePaths = listOf("file1.txt", "file2.doc", "file3.") | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext) | ||
assertFalse(result) | ||
} | ||
|
||
@Test | ||
fun testOnlyFolderPath() { | ||
val folderPath = "/A1/Aaaww/W/C2/" | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, listOf(), capability, targetContext) | ||
assertTrue(result) | ||
} | ||
|
||
@Test | ||
fun testOnlyFolderPathWithOneReservedName() { | ||
val folderPath = "/A1/Aaaww/CON/W/C2/" | ||
|
||
val result = FileNameValidator.checkFolderAndFilePaths(folderPath, listOf(), capability, targetContext) | ||
assertFalse(result) | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
app/src/androidTest/java/com/owncloud/android/utils/EspressoIdlingResource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
app/src/main/java/com/nextcloud/utils/extensions/OCCapabilityExtensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Nextcloud - Android Client | ||
* | ||
* SPDX-FileCopyrightText: 2024 Alper Ozturk <[email protected]> | ||
* SPDX-License-Identifier: AGPL-3.0-or-later | ||
*/ | ||
|
||
package com.nextcloud.utils.extensions | ||
|
||
import com.google.gson.Gson | ||
import com.owncloud.android.lib.resources.status.OCCapability | ||
import org.json.JSONException | ||
|
||
private val gson = Gson() | ||
|
||
fun OCCapability.forbiddenFilenames(): List<String> = jsonToList(forbiddenFilenamesJson) | ||
|
||
fun OCCapability.forbiddenFilenameCharacters(): List<String> = jsonToList(forbiddenFilenameCharactersJson) | ||
|
||
fun OCCapability.forbiddenFilenameExtension(): List<String> = jsonToList(forbiddenFilenameExtensionJson) | ||
|
||
fun OCCapability.forbiddenFilenameBaseNames(): List<String> = jsonToList(forbiddenFilenameBaseNamesJson) | ||
|
||
@Suppress("ReturnCount") | ||
private fun jsonToList(json: String?): List<String> { | ||
if (json == null) return emptyList() | ||
|
||
return try { | ||
return gson.fromJson(json, Array<String>::class.java).toList() | ||
} catch (e: JSONException) { | ||
emptyList() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.