FileProviderEnumerator.swift 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. //
  2. // FileProviderEnumerator.swift
  3. // Files
  4. //
  5. // Created by Marino Faggiana on 26/03/18.
  6. // Copyright © 2018 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. import FileProvider
  25. import RealmSwift
  26. import NextcloudKit
  27. class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
  28. var enumeratedItemIdentifier: NSFileProviderItemIdentifier
  29. var serverUrl: String?
  30. let providerUtility = fileProviderUtility()
  31. let database = NCManageDatabase.shared
  32. var recordsPerPage: Int = 20
  33. var anchor: UInt64 = 0
  34. init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
  35. self.enumeratedItemIdentifier = enumeratedItemIdentifier
  36. if enumeratedItemIdentifier == .rootContainer {
  37. serverUrl = NCUtilityFileSystem().getHomeServer(session: fileProviderData.shared.session)
  38. } else {
  39. if let metadata = providerUtility.getTableMetadataFromItemIdentifier(enumeratedItemIdentifier),
  40. let directorySource = self.database.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
  41. serverUrl = directorySource.serverUrl + "/" + metadata.fileName
  42. }
  43. }
  44. super.init()
  45. }
  46. func invalidate() { }
  47. func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) {
  48. var items: [NSFileProviderItemProtocol] = []
  49. /// WorkingSet
  50. if enumeratedItemIdentifier == .workingSet {
  51. var itemIdentifierMetadata: [NSFileProviderItemIdentifier: tableMetadata] = [:]
  52. /// Tags
  53. let tags = self.database.getTags(predicate: NSPredicate(format: "account == %@", fileProviderData.shared.session.account))
  54. for tag in tags {
  55. guard let metadata = self.database.getMetadataFromOcId(tag.ocId) else { continue }
  56. itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
  57. }
  58. /// Favorite
  59. fileProviderData.shared.listFavoriteIdentifierRank = self.database.getTableMetadatasDirectoryFavoriteIdentifierRank(account: fileProviderData.shared.session.account)
  60. for (identifier, _) in fileProviderData.shared.listFavoriteIdentifierRank {
  61. guard let metadata = self.database.getMetadataFromOcId(identifier) else { continue }
  62. itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
  63. }
  64. /// Create items
  65. for (_, metadata) in itemIdentifierMetadata {
  66. if let parentItemIdentifier = providerUtility.getParentItemIdentifier(metadata: metadata) {
  67. let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
  68. items.append(item)
  69. }
  70. }
  71. observer.didEnumerate(items)
  72. observer.finishEnumerating(upTo: nil)
  73. } else {
  74. /// ServerUrl
  75. guard let serverUrl = serverUrl else {
  76. observer.finishEnumerating(upTo: nil)
  77. return
  78. }
  79. var pageNumber = 1
  80. if let stringPage = String(data: page.rawValue, encoding: .utf8),
  81. let intPage = Int(stringPage) {
  82. pageNumber = intPage
  83. }
  84. self.fetchItemsForPage(serverUrl: serverUrl, pageNumber: pageNumber) { metadatas in
  85. if let metadatas {
  86. for metadata in metadatas {
  87. if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionUploadBackgroundExt) {
  88. continue
  89. }
  90. if let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadata) {
  91. let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
  92. items.append(item)
  93. }
  94. }
  95. }
  96. observer.didEnumerate(items)
  97. if let metadatas,
  98. metadatas.count == self.recordsPerPage {
  99. pageNumber += 1
  100. let providerPage = NSFileProviderPage("\(pageNumber)".data(using: .utf8)!)
  101. observer.finishEnumerating(upTo: providerPage)
  102. } else {
  103. observer.finishEnumerating(upTo: nil)
  104. }
  105. }
  106. }
  107. }
  108. func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) {
  109. var itemsDelete: [NSFileProviderItemIdentifier] = []
  110. var itemsUpdate: [FileProviderItem] = []
  111. // Report the deleted items
  112. //
  113. if self.enumeratedItemIdentifier == .workingSet {
  114. for (itemIdentifier, _) in fileProviderData.shared.fileProviderSignalDeleteWorkingSetItemIdentifier {
  115. itemsDelete.append(itemIdentifier)
  116. }
  117. fileProviderData.shared.fileProviderSignalDeleteWorkingSetItemIdentifier.removeAll()
  118. } else {
  119. for (itemIdentifier, _) in fileProviderData.shared.fileProviderSignalDeleteContainerItemIdentifier {
  120. itemsDelete.append(itemIdentifier)
  121. }
  122. fileProviderData.shared.fileProviderSignalDeleteContainerItemIdentifier.removeAll()
  123. }
  124. // Report the updated items
  125. //
  126. if self.enumeratedItemIdentifier == .workingSet {
  127. for (_, item) in fileProviderData.shared.fileProviderSignalUpdateWorkingSetItem {
  128. itemsUpdate.append(item)
  129. }
  130. fileProviderData.shared.fileProviderSignalUpdateWorkingSetItem.removeAll()
  131. } else {
  132. for (_, item) in fileProviderData.shared.fileProviderSignalUpdateContainerItem {
  133. itemsUpdate.append(item)
  134. }
  135. fileProviderData.shared.fileProviderSignalUpdateContainerItem.removeAll()
  136. }
  137. observer.didDeleteItems(withIdentifiers: itemsDelete)
  138. observer.didUpdate(itemsUpdate)
  139. let data = "\(self.anchor)".data(using: .utf8)
  140. observer.finishEnumeratingChanges(upTo: NSFileProviderSyncAnchor(data!), moreComing: false)
  141. }
  142. func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) {
  143. let data = "\(self.anchor)".data(using: .utf8)
  144. completionHandler(NSFileProviderSyncAnchor(data!))
  145. }
  146. func fetchItemsForPage(serverUrl: String, pageNumber: Int, completionHandler: @escaping (_ metadatas: Results<tableMetadata>?) -> Void) {
  147. let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.session.account, serverUrl)
  148. if pageNumber == 1 {
  149. NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: NCKeychain().showHiddenFiles, account: fileProviderData.shared.session.account) { _, files, _, error in
  150. if error == .success, let files {
  151. self.database.convertFilesToMetadatas(files, useFirstAsMetadataFolder: true) { metadataFolder, metadatas in
  152. /// FOLDER
  153. self.database.addMetadata(metadataFolder)
  154. self.database.addDirectory(e2eEncrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, richWorkspace: metadataFolder.richWorkspace, serverUrl: serverUrl, account: metadataFolder.account)
  155. /// FILES
  156. self.database.deleteMetadata(predicate: predicate)
  157. self.database.addMetadatas(metadatas)
  158. }
  159. }
  160. let resultsMetadata = self.database.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: self.recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
  161. completionHandler(resultsMetadata)
  162. }
  163. } else {
  164. let resultsMetadata = self.database.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
  165. completionHandler(resultsMetadata)
  166. }
  167. }
  168. }