diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index fd3670a38f2c2..db5eda2d50beb 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -104,7 +104,15 @@ import OSLog request _: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void ) -> Progress { - if let item = Item.storedItem(identifier: identifier, remoteInterface: ncKit) { + if ncAccount == nil { + Logger.fileProviderExtension.error( + """ + Not fetching item for identifier: \(identifier.rawValue, privacy: .public) + as account not set up yet. + """ + ) + completionHandler(nil, NSFileProviderError(.notAuthenticated)) + } else if let item = Item.storedItem(identifier: identifier, remoteInterface: ncKit) { completionHandler(item, nil) } else { completionHandler(nil, NSFileProviderError(.noSuchItem)) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/DocumentActionViewController.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/DocumentActionViewController.swift index 96363e962b124..dfff3bf34ae7a 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/DocumentActionViewController.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/DocumentActionViewController.swift @@ -36,17 +36,21 @@ class DocumentActionViewController: FPUIActionExtensionViewController { ) { Logger.actionViewController.info("Preparing action: \(actionIdentifier, privacy: .public)") - if actionIdentifier == "com.nextcloud.desktopclient.FileProviderUIExt.ShareAction" { + switch (actionIdentifier) { + case "com.nextcloud.desktopclient.FileProviderUIExt.ShareAction": prepare(childViewController: ShareViewController(itemIdentifiers)) + case "com.nextcloud.desktopclient.FileProviderUIExt.LockFileAction": + prepare(childViewController: LockViewController(itemIdentifiers, locking: true)) + case "com.nextcloud.desktopclient.FileProviderUIExt.UnlockFileAction": + prepare(childViewController: LockViewController(itemIdentifiers, locking: false)) + default: + return } - } override func prepare(forError error: Error) { Logger.actionViewController.info( - """ - Preparing for error: \(error.localizedDescription, privacy: .public) - """ + "Preparing for error: \(error.localizedDescription, privacy: .public)" ) } diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Extensions/Logger+Extensions.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Extensions/Logger+Extensions.swift index 960171034da09..d2e38c3cc0953 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Extensions/Logger+Extensions.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Extensions/Logger+Extensions.swift @@ -11,6 +11,8 @@ extension Logger { private static var subsystem = Bundle.main.bundleIdentifier! static let actionViewController = Logger(subsystem: subsystem, category: "actionViewController") + static let lockViewController = Logger(subsystem: subsystem, category: "lockViewController") + static let metadataProvider = Logger(subsystem: subsystem, category: "metadataProvider") static let shareCapabilities = Logger(subsystem: subsystem, category: "shareCapabilities") static let shareController = Logger(subsystem: subsystem, category: "shareController") static let shareeDataSource = Logger(subsystem: subsystem, category: "shareeDataSource") diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderCommunication.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderCommunication.swift new file mode 100644 index 0000000000000..111e282dcadb7 --- /dev/null +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderCommunication.swift @@ -0,0 +1,31 @@ +// +// FileProviderCommunication.swift +// FileProviderUIExt +// +// Created by Claudio Cambra on 30/7/24. +// + +import FileProvider + +enum FileProviderCommunicationError: Error { + case serviceNotFound + case remoteProxyObjectInvalid +} + +func serviceConnection( + url: URL, interruptionHandler: @escaping () -> Void +) async throws -> FPUIExtensionService { + let services = try await FileManager().fileProviderServicesForItem(at: url) + guard let service = services[fpUiExtensionServiceName] else { + throw FileProviderCommunicationError.serviceNotFound + } + let connection: NSXPCConnection + connection = try await service.fileProviderConnection() + connection.remoteObjectInterface = NSXPCInterface(with: FPUIExtensionService.self) + connection.interruptionHandler = interruptionHandler + connection.resume() + guard let proxy = connection.remoteObjectProxy as? FPUIExtensionService else { + throw FileProviderCommunicationError.remoteProxyObjectInvalid + } + return proxy +} diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Info.plist index 85f108b15fc96..31930b7627abc 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Info.plist @@ -2,16 +2,32 @@ - CFBundleName - $(PRODUCT_NAME) - CFBundleDisplayName - $(OC_APPLICATION_NAME) File Provider UI Extension - CFBundleIdentifier - $(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME) + CFBundleName + $(PRODUCT_NAME) + CFBundleDisplayName + $(OC_APPLICATION_NAME) File Provider UI Extension + CFBundleIdentifier + $(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME) NSExtension NSExtensionFileProviderActions + + NSExtensionFileProviderActionIdentifier + com.nextcloud.desktopclient.FileProviderUIExt.UnlockFileAction + NSExtensionFileProviderActionName + Unlock file + NSExtensionFileProviderActionActivationRule + SUBQUERY ( fileproviderItems, $fileproviderItem, $fileproviderItem.userInfo.locked != nil && !($fileproviderItem.contentType.identifier UTI-CONFORMS-TO "public.folder") ).@count > 0 + + + NSExtensionFileProviderActionActivationRule + SUBQUERY ( fileproviderItems, $fileproviderItem, $fileproviderItem.userInfo.locked == nil && !($fileproviderItem.contentType.identifier UTI-CONFORMS-TO "public.folder") ).@count > 0 + NSExtensionFileProviderActionName + Lock file + NSExtensionFileProviderActionIdentifier + com.nextcloud.desktopclient.FileProviderUIExt.LockFileAction + NSExtensionFileProviderActionActivationRule TRUEPREDICATE diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.swift new file mode 100644 index 0000000000000..f7347848afc7f --- /dev/null +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.swift @@ -0,0 +1,234 @@ +// +// LockViewController.swift +// FileProviderUIExt +// +// Created by Claudio Cambra on 30/7/24. +// + +import AppKit +import FileProvider +import NextcloudFileProviderKit +import NextcloudKit +import OSLog +import QuickLookThumbnailing + +class LockViewController: NSViewController { + let itemIdentifiers: [NSFileProviderItemIdentifier] + let locking: Bool + + @IBOutlet weak var fileNameIcon: NSImageView! + @IBOutlet weak var fileNameLabel: NSTextField! + @IBOutlet weak var descriptionLabel: NSTextField! + @IBOutlet weak var closeButton: NSButton! + @IBOutlet weak var loadingIndicator: NSProgressIndicator! + @IBOutlet weak var warnImage: NSImageView! + + public override var nibName: NSNib.Name? { + return NSNib.Name(self.className) + } + + var actionViewController: DocumentActionViewController! { + return parent as? DocumentActionViewController + } + + init(_ itemIdentifiers: [NSFileProviderItemIdentifier], locking: Bool) { + self.itemIdentifiers = itemIdentifiers + self.locking = locking + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + guard let firstItem = itemIdentifiers.first else { + Logger.shareViewController.error("called without items") + closeAction(self) + return + } + + Logger.lockViewController.info( + """ + Locking \(self.locking ? "enabled" : "disabled", privacy: .public) for items: + \(firstItem.rawValue, privacy: .public) + """ + ) + + Task { + await processItemIdentifier(firstItem) + } + } + + @IBAction func closeAction(_ sender: Any) { + actionViewController.extensionContext.completeRequest() + } + + private func stopIndicatingLoading() { + loadingIndicator.stopAnimation(self) + loadingIndicator.isHidden = true + warnImage.isHidden = false + } + + private func presentError(_ error: String) { + Logger.lockViewController.error("Error: \(error, privacy: .public)") + descriptionLabel.stringValue = "Error: \(error)" + stopIndicatingLoading() + } + + private func processItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) async { + guard let manager = NSFileProviderManager(for: actionViewController.domain) else { + fatalError("NSFileProviderManager isn't expected to fail") + } + + do { + let itemUrl = try await manager.getUserVisibleURL(for: itemIdentifier) + guard itemUrl.startAccessingSecurityScopedResource() else { + Logger.lockViewController.error("Could not access scoped resource for item url!") + return + } + await updateFileDetailsDisplay(itemUrl: itemUrl) + itemUrl.stopAccessingSecurityScopedResource() + await lockOrUnlockFile(localItemUrl: itemUrl) + } catch let error { + let errorString = "Error processing item: \(error)" + Logger.lockViewController.error("\(errorString, privacy: .public)") + fileNameLabel.stringValue = "Could not lock unknown item…" + descriptionLabel.stringValue = errorString + } + } + + private func updateFileDetailsDisplay(itemUrl: URL) async { + let lockAction = locking ? "Locking" : "Unlocking" + fileNameLabel.stringValue = "\(lockAction) file \(itemUrl.lastPathComponent)…" + + let request = QLThumbnailGenerator.Request( + fileAt: itemUrl, + size: CGSize(width: 48, height: 48), + scale: 1.0, + representationTypes: .icon + ) + let generator = QLThumbnailGenerator.shared + let fileThumbnail = await withCheckedContinuation { continuation in + generator.generateRepresentations(for: request) { thumbnail, type, error in + if thumbnail == nil || error != nil { + Logger.lockViewController.error( + "Could not get thumbnail: \(error, privacy: .public)" + ) + } + continuation.resume(returning: thumbnail) + } + } + + fileNameIcon.image = + fileThumbnail?.nsImage ?? + NSImage(systemSymbolName: "doc", accessibilityDescription: "doc") + } + + private func lockOrUnlockFile(localItemUrl: URL) async { + descriptionLabel.stringValue = "Fetching file details…" + + guard let itemIdentifier = await withCheckedContinuation({ + (continuation: CheckedContinuation) -> Void in + NSFileProviderManager.getIdentifierForUserVisibleFile( + at: localItemUrl + ) { identifier, domainIdentifier, error in + defer { continuation.resume(returning: identifier) } + guard error == nil else { + self.presentError("No item with identifier: \(error.debugDescription)") + return + } + } + }) else { + presentError("Could not get identifier for item, no shares can be acquired.") + return + } + + do { + let connection = try await serviceConnection(url: localItemUrl, interruptionHandler: { + Logger.lockViewController.error("Service connection interrupted") + }) + guard let serverPath = await connection.itemServerPath(identifier: itemIdentifier), + let credentials = await connection.credentials() as? Dictionary, + let account = Account(dictionary: credentials), + !account.password.isEmpty + else { + presentError("Failed to get details from File Provider Extension.") + return + } + let serverPathString = serverPath as String + let kit = NextcloudKit() + kit.setup( + user: account.username, + userId: account.username, + password: account.password, + urlBase: account.serverUrl + ) + // guard let capabilities = await fetchCapabilities() else { + guard let itemMetadata = await fetchItemMetadata( + itemRelativePath: serverPathString, kit: kit + ) else { + presentError("Could not get item metadata.") + return + } + + // Run lock state checks + if locking { + guard !itemMetadata.lock else { + presentError("File is already locked.") + return + } + } else { + guard itemMetadata.lock else { + presentError("File is already unlocked.") + return + } + } + + descriptionLabel.stringValue = + "Communicating with server, \(locking ? "locking" : "unlocking") file…" + + let serverUrlFileName = itemMetadata.serverUrl + "/" + itemMetadata.fileName + Logger.lockViewController.info( + """ + Locking file: \(serverUrlFileName, privacy: .public) + \(self.locking ? "locking" : "unlocking", privacy: .public) + """ + ) + + let error = await withCheckedContinuation { continuation in + kit.lockUnlockFile( + serverUrlFileName: serverUrlFileName, + shouldLock: locking, + completion: { _, error in + continuation.resume(returning: error) + } + ) + } + if error == .success { + descriptionLabel.stringValue = "File \(self.locking ? "locked" : "unlocked")!" + warnImage.image = NSImage( + systemSymbolName: "checkmark.circle.fill", + accessibilityDescription: "checkmark.circle.fill" + ) + stopIndicatingLoading() + if let manager = NSFileProviderManager(for: actionViewController.domain) { + do { + try await manager.signalEnumerator(for: itemIdentifier) + } catch let error { + presentError( + """ + Could not signal lock state change in virtual file. + Changes may take a while to be reflected on your Mac. + Error: \(error.localizedDescription) + """) + } + } + } else { + presentError("Could not lock file: \(error.errorDescription).") + } + } catch let error { + presentError("Could not lock file: \(error).") + } + } +} diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.xib b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.xib new file mode 100644 index 0000000000000..6df0fea32b51b --- /dev/null +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Locking/LockViewController.xib @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/MetadataProvider.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/MetadataProvider.swift new file mode 100644 index 0000000000000..890c3206577e2 --- /dev/null +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/MetadataProvider.swift @@ -0,0 +1,45 @@ +// +// MetadataProvider.swift +// FileProviderUIExt +// +// Created by Claudio Cambra on 30/7/24. +// + +import Foundation +import NextcloudKit +import OSLog + +func fetchItemMetadata(itemRelativePath: String, kit: NextcloudKit) async -> NKFile? { + func slashlessPath(_ string: String) -> String { + var strCopy = string + if strCopy.hasPrefix("/") { + strCopy.removeFirst() + } + if strCopy.hasSuffix("/") { + strCopy.removeLast() + } + return strCopy + } + + let nkCommon = kit.nkCommonInstance + let urlBase = slashlessPath(nkCommon.urlBase) + let davSuffix = slashlessPath(nkCommon.dav) + let userId = nkCommon.userId + let itemRelPath = slashlessPath(itemRelativePath) + + let itemFullServerPath = "\(urlBase)/\(davSuffix)/files/\(userId)/\(itemRelPath)" + return await withCheckedContinuation { continuation in + kit.readFileOrFolder(serverUrlFileName: itemFullServerPath, depth: "0") { + account, files, data, error in + guard error == .success else { + Logger.metadataProvider.error( + "Error getting item metadata: \(error.errorDescription)" + ) + continuation.resume(returning: nil) + return + } + Logger.metadataProvider.info("Successfully retrieved item metadata") + continuation.resume(returning: files.first) + } + } +} diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareController.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareController.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareController.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareController.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareOptionsView.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareOptionsView.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareOptionsView.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareOptionsView.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableItemView.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableItemView.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableItemView.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableItemView.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableItemView.xib b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableItemView.xib similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableItemView.xib rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableItemView.xib diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableViewDataSource.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableViewDataSource.swift similarity index 75% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableViewDataSource.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableViewDataSource.swift index 3cca1d1a9df51..31d9b578fc68e 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareTableViewDataSource.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareTableViewDataSource.swift @@ -66,7 +66,10 @@ class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDele } func reload() async { - guard let itemURL = itemURL else { return } + guard let itemURL else { + presentError("No item URL, cannot reload data!") + return + } guard let itemIdentifier = await withCheckedContinuation({ (continuation: CheckedContinuation) -> Void in NSFileProviderManager.getIdentifierForUserVisibleFile( @@ -84,7 +87,9 @@ class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDele } do { - let connection = try await serviceConnection(url: itemURL) + let connection = try await serviceConnection(url: itemURL, interruptionHandler: { + Logger.sharesDataSource.error("Service connection interrupted") + }) guard let serverPath = await connection.itemServerPath(identifier: itemIdentifier), let credentials = await connection.credentials() as? Dictionary, let convertedAccount = Account(dictionary: credentials), @@ -104,7 +109,11 @@ class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDele presentError("Server does not support shares.") return } - itemMetadata = await fetchItemMetadata(itemRelativePath: serverPathString) + guard let kit else { + presentError("NextcloudKit instance is unavailable, cannot reload data!") + return + } + itemMetadata = await fetchItemMetadata(itemRelativePath: serverPathString, kit: kit) guard itemMetadata?.permissions.contains("R") == true else { presentError("This file cannot be shared.") return @@ -118,25 +127,6 @@ class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDele } } - private func serviceConnection(url: URL) async throws -> FPUIExtensionService { - let services = try await FileManager().fileProviderServicesForItem(at: url) - guard let service = services[fpUiExtensionServiceName] else { - Logger.sharesDataSource.error("Couldn't get service, required service not present") - throw NSFileProviderError(.providerNotFound) - } - let connection: NSXPCConnection - connection = try await service.fileProviderConnection() - connection.remoteObjectInterface = NSXPCInterface(with: FPUIExtensionService.self) - connection.interruptionHandler = { - Logger.sharesDataSource.error("Service connection interrupted") - } - connection.resume() - guard let proxy = connection.remoteObjectProxy as? FPUIExtensionService else { - throw NSFileProviderError(.serverUnreachable) - } - return proxy - } - private func fetch( itemIdentifier: NSFileProviderItemIdentifier, itemRelativePath: String ) async -> [NKShare] { @@ -180,44 +170,6 @@ class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDele } } - private func fetchItemMetadata(itemRelativePath: String) async -> NKFile? { - guard let kit = kit else { - presentError("Could not fetch item metadata as NextcloudKit instance is unavailable") - return nil - } - - func slashlessPath(_ string: String) -> String { - var strCopy = string - if strCopy.hasPrefix("/") { - strCopy.removeFirst() - } - if strCopy.hasSuffix("/") { - strCopy.removeLast() - } - return strCopy - } - - let nkCommon = kit.nkCommonInstance - let urlBase = slashlessPath(nkCommon.urlBase) - let davSuffix = slashlessPath(nkCommon.dav) - let userId = nkCommon.userId - let itemRelPath = slashlessPath(itemRelativePath) - - let itemFullServerPath = "\(urlBase)/\(davSuffix)/files/\(userId)/\(itemRelPath)" - return await withCheckedContinuation { continuation in - kit.readFileOrFolder(serverUrlFileName: itemFullServerPath, depth: "0") { - account, files, data, error in - guard error == .success else { - self.presentError("Error getting item metadata: \(error.errorDescription)") - continuation.resume(returning: nil) - return - } - Logger.sharesDataSource.info("Successfully retrieved item metadata") - continuation.resume(returning: files.first) - } - } - } - private func presentError(_ errorString: String) { Logger.sharesDataSource.error("\(errorString, privacy: .public)") Task { @MainActor in self.uiDelegate?.showError(errorString) } diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewController.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewController.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewController.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewController.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewController.xib b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewController.xib similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewController.xib rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewController.xib diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewDataSourceUIDelegate.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewDataSourceUIDelegate.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareViewDataSourceUIDelegate.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareViewDataSourceUIDelegate.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareeSuggestionsDataSource.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareeSuggestionsDataSource.swift similarity index 100% rename from shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/ShareeSuggestionsDataSource.swift rename to shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Sharing/ShareeSuggestionsDataSource.swift diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 7162acf23b440..b2613dddd6c04 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -25,6 +25,10 @@ 537630952B860D560026BFAB /* FPUIExtensionServiceSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630942B860D560026BFAB /* FPUIExtensionServiceSource.swift */; }; 537630972B860D920026BFAB /* FPUIExtensionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630962B860D920026BFAB /* FPUIExtensionService.swift */; }; 537630982B8612F00026BFAB /* FPUIExtensionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630962B860D920026BFAB /* FPUIExtensionService.swift */; }; + 537BD67A2C58D67800446ED0 /* LockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BD6792C58D67800446ED0 /* LockViewController.swift */; }; + 537BD67C2C58D7B700446ED0 /* LockViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 537BD67B2C58D7B700446ED0 /* LockViewController.xib */; }; + 537BD6802C58F01B00446ED0 /* FileProviderCommunication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BD67F2C58F01B00446ED0 /* FileProviderCommunication.swift */; }; + 537BD6822C58F72E00446ED0 /* MetadataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BD6812C58F72E00446ED0 /* MetadataProvider.swift */; }; 538E396A27F4765000FA63D5 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */; }; 538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */; }; 538E397627F4765000FA63D5 /* FileProviderExt.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 538E396727F4765000FA63D5 /* FileProviderExt.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -163,6 +167,10 @@ 537630922B85F4B00026BFAB /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 537630942B860D560026BFAB /* FPUIExtensionServiceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUIExtensionServiceSource.swift; sourceTree = ""; }; 537630962B860D920026BFAB /* FPUIExtensionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUIExtensionService.swift; sourceTree = ""; }; + 537BD6792C58D67800446ED0 /* LockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockViewController.swift; sourceTree = ""; }; + 537BD67B2C58D7B700446ED0 /* LockViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LockViewController.xib; sourceTree = ""; }; + 537BD67F2C58F01B00446ED0 /* FileProviderCommunication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderCommunication.swift; sourceTree = ""; }; + 537BD6812C58F72E00446ED0 /* MetadataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataProvider.swift; sourceTree = ""; }; 538E396727F4765000FA63D5 /* FileProviderExt.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FileProviderExt.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; }; 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderExtension.swift; sourceTree = ""; }; @@ -280,6 +288,31 @@ path = Extensions; sourceTree = ""; }; + 537BD6772C58D0C400446ED0 /* Sharing */ = { + isa = PBXGroup; + children = ( + 5374FD432B95EE1400C78D54 /* ShareController.swift */, + 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */, + 53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */, + 53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */, + 531522812B8E01C6002E31BE /* ShareTableItemView.xib */, + 53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */, + 537630922B85F4B00026BFAB /* ShareViewController.swift */, + 537630902B85F4980026BFAB /* ShareViewController.xib */, + 53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */, + ); + path = Sharing; + sourceTree = ""; + }; + 537BD6782C58D0FC00446ED0 /* Locking */ = { + isa = PBXGroup; + children = ( + 537BD6792C58D67800446ED0 /* LockViewController.swift */, + 537BD67B2C58D7B700446ED0 /* LockViewController.xib */, + ); + path = Locking; + sourceTree = ""; + }; 538E396827F4765000FA63D5 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -321,16 +354,11 @@ isa = PBXGroup; children = ( 5376307B2B85E2E00026BFAB /* Extensions */, + 537BD6782C58D0FC00446ED0 /* Locking */, + 537BD6772C58D0C400446ED0 /* Sharing */, 53B979802B84C81F002DA742 /* DocumentActionViewController.swift */, - 5374FD432B95EE1400C78D54 /* ShareController.swift */, - 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */, - 53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */, - 53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */, - 531522812B8E01C6002E31BE /* ShareTableItemView.xib */, - 53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */, - 537630922B85F4B00026BFAB /* ShareViewController.swift */, - 537630902B85F4980026BFAB /* ShareViewController.xib */, - 53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */, + 537BD67F2C58F01B00446ED0 /* FileProviderCommunication.swift */, + 537BD6812C58F72E00446ED0 /* MetadataProvider.swift */, 53FE14572B8E3A7C006C4193 /* FileProviderUIExt.entitlements */, 53B979852B84C81F002DA742 /* Info.plist */, ); @@ -617,6 +645,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 537BD67C2C58D7B700446ED0 /* LockViewController.xib in Resources */, 531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */, 537630912B85F4980026BFAB /* ShareViewController.xib in Resources */, ); @@ -691,9 +720,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 537BD6822C58F72E00446ED0 /* MetadataProvider.swift in Sources */, 537630932B85F4B00026BFAB /* ShareViewController.swift in Sources */, 53FE14672B8F78B6006C4193 /* ShareOptionsView.swift in Sources */, 53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */, + 537BD67A2C58D67800446ED0 /* LockViewController.swift in Sources */, 53FE14652B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift in Sources */, 53B979812B84C81F002DA742 /* DocumentActionViewController.swift in Sources */, 5374FD442B95EE1400C78D54 /* ShareController.swift in Sources */, @@ -701,6 +732,7 @@ 53FE14592B8E3F6C006C4193 /* ShareTableItemView.swift in Sources */, 5376307D2B85E2ED0026BFAB /* Logger+Extensions.swift in Sources */, 53FE14502B8E0658006C4193 /* ShareTableViewDataSource.swift in Sources */, + 537BD6802C58F01B00446ED0 /* FileProviderCommunication.swift in Sources */, 537630982B8612F00026BFAB /* FPUIExtensionService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;