Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Compatible File Names #13220

Merged
merged 65 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f2f28a0
solve git conflict
alperozturk96 Aug 19, 2024
90c495f
Use New Validator inside CreateFolderDialogFragment
alperozturk96 Jul 2, 2024
52d258a
Use New Validator inside RenameFileDialogFragment
alperozturk96 Jul 2, 2024
47a4ac4
Use New Validator inside ChooseTemplateDialogFragment
alperozturk96 Jul 2, 2024
5ae25ec
Invalid characters added into error message
alperozturk96 Jul 2, 2024
b5e7946
Remove duplicated logics
alperozturk96 Jul 2, 2024
d77af05
Simplify logic for ChooseTemplateDialogFragment
alperozturk96 Jul 2, 2024
1ba56bd
Simplify
alperozturk96 Jul 2, 2024
f547e50
Add FileNameValidatorTests
alperozturk96 Jul 2, 2024
d48b043
Fix code analytics
alperozturk96 Jul 2, 2024
5d36d76
Add New OCCapabilities
alperozturk96 Jul 3, 2024
5800a45
Add forbiddenFilenameCharacterList values coming from server
alperozturk96 Jul 3, 2024
366a384
Add new capability to the local DB
alperozturk96 Jul 3, 2024
94b6e91
Extract checkInvalidCharacters
alperozturk96 Jul 3, 2024
7e85d80
Simplify checkInvalidCharacters
alperozturk96 Jul 3, 2024
b6a732c
Add checks for move or copy
alperozturk96 Jul 3, 2024
1aedf27
local lib support
alperozturk96 Jul 4, 2024
34055bb
Rename .java to .kt
alperozturk96 Jul 4, 2024
5c6439f
Convert to Kotlin
alperozturk96 Jul 4, 2024
199cd2b
revert
alperozturk96 Jul 4, 2024
15f9fcc
Check file name before add new file
alperozturk96 Jul 10, 2024
c742549
Fix folderPaths check
alperozturk96 Jul 10, 2024
59683a2
Refactor current logic
alperozturk96 Jul 10, 2024
63e7c29
add documentation
alperozturk96 Jul 10, 2024
27774fc
Check file names for upload content from other apps
alperozturk96 Jul 10, 2024
aa67830
Add todos
alperozturk96 Jul 11, 2024
eeb7110
Add dot and space strings globally
alperozturk96 Jul 17, 2024
ab4b63f
Check isFileNameAlreadyExist when existedFileNames available
alperozturk96 Jul 17, 2024
03281fa
Check folder creation from other file manager apps
alperozturk96 Jul 17, 2024
ec3e9cf
Check folder creation from other file manager apps
alperozturk96 Jul 17, 2024
0a802e8
Check template creation
alperozturk96 Jul 17, 2024
a5552a6
Add checks for copy, move, rename from file manager
alperozturk96 Jul 17, 2024
b373e24
Remove unnecessary assingment
alperozturk96 Jul 17, 2024
53650b9
Add forbidden file name extensions
alperozturk96 Jul 17, 2024
436f0de
Add test for forbidden file name extension
alperozturk96 Jul 17, 2024
15bf9d4
Use backend response for validation
alperozturk96 Jul 17, 2024
d22beb6
Fix tests
alperozturk96 Jul 18, 2024
577ee33
Add base names
alperozturk96 Jul 19, 2024
d472a92
Update tests
alperozturk96 Jul 19, 2024
c52ee55
Fix code analytics
alperozturk96 Jul 19, 2024
a845acf
Lowercase to backend response
alperozturk96 Jul 19, 2024
f0cd067
Update new names
alperozturk96 Jul 26, 2024
e197d87
Remove unnecessary check for move or copy
alperozturk96 Jul 26, 2024
1725538
Fix test
alperozturk96 Jul 26, 2024
3831343
Only show alert dialog for sharing invalid file
alperozturk96 Aug 7, 2024
cf607f4
Add folder path check for choose auto upload directory and move or co…
alperozturk96 Aug 7, 2024
cbf9d86
Check file name before move or copy
alperozturk96 Aug 7, 2024
bcfde98
Browse to root if file in incorrect folder path
alperozturk96 Aug 7, 2024
72fc1d0
During folder creating wont allow to user to press create button when…
alperozturk96 Aug 7, 2024
50f7639
Check url emptiness
alperozturk96 Aug 7, 2024
bcd9941
Highlight selected template
alperozturk96 Aug 7, 2024
6ac2207
Fix click effect
alperozturk96 Aug 7, 2024
edb9dee
Show specific one invalid file name before move or copy if file name …
alperozturk96 Aug 7, 2024
ff32c0a
Add check to folderOnItemClick
alperozturk96 Aug 8, 2024
3889dab
Add check to share
alperozturk96 Aug 8, 2024
cfa5e96
Update error texts
alperozturk96 Aug 19, 2024
a10413d
use library commit
tobiasKaminsky Aug 23, 2024
49b23d2
Analysis: update lint results to reflect reduced error/warning count
invalid-email-address Aug 23, 2024
3e0bb40
fix test
tobiasKaminsky Aug 23, 2024
453dbe3
empty line is needed
tobiasKaminsky Aug 23, 2024
e3069b7
Update library
alperozturk96 Aug 23, 2024
9cc13cc
Suppress codacy checks
alperozturk96 Aug 23, 2024
007f28b
better check
tobiasKaminsky Aug 23, 2024
f34a876
Update screenshots
alperozturk96 Aug 23, 2024
425288e
Fix license header
alperozturk96 Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,233 changes: 1,233 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json

Large diffs are not rendered by default.

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 app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt
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)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass
return RemoteOperationResult<Void>(RemoteOperationResult.ResultCode.OK)
}

@Suppress("LongMethod")
override fun getTaskList(appId: String): RemoteOperationResult<TaskList> {
val taskList = if (giveEmptyTasks) {
TaskList(listOf())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 77, to = 78),
AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 79, to = 80),
AutoMigration(from = 80, to = 81)
AutoMigration(from = 80, to = 81),
AutoMigration(from = 81, to = 82)
],
exportSchema = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,13 @@ data class CapabilityEntity(
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)
val dropAccount: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_SECURITY_GUARD)
val securityGuard: Int?
val securityGuard: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS)
val forbiddenFileNameCharacters: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES)
val forbiddenFileNames: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS)
val forbiddenFileNameExtensions: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES)
val forbiddenFilenameBaseNames: Int?
)
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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,18 @@ fun String.getRandomString(length: Int): String {

return this + result
}

fun String.removeFileExtension(): String {
val dotIndex = lastIndexOf('.')
return if (dotIndex != -1) {
substring(0, dotIndex)
} else {
this
}
}

object StringConstants {
const val SLASH = "/"
const val DOT = "."
const val SPACE = " "
}
Loading
Loading