NCShareExtension+Files.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //
  2. // NCShareExtension+Files.swift
  3. // Share
  4. //
  5. // Created by Henrik Storch on 29.12.21.
  6. // Copyright © 2021 Henrik Storch. All rights reserved.
  7. //
  8. // Author Henrik Storch <henrik.storch@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import Foundation
  24. import UIKit
  25. import UniformTypeIdentifiers
  26. import NextcloudKit
  27. extension NCShareExtension {
  28. @objc func reloadDatasource(withLoadFolder: Bool) {
  29. let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: keyLayout, serverUrl: serverUrl) ?? NCDBLayoutForView()
  30. let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", session.account, serverUrl)
  31. let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView)
  32. self.dataSource = NCCollectionViewDataSource(metadatas: metadatas)
  33. if withLoadFolder {
  34. loadFolder()
  35. } else {
  36. self.refreshControl.endRefreshing()
  37. }
  38. collectionView.reloadData()
  39. }
  40. @objc func didCreateFolder(_ notification: NSNotification) {
  41. guard let userInfo = notification.userInfo as NSDictionary?,
  42. let ocId = userInfo["ocId"] as? String,
  43. let metadata = self.database.getMetadataFromOcId(ocId)
  44. else { return }
  45. self.serverUrl += "/" + metadata.fileName
  46. self.reloadDatasource(withLoadFolder: true)
  47. self.setNavigationBar(navigationTitle: metadata.fileNameView)
  48. }
  49. func loadFolder() {
  50. NCNetworking.shared.readFolder(serverUrl: serverUrl,
  51. account: session.account,
  52. checkResponseDataChanged: false,
  53. queue: .main) { task in
  54. self.dataSourceTask = task
  55. self.collectionView.reloadData()
  56. } completion: { _, metadataFolder, _, _, error in
  57. DispatchQueue.main.async {
  58. if error != .success {
  59. self.showAlert(description: error.errorDescription)
  60. }
  61. self.metadataFolder = metadataFolder
  62. self.reloadDatasource(withLoadFolder: false)
  63. }
  64. }
  65. }
  66. }
  67. class NCFilesExtensionHandler {
  68. var itemsProvider: [NSItemProvider] = []
  69. lazy var fileNames: [String] = []
  70. let dateFormatter: DateFormatter = {
  71. let formatter = DateFormatter()
  72. formatter.dateFormat = "yyyy-MM-dd HH-mm-ss-"
  73. return formatter
  74. }()
  75. @discardableResult
  76. init(items: [NSExtensionItem], completion: @escaping ([String]) -> Void) {
  77. NCUtilityFileSystem().emptyTemporaryDirectory()
  78. var counter = 0
  79. self.itemsProvider = items.compactMap({ $0.attachments }).flatMap { $0.filter({
  80. $0.hasItemConformingToTypeIdentifier(UTType.item.identifier as String) || $0.hasItemConformingToTypeIdentifier("public.url")
  81. }) }
  82. for (ix, provider) in itemsProvider.enumerated() {
  83. provider.loadItem(forTypeIdentifier: provider.typeIdentifier) { [self] item, error in
  84. defer {
  85. counter += 1
  86. if counter == itemsProvider.count { completion(self.fileNames) }
  87. }
  88. guard error == nil else { return }
  89. var originalName = (dateFormatter.string(from: Date())) + String(ix)
  90. if let url = item as? URL, url.isFileURL, !url.lastPathComponent.isEmpty {
  91. originalName = url.lastPathComponent
  92. if fileNames.contains(originalName) {
  93. let incrementalNumber = NCKeychain().incrementalNumber
  94. originalName = "\(url.deletingPathExtension().lastPathComponent) \(incrementalNumber).\(url.pathExtension)"
  95. }
  96. }
  97. var fileName: String?
  98. switch item {
  99. case let image as UIImage:
  100. fileName = getItem(image: image, fileName: originalName)
  101. case let url as URL:
  102. fileName = getItem(url: url, fileName: originalName)
  103. case let data as Data:
  104. fileName = getItem(data: data, fileName: originalName, provider: provider)
  105. case let text as String:
  106. fileName = getItem(string: text, fileName: originalName)
  107. default: return
  108. }
  109. if let fileName, !fileNames.contains(fileName) {
  110. fileNames.append(fileName)
  111. }
  112. }
  113. }
  114. }
  115. // Image
  116. func getItem(image: UIImage, fileName: String) -> String? {
  117. var fileUrl = URL(fileURLWithPath: NSTemporaryDirectory() + fileName)
  118. if fileUrl.pathExtension.isEmpty { fileUrl.appendPathExtension("png") }
  119. guard let pngImageData = image.pngData(), (try? pngImageData.write(to: fileUrl, options: [.atomic])) != nil
  120. else { return nil }
  121. return fileUrl.lastPathComponent
  122. }
  123. // URL
  124. // Does not work for directories
  125. func getItem(url: URL, fileName: String) -> String? {
  126. var fileName = fileName
  127. guard url.isFileURL else {
  128. guard !fileNames.contains(url.lastPathComponent) else { return nil }
  129. if !url.deletingPathExtension().lastPathComponent.isEmpty { fileName = url.deletingPathExtension().lastPathComponent }
  130. fileName += "." + (url.pathExtension.isEmpty ? "html" : url.pathExtension)
  131. let filenamePath = NSTemporaryDirectory() + fileName
  132. do {
  133. let downloadedContent = try Data(contentsOf: url)
  134. guard !FileManager.default.fileExists(atPath: filenamePath) else { return nil }
  135. try downloadedContent.write(to: URL(fileURLWithPath: filenamePath))
  136. } catch { print(error); return nil }
  137. return fileName
  138. }
  139. let filenamePath = NSTemporaryDirectory() + fileName
  140. try? FileManager.default.removeItem(atPath: filenamePath)
  141. do {
  142. try FileManager.default.copyItem(atPath: url.path, toPath: filenamePath)
  143. let attr = try FileManager.default.attributesOfItem(atPath: filenamePath)
  144. guard !attr.isEmpty else { return nil }
  145. return fileName
  146. } catch { return nil }
  147. }
  148. // Data
  149. func getItem(data: Data, fileName: String, provider: NSItemProvider) -> String? {
  150. guard !data.isEmpty else { return nil }
  151. var fileName = fileName
  152. if let url = URL(string: fileName), !url.pathExtension.isEmpty {
  153. fileName = url.lastPathComponent
  154. } else if let name = provider.suggestedName {
  155. fileName = name
  156. } else if let ext = provider.registeredTypeIdentifiers.last?.split(separator: ".").last {
  157. fileName += "." + ext
  158. } // else: no file information, use default name without ext
  159. // when sharing images in safari only data is retuned.
  160. // also, when sharing option "Automatic" is slected extension will return both raw data and a url, which will be downloaded, causing the image to appear twice with different names
  161. if let image = UIImage(data: data) {
  162. return getItem(image: image, fileName: fileName)
  163. }
  164. let filenamePath = NSTemporaryDirectory() + fileName
  165. FileManager.default.createFile(atPath: filenamePath, contents: data, attributes: nil)
  166. return fileName
  167. }
  168. // String
  169. func getItem(string: String, fileName: String) -> String? {
  170. guard !string.isEmpty else { return nil }
  171. let filenamePath = NSTemporaryDirectory() + fileName + ".txt"
  172. FileManager.default.createFile(atPath: filenamePath, contents: string.data(using: String.Encoding.utf8), attributes: nil)
  173. return fileName
  174. }
  175. }
  176. extension NSItemProvider {
  177. var typeIdentifier: String {
  178. if hasItemConformingToTypeIdentifier("public.url") { return "public.url" } else
  179. if hasItemConformingToTypeIdentifier(UTType.item.identifier as String) { return UTType.item.identifier as String } else { return "" }
  180. }
  181. }