From f2dc414c98fbcdb664f9e716e3e98e5c2eac9ae7 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Wed, 17 Jul 2024 18:31:53 +0200 Subject: [PATCH 01/16] WIP Signed-off-by: Milen Pivchev --- iOSClient/Data/NCManageDatabase+Capabilities.swift | 13 +++++++++++++ iOSClient/NCGlobal.swift | 5 +++++ .../Supporting Files/en.lproj/Localizable.strings | 8 ++++++++ 3 files changed, 26 insertions(+) diff --git a/iOSClient/Data/NCManageDatabase+Capabilities.swift b/iOSClient/Data/NCManageDatabase+Capabilities.swift index 73820d709a..4a90ae97ef 100644 --- a/iOSClient/Data/NCManageDatabase+Capabilities.swift +++ b/iOSClient/Data/NCManageDatabase+Capabilities.swift @@ -229,11 +229,19 @@ extension NCManageDatabase { let bigfilechunking: Bool? let versiondeletion: Bool? let versionlabeling: Bool? + let forbiddenFileNames: [String]? + let forbiddenFileNameBasenames: [String]? + let forbiddenFileNameCharacters: [String]? + let forbiddenFileNameExtensions: [String]? enum CodingKeys: String, CodingKey { case undelete, locking, comments, versioning, directEditing, bigfilechunking case versiondeletion = "version_deletion" case versionlabeling = "version_labeling" + case forbiddenFileNames = "forbidden_filenames" + case forbiddenFileNameBasenames = "forbidden_filename_basenames" + case forbiddenFileNameCharacters = "forbidden_filename_characters" + case forbiddenFileNameExtensions = "forbidden_filename_extensions" } struct DirectEditing: Codable { @@ -360,6 +368,11 @@ extension NCManageDatabase { } global.capabilityGroupfoldersEnabled = data.capabilities.groupfolders?.hasGroupFolders ?? false global.capabilitySecurityGuardDiagnostics = data.capabilities.securityguard?.diagnostics ?? false + + global.capabilityForbiddenFileNames = data.capabilities.files?.forbiddenFileNames + global.capabilityForbiddenFileNameBasenames = data.capabilities.files?.forbiddenFileNameBasenames + global.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters + global.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions } catch let error as NSError { NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)") return diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index c9e20b7641..e8d454c261 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -448,6 +448,11 @@ class NCGlobal: NSObject { var capabilitySecurityGuardDiagnostics = false + var capabilityForbiddenFileNames: [String] = [] + var capabilityForbiddenFileNameBasenames: [String] = [] + var capabilityForbiddenFileNameCharacters: [String] = [] + var capabilityForbiddenFileNameExtensions: [String] = [] + // MORE NEXTCLOUD APPS let talkSchemeUrl = "nextcloudtalk://" let notesSchemeUrl = "nextcloudnotes://" diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index a8813443af..ba0e68ea8f 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1088,3 +1088,11 @@ // MARK: Login poll "_poll_desc_" = "Please complete the log in process in your browser"; + +// MARK: File name validator +"_filename_empty_" = "File name is empty"; +"_file_name_validator_upload_content_error_" = "Some files cannot be uploaded due to reserved names or invalid characters"; +"_file_name_validator_error_contains_reserved_names_or_invalid_characters_" = "Folder path contains reserved names or invalid characters"; +"_file_name_validator_error_invalid_character_" = "File name contains invalid characters: %s"; +"_file_name_validator_error_reserved_names_" = "%s is a reserved name"; +"_file_name_validator_error_ends_with_space_period_" = "File name ends with a space or a period"; From 6c69bd132569173469418ac9599d1de5526df1a1 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 19 Jul 2024 14:41:00 +0200 Subject: [PATCH 02/16] WIP Signed-off-by: Milen Pivchev --- Nextcloud.xcodeproj/project.pbxproj | 4 +-- iOSClient/AppDelegate.swift | 9 ++++- .../Data/NCManageDatabase+Capabilities.swift | 8 ++--- .../UIAlertController+Extension.swift | 17 ++++++++++ .../Menu/NCCollectionViewCommon+Menu.swift | 8 ++++- iOSClient/NCGlobal.swift | 1 + iOSClient/Rename file/NCRenameFile.storyboard | 34 ++++++++----------- iOSClient/Rename file/NCRenameFile.swift | 8 +++++ iOSClient/Utility/NCPopupViewController.swift | 2 +- 9 files changed, 63 insertions(+), 28 deletions(-) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index d3e84b2eaa..cceea4be04 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -5734,8 +5734,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nextcloud/NextcloudKit"; requirement = { - kind = exactVersion; - version = 4.0.2; + branch = "filename-validator"; + kind = branch; }; }; F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 54e7ed0a3d..7962559848 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -92,7 +92,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD utilityFileSystem.removeFile(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NextcloudKit.shared.nkCommonInstance.filenameLog) } else { - levelLog = NCKeychain().logLevel NextcloudKit.shared.nkCommonInstance.levelLog = levelLog NextcloudKit.shared.nkCommonInstance.copyLogToDocumentDirectory = true @@ -152,6 +151,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD self.handleProcessingTask(task) } + FileNameValidator.shared.setup( + forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames, + forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames, + forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters, + forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions + ) +// FileNameValidator.fileAlreadyExistsError = NCGlobal.filealr + return true } diff --git a/iOSClient/Data/NCManageDatabase+Capabilities.swift b/iOSClient/Data/NCManageDatabase+Capabilities.swift index 4a90ae97ef..1a67389ee5 100644 --- a/iOSClient/Data/NCManageDatabase+Capabilities.swift +++ b/iOSClient/Data/NCManageDatabase+Capabilities.swift @@ -369,10 +369,10 @@ extension NCManageDatabase { global.capabilityGroupfoldersEnabled = data.capabilities.groupfolders?.hasGroupFolders ?? false global.capabilitySecurityGuardDiagnostics = data.capabilities.securityguard?.diagnostics ?? false - global.capabilityForbiddenFileNames = data.capabilities.files?.forbiddenFileNames - global.capabilityForbiddenFileNameBasenames = data.capabilities.files?.forbiddenFileNameBasenames - global.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters - global.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions + global.capabilityForbiddenFileNames = data.capabilities.files?.forbiddenFileNames ?? [] + global.capabilityForbiddenFileNameBasenames = data.capabilities.files?.forbiddenFileNameBasenames ?? [] + global.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? [] + global.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? [] } catch let error as NSError { NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)") return diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 755c8f7ee9..11f09eee89 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -149,4 +149,21 @@ extension UIAlertController { }) return alertController } + +// static func renameFile(completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController { +// let alertController = UIAlertController( +// title: NSLocalizedString("_rename_", comment: ""), +// message: nil, +// preferredStyle: .alert) +// alertController.addTextField { textField in +// textField.autocapitalizationType = .words +// } +// alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { _ in +// completion(true) +// }) +// alertController.addAction(UIAlertAction(title: NSLocalizedString("_rename_", comment: ""), style: .default) { _ in +// completion(false) +// }) +// return alertController +// } } diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index 3b64e428ea..067462fd9c 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -281,6 +281,11 @@ extension NCCollectionViewCommon { icon: utility.loadImage(named: "text.cursor", colors: [NCBrandColor.shared.iconImageColor]), order: 120, action: { _ in +// let alert = UIAlertController.renameFile { cancelled in +// +// } +// +// self.present(alert, animated: true) if let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile { @@ -289,7 +294,8 @@ extension NCCollectionViewCommon { vcRename.indexPath = indexPath let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) - + popup.shadowEnabled = false + self.present(popup, animated: true) } } diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index e8d454c261..dc027c437a 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -466,6 +466,7 @@ class NCGlobal: NSObject { // FORBIDDEN CHARACTERS // + // TODO: Remove this let forbiddenCharacters = ["/", "\\", ":", "\"", "|", "?", "*", "<", ">"] // DIAGNOSTICS CLIENTS diff --git a/iOSClient/Rename file/NCRenameFile.storyboard b/iOSClient/Rename file/NCRenameFile.storyboard index a2ac6fa094..7e83dfd1e0 100644 --- a/iOSClient/Rename file/NCRenameFile.storyboard +++ b/iOSClient/Rename file/NCRenameFile.storyboard @@ -1,9 +1,9 @@ - + - + @@ -13,24 +13,24 @@ - + - + - - @@ -146,8 +145,5 @@ - - - diff --git a/iOSClient/Rename file/NCRenameFile.swift b/iOSClient/Rename file/NCRenameFile.swift index 66ec9959d9..d2b7ee73db 100644 --- a/iOSClient/Rename file/NCRenameFile.swift +++ b/iOSClient/Rename file/NCRenameFile.swift @@ -58,6 +58,14 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() +// let blur = UIBlurEffect(style: .systemThinMaterial) +// let blurredEffectView = UIVisualEffectView(effect: blur) +// blurredEffectView.frame = view.bounds +// blurredEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] +// blurredEffectView.isUserInteractionEnabled = false +// view.insertSubview(blurredEffectView, at: 1) + + view.insertBlur(style: .systemMaterial) if let metadata = self.metadata { diff --git a/iOSClient/Utility/NCPopupViewController.swift b/iOSClient/Utility/NCPopupViewController.swift index 78053bac0b..8a4fdf1e85 100644 --- a/iOSClient/Utility/NCPopupViewController.swift +++ b/iOSClient/Utility/NCPopupViewController.swift @@ -43,7 +43,7 @@ public class NCPopupViewController: UIViewController { private(set) public var popupHeight: CGFloat? // Background alpha, default is 0.3 - public var backgroundAlpha: CGFloat = 0.3 + public var backgroundAlpha: CGFloat = 0.2 // Background color, default is black public var backgroundColor = UIColor.black From 14624948ecb17950635ea934d30208f5fabfe7a9 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Wed, 24 Jul 2024 12:59:53 +0200 Subject: [PATCH 03/16] WIP Signed-off-by: Milen Pivchev --- Brand/NCBrand.swift | 2 +- Nextcloud.xcodeproj/project.pbxproj | 16 ++ .../Create folder/NCCreateFolder.storyboard | 115 ++++++++ iOSClient/Create folder/NCCreateFolder.swift | 245 ++++++++++++++++++ .../UIAlertController+Extension.swift | 63 +++++ .../Menu/NCCollectionViewCommon+Menu.swift | 64 ++++- iOSClient/Rename file/NCRenameFile.swift | 6 - 7 files changed, 503 insertions(+), 8 deletions(-) create mode 100644 iOSClient/Create folder/NCCreateFolder.storyboard create mode 100644 iOSClient/Create folder/NCCreateFolder.swift diff --git a/Brand/NCBrand.swift b/Brand/NCBrand.swift index 7277e85951..1ddf7959ec 100755 --- a/Brand/NCBrand.swift +++ b/Brand/NCBrand.swift @@ -69,7 +69,7 @@ let userAgent: String = { var disable_crash_service: Bool = false var disable_log: Bool = false var disable_mobileconfig: Bool = false - var disable_show_more_nextcloud_apps_in_settings: Bool = false + var disable_show_more_nextcloud_apps_in_settings: Bool = true var doNotAskPasscodeAtStartup: Bool = false // Internal option behaviour diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index cceea4be04..b791d9e668 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -154,6 +154,8 @@ F3A0479B2BD2668800658E7B /* NCAssistant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A047962BD2668800658E7B /* NCAssistant.swift */; }; F3A0479E2BD268B500658E7B /* PopupView in Frameworks */ = {isa = PBXBuildFile; productRef = F3A0479D2BD268B500658E7B /* PopupView */; }; F3A7AFC62A41AA82001FC89C /* BaseUIXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */; }; + F3AECBBD2C4A9E100067CA8C /* NCCreateFolder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */; }; + F3AECBBE2C4A9E100067CA8C /* NCCreateFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */; }; F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */; }; F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */; }; F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */; }; @@ -1140,6 +1142,8 @@ F3A047952BD2668800658E7B /* NCAssistantTaskDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAssistantTaskDetail.swift; sourceTree = ""; }; F3A047962BD2668800658E7B /* NCAssistant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAssistant.swift; sourceTree = ""; }; F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUIXCTestCase.swift; sourceTree = ""; }; + F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCCreateFolder.storyboard; sourceTree = ""; }; + F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCCreateFolder.swift; sourceTree = ""; }; F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMoreAppSuggestionsCell.xib; sourceTree = ""; }; F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreAppSuggestionsCell.swift; sourceTree = ""; }; F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCCellMore.swift; sourceTree = ""; }; @@ -1997,6 +2001,15 @@ path = "Task Detail"; sourceTree = ""; }; + F3AECBB62C4A9D550067CA8C /* Create folder */ = { + isa = PBXGroup; + children = ( + F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */, + F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */, + ); + path = "Create folder"; + sourceTree = ""; + }; F3BB46502A39EC2D00461F6E /* Cells */ = { isa = PBXGroup; children = ( @@ -2947,6 +2960,7 @@ F7381ED9218218A4000B1560 /* Offline */, F713418B2597513800768D21 /* PushNotification */, F765F72E25237E3F00391DBE /* Recent */, + F3AECBB62C4A9D550067CA8C /* Create folder */, F70D87CC25EE6E58008CBBBD /* Rename file */, F7CADB3D23CCDDA1000EEC78 /* RichWorkspace */, F76882042C0DD1E7001CF441 /* Settings */, @@ -3697,6 +3711,7 @@ F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */, F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */, F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */, + F3AECBBD2C4A9E100067CA8C /* NCCreateFolder.storyboard in Resources */, F74C0437253F1CDC009762AB /* NCShares.storyboard in Resources */, F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */, F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */, @@ -4377,6 +4392,7 @@ F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */, F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */, F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */, + F3AECBBE2C4A9E100067CA8C /* NCCreateFolder.swift in Sources */, AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */, F77BC3ED293E528A005F2B08 /* NCConfigServer.swift in Sources */, F7A560422AE1593700BE8FD6 /* NCOperationSaveLivePhoto.swift in Sources */, diff --git a/iOSClient/Create folder/NCCreateFolder.storyboard b/iOSClient/Create folder/NCCreateFolder.storyboard new file mode 100644 index 0000000000..9b93fc71f5 --- /dev/null +++ b/iOSClient/Create folder/NCCreateFolder.storyboard @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOSClient/Create folder/NCCreateFolder.swift b/iOSClient/Create folder/NCCreateFolder.swift new file mode 100644 index 0000000000..93810e66e2 --- /dev/null +++ b/iOSClient/Create folder/NCCreateFolder.swift @@ -0,0 +1,245 @@ +// +// NCRenameFile.swift +// Nextcloud +// +// Created by Marino Faggiana on 26/02/21. +// Copyright © 2021 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import UIKit +import NextcloudKit + +public protocol NCCreateFolderDelegate: AnyObject { + func rename(fileName: String, fileNameNew: String) +} + +// optional func +public extension NCCreateFolderDelegate { + func rename(fileName: String, fileNameNew: String) {} +} + +class NCCreateFolder: UIViewController, UITextFieldDelegate { + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var previewFile: UIImageView! + @IBOutlet weak var fileNameNoExtension: UITextField! + @IBOutlet weak var point: UILabel! + @IBOutlet weak var ext: UITextField! + @IBOutlet weak var fileNameNoExtensionTrailingContraint: NSLayoutConstraint! + @IBOutlet weak var cancelButton: UIButton! + @IBOutlet weak var renameButton: UIButton! + + let width: CGFloat = 300 + let height: CGFloat = 310 + + var metadata: tableMetadata? + var indexPath: IndexPath = IndexPath() + var fileName: String? + var imagePreview: UIImage? + var disableChangeExt: Bool = false + weak var delegate: NCCreateFolderDelegate? + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + if let metadata = self.metadata { + + if metadata.directory { + titleLabel.text = NSLocalizedString("_rename_folder_", comment: "") + } else { + titleLabel.text = NSLocalizedString("_rename_file_", comment: "") + } + + fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension + fileNameNoExtension.delegate = self + fileNameNoExtension.becomeFirstResponder() + + ext.text = metadata.fileExtension + ext.delegate = self + if disableChangeExt { + ext.isEnabled = false + ext.textColor = .lightGray + } + + previewFile.image = imagePreview + previewFile.layer.cornerRadius = 10 + previewFile.layer.masksToBounds = true + + if metadata.directory { + + if imagePreview == nil { + previewFile.image = NCImageCache.images.folder + } + + ext.isHidden = true + point.isHidden = true + fileNameNoExtensionTrailingContraint.constant = 20 + + } else { + + if imagePreview == nil { + previewFile.image = NCImageCache.images.file + } + + fileNameNoExtensionTrailingContraint.constant = 90 + } + + } else if let fileName = self.fileName { + + titleLabel.text = NSLocalizedString("_rename_file_", comment: "") + + fileNameNoExtension.text = (fileName as NSString).deletingPathExtension + fileNameNoExtension.delegate = self + fileNameNoExtension.becomeFirstResponder() + fileNameNoExtensionTrailingContraint.constant = 90 + + ext.text = (fileName as NSString).pathExtension + ext.delegate = self + + if imagePreview == nil { + previewFile.image = NCImageCache.images.file + } else { + previewFile.image = imagePreview + } + previewFile.layer.cornerRadius = 10 + previewFile.layer.masksToBounds = true + } + + cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) + cancelButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) + renameButton.setTitle(NSLocalizedString("_rename_", comment: ""), for: .normal) + renameButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if metadata == nil && fileName == nil { + dismiss(animated: true) + } + + fileNameNoExtension.selectAll(nil) + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + + textField.resignFirstResponder() + renameFile(textField) + return true + } + + // MARK: - Action + + @IBAction func cancel(_ sender: Any) { + + dismiss(animated: true) + } + + @IBAction func renameFile(_ sender: Any) { + + var fileNameNoExtensionNew = "" + var extNew = "" + var fileNameNew = "" + + if let metadata = self.metadata { + + let extCurrent = (metadata.fileNameView as NSString).pathExtension + + if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { + return self.fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension + } else { + fileNameNoExtensionNew = fileNameNoExtension.text! + } + + if metadata.directory { + fileNameNew = fileNameNoExtensionNew + renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) + + } else { + + if ext.text == nil || ext.text?.count == 0 { + return self.ext.text = metadata.fileExtension + } else { + extNew = ext.text! + } + + if extNew != extCurrent { + + let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent) + let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert) + + var title = NSLocalizedString("_use_", comment: "") + " ." + extNew + alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in + + fileNameNew = fileNameNoExtensionNew + "." + extNew + self.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: self.indexPath) + })) + + title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent + alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in + self.ext.text = metadata.fileExtension + })) + + self.present(alertController, animated: true) + + } else { + fileNameNew = fileNameNoExtensionNew + "." + extNew + renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) + } + } + + } else if let fileName = self.fileName { + + if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { + return fileNameNoExtension.text = (fileName as NSString).deletingPathExtension + } else if ext.text == nil || ext.text?.count == 0 { + return ext.text = (fileName as NSString).pathExtension + } + + fileNameNew = (fileNameNoExtension.text ?? "") + "." + (ext.text ?? "") + self.dismiss(animated: true) { + self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew) + } + } + } + + // MARK: - Networking + + func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, indexPath: IndexPath) { + + // verify if already exists + if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil { + NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_")) + return + } + + NCActivityIndicator.shared.start() + + NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath, viewController: self) { error in + + NCActivityIndicator.shared.stop() + + if error == .success { + self.dismiss(animated: true) + } else { + NCContentPresenter().showError(error: error) + } + } + } +} diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 11f09eee89..6c8b16c74e 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -68,6 +68,7 @@ extension UIAlertController { textField.autocapitalizationType = .words } + // only allow saving if folder name exists NotificationCenter.default.addObserver( forName: UITextField.textDidChangeNotification, @@ -75,7 +76,9 @@ extension UIAlertController { queue: .main) { _ in guard let text = alertController.textFields?.first?.text else { return } let folderName = text.trimmingCharacters(in: .whitespaces) + let checkFolderName = FileNameValidator.shared.fileAlreadyExistsError okAction.isEnabled = !folderName.isEmpty && folderName != "." && folderName != ".." +// alertController.message = folderName != "." && folderName != ".." ? "This is an error" : "" } alertController.addAction(cancelAction) @@ -166,4 +169,64 @@ extension UIAlertController { // }) // return alertController // } + + static func renameFile(oldName: String, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController { +// class Test: NSObject, UITextFieldDelegate { +// func textFieldDidBeginEditing(_ textField: UITextField) { +// textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument) +// textField.becomeFirstResponder() +// } +// } + + + let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) + + let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in + guard let fileNameFolder = alertController.textFields?.first?.text else { return } + }) + + // text field is initially empty, no action + okAction.isEnabled = false + let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) + + alertController.addTextField { textField in + textField.text = oldName + textField.autocapitalizationType = .words +// textField.becomeFirstResponder() +// if let start = textField.position(from: textField.beginningOfDocument, offset: 0), +// let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { +// textField.selectedTextRange = textField.textRange(from: start, to: end) +// } +// textField.selectAll(nil) + } + + // only allow saving if folder name exists + NotificationCenter.default.addObserver( + forName: UITextField.textDidBeginEditingNotification, + object: alertController.textFields?.first, + queue: .main) { _ in + guard let textField = alertController.textFields?.first else { return } + + if let start = textField.position(from: textField.beginningOfDocument, offset: 0), + let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { + textField.selectedTextRange = textField.textRange(from: start, to: end) + } +// textField.selectAll(nil) + } + + NotificationCenter.default.addObserver( + forName: UITextField.textDidChangeNotification, + object: alertController.textFields?.first, + queue: .main) { _ in + guard let text = alertController.textFields?.first?.text else { return } + + let textCheck = FileNameValidator.shared.checkFileName(text) + okAction.isEnabled = textCheck?.error == nil + alertController.message = textCheck?.error.localizedDescription + } + + alertController.addAction(cancelAction) + alertController.addAction(okAction) + return alertController + } } diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index 067462fd9c..f7ef295192 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -295,8 +295,42 @@ extension NCCollectionViewCommon { let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) popup.shadowEnabled = false + +// let alertController = UIAlertController(title: "Alert", message: nil, preferredStyle: .alert) - self.present(popup, animated: true) + + +// // Create an instance of your custom view controller +// let customViewController = CustomAlertViewController() +// customViewController.alertController = alertController +// +// // Add the custom view controller to the alert +// alertController.setValue(customViewController, forKey: "contentViewController") + +// alertController.addTextField { +// $0.placeholder = "Email" +// $0.addTarget(alertController, action: #selector(alertController.textDidChangeInLoginAlert), for: .editingChanged) +// } +// +// let updateAction = UIAlertAction(title: "Update", style: .default) { _ in +// // Code to update the alert controller +// alertController.title = "Updated Title" +// alertController.message = "Updated Message" +// +// // Adding a new action as an example +// let newAction = UIAlertAction(title: "New Action", style: .default, handler: nil) +// alertController.addAction(newAction) +// } +// alertController.addAction(updateAction) +// +// +// // Add a dummy action to ensure there's something to present the alert +// let dummyAction = UIAlertAction(title: "Close", style: .cancel, handler: nil) +// alertController.addAction(dummyAction) +// +// self.present(alertController, animated: true, completion: nil) + + self.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) } } ) @@ -383,3 +417,31 @@ extension TimeInterval { return formatter.string(from: self) } } + +//class CustomAlertViewController: UIViewController { +// var alertController: UIAlertController? +// +// override func viewDidLoad() { +// super.viewDidLoad() +// +// // Add your custom views and logic herey +// +// let button = UIButton(type: .system) +// button.setTitle("Press me", for: .normal) +// button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) +// +// // Add button to the view +// button.translatesAutoresizingMaskIntoConstraints = false +// view.addSubview(button) +// +// NSLayoutConstraint.activate([ +// button.centerXAnchor.constraint(equalTo: view.centerXAnchor), +// button.centerYAnchor.constraint(equalTo: view.centerYAnchor) +// ]) +// } +// +// @objc func buttonPressed() { +// // Handle button press without dismissing the alert +// print("Button pressed!") +// } +//} diff --git a/iOSClient/Rename file/NCRenameFile.swift b/iOSClient/Rename file/NCRenameFile.swift index d2b7ee73db..5ce1296ae1 100644 --- a/iOSClient/Rename file/NCRenameFile.swift +++ b/iOSClient/Rename file/NCRenameFile.swift @@ -58,12 +58,6 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() -// let blur = UIBlurEffect(style: .systemThinMaterial) -// let blurredEffectView = UIVisualEffectView(effect: blur) -// blurredEffectView.frame = view.bounds -// blurredEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] -// blurredEffectView.isUserInteractionEnabled = false -// view.insertSubview(blurredEffectView, at: 1) view.insertBlur(style: .systemMaterial) From 13e59af9db3edb24d802d8d65ffe880e413af14b Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Mon, 29 Jul 2024 19:08:00 +0200 Subject: [PATCH 04/16] File provider support Signed-off-by: Milen Pivchev --- .../FileProviderData.swift | 4 +- .../FileProviderExtension+Actions.swift | 8 +- Share/NCShareExtension+DataSource.swift | 6 +- Share/NCShareExtension+NCDelegate.swift | 104 +++++++++--------- iOSClient/AppDelegate.swift | 8 +- .../UIAlertController+Extension.swift | 21 ++-- iOSClient/Login/NCLogin.swift | 2 +- .../Menu/NCCollectionViewCommon+Menu.swift | 52 +-------- iOSClient/Menu/NCViewer+Menu.swift | 13 +-- .../Networking/NCNetworking+WebDAV.swift | 1 + .../en.lproj/Localizable.strings | 13 ++- 11 files changed, 88 insertions(+), 144 deletions(-) diff --git a/File Provider Extension/FileProviderData.swift b/File Provider Extension/FileProviderData.swift index bc271a4649..04a21fdff0 100644 --- a/File Provider Extension/FileProviderData.swift +++ b/File Provider Extension/FileProviderData.swift @@ -83,7 +83,7 @@ class fileProviderData: NSObject { homeServerUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) NCManageDatabase.shared.setCapabilities(account: account) - NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup, delegate: NCNetworking.shared) + NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared) NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate return tableAccount.init(value: activeAccount) @@ -106,7 +106,7 @@ class fileProviderData: NSObject { NCManageDatabase.shared.setCapabilities(account: account) - NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup, delegate: NCNetworking.shared) + NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared) NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate return tableAccount.init(value: activeAccount) diff --git a/File Provider Extension/FileProviderExtension+Actions.swift b/File Provider Extension/FileProviderExtension+Actions.swift index b1e7921454..852c947b33 100644 --- a/File Provider Extension/FileProviderExtension+Actions.swift +++ b/File Provider Extension/FileProviderExtension+Actions.swift @@ -54,7 +54,7 @@ extension FileProviderExtension { } } } else { - completionHandler(nil, NSFileProviderError(.serverUnreachable)) + completionHandler(nil, NSFileProviderError(.filenameCollision)) } } } @@ -70,7 +70,7 @@ extension FileProviderExtension { let fileName = metadata.fileName NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName) { account, error in - if error == .success { // || error == kOCErrorServerPathNotFound { + if error == .success { let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue) do { @@ -122,6 +122,8 @@ extension FileProviderExtension { let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier) completionHandler(item, nil) + } else if error.errorCode == NCGlobal.shared.errorBadRequest { + completionHandler(nil, NSFileProviderError(.noSuchItem, userInfo: [NSLocalizedDescriptionKey: error.errorDescription, NSLocalizedFailureReasonErrorKey: ""])) } else { completionHandler(nil, NSFileProviderError(.serverUnreachable)) } @@ -161,6 +163,8 @@ extension FileProviderExtension { } let item = FileProviderItem(metadata: tableMetadata.init(value: metadata), parentItemIdentifier: parentItemIdentifier) completionHandler(item, nil) + } else if error.errorCode == NCGlobal.shared.errorBadRequest { + completionHandler(nil, NSFileProviderError(.noSuchItem, userInfo: [NSLocalizedDescriptionKey: error.errorDescription, NSLocalizedFailureReasonErrorKey: ""])) } else { completionHandler(nil, NSFileProviderError(.serverUnreachable)) } diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index 2d10a75bf8..ce36ce2b79 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -84,7 +84,7 @@ extension NCShareExtension: UICollectionViewDataSource { return UICollectionViewCell() } - cell.listCellDelegate = self +// cell.listCellDelegate = self cell.fileObjectId = metadata.ocId cell.indexPath = indexPath @@ -176,7 +176,7 @@ extension NCShareExtension: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard !uploadStarted else { return } let fileName = filesName[indexPath.row] - renameFile(named: fileName) +// renameFile(named: fileName) } } @@ -190,7 +190,7 @@ extension NCShareExtension: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NCShareCell else { return UITableViewCell() } let fileName = filesName[indexPath.row] cell.setup(fileName: fileName) - cell.delegate = self +// cell.delegate = self return cell } } diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index d13e00f681..95108e54b1 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -96,58 +96,58 @@ extension NCShareExtension: NCAccountRequestDelegate { } } -extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCellDelegate { - func removeFile(named fileName: String) { - guard let index = self.filesName.firstIndex(of: fileName) else { - return showAlert(title: "_file_not_found_", description: fileName) - } - self.filesName.remove(at: index) - if self.filesName.isEmpty { - cancel(with: NCShareExtensionError.noFiles) - } else { - self.setCommandView() - } - } - - func renameFile(named fileName: String) { - guard let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile else { return } - - let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false) - vcRename.delegate = self - vcRename.fileName = fileName - vcRename.indexPath = IndexPath() - if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) { - vcRename.imagePreview = previewImage - } else { - vcRename.imagePreview = UIImage(named: resultInternalType.iconName) ?? NCImageCache.images.file - } - - let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) - - self.present(popup, animated: true) - } - - func rename(fileName: String, fileNameNew: String) { - guard fileName != fileNameNew else { return } - guard let fileIx = self.filesName.firstIndex(of: fileName), - !self.filesName.contains(fileNameNew), - utilityFileSystem.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + fileNameNew)) else { - return showAlert(title: "_single_file_conflict_title_", description: "'\(fileName)' -> '\(fileNameNew)'") - } - - filesName[fileIx] = fileNameNew - tableView.reloadData() - } - - func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { - } - - func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) { - } - - func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { - } -} +//extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCellDelegate { +// func removeFile(named fileName: String) { +// guard let index = self.filesName.firstIndex(of: fileName) else { +// return showAlert(title: "_file_not_found_", description: fileName) +// } +// self.filesName.remove(at: index) +// if self.filesName.isEmpty { +// cancel(with: NCShareExtensionError.noFiles) +// } else { +// self.setCommandView() +// } +// } +// +// func renameFile(named fileName: String) { +// guard let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile else { return } +// +// let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false) +// vcRename.delegate = self +// vcRename.fileName = fileName +// vcRename.indexPath = IndexPath() +// if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) { +// vcRename.imagePreview = previewImage +// } else { +// vcRename.imagePreview = UIImage(named: resultInternalType.iconName) ?? NCImageCache.images.file +// } +// +// let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) +// +// self.present(popup, animated: true) +// } +// +// func rename(fileName: String, fileNameNew: String) { +// guard fileName != fileNameNew else { return } +// guard let fileIx = self.filesName.firstIndex(of: fileName), +// !self.filesName.contains(fileNameNew), +// utilityFileSystem.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + fileNameNew)) else { +// return showAlert(title: "_single_file_conflict_title_", description: "'\(fileName)' -> '\(fileNameNew)'") +// } +// +// filesName[fileIx] = fileNameNew +// tableView.reloadData() +// } +// +// func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { +// } +// +// func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) { +// } +// +// func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { +// } +//} extension NCShareExtension: NCCreateFormUploadConflictDelegate { func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index a852c3d454..a6b5519ce9 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -109,7 +109,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD userId = activeAccount.userId password = NCKeychain().getPassword(account: account) - NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) + NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase) NCManageDatabase.shared.setCapabilities(account: account) NCBrandColor.shared.settingThemingColor(account: activeAccount.account) @@ -481,7 +481,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if urlBase.last == "/" { urlBase = String(urlBase.dropLast()) } let account: String = "\(user) \(urlBase)" - NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) + NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase) NextcloudKit.shared.getUserProfile { _, userProfile, _, error in if error == .success, let userProfile { NCManageDatabase.shared.deleteAccount(account) @@ -491,7 +491,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD completion(error) } } else { - NextcloudKit.shared.setup(account: self.account, user: self.user, userId: self.userId, password: self.password, urlBase: self.urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) + NextcloudKit.shared.setup(account: self.account, user: self.user, userId: self.userId, password: self.password, urlBase: self.urlBase) let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true) @@ -528,7 +528,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD self.userId = tableAccount.userId self.password = NCKeychain().getPassword(account: tableAccount.account) - NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) + NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase) NCManageDatabase.shared.setCapabilities(account: account) if let userProfile { diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 6c8b16c74e..251591bdbd 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -68,7 +68,6 @@ extension UIAlertController { textField.autocapitalizationType = .words } - // only allow saving if folder name exists NotificationCenter.default.addObserver( forName: UITextField.textDidChangeNotification, @@ -76,9 +75,10 @@ extension UIAlertController { queue: .main) { _ in guard let text = alertController.textFields?.first?.text else { return } let folderName = text.trimmingCharacters(in: .whitespaces) - let checkFolderName = FileNameValidator.shared.fileAlreadyExistsError - okAction.isEnabled = !folderName.isEmpty && folderName != "." && folderName != ".." -// alertController.message = folderName != "." && folderName != ".." ? "This is an error" : "" + + let textCheck = FileNameValidator.shared.checkFileName(folderName) + okAction.isEnabled = textCheck?.error == nil && !folderName.isEmpty + alertController.message = textCheck?.error.localizedDescription } alertController.addAction(cancelAction) @@ -196,8 +196,8 @@ extension UIAlertController { // if let start = textField.position(from: textField.beginningOfDocument, offset: 0), // let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { // textField.selectedTextRange = textField.textRange(from: start, to: end) -// } -// textField.selectAll(nil) + // } + // textField.selectAll(nil) } // only allow saving if folder name exists @@ -208,10 +208,9 @@ extension UIAlertController { guard let textField = alertController.textFields?.first else { return } if let start = textField.position(from: textField.beginningOfDocument, offset: 0), - let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { - textField.selectedTextRange = textField.textRange(from: start, to: end) - } -// textField.selectAll(nil) + let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { + textField.selectedTextRange = textField.textRange(from: start, to: end) + } } NotificationCenter.default.addObserver( @@ -221,7 +220,7 @@ extension UIAlertController { guard let text = alertController.textFields?.first?.text else { return } let textCheck = FileNameValidator.shared.checkFileName(text) - okAction.isEnabled = textCheck?.error == nil + okAction.isEnabled = textCheck?.error == nil && !text.isEmpty alertController.message = textCheck?.error.localizedDescription } diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 22a445ab24..315fdca72a 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -386,7 +386,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } private func getAppPassword(urlBase: String, user: String, password: String) { - NextcloudKit.shared.getAppPassword(url: urlBase, user: user, password: password) { token, _, error in + NextcloudKit.shared.getAppPassword(serverUrl: urlBase, username: user, password: password) { token, _, error in if error == .success, let password = token { self.createAccount(urlBase: urlBase, user: user, password: password) } else { diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index f7ef295192..29ecc60ed9 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -281,57 +281,7 @@ extension NCCollectionViewCommon { icon: utility.loadImage(named: "text.cursor", colors: [NCBrandColor.shared.iconImageColor]), order: 120, action: { _ in -// let alert = UIAlertController.renameFile { cancelled in -// -// } -// -// self.present(alert, animated: true) - - if let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile { - - vcRename.metadata = metadata - vcRename.imagePreview = imageIcon - vcRename.indexPath = indexPath - - let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) - popup.shadowEnabled = false - -// let alertController = UIAlertController(title: "Alert", message: nil, preferredStyle: .alert) - - - -// // Create an instance of your custom view controller -// let customViewController = CustomAlertViewController() -// customViewController.alertController = alertController -// -// // Add the custom view controller to the alert -// alertController.setValue(customViewController, forKey: "contentViewController") - -// alertController.addTextField { -// $0.placeholder = "Email" -// $0.addTarget(alertController, action: #selector(alertController.textDidChangeInLoginAlert), for: .editingChanged) -// } -// -// let updateAction = UIAlertAction(title: "Update", style: .default) { _ in -// // Code to update the alert controller -// alertController.title = "Updated Title" -// alertController.message = "Updated Message" -// -// // Adding a new action as an example -// let newAction = UIAlertAction(title: "New Action", style: .default, handler: nil) -// alertController.addAction(newAction) -// } -// alertController.addAction(updateAction) -// -// -// // Add a dummy action to ensure there's something to present the alert -// let dummyAction = UIAlertAction(title: "Close", style: .cancel, handler: nil) -// alertController.addAction(dummyAction) -// -// self.present(alertController, animated: true, completion: nil) - - self.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) - } + self.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) } ) ) diff --git a/iOSClient/Menu/NCViewer+Menu.swift b/iOSClient/Menu/NCViewer+Menu.swift index d5626299a0..b9f8a9f0a7 100644 --- a/iOSClient/Menu/NCViewer+Menu.swift +++ b/iOSClient/Menu/NCViewer+Menu.swift @@ -152,18 +152,7 @@ extension NCViewer { title: NSLocalizedString("_rename_", comment: ""), icon: utility.loadImage(named: "text.cursor", colors: [NCBrandColor.shared.iconImageColor]), action: { _ in - - if let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile { - - vcRename.metadata = metadata - vcRename.disableChangeExt = true - vcRename.imagePreview = imageIcon - vcRename.indexPath = indexPath - - let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) - - viewController.present(popup, animated: true) - } + viewController.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) } ) ) diff --git a/iOSClient/Networking/NCNetworking+WebDAV.swift b/iOSClient/Networking/NCNetworking+WebDAV.swift index 99d8a59f7e..2b92b9625f 100644 --- a/iOSClient/Networking/NCNetworking+WebDAV.swift +++ b/iOSClient/Networking/NCNetworking+WebDAV.swift @@ -216,6 +216,7 @@ extension NCNetworking { if fileNameFolder.isEmpty { return completion(NKError()) } + let fileNameFolderUrl = serverUrl + "/" + fileNameFolder NextcloudKit.shared.createFolder(serverUrlFileName: fileNameFolderUrl) { account, _, _, error in diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index ba0e68ea8f..297b003be7 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1090,9 +1090,10 @@ "_poll_desc_" = "Please complete the log in process in your browser"; // MARK: File name validator -"_filename_empty_" = "File name is empty"; -"_file_name_validator_upload_content_error_" = "Some files cannot be uploaded due to reserved names or invalid characters"; -"_file_name_validator_error_contains_reserved_names_or_invalid_characters_" = "Folder path contains reserved names or invalid characters"; -"_file_name_validator_error_invalid_character_" = "File name contains invalid characters: %s"; -"_file_name_validator_error_reserved_names_" = "%s is a reserved name"; -"_file_name_validator_error_ends_with_space_period_" = "File name ends with a space or a period"; +"_file_name_validator_error_upload_invalid_name_" = "Some files could not be uploaded as they contain forbidden names or invalid characters"; +"_file_name_validator_error_file_invalid_name_" = "File or folder name contains forbidden names or invalid characters"; +"_file_name_validator_error_folder_invalid_name_" = "Folder path contains forbidden names or invalid characters"; +"_file_name_validator_error_ends_with_space_period_" = "File or folder name ends with a space or a period"; +"_file_name_validator_error_reserved_name_" = "%s is a reserved name"; +"_file_name_validator_error_forbidden_file_extension_" = ".%s is a forbidden file extension"; +"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %s"; From bc2fe781c34799562f2b216188b798d20453499a Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 1 Aug 2024 16:31:16 +0200 Subject: [PATCH 05/16] WIP Signed-off-by: Milen Pivchev --- Nextcloud.xcodeproj/project.pbxproj | 4 ++-- iOSClient/AppDelegate.swift | 8 +++++++- iOSClient/Supporting Files/en.lproj/Localizable.strings | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 6cc42d0b1a..f578465cab 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -5457,7 +5457,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.5.0; + MARKETING_VERSION = 5.5.3; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; @@ -5520,7 +5520,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.5.0; + MARKETING_VERSION = 5.5.3; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index a6b5519ce9..17a1de0925 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -154,7 +154,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters, forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions ) -// FileNameValidator.fileAlreadyExistsError = NCGlobal.filealr return true } @@ -542,6 +541,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads") } + FileNameValidator.shared.setup( + forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames, + forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames, + forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters, + forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions + ) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser) completion() } diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 297b003be7..3f18a7fabe 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1094,6 +1094,6 @@ "_file_name_validator_error_file_invalid_name_" = "File or folder name contains forbidden names or invalid characters"; "_file_name_validator_error_folder_invalid_name_" = "Folder path contains forbidden names or invalid characters"; "_file_name_validator_error_ends_with_space_period_" = "File or folder name ends with a space or a period"; -"_file_name_validator_error_reserved_name_" = "%s is a reserved name"; -"_file_name_validator_error_forbidden_file_extension_" = ".%s is a forbidden file extension"; -"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %s"; +"_file_name_validator_error_reserved_name_" = "%@ is a reserved name"; +"_file_name_validator_error_forbidden_file_extension_" = ".%@ is a forbidden file extension"; +"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %@"; From 657fba6089d20089b408eae24cdea902cd394bd1 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 1 Aug 2024 16:44:16 +0200 Subject: [PATCH 06/16] WIP Signed-off-by: Milen Pivchev --- Nextcloud.xcodeproj/project.pbxproj | 36 --- Share/NCShareExtension+NCDelegate.swift | 53 ---- .../Create folder/NCCreateFolder.storyboard | 115 -------- iOSClient/Create folder/NCCreateFolder.swift | 245 ----------------- .../UIAlertController+Extension.swift | 31 --- .../Menu/NCCollectionViewCommon+Menu.swift | 30 +-- iOSClient/Rename file/NCRenameFile.storyboard | 149 ----------- iOSClient/Rename file/NCRenameFile.swift | 253 ------------------ iOSClient/Utility/NCLivePhoto.swift | 2 +- 9 files changed, 2 insertions(+), 912 deletions(-) delete mode 100644 iOSClient/Create folder/NCCreateFolder.storyboard delete mode 100644 iOSClient/Create folder/NCCreateFolder.swift delete mode 100644 iOSClient/Rename file/NCRenameFile.storyboard delete mode 100644 iOSClient/Rename file/NCRenameFile.swift diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 3e6842d9e9..66d38e884f 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -154,8 +154,6 @@ F3A0479B2BD2668800658E7B /* NCAssistant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A047962BD2668800658E7B /* NCAssistant.swift */; }; F3A0479E2BD268B500658E7B /* PopupView in Frameworks */ = {isa = PBXBuildFile; productRef = F3A0479D2BD268B500658E7B /* PopupView */; }; F3A7AFC62A41AA82001FC89C /* BaseUIXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */; }; - F3AECBBD2C4A9E100067CA8C /* NCCreateFolder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */; }; - F3AECBBE2C4A9E100067CA8C /* NCCreateFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */; }; F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */; }; F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */; }; F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */; }; @@ -199,8 +197,6 @@ F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */; }; F70CEF5623E9C7E50007035B /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; }; F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */; }; - F70D87CF25EE6E58008CBBBD /* NCRenameFile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70D87CD25EE6E58008CBBBD /* NCRenameFile.storyboard */; }; - F70D87D025EE6E58008CBBBD /* NCRenameFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */; }; F70D8D8124A4A9BF000A5756 /* NCNetworkingProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70D8D8024A4A9BF000A5756 /* NCNetworkingProcess.swift */; }; F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = F710D1F42405770F00A6033D /* NCViewerPDF.swift */; }; F710D2022405826100A6033D /* NCViewer+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F710D2012405826100A6033D /* NCViewer+Menu.swift */; }; @@ -714,8 +710,6 @@ F79B646226CA661600838ACA /* UIControl+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B645F26CA661600838ACA /* UIControl+Extension.swift */; }; F79B646326CA661600838ACA /* UIControl+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B645F26CA661600838ACA /* UIControl+Extension.swift */; }; F79B869B265E19D40085C0E0 /* NSMutableAttributedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B869A265E19D40085C0E0 /* NSMutableAttributedString+Extension.swift */; }; - F79EC77F26316193004E59D6 /* NCRenameFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */; }; - F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70D87CD25EE6E58008CBBBD /* NCRenameFile.storyboard */; }; F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F702F30725EE5D47008F8E80 /* NCPopupViewController.swift */; }; F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */; }; F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDAA126B004980007D134 /* NCPlayer.swift */; }; @@ -1153,8 +1147,6 @@ F3A047952BD2668800658E7B /* NCAssistantTaskDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAssistantTaskDetail.swift; sourceTree = ""; }; F3A047962BD2668800658E7B /* NCAssistant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAssistant.swift; sourceTree = ""; }; F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUIXCTestCase.swift; sourceTree = ""; }; - F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCCreateFolder.storyboard; sourceTree = ""; }; - F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCCreateFolder.swift; sourceTree = ""; }; F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMoreAppSuggestionsCell.xib; sourceTree = ""; }; F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreAppSuggestionsCell.swift; sourceTree = ""; }; F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCCellMore.swift; sourceTree = ""; }; @@ -1221,8 +1213,6 @@ F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NCEndToEndEncryption.m; sourceTree = ""; }; F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCCollectionViewCommon.swift; sourceTree = ""; }; - F70D87CD25EE6E58008CBBBD /* NCRenameFile.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCRenameFile.storyboard; sourceTree = ""; }; - F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCRenameFile.swift; sourceTree = ""; }; F70D8D8024A4A9BF000A5756 /* NCNetworkingProcess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingProcess.swift; sourceTree = ""; }; F70F2BA4225F2D8900EBB73E /* ZIPFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZIPFoundation.framework; path = Carthage/Build/iOS/ZIPFoundation.framework; sourceTree = ""; }; F70F96AF2874394B006C8379 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = ""; }; @@ -2023,15 +2013,6 @@ path = "Task Detail"; sourceTree = ""; }; - F3AECBB62C4A9D550067CA8C /* Create folder */ = { - isa = PBXGroup; - children = ( - F3AECBBB2C4A9E100067CA8C /* NCCreateFolder.storyboard */, - F3AECBBC2C4A9E100067CA8C /* NCCreateFolder.swift */, - ); - path = "Create folder"; - sourceTree = ""; - }; F3BB46502A39EC2D00461F6E /* Cells */ = { isa = PBXGroup; children = ( @@ -2095,15 +2076,6 @@ path = Color; sourceTree = ""; }; - F70D87CC25EE6E58008CBBBD /* Rename file */ = { - isa = PBXGroup; - children = ( - F70D87CD25EE6E58008CBBBD /* NCRenameFile.storyboard */, - F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */, - ); - path = "Rename file"; - sourceTree = ""; - }; F713418B2597513800768D21 /* PushNotification */ = { isa = PBXGroup; children = ( @@ -2993,8 +2965,6 @@ F7381ED9218218A4000B1560 /* Offline */, F713418B2597513800768D21 /* PushNotification */, F765F72E25237E3F00391DBE /* Recent */, - F3AECBB62C4A9D550067CA8C /* Create folder */, - F70D87CC25EE6E58008CBBBD /* Rename file */, F7CADB3D23CCDDA1000EEC78 /* RichWorkspace */, F76882042C0DD1E7001CF441 /* Settings */, F758B41E212C516300515F55 /* Scan document */, @@ -3682,7 +3652,6 @@ F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */, F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */, F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */, - F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */, F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */, F700222D1EC479840080073F /* Custom.xcassets in Resources */, ); @@ -3728,7 +3697,6 @@ F702F2F125EE5CDB008F8E80 /* NCLogin.storyboard in Resources */, F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */, F758B45A212C564000515F55 /* NCScan.storyboard in Resources */, - F70D87CF25EE6E58008CBBBD /* NCRenameFile.storyboard in Resources */, F765F73225237E3F00391DBE /* NCRecent.storyboard in Resources */, F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */, F702F30225EE5D2C008F8E80 /* english.txt in Resources */, @@ -3743,7 +3711,6 @@ F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */, F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */, F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */, - F3AECBBD2C4A9E100067CA8C /* NCCreateFolder.storyboard in Resources */, F74C0437253F1CDC009762AB /* NCShares.storyboard in Resources */, F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */, F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */, @@ -4061,7 +4028,6 @@ F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F73EF7CA2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */, F749B654297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */, - F79EC77F26316193004E59D6 /* NCRenameFile.swift in Sources */, AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */, F74C86382AEFBE64009A1D4A /* NCImageCache.swift in Sources */, F73EF7C22B02250B0087E6E9 /* NCManageDatabase+GPS.swift in Sources */, @@ -4249,7 +4215,6 @@ F702F30825EE5D47008F8E80 /* NCPopupViewController.swift in Sources */, F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */, 370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */, - F70D87D025EE6E58008CBBBD /* NCRenameFile.swift in Sources */, F768822C2C0DD1E7001CF441 /* NCKeychain.swift in Sources */, F71CD6CA2930D7B1006C95C1 /* NCApplicationHandle.swift in Sources */, F73EF7D72B0226080087E6E9 /* NCManageDatabase+Tip.swift in Sources */, @@ -4435,7 +4400,6 @@ F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */, F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */, F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */, - F3AECBBE2C4A9E100067CA8C /* NCCreateFolder.swift in Sources */, AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */, F77BC3ED293E528A005F2B08 /* NCConfigServer.swift in Sources */, F7A560422AE1593700BE8FD6 /* NCOperationSaveLivePhoto.swift in Sources */, diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index 95108e54b1..c24142d006 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -96,59 +96,6 @@ extension NCShareExtension: NCAccountRequestDelegate { } } -//extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCellDelegate { -// func removeFile(named fileName: String) { -// guard let index = self.filesName.firstIndex(of: fileName) else { -// return showAlert(title: "_file_not_found_", description: fileName) -// } -// self.filesName.remove(at: index) -// if self.filesName.isEmpty { -// cancel(with: NCShareExtensionError.noFiles) -// } else { -// self.setCommandView() -// } -// } -// -// func renameFile(named fileName: String) { -// guard let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile else { return } -// -// let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false) -// vcRename.delegate = self -// vcRename.fileName = fileName -// vcRename.indexPath = IndexPath() -// if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) { -// vcRename.imagePreview = previewImage -// } else { -// vcRename.imagePreview = UIImage(named: resultInternalType.iconName) ?? NCImageCache.images.file -// } -// -// let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height) -// -// self.present(popup, animated: true) -// } -// -// func rename(fileName: String, fileNameNew: String) { -// guard fileName != fileNameNew else { return } -// guard let fileIx = self.filesName.firstIndex(of: fileName), -// !self.filesName.contains(fileNameNew), -// utilityFileSystem.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + fileNameNew)) else { -// return showAlert(title: "_single_file_conflict_title_", description: "'\(fileName)' -> '\(fileNameNew)'") -// } -// -// filesName[fileIx] = fileNameNew -// tableView.reloadData() -// } -// -// func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { -// } -// -// func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) { -// } -// -// func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { -// } -//} - extension NCShareExtension: NCCreateFormUploadConflictDelegate { func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { guard let metadatas = metadatas else { diff --git a/iOSClient/Create folder/NCCreateFolder.storyboard b/iOSClient/Create folder/NCCreateFolder.storyboard deleted file mode 100644 index 9b93fc71f5..0000000000 --- a/iOSClient/Create folder/NCCreateFolder.storyboard +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iOSClient/Create folder/NCCreateFolder.swift b/iOSClient/Create folder/NCCreateFolder.swift deleted file mode 100644 index 93810e66e2..0000000000 --- a/iOSClient/Create folder/NCCreateFolder.swift +++ /dev/null @@ -1,245 +0,0 @@ -// -// NCRenameFile.swift -// Nextcloud -// -// Created by Marino Faggiana on 26/02/21. -// Copyright © 2021 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -import UIKit -import NextcloudKit - -public protocol NCCreateFolderDelegate: AnyObject { - func rename(fileName: String, fileNameNew: String) -} - -// optional func -public extension NCCreateFolderDelegate { - func rename(fileName: String, fileNameNew: String) {} -} - -class NCCreateFolder: UIViewController, UITextFieldDelegate { - - @IBOutlet weak var titleLabel: UILabel! - @IBOutlet weak var previewFile: UIImageView! - @IBOutlet weak var fileNameNoExtension: UITextField! - @IBOutlet weak var point: UILabel! - @IBOutlet weak var ext: UITextField! - @IBOutlet weak var fileNameNoExtensionTrailingContraint: NSLayoutConstraint! - @IBOutlet weak var cancelButton: UIButton! - @IBOutlet weak var renameButton: UIButton! - - let width: CGFloat = 300 - let height: CGFloat = 310 - - var metadata: tableMetadata? - var indexPath: IndexPath = IndexPath() - var fileName: String? - var imagePreview: UIImage? - var disableChangeExt: Bool = false - weak var delegate: NCCreateFolderDelegate? - - // MARK: - View Life Cycle - - override func viewDidLoad() { - super.viewDidLoad() - if let metadata = self.metadata { - - if metadata.directory { - titleLabel.text = NSLocalizedString("_rename_folder_", comment: "") - } else { - titleLabel.text = NSLocalizedString("_rename_file_", comment: "") - } - - fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension - fileNameNoExtension.delegate = self - fileNameNoExtension.becomeFirstResponder() - - ext.text = metadata.fileExtension - ext.delegate = self - if disableChangeExt { - ext.isEnabled = false - ext.textColor = .lightGray - } - - previewFile.image = imagePreview - previewFile.layer.cornerRadius = 10 - previewFile.layer.masksToBounds = true - - if metadata.directory { - - if imagePreview == nil { - previewFile.image = NCImageCache.images.folder - } - - ext.isHidden = true - point.isHidden = true - fileNameNoExtensionTrailingContraint.constant = 20 - - } else { - - if imagePreview == nil { - previewFile.image = NCImageCache.images.file - } - - fileNameNoExtensionTrailingContraint.constant = 90 - } - - } else if let fileName = self.fileName { - - titleLabel.text = NSLocalizedString("_rename_file_", comment: "") - - fileNameNoExtension.text = (fileName as NSString).deletingPathExtension - fileNameNoExtension.delegate = self - fileNameNoExtension.becomeFirstResponder() - fileNameNoExtensionTrailingContraint.constant = 90 - - ext.text = (fileName as NSString).pathExtension - ext.delegate = self - - if imagePreview == nil { - previewFile.image = NCImageCache.images.file - } else { - previewFile.image = imagePreview - } - previewFile.layer.cornerRadius = 10 - previewFile.layer.masksToBounds = true - } - - cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) - cancelButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) - renameButton.setTitle(NSLocalizedString("_rename_", comment: ""), for: .normal) - renameButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if metadata == nil && fileName == nil { - dismiss(animated: true) - } - - fileNameNoExtension.selectAll(nil) - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - - textField.resignFirstResponder() - renameFile(textField) - return true - } - - // MARK: - Action - - @IBAction func cancel(_ sender: Any) { - - dismiss(animated: true) - } - - @IBAction func renameFile(_ sender: Any) { - - var fileNameNoExtensionNew = "" - var extNew = "" - var fileNameNew = "" - - if let metadata = self.metadata { - - let extCurrent = (metadata.fileNameView as NSString).pathExtension - - if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { - return self.fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension - } else { - fileNameNoExtensionNew = fileNameNoExtension.text! - } - - if metadata.directory { - fileNameNew = fileNameNoExtensionNew - renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) - - } else { - - if ext.text == nil || ext.text?.count == 0 { - return self.ext.text = metadata.fileExtension - } else { - extNew = ext.text! - } - - if extNew != extCurrent { - - let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent) - let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert) - - var title = NSLocalizedString("_use_", comment: "") + " ." + extNew - alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in - - fileNameNew = fileNameNoExtensionNew + "." + extNew - self.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: self.indexPath) - })) - - title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent - alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in - self.ext.text = metadata.fileExtension - })) - - self.present(alertController, animated: true) - - } else { - fileNameNew = fileNameNoExtensionNew + "." + extNew - renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) - } - } - - } else if let fileName = self.fileName { - - if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { - return fileNameNoExtension.text = (fileName as NSString).deletingPathExtension - } else if ext.text == nil || ext.text?.count == 0 { - return ext.text = (fileName as NSString).pathExtension - } - - fileNameNew = (fileNameNoExtension.text ?? "") + "." + (ext.text ?? "") - self.dismiss(animated: true) { - self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew) - } - } - } - - // MARK: - Networking - - func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, indexPath: IndexPath) { - - // verify if already exists - if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil { - NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_")) - return - } - - NCActivityIndicator.shared.start() - - NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath, viewController: self) { error in - - NCActivityIndicator.shared.stop() - - if error == .success { - self.dismiss(animated: true) - } else { - NCContentPresenter().showError(error: error) - } - } - } -} diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 2f45eba494..ffd66cd94d 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -153,32 +153,7 @@ extension UIAlertController { return alertController } -// static func renameFile(completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController { -// let alertController = UIAlertController( -// title: NSLocalizedString("_rename_", comment: ""), -// message: nil, -// preferredStyle: .alert) -// alertController.addTextField { textField in -// textField.autocapitalizationType = .words -// } -// alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { _ in -// completion(true) -// }) -// alertController.addAction(UIAlertAction(title: NSLocalizedString("_rename_", comment: ""), style: .default) { _ in -// completion(false) -// }) -// return alertController -// } - static func renameFile(oldName: String, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController { -// class Test: NSObject, UITextFieldDelegate { -// func textFieldDidBeginEditing(_ textField: UITextField) { -// textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument) -// textField.becomeFirstResponder() -// } -// } - - let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in @@ -192,12 +167,6 @@ extension UIAlertController { alertController.addTextField { textField in textField.text = oldName textField.autocapitalizationType = .words -// textField.becomeFirstResponder() -// if let start = textField.position(from: textField.beginningOfDocument, offset: 0), -// let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { -// textField.selectedTextRange = textField.textRange(from: start, to: end) - // } - // textField.selectAll(nil) } // only allow saving if folder name exists diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index eba6b58029..9dea85e406 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -171,7 +171,7 @@ extension NCCollectionViewCommon { icon: utility.loadImage(named: "lock", colors: [NCBrandColor.shared.iconImageColor]), order: 30, action: { _ in - NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true, account: metadata.account) { _, error in + NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true) { _, error in if error == .success { NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, serverUrl)) NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, encrypted: false, account: metadata.account) @@ -367,31 +367,3 @@ extension TimeInterval { return formatter.string(from: self) } } - -//class CustomAlertViewController: UIViewController { -// var alertController: UIAlertController? -// -// override func viewDidLoad() { -// super.viewDidLoad() -// -// // Add your custom views and logic herey -// -// let button = UIButton(type: .system) -// button.setTitle("Press me", for: .normal) -// button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) -// -// // Add button to the view -// button.translatesAutoresizingMaskIntoConstraints = false -// view.addSubview(button) -// -// NSLayoutConstraint.activate([ -// button.centerXAnchor.constraint(equalTo: view.centerXAnchor), -// button.centerYAnchor.constraint(equalTo: view.centerYAnchor) -// ]) -// } -// -// @objc func buttonPressed() { -// // Handle button press without dismissing the alert -// print("Button pressed!") -// } -//} diff --git a/iOSClient/Rename file/NCRenameFile.storyboard b/iOSClient/Rename file/NCRenameFile.storyboard deleted file mode 100644 index 7e83dfd1e0..0000000000 --- a/iOSClient/Rename file/NCRenameFile.storyboard +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iOSClient/Rename file/NCRenameFile.swift b/iOSClient/Rename file/NCRenameFile.swift deleted file mode 100644 index 5ce1296ae1..0000000000 --- a/iOSClient/Rename file/NCRenameFile.swift +++ /dev/null @@ -1,253 +0,0 @@ -// -// NCRenameFile.swift -// Nextcloud -// -// Created by Marino Faggiana on 26/02/21. -// Copyright © 2021 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -import UIKit -import NextcloudKit - -public protocol NCRenameFileDelegate: AnyObject { - func rename(fileName: String, fileNameNew: String) -} - -// optional func -public extension NCRenameFileDelegate { - func rename(fileName: String, fileNameNew: String) {} -} - -class NCRenameFile: UIViewController, UITextFieldDelegate { - - @IBOutlet weak var titleLabel: UILabel! - @IBOutlet weak var previewFile: UIImageView! - @IBOutlet weak var fileNameNoExtension: UITextField! - @IBOutlet weak var point: UILabel! - @IBOutlet weak var ext: UITextField! - @IBOutlet weak var fileNameNoExtensionTrailingContraint: NSLayoutConstraint! - @IBOutlet weak var cancelButton: UIButton! - @IBOutlet weak var renameButton: UIButton! - - let width: CGFloat = 300 - let height: CGFloat = 310 - - var metadata: tableMetadata? - var indexPath: IndexPath = IndexPath() - var fileName: String? - var imagePreview: UIImage? - var disableChangeExt: Bool = false - weak var delegate: NCRenameFileDelegate? - - // MARK: - View Life Cycle - - override func viewDidLoad() { - super.viewDidLoad() - - view.insertBlur(style: .systemMaterial) - - if let metadata = self.metadata { - - if metadata.directory { - titleLabel.text = NSLocalizedString("_rename_folder_", comment: "") - } else { - titleLabel.text = NSLocalizedString("_rename_file_", comment: "") - } - - fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension - fileNameNoExtension.delegate = self - fileNameNoExtension.becomeFirstResponder() - - ext.text = metadata.fileExtension - ext.delegate = self - if disableChangeExt { - ext.isEnabled = false - ext.textColor = .lightGray - } - - previewFile.image = imagePreview - previewFile.layer.cornerRadius = 10 - previewFile.layer.masksToBounds = true - - if metadata.directory { - - if imagePreview == nil { - previewFile.image = NCImageCache.images.folder - } - - ext.isHidden = true - point.isHidden = true - fileNameNoExtensionTrailingContraint.constant = 20 - - } else { - - if imagePreview == nil { - previewFile.image = NCImageCache.images.file - } - - fileNameNoExtensionTrailingContraint.constant = 90 - } - - } else if let fileName = self.fileName { - - titleLabel.text = NSLocalizedString("_rename_file_", comment: "") - - fileNameNoExtension.text = (fileName as NSString).deletingPathExtension - fileNameNoExtension.delegate = self - fileNameNoExtension.becomeFirstResponder() - fileNameNoExtensionTrailingContraint.constant = 90 - - ext.text = (fileName as NSString).pathExtension - ext.delegate = self - - if imagePreview == nil { - previewFile.image = NCImageCache.images.file - } else { - previewFile.image = imagePreview - } - previewFile.layer.cornerRadius = 10 - previewFile.layer.masksToBounds = true - } - - cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) - cancelButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) - renameButton.setTitle(NSLocalizedString("_rename_", comment: ""), for: .normal) - renameButton.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if metadata == nil && fileName == nil { - dismiss(animated: true) - } - - fileNameNoExtension.selectAll(nil) - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - - textField.resignFirstResponder() - renameFile(textField) - return true - } - - // MARK: - Action - - @IBAction func cancel(_ sender: Any) { - - dismiss(animated: true) - } - - @IBAction func renameFile(_ sender: Any) { - - var fileNameNoExtensionNew = "" - var extNew = "" - var fileNameNew = "" - - if let metadata = self.metadata { - - let extCurrent = (metadata.fileNameView as NSString).pathExtension - - if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { - return self.fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension - } else { - fileNameNoExtensionNew = fileNameNoExtension.text! - } - - if metadata.directory { - - fileNameNew = fileNameNoExtensionNew - renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) - - } else { - - if ext.text == nil || ext.text?.count == 0 { - return self.ext.text = metadata.fileExtension - } else { - extNew = ext.text! - } - - if extNew != extCurrent { - - let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent) - let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert) - - var title = NSLocalizedString("_use_", comment: "") + " ." + extNew - alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in - - fileNameNew = fileNameNoExtensionNew + "." + extNew - self.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: self.indexPath) - })) - - title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent - alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in - self.ext.text = metadata.fileExtension - })) - - self.present(alertController, animated: true) - - } else { - - fileNameNew = fileNameNoExtensionNew + "." + extNew - renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath) - } - } - - } else if let fileName = self.fileName { - - if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 { - return fileNameNoExtension.text = (fileName as NSString).deletingPathExtension - } else if ext.text == nil || ext.text?.count == 0 { - return ext.text = (fileName as NSString).pathExtension - } - - fileNameNew = (fileNameNoExtension.text ?? "") + "." + (ext.text ?? "") - self.dismiss(animated: true) { - self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew) - } - } - } - - // MARK: - Networking - - func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, indexPath: IndexPath) { - - // verify if already exists - if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil { - NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_")) - return - } - - NCActivityIndicator.shared.start() - - NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath, viewController: self) { error in - - NCActivityIndicator.shared.stop() - - if error == .success { - - self.dismiss(animated: true) - - } else { - - NCContentPresenter().showError(error: error) - } - } - } -} diff --git a/iOSClient/Utility/NCLivePhoto.swift b/iOSClient/Utility/NCLivePhoto.swift index d599f12744..2e22bc6eb6 100644 --- a/iOSClient/Utility/NCLivePhoto.swift +++ b/iOSClient/Utility/NCLivePhoto.swift @@ -468,7 +468,7 @@ extension NCLivePhoto { account: String, options: NKRequestOptions = NKRequestOptions()) async -> (account: String, error: NKError) { await withUnsafeContinuation({ continuation in - NextcloudKit.shared.setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, livePhotoFile: livePhotoFile, account: account, options: options) { account, error in + NextcloudKit.shared.setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, livePhotoFile: livePhotoFile, options: options) { account, error in continuation.resume(returning: (account: account, error: error)) } }) From feac52782b5bb5a62d86ad9b0a5cebbd7c3eb5ed Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 1 Aug 2024 16:54:47 +0200 Subject: [PATCH 07/16] Merge fixes Signed-off-by: Milen Pivchev --- iOSClient/Login/NCLogin.swift | 2 +- iOSClient/Menu/NCCollectionViewCommon+Menu.swift | 2 +- iOSClient/Utility/NCLivePhoto.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 8ab17c891a..5f7b45f28e 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -388,7 +388,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } private func getAppPassword(urlBase: String, user: String, password: String) { - NextcloudKit.shared.getAppPassword(serverUrl: urlBase, username: user, password: password) { token, _, error in + NextcloudKit.shared.getAppPassword(url: urlBase, user: user, password: password) { token, _, error in if error == .success, let password = token { self.createAccount(urlBase: urlBase, user: user, password: password) } else { diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index 9dea85e406..2bd8f64d60 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -171,7 +171,7 @@ extension NCCollectionViewCommon { icon: utility.loadImage(named: "lock", colors: [NCBrandColor.shared.iconImageColor]), order: 30, action: { _ in - NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true) { _, error in + NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true, account: metadata.account) { _, error in if error == .success { NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, serverUrl)) NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, encrypted: false, account: metadata.account) diff --git a/iOSClient/Utility/NCLivePhoto.swift b/iOSClient/Utility/NCLivePhoto.swift index 2e22bc6eb6..d599f12744 100644 --- a/iOSClient/Utility/NCLivePhoto.swift +++ b/iOSClient/Utility/NCLivePhoto.swift @@ -468,7 +468,7 @@ extension NCLivePhoto { account: String, options: NKRequestOptions = NKRequestOptions()) async -> (account: String, error: NKError) { await withUnsafeContinuation({ continuation in - NextcloudKit.shared.setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, livePhotoFile: livePhotoFile, options: options) { account, error in + NextcloudKit.shared.setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, livePhotoFile: livePhotoFile, account: account, options: options) { account, error in continuation.resume(returning: (account: account, error: error)) } }) From d5741db8e03671fc410bdb35a7a6fae6c2e15c97 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 8 Aug 2024 14:48:48 +0200 Subject: [PATCH 08/16] WIP Signed-off-by: Milen Pivchev --- Share/NCShareExtension+DataSource.swift | 5 +++ Share/NCShareExtension+Files.swift | 1 + Share/NCShareExtension+NCDelegate.swift | 7 ++++ Share/NCShareExtension.swift | 7 ++++ .../UIAlertController+Extension.swift | 33 +++++++++++++++++-- iOSClient/Main/NCPickerViewController.swift | 14 ++++++-- .../Menu/NCCollectionViewCommon+Menu.swift | 2 +- iOSClient/Menu/NCMenuAction.swift | 19 +++++++++-- iOSClient/Menu/NCViewer+Menu.swift | 2 +- .../E2EE/NCNetworkingE2EEUpload.swift | 1 - .../Networking/NCNetworking+WebDAV.swift | 1 - iOSClient/Select/NCSelect.swift | 17 ++++++---- .../en.lproj/Localizable.strings | 15 +++++---- 13 files changed, 98 insertions(+), 26 deletions(-) diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index ce36ce2b79..7eb9a71ab6 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -35,6 +35,11 @@ extension NCShareExtension: UICollectionViewDelegate { showAlert(title: "_info_", description: "_e2e_goto_settings_for_enable_") } + if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView) { + present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + return + } + self.serverUrl = serverUrl reloadDatasource(withLoadFolder: true) setNavigationBar(navigationTitle: metadata.fileNameView) diff --git a/Share/NCShareExtension+Files.swift b/Share/NCShareExtension+Files.swift index 3d8565c827..1da8b52ef6 100644 --- a/Share/NCShareExtension+Files.swift +++ b/Share/NCShareExtension+Files.swift @@ -23,6 +23,7 @@ import Foundation import UniformTypeIdentifiers +import NextcloudKit extension NCShareExtension { @objc func reloadDatasource(withLoadFolder: Bool) { diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index c24142d006..f59784af64 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -93,6 +93,13 @@ extension NCShareExtension: NCAccountRequestDelegate { reloadDatasource(withLoadFolder: true) setNavigationBar(navigationTitle: NCBrandOptions.shared.brand) + + FileNameValidator.shared.setup( + forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames, + forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames, + forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters, + forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions + ) } } diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift index d7c91c4e8f..47fa2bd13f 100644 --- a/Share/NCShareExtension.swift +++ b/Share/NCShareExtension.swift @@ -298,6 +298,12 @@ extension NCShareExtension { var conflicts: [tableMetadata] = [] for fileName in filesName { + if let fileNameError = FileNameValidator.shared.checkFileName(fileName) { + present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + + continue + } + let ocId = NSUUID().uuidString let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName) guard utilityFileSystem.copyFile(atPath: (NSTemporaryDirectory() + fileName), toPath: toPath) else { continue } @@ -335,6 +341,7 @@ extension NCShareExtension { guard uploadStarted else { return } guard uploadMetadata.count > counterUploaded else { return DispatchQueue.main.async { self.finishedUploading() } } let metadata = uploadMetadata[counterUploaded] + let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false) metadata.contentType = results.mimeType metadata.iconName = results.iconName diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index ffd66cd94d..495e848e0e 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -153,11 +153,28 @@ extension UIAlertController { return alertController } - static func renameFile(oldName: String, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController { + static func renameFile(metadata: tableMetadata, indexPath: IndexPath, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController { let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in - guard let fileNameFolder = alertController.textFields?.first?.text else { return } + guard let newFileName = alertController.textFields?.first?.text else { return } + + // verify if already exists + if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, newFileName)) != nil { + NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_")) + return + } + + NCActivityIndicator.shared.start() + + NCNetworking.shared.renameMetadata(metadata, fileNameNew: newFileName, indexPath: indexPath) { error in + + NCActivityIndicator.shared.stop() + + if error != .success { + NCContentPresenter().showError(error: error) + } + } }) // text field is initially empty, no action @@ -165,7 +182,7 @@ extension UIAlertController { let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) alertController.addTextField { textField in - textField.text = oldName + textField.text = metadata.fileName textField.autocapitalizationType = .words } @@ -197,4 +214,14 @@ extension UIAlertController { alertController.addAction(okAction) return alertController } + + static func warning(title: String? = nil, message: String? = nil) -> UIAlertController { + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + + let okAction = UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default) + + alertController.addAction(okAction) + + return alertController + } } diff --git a/iOSClient/Main/NCPickerViewController.swift b/iOSClient/Main/NCPickerViewController.swift index 19b09e5f8b..97afaaf4a8 100644 --- a/iOSClient/Main/NCPickerViewController.swift +++ b/iOSClient/Main/NCPickerViewController.swift @@ -142,8 +142,13 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate { if metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue { metadata.classFile = NKCommon.TypeClassFile.video.rawValue } - NCManageDatabase.shared.addMetadata(metadata) - NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) + + if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView) { + mainTabBarController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + } else { + NCManageDatabase.shared.addMetadata(metadata) + NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) + } } else { let serverUrl = mainTabBarController.currentServerUrl() @@ -161,6 +166,11 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate { let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "") + if let fileNameError = FileNameValidator.shared.checkFileName(metadataForUpload.fileNameView) { + mainTabBarController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + continue + } + metadataForUpload.session = NCNetworking.shared.sessionUploadBackground metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile metadataForUpload.size = utilityFileSystem.getFileSize(filePath: toPath) diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index 2bd8f64d60..8fb5bf9c13 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -281,7 +281,7 @@ extension NCCollectionViewCommon { icon: utility.loadImage(named: "text.cursor", colors: [NCBrandColor.shared.iconImageColor]), order: 120, action: { _ in - self.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) + self.present(UIAlertController.renameFile(metadata: metadata, indexPath: indexPath), animated: true) } ) ) diff --git a/iOSClient/Menu/NCMenuAction.swift b/iOSClient/Menu/NCMenuAction.swift index 6e082b5dab..208693c756 100644 --- a/iOSClient/Menu/NCMenuAction.swift +++ b/iOSClient/Menu/NCMenuAction.swift @@ -201,9 +201,22 @@ extension NCMenuAction { icon: NCUtility().loadImage(named: "rectangle.portrait.and.arrow.right", colors: [NCBrandColor.shared.iconImageColor]), order: order, action: { _ in - let controller = viewController.tabBarController as? NCMainTabBarController - NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller) - completion?() + var fileNameError: NKError? + + for metadata in selectedMetadatas { + if let checkError = FileNameValidator.shared.checkFileName(metadata.fileNameView) { + fileNameError = checkError + break + } + } + + if let fileNameError { + viewController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true, completion: nil) + } else { + let controller = viewController.tabBarController as? NCMainTabBarController + NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller) + completion?() + } } ) } diff --git a/iOSClient/Menu/NCViewer+Menu.swift b/iOSClient/Menu/NCViewer+Menu.swift index c363e7e030..9f8de5dcc5 100644 --- a/iOSClient/Menu/NCViewer+Menu.swift +++ b/iOSClient/Menu/NCViewer+Menu.swift @@ -152,7 +152,7 @@ extension NCViewer { title: NSLocalizedString("_rename_", comment: ""), icon: utility.loadImage(named: "text.cursor", colors: [NCBrandColor.shared.iconImageColor]), action: { _ in - viewController.present(UIAlertController.renameFile(oldName: metadata.fileName), animated: true) + viewController.present(UIAlertController.renameFile(metadata: metadata, indexPath: indexPath), animated: true) } ) ) diff --git a/iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift b/iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift index 40ed82627a..136d632c07 100644 --- a/iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift +++ b/iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift @@ -44,7 +44,6 @@ class NCNetworkingE2EEUpload: NSObject { var numChunks: Int = 0 func upload(metadata: tableMetadata, uploadE2EEDelegate: uploadE2EEDelegate? = nil, hudView: UIView?, hud: JGProgressHUD?) async -> NKError { - var metadata = metadata let ocIdTemp = metadata.ocId diff --git a/iOSClient/Networking/NCNetworking+WebDAV.swift b/iOSClient/Networking/NCNetworking+WebDAV.swift index 25f1120de3..35ee30dc24 100644 --- a/iOSClient/Networking/NCNetworking+WebDAV.swift +++ b/iOSClient/Networking/NCNetworking+WebDAV.swift @@ -412,7 +412,6 @@ extension NCNetworking { func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, indexPath: IndexPath, - viewController: UIViewController?, completion: @escaping (_ error: NKError) -> Void) { let metadataLive = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) let fileNameNew = fileNameNew.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index 78b7846c2c..bb706f0d3f 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -147,11 +147,11 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - self.navigationItem.title = titleCurrentFolder + let folderPath = utilityFileSystem.getFileNamePath("", serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId) - // set the serverUrl - if serverUrl.isEmpty { + if serverUrl.isEmpty || !FileNameValidator.shared.checkFolderPath(folderPath: folderPath) { serverUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) + titleCurrentFolder = NCBrandOptions.shared.brand } // get auto upload folder @@ -159,6 +159,8 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, userId: activeAccount.userId, account: activeAccount.account) loadDatasource(withLoadFolder: true) + + self.navigationItem.title = titleCurrentFolder } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -252,7 +254,11 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent viewController.titleCurrentFolder = metadata.fileNameView viewController.serverUrl = serverUrlPush - self.navigationController?.pushViewController(viewController, animated: true) + if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView) { + present(UIAlertController.warning(message: fileNameError.errorDescription), animated: true, completion: nil) + } else { + navigationController?.pushViewController(viewController, animated: true) + } } } @@ -265,11 +271,8 @@ extension NCSelect: UICollectionViewDelegate { guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return } if metadata.directory { - pushMetadata(metadata) - } else { - delegate?.dismissSelect(serverUrl: serverUrl, metadata: metadata, type: type, items: items, overwrite: overwrite, copy: false, move: false) self.dismiss(animated: true, completion: nil) } diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 06e277713a..7fd08a4ffa 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1017,6 +1017,7 @@ "_add_audio_" = "Add an external audio"; "_maintenance_mode_" = "Server is currently in maintenance mode"; "_upload_foreground_msg_" = "Do not close %@ to complete the transfer …"; +"_please_rename_file_" = "Please rename the file or folder."; // Tip "_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails."; @@ -1091,10 +1092,10 @@ "_poll_desc_" = "Please complete the log in process in your browser"; // MARK: File name validator -"_file_name_validator_error_upload_invalid_name_" = "Some files could not be uploaded as they contain forbidden names or invalid characters"; -"_file_name_validator_error_file_invalid_name_" = "File or folder name contains forbidden names or invalid characters"; -"_file_name_validator_error_folder_invalid_name_" = "Folder path contains forbidden names or invalid characters"; -"_file_name_validator_error_ends_with_space_period_" = "File or folder name ends with a space or a period"; -"_file_name_validator_error_reserved_name_" = "%@ is a reserved name"; -"_file_name_validator_error_forbidden_file_extension_" = ".%@ is a forbidden file extension"; -"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %@"; +//"_file_name_validator_error_upload_invalid_name_" = "Some files could not be uploaded as they contain forbidden names or invalid characters."; +"_file_name_validator_error_file_invalid_name_" = "Name contains forbidden names or invalid characters."; +"_file_name_validator_error_folder_invalid_name_" = "Folder path contains forbidden names or invalid characters."; +"_file_name_validator_error_ends_with_space_period_" = "Name ends with a space or a period."; +"_file_name_validator_error_reserved_name_" = "%@ is a forbidden name."; +"_file_name_validator_error_forbidden_file_extension_" = ".%@ is a forbidden file extension."; +"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %@."; From 1b8bf9af134f316179a292bd636ece8fd16787f5 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 9 Aug 2024 10:59:24 +0200 Subject: [PATCH 09/16] WIP Signed-off-by: Milen Pivchev --- iOSClient/DeepLink/NCDeepLinkHandler.swift | 11 ++ .../NCCollectionViewCommon+SelectTabBar.swift | 1 + iOSClient/Main/NCActionCenter.swift | 182 +++++++++--------- iOSClient/Main/NCMainTabBar.swift | 12 +- iOSClient/Select/NCSelect.swift | 2 +- 5 files changed, 118 insertions(+), 90 deletions(-) diff --git a/iOSClient/DeepLink/NCDeepLinkHandler.swift b/iOSClient/DeepLink/NCDeepLinkHandler.swift index d45533d0dd..cca6971d63 100644 --- a/iOSClient/DeepLink/NCDeepLinkHandler.swift +++ b/iOSClient/DeepLink/NCDeepLinkHandler.swift @@ -22,6 +22,7 @@ import Foundation import UIKit import SwiftUI +import NextcloudKit enum DeepLink: String { case openFiles // nextcloud://openFiles @@ -109,6 +110,16 @@ class NCDeepLinkHandler { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } controller.selectedIndex = ControllerConstants.filesIndex DispatchQueue.main.asyncAfter(deadline: .now() + 4) { + let serverUrl = controller.currentServerUrl() + let fileFolderPath = NCUtilityFileSystem().getFileNamePath("", serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId) + let fileFolderName = (serverUrl as NSString).lastPathComponent + + if !FileNameValidator.shared.checkFolderPath(folderPath: fileFolderPath) { + controller.present(UIAlertController.warning(message: "\(String(format: NSLocalizedString("_file_name_validator_error_reserved_name_", comment: ""), fileFolderName)) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + + return + } + appDelegate.toggleMenu(controller: controller) } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift index 56fc6d6eed..39e0dcebcc 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift @@ -87,6 +87,7 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { func move() { let metadatas = getSelectedMetadatas() + NCActionCenter.shared.openSelectView(items: metadatas, controller: self.tabBarController as? NCMainTabBarController) setEditMode(false) } diff --git a/iOSClient/Main/NCActionCenter.swift b/iOSClient/Main/NCActionCenter.swift index f2d8e46cb8..36fab8606c 100644 --- a/iOSClient/Main/NCActionCenter.swift +++ b/iOSClient/Main/NCActionCenter.swift @@ -36,17 +36,17 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec NotificationCenter.default.addObserver(instance, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) return instance }() - + var viewerQuickLook: NCViewerQuickLook? var documentController: UIDocumentInteractionController? let utilityFileSystem = NCUtilityFileSystem() let utility = NCUtility() let appDelegate = UIApplication.shared.delegate as? AppDelegate - + // MARK: - Download - + @objc func downloadedFile(_ notification: NSNotification) { - + guard let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let selector = userInfo["selector"] as? String, @@ -54,7 +54,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec let account = userInfo["account"] as? String, account == appDelegate?.account else { return } - + guard error == .success else { // File do not exists on server, remove in local if error.errorCode == NCGlobal.shared.errorResourceNotFound || error.errorCode == NCGlobal.shared.errorBadServerResponse { @@ -69,9 +69,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec return } guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } - + DispatchQueue.main.async { - + // Select UIWindowScene active in serverUrl var controller: NCMainTabBarController? let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } @@ -90,10 +90,10 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } guard let controller else { return } - + switch selector { case NCGlobal.shared.selectorLoadFileQuickLook: - + let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) let fileNameTemp = NSTemporaryDirectory() + metadata.fileNameView let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNameTemp), isEditingEnabled: true, metadata: metadata) @@ -112,9 +112,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec self.utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNameTemp) controller.present(viewerQuickLook, animated: true) } - + case NCGlobal.shared.selectorLoadFileView: - + guard UIApplication.shared.applicationState == .active else { return } if metadata.contentType.contains("opendocument") && !self.utility.isTypeFileRichDocument(metadata) { self.openDocumentController(metadata: metadata, controller: controller) @@ -126,31 +126,31 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon) } } - + case NCGlobal.shared.selectorOpenIn: - + if UIApplication.shared.applicationState == .active { self.openDocumentController(metadata: metadata, controller: controller) } - + case NCGlobal.shared.selectorSaveAlbum: - + self.saveAlbum(metadata: metadata, controller: controller) - + case NCGlobal.shared.selectorSaveAsScan: - + self.saveAsScan(metadata: metadata, controller: controller) - + case NCGlobal.shared.selectorOpenDetail: NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId]) - + default: let applicationHandle = NCApplicationHandle() applicationHandle.downloadedFile(selector: selector, metadata: metadata) } } } - + func setMetadataAvalableOffline(_ metadata: tableMetadata, isOffline: Bool) { let serverUrl = metadata.serverUrl + "/" + metadata.fileName if isOffline { @@ -177,12 +177,12 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec selector: NCGlobal.shared.selectorSynchronizationOffline) } } - + func viewerFile(account: String, fileId: String, viewController: UIViewController) { - + guard let hudView = viewController.tabBarController?.view else { return } var downloadRequest: DownloadRequest? - + if let metadata = NCManageDatabase.shared.getMetadataFromFileId(fileId) { do { let attr = try FileManager.default.attributesOfItem(atPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) @@ -195,7 +195,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec print("Error: \(error)") } } - + let hud = JGProgressHUD() hud.indicatorView = JGProgressHUDRingIndicatorView() if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView { @@ -208,21 +208,21 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } hud.show(in: hudView) - + NextcloudKit.shared.getFileFromFileId(fileId: fileId, account: account) { account, file, _, error in - + hud.dismiss() if error != .success { NCContentPresenter().showError(error: error) } else if let file = file { - + let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) NCManageDatabase.shared.addMetadata(metadata) - + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) - + if metadata.isAudioOrVideo { NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) } else { @@ -246,56 +246,56 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Upload - + @objc func uploadedFile(_ notification: NSNotification) { guard let userInfo = notification.userInfo as NSDictionary?, let error = userInfo["error"] as? NKError, let account = userInfo["account"] as? String, account == self.appDelegate?.account else { return } - + if error != .success, error.errorCode != NSURLErrorCancelled, error.errorCode != NCGlobal.shared.errorRequestExplicityCancelled { NCContentPresenter().messageNotification("_upload_file_", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, priority: .max) } } - + // MARK: - - + func openShare(viewController: UIViewController, metadata: tableMetadata, page: NCBrandOptions.NCInfoPagingTab) { - + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName var page = page - + NCActivityIndicator.shared.start(backgroundView: viewController.view) NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account, queue: .main) { _, metadata, error in - + NCActivityIndicator.shared.stop() - + if let metadata = metadata, error == .success { - + var pages: [NCBrandOptions.NCInfoPagingTab] = [] - + let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as? UINavigationController let shareViewController = shareNavigationController?.topViewController as? NCSharePaging - + for value in NCBrandOptions.NCInfoPagingTab.allCases { pages.append(value) } - + if NCGlobal.shared.capabilityActivity.isEmpty, let idx = pages.firstIndex(of: .activity) { pages.remove(at: idx) } if !metadata.isSharable(), let idx = pages.firstIndex(of: .sharing) { pages.remove(at: idx) } - + (pages, page) = NCApplicationHandle().filterPages(pages: pages, page: page, metadata: metadata) - + shareViewController?.pages = pages shareViewController?.metadata = metadata - + if pages.contains(page) { shareViewController?.page = page } else if let page = pages.first { @@ -303,7 +303,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } else { return } - + shareNavigationController?.modalPresentationStyle = .formSheet if let shareNavigationController = shareNavigationController { viewController.present(shareNavigationController, animated: true, completion: nil) @@ -311,26 +311,26 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Open in ... - + func openDocumentController(metadata: tableMetadata, controller: NCMainTabBarController?) { - + guard let mainTabBarController = controller, let mainTabBar = mainTabBarController.tabBar as? NCMainTabBar else { return } let fileURL = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) - + documentController = UIDocumentInteractionController(url: fileURL) documentController?.presentOptionsMenu(from: mainTabBar.menuRect, in: mainTabBar, animated: true) } - + func openActivityViewController(selectedMetadata: [tableMetadata], controller: NCMainTabBarController?) { guard let controller, let mainTabBar = controller.tabBar as? NCMainTabBar else { return } let metadatas = selectedMetadata.filter({ !$0.directory }) var items: [URL] = [] var downloadMetadata: [(tableMetadata, URL)] = [] - + for metadata in metadatas { let fileURL = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) if utilityFileSystem.fileProviderStorageExists(metadata) { @@ -339,7 +339,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec downloadMetadata.append((metadata, fileURL)) } } - + let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadata.count, hudView: controller.view) for (metadata, url) in downloadMetadata { processor.execute { completion in @@ -356,7 +356,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + processor.completeWork { guard !items.isEmpty else { return } let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil) @@ -366,16 +366,16 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec controller.present(activityViewController, animated: true) } } - + // MARK: - Save as scan - + func saveAsScan(metadata: tableMetadata, controller: NCMainTabBarController?) { let fileNamePath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) let fileNameDestination = utilityFileSystem.createFileName("scan.png", fileDate: Date(), fileType: PHAssetMediaType.image, notUseMask: true) let fileNamePathDestination = utilityFileSystem.directoryScan + "/" + fileNameDestination - + utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNamePathDestination) - + if let navigationController = UIStoryboard(name: "NCScan", bundle: nil).instantiateInitialViewController() { navigationController.modalPresentationStyle = UIModalPresentationStyle.pageSheet let viewController = navigationController.presentedViewController as? NCScan @@ -383,20 +383,20 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec controller?.present(navigationController, animated: true, completion: nil) } } - + // MARK: - Save photo - + func saveAlbum(metadata: tableMetadata, controller: NCMainTabBarController?) { let fileNamePath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) - + NCAskAuthorization().askAuthorizationPhotoLibrary(viewController: controller) { hasPermission in guard hasPermission else { let error = NKError(errorCode: NCGlobal.shared.errorFileNotSaved, errorDescription: "_access_photo_not_enabled_msg_") return NCContentPresenter().messageNotification("_access_photo_not_enabled_", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error) } - + let errorSave = NKError(errorCode: NCGlobal.shared.errorFileNotSaved, errorDescription: "_file_not_saved_cameraroll_") - + do { if metadata.isImage { let data = try Data(contentsOf: URL(fileURLWithPath: fileNamePath)) @@ -425,13 +425,13 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Copy & Paste - + func pastePasteboard(serverUrl: String, account: String, hudView: UIView?) { var fractionCompleted: Float = 0 let processor = ParallelWorker(n: 5, titleKey: "_uploading_", totalTasks: nil, hudView: hudView) - + func uploadPastePasteboard(fileName: String, serverUrlFileName: String, fileNameLocalPath: String, serverUrl: String, completion: @escaping () -> Void) { NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: account) { request in NCNetworking.shared.uploadRequest[fileNameLocalPath] = request @@ -456,7 +456,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec completion() } } - + for (index, items) in UIPasteboard.general.items.enumerated() { for item in items { let results = NextcloudKit.shared.nkCommonInstance.getFileProperties(inUTI: item.key as CFString) @@ -475,16 +475,16 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } processor.completeWork() } - + // MARK: - - + func openFileViewInFolder(serverUrl: String, fileNameBlink: String?, fileNameOpen: String?, sceneIdentifier: String) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let controller = SceneManager.shared.getController(sceneIdentifier: sceneIdentifier), let navigationController = controller.viewControllers?.first as? UINavigationController else { return } var serverUrlPush = self.utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) - + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { navigationController.popToRootViewController(animated: false) controller.selectedIndex = 0 @@ -494,15 +494,15 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController.openFile(fileName: fileNameOpen) return } - + let diffDirectory = serverUrl.replacingOccurrences(of: serverUrlPush, with: "") var subDirs = diffDirectory.split(separator: "/") - + while serverUrlPush != serverUrl, !subDirs.isEmpty { - + guard let dir = subDirs.first else { return } serverUrlPush = serverUrlPush + "/" + dir - + if let viewController = controller.navigationCollectionViewCommon.first(where: { $0.navigationController == navigationController && $0.serverUrl == serverUrlPush})?.viewController as? NCFiles, viewController.isViewLoaded { viewController.fileNameBlink = fileNameBlink viewController.fileNameOpen = fileNameOpen @@ -513,9 +513,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController.serverUrl = serverUrlPush viewController.titleCurrentFolder = String(dir) viewController.navigationItem.backButtonTitle = viewController.titleCurrentFolder - + controller.navigationCollectionViewCommon.append(NavigationCollectionViewCommon(serverUrl: serverUrlPush, navigationController: navigationController, viewController: viewController)) - + if serverUrlPush == serverUrl { viewController.fileNameBlink = fileNameBlink viewController.fileNameOpen = fileNameOpen @@ -527,9 +527,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - NCSelect + Delegate - + func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { if let serverUrl, !items.isEmpty { if copy { @@ -565,22 +565,28 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + func openSelectView(items: [tableMetadata], controller: NCMainTabBarController?) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - + let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as? UINavigationController let topViewController = navigationController?.topViewController as? NCSelect var listViewController = [NCSelect]() - + var copyItems: [tableMetadata] = [] for item in items { + if let fileNameError = FileNameValidator.shared.checkFileName(item.fileNameView) { + controller?.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + + return + } + copyItems.append(item) } - + let homeUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) var serverUrl = copyItems[0].serverUrl - + // Setup view controllers such that the current view is of the same directory the items to be copied are in while true { // If not in the topmost directory, create a new view controller and set correct title. @@ -596,15 +602,15 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController = topViewController } guard let vc = viewController else { return } - + vc.delegate = self vc.typeOfCommandView = .copyMove vc.items = copyItems vc.serverUrl = serverUrl - + vc.navigationItem.backButtonTitle = vc.titleCurrentFolder listViewController.insert(vc, at: 0) - + if serverUrl != homeUrl { if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) { serverUrl = path @@ -613,10 +619,10 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec break } } - + navigationController?.setViewControllers(listViewController, animated: false) navigationController?.modalPresentationStyle = .formSheet - + if let navigationController = navigationController { controller?.present(navigationController, animated: true, completion: nil) } diff --git a/iOSClient/Main/NCMainTabBar.swift b/iOSClient/Main/NCMainTabBar.swift index 5aa20d18d6..b1449cf3a4 100644 --- a/iOSClient/Main/NCMainTabBar.swift +++ b/iOSClient/Main/NCMainTabBar.swift @@ -180,7 +180,7 @@ class NCMainTabBar: UITabBar { centerButton.layer.shadowOffset = CGSize(width: 0, height: 0) centerButton.layer.shadowRadius = 3.0 centerButton.layer.shadowOpacity = 0.5 - centerButton.action(for: .touchUpInside) { _ in + centerButton.action(for: .touchUpInside) { [self] _ in if let controller = self.window?.rootViewController as? NCMainTabBarController { let serverUrl = controller.currentServerUrl() @@ -191,6 +191,16 @@ class NCMainTabBar: UITabBar { return } } + + let fileFolderPath = NCUtilityFileSystem().getFileNamePath("", serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId) + let fileFolderName = (serverUrl as NSString).lastPathComponent + + if !FileNameValidator.shared.checkFolderPath(folderPath: fileFolderPath) { + controller.present(UIAlertController.warning(message: "\(String(format: NSLocalizedString("_file_name_validator_error_reserved_name_", comment: ""), fileFolderName)) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) + + return + } + self.appDelegate.toggleMenu(controller: controller) } } diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index bb706f0d3f..8fa401cc23 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -255,7 +255,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent viewController.serverUrl = serverUrlPush if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView) { - present(UIAlertController.warning(message: fileNameError.errorDescription), animated: true, completion: nil) + present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) } else { navigationController?.pushViewController(viewController, animated: true) } From 3040b59175b120ad13ab8a5f2c9d572728e76090 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 9 Aug 2024 11:58:52 +0200 Subject: [PATCH 10/16] Scanner Signed-off-by: Milen Pivchev --- iOSClient/Main/NCActionCenter.swift | 180 +++++++++--------- .../Scan document/NCUploadScanDocument.swift | 15 +- 2 files changed, 103 insertions(+), 92 deletions(-) diff --git a/iOSClient/Main/NCActionCenter.swift b/iOSClient/Main/NCActionCenter.swift index 36fab8606c..1772f8d4b5 100644 --- a/iOSClient/Main/NCActionCenter.swift +++ b/iOSClient/Main/NCActionCenter.swift @@ -36,17 +36,17 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec NotificationCenter.default.addObserver(instance, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) return instance }() - + var viewerQuickLook: NCViewerQuickLook? var documentController: UIDocumentInteractionController? let utilityFileSystem = NCUtilityFileSystem() let utility = NCUtility() let appDelegate = UIApplication.shared.delegate as? AppDelegate - + // MARK: - Download - + @objc func downloadedFile(_ notification: NSNotification) { - + guard let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let selector = userInfo["selector"] as? String, @@ -54,7 +54,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec let account = userInfo["account"] as? String, account == appDelegate?.account else { return } - + guard error == .success else { // File do not exists on server, remove in local if error.errorCode == NCGlobal.shared.errorResourceNotFound || error.errorCode == NCGlobal.shared.errorBadServerResponse { @@ -69,9 +69,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec return } guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } - + DispatchQueue.main.async { - + // Select UIWindowScene active in serverUrl var controller: NCMainTabBarController? let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } @@ -90,10 +90,10 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } guard let controller else { return } - + switch selector { case NCGlobal.shared.selectorLoadFileQuickLook: - + let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) let fileNameTemp = NSTemporaryDirectory() + metadata.fileNameView let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNameTemp), isEditingEnabled: true, metadata: metadata) @@ -112,9 +112,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec self.utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNameTemp) controller.present(viewerQuickLook, animated: true) } - + case NCGlobal.shared.selectorLoadFileView: - + guard UIApplication.shared.applicationState == .active else { return } if metadata.contentType.contains("opendocument") && !self.utility.isTypeFileRichDocument(metadata) { self.openDocumentController(metadata: metadata, controller: controller) @@ -126,31 +126,31 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon) } } - + case NCGlobal.shared.selectorOpenIn: - + if UIApplication.shared.applicationState == .active { self.openDocumentController(metadata: metadata, controller: controller) } - + case NCGlobal.shared.selectorSaveAlbum: - + self.saveAlbum(metadata: metadata, controller: controller) - + case NCGlobal.shared.selectorSaveAsScan: - + self.saveAsScan(metadata: metadata, controller: controller) - + case NCGlobal.shared.selectorOpenDetail: NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId]) - + default: let applicationHandle = NCApplicationHandle() applicationHandle.downloadedFile(selector: selector, metadata: metadata) } } } - + func setMetadataAvalableOffline(_ metadata: tableMetadata, isOffline: Bool) { let serverUrl = metadata.serverUrl + "/" + metadata.fileName if isOffline { @@ -177,12 +177,12 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec selector: NCGlobal.shared.selectorSynchronizationOffline) } } - + func viewerFile(account: String, fileId: String, viewController: UIViewController) { - + guard let hudView = viewController.tabBarController?.view else { return } var downloadRequest: DownloadRequest? - + if let metadata = NCManageDatabase.shared.getMetadataFromFileId(fileId) { do { let attr = try FileManager.default.attributesOfItem(atPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) @@ -195,7 +195,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec print("Error: \(error)") } } - + let hud = JGProgressHUD() hud.indicatorView = JGProgressHUDRingIndicatorView() if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView { @@ -208,21 +208,21 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } hud.show(in: hudView) - + NextcloudKit.shared.getFileFromFileId(fileId: fileId, account: account) { account, file, _, error in - + hud.dismiss() if error != .success { NCContentPresenter().showError(error: error) } else if let file = file { - + let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) NCManageDatabase.shared.addMetadata(metadata) - + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) - + if metadata.isAudioOrVideo { NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) } else { @@ -246,56 +246,56 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Upload - + @objc func uploadedFile(_ notification: NSNotification) { guard let userInfo = notification.userInfo as NSDictionary?, let error = userInfo["error"] as? NKError, let account = userInfo["account"] as? String, account == self.appDelegate?.account else { return } - + if error != .success, error.errorCode != NSURLErrorCancelled, error.errorCode != NCGlobal.shared.errorRequestExplicityCancelled { NCContentPresenter().messageNotification("_upload_file_", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, priority: .max) } } - + // MARK: - - + func openShare(viewController: UIViewController, metadata: tableMetadata, page: NCBrandOptions.NCInfoPagingTab) { - + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName var page = page - + NCActivityIndicator.shared.start(backgroundView: viewController.view) NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account, queue: .main) { _, metadata, error in - + NCActivityIndicator.shared.stop() - + if let metadata = metadata, error == .success { - + var pages: [NCBrandOptions.NCInfoPagingTab] = [] - + let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as? UINavigationController let shareViewController = shareNavigationController?.topViewController as? NCSharePaging - + for value in NCBrandOptions.NCInfoPagingTab.allCases { pages.append(value) } - + if NCGlobal.shared.capabilityActivity.isEmpty, let idx = pages.firstIndex(of: .activity) { pages.remove(at: idx) } if !metadata.isSharable(), let idx = pages.firstIndex(of: .sharing) { pages.remove(at: idx) } - + (pages, page) = NCApplicationHandle().filterPages(pages: pages, page: page, metadata: metadata) - + shareViewController?.pages = pages shareViewController?.metadata = metadata - + if pages.contains(page) { shareViewController?.page = page } else if let page = pages.first { @@ -303,7 +303,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } else { return } - + shareNavigationController?.modalPresentationStyle = .formSheet if let shareNavigationController = shareNavigationController { viewController.present(shareNavigationController, animated: true, completion: nil) @@ -311,26 +311,26 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Open in ... - + func openDocumentController(metadata: tableMetadata, controller: NCMainTabBarController?) { - + guard let mainTabBarController = controller, let mainTabBar = mainTabBarController.tabBar as? NCMainTabBar else { return } let fileURL = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) - + documentController = UIDocumentInteractionController(url: fileURL) documentController?.presentOptionsMenu(from: mainTabBar.menuRect, in: mainTabBar, animated: true) } - + func openActivityViewController(selectedMetadata: [tableMetadata], controller: NCMainTabBarController?) { guard let controller, let mainTabBar = controller.tabBar as? NCMainTabBar else { return } let metadatas = selectedMetadata.filter({ !$0.directory }) var items: [URL] = [] var downloadMetadata: [(tableMetadata, URL)] = [] - + for metadata in metadatas { let fileURL = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) if utilityFileSystem.fileProviderStorageExists(metadata) { @@ -339,7 +339,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec downloadMetadata.append((metadata, fileURL)) } } - + let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadata.count, hudView: controller.view) for (metadata, url) in downloadMetadata { processor.execute { completion in @@ -356,7 +356,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + processor.completeWork { guard !items.isEmpty else { return } let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil) @@ -366,16 +366,16 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec controller.present(activityViewController, animated: true) } } - + // MARK: - Save as scan - + func saveAsScan(metadata: tableMetadata, controller: NCMainTabBarController?) { let fileNamePath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) let fileNameDestination = utilityFileSystem.createFileName("scan.png", fileDate: Date(), fileType: PHAssetMediaType.image, notUseMask: true) let fileNamePathDestination = utilityFileSystem.directoryScan + "/" + fileNameDestination - + utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNamePathDestination) - + if let navigationController = UIStoryboard(name: "NCScan", bundle: nil).instantiateInitialViewController() { navigationController.modalPresentationStyle = UIModalPresentationStyle.pageSheet let viewController = navigationController.presentedViewController as? NCScan @@ -383,20 +383,20 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec controller?.present(navigationController, animated: true, completion: nil) } } - + // MARK: - Save photo - + func saveAlbum(metadata: tableMetadata, controller: NCMainTabBarController?) { let fileNamePath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) - + NCAskAuthorization().askAuthorizationPhotoLibrary(viewController: controller) { hasPermission in guard hasPermission else { let error = NKError(errorCode: NCGlobal.shared.errorFileNotSaved, errorDescription: "_access_photo_not_enabled_msg_") return NCContentPresenter().messageNotification("_access_photo_not_enabled_", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error) } - + let errorSave = NKError(errorCode: NCGlobal.shared.errorFileNotSaved, errorDescription: "_file_not_saved_cameraroll_") - + do { if metadata.isImage { let data = try Data(contentsOf: URL(fileURLWithPath: fileNamePath)) @@ -425,13 +425,13 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - Copy & Paste - + func pastePasteboard(serverUrl: String, account: String, hudView: UIView?) { var fractionCompleted: Float = 0 let processor = ParallelWorker(n: 5, titleKey: "_uploading_", totalTasks: nil, hudView: hudView) - + func uploadPastePasteboard(fileName: String, serverUrlFileName: String, fileNameLocalPath: String, serverUrl: String, completion: @escaping () -> Void) { NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: account) { request in NCNetworking.shared.uploadRequest[fileNameLocalPath] = request @@ -456,7 +456,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec completion() } } - + for (index, items) in UIPasteboard.general.items.enumerated() { for item in items { let results = NextcloudKit.shared.nkCommonInstance.getFileProperties(inUTI: item.key as CFString) @@ -475,16 +475,16 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } processor.completeWork() } - + // MARK: - - + func openFileViewInFolder(serverUrl: String, fileNameBlink: String?, fileNameOpen: String?, sceneIdentifier: String) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let controller = SceneManager.shared.getController(sceneIdentifier: sceneIdentifier), let navigationController = controller.viewControllers?.first as? UINavigationController else { return } var serverUrlPush = self.utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) - + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { navigationController.popToRootViewController(animated: false) controller.selectedIndex = 0 @@ -494,15 +494,15 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController.openFile(fileName: fileNameOpen) return } - + let diffDirectory = serverUrl.replacingOccurrences(of: serverUrlPush, with: "") var subDirs = diffDirectory.split(separator: "/") - + while serverUrlPush != serverUrl, !subDirs.isEmpty { - + guard let dir = subDirs.first else { return } serverUrlPush = serverUrlPush + "/" + dir - + if let viewController = controller.navigationCollectionViewCommon.first(where: { $0.navigationController == navigationController && $0.serverUrl == serverUrlPush})?.viewController as? NCFiles, viewController.isViewLoaded { viewController.fileNameBlink = fileNameBlink viewController.fileNameOpen = fileNameOpen @@ -513,9 +513,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController.serverUrl = serverUrlPush viewController.titleCurrentFolder = String(dir) viewController.navigationItem.backButtonTitle = viewController.titleCurrentFolder - + controller.navigationCollectionViewCommon.append(NavigationCollectionViewCommon(serverUrl: serverUrlPush, navigationController: navigationController, viewController: viewController)) - + if serverUrlPush == serverUrl { viewController.fileNameBlink = fileNameBlink viewController.fileNameOpen = fileNameOpen @@ -527,9 +527,9 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + // MARK: - NCSelect + Delegate - + func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { if let serverUrl, !items.isEmpty { if copy { @@ -565,28 +565,28 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } - + func openSelectView(items: [tableMetadata], controller: NCMainTabBarController?) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - + let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as? UINavigationController let topViewController = navigationController?.topViewController as? NCSelect var listViewController = [NCSelect]() - + var copyItems: [tableMetadata] = [] for item in items { if let fileNameError = FileNameValidator.shared.checkFileName(item.fileNameView) { controller?.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true) - + return } - + copyItems.append(item) } - + let homeUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) var serverUrl = copyItems[0].serverUrl - + // Setup view controllers such that the current view is of the same directory the items to be copied are in while true { // If not in the topmost directory, create a new view controller and set correct title. @@ -602,15 +602,15 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec viewController = topViewController } guard let vc = viewController else { return } - + vc.delegate = self vc.typeOfCommandView = .copyMove vc.items = copyItems vc.serverUrl = serverUrl - + vc.navigationItem.backButtonTitle = vc.titleCurrentFolder listViewController.insert(vc, at: 0) - + if serverUrl != homeUrl { if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) { serverUrl = path @@ -619,10 +619,10 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec break } } - + navigationController?.setViewControllers(listViewController, animated: false) navigationController?.modalPresentationStyle = .formSheet - + if let navigationController = navigationController { controller?.present(navigationController, animated: true, completion: nil) } diff --git a/iOSClient/Scan document/NCUploadScanDocument.swift b/iOSClient/Scan document/NCUploadScanDocument.swift index 47459f55b6..3f0d2cd88c 100644 --- a/iOSClient/Scan document/NCUploadScanDocument.swift +++ b/iOSClient/Scan document/NCUploadScanDocument.swift @@ -321,6 +321,7 @@ extension NCUploadScanDocument: NCCreateFormUploadConflictDelegate { struct UploadScanDocumentView: View { @State var fileName = NCUtilityFileSystem().createFileNameDate("scan", ext: "") + @State var footer = "" @State var password: String = "" @State var isSecuredPassword: Bool = true @State var isTextRecognition: Bool = NCKeychain().textRecognitionStatus @@ -349,7 +350,7 @@ struct UploadScanDocumentView: View { GeometryReader { geo in ZStack(alignment: .top) { List { - Section(header: Text(NSLocalizedString("_file_creation_", comment: ""))) { + Section(header: Text(NSLocalizedString("_file_creation_", comment: "")), footer: Text(footer)) { HStack { Label { if NCUtilityFileSystem().getHomeServer(urlBase: uploadScanDocument.userBaseUrl.urlBase, userId: uploadScanDocument.userBaseUrl.userId) == uploadScanDocument.serverUrl { @@ -383,6 +384,14 @@ struct UploadScanDocumentView: View { TextField(NSLocalizedString("_enter_filename_", comment: ""), text: $fileName) .modifier(TextFieldClearButton(text: $fileName)) .multilineTextAlignment(.trailing) + .onChange(of: fileName) { _ in + if let fileNameError = FileNameValidator.shared.checkFileName(fileName) { + footer = fileNameError.errorDescription + } else { + footer = "" + + } + } } HStack { Group { @@ -414,6 +423,7 @@ struct UploadScanDocumentView: View { .complexModifier { view in view.listRowSeparator(.hidden) } + VStack(spacing: 20) { Toggle(NSLocalizedString("_delete_all_scanned_images_", comment: ""), isOn: $removeAllFiles) .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brandElement))) @@ -436,7 +446,8 @@ struct UploadScanDocumentView: View { } } } - .buttonStyle(ButtonRounded(disabled: fileName.isEmpty)) + .buttonStyle(ButtonRounded(disabled: fileName.isEmpty || !footer.isEmpty)) + .disabled(fileName.isEmpty || !footer.isEmpty) } Section(header: Text(NSLocalizedString("_quality_image_title_", comment: ""))) { From 79493512f73cea5bb878737cd0bb93032e2a07f2 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 16 Aug 2024 15:26:41 +0200 Subject: [PATCH 11/16] WIP Signed-off-by: Milen Pivchev --- iOSClient/Supporting Files/en.lproj/Localizable.strings | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 7fd08a4ffa..a2e1e9ad84 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1092,10 +1092,7 @@ "_poll_desc_" = "Please complete the log in process in your browser"; // MARK: File name validator -//"_file_name_validator_error_upload_invalid_name_" = "Some files could not be uploaded as they contain forbidden names or invalid characters."; -"_file_name_validator_error_file_invalid_name_" = "Name contains forbidden names or invalid characters."; -"_file_name_validator_error_folder_invalid_name_" = "Folder path contains forbidden names or invalid characters."; "_file_name_validator_error_ends_with_space_period_" = "Name ends with a space or a period."; "_file_name_validator_error_reserved_name_" = "%@ is a forbidden name."; "_file_name_validator_error_forbidden_file_extension_" = ".%@ is a forbidden file extension."; -"_file_name_validator_error_invalid_character_" = "File or folder name contains an invalid character: %@."; +"_file_name_validator_error_invalid_character_" = "Name contains an invalid character: %@."; From 4f438c727550966c3aa92c21bffccb73cbe50be8 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Mon, 19 Aug 2024 16:10:48 +0200 Subject: [PATCH 12/16] WIP Signed-off-by: Milen Pivchev --- iOSClient/Supporting Files/en.lproj/Localizable.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 1e6f05869d..6e32df5c89 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1094,6 +1094,6 @@ // MARK: File name validator "_file_name_validator_error_ends_with_space_period_" = "Name ends with a space or a period."; -"_file_name_validator_error_reserved_name_" = "%@ is a forbidden name."; -"_file_name_validator_error_forbidden_file_extension_" = ".%@ is a forbidden file extension."; -"_file_name_validator_error_invalid_character_" = "Name contains an invalid character: %@."; +"_file_name_validator_error_reserved_name_" = "\"%@\" is a forbidden name."; +"_file_name_validator_error_forbidden_file_extension_" = "\".%@\" is a forbidden file extension."; +"_file_name_validator_error_invalid_character_" = "Name contains an invalid character: \"%@\"."; From aa26f659678f45ddca236b09d9b81795f3423903 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 20 Aug 2024 10:55:14 +0200 Subject: [PATCH 13/16] Add asset upload check Signed-off-by: Milen Pivchev --- .../UIAlertController+Extension.swift | 2 +- .../Upload Assets/NCUploadAssetsView.swift | 30 +++++++++++++++---- .../en.lproj/Localizable.strings | 14 ++++----- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 495e848e0e..3c486073a9 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -153,7 +153,7 @@ extension UIAlertController { return alertController } - static func renameFile(metadata: tableMetadata, indexPath: IndexPath, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController { + static func renameFile(metadata: tableMetadata, indexPath: IndexPath) -> UIAlertController { let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in diff --git a/iOSClient/Main/Create cloud/Upload Assets/NCUploadAssetsView.swift b/iOSClient/Main/Create cloud/Upload Assets/NCUploadAssetsView.swift index 12079dcefa..3f8bd26a23 100644 --- a/iOSClient/Main/Create cloud/Upload Assets/NCUploadAssetsView.swift +++ b/iOSClient/Main/Create cloud/Upload Assets/NCUploadAssetsView.swift @@ -7,13 +7,15 @@ // import SwiftUI +import NextcloudKit struct NCUploadAssetsView: View { @ObservedObject var model: NCUploadAssetsModel @State private var showSelect = false @State private var showUploadConflict = false @State private var showQuickLook = false - @State private var shorRenameAlert = false + @State private var showRenameAlert = false + @State private var renameError = "" @State private var renameFileName: String = "" @State private var renameIndex: Int = 0 @State private var index: Int = 0 @@ -39,7 +41,7 @@ struct NCUploadAssetsView: View { Button(action: { renameFileName = model.previewStore[index].fileName renameIndex = index - shorRenameAlert = true + showRenameAlert = true }) { Label(NSLocalizedString("_rename_", comment: ""), systemImage: "pencil") } @@ -84,21 +86,37 @@ struct NCUploadAssetsView: View { } } label: { ImageAsset(model: model, index: index) - .alert(NSLocalizedString("_rename_file_", comment: ""), isPresented: $shorRenameAlert) { - TextField(NSLocalizedString("_enter_filename_", comment: ""), text: $renameFileName) + .alert(NSLocalizedString("_rename_", comment: ""), isPresented: $showRenameAlert) { + TextField("", text: $renameFileName) .autocapitalization(.none) .autocorrectionDisabled() + Button(NSLocalizedString("_rename_", comment: ""), action: { - model.previewStore[renameIndex].fileName = renameFileName.trimmingCharacters(in: .whitespacesAndNewlines) + if !renameError.isEmpty { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + showRenameAlert = true + } + } else { + model.previewStore[renameIndex].fileName = renameFileName.trimmingCharacters(in: .whitespacesAndNewlines) + } }) + Button(NSLocalizedString("_cancel_", comment: ""), role: .cancel, action: {}) + } message: { + Text(renameError) } } + .onChange(of: renameFileName) { newValue in + if let error = FileNameValidator.shared.checkFileName(newValue) { + renameError = error.errorDescription + } else { + renameError = "" + } + } } } } } - // .redacted(reason: uploadAssets.previewStore.isEmpty ? .placeholder : []) Section { Toggle(isOn: $model.useAutoUploadFolder, label: { diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 6e32df5c89..9f5aed61d0 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -944,13 +944,13 @@ "_calendar_contacts_footer_" = "After downloading the profile you can install it from Settings."; "_preview_" = "Preview"; "_crop_" = "Crop"; -"_modify_image_desc_" = "Tap the image for modify"; -"_message_disable_livephoto_" = "This image is a Live Photo, changing it will lose the Live effect"; +"_modify_image_desc_" = "Tap on a file to modify or rename."; +"_message_disable_livephoto_" = "This image is a Live Photo, changing it will lose the Live Photo effect"; "_enable_livephoto_" = "Enable Live Photo"; "_disable_livephoto_" = "Disable Live Photo"; -"_undo_modify_" = "Undo the modify"; -"_unauthorizedFilesPasscode_" = "Files cannot be used with an activated passcode"; -"_disableFilesApp_" = "Files cannot be used because it is disabled"; +"_undo_modify_" = "Undo modifying"; +"_unauthorizedFilesPasscode_" = "Files app cannot be used with an activated passcode"; +"_disableFilesApp_" = "Files app cannot be used because it is disabled"; "_reset_application_done_" = "Reset application, done."; "_rename_already_exists_" = "A file with this name already exists"; "_created_" = "Created"; @@ -985,7 +985,7 @@ "_delete_selected_photos_" = "Delete selected photos"; "_media_square_" = "Square grid"; "_media_ratio_" = "Aspect ratio grid"; -"_autoupload_notice_" = "To ensure the proper functioning of the application, it is necessary to enable Background App Refresh. Otherwise, new photos or videos will not be detected when the application is in the background.\n\nAdditionally, please note that the application will not be able to detect new photos and videos if it is manually terminated. When the app is in the background, data transfer may be slower, and new photos and/or videos will generally be detected every 10 minutes, depending on the device’s battery level.\n\nTo verify that the app is functioning correctly, you can use the log file available in Advanced."; +"_autoupload_notice_" = "To ensure the proper functioning of the application, it is necessary to enable Background App Refresh. Otherwise, new photos or videos will not be detected when the application is in the background.\n\nAdditionally, please note that the application will not be able to detect new photos and videos if it is manually terminated. When the app is in the background, data transfer may be slower, and new photos and/or videos will generally be detected every 10 minutes, depending on the device’s battery level.\n\nTo verify that the app is functioning correctly, you can use the log file available in Advanced settings."; "_display_" = "Display"; "_appearance_" = "Appearance"; "_light_" = "Light"; @@ -1018,7 +1018,6 @@ "_add_audio_" = "Add an external audio"; "_maintenance_mode_" = "Server is currently in maintenance mode"; "_upload_foreground_msg_" = "Do not close %@ to complete the transfer …"; -"_please_rename_file_" = "Please rename the file or folder."; // Tip "_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails."; @@ -1097,3 +1096,4 @@ "_file_name_validator_error_reserved_name_" = "\"%@\" is a forbidden name."; "_file_name_validator_error_forbidden_file_extension_" = "\".%@\" is a forbidden file extension."; "_file_name_validator_error_invalid_character_" = "Name contains an invalid character: \"%@\"."; +"_please_rename_file_" = "Please rename the file or folder."; From a92010553b24666c5108911c17db5bb86e400457 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 20 Aug 2024 17:17:36 +0200 Subject: [PATCH 14/16] WIP Signed-off-by: Milen Pivchev --- Share/NCShareExtension+DataSource.swift | 6 +-- Share/NCShareExtension+NCDelegate.swift | 28 +++++++++++ .../UIAlertController+Extension.swift | 47 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index 7eb9a71ab6..50b6d2c2ed 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -89,7 +89,7 @@ extension NCShareExtension: UICollectionViewDataSource { return UICollectionViewCell() } -// cell.listCellDelegate = self + cell.listCellDelegate = self cell.fileObjectId = metadata.ocId cell.indexPath = indexPath @@ -181,7 +181,7 @@ extension NCShareExtension: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard !uploadStarted else { return } let fileName = filesName[indexPath.row] -// renameFile(named: fileName) + renameFile(named: fileName) } } @@ -195,7 +195,7 @@ extension NCShareExtension: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NCShareCell else { return UITableViewCell() } let fileName = filesName[indexPath.row] cell.setup(fileName: fileName) -// cell.delegate = self + cell.delegate = self return cell } } diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index f59784af64..036a9641d4 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -115,3 +115,31 @@ extension NCShareExtension: NCCreateFormUploadConflictDelegate { self.upload() } } + +extension NCShareExtension: NCShareCellDelegate, NCListCellDelegate { + + func removeFile(named fileName: String) { + guard let index = self.filesName.firstIndex(of: fileName) else { + return showAlert(title: "_file_not_found_", description: fileName) + } + self.filesName.remove(at: index) + if self.filesName.isEmpty { + cancel(with: NCShareExtensionError.noFiles) + } else { + self.setCommandView() + } + } + + func renameFile(named fileName: String) { + let alert = UIAlertController.renameFile(fileName: fileName) { [self] newFileName in + guard let fileIx = self.filesName.firstIndex(of: fileName), + !self.filesName.contains(newFileName), + utilityFileSystem.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + newFileName)) else { + return showAlert(title: "_single_file_conflict_title_", description: "'\(fileName)' -> '\(newFileName)'") + } + + filesName[fileIx] = newFileName + tableView.reloadData() + } + } +} diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index 3c486073a9..97357bb94b 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -153,6 +153,53 @@ extension UIAlertController { return alertController } + static func renameFile(fileName: String, completion: @escaping (_ newFileName: String) -> Void) -> UIAlertController { + let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) + + let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in + guard let newFileName = alertController.textFields?.first?.text else { return } + + completion(newFileName) + }) + + // text field is initially empty, no action + okAction.isEnabled = false + let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) + + alertController.addTextField { textField in + textField.text = fileName + textField.autocapitalizationType = .words + } + + // only allow saving if folder name exists + NotificationCenter.default.addObserver( + forName: UITextField.textDidBeginEditingNotification, + object: alertController.textFields?.first, + queue: .main) { _ in + guard let textField = alertController.textFields?.first else { return } + + if let start = textField.position(from: textField.beginningOfDocument, offset: 0), + let end = textField.position(from: start, offset: textField.text?.withRemovedFileExtension.count ?? 0) { + textField.selectedTextRange = textField.textRange(from: start, to: end) + } + } + + NotificationCenter.default.addObserver( + forName: UITextField.textDidChangeNotification, + object: alertController.textFields?.first, + queue: .main) { _ in + guard let text = alertController.textFields?.first?.text else { return } + + let textCheck = FileNameValidator.shared.checkFileName(text) + okAction.isEnabled = textCheck?.error == nil && !text.isEmpty + alertController.message = textCheck?.error.localizedDescription + } + + alertController.addAction(cancelAction) + alertController.addAction(okAction) + return alertController + } + static func renameFile(metadata: tableMetadata, indexPath: IndexPath) -> UIAlertController { let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert) From af42f6c72504a665a96616a1b7d35617e5e5d160 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 20 Aug 2024 17:37:34 +0200 Subject: [PATCH 15/16] Fix PR Signed-off-by: Milen Pivchev --- Share/NCShareExtension+DataSource.swift | 2 -- Share/NCShareExtension+NCDelegate.swift | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index 50b6d2c2ed..e96e0aaa5b 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -89,8 +89,6 @@ extension NCShareExtension: UICollectionViewDataSource { return UICollectionViewCell() } - cell.listCellDelegate = self - cell.fileObjectId = metadata.ocId cell.indexPath = indexPath cell.fileUser = metadata.ownerId diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index 036a9641d4..a3a1e1f76e 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -116,8 +116,7 @@ extension NCShareExtension: NCCreateFormUploadConflictDelegate { } } -extension NCShareExtension: NCShareCellDelegate, NCListCellDelegate { - +extension NCShareExtension: NCShareCellDelegate { func removeFile(named fileName: String) { guard let index = self.filesName.firstIndex(of: fileName) else { return showAlert(title: "_file_not_found_", description: fileName) @@ -141,5 +140,7 @@ extension NCShareExtension: NCShareCellDelegate, NCListCellDelegate { filesName[fileIx] = newFileName tableView.reloadData() } + + present(alert, animated: true) } } From f3dad334c279c1f0b44167a90ba48afdafadc580 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Wed, 21 Aug 2024 13:34:42 +0200 Subject: [PATCH 16/16] Update NCBrand.swift Signed-off-by: Milen Pivchev --- Brand/NCBrand.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Brand/NCBrand.swift b/Brand/NCBrand.swift index 9611d5b337..cfeef5ca6d 100755 --- a/Brand/NCBrand.swift +++ b/Brand/NCBrand.swift @@ -69,7 +69,7 @@ let userAgent: String = { var disable_crash_service: Bool = false var disable_log: Bool = false var disable_mobileconfig: Bool = false - var disable_show_more_nextcloud_apps_in_settings: Bool = true + var disable_show_more_nextcloud_apps_in_settings: Bool = false var doNotAskPasscodeAtStartup: Bool = false // Internal option behaviour