Remove redundant directory metadata tables in File Provider
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Sat, 18 Mar 2023 00:43:05 +0000 (01:43 +0100)
committerClaudio Cambra <claudio.cambra@nextcloud.com>
Fri, 12 May 2023 05:29:55 +0000 (13:29 +0800)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Database/NextcloudDirectoryMetadataTable.swift [deleted file]
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Database/NextcloudFilesDatabaseManager.swift
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderEnumerator.swift
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/NextcloudAccount.swift
shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj

diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Database/NextcloudDirectoryMetadataTable.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Database/NextcloudDirectoryMetadataTable.swift
deleted file mode 100644 (file)
index fa941fc..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * 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 2 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.
- */
-
-import Foundation
-import RealmSwift
-
-class NextcloudDirectoryMetadataTable: Object {
-    func isInSameRemoteState(_ comparingMetadata: NextcloudDirectoryMetadataTable) -> Bool {
-        return comparingMetadata.etag == self.etag &&
-            comparingMetadata.e2eEncrypted == self.e2eEncrypted &&
-            comparingMetadata.favorite == self.favorite &&
-            comparingMetadata.permissions == self.permissions
-    }
-
-    @Persisted(primaryKey: true) var ocId: String
-    @Persisted var account = ""
-    @Persisted var colorFolder: String?
-    @Persisted var e2eEncrypted: Bool = false
-    @Persisted var etag = ""
-    @Persisted var favorite: Bool = false
-    @Persisted var fileId = ""
-    @Persisted var offline: Bool = false
-    @Persisted var permissions = ""
-    @Persisted var richWorkspace: String?
-    @Persisted var serverUrl = ""
-    @Persisted var parentDirectoryServerUrl = ""
-}
index 445662285eadf03c8d9243d810fe03e54c7949a6..db94a0d61d7e6beb78a2e0302ad45e02a64eb792 100644 (file)
@@ -54,7 +54,7 @@ class NextcloudFilesDatabaseManager : NSObject {
         let config = Realm.Configuration(
             fileURL: self.databasePath,
             schemaVersion: self.schemaVersion,
-            objectTypes: [NextcloudItemMetadataTable.self, NextcloudDirectoryMetadataTable.self, NextcloudLocalFileMetadataTable.self]
+            objectTypes: [NextcloudItemMetadataTable.self, NextcloudLocalFileMetadataTable.self]
         )
 
         Realm.Configuration.defaultConfiguration = config
@@ -290,163 +290,60 @@ class NextcloudFilesDatabaseManager : NSObject {
         return nil
     }
 
-    func directoryMetadata(account: String, serverUrl: String) -> NextcloudDirectoryMetadataTable? {
-        if let metadata = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first {
-            return NextcloudDirectoryMetadataTable(value: metadata)
-        }
+    func directoryMetadata(account: String, serverUrl: String) -> NextcloudItemMetadataTable? {
+        // We want to split by "/" (e.g. cloud.nc.com/files/a/b) but we need to be mindful of "https://c.nc.com"
+        let problematicSeparator = "://"
+        let placeholderSeparator = "__TEMP_REPLACE__"
+        let serverUrlWithoutPrefix = serverUrl.replacingOccurrences(of: problematicSeparator, with: placeholderSeparator)
+        var splitServerUrl = serverUrlWithoutPrefix.split(separator: "/")
+        let directoryItemFileName = String(splitServerUrl.removeLast())
+        let directoryItemServerUrl = splitServerUrl.joined(separator: "/").replacingOccurrences(of: placeholderSeparator, with: problematicSeparator)
 
-        return nil
-    }
-
-    func directoryMetadata(ocId: String) -> NextcloudDirectoryMetadataTable? {
-        if let metadata = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first {
-            return NextcloudDirectoryMetadataTable(value: metadata)
+        if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@ AND directory == true", account, directoryItemServerUrl, directoryItemFileName).first {
+            return NextcloudItemMetadataTable(value: metadata)
         }
 
         return nil
     }
 
-    private func sortedDirectoryMetadatas(_ metadatas: Results<NextcloudDirectoryMetadataTable>) -> [NextcloudDirectoryMetadataTable] {
-        let sortedMetadatas = metadatas.sorted(byKeyPath: "serverUrl", ascending: true)
-        return Array(sortedMetadatas.map { NextcloudDirectoryMetadataTable(value: $0) })
-    }
-
-    func childDirectoriesForDirectory(_ directoryMetadata: NextcloudDirectoryMetadataTable) -> [NextcloudDirectoryMetadataTable] {
-        let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("serverUrl BEGINSWITH %@ AND ocId != %@", directoryMetadata.serverUrl, directoryMetadata.account)
-        return sortedDirectoryMetadatas(metadatas)
+    func childDirectoriesForDirectory(_ directoryMetadata: NextcloudItemMetadataTable) -> [NextcloudItemMetadataTable] {
+        let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+        let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("serverUrl BEGINSWITH %@ AND ocId != %@ AND directory == true", directoryServerUrl, directoryMetadata.account)
+        return sortedItemMetadatas(metadatas)
     }
 
-    func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable) -> NextcloudDirectoryMetadataTable? {
+    func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable) -> NextcloudItemMetadataTable? {
         return directoryMetadata(account: itemMetadata.account, serverUrl: itemMetadata.serverUrl)
     }
 
-    func directoryMetadatas(account: String) -> [NextcloudDirectoryMetadataTable] {
-        let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@", account)
-        return sortedDirectoryMetadatas(metadatas)
-    }
-
-    func directoryMetadatas(account: String, parentDirectoryServerUrl: String) -> [NextcloudDirectoryMetadataTable] {
-        let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@", account, parentDirectoryServerUrl)
-        return sortedDirectoryMetadatas(metadatas)
-    }
-
-    private func processDirectoryMetadatasToDelete(databaseToWriteTo: Realm,
-                                           existingDirectoryMetadatas: Results<NextcloudDirectoryMetadataTable>,
-                                           updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
-
-        for existingMetadata in existingDirectoryMetadatas {
-            guard !updatedDirectoryMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
-                  let metadataToDelete = directoryMetadata(ocId: existingMetadata.ocId) else { continue }
-
-            Logger.ncFilesDatabase.debug("Deleting directory metadata during update. ocID: \(existingMetadata.ocId, privacy: .public), etag: \(existingMetadata.etag, privacy: .public), serverUrl: \(existingMetadata.serverUrl)")
-
-            self.deleteDirectoryAndSubdirectoriesMetadata(ocId: metadataToDelete.ocId)
-        }
-    }
-
-    private func processDirectoryMetadatasToUpdate(databaseToWriteTo: Realm,
-                                           existingDirectoryMetadatas: Results<NextcloudDirectoryMetadataTable>,
-                                           updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
-
-        assert(databaseToWriteTo.isInWriteTransaction)
-
-        for updatedMetadata in updatedDirectoryMetadatas {
-            if let existingMetadata = existingDirectoryMetadatas.first(where: { $0.ocId == updatedMetadata.ocId }) {
-
-                if !existingMetadata.isInSameRemoteState(updatedMetadata) {
-
-                    databaseToWriteTo.add(NextcloudDirectoryMetadataTable(value: updatedMetadata), update: .all)
-                    Logger.ncFilesDatabase.debug("Updated existing directory metadata. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), serverUrl: \(updatedMetadata.serverUrl)")
-                }
-                // Don't update under other circumstances in which the metadata already exists
-
-            } else { // This is a new metadata
-                databaseToWriteTo.add(NextcloudDirectoryMetadataTable(value: updatedMetadata), update: .all)
-                Logger.ncFilesDatabase.debug("Created new directory metadata during update. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), serverUrl: \(updatedMetadata.serverUrl)")
-            }
-        }
-    }
-
-    func updateDirectoryMetadatas(account: String, parentDirectoryServerUrl: String, updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
-        let database = ncDatabase()
-
-        let existingDirectoryMetadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@", account, parentDirectoryServerUrl)
-
-        // Actual db writing handled internally
-        processDirectoryMetadatasToDelete(databaseToWriteTo: database,
-                                          existingDirectoryMetadatas: existingDirectoryMetadatas,
-                                          updatedDirectoryMetadatas: updatedDirectoryMetadatas)
-
-        do {
-            try database.write {
-
-                processDirectoryMetadatasToUpdate(databaseToWriteTo: database,
-                                                  existingDirectoryMetadatas: existingDirectoryMetadatas,
-                                                  updatedDirectoryMetadatas: updatedDirectoryMetadatas)
-            }
-        } catch let error {
-            Logger.ncFilesDatabase.error("Could not update directory metadatas, received error: \(error.localizedDescription)")
-        }
-    }
-
-    func directoryMetadataFromItemMetadata(directoryItemMetadata: NextcloudItemMetadataTable, recordEtag: Bool = false) -> NextcloudDirectoryMetadataTable {
-        var newDirectoryMetadata = NextcloudDirectoryMetadataTable()
-        let directoryOcId = directoryItemMetadata.ocId
-
-        if let existingDirectoryMetadata = directoryMetadata(ocId: directoryOcId) {
-            newDirectoryMetadata = existingDirectoryMetadata
+    func directoryMetadata(ocId: String) -> NextcloudItemMetadataTable? {
+        if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first {
+            return NextcloudItemMetadataTable(value: metadata)
         }
 
-        if recordEtag {
-            newDirectoryMetadata.etag = directoryItemMetadata.etag
-        }
-
-        newDirectoryMetadata.ocId = directoryOcId
-        newDirectoryMetadata.fileId = directoryItemMetadata.fileId
-        newDirectoryMetadata.parentDirectoryServerUrl = directoryItemMetadata.serverUrl
-        newDirectoryMetadata.serverUrl = directoryItemMetadata.serverUrl + "/" + directoryItemMetadata.fileNameView
-        newDirectoryMetadata.account = directoryItemMetadata.account
-        newDirectoryMetadata.e2eEncrypted = directoryItemMetadata.e2eEncrypted
-        newDirectoryMetadata.favorite = directoryItemMetadata.favorite
-        newDirectoryMetadata.permissions = directoryItemMetadata.permissions
-
-        return newDirectoryMetadata
+        return nil
     }
 
-    func updateDirectoryMetadatasFromItemMetadatas(account: String, parentDirectoryServerUrl: String, updatedDirectoryItemMetadatas: [NextcloudItemMetadataTable], recordEtag: Bool = false) {
-
-        var updatedDirMetadatas: [NextcloudDirectoryMetadataTable] = []
-
-        for directoryItemMetadata in updatedDirectoryItemMetadatas {
-            let newDirectoryMetadata = directoryMetadataFromItemMetadata(directoryItemMetadata: directoryItemMetadata, recordEtag: recordEtag)
-            updatedDirMetadatas.append(newDirectoryMetadata)
-        }
-
-        updateDirectoryMetadatas(account: account, parentDirectoryServerUrl: parentDirectoryServerUrl, updatedDirectoryMetadatas: updatedDirMetadatas)
+    func directoryMetadatas(account: String) -> [NextcloudItemMetadataTable] {
+        let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND directory == true", account)
+        return sortedItemMetadatas(metadatas)
     }
 
-    func addDirectoryMetadata(_ metadata: NextcloudDirectoryMetadataTable) {
-        let database = ncDatabase()
-
-        do {
-            try database.write {
-                database.add(metadata, update: .all)
-                Logger.ncFilesDatabase.debug("Added new directory metadata. ocId: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), serverUrl: \(metadata.serverUrl)")
-            }
-        } catch let error {
-            Logger.ncFilesDatabase.error("Could not add new directory metadata. ocId: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), serverUrl: \(metadata.serverUrl), received error: \(error.localizedDescription, privacy: .public)")
-        }
+    func directoryMetadatas(account: String, parentDirectoryServerUrl: String) -> [NextcloudItemMetadataTable] {
+        let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@ AND directory == true", account, parentDirectoryServerUrl)
+        return sortedItemMetadatas(metadatas)
     }
 
     // Deletes all metadatas related to the info of the directory provided
     func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) {
         let database = ncDatabase()
-        guard let directoryMetadata = database.objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first else {
+        guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
             Logger.ncFilesDatabase.error("Could not find directory metadata for ocId \(ocId, privacy: .public). Not proceeding with deletion")
             return
         }
 
-        let results = database.objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, directoryMetadata.serverUrl)
+        let directoryUrlPath = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+        let results = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, directoryUrlPath)
 
         for result in results {
             deleteItemMetadata(ocId: result.ocId)
@@ -455,11 +352,11 @@ class NextcloudFilesDatabaseManager : NSObject {
 
         do {
             try database.write {
-                Logger.ncFilesDatabase.debug("Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryMetadata.serverUrl)")
+                Logger.ncFilesDatabase.debug("Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryUrlPath)")
                 database.delete(results)
             }
         } catch let error {
-            Logger.ncFilesDatabase.error("Could not delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryMetadata.serverUrl), received error: \(error.localizedDescription, privacy: .public)")
+            Logger.ncFilesDatabase.error("Could not delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryUrlPath), received error: \(error.localizedDescription, privacy: .public)")
         }
     }
 
@@ -468,25 +365,19 @@ class NextcloudFilesDatabaseManager : NSObject {
         let database = ncDatabase()
 
         do {
-            try database.write {
-                guard let directoryTableResult = database.objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first,
-                      let directoryItemResult = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@", ocId).first else {
-                    Logger.ncFilesDatabase.error("Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming")
-                    return
-                }
+            guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
+                Logger.ncFilesDatabase.error("Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming")
+                return
+            }
 
-                let oldServerUrl = directoryTableResult.serverUrl
+            let oldServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
 
-                let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryTableResult.account, oldServerUrl)
-                let childDirectoryResults = database.objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryTableResult.account, oldServerUrl)
+            let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, oldServerUrl)
 
-                directoryTableResult.serverUrl = newServerUrl
-                database.add(directoryTableResult, update: .all)
-                directoryItemResult.fileName = newFileName
-                directoryItemResult.fileNameView = newFileName
-                database.add(directoryItemResult, update: .all)
-                Logger.ncFilesDatabase.debug("Renamed root renaming directory at \(oldServerUrl) to \(newServerUrl)")
+            renameItemMetadata(ocId: ocId, newServerUrl: newServerUrl, newFileName: newFileName)
+            Logger.ncFilesDatabase.debug("Renamed root renaming directory")
 
+            try database.write {
                 for childItem in childItemResults {
                     let oldServerUrl = childItem.serverUrl
                     let movedServerUrl = oldServerUrl.replacingOccurrences(of: oldServerUrl, with: newServerUrl)
@@ -494,17 +385,6 @@ class NextcloudFilesDatabaseManager : NSObject {
                     database.add(childItem, update: .all)
                     Logger.ncFilesDatabase.debug("Moved childItem at \(oldServerUrl) to \(movedServerUrl)")
                 }
-
-                for childDirectory in childDirectoryResults {
-                    let oldServerUrl = childDirectory.serverUrl
-                    let oldParentServerUrl = childDirectory.parentDirectoryServerUrl
-                    let movedServerUrl = oldServerUrl.replacingOccurrences(of: oldServerUrl, with: newServerUrl)
-                    let movedParentServerUrl = oldServerUrl.replacingOccurrences(of: oldParentServerUrl, with: newServerUrl)
-                    childDirectory.serverUrl = movedServerUrl
-                    childDirectory.parentDirectoryServerUrl = movedParentServerUrl
-                    database.add(childDirectory, update: .all)
-                    Logger.ncFilesDatabase.debug("Moved childDirectory at \(oldServerUrl) to \(movedServerUrl)")
-                }
             }
         } catch let error {
             Logger.ncFilesDatabase.error("Could not rename directory metadata with ocId: \(ocId, privacy: .public) to new serverUrl: \(newServerUrl), received error: \(error.localizedDescription, privacy: .public)")
index 2e8b806b0e5c2d085701d5be46ab63c3a96057eb..051132fe90f588a32bf2351782131d04e177e50c 100644 (file)
@@ -113,8 +113,10 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                     dispatchGroup.enter() // Add to outer counter
 
                     dispatchQueue.async {
+                        let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+
                         guard serverError == nil else {
-                            Logger.enumeration.info("Skipping enumeration of directory for working set: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) as we have an error.")
+                            Logger.enumeration.info("Skipping enumeration of directory for working set: \(directoryServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) as we have an error.")
                             dispatchGroup.leave()
                             return;
                         }
@@ -122,9 +124,9 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                         let currentNetworkTaskDispatchGroup = DispatchGroup() // To make this serial queue wait until this task is done
                         currentNetworkTaskDispatchGroup.enter()
 
-                        FileProviderEnumerator.readServerUrl(directoryMetadata.serverUrl, ncAccount: self.ncAccount, ncKit: self.ncKit) { metadatas, _, _, _, readError in
+                        FileProviderEnumerator.readServerUrl(directoryServerUrl, ncAccount: self.ncAccount, ncKit: self.ncKit) { metadatas, _, _, _, readError in
                             guard readError == nil else {
-                                Logger.enumeration.error("Finishing enumeration of working set directory \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with error \(readError!.localizedDescription, privacy: .public)")
+                                Logger.enumeration.error("Finishing enumeration of working set directory \(directoryServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with error \(readError!.localizedDescription, privacy: .public)")
 
                                 let nkError = NKError(error: readError!)
                                 if nkError.isUnauthenticatedError || nkError.isCouldntConnectError {
@@ -140,7 +142,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                             if let metadatas = metadatas {
                                 allMetadatas += metadatas
                             } else {
-                                allMetadatas += dbManager.itemMetadatas(account: self.ncAccount.ncKitAccount, serverUrl: directoryMetadata.serverUrl)
+                                allMetadatas += dbManager.itemMetadatas(account: self.ncAccount.ncKitAccount, serverUrl: directoryServerUrl)
                             }
 
                             currentNetworkTaskDispatchGroup.leave()
@@ -419,9 +421,8 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
     private static func fullRecursiveScan(ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool, completionHandler: @escaping(_ newMetadatas: [NextcloudItemMetadataTable], _ updatedMetadatas: [NextcloudItemMetadataTable], _ deletedMetadatas: [NextcloudItemMetadataTable]) -> Void) {
 
-        let rootContainerDirectoryMetadata = NextcloudDirectoryMetadataTable()
-        rootContainerDirectoryMetadata.serverUrl = ncAccount.davFilesUrl
-        rootContainerDirectoryMetadata.account = ncAccount.ncKitAccount
+        let rootContainerDirectoryMetadata = NextcloudItemMetadataTable()
+        rootContainerDirectoryMetadata.directory = true
         rootContainerDirectoryMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
 
         // Create a serial dispatch queue
@@ -436,7 +437,9 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
         }
     }
 
-    private static func scanRecursively(_ directoryMetadata: NextcloudDirectoryMetadataTable, ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool) -> (metadatas: [NextcloudItemMetadataTable], newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable], deletedMetadatas: [NextcloudItemMetadataTable]) {
+    private static func scanRecursively(_ directoryMetadata: NextcloudItemMetadataTable, ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool) -> (metadatas: [NextcloudItemMetadataTable], newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable], deletedMetadatas: [NextcloudItemMetadataTable]) {
+
+        assert(directoryMetadata.directory, "Can only recursively scan a directory.")
 
         var allMetadatas: [NextcloudItemMetadataTable] = []
         var allNewMetadatas: [NextcloudItemMetadataTable] = []
@@ -448,8 +451,12 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
         dispatchGroup.enter()
 
-        Logger.enumeration.debug("About to read: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash))")
-        FileProviderEnumerator.readServerUrl(directoryMetadata.serverUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
+        let itemServerUrl = directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue ?
+            ncAccount.davFilesUrl : directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+
+        Logger.enumeration.debug("About to read: \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash))")
+
+        FileProviderEnumerator.readServerUrl(itemServerUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
 
             if readError != nil {
                 let nkReadError = NKError(error: readError!)
@@ -457,7 +464,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                 // Is the error is that we have found matching etags on this item, then ignore it
                 // if we are doing a full rescan
                 guard nkReadError.isNoChangesError && scanChangesOnly else {
-                    Logger.enumeration.error("Finishing enumeration of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with \(readError!.localizedDescription, privacy: .public)")
+                    Logger.enumeration.error("Finishing enumeration of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with \(readError!.localizedDescription, privacy: .public)")
 
                     if nkReadError.isNotFoundError {
                         Logger.enumeration.info("404 error means item no longer exists. Deleting metadata and reporting as deletion without error")
@@ -479,30 +486,30 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                 }
             }
 
-            Logger.enumeration.info("Finished reading serverUrl: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+            Logger.enumeration.info("Finished reading serverUrl: \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
 
             if let metadatas = metadatas {
                 allMetadatas += metadatas
             } else {
-                Logger.enumeration.warning("WARNING: Nil metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+                Logger.enumeration.warning("WARNING: Nil metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
             }
 
             if let newMetadatas = newMetadatas {
                 allNewMetadatas += newMetadatas
             } else {
-                Logger.enumeration.warning("WARNING: Nil new metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+                Logger.enumeration.warning("WARNING: Nil new metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
             }
 
             if let updatedMetadatas = updatedMetadatas {
                 allUpdatedMetadatas += updatedMetadatas
             } else {
-                Logger.enumeration.warning("WARNING: Nil updated metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+                Logger.enumeration.warning("WARNING: Nil updated metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
             }
 
             if let deletedMetadatas = deletedMetadatas {
                 allDeletedMetadatas += deletedMetadatas
             } else {
-                Logger.enumeration.warning("WARNING: Nil deleted metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+                Logger.enumeration.warning("WARNING: Nil deleted metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
             }
 
             dispatchGroup.leave()
@@ -510,7 +517,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
         dispatchGroup.wait()
 
-        var updatedDirectories: [NextcloudDirectoryMetadataTable] = []
+        var updatedDirectories: [NextcloudItemMetadataTable] = []
         for updatedMetadata in allUpdatedMetadatas {
             if updatedMetadata.directory {
                 guard let directoryMetadata = dbManager.directoryMetadata(ocId: updatedMetadata.ocId) else {
@@ -600,18 +607,9 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
                         // We have now scanned this directory's contents, so update with etag in order to not check again if not needed
                         // unless it's the root container
                         if serverUrl != ncAccount.davFilesUrl {
-                            let directoryItemMetadata = dbManager.directoryMetadataFromItemMetadata(directoryItemMetadata: directoryMetadata, recordEtag: true)
-                            dbManager.addDirectoryMetadata(directoryItemMetadata)
+                            dbManager.addItemMetadata(directoryMetadata)
                         }
 
-                        // STORE ETAG-LESS DIRECTORY METADATA FOR CHILD DIRECTORIES
-                        // Since we haven't scanned the contents of the child directories, don't record their itemMetadata etags in the directory tables
-                        // This will delete database records for directories that we did not get from the readFileOrFolder (indicating they were deleted)
-                        // as well as deleting the records for all the children contained by the directories.
-                        // TODO: Find a way to detect if files have been moved rather than deleted and change the metadata server urls, move the materialised files
-                        dbManager.updateDirectoryMetadatasFromItemMetadatas(account: ncKitAccount, parentDirectoryServerUrl: serverUrl, updatedDirectoryItemMetadatas: childDirectoriesMetadata)
-                        // TODO: Notify working set changed if new folders found
-
                         dbManager.updateItemMetadatas(account: ncKitAccount, serverUrl: serverUrl, updatedMetadatas: metadatas) { newMetadatas, updatedMetadatas, deletedMetadatas in
                             completionHandler(metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, nil)
                         }
index 3ff83fffbbb2869da3f51a932d33f1a7875adfa2..9088bc5b41eb25b5c82500a0647ed9b4d13ed1a3 100644 (file)
@@ -232,28 +232,22 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
             return Progress()
         }
 
-        var parentItemMetadata: NextcloudDirectoryMetadataTable?
+        var parentItemServerUrl: String
 
         if parentItemIdentifier == .rootContainer {
-            let rootMetadata = NextcloudDirectoryMetadataTable()
-
-            rootMetadata.account = ncAccount.ncKitAccount
-            rootMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
-            rootMetadata.serverUrl = ncAccount.davFilesUrl
-
-            parentItemMetadata = rootMetadata
+            parentItemServerUrl = ncAccount.davFilesUrl
         } else {
-            parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue)
-        }
+            guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
+                Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
+                completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
+                return Progress()
+            }
 
-        guard let parentItemMetadata = parentItemMetadata else {
-            Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
-            completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
-            return Progress()
+            parentItemServerUrl = parentItemMetadata.serverUrl + "/" + parentItemMetadata.fileName
         }
 
         let fileNameLocalPath = url?.path ?? ""
-        let newServerUrlFileName = parentItemMetadata.serverUrl + "/" + itemTemplate.filename
+        let newServerUrlFileName = parentItemServerUrl + "/" + itemTemplate.filename
 
         Logger.fileProviderExtension.debug("About to upload item with identifier: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) of type: \(itemTemplate.contentType?.identifier ?? "UNKNOWN") (is folder: \(itemTemplateIsFolder ? "yes" : "no") and filename: \(itemTemplate.filename) to server url: \(newServerUrlFileName, privacy: OSLogPrivacy.auto(mask: .hash)) with contents located at: \(fileNameLocalPath, privacy: OSLogPrivacy.auto(mask: .hash))")
 
@@ -275,8 +269,6 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
                     DispatchQueue.global().async {
                         dbManager.convertNKFilesFromDirectoryReadToItemMetadatas(files, account: account) { directoryMetadata, childDirectoriesMetadata, metadatas in
 
-                            let newDirectoryMetadata = dbManager.directoryMetadataFromItemMetadata(directoryItemMetadata: directoryMetadata)
-                            dbManager.addDirectoryMetadata(newDirectoryMetadata)
                             dbManager.addItemMetadata(directoryMetadata)
 
                             let fpItem = FileProviderItem(metadata: directoryMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
@@ -323,7 +315,7 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
             newMetadata.size = size
             newMetadata.contentType = itemTemplate.contentType?.preferredMIMEType ?? ""
             newMetadata.directory = itemTemplateIsFolder
-            newMetadata.serverUrl = parentItemMetadata.serverUrl
+            newMetadata.serverUrl = parentItemServerUrl
             newMetadata.session = ""
             newMetadata.sessionError = ""
             newMetadata.sessionTaskIdentifier = 0
@@ -362,28 +354,22 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
             Logger.fileProviderExtension.warning("Modification for item: \(item.itemIdentifier.rawValue, privacy: .public) may already exist")
         }
 
-        var parentItemMetadata: NextcloudDirectoryMetadataTable?
+        var parentItemServerUrl: String
 
         if parentItemIdentifier == .rootContainer {
-            let rootMetadata = NextcloudDirectoryMetadataTable()
-
-            rootMetadata.account = ncAccount.ncKitAccount
-            rootMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
-            rootMetadata.serverUrl = ncAccount.davFilesUrl
-
-            parentItemMetadata = rootMetadata
+            parentItemServerUrl = ncAccount.davFilesUrl
         } else {
-            parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue)
-        }
+            guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
+                Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
+                completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
+                return Progress()
+            }
 
-        guard let parentItemMetadata = parentItemMetadata else {
-            Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
-            completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
-            return Progress()
+            parentItemServerUrl = parentItemMetadata.serverUrl + "/" + parentItemMetadata.fileName
         }
 
         let fileNameLocalPath = newContents?.path ?? ""
-        let newServerUrlFileName = parentItemMetadata.serverUrl + "/" + item.filename
+        let newServerUrlFileName = parentItemServerUrl + "/" + item.filename
 
         Logger.fileProviderExtension.debug("About to upload modified item with identifier: \(item.itemIdentifier.rawValue, privacy: .public) of type: \(item.contentType?.identifier ?? "UNKNOWN") (is folder: \(itemTemplateIsFolder ? "yes" : "no") and filename: \(item.filename, privacy: OSLogPrivacy.auto(mask: .hash)) to server url: \(newServerUrlFileName, privacy: OSLogPrivacy.auto(mask: .hash)) with contents located at: \(fileNameLocalPath, privacy: OSLogPrivacy.auto(mask: .hash))")
 
@@ -427,7 +413,7 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
                     if itemTemplateIsFolder {
                         dbManager.renameDirectoryAndPropagateToChildren(ocId: ocId, newServerUrl: newServerUrlFileName, newFileName: item.filename)
                     } else {
-                        dbManager.renameItemMetadata(ocId: ocId, newServerUrl: parentItemMetadata.serverUrl, newFileName: item.filename)
+                        dbManager.renameItemMetadata(ocId: ocId, newServerUrl: parentItemServerUrl, newFileName: item.filename)
                     }
 
                     guard let newMetadata = dbManager.itemMetadataFromOcId(ocId) else {
@@ -519,7 +505,7 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
                             newMetadata.size = size
                             newMetadata.contentType = item.contentType?.preferredMIMEType ?? ""
                             newMetadata.directory = itemTemplateIsFolder
-                            newMetadata.serverUrl = parentItemMetadata.serverUrl
+                            newMetadata.serverUrl = parentItemServerUrl
                             newMetadata.session = ""
                             newMetadata.sessionError = ""
                             newMetadata.sessionTaskIdentifier = 0
@@ -631,6 +617,15 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
         materialisedEnumerator.enumerateItems(for: materialisedObserver, startingAt: startingPage)
     }
 
+    func signalEnumerator(completionHandler: @escaping(_ error: Error?) -> Void) {
+        guard let fpManager = NSFileProviderManager(for: self.domain) else {
+            Logger.fileProviderExtension.error("Could not get file provider manager for domain, could not signal enumerator. This might lead to future conflicts.")
+            return
+        }
+
+        fpManager.signalEnumerator(for: .workingSet, completionHandler: completionHandler)
+    }
+
     // MARK: Nextcloud desktop client communication
     func sendFileProviderDomainIdentifier() {
         let command = "FILE_PROVIDER_DOMAIN_IDENTIFIER_REQUEST_REPLY"
index 6237fa5783d25c6b8d4304e38f8879311894e248..a1b9963851e6354cad9ddee4290a70c767373fe9 100644 (file)
@@ -16,7 +16,7 @@ import Foundation
 import FileProvider
 
 class NextcloudAccount: NSObject {
-    let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
+    private let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
     let username, password, ncKitAccount, serverUrl, davFilesUrl: String
 
     init(user: String, serverUrl: String, password: String) {
index b3ee9b3944177ae3b1d6e1c0d513edbbb9225a8c..93502d4bde6c590774065692be30cc7df1b6503b 100644 (file)
@@ -12,7 +12,6 @@
                5307A6EB2965DB8D001E0C6A /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6EA2965DB8D001E0C6A /* RealmSwift */; };
                5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */; };
                5318AD9129BF42FB00CBB71C /* NextcloudItemMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */; };
-               5318AD9329BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */; };
                5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */; };
                5318AD9729BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */; };
                5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */; };
 /* Begin PBXFileReference section */
                5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudFilesDatabaseManager.swift; sourceTree = "<group>"; };
                5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudItemMetadataTable.swift; sourceTree = "<group>"; };
-               5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudDirectoryMetadataTable.swift; sourceTree = "<group>"; };
                5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudLocalFileMetadataTable.swift; sourceTree = "<group>"; };
                5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderMaterialisedEnumerationObserver.swift; sourceTree = "<group>"; };
                5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NKError+Extensions.swift"; sourceTree = "<group>"; };
                        isa = PBXGroup;
                        children = (
                                5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */,
-                               5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */,
                                5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */,
                                5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */,
                        );
                                5352E85B29B7BFE6002CE85C /* Progress+Extensions.swift in Sources */,
                                536EFC36295E3C1100F4CB13 /* NextcloudAccount.swift in Sources */,
                                538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */,
-                               5318AD9329BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift in Sources */,
                                536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */,
                                5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */,
                                5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */,