+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import Foundation
-import OSLog
-
-extension NextcloudFilesDatabaseManager {
- func directoryMetadata(account: String, serverUrl: String) -> NextcloudItemMetadataTable? {
- // We want to split by "/" (e.g. cloud.nc.com/files/a/b) but we need to be mindful of "https://c.nc.com"
- let problematicSeparator = "://"
- let placeholderSeparator = "__TEMP_REPLACE__"
- let serverUrlWithoutPrefix = serverUrl.replacingOccurrences(
- of: problematicSeparator, with: placeholderSeparator)
- var splitServerUrl = serverUrlWithoutPrefix.split(separator: "/")
- let directoryItemFileName = String(splitServerUrl.removeLast())
- let directoryItemServerUrl = splitServerUrl.joined(separator: "/").replacingOccurrences(
- of: placeholderSeparator, with: problematicSeparator)
-
- if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl == %@ AND fileName == %@ AND directory == true",
- account,
- directoryItemServerUrl,
- directoryItemFileName
- ).first {
- return NextcloudItemMetadataTable(value: metadata)
- }
-
- return nil
- }
-
- func childItemsForDirectory(_ directoryMetadata: NextcloudItemMetadataTable)
- -> [NextcloudItemMetadataTable]
- {
- let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "serverUrl BEGINSWITH %@", directoryServerUrl)
- return sortedItemMetadatas(metadatas)
- }
-
- func childDirectoriesForDirectory(_ directoryMetadata: NextcloudItemMetadataTable)
- -> [NextcloudItemMetadataTable]
- {
- let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "serverUrl BEGINSWITH %@ AND directory == true", directoryServerUrl)
- return sortedItemMetadatas(metadatas)
- }
-
- func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable)
- -> NextcloudItemMetadataTable?
- {
- directoryMetadata(account: itemMetadata.account, serverUrl: itemMetadata.serverUrl)
- }
-
- func directoryMetadata(ocId: String) -> NextcloudItemMetadataTable? {
- if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@ AND directory == true", ocId
- ).first {
- return NextcloudItemMetadataTable(value: metadata)
- }
-
- return nil
- }
-
- func directoryMetadatas(account: String) -> [NextcloudItemMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND directory == true", account)
- return sortedItemMetadatas(metadatas)
- }
-
- func directoryMetadatas(account: String, parentDirectoryServerUrl: String)
- -> [NextcloudItemMetadataTable]
- {
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND parentDirectoryServerUrl == %@ AND directory == true", account,
- parentDirectoryServerUrl)
- return sortedItemMetadatas(metadatas)
- }
-
- // Deletes all metadatas related to the info of the directory provided
- func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) -> [NextcloudItemMetadataTable]? {
- let database = ncDatabase()
- guard
- let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@ AND directory == true", ocId
- ).first
- else {
- Logger.ncFilesDatabase.error(
- "Could not find directory metadata for ocId \(ocId, privacy: .public). Not proceeding with deletion"
- )
- return nil
- }
-
- let directoryMetadataCopy = NextcloudItemMetadataTable(value: directoryMetadata)
- let directoryUrlPath = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
- let directoryAccount = directoryMetadata.account
- 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)"
- )
-
- 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)"
- )
- return nil
- }
-
- var deletedMetadatas: [NextcloudItemMetadataTable] = [directoryMetadataCopy]
-
- let results = database.objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl BEGINSWITH %@", directoryAccount, directoryUrlPath)
-
- for result in results {
- let successfulItemMetadataDelete = deleteItemMetadata(ocId: result.ocId)
- if successfulItemMetadataDelete {
- deletedMetadatas.append(NextcloudItemMetadataTable(value: result))
- }
-
- if localFileMetadataFromOcId(result.ocId) != nil {
- deleteLocalFileMetadata(ocId: result.ocId)
- }
- }
-
- Logger.ncFilesDatabase.debug(
- "Completed deletions in directory recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryEtag, privacy: .public), serverUrl: \(directoryUrlPath, privacy: .public)"
- )
-
- return deletedMetadatas
- }
-
- func renameDirectoryAndPropagateToChildren(
- ocId: String, newServerUrl: String, newFileName: String
- ) -> [NextcloudItemMetadataTable]? {
- let database = ncDatabase()
-
- guard
- let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@ AND directory == true", ocId
- ).first
- else {
- Logger.ncFilesDatabase.error(
- "Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming"
- )
- return nil
- }
-
- let oldItemServerUrl = directoryMetadata.serverUrl
- let oldDirectoryServerUrl = oldItemServerUrl + "/" + directoryMetadata.fileName
- let newDirectoryServerUrl = newServerUrl + "/" + newFileName
- let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account,
- oldDirectoryServerUrl)
-
- renameItemMetadata(ocId: ocId, newServerUrl: newServerUrl, newFileName: newFileName)
- Logger.ncFilesDatabase.debug("Renamed root renaming directory")
-
- do {
- try database.write {
- for childItem in childItemResults {
- let oldServerUrl = childItem.serverUrl
- let movedServerUrl = oldServerUrl.replacingOccurrences(
- of: oldDirectoryServerUrl, with: newDirectoryServerUrl)
- childItem.serverUrl = movedServerUrl
- database.add(childItem, update: .all)
- Logger.ncFilesDatabase.debug(
- "Moved childItem at \(oldServerUrl) to \(movedServerUrl)")
- }
- }
- } catch {
- 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
- }
-
- let updatedChildItemResults = database.objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account,
- newDirectoryServerUrl)
- return sortedItemMetadatas(updatedChildItemResults)
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import Foundation
-import OSLog
-import RealmSwift
-
-extension NextcloudFilesDatabaseManager {
- func localFileMetadataFromOcId(_ ocId: String) -> NextcloudLocalFileMetadataTable? {
- if let metadata = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter(
- "ocId == %@", ocId
- ).first {
- return NextcloudLocalFileMetadataTable(value: metadata)
- }
-
- return nil
- }
-
- func addLocalFileMetadataFromItemMetadata(_ itemMetadata: NextcloudItemMetadataTable) {
- let database = ncDatabase()
-
- do {
- try database.write {
- let newLocalFileMetadata = NextcloudLocalFileMetadataTable()
-
- newLocalFileMetadata.ocId = itemMetadata.ocId
- newLocalFileMetadata.fileName = itemMetadata.fileName
- newLocalFileMetadata.account = itemMetadata.account
- newLocalFileMetadata.etag = itemMetadata.etag
- newLocalFileMetadata.exifDate = Date()
- newLocalFileMetadata.exifLatitude = "-1"
- newLocalFileMetadata.exifLongitude = "-1"
-
- 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)"
- )
- }
- } 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)"
- )
- }
- }
-
- func deleteLocalFileMetadata(ocId: String) {
- let database = ncDatabase()
-
- do {
- try database.write {
- let results = database.objects(NextcloudLocalFileMetadataTable.self).filter(
- "ocId == %@", ocId)
- database.delete(results)
- }
- } catch {
- 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]
- {
- let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
- return Array(sortedMetadatas.map { NextcloudLocalFileMetadataTable(value: $0) })
- }
-
- func localFileMetadatas(account: String) -> [NextcloudLocalFileMetadataTable] {
- let results = ncDatabase().objects(NextcloudLocalFileMetadataTable.self).filter(
- "account == %@", account)
- return sortedLocalFileMetadatas(results)
- }
-
- func localFileItemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
- let localFileMetadatas = localFileMetadatas(account: account)
- let localFileMetadatasOcIds = Array(localFileMetadatas.map(\.ocId))
-
- var itemMetadatas: [NextcloudItemMetadataTable] = []
-
- for ocId in localFileMetadatasOcIds {
- 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)"
- )
- continue
- }
-
- itemMetadatas.append(NextcloudItemMetadataTable(value: itemMetadata))
- }
-
- return itemMetadatas
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import FileProvider
-import Foundation
-import NextcloudKit
-import OSLog
-import RealmSwift
-
-class NextcloudFilesDatabaseManager {
- static let shared = NextcloudFilesDatabaseManager()!
-
- private static let relativeDatabaseFolderPath = "Database/"
- private static let databaseFilename = "fileproviderextdatabase.realm"
- private static let schemaVersion: UInt64 = 100
-
- init(realmConfig: Realm.Configuration = Realm.Configuration.defaultConfiguration) {
- Realm.Configuration.defaultConfiguration = realmConfig
-
- do {
- _ = try Realm()
- Logger.ncFilesDatabase.info("Successfully started Realm db for FileProviderExt")
- } catch let error {
- Logger.ncFilesDatabase.error("Error opening Realm db: \(error, privacy: .public)")
- }
- }
-
- convenience init?() {
- let relativeDatabaseFilePath = Self.relativeDatabaseFolderPath + Self.databaseFilename
- guard let fileProviderDataDirUrl = pathForFileProviderExtData() else { return nil }
- let databasePath = fileProviderDataDirUrl.appendingPathComponent(relativeDatabaseFilePath)
-
- // Disable file protection for directory DB
- // https://docs.mongodb.com/realm/sdk/ios/examples/configure-and-open-a-realm/
- let dbFolder = fileProviderDataDirUrl.appendingPathComponent(Self.relativeDatabaseFolderPath)
- let dbFolderPath = dbFolder.path
- do {
- try FileManager.default.createDirectory(at: dbFolder, withIntermediateDirectories: true)
- try FileManager.default.setAttributes(
- [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
- ofItemAtPath: dbFolderPath
- )
- } catch {
- Logger.ncFilesDatabase.error(
- "Could not set permission level for db folder: \(error, privacy: .public)"
- )
- }
-
- let config = Realm.Configuration(
- fileURL: databasePath,
- schemaVersion: Self.schemaVersion,
- objectTypes: [NextcloudItemMetadataTable.self, NextcloudLocalFileMetadataTable.self]
- )
- self.init(realmConfig: config)
- }
-
- func ncDatabase() -> Realm {
- let realm = try! Realm()
- realm.refresh()
- return realm
- }
-
- func anyItemMetadatasForAccount(_ account: String) -> Bool {
- !ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@", account)
- .isEmpty
- }
-
- func itemMetadataFromOcId(_ ocId: String) -> NextcloudItemMetadataTable? {
- // 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
- if let itemMetadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@", ocId
- ).first {
- return NextcloudItemMetadataTable(value: itemMetadata)
- }
-
- return nil
- }
-
- func sortedItemMetadatas(_ metadatas: Results<NextcloudItemMetadataTable>)
- -> [NextcloudItemMetadataTable]
- {
- let sortedMetadatas = metadatas.sorted(byKeyPath: "fileName", ascending: true)
- return Array(sortedMetadatas.map { NextcloudItemMetadataTable(value: $0) })
- }
-
- func itemMetadatas(account: String) -> [NextcloudItemMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "account == %@", account)
- return sortedItemMetadatas(metadatas)
- }
-
- func itemMetadatas(account: String, serverUrl: String) -> [NextcloudItemMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl == %@", account, serverUrl)
- return sortedItemMetadatas(metadatas)
- }
-
- func itemMetadatas(
- 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)
- }
-
- func itemMetadataFromFileProviderItemIdentifier(_ identifier: NSFileProviderItemIdentifier)
- -> NextcloudItemMetadataTable?
- {
- let ocId = identifier.rawValue
- return itemMetadataFromOcId(ocId)
- }
-
- private func processItemMetadatasToDelete(
- existingMetadatas: Results<NextcloudItemMetadataTable>,
- updatedMetadatas: [NextcloudItemMetadataTable]
- ) -> [NextcloudItemMetadataTable] {
- var deletedMetadatas: [NextcloudItemMetadataTable] = []
-
- for existingMetadata in existingMetadatas {
- guard !updatedMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
- let metadataToDelete = itemMetadataFromOcId(existingMetadata.ocId)
- else { continue }
-
- 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)"
- )
- }
-
- return deletedMetadatas
- }
-
- private func processItemMetadatasToUpdate(
- existingMetadatas: Results<NextcloudItemMetadataTable>,
- updatedMetadatas: [NextcloudItemMetadataTable],
- updateDirectoryEtags: Bool
- ) -> (
- newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable],
- directoriesNeedingRename: [NextcloudItemMetadataTable]
- ) {
- var returningNewMetadatas: [NextcloudItemMetadataTable] = []
- var returningUpdatedMetadatas: [NextcloudItemMetadataTable] = []
- var directoriesNeedingRename: [NextcloudItemMetadataTable] = []
-
- for updatedMetadata in updatedMetadatas {
- if let existingMetadata = existingMetadatas.first(where: {
- $0.ocId == updatedMetadata.ocId
- }) {
- if existingMetadata.status == NextcloudItemMetadataTable.Status.normal.rawValue,
- !existingMetadata.isInSameDatabaseStoreableRemoteState(updatedMetadata)
- {
- if updatedMetadata.directory {
- if updatedMetadata.serverUrl != existingMetadata.serverUrl
- || updatedMetadata.fileName != existingMetadata.fileName
- {
- directoriesNeedingRename.append(
- NextcloudItemMetadataTable(value: updatedMetadata))
- updatedMetadata.etag = "" // Renaming doesn't change the etag so reset manually
-
- } else if !updateDirectoryEtags {
- updatedMetadata.etag = existingMetadata.etag
- }
- }
-
- returningUpdatedMetadatas.append(updatedMetadata)
-
- Logger.ncFilesDatabase.debug(
- "Updated existing item metadata. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), fileName: \(updatedMetadata.fileName, privacy: .public)"
- )
- } 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)"
- )
- }
-
- } else { // This is a new metadata
- if !updateDirectoryEtags, updatedMetadata.directory {
- updatedMetadata.etag = ""
- }
-
- 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)"
- )
- }
- }
-
- return (returningNewMetadatas, returningUpdatedMetadatas, directoriesNeedingRename)
- }
-
- func updateItemMetadatas(
- account: String,
- serverUrl: String,
- updatedMetadatas: [NextcloudItemMetadataTable],
- updateDirectoryEtags: Bool
- ) -> (
- newMetadatas: [NextcloudItemMetadataTable]?,
- updatedMetadatas: [NextcloudItemMetadataTable]?,
- deletedMetadatas: [NextcloudItemMetadataTable]?
- ) {
- let database = ncDatabase()
-
- do {
- let existingMetadatas = database.objects(NextcloudItemMetadataTable.self).filter(
- "account == %@ AND serverUrl == %@ AND status == %@",
- account,
- serverUrl,
- NextcloudItemMetadataTable.Status.normal.rawValue)
-
- let metadatasToDelete = processItemMetadatasToDelete(
- existingMetadatas: existingMetadatas,
- updatedMetadatas: updatedMetadatas)
-
- let metadatasToChange = processItemMetadatasToUpdate(
- existingMetadatas: existingMetadatas,
- updatedMetadatas: updatedMetadatas,
- updateDirectoryEtags: updateDirectoryEtags)
-
- var metadatasToUpdate = metadatasToChange.updatedMetadatas
- let metadatasToCreate = metadatasToChange.newMetadatas
- let directoriesNeedingRename = metadatasToChange.directoriesNeedingRename
-
- let metadatasToAdd =
- Array(metadatasToUpdate.map { NextcloudItemMetadataTable(value: $0) })
- + Array(metadatasToCreate.map { NextcloudItemMetadataTable(value: $0) })
-
- for metadata in directoriesNeedingRename {
- if let updatedDirectoryChildren = renameDirectoryAndPropagateToChildren(
- ocId: metadata.ocId,
- newServerUrl: metadata.serverUrl,
- newFileName: metadata.fileName)
- {
- metadatasToUpdate += updatedDirectoryChildren
- }
- }
-
- try database.write {
- for metadata in metadatasToDelete {
- // Can't pass copies, we need the originals from the database
- database.delete(
- ncDatabase().objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@", metadata.ocId))
- }
-
- for metadata in metadatasToAdd {
- database.add(metadata, update: .all)
- }
- }
-
- return (
- newMetadatas: metadatasToCreate,
- updatedMetadatas: metadatasToUpdate,
- deletedMetadatas: metadatasToDelete
- )
- } catch {
- Logger.ncFilesDatabase.error(
- "Could not update any item metadatas, received error: \(error.localizedDescription, privacy: .public)"
- )
- return (nil, nil, nil)
- }
- }
-
- func setStatusForItemMetadata(
- _ metadata: NextcloudItemMetadataTable,
- status: NextcloudItemMetadataTable.Status,
- completionHandler: @escaping (_ updatedMetadata: NextcloudItemMetadataTable?) -> Void
- ) {
- let database = ncDatabase()
-
- do {
- try database.write {
- guard
- 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
- }
-
- result.status = status.rawValue
- 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)"
- )
-
- completionHandler(NextcloudItemMetadataTable(value: result))
- }
- } 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)"
- )
- completionHandler(nil)
- }
- }
-
- func addItemMetadata(_ metadata: NextcloudItemMetadataTable) {
- let database = ncDatabase()
-
- do {
- try database.write {
- 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)"
- )
- }
- } 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)"
- )
- }
- }
-
- @discardableResult func deleteItemMetadata(ocId: String) -> Bool {
- let database = ncDatabase()
-
- do {
- try database.write {
- let results = database.objects(NextcloudItemMetadataTable.self).filter(
- "ocId == %@", ocId)
-
- Logger.ncFilesDatabase.debug("Deleting item metadata. \(ocId, privacy: .public)")
- database.delete(results)
- }
-
- return true
- } catch {
- Logger.ncFilesDatabase.error(
- "Could not delete item metadata with ocId: \(ocId, privacy: .public), received error: \(error.localizedDescription, privacy: .public)"
- )
- return false
- }
- }
-
- func renameItemMetadata(ocId: String, newServerUrl: String, newFileName: String) {
- let database = ncDatabase()
-
- do {
- try database.write {
- guard
- 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
- }
-
- let oldFileName = itemMetadata.fileName
- let oldServerUrl = itemMetadata.serverUrl
-
- itemMetadata.fileName = newFileName
- itemMetadata.fileNameView = newFileName
- itemMetadata.serverUrl = newServerUrl
-
- 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)"
- )
- }
- } 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)"
- )
- }
- }
-
- func parentItemIdentifierFromMetadata(_ metadata: NextcloudItemMetadataTable)
- -> NSFileProviderItemIdentifier?
- {
- let homeServerFilesUrl = metadata.urlBase + "/remote.php/dav/files/" + metadata.userId
-
- if metadata.serverUrl == homeServerFilesUrl {
- return .rootContainer
- }
-
- 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)"
- )
- return nil
- }
-
- if let parentDirectoryMetadata = itemMetadataFromOcId(itemParentDirectory.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)"
- )
- return nil
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import Foundation
-import NextcloudKit
-
-extension NextcloudItemMetadataTable {
- static func fromNKFile(_ file: NKFile, account: String) -> NextcloudItemMetadataTable {
- let metadata = NextcloudItemMetadataTable()
-
- metadata.account = account
- metadata.checksums = file.checksums
- metadata.commentsUnread = file.commentsUnread
- metadata.contentType = file.contentType
- if let date = file.creationDate {
- metadata.creationDate = date as Date
- } else {
- metadata.creationDate = file.date as Date
- }
- metadata.dataFingerprint = file.dataFingerprint
- metadata.date = file.date as Date
- metadata.directory = file.directory
- metadata.downloadURL = file.downloadURL
- metadata.e2eEncrypted = file.e2eEncrypted
- metadata.etag = file.etag
- metadata.favorite = file.favorite
- metadata.fileId = file.fileId
- metadata.fileName = file.fileName
- metadata.fileNameView = file.fileName
- metadata.hasPreview = file.hasPreview
- metadata.iconName = file.iconName
- metadata.mountType = file.mountType
- metadata.name = file.name
- metadata.note = file.note
- metadata.ocId = file.ocId
- metadata.ownerId = file.ownerId
- metadata.ownerDisplayName = file.ownerDisplayName
- metadata.lock = file.lock
- metadata.lockOwner = file.lockOwner
- metadata.lockOwnerEditor = file.lockOwnerEditor
- metadata.lockOwnerType = file.lockOwnerType
- metadata.lockOwnerDisplayName = file.lockOwnerDisplayName
- metadata.lockTime = file.lockTime
- metadata.lockTimeOut = file.lockTimeOut
- metadata.path = file.path
- metadata.permissions = file.permissions
- metadata.quotaUsedBytes = file.quotaUsedBytes
- metadata.quotaAvailableBytes = file.quotaAvailableBytes
- metadata.richWorkspace = file.richWorkspace
- metadata.resourceType = file.resourceType
- metadata.serverUrl = file.serverUrl
- metadata.sharePermissionsCollaborationServices = file.sharePermissionsCollaborationServices
- for element in file.sharePermissionsCloudMesh {
- metadata.sharePermissionsCloudMesh.append(element)
- }
- for element in file.shareType {
- metadata.shareType.append(element)
- }
- metadata.size = file.size
- metadata.classFile = file.classFile
- // 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
- {
- metadata.classFile = NKCommon.TypeClassFile.document.rawValue
- }
- if let date = file.uploadDate {
- metadata.uploadDate = date as Date
- } else {
- metadata.uploadDate = file.date as Date
- }
- metadata.urlBase = file.urlBase
- metadata.user = file.user
- metadata.userId = file.userId
-
- // Support for finding the correct filename for e2ee files should go here
-
- return metadata
- }
-
- static func metadatasFromDirectoryReadNKFiles(
- _ files: [NKFile],
- account: String,
- completionHandler: @escaping (
- _ directoryMetadata: NextcloudItemMetadataTable,
- _ childDirectoriesMetadatas: [NextcloudItemMetadataTable],
- _ metadatas: [NextcloudItemMetadataTable]
- ) -> Void
- ) {
- var directoryMetadataSet = false
- var directoryMetadata = NextcloudItemMetadataTable()
- var childDirectoriesMetadatas: [NextcloudItemMetadataTable] = []
- var metadatas: [NextcloudItemMetadataTable] = []
-
- let conversionQueue = DispatchQueue(
- label: "nkFileToMetadataConversionQueue",
- qos: .userInitiated,
- attributes: .concurrent)
- // appendQueue is a serial queue, not concurrent
- let appendQueue = DispatchQueue(label: "metadataAppendQueue", qos: .userInitiated)
- let dispatchGroup = DispatchGroup()
-
- for file in files {
- if metadatas.isEmpty, !directoryMetadataSet {
- let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
- directoryMetadata = metadata
- directoryMetadataSet = true
- } else {
- conversionQueue.async(group: dispatchGroup) {
- let metadata = NextcloudItemMetadataTable.fromNKFile(file, account: account)
-
- appendQueue.async(group: dispatchGroup) {
- metadatas.append(metadata)
- if metadata.directory {
- childDirectoriesMetadatas.append(metadata)
- }
- }
- }
- }
- }
-
- dispatchGroup.notify(queue: DispatchQueue.main) {
- completionHandler(directoryMetadata, childDirectoriesMetadatas, metadatas)
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import FileProvider
-import Foundation
-import NextcloudKit
-import RealmSwift
-
-class NextcloudItemMetadataTable: Object {
- enum Status: Int {
- case downloadError = -4
- case downloading = -3
- case inDownload = -2
- case waitDownload = -1
-
- case normal = 0
-
- case waitUpload = 1
- case inUpload = 2
- case uploading = 3
- case uploadError = 4
- }
-
- enum SharePermissions: Int {
- case readShare = 1
- case updateShare = 2
- case createShare = 4
- case deleteShare = 8
- case shareShare = 16
-
- case maxFileShare = 19
- case maxFolderShare = 31
- }
-
- @Persisted(primaryKey: true) var ocId: String
- @Persisted var account = ""
- @Persisted var assetLocalIdentifier = ""
- @Persisted var checksums = ""
- @Persisted var chunk: Bool = false
- @Persisted var classFile = ""
- @Persisted var commentsUnread: Bool = false
- @Persisted var contentType = ""
- @Persisted var creationDate = Date()
- @Persisted var dataFingerprint = ""
- @Persisted var date = Date()
- @Persisted var directory: Bool = false
- @Persisted var deleteAssetLocalIdentifier: Bool = false
- @Persisted var downloadURL = ""
- @Persisted var e2eEncrypted: Bool = false
- @Persisted var edited: Bool = false
- @Persisted var etag = ""
- @Persisted var etagResource = ""
- @Persisted var favorite: Bool = false
- @Persisted var fileId = ""
- @Persisted var fileName = ""
- @Persisted var fileNameView = ""
- @Persisted var hasPreview: Bool = false
- @Persisted var iconName = ""
- @Persisted var iconUrl = ""
- @Persisted var isExtractFile: Bool = false
- @Persisted var livePhoto: Bool = false
- @Persisted var mountType = ""
- @Persisted var name = "" // for unifiedSearch is the provider.id
- @Persisted var note = ""
- @Persisted var ownerId = ""
- @Persisted var ownerDisplayName = ""
- @Persisted var lock = false
- @Persisted var lockOwner = ""
- @Persisted var lockOwnerEditor = ""
- @Persisted var lockOwnerType = 0
- @Persisted var lockOwnerDisplayName = ""
- @Persisted var lockTime: Date?
- @Persisted var lockTimeOut: Date?
- @Persisted var path = ""
- @Persisted var permissions = ""
- @Persisted var quotaUsedBytes: Int64 = 0
- @Persisted var quotaAvailableBytes: Int64 = 0
- @Persisted var resourceType = ""
- @Persisted var richWorkspace: String?
- @Persisted var serverUrl = "" // For parent directory!!
- @Persisted var session = ""
- @Persisted var sessionError = ""
- @Persisted var sessionSelector = ""
- @Persisted var sessionTaskIdentifier: Int = 0
- @Persisted var sharePermissionsCollaborationServices: Int = 0
- // TODO: Find a way to compare these two below in remote state check
- let sharePermissionsCloudMesh = List<String>()
- let shareType = List<Int>()
- @Persisted var size: Int64 = 0
- @Persisted var status: Int = 0
- @Persisted var subline: String?
- @Persisted var trashbinFileName = ""
- @Persisted var trashbinOriginalLocation = ""
- @Persisted var trashbinDeletionTime = Date()
- @Persisted var uploadDate = Date()
- @Persisted var url = ""
- @Persisted var urlBase = ""
- @Persisted var user = ""
- @Persisted var userId = ""
-
- var fileExtension: String {
- (fileNameView as NSString).pathExtension
- }
-
- var fileNoExtension: String {
- (fileNameView as NSString).deletingPathExtension
- }
-
- var isRenameable: Bool {
- lock
- }
-
- var isPrintable: Bool {
- if isDocumentViewableOnly {
- return false
- }
- if ["application/pdf", "com.adobe.pdf"].contains(contentType)
- || contentType.hasPrefix("text/")
- || classFile == NKCommon.TypeClassFile.image.rawValue
- {
- return true
- }
- return false
- }
-
- var isDocumentViewableOnly: Bool {
- sharePermissionsCollaborationServices == SharePermissions.readShare.rawValue
- && classFile == NKCommon.TypeClassFile.document.rawValue
- }
-
- var isCopyableInPasteboard: Bool {
- !isDocumentViewableOnly && !directory
- }
-
- var isModifiableWithQuickLook: Bool {
- if directory || isDocumentViewableOnly {
- return false
- }
- return contentType == "com.adobe.pdf" || contentType == "application/pdf"
- || classFile == NKCommon.TypeClassFile.image.rawValue
- }
-
- var isSettableOnOffline: Bool {
- session.isEmpty && !isDocumentViewableOnly
- }
-
- var canOpenIn: Bool {
- session.isEmpty && !isDocumentViewableOnly && !directory
- }
-
- var isDownloadUpload: Bool {
- status == Status.inDownload.rawValue || status == Status.downloading.rawValue
- || status == Status.inUpload.rawValue || status == Status.uploading.rawValue
- }
-
- var isDownload: Bool {
- status == Status.inDownload.rawValue || status == Status.downloading.rawValue
- }
-
- var isUpload: Bool {
- status == Status.inUpload.rawValue || status == Status.uploading.rawValue
- }
-
- override func isEqual(_ object: Any?) -> Bool {
- if let object = object as? NextcloudItemMetadataTable {
- return fileId == object.fileId && account == object.account && path == object.path
- && fileName == object.fileName
- }
-
- return false
- }
-
- func isInSameDatabaseStoreableRemoteState(_ comparingMetadata: NextcloudItemMetadataTable)
- -> Bool
- {
- comparingMetadata.etag == etag
- && comparingMetadata.fileNameView == fileNameView
- && comparingMetadata.date == date
- && comparingMetadata.permissions == permissions
- && comparingMetadata.hasPreview == hasPreview
- && comparingMetadata.note == note
- && comparingMetadata.lock == lock
- && comparingMetadata.sharePermissionsCollaborationServices
- == sharePermissionsCollaborationServices
- && comparingMetadata.favorite == favorite
- }
-
- /// 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 {
- !lock || (lockOwner == user && lockOwnerType == 0)
- }
-
- func thumbnailUrl(size: CGSize) -> URL? {
- guard hasPreview else {
- return nil
- }
-
- let urlBase = urlBase.urlEncoded!
- // Leave the leading slash in webdavUrl
- let webdavUrl = urlBase + NextcloudAccount.webDavFilesUrlSuffix + user
- 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"
- return URL(string: urlString)
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import Foundation
-import RealmSwift
-
-class NextcloudLocalFileMetadataTable: Object {
- @Persisted(primaryKey: true) var ocId: String
- @Persisted var account = ""
- @Persisted var etag = ""
- @Persisted var exifDate: Date?
- @Persisted var exifLatitude = ""
- @Persisted var exifLongitude = ""
- @Persisted var exifLensModel: String?
- @Persisted var favorite: Bool = false
- @Persisted var fileName = ""
- @Persisted var offline: Bool = false
-}
static let fileProviderExtension = Logger(
subsystem: subsystem, category: "fileproviderextension")
static let fileTransfer = Logger(subsystem: subsystem, category: "filetransfer")
- static let localFileOps = Logger(subsystem: subsystem, category: "localfileoperations")
- static let ncFilesDatabase = Logger(subsystem: subsystem, category: "nextcloudfilesdatabase")
static let shares = Logger(subsystem: subsystem, category: "shares")
- static let ncAccount = Logger(subsystem: subsystem, category: "ncAccount")
static let materialisedFileHandling = Logger(
subsystem: subsystem, category: "materialisedfilehandling"
)
import FileProvider
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
extension FileProviderEnumerator {
func fullRecursiveScan(
- ncAccount: NextcloudAccount,
+ ncAccount: Account,
ncKit: NextcloudKit,
scanChangesOnly: Bool,
completionHandler: @escaping (
- _ metadatas: [NextcloudItemMetadataTable],
- _ newMetadatas: [NextcloudItemMetadataTable],
- _ updatedMetadatas: [NextcloudItemMetadataTable],
- _ deletedMetadatas: [NextcloudItemMetadataTable],
+ _ metadatas: [ItemMetadata],
+ _ newMetadatas: [ItemMetadata],
+ _ updatedMetadatas: [ItemMetadata],
+ _ deletedMetadatas: [ItemMetadata],
_ error: NKError?
) -> Void
) {
- let rootContainerDirectoryMetadata = NextcloudItemMetadataTable()
+ let rootContainerDirectoryMetadata = ItemMetadata()
rootContainerDirectoryMetadata.directory = true
rootContainerDirectoryMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
}
private func scanRecursively(
- _ directoryMetadata: NextcloudItemMetadataTable,
- ncAccount: NextcloudAccount,
+ _ directoryMetadata: ItemMetadata,
+ ncAccount: Account,
ncKit: NextcloudKit,
scanChangesOnly: Bool
) -> (
- metadatas: [NextcloudItemMetadataTable],
- newMetadatas: [NextcloudItemMetadataTable],
- updatedMetadatas: [NextcloudItemMetadataTable],
- deletedMetadatas: [NextcloudItemMetadataTable],
+ metadatas: [ItemMetadata],
+ newMetadatas: [ItemMetadata],
+ updatedMetadatas: [ItemMetadata],
+ deletedMetadatas: [ItemMetadata],
error: NKError?
) {
if isInvalidated {
assert(directoryMetadata.directory, "Can only recursively scan a directory.")
// Will include results of recursive calls
- var allMetadatas: [NextcloudItemMetadataTable] = []
- var allNewMetadatas: [NextcloudItemMetadataTable] = []
- var allUpdatedMetadatas: [NextcloudItemMetadataTable] = []
- var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
+ var allMetadatas: [ItemMetadata] = []
+ var allNewMetadatas: [ItemMetadata] = []
+ var allUpdatedMetadatas: [ItemMetadata] = []
+ var allDeletedMetadatas: [ItemMetadata] = []
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let dispatchGroup = DispatchGroup() // TODO: Maybe own thread?
dispatchGroup.enter()
return ([], [], [], [], error: criticalError)
}
- var childDirectoriesToScan: [NextcloudItemMetadataTable] = []
- var candidateMetadatas: [NextcloudItemMetadataTable]
+ var childDirectoriesToScan: [ItemMetadata] = []
+ var candidateMetadatas: [ItemMetadata]
if scanChangesOnly, fastEnumeration {
candidateMetadatas = allUpdatedMetadatas
static func handleDepth1ReadFileOrFolder(
serverUrl: String,
- ncAccount: NextcloudAccount,
+ ncAccount: Account,
files: [NKFile],
error: NKError,
completionHandler: @escaping (
- _ metadatas: [NextcloudItemMetadataTable]?,
- _ newMetadatas: [NextcloudItemMetadataTable]?,
- _ updatedMetadatas: [NextcloudItemMetadataTable]?,
- _ deletedMetadatas: [NextcloudItemMetadataTable]?,
+ _ metadatas: [ItemMetadata]?,
+ _ newMetadatas: [ItemMetadata]?,
+ _ updatedMetadatas: [ItemMetadata]?,
+ _ deletedMetadatas: [ItemMetadata]?,
_ readError: Error?
) -> Void
) {
"Starting async conversion of NKFiles for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
)
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
DispatchQueue.global(qos: .userInitiated).async {
- NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(
+ ItemMetadata.metadatasFromDirectoryReadNKFiles(
files, account: ncAccount.ncKitAccount
) { directoryMetadata, _, metadatas in
static func readServerUrl(
_ serverUrl: String,
- ncAccount: NextcloudAccount,
+ ncAccount: Account,
ncKit: NextcloudKit,
stopAtMatchingEtags: Bool = false,
depth: String = "1",
completionHandler: @escaping (
- _ metadatas: [NextcloudItemMetadataTable]?,
- _ newMetadatas: [NextcloudItemMetadataTable]?,
- _ updatedMetadatas: [NextcloudItemMetadataTable]?,
- _ deletedMetadatas: [NextcloudItemMetadataTable]?,
+ _ metadatas: [ItemMetadata]?,
+ _ newMetadatas: [ItemMetadata]?,
+ _ updatedMetadatas: [ItemMetadata]?,
+ _ deletedMetadatas: [ItemMetadata]?,
_ readError: Error?
) -> Void
) {
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let ncKitAccount = ncAccount.ncKitAccount
Logger.enumeration.debug(
Logger.enumeration.debug(
"Read item is a file. Converting NKfile for serverUrl: \(serverUrl, privacy: .public) for user: \(ncAccount.ncKitAccount, privacy: .public)"
)
- let itemMetadata = NextcloudItemMetadataTable.fromNKFile(
+ let itemMetadata = ItemMetadata.fromNKFile(
receivedFile, account: ncKitAccount)
dbManager.addItemMetadata(itemMetadata) // TODO: Return some value when it is an update
completionHandler([itemMetadata], nil, nil, nil, error.error)
if depth == "0" {
if serverUrl != ncAccount.davFilesUrl {
- let metadata = NextcloudItemMetadataTable.fromNKFile(
+ let metadata = ItemMetadata.fromNKFile(
receivedFile, account: ncKitAccount)
let isNew = dbManager.itemMetadataFromOcId(metadata.ocId) == nil
let updatedMetadatas = isNew ? [] : [metadata]
import FileProvider
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
private let enumeratedItemIdentifier: NSFileProviderItemIdentifier
- private var enumeratedItemMetadata: NextcloudItemMetadataTable?
+ private var enumeratedItemMetadata: ItemMetadata?
private var enumeratingSystemIdentifier: Bool {
FileProviderEnumerator.isSystemIdentifier(enumeratedItemIdentifier)
}
// TODO: actually use this in NCKit and server requests
private let anchor = NSFileProviderSyncAnchor(Date().description.data(using: .utf8)!)
private static let maxItemsPerFileProviderPage = 100
- let ncAccount: NextcloudAccount
+ let ncAccount: Account
let ncKit: NextcloudKit
let fastEnumeration: Bool
var serverUrl: String = ""
init(
enumeratedItemIdentifier: NSFileProviderItemIdentifier,
- ncAccount: NextcloudAccount,
+ ncAccount: Account,
ncKit: NextcloudKit,
fastEnumeration: Bool = true
) {
Logger.enumeration.debug(
"Providing enumerator for item with identifier: \(enumeratedItemIdentifier.rawValue, privacy: .public)"
)
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
enumeratedItemMetadata = dbManager.itemMetadataFromFileProviderItemIdentifier(
enumeratedItemIdentifier)
) { _, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
// If we get a 404 we might add more deleted metadatas
- var currentDeletedMetadatas: [NextcloudItemMetadataTable] = []
+ var currentDeletedMetadatas: [ItemMetadata] = []
if let notNilDeletedMetadatas = deletedMetadatas {
currentDeletedMetadatas = notNilDeletedMetadatas
}
return
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
if itemMetadata.directory {
if let deletedDirectoryMetadatas =
dbManager.deleteDirectoryAndSubdirectoriesMetadata(
// MARK: - Helper methods
private static func metadatasToFileProviderItems(
- _ itemMetadatas: [NextcloudItemMetadataTable], ncKit: NextcloudKit,
+ _ itemMetadatas: [ItemMetadata], ncKit: NextcloudKit,
completionHandler: @escaping (_ items: [NSFileProviderItem]) -> Void
) {
var items: [NSFileProviderItem] = []
return
}
- if let parentItemIdentifier = NextcloudFilesDatabaseManager.shared
+ if let parentItemIdentifier = FilesDatabaseManager.shared
.parentItemIdentifierFromMetadata(itemMetadata)
{
let item = FileProviderItem(
private static func completeEnumerationObserver(
_ observer: NSFileProviderEnumerationObserver, ncKit: NextcloudKit, numPage: Int,
- itemMetadatas: [NextcloudItemMetadataTable]
+ itemMetadatas: [ItemMetadata]
) {
metadatasToFileProviderItems(itemMetadatas, ncKit: ncKit) { items in
observer.didEnumerate(items)
private static func completeChangesObserver(
_ observer: NSFileProviderChangeObserver, anchor: NSFileProviderSyncAnchor,
ncKit: NextcloudKit,
- newMetadatas: [NextcloudItemMetadataTable]?,
- updatedMetadatas: [NextcloudItemMetadataTable]?,
- deletedMetadatas: [NextcloudItemMetadataTable]?
+ newMetadatas: [ItemMetadata]?,
+ updatedMetadatas: [ItemMetadata]?,
+ deletedMetadatas: [ItemMetadata]?
) {
guard newMetadatas != nil || updatedMetadatas != nil || deletedMetadatas != nil else {
Logger.enumeration.error(
}
// Observer does not care about new vs updated, so join
- var allUpdatedMetadatas: [NextcloudItemMetadataTable] = []
- var allDeletedMetadatas: [NextcloudItemMetadataTable] = []
+ var allUpdatedMetadatas: [ItemMetadata] = []
+ var allDeletedMetadatas: [ItemMetadata] = []
if let newMetadatas {
allUpdatedMetadatas += newMetadatas
import Foundation
import NCDesktopClientSocketKit
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
extension FileProviderExtension: NSFileProviderServicing {
}
@objc func setupDomainAccount(user: String, serverUrl: String, password: String) {
- let newNcAccount = NextcloudAccount(user: user, serverUrl: serverUrl, password: password)
+ let newNcAccount = Account(user: user, serverUrl: serverUrl, password: password)
guard newNcAccount != ncAccount else { return }
ncAccount = newNcAccount
ncKit.setup(
import FileProvider
import Foundation
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
extension FileProviderExtension: NSFileProviderThumbnailing {
"Fetching thumbnail for item with identifier:\(itemIdentifier.rawValue, privacy: .public)"
)
guard
- let metadata = NextcloudFilesDatabaseManager.shared
+ let metadata = FilesDatabaseManager.shared
.itemMetadataFromFileProviderItemIdentifier(itemIdentifier),
let thumbnailUrl = metadata.thumbnailUrl(size: size)
else {
import FileProvider
import NCDesktopClientSocketKit
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
@objc class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension, NKCommonDelegate {
let domain: NSFileProviderDomain
let ncKit = NextcloudKit()
let appGroupIdentifier = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String
- var ncAccount: NextcloudAccount?
+ var ncAccount: Account?
lazy var ncKitBackground = NKBackground(nkCommonInstance: ncKit.nkCommonInstance)
lazy var socketClient: LocalSocketClient? = {
guard let containerUrl = pathForAppGroupContainer() else {
return Progress()
}
- let metadata = NextcloudItemMetadataTable()
+ let metadata = ItemMetadata()
metadata.account = ncAccount.ncKitAccount
metadata.directory = true
return Progress()
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
guard let metadata = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier),
let parentItemIdentifier = dbManager.parentItemIdentifierFromMetadata(metadata)
return Progress()
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let ocId = itemIdentifier.rawValue
guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
Logger.fileProviderExtension.error(
ocId: metadata.ocId, fileNameView: metadata.fileNameView, domain: domain)
dbManager.setStatusForItemMetadata(
- metadata, status: NextcloudItemMetadataTable.Status.downloading
+ metadata, status: ItemMetadata.Status.downloading
) { updatedMetadata in
guard let updatedMetadata else {
"Acquired contents of item with identifier: \(itemIdentifier.rawValue, privacy: .public) and filename: \(updatedMetadata.fileName, privacy: .public)"
)
- updatedMetadata.status = NextcloudItemMetadataTable.Status.normal.rawValue
+ updatedMetadata.status = ItemMetadata.Status.normal.rawValue
updatedMetadata.sessionError = ""
updatedMetadata.date = (date ?? NSDate()) as Date
updatedMetadata.etag = etag ?? ""
)
updatedMetadata.status =
- NextcloudItemMetadataTable.Status.downloadError.rawValue
+ ItemMetadata.Status.downloadError.rawValue
updatedMetadata.sessionError = error.errorDescription
dbManager.addItemMetadata(updatedMetadata)
return Progress()
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let parentItemIdentifier = itemTemplate.parentItemIdentifier
let itemTemplateIsFolder =
itemTemplate.contentType == .folder || itemTemplate.contentType == .directory
}
DispatchQueue.global().async {
- NextcloudItemMetadataTable.metadatasFromDirectoryReadNKFiles(
+ ItemMetadata.metadatasFromDirectoryReadNKFiles(
files, account: account
) {
directoryMetadata, _, _ in
)
}
- let newMetadata = NextcloudItemMetadataTable()
+ let newMetadata = ItemMetadata()
newMetadata.date = (date ?? NSDate()) as Date
newMetadata.etag = etag ?? ""
newMetadata.account = account
newMetadata.session = ""
newMetadata.sessionError = ""
newMetadata.sessionTaskIdentifier = 0
- newMetadata.status = NextcloudItemMetadataTable.Status.normal.rawValue
+ newMetadata.status = ItemMetadata.Status.normal.rawValue
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
dbManager.addItemMetadata(newMetadata)
return Progress()
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let parentItemIdentifier = item.parentItemIdentifier
let itemTemplateIsFolder = item.contentType == .folder || item.contentType == .directory
}
dbManager.setStatusForItemMetadata(
- metadata, status: NextcloudItemMetadataTable.Status.uploading
+ metadata, status: ItemMetadata.Status.uploading
) { updatedMetadata in
if updatedMetadata == nil {
)
}
- let newMetadata = NextcloudItemMetadataTable()
+ let newMetadata = ItemMetadata()
newMetadata.date = (date ?? NSDate()) as Date
newMetadata.etag = etag ?? ""
newMetadata.account = account
newMetadata.session = ""
newMetadata.sessionError = ""
newMetadata.sessionTaskIdentifier = 0
- newMetadata.status = NextcloudItemMetadataTable.Status.normal.rawValue
+ newMetadata.status = ItemMetadata.Status.normal.rawValue
dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
dbManager.addItemMetadata(newMetadata)
"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 = ItemMetadata.Status.uploadError.rawValue
metadata.sessionError = error.errorDescription
dbManager.addItemMetadata(metadata)
return Progress()
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let ocId = identifier.rawValue
guard let itemMetadata = dbManager.itemMetadataFromOcId(ocId) else {
completionHandler(NSFileProviderError(.noSuchItem))
import FileProvider
import NextcloudKit
+import NextcloudFileProviderKit
import UniformTypeIdentifiers
class FileProviderItem: NSObject, NSFileProviderItem {
case uploadError
}
- let metadata: NextcloudItemMetadataTable
+ let metadata: ItemMetadata
let parentItemIdentifier: NSFileProviderItemIdentifier
let ncKit: NextcloudKit
var isDownloaded: Bool {
metadata.directory
- || NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
+ || FilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
}
var isDownloading: Bool {
- metadata.status == NextcloudItemMetadataTable.Status.downloading.rawValue
+ metadata.status == ItemMetadata.Status.downloading.rawValue
}
var downloadingError: Error? {
- if metadata.status == NextcloudItemMetadataTable.Status.downloadError.rawValue {
+ if metadata.status == ItemMetadata.Status.downloadError.rawValue {
return FileProviderItemTransferError.downloadError
}
return nil
}
var isUploaded: Bool {
- NextcloudFilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
+ FilesDatabaseManager.shared.localFileMetadataFromOcId(metadata.ocId) != nil
}
var isUploading: Bool {
- metadata.status == NextcloudItemMetadataTable.Status.uploading.rawValue
+ metadata.status == ItemMetadata.Status.uploading.rawValue
}
var uploadingError: Error? {
- if metadata.status == NextcloudItemMetadataTable.Status.uploadError.rawValue {
+ if metadata.status == ItemMetadata.Status.uploadError.rawValue {
FileProviderItemTransferError.uploadError
} else {
nil
var childItemCount: NSNumber? {
if metadata.directory {
NSNumber(
- integerLiteral: NextcloudFilesDatabaseManager.shared.childItemsForDirectory(
+ integerLiteral: FilesDatabaseManager.shared.childItemsForDirectory(
metadata
).count)
} else {
}
required init(
- metadata: NextcloudItemMetadataTable,
+ metadata: ItemMetadata,
parentItemIdentifier: NSFileProviderItemIdentifier,
ncKit: NextcloudKit
) {
import FileProvider
import Foundation
+import NextcloudFileProviderKit
import OSLog
class FileProviderMaterialisedEnumerationObserver: NSObject, NSFileProviderEnumerationObserver {
_ itemIds: Set<String>, account: String,
completionHandler: @escaping (_ deletedOcIds: Set<String>) -> Void
) {
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
let databaseLocalFileMetadatas = dbManager.localFileMetadatas(account: account)
var noLongerMaterialisedIds = Set<String>()
+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import FileProvider
-import Foundation
-import OSLog
-
-func pathForAppGroupContainer() -> URL? {
- guard
- 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 FileManager.default.containerURL(
- forSecurityApplicationGroupIdentifier: appGroupIdentifier)
-}
-
-func pathForFileProviderExtData() -> URL? {
- let containerUrl = pathForAppGroupContainer()
- return containerUrl?.appendingPathComponent("FileProviderExt/")
-}
-
-func pathForFileProviderTempFilesForDomain(_ domain: NSFileProviderDomain) throws -> URL? {
- guard let fpManager = NSFileProviderManager(for: domain) else {
- Logger.localFileOps.error(
- "Unable to get file provider manager for domain: \(domain.displayName, privacy: .public)"
- )
- throw NSFileProviderError(.providerNotFound)
- }
-
- let fileProviderDataUrl = try fpManager.temporaryDirectoryURL()
- return fileProviderDataUrl.appendingPathComponent("TemporaryNextcloudFiles/")
-}
-
-func localPathForNCFile(ocId _: String, fileNameView: String, domain: NSFileProviderDomain) throws
- -> URL
-{
- 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)"
- )
- throw URLError(.badURL)
- }
-
- let filePathUrl = fileProviderFilesPathUrl.appendingPathComponent(fileNameView)
- let filePath = filePathUrl.path
-
- if !FileManager.default.fileExists(atPath: filePath) {
- FileManager.default.createFile(atPath: filePath, contents: nil)
- }
-
- return filePathUrl
-}
+++ /dev/null
-/*
- * Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import FileProvider
-import Foundation
-
-let ncAccountDictUsernameKey = "usernameKey"
-let ncAccountDictPasswordKey = "passwordKey"
-let ncAccountDictNcKitAccountKey = "ncKitAccountKey"
-let ncAccountDictServerUrlKey = "serverUrlKey"
-let ncAccountDictDavFilesUrlKey = "davFilesUrlKey"
-
-struct NextcloudAccount: Equatable {
- static let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
- let username, password, ncKitAccount, serverUrl, davFilesUrl: String
-
- init(user: String, serverUrl: String, password: String) {
- username = user
- self.password = password
- ncKitAccount = user + " " + serverUrl
- self.serverUrl = serverUrl
- davFilesUrl = serverUrl + NextcloudAccount.webDavFilesUrlSuffix + user
- }
-
- init?(dictionary: Dictionary<String, String>) {
- guard let username = dictionary[ncAccountDictUsernameKey],
- let password = dictionary[ncAccountDictPasswordKey],
- let ncKitAccount = dictionary[ncAccountDictNcKitAccountKey],
- let serverUrl = dictionary[ncAccountDictServerUrlKey],
- let davFilesUrl = dictionary[ncAccountDictDavFilesUrlKey]
- else {
- return nil
- }
-
- self.username = username
- self.password = password
- self.ncKitAccount = ncKitAccount
- self.serverUrl = serverUrl
- self.davFilesUrl = davFilesUrl
- }
-
- func dictionary() -> Dictionary<String, String> {
- return [
- ncAccountDictUsernameKey: username,
- ncAccountDictPasswordKey: password,
- ncAccountDictNcKitAccountKey: ncKitAccount,
- ncAccountDictServerUrlKey: serverUrl,
- ncAccountDictDavFilesUrlKey: davFilesUrl
- ]
- }
-}
import FileProvider
import Foundation
import NextcloudKit
+import NextcloudFileProviderKit
import OSLog
class FPUIExtensionServiceSource: NSObject, NSFileProviderServiceSource, NSXPCListenerDelegate, FPUIExtensionService {
return nil
}
- let dbManager = NextcloudFilesDatabaseManager.shared
+ let dbManager = FilesDatabaseManager.shared
guard let item = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier) else {
Logger.shares.error("No item \(rawIdentifier, privacy: .public) in db, no shares.")
return nil
private(set) var shares: [NKShare] = [] {
didSet { Task { @MainActor in sharesTableView?.reloadData() } }
}
- private var account: NextcloudAccount? {
+ private var account: Account? {
didSet {
guard let account = account else { return }
kit = NextcloudKit()
let connection = try await serviceConnection(url: itemURL)
guard let serverPath = await connection.itemServerPath(identifier: itemIdentifier),
let credentials = await connection.credentials() as? Dictionary<String, String>,
- let convertedAccount = NextcloudAccount(dictionary: credentials),
+ let convertedAccount = Account(dictionary: credentials),
!convertedAccount.password.isEmpty
else {
presentError("Failed to get details from File Provider Extension. Retrying.")
5307A6E62965C6FA001E0C6A /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6E52965C6FA001E0C6A /* NextcloudKit */; };
5307A6E82965DAD8001E0C6A /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6E72965DAD8001E0C6A /* NextcloudKit */; };
5307A6EB2965DB8D001E0C6A /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6EA2965DB8D001E0C6A /* RealmSwift */; };
- 5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */; };
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 531522812B8E01C6002E31BE /* ShareTableItemView.xib */; };
- 5318AD9129BF42FB00CBB71C /* NextcloudItemMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */; };
- 5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */; };
5318AD9729BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */; };
5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */; };
5350E4E92B0C534A00F276CB /* ClientCommunicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5350E4E82B0C534A00F276CB /* ClientCommunicationService.swift */; };
- 5352B36629DC14970011CE03 /* NextcloudFilesDatabaseManager+Directories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36529DC14970011CE03 /* NextcloudFilesDatabaseManager+Directories.swift */; };
- 5352B36829DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36729DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift */; };
5352B36C29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */; };
5352E85B29B7BFE6002CE85C /* Progress+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352E85A29B7BFE6002CE85C /* Progress+Extensions.swift */; };
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */; };
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */; };
53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */; };
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */; };
- 536EFC36295E3C1100F4CB13 /* NextcloudAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */; };
5374FD442B95EE1400C78D54 /* ShareController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5374FD432B95EE1400C78D54 /* ShareController.swift */; };
5376307D2B85E2ED0026BFAB /* Logger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5376307C2B85E2ED0026BFAB /* Logger+Extensions.swift */; };
537630912B85F4980026BFAB /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 537630902B85F4980026BFAB /* ShareViewController.xib */; };
53903D37295618A400D0B308 /* LineProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 53903D36295618A400D0B308 /* LineProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; };
539158AC27BE71A900816F56 /* FinderSyncSocketLineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.m */; };
53B979812B84C81F002DA742 /* DocumentActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B979802B84C81F002DA742 /* DocumentActionViewController.swift */; };
- 53D056312970594F00988392 /* LocalFilesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D056302970594F00988392 /* LocalFilesUtils.swift */; };
53C331B22BCD28C30093D38B /* NextcloudFileProviderKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53C331B12BCD28C30093D38B /* NextcloudFileProviderKit */; };
53D666612B70C9A70042C03D /* FileProviderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D666602B70C9A70042C03D /* FileProviderConfig.swift */; };
53ED472029C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED471F29C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift */; };
- 53ED472829C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472729C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift */; };
53ED473029C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472F29C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift */; };
53FE14502B8E0658006C4193 /* ShareTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */; };
53FE14542B8E1219006C4193 /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53FE14532B8E1219006C4193 /* NextcloudKit */; };
- 53FE14552B8E28E9006C4193 /* NextcloudAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */; };
53FE14592B8E3F6C006C4193 /* ShareTableItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */; };
53FE145B2B8F1305006C4193 /* NKShare+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE145A2B8F1305006C4193 /* NKShare+Extensions.swift */; };
53FE14652B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */; };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudFilesDatabaseManager.swift; sourceTree = "<group>"; };
531522812B8E01C6002E31BE /* ShareTableItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareTableItemView.xib; sourceTree = "<group>"; };
- 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudItemMetadataTable.swift; sourceTree = "<group>"; };
- 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudLocalFileMetadataTable.swift; sourceTree = "<group>"; };
5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderMaterialisedEnumerationObserver.swift; sourceTree = "<group>"; };
5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NKError+Extensions.swift"; sourceTree = "<group>"; };
5350E4E72B0C514400F276CB /* ClientCommunicationProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClientCommunicationProtocol.h; sourceTree = "<group>"; };
5350E4E82B0C534A00F276CB /* ClientCommunicationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientCommunicationService.swift; sourceTree = "<group>"; };
5350E4EA2B0C9CE100F276CB /* FileProviderExt-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FileProviderExt-Bridging-Header.h"; sourceTree = "<group>"; };
- 5352B36529DC14970011CE03 /* NextcloudFilesDatabaseManager+Directories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudFilesDatabaseManager+Directories.swift"; sourceTree = "<group>"; };
- 5352B36729DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudFilesDatabaseManager+LocalFiles.swift"; sourceTree = "<group>"; };
5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderExtension+Thumbnailing.swift"; sourceTree = "<group>"; };
5352E85A29B7BFE6002CE85C /* Progress+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+Extensions.swift"; sourceTree = "<group>"; };
535AE30D29C0A2CC0042A9BA /* Logger+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Extensions.swift"; sourceTree = "<group>"; };
53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareeSuggestionsDataSource.swift; sourceTree = "<group>"; };
536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderSocketLineProcessor.swift; sourceTree = "<group>"; };
- 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudAccount.swift; sourceTree = "<group>"; };
5374FD432B95EE1400C78D54 /* ShareController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareController.swift; sourceTree = "<group>"; };
5376307C2B85E2ED0026BFAB /* Logger+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Extensions.swift"; sourceTree = "<group>"; };
5376307E2B85E5650026BFAB /* FileProviderUIExt.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = FileProviderUIExt.entitlements; sourceTree = "<group>"; };
53B9797E2B84C81F002DA742 /* FileProviderUIExt.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FileProviderUIExt.appex; sourceTree = BUILT_PRODUCTS_DIR; };
53B979802B84C81F002DA742 /* DocumentActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentActionViewController.swift; sourceTree = "<group>"; };
53B979852B84C81F002DA742 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- 53D056302970594F00988392 /* LocalFilesUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalFilesUtils.swift; sourceTree = "<group>"; };
53D666602B70C9A70042C03D /* FileProviderConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderConfig.swift; sourceTree = "<group>"; };
53ED471F29C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderEnumerator+SyncEngine.swift"; sourceTree = "<group>"; };
- 53ED472729C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudItemMetadataTable+NKFile.swift"; sourceTree = "<group>"; };
53ED472F29C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderExtension+ClientInterface.swift"; sourceTree = "<group>"; };
53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareTableViewDataSource.swift; sourceTree = "<group>"; };
53FE14572B8E3A7C006C4193 /* FileProviderUIExtRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FileProviderUIExtRelease.entitlements; sourceTree = "<group>"; };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 5318AD8F29BF406500CBB71C /* Database */ = {
- isa = PBXGroup;
- children = (
- 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */,
- 5352B36529DC14970011CE03 /* NextcloudFilesDatabaseManager+Directories.swift */,
- 5352B36729DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift */,
- 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */,
- 53ED472729C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift */,
- 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */,
- );
- path = Database;
- sourceTree = "<group>";
- };
5350E4C72B0C368B00F276CB /* Services */ = {
isa = PBXGroup;
children = (
538E396B27F4765000FA63D5 /* FileProviderExt */ = {
isa = PBXGroup;
children = (
- 5318AD8F29BF406500CBB71C /* Database */,
5352E85929B7BFB4002CE85C /* Extensions */,
5350E4C72B0C368B00F276CB /* Services */,
53D666602B70C9A70042C03D /* FileProviderConfig.swift */,
538E396E27F4765000FA63D5 /* FileProviderItem.swift */,
5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */,
536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */,
- 53D056302970594F00988392 /* LocalFilesUtils.swift */,
- 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */,
538E397327F4765000FA63D5 /* FileProviderExt.entitlements */,
538E397227F4765000FA63D5 /* Info.plist */,
5350E4EA2B0C9CE100F276CB /* FileProviderExt-Bridging-Header.h */,
files = (
5352E85B29B7BFE6002CE85C /* Progress+Extensions.swift in Sources */,
53D666612B70C9A70042C03D /* FileProviderConfig.swift in Sources */,
- 536EFC36295E3C1100F4CB13 /* NextcloudAccount.swift in Sources */,
53ED473029C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift in Sources */,
538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */,
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */,
53ED472029C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift in Sources */,
5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */,
- 53ED472829C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift in Sources */,
537630972B860D920026BFAB /* FPUIExtensionService.swift in Sources */,
- 5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */,
535AE30E29C0A2CC0042A9BA /* Logger+Extensions.swift in Sources */,
- 5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */,
537630952B860D560026BFAB /* FPUIExtensionServiceSource.swift in Sources */,
- 53D056312970594F00988392 /* LocalFilesUtils.swift in Sources */,
538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */,
- 5352B36829DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift in Sources */,
- 5318AD9129BF42FB00CBB71C /* NextcloudItemMetadataTable.swift in Sources */,
5350E4E92B0C534A00F276CB /* ClientCommunicationService.swift in Sources */,
- 5352B36629DC14970011CE03 /* NextcloudFilesDatabaseManager+Directories.swift in Sources */,
5318AD9729BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift in Sources */,
5352B36C29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift in Sources */,
538E397127F4765000FA63D5 /* FileProviderEnumerator.swift in Sources */,
5374FD442B95EE1400C78D54 /* ShareController.swift in Sources */,
53FE145B2B8F1305006C4193 /* NKShare+Extensions.swift in Sources */,
53FE14592B8E3F6C006C4193 /* ShareTableItemView.swift in Sources */,
- 53FE14552B8E28E9006C4193 /* NextcloudAccount.swift in Sources */,
5376307D2B85E2ED0026BFAB /* Logger+Extensions.swift in Sources */,
53FE14502B8E0658006C4193 /* ShareTableViewDataSource.swift in Sources */,
537630982B8612F00026BFAB /* FPUIExtensionService.swift in Sources */,