Browse Source

Move "Files" to "Develop"

Marino Faggiana 7 years ago
parent
commit
6254e0f178
100 changed files with 1035 additions and 161 deletions
  1. 18 0
      Nextcloud.xcodeproj/project.pbxproj
  2. 423 148
      PickerFileProvider/FileProvider.swift
  3. 157 0
      PickerFileProvider/FileProviderEnumerator.swift
  4. 105 0
      PickerFileProvider/FileProviderItem.swift
  5. 216 0
      PickerFileProvider/OLDFileProvider.swift
  6. 2 2
      iOSClient/Brand/Picker.plist
  7. 6 2
      iOSClient/Brand/PickerFileProvider.plist
  8. 2 2
      iOSClient/Brand/Share.plist
  9. 2 2
      iOSClient/Brand/iOSClient.plist
  10. 3 1
      iOSClient/Networking/OCNetworking.h
  11. 101 4
      iOSClient/Networking/OCNetworking.m
  12. BIN
      iOSClient/Supporting Files/es-419.lproj/BKPasscodeView.strings
  13. BIN
      iOSClient/Supporting Files/es-419.lproj/CTAssetsPicker.strings
  14. BIN
      iOSClient/Supporting Files/es-419.lproj/Error.strings
  15. BIN
      iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings
  16. BIN
      iOSClient/Supporting Files/es-419.lproj/Intro.strings
  17. BIN
      iOSClient/Supporting Files/es-419.lproj/Localizable.strings
  18. BIN
      iOSClient/Supporting Files/es-419.lproj/SwiftWebVC.strings
  19. BIN
      iOSClient/Supporting Files/es-CO.lproj/BKPasscodeView.strings
  20. BIN
      iOSClient/Supporting Files/es-CO.lproj/CTAssetsPicker.strings
  21. BIN
      iOSClient/Supporting Files/es-CO.lproj/Error.strings
  22. BIN
      iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings
  23. BIN
      iOSClient/Supporting Files/es-CO.lproj/Intro.strings
  24. BIN
      iOSClient/Supporting Files/es-CO.lproj/Localizable.strings
  25. BIN
      iOSClient/Supporting Files/es-CO.lproj/SwiftWebVC.strings
  26. BIN
      iOSClient/Supporting Files/es-DO.lproj/BKPasscodeView.strings
  27. BIN
      iOSClient/Supporting Files/es-DO.lproj/CTAssetsPicker.strings
  28. BIN
      iOSClient/Supporting Files/es-DO.lproj/Error.strings
  29. BIN
      iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings
  30. BIN
      iOSClient/Supporting Files/es-DO.lproj/Intro.strings
  31. BIN
      iOSClient/Supporting Files/es-DO.lproj/Localizable.strings
  32. BIN
      iOSClient/Supporting Files/es-DO.lproj/SwiftWebVC.strings
  33. BIN
      iOSClient/Supporting Files/es-GT.lproj/BKPasscodeView.strings
  34. BIN
      iOSClient/Supporting Files/es-GT.lproj/CTAssetsPicker.strings
  35. BIN
      iOSClient/Supporting Files/es-GT.lproj/Error.strings
  36. BIN
      iOSClient/Supporting Files/es-GT.lproj/InfoPlist.strings
  37. BIN
      iOSClient/Supporting Files/es-GT.lproj/Intro.strings
  38. BIN
      iOSClient/Supporting Files/es-GT.lproj/Localizable.strings
  39. BIN
      iOSClient/Supporting Files/es-GT.lproj/SwiftWebVC.strings
  40. BIN
      iOSClient/Supporting Files/es-NI.lproj/BKPasscodeView.strings
  41. BIN
      iOSClient/Supporting Files/es-NI.lproj/CTAssetsPicker.strings
  42. BIN
      iOSClient/Supporting Files/es-NI.lproj/Error.strings
  43. BIN
      iOSClient/Supporting Files/es-NI.lproj/InfoPlist.strings
  44. BIN
      iOSClient/Supporting Files/es-NI.lproj/Intro.strings
  45. BIN
      iOSClient/Supporting Files/es-NI.lproj/Localizable.strings
  46. BIN
      iOSClient/Supporting Files/es-NI.lproj/SwiftWebVC.strings
  47. BIN
      iOSClient/Supporting Files/es-PA.lproj/BKPasscodeView.strings
  48. BIN
      iOSClient/Supporting Files/es-PA.lproj/CTAssetsPicker.strings
  49. BIN
      iOSClient/Supporting Files/es-PA.lproj/Error.strings
  50. BIN
      iOSClient/Supporting Files/es-PA.lproj/InfoPlist.strings
  51. BIN
      iOSClient/Supporting Files/es-PA.lproj/Intro.strings
  52. BIN
      iOSClient/Supporting Files/es-PA.lproj/Localizable.strings
  53. BIN
      iOSClient/Supporting Files/es-PA.lproj/SwiftWebVC.strings
  54. BIN
      iOSClient/Supporting Files/es-PR.lproj/BKPasscodeView.strings
  55. BIN
      iOSClient/Supporting Files/es-PR.lproj/CTAssetsPicker.strings
  56. BIN
      iOSClient/Supporting Files/es-PR.lproj/Error.strings
  57. BIN
      iOSClient/Supporting Files/es-PR.lproj/InfoPlist.strings
  58. BIN
      iOSClient/Supporting Files/es-PR.lproj/Intro.strings
  59. BIN
      iOSClient/Supporting Files/es-PR.lproj/Localizable.strings
  60. BIN
      iOSClient/Supporting Files/es-PR.lproj/SwiftWebVC.strings
  61. BIN
      iOSClient/Supporting Files/es-PY.lproj/BKPasscodeView.strings
  62. BIN
      iOSClient/Supporting Files/es-PY.lproj/CTAssetsPicker.strings
  63. BIN
      iOSClient/Supporting Files/es-PY.lproj/Error.strings
  64. BIN
      iOSClient/Supporting Files/es-PY.lproj/InfoPlist.strings
  65. BIN
      iOSClient/Supporting Files/es-PY.lproj/Intro.strings
  66. BIN
      iOSClient/Supporting Files/es-PY.lproj/Localizable.strings
  67. BIN
      iOSClient/Supporting Files/es-PY.lproj/SwiftWebVC.strings
  68. BIN
      iOSClient/Supporting Files/es-UY.lproj/BKPasscodeView.strings
  69. BIN
      iOSClient/Supporting Files/es-UY.lproj/CTAssetsPicker.strings
  70. BIN
      iOSClient/Supporting Files/es-UY.lproj/Error.strings
  71. BIN
      iOSClient/Supporting Files/es-UY.lproj/InfoPlist.strings
  72. BIN
      iOSClient/Supporting Files/es-UY.lproj/Intro.strings
  73. BIN
      iOSClient/Supporting Files/es-UY.lproj/Localizable.strings
  74. BIN
      iOSClient/Supporting Files/es-UY.lproj/SwiftWebVC.strings
  75. BIN
      iOSClient/Supporting Files/is.lproj/BKPasscodeView.strings
  76. BIN
      iOSClient/Supporting Files/is.lproj/CTAssetsPicker.strings
  77. BIN
      iOSClient/Supporting Files/is.lproj/Error.strings
  78. BIN
      iOSClient/Supporting Files/is.lproj/InfoPlist.strings
  79. BIN
      iOSClient/Supporting Files/is.lproj/Intro.strings
  80. BIN
      iOSClient/Supporting Files/is.lproj/Localizable.strings
  81. BIN
      iOSClient/Supporting Files/is.lproj/SwiftWebVC.strings
  82. BIN
      iOSClient/Supporting Files/sv.lproj/BKPasscodeView.strings
  83. BIN
      iOSClient/Supporting Files/sv.lproj/CTAssetsPicker.strings
  84. BIN
      iOSClient/Supporting Files/sv.lproj/Error.strings
  85. BIN
      iOSClient/Supporting Files/sv.lproj/InfoPlist.strings
  86. BIN
      iOSClient/Supporting Files/sv.lproj/Intro.strings
  87. BIN
      iOSClient/Supporting Files/sv.lproj/Localizable.strings
  88. BIN
      iOSClient/Supporting Files/sv.lproj/SwiftWebVC.strings
  89. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/BKPasscodeView.strings
  90. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/CTAssetsPicker.strings
  91. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/Error.strings
  92. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/InfoPlist.strings
  93. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/Intro.strings
  94. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings
  95. BIN
      iOSClient/Supporting Files/zh-Hans.lproj/SwiftWebVC.strings
  96. BIN
      iOSClient/Supporting Files/zh-Hant-TW.lproj/BKPasscodeView.strings
  97. BIN
      iOSClient/Supporting Files/zh-Hant-TW.lproj/CTAssetsPicker.strings
  98. BIN
      iOSClient/Supporting Files/zh-Hant-TW.lproj/Error.strings
  99. BIN
      iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings
  100. BIN
      iOSClient/Supporting Files/zh-Hant-TW.lproj/Intro.strings

