mirror of https://github.com/nextcloud/desktop
Run swift-format on swift code
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
This commit is contained in:
parent
f1ed494b31
commit
3928573ff4
|
@ -20,36 +20,51 @@ extension NextcloudFilesDatabaseManager {
|
||||||
// We want to split by "/" (e.g. cloud.nc.com/files/a/b) but we need to be mindful of "https://c.nc.com"
|
// 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 problematicSeparator = "://"
|
||||||
let placeholderSeparator = "__TEMP_REPLACE__"
|
let placeholderSeparator = "__TEMP_REPLACE__"
|
||||||
let serverUrlWithoutPrefix = serverUrl.replacingOccurrences(of: problematicSeparator, with: placeholderSeparator)
|
let serverUrlWithoutPrefix = serverUrl.replacingOccurrences(
|
||||||
|
of: problematicSeparator, with: placeholderSeparator)
|
||||||
var splitServerUrl = serverUrlWithoutPrefix.split(separator: "/")
|
var splitServerUrl = serverUrlWithoutPrefix.split(separator: "/")
|
||||||
let directoryItemFileName = String(splitServerUrl.removeLast())
|
let directoryItemFileName = String(splitServerUrl.removeLast())
|
||||||
let directoryItemServerUrl = splitServerUrl.joined(separator: "/").replacingOccurrences(of: placeholderSeparator, with: problematicSeparator)
|
let directoryItemServerUrl = splitServerUrl.joined(separator: "/").replacingOccurrences(
|
||||||
|
of: placeholderSeparator, with: problematicSeparator)
|
||||||
|
|
||||||
if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@ AND directory == true", account, directoryItemServerUrl, directoryItemFileName).first {
|
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 NextcloudItemMetadataTable(value: metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func childItemsForDirectory(_ directoryMetadata: NextcloudItemMetadataTable) -> [NextcloudItemMetadataTable] {
|
func childItemsForDirectory(_ directoryMetadata: NextcloudItemMetadataTable)
|
||||||
|
-> [NextcloudItemMetadataTable]
|
||||||
|
{
|
||||||
let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("serverUrl BEGINSWITH %@", directoryServerUrl)
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"serverUrl BEGINSWITH %@", directoryServerUrl)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func childDirectoriesForDirectory(_ directoryMetadata: NextcloudItemMetadataTable) -> [NextcloudItemMetadataTable] {
|
func childDirectoriesForDirectory(_ directoryMetadata: NextcloudItemMetadataTable)
|
||||||
|
-> [NextcloudItemMetadataTable]
|
||||||
|
{
|
||||||
let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("serverUrl BEGINSWITH %@ AND directory == true", directoryServerUrl)
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"serverUrl BEGINSWITH %@ AND directory == true", directoryServerUrl)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable) -> NextcloudItemMetadataTable? {
|
func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable)
|
||||||
return directoryMetadata(account: itemMetadata.account, serverUrl: itemMetadata.serverUrl)
|
-> NextcloudItemMetadataTable?
|
||||||
|
{
|
||||||
|
directoryMetadata(account: itemMetadata.account, serverUrl: itemMetadata.serverUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func directoryMetadata(ocId: String) -> NextcloudItemMetadataTable? {
|
func directoryMetadata(ocId: String) -> NextcloudItemMetadataTable? {
|
||||||
if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first {
|
if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@ AND directory == true", ocId
|
||||||
|
).first {
|
||||||
return NextcloudItemMetadataTable(value: metadata)
|
return NextcloudItemMetadataTable(value: metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,20 +72,31 @@ extension NextcloudFilesDatabaseManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
func directoryMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
func directoryMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND directory == true", account)
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND directory == true", account)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func directoryMetadatas(account: String, parentDirectoryServerUrl: String) -> [NextcloudItemMetadataTable] {
|
func directoryMetadatas(account: String, parentDirectoryServerUrl: String)
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@ AND directory == true", account, parentDirectoryServerUrl)
|
-> [NextcloudItemMetadataTable]
|
||||||
|
{
|
||||||
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND parentDirectoryServerUrl == %@ AND directory == true", account,
|
||||||
|
parentDirectoryServerUrl)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes all metadatas related to the info of the directory provided
|
// Deletes all metadatas related to the info of the directory provided
|
||||||
func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) -> [NextcloudItemMetadataTable]? {
|
func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) -> [NextcloudItemMetadataTable]? {
|
||||||
let database = ncDatabase()
|
let database = ncDatabase()
|
||||||
guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
|
guard
|
||||||
Logger.ncFilesDatabase.error("Could not find directory metadata for ocId \(ocId, privacy: .public). Not proceeding with deletion")
|
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 nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,20 +105,25 @@ extension NextcloudFilesDatabaseManager {
|
||||||
let directoryAccount = directoryMetadata.account
|
let directoryAccount = directoryMetadata.account
|
||||||
let directoryEtag = directoryMetadata.etag
|
let directoryEtag = directoryMetadata.etag
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
guard deleteItemMetadata(ocId: directoryMetadata.ocId) else {
|
guard deleteItemMetadata(ocId: directoryMetadata.ocId) else {
|
||||||
Logger.ncFilesDatabase.debug("Failure to delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Failure to delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)"
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var deletedMetadatas: [NextcloudItemMetadataTable] = [directoryMetadataCopy]
|
var deletedMetadatas: [NextcloudItemMetadataTable] = [directoryMetadataCopy]
|
||||||
|
|
||||||
let results = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryAccount, directoryUrlPath)
|
let results = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl BEGINSWITH %@", directoryAccount, directoryUrlPath)
|
||||||
|
|
||||||
for result in results {
|
for result in results {
|
||||||
let successfulItemMetadataDelete = deleteItemMetadata(ocId: result.ocId)
|
let successfulItemMetadataDelete = deleteItemMetadata(ocId: result.ocId)
|
||||||
if (successfulItemMetadataDelete) {
|
if successfulItemMetadataDelete {
|
||||||
deletedMetadatas.append(NextcloudItemMetadataTable(value: result))
|
deletedMetadatas.append(NextcloudItemMetadataTable(value: result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,24 +132,35 @@ extension NextcloudFilesDatabaseManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Completed deletions in directory recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Completed deletions in directory recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
return deletedMetadatas
|
return deletedMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameDirectoryAndPropagateToChildren(ocId: String, newServerUrl: String, newFileName: String) -> [NextcloudItemMetadataTable]? {
|
func renameDirectoryAndPropagateToChildren(
|
||||||
|
ocId: String, newServerUrl: String, newFileName: String
|
||||||
|
) -> [NextcloudItemMetadataTable]? {
|
||||||
let database = ncDatabase()
|
let database = ncDatabase()
|
||||||
|
|
||||||
guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
|
guard
|
||||||
Logger.ncFilesDatabase.error("Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming")
|
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 nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldItemServerUrl = directoryMetadata.serverUrl
|
let oldItemServerUrl = directoryMetadata.serverUrl
|
||||||
let oldDirectoryServerUrl = oldItemServerUrl + "/" + directoryMetadata.fileName
|
let oldDirectoryServerUrl = oldItemServerUrl + "/" + directoryMetadata.fileName
|
||||||
let newDirectoryServerUrl = newServerUrl + "/" + newFileName
|
let newDirectoryServerUrl = newServerUrl + "/" + newFileName
|
||||||
let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, oldDirectoryServerUrl)
|
let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account,
|
||||||
|
oldDirectoryServerUrl)
|
||||||
|
|
||||||
renameItemMetadata(ocId: ocId, newServerUrl: newServerUrl, newFileName: newFileName)
|
renameItemMetadata(ocId: ocId, newServerUrl: newServerUrl, newFileName: newFileName)
|
||||||
Logger.ncFilesDatabase.debug("Renamed root renaming directory")
|
Logger.ncFilesDatabase.debug("Renamed root renaming directory")
|
||||||
|
@ -127,19 +169,25 @@ extension NextcloudFilesDatabaseManager {
|
||||||
try database.write {
|
try database.write {
|
||||||
for childItem in childItemResults {
|
for childItem in childItemResults {
|
||||||
let oldServerUrl = childItem.serverUrl
|
let oldServerUrl = childItem.serverUrl
|
||||||
let movedServerUrl = oldServerUrl.replacingOccurrences(of: oldDirectoryServerUrl, with: newDirectoryServerUrl)
|
let movedServerUrl = oldServerUrl.replacingOccurrences(
|
||||||
|
of: oldDirectoryServerUrl, with: newDirectoryServerUrl)
|
||||||
childItem.serverUrl = movedServerUrl
|
childItem.serverUrl = movedServerUrl
|
||||||
database.add(childItem, update: .all)
|
database.add(childItem, update: .all)
|
||||||
Logger.ncFilesDatabase.debug("Moved childItem at \(oldServerUrl) to \(movedServerUrl)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Moved childItem at \(oldServerUrl) to \(movedServerUrl)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not rename directory metadata with ocId: \(ocId, privacy: .public) to new serverUrl: \(newServerUrl), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not rename directory metadata with ocId: \(ocId, privacy: .public) to new serverUrl: \(newServerUrl), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedChildItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, newDirectoryServerUrl)
|
let updatedChildItemResults = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account,
|
||||||
|
newDirectoryServerUrl)
|
||||||
return sortedItemMetadatas(updatedChildItemResults)
|
return sortedItemMetadatas(updatedChildItemResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import RealmSwift
|
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import RealmSwift
|
||||||
|
|
||||||
extension NextcloudFilesDatabaseManager {
|
extension NextcloudFilesDatabaseManager {
|
||||||
func localFileMetadataFromOcId(_ ocId: String) -> NextcloudLocalFileMetadataTable? {
|
func localFileMetadataFromOcId(_ ocId: String) -> NextcloudLocalFileMetadataTable? {
|
||||||
if let metadata = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter("ocId == %@", ocId).first {
|
if let metadata = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter(
|
||||||
|
"ocId == %@", ocId
|
||||||
|
).first {
|
||||||
return NextcloudLocalFileMetadataTable(value: metadata)
|
return NextcloudLocalFileMetadataTable(value: metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +43,14 @@ extension NextcloudFilesDatabaseManager {
|
||||||
newLocalFileMetadata.exifLongitude = "-1"
|
newLocalFileMetadata.exifLongitude = "-1"
|
||||||
|
|
||||||
database.add(newLocalFileMetadata, update: .all)
|
database.add(newLocalFileMetadata, update: .all)
|
||||||
Logger.ncFilesDatabase.debug("Added local file metadata from item metadata. ocID: \(itemMetadata.ocId, privacy: .public), etag: \(itemMetadata.etag, privacy: .public), fileName: \(itemMetadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Added local file metadata from item metadata. ocID: \(itemMetadata.ocId, privacy: .public), etag: \(itemMetadata.etag, privacy: .public), fileName: \(itemMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not add local file metadata from item metadata. ocID: \(itemMetadata.ocId, privacy: .public), etag: \(itemMetadata.etag, privacy: .public), fileName: \(itemMetadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not add local file metadata from item metadata. ocID: \(itemMetadata.ocId, privacy: .public), etag: \(itemMetadata.etag, privacy: .public), fileName: \(itemMetadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,34 +59,42 @@ extension NextcloudFilesDatabaseManager {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try database.write {
|
try database.write {
|
||||||
let results = database.objects(NextcloudLocalFileMetadataTable.self).filter("ocId == %@", ocId)
|
let results = database.objects(NextcloudLocalFileMetadataTable.self).filter(
|
||||||
|
"ocId == %@", ocId)
|
||||||
database.delete(results)
|
database.delete(results)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not delete local file metadata with ocId: \(ocId, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not delete local file metadata with ocId: \(ocId, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sortedLocalFileMetadatas(_ metadatas: Results<NextcloudLocalFileMetadataTable>) -> [NextcloudLocalFileMetadataTable] {
|
private func sortedLocalFileMetadatas(_ metadatas: Results<NextcloudLocalFileMetadataTable>)
|
||||||
|
-> [NextcloudLocalFileMetadataTable]
|
||||||
|
{
|
||||||
let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
|
let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
|
||||||
return Array(sortedMetadatas.map { NextcloudLocalFileMetadataTable(value: $0) })
|
return Array(sortedMetadatas.map { NextcloudLocalFileMetadataTable(value: $0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func localFileMetadatas(account: String) -> [NextcloudLocalFileMetadataTable] {
|
func localFileMetadatas(account: String) -> [NextcloudLocalFileMetadataTable] {
|
||||||
let results = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter("account == %@", account)
|
let results = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter(
|
||||||
|
"account == %@", account)
|
||||||
return sortedLocalFileMetadatas(results)
|
return sortedLocalFileMetadatas(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func localFileItemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
func localFileItemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
||||||
let localFileMetadatas = localFileMetadatas(account: account)
|
let localFileMetadatas = localFileMetadatas(account: account)
|
||||||
let localFileMetadatasOcIds = Array(localFileMetadatas.map { $0.ocId })
|
let localFileMetadatasOcIds = Array(localFileMetadatas.map(\.ocId))
|
||||||
|
|
||||||
var itemMetadatas: [NextcloudItemMetadataTable] = []
|
var itemMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
for ocId in localFileMetadatasOcIds {
|
for ocId in localFileMetadatasOcIds {
|
||||||
guard let itemMetadata = itemMetadataFromOcId(ocId) else {
|
guard let itemMetadata = itemMetadataFromOcId(ocId) else {
|
||||||
Logger.ncFilesDatabase.error("Could not find matching item metadata for local file metadata with ocId: \(ocId, privacy: .public) with request from account: \(account)")
|
Logger.ncFilesDatabase.error(
|
||||||
continue;
|
"Could not find matching item metadata for local file metadata with ocId: \(ocId, privacy: .public) with request from account: \(account)"
|
||||||
|
)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
itemMetadatas.append(NextcloudItemMetadataTable(value: itemMetadata))
|
itemMetadatas.append(NextcloudItemMetadataTable(value: itemMetadata))
|
||||||
|
|
|
@ -12,16 +12,14 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import RealmSwift
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import RealmSwift
|
||||||
|
|
||||||
class NextcloudFilesDatabaseManager : NSObject {
|
class NextcloudFilesDatabaseManager: NSObject {
|
||||||
static let shared = {
|
static let shared = NextcloudFilesDatabaseManager()
|
||||||
return NextcloudFilesDatabaseManager();
|
|
||||||
}()
|
|
||||||
|
|
||||||
let relativeDatabaseFolderPath = "Database/"
|
let relativeDatabaseFolderPath = "Database/"
|
||||||
let databaseFilename = "fileproviderextdatabase.realm"
|
let databaseFilename = "fileproviderextdatabase.realm"
|
||||||
|
@ -31,29 +29,36 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
let schemaVersion: UInt64 = 100
|
let schemaVersion: UInt64 = 100
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.relativeDatabaseFilePath = self.relativeDatabaseFolderPath + self.databaseFilename
|
relativeDatabaseFilePath = relativeDatabaseFolderPath + databaseFilename
|
||||||
|
|
||||||
guard let fileProviderDataDirUrl = pathForFileProviderExtData() else {
|
guard let fileProviderDataDirUrl = pathForFileProviderExtData() else {
|
||||||
super.init()
|
super.init()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.databasePath = fileProviderDataDirUrl.appendingPathComponent(self.relativeDatabaseFilePath)
|
databasePath = fileProviderDataDirUrl.appendingPathComponent(relativeDatabaseFilePath)
|
||||||
|
|
||||||
// Disable file protection for directory DB
|
// Disable file protection for directory DB
|
||||||
// https://docs.mongodb.com/realm/sdk/ios/examples/configure-and-open-a-realm/#std-label-ios-open-a-local-realm
|
// https://docs.mongodb.com/realm/sdk/ios/examples/configure-and-open-a-realm/#std-label-ios-open-a-local-realm
|
||||||
let dbFolder = fileProviderDataDirUrl.appendingPathComponent(self.relativeDatabaseFolderPath)
|
let dbFolder = fileProviderDataDirUrl.appendingPathComponent(relativeDatabaseFolderPath)
|
||||||
let dbFolderPath = dbFolder.path
|
let dbFolderPath = dbFolder.path
|
||||||
do {
|
do {
|
||||||
try FileManager.default.createDirectory(at: dbFolder, withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: dbFolder, withIntermediateDirectories: true)
|
||||||
try FileManager.default.setAttributes([FileAttributeKey.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], ofItemAtPath: dbFolderPath)
|
try FileManager.default.setAttributes(
|
||||||
} catch let error {
|
[
|
||||||
Logger.ncFilesDatabase.error("Could not set permission level for File Provider database folder, received error: \(error.localizedDescription, privacy: .public)")
|
FileAttributeKey.protectionKey: FileProtectionType
|
||||||
|
.completeUntilFirstUserAuthentication
|
||||||
|
],
|
||||||
|
ofItemAtPath: dbFolderPath)
|
||||||
|
} catch {
|
||||||
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not set permission level for File Provider database folder, received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = Realm.Configuration(
|
let config = Realm.Configuration(
|
||||||
fileURL: self.databasePath,
|
fileURL: databasePath,
|
||||||
schemaVersion: self.schemaVersion,
|
schemaVersion: schemaVersion,
|
||||||
objectTypes: [NextcloudItemMetadataTable.self, NextcloudLocalFileMetadataTable.self]
|
objectTypes: [NextcloudItemMetadataTable.self, NextcloudLocalFileMetadataTable.self]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +68,8 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
_ = try Realm()
|
_ = try Realm()
|
||||||
Logger.ncFilesDatabase.info("Successfully started Realm db for FileProviderExt")
|
Logger.ncFilesDatabase.info("Successfully started Realm db for FileProviderExt")
|
||||||
} catch let error as NSError {
|
} catch let error as NSError {
|
||||||
Logger.ncFilesDatabase.error("Error opening Realm db: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Error opening Realm db: \(error.localizedDescription, privacy: .public)")
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
@ -76,81 +82,106 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func anyItemMetadatasForAccount(_ account: String) -> Bool {
|
func anyItemMetadatasForAccount(_ account: String) -> Bool {
|
||||||
return !ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@", account).isEmpty
|
!ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@", account)
|
||||||
|
.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemMetadataFromOcId(_ ocId: String) -> NextcloudItemMetadataTable? {
|
func itemMetadataFromOcId(_ ocId: String) -> NextcloudItemMetadataTable? {
|
||||||
// Realm objects are live-fire, i.e. they will be changed and invalidated according to changes in the db
|
// Realm objects are live-fire, i.e. they will be changed and invalidated according to changes in the db
|
||||||
// Let's therefore create a copy
|
// Let's therefore create a copy
|
||||||
if let itemMetadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("ocId == %@", ocId).first {
|
if let itemMetadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@", ocId
|
||||||
|
).first {
|
||||||
return NextcloudItemMetadataTable(value: itemMetadata)
|
return NextcloudItemMetadataTable(value: itemMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortedItemMetadatas(_ metadatas: Results<NextcloudItemMetadataTable>) -> [NextcloudItemMetadataTable] {
|
func sortedItemMetadatas(_ metadatas: Results<NextcloudItemMetadataTable>)
|
||||||
|
-> [NextcloudItemMetadataTable]
|
||||||
|
{
|
||||||
let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
|
let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
|
||||||
return Array(sortedMetadatas.map { NextcloudItemMetadataTable(value: $0) })
|
return Array(sortedMetadatas.map { NextcloudItemMetadataTable(value: $0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
func itemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@", account)
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@", account)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemMetadatas(account: String, serverUrl: String) -> [NextcloudItemMetadataTable] {
|
func itemMetadatas(account: String, serverUrl: String) -> [NextcloudItemMetadataTable] {
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@", account, serverUrl)
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl == %@", account, serverUrl)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemMetadatas(account: String, serverUrl: String, status: NextcloudItemMetadataTable.Status) -> [NextcloudItemMetadataTable] {
|
func itemMetadatas(
|
||||||
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@ AND status == %@", account, serverUrl, status.rawValue)
|
account: String, serverUrl: String, status: NextcloudItemMetadataTable.Status
|
||||||
|
)
|
||||||
|
-> [NextcloudItemMetadataTable]
|
||||||
|
{
|
||||||
|
let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl == %@ AND status == %@", account, serverUrl,
|
||||||
|
status.rawValue)
|
||||||
return sortedItemMetadatas(metadatas)
|
return sortedItemMetadatas(metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemMetadataFromFileProviderItemIdentifier(_ identifier: NSFileProviderItemIdentifier) -> NextcloudItemMetadataTable? {
|
func itemMetadataFromFileProviderItemIdentifier(_ identifier: NSFileProviderItemIdentifier)
|
||||||
|
-> NextcloudItemMetadataTable?
|
||||||
|
{
|
||||||
let ocId = identifier.rawValue
|
let ocId = identifier.rawValue
|
||||||
return itemMetadataFromOcId(ocId)
|
return itemMetadataFromOcId(ocId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processItemMetadatasToDelete(existingMetadatas: Results<NextcloudItemMetadataTable>,
|
private func processItemMetadatasToDelete(
|
||||||
updatedMetadatas: [NextcloudItemMetadataTable]) -> [NextcloudItemMetadataTable] {
|
existingMetadatas: Results<NextcloudItemMetadataTable>,
|
||||||
|
updatedMetadatas: [NextcloudItemMetadataTable]
|
||||||
|
) -> [NextcloudItemMetadataTable] {
|
||||||
var deletedMetadatas: [NextcloudItemMetadataTable] = []
|
var deletedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
for existingMetadata in existingMetadatas {
|
for existingMetadata in existingMetadatas {
|
||||||
guard !updatedMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
|
guard !updatedMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
|
||||||
let metadataToDelete = itemMetadataFromOcId(existingMetadata.ocId) else { continue }
|
let metadataToDelete = itemMetadataFromOcId(existingMetadata.ocId)
|
||||||
|
else { continue }
|
||||||
|
|
||||||
deletedMetadatas.append(metadataToDelete)
|
deletedMetadatas.append(metadataToDelete)
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Deleting item metadata during update. ocID: \(existingMetadata.ocId, privacy: .public), etag: \(existingMetadata.etag, privacy: .public), fileName: \(existingMetadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Deleting item metadata during update. ocID: \(existingMetadata.ocId, privacy: .public), etag: \(existingMetadata.etag, privacy: .public), fileName: \(existingMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deletedMetadatas
|
return deletedMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processItemMetadatasToUpdate(existingMetadatas: Results<NextcloudItemMetadataTable>,
|
private func processItemMetadatasToUpdate(
|
||||||
updatedMetadatas: [NextcloudItemMetadataTable],
|
existingMetadatas: Results<NextcloudItemMetadataTable>,
|
||||||
updateDirectoryEtags: Bool) -> (newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable], directoriesNeedingRename: [NextcloudItemMetadataTable]) {
|
updatedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
updateDirectoryEtags: Bool
|
||||||
|
) -> (
|
||||||
|
newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
directoriesNeedingRename: [NextcloudItemMetadataTable]
|
||||||
|
) {
|
||||||
var returningNewMetadatas: [NextcloudItemMetadataTable] = []
|
var returningNewMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
var returningUpdatedMetadatas: [NextcloudItemMetadataTable] = []
|
var returningUpdatedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
var directoriesNeedingRename: [NextcloudItemMetadataTable] = []
|
var directoriesNeedingRename: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
for updatedMetadata in updatedMetadatas {
|
for updatedMetadata in updatedMetadatas {
|
||||||
if let existingMetadata = existingMetadatas.first(where: { $0.ocId == updatedMetadata.ocId }) {
|
if let existingMetadata = existingMetadatas.first(where: {
|
||||||
|
$0.ocId == updatedMetadata.ocId
|
||||||
if existingMetadata.status == NextcloudItemMetadataTable.Status.normal.rawValue &&
|
}) {
|
||||||
!existingMetadata.isInSameDatabaseStoreableRemoteState(updatedMetadata) {
|
if existingMetadata.status == NextcloudItemMetadataTable.Status.normal.rawValue,
|
||||||
|
!existingMetadata.isInSameDatabaseStoreableRemoteState(updatedMetadata)
|
||||||
|
{
|
||||||
if updatedMetadata.directory {
|
if updatedMetadata.directory {
|
||||||
|
if updatedMetadata.serverUrl != existingMetadata.serverUrl
|
||||||
if updatedMetadata.serverUrl != existingMetadata.serverUrl || updatedMetadata.fileName != existingMetadata.fileName {
|
|| updatedMetadata.fileName != existingMetadata.fileName
|
||||||
|
{
|
||||||
directoriesNeedingRename.append(NextcloudItemMetadataTable(value: updatedMetadata))
|
directoriesNeedingRename.append(
|
||||||
updatedMetadata.etag = "" // Renaming doesn't change the etag so reset manually
|
NextcloudItemMetadataTable(value: updatedMetadata))
|
||||||
|
updatedMetadata.etag = "" // Renaming doesn't change the etag so reset manually
|
||||||
|
|
||||||
} else if !updateDirectoryEtags {
|
} else if !updateDirectoryEtags {
|
||||||
updatedMetadata.etag = existingMetadata.etag
|
updatedMetadata.etag = existingMetadata.etag
|
||||||
|
@ -159,49 +190,68 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
|
|
||||||
returningUpdatedMetadatas.append(updatedMetadata)
|
returningUpdatedMetadatas.append(updatedMetadata)
|
||||||
|
|
||||||
|
Logger.ncFilesDatabase.debug(
|
||||||
Logger.ncFilesDatabase.debug("Updated existing item metadata. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)")
|
"Updated existing item metadata. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Logger.ncFilesDatabase.debug("Skipping item metadata update; same as existing, or still downloading/uploading. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Skipping item metadata update; same as existing, or still downloading/uploading. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // This is a new metadata
|
} else { // This is a new metadata
|
||||||
if !updateDirectoryEtags && updatedMetadata.directory {
|
if !updateDirectoryEtags, updatedMetadata.directory {
|
||||||
updatedMetadata.etag = ""
|
updatedMetadata.etag = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
returningNewMetadatas.append(updatedMetadata)
|
returningNewMetadatas.append(updatedMetadata)
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Created new item metadata during update. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Created new item metadata during update. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (returningNewMetadatas, returningUpdatedMetadatas, directoriesNeedingRename)
|
return (returningNewMetadatas, returningUpdatedMetadatas, directoriesNeedingRename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateItemMetadatas(account: String, serverUrl: String, updatedMetadatas: [NextcloudItemMetadataTable], updateDirectoryEtags: Bool) -> (newMetadatas: [NextcloudItemMetadataTable]?, updatedMetadatas: [NextcloudItemMetadataTable]?, deletedMetadatas: [NextcloudItemMetadataTable]?) {
|
func updateItemMetadatas(
|
||||||
|
account: String, serverUrl: String, updatedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
updateDirectoryEtags: Bool
|
||||||
|
) -> (
|
||||||
|
newMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
updatedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
deletedMetadatas: [NextcloudItemMetadataTable]?
|
||||||
|
) {
|
||||||
let database = ncDatabase()
|
let database = ncDatabase()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let existingMetadatas = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@ AND status == %@", account, serverUrl, NextcloudItemMetadataTable.Status.normal.rawValue)
|
let existingMetadatas = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"account == %@ AND serverUrl == %@ AND status == %@", account, serverUrl,
|
||||||
|
NextcloudItemMetadataTable.Status.normal.rawValue)
|
||||||
|
|
||||||
let metadatasToDelete = processItemMetadatasToDelete(existingMetadatas: existingMetadatas,
|
let metadatasToDelete = processItemMetadatasToDelete(
|
||||||
updatedMetadatas: updatedMetadatas)
|
existingMetadatas: existingMetadatas,
|
||||||
|
updatedMetadatas: updatedMetadatas)
|
||||||
|
|
||||||
let metadatasToChange = processItemMetadatasToUpdate(existingMetadatas: existingMetadatas,
|
let metadatasToChange = processItemMetadatasToUpdate(
|
||||||
updatedMetadatas: updatedMetadatas,
|
existingMetadatas: existingMetadatas,
|
||||||
updateDirectoryEtags: updateDirectoryEtags)
|
updatedMetadatas: updatedMetadatas,
|
||||||
|
updateDirectoryEtags: updateDirectoryEtags)
|
||||||
|
|
||||||
var metadatasToUpdate = metadatasToChange.updatedMetadatas
|
var metadatasToUpdate = metadatasToChange.updatedMetadatas
|
||||||
let metadatasToCreate = metadatasToChange.newMetadatas
|
let metadatasToCreate = metadatasToChange.newMetadatas
|
||||||
let directoriesNeedingRename = metadatasToChange.directoriesNeedingRename
|
let directoriesNeedingRename = metadatasToChange.directoriesNeedingRename
|
||||||
|
|
||||||
let metadatasToAdd = Array(metadatasToUpdate.map { NextcloudItemMetadataTable(value: $0) }) +
|
let metadatasToAdd =
|
||||||
Array(metadatasToCreate.map { NextcloudItemMetadataTable(value: $0) })
|
Array(metadatasToUpdate.map { NextcloudItemMetadataTable(value: $0) })
|
||||||
|
+ Array(metadatasToCreate.map { NextcloudItemMetadataTable(value: $0) })
|
||||||
|
|
||||||
for metadata in directoriesNeedingRename {
|
for metadata in directoriesNeedingRename {
|
||||||
|
if let updatedDirectoryChildren = renameDirectoryAndPropagateToChildren(
|
||||||
if let updatedDirectoryChildren = renameDirectoryAndPropagateToChildren(ocId: metadata.ocId, newServerUrl: metadata.serverUrl, newFileName: metadata.fileName) {
|
ocId: metadata.ocId, newServerUrl: metadata.serverUrl,
|
||||||
|
newFileName: metadata.fileName)
|
||||||
|
{
|
||||||
metadatasToUpdate += updatedDirectoryChildren
|
metadatasToUpdate += updatedDirectoryChildren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,40 +259,59 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
try database.write {
|
try database.write {
|
||||||
for metadata in metadatasToDelete {
|
for metadata in metadatasToDelete {
|
||||||
// Can't pass copies, we need the originals from the database
|
// Can't pass copies, we need the originals from the database
|
||||||
database.delete(ncDatabase().objects(NextcloudItemMetadataTable.self).filter("ocId == %@", metadata.ocId))
|
database.delete(
|
||||||
|
ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@", metadata.ocId))
|
||||||
}
|
}
|
||||||
|
|
||||||
for metadata in metadatasToAdd {
|
for metadata in metadatasToAdd {
|
||||||
database.add(metadata, update: .all)
|
database.add(metadata, update: .all)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (newMetadatas: metadatasToCreate, updatedMetadatas: metadatasToUpdate, deletedMetadatas: metadatasToDelete)
|
return (
|
||||||
} catch let error {
|
newMetadatas: metadatasToCreate, updatedMetadatas: metadatasToUpdate,
|
||||||
Logger.ncFilesDatabase.error("Could not update any item metadatas, received error: \(error.localizedDescription, privacy: .public)")
|
deletedMetadatas: metadatasToDelete
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not update any item metadatas, received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
return (nil, nil, nil)
|
return (nil, nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setStatusForItemMetadata(_ metadata: NextcloudItemMetadataTable, status: NextcloudItemMetadataTable.Status, completionHandler: @escaping(_ updatedMetadata: NextcloudItemMetadataTable?) -> Void) {
|
func setStatusForItemMetadata(
|
||||||
|
_ metadata: NextcloudItemMetadataTable, status: NextcloudItemMetadataTable.Status,
|
||||||
|
completionHandler: @escaping (_ updatedMetadata: NextcloudItemMetadataTable?) -> Void
|
||||||
|
) {
|
||||||
let database = ncDatabase()
|
let database = ncDatabase()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try database.write {
|
try database.write {
|
||||||
guard let result = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@", metadata.ocId).first else {
|
guard
|
||||||
Logger.ncFilesDatabase.debug("Did not update status for item metadata as it was not found. ocID: \(metadata.ocId, privacy: .public)")
|
let result = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@", metadata.ocId
|
||||||
|
).first
|
||||||
|
else {
|
||||||
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Did not update status for item metadata as it was not found. ocID: \(metadata.ocId, privacy: .public)"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result.status = status.rawValue
|
result.status = status.rawValue
|
||||||
database.add(result, update: .all)
|
database.add(result, update: .all)
|
||||||
Logger.ncFilesDatabase.debug("Updated status for item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Updated status for item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
completionHandler(NextcloudItemMetadataTable(value: result))
|
completionHandler(NextcloudItemMetadataTable(value: result))
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not update status for item metadata with ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not update status for item metadata with ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,10 +322,14 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
do {
|
do {
|
||||||
try database.write {
|
try database.write {
|
||||||
database.add(metadata, update: .all)
|
database.add(metadata, update: .all)
|
||||||
Logger.ncFilesDatabase.debug("Added item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Added item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not add item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not add item metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,15 +338,18 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try database.write {
|
try database.write {
|
||||||
let results = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@", ocId)
|
let results = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@", ocId)
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Deleting item metadata. \(ocId, privacy: .public)")
|
Logger.ncFilesDatabase.debug("Deleting item metadata. \(ocId, privacy: .public)")
|
||||||
database.delete(results)
|
database.delete(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not delete item metadata with ocId: \(ocId, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not delete item metadata with ocId: \(ocId, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,8 +359,14 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try database.write {
|
try database.write {
|
||||||
guard let itemMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@", ocId).first else {
|
guard
|
||||||
Logger.ncFilesDatabase.debug("Could not find an item with ocID \(ocId, privacy: .public) to rename to \(newFileName, privacy: .public)")
|
let itemMetadata = database.objects(NextcloudItemMetadataTable.self).filter(
|
||||||
|
"ocId == %@", ocId
|
||||||
|
).first
|
||||||
|
else {
|
||||||
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Could not find an item with ocID \(ocId, privacy: .public) to rename to \(newFileName, privacy: .public)"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,14 +379,20 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
|
|
||||||
database.add(itemMetadata, update: .all)
|
database.add(itemMetadata, update: .all)
|
||||||
|
|
||||||
Logger.ncFilesDatabase.debug("Renamed item \(oldFileName, privacy: .public) to \(newFileName, privacy: .public), moved from serverUrl: \(oldServerUrl, privacy: .public) to serverUrl: \(newServerUrl, privacy: .public)")
|
Logger.ncFilesDatabase.debug(
|
||||||
|
"Renamed item \(oldFileName, privacy: .public) to \(newFileName, privacy: .public), moved from serverUrl: \(oldServerUrl, privacy: .public) to serverUrl: \(newServerUrl, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.ncFilesDatabase.error("Could not rename filename of item metadata with ocID: \(ocId, privacy: .public) to proposed name \(newFileName, privacy: .public) at proposed serverUrl \(newServerUrl, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not rename filename of item metadata with ocID: \(ocId, privacy: .public) to proposed name \(newFileName, privacy: .public) at proposed serverUrl \(newServerUrl, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parentItemIdentifierFromMetadata(_ metadata: NextcloudItemMetadataTable) -> NSFileProviderItemIdentifier? {
|
func parentItemIdentifierFromMetadata(_ metadata: NextcloudItemMetadataTable)
|
||||||
|
-> NSFileProviderItemIdentifier?
|
||||||
|
{
|
||||||
let homeServerFilesUrl = metadata.urlBase + "/remote.php/dav/files/" + metadata.userId
|
let homeServerFilesUrl = metadata.urlBase + "/remote.php/dav/files/" + metadata.userId
|
||||||
|
|
||||||
if metadata.serverUrl == homeServerFilesUrl {
|
if metadata.serverUrl == homeServerFilesUrl {
|
||||||
|
@ -312,7 +400,9 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let itemParentDirectory = parentDirectoryMetadataForItem(metadata) else {
|
guard let itemParentDirectory = parentDirectoryMetadataForItem(metadata) else {
|
||||||
Logger.ncFilesDatabase.error("Could not get item parent directory metadata for metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not get item parent directory metadata for metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +410,9 @@ class NextcloudFilesDatabaseManager : NSObject {
|
||||||
return NSFileProviderItemIdentifier(parentDirectoryMetadata.ocId)
|
return NSFileProviderItemIdentifier(parentDirectoryMetadata.ocId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.ncFilesDatabase.error("Could not get item parent directory item metadata for metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)")
|
Logger.ncFilesDatabase.error(
|
||||||
|
"Could not get item parent directory item metadata for metadata. ocID: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), fileName: \(metadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,10 @@ extension NextcloudItemMetadataTable {
|
||||||
}
|
}
|
||||||
metadata.size = file.size
|
metadata.size = file.size
|
||||||
metadata.classFile = file.classFile
|
metadata.classFile = file.classFile
|
||||||
//FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
|
// FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
|
||||||
if (metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown") && metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue {
|
if metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown",
|
||||||
|
metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue
|
||||||
|
{
|
||||||
metadata.classFile = NKCommon.TypeClassFile.document.rawValue
|
metadata.classFile = NKCommon.TypeClassFile.document.rawValue
|
||||||
}
|
}
|
||||||
if let date = file.uploadDate {
|
if let date = file.uploadDate {
|
||||||
|
@ -87,26 +89,30 @@ extension NextcloudItemMetadataTable {
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
static func metadatasFromDirectoryReadNKFiles(_ files: [NKFile],
|
static func metadatasFromDirectoryReadNKFiles(
|
||||||
account: String,
|
_ files: [NKFile],
|
||||||
completionHandler: @escaping (_ directoryMetadata: NextcloudItemMetadataTable,
|
account: String,
|
||||||
_ childDirectoriesMetadatas: [NextcloudItemMetadataTable],
|
completionHandler: @escaping (
|
||||||
_ metadatas: [NextcloudItemMetadataTable]) -> Void) {
|
_ directoryMetadata: NextcloudItemMetadataTable,
|
||||||
|
_ childDirectoriesMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
_ metadatas: [NextcloudItemMetadataTable]
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
var directoryMetadataSet = false
|
var directoryMetadataSet = false
|
||||||
var directoryMetadata = NextcloudItemMetadataTable()
|
var directoryMetadata = NextcloudItemMetadataTable()
|
||||||
var childDirectoriesMetadatas: [NextcloudItemMetadataTable] = []
|
var childDirectoriesMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
var metadatas: [NextcloudItemMetadataTable] = []
|
var metadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
let conversionQueue = DispatchQueue(label: "nkFileToMetadataConversionQueue", qos: .userInitiated, attributes: .concurrent)
|
let conversionQueue = DispatchQueue(
|
||||||
let appendQueue = DispatchQueue(label: "metadataAppendQueue", qos: .userInitiated) // Serial queue
|
label: "nkFileToMetadataConversionQueue", qos: .userInitiated, attributes: .concurrent)
|
||||||
|
let appendQueue = DispatchQueue(label: "metadataAppendQueue", qos: .userInitiated) // Serial queue
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
for file in files {
|
for file in files {
|
||||||
if metadatas.isEmpty && !directoryMetadataSet {
|
if metadatas.isEmpty, !directoryMetadataSet {
|
||||||
let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
|
let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
|
||||||
directoryMetadata = metadata;
|
directoryMetadata = metadata
|
||||||
directoryMetadataSet = true;
|
directoryMetadataSet = true
|
||||||
} else {
|
} else {
|
||||||
conversionQueue.async(group: dispatchGroup) {
|
conversionQueue.async(group: dispatchGroup) {
|
||||||
let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
|
let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import RealmSwift
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
|
import RealmSwift
|
||||||
|
|
||||||
class NextcloudItemMetadataTable: Object {
|
class NextcloudItemMetadataTable: Object {
|
||||||
enum Status: Int {
|
enum Status: Int {
|
||||||
|
@ -71,7 +71,7 @@ class NextcloudItemMetadataTable: Object {
|
||||||
@Persisted var isExtractFile: Bool = false
|
@Persisted var isExtractFile: Bool = false
|
||||||
@Persisted var livePhoto: Bool = false
|
@Persisted var livePhoto: Bool = false
|
||||||
@Persisted var mountType = ""
|
@Persisted var mountType = ""
|
||||||
@Persisted var name = "" // for unifiedSearch is the provider.id
|
@Persisted var name = "" // for unifiedSearch is the provider.id
|
||||||
@Persisted var note = ""
|
@Persisted var note = ""
|
||||||
@Persisted var ownerId = ""
|
@Persisted var ownerId = ""
|
||||||
@Persisted var ownerDisplayName = ""
|
@Persisted var ownerDisplayName = ""
|
||||||
|
@ -88,13 +88,13 @@ class NextcloudItemMetadataTable: Object {
|
||||||
@Persisted var quotaAvailableBytes: Int64 = 0
|
@Persisted var quotaAvailableBytes: Int64 = 0
|
||||||
@Persisted var resourceType = ""
|
@Persisted var resourceType = ""
|
||||||
@Persisted var richWorkspace: String?
|
@Persisted var richWorkspace: String?
|
||||||
@Persisted var serverUrl = "" // For parent directory!!
|
@Persisted var serverUrl = "" // For parent directory!!
|
||||||
@Persisted var session = ""
|
@Persisted var session = ""
|
||||||
@Persisted var sessionError = ""
|
@Persisted var sessionError = ""
|
||||||
@Persisted var sessionSelector = ""
|
@Persisted var sessionSelector = ""
|
||||||
@Persisted var sessionTaskIdentifier: Int = 0
|
@Persisted var sessionTaskIdentifier: Int = 0
|
||||||
@Persisted var sharePermissionsCollaborationServices: Int = 0
|
@Persisted var sharePermissionsCollaborationServices: Int = 0
|
||||||
let sharePermissionsCloudMesh = List<String>() // TODO: Find a way to compare these in remote state check
|
let sharePermissionsCloudMesh = List<String>() // TODO: Find a way to compare these in remote state check
|
||||||
let shareType = List<Int>()
|
let shareType = List<Int>()
|
||||||
@Persisted var size: Int64 = 0
|
@Persisted var size: Int64 = 0
|
||||||
@Persisted var status: Int = 0
|
@Persisted var status: Int = 0
|
||||||
|
@ -117,22 +117,25 @@ class NextcloudItemMetadataTable: Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isRenameable: Bool {
|
var isRenameable: Bool {
|
||||||
return lock
|
lock
|
||||||
}
|
}
|
||||||
|
|
||||||
var isPrintable: Bool {
|
var isPrintable: Bool {
|
||||||
if isDocumentViewableOnly {
|
if isDocumentViewableOnly {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/") || classFile == NKCommon.TypeClassFile.image.rawValue {
|
if ["application/pdf", "com.adobe.pdf"].contains(contentType)
|
||||||
|
|| contentType.hasPrefix("text/")
|
||||||
|
|| classFile == NKCommon.TypeClassFile.image.rawValue
|
||||||
|
{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDocumentViewableOnly: Bool {
|
var isDocumentViewableOnly: Bool {
|
||||||
return sharePermissionsCollaborationServices == SharePermissions.readShare.rawValue &&
|
sharePermissionsCollaborationServices == SharePermissions.readShare.rawValue
|
||||||
classFile == NKCommon.TypeClassFile.document.rawValue
|
&& classFile == NKCommon.TypeClassFile.document.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var isCopyableInPasteboard: Bool {
|
var isCopyableInPasteboard: Bool {
|
||||||
|
@ -143,22 +146,21 @@ class NextcloudItemMetadataTable: Object {
|
||||||
if directory || isDocumentViewableOnly {
|
if directory || isDocumentViewableOnly {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return contentType == "com.adobe.pdf" || contentType == "application/pdf" || classFile == NKCommon.TypeClassFile.image.rawValue
|
return contentType == "com.adobe.pdf" || contentType == "application/pdf"
|
||||||
|
|| classFile == NKCommon.TypeClassFile.image.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var isSettableOnOffline: Bool {
|
var isSettableOnOffline: Bool {
|
||||||
return session.isEmpty && !isDocumentViewableOnly
|
session.isEmpty && !isDocumentViewableOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
var canOpenIn: Bool {
|
var canOpenIn: Bool {
|
||||||
return session.isEmpty && !isDocumentViewableOnly && !directory
|
session.isEmpty && !isDocumentViewableOnly && !directory
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDownloadUpload: Bool {
|
var isDownloadUpload: Bool {
|
||||||
return status == Status.inDownload.rawValue ||
|
status == Status.inDownload.rawValue || status == Status.downloading.rawValue
|
||||||
status == Status.downloading.rawValue ||
|
|| status == Status.inUpload.rawValue || status == Status.uploading.rawValue
|
||||||
status == Status.inUpload.rawValue ||
|
|
||||||
status == Status.uploading.rawValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDownload: Bool {
|
var isDownload: Bool {
|
||||||
|
@ -171,30 +173,28 @@ class NextcloudItemMetadataTable: Object {
|
||||||
|
|
||||||
override func isEqual(_ object: Any?) -> Bool {
|
override func isEqual(_ object: Any?) -> Bool {
|
||||||
if let object = object as? NextcloudItemMetadataTable {
|
if let object = object as? NextcloudItemMetadataTable {
|
||||||
return self.fileId == object.fileId &&
|
return fileId == object.fileId && account == object.account && path == object.path
|
||||||
self.account == object.account &&
|
&& fileName == object.fileName
|
||||||
self.path == object.path &&
|
|
||||||
self.fileName == object.fileName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInSameDatabaseStoreableRemoteState(_ comparingMetadata: NextcloudItemMetadataTable) -> Bool {
|
func isInSameDatabaseStoreableRemoteState(_ comparingMetadata: NextcloudItemMetadataTable)
|
||||||
return comparingMetadata.etag == self.etag &&
|
-> Bool
|
||||||
comparingMetadata.fileNameView == self.fileNameView &&
|
{
|
||||||
comparingMetadata.date == self.date &&
|
comparingMetadata.etag == etag && comparingMetadata.fileNameView == fileNameView
|
||||||
comparingMetadata.permissions == self.permissions &&
|
&& comparingMetadata.date == date && comparingMetadata.permissions == permissions
|
||||||
comparingMetadata.hasPreview == self.hasPreview &&
|
&& comparingMetadata.hasPreview == hasPreview && comparingMetadata.note == note
|
||||||
comparingMetadata.note == self.note &&
|
&& comparingMetadata.lock == lock
|
||||||
comparingMetadata.lock == self.lock &&
|
&& comparingMetadata.sharePermissionsCollaborationServices
|
||||||
comparingMetadata.sharePermissionsCollaborationServices == self.sharePermissionsCollaborationServices &&
|
== sharePermissionsCollaborationServices
|
||||||
comparingMetadata.favorite == self.favorite
|
&& comparingMetadata.favorite == favorite
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns false if the user is lokced out of the file. I.e. The file is locked but by someone else
|
/// Returns false if the user is lokced out of the file. I.e. The file is locked but by someone else
|
||||||
func canUnlock(as user: String) -> Bool {
|
func canUnlock(as user: String) -> Bool {
|
||||||
return !lock || (lockOwner == user && lockOwnerType == 0)
|
!lock || (lockOwner == user && lockOwnerType == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func thumbnailUrl(size: CGSize) -> URL? {
|
func thumbnailUrl(size: CGSize) -> URL? {
|
||||||
|
@ -203,10 +203,12 @@ class NextcloudItemMetadataTable: Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
let urlBase = urlBase.urlEncoded!
|
let urlBase = urlBase.urlEncoded!
|
||||||
let webdavUrl = urlBase + NextcloudAccount.webDavFilesUrlSuffix + user // Leave the leading slash
|
let webdavUrl = urlBase + NextcloudAccount.webDavFilesUrlSuffix + user // Leave the leading slash
|
||||||
let serverFileRelativeUrl = serverUrl.replacingOccurrences(of: webdavUrl, with: "") + "/" + fileName
|
let serverFileRelativeUrl =
|
||||||
|
serverUrl.replacingOccurrences(of: webdavUrl, with: "") + "/" + fileName
|
||||||
|
|
||||||
let urlString = "\(urlBase)/index.php/core/preview.png?file=\(serverFileRelativeUrl)&x=\(size.width)&y=\(size.height)&a=1&mode=cover"
|
let urlString =
|
||||||
|
"\(urlBase)/index.php/core/preview.png?file=\(serverFileRelativeUrl)&x=\(size.width)&y=\(size.height)&a=1&mode=cover"
|
||||||
|
|
||||||
return URL(string: urlString)
|
return URL(string: urlString)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,14 @@ import OSLog
|
||||||
extension Logger {
|
extension Logger {
|
||||||
private static var subsystem = Bundle.main.bundleIdentifier!
|
private static var subsystem = Bundle.main.bundleIdentifier!
|
||||||
|
|
||||||
static let desktopClientConnection = Logger(subsystem: subsystem, category: "desktopclientconnection")
|
static let desktopClientConnection = Logger(
|
||||||
|
subsystem: subsystem, category: "desktopclientconnection")
|
||||||
static let enumeration = Logger(subsystem: subsystem, category: "enumeration")
|
static let enumeration = Logger(subsystem: subsystem, category: "enumeration")
|
||||||
static let fileProviderExtension = Logger(subsystem: subsystem, category: "fileproviderextension")
|
static let fileProviderExtension = Logger(
|
||||||
|
subsystem: subsystem, category: "fileproviderextension")
|
||||||
static let fileTransfer = Logger(subsystem: subsystem, category: "filetransfer")
|
static let fileTransfer = Logger(subsystem: subsystem, category: "filetransfer")
|
||||||
static let localFileOps = Logger(subsystem: subsystem, category: "localfileoperations")
|
static let localFileOps = Logger(subsystem: subsystem, category: "localfileoperations")
|
||||||
static let ncFilesDatabase = Logger(subsystem: subsystem, category: "nextcloudfilesdatabase")
|
static let ncFilesDatabase = Logger(subsystem: subsystem, category: "nextcloudfilesdatabase")
|
||||||
static let materialisedFileHandling = Logger(subsystem: subsystem, category: "materialisedfilehandling")
|
static let materialisedFileHandling = Logger(
|
||||||
|
subsystem: subsystem, category: "materialisedfilehandling")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,57 +12,49 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
|
|
||||||
extension NKError {
|
extension NKError {
|
||||||
static var noChangesErrorCode: Int {
|
static var noChangesErrorCode: Int {
|
||||||
return -200
|
-200
|
||||||
}
|
}
|
||||||
|
|
||||||
var isCouldntConnectError: Bool {
|
var isCouldntConnectError: Bool {
|
||||||
return errorCode == -9999 ||
|
errorCode == -9999 || errorCode == -1001 || errorCode == -1004 || errorCode == -1005
|
||||||
errorCode == -1001 ||
|
|| errorCode == -1009 || errorCode == -1012 || errorCode == -1200 || errorCode == -1202
|
||||||
errorCode == -1004 ||
|
|| errorCode == 500 || errorCode == 503 || errorCode == 200
|
||||||
errorCode == -1005 ||
|
|
||||||
errorCode == -1009 ||
|
|
||||||
errorCode == -1012 ||
|
|
||||||
errorCode == -1200 ||
|
|
||||||
errorCode == -1202 ||
|
|
||||||
errorCode == 500 ||
|
|
||||||
errorCode == 503 ||
|
|
||||||
errorCode == 200
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isUnauthenticatedError: Bool {
|
var isUnauthenticatedError: Bool {
|
||||||
return errorCode == -1013
|
errorCode == -1013
|
||||||
}
|
}
|
||||||
|
|
||||||
var isGoingOverQuotaError: Bool {
|
var isGoingOverQuotaError: Bool {
|
||||||
return errorCode == 507
|
errorCode == 507
|
||||||
}
|
}
|
||||||
|
|
||||||
var isNotFoundError: Bool {
|
var isNotFoundError: Bool {
|
||||||
return errorCode == 404
|
errorCode == 404
|
||||||
}
|
}
|
||||||
|
|
||||||
var isNoChangesError: Bool {
|
var isNoChangesError: Bool {
|
||||||
return errorCode == NKError.noChangesErrorCode
|
errorCode == NKError.noChangesErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileProviderError: NSFileProviderError {
|
var fileProviderError: NSFileProviderError {
|
||||||
if isNotFoundError {
|
if isNotFoundError {
|
||||||
return NSFileProviderError(.noSuchItem)
|
NSFileProviderError(.noSuchItem)
|
||||||
} else if isCouldntConnectError {
|
} else if isCouldntConnectError {
|
||||||
// Provide something the file provider can do something with
|
// Provide something the file provider can do something with
|
||||||
return NSFileProviderError(.serverUnreachable)
|
NSFileProviderError(.serverUnreachable)
|
||||||
} else if isUnauthenticatedError {
|
} else if isUnauthenticatedError {
|
||||||
return NSFileProviderError(.notAuthenticated)
|
NSFileProviderError(.notAuthenticated)
|
||||||
} else if isGoingOverQuotaError {
|
} else if isGoingOverQuotaError {
|
||||||
return NSFileProviderError(.insufficientQuota)
|
NSFileProviderError(.insufficientQuota)
|
||||||
} else {
|
} else {
|
||||||
return NSFileProviderError(.cannotSynchronize)
|
NSFileProviderError(.cannotSynchronize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,39 +12,39 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Alamofire
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
|
||||||
extension Progress {
|
extension Progress {
|
||||||
func setHandlersFromAfRequest(_ request: Request) {
|
func setHandlersFromAfRequest(_ request: Request) {
|
||||||
self.cancellationHandler = { request.cancel() }
|
cancellationHandler = { request.cancel() }
|
||||||
self.pausingHandler = { request.suspend() }
|
pausingHandler = { request.suspend() }
|
||||||
self.resumingHandler = { request.resume() }
|
resumingHandler = { request.resume() }
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyCurrentStateToProgress(_ otherProgress: Progress, includeHandlers: Bool = false) {
|
func copyCurrentStateToProgress(_ otherProgress: Progress, includeHandlers: Bool = false) {
|
||||||
if includeHandlers {
|
if includeHandlers {
|
||||||
otherProgress.cancellationHandler = self.cancellationHandler
|
otherProgress.cancellationHandler = cancellationHandler
|
||||||
otherProgress.pausingHandler = self.pausingHandler
|
otherProgress.pausingHandler = pausingHandler
|
||||||
otherProgress.resumingHandler = self.resumingHandler
|
otherProgress.resumingHandler = resumingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
otherProgress.totalUnitCount = self.totalUnitCount
|
|
||||||
otherProgress.completedUnitCount = self.completedUnitCount
|
|
||||||
otherProgress.estimatedTimeRemaining = self.estimatedTimeRemaining
|
|
||||||
otherProgress.localizedDescription = self.localizedAdditionalDescription
|
|
||||||
otherProgress.localizedAdditionalDescription = self.localizedAdditionalDescription
|
|
||||||
otherProgress.isCancellable = self.isCancellable
|
|
||||||
otherProgress.isPausable = self.isPausable
|
|
||||||
otherProgress.fileCompletedCount = self.fileCompletedCount
|
|
||||||
otherProgress.fileURL = self.fileURL
|
|
||||||
otherProgress.fileTotalCount = self.fileTotalCount
|
|
||||||
otherProgress.fileCompletedCount = self.fileCompletedCount
|
|
||||||
otherProgress.fileOperationKind = self.fileOperationKind
|
|
||||||
otherProgress.kind = self.kind
|
|
||||||
otherProgress.throughput = self.throughput
|
|
||||||
|
|
||||||
for (key, object) in self.userInfo {
|
otherProgress.totalUnitCount = totalUnitCount
|
||||||
|
otherProgress.completedUnitCount = completedUnitCount
|
||||||
|
otherProgress.estimatedTimeRemaining = estimatedTimeRemaining
|
||||||
|
otherProgress.localizedDescription = localizedAdditionalDescription
|
||||||
|
otherProgress.localizedAdditionalDescription = localizedAdditionalDescription
|
||||||
|
otherProgress.isCancellable = isCancellable
|
||||||
|
otherProgress.isPausable = isPausable
|
||||||
|
otherProgress.fileCompletedCount = fileCompletedCount
|
||||||
|
otherProgress.fileURL = fileURL
|
||||||
|
otherProgress.fileTotalCount = fileTotalCount
|
||||||
|
otherProgress.fileCompletedCount = fileCompletedCount
|
||||||
|
otherProgress.fileOperationKind = fileOperationKind
|
||||||
|
otherProgress.kind = kind
|
||||||
|
otherProgress.throughput = throughput
|
||||||
|
|
||||||
|
for (key, object) in userInfo {
|
||||||
otherProgress.setUserInfoObject(object, forKey: key)
|
otherProgress.setUserInfoObject(object, forKey: key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,27 +17,32 @@ import NextcloudKit
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
extension FileProviderEnumerator {
|
extension FileProviderEnumerator {
|
||||||
func fullRecursiveScan(ncAccount: NextcloudAccount,
|
func fullRecursiveScan(
|
||||||
ncKit: NextcloudKit,
|
ncAccount: NextcloudAccount,
|
||||||
scanChangesOnly: Bool,
|
ncKit: NextcloudKit,
|
||||||
completionHandler: @escaping(_ metadatas: [NextcloudItemMetadataTable],
|
scanChangesOnly: Bool,
|
||||||
_ newMetadatas: [NextcloudItemMetadataTable],
|
completionHandler: @escaping (
|
||||||
_ updatedMetadatas: [NextcloudItemMetadataTable],
|
_ metadatas: [NextcloudItemMetadataTable],
|
||||||
_ deletedMetadatas: [NextcloudItemMetadataTable],
|
_ newMetadatas: [NextcloudItemMetadataTable],
|
||||||
_ error: NKError?) -> Void) {
|
_ updatedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
_ deletedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
_ error: NKError?
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
let rootContainerDirectoryMetadata = NextcloudItemMetadataTable()
|
let rootContainerDirectoryMetadata = NextcloudItemMetadataTable()
|
||||||
rootContainerDirectoryMetadata.directory = true
|
rootContainerDirectoryMetadata.directory = true
|
||||||
rootContainerDirectoryMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
|
rootContainerDirectoryMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
|
||||||
|
|
||||||
// Create a serial dispatch queue
|
// Create a serial dispatch queue
|
||||||
let dispatchQueue = DispatchQueue(label: "recursiveChangeEnumerationQueue", qos: .userInitiated)
|
let dispatchQueue = DispatchQueue(
|
||||||
|
label: "recursiveChangeEnumerationQueue", qos: .userInitiated)
|
||||||
|
|
||||||
dispatchQueue.async {
|
dispatchQueue.async {
|
||||||
let results = self.scanRecursively(rootContainerDirectoryMetadata,
|
let results = self.scanRecursively(
|
||||||
ncAccount: ncAccount,
|
rootContainerDirectoryMetadata,
|
||||||
ncKit: ncKit,
|
ncAccount: ncAccount,
|
||||||
scanChangesOnly: scanChangesOnly)
|
ncKit: ncKit,
|
||||||
|
scanChangesOnly: scanChangesOnly)
|
||||||
|
|
||||||
// Run a check to ensure files deleted in one location are not updated in another (e.g. when moved)
|
// Run a check to ensure files deleted in one location are not updated in another (e.g. when moved)
|
||||||
// The recursive scan provides us with updated/deleted metadatas only on a folder by folder basis;
|
// The recursive scan provides us with updated/deleted metadatas only on a folder by folder basis;
|
||||||
|
@ -45,29 +50,38 @@ extension FileProviderEnumerator {
|
||||||
var checkedDeletedMetadatas = results.deletedMetadatas
|
var checkedDeletedMetadatas = results.deletedMetadatas
|
||||||
|
|
||||||
for updatedMetadata in results.updatedMetadatas {
|
for updatedMetadata in results.updatedMetadatas {
|
||||||
guard let matchingDeletedMetadataIdx = checkedDeletedMetadatas.firstIndex(where: { $0.ocId == updatedMetadata.ocId } ) else {
|
guard
|
||||||
continue;
|
let matchingDeletedMetadataIdx = checkedDeletedMetadatas.firstIndex(where: {
|
||||||
|
$0.ocId == updatedMetadata.ocId
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
checkedDeletedMetadatas.remove(at: matchingDeletedMetadataIdx)
|
checkedDeletedMetadatas.remove(at: matchingDeletedMetadataIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
completionHandler(results.metadatas, results.newMetadatas, results.updatedMetadatas, checkedDeletedMetadatas, results.error)
|
completionHandler(
|
||||||
|
results.metadatas, results.newMetadatas, results.updatedMetadatas,
|
||||||
|
checkedDeletedMetadatas, results.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func scanRecursively(_ directoryMetadata: NextcloudItemMetadataTable,
|
private func scanRecursively(
|
||||||
ncAccount: NextcloudAccount,
|
_ directoryMetadata: NextcloudItemMetadataTable,
|
||||||
ncKit: NextcloudKit,
|
ncAccount: NextcloudAccount,
|
||||||
scanChangesOnly: Bool) -> (metadatas: [NextcloudItemMetadataTable],
|
ncKit: NextcloudKit,
|
||||||
newMetadatas: [NextcloudItemMetadataTable],
|
scanChangesOnly: Bool
|
||||||
updatedMetadatas: [NextcloudItemMetadataTable],
|
) -> (
|
||||||
deletedMetadatas: [NextcloudItemMetadataTable],
|
metadatas: [NextcloudItemMetadataTable],
|
||||||
error: NKError?) {
|
newMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
updatedMetadatas: [NextcloudItemMetadataTable],
|
||||||
if self.isInvalidated {
|
deletedMetadatas: [NextcloudItemMetadataTable],
|
||||||
|
error: NKError?
|
||||||
|
) {
|
||||||
|
if isInvalidated {
|
||||||
return ([], [], [], [], nil)
|
return ([], [], [], [], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,43 +94,59 @@ extension FileProviderEnumerator {
|
||||||
var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let dispatchGroup = DispatchGroup() // TODO: Maybe own thread?
|
let dispatchGroup = DispatchGroup() // TODO: Maybe own thread?
|
||||||
|
|
||||||
dispatchGroup.enter()
|
dispatchGroup.enter()
|
||||||
|
|
||||||
var criticalError: NKError?
|
var criticalError: NKError?
|
||||||
let itemServerUrl = directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue ?
|
let itemServerUrl =
|
||||||
ncAccount.davFilesUrl : directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue
|
||||||
|
? ncAccount.davFilesUrl : directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
|
||||||
|
|
||||||
Logger.enumeration.debug("About to read: \(itemServerUrl, privacy: .public)")
|
Logger.enumeration.debug("About to read: \(itemServerUrl, privacy: .public)")
|
||||||
|
|
||||||
FileProviderEnumerator.readServerUrl(itemServerUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
|
FileProviderEnumerator.readServerUrl(
|
||||||
|
itemServerUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly
|
||||||
|
) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
|
||||||
|
|
||||||
if readError != nil {
|
if readError != nil {
|
||||||
let nkReadError = NKError(error: readError!)
|
let nkReadError = NKError(error: readError!)
|
||||||
|
|
||||||
// Is the error is that we have found matching etags on this item, then ignore it
|
// Is the error is that we have found matching etags on this item, then ignore it
|
||||||
// if we are doing a full rescan
|
// if we are doing a full rescan
|
||||||
guard nkReadError.isNoChangesError && scanChangesOnly else {
|
guard nkReadError.isNoChangesError, scanChangesOnly else {
|
||||||
Logger.enumeration.error("Finishing enumeration of changes at \(itemServerUrl, privacy: .public) with \(readError!.localizedDescription, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"Finishing enumeration of changes at \(itemServerUrl, privacy: .public) with \(readError!.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if nkReadError.isNotFoundError {
|
if nkReadError.isNotFoundError {
|
||||||
Logger.enumeration.info("404 error means item no longer exists. Deleting metadata and reporting as deletion without error")
|
Logger.enumeration.info(
|
||||||
|
"404 error means item no longer exists. Deleting metadata and reporting as deletion without error"
|
||||||
|
)
|
||||||
|
|
||||||
if let deletedMetadatas = dbManager.deleteDirectoryAndSubdirectoriesMetadata(ocId: directoryMetadata.ocId) {
|
if let deletedMetadatas =
|
||||||
|
dbManager.deleteDirectoryAndSubdirectoriesMetadata(
|
||||||
|
ocId: directoryMetadata.ocId)
|
||||||
|
{
|
||||||
allDeletedMetadatas += deletedMetadatas
|
allDeletedMetadatas += deletedMetadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.error("An error occurred while trying to delete directory and children not found in recursive scan")
|
Logger.enumeration.error(
|
||||||
|
"An error occurred while trying to delete directory and children not found in recursive scan"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if nkReadError.isNoChangesError { // All is well, just no changed etags
|
} else if nkReadError.isNoChangesError { // All is well, just no changed etags
|
||||||
Logger.enumeration.info("Error was to say no changed files -- not bad error. No need to check children.")
|
Logger.enumeration.info(
|
||||||
|
"Error was to say no changed files -- not bad error. No need to check children."
|
||||||
|
)
|
||||||
|
|
||||||
} else if nkReadError.isUnauthenticatedError || nkReadError.isCouldntConnectError {
|
} else if nkReadError.isUnauthenticatedError
|
||||||
|
|| nkReadError.isCouldntConnectError
|
||||||
|
{
|
||||||
// If it is a critical error then stop, if not then continue
|
// If it is a critical error then stop, if not then continue
|
||||||
Logger.enumeration.error("Error will affect next enumerated items, so stopping enumeration.")
|
Logger.enumeration.error(
|
||||||
|
"Error will affect next enumerated items, so stopping enumeration.")
|
||||||
criticalError = nkReadError
|
criticalError = nkReadError
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
|
@ -124,30 +154,40 @@ extension FileProviderEnumerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Finished reading serverUrl: \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Finished reading serverUrl: \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if let metadatas = metadatas {
|
if let metadatas {
|
||||||
allMetadatas += metadatas
|
allMetadatas += metadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.warning("WARNING: Nil metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.warning(
|
||||||
|
"WARNING: Nil metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let newMetadatas = newMetadatas {
|
if let newMetadatas {
|
||||||
allNewMetadatas += newMetadatas
|
allNewMetadatas += newMetadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.warning("WARNING: Nil new metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.warning(
|
||||||
|
"WARNING: Nil new metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let updatedMetadatas = updatedMetadatas {
|
if let updatedMetadatas {
|
||||||
allUpdatedMetadatas += updatedMetadatas
|
allUpdatedMetadatas += updatedMetadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.warning("WARNING: Nil updated metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.warning(
|
||||||
|
"WARNING: Nil updated metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let deletedMetadatas = deletedMetadatas {
|
if let deletedMetadatas {
|
||||||
allDeletedMetadatas += deletedMetadatas
|
allDeletedMetadatas += deletedMetadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.warning("WARNING: Nil deleted metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.warning(
|
||||||
|
"WARNING: Nil deleted metadatas received for reading of changes at \(itemServerUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
|
@ -160,13 +200,12 @@ extension FileProviderEnumerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
var childDirectoriesToScan: [NextcloudItemMetadataTable] = []
|
var childDirectoriesToScan: [NextcloudItemMetadataTable] = []
|
||||||
var candidateMetadatas: [NextcloudItemMetadataTable]
|
var candidateMetadatas: [NextcloudItemMetadataTable] =
|
||||||
|
if scanChangesOnly {
|
||||||
if scanChangesOnly {
|
allUpdatedMetadatas + allNewMetadatas
|
||||||
candidateMetadatas = allUpdatedMetadatas + allNewMetadatas
|
} else {
|
||||||
} else {
|
allMetadatas
|
||||||
candidateMetadatas = allMetadatas
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for candidateMetadata in candidateMetadatas {
|
for candidateMetadata in candidateMetadatas {
|
||||||
if candidateMetadata.directory {
|
if candidateMetadata.directory {
|
||||||
|
@ -175,14 +214,18 @@ extension FileProviderEnumerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
if childDirectoriesToScan.isEmpty {
|
if childDirectoriesToScan.isEmpty {
|
||||||
return (metadatas: allMetadatas, newMetadatas: allNewMetadatas, updatedMetadatas: allUpdatedMetadatas, deletedMetadatas: allDeletedMetadatas, nil)
|
return (
|
||||||
|
metadatas: allMetadatas, newMetadatas: allNewMetadatas,
|
||||||
|
updatedMetadatas: allUpdatedMetadatas, deletedMetadatas: allDeletedMetadatas, nil
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for childDirectory in childDirectoriesToScan {
|
for childDirectory in childDirectoriesToScan {
|
||||||
let childScanResult = scanRecursively(childDirectory,
|
let childScanResult = scanRecursively(
|
||||||
ncAccount: ncAccount,
|
childDirectory,
|
||||||
ncKit: ncKit,
|
ncAccount: ncAccount,
|
||||||
scanChangesOnly: scanChangesOnly)
|
ncKit: ncKit,
|
||||||
|
scanChangesOnly: scanChangesOnly)
|
||||||
|
|
||||||
allMetadatas += childScanResult.metadatas
|
allMetadatas += childScanResult.metadatas
|
||||||
allNewMetadatas += childScanResult.newMetadatas
|
allNewMetadatas += childScanResult.newMetadatas
|
||||||
|
@ -190,31 +233,44 @@ extension FileProviderEnumerator {
|
||||||
allDeletedMetadatas += childScanResult.deletedMetadatas
|
allDeletedMetadatas += childScanResult.deletedMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
return (metadatas: allMetadatas, newMetadatas: allNewMetadatas, updatedMetadatas: allUpdatedMetadatas, deletedMetadatas: allDeletedMetadatas, nil)
|
return (
|
||||||
|
metadatas: allMetadatas, newMetadatas: allNewMetadatas,
|
||||||
|
updatedMetadatas: allUpdatedMetadatas,
|
||||||
|
deletedMetadatas: allDeletedMetadatas, nil
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleDepth1ReadFileOrFolder(serverUrl: String,
|
static func handleDepth1ReadFileOrFolder(
|
||||||
ncAccount: NextcloudAccount,
|
serverUrl: String,
|
||||||
files: [NKFile],
|
ncAccount: NextcloudAccount,
|
||||||
error: NKError,
|
files: [NKFile],
|
||||||
completionHandler: @escaping (_ metadatas: [NextcloudItemMetadataTable]?,
|
error: NKError,
|
||||||
_ newMetadatas: [NextcloudItemMetadataTable]?,
|
completionHandler: @escaping (
|
||||||
_ updatedMetadatas: [NextcloudItemMetadataTable]?,
|
_ metadatas: [NextcloudItemMetadataTable]?,
|
||||||
_ deletedMetadatas: [NextcloudItemMetadataTable]?,
|
_ newMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
_ readError: Error?) -> Void) {
|
_ updatedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
_ deletedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
_ readError: Error?
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.enumeration.error("1 depth readFileOrFolder of url: \(serverUrl, privacy: .public) did not complete successfully, received error: \(error.errorDescription, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"1 depth readFileOrFolder of url: \(serverUrl, privacy: .public) did not complete successfully, received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, nil, nil, error.error)
|
completionHandler(nil, nil, nil, nil, error.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.debug("Starting async conversion of NKFiles for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Starting async conversion of NKFiles for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(files, account: ncAccount.ncKitAccount) { directoryMetadata, childDirectoriesMetadata, metadatas in
|
NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(
|
||||||
|
files, account: ncAccount.ncKitAccount
|
||||||
|
) { directoryMetadata, _, metadatas in
|
||||||
|
|
||||||
// STORE DATA FOR CURRENTLY SCANNED DIRECTORY
|
// STORE DATA FOR CURRENTLY SCANNED DIRECTORY
|
||||||
// We have now scanned this directory's contents, so update with etag in order to not check again if not needed
|
// We have now scanned this directory's contents, so update with etag in order to not check again if not needed
|
||||||
|
@ -228,64 +284,88 @@ extension FileProviderEnumerator {
|
||||||
// that our local copies are up to date -- instead, leave them as the old.
|
// that our local copies are up to date -- instead, leave them as the old.
|
||||||
// They will get updated when they are the subject of a readServerUrl call.
|
// They will get updated when they are the subject of a readServerUrl call.
|
||||||
// (See above)
|
// (See above)
|
||||||
let changedMetadatas = dbManager.updateItemMetadatas(account: ncAccount.ncKitAccount, serverUrl: serverUrl, updatedMetadatas: metadatas, updateDirectoryEtags: false)
|
let changedMetadatas = dbManager.updateItemMetadatas(
|
||||||
|
account: ncAccount.ncKitAccount, serverUrl: serverUrl,
|
||||||
|
updatedMetadatas: metadatas,
|
||||||
|
updateDirectoryEtags: false)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
completionHandler(metadatas, changedMetadatas.newMetadatas, changedMetadatas.updatedMetadatas, changedMetadatas.deletedMetadatas, nil)
|
completionHandler(
|
||||||
|
metadatas, changedMetadatas.newMetadatas, changedMetadatas.updatedMetadatas,
|
||||||
|
changedMetadatas.deletedMetadatas, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func readServerUrl(_ serverUrl: String,
|
static func readServerUrl(
|
||||||
ncAccount: NextcloudAccount,
|
_ serverUrl: String,
|
||||||
ncKit: NextcloudKit,
|
ncAccount: NextcloudAccount,
|
||||||
stopAtMatchingEtags: Bool = false,
|
ncKit: NextcloudKit,
|
||||||
depth: String = "1",
|
stopAtMatchingEtags: Bool = false,
|
||||||
completionHandler: @escaping (_ metadatas: [NextcloudItemMetadataTable]?,
|
depth: String = "1",
|
||||||
_ newMetadatas: [NextcloudItemMetadataTable]?,
|
completionHandler: @escaping (
|
||||||
_ updatedMetadatas: [NextcloudItemMetadataTable]?,
|
_ metadatas: [NextcloudItemMetadataTable]?,
|
||||||
_ deletedMetadatas: [NextcloudItemMetadataTable]?,
|
_ newMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
_ readError: Error?) -> Void) {
|
_ updatedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
_ deletedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
_ readError: Error?
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let ncKitAccount = ncAccount.ncKitAccount
|
let ncKitAccount = ncAccount.ncKitAccount
|
||||||
|
|
||||||
Logger.enumeration.debug("Starting to read serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public) at depth \(depth, privacy: .public). NCKit info: userId: \(ncKit.nkCommonInstance.user, privacy: .public), password is empty: \(ncKit.nkCommonInstance.password == "" ? "EMPTY PASSWORD" : "NOT EMPTY PASSWORD"), urlBase: \(ncKit.nkCommonInstance.urlBase, privacy: .public), ncVersion: \(ncKit.nkCommonInstance.nextcloudVersion, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Starting to read serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public) at depth \(depth, privacy: .public). NCKit info: userId: \(ncKit.nkCommonInstance.user, privacy: .public), password is empty: \(ncKit.nkCommonInstance.password == "" ? "EMPTY PASSWORD" : "NOT EMPTY PASSWORD"), urlBase: \(ncKit.nkCommonInstance.urlBase, privacy: .public), ncVersion: \(ncKit.nkCommonInstance.nextcloudVersion, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
ncKit.readFileOrFolder(serverUrlFileName: serverUrl, depth: depth, showHiddenFiles: true) { _, files, _, error in
|
ncKit.readFileOrFolder(serverUrlFileName: serverUrl, depth: depth, showHiddenFiles: true) {
|
||||||
|
_, files, _, error in
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.enumeration.error("\(depth, privacy: .public) depth readFileOrFolder of url: \(serverUrl, privacy: .public) did not complete successfully, received error: \(error.errorDescription, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"\(depth, privacy: .public) depth readFileOrFolder of url: \(serverUrl, privacy: .public) did not complete successfully, received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, nil, nil, error.error)
|
completionHandler(nil, nil, nil, nil, error.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let receivedFile = files.first else {
|
guard let receivedFile = files.first else {
|
||||||
Logger.enumeration.error("Received no items from readFileOrFolder of \(serverUrl, privacy: .public), not much we can do...")
|
Logger.enumeration.error(
|
||||||
|
"Received no items from readFileOrFolder of \(serverUrl, privacy: .public), not much we can do..."
|
||||||
|
)
|
||||||
completionHandler(nil, nil, nil, nil, error.error)
|
completionHandler(nil, nil, nil, nil, error.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard receivedFile.directory else {
|
guard receivedFile.directory else {
|
||||||
Logger.enumeration.debug("Read item is a file. Converting NKfile for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
let itemMetadata = NextcloudItemMetadataTable.fromNKFile(receivedFile, account: ncKitAccount)
|
"Read item is a file. Converting NKfile for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
|
||||||
dbManager.addItemMetadata(itemMetadata) // TODO: Return some value when it is an update
|
)
|
||||||
|
let itemMetadata = NextcloudItemMetadataTable.fromNKFile(
|
||||||
|
receivedFile, account: ncKitAccount)
|
||||||
|
dbManager.addItemMetadata(itemMetadata) // TODO: Return some value when it is an update
|
||||||
completionHandler([itemMetadata], nil, nil, nil, error.error)
|
completionHandler([itemMetadata], nil, nil, nil, error.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if stopAtMatchingEtags,
|
if stopAtMatchingEtags,
|
||||||
let directoryMetadata = dbManager.directoryMetadata(account: ncKitAccount, serverUrl: serverUrl) {
|
let directoryMetadata = dbManager.directoryMetadata(
|
||||||
|
account: ncKitAccount, serverUrl: serverUrl)
|
||||||
|
{
|
||||||
let directoryEtag = directoryMetadata.etag
|
let directoryEtag = directoryMetadata.etag
|
||||||
|
|
||||||
guard directoryEtag == "" || directoryEtag != receivedFile.etag else {
|
guard directoryEtag == "" || directoryEtag != receivedFile.etag else {
|
||||||
Logger.enumeration.debug("Read server url called with flag to stop enumerating at matching etags. Returning and providing soft error.")
|
Logger.enumeration.debug(
|
||||||
|
"Read server url called with flag to stop enumerating at matching etags. Returning and providing soft error."
|
||||||
|
)
|
||||||
|
|
||||||
let description = "Fetched directory etag is same as that stored locally. Not fetching child items."
|
let description =
|
||||||
let nkError = NKError(errorCode: NKError.noChangesErrorCode, errorDescription: description)
|
"Fetched directory etag is same as that stored locally. Not fetching child items."
|
||||||
|
let nkError = NKError(
|
||||||
|
errorCode: NKError.noChangesErrorCode, errorDescription: description)
|
||||||
|
|
||||||
let metadatas = dbManager.itemMetadatas(account: ncKitAccount, serverUrl: serverUrl)
|
let metadatas = dbManager.itemMetadatas(
|
||||||
|
account: ncKitAccount, serverUrl: serverUrl)
|
||||||
|
|
||||||
completionHandler(metadatas, nil, nil, nil, nkError.error)
|
completionHandler(metadatas, nil, nil, nil, nkError.error)
|
||||||
return
|
return
|
||||||
|
@ -294,7 +374,8 @@ extension FileProviderEnumerator {
|
||||||
|
|
||||||
if depth == "0" {
|
if depth == "0" {
|
||||||
if serverUrl != ncAccount.davFilesUrl {
|
if serverUrl != ncAccount.davFilesUrl {
|
||||||
let metadata = NextcloudItemMetadataTable.fromNKFile(receivedFile, account: ncKitAccount)
|
let metadata = NextcloudItemMetadataTable.fromNKFile(
|
||||||
|
receivedFile, account: ncKitAccount)
|
||||||
let isNew = dbManager.itemMetadataFromOcId(metadata.ocId) == nil
|
let isNew = dbManager.itemMetadataFromOcId(metadata.ocId) == nil
|
||||||
let updatedMetadatas = isNew ? [] : [metadata]
|
let updatedMetadatas = isNew ? [] : [metadata]
|
||||||
let newMetadatas = isNew ? [metadata] : []
|
let newMetadatas = isNew ? [metadata] : []
|
||||||
|
@ -306,7 +387,9 @@ extension FileProviderEnumerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleDepth1ReadFileOrFolder(serverUrl: serverUrl, ncAccount: ncAccount, files: files, error: error, completionHandler: completionHandler)
|
handleDepth1ReadFileOrFolder(
|
||||||
|
serverUrl: serverUrl, ncAccount: ncAccount, files: files, error: error,
|
||||||
|
completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ import NextcloudKit
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
|
|
||||||
private let enumeratedItemIdentifier: NSFileProviderItemIdentifier
|
private let enumeratedItemIdentifier: NSFileProviderItemIdentifier
|
||||||
private var enumeratedItemMetadata: NextcloudItemMetadataTable?
|
private var enumeratedItemMetadata: NextcloudItemMetadataTable?
|
||||||
private var enumeratingSystemIdentifier: Bool {
|
private var enumeratingSystemIdentifier: Bool {
|
||||||
return FileProviderEnumerator.isSystemIdentifier(enumeratedItemIdentifier)
|
FileProviderEnumerator.isSystemIdentifier(enumeratedItemIdentifier)
|
||||||
}
|
}
|
||||||
private let anchor = NSFileProviderSyncAnchor(Date().description.data(using: .utf8)!) // TODO: actually use this in NCKit and server requests
|
|
||||||
|
private let anchor = NSFileProviderSyncAnchor(Date().description.data(using: .utf8)!) // TODO: actually use this in NCKit and server requests
|
||||||
private static let maxItemsPerFileProviderPage = 100
|
private static let maxItemsPerFileProviderPage = 100
|
||||||
let ncAccount: NextcloudAccount
|
let ncAccount: NextcloudAccount
|
||||||
let ncKit: NextcloudKit
|
let ncKit: NextcloudKit
|
||||||
|
@ -31,59 +31,78 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
var isInvalidated = false
|
var isInvalidated = false
|
||||||
|
|
||||||
private static func isSystemIdentifier(_ identifier: NSFileProviderItemIdentifier) -> Bool {
|
private static func isSystemIdentifier(_ identifier: NSFileProviderItemIdentifier) -> Bool {
|
||||||
return identifier == .rootContainer ||
|
identifier == .rootContainer || identifier == .trashContainer || identifier == .workingSet
|
||||||
identifier == .trashContainer ||
|
|
||||||
identifier == .workingSet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(enumeratedItemIdentifier: NSFileProviderItemIdentifier, ncAccount: NextcloudAccount, ncKit: NextcloudKit) {
|
init(
|
||||||
|
enumeratedItemIdentifier: NSFileProviderItemIdentifier, ncAccount: NextcloudAccount,
|
||||||
|
ncKit: NextcloudKit
|
||||||
|
) {
|
||||||
self.enumeratedItemIdentifier = enumeratedItemIdentifier
|
self.enumeratedItemIdentifier = enumeratedItemIdentifier
|
||||||
self.ncAccount = ncAccount
|
self.ncAccount = ncAccount
|
||||||
self.ncKit = ncKit
|
self.ncKit = ncKit
|
||||||
|
|
||||||
if FileProviderEnumerator.isSystemIdentifier(enumeratedItemIdentifier) {
|
if FileProviderEnumerator.isSystemIdentifier(enumeratedItemIdentifier) {
|
||||||
Logger.enumeration.debug("Providing enumerator for a system defined container: \(enumeratedItemIdentifier.rawValue, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
self.serverUrl = ncAccount.davFilesUrl
|
"Providing enumerator for a system defined container: \(enumeratedItemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
|
serverUrl = ncAccount.davFilesUrl
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.debug("Providing enumerator for item with identifier: \(enumeratedItemIdentifier.rawValue, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Providing enumerator for item with identifier: \(enumeratedItemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
|
|
||||||
enumeratedItemMetadata = dbManager.itemMetadataFromFileProviderItemIdentifier(enumeratedItemIdentifier)
|
enumeratedItemMetadata = dbManager.itemMetadataFromFileProviderItemIdentifier(
|
||||||
|
enumeratedItemIdentifier)
|
||||||
if enumeratedItemMetadata != nil {
|
if enumeratedItemMetadata != nil {
|
||||||
self.serverUrl = enumeratedItemMetadata!.serverUrl + "/" + enumeratedItemMetadata!.fileName
|
serverUrl =
|
||||||
|
enumeratedItemMetadata!.serverUrl + "/" + enumeratedItemMetadata!.fileName
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.error("Could not find itemMetadata for file with identifier: \(enumeratedItemIdentifier.rawValue, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"Could not find itemMetadata for file with identifier: \(enumeratedItemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Set up enumerator for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Set up enumerator for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidate() {
|
func invalidate() {
|
||||||
Logger.enumeration.debug("Enumerator is being invalidated for item with identifier: \(self.enumeratedItemIdentifier.rawValue, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
self.isInvalidated = true
|
"Enumerator is being invalidated for item with identifier: \(self.enumeratedItemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
|
isInvalidated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Protocol methods
|
// MARK: - Protocol methods
|
||||||
|
|
||||||
func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) {
|
func enumerateItems(
|
||||||
Logger.enumeration.debug("Received enumerate items request for enumerator with user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage
|
||||||
|
) {
|
||||||
|
Logger.enumeration.debug(
|
||||||
|
"Received enumerate items request for enumerator with user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
/*
|
/*
|
||||||
- inspect the page to determine whether this is an initial or a follow-up request (TODO)
|
- inspect the page to determine whether this is an initial or a follow-up request (TODO)
|
||||||
|
|
||||||
If this is an enumerator for a directory, the root container or all directories:
|
If this is an enumerator for a directory, the root container or all directories:
|
||||||
- perform a server request to fetch directory contents
|
- perform a server request to fetch directory contents
|
||||||
If this is an enumerator for the working set:
|
If this is an enumerator for the working set:
|
||||||
- perform a server request to update your local database
|
- perform a server request to update your local database
|
||||||
- fetch the working set from your local database
|
- fetch the working set from your local database
|
||||||
|
|
||||||
- inform the observer about the items returned by the server (possibly multiple times)
|
- inform the observer about the items returned by the server (possibly multiple times)
|
||||||
- inform the observer that you are finished with this page
|
- inform the observer that you are finished with this page
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if enumeratedItemIdentifier == .trashContainer {
|
if enumeratedItemIdentifier == .trashContainer {
|
||||||
Logger.enumeration.debug("Enumerating trash set for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Enumerating trash set for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
// TODO!
|
// TODO!
|
||||||
|
|
||||||
observer.finishEnumerating(upTo: nil)
|
observer.finishEnumerating(upTo: nil)
|
||||||
|
@ -98,105 +117,140 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
// navigate a little bit in Finder, file picker, etc
|
// navigate a little bit in Finder, file picker, etc
|
||||||
|
|
||||||
guard serverUrl != "" else {
|
guard serverUrl != "" else {
|
||||||
Logger.enumeration.error("Enumerator has empty serverUrl -- can't enumerate that! For identifier: \(self.enumeratedItemIdentifier.rawValue, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"Enumerator has empty serverUrl -- can't enumerate that! For identifier: \(self.enumeratedItemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(NSFileProviderError(.noSuchItem))
|
observer.finishEnumeratingWithError(NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make better use of pagination and handle paging properly
|
// TODO: Make better use of pagination and handle paging properly
|
||||||
if page == NSFileProviderPage.initialPageSortedByDate as NSFileProviderPage ||
|
if page == NSFileProviderPage.initialPageSortedByDate as NSFileProviderPage
|
||||||
page == NSFileProviderPage.initialPageSortedByName as NSFileProviderPage {
|
|| page == NSFileProviderPage.initialPageSortedByName as NSFileProviderPage
|
||||||
|
{
|
||||||
|
Logger.enumeration.debug(
|
||||||
|
"Enumerating initial page for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
Logger.enumeration.debug("Enumerating initial page for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
FileProviderEnumerator.readServerUrl(serverUrl, ncAccount: ncAccount, ncKit: ncKit) {
|
||||||
|
metadatas, _, _, _, readError in
|
||||||
FileProviderEnumerator.readServerUrl(serverUrl, ncAccount: ncAccount, ncKit: ncKit) { metadatas, _, _, _, readError in
|
|
||||||
|
|
||||||
guard readError == nil else {
|
guard readError == nil else {
|
||||||
Logger.enumeration.error("Finishing enumeration for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error \(readError!.localizedDescription, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"Finishing enumeration for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error \(readError!.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
let nkReadError = NKError(error: readError!)
|
let nkReadError = NKError(error: readError!)
|
||||||
observer.finishEnumeratingWithError(nkReadError.fileProviderError)
|
observer.finishEnumeratingWithError(nkReadError.fileProviderError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let metadatas = metadatas else {
|
guard let metadatas else {
|
||||||
Logger.enumeration.error("Finishing enumeration for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with invalid metadatas.")
|
Logger.enumeration.error(
|
||||||
|
"Finishing enumeration for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with invalid metadatas."
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(NSFileProviderError(.cannotSynchronize))
|
observer.finishEnumeratingWithError(NSFileProviderError(.cannotSynchronize))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Finished reading serverUrl: \(self.serverUrl, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public). Processed \(metadatas.count) metadatas")
|
Logger.enumeration.info(
|
||||||
|
"Finished reading serverUrl: \(self.serverUrl, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public). Processed \(metadatas.count) metadatas"
|
||||||
|
)
|
||||||
|
|
||||||
FileProviderEnumerator.completeEnumerationObserver(observer, ncKit: self.ncKit, numPage: 1, itemMetadatas: metadatas)
|
FileProviderEnumerator.completeEnumerationObserver(
|
||||||
|
observer, ncKit: self.ncKit, numPage: 1, itemMetadatas: metadatas)
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let numPage = Int(String(data: page.rawValue, encoding: .utf8)!)!
|
let numPage = Int(String(data: page.rawValue, encoding: .utf8)!)!
|
||||||
Logger.enumeration.debug("Enumerating page \(numPage, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Enumerating page \(numPage, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
// TODO: Handle paging properly
|
// TODO: Handle paging properly
|
||||||
// FileProviderEnumerator.completeObserver(observer, ncKit: ncKit, numPage: numPage, itemMetadatas: nil)
|
// FileProviderEnumerator.completeObserver(observer, ncKit: ncKit, numPage: numPage, itemMetadatas: nil)
|
||||||
observer.finishEnumerating(upTo: nil)
|
observer.finishEnumerating(upTo: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) {
|
func enumerateChanges(
|
||||||
Logger.enumeration.debug("Received enumerate changes request for enumerator for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor
|
||||||
|
) {
|
||||||
|
Logger.enumeration.debug(
|
||||||
|
"Received enumerate changes request for enumerator for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
/*
|
/*
|
||||||
- query the server for updates since the passed-in sync anchor (TODO)
|
- query the server for updates since the passed-in sync anchor (TODO)
|
||||||
|
|
||||||
If this is an enumerator for the working set:
|
If this is an enumerator for the working set:
|
||||||
- note the changes in your local database
|
- note the changes in your local database
|
||||||
|
|
||||||
- inform the observer about item deletions and updates (modifications + insertions)
|
- inform the observer about item deletions and updates (modifications + insertions)
|
||||||
- inform the observer when you have finished enumerating up to a subsequent sync anchor
|
- inform the observer when you have finished enumerating up to a subsequent sync anchor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if enumeratedItemIdentifier == .workingSet {
|
if enumeratedItemIdentifier == .workingSet {
|
||||||
Logger.enumeration.debug("Enumerating changes in working set for user: \(self.ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Enumerating changes in working set for user: \(self.ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
// Unlike when enumerating items we can't progressively enumerate items as we need to wait to resolve which items are truly deleted and which
|
// Unlike when enumerating items we can't progressively enumerate items as we need to wait to resolve which items are truly deleted and which
|
||||||
// have just been moved elsewhere.
|
// have just been moved elsewhere.
|
||||||
fullRecursiveScan(ncAccount: self.ncAccount,
|
fullRecursiveScan(
|
||||||
ncKit: self.ncKit,
|
ncAccount: ncAccount,
|
||||||
scanChangesOnly: true) { _, newMetadatas, updatedMetadatas, deletedMetadatas, error in
|
ncKit: ncKit,
|
||||||
|
scanChangesOnly: true
|
||||||
|
) { _, newMetadatas, updatedMetadatas, deletedMetadatas, error in
|
||||||
|
|
||||||
if self.isInvalidated {
|
if self.isInvalidated {
|
||||||
Logger.enumeration.info("Enumerator invalidated during working set change scan. For user: \(self.ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Enumerator invalidated during working set change scan. For user: \(self.ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(NSFileProviderError(.cannotSynchronize))
|
observer.finishEnumeratingWithError(NSFileProviderError(.cannotSynchronize))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard error == nil else {
|
guard error == nil else {
|
||||||
Logger.enumeration.info("Finished recursive change enumeration of working set for user: \(self.ncAccount.ncKitAccount, privacy: .public) with error: \(error!.errorDescription, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Finished recursive change enumeration of working set for user: \(self.ncAccount.ncKitAccount, privacy: .public) with error: \(error!.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(error!.fileProviderError)
|
observer.finishEnumeratingWithError(error!.fileProviderError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Finished recursive change enumeration of working set for user: \(self.ncAccount.ncKitAccount, privacy: .public). Enumerating items.")
|
Logger.enumeration.info(
|
||||||
|
"Finished recursive change enumeration of working set for user: \(self.ncAccount.ncKitAccount, privacy: .public). Enumerating items."
|
||||||
|
)
|
||||||
|
|
||||||
FileProviderEnumerator.completeChangesObserver(observer,
|
FileProviderEnumerator.completeChangesObserver(
|
||||||
anchor: anchor,
|
observer,
|
||||||
ncKit: self.ncKit,
|
anchor: anchor,
|
||||||
newMetadatas: newMetadatas,
|
ncKit: self.ncKit,
|
||||||
updatedMetadatas: updatedMetadatas,
|
newMetadatas: newMetadatas,
|
||||||
deletedMetadatas: deletedMetadatas)
|
updatedMetadatas: updatedMetadatas,
|
||||||
|
deletedMetadatas: deletedMetadatas)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if enumeratedItemIdentifier == .trashContainer {
|
} else if enumeratedItemIdentifier == .trashContainer {
|
||||||
Logger.enumeration.debug("Enumerating changes in trash set for user: \(self.ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.debug(
|
||||||
|
"Enumerating changes in trash set for user: \(self.ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
// TODO!
|
// TODO!
|
||||||
|
|
||||||
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Enumerating changes for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Enumerating changes for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
// No matter what happens here we finish enumeration in some way, either from the error
|
// No matter what happens here we finish enumeration in some way, either from the error
|
||||||
// handling below or from the completeChangesObserver
|
// handling below or from the completeChangesObserver
|
||||||
// TODO: Move to the sync engine extension
|
// TODO: Move to the sync engine extension
|
||||||
FileProviderEnumerator.readServerUrl(serverUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: true) { _, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
|
FileProviderEnumerator.readServerUrl(
|
||||||
|
serverUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: true
|
||||||
|
) { _, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
|
||||||
|
|
||||||
// If we get a 404 we might add more deleted metadatas
|
// If we get a 404 we might add more deleted metadatas
|
||||||
var currentDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
var currentDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
@ -205,46 +259,66 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard readError == nil else {
|
guard readError == nil else {
|
||||||
Logger.enumeration.error("Finishing enumeration of changes for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error: \(readError!.localizedDescription, privacy: .public)")
|
Logger.enumeration.error(
|
||||||
|
"Finishing enumeration of changes for user: \(self.ncAccount.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error: \(readError!.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
let nkReadError = NKError(error: readError!)
|
let nkReadError = NKError(error: readError!)
|
||||||
let fpError = nkReadError.fileProviderError
|
let fpError = nkReadError.fileProviderError
|
||||||
|
|
||||||
if nkReadError.isNotFoundError {
|
if nkReadError.isNotFoundError {
|
||||||
Logger.enumeration.info("404 error means item no longer exists. Deleting metadata and reporting \(self.serverUrl, privacy: .public) as deletion without error")
|
Logger.enumeration.info(
|
||||||
|
"404 error means item no longer exists. Deleting metadata and reporting \(self.serverUrl, privacy: .public) as deletion without error"
|
||||||
|
)
|
||||||
|
|
||||||
guard let itemMetadata = self.enumeratedItemMetadata else {
|
guard let itemMetadata = self.enumeratedItemMetadata else {
|
||||||
Logger.enumeration.error("Invalid enumeratedItemMetadata, could not delete metadata nor report deletion")
|
Logger.enumeration.error(
|
||||||
|
"Invalid enumeratedItemMetadata, could not delete metadata nor report deletion"
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(fpError)
|
observer.finishEnumeratingWithError(fpError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
if itemMetadata.directory {
|
if itemMetadata.directory {
|
||||||
if let deletedDirectoryMetadatas = dbManager.deleteDirectoryAndSubdirectoriesMetadata(ocId: itemMetadata.ocId) {
|
if let deletedDirectoryMetadatas =
|
||||||
|
dbManager.deleteDirectoryAndSubdirectoriesMetadata(
|
||||||
|
ocId: itemMetadata.ocId)
|
||||||
|
{
|
||||||
currentDeletedMetadatas += deletedDirectoryMetadatas
|
currentDeletedMetadatas += deletedDirectoryMetadatas
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.error("Something went wrong when recursively deleting directory not found.")
|
Logger.enumeration.error(
|
||||||
|
"Something went wrong when recursively deleting directory not found."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dbManager.deleteItemMetadata(ocId: itemMetadata.ocId)
|
dbManager.deleteItemMetadata(ocId: itemMetadata.ocId)
|
||||||
}
|
}
|
||||||
|
|
||||||
FileProviderEnumerator.completeChangesObserver(observer, anchor: anchor, ncKit: self.ncKit, newMetadatas: nil, updatedMetadatas: nil, deletedMetadatas: [itemMetadata])
|
FileProviderEnumerator.completeChangesObserver(
|
||||||
|
observer, anchor: anchor, ncKit: self.ncKit, newMetadatas: nil,
|
||||||
|
updatedMetadatas: nil,
|
||||||
|
deletedMetadatas: [itemMetadata])
|
||||||
return
|
return
|
||||||
} else if nkReadError.isNoChangesError { // All is well, just no changed etags
|
} else if nkReadError.isNoChangesError { // All is well, just no changed etags
|
||||||
Logger.enumeration.info("Error was to say no changed files -- not bad error. Finishing change enumeration.")
|
Logger.enumeration.info(
|
||||||
|
"Error was to say no changed files -- not bad error. Finishing change enumeration."
|
||||||
|
)
|
||||||
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
observer.finishEnumeratingWithError(fpError)
|
observer.finishEnumeratingWithError(fpError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Finished reading serverUrl: \(self.serverUrl, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Finished reading serverUrl: \(self.serverUrl, privacy: .public) for user: \(self.ncAccount.ncKitAccount, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
FileProviderEnumerator.completeChangesObserver(observer, anchor: anchor, ncKit: self.ncKit, newMetadatas: newMetadatas, updatedMetadatas: updatedMetadatas, deletedMetadatas: deletedMetadatas)
|
FileProviderEnumerator.completeChangesObserver(
|
||||||
|
observer, anchor: anchor, ncKit: self.ncKit, newMetadatas: newMetadatas,
|
||||||
|
updatedMetadatas: updatedMetadatas, deletedMetadatas: deletedMetadatas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,29 +328,43 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
|
|
||||||
// MARK: - Helper methods
|
// MARK: - Helper methods
|
||||||
|
|
||||||
private static func metadatasToFileProviderItems(_ itemMetadatas: [NextcloudItemMetadataTable], ncKit: NextcloudKit, completionHandler: @escaping(_ items: [NSFileProviderItem]) -> Void) {
|
private static func metadatasToFileProviderItems(
|
||||||
|
_ itemMetadatas: [NextcloudItemMetadataTable], ncKit: NextcloudKit,
|
||||||
|
completionHandler: @escaping (_ items: [NSFileProviderItem]) -> Void
|
||||||
|
) {
|
||||||
var items: [NSFileProviderItem] = []
|
var items: [NSFileProviderItem] = []
|
||||||
|
|
||||||
let conversionQueue = DispatchQueue(label: "metadataToItemConversionQueue", qos: .userInitiated, attributes: .concurrent)
|
let conversionQueue = DispatchQueue(
|
||||||
let appendQueue = DispatchQueue(label: "enumeratorItemAppendQueue", qos: .userInitiated) // Serial queue
|
label: "metadataToItemConversionQueue", qos: .userInitiated, attributes: .concurrent)
|
||||||
|
let appendQueue = DispatchQueue(label: "enumeratorItemAppendQueue", qos: .userInitiated) // Serial queue
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
for itemMetadata in itemMetadatas {
|
for itemMetadata in itemMetadatas {
|
||||||
conversionQueue.async(group: dispatchGroup) {
|
conversionQueue.async(group: dispatchGroup) {
|
||||||
if itemMetadata.e2eEncrypted {
|
if itemMetadata.e2eEncrypted {
|
||||||
Logger.enumeration.info("Skipping encrypted metadata in enumeration: \(itemMetadata.ocId, privacy: .public) \(itemMetadata.fileName, privacy: .public)")
|
Logger.enumeration.info(
|
||||||
|
"Skipping encrypted metadata in enumeration: \(itemMetadata.ocId, privacy: .public) \(itemMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let parentItemIdentifier = NextcloudFilesDatabaseManager.shared.parentItemIdentifierFromMetadata(itemMetadata) {
|
if let parentItemIdentifier = NextcloudFilesDatabaseManager.shared
|
||||||
let item = FileProviderItem(metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit)
|
.parentItemIdentifierFromMetadata(itemMetadata)
|
||||||
Logger.enumeration.debug("Will enumerate item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: .public)")
|
{
|
||||||
|
let item = FileProviderItem(
|
||||||
|
metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier,
|
||||||
|
ncKit: ncKit)
|
||||||
|
Logger.enumeration.debug(
|
||||||
|
"Will enumerate item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
appendQueue.async(group: dispatchGroup) {
|
appendQueue.async(group: dispatchGroup) {
|
||||||
items.append(item)
|
items.append(item)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger.enumeration.error("Could not get valid parentItemIdentifier for item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: .public), skipping enumeration")
|
Logger.enumeration.error(
|
||||||
|
"Could not get valid parentItemIdentifier for item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: .public), skipping enumeration"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,11 +375,13 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func fileProviderPageforNumPage(_ numPage: Int) -> NSFileProviderPage {
|
private static func fileProviderPageforNumPage(_ numPage: Int) -> NSFileProviderPage {
|
||||||
return NSFileProviderPage("\(numPage)".data(using: .utf8)!)
|
NSFileProviderPage("\(numPage)".data(using: .utf8)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func completeEnumerationObserver(_ observer: NSFileProviderEnumerationObserver, ncKit: NextcloudKit, numPage: Int, itemMetadatas: [NextcloudItemMetadataTable]) {
|
private static func completeEnumerationObserver(
|
||||||
|
_ observer: NSFileProviderEnumerationObserver, ncKit: NextcloudKit, numPage: Int,
|
||||||
|
itemMetadatas: [NextcloudItemMetadataTable]
|
||||||
|
) {
|
||||||
metadatasToFileProviderItems(itemMetadatas, ncKit: ncKit) { items in
|
metadatasToFileProviderItems(itemMetadatas, ncKit: ncKit) { items in
|
||||||
observer.didEnumerate(items)
|
observer.didEnumerate(items)
|
||||||
Logger.enumeration.info("Did enumerate \(items.count) items")
|
Logger.enumeration.info("Did enumerate \(items.count) items")
|
||||||
|
@ -310,10 +400,17 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func completeChangesObserver(_ observer: NSFileProviderChangeObserver, anchor: NSFileProviderSyncAnchor, ncKit: NextcloudKit, newMetadatas: [NextcloudItemMetadataTable]?, updatedMetadatas: [NextcloudItemMetadataTable]?, deletedMetadatas: [NextcloudItemMetadataTable]?) {
|
private static func completeChangesObserver(
|
||||||
|
_ observer: NSFileProviderChangeObserver, anchor: NSFileProviderSyncAnchor,
|
||||||
|
ncKit: NextcloudKit,
|
||||||
|
newMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
updatedMetadatas: [NextcloudItemMetadataTable]?,
|
||||||
|
deletedMetadatas: [NextcloudItemMetadataTable]?
|
||||||
|
) {
|
||||||
guard newMetadatas != nil || updatedMetadatas != nil || deletedMetadatas != nil else {
|
guard newMetadatas != nil || updatedMetadatas != nil || deletedMetadatas != nil else {
|
||||||
Logger.enumeration.error("Received invalid newMetadatas, updatedMetadatas or deletedMetadatas. Finished enumeration of changes with error.")
|
Logger.enumeration.error(
|
||||||
|
"Received invalid newMetadatas, updatedMetadatas or deletedMetadatas. Finished enumeration of changes with error."
|
||||||
|
)
|
||||||
observer.finishEnumeratingWithError(NSFileProviderError(.noSuchItem))
|
observer.finishEnumeratingWithError(NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -322,19 +419,20 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
var allUpdatedMetadatas: [NextcloudItemMetadataTable] = []
|
var allUpdatedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
|
||||||
|
|
||||||
if let newMetadatas = newMetadatas {
|
if let newMetadatas {
|
||||||
allUpdatedMetadatas += newMetadatas
|
allUpdatedMetadatas += newMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
if let updatedMetadatas = updatedMetadatas {
|
if let updatedMetadatas {
|
||||||
allUpdatedMetadatas += updatedMetadatas
|
allUpdatedMetadatas += updatedMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
if let deletedMetadatas = deletedMetadatas {
|
if let deletedMetadatas {
|
||||||
allDeletedMetadatas = deletedMetadatas
|
allDeletedMetadatas = deletedMetadatas
|
||||||
}
|
}
|
||||||
|
|
||||||
let allFpItemDeletionsIdentifiers = Array(allDeletedMetadatas.map { NSFileProviderItemIdentifier($0.ocId) })
|
let allFpItemDeletionsIdentifiers = Array(
|
||||||
|
allDeletedMetadatas.map { NSFileProviderItemIdentifier($0.ocId) })
|
||||||
if !allFpItemDeletionsIdentifiers.isEmpty {
|
if !allFpItemDeletionsIdentifiers.isEmpty {
|
||||||
observer.didDeleteItems(withIdentifiers: allFpItemDeletionsIdentifiers)
|
observer.didDeleteItems(withIdentifiers: allFpItemDeletionsIdentifiers)
|
||||||
}
|
}
|
||||||
|
@ -345,7 +443,9 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
||||||
observer.didUpdate(updatedItems)
|
observer.didUpdate(updatedItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.enumeration.info("Processed \(updatedItems.count) new or updated metadatas, \(allDeletedMetadatas.count) deleted metadatas.")
|
Logger.enumeration.info(
|
||||||
|
"Processed \(updatedItems.count) new or updated metadatas, \(allDeletedMetadatas.count) deleted metadatas."
|
||||||
|
)
|
||||||
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
import OSLog
|
import Foundation
|
||||||
import NCDesktopClientSocketKit
|
import NCDesktopClientSocketKit
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
|
import OSLog
|
||||||
|
|
||||||
extension FileProviderExtension {
|
extension FileProviderExtension {
|
||||||
func sendFileProviderDomainIdentifier() {
|
func sendFileProviderDomainIdentifier() {
|
||||||
|
@ -28,7 +28,9 @@ extension FileProviderExtension {
|
||||||
|
|
||||||
private func signalEnumeratorAfterAccountSetup() {
|
private func signalEnumeratorAfterAccountSetup() {
|
||||||
guard let fpManager = NSFileProviderManager(for: domain) else {
|
guard let fpManager = NSFileProviderManager(for: domain) else {
|
||||||
Logger.fileProviderExtension.error("Could not get file provider manager for domain \(self.domain.displayName, privacy: .public), cannot notify after account setup")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not get file provider manager for domain \(self.domain.displayName, privacy: .public), cannot notify after account setup"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,36 +38,47 @@ extension FileProviderExtension {
|
||||||
|
|
||||||
fpManager.signalErrorResolved(NSFileProviderError(.notAuthenticated)) { error in
|
fpManager.signalErrorResolved(NSFileProviderError(.notAuthenticated)) { error in
|
||||||
if error != nil {
|
if error != nil {
|
||||||
Logger.fileProviderExtension.error("Error resolving not authenticated, received error: \(error!.localizedDescription)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Error resolving not authenticated, received error: \(error!.localizedDescription)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Signalling enumerators for user \(self.ncAccount!.username) at server \(self.ncAccount!.serverUrl, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Signalling enumerators for user \(self.ncAccount!.username) at server \(self.ncAccount!.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
fpManager.signalEnumerator(for: .workingSet) { error in
|
fpManager.signalEnumerator(for: .workingSet) { error in
|
||||||
if error != nil {
|
if error != nil {
|
||||||
Logger.fileProviderExtension.error("Error signalling enumerator for working set, received error: \(error!.localizedDescription, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Error signalling enumerator for working set, received error: \(error!.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDomainAccount(user: String, serverUrl: String, password: String) {
|
func setupDomainAccount(user: String, serverUrl: String, password: String) {
|
||||||
ncAccount = NextcloudAccount(user: user, serverUrl: serverUrl, password: password)
|
ncAccount = NextcloudAccount(user: user, serverUrl: serverUrl, password: password)
|
||||||
ncKit.setup(user: ncAccount!.username,
|
ncKit.setup(
|
||||||
userId: ncAccount!.username,
|
user: ncAccount!.username,
|
||||||
password: ncAccount!.password,
|
userId: ncAccount!.username,
|
||||||
urlBase: ncAccount!.serverUrl,
|
password: ncAccount!.password,
|
||||||
userAgent: "Nextcloud-macOS/FileProviderExt",
|
urlBase: ncAccount!.serverUrl,
|
||||||
nextcloudVersion: 25,
|
userAgent: "Nextcloud-macOS/FileProviderExt",
|
||||||
delegate: nil) // TODO: add delegate methods for self
|
nextcloudVersion: 25,
|
||||||
|
delegate: nil) // TODO: add delegate methods for self
|
||||||
|
|
||||||
Logger.fileProviderExtension.info("Nextcloud account set up in File Provider extension for user: \(user, privacy: .public) at server: \(serverUrl, privacy: .public)")
|
Logger.fileProviderExtension.info(
|
||||||
|
"Nextcloud account set up in File Provider extension for user: \(user, privacy: .public) at server: \(serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
signalEnumeratorAfterAccountSetup()
|
signalEnumeratorAfterAccountSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAccountConfig() {
|
func removeAccountConfig() {
|
||||||
Logger.fileProviderExtension.info("Received instruction to remove account data for user \(self.ncAccount!.username, privacy: .public) at server \(self.ncAccount!.serverUrl, privacy: .public)")
|
Logger.fileProviderExtension.info(
|
||||||
|
"Received instruction to remove account data for user \(self.ncAccount!.username, privacy: .public) at server \(self.ncAccount!.serverUrl, privacy: .public)"
|
||||||
|
)
|
||||||
ncAccount = nil
|
ncAccount = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,19 +12,22 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
extension FileProviderExtension: NSFileProviderThumbnailing {
|
extension FileProviderExtension: NSFileProviderThumbnailing {
|
||||||
func fetchThumbnails(for itemIdentifiers: [NSFileProviderItemIdentifier],
|
func fetchThumbnails(
|
||||||
requestedSize size: CGSize,
|
for itemIdentifiers: [NSFileProviderItemIdentifier],
|
||||||
perThumbnailCompletionHandler: @escaping (NSFileProviderItemIdentifier,
|
requestedSize size: CGSize,
|
||||||
Data?,
|
perThumbnailCompletionHandler: @escaping (
|
||||||
Error?) -> Void,
|
NSFileProviderItemIdentifier,
|
||||||
completionHandler: @escaping (Error?) -> Void) -> Progress {
|
Data?,
|
||||||
|
Error?
|
||||||
|
) -> Void,
|
||||||
|
completionHandler: @escaping (Error?) -> Void
|
||||||
|
) -> Progress {
|
||||||
let progress = Progress(totalUnitCount: Int64(itemIdentifiers.count))
|
let progress = Progress(totalUnitCount: Int64(itemIdentifiers.count))
|
||||||
var progressCounter: Int64 = 0
|
var progressCounter: Int64 = 0
|
||||||
|
|
||||||
|
@ -37,21 +40,29 @@ extension FileProviderExtension: NSFileProviderThumbnailing {
|
||||||
}
|
}
|
||||||
|
|
||||||
for itemIdentifier in itemIdentifiers {
|
for itemIdentifier in itemIdentifiers {
|
||||||
Logger.fileProviderExtension.debug("Fetching thumbnail for item with identifier:\(itemIdentifier.rawValue, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
guard let metadata = NextcloudFilesDatabaseManager.shared.itemMetadataFromFileProviderItemIdentifier(itemIdentifier),
|
"Fetching thumbnail for item with identifier:\(itemIdentifier.rawValue, privacy: .public)"
|
||||||
let thumbnailUrl = metadata.thumbnailUrl(size: size) else {
|
)
|
||||||
|
guard
|
||||||
|
let metadata = NextcloudFilesDatabaseManager.shared
|
||||||
|
.itemMetadataFromFileProviderItemIdentifier(itemIdentifier),
|
||||||
|
let thumbnailUrl = metadata.thumbnailUrl(size: size)
|
||||||
|
else {
|
||||||
Logger.fileProviderExtension.debug("Did not fetch thumbnail URL")
|
Logger.fileProviderExtension.debug("Did not fetch thumbnail URL")
|
||||||
finishCurrent()
|
finishCurrent()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Fetching thumbnail for file:\(metadata.fileName) at:\(thumbnailUrl.absoluteString, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Fetching thumbnail for file:\(metadata.fileName) at:\(thumbnailUrl.absoluteString, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
self.ncKit.getPreview(url: thumbnailUrl) { _, data, error in
|
ncKit.getPreview(url: thumbnailUrl) { _, data, error in
|
||||||
if error == .success && data != nil {
|
if error == .success, data != nil {
|
||||||
perThumbnailCompletionHandler(itemIdentifier, data, nil)
|
perThumbnailCompletionHandler(itemIdentifier, data, nil)
|
||||||
} else {
|
} else {
|
||||||
perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
|
perThumbnailCompletionHandler(
|
||||||
|
itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
|
||||||
}
|
}
|
||||||
finishCurrent()
|
finishCurrent()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
import OSLog
|
|
||||||
import NCDesktopClientSocketKit
|
import NCDesktopClientSocketKit
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
|
import OSLog
|
||||||
|
|
||||||
class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKCommonDelegate {
|
class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKCommonDelegate {
|
||||||
let domain: NSFileProviderDomain
|
let domain: NSFileProviderDomain
|
||||||
|
@ -25,15 +25,19 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
return nckb
|
return nckb
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let appGroupIdentifier: String? = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String
|
let appGroupIdentifier: String? =
|
||||||
|
Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String
|
||||||
var ncAccount: NextcloudAccount?
|
var ncAccount: NextcloudAccount?
|
||||||
lazy var socketClient: LocalSocketClient? = {
|
lazy var socketClient: LocalSocketClient? = {
|
||||||
guard let containerUrl = pathForAppGroupContainer() else {
|
guard let containerUrl = pathForAppGroupContainer() else {
|
||||||
Logger.fileProviderExtension.critical("Could not start file provider socket client properly as could not get container url")
|
Logger.fileProviderExtension.critical(
|
||||||
return nil;
|
"Could not start file provider socket client properly as could not get container url"
|
||||||
|
)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let socketPath = containerUrl.appendingPathComponent(".fileprovidersocket", conformingTo: .archive)
|
let socketPath = containerUrl.appendingPathComponent(
|
||||||
|
".fileprovidersocket", conformingTo: .archive)
|
||||||
let lineProcessor = FileProviderSocketLineProcessor(delegate: self)
|
let lineProcessor = FileProviderSocketLineProcessor(delegate: self)
|
||||||
|
|
||||||
return LocalSocketClient(socketPath: socketPath.path, lineProcessor: lineProcessor)
|
return LocalSocketClient(socketPath: socketPath.path, lineProcessor: lineProcessor)
|
||||||
|
@ -50,7 +54,9 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
|
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
|
||||||
configuration.sharedContainerIdentifier = appGroupIdentifier
|
configuration.sharedContainerIdentifier = appGroupIdentifier
|
||||||
|
|
||||||
let session = URLSession(configuration: configuration, delegate: ncKitBackground, delegateQueue: OperationQueue.main)
|
let session = URLSession(
|
||||||
|
configuration: configuration, delegate: ncKitBackground,
|
||||||
|
delegateQueue: OperationQueue.main)
|
||||||
return session
|
return session
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -59,23 +65,31 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
// The containing application must create a domain using `NSFileProviderManager.add(_:, completionHandler:)`. The system will then launch the application extension process, call `FileProviderExtension.init(domain:)` to instantiate the extension for that domain, and call methods on the instance.
|
// The containing application must create a domain using `NSFileProviderManager.add(_:, completionHandler:)`. The system will then launch the application extension process, call `FileProviderExtension.init(domain:)` to instantiate the extension for that domain, and call methods on the instance.
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
self.socketClient?.start()
|
socketClient?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidate() {
|
func invalidate() {
|
||||||
// TODO: cleanup any resources
|
// TODO: cleanup any resources
|
||||||
Logger.fileProviderExtension.debug("Extension for domain \(self.domain.displayName, privacy: .public) is being torn down")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Extension for domain \(self.domain.displayName, privacy: .public) is being torn down")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NSFileProviderReplicatedExtension protocol methods
|
// MARK: NSFileProviderReplicatedExtension protocol methods
|
||||||
|
|
||||||
func item(for identifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress {
|
func item(
|
||||||
|
for identifier: NSFileProviderItemIdentifier, request _: NSFileProviderRequest,
|
||||||
|
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void
|
||||||
|
) -> Progress {
|
||||||
// resolve the given identifier to a record in the model
|
// resolve the given identifier to a record in the model
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Received item request for item with identifier: \(identifier.rawValue, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Received item request for item with identifier: \(identifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
if identifier == .rootContainer {
|
if identifier == .rootContainer {
|
||||||
guard let ncAccount = ncAccount else {
|
guard let ncAccount else {
|
||||||
Logger.fileProviderExtension.error("Not providing item: \(identifier.rawValue, privacy: .public) as account not set up yet")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not providing item: \(identifier.rawValue, privacy: .public) as account not set up yet"
|
||||||
|
)
|
||||||
completionHandler(nil, NSFileProviderError(.notAuthenticated))
|
completionHandler(nil, NSFileProviderError(.notAuthenticated))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
@ -90,35 +104,52 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
metadata.serverUrl = ncAccount.serverUrl
|
metadata.serverUrl = ncAccount.serverUrl
|
||||||
metadata.classFile = NKCommon.TypeClassFile.directory.rawValue
|
metadata.classFile = NKCommon.TypeClassFile.directory.rawValue
|
||||||
|
|
||||||
completionHandler(FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier.rootContainer, ncKit: ncKit), nil)
|
completionHandler(
|
||||||
|
FileProviderItem(
|
||||||
|
metadata: metadata,
|
||||||
|
parentItemIdentifier: NSFileProviderItemIdentifier.rootContainer,
|
||||||
|
ncKit: ncKit), nil)
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
|
|
||||||
guard let metadata = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier),
|
guard let metadata = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier),
|
||||||
let parentItemIdentifier = dbManager.parentItemIdentifierFromMetadata(metadata) else {
|
let parentItemIdentifier = dbManager.parentItemIdentifierFromMetadata(metadata)
|
||||||
|
else {
|
||||||
completionHandler(nil, NSFileProviderError(.noSuchItem))
|
completionHandler(nil, NSFileProviderError(.noSuchItem))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
completionHandler(FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit), nil)
|
completionHandler(
|
||||||
|
FileProviderItem(
|
||||||
|
metadata: metadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit), nil)
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier, version requestedVersion: NSFileProviderItemVersion?, request: NSFileProviderRequest, completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress {
|
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Received request to fetch contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public)")
|
func fetchContents(
|
||||||
|
for itemIdentifier: NSFileProviderItemIdentifier,
|
||||||
|
version requestedVersion: NSFileProviderItemVersion?, request: NSFileProviderRequest,
|
||||||
|
completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void
|
||||||
|
) -> Progress {
|
||||||
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Received request to fetch contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
guard requestedVersion == nil else {
|
guard requestedVersion == nil else {
|
||||||
// TODO: Add proper support for file versioning
|
// TODO: Add proper support for file versioning
|
||||||
Logger.fileProviderExtension.error("Can't return contents for specific version as this is not supported.")
|
Logger.fileProviderExtension.error(
|
||||||
completionHandler(nil, nil, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
|
"Can't return contents for specific version as this is not supported.")
|
||||||
|
completionHandler(
|
||||||
|
nil, nil,
|
||||||
|
NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo: [:]))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
guard ncAccount != nil else {
|
guard ncAccount != nil else {
|
||||||
Logger.fileProviderExtension.error("Not fetching contents item: \(itemIdentifier.rawValue, privacy: .public) as account not set up yet")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not fetching contents item: \(itemIdentifier.rawValue, privacy: .public) as account not set up yet"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, NSFileProviderError(.notAuthenticated))
|
completionHandler(nil, nil, NSFileProviderError(.notAuthenticated))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
@ -126,46 +157,65 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let ocId = itemIdentifier.rawValue
|
let ocId = itemIdentifier.rawValue
|
||||||
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
||||||
Logger.fileProviderExtension.error("Could not acquire metadata of item with identifier: \(itemIdentifier.rawValue, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not acquire metadata of item with identifier: \(itemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !metadata.isDocumentViewableOnly else {
|
guard !metadata.isDocumentViewableOnly else {
|
||||||
Logger.fileProviderExtension.error("Could not get contents of item as is readonly: \(itemIdentifier.rawValue, privacy: .public) \(metadata.fileName, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not get contents of item as is readonly: \(itemIdentifier.rawValue, privacy: .public) \(metadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, NSFileProviderError(.cannotSynchronize))
|
completionHandler(nil, nil, NSFileProviderError(.cannotSynchronize))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
|
let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Fetching file with name \(metadata.fileName, privacy: .public) at URL: \(serverUrlFileName, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Fetching file with name \(metadata.fileName, privacy: .public) at URL: \(serverUrlFileName, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
let progress = Progress()
|
let progress = Progress()
|
||||||
|
|
||||||
// TODO: Handle folders nicely
|
// TODO: Handle folders nicely
|
||||||
do {
|
do {
|
||||||
let fileNameLocalPath = try localPathForNCFile(ocId: metadata.ocId, fileNameView: metadata.fileNameView, domain: self.domain)
|
let fileNameLocalPath = try localPathForNCFile(
|
||||||
|
ocId: metadata.ocId, fileNameView: metadata.fileNameView, domain: domain)
|
||||||
|
|
||||||
dbManager.setStatusForItemMetadata(metadata, status: NextcloudItemMetadataTable.Status.downloading) { updatedMetadata in
|
dbManager.setStatusForItemMetadata(
|
||||||
|
metadata, status: NextcloudItemMetadataTable.Status.downloading
|
||||||
|
) { updatedMetadata in
|
||||||
|
|
||||||
guard let updatedMetadata = updatedMetadata else {
|
guard let updatedMetadata else {
|
||||||
Logger.fileProviderExtension.error("Could not acquire updated metadata of item with identifier: \(itemIdentifier.rawValue, privacy: .public), unable to update item status to downloading")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not acquire updated metadata of item with identifier: \(itemIdentifier.rawValue, privacy: .public), unable to update item status to downloading"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ncKit.download(serverUrlFileName: serverUrlFileName,
|
self.ncKit.download(
|
||||||
fileNameLocalPath: fileNameLocalPath.path,
|
serverUrlFileName: serverUrlFileName,
|
||||||
requestHandler: { request in
|
fileNameLocalPath: fileNameLocalPath.path,
|
||||||
progress.setHandlersFromAfRequest(request)
|
requestHandler: { request in
|
||||||
}, taskHandler: { task in
|
progress.setHandlersFromAfRequest(request)
|
||||||
NSFileProviderManager(for: self.domain)?.register(task, forItemWithIdentifier: itemIdentifier, completionHandler: { _ in })
|
},
|
||||||
}, progressHandler: { downloadProgress in
|
taskHandler: { task in
|
||||||
downloadProgress.copyCurrentStateToProgress(progress)
|
NSFileProviderManager(for: self.domain)?.register(
|
||||||
}) { _, etag, date, _, _, _, error in
|
task, forItemWithIdentifier: itemIdentifier, completionHandler: { _ in }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
progressHandler: { downloadProgress in
|
||||||
|
downloadProgress.copyCurrentStateToProgress(progress)
|
||||||
|
}
|
||||||
|
) { _, etag, date, _, _, _, error in
|
||||||
if error == .success {
|
if error == .success {
|
||||||
Logger.fileTransfer.debug("Acquired contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public) and filename: \(updatedMetadata.fileName, privacy: .public)")
|
Logger.fileTransfer.debug(
|
||||||
|
"Acquired contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public) and filename: \(updatedMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
updatedMetadata.status = NextcloudItemMetadataTable.Status.normal.rawValue
|
updatedMetadata.status = NextcloudItemMetadataTable.Status.normal.rawValue
|
||||||
updatedMetadata.sessionError = ""
|
updatedMetadata.sessionError = ""
|
||||||
|
@ -175,18 +225,26 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
dbManager.addLocalFileMetadataFromItemMetadata(updatedMetadata)
|
dbManager.addLocalFileMetadataFromItemMetadata(updatedMetadata)
|
||||||
dbManager.addItemMetadata(updatedMetadata)
|
dbManager.addItemMetadata(updatedMetadata)
|
||||||
|
|
||||||
guard let parentItemIdentifier = dbManager.parentItemIdentifierFromMetadata(updatedMetadata) else {
|
guard
|
||||||
|
let parentItemIdentifier = dbManager.parentItemIdentifierFromMetadata(
|
||||||
|
updatedMetadata)
|
||||||
|
else {
|
||||||
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
completionHandler(nil, nil, NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let fpItem = FileProviderItem(metadata: updatedMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
|
let fpItem = FileProviderItem(
|
||||||
|
metadata: updatedMetadata, parentItemIdentifier: parentItemIdentifier,
|
||||||
|
ncKit: self.ncKit)
|
||||||
|
|
||||||
completionHandler(fileNameLocalPath, fpItem, nil)
|
completionHandler(fileNameLocalPath, fpItem, nil)
|
||||||
} else {
|
} else {
|
||||||
Logger.fileTransfer.error("Could not acquire contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public) and fileName: \(updatedMetadata.fileName, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not acquire contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public) and fileName: \(updatedMetadata.fileName, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
updatedMetadata.status = NextcloudItemMetadataTable.Status.downloadError.rawValue
|
updatedMetadata.status =
|
||||||
|
NextcloudItemMetadataTable.Status.downloadError.rawValue
|
||||||
updatedMetadata.sessionError = error.errorDescription
|
updatedMetadata.sessionError = error.errorDescription
|
||||||
|
|
||||||
dbManager.addItemMetadata(updatedMetadata)
|
dbManager.addItemMetadata(updatedMetadata)
|
||||||
|
@ -195,40 +253,60 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch {
|
||||||
Logger.fileProviderExtension.error("Could not find local path for file \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not find local path for file \(metadata.fileName, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(nil, nil, NSFileProviderError(.cannotSynchronize))
|
completionHandler(nil, nil, NSFileProviderError(.cannotSynchronize))
|
||||||
}
|
}
|
||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
func createItem(basedOn itemTemplate: NSFileProviderItem, fields: NSFileProviderItemFields, contents url: URL?, options: NSFileProviderCreateItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress {
|
func createItem(
|
||||||
|
basedOn itemTemplate: NSFileProviderItem, fields _: NSFileProviderItemFields,
|
||||||
|
contents url: URL?, options: NSFileProviderCreateItemOptions = [],
|
||||||
|
request: NSFileProviderRequest,
|
||||||
|
completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?)
|
||||||
|
->
|
||||||
|
Void
|
||||||
|
) -> Progress {
|
||||||
// TODO: a new item was created on disk, process the item's creation
|
// TODO: a new item was created on disk, process the item's creation
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Received create item request for item with identifier: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) and filename: \(itemTemplate.filename, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Received create item request for item with identifier: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) and filename: \(itemTemplate.filename, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
guard itemTemplate.contentType != .symbolicLink else {
|
guard itemTemplate.contentType != .symbolicLink else {
|
||||||
Logger.fileProviderExtension.error("Cannot create item, symbolic links not supported.")
|
Logger.fileProviderExtension.error("Cannot create item, symbolic links not supported.")
|
||||||
completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
|
completionHandler(
|
||||||
|
itemTemplate, NSFileProviderItemFields(), false,
|
||||||
|
NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo: [:]))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let ncAccount = ncAccount else {
|
guard let ncAccount else {
|
||||||
Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) as account not set up yet")
|
Logger.fileProviderExtension.error(
|
||||||
completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.notAuthenticated))
|
"Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) as account not set up yet"
|
||||||
|
)
|
||||||
|
completionHandler(
|
||||||
|
itemTemplate, NSFileProviderItemFields(), false,
|
||||||
|
NSFileProviderError(.notAuthenticated))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let parentItemIdentifier = itemTemplate.parentItemIdentifier
|
let parentItemIdentifier = itemTemplate.parentItemIdentifier
|
||||||
let itemTemplateIsFolder = itemTemplate.contentType == .folder ||
|
let itemTemplateIsFolder =
|
||||||
itemTemplate.contentType == .directory
|
itemTemplate.contentType == .folder || itemTemplate.contentType == .directory
|
||||||
|
|
||||||
if options.contains(.mayAlreadyExist) {
|
if options.contains(.mayAlreadyExist) {
|
||||||
// TODO: This needs to be properly handled with a check in the db
|
// TODO: This needs to be properly handled with a check in the db
|
||||||
Logger.fileProviderExtension.info("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) as it may already exist")
|
Logger.fileProviderExtension.info(
|
||||||
completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
|
"Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) as it may already exist"
|
||||||
|
)
|
||||||
|
completionHandler(
|
||||||
|
itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,9 +315,16 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
if parentItemIdentifier == .rootContainer {
|
if parentItemIdentifier == .rootContainer {
|
||||||
parentItemServerUrl = ncAccount.davFilesUrl
|
parentItemServerUrl = ncAccount.davFilesUrl
|
||||||
} else {
|
} else {
|
||||||
guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
|
guard
|
||||||
Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
|
let parentItemMetadata = dbManager.directoryMetadata(
|
||||||
completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
|
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()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,29 +334,43 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
let fileNameLocalPath = url?.path ?? ""
|
let fileNameLocalPath = url?.path ?? ""
|
||||||
let newServerUrlFileName = parentItemServerUrl + "/" + 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: .public) with contents located at: \(fileNameLocalPath, privacy: .public)")
|
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: .public) with contents located at: \(fileNameLocalPath, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if itemTemplateIsFolder {
|
if itemTemplateIsFolder {
|
||||||
self.ncKit.createFolder(serverUrlFileName: newServerUrlFileName) { account, ocId, _, error in
|
ncKit.createFolder(serverUrlFileName: newServerUrlFileName) { account, _, _, error in
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.fileTransfer.error("Could not create new folder with name: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not create new folder with name: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(itemTemplate, [], false, error.fileProviderError)
|
completionHandler(itemTemplate, [], false, error.fileProviderError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read contents after creation
|
// Read contents after creation
|
||||||
self.ncKit.readFileOrFolder(serverUrlFileName: newServerUrlFileName, depth: "0", showHiddenFiles: true) { account, files, _, error in
|
self.ncKit.readFileOrFolder(
|
||||||
|
serverUrlFileName: newServerUrlFileName, depth: "0", showHiddenFiles: true
|
||||||
|
) { account, files, _, error in
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.fileTransfer.error("Could not read new folder with name: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not read new folder with name: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(files, account: account) { directoryMetadata, childDirectoriesMetadata, metadatas in
|
NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(
|
||||||
|
files, account: account
|
||||||
|
) {
|
||||||
|
directoryMetadata, _, _ in
|
||||||
|
|
||||||
dbManager.addItemMetadata(directoryMetadata)
|
dbManager.addItemMetadata(directoryMetadata)
|
||||||
|
|
||||||
let fpItem = FileProviderItem(metadata: directoryMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
|
let fpItem = FileProviderItem(
|
||||||
|
metadata: directoryMetadata,
|
||||||
|
parentItemIdentifier: parentItemIdentifier,
|
||||||
|
ncKit: self.ncKit)
|
||||||
|
|
||||||
completionHandler(fpItem, [], true, nil)
|
completionHandler(fpItem, [], true, nil)
|
||||||
}
|
}
|
||||||
|
@ -284,25 +383,37 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
|
|
||||||
let progress = Progress()
|
let progress = Progress()
|
||||||
|
|
||||||
self.ncKit.upload(serverUrlFileName: newServerUrlFileName,
|
ncKit.upload(
|
||||||
fileNameLocalPath: fileNameLocalPath,
|
serverUrlFileName: newServerUrlFileName,
|
||||||
requestHandler: { request in
|
fileNameLocalPath: fileNameLocalPath,
|
||||||
progress.setHandlersFromAfRequest(request)
|
requestHandler: { request in
|
||||||
}, taskHandler: { task in
|
progress.setHandlersFromAfRequest(request)
|
||||||
NSFileProviderManager(for: self.domain)?.register(task, forItemWithIdentifier: itemTemplate.itemIdentifier, completionHandler: { _ in })
|
},
|
||||||
}, progressHandler: { uploadProgress in
|
taskHandler: { task in
|
||||||
uploadProgress.copyCurrentStateToProgress(progress)
|
NSFileProviderManager(for: self.domain)?.register(
|
||||||
}) { account, ocId, etag, date, size, _, _, error in
|
task, forItemWithIdentifier: itemTemplate.itemIdentifier,
|
||||||
guard error == .success, let ocId = ocId else {
|
completionHandler: { _ in })
|
||||||
Logger.fileTransfer.error("Could not upload item with filename: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
},
|
||||||
|
progressHandler: { uploadProgress in
|
||||||
|
uploadProgress.copyCurrentStateToProgress(progress)
|
||||||
|
}
|
||||||
|
) { account, ocId, etag, date, size, _, _, error in
|
||||||
|
guard error == .success, let ocId else {
|
||||||
|
Logger.fileTransfer.error(
|
||||||
|
"Could not upload item with filename: \(itemTemplate.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(itemTemplate, [], false, error.fileProviderError)
|
completionHandler(itemTemplate, [], false, error.fileProviderError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.fileTransfer.info("Successfully uploaded item with identifier: \(ocId, privacy: .public) and filename: \(itemTemplate.filename, privacy: .public)")
|
Logger.fileTransfer.info(
|
||||||
|
"Successfully uploaded item with identifier: \(ocId, privacy: .public) and filename: \(itemTemplate.filename, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if size != itemTemplate.documentSize as? Int64 {
|
if size != itemTemplate.documentSize as? Int64 {
|
||||||
Logger.fileTransfer.warning("Created item upload reported as successful, but there are differences between the received file size (\(size, privacy: .public)) and the original file size (\(itemTemplate.documentSize??.int64Value ?? 0))")
|
Logger.fileTransfer.warning(
|
||||||
|
"Created item upload reported as successful, but there are differences between the received file size (\(size, privacy: .public)) and the original file size (\(itemTemplate.documentSize??.int64Value ?? 0))"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let newMetadata = NextcloudItemMetadataTable()
|
let newMetadata = NextcloudItemMetadataTable()
|
||||||
|
@ -324,34 +435,48 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
|
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
|
||||||
dbManager.addItemMetadata(newMetadata)
|
dbManager.addItemMetadata(newMetadata)
|
||||||
|
|
||||||
let fpItem = FileProviderItem(metadata: newMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
|
let fpItem = FileProviderItem(
|
||||||
|
metadata: newMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit
|
||||||
|
)
|
||||||
|
|
||||||
completionHandler(fpItem, [], false, nil)
|
completionHandler(fpItem, [], false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
func modifyItem(_ item: NSFileProviderItem, baseVersion version: NSFileProviderItemVersion, changedFields: NSFileProviderItemFields, contents newContents: URL?, options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress {
|
func modifyItem(
|
||||||
|
_ item: NSFileProviderItem, baseVersion _: NSFileProviderItemVersion,
|
||||||
|
changedFields: NSFileProviderItemFields, contents newContents: URL?,
|
||||||
|
options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest,
|
||||||
|
completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?)
|
||||||
|
->
|
||||||
|
Void
|
||||||
|
) -> Progress {
|
||||||
// An item was modified on disk, process the item's modification
|
// An item was modified on disk, process the item's modification
|
||||||
// TODO: Handle finder things like tags, other possible item changed fields
|
// TODO: Handle finder things like tags, other possible item changed fields
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Received modify item request for item with identifier: \(item.itemIdentifier.rawValue, privacy: .public) and filename: \(item.filename, privacy: .public)")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Received modify item request for item with identifier: \(item.itemIdentifier.rawValue, privacy: .public) and filename: \(item.filename, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
guard let ncAccount = ncAccount else {
|
guard let ncAccount else {
|
||||||
Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public) as account not set up yet")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public) as account not set up yet"
|
||||||
|
)
|
||||||
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
|
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let parentItemIdentifier = item.parentItemIdentifier
|
let parentItemIdentifier = item.parentItemIdentifier
|
||||||
let itemTemplateIsFolder = item.contentType == .folder ||
|
let itemTemplateIsFolder = item.contentType == .folder || item.contentType == .directory
|
||||||
item.contentType == .directory
|
|
||||||
|
|
||||||
if options.contains(.mayAlreadyExist) {
|
if options.contains(.mayAlreadyExist) {
|
||||||
// TODO: This needs to be properly handled with a check in the db
|
// TODO: This needs to be properly handled with a check in the db
|
||||||
Logger.fileProviderExtension.warning("Modification for item: \(item.itemIdentifier.rawValue, privacy: .public) may already exist")
|
Logger.fileProviderExtension.warning(
|
||||||
|
"Modification for item: \(item.itemIdentifier.rawValue, privacy: .public) may already exist"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var parentItemServerUrl: String
|
var parentItemServerUrl: String
|
||||||
|
@ -359,8 +484,13 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
if parentItemIdentifier == .rootContainer {
|
if parentItemIdentifier == .rootContainer {
|
||||||
parentItemServerUrl = ncAccount.davFilesUrl
|
parentItemServerUrl = ncAccount.davFilesUrl
|
||||||
} else {
|
} else {
|
||||||
guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
|
guard
|
||||||
Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
|
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))
|
completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
@ -371,7 +501,9 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
let fileNameLocalPath = newContents?.path ?? ""
|
let fileNameLocalPath = newContents?.path ?? ""
|
||||||
let newServerUrlFileName = parentItemServerUrl + "/" + 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: .public) to server url: \(newServerUrlFileName, privacy: .public) with contents located at: \(fileNameLocalPath, privacy: .public)")
|
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: .public) to server url: \(newServerUrlFileName, privacy: .public) with contents located at: \(fileNameLocalPath, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
var modifiedItem = item
|
var modifiedItem = item
|
||||||
|
|
||||||
|
@ -384,10 +516,14 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
if changedFields.contains(.filename) || changedFields.contains(.parentItemIdentifier) {
|
if changedFields.contains(.filename) || changedFields.contains(.parentItemIdentifier) {
|
||||||
dispatchQueue.async {
|
dispatchQueue.async {
|
||||||
let ocId = item.itemIdentifier.rawValue
|
let ocId = item.itemIdentifier.rawValue
|
||||||
Logger.fileProviderExtension.debug("Changed fields for item \(ocId, privacy: .public) with filename \(item.filename, privacy: .public) includes filename or parentitemidentifier...")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Changed fields for item \(ocId, privacy: .public) with filename \(item.filename, privacy: .public) includes filename or parentitemidentifier..."
|
||||||
|
)
|
||||||
|
|
||||||
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
||||||
Logger.fileProviderExtension.error("Could not acquire metadata of item with identifier: \(item.itemIdentifier.rawValue, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not acquire metadata of item with identifier: \(item.itemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
|
completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -395,14 +531,18 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
var renameError: NSFileProviderError?
|
var renameError: NSFileProviderError?
|
||||||
let oldServerUrlFileName = metadata.serverUrl + "/" + metadata.fileName
|
let oldServerUrlFileName = metadata.serverUrl + "/" + metadata.fileName
|
||||||
|
|
||||||
let moveFileOrFolderDispatchGroup = DispatchGroup() // Make this block wait until done
|
let moveFileOrFolderDispatchGroup = DispatchGroup() // Make this block wait until done
|
||||||
moveFileOrFolderDispatchGroup.enter()
|
moveFileOrFolderDispatchGroup.enter()
|
||||||
|
|
||||||
self.ncKit.moveFileOrFolder(serverUrlFileNameSource: oldServerUrlFileName,
|
self.ncKit.moveFileOrFolder(
|
||||||
serverUrlFileNameDestination: newServerUrlFileName,
|
serverUrlFileNameSource: oldServerUrlFileName,
|
||||||
overwrite: false) { account, error in
|
serverUrlFileNameDestination: newServerUrlFileName,
|
||||||
|
overwrite: false
|
||||||
|
) { _, error in
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.fileTransfer.error("Could not move file or folder: \(oldServerUrlFileName, privacy: .public) to \(newServerUrlFileName, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not move file or folder: \(oldServerUrlFileName, privacy: .public) to \(newServerUrlFileName, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
renameError = error.fileProviderError
|
renameError = error.fileProviderError
|
||||||
moveFileOrFolderDispatchGroup.leave()
|
moveFileOrFolderDispatchGroup.leave()
|
||||||
return
|
return
|
||||||
|
@ -411,37 +551,49 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
// Remember that a folder metadata's serverUrl is its direct server URL, while for
|
// Remember that a folder metadata's serverUrl is its direct server URL, while for
|
||||||
// an item metadata the server URL is the parent folder's URL
|
// an item metadata the server URL is the parent folder's URL
|
||||||
if itemTemplateIsFolder {
|
if itemTemplateIsFolder {
|
||||||
_ = dbManager.renameDirectoryAndPropagateToChildren(ocId: ocId, newServerUrl: newServerUrlFileName, newFileName: item.filename)
|
_ = dbManager.renameDirectoryAndPropagateToChildren(
|
||||||
|
ocId: ocId, newServerUrl: newServerUrlFileName,
|
||||||
|
newFileName: item.filename)
|
||||||
self.signalEnumerator { error in
|
self.signalEnumerator { error in
|
||||||
if error != nil {
|
if error != nil {
|
||||||
Logger.fileTransfer.error("Error notifying change in moved directory: \(error)")
|
Logger.fileTransfer.error(
|
||||||
|
"Error notifying change in moved directory: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dbManager.renameItemMetadata(ocId: ocId, newServerUrl: parentItemServerUrl, newFileName: item.filename)
|
dbManager.renameItemMetadata(
|
||||||
|
ocId: ocId, newServerUrl: parentItemServerUrl,
|
||||||
|
newFileName: item.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let newMetadata = dbManager.itemMetadataFromOcId(ocId) else {
|
guard let newMetadata = dbManager.itemMetadataFromOcId(ocId) else {
|
||||||
Logger.fileTransfer.error("Could not acquire metadata of item with identifier: \(ocId, privacy: .public), cannot correctly inform of modification")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not acquire metadata of item with identifier: \(ocId, privacy: .public), cannot correctly inform of modification"
|
||||||
|
)
|
||||||
renameError = NSFileProviderError(.noSuchItem)
|
renameError = NSFileProviderError(.noSuchItem)
|
||||||
moveFileOrFolderDispatchGroup.leave()
|
moveFileOrFolderDispatchGroup.leave()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiedItem = FileProviderItem(metadata: newMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
|
modifiedItem = FileProviderItem(
|
||||||
|
metadata: newMetadata, parentItemIdentifier: parentItemIdentifier,
|
||||||
|
ncKit: self.ncKit)
|
||||||
moveFileOrFolderDispatchGroup.leave()
|
moveFileOrFolderDispatchGroup.leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
moveFileOrFolderDispatchGroup.wait()
|
moveFileOrFolderDispatchGroup.wait()
|
||||||
|
|
||||||
guard renameError == nil else {
|
guard renameError == nil else {
|
||||||
Logger.fileTransfer.error("Stopping rename of item with ocId \(ocId, privacy: .public) due to error: \(renameError!.localizedDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Stopping rename of item with ocId \(ocId, privacy: .public) due to error: \(renameError!.localizedDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(modifiedItem, [], false, renameError)
|
completionHandler(modifiedItem, [], false, renameError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !itemTemplateIsFolder else {
|
guard !itemTemplateIsFolder else {
|
||||||
Logger.fileTransfer.debug("Only handling renaming for folders. ocId: \(ocId, privacy: .public)")
|
Logger.fileTransfer.debug(
|
||||||
|
"Only handling renaming for folders. ocId: \(ocId, privacy: .public)")
|
||||||
completionHandler(modifiedItem, [], false, nil)
|
completionHandler(modifiedItem, [], false, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -454,7 +606,9 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !itemTemplateIsFolder else {
|
guard !itemTemplateIsFolder else {
|
||||||
Logger.fileTransfer.debug("System requested modification for folder with ocID \(item.itemIdentifier.rawValue, privacy: .public) (\(newServerUrlFileName, privacy: .public)) of something other than folder name.")
|
Logger.fileTransfer.debug(
|
||||||
|
"System requested modification for folder with ocID \(item.itemIdentifier.rawValue, privacy: .public) (\(newServerUrlFileName, privacy: .public)) of something other than folder name."
|
||||||
|
)
|
||||||
completionHandler(modifiedItem, [], false, nil)
|
completionHandler(modifiedItem, [], false, nil)
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
@ -463,41 +617,62 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
|
|
||||||
if changedFields.contains(.contents) {
|
if changedFields.contains(.contents) {
|
||||||
dispatchQueue.async {
|
dispatchQueue.async {
|
||||||
Logger.fileProviderExtension.debug("Item modification for \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public) includes contents")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Item modification for \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public) includes contents"
|
||||||
|
)
|
||||||
|
|
||||||
guard newContents != nil else {
|
guard newContents != nil else {
|
||||||
Logger.fileProviderExtension.warning("WARNING. Could not upload modified contents as was provided nil contents url. ocId: \(item.itemIdentifier.rawValue, privacy: .public)")
|
Logger.fileProviderExtension.warning(
|
||||||
|
"WARNING. Could not upload modified contents as was provided nil contents url. ocId: \(item.itemIdentifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(modifiedItem, [], false, NSFileProviderError(.noSuchItem))
|
completionHandler(modifiedItem, [], false, NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ocId = item.itemIdentifier.rawValue
|
let ocId = item.itemIdentifier.rawValue
|
||||||
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
|
||||||
Logger.fileProviderExtension.error("Could not acquire metadata of item with identifier: \(ocId, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
completionHandler(item, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
|
"Could not acquire metadata of item with identifier: \(ocId, privacy: .public)"
|
||||||
|
)
|
||||||
|
completionHandler(
|
||||||
|
item, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dbManager.setStatusForItemMetadata(metadata, status: NextcloudItemMetadataTable.Status.uploading) { updatedMetadata in
|
dbManager.setStatusForItemMetadata(
|
||||||
|
metadata, status: NextcloudItemMetadataTable.Status.uploading
|
||||||
|
) { updatedMetadata in
|
||||||
|
|
||||||
if updatedMetadata == nil {
|
if updatedMetadata == nil {
|
||||||
Logger.fileProviderExtension.warning("Could not acquire updated metadata of item with identifier: \(ocId, privacy: .public), unable to update item status to uploading")
|
Logger.fileProviderExtension.warning(
|
||||||
|
"Could not acquire updated metadata of item with identifier: \(ocId, privacy: .public), unable to update item status to uploading"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ncKit.upload(serverUrlFileName: newServerUrlFileName,
|
self.ncKit.upload(
|
||||||
fileNameLocalPath: fileNameLocalPath,
|
serverUrlFileName: newServerUrlFileName,
|
||||||
requestHandler: { request in
|
fileNameLocalPath: fileNameLocalPath,
|
||||||
progress.setHandlersFromAfRequest(request)
|
requestHandler: { request in
|
||||||
}, taskHandler: { task in
|
progress.setHandlersFromAfRequest(request)
|
||||||
NSFileProviderManager(for: self.domain)?.register(task, forItemWithIdentifier: item.itemIdentifier, completionHandler: { _ in })
|
},
|
||||||
}, progressHandler: { uploadProgress in
|
taskHandler: { task in
|
||||||
uploadProgress.copyCurrentStateToProgress(progress)
|
NSFileProviderManager(for: self.domain)?.register(
|
||||||
}) { account, ocId, etag, date, size, _, _, error in
|
task, forItemWithIdentifier: item.itemIdentifier,
|
||||||
if error == .success, let ocId = ocId {
|
completionHandler: { _ in })
|
||||||
Logger.fileProviderExtension.info("Successfully uploaded item with identifier: \(ocId, privacy: .public) and filename: \(item.filename, privacy: .public)")
|
},
|
||||||
|
progressHandler: { uploadProgress in
|
||||||
|
uploadProgress.copyCurrentStateToProgress(progress)
|
||||||
|
}
|
||||||
|
) { account, ocId, etag, date, size, _, _, error in
|
||||||
|
if error == .success, let ocId {
|
||||||
|
Logger.fileProviderExtension.info(
|
||||||
|
"Successfully uploaded item with identifier: \(ocId, privacy: .public) and filename: \(item.filename, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if size != item.documentSize as? Int64 {
|
if size != item.documentSize as? Int64 {
|
||||||
Logger.fileTransfer.warning("Created item upload reported as successful, but there are differences between the received file size (\(size, privacy: .public)) and the original file size (\(item.documentSize??.int64Value ?? 0))")
|
Logger.fileTransfer.warning(
|
||||||
|
"Created item upload reported as successful, but there are differences between the received file size (\(size, privacy: .public)) and the original file size (\(item.documentSize??.int64Value ?? 0))"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let newMetadata = NextcloudItemMetadataTable()
|
let newMetadata = NextcloudItemMetadataTable()
|
||||||
|
@ -519,10 +694,15 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
|
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
|
||||||
dbManager.addItemMetadata(newMetadata)
|
dbManager.addItemMetadata(newMetadata)
|
||||||
|
|
||||||
modifiedItem = FileProviderItem(metadata: newMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
|
modifiedItem = FileProviderItem(
|
||||||
|
metadata: newMetadata, parentItemIdentifier: parentItemIdentifier,
|
||||||
|
ncKit: self.ncKit
|
||||||
|
)
|
||||||
completionHandler(modifiedItem, [], false, nil)
|
completionHandler(modifiedItem, [], false, nil)
|
||||||
} else {
|
} else {
|
||||||
Logger.fileTransfer.error("Could not upload item \(item.itemIdentifier.rawValue, privacy: .public) with filename: \(item.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not upload item \(item.itemIdentifier.rawValue, privacy: .public) with filename: \(item.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
metadata.status = NextcloudItemMetadataTable.Status.uploadError.rawValue
|
metadata.status = NextcloudItemMetadataTable.Status.uploadError.rawValue
|
||||||
metadata.sessionError = error.errorDescription
|
metadata.sessionError = error.errorDescription
|
||||||
|
@ -536,19 +716,28 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger.fileProviderExtension.debug("Nothing more to do with \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public), modifications complete")
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Nothing more to do with \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public), modifications complete"
|
||||||
|
)
|
||||||
completionHandler(modifiedItem, [], false, nil)
|
completionHandler(modifiedItem, [], false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteItem(identifier: NSFileProviderItemIdentifier, baseVersion version: NSFileProviderItemVersion, options: NSFileProviderDeleteItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (Error?) -> Void) -> Progress {
|
|
||||||
|
|
||||||
Logger.fileProviderExtension.debug("Received delete item request for item with identifier: \(identifier.rawValue, privacy: .public)")
|
func deleteItem(
|
||||||
|
identifier: NSFileProviderItemIdentifier, baseVersion _: NSFileProviderItemVersion,
|
||||||
|
options _: NSFileProviderDeleteItemOptions = [], request _: NSFileProviderRequest,
|
||||||
|
completionHandler: @escaping (Error?) -> Void
|
||||||
|
) -> Progress {
|
||||||
|
Logger.fileProviderExtension.debug(
|
||||||
|
"Received delete item request for item with identifier: \(identifier.rawValue, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
guard ncAccount != nil else {
|
guard ncAccount != nil else {
|
||||||
Logger.fileProviderExtension.error("Not deleting item: \(identifier.rawValue, privacy: .public) as account not set up yet")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not deleting item: \(identifier.rawValue, privacy: .public) as account not set up yet"
|
||||||
|
)
|
||||||
completionHandler(NSFileProviderError(.notAuthenticated))
|
completionHandler(NSFileProviderError(.notAuthenticated))
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
@ -566,14 +755,18 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ncKit.deleteFileOrFolder(serverUrlFileName: serverFileNameUrl) { account, error in
|
ncKit.deleteFileOrFolder(serverUrlFileName: serverFileNameUrl) { _, error in
|
||||||
guard error == .success else {
|
guard error == .success else {
|
||||||
Logger.fileTransfer.error("Could not delete item with ocId \(identifier.rawValue, privacy: .public) at \(serverFileNameUrl, privacy: .public), received error: \(error.errorDescription, privacy: .public)")
|
Logger.fileTransfer.error(
|
||||||
|
"Could not delete item with ocId \(identifier.rawValue, privacy: .public) at \(serverFileNameUrl, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler(error.fileProviderError)
|
completionHandler(error.fileProviderError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.fileTransfer.info("Successfully deleted item with identifier: \(identifier.rawValue, privacy: .public) at: \(serverFileNameUrl, privacy: .public)")
|
Logger.fileTransfer.info(
|
||||||
|
"Successfully deleted item with identifier: \(identifier.rawValue, privacy: .public) at: \(serverFileNameUrl, privacy: .public)"
|
||||||
|
)
|
||||||
|
|
||||||
if itemMetadata.directory {
|
if itemMetadata.directory {
|
||||||
_ = dbManager.deleteDirectoryAndSubdirectoriesMetadata(ocId: ocId)
|
_ = dbManager.deleteDirectoryAndSubdirectoriesMetadata(ocId: ocId)
|
||||||
|
@ -589,32 +782,41 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
|
|
||||||
return Progress()
|
return Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest) throws -> NSFileProviderEnumerator {
|
|
||||||
|
|
||||||
guard let ncAccount = ncAccount else {
|
func enumerator(
|
||||||
Logger.fileProviderExtension.error("Not providing enumerator for container with identifier \(containerItemIdentifier.rawValue, privacy: .public) yet as account not set up")
|
for containerItemIdentifier: NSFileProviderItemIdentifier, request _: NSFileProviderRequest
|
||||||
|
) throws -> NSFileProviderEnumerator {
|
||||||
|
guard let ncAccount else {
|
||||||
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not providing enumerator for container with identifier \(containerItemIdentifier.rawValue, privacy: .public) yet as account not set up"
|
||||||
|
)
|
||||||
throw NSFileProviderError(.notAuthenticated)
|
throw NSFileProviderError(.notAuthenticated)
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier, ncAccount: ncAccount, ncKit: ncKit)
|
return FileProviderEnumerator(
|
||||||
|
enumeratedItemIdentifier: containerItemIdentifier, ncAccount: ncAccount, ncKit: ncKit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func materializedItemsDidChange(completionHandler: @escaping () -> Void) {
|
func materializedItemsDidChange(completionHandler: @escaping () -> Void) {
|
||||||
guard let ncAccount = self.ncAccount else {
|
guard let ncAccount else {
|
||||||
Logger.fileProviderExtension.error("Not purging stale local file metadatas, account not set up")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Not purging stale local file metadatas, account not set up")
|
||||||
completionHandler()
|
completionHandler()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let fpManager = NSFileProviderManager(for: domain) else {
|
guard let fpManager = NSFileProviderManager(for: domain) else {
|
||||||
Logger.fileProviderExtension.error("Could not get file provider manager for domain: \(self.domain.displayName, privacy: .public)")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not get file provider manager for domain: \(self.domain.displayName, privacy: .public)"
|
||||||
|
)
|
||||||
completionHandler()
|
completionHandler()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let materialisedEnumerator = fpManager.enumeratorForMaterializedItems()
|
let materialisedEnumerator = fpManager.enumeratorForMaterializedItems()
|
||||||
let materialisedObserver = FileProviderMaterialisedEnumerationObserver(ncKitAccount: ncAccount.ncKitAccount) { _ in
|
let materialisedObserver = FileProviderMaterialisedEnumerationObserver(
|
||||||
|
ncKitAccount: ncAccount.ncKitAccount
|
||||||
|
) { _ in
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
let startingPage = NSFileProviderPage(NSFileProviderPage.initialPageSortedByName as Data)
|
let startingPage = NSFileProviderPage(NSFileProviderPage.initialPageSortedByName as Data)
|
||||||
|
@ -622,9 +824,11 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKComm
|
||||||
materialisedEnumerator.enumerateItems(for: materialisedObserver, startingAt: startingPage)
|
materialisedEnumerator.enumerateItems(for: materialisedObserver, startingAt: startingPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func signalEnumerator(completionHandler: @escaping(_ error: Error?) -> Void) {
|
func signalEnumerator(completionHandler: @escaping (_ error: Error?) -> Void) {
|
||||||
guard let fpManager = NSFileProviderManager(for: self.domain) else {
|
guard let fpManager = NSFileProviderManager(for: domain) else {
|
||||||
Logger.fileProviderExtension.error("Could not get file provider manager for domain, could not signal enumerator. This might lead to future conflicts.")
|
Logger.fileProviderExtension.error(
|
||||||
|
"Could not get file provider manager for domain, could not signal enumerator. This might lead to future conflicts."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
import UniformTypeIdentifiers
|
|
||||||
import NextcloudKit
|
import NextcloudKit
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
class FileProviderItem: NSObject, NSFileProviderItem {
|
class FileProviderItem: NSObject, NSFileProviderItem {
|
||||||
|
|
||||||
enum FileProviderItemTransferError: Error {
|
enum FileProviderItemTransferError: Error {
|
||||||
case downloadError
|
case downloadError
|
||||||
case uploadError
|
case uploadError
|
||||||
|
@ -26,71 +25,78 @@ class FileProviderItem: NSObject, NSFileProviderItem {
|
||||||
let metadata: NextcloudItemMetadataTable
|
let metadata: NextcloudItemMetadataTable
|
||||||
let parentItemIdentifier: NSFileProviderItemIdentifier
|
let parentItemIdentifier: NSFileProviderItemIdentifier
|
||||||
let ncKit: NextcloudKit
|
let ncKit: NextcloudKit
|
||||||
|
|
||||||
var itemIdentifier: NSFileProviderItemIdentifier {
|
var itemIdentifier: NSFileProviderItemIdentifier {
|
||||||
return NSFileProviderItemIdentifier(metadata.ocId)
|
NSFileProviderItemIdentifier(metadata.ocId)
|
||||||
}
|
}
|
||||||
|
|
||||||
var capabilities: NSFileProviderItemCapabilities {
|
var capabilities: NSFileProviderItemCapabilities {
|
||||||
guard !metadata.directory else {
|
guard !metadata.directory else {
|
||||||
return [ .allowsAddingSubItems,
|
return [
|
||||||
.allowsContentEnumerating,
|
.allowsAddingSubItems,
|
||||||
.allowsReading,
|
.allowsContentEnumerating,
|
||||||
.allowsDeleting,
|
.allowsReading,
|
||||||
.allowsRenaming ]
|
.allowsDeleting,
|
||||||
|
.allowsRenaming,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
guard !metadata.lock else {
|
guard !metadata.lock else {
|
||||||
return [ .allowsReading ]
|
return [.allowsReading]
|
||||||
}
|
}
|
||||||
return [ .allowsWriting,
|
return [
|
||||||
.allowsReading,
|
.allowsWriting,
|
||||||
.allowsDeleting,
|
.allowsReading,
|
||||||
.allowsRenaming,
|
.allowsDeleting,
|
||||||
.allowsReparenting ]
|
.allowsRenaming,
|
||||||
|
.allowsReparenting,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemVersion: NSFileProviderItemVersion {
|
var itemVersion: NSFileProviderItemVersion {
|
||||||
NSFileProviderItemVersion(contentVersion: metadata.etag.data(using: .utf8)!,
|
NSFileProviderItemVersion(
|
||||||
metadataVersion: metadata.etag.data(using: .utf8)!)
|
contentVersion: metadata.etag.data(using: .utf8)!,
|
||||||
|
metadataVersion: metadata.etag.data(using: .utf8)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename: String {
|
var filename: String {
|
||||||
return metadata.fileNameView
|
metadata.fileNameView
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentType: UTType {
|
var contentType: UTType {
|
||||||
if self.itemIdentifier == .rootContainer || metadata.directory {
|
if itemIdentifier == .rootContainer || metadata.directory {
|
||||||
return .folder
|
return .folder
|
||||||
}
|
}
|
||||||
|
|
||||||
let internalType = ncKit.nkCommonInstance.getInternalType(fileName: metadata.fileNameView,
|
let internalType = ncKit.nkCommonInstance.getInternalType(
|
||||||
mimeType: "",
|
fileName: metadata.fileNameView,
|
||||||
directory: metadata.directory)
|
mimeType: "",
|
||||||
|
directory: metadata.directory)
|
||||||
return UTType(filenameExtension: internalType.ext) ?? .content
|
return UTType(filenameExtension: internalType.ext) ?? .content
|
||||||
}
|
}
|
||||||
|
|
||||||
var documentSize: NSNumber? {
|
var documentSize: NSNumber? {
|
||||||
return NSNumber(value: metadata.size)
|
NSNumber(value: metadata.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
var creationDate: Date? {
|
var creationDate: Date? {
|
||||||
return metadata.creationDate as Date
|
metadata.creationDate as Date
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastUsedDate: Date? {
|
var lastUsedDate: Date? {
|
||||||
return metadata.date as Date
|
metadata.date as Date
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentModificationDate: Date? {
|
var contentModificationDate: Date? {
|
||||||
return metadata.date as Date
|
metadata.date as Date
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDownloaded: Bool {
|
var isDownloaded: Bool {
|
||||||
return metadata.directory || NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
|
metadata.directory
|
||||||
|
|| NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDownloading: Bool {
|
var isDownloading: Bool {
|
||||||
return metadata.status == NextcloudItemMetadataTable.Status.downloading.rawValue
|
metadata.status == NextcloudItemMetadataTable.Status.downloading.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadingError: Error? {
|
var downloadingError: Error? {
|
||||||
|
@ -101,30 +107,36 @@ class FileProviderItem: NSObject, NSFileProviderItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isUploaded: Bool {
|
var isUploaded: Bool {
|
||||||
return NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
|
NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var isUploading: Bool {
|
var isUploading: Bool {
|
||||||
return metadata.status == NextcloudItemMetadataTable.Status.uploading.rawValue
|
metadata.status == NextcloudItemMetadataTable.Status.uploading.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploadingError: Error? {
|
var uploadingError: Error? {
|
||||||
if metadata.status == NextcloudItemMetadataTable.Status.uploadError.rawValue {
|
if metadata.status == NextcloudItemMetadataTable.Status.uploadError.rawValue {
|
||||||
return FileProviderItemTransferError.uploadError
|
FileProviderItemTransferError.uploadError
|
||||||
} else {
|
} else {
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var childItemCount: NSNumber? {
|
var childItemCount: NSNumber? {
|
||||||
if metadata.directory {
|
if metadata.directory {
|
||||||
return NSNumber(integerLiteral: NextcloudFilesDatabaseManager.shared.childItemsForDirectory(metadata).count)
|
NSNumber(
|
||||||
|
integerLiteral: NextcloudFilesDatabaseManager.shared.childItemsForDirectory(
|
||||||
|
metadata
|
||||||
|
).count)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(metadata: NextcloudItemMetadataTable, parentItemIdentifier: NSFileProviderItemIdentifier, ncKit: NextcloudKit) {
|
required init(
|
||||||
|
metadata: NextcloudItemMetadataTable, parentItemIdentifier: NSFileProviderItemIdentifier,
|
||||||
|
ncKit: NextcloudKit
|
||||||
|
) {
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.parentItemIdentifier = parentItemIdentifier
|
self.parentItemIdentifier = parentItemIdentifier
|
||||||
self.ncKit = ncKit
|
self.ncKit = ncKit
|
||||||
|
|
|
@ -12,44 +12,53 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
class FileProviderMaterialisedEnumerationObserver : NSObject, NSFileProviderEnumerationObserver {
|
class FileProviderMaterialisedEnumerationObserver: NSObject, NSFileProviderEnumerationObserver {
|
||||||
let ncKitAccount: String
|
let ncKitAccount: String
|
||||||
let completionHandler: (_ deletedOcIds: Set<String>) -> Void
|
let completionHandler: (_ deletedOcIds: Set<String>) -> Void
|
||||||
var allEnumeratedItemIds: Set<String> = Set<String>()
|
var allEnumeratedItemIds: Set<String> = .init()
|
||||||
|
|
||||||
required init(ncKitAccount: String, completionHandler: @escaping(_ deletedOcIds: Set<String>) -> Void) {
|
required init(
|
||||||
|
ncKitAccount: String, completionHandler: @escaping (_ deletedOcIds: Set<String>) -> Void
|
||||||
|
) {
|
||||||
self.ncKitAccount = ncKitAccount
|
self.ncKitAccount = ncKitAccount
|
||||||
self.completionHandler = completionHandler
|
self.completionHandler = completionHandler
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func didEnumerate(_ updatedItems: [NSFileProviderItemProtocol]) {
|
func didEnumerate(_ updatedItems: [NSFileProviderItemProtocol]) {
|
||||||
let updatedItemsIds = Array(updatedItems.map { $0.itemIdentifier.rawValue })
|
let updatedItemsIds = Array(updatedItems.map(\.itemIdentifier.rawValue))
|
||||||
|
|
||||||
for updatedItemsId in updatedItemsIds {
|
for updatedItemsId in updatedItemsIds {
|
||||||
allEnumeratedItemIds.insert(updatedItemsId)
|
allEnumeratedItemIds.insert(updatedItemsId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishEnumerating(upTo nextPage: NSFileProviderPage?) {
|
func finishEnumerating(upTo _: NSFileProviderPage?) {
|
||||||
Logger.materialisedFileHandling.debug("Handling enumerated materialised items.")
|
Logger.materialisedFileHandling.debug("Handling enumerated materialised items.")
|
||||||
FileProviderMaterialisedEnumerationObserver.handleEnumeratedItems(self.allEnumeratedItemIds,
|
FileProviderMaterialisedEnumerationObserver.handleEnumeratedItems(
|
||||||
account: self.ncKitAccount,
|
allEnumeratedItemIds,
|
||||||
completionHandler: self.completionHandler)
|
account: ncKitAccount,
|
||||||
|
completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishEnumeratingWithError(_ error: Error) {
|
func finishEnumeratingWithError(_ error: Error) {
|
||||||
Logger.materialisedFileHandling.error("Ran into error when enumerating materialised items: \(error.localizedDescription, privacy: .public). Handling items enumerated so far")
|
Logger.materialisedFileHandling.error(
|
||||||
FileProviderMaterialisedEnumerationObserver.handleEnumeratedItems(self.allEnumeratedItemIds,
|
"Ran into error when enumerating materialised items: \(error.localizedDescription, privacy: .public). Handling items enumerated so far"
|
||||||
account: self.ncKitAccount,
|
)
|
||||||
completionHandler: self.completionHandler)
|
FileProviderMaterialisedEnumerationObserver.handleEnumeratedItems(
|
||||||
|
allEnumeratedItemIds,
|
||||||
|
account: ncKitAccount,
|
||||||
|
completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleEnumeratedItems(_ itemIds: Set<String>, account: String, completionHandler: @escaping(_ deletedOcIds: Set<String>) -> Void) {
|
static func handleEnumeratedItems(
|
||||||
|
_ itemIds: Set<String>, account: String,
|
||||||
|
completionHandler: @escaping (_ deletedOcIds: Set<String>) -> Void
|
||||||
|
) {
|
||||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||||
let databaseLocalFileMetadatas = dbManager.localFileMetadatas(account: account)
|
let databaseLocalFileMetadatas = dbManager.localFileMetadatas(account: account)
|
||||||
var noLongerMaterialisedIds = Set<String>()
|
var noLongerMaterialisedIds = Set<String>()
|
||||||
|
@ -60,12 +69,13 @@ class FileProviderMaterialisedEnumerationObserver : NSObject, NSFileProviderEnum
|
||||||
|
|
||||||
guard itemIds.contains(localFileOcId) else {
|
guard itemIds.contains(localFileOcId) else {
|
||||||
noLongerMaterialisedIds.insert(localFileOcId)
|
noLongerMaterialisedIds.insert(localFileOcId)
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Logger.materialisedFileHandling.info("Cleaning up local file metadatas for unmaterialised items")
|
Logger.materialisedFileHandling.info(
|
||||||
|
"Cleaning up local file metadatas for unmaterialised items")
|
||||||
for itemId in noLongerMaterialisedIds {
|
for itemId in noLongerMaterialisedIds {
|
||||||
dbManager.deleteLocalFileMetadata(ocId: itemId)
|
dbManager.deleteLocalFileMetadata(ocId: itemId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,25 +24,27 @@ class FileProviderSocketLineProcessor: NSObject, LineProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
func process(_ line: String) {
|
func process(_ line: String) {
|
||||||
if (line.contains("~")) { // We use this as the separator specifically in ACCOUNT_DETAILS
|
if line.contains("~") { // We use this as the separator specifically in ACCOUNT_DETAILS
|
||||||
Logger.desktopClientConnection.debug("Processing file provider line with potentially sensitive user data")
|
Logger.desktopClientConnection.debug(
|
||||||
|
"Processing file provider line with potentially sensitive user data")
|
||||||
} else {
|
} else {
|
||||||
Logger.desktopClientConnection.debug("Processing file provider line: \(line, privacy: .public)")
|
Logger.desktopClientConnection.debug(
|
||||||
|
"Processing file provider line: \(line, privacy: .public)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let splitLine = line.split(separator: ":", maxSplits: 1)
|
let splitLine = line.split(separator: ":", maxSplits: 1)
|
||||||
guard let commandSubsequence = splitLine.first else {
|
guard let commandSubsequence = splitLine.first else {
|
||||||
Logger.desktopClientConnection.error("Input line did not have a first element")
|
Logger.desktopClientConnection.error("Input line did not have a first element")
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
let command = String(commandSubsequence);
|
let command = String(commandSubsequence)
|
||||||
|
|
||||||
Logger.desktopClientConnection.debug("Received command: \(command, privacy: .public)")
|
Logger.desktopClientConnection.debug("Received command: \(command, privacy: .public)")
|
||||||
if (command == "SEND_FILE_PROVIDER_DOMAIN_IDENTIFIER") {
|
if command == "SEND_FILE_PROVIDER_DOMAIN_IDENTIFIER" {
|
||||||
delegate.sendFileProviderDomainIdentifier()
|
delegate.sendFileProviderDomainIdentifier()
|
||||||
} else if (command == "ACCOUNT_NOT_AUTHENTICATED") {
|
} else if command == "ACCOUNT_NOT_AUTHENTICATED" {
|
||||||
delegate.removeAccountConfig()
|
delegate.removeAccountConfig()
|
||||||
} else if (command == "ACCOUNT_DETAILS") {
|
} else if command == "ACCOUNT_DETAILS" {
|
||||||
guard let accountDetailsSubsequence = splitLine.last else { return }
|
guard let accountDetailsSubsequence = splitLine.last else { return }
|
||||||
let splitAccountDetails = accountDetailsSubsequence.split(separator: "~", maxSplits: 2)
|
let splitAccountDetails = accountDetailsSubsequence.split(separator: "~", maxSplits: 2)
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,22 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
func pathForAppGroupContainer() -> URL? {
|
func pathForAppGroupContainer() -> URL? {
|
||||||
guard let appGroupIdentifier = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String else {
|
guard
|
||||||
Logger.localFileOps.critical("Could not get container url as missing SocketApiPrefix info in app Info.plist")
|
let appGroupIdentifier = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix")
|
||||||
|
as? String
|
||||||
|
else {
|
||||||
|
Logger.localFileOps.critical(
|
||||||
|
"Could not get container url as missing SocketApiPrefix info in app Info.plist")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
|
return FileManager.default.containerURL(
|
||||||
|
forSecurityApplicationGroupIdentifier: appGroupIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathForFileProviderExtData() -> URL? {
|
func pathForFileProviderExtData() -> URL? {
|
||||||
|
@ -32,7 +37,9 @@ func pathForFileProviderExtData() -> URL? {
|
||||||
|
|
||||||
func pathForFileProviderTempFilesForDomain(_ domain: NSFileProviderDomain) throws -> URL? {
|
func pathForFileProviderTempFilesForDomain(_ domain: NSFileProviderDomain) throws -> URL? {
|
||||||
guard let fpManager = NSFileProviderManager(for: domain) else {
|
guard let fpManager = NSFileProviderManager(for: domain) else {
|
||||||
Logger.localFileOps.error("Unable to get file provider manager for domain: \(domain.displayName, privacy: .public)")
|
Logger.localFileOps.error(
|
||||||
|
"Unable to get file provider manager for domain: \(domain.displayName, privacy: .public)"
|
||||||
|
)
|
||||||
throw NSFileProviderError(.providerNotFound)
|
throw NSFileProviderError(.providerNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +47,13 @@ func pathForFileProviderTempFilesForDomain(_ domain: NSFileProviderDomain) throw
|
||||||
return fileProviderDataUrl.appendingPathComponent("TemporaryNextcloudFiles/")
|
return fileProviderDataUrl.appendingPathComponent("TemporaryNextcloudFiles/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func localPathForNCFile(ocId: String, fileNameView: String, domain: NSFileProviderDomain) throws -> URL {
|
func localPathForNCFile(ocId _: String, fileNameView: String, domain: NSFileProviderDomain) throws
|
||||||
|
-> URL
|
||||||
|
{
|
||||||
guard let fileProviderFilesPathUrl = try pathForFileProviderTempFilesForDomain(domain) else {
|
guard let fileProviderFilesPathUrl = try pathForFileProviderTempFilesForDomain(domain) else {
|
||||||
Logger.localFileOps.error("Unable to get path for file provider temp files for domain: \(domain.displayName, privacy: .public)")
|
Logger.localFileOps.error(
|
||||||
|
"Unable to get path for file provider temp files for domain: \(domain.displayName, privacy: .public)"
|
||||||
|
)
|
||||||
throw URLError(.badURL)
|
throw URLError(.badURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,20 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import FileProvider
|
import FileProvider
|
||||||
|
import Foundation
|
||||||
|
|
||||||
class NextcloudAccount: NSObject {
|
class NextcloudAccount: NSObject {
|
||||||
static let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
|
static let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
|
||||||
let username, password, ncKitAccount, serverUrl, davFilesUrl: String
|
let username, password, ncKitAccount, serverUrl, davFilesUrl: String
|
||||||
|
|
||||||
init(user: String, serverUrl: String, password: String) {
|
init(user: String, serverUrl: String, password: String) {
|
||||||
self.username = user
|
username = user
|
||||||
self.password = password
|
self.password = password
|
||||||
self.ncKitAccount = user + " " + serverUrl
|
ncKitAccount = user + " " + serverUrl
|
||||||
self.serverUrl = serverUrl
|
self.serverUrl = serverUrl
|
||||||
self.davFilesUrl = serverUrl + NextcloudAccount.webDavFilesUrlSuffix + user
|
davFilesUrl = serverUrl + NextcloudAccount.webDavFilesUrlSuffix + user
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue