// // FileProvider.swift // PickerFileProvider // // Created by Marino Faggiana on 27/12/16. // Copyright © 2017 TWS. All rights reserved. // // Author Marino Faggiana // // 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 3 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. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // import UIKit class FileProvider: NSFileProviderExtension, CCNetworkingDelegate { lazy var networkingOperationQueue: OperationQueue = { var queue = OperationQueue() queue.name = k_queue queue.maxConcurrentOperationCount = 10 return queue }() var fileCoordinator: NSFileCoordinator { let fileCoordinator = NSFileCoordinator() fileCoordinator.purposeIdentifier = self.providerIdentifier return fileCoordinator } override init() { super.init() self.fileCoordinator.coordinate(writingItemAt: self.documentStorageURL, options: [], error: nil, byAccessor: { newURL in // ensure the documentStorageURL actually exists do { try FileManager.default.createDirectory(at: newURL, withIntermediateDirectories: true, attributes: nil) } catch { // Handle error } }) } override func providePlaceholder(at url: URL, completionHandler: ((_ error: Error?) -> Void)?) { // Should call writePlaceholderAtURL(_:withMetadata:error:) with the placeholder URL, then call the completion handler with the error if applicable. let fileName = url.lastPathComponent let placeholderURL = NSFileProviderExtension.placeholderURL(for: self.documentStorageURL.appendingPathComponent(fileName)) // TODO: get file size for file at from model let fileSize = 0 let metadata = [AnyHashable(URLResourceKey.fileSizeKey): fileSize] do { try NSFileProviderExtension.writePlaceholder(at: placeholderURL, withMetadata: metadata as! [URLResourceKey : Any]) } catch { // Handle error } completionHandler?(nil) } override func startProvidingItem(at url: URL, completionHandler: ((_ error: Error?) -> Void)?) { guard let fileData = try? Data(contentsOf: url) else { // NOTE: you would generate an NSError to supply to the completionHandler // here however that is outside of the scope for this tutorial completionHandler?(nil) return } do { _ = try fileData.write(to: url, options: NSData.WritingOptions()) completionHandler?(nil) } catch let error as NSError { print("error writing file to URL") completionHandler?(error) } } override func itemChanged(at url: URL) { // Called at some point after the file has changed; the provider may then trigger an upload let fileSize = (try! FileManager.default.attributesOfItem(atPath: url.path)[FileAttributeKey.size] as! NSNumber).uint64Value NSLog("[LOG] Item changed at URL %@ %lu", url as NSURL, fileSize) guard let account = NCManageDatabase.sharedInstance.getAccountActive() else { self.stopProvidingItem(at: url) return } guard let fileName = CCUtility.getFileNameExt() else { self.stopProvidingItem(at: url) return } // -------> Fix : Clear FileName for twice Office 365 CCUtility.setFileNameExt("") // -------------------------------------------------- if (fileName != url.lastPathComponent) { self.stopProvidingItem(at: url) return } guard let serverUrl = CCUtility.getServerUrlExt() else { self.stopProvidingItem(at: url) return } guard let directoryID = NCManageDatabase.sharedInstance.getDirectoryID(serverUrl) else { self.stopProvidingItem(at: url) return } let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "fileName == %@ AND directoryID == %@", fileName, directoryID)) if metadata != nil { // Update let uploadID = k_uploadSessionID + CCUtility.createRandomString(16) let directoryUser = CCUtility.getDirectoryActiveUser(account.user, activeUrl: account.url) let destinationDirectoryUser = "\(directoryUser!)/\(uploadID)" // copy sourceURL on directoryUser do { try FileManager.default.removeItem(atPath: destinationDirectoryUser) } catch _ { print("file do not exists") } do { try FileManager.default.copyItem(atPath: url.path, toPath: destinationDirectoryUser) } catch _ { print("file do not exists") self.stopProvidingItem(at: url) return } // Prepare for send Metadata metadata!.sessionID = uploadID metadata!.session = k_upload_session metadata!.sessionTaskIdentifier = Int(k_taskIdentifierWaitStart) _ = NCManageDatabase.sharedInstance.updateMetadata(metadata!) } else { // New let directoryUser = CCUtility.getDirectoryActiveUser(account.user, activeUrl: account.url) let destinationDirectoryUser = "\(directoryUser!)/\(fileName)" do { try FileManager.default.removeItem(atPath: destinationDirectoryUser) } catch _ { print("file do not exists") } do { try FileManager.default.copyItem(atPath: url.path, toPath: destinationDirectoryUser) } catch _ { print("file do not exists") self.stopProvidingItem(at: url) return } CCNetworking.shared().uploadFile(fileName, serverUrl: serverUrl, session: k_upload_session, taskStatus: Int(k_taskStatusResume), selector: nil, selectorPost: nil, errorCode: 0, delegate: self) } self.stopProvidingItem(at: url) } override func stopProvidingItem(at url: URL) { // Called after the last claim to the file has been released. At this point, it is safe for the file provider to remove the content file. // Care should be taken that the corresponding placeholder file stays behind after the content file has been deleted. do { _ = try FileManager.default.removeItem(at: url) } catch { // Handle error } self.providePlaceholder(at: url, completionHandler: { error in // TODO: handle any error, do any necessary cleanup }) } // UTILITY // func appGroupContainerURL() -> URL? { guard let groupURL = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.sharedInstance.capabilitiesGroups) else { return nil } let storagePathUrl = groupURL.appendingPathComponent("File Provider Storage") let storagePath = storagePathUrl.path if !FileManager.default.fileExists(atPath: storagePath) { do { try FileManager.default.createDirectory(atPath: storagePath, withIntermediateDirectories: false, attributes: nil) } catch let error { print("error creating filepath: \(error)") return nil } } return storagePathUrl } }