+ 18 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -225,6 +225,8 @@
 		F743448A1E127FF2001CC831 /* CCHud.m in Sources */ = {isa = PBXBuildFile; fileRef = F7514EDB1C7B1336008F3338 /* CCHud.m */; };
 		F74344921E128EB0001CC831 /* Picker.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F74344901E128E8F001CC831 /* Picker.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		F74344931E128EB4001CC831 /* PickerFileProvider.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F74344911E128E96001CC831 /* PickerFileProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+		F7496B7D208F548E004B299C /* FileProviderEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7496B7B208F548E004B299C /* FileProviderEnumerator.swift */; };
+		F7496B7E208F548E004B299C /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7496B7C208F548E004B299C /* FileProviderItem.swift */; };
 		F749E4E91DC1FB38009BA2FD /* Share.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		F750374D1DBFA91A008FB480 /* ALView+PureLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = F75037441DBFA91A008FB480 /* ALView+PureLayout.m */; };
 		F750374F1DBFA91A008FB480 /* NSArray+PureLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = F75037461DBFA91A008FB480 /* NSArray+PureLayout.m */; };
@@ -919,6 +921,13 @@
 		F74344911E128E96001CC831 /* PickerFileProvider.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PickerFileProvider.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		F743B2C31C95BBE8006F5B4A /* CCShareInfoCMOC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCShareInfoCMOC.h; sourceTree = "<group>"; };
 		F743B2C41C95BBE8006F5B4A /* CCShareInfoCMOC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCShareInfoCMOC.m; sourceTree = "<group>"; };
+		F7496B7B208F548E004B299C /* FileProviderEnumerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileProviderEnumerator.swift; sourceTree = "<group>"; };
+		F7496B7C208F548E004B299C /* FileProviderItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileProviderItem.swift; sourceTree = "<group>"; };
+		F7496B7F208F55F5004B299C /* OLDFileProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OLDFileProvider.swift; sourceTree = "<group>"; };
+		F7496B80208F5651004B299C /* Picker.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Picker.plist; sourceTree = "<group>"; };
+		F7496B81208F5651004B299C /* iOSClient.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = iOSClient.plist; sourceTree = "<group>"; };
+		F7496B82208F5652004B299C /* PickerFileProvider.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PickerFileProvider.plist; sourceTree = "<group>"; };
+		F7496B83208F5652004B299C /* Share.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Share.plist; sourceTree = "<group>"; };
 		F74D3DBD1BAC1941000BAE4B /* OCNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCNetworking.h; sourceTree = "<group>"; };
 		F74D3DBE1BAC1941000BAE4B /* OCNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCNetworking.m; sourceTree = "<group>"; };
 		F75037431DBFA91A008FB480 /* ALView+PureLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ALView+PureLayout.h"; sourceTree = "<group>"; };
@@ -2151,7 +2160,10 @@
 			isa = PBXGroup;
 			children = (
 				F7F54CAB1E5AFF1E00E19C62 /* PickerFileProvider-Bridging-Header.h */,
+				F7496B7B208F548E004B299C /* FileProviderEnumerator.swift */,
+				F7496B7C208F548E004B299C /* FileProviderItem.swift */,
 				F74344241E1264EE001CC831 /* FileProvider.swift */,
+				F7496B7F208F55F5004B299C /* OLDFileProvider.swift */,
 			);
 			path = PickerFileProvider;
 			sourceTree = "<group>";
@@ -2772,6 +2784,10 @@
 				F7C742E61E7BD71A00D9C973 /* PickerFileProvider.entitlements */,
 				F7C742D01E7BD35B00D9C973 /* Share.entitlements */,
 				F7C742D41E7BD44400D9C973 /* Picker.entitlements */,
+				F7496B81208F5651004B299C /* iOSClient.plist */,
+				F7496B80208F5651004B299C /* Picker.plist */,
+				F7496B82208F5652004B299C /* PickerFileProvider.plist */,
+				F7496B83208F5652004B299C /* Share.plist */,
 			);
 			name = "Supporting Files";
 			sourceTree = "<group>";
@@ -3755,6 +3771,7 @@
 				F7BB50E41F2238D500C47094 /* AFURLSessionManager.m in Sources */,
 				F7BB50F91F2239C800C47094 /* BKShiftingView.m in Sources */,
 				F7BB50F21F22395D00C47094 /* UICKeyChainStore.m in Sources */,
+				F7496B7E208F548E004B299C /* FileProviderItem.swift in Sources */,
 				F7BB50D01F22388600C47094 /* OCRichObjectStrings.m in Sources */,
 				F7BB50C91F22386400C47094 /* OCActivity.m in Sources */,
 				F7BB50AF1F22370300C47094 /* NCManageDatabase.swift in Sources */,
@@ -3762,6 +3779,7 @@
 				F7BB50BB1F22374E00C47094 /* OCNetworking.m in Sources */,
 				F7BB50C11F22378E00C47094 /* CCHud.m in Sources */,
 				F7BB50DE1F2238B700C47094 /* UtilsFramework.m in Sources */,
+				F7496B7D208F548E004B299C /* FileProviderEnumerator.swift in Sources */,
 				F7BB50D61F22389900C47094 /* OCHTTPRequestOperation.m in Sources */,
 				F75131661FA5EFF4002BB4D1 /* NCNetworkingSync.m in Sources */,
 				F7BB50BD1F22376000C47094 /* CCCertificate.m in Sources */,

+ 423 - 148
PickerFileProvider/FileProvider.swift

@@ -1,216 +1,491 @@
 //
-//  FileProvider.swift
-//  PickerFileProvider
+//  FileProviderExtension.swift
+//  Files
 //
-//  Created by Marino Faggiana on 27/12/16.
-//  Copyright © 2017 TWS. All rights reserved.
-//
-//  Author Marino Faggiana <m.faggiana@twsweb.it>
-//
-//  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 <http://www.gnu.org/licenses/>.
+//  Created by Marino Faggiana on 26/03/18.
+//  Copyright © 2018 TWS. All rights reserved.
 //
 
+import FileProvider
 import UIKit
+import MobileCoreServices
+
+var ocNetworking: OCnetworking?
+var account = ""
+var accountUser = ""
+var accountUserID = ""
+var accountPassword = ""
+var accountUrl = ""
+var homeServerUrl = ""
+var directoryUser = ""
+var groupURL: URL?
 
-class FileProvider: NSFileProviderExtension, CCNetworkingDelegate {
+@available(iOSApplicationExtension 11.0, *)
 
-    lazy var networkingOperationQueue: OperationQueue = {
+class FileProvider: NSFileProviderExtension {
+    
+    var uploading = [String]()
+    
+    override init() {
+        
+        super.init()
         
-        var queue = OperationQueue()
-        queue.name = k_queue
-        queue.maxConcurrentOperationCount = 10
+        guard let activeAccount = NCManageDatabase.sharedInstance.getAccountActive() else {
+            return
+        }
         
-        return queue
-    }()
+        account = activeAccount.account
+        accountUser = activeAccount.user
+        accountUserID = activeAccount.userID
+        accountPassword = activeAccount.password
+        accountUrl = activeAccount.url
+        homeServerUrl = CCUtility.getHomeServerUrlActiveUrl(activeAccount.url)
+        directoryUser = CCUtility.getDirectoryActiveUser(activeAccount.user, activeUrl: activeAccount.url)
 
-    var fileCoordinator: NSFileCoordinator {
-        let fileCoordinator = NSFileCoordinator()
-        fileCoordinator.purposeIdentifier = self.providerIdentifier
-        return fileCoordinator
+        ocNetworking = OCnetworking.init(delegate: nil, metadataNet: nil, withUser: accountUser, withUserID: accountUserID, withPassword: accountPassword, withUrl: accountUrl)
+        
+        groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.sharedInstance.capabilitiesGroups)
     }
     
-    override init() {
-        super.init()
+    override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
         
-        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
+        if identifier == .rootContainer {
+            
+            if let directory = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND serverUrl = %@", account, homeServerUrl)) {
+                    
+                let metadata = tableMetadata()
+                    
+                metadata.account = account
+                metadata.directory = true
+                metadata.directoryID = directory.directoryID
+                metadata.fileID = identifier.rawValue
+                metadata.fileName = NCBrandOptions.sharedInstance.brand
+                metadata.fileNameView = NCBrandOptions.sharedInstance.brand
+                metadata.typeFile = k_metadataTypeFile_directory
+                    
+                return FileProviderItem(metadata: metadata, serverUrl: homeServerUrl)
             }
-        })
+            
+        } else {
+        
+            if let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, identifier.rawValue))  {
+                if let directory = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND directoryID = %@", account, metadata.directoryID)) {
+                    
+                    if (!metadata.directory) {
+                        
+                        let identifierPathUrl = groupURL!.appendingPathComponent("File Provider Storage").appendingPathComponent(metadata.fileID)
+                        let toPath = "\(identifierPathUrl.path)/\(metadata.fileNameView)"
+                        let atPath = "\(directoryUser)/\(metadata.fileID)"
+                        
+                        if !FileManager.default.fileExists(atPath: toPath) {
+
+                            try? FileManager.default.createDirectory(atPath: identifierPathUrl.path, withIntermediateDirectories: true, attributes: nil)
+    
+                            if FileManager.default.fileExists(atPath: atPath) {
+                                try? FileManager.default.copyItem(atPath: atPath, toPath: toPath)
+                            } else {
+                                FileManager.default.createFile(atPath: toPath, contents: nil, attributes: nil)
+                            }
+                        }
+                    }
+                
+                    return FileProviderItem(metadata: metadata, serverUrl: directory.serverUrl)
+                }
+            }
+        }
+        // TODO: implement the actual lookup
+        throw NSFileProviderError(.noSuchItem)
     }
     
-    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
+    override func urlForItem(withPersistentIdentifier identifier: NSFileProviderItemIdentifier) -> URL? {
+        
+        // resolve the given identifier to a file on disk
+                
+        guard let item = try? item(for: identifier) else {
+            return nil
+        }
         
-        let placeholderURL = NSFileProviderExtension.placeholderURL(for: self.documentStorageURL.appendingPathComponent(fileName))
+        // in this implementation, all paths are structured as <base storage directory>/<item identifier>/<item file name>
         
-        // TODO: get file size for file at <url> 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
+        let manager = NSFileProviderManager.default
+        var url = manager.documentStorageURL.appendingPathComponent(identifier.rawValue, isDirectory: true)
+        
+        if item.typeIdentifier == (kUTTypeFolder as String) {
+            url = url.appendingPathComponent(item.filename, isDirectory:true)
+        } else {
+            url = url.appendingPathComponent(item.filename, isDirectory:false)
         }
         
-        completionHandler?(nil)
+        return url
     }
     
