Browse Source

Implement file lock client support

- remove delete (server) and rename options from menus if a file is locked by someone else
- disable "move" option in move or copy
- allow modify, but don't allow override (only save as copy)

Signed-off-by: Henrik Storch <henrik.storch@nextcloud.com>
Henrik Storch 3 years ago
parent
commit
8106aab2d1

+ 5 - 0
iOSClient/Data/NCDatabase.swift

@@ -424,6 +424,11 @@ extension tableMetadata {
     var isPrintable: Bool {
     var isPrintable: Bool {
         classFile == NCCommunicationCommon.typeClassFile.image.rawValue || ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/")
         classFile == NCCommunicationCommon.typeClassFile.image.rawValue || ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/")
     }
     }
+
+    /// Returns false if the user is lokced out of the file. I.e. The file is locked but by somone else
+    func canUnlock(as user: String) -> Bool {
+        return !lock || lockOwner == user
+    }
 }
 }
 
 
 class tablePhotoLibrary: Object {
 class tablePhotoLibrary: Object {

+ 9 - 2
iOSClient/Main/Collection Common/NCSelectableNavigationView.swift

@@ -97,6 +97,7 @@ extension NCSelectableNavigationView where Self: UIViewController {
         var isAnyOffline = false
         var isAnyOffline = false
         var isAnyFolder = false
         var isAnyFolder = false
         var isAnyLocked = false
         var isAnyLocked = false
+        var canUnlock = true
 
 
         for ocId in selectOcId {
         for ocId in selectOcId {
             guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
             guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
@@ -105,7 +106,13 @@ extension NCSelectableNavigationView where Self: UIViewController {
                 selectedMediaMetadatas.append(metadata)
                 selectedMediaMetadatas.append(metadata)
             }
             }
             if metadata.directory { isAnyFolder = true }
             if metadata.directory { isAnyFolder = true }
-            if metadata.lock { isAnyLocked = true }
+            if metadata.lock {
+                isAnyLocked = true
+                if metadata.lockOwner != appDelegate.userId {
+                    canUnlock = false
+                }
+            }
+
             guard !isAnyOffline else { continue }
             guard !isAnyOffline else { continue }
             if metadata.directory,
             if metadata.directory,
                let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
                let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
@@ -117,7 +124,7 @@ extension NCSelectableNavigationView where Self: UIViewController {
 
 
         actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
         actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
 
 
-        if !isAnyFolder {
+        if !isAnyFolder, canUnlock {
             actions.append(.lockUnlockFiles(shouldLock: !isAnyLocked, metadatas: selectedMetadatas, completion: tapSelect))
             actions.append(.lockUnlockFiles(shouldLock: !isAnyLocked, metadatas: selectedMetadatas, completion: tapSelect))
         }
         }
 
 

+ 9 - 1
iOSClient/Main/NCFunctionCenter.swift

@@ -745,7 +745,15 @@ import SVGKit
 
 
         // FILE
         // FILE
 
 
-        var children: [UIMenuElement] = [favorite, lockUnlock, offline, openIn, rename, moveCopy, copy, copyPath, delete]
+        var children: [UIMenuElement] = [favorite, offline, openIn, moveCopy, copy, copyPath]
+        
+        if metadata.canUnlock(as: appDelegate.userId) {
+            children.insert(rename, at: 3)
+            children.insert(lockUnlock, at: 2)
+            children.append(delete)
+        } else if enableDeleteLocal {
+            children.append(deleteConfirmLocal)
+        }
 
 
         if (metadata.contentType != "image/svg+xml") && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue) {
         if (metadata.contentType != "image/svg+xml") && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue) {
             children.insert(save, at: 2)
             children.insert(save, at: 2)

+ 5 - 3
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -41,6 +41,7 @@ extension NCCollectionViewCommon {
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
         let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
         let isOffline: Bool
         let isOffline: Bool
+        let canUnlock = metadata.canUnlock(as: appDelegate.userId)
 
 
         if metadata.directory, let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
         if metadata.directory, let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
             isOffline = directory.offline
             isOffline = directory.offline
@@ -73,9 +74,10 @@ extension NCCollectionViewCommon {
         
         
         if metadata.lock {
         if metadata.lock {
             let lockOwnerName = metadata.lockOwnerDisplayName.isEmpty ? metadata.lockOwner : metadata.lockOwnerDisplayName
             let lockOwnerName = metadata.lockOwnerDisplayName.isEmpty ? metadata.lockOwner : metadata.lockOwnerDisplayName
+            let lockTime = DateFormatter.localizedString(from: metadata.lockTime ?? Date(), dateStyle: .short, timeStyle: .short)
             actions.append(
             actions.append(
                 NCMenuAction(
                 NCMenuAction(
-                    title: String(format: NSLocalizedString("_file_locked_by_", comment: ""), lockOwnerName),
+                    title: String(format: NSLocalizedString("_file_locked_by_at_", comment: ""), lockOwnerName, lockTime),
                     icon: NCUtility.shared.loadUserImage(
                     icon: NCUtility.shared.loadUserImage(
                         for: metadata.lockOwner,
                         for: metadata.lockOwner,
                            displayName: lockOwnerName,
                            displayName: lockOwnerName,
@@ -103,7 +105,7 @@ extension NCCollectionViewCommon {
             )
             )
         )
         )
 
 
-        if !metadata.directory {
+        if !metadata.directory, canUnlock {
             actions.append(.lockUnlockFiles(shouldLock: !metadata.lock, metadatas: [metadata]))
             actions.append(.lockUnlockFiles(shouldLock: !metadata.lock, metadatas: [metadata]))
         }
         }
 
 
@@ -202,7 +204,7 @@ extension NCCollectionViewCommon {
         //
         //
         // RENAME
         // RENAME
         //
         //
-        if !(isFolderEncrypted && metadata.serverUrl == serverUrlHome) {
+        if !(isFolderEncrypted && metadata.serverUrl == serverUrlHome), canUnlock {
             actions.append(
             actions.append(
                 NCMenuAction(
                 NCMenuAction(
                     title: NSLocalizedString("_rename_", comment: ""),
                     title: NSLocalizedString("_rename_", comment: ""),

+ 4 - 2
iOSClient/Menu/NCMedia+Menu.swift

@@ -173,8 +173,10 @@ extension NCMedia {
 
 
             //
             //
             // DELETE
             // DELETE
-            //
-            actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect))
+            // can't delete from cache because is needed for NCMedia view, and if locked can't delete from server either.
+            if !selectedMetadatas.contains(where: { $0.lock && $0.lockOwner != appDelegate.userId }) {
+                actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect))
+            }
         }
         }
     }
     }
 }
 }

+ 9 - 5
iOSClient/Menu/NCMenuAction.swift

@@ -42,7 +42,7 @@ class NCMenuAction {
 
 
 extension NCMenuAction {
 extension NCMenuAction {
     static let seperatorIdentifier = "NCMenuAction.SEPERATOR"
     static let seperatorIdentifier = "NCMenuAction.SEPERATOR"
-    
+
     /// A static seperator, with no actions, text, or icons
     /// A static seperator, with no actions, text, or icons
     static var seperator: NCMenuAction {
     static var seperator: NCMenuAction {
         return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), action: nil)
         return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), action: nil)
@@ -92,6 +92,8 @@ extension NCMenuAction {
             }
             }
         } // else: no metadata selected
         } // else: no metadata selected
 
 
+        let canDeleteServer = selectedMetadatas.contains(
+            where: { $0.canUnlock(as: (UIApplication.shared.delegate as? AppDelegate)?.userId ?? "") })
         var fileList = ""
         var fileList = ""
         for (ix, metadata) in selectedMetadatas.enumerated() {
         for (ix, metadata) in selectedMetadatas.enumerated() {
             guard ix < 3 else { fileList += "\n - ..."; break }
             guard ix < 3 else { fileList += "\n - ..."; break }
@@ -106,10 +108,12 @@ extension NCMenuAction {
                     title: titleDelete,
                     title: titleDelete,
                     message: NSLocalizedString("_want_delete_", comment: "") + fileList,
                     message: NSLocalizedString("_want_delete_", comment: "") + fileList,
                     preferredStyle: .alert)
                     preferredStyle: .alert)
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-                    selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: false) })
-                    completion?()
-                })
+                if canDeleteServer {
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
+                        selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: false) })
+                        completion?()
+                    })
+                }
 
 
                 // NCMedia removes image from collection view if removed from cache
                 // NCMedia removes image from collection view if removed from cache
                 if !(viewController is NCMedia) {
                 if !(viewController is NCMedia) {

+ 1 - 1
iOSClient/Menu/NCViewer+Menu.swift

@@ -146,7 +146,7 @@ extension NCViewer {
         //
         //
         // RENAME
         // RENAME
         //
         //
-        if !webView {
+        if !webView, metadata.canUnlock(as: appDelegate.userId) {
             actions.append(
             actions.append(
                 NCMenuAction(
                 NCMenuAction(
                     title: NSLocalizedString("_rename_", comment: ""),
                     title: NSLocalizedString("_rename_", comment: ""),

+ 1 - 1
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -152,7 +152,7 @@
 "_unlock_file_"                     = "Unock file";
 "_unlock_file_"                     = "Unock file";
 "_lock_selected_files_"             = "Lock files";
 "_lock_selected_files_"             = "Lock files";
 "_unlock_selected_files_"           = "Unock files";
 "_unlock_selected_files_"           = "Unock files";
-"_file_locked_by_"                  = "File locked by %@";
+"_file_locked_by_at_"                  = "File locked by %@ at %@";
 
 
 /* Remove a file from a list, don't delete it entirely */
 /* Remove a file from a list, don't delete it entirely */
 "_remove_file_"             = "Remove file";
 "_remove_file_"             = "Remove file";

+ 2 - 2
iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift

@@ -88,8 +88,8 @@ import NCCommunication
         guard isEditingEnabled, hasChanges else { return }
         guard isEditingEnabled, hasChanges else { return }
 
 
         let alertController = UIAlertController(title: NSLocalizedString("_save_", comment: ""), message: "", preferredStyle: .alert)
         let alertController = UIAlertController(title: NSLocalizedString("_save_", comment: ""), message: "", preferredStyle: .alert)
-
-        if metadata?.livePhoto == false {
+        let userId = (UIApplication.shared.delegate as? AppDelegate)?.userId ?? ""
+        if metadata?.livePhoto == false, metadata?.canUnlock(as: userId) != false {
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_overwrite_original_", comment: ""), style: .default) { _ in
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_overwrite_original_", comment: ""), style: .default) { _ in
                 self.saveModifiedFile(override: true)
                 self.saveModifiedFile(override: true)
             })
             })