-    override func startProvidingItem(at url: URL, completionHandler: ((_ error: Error?) -> Void)?) {
+    override func persistentIdentifierForItem(at url: URL) -> NSFileProviderItemIdentifier? {
+        // resolve the given URL to a persistent identifier using a database
+        let pathComponents = url.pathComponents
+        
+        // exploit the fact that the path structure has been defined as
+        // <base storage directory>/<item identifier>/<item file name> above
+        assert(pathComponents.count > 2)
         
-        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)
+        let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
+        return itemIdentifier
+    }
+    
+    override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) {
+        guard let identifier = persistentIdentifierForItem(at: url) else {
+            completionHandler(NSFileProviderError(.noSuchItem))
             return
         }
+
+        do {
+            let fileProviderItem = try item(for: identifier)
+            let placeholderURL = NSFileProviderManager.placeholderURL(for: url)
+            try NSFileProviderManager.writePlaceholder(at: placeholderURL,withMetadata: fileProviderItem)
+            completionHandler(nil)
+        } catch let error {
+            completionHandler(error)
+        }
+    }
+
+    override func startProvidingItem(at url: URL, completionHandler: @escaping ((_ error: Error?) -> Void)) {
+        
+        var fileSize : UInt64 = 0
         
         do {
-            _ = try fileData.write(to: url, options: NSData.WritingOptions())
-            completionHandler?(nil)
-        } catch let error as NSError {
-            print("error writing file to URL")
-            completionHandler?(error)
+            let attr = try FileManager.default.attributesOfItem(atPath: url.path)
+            fileSize = attr[FileAttributeKey.size] as! UInt64
+        } catch {
+            print("Error: \(error)")
+            completionHandler(NSFileProviderError(.noSuchItem))
+        }
+            
+        // Do not exists
+        if fileSize == 0 {
+                
+            let pathComponents = url.pathComponents
+            let identifier = pathComponents[pathComponents.count - 2]
+            
+            guard let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, identifier)) else {
+                completionHandler(NSFileProviderError(.noSuchItem))
+                return
+            }
+                
+            guard let directory = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND directoryID = %@", account, metadata.directoryID)) else {
+                completionHandler(NSFileProviderError(.noSuchItem))
+                return
+            }
+            
+            _ = ocNetworking?.downloadFileNameServerUrl("\(directory.serverUrl)/\(metadata.fileName)", fileNameLocalPath: "\(directoryUser)/\(metadata.fileID)", success: { (lenght) in
+                
+                if (lenght > 0) {
+                    
+                    // copy download file to url
+                    try? FileManager.default.removeItem(atPath: url.path)
+                    try? FileManager.default.copyItem(atPath: "\(directoryUser)/\(metadata.fileID)", toPath: url.path)
+                    // create thumbnail
+                    CCGraphics.createNewImage(from: metadata.fileID, directoryUser: directoryUser, fileNameTo: metadata.fileID, extension: (metadata.fileNameView as NSString).pathExtension, size: "m", imageForUpload: false, typeFile: metadata.typeFile, writePreview: true, optimizedFileName: CCUtility.getOptimizedPhoto())
+                
+                    NCManageDatabase.sharedInstance.addLocalFile(metadata: metadata)
+                    if (metadata.typeFile == k_metadataTypeFile_image) {
+                        CCExifGeo.sharedInstance().setExifLocalTableEtag(metadata, directoryUser: directoryUser, activeAccount: account)
+                    }
+                }
+                
+                completionHandler(nil)
+                    
+            }, failure: { (message, errorCode) in
+                completionHandler(NSFileProviderError(.serverUnreachable))
+            })
+                
+        } else {
+                
+            // Exists
+            completionHandler(nil)
         }
     }
     
     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)
+        if (fileSize == 0) {
             return
         }
-        guard let fileName = CCUtility.getFileNameExt() else {
-            self.stopProvidingItem(at: url)
+        
+        let fileName = url.lastPathComponent
+        let pathComponents = url.pathComponents
+        assert(pathComponents.count > 2)
+        let identifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
+
+        if (uploading.contains(identifier.rawValue) == true) {
             return
         }
-        // -------> Fix : Clear FileName for twice Office 365
-        CCUtility.setFileNameExt("")
-        // --------------------------------------------------
-        if (fileName != url.lastPathComponent) {
-            self.stopProvidingItem(at: url)
+        
+        if let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, identifier.rawValue))  {
+            
+            guard let serverUrl = NCManageDatabase.sharedInstance.getServerUrl(metadata.directoryID) else {
+                return
+            }
+            
+            uploading.append(identifier.rawValue)
+            
+            _ =  ocNetworking?.uploadFileNameServerUrl(serverUrl+"/"+fileName, fileNameLocalPath: url.path, success: { (fileID, etag, date) in
+                
+                let toPath = "\(directoryUser)/\(metadata.fileID)"
+
+                try? FileManager.default.removeItem(atPath: toPath)
+                try? FileManager.default.copyItem(atPath:  url.path, toPath: toPath)
+                // create thumbnail
+                CCGraphics.createNewImage(from: metadata.fileID, directoryUser: directoryUser, fileNameTo: metadata.fileID, extension: (metadata.fileNameView as NSString).pathExtension, size: "m", imageForUpload: false, typeFile: metadata.typeFile, writePreview: true, optimizedFileName: CCUtility.getOptimizedPhoto())
+                
+                metadata.date = date! as NSDate
+              
+                do {
+                    let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
+                    metadata.size = attributes[FileAttributeKey.size] as! Double
+                } catch {
+                }
+                
+                guard let metadataDB = NCManageDatabase.sharedInstance.addMetadata(metadata) else {
+                    return
+                }
+                
+                // item
+                _ = FileProviderItem(metadata: metadataDB, serverUrl: serverUrl)
+                
+                self.uploading = self.uploading.filter() { $0 != identifier.rawValue }
+        
+            }, failure: { (message, errorCode) in
+                
+                self.uploading = self.uploading.filter() { $0 != identifier.rawValue }
+            })
+        }
+    }
+    
+    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.
+        
+        // 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.
+        
+        // TODO: look up whether the file has local changes
+        let fileHasLocalChanges = false
+        
+        if !fileHasLocalChanges {
+            // remove the existing file to free up space
+            do {
+                _ = try FileManager.default.removeItem(at: url)
+            } catch {
+                // Handle error
+            }
+            
+            // write out a placeholder to facilitate future property lookups
+            self.providePlaceholder(at: url, completionHandler: { error in
+                // TODO: handle any error, do any necessary cleanup
+            })
+        }
+    }
+    
+    // MARK: - Actions
+    
+    /* TODO: implement the actions for items here
+     each of the actions follows the same pattern:
+     - make a note of the change in the local model
+     - schedule a server request as a background task to inform the server of the change
+     - call the completion block with the modified item in its post-modification state
+     */
+    
+    // MARK: - Enumeration
+    
+    override func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {
+        
+        let maybeEnumerator = FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier)
+        
+        return maybeEnumerator
+    }
+    
+    override func fetchThumbnails(for itemIdentifiers: [NSFileProviderItemIdentifier], requestedSize size: CGSize, perThumbnailCompletionHandler: @escaping (NSFileProviderItemIdentifier, Data?, Error?) -> Void, completionHandler: @escaping (Error?) -> Void) -> Progress {
+        
+        let progress = Progress(totalUnitCount: Int64(itemIdentifiers.count))
+        var counterProgress: Int64 = 0
+        
+        for item in itemIdentifiers {
+            
+            if let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, item.rawValue))  {
+                
+                if (metadata.typeFile == k_metadataTypeFile_image || metadata.typeFile == k_metadataTypeFile_video) {
+                    
+                    let serverUrl = NCManageDatabase.sharedInstance.getServerUrl(metadata.directoryID)
+                    let fileName = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: serverUrl, activeUrl: accountUrl)
+                    let fileNameLocal = metadata.fileID
+
+                    ocNetworking?.downloadThumbnail(withDimOfThumbnail: "m", fileName: fileName, fileNameLocal: fileNameLocal, success: {
+
+                        do {
+                            let url = URL.init(fileURLWithPath: "\(directoryUser)/\(item.rawValue).ico")
+                            let data = try Data.init(contentsOf: url)
+                            perThumbnailCompletionHandler(item, data, nil)
+                        } catch {
+                            perThumbnailCompletionHandler(item, nil, NSFileProviderError(.noSuchItem))
+                        }
+                        
+                        counterProgress += 1
+                        if (counterProgress == progress.totalUnitCount) {
+                            completionHandler(nil)
+                        }
+                        
+                    }, failure: { (message, errorCode) in
+
+                        perThumbnailCompletionHandler(item, nil, NSFileProviderError(.serverUnreachable))
+                        
+                        counterProgress += 1
+                        if (counterProgress == progress.totalUnitCount) {
+                            completionHandler(nil)
+                        }
+                    })
+                    
+                } else {
+                    
+                    counterProgress += 1
+                    if (counterProgress == progress.totalUnitCount) {
+                        completionHandler(nil)
+                    }
+                }
+            } else {
+                counterProgress += 1
+                if (counterProgress == progress.totalUnitCount) {
+                    completionHandler(nil)
+                }
+            }
+        }
+        
+        return progress
+    }
+    
+    override func createDirectory(withName directoryName: String, inParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
+
+        guard let directoryParent = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, parentItemIdentifier.rawValue)) else {
+            completionHandler(nil, NSFileProviderError(.noSuchItem))
             return
         }
-        guard let serverUrl = CCUtility.getServerUrlExt() else {
-            self.stopProvidingItem(at: url)
+        
+        ocNetworking?.createFolder(directoryName, serverUrl: directoryParent.serverUrl, account: account, success: { (fileID, date) in
+            
+            guard let newTableDirectory = NCManageDatabase.sharedInstance.addDirectory(encrypted: false, favorite: false, fileID: fileID, permissions: nil, serverUrl: directoryParent.serverUrl+"/"+directoryName) else {
+                completionHandler(nil, NSFileProviderError(.noSuchItem))
+                return
+            }
+            
+            let metadata = tableMetadata()
+            
+            metadata.account = account
+            metadata.directory = true
+            metadata.directoryID = newTableDirectory.directoryID
+            metadata.fileID = fileID!
+            metadata.fileName = directoryName
+            metadata.fileNameView = directoryName
+            metadata.typeFile = k_metadataTypeFile_directory
+                        
+            let item = FileProviderItem(metadata: metadata, serverUrl: directoryParent.serverUrl)
+            
+            completionHandler(item, nil)
+            
+        }, failure: { (message, errorCode) in
+            completionHandler(nil, NSFileProviderError(.serverUnreachable))
+        })
+    }
+    
+    override func importDocument(at fileURL: URL, toParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
+        
+        let fileName = fileURL.lastPathComponent
+        let fileCoordinator = NSFileCoordinator()
+        var error: NSError?
+        var directoryPredicate: NSPredicate
+        
+        if parentItemIdentifier == .rootContainer {
+            directoryPredicate = NSPredicate(format: "account = %@ AND serverUrl = %@", account, homeServerUrl)
+        } else {
+            directoryPredicate = NSPredicate(format: "account = %@ AND fileID = %@", account, parentItemIdentifier.rawValue)
+        }
+        
+        guard let directoryParent = NCManageDatabase.sharedInstance.getTableDirectory(predicate: directoryPredicate) else {
+            completionHandler(nil, NSFileProviderError(.noSuchItem))
             return
         }
-        guard let directoryID = NCManageDatabase.sharedInstance.getDirectoryID(serverUrl) else {
-            self.stopProvidingItem(at: url)
+        
+        if fileURL.startAccessingSecurityScopedResource() == false {
+            completionHandler(nil, NSFileProviderError(.noSuchItem))
             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)"
+        let fileNameLocalPath = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileURL.lastPathComponent)!
+        
+        fileCoordinator.coordinate(readingItemAt: fileURL, options: NSFileCoordinator.ReadingOptions.withoutChanges, error: &error) { (url) in
             
-            // copy sourceURL on directoryUser
             do {
-                try FileManager.default.removeItem(atPath: destinationDirectoryUser)
+                try FileManager.default.removeItem(atPath: fileNameLocalPath.path)
             } catch _ {
                 print("file do not exists")
             }
             
             do {
-                try FileManager.default.copyItem(atPath: url.path, toPath: destinationDirectoryUser)
+                try FileManager.default.copyItem(atPath: url.path, toPath: fileNameLocalPath.path)
             } 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 {
+        fileURL.stopAccessingSecurityScopedResource()
+
+        _ = ocNetworking?.uploadFileNameServerUrl(directoryParent.serverUrl+"/"+fileName, fileNameLocalPath: fileNameLocalPath.path, success: { (fileID, etag, date) in
             
-            // New
-            let directoryUser = CCUtility.getDirectoryActiveUser(account.user, activeUrl: account.url)
-            let destinationDirectoryUser = "\(directoryUser!)/\(fileName)"
+            let metadata = tableMetadata()
             
+            metadata.account = account
+            metadata.date = date! as NSDate
+            metadata.directory = false
+            metadata.directoryID = directoryParent.directoryID
+            metadata.etag = etag!
+            metadata.fileID = fileID!
+            metadata.fileName = fileName
+            metadata.fileNameView = fileName
+
             do {
-                try FileManager.default.removeItem(atPath: destinationDirectoryUser)
-            } catch _ {
-                print("file do not exists")
+                let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
+                metadata.size = attributes[FileAttributeKey.size] as! Double
+            } catch {
             }
-            do {
-                try FileManager.default.copyItem(atPath: url.path, toPath: destinationDirectoryUser)
-            } catch _ {
-                print("file do not exists")
-                self.stopProvidingItem(at: url)
+            
+            CCUtility.insertTypeFileIconName(fileName, metadata: metadata)
+            
+            guard let metadataDB = NCManageDatabase.sharedInstance.addMetadata(metadata) else {
+                completionHandler(nil, NSFileProviderError(.noSuchItem))
                 return
             }
+
+            // Copy on ItemIdentifier path
+            let identifierPathUrl = groupURL!.appendingPathComponent("File Provider Storage").appendingPathComponent(metadata.fileID)
+            let toPath = "\(identifierPathUrl.path)/\(metadata.fileNameView)"
             
-            CCNetworking.shared().uploadFile(fileName, serverUrl: serverUrl, session: k_upload_session, taskStatus: Int(k_taskStatusResume), selector: nil, selectorPost: nil, errorCode: 0, delegate: self)
-        }
+            if !FileManager.default.fileExists(atPath: identifierPathUrl.path) {
+                do {
+                    try FileManager.default.createDirectory(atPath: identifierPathUrl.path, withIntermediateDirectories: true, attributes: nil)
+                } catch let error {
+                    print("error creating filepath: \(error)")
+                }
+            }
+            
+            try? FileManager.default.removeItem(atPath: toPath)
+            try? FileManager.default.copyItem(atPath:  fileNameLocalPath.path, toPath: toPath)
 
-        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
+            // add item
+            let item = FileProviderItem(metadata: metadataDB, serverUrl: directoryParent.serverUrl)
+            
+            completionHandler(item, nil)
+
+        }, failure: { (message, errorCode) in
+            completionHandler(nil, NSFileProviderError(.serverUnreachable))
         })
     }
     
-    // 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
-    }
-
-
 }

+ 157 - 0
PickerFileProvider/FileProviderEnumerator.swift

@@ -0,0 +1,157 @@
+//
+//  FileProviderEnumerator.swift
+//  Files
+//
+//  Created by Marino Faggiana on 26/03/18.
+//  Copyright © 2018 TWS. All rights reserved.
+//
+
+import FileProvider
+
+class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
+    
+    var enumeratedItemIdentifier: NSFileProviderItemIdentifier
+    let recordForPage = 10
+    var serverUrl: String?
+    
+    init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
+        
+        self.enumeratedItemIdentifier = enumeratedItemIdentifier
+        
+        // Select ServerUrl
+        if #available(iOSApplicationExtension 11.0, *) {
+
+            if (enumeratedItemIdentifier == .rootContainer) {
+                serverUrl = homeServerUrl
+            } else {
+                if let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", account, enumeratedItemIdentifier.rawValue))  {
+                    if let directorySource = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND directoryID = %@", account, metadata.directoryID))  {
+                        serverUrl = directorySource.serverUrl + "/" + metadata.fileName
+                    }
+                }
+            }
+        }
+        
+        super.init()
+    }
+
+    func invalidate() {
+        // TODO: perform invalidation of server connection if necessary
+    }
+
+    func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) {
+        
+        var items: [NSFileProviderItemProtocol] = []
+        var metadatas: [tableMetadata]?
+
+        if #available(iOSApplicationExtension 11.0, *) {
+            
+            guard let serverUrl = serverUrl else {
+                observer.finishEnumerating(upTo: nil)
+                return
+            }
+            
+            // Select items from database
+            if let directory = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND serverUrl = %@", account, serverUrl))  {
+                metadatas = NCManageDatabase.sharedInstance.getMetadatas(predicate: NSPredicate(format: "account = %@ AND directoryID = %@", account, directory.directoryID), sorted: "fileName", ascending: true)
+            }
+            
+            // Calculate current page
+            if (page != NSFileProviderPage.initialPageSortedByDate as NSFileProviderPage && page != NSFileProviderPage.initialPageSortedByName as NSFileProviderPage) {
+                
+                var numPage = Int(String(data: page.rawValue, encoding: .utf8)!)!
+                
+                if (metadatas != nil) {
+                    items = self.selectItems(numPage: numPage, account: account, serverUrl: serverUrl, metadatas: metadatas!)
+                    observer.didEnumerate(items)
+                }
+                if (items.count == self.recordForPage) {
+                    numPage += 1
+                    let providerPage = NSFileProviderPage("\(numPage)".data(using: .utf8)!)
+                    observer.finishEnumerating(upTo: providerPage)
+                } else {
+                    observer.finishEnumerating(upTo: nil)
+                }
+                return
+            }
+            
+            // Read Folder
+            ocNetworking?.readFolder(withServerUrl: serverUrl, depth: "1", account: account, success: { (metadatas, metadataFolder, directoryID) in
+                
+                if (metadatas != nil) {
+                    NCManageDatabase.sharedInstance.deleteMetadata(predicate: NSPredicate(format: "account = %@ AND directoryID = %@ AND session = ''", account, directoryID!), clearDateReadDirectoryID: directoryID!)
+                    if let metadataDB = NCManageDatabase.sharedInstance.addMetadatas(metadatas as! [tableMetadata], serverUrl: serverUrl) {
+                        items = self.selectItems(numPage: 0, account: account, serverUrl: serverUrl, metadatas: metadataDB)
+                        observer.didEnumerate(items)
+                    }
+                }
+                if (items.count == self.recordForPage) {
+                    let providerPage = NSFileProviderPage("1".data(using: .utf8)!)
+                    observer.finishEnumerating(upTo: providerPage)
+                } else {
+                    observer.finishEnumerating(upTo: nil)
+                }
+                
+            }, failure: { (message, errorCode) in
+                
+                // select item from database
+                if (metadatas != nil) {
+                    items = self.selectItems(numPage: 0, account: account, serverUrl: serverUrl, metadatas: metadatas!)
+                    observer.didEnumerate(items)
+                }
+                if (items.count == self.recordForPage) {
+                    let providerPage = NSFileProviderPage("1".data(using: .utf8)!)
+                    observer.finishEnumerating(upTo: providerPage)
+                } else {
+                    observer.finishEnumerating(upTo: nil)
+                }
+            })
+            
+        } else {
+            // < iOS 11
+            observer.finishEnumerating(upTo: nil)
+        }
+    }
+    
+    func selectItems(numPage: Int, account: String, serverUrl: String, metadatas: [tableMetadata]) -> [NSFileProviderItemProtocol] {
+        
+        var items: [NSFileProviderItemProtocol] = []
+        let start = numPage * self.recordForPage + 1
+        let stop = start + (self.recordForPage - 1)
+        var counter = 0
+
+        for metadata in metadatas {
+            counter += 1
+            if (counter >= start && counter <= stop) {
+                let item = FileProviderItem(metadata: metadata, serverUrl: serverUrl)
+                items.append(item)
+            }
+        }
+    
+        return items
+    }
+    
+    func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) {
+        /* TODO:
+         - query the server for updates since the passed-in sync anchor
+         
+         If this is an enumerator for the active set:
+         - note the changes in your local database
+         
+         - inform the observer about item deletions and updates (modifications + insertions)
+         - inform the observer when you have finished enumerating up to a subsequent sync anchor
+         */
+        
+        observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
+    }
+    
+    func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) {
+        
+        guard let serverUrl = serverUrl else {
+            return
+        }
+        
+        let anchor = NSFileProviderSyncAnchor(serverUrl.data(using: .utf8)!)
+        completionHandler(anchor)
+    }
+}

+ 105 - 0
PickerFileProvider/FileProviderItem.swift

@@ -0,0 +1,105 @@
+//
+//  FileProviderItem.swift
+//  Files
+//
+//  Created by Marino Faggiana on 26/03/18.
+//  Copyright © 2018 TWS. All rights reserved.
+//
+
+import FileProvider
+
+class FileProviderItem: NSObject, NSFileProviderItem {
+
+    // TODO: implement an initializer to create an item from your extension's backing model
+    // TODO: implement the accessors to return the values from your extension's backing model
+
+    // Providing Required Properties
+    var itemIdentifier: NSFileProviderItemIdentifier                // The item's persistent identifier
+    var filename: String = ""                                       // The item's filename
+    var typeIdentifier: String = ""                                 // The item's uniform type identifiers
+    var capabilities: NSFileProviderItemCapabilities {              // The item's capabilities
+        
+        if (self.isDirectory) {
+            return [ .allowsAddingSubItems, .allowsContentEnumerating, .allowsReading ]
+        } else {
+            return [ .allowsReading ]
+        }
+    }
+    
+    // Managing Content
+    var childItemCount: NSNumber?                                   // The number of items contained by this item
+    var documentSize: NSNumber?                                     // The document's size, in bytes
+
+    // Specifying Content Location
+    var parentItemIdentifier: NSFileProviderItemIdentifier          // The persistent identifier of the item's parent folder
+    var isTrashed: Bool = false                                     // A Boolean value that indicates whether an item is in the trash
+   
+    // Tracking Usage
+    var contentModificationDate: Date?                              // The date the item was last modified
+    var creationDate: Date?                                         // The date the item was created
+    //var lastUsedDate: Date?                                         // The date the item was last used
+
+    // Tracking Versions
+    var versionIdentifier: Data?                                    // A data value used to determine when the item changes
+    var isMostRecentVersionDownloaded: Bool = false                 // A Boolean value that indicates whether the item is the most recent version downloaded from the server
+
+    // Monitoring File Transfers
+    var isUploading: Bool = false                                   // A Boolean value that indicates whether the item is currently uploading to your remote server
+    var isUploaded: Bool = true                                     // A Boolean value that indicates whether the item has been uploaded to your remote server
+    var uploadingError: Error?                                      // An error that occurred while uploading to your remote server
+    
+    var isDownloading: Bool = false                                 // A Boolean value that indicates whether the item is currently downloading from your remote server
+    var isDownloaded: Bool = true                                   // A Boolean value that indicates whether the item has been downloaded from your remote server
+    var downloadingError: Error?                                    // An error that occurred while downloading the item
+
+    var isDirectory = false;
+    
+    init(metadata: tableMetadata, serverUrl: String) {
+        
+        self.contentModificationDate = metadata.date as Date
+        self.creationDate = metadata.date as Date
+        self.documentSize = NSNumber(value: metadata.size)
+        self.filename = metadata.fileNameView
+        self.itemIdentifier = NSFileProviderItemIdentifier("\(metadata.fileID)")
+        self.isDirectory = metadata.directory
+
+        // parentItemIdentifier
+        if #available(iOSApplicationExtension 11.0, *) {
+            
+            self.parentItemIdentifier = NSFileProviderItemIdentifier.rootContainer
+            
+            // NOT .rootContainer
+            if (serverUrl != homeServerUrl) {
+                if let directoryParent = NCManageDatabase.sharedInstance.getTableDirectory(predicate: NSPredicate(format: "account = %@ AND directoryID = %@", metadata.account, metadata.directoryID))  {
+                    if let metadataParent = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account = %@ AND fileID = %@", metadata.account, directoryParent.fileID))  {
+                        self.parentItemIdentifier = NSFileProviderItemIdentifier(metadataParent.fileID)
+                    }
+                }
+            }
+            
+        } else {
+            // < iOS 11
+            self.parentItemIdentifier = NSFileProviderItemIdentifier("")
+        }
+        
+        // typeIdentifier
+        if let fileType = CCUtility.insertTypeFileIconName(metadata.fileNameView, metadata: metadata) {
+            self.typeIdentifier = fileType 
+        }
+        self.versionIdentifier = metadata.etag.data(using: .utf8)
+        
+        // Verify file exists on cache
+        if (!metadata.directory) {
+            
+            let filePath = "\(directoryUser)/\(metadata.fileID)"
+            
+            if FileManager().fileExists(atPath: filePath) {
+                self.isDownloaded = true
+                self.isMostRecentVersionDownloaded = true;
+            } else {
+                self.isDownloaded = false
+                self.isMostRecentVersionDownloaded = false;
+            }
+        }
+    }
+}

+ 216 - 0
PickerFileProvider/OLDFileProvider.swift

@@ -0,0 +1,216 @@
+//
+//  FileProvider.swift
+//  PickerFileProvider
+//
+//  Created by Marino Faggiana on 27/12/16.
+//  Copyright © 2017 TWS. All rights reserved.
+//
+//  Author Marino Faggiana <m.faggiana@twsweb.it>
+//
+//  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 <http://www.gnu.org/licenses/>.
+//
+
+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 <url> 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
+    }
+
+
+}

+ 2 - 2
iOSClient/Brand/Picker.plist

@@ -17,9 +17,9 @@
 	<key>CFBundlePackageType</key>
 	<string>XPC!</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.20.9</string>
+	<string>2.21.0</string>
 	<key>CFBundleVersion</key>
-	<string>00002</string>
+	<string>00001</string>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 6 - 2
iOSClient/Brand/PickerFileProvider.plist

@@ -17,17 +17,21 @@
 	<key>CFBundlePackageType</key>
 	<string>XPC!</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.20.9</string>
+	<string>2.21.0</string>
 	<key>CFBundleVersion</key>
-	<string>00002</string>
+	<string>00001</string>
 	<key>NSExtension</key>
 	<dict>
 		<key>NSExtensionFileProviderDocumentGroup</key>
 		<string>group.it.twsweb.Crypto-Cloud</string>
+		<key>NSExtensionFileProviderSupportsEnumeration</key>
+		<true/>
 		<key>NSExtensionPointIdentifier</key>
 		<string>com.apple.fileprovider-nonui</string>
 		<key>NSExtensionPrincipalClass</key>
 		<string>$(PRODUCT_MODULE_NAME).FileProvider</string>
 	</dict>
+	<key>NSExtensionFileProviderSupportsEnumeration</key>
+	<true/>
 </dict>
 </plist>

+ 2 - 2
iOSClient/Brand/Share.plist

@@ -17,9 +17,9 @@
 	<key>CFBundlePackageType</key>
 	<string>XPC!</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.20.9</string>
+	<string>2.21.0</string>
 	<key>CFBundleVersion</key>
-	<string>00002</string>
+	<string>00001</string>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 2 - 2
iOSClient/Brand/iOSClient.plist

@@ -46,7 +46,7 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.20.9</string>
+	<string>2.21.0</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleURLTypes</key>
@@ -69,7 +69,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>00002</string>
+	<string>00001</string>
 	<key>Fabric</key>
 	<dict>
 		<key>APIKey</key>

+ 3 - 1
iOSClient/Networking/OCNetworking.h

@@ -42,10 +42,12 @@
 @property (nonatomic, assign) BOOL isExecuting;
 @property (nonatomic, assign) BOOL isFinished;
 
-- (void)downloadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void (^)(void))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
+- (NSURLSessionTask *)downloadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void (^)(int64_t length))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
+- (NSURLSessionTask *)uploadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void(^)(NSString *fileID, NSString *etag, NSDate *date))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
 - (void)downloadThumbnailWithDimOfThumbnail:(NSString *)dimOfThumbnail fileName:(NSString *)fileName fileNameLocal:(NSString *)fileNameLocal success:(void (^)(void))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
 - (void)readFolderWithServerUrl:(NSString *)serverUrl depth:(NSString *)depth account:(NSString *)account success:(void(^)(NSArray *metadatas, tableMetadata *metadataFolder, NSString *directoryID))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
 - (void)deleteFileOrFolder:(NSString *)fileName serverUrl:(NSString *)serverUrl success:(void (^)(void))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
+- (void)createFolder:(NSString *)fileName serverUrl:(NSString *)serverUrl account:(NSString *)account success:(void(^)(NSString *fileID, NSDate *date))success failure:(void (^)(NSString *message, NSInteger errorCode))failure;
 
 @end
 

+ 101 - 4
iOSClient/Networking/OCNetworking.m

@@ -132,20 +132,29 @@
 #pragma mark ===== download =====
 #pragma --------------------------------------------------------------------------------------------
 
-- (void)downloadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void (^)(void))success failure:(void (^)(NSString *message, NSInteger errorCode))failure
+- (NSURLSessionTask *)downloadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void (^)(int64_t length))success failure:(void (^)(NSString *message, NSInteger errorCode))failure
 {
     OCCommunication *communication = [CCNetworking sharedNetworking].sharedOCCommunication;
     
     [communication setCredentialsWithUser:_activeUser andUserID:_activeUserID andPassword:_activePassword];
     [communication setUserAgent:[CCUtility getUserAgent]];
     
-    [communication downloadFileSession:fileNameServerUrl toDestiny:fileNameLocalPath defaultPriority:YES onCommunication:communication progress:^(NSProgress *progress) {
+    NSURLSessionTask *sessionTask = [communication downloadFileSession:fileNameServerUrl toDestiny:fileNameLocalPath defaultPriority:YES onCommunication:communication progress:^(NSProgress *progress) {
 
         //float percent = roundf (progress.fractionCompleted * 100);
 
     } successRequest:^(NSURLResponse *response, NSURL *filePath) {
 
-        success();
+        int64_t totalUnitCount = 0;
+        
+        NSDictionary *fields = [(NSHTTPURLResponse*)response allHeaderFields];
+
+        NSString *contentLength = [fields objectForKey:@"Content-Length"];
+        if(contentLength) {
+            totalUnitCount = (int64_t) [contentLength longLongValue];
+        }
+        
+        success(totalUnitCount);
         
     } failureRequest:^(NSURLResponse *response, NSError *error) {
         
@@ -166,12 +175,58 @@
         
         failure(message, errorCode);
     }];
+    
+    return sessionTask;
 }
 
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark ===== upload =====
 #pragma --------------------------------------------------------------------------------------------
 
+- (NSURLSessionTask *)uploadFileNameServerUrl:(NSString *)fileNameServerUrl fileNameLocalPath:(NSString *)fileNameLocalPath success:(void(^)(NSString *fileID, NSString *etag, NSDate *date))success failure:(void (^)(NSString *message, NSInteger errorCode))failure
+{
+    OCCommunication *communication = [CCNetworking sharedNetworking].sharedOCCommunication;
+    
+    [communication setCredentialsWithUser:_activeUser andUserID:_activeUserID andPassword:_activePassword];
+    [communication setUserAgent:[CCUtility getUserAgent]];
+    
+    NSURLSessionTask *sessionTask = [communication uploadFileSession:fileNameLocalPath toDestiny:fileNameServerUrl onCommunication:communication progress:^(NSProgress *progress) {
+        //float percent = roundf (progress.fractionCompleted * 100);
+    } successRequest:^(NSURLResponse *response, NSString *redirectedServer) {
+    
+        NSDictionary *fields = [(NSHTTPURLResponse*)response allHeaderFields];
+
+        NSString *fileID = [CCUtility removeForbiddenCharactersFileSystem:[fields objectForKey:@"OC-FileId"]];
+        NSString *etag = [CCUtility removeForbiddenCharactersFileSystem:[fields objectForKey:@"OC-ETag"]];
+        NSDate *date = [CCUtility dateEnUsPosixFromCloud:[fields objectForKey:@"Date"]];
+        
+        success(fileID, etag, date);
+        
+    } failureRequest:^(NSURLResponse *response, NSString *redirectedServer, NSError *error) {
+        
+        NSString *message;
+        NSInteger errorCode;
+        
+        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
+        errorCode = httpResponse.statusCode;
+        
+        if (errorCode == 0 || (errorCode >= 200 && errorCode < 300))
+            errorCode = error.code;
+        
+        // Error
+        if (errorCode == 503)
+            message = NSLocalizedStringFromTable(@"_server_error_retry_", @"Error", nil);
+        else
+            message = [error.userInfo valueForKey:@"NSLocalizedDescription"];
+        
+        failure(message, errorCode);
+        
+    } failureBeforeRequest:^(NSError *error) {
+        failure(@"", error.code);
+    }];
+    
+    return sessionTask;
+}
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark ===== downloadThumbnail =====
 #pragma --------------------------------------------------------------------------------------------
@@ -848,7 +903,7 @@
         
     } errorBeforeRequest:^(NSError *error) {
         
-         NSString *message;
+        NSString *message;
         
         if (([_metadataNet.fileName isEqualToString:autoUploadFileName] == YES && [_metadataNet.serverUrl isEqualToString:autoUploadDirectory] == YES))
             message = nil;
@@ -873,6 +928,48 @@
     }];
 }
 
+- (void)createFolder:(NSString *)fileName serverUrl:(NSString *)serverUrl account:(NSString *)account success:(void(^)(NSString *fileID, NSDate *date))success failure:(void (^)(NSString *message, NSInteger errorCode))failure
+{
+    OCCommunication *communication = [CCNetworking sharedNetworking].sharedOCCommunication;
+    
+    NSString *serverFileUrl = [NSString stringWithFormat:@"%@/%@", serverUrl, fileName];
+    
+    [communication setCredentialsWithUser:_activeUser andUserID:_activeUserID andPassword:_activePassword];
+    [communication setUserAgent:[CCUtility getUserAgent]];
+    
+    [communication createFolder:serverFileUrl onCommunication:communication withForbiddenCharactersSupported:YES successRequest:^(NSHTTPURLResponse *response, NSString *redirectedServer) {
+
+        if (![[[NCManageDatabase sharedInstance] getAccountActive].account isEqualToString:account]) {
+            
+            failure(NSLocalizedStringFromTable(@"_error_user_not_available_", @"Error", nil), k_CCErrorUserNotAvailble);
+            
+        } else {
+            
+            NSDictionary *fields = [response allHeaderFields];
+            
+            NSString *fileID = [CCUtility removeForbiddenCharactersFileSystem:[fields objectForKey:@"OC-FileId"]];
+            NSDate *date = [CCUtility dateEnUsPosixFromCloud:[fields objectForKey:@"Date"]];
+            
+            success(fileID, date);
+        }
+        
+    } failureRequest:^(NSHTTPURLResponse *response, NSError *error, NSString *redirectedServer) {
+
+        failure(@"", error.code);
+
+    } errorBeforeRequest:^(NSError *error) {
+        
+        NSString *message;
+    
+        if (error.code == OCErrorForbidenCharacters)
+            message = NSLocalizedStringFromTable(@"_forbidden_characters_from_server_", @"Error", nil);
+        else
+            message = NSLocalizedStringFromTable(@"_unknow_response_server_", @"Error", nil);
+        
+        failure(message, error.code);
+    }];
+}
+
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark =====  Delete =====
 #pragma --------------------------------------------------------------------------------------------

BIN
iOSClient/Supporting Files/es-419.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-419.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-419.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-419.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-419.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-419.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/Error.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/Intro.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/is.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/is.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/is.lproj/Error.strings


BIN
iOSClient/Supporting Files/is.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/is.lproj/Intro.strings


BIN
iOSClient/Supporting Files/is.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/is.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/sv.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/sv.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/sv.lproj/Error.strings


BIN
iOSClient/Supporting Files/sv.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/sv.lproj/Intro.strings


BIN
iOSClient/Supporting Files/sv.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/sv.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/Error.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/Intro.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/zh-Hans.lproj/SwiftWebVC.strings


BIN
iOSClient/Supporting Files/zh-Hant-TW.lproj/BKPasscodeView.strings


BIN
iOSClient/Supporting Files/zh-Hant-TW.lproj/CTAssetsPicker.strings


BIN
iOSClient/Supporting Files/zh-Hant-TW.lproj/Error.strings


BIN
iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/zh-Hant-TW.lproj/Intro.strings


Some files were not shown because too many files changed in this diff