From 77621beed8b2879902dd7184562b2ea0d4b6c7e8 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 15 Nov 2023 14:49:41 +0530
Subject: [PATCH 1/4] NMC 1933 - Collabora customisation changes
---
.../CollaboraTestCase.swift | 142 +++++
iOSClient/BrowserWeb/NCBrowserWeb.swift | 2 +-
.../Data/NCManageDatabase+Metadata.swift | 13 +
.../NCCreateFormUploadDocuments.storyboard | 149 +++++
.../Create/NCCreateFormUploadDocuments.swift | 556 ++++++++++++++++++
.../FolderPathCustomCell.swift | 33 ++
.../NMC Custom Views/FolderPathCustomCell.xib | 74 +++
.../NCCreateDocumentCustomTextField.swift | 70 +++
.../NCCreateDocumentCustomTextField.xib | 80 +++
9 files changed, 1118 insertions(+), 1 deletion(-)
create mode 100644 Tests/NextcloudUnitTests/CollaboraTestCase.swift
create mode 100644 iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard
create mode 100644 iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
create mode 100644 iOSClient/NMC Custom Views/FolderPathCustomCell.swift
create mode 100644 iOSClient/NMC Custom Views/FolderPathCustomCell.xib
create mode 100644 iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
create mode 100644 iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib
diff --git a/Tests/NextcloudUnitTests/CollaboraTestCase.swift b/Tests/NextcloudUnitTests/CollaboraTestCase.swift
new file mode 100644
index 0000000000..51b3ecce1f
--- /dev/null
+++ b/Tests/NextcloudUnitTests/CollaboraTestCase.swift
@@ -0,0 +1,142 @@
+//
+// CollaboraTestCase.swift
+// NextcloudTests
+//
+// Created by A200073704 on 06/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class CollaboraTestCase: XCTestCase {
+
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testCollaboraDocumentIsPresent() {
+
+ var viewForDocument: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_document") {
+ viewForDocument = NCMenuAction(title: NSLocalizedString("_create_new_document_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_document_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForDocument)
+
+ }
+
+ func testCollaboraPresentationIsPresent() {
+
+ var viewForPresentation: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_ppt") {
+ viewForPresentation = NCMenuAction(title: NSLocalizedString("_create_new_presentation_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForPresentation)
+
+ }
+
+ func testCollaboraSpreadsheetIsPresent() {
+
+ var viewForSpreadsheet: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_xls") {
+ viewForSpreadsheet = NCMenuAction(title: NSLocalizedString("_create_new_spreadsheet_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForSpreadsheet)
+
+ }
+
+ func testTextDocumentIsPresent() {
+
+ var textMenu: NCMenuAction?
+
+ if let image = UIImage(named: "file_txt_menu") {
+ textMenu = NCMenuAction(title: NSLocalizedString("_create_nextcloudtext_document_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(textMenu)
+
+ }
+
+ func testTextDocumentAction() {
+
+ let text = NCGlobal.shared.actionTextDocument
+ XCTAssertNotNil(text, "Text Editor Should be opened")
+ }
+
+ func testTextFieldIsPresent() {
+
+ let storyboard = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCCreateFormUploadDocuments else {
+ return
+ }
+
+ // Verify that a text field is present in the view controller
+ let textFields = viewController.view.subviews.filter { $0 is UITextField }
+ XCTAssertFalse(textFields.isEmpty, "No text field found in NCCreateFormUploadDocuments")
+ }
+
+ func testSavePathFolder() {
+
+ let viewController = NCCreateFormUploadDocuments()
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var row : XLFormRowDescriptor
+
+ // the section with the title "Folder Destination"
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
+ row.action.formSelector = #selector(viewController.changeDestinationFolder(_:))
+
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected save path section to exist in form.")
+
+ }
+
+
+
+
+
+
+}
diff --git a/iOSClient/BrowserWeb/NCBrowserWeb.swift b/iOSClient/BrowserWeb/NCBrowserWeb.swift
index 206197c76c..4bf4a4f613 100644
--- a/iOSClient/BrowserWeb/NCBrowserWeb.swift
+++ b/iOSClient/BrowserWeb/NCBrowserWeb.swift
@@ -36,7 +36,7 @@ class NCBrowserWeb: UIViewController {
buttonExit.isHidden = true
} else {
self.view.bringSubviewToFront(buttonExit)
- let image = NCUtility().loadImage(named: "xmark", colors: [.systemBlue])
+ let image = NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.customer])
buttonExit.setImage(image, for: .normal)
}
diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift
index f03ef6cf8d..5ce5a4250b 100644
--- a/iOSClient/Data/NCManageDatabase+Metadata.swift
+++ b/iOSClient/Data/NCManageDatabase+Metadata.swift
@@ -333,6 +333,18 @@ extension tableMetadata {
return !lock || (lockOwner == user && lockOwnerType == 0)
}
+ // Return if is sharable
+ func isSharable() -> Bool {
+ guard let capabilities = NCNetworking.shared.capabilities[account] else {
+ return false
+ }
+ if !capabilities.fileSharingApiEnabled || (capabilities.e2EEEnabled && isDirectoryE2EE) {
+// if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
+ return false
+ }
+ return true
+ }
+
/// Returns a detached (unmanaged) deep copy of the current `tableMetadata` object.
///
/// - Note: The Realm `List` properties containing primitive types (e.g., `tags`, `shareType`) are copied automatically
@@ -354,6 +366,7 @@ extension tableMetadata {
extension NCManageDatabase {
#if !EXTENSION
+
func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
var isShare = false
var isMounted = false
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard b/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard
new file mode 100644
index 0000000000..6995ac52d9
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
new file mode 100644
index 0000000000..61177fa174
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
@@ -0,0 +1,556 @@
+//
+// NCCreateFormUploadDocuments.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 14/11/18.
+// Copyright © 2018 Marino Faggiana. 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
+import NextcloudKit
+import XLForm
+
+// MARK: -
+
+@objc class NCCreateFormUploadDocuments: XLFormViewController, NCSelectDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, NCCreateFormUploadConflictDelegate {
+
+ @IBOutlet weak var indicator: UIActivityIndicatorView!
+ @IBOutlet weak var collectionView: UICollectionView!
+ @IBOutlet weak var collectionViewHeigth: NSLayoutConstraint!
+
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ var editorId = ""
+ var creatorId = ""
+ var typeTemplate = ""
+ var templateIdentifier = ""
+ var serverUrl = ""
+ var fileNameFolder = ""
+ var fileName = ""
+ var fileNameExtension = ""
+ var titleForm = ""
+ var listOfTemplate: [NKEditorTemplates] = []
+ var selectTemplate: NKEditorTemplates?
+ let utilityFileSystem = NCUtilityFileSystem()
+ let utility = NCUtility()
+
+ // Layout
+ let numItems = 2
+ let sectionInsets: CGFloat = 10
+ let highLabelName: CGFloat = 20
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ fileNameFolder = "/"
+ } else {
+ fileNameFolder = (serverUrl as NSString).lastPathComponent
+ }
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+ view.backgroundColor = .systemGroupedBackground
+ collectionView.backgroundColor = .systemGroupedBackground
+ tableView.backgroundColor = .secondarySystemGroupedBackground
+
+ let cancelButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
+ let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
+ cancelButton.tintColor = NCBrandColor.shared.brand
+ saveButton.tintColor = NCBrandColor.shared.brand
+
+ self.navigationItem.leftBarButtonItem = cancelButton
+ self.navigationItem.rightBarButtonItem = saveButton
+ self.navigationItem.rightBarButtonItem?.isEnabled = false
+
+ // title
+ self.title = titleForm
+
+ fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
+
+ initializeForm()
+ getTemplate()
+ }
+
+ // MARK: - Tableview (XLForm)
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder")!.imageColor(NCBrandColor.shared.customer)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.right.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
+ if(self.fileNameFolder == "/"){
+ row.cellConfig["photoLabel.text"] = NSLocalizedString("_prefix_upload_path_", comment: "")
+ }else{
+ row.cellConfig["photoLabel.text"] = self.fileNameFolder
+ }
+ row.cellConfig["textLabel.text"] = ""
+
+ section.addFormRow(row)
+
+ // Section: File Name
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: "").uppercased())
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kMyAppCustomCellType"] = NCCreateDocumentCustomTextField.self
+
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "kMyAppCustomCellType", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = NCCreateDocumentCustomTextField.self
+
+ row.cellConfigAtConfigure["backgroundColor"] = UIColor.secondarySystemGroupedBackground;
+ row.cellConfig["fileNameTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameTextField.textColor"] = UIColor.label
+ row.cellConfig["fileNameTextField.placeholder"] = self.fileName
+
+ section.addFormRow(row)
+
+ self.form = form
+ // tableView.reloadData()
+ // collectionView.reloadData()
+ }
+
+ override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = tableView.backgroundColor
+ }
+
+ override func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = tableView.backgroundColor
+ }
+
+ // MARK: - CollectionView
+
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return listOfTemplate.count
+ }
+
+ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+
+ let itemWidth: CGFloat = (collectionView.frame.width - (sectionInsets * 4) - CGFloat(numItems)) / CGFloat(numItems)
+ let itemHeight: CGFloat = itemWidth + highLabelName
+
+ collectionViewHeigth.constant = itemHeight + sectionInsets
+
+ return CGSize(width: itemWidth, height: itemHeight)
+ }
+
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
+
+ let template = listOfTemplate[indexPath.row]
+
+ // image
+ let imagePreview = cell.viewWithTag(100) as? UIImageView
+ if !template.preview.isEmpty {
+ let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + template.name + ".png"
+ if FileManager.default.fileExists(atPath: fileNameLocalPath) {
+ let imageURL = URL(fileURLWithPath: fileNameLocalPath)
+ if let image = UIImage(contentsOfFile: imageURL.path) {
+ imagePreview?.image = image
+ }
+ } else {
+ getImageFromTemplate(name: template.name, preview: template.preview, indexPath: indexPath)
+ }
+ }
+
+ // name
+ let name = cell.viewWithTag(200) as? UILabel
+ name?.text = template.name
+ name?.textColor = .secondarySystemGroupedBackground
+
+ // select
+ let imageSelect = cell.viewWithTag(300) as? UIImageView
+ if selectTemplate != nil && selectTemplate?.name == template.name {
+ cell.backgroundColor = .label
+ imageSelect?.image = UIImage(named: "plus100")
+ imageSelect?.isHidden = false
+ } else {
+ cell.backgroundColor = .secondarySystemGroupedBackground
+ imageSelect?.isHidden = true
+ }
+
+ return cell
+ }
+
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+
+ let template = listOfTemplate[indexPath.row]
+
+ selectTemplate = template
+ fileNameExtension = template.ext
+
+ collectionView.reloadData()
+ }
+
+ // MARK: - Action
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+
+ guard let serverUrl = serverUrl else { return }
+
+ self.serverUrl = serverUrl
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ fileNameFolder = "/"
+ } else {
+ fileNameFolder = (serverUrl as NSString).lastPathComponent
+ }
+
+ let buttonDestinationFolder: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
+ buttonDestinationFolder.cellConfig["photoLabel.text"] = fileNameFolder
+
+ self.tableView.reloadData()
+ }
+
+// override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+// super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+//// if formRow.tag == "fileName" {
+//// self.form.delegate = nil
+//// if let fileNameNew = formRow.value {
+//// self.fileName = CCUtility.removeForbiddenCharactersServer(fileNameNew as? String)
+//// }
+//// formRow.value = self.fileName
+//// self.form.delegate = self
+//// }
+// }
+
+ @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
+
+ self.deselectFormRow(sender)
+
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+ let viewController = navigationController.topViewController as? NCSelect {
+
+ viewController.delegate = self
+ viewController.typeOfCommandView = .selectCreateFolder
+
+ self.present(navigationController, animated: true, completion: nil)
+ }
+ }
+
+ @objc func save() {
+
+ guard let selectTemplate = self.selectTemplate else { return }
+ templateIdentifier = selectTemplate.identifier
+
+ let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ var fileName = rowFileName.value as? String
+ if fileName?.isEmpty ?? false || fileName == nil {
+ fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
+ } else if fileName?.trimmingCharacters(in: .whitespaces).isEmpty ?? false {
+ let alert = UIAlertController(title: "", message: NSLocalizedString("_please_enter_file_name_", comment: ""), preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
+ self.present(alert, animated: true)
+ return
+ }
+ guard var fileNameForm: String = fileName, !fileNameForm.isEmpty else { return }
+
+ // Trim whitespaces after checks above
+ fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false)
+ if utility.isDirectEditing(account: appDelegate.account, contentType: result.mimeType).isEmpty {
+ fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
+ }
+
+ if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm)) != nil {
+
+ let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
+
+ guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+
+ conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+ conflict.alwaysNewFileNameNumber = true
+ conflict.serverUrl = serverUrl
+ conflict.metadatasUploadInConflict = [metadataForUpload]
+ conflict.delegate = self
+
+ self.present(conflict, animated: true, completion: nil)
+
+ } else {
+
+ let fileNamePath = utilityFileSystem.getFileNamePath(String(describing: fileNameForm), serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+ createDocument(fileNamePath: fileNamePath, fileName: String(describing: fileNameForm))
+ }
+ }
+
+ func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+
+ if let metadatas, metadatas.count > 0 {
+ let fileName = metadatas[0].fileName
+ let fileNamePath = utilityFileSystem.getFileNamePath(fileName, serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+ createDocument(fileNamePath: fileNamePath, fileName: fileName)
+ } else {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.cancel()
+ }
+ }
+ }
+
+ func createDocument(fileNamePath: String, fileName: String) {
+
+ self.navigationItem.rightBarButtonItem?.isEnabled = false
+ var UUID = NSUUID().uuidString
+ UUID = "TEMP" + UUID.replacingOccurrences(of: "-", with: "")
+
+ if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
+
+ var options = NKRequestOptions()
+ if self.editorId == NCGlobal.shared.editorOnlyoffice {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
+ } else if editorId == NCGlobal.shared.editorText {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
+ }
+
+ NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, options: options) { account, url, _, error in
+ guard error == .success, account == self.appDelegate.account, let url = url else {
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ NCContentPresenter().showError(error: error)
+ return
+ }
+
+ var results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false)
+ // FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+ if results.mimeType.isEmpty {
+ results.mimeType = "text/x-markdown"
+ }
+
+ self.dismiss(animated: true, completion: {
+ let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: UUID, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: results.mimeType)
+ if let viewController = self.appDelegate.activeViewController {
+ NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
+ }
+ })
+ }
+ }
+
+ if self.editorId == NCGlobal.shared.editorCollabora {
+
+ NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, _, error in
+ guard error == .success, account == self.appDelegate.account, let url = url else {
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ NCContentPresenter().showError(error: error)
+ return
+ }
+
+ self.dismiss(animated: true, completion: {
+ let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension
+ let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: UUID, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: "")
+ if let viewController = self.appDelegate.activeViewController {
+ NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
+ }
+ })
+ }
+ }
+ }
+
+ @objc func cancel() {
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ // MARK: NC API
+
+ func getTemplate() {
+
+ indicator.color = NCBrandColor.shared.brandElement
+ indicator.startAnimating()
+
+ if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
+
+ var options = NKRequestOptions()
+ if self.editorId == NCGlobal.shared.editorOnlyoffice {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
+ } else if editorId == NCGlobal.shared.editorText {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
+ }
+
+ NextcloudKit.shared.NCTextGetListOfTemplates(options: options) { account, templates, _, error in
+
+ self.indicator.stopAnimating()
+
+ if error == .success && account == self.appDelegate.account {
+
+ for template in templates {
+
+ let temp = NKEditorTemplates()
+
+ temp.identifier = template.identifier
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+
+ self.listOfTemplate.append(temp)
+
+ // default: template empty
+ if temp.preview.isEmpty {
+ self.selectTemplate = temp
+ self.fileNameExtension = template.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+ }
+ }
+
+ if self.listOfTemplate.isEmpty {
+
+ let temp = NKEditorTemplates()
+
+ temp.identifier = ""
+ if self.editorId == NCGlobal.shared.editorText {
+ temp.ext = "md"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templateDocument {
+ temp.ext = "docx"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templateSpreadsheet {
+ temp.ext = "xlsx"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templatePresentation {
+ temp.ext = "pptx"
+ }
+ temp.name = "Empty"
+ temp.preview = ""
+
+ self.listOfTemplate.append(temp)
+
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+
+ self.collectionView.reloadData()
+ }
+
+ }
+
+ if self.editorId == NCGlobal.shared.editorCollabora {
+
+ NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate) { account, templates, _, error in
+
+ self.indicator.stopAnimating()
+
+ if error == .success && account == self.appDelegate.account {
+
+ for template in templates! {
+
+ let temp = NKEditorTemplates()
+
+ temp.identifier = "\(template.templateId)"
+ temp.delete = template.delete
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+ temp.type = template.type
+
+ self.listOfTemplate.append(temp)
+
+ // default: template empty
+ if temp.preview.isEmpty {
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+ }
+ }
+
+ if self.listOfTemplate.isEmpty {
+
+ let temp = NKEditorTemplates()
+
+ temp.identifier = ""
+ if self.typeTemplate == NCGlobal.shared.templateDocument {
+ temp.ext = "docx"
+ } else if self.typeTemplate == NCGlobal.shared.templateSpreadsheet {
+ temp.ext = "xlsx"
+ } else if self.typeTemplate == NCGlobal.shared.templatePresentation {
+ temp.ext = "pptx"
+ }
+ temp.name = "Empty"
+ temp.preview = ""
+
+ self.listOfTemplate.append(temp)
+
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+
+ self.collectionView.reloadData()
+ }
+ }
+ }
+
+ func getImageFromTemplate(name: String, preview: String, indexPath: IndexPath) {
+
+ let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + name + ".png"
+
+ NextcloudKit.shared.download(serverUrlFileName: preview, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
+
+ }, taskHandler: { _ in
+
+ }, progressHandler: { _ in
+
+ }) { account, _, _, _, _, _, error in
+
+ if error == .success && account == self.appDelegate.account {
+ self.collectionView.reloadItems(at: [indexPath])
+ } else if error != .success {
+ print("\(error.errorCode)")
+ } else {
+ print("[ERROR] It has been changed user during networking process, error.")
+ }
+ }
+ }
+
+ func getFileExtension() -> String {
+ switch typeTemplate {
+ case NCGlobal.shared.editorText:
+ return "md"
+ case NCGlobal.shared.templateDocument:
+ return "docx"
+ case NCGlobal.shared.templateSpreadsheet:
+ return "xlsx"
+ case NCGlobal.shared.templatePresentation:
+ return "pptx"
+ default:
+ return ""
+ }
+ }
+}
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.swift b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
new file mode 100644
index 0000000000..901ce667ae
--- /dev/null
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
@@ -0,0 +1,33 @@
+//
+// FolderPathCustomCell.swift
+// Nextcloud
+//
+// Created by A200073704 on 04/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+class FolderPathCustomCell: XLFormButtonCell{
+
+ @IBOutlet weak var photoLabel: UILabel!
+ @IBOutlet weak var folderImage: UIImageView!
+ @IBOutlet weak var bottomLineView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ if (rowDescriptor.tag == "PhotoButtonDestinationFolder"){
+ bottomLineView.isHidden = true
+ }else{
+ bottomLineView.isHidden = false
+ }
+ }
+}
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.xib b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
new file mode 100644
index 0000000000..a231ae7c72
--- /dev/null
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
new file mode 100644
index 0000000000..0242fa0f7a
--- /dev/null
+++ b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
@@ -0,0 +1,70 @@
+//
+// NCCreateDocumentCustomTextField.swift
+// Nextcloud
+//
+// Created by A200073704 on 04/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+class NCCreateDocumentCustomTextField: XLFormBaseCell,UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameTextField: UITextField!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+ fileNameTextField.delegate = self
+
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameTextField == textField {
+ if let rowDescriptor = rowDescriptor {
+ if let text = textField.text{
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameTextField)
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
diff --git a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib
new file mode 100644
index 0000000000..3fd86bb2d6
--- /dev/null
+++ b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 1edebbad60b77bfa6be69b61d6c8e61035f43a09 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Fri, 11 Apr 2025 10:54:41 +0530
Subject: [PATCH 2/4] NMC 1933 - Collabora customisations changes
---
Nextcloud.xcodeproj/project.pbxproj | 41 +++++
.../Data/NCManageDatabase+Metadata.swift | 159 +++++++++++++++++-
.../Create/NCCreateFormUploadDocuments.swift | 55 +++---
3 files changed, 226 insertions(+), 29 deletions(-)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index 8dd4c5545f..f3d77d9c6e 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -85,6 +85,16 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B52FAE902DA8D9E1001AB1BD /* CollaboraTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52FAE8F2DA8D9E1001AB1BD /* CollaboraTestCase.swift */; };
+ B52FAE932DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B52FAE912DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.storyboard */; };
+ B52FAE942DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52FAE922DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.swift */; };
+ B52FAE9C2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.xib in Resources */ = {isa = PBXBuildFile; fileRef = B52FAE9A2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.xib */; };
+ B52FAE9D2DA8DED9001AB1BD /* FolderPathCustomCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B52FAE982DA8DED9001AB1BD /* FolderPathCustomCell.xib */; };
+ B52FAE9E2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52FAE992DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.swift */; };
+ B52FAE9F2DA8DED9001AB1BD /* FolderPathCustomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52FAE972DA8DED9001AB1BD /* FolderPathCustomCell.swift */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
+ D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; };
@@ -1221,6 +1231,14 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B52FAE8F2DA8D9E1001AB1BD /* CollaboraTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollaboraTestCase.swift; sourceTree = ""; };
+ B52FAE912DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCCreateFormUploadDocuments.storyboard; sourceTree = ""; };
+ B52FAE922DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadDocuments.swift; sourceTree = ""; };
+ B52FAE972DA8DED9001AB1BD /* FolderPathCustomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPathCustomCell.swift; sourceTree = ""; };
+ B52FAE982DA8DED9001AB1BD /* FolderPathCustomCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FolderPathCustomCell.xib; sourceTree = ""; };
+ B52FAE992DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateDocumentCustomTextField.swift; sourceTree = ""; };
+ B52FAE9A2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCCreateDocumentCustomTextField.xib; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2024,6 +2042,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B52FAE8F2DA8D9E1001AB1BD /* CollaboraTestCase.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -2062,6 +2082,17 @@
path = Advanced;
sourceTree = "";
};
+ B52FAE9B2DA8DED9001AB1BD /* NMC Custom Views */ = {
+ isa = PBXGroup;
+ children = (
+ B52FAE972DA8DED9001AB1BD /* FolderPathCustomCell.swift */,
+ B52FAE982DA8DED9001AB1BD /* FolderPathCustomCell.xib */,
+ B52FAE992DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.swift */,
+ B52FAE9A2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.xib */,
+ );
+ path = "NMC Custom Views";
+ sourceTree = "";
+ };
C0046CDB2A17B98400D87C9D /* NextcloudUITests */ = {
isa = PBXGroup;
children = (
@@ -3103,6 +3134,8 @@
F7DFB7E9219C5A0500680748 /* Create */ = {
isa = PBXGroup;
children = (
+ B52FAE912DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.storyboard */,
+ B52FAE922DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.swift */,
F7FA7FFD2C0F4F3B0072FC60 /* Upload Assets */,
F7A509242C26BD5D00326106 /* NCCreate.swift */,
F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */,
@@ -3226,6 +3259,7 @@
isa = PBXGroup;
children = (
AA517BB42D66149900F8D37C /* .tx */,
+ B52FAE9B2DA8DED9001AB1BD /* NMC Custom Views */,
F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */,
F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */,
F7CF067A2E0FF38F0063AD04 /* NCAppStateManager.swift */,
@@ -3952,6 +3986,8 @@
F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */,
F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */,
F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */,
+ B52FAE9C2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.xib in Resources */,
+ B52FAE9D2DA8DED9001AB1BD /* FolderPathCustomCell.xib in Resources */,
AF93471927E2361E002537EE /* NCShareAdvancePermissionFooter.xib in Resources */,
F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */,
F7CBC1232BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib in Resources */,
@@ -3971,6 +4007,7 @@
F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */,
F751247E2C42919C00E63DB8 /* NCPhotoCell.xib in Resources */,
F704B5E32430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard in Resources */,
+ B52FAE932DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.storyboard in Resources */,
F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */,
F732D23327CF8AED000B0F1B /* NCPlayerToolBar.xib in Resources */,
F73D11FA253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard in Resources */,
@@ -4392,6 +4429,7 @@
F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */,
F73EF7A72B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */,
F33918C42C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */,
+ B52FAE942DA8DCB2001AB1BD /* NCCreateFormUploadDocuments.swift in Sources */,
F39170AD2CB82024006127BC /* FileAutoRenamer+Extensions.swift in Sources */,
F799DF882C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift in Sources */,
F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */,
@@ -4525,6 +4563,9 @@
F76882352C0DD1E7001CF441 /* NCWebBrowserView.swift in Sources */,
F3A047972BD2668800658E7B /* NCAssistantEmptyView.swift in Sources */,
F757CC8D29E82D0500F31428 /* NCGroupfolders.swift in Sources */,
+ F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */,
+ B52FAE9E2DA8DED9001AB1BD /* NCCreateDocumentCustomTextField.swift in Sources */,
+ B52FAE9F2DA8DED9001AB1BD /* FolderPathCustomCell.swift in Sources */,
F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */,
AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */,
F7F3E58E2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */,
diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift
index 5ce5a4250b..8d93019f18 100644
--- a/iOSClient/Data/NCManageDatabase+Metadata.swift
+++ b/iOSClient/Data/NCManageDatabase+Metadata.swift
@@ -139,10 +139,37 @@ extension tableMetadata {
(fileNameView as NSString).deletingPathExtension
}
+ var isRenameable: Bool {
+ if !NCMetadataPermissions.canRename(self) {
+ return false
+ }
+ if lock {
+ return false
+ }
+ if !isDirectoryE2EE && e2eEncrypted {
+ return false
+ }
+ return true
+ }
+
+ var isPrintable: Bool {
+ if isDocumentViewableOnly {
+ return false
+ }
+ if ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/") || classFile == NKCommon.TypeClassFile.image.rawValue {
+ return true
+ }
+ return false
+ }
+
var isSavebleInCameraRoll: Bool {
return (classFile == NKTypeClassFile.image.rawValue && contentType != "image/svg+xml") || classFile == NKTypeClassFile.video.rawValue
}
+ var isDocumentViewableOnly: Bool {
+ sharePermissionsCollaborationServices == NCPermissions().permissionReadShare && classFile == NKCommon.TypeClassFile.document.rawValue
+ }
+
var isAudioOrVideo: Bool {
return classFile == NKTypeClassFile.audio.rawValue || classFile == NKTypeClassFile.video.rawValue
}
@@ -168,7 +195,7 @@ extension tableMetadata {
}
var isCopyableInPasteboard: Bool {
- !directory
+ !isDocumentViewableOnly && !directory
}
#if !EXTENSION_FILE_PROVIDER_EXTENSION
@@ -177,11 +204,11 @@ extension tableMetadata {
}
var isCopyableMovable: Bool {
- !isDirectoryE2EE && !e2eEncrypted
+ !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
}
var isModifiableWithQuickLook: Bool {
- if directory || isDirectoryE2EE {
+ if directory || isDocumentViewableOnly || isDirectoryE2EE {
return false
}
return isPDF || isImage
@@ -212,7 +239,7 @@ extension tableMetadata {
}
var canSetAsAvailableOffline: Bool {
- return session.isEmpty && !isDirectoryE2EE && !e2eEncrypted
+ return session.isEmpty && !isDocumentViewableOnly //!isDirectoryE2EE && !e2eEncrypted
}
// Return if is sharable
@@ -225,6 +252,68 @@ extension tableMetadata {
}
return true
}
+
+ var canShare: Bool {
+ return session.isEmpty && !isDocumentViewableOnly && !directory && !NCBrandOptions.shared.disable_openin_file
+ }
+
+ var canUnsetDirectoryAsE2EE: Bool {
+ return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCPreferences().isEndToEndEnabled(account: account)
+ }
+
+ var canOpenExternalEditor: Bool {
+ if isDocumentViewableOnly {
+ return false
+ }
+ let utility = NCUtility()
+ let editors = utility.editorsDirectEditing(account: account, contentType: contentType)
+ let isRichDocument = utility.isTypeFileRichDocument(self)
+ return classFile == NKCommon.TypeClassFile.document.rawValue && editors.contains(NCGlobal.shared.editorText) && ((editors.contains(NCGlobal.shared.editorOnlyoffice) || isRichDocument))
+ }
+
+ var isWaitingTransfer: Bool {
+ status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
+ }
+
+ var isInTransfer: Bool {
+ status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusUploading
+ }
+
+ var isTransferInForeground: Bool {
+ (status > 0 && (chunk > 0 || e2eEncrypted))
+ }
+
+ var isDownloadUpload: Bool {
+ status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusUploading
+ }
+
+ var isDownload: Bool {
+ status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusDownloading
+ }
+
+ var isUpload: Bool {
+ status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploading
+ }
+
+ var isDirectory: Bool {
+ directory
+ }
+
+ @objc var isDirectoryE2EE: Bool {
+ return NCUtilityFileSystem().isDirectoryE2EE(serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
+ }
+
+ var isLivePhoto: Bool {
+ !livePhotoFile.isEmpty
+ }
+
+ var isNotFlaggedAsLivePhotoByServer: Bool {
+ !isFlaggedAsLivePhotoByServer
+ }
+
+ var imageSize: CGSize {
+ CGSize(width: width, height: height)
+ }
var hasPreviewBorder: Bool {
!isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024, userId: userId, urlBase: urlBase)
@@ -342,7 +431,7 @@ extension tableMetadata {
// if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
return false
}
- return true
+ return !e2eEncrypted
}
/// Returns a detached (unmanaged) deep copy of the current `tableMetadata` object.
@@ -1118,6 +1207,25 @@ extension NCManageDatabase {
} ?? []
}
+ func getTableMetadatasDirectoryFavoriteIdentifierRankAsync(account: String) async -> [String: NSNumber] {
+ let result = await performRealmReadAsync { realm in
+ var listIdentifierRank: [String: NSNumber] = [:]
+ var counter = Int64(10)
+
+ let results = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND directory == true AND favorite == true", account)
+ .sorted(byKeyPath: "fileNameView", ascending: true)
+
+ results.forEach { item in
+ counter += 1
+ listIdentifierRank[item.ocId] = NSNumber(value: counter)
+ }
+
+ return listIdentifierRank
+ }
+ return result ?? [:]
+ }
+
func getAssetLocalIdentifiersUploadedAsync() async -> [String]? {
return await core.performRealmReadAsync { realm in
let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
@@ -1125,6 +1233,18 @@ extension NCManageDatabase {
}
}
+ @objc func clearMetadatasUpload(account: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter("account == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusUploadError)
+ realm.delete(results)
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+ }
+ }
+
func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
guard let fileId else {
return nil
@@ -1302,4 +1422,33 @@ extension NCManageDatabase {
.first != nil
} ?? false
}
+
+ func getAdvancedMetadatas(predicate: NSPredicate, page: Int = 0, limit: Int = 0, sorted: String, ascending: Bool) -> [tableMetadata] {
+
+ var metadatas: [tableMetadata] = []
+
+ do {
+ let realm = try Realm()
+ realm.refresh()
+ let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
+ if !results.isEmpty {
+ if page == 0 || limit == 0 {
+ return Array(results.map { tableMetadata.init(value: $0) })
+ } else {
+ let nFrom = (page - 1) * limit
+ let nTo = nFrom + (limit - 1)
+ for n in nFrom...nTo {
+ if n == results.count {
+ break
+ }
+ metadatas.append(tableMetadata.init(value: results[n]))
+ }
+ }
+ }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+ }
+
+ return metadatas
+ }
}
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
index 61177fa174..ec489d893a 100644
--- a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
@@ -53,15 +53,20 @@ import XLForm
let sectionInsets: CGFloat = 10
let highLabelName: CGFloat = 20
+ var controller: NCMainTabBarController!
+ var session: NCSession.Session {
+ NCSession.shared.getSession(controller: controller)
+ }
+
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
- if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
fileNameFolder = "/"
} else {
- fileNameFolder = (serverUrl as NSString).lastPathComponent
+ fileNameFolder = utilityFileSystem.getTextServerUrl(session: session, serverUrl: serverUrl)//(serverUrl as NSString).lastPathComponent
}
self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
@@ -229,7 +234,7 @@ import XLForm
guard let serverUrl = serverUrl else { return }
self.serverUrl = serverUrl
- if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
fileNameFolder = "/"
} else {
fileNameFolder = (serverUrl as NSString).lastPathComponent
@@ -288,14 +293,15 @@ import XLForm
// Trim whitespaces after checks above
fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
- let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false)
- if utility.isDirectEditing(account: appDelegate.account, contentType: result.mimeType).isEmpty {
+ let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false, account: session.account
+ )
+ if utility.editorsDirectEditing(account: session.account, contentType: result.mimeType).isEmpty {
fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
}
- if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm)) != nil {
+ if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
- let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
+ let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
@@ -309,7 +315,7 @@ import XLForm
} else {
- let fileNamePath = utilityFileSystem.getFileNamePath(String(describing: fileNameForm), serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+ let fileNamePath = utilityFileSystem.getFileNamePath(String(describing: fileNameForm), serverUrl: serverUrl, session: session)
createDocument(fileNamePath: fileNamePath, fileName: String(describing: fileNameForm))
}
}
@@ -318,7 +324,7 @@ import XLForm
if let metadatas, metadatas.count > 0 {
let fileName = metadatas[0].fileName
- let fileNamePath = utilityFileSystem.getFileNamePath(fileName, serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+ let fileNamePath = utilityFileSystem.getFileNamePath(fileName, serverUrl: serverUrl, session: session)
createDocument(fileNamePath: fileNamePath, fileName: fileName)
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
@@ -342,23 +348,23 @@ import XLForm
options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
}
- NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, options: options) { account, url, _, error in
- guard error == .success, account == self.appDelegate.account, let url = url else {
+ NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { account, url, _, error in
+ guard error == .success, account == self.session.account, let url = url else {
self.navigationItem.rightBarButtonItem?.isEnabled = true
NCContentPresenter().showError(error: error)
return
}
- var results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false)
+ var results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false, account: self.session.account)
// FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
if results.mimeType.isEmpty {
results.mimeType = "text/x-markdown"
}
self.dismiss(animated: true, completion: {
- let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: UUID, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: results.mimeType)
+ let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName, fileNameView: fileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: results.mimeType, session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
if let viewController = self.appDelegate.activeViewController {
- NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
+ NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
}
})
}
@@ -366,8 +372,8 @@ import XLForm
if self.editorId == NCGlobal.shared.editorCollabora {
- NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, _, error in
- guard error == .success, account == self.appDelegate.account, let url = url else {
+ NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier, account: session.account) { account, url, _, error in
+ guard error == .success, account == self.session.account, let url = url else {
self.navigationItem.rightBarButtonItem?.isEnabled = true
NCContentPresenter().showError(error: error)
return
@@ -375,9 +381,10 @@ import XLForm
self.dismiss(animated: true, completion: {
let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension
- let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: UUID, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: "")
+ let metadata = NCManageDatabase.shared.createMetadata(fileName: createFileName, fileNameView: createFileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: "", session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
+ AnalyticsHelper.shared.trackCreateFile(metadata: metadata)
if let viewController = self.appDelegate.activeViewController {
- NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
+ NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
}
})
}
@@ -405,11 +412,11 @@ import XLForm
options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
}
- NextcloudKit.shared.NCTextGetListOfTemplates(options: options) { account, templates, _, error in
+ NextcloudKit.shared.NCTextGetListOfTemplates(account: session.account, options: options) { account, templates, _, error in
self.indicator.stopAnimating()
- if error == .success && account == self.appDelegate.account {
+ if error == .success && account == self.session.account, let templates = templates {
for template in templates {
@@ -462,11 +469,11 @@ import XLForm
if self.editorId == NCGlobal.shared.editorCollabora {
- NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate) { account, templates, _, error in
+ NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate, account: session.account) { account, templates, _, error in
self.indicator.stopAnimating()
- if error == .success && account == self.appDelegate.account {
+ if error == .success && account == self.session.account {
for template in templates! {
@@ -521,7 +528,7 @@ import XLForm
let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + name + ".png"
- NextcloudKit.shared.download(serverUrlFileName: preview, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
+ NextcloudKit.shared.download(serverUrlFileName: preview, fileNameLocalPath: fileNameLocalPath, account: session.account, requestHandler: { _ in
}, taskHandler: { _ in
@@ -529,7 +536,7 @@ import XLForm
}) { account, _, _, _, _, _, error in
- if error == .success && account == self.appDelegate.account {
+ if error == .success && account == self.session.account {
self.collectionView.reloadItems(at: [indexPath])
} else if error != .success {
print("\(error.errorCode)")
From 565958d8a51e1467efd0577766927beee07af53d Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 1 Oct 2025 14:13:06 +0530
Subject: [PATCH 3/4] NMC 1933 - Collabora customisation changes
---
iOSClient/BrowserWeb/NCBrowserWeb.swift | 25 +-
.../Data/NCManageDatabase+Metadata.swift | 1846 +++++++++--------
.../Create/NCCreateFormUploadDocuments.swift | 48 +-
.../FolderPathCustomCell.swift | 1 +
.../NMC Custom Views/FolderPathCustomCell.xib | 11 +-
.../NCCreateDocumentCustomTextField.swift | 1 +
6 files changed, 1059 insertions(+), 873 deletions(-)
diff --git a/iOSClient/BrowserWeb/NCBrowserWeb.swift b/iOSClient/BrowserWeb/NCBrowserWeb.swift
index 4bf4a4f613..10ee6d71ca 100644
--- a/iOSClient/BrowserWeb/NCBrowserWeb.swift
+++ b/iOSClient/BrowserWeb/NCBrowserWeb.swift
@@ -1,6 +1,25 @@
-// SPDX-FileCopyrightText: Nextcloud GmbH
-// SPDX-FileCopyrightText: 2019 Marino Faggiana
-// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// NCBrowserWeb.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 22/08/2019.
+// Copyright (c) 2019 Marino Faggiana. 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
@preconcurrency import WebKit
diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift
index 8d93019f18..5c82a8767a 100644
--- a/iOSClient/Data/NCManageDatabase+Metadata.swift
+++ b/iOSClient/Data/NCManageDatabase+Metadata.swift
@@ -1,12 +1,30 @@
-// SPDX-FileCopyrightText: Nextcloud GmbH
-// SPDX-FileCopyrightText: 2021 Marino Faggiana
-// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// NCManageDatabase+Metadata.swift
+// Nextcloud
+//
+// Created by Henrik Storch on 30.11.21.
+// Copyright © 2021 Marino Faggiana. 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 Foundation
import UIKit
import RealmSwift
import NextcloudKit
-import Photos
class tableMetadata: Object {
override func isEqual(_ object: Any?) -> Bool {
@@ -33,7 +51,8 @@ class tableMetadata: Object {
self.altitude == object.altitude,
self.status == object.status,
Array(self.tags).elementsEqual(Array(object.tags)),
- Array(self.shareType).elementsEqual(Array(object.shareType)) {
+ Array(self.shareType).elementsEqual(Array(object.shareType)),
+ Array(self.sharePermissionsCloudMesh).elementsEqual(Array(object.sharePermissionsCloudMesh)) {
return true
} else {
return false
@@ -56,6 +75,7 @@ class tableMetadata: Object {
@objc dynamic var e2eEncrypted: Bool = false
@objc dynamic var edited: Bool = false
@objc dynamic var etag = ""
+ @objc dynamic var etagResource = ""
let exifPhotos = List()
@objc dynamic var favorite: Bool = false
@objc dynamic var fileId = ""
@@ -82,7 +102,6 @@ class tableMetadata: Object {
@objc public var lockOwnerDisplayName = ""
@objc public var lockTime: Date?
@objc public var lockTimeOut: Date?
- @objc dynamic var mediaSearch: Bool = false
@objc dynamic var path = ""
@objc dynamic var permissions = ""
@objc dynamic var placePhotos: String?
@@ -92,15 +111,15 @@ class tableMetadata: Object {
@objc dynamic var richWorkspace: String?
@objc dynamic var sceneIdentifier: String?
@objc dynamic var serverUrl = ""
- @objc dynamic var serverUrlFileName = ""
- @objc dynamic var destination = ""
+ @objc dynamic var serveUrlFileName = ""
+ @objc dynamic var serverUrlTo = ""
@objc dynamic var session = ""
@objc dynamic var sessionDate: Date?
@objc dynamic var sessionError = ""
@objc dynamic var sessionSelector = ""
@objc dynamic var sessionTaskIdentifier: Int = 0
- /// The integer for sharing permissions.
@objc dynamic var sharePermissionsCollaborationServices: Int = 0
+ let sharePermissionsCloudMesh = List()
let shareType = List()
@objc dynamic var size: Int64 = 0
@objc dynamic var status: Int = 0
@@ -123,7 +142,6 @@ class tableMetadata: Object {
@objc dynamic var errorCode: Int = 0
@objc dynamic var nativeFormat: Bool = false
@objc dynamic var autoUploadServerUrlBase: String?
- @objc dynamic var typeIdentifier: String = ""
override static func primaryKey() -> String {
return "ocId"
@@ -140,9 +158,6 @@ extension tableMetadata {
}
var isRenameable: Bool {
- if !NCMetadataPermissions.canRename(self) {
- return false
- }
if lock {
return false
}
@@ -161,48 +176,44 @@ extension tableMetadata {
}
return false
}
-
+
var isSavebleInCameraRoll: Bool {
- return (classFile == NKTypeClassFile.image.rawValue && contentType != "image/svg+xml") || classFile == NKTypeClassFile.video.rawValue
+ return (classFile == NKCommon.TypeClassFile.image.rawValue && contentType != "image/svg+xml") || classFile == NKCommon.TypeClassFile.video.rawValue
}
+
var isDocumentViewableOnly: Bool {
sharePermissionsCollaborationServices == NCPermissions().permissionReadShare && classFile == NKCommon.TypeClassFile.document.rawValue
}
var isAudioOrVideo: Bool {
- return classFile == NKTypeClassFile.audio.rawValue || classFile == NKTypeClassFile.video.rawValue
+ return classFile == NKCommon.TypeClassFile.audio.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
}
var isImageOrVideo: Bool {
- return classFile == NKTypeClassFile.image.rawValue || classFile == NKTypeClassFile.video.rawValue
+ return classFile == NKCommon.TypeClassFile.image.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
}
var isVideo: Bool {
- return classFile == NKTypeClassFile.video.rawValue
+ return classFile == NKCommon.TypeClassFile.video.rawValue
}
var isAudio: Bool {
- return classFile == NKTypeClassFile.audio.rawValue
+ return classFile == NKCommon.TypeClassFile.audio.rawValue
}
var isImage: Bool {
- return classFile == NKTypeClassFile.image.rawValue
+ return classFile == NKCommon.TypeClassFile.image.rawValue
}
var isSavebleAsImage: Bool {
- classFile == NKTypeClassFile.image.rawValue && contentType != "image/svg+xml"
+ classFile == NKCommon.TypeClassFile.image.rawValue && contentType != "image/svg+xml"
}
var isCopyableInPasteboard: Bool {
!isDocumentViewableOnly && !directory
}
-#if !EXTENSION_FILE_PROVIDER_EXTENSION
- @objc var isDirectoryE2EE: Bool {
- return NCUtilityFileSystem().isDirectoryE2EE(serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
- }
-
var isCopyableMovable: Bool {
!isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
}
@@ -214,51 +225,28 @@ extension tableMetadata {
return isPDF || isImage
}
- var isRenameable: Bool {
- if !NCMetadataPermissions.canRename(self) {
- return false
- }
- if lock {
- return false
- }
- if !isDirectoryE2EE && e2eEncrypted {
- return false
- }
- return true
- }
-
- var canUnsetDirectoryAsE2EE: Bool {
- return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCPreferences().isEndToEndEnabled(account: account)
- }
-
var isDeletable: Bool {
- if (!isDirectoryE2EE && e2eEncrypted) || !NCMetadataPermissions.canDelete(self) {
+ if !isDirectoryE2EE && e2eEncrypted {
return false
}
return true
}
var canSetAsAvailableOffline: Bool {
- return session.isEmpty && !isDocumentViewableOnly //!isDirectoryE2EE && !e2eEncrypted
+// return session.isEmpty && !isDocumentViewableOnly //!isDirectoryE2EE && !e2eEncrypted
+ return session.isEmpty && !isDirectoryE2EE && !e2eEncrypted
}
- // Return if is sharable
- func isSharable() -> Bool {
- guard let capabilities = NCNetworking.shared.capabilities[account] else {
- return false
- }
- if !capabilities.fileSharingApiEnabled || (capabilities.e2EEEnabled && isDirectoryE2EE) {
- return false
- }
- return true
- }
-
var canShare: Bool {
return session.isEmpty && !isDocumentViewableOnly && !directory && !NCBrandOptions.shared.disable_openin_file
}
+ var canSetDirectoryAsE2EE: Bool {
+ return directory && size == 0 && !e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
+ }
+
var canUnsetDirectoryAsE2EE: Bool {
- return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCPreferences().isEndToEndEnabled(account: account)
+ return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
}
var canOpenExternalEditor: Bool {
@@ -300,7 +288,12 @@ extension tableMetadata {
}
@objc var isDirectoryE2EE: Bool {
- return NCUtilityFileSystem().isDirectoryE2EE(serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
+ let session = NCSession.Session(account: account, urlBase: urlBase, user: user, userId: userId)
+ return NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
+ }
+
+ var isDirectoryE2EETop: Bool {
+ NCUtilityFileSystem().isDirectoryE2EETop(account: account, serverUrl: serverUrl)
}
var isLivePhoto: Bool {
@@ -316,27 +309,21 @@ extension tableMetadata {
}
var hasPreviewBorder: Bool {
- !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024, userId: userId, urlBase: urlBase)
+ !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024)
}
var isAvailableEditorView: Bool {
guard !isPDF,
- classFile == NKTypeClassFile.document.rawValue,
- NextcloudKit.shared.isNetworkReachable() else {
- return false
- }
+ classFile == NKCommon.TypeClassFile.document.rawValue,
+ NextcloudKit.shared.isNetworkReachable() else { return false }
let utility = NCUtility()
- let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType).map { $0.lowercased() }
+ let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType)
let richDocumentEditor = utility.isTypeFileRichDocument(self)
- let capabilities = NCNetworking.shared.capabilities[account]
- if let capabilities,
- capabilities.richDocumentsEnabled,
- richDocumentEditor,
- directEditingEditors.isEmpty {
+ if NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled && richDocumentEditor && directEditingEditors.isEmpty {
// RichDocument: Collabora
return true
- } else if directEditingEditors.contains("nextcloud text") || directEditingEditors.contains("onlyoffice") {
+ } else if directEditingEditors.contains(NCGlobal.shared.editorText) || directEditingEditors.contains(NCGlobal.shared.editorOnlyoffice) {
// DirectEditing: Nextcloud Text - OnlyOffice
return true
}
@@ -344,9 +331,8 @@ extension tableMetadata {
}
var isAvailableRichDocumentEditorView: Bool {
- guard let capabilities = NCNetworking.shared.capabilities[account],
- classFile == NKTypeClassFile.document.rawValue,
- capabilities.richDocumentsEnabled,
+ guard classFile == NKCommon.TypeClassFile.document.rawValue,
+ NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled,
NextcloudKit.shared.isNetworkReachable() else { return false }
if NCUtility().isTypeFileRichDocument(self) {
@@ -356,12 +342,10 @@ extension tableMetadata {
}
var isAvailableDirectEditingEditorView: Bool {
- guard (classFile == NKTypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else {
- return false
- }
- let editors = NCUtility().editorsDirectEditing(account: account, contentType: contentType).map { $0.lowercased() }
+ guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else { return false }
+ let editors = NCUtility().editorsDirectEditing(account: account, contentType: contentType)
- if editors.contains("nextcloud text") || editors.contains("onlyoffice") {
+ if editors.contains(NCGlobal.shared.editorText) || editors.contains(NCGlobal.shared.editorOnlyoffice) {
return true
}
return false
@@ -371,52 +355,6 @@ extension tableMetadata {
return (contentType == "application/pdf" || contentType == "com.adobe.pdf")
}
- var isCreatable: Bool {
- if isDirectory {
- return NCMetadataPermissions.canCreateFolder(self)
- } else {
- return NCMetadataPermissions.canCreateFile(self)
- }
- }
-
-#endif
-
- var canShare: Bool {
- return session.isEmpty && !directory && !NCBrandOptions.shared.disable_openin_file
- }
-
- var isDownload: Bool {
- status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusDownloading
- }
-
- var isUpload: Bool {
- status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploading
- }
-
- var isDirectory: Bool {
- directory
- }
-
- var isLivePhoto: Bool {
- !livePhotoFile.isEmpty
- }
-
- var isLivePhotoVideo: Bool {
- !livePhotoFile.isEmpty && classFile == NKTypeClassFile.video.rawValue
- }
-
- var isLivePhotoImage: Bool {
- !livePhotoFile.isEmpty && classFile == NKTypeClassFile.image.rawValue
- }
-
- var isNotFlaggedAsLivePhotoByServer: Bool {
- !isFlaggedAsLivePhotoByServer
- }
-
- var imageSize: CGSize {
- CGSize(width: width, height: height)
- }
-
/// Returns false if the user is lokced out of the file. I.e. The file is locked but by somone else
func canUnlock(as user: String) -> Bool {
return !lock || (lockOwner == user && lockOwnerType == 0)
@@ -424,694 +362,828 @@ extension tableMetadata {
// Return if is sharable
func isSharable() -> Bool {
- guard let capabilities = NCNetworking.shared.capabilities[account] else {
- return false
- }
- if !capabilities.fileSharingApiEnabled || (capabilities.e2EEEnabled && isDirectoryE2EE) {
-// if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
+ if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
return false
}
return !e2eEncrypted
}
-
- /// Returns a detached (unmanaged) deep copy of the current `tableMetadata` object.
- ///
- /// - Note: The Realm `List` properties containing primitive types (e.g., `tags`, `shareType`) are copied automatically
- /// by the Realm initializer `init(value:)`. For `List` containing Realm objects (e.g., `exifPhotos`), this method
- /// creates new instances to ensure the copy is fully detached and safe to use outside of a Realm context.
- ///
- /// - Returns: A new `tableMetadata` instance fully detached from Realm.
- func detachedCopy() -> tableMetadata {
- // Use Realm's built-in copy constructor for primitive properties and List of primitives
- let detached = tableMetadata(value: self)
-
- // Deep copy of List of Realm objects (exifPhotos)
- detached.exifPhotos.removeAll()
- detached.exifPhotos.append(objectsIn: self.exifPhotos.map { NCKeyValue(value: $0) })
-
- return detached
- }
}
extension NCManageDatabase {
-#if !EXTENSION
-
- func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
- var isShare = false
- var isMounted = false
-
- if metadataFolder != nil {
- isShare = metadata.permissions.contains(NCMetadataPermissions.permissionShared) && !metadataFolder!.permissions.contains(NCMetadataPermissions.permissionShared)
- isMounted = metadata.permissions.contains(NCMetadataPermissions.permissionMounted) && !metadataFolder!.permissions.contains(NCMetadataPermissions.permissionMounted)
- } else if let directory = getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
- isShare = metadata.permissions.contains(NCMetadataPermissions.permissionShared) && !directory.permissions.contains(NCMetadataPermissions.permissionShared)
- isMounted = metadata.permissions.contains(NCMetadataPermissions.permissionMounted) && !directory.permissions.contains(NCMetadataPermissions.permissionMounted)
+ func convertFileToMetadata(_ file: NKFile, isDirectoryE2EE: Bool) -> tableMetadata {
+ let metadata = tableMetadata()
+
+ metadata.account = file.account
+ metadata.checksums = file.checksums
+ metadata.commentsUnread = file.commentsUnread
+ metadata.contentType = file.contentType
+ if let date = file.creationDate {
+ metadata.creationDate = date as NSDate
+ } else {
+ metadata.creationDate = file.date as NSDate
}
-
- if isShare || isMounted {
- return true
+ metadata.dataFingerprint = file.dataFingerprint
+ metadata.date = file.date as NSDate
+ if let datePhotosOriginal = file.datePhotosOriginal {
+ metadata.datePhotosOriginal = datePhotosOriginal as NSDate
} else {
- return false
+ metadata.datePhotosOriginal = metadata.date
+ }
+ metadata.directory = file.directory
+ metadata.downloadURL = file.downloadURL
+ metadata.e2eEncrypted = file.e2eEncrypted
+ metadata.etag = file.etag
+ for dict in file.exifPhotos {
+ for (key, value) in dict {
+ let keyValue = NCKeyValue()
+ keyValue.key = key
+ keyValue.value = value
+ metadata.exifPhotos.append(keyValue)
+ }
+ }
+ metadata.favorite = file.favorite
+ metadata.fileId = file.fileId
+ metadata.fileName = file.fileName
+ metadata.fileNameView = file.fileName
+ metadata.hasPreview = file.hasPreview
+ metadata.hidden = file.hidden
+ switch (file.fileName as NSString).pathExtension {
+ case "odg":
+ metadata.iconName = "diagram"
+ case "csv", "xlsm" :
+ metadata.iconName = "file_xls"
+ default:
+ metadata.iconName = file.iconName
+ }
+ metadata.mountType = file.mountType
+ metadata.name = file.name
+ metadata.note = file.note
+ metadata.ocId = file.ocId
+ metadata.ocIdTransfer = file.ocId
+ metadata.ownerId = file.ownerId
+ metadata.ownerDisplayName = file.ownerDisplayName
+ metadata.lock = file.lock
+ metadata.lockOwner = file.lockOwner
+ metadata.lockOwnerEditor = file.lockOwnerEditor
+ metadata.lockOwnerType = file.lockOwnerType
+ metadata.lockOwnerDisplayName = file.lockOwnerDisplayName
+ metadata.lockTime = file.lockTime
+ metadata.lockTimeOut = file.lockTimeOut
+ metadata.path = file.path
+ metadata.permissions = file.permissions
+ metadata.placePhotos = file.placePhotos
+ metadata.quotaUsedBytes = file.quotaUsedBytes
+ metadata.quotaAvailableBytes = file.quotaAvailableBytes
+ metadata.richWorkspace = file.richWorkspace
+ metadata.resourceType = file.resourceType
+ metadata.serverUrl = file.serverUrl
+ metadata.serveUrlFileName = file.serverUrl + "/" + file.fileName
+ metadata.sharePermissionsCollaborationServices = file.sharePermissionsCollaborationServices
+ for element in file.sharePermissionsCloudMesh {
+ metadata.sharePermissionsCloudMesh.append(element)
+ }
+ for element in file.shareType {
+ metadata.shareType.append(element)
+ }
+ for element in file.tags {
+ metadata.tags.append(element)
+ }
+ metadata.size = file.size
+ metadata.classFile = file.classFile
+ // iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+ if (metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown") && metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue {
+ metadata.classFile = NKCommon.TypeClassFile.document.rawValue
+ }
+ if let date = file.uploadDate {
+ metadata.uploadDate = date as NSDate
+ } else {
+ metadata.uploadDate = file.date as NSDate
+ }
+ metadata.urlBase = file.urlBase
+ metadata.user = file.user
+ metadata.userId = file.userId
+ metadata.latitude = file.latitude
+ metadata.longitude = file.longitude
+ metadata.altitude = file.altitude
+ metadata.height = Int(file.height)
+ metadata.width = Int(file.width)
+ metadata.livePhotoFile = file.livePhotoFile
+ metadata.isFlaggedAsLivePhotoByServer = file.isFlaggedAsLivePhotoByServer
+
+ // E2EE find the fileName for fileNameView
+ if isDirectoryE2EE || file.e2eEncrypted {
+ if let tableE2eEncryption = getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", file.account, file.serverUrl, file.fileName)) {
+ metadata.fileNameView = tableE2eEncryption.fileName
+ let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: file.contentType, directory: file.directory, account: file.account)
+ metadata.contentType = results.mimeType
+ metadata.iconName = results.iconName
+ metadata.classFile = results.classFile
+ }
}
+ return metadata
}
- func getMetadataProcess() async -> [tableMetadata] {
- return await core.performRealmReadAsync { realm in
- let predicate = NSPredicate(format: "status != %d", NCGlobal.shared.metadataStatusNormal)
- let sortDescriptors = [
- RealmSwift.SortDescriptor(keyPath: "status", ascending: false),
- RealmSwift.SortDescriptor(keyPath: "sessionDate", ascending: true)
- ]
- let limit = NCBrandOptions.shared.numMaximumProcess * 4
-
- let results = realm.objects(tableMetadata.self)
- .filter(predicate)
- .sorted(by: sortDescriptors)
-
- let sliced = results.prefix(limit)
- return sliced.map { $0.detachedCopy() }
- } ?? []
- }
-#endif
+ func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatas: [tableMetadata]) -> Void) {
+ var counter: Int = 0
+ var isDirectoryE2EE: Bool = false
+ let listServerUrl = ThreadSafeDictionary()
+ var metadataFolder = tableMetadata()
+ var metadatas: [tableMetadata] = []
- // MARK: - Realm Write
+ for file in files {
+ if let key = listServerUrl[file.serverUrl] {
+ isDirectoryE2EE = key
+ } else {
+ isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
+ listServerUrl[file.serverUrl] = isDirectoryE2EE
+ }
- func addAndReturnMetadata(_ metadata: tableMetadata) -> tableMetadata? {
- let detached = metadata.detachedCopy()
+ let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
- core.performRealmWrite { realm in
- realm.add(detached, update: .all)
- }
+ if counter == 0 && useFirstAsMetadataFolder {
+ metadataFolder = tableMetadata(value: metadata)
+ } else {
+ metadatas.append(metadata)
+ }
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter("ocId == %@", metadata.ocId)
- .first
- .map { $0.detachedCopy() }
+ counter += 1
}
+ completion(metadataFolder, metadatas)
}
+
+ func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatasFolder: [tableMetadata], _ metadatas: [tableMetadata]) -> Void) {
- func addAndReturnMetadataAsync(_ metadata: tableMetadata) async -> tableMetadata? {
- let detached = metadata.detachedCopy()
+ var counter: Int = 0
+ var isDirectoryE2EE: Bool = false
+ let listServerUrl = ThreadSafeDictionary()
- await core.performRealmWriteAsync { realm in
- realm.add(detached, update: .all)
- }
+ var metadataFolder = tableMetadata()
+ var metadataFolders: [tableMetadata] = []
+ var metadatas: [tableMetadata] = []
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter("ocId == %@", metadata.ocId)
- .first?
- .detachedCopy()
- }
- }
+ for file in files {
- func addMetadata(_ metadata: tableMetadata, sync: Bool = true) {
- let detached = metadata.detachedCopy()
+ if let key = listServerUrl[file.serverUrl] {
+ isDirectoryE2EE = key
+ } else {
+ isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
+ listServerUrl[file.serverUrl] = isDirectoryE2EE
+ }
- core.performRealmWrite(sync: sync) { realm in
- realm.add(detached, update: .all)
- }
- }
+ let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+
+ if counter == 0 && useMetadataFolder {
+ metadataFolder = tableMetadata.init(value: metadata)
+ } else {
+ metadatas.append(metadata)
+ if metadata.directory {
+ metadataFolders.append(metadata)
+ }
+ }
- func addMetadataAsync(_ metadata: tableMetadata) async {
- let detached = metadata.detachedCopy()
+ counter += 1
+ }
+
+ completion(metadataFolder, metadataFolders, metadatas)
+ }
+
+// func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatasFolder: [tableMetadata], _ metadatas: [tableMetadata]) -> Void) {
+//
+// var counter: Int = 0
+// var isDirectoryE2EE: Bool = false
+// let listServerUrl = ThreadSafeDictionary()
+//
+// var metadataFolder = tableMetadata()
+// var metadataFolders: [tableMetadata] = []
+// var metadatas: [tableMetadata] = []
+//
+// for file in files {
+//
+// if let key = listServerUrl[file.serverUrl] {
+// isDirectoryE2EE = key
+// } else {
+// isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
+// listServerUrl[file.serverUrl] = isDirectoryE2EE
+// }
+//
+// let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+//
+// if counter == 0 && useMetadataFolder {
+// metadataFolder = tableMetadata.init(value: metadata)
+// } else {
+// metadatas.append(metadata)
+// if metadata.directory {
+// metadataFolders.append(metadata)
+// }
+// }
+//
+// counter += 1
+// }
+//
+// completion(metadataFolder, metadataFolders, metadatas)
+// }
+
+ func getMetadataDirectoryFrom(files: [NKFile]) -> tableMetadata? {
+ guard let file = files.first else { return nil }
+ let isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
+ let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
- await core.performRealmWriteAsync { realm in
- realm.add(detached, update: .all)
- }
+ return metadata
}
- func addMetadatas(_ metadatas: [tableMetadata], sync: Bool = true) {
- let detached = metadatas.map { $0.detachedCopy() }
-
- core.performRealmWrite(sync: sync) { realm in
- realm.add(detached, update: .all)
- }
+ func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatas: [tableMetadata]) {
+ await withUnsafeContinuation({ continuation in
+ convertFilesToMetadatas(files, useFirstAsMetadataFolder: useFirstAsMetadataFolder) { metadataFolder, metadatas in
+ continuation.resume(returning: (metadataFolder, metadatas))
+ }
+ })
}
- func addMetadatasAsync(_ metadatas: [tableMetadata]) async {
- let detached = metadatas.map { $0.detachedCopy() }
+ func createMetadata(fileName: String, fileNameView: String, ocId: String, serverUrl: String, url: String, contentType: String, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil, directory: Bool = false, session: NCSession.Session, sceneIdentifier: String?) -> tableMetadata {
+ let metadata = tableMetadata()
- await core.performRealmWriteAsync { realm in
- realm.add(detached, update: .all)
+ if isUrl {
+ metadata.contentType = "text/uri-list"
+ if let iconName = iconName {
+ metadata.iconName = iconName
+ } else {
+ metadata.iconName = NKCommon.TypeClassFile.url.rawValue
+ }
+ metadata.classFile = NKCommon.TypeClassFile.url.rawValue
+ } else {
+ let (mimeType, classFile, iconName, _, _, _) = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: contentType, directory: directory, account: session.account)
+ metadata.contentType = mimeType
+ metadata.iconName = iconName
+ metadata.classFile = classFile
+ // iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+ if classFile == NKCommon.TypeClassFile.unknow.rawValue && (mimeType == "text/x-markdown" || mimeType == "text/markdown") {
+ metadata.iconName = NKCommon.TypeIconFile.txt.rawValue
+ metadata.classFile = NKCommon.TypeClassFile.document.rawValue
+ }
}
+ if let iconUrl = iconUrl {
+ metadata.iconUrl = iconUrl
+ }
+
+ let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ metadata.account = session.account
+ metadata.creationDate = Date() as NSDate
+ metadata.date = Date() as NSDate
+ metadata.directory = directory
+ metadata.hasPreview = true
+ metadata.etag = ocId
+ metadata.fileName = fileName
+ metadata.fileNameView = fileName
+ metadata.name = name
+ metadata.ocId = ocId
+ metadata.ocIdTransfer = ocId
+ metadata.permissions = "RGDNVW"
+ metadata.serverUrl = serverUrl
+ metadata.serveUrlFileName = serverUrl + "/" + fileName
+ metadata.subline = subline
+ metadata.uploadDate = Date() as NSDate
+ metadata.url = url
+ metadata.urlBase = session.urlBase
+ metadata.user = session.user
+ metadata.userId = session.userId
+ metadata.sceneIdentifier = sceneIdentifier
+ metadata.nativeFormat = !NCKeychain().formatCompatibility
+
+ if !metadata.urlBase.isEmpty, metadata.serverUrl.hasPrefix(metadata.urlBase) {
+ metadata.path = String(metadata.serverUrl.dropFirst(metadata.urlBase.count)) + "/"
+ }
+ return metadata
}
- func addMetadataIfNotExistsAsync(_ metadata: tableMetadata) async {
- let detached = metadata.detachedCopy()
+ func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
+ let permissions = NCPermissions()
+ var isShare = false
+ var isMounted = false
- await core.performRealmWriteAsync { realm in
- if realm.object(ofType: tableMetadata.self, forPrimaryKey: metadata.ocId) == nil {
- realm.add(detached)
- }
+ if metadataFolder != nil {
+ isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
+ isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
+ } else if let directory = getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+ isShare = metadata.permissions.contains(permissions.permissionShared) && !directory.permissions.contains(permissions.permissionShared)
+ isMounted = metadata.permissions.contains(permissions.permissionMounted) && !directory.permissions.contains(permissions.permissionMounted)
}
- }
- func deleteMetadataAsync(predicate: NSPredicate) async {
- await core.performRealmWriteAsync { realm in
- let result = realm.objects(tableMetadata.self)
- .filter(predicate)
- realm.delete(result)
+ if isShare || isMounted {
+ return true
+ } else {
+ return false
}
}
- func deleteMetadataAsync(id: String?) async {
- guard let id else { return }
+ // MARK: - Set
- await core.performRealmWriteAsync { realm in
- let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@ OR fileId == %@", id, id)
- realm.delete(result)
+ @discardableResult
+ func addMetadata(_ metadata: tableMetadata) -> tableMetadata {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ return tableMetadata(value: realm.create(tableMetadata.self, value: metadata, update: .all))
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
+
+ return tableMetadata(value: metadata)
}
- func deleteMetadataAsync(ocId: String) async {
- await core.performRealmWriteAsync { realm in
- if let object = realm.object(ofType: tableMetadata.self, forPrimaryKey: ocId) {
- realm.delete(object)
+ func addMetadatas(_ metadatas: [tableMetadata]) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ for metadata in metadatas {
+ realm.create(tableMetadata.self, value: metadata, update: .all)
+ }
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func replaceMetadataAsync(ocId: String, metadata: tableMetadata) async {
- let detached = metadata.detachedCopy()
-
- await core.performRealmWriteAsync { realm in
- if let object = realm.object(ofType: tableMetadata.self, forPrimaryKey: ocId) {
- realm.delete(object)
+ func deleteMetadata(predicate: NSPredicate) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ realm.delete(results)
}
- realm.add(detached, update: .modified)
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func replaceMetadatasAsync(ocId: [String], metadatas: [tableMetadata]) async {
- guard !ocId.isEmpty else {
- return
- }
- var detacheds: [tableMetadata] = []
- for metadata in metadatas {
- metadata.ocIdTransfer = metadata.ocId
- detacheds.append(metadata.detachedCopy())
- }
+ func deleteMetadataOcId(_ ocId: String?) {
+ guard let ocId else { return }
- await core.performRealmWriteAsync { realm in
- let results = realm.objects(tableMetadata.self)
- .filter("ocId IN %@", ocId)
- realm.delete(results)
- realm.add(detacheds, update: .all)
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter("ocId == %@", ocId)
+ realm.delete(results)
+ }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
}
- // Asynchronously deletes an array of `tableMetadata` entries from the Realm database.
- /// - Parameter metadatas: The `tableMetadata` objects to be deleted.
- func deleteMetadatasAsync(_ metadatas: [tableMetadata]) async {
- guard !metadatas.isEmpty else {
- return
+ func deleteMetadataOcIds(_ ocIds: [String]) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter("ocId IN %@", ocIds)
+ realm.delete(results)
+ }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
- let detached = metadatas.map { $0.detachedCopy() }
+ }
- await core.performRealmWriteAsync { realm in
- for detached in detached {
- if let managed = realm.object(ofType: tableMetadata.self, forPrimaryKey: detached.ocId) {
- realm.delete(managed)
- }
+ func deleteMetadatas(_ metadatas: [tableMetadata]) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ realm.delete(metadatas)
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func renameMetadata(fileNameNew: String, ocId: String, status: Int = NCGlobal.shared.metadataStatusNormal) async {
- await core.performRealmWriteAsync { realm in
- guard let metadata = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first else {
- return
- }
+ func renameMetadata(fileNameNew: String, ocId: String, status: Int = NCGlobal.shared.metadataStatusNormal) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+ let fileNameView = result.fileNameView
+ let fileIdMOV = result.livePhotoFile
+ let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameView)
+ let resultsType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameNew, mimeType: "", directory: result.directory, account: result.account)
+
+ result.fileName = fileNameNew
+ result.fileNameView = fileNameNew
+ result.iconName = resultsType.iconName
+ result.contentType = resultsType.mimeType
+ result.classFile = resultsType.classFile
+ result.status = status
+
+ if status == NCGlobal.shared.metadataStatusNormal {
+ result.sessionDate = nil
+ } else {
+ result.sessionDate = Date()
+ }
- let utilityFileSystem = NCUtilityFileSystem()
- let oldFileNameView = metadata.fileNameView
- let account = metadata.account
- let originalServerUrl = metadata.serverUrl
+ if result.directory,
+ let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
+ let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameNew)
- metadata.fileName = fileNameNew
- metadata.fileNameView = fileNameNew
- metadata.status = status
- metadata.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
+ resultDirectory.serverUrl = serverUrlTo
+ } else {
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameNew
- if metadata.directory {
- let oldDirUrl = utilityFileSystem.createServerUrl(serverUrl: originalServerUrl, fileName: oldFileNameView)
- let newDirUrl = utilityFileSystem.createServerUrl(serverUrl: originalServerUrl, fileName: fileNameNew)
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
+
+ if result.isLivePhoto,
+ let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
+ let fileNameView = resultMOV.fileNameView
+ let fileName = (fileNameNew as NSString).deletingPathExtension
+ let ext = (resultMOV.fileName as NSString).pathExtension
+ resultMOV.fileName = fileName + "." + ext
+ resultMOV.fileNameView = fileName + "." + ext
- if let dir = realm.objects(tableDirectory.self)
- .filter("account == %@ AND serverUrl == %@", account, oldDirUrl)
- .first {
- dir.serverUrl = newDirUrl
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
+
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
}
- } else {
- let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, userId: metadata.userId, urlBase: metadata.urlBase) + "/" + oldFileNameView
- let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, userId: metadata.userId, urlBase: metadata.urlBase) + "/" + fileNameNew
- utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- /// Asynchronously restores the file name of a metadata entry and updates related file system and Realm entries.
- /// - Parameter ocId: The object ID (ocId) of the file to restore.
- func restoreMetadataFileNameAsync(ocId: String) async {
- await core.performRealmWriteAsync { realm in
- guard let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first,
- let encodedURLString = result.serverUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
- let url = URL(string: encodedURLString)
- else {
- return
- }
+ func restoreMetadataFileName(ocId: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first,
+ let encodedURLString = result.serveUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
+ let url = URL(string: encodedURLString) {
+ let fileIdMOV = result.livePhotoFile
+ let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileNameView)
+ let lastPathComponent = url.lastPathComponent
+ let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
+ let fileNameView = result.fileNameView
+
+ result.fileName = fileName
+ result.fileNameView = fileName
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
+
+ if result.directory,
+ let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
+ let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileName)
+
+ resultDirectory.serverUrl = serverUrlTo
+ } else {
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileName
+
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
- let utilityFileSystem = NCUtilityFileSystem()
- let fileIdMOV = result.livePhotoFile
- let directoryServerUrl = utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: result.fileNameView)
- let lastPathComponent = url.lastPathComponent
- let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
- let fileNameView = result.fileNameView
-
- result.fileName = fileName
- result.fileNameView = fileName
- result.status = NCGlobal.shared.metadataStatusNormal
- result.sessionDate = nil
-
- if result.directory,
- let resultDirectory = realm.objects(tableDirectory.self)
- .filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl)
- .first {
- let serverUrlTo = utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: fileName)
- resultDirectory.serverUrl = serverUrlTo
- } else {
- let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileNameView
- let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileName
- utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
+ if result.isLivePhoto,
+ let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
+ let fileNameView = resultMOV.fileNameView
+ let fileName = (fileName as NSString).deletingPathExtension
+ let ext = (resultMOV.fileName as NSString).pathExtension
+ resultMOV.fileName = fileName + "." + ext
+ resultMOV.fileNameView = fileName + "." + ext
- if result.isLivePhoto,
- let resultMOV = realm.objects(tableMetadata.self)
- .filter("fileId == %@ AND account == %@", fileIdMOV, result.account)
- .first {
- let fileNameViewMOV = resultMOV.fileNameView
- let baseName = (fileName as NSString).deletingPathExtension
- let ext = (resultMOV.fileName as NSString).pathExtension
- let fullFileName = baseName + "." + ext
-
- resultMOV.fileName = fullFileName
- resultMOV.fileNameView = fullFileName
-
- let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fileNameViewMOV
- let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fullFileName
- utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
- }
- }
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
- func setMetadataServerUrlFileNameStatusNormalAsync(ocId: String) async {
- await core.performRealmWriteAsync { realm in
- if let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first {
- result.serverUrlFileName = NCUtilityFileSystem().createServerUrl(serverUrl: result.serverUrl, fileName: result.fileName)
- result.status = NCGlobal.shared.metadataStatusNormal
- result.sessionDate = nil
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
+ }
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataLivePhotoByServerAsync(account: String,
- ocId: String,
- livePhotoFile: String) async {
- await core.performRealmWriteAsync { realm in
- if let result = realm.objects(tableMetadata.self)
- .filter("account == %@ AND ocId == %@", account, ocId)
- .first {
- result.isFlaggedAsLivePhotoByServer = true
- result.livePhotoFile = livePhotoFile
+ func setMetadataServeUrlFileNameStatusNormal(ocId: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+ result.serveUrlFileName = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileName)
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
+ }
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func updateMetadatasFavoriteAsync(account: String, metadatas: [tableMetadata]) async {
- guard !metadatas.isEmpty else { return }
-
- await core.performRealmWriteAsync { realm in
- let oldFavorites = realm.objects(tableMetadata.self)
- .filter("account == %@ AND favorite == true", account)
- for item in oldFavorites {
- item.favorite = false
- }
- realm.add(metadatas, update: .all)
- }
- }
-
- /// Asynchronously updates a list of `tableMetadata` entries in Realm for a given account and server URL.
- ///
- /// This function performs the following steps:
- /// 1. Skips all entries with `status != metadataStatusNormal`.
- /// 2. Deletes existing metadata entries with `status == metadataStatusNormal` that are not in the skip list.
- /// 3. Copies matching `mediaSearch` from previously deleted metadata to the incoming list.
- /// 4. Inserts or updates new metadata entries into Realm, except those in the skip list.
- ///
- /// - Parameters:
- /// - metadatas: An array of incoming detached `tableMetadata` objects to insert or update.
- /// - serverUrl: The server URL associated with the metadata entries.
- /// - account: The account identifier used to scope the metadata update.
- func updateMetadatasFilesAsync(_ metadatas: [tableMetadata], serverUrl: String, account: String) async {
- await core.performRealmWriteAsync { realm in
- let ocIdsToSkip = Set(
- realm.objects(tableMetadata.self)
- .filter("status != %d", NCGlobal.shared.metadataStatusNormal)
- .map(\.ocId)
- )
-
- let resultsToDelete = realm.objects(tableMetadata.self)
- .filter("account == %@ AND serverUrl == %@ AND status == %d AND fileName != %@", account, serverUrl, NCGlobal.shared.metadataStatusNormal, NextcloudKit.shared.nkCommonInstance.rootFileName)
- .filter { !ocIdsToSkip.contains($0.ocId) }
-
- // Cache mediaSearch (and anything else needed) before deletion, keyed by ocId.
- let metadatasByOcId: [String: tableMetadata] = Dictionary(
- uniqueKeysWithValues: resultsToDelete.map { object in
- (object.ocId, tableMetadata(value: object))
- }
- )
-
- realm.delete(resultsToDelete)
+ func setMetadataEtagResource(ocId: String, etagResource: String?) {
+ guard let etagResource else { return }
- for metadata in metadatas {
- guard !ocIdsToSkip.contains(metadata.ocId) else {
- continue
- }
- if let previous = metadatasByOcId[metadata.ocId] {
- metadata.mediaSearch = previous.mediaSearch
- }
-
- realm.add(metadata.detachedCopy(), update: .all)
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+ result?.etagResource = etagResource
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataEncryptedAsync(ocId: String, encrypted: Bool) async {
- await core.performRealmWriteAsync { realm in
- let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first
- result?.e2eEncrypted = encrypted
+ func setMetadataLivePhotoByServer(account: String, ocId: String, livePhotoFile: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ if let result = realm.objects(tableMetadata.self).filter("account == %@ AND ocId == %@", account, ocId).first {
+ result.isFlaggedAsLivePhotoByServer = true
+ result.livePhotoFile = livePhotoFile
+ }
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataFileNameViewAsync(serverUrl: String, fileName: String, newFileNameView: String, account: String) async {
- await core.performRealmWriteAsync { realm in
- let result = realm.objects(tableMetadata.self)
- .filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName)
- .first
- result?.fileNameView = newFileNameView
+ func updateMetadatasFavorite(account: String, metadatas: [tableMetadata]) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter("account == %@ AND favorite == true", account)
+ for result in results {
+ result.favorite = false
+ }
+ realm.add(metadatas, update: .all)
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func moveMetadataAsync(ocId: String, serverUrlTo: String) async {
- await core.performRealmWriteAsync { realm in
- if let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first {
- result.serverUrl = serverUrlTo
+ func updateMetadatasFiles(_ metadatas: [tableMetadata], serverUrl: String, account: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal))
+ realm.delete(results)
+ for metadata in metadatas {
+ if realm.objects(tableMetadata.self).filter(NSPredicate(format: "ocId == %@ AND status != %d", metadata.ocId, NCGlobal.shared.metadataStatusNormal)).first != nil {
+ continue
+ }
+ realm.add(tableMetadata(value: metadata), update: .all)
+ }
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setLivePhotoFile(fileId: String, livePhotoFile: String) async {
- await core.performRealmWriteAsync { realm in
- let result = realm.objects(tableMetadata.self)
- .filter("fileId == %@", fileId)
- .first
- result?.livePhotoFile = livePhotoFile
- }
- }
-
- func clearAssetLocalIdentifiersAsync(_ assetLocalIdentifiers: [String]) async {
- await core.performRealmWriteAsync { realm in
- let results = realm.objects(tableMetadata.self)
- .filter("assetLocalIdentifier IN %@", assetLocalIdentifiers)
- for result in results {
- result.assetLocalIdentifier = ""
+ func setMetadataEncrypted(ocId: String, encrypted: Bool) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+ result?.e2eEncrypted = encrypted
}
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- /// Asynchronously sets the favorite status of a `tableMetadata` entry.
- /// Optionally stores the previous favorite flag and updates the sync status.
- func setMetadataFavoriteAsync(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int) async {
- await core.performRealmWriteAsync { realm in
- guard let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first else {
- return
- }
-
- if let favorite {
- result.favorite = favorite
+ func setMetadataFileNameView(serverUrl: String, fileName: String, newFileNameView: String, account: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first
+ result?.fileNameView = newFileNameView
}
-
- result.storeFlag = saveOldFavorite
- result.status = status
- result.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- /// Asynchronously updates a `tableMetadata` entry to set copy/move status and target server URL.
- func setMetadataCopyMoveAsync(ocId: String, destination: String, overwrite: String?, status: Int) async {
- await core.performRealmWriteAsync { realm in
- guard let result = realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first else {
- return
+ func moveMetadata(ocId: String, serverUrlTo: String) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+ result.serverUrl = serverUrlTo
+ }
}
-
- result.destination = destination
- result.storeFlag = overwrite
- result.status = status
- result.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func clearMetadatasUploadAsync(account: String) async {
- await core.performRealmWriteAsync { realm in
- let results = realm.objects(tableMetadata.self)
- .filter("account == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusUploadError)
- realm.delete(results)
+ func clearAssetLocalIdentifiers(_ assetLocalIdentifiers: [String]) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier IN %@", assetLocalIdentifiers)
+ for result in results {
+ result.assetLocalIdentifier = ""
+ }
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- /// Syncs the remote and local metadata.
- /// Returns true if there were changes (additions or deletions), false if everything was already up-to-date.
- func mergeRemoteMetadatasAsync(remoteMetadatas: [tableMetadata], localMetadatas: [tableMetadata]) async -> Bool {
- // Set of ocId
- let remoteOcIds = Set(remoteMetadatas.map { $0.ocId })
- let localOcIds = Set(localMetadatas.map { $0.ocId })
-
- // Calculate diffs
- let toDeleteOcIds = localOcIds.subtracting(remoteOcIds)
- let toAddOcIds = remoteOcIds.subtracting(localOcIds)
+ func setMetadataFavorite(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+ if let favorite {
+ result?.favorite = favorite
+ }
+ result?.storeFlag = saveOldFavorite
+ result?.status = status
- guard !toDeleteOcIds.isEmpty || !toAddOcIds.isEmpty else {
- return false // No changes needed
+ if status == NCGlobal.shared.metadataStatusNormal {
+ result?.sessionDate = nil
+ } else {
+ result?.sessionDate = Date()
+ }
+ }
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
+ }
- let toDeleteKeys = Array(toDeleteOcIds)
+ func setMetadataCopyMove(ocId: String, serverUrlTo: String, overwrite: String?, status: Int) {
+ do {
+ let realm = try Realm()
+ try realm.write {
+ let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+ result?.serverUrlTo = serverUrlTo
+ result?.storeFlag = overwrite
+ result?.status = status
- await core.performRealmWriteAsync { realm in
- let toAdd = remoteMetadatas.filter { toAddOcIds.contains($0.ocId) }
- let toDelete = toDeleteKeys.compactMap {
- realm.object(ofType: tableMetadata.self, forPrimaryKey: $0)
+ if status == NCGlobal.shared.metadataStatusNormal {
+ result?.sessionDate = nil
+ } else {
+ result?.sessionDate = Date()
+ }
}
-
- realm.delete(toDelete)
- realm.add(toAdd, update: .modified)
+ } catch let error {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
-
- return true
}
- // MARK: - Realm Read
-
- func getAllTableMetadataAsync() async -> [tableMetadata] {
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self).map { tableMetadata(value: $0) }
- } ?? []
- }
+ // MARK: - GetMetadata
func getMetadata(predicate: NSPredicate) -> tableMetadata? {
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .first
- .map { $0.detachedCopy() }
- }
- }
-
- func getMetadataAsync(predicate: NSPredicate) async -> tableMetadata? {
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .first
- .map { $0.detachedCopy() }
+ do {
+ let realm = try Realm()
+ guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
+ return tableMetadata(value: result)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
- }
- func getResultsMetadatasAsync(predicate: NSPredicate) async -> Results? {
- await core.performRealmReadAsync { realm in
- let results = realm.objects(tableMetadata.self)
- .filter(predicate)
- return results.freeze()
- }
+ return nil
}
func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
- core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .map { $0.detachedCopy() }
- } ?? []
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ return Array(results.map { tableMetadata(value: $0) })
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return []
}
- func getMetadatas(predicate: NSPredicate,
- sortedByKeyPath: String,
- ascending: Bool = false) -> [tableMetadata]? {
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
- .map { $0.detachedCopy() }
+ func getMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool = false) -> [tableMetadata]? {
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
+ return Array(results.map { tableMetadata(value: $0) })
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return nil
}
- func getMetadatasAsync(predicate: NSPredicate,
- sortedByKeyPath: String,
- ascending: Bool = false,
- limit: Int? = nil) async -> [tableMetadata]? {
- return await core.performRealmReadAsync { realm in
- let results = realm.objects(tableMetadata.self)
- .filter(predicate)
- .sorted(byKeyPath: sortedByKeyPath,
- ascending: ascending)
+ func getMetadatas(predicate: NSPredicate, numItems: Int, sorted: String, ascending: Bool) -> [tableMetadata] {
+ var counter: Int = 0
+ var metadatas: [tableMetadata] = []
- if let limit {
- let sliced = results.prefix(limit)
- return sliced.map { $0.detachedCopy() }
- } else {
- return results.map { $0.detachedCopy() }
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
+ for result in results where counter < numItems {
+ metadatas.append(tableMetadata(value: result))
+ counter += 1
}
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
- }
-
- func getMetadatas(predicate: NSPredicate,
- numItems: Int,
- sorted: String,
- ascending: Bool) -> [tableMetadata] {
- return core.performRealmRead { realm in
- let results = realm.objects(tableMetadata.self)
- .filter(predicate)
- .sorted(byKeyPath: sorted, ascending: ascending)
- return results.prefix(numItems)
- .map { $0.detachedCopy() }
- } ?? []
+ return metadatas
}
func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
guard let ocId else { return nil }
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first
- .map { $0.detachedCopy() }
- }
- }
-
- func getMetadataFromOcIdAsync(_ ocId: String?) async -> tableMetadata? {
+ do {
+ let realm = try Realm()
+ guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
+ return tableMetadata(value: result)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
+ }
+
+// func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
+// guard let ocId else { return nil }
+//
+// do {
+// let realm = try Realm()
+// if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+// return tableMetadata(value: result)
+// }
+// if let result = realm.objects(tableMetadata.self).filter("ocIdTransfer == %@", ocId).first {
+// return tableMetadata(value: result)
+// }
+// } catch let error as NSError {
+// NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+// }
+// return nil
+// }
+
+ func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
guard let ocId else { return nil }
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter("ocId == %@", ocId)
- .first
- .map { $0.detachedCopy() }
- }
- }
-
- func getMetadataFromOcIdAndocIdTransferAsync(_ ocId: String?) async -> tableMetadata? {
- guard let ocId else {
- return nil
- }
-
- return await core.performRealmReadAsync { realm in
+ return performRealmRead { realm in
realm.objects(tableMetadata.self)
.filter("ocId == %@ OR ocIdTransfer == %@", ocId, ocId)
.first
- .map { $0.detachedCopy() }
+ .map { tableMetadata(value: $0) }
}
}
-
- /// Asynchronously retrieves the metadata for a folder, based on its session and serverUrl.
- /// Handles the home directory case rootFileName) and detaches the Realm object before returning.
- func getMetadataFolderAsync(session: NCSession.Session, serverUrl: String) async -> tableMetadata? {
+
+// func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
+// guard let ocId else { return nil }
+//
+// return performRealmRead { realm in
+// // First, try to find by ocId
+// if let result = realm.objects(tableMetadata.self)
+// .filter("ocId == %@", ocId)
+// .first {
+// return tableMetadata(value: result)
+// }
+//
+// // If not found, try to find by ocIdTransfer
+// if let result = realm.objects(tableMetadata.self)
+// .filter("ocIdTransfer == %@", ocId)
+// .first {
+// return tableMetadata(value: result)
+// }
+//
+// // Not found
+// return nil
+// }
+// }
+
+ func getMetadataFolder(session: NCSession.Session, serverUrl: String) -> tableMetadata? {
var serverUrl = serverUrl
var fileName = ""
- let home = NCUtilityFileSystem().getHomeServer(session: session)
+ let serverUrlHome = utilityFileSystem.getHomeServer(session: session)
- if home == serverUrl {
- fileName = NextcloudKit.shared.nkCommonInstance.rootFileName
+ if serverUrlHome == serverUrl {
+ fileName = "."
+ serverUrl = ".."
} else {
fileName = (serverUrl as NSString).lastPathComponent
- if let serverDirectoryUp = NCUtilityFileSystem().serverDirectoryUp(serverUrl: serverUrl, home: home) {
- serverUrl = serverDirectoryUp
+ if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) {
+ serverUrl = path
}
}
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter("account == %@ AND serverUrl == %@ AND fileName == %@", session.account, serverUrl, fileName)
- .first
- .map { $0.detachedCopy() }
+ do {
+ let realm = try Realm()
+ guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", session.account, serverUrl, fileName).first else { return nil }
+ return tableMetadata(value: result)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return nil
}
func getMetadataLivePhoto(metadata: tableMetadata) -> tableMetadata? {
- guard metadata.isLivePhoto else {
- return nil
- }
- let detached = metadata.detachedCopy()
-
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
- detached.account,
- detached.serverUrl,
- detached.livePhotoFile))
- .first
- .map { $0.detachedCopy() }
- }
- }
-
- func getMetadataLivePhotoAsync(metadata: tableMetadata) async -> tableMetadata? {
- guard metadata.isLivePhoto else {
- return nil
- }
- let detached = metadata.detachedCopy()
+ guard metadata.isLivePhoto else { return nil }
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
- detached.account,
- detached.serverUrl,
- detached.livePhotoFile))
- .first
- .map { $0.detachedCopy() }
+ do {
+ let realm = try Realm()
+ guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
+ metadata.account,
+ metadata.serverUrl,
+ metadata.livePhotoFile)).first else { return nil }
+ return tableMetadata(value: result)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return nil
}
func getMetadataConflict(account: String, serverUrl: String, fileNameView: String, nativeFormat: Bool) -> tableMetadata? {
@@ -1128,109 +1200,122 @@ extension NCManageDatabase {
fileNameConflict))
}
- func getTableMetadatasDirectoryFavoriteIdentifierRankAsync(account: String) async -> [String: NSNumber] {
- let result = await core.performRealmReadAsync { realm in
- var listIdentifierRank: [String: NSNumber] = [:]
- var counter = Int64(10)
+ // MARK: - GetResult(s)Metadata
- let results = realm.objects(tableMetadata.self)
- .filter("account == %@ AND directory == true AND favorite == true", account)
- .sorted(byKeyPath: "fileNameView", ascending: true)
-
- results.forEach { item in
- counter += 1
- listIdentifierRank[item.ocId] = NSNumber(value: counter)
+ func getResultsMetadatasPredicate(_ predicate: NSPredicate, layoutForView: NCDBLayoutForView?, directoryOnTop: Bool = true) -> [tableMetadata] {
+ do {
+ let realm = try Realm()
+ var results = realm.objects(tableMetadata.self).filter(predicate).freeze()
+ let layout: NCDBLayoutForView = layoutForView ?? NCDBLayoutForView()
+
+ if layout.sort == "fileName" {
+ let sortedResults = results.sorted {
+ let ordered = layout.ascending ? ComparisonResult.orderedAscending : ComparisonResult.orderedDescending
+ // 1. favorite order
+ if $0.favorite == $1.favorite {
+ // 2. directory order TOP
+ if directoryOnTop {
+ if $0.directory == $1.directory {
+ // 3. natural fileName
+ return $0.fileNameView.localizedStandardCompare($1.fileNameView) == ordered
+ } else {
+ return $0.directory && !$1.directory
+ }
+ } else {
+ return $0.fileNameView.localizedStandardCompare($1.fileNameView) == ordered
+ }
+ } else {
+ return $0.favorite && !$1.favorite
+ }
+ }
+ return sortedResults
+ } else {
+ if directoryOnTop {
+ results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false).sorted(byKeyPath: "directory", ascending: false)
+ } else {
+ results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false)
+ }
}
+ return Array(results)
- return listIdentifierRank
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
- return result ?? [:]
+ return []
}
-#if !EXTENSION
- /// Asynchronously retrieves and sorts `tableMetadata` associated with groupfolders for a given session.
- /// - Parameters:
- /// - session: The `NCSession.Session` containing account and server information.
- /// - layoutForView: An optional layout configuration used for sorting.
- /// - Returns: An array of sorted and detached `tableMetadata` objects.
- func getMetadatasFromGroupfoldersAsync(session: NCSession.Session, layoutForView: NCDBLayoutForView?) async -> [tableMetadata] {
- let homeServerUrl = NCUtilityFileSystem().getHomeServer(session: session)
+ func getResultsMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool, arraySlice: Int) -> [tableMetadata] {
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending).prefix(arraySlice)
+ return Array(results)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return []
+ }
+
+ func getResultMetadata(predicate: NSPredicate) -> tableMetadata? {
+ do {
+ let realm = try Realm()
+ return realm.objects(tableMetadata.self).filter(predicate).first
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
+ }
+
+ func getResultMetadataFromFileName(_ fileName: String, serverUrl: String, sessionTaskIdentifier: Int) -> tableMetadata? {
+ do {
+ let realm = try Realm()
+ return realm.objects(tableMetadata.self).filter("fileName == %@ AND serverUrl == %@ AND sessionTaskIdentifier == %d", fileName, serverUrl, sessionTaskIdentifier).first
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
+ }
- let detachedMetadatas: [tableMetadata] = await core.performRealmReadAsync { realm in
- var ocIds: [String] = []
+ func getResultsMetadatasFromGroupfolders(session: NCSession.Session) -> Results? {
+ var ocId: [String] = []
+ let homeServerUrl = utilityFileSystem.getHomeServer(session: session)
- // Safely fetch and detach groupfolders
- let groupfolders = realm.objects(TableGroupfolders.self)
- .filter("account == %@", session.account)
- .sorted(byKeyPath: "mountPoint", ascending: true)
- .map { TableGroupfolders(value: $0) }
+ do {
+ let realm = try Realm()
+ let groupfolders = realm.objects(TableGroupfolders.self).filter("account == %@", session.account).sorted(byKeyPath: "mountPoint", ascending: true)
for groupfolder in groupfolders {
let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
let serverUrlFileName = homeServerUrl + mountPoint
- if let directory = realm.objects(tableDirectory.self)
- .filter("account == %@ AND serverUrl == %@", session.account, serverUrlFileName)
- .first,
- let metadata = realm.objects(tableMetadata.self)
- .filter("ocId == %@", directory.ocId)
- .first {
- ocIds.append(metadata.ocId)
+ if let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", session.account, serverUrlFileName).first,
+ let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first {
+ ocId.append(result.ocId)
}
}
- // Fetch and detach the corresponding metadatas
- return realm.objects(tableMetadata.self)
- .filter("ocId IN %@", ocIds)
- .map { $0.detachedCopy() }
- } ?? []
-
- let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: session.account, metadatas: detachedMetadatas)
- return sorted
- }
-#endif
-
- func getRootContainerMetadataAsync(accout: String) async -> tableMetadata? {
- return await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter("fileName == %@ AND account == %@", NextcloudKit.shared.nkCommonInstance.rootFileName, accout)
- .first
- .map { $0.detachedCopy() }
+ return realm.objects(tableMetadata.self).filter("ocId IN %@", ocId)
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
- }
- func getMetadatasAsync(predicate: NSPredicate) async -> [tableMetadata] {
- await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .map { $0.detachedCopy() }
- } ?? []
+ return nil
}
- func getTableMetadatasDirectoryFavoriteIdentifierRankAsync(account: String) async -> [String: NSNumber] {
- let result = await performRealmReadAsync { realm in
- var listIdentifierRank: [String: NSNumber] = [:]
- var counter = Int64(10)
+ func getTableMetadatasDirectoryFavoriteIdentifierRank(account: String) -> [String: NSNumber] {
+ var listIdentifierRank: [String: NSNumber] = [:]
+ var counter = 10 as Int64
- let results = realm.objects(tableMetadata.self)
- .filter("account == %@ AND directory == true AND favorite == true", account)
- .sorted(byKeyPath: "fileNameView", ascending: true)
-
- results.forEach { item in
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter("account == %@ AND directory == true AND favorite == true", account).sorted(byKeyPath: "fileNameView", ascending: true)
+ for result in results {
counter += 1
- listIdentifierRank[item.ocId] = NSNumber(value: counter)
+ listIdentifierRank[result.ocId] = NSNumber(value: Int64(counter))
}
-
- return listIdentifierRank
- }
- return result ?? [:]
- }
-
- func getAssetLocalIdentifiersUploadedAsync() async -> [String]? {
- return await core.performRealmReadAsync { realm in
- let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
- return results.map { $0.assetLocalIdentifier }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return listIdentifierRank
}
@objc func clearMetadatasUpload(account: String) {
@@ -1244,106 +1329,59 @@ extension NCManageDatabase {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
-
- func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
- guard let fileId else {
- return nil
- }
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter("fileId == %@", fileId)
- .first
- .map { $0.detachedCopy() }
- }
- }
-
- /// Asynchronously retrieves a `tableMetadata` object matching the given `fileId`, if available.
- /// - Parameter fileId: The file identifier used to query the Realm database.
- /// - Returns: A detached copy of the `tableMetadata` object, or `nil` if not found.
- func getMetadataFromFileIdAsync(_ fileId: String?) async -> tableMetadata? {
- guard let fileId else {
- return nil
- }
+ func getAssetLocalIdentifiersUploaded() -> [String]? {
+ var assetLocalIdentifiers: [String] = []
- return await core.performRealmReadAsync { realm in
- let object = realm.objects(tableMetadata.self)
- .filter("fileId == %@", fileId)
- .first
- return object?.detachedCopy()
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
+ for result in results {
+ assetLocalIdentifiers.append(result.assetLocalIdentifier)
+ }
+ return assetLocalIdentifiers
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return nil
}
-#if !EXTENSION_FILE_PROVIDER_EXTENSION
- /// Asynchronously retrieves and sorts `tableMetadata` objects matching a given predicate and layout.
- func getMetadatasAsync(predicate: NSPredicate,
- withLayout layoutForView: NCDBLayoutForView?,
- withAccount account: String) async -> [tableMetadata] {
- let detachedMetadatas = await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .map { $0.detachedCopy() }
- } ?? []
-
- let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: account, metadatas: detachedMetadatas)
- return sorted
- }
-
- /// Asynchronously retrieves and sorts `tableMetadata` objects matching a given predicate and layout.
- func getMetadatasAsyncDataSource(withServerUrl serverUrl: String,
- withUserId userId: String,
- withAccount account: String,
- withLayout layoutForView: NCDBLayoutForView?,
- withPreficate predicateSource: NSPredicate? = nil) async -> [tableMetadata] {
- var predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName != %@ AND NOT (status IN %@)", account, serverUrl, NextcloudKit.shared.nkCommonInstance.rootFileName, NCGlobal.shared.metadataStatusHideInView)
-
- if NCPreferences().getPersonalFilesOnly(account: account) {
- predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName != %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@)", account, serverUrl, NextcloudKit.shared.nkCommonInstance.rootFileName, userId, NCGlobal.shared.metadataStatusHideInView)
- }
-
- if let predicateSource {
- predicate = predicateSource
+ func getMetadataFromDirectory(account: String, serverUrl: String) -> Bool {
+ do {
+ let realm = try Realm()
+ guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first,
+ realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first != nil else { return false }
+ return true
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
-
- let detachedMetadatas = await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .map { $0.detachedCopy() }
- } ?? []
-
- let cleanedMetadatas = filterAndNormalizeLivePhotos(from: detachedMetadatas)
- let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: account, metadatas: cleanedMetadatas)
-
- return sorted
+ return false
}
-#endif
- func getMetadatasAsync(predicate: NSPredicate,
- withSort sortDescriptors: [RealmSwift.SortDescriptor] = [],
- withLimit limit: Int? = nil) async -> [tableMetadata]? {
- await core.performRealmReadAsync { realm in
- var results = realm.objects(tableMetadata.self)
- .filter(predicate)
-
- if !sortDescriptors.isEmpty {
- results = results.sorted(by: sortDescriptors)
- }
+ func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
+ guard let fileId else { return nil }
- if let limit {
- let sliced = results.prefix(limit)
- return sliced.map { $0.detachedCopy() }
- } else {
- return results.map { $0.detachedCopy() }
+ do {
+ let realm = try Realm()
+ if let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first {
+ return tableMetadata(value: result)
}
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
+ return nil
}
- func hasUploadingMetadataWithChunksOrE2EE() -> Bool {
- return core.performRealmRead { realm in
- realm.objects(tableMetadata.self)
- .filter("status == %d AND (chunk > 0 OR e2eEncrypted == true)", NCGlobal.shared.metadataStatusUploading)
- .first != nil
- } ?? false
+ func getResultMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
+ guard let ocId else { return nil }
+
+ do {
+ let realm = try Realm()
+ return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
}
func getMetadataDirectoryAsync(serverUrl: String, account: String) async -> tableMetadata? {
@@ -1359,7 +1397,7 @@ extension NCManageDatabase {
return nil
}
- return await core.performRealmReadAsync { realm in
+ return await performRealmReadAsync { realm in
let object = realm.objects(tableMetadata.self)
.filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, decodedBaseUrl, fileName)
.first
@@ -1379,8 +1417,8 @@ extension NCManageDatabase {
guard let decodedBaseUrl = baseUrl.removingPercentEncoding else {
return nil
}
-
- return core.performRealmRead { realm in
+
+ return performRealmRead { realm in
let object = realm.objects(tableMetadata.self)
.filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, decodedBaseUrl, fileName)
.first
@@ -1388,39 +1426,141 @@ extension NCManageDatabase {
}
}
- func getTransferAsync(tranfersSuccess: [tableMetadata]) async -> [tableMetadata] {
- await core.performRealmReadAsync { realm in
- let predicate = NSPredicate(format: "status IN %@", NCGlobal.shared.metadataStatusTransfers)
- let sortDescriptors = [
- RealmSwift.SortDescriptor(keyPath: "status", ascending: false),
- RealmSwift.SortDescriptor(keyPath: "sessionDate", ascending: true)
- ]
+ func createMetadatasFolder(assets: [PHAsset],
+ useSubFolder: Bool,
+ session: NCSession.Session, completion: @escaping ([tableMetadata]) -> Void) {
+ var foldersCreated: Set = []
+ var metadatas: [tableMetadata] = []
+ let serverUrlBase = getAccountAutoUploadDirectory(session: session)
+ let fileNameBase = getAccountAutoUploadFileName(account: session.account)
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl BEGINSWITH %@ AND directory == true", session.account, serverUrlBase)
+
+ func createMetadata(serverUrl: String, fileName: String, metadata: tableMetadata?) {
+ guard !foldersCreated.contains(serverUrl + "/" + fileName) else {
+ return
+ }
+ foldersCreated.insert(serverUrl + "/" + fileName)
+
+ if let metadata {
+ metadata.status = NCGlobal.shared.metadataStatusWaitCreateFolder
+ metadata.sessionSelector = NCGlobal.shared.selectorUploadAutoUpload
+ metadata.sessionDate = Date()
+ metadatas.append(tableMetadata(value: metadata))
+ } else {
+ let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName,
+ fileNameView: fileName,
+ ocId: NSUUID().uuidString,
+ serverUrl: serverUrl,
+ url: "",
+ contentType: "httpd/unix-directory",
+ directory: true,
+ session: session,
+ sceneIdentifier: nil)
+ metadata.status = NCGlobal.shared.metadataStatusWaitCreateFolder
+ metadata.sessionSelector = NCGlobal.shared.selectorUploadAutoUpload
+ metadata.sessionDate = Date()
+ metadatas.append(metadata)
+ }
+ }
+
+ let metadatasFolder = getMetadatas(predicate: predicate)
+ let targetPath = serverUrlBase + "/" + fileNameBase
+ let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
+ createMetadata(serverUrl: serverUrlBase, fileName: fileNameBase, metadata: metadata)
+
+ if useSubFolder {
+ let autoUploadServerUrlBase = self.getAccountAutoUploadServerUrlBase(session: session)
+ let autoUploadSubfolderGranularity = self.getAccountAutoUploadSubfolderGranularity()
+ let folders = Set(assets.map { self.utilityFileSystem.createGranularityPath(asset: $0) }).sorted()
+
+ for folder in folders {
+ let componentsDate = folder.split(separator: "/")
+ let year = componentsDate[0]
+ let serverUrl = autoUploadServerUrlBase
+ let fileName = String(year)
+ let targetPath = serverUrl + "/" + fileName
+ let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
+
+ createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
+
+ if autoUploadSubfolderGranularity >= NCGlobal.shared.subfolderGranularityMonthly {
+ let month = componentsDate[1]
+ let serverUrl = autoUploadServerUrlBase + "/" + year
+ let fileName = String(month)
+ let targetPath = serverUrl + "/" + fileName
+ let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
+
+ createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
- let results = realm.objects(tableMetadata.self)
- .filter(predicate)
- .sorted(by: sortDescriptors)
+ if autoUploadSubfolderGranularity == NCGlobal.shared.subfolderGranularityDaily {
+ let day = componentsDate[2]
+ let serverUrl = autoUploadServerUrlBase + "/" + year + "/" + month
+ let fileName = String(day)
+ let targetPath = serverUrl + "/" + fileName
+ let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
- let excludedIds = Set(tranfersSuccess.compactMap { $0.ocIdTransfer })
- let filtered = results.filter { !excludedIds.contains($0.ocIdTransfer) }
+ createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
+ }
- return filtered.map { $0.detachedCopy() }
- } ?? []
+ }
+ return results
+ } else {
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ if freeze {
+ return results.freeze()
+ }
+ return results
+ }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
}
- func getMetadatasInWaitingCountAsync() async -> Int {
- await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter("status IN %@", NCGlobal.shared.metadatasStatusInWaiting)
- .count
- } ?? 0
+ func getCalculateCumulativeHash(for metadatas: [tableMetadata], account: String, serverUrl: String) -> String {
+ let concatenatedEtags = metadatas.map { $0.etag }.joined(separator: "-")
+ return sha256Hash(concatenatedEtags)
}
+
+ func getMediaMetadatas(predicate: NSPredicate) -> ThreadSafeArray? {
- func metadataExistsAsync(predicate: NSPredicate) async -> Bool {
- await core.performRealmReadAsync { realm in
- realm.objects(tableMetadata.self)
- .filter(predicate)
- .first != nil
- } ?? false
+ do {
+ let realm = try Realm()
+ let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+ }
+
+ return nil
+ }
+
+ func getMediaMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> ThreadSafeArray? {
+
+ do {
+ let realm = try Realm()
+ if let sorted {
+ var results: [tableMetadata] = []
+ switch NCKeychain().mediaSortDate {
+ case "date":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.date as Date) > ($1.date as Date) }
+ case "creationDate":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.creationDate as Date) > ($1.creationDate as Date) }
+ case "uploadDate":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.uploadDate as Date) > ($1.uploadDate as Date) }
+ default:
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ }
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ } else {
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ }
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+ }
+ return nil
}
func getAdvancedMetadatas(predicate: NSPredicate, page: Int = 0, limit: Int = 0, sorted: String, ascending: Bool) -> [tableMetadata] {
@@ -1451,4 +1591,16 @@ extension NCManageDatabase {
return metadatas
}
+
+ func getResultMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
+ guard let fileId else { return nil }
+
+ do {
+ let realm = try Realm()
+ return realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first
+ } catch let error as NSError {
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ }
+ return nil
+ }
}
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
index ec489d893a..c9aae962b1 100644
--- a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
@@ -84,7 +84,7 @@ import XLForm
self.navigationItem.rightBarButtonItem = saveButton
self.navigationItem.rightBarButtonItem?.isEnabled = false
- // title
+ // title
self.title = titleForm
fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
@@ -113,7 +113,7 @@ import XLForm
row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
row.action.formSelector = #selector(changeDestinationFolder(_:))
row.cellConfig["folderImage.image"] = UIImage(named: "folder")!.imageColor(NCBrandColor.shared.customer)
- row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.right.rawValue
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
if(self.fileNameFolder == "/"){
@@ -288,10 +288,14 @@ import XLForm
self.present(alert, animated: true)
return
}
+
+ // Ensure fileName is not nil or empty
guard var fileNameForm: String = fileName, !fileNameForm.isEmpty else { return }
- // Trim whitespaces after checks above
+ // Trim whitespaces and newlines
fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ fileName = FileAutoRenamer.rename(fileNameForm, account: session.account)
let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false, account: session.account
)
@@ -299,19 +303,25 @@ import XLForm
fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
}
- if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
-
- let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
-
- guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
-
- conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
- conflict.alwaysNewFileNameNumber = true
- conflict.serverUrl = serverUrl
- conflict.metadatasUploadInConflict = [metadataForUpload]
- conflict.delegate = self
-
- self.present(conflict, animated: true, completion: nil)
+ // verify if already exists
+ if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", session.account, self.serverUrl, fileNameForm)) != nil {
+ NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
+ return
+// }
+//
+// if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
+//
+// let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
+//
+// guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+//
+// conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+// conflict.alwaysNewFileNameNumber = true
+// conflict.serverUrl = serverUrl
+// conflict.metadatasUploadInConflict = [metadataForUpload]
+// conflict.delegate = self
+//
+// self.present(conflict, animated: true, completion: nil)
} else {
@@ -348,7 +358,7 @@ import XLForm
options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
}
- NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { account, url, _, error in
+ NextcloudKit.shared.textCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { account, url, _, error in
guard error == .success, account == self.session.account, let url = url else {
self.navigationItem.rightBarButtonItem?.isEnabled = true
NCContentPresenter().showError(error: error)
@@ -364,6 +374,7 @@ import XLForm
self.dismiss(animated: true, completion: {
let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName, fileNameView: fileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: results.mimeType, session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
if let viewController = self.appDelegate.activeViewController {
+// NCViewer().view(viewController: viewController, metadata: metadata)
NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
}
})
@@ -384,6 +395,7 @@ import XLForm
let metadata = NCManageDatabase.shared.createMetadata(fileName: createFileName, fileNameView: createFileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: "", session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
AnalyticsHelper.shared.trackCreateFile(metadata: metadata)
if let viewController = self.appDelegate.activeViewController {
+// NCViewer().view(viewController: viewController, metadata: metadata)
NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
}
})
@@ -412,7 +424,7 @@ import XLForm
options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
}
- NextcloudKit.shared.NCTextGetListOfTemplates(account: session.account, options: options) { account, templates, _, error in
+ NextcloudKit.shared.textGetListOfTemplates(account: session.account, options: options) { account, templates, _, error in
self.indicator.stopAnimating()
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.swift b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
index 901ce667ae..cb7553eda7 100644
--- a/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
@@ -7,6 +7,7 @@
//
import UIKit
+import XLForm
class FolderPathCustomCell: XLFormButtonCell{
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.xib b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
index a231ae7c72..caca063abf 100644
--- a/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
@@ -1,9 +1,9 @@
-
+
-
+
@@ -18,13 +18,13 @@
-
+
@@ -55,6 +55,7 @@
+
@@ -68,7 +69,7 @@
-
+
diff --git a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
index 0242fa0f7a..3983e413a6 100644
--- a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
+++ b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
@@ -7,6 +7,7 @@
//
import UIKit
+import XLForm
class NCCreateDocumentCustomTextField: XLFormBaseCell,UITextFieldDelegate {
From ce85acca0de85a3ccc31786c37870858f26d10be Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 16 Dec 2025 14:51:10 +0530
Subject: [PATCH 4/4] NMC 1933 - Collabora customisation changes
---
iOSClient/BrowserWeb/NCBrowserWeb.swift | 27 +-
.../Data/NCManageDatabase+Metadata.swift | 1919 ++++++++---------
.../Create/NCCreateFormUploadDocuments.swift | 226 +-
3 files changed, 971 insertions(+), 1201 deletions(-)
diff --git a/iOSClient/BrowserWeb/NCBrowserWeb.swift b/iOSClient/BrowserWeb/NCBrowserWeb.swift
index 10ee6d71ca..206197c76c 100644
--- a/iOSClient/BrowserWeb/NCBrowserWeb.swift
+++ b/iOSClient/BrowserWeb/NCBrowserWeb.swift
@@ -1,25 +1,6 @@
-//
-// NCBrowserWeb.swift
-// Nextcloud
-//
-// Created by Marino Faggiana on 22/08/2019.
-// Copyright (c) 2019 Marino Faggiana. 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 .
-//
+// SPDX-FileCopyrightText: Nextcloud GmbH
+// SPDX-FileCopyrightText: 2019 Marino Faggiana
+// SPDX-License-Identifier: GPL-3.0-or-later
import UIKit
@preconcurrency import WebKit
@@ -55,7 +36,7 @@ class NCBrowserWeb: UIViewController {
buttonExit.isHidden = true
} else {
self.view.bringSubviewToFront(buttonExit)
- let image = NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.customer])
+ let image = NCUtility().loadImage(named: "xmark", colors: [.systemBlue])
buttonExit.setImage(image, for: .normal)
}
diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift
index 5c82a8767a..39ab3d0da6 100644
--- a/iOSClient/Data/NCManageDatabase+Metadata.swift
+++ b/iOSClient/Data/NCManageDatabase+Metadata.swift
@@ -1,30 +1,12 @@
-//
-// NCManageDatabase+Metadata.swift
-// Nextcloud
-//
-// Created by Henrik Storch on 30.11.21.
-// Copyright © 2021 Marino Faggiana. 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 .
-//
+// SPDX-FileCopyrightText: Nextcloud GmbH
+// SPDX-FileCopyrightText: 2021 Marino Faggiana
+// SPDX-License-Identifier: GPL-3.0-or-later
import Foundation
import UIKit
import RealmSwift
import NextcloudKit
+import Photos
class tableMetadata: Object {
override func isEqual(_ object: Any?) -> Bool {
@@ -51,8 +33,7 @@ class tableMetadata: Object {
self.altitude == object.altitude,
self.status == object.status,
Array(self.tags).elementsEqual(Array(object.tags)),
- Array(self.shareType).elementsEqual(Array(object.shareType)),
- Array(self.sharePermissionsCloudMesh).elementsEqual(Array(object.sharePermissionsCloudMesh)) {
+ Array(self.shareType).elementsEqual(Array(object.shareType)) {
return true
} else {
return false
@@ -75,7 +56,6 @@ class tableMetadata: Object {
@objc dynamic var e2eEncrypted: Bool = false
@objc dynamic var edited: Bool = false
@objc dynamic var etag = ""
- @objc dynamic var etagResource = ""
let exifPhotos = List()
@objc dynamic var favorite: Bool = false
@objc dynamic var fileId = ""
@@ -102,6 +82,7 @@ class tableMetadata: Object {
@objc public var lockOwnerDisplayName = ""
@objc public var lockTime: Date?
@objc public var lockTimeOut: Date?
+ @objc dynamic var mediaSearch: Bool = false
@objc dynamic var path = ""
@objc dynamic var permissions = ""
@objc dynamic var placePhotos: String?
@@ -111,15 +92,15 @@ class tableMetadata: Object {
@objc dynamic var richWorkspace: String?
@objc dynamic var sceneIdentifier: String?
@objc dynamic var serverUrl = ""
- @objc dynamic var serveUrlFileName = ""
- @objc dynamic var serverUrlTo = ""
+ @objc dynamic var serverUrlFileName = ""
+ @objc dynamic var destination = ""
@objc dynamic var session = ""
@objc dynamic var sessionDate: Date?
@objc dynamic var sessionError = ""
@objc dynamic var sessionSelector = ""
@objc dynamic var sessionTaskIdentifier: Int = 0
+ /// The integer for sharing permissions.
@objc dynamic var sharePermissionsCollaborationServices: Int = 0
- let sharePermissionsCloudMesh = List()
let shareType = List()
@objc dynamic var size: Int64 = 0
@objc dynamic var status: Int = 0
@@ -142,6 +123,8 @@ class tableMetadata: Object {
@objc dynamic var errorCode: Int = 0
@objc dynamic var nativeFormat: Bool = false
@objc dynamic var autoUploadServerUrlBase: String?
+ @objc dynamic var typeIdentifier: String = ""
+ @objc dynamic var progress: Double = 0
override static func primaryKey() -> String {
return "ocId"
@@ -158,6 +141,9 @@ extension tableMetadata {
}
var isRenameable: Bool {
+ if !NCMetadataPermissions.canRename(self) {
+ return false
+ }
if lock {
return false
}
@@ -166,115 +152,95 @@ extension tableMetadata {
}
return true
}
-
+
var isPrintable: Bool {
if isDocumentViewableOnly {
return false
}
- if ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/") || classFile == NKCommon.TypeClassFile.image.rawValue {
+ if ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/") || classFile == NKTypeClassFile.image.rawValue {
return true
}
return false
}
var isSavebleInCameraRoll: Bool {
- return (classFile == NKCommon.TypeClassFile.image.rawValue && contentType != "image/svg+xml") || classFile == NKCommon.TypeClassFile.video.rawValue
+ return (classFile == NKTypeClassFile.image.rawValue && contentType != "image/svg+xml") || classFile == NKTypeClassFile.video.rawValue
}
-
var isDocumentViewableOnly: Bool {
- sharePermissionsCollaborationServices == NCPermissions().permissionReadShare && classFile == NKCommon.TypeClassFile.document.rawValue
+ sharePermissionsCollaborationServices == NCPermissions().permissionReadShare && classFile == NKTypeClassFile.document.rawValue
}
-
+
var isAudioOrVideo: Bool {
- return classFile == NKCommon.TypeClassFile.audio.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
+ return classFile == NKTypeClassFile.audio.rawValue || classFile == NKTypeClassFile.video.rawValue
}
var isImageOrVideo: Bool {
- return classFile == NKCommon.TypeClassFile.image.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
+ return classFile == NKTypeClassFile.image.rawValue || classFile == NKTypeClassFile.video.rawValue
}
var isVideo: Bool {
- return classFile == NKCommon.TypeClassFile.video.rawValue
+ return classFile == NKTypeClassFile.video.rawValue
}
var isAudio: Bool {
- return classFile == NKCommon.TypeClassFile.audio.rawValue
+ return classFile == NKTypeClassFile.audio.rawValue
}
var isImage: Bool {
- return classFile == NKCommon.TypeClassFile.image.rawValue
+ return classFile == NKTypeClassFile.image.rawValue
}
var isSavebleAsImage: Bool {
- classFile == NKCommon.TypeClassFile.image.rawValue && contentType != "image/svg+xml"
+ classFile == NKTypeClassFile.image.rawValue && contentType != "image/svg+xml"
}
var isCopyableInPasteboard: Bool {
- !isDocumentViewableOnly && !directory
+ !directory
}
var isCopyableMovable: Bool {
- !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
+ !isDirectoryE2EE && !e2eEncrypted
}
var isModifiableWithQuickLook: Bool {
- if directory || isDocumentViewableOnly || isDirectoryE2EE {
+ if directory || isDirectoryE2EE {
return false
}
return isPDF || isImage
}
+ var isCreatable: Bool {
+ if isDirectory {
+ return NCMetadataPermissions.canCreateFolder(self)
+ } else {
+ return NCMetadataPermissions.canCreateFile(self)
+ }
+ }
+
var isDeletable: Bool {
- if !isDirectoryE2EE && e2eEncrypted {
+ if (!isDirectoryE2EE && e2eEncrypted) || !NCMetadataPermissions.canDelete(self) {
return false
}
return true
}
var canSetAsAvailableOffline: Bool {
-// return session.isEmpty && !isDocumentViewableOnly //!isDirectoryE2EE && !e2eEncrypted
return session.isEmpty && !isDirectoryE2EE && !e2eEncrypted
}
var canShare: Bool {
- return session.isEmpty && !isDocumentViewableOnly && !directory && !NCBrandOptions.shared.disable_openin_file
+ return session.isEmpty && !directory && !NCBrandOptions.shared.disable_openin_file
}
var canSetDirectoryAsE2EE: Bool {
- return directory && size == 0 && !e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
+ return directory && size == 0 && !e2eEncrypted && NCPreferences().isEndToEndEnabled(account: account)
}
var canUnsetDirectoryAsE2EE: Bool {
- return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
- }
-
- var canOpenExternalEditor: Bool {
- if isDocumentViewableOnly {
- return false
- }
- let utility = NCUtility()
- let editors = utility.editorsDirectEditing(account: account, contentType: contentType)
- let isRichDocument = utility.isTypeFileRichDocument(self)
- return classFile == NKCommon.TypeClassFile.document.rawValue && editors.contains(NCGlobal.shared.editorText) && ((editors.contains(NCGlobal.shared.editorOnlyoffice) || isRichDocument))
- }
-
- var isWaitingTransfer: Bool {
- status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
+ return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCPreferences().isEndToEndEnabled(account: account)
}
- var isInTransfer: Bool {
- status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusUploading
- }
-
- var isTransferInForeground: Bool {
- (status > 0 && (chunk > 0 || e2eEncrypted))
- }
-
- var isDownloadUpload: Bool {
- status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusUploading
- }
-
var isDownload: Bool {
status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusDownloading
}
@@ -288,12 +254,7 @@ extension tableMetadata {
}
@objc var isDirectoryE2EE: Bool {
- let session = NCSession.Session(account: account, urlBase: urlBase, user: user, userId: userId)
- return NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
- }
-
- var isDirectoryE2EETop: Bool {
- NCUtilityFileSystem().isDirectoryE2EETop(account: account, serverUrl: serverUrl)
+ return NCUtilityFileSystem().isDirectoryE2EE(serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
}
var isLivePhoto: Bool {
@@ -309,21 +270,27 @@ extension tableMetadata {
}
var hasPreviewBorder: Bool {
- !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024)
+ !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024, userId: userId, urlBase: urlBase)
}
var isAvailableEditorView: Bool {
guard !isPDF,
- classFile == NKCommon.TypeClassFile.document.rawValue,
- NextcloudKit.shared.isNetworkReachable() else { return false }
+ classFile == NKTypeClassFile.document.rawValue,
+ NextcloudKit.shared.isNetworkReachable() else {
+ return false
+ }
let utility = NCUtility()
- let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType)
+ let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType).map { $0.lowercased() }
let richDocumentEditor = utility.isTypeFileRichDocument(self)
+ let capabilities = NCNetworking.shared.capabilities[account]
- if NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled && richDocumentEditor && directEditingEditors.isEmpty {
+ if let capabilities,
+ capabilities.richDocumentsEnabled,
+ richDocumentEditor,
+ directEditingEditors.isEmpty {
// RichDocument: Collabora
return true
- } else if directEditingEditors.contains(NCGlobal.shared.editorText) || directEditingEditors.contains(NCGlobal.shared.editorOnlyoffice) {
+ } else if directEditingEditors.contains("nextcloud text") || directEditingEditors.contains("onlyoffice") {
// DirectEditing: Nextcloud Text - OnlyOffice
return true
}
@@ -331,8 +298,9 @@ extension tableMetadata {
}
var isAvailableRichDocumentEditorView: Bool {
- guard classFile == NKCommon.TypeClassFile.document.rawValue,
- NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled,
+ guard let capabilities = NCNetworking.shared.capabilities[account],
+ classFile == NKTypeClassFile.document.rawValue,
+ capabilities.richDocumentsEnabled,
NextcloudKit.shared.isNetworkReachable() else { return false }
if NCUtility().isTypeFileRichDocument(self) {
@@ -342,10 +310,12 @@ extension tableMetadata {
}
var isAvailableDirectEditingEditorView: Bool {
- guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else { return false }
- let editors = NCUtility().editorsDirectEditing(account: account, contentType: contentType)
+ guard (classFile == NKTypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else {
+ return false
+ }
+ let editors = NCUtility().editorsDirectEditing(account: account, contentType: contentType).map { $0.lowercased() }
- if editors.contains(NCGlobal.shared.editorText) || editors.contains(NCGlobal.shared.editorOnlyoffice) {
+ if editors.contains("nextcloud text") || editors.contains("onlyoffice") {
return true
}
return false
@@ -362,1026 +332,1003 @@ extension tableMetadata {
// Return if is sharable
func isSharable() -> Bool {
- if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
+ guard let capabilities = NCNetworking.shared.capabilities[account] else {
+ return false
+ }
+ if !capabilities.fileSharingApiEnabled || (capabilities.e2EEEnabled && isDirectoryE2EE), !e2eEncrypted {
return false
}
return !e2eEncrypted
}
+
+ /// Returns a detached (unmanaged) deep copy of the current `tableMetadata` object.
+ ///
+ /// - Note: The Realm `List` properties containing primitive types (e.g., `tags`, `shareType`) are copied automatically
+ /// by the Realm initializer `init(value:)`. For `List` containing Realm objects (e.g., `exifPhotos`), this method
+ /// creates new instances to ensure the copy is fully detached and safe to use outside of a Realm context.
+ ///
+ /// - Returns: A new `tableMetadata` instance fully detached from Realm.
+ func detachedCopy() -> tableMetadata {
+ // Use Realm's built-in copy constructor for primitive properties and List of primitives
+ let detached = tableMetadata(value: self)
+
+ // Deep copy of List of Realm objects (exifPhotos)
+ detached.exifPhotos.removeAll()
+ detached.exifPhotos.append(objectsIn: self.exifPhotos.map { NCKeyValue(value: $0) })
+
+ return detached
+ }
}
extension NCManageDatabase {
- func convertFileToMetadata(_ file: NKFile, isDirectoryE2EE: Bool) -> tableMetadata {
- let metadata = tableMetadata()
-
- metadata.account = file.account
- metadata.checksums = file.checksums
- metadata.commentsUnread = file.commentsUnread
- metadata.contentType = file.contentType
- if let date = file.creationDate {
- metadata.creationDate = date as NSDate
- } else {
- metadata.creationDate = file.date as NSDate
- }
- metadata.dataFingerprint = file.dataFingerprint
- metadata.date = file.date as NSDate
- if let datePhotosOriginal = file.datePhotosOriginal {
- metadata.datePhotosOriginal = datePhotosOriginal as NSDate
- } else {
- metadata.datePhotosOriginal = metadata.date
- }
- metadata.directory = file.directory
- metadata.downloadURL = file.downloadURL
- metadata.e2eEncrypted = file.e2eEncrypted
- metadata.etag = file.etag
- for dict in file.exifPhotos {
- for (key, value) in dict {
- let keyValue = NCKeyValue()
- keyValue.key = key
- keyValue.value = value
- metadata.exifPhotos.append(keyValue)
- }
+ func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
+ var isShare = false
+ var isMounted = false
+
+ if metadataFolder != nil {
+ isShare = metadata.permissions.contains(NCMetadataPermissions.permissionShared) && !metadataFolder!.permissions.contains(NCMetadataPermissions.permissionShared)
+ isMounted = metadata.permissions.contains(NCMetadataPermissions.permissionMounted) && !metadataFolder!.permissions.contains(NCMetadataPermissions.permissionMounted)
+ } else if let directory = getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+ isShare = metadata.permissions.contains(NCMetadataPermissions.permissionShared) && !directory.permissions.contains(NCMetadataPermissions.permissionShared)
+ isMounted = metadata.permissions.contains(NCMetadataPermissions.permissionMounted) && !directory.permissions.contains(NCMetadataPermissions.permissionMounted)
}
- metadata.favorite = file.favorite
- metadata.fileId = file.fileId
- metadata.fileName = file.fileName
- metadata.fileNameView = file.fileName
- metadata.hasPreview = file.hasPreview
- metadata.hidden = file.hidden
- switch (file.fileName as NSString).pathExtension {
- case "odg":
- metadata.iconName = "diagram"
- case "csv", "xlsm" :
- metadata.iconName = "file_xls"
- default:
- metadata.iconName = file.iconName
- }
- metadata.mountType = file.mountType
- metadata.name = file.name
- metadata.note = file.note
- metadata.ocId = file.ocId
- metadata.ocIdTransfer = file.ocId
- metadata.ownerId = file.ownerId
- metadata.ownerDisplayName = file.ownerDisplayName
- metadata.lock = file.lock
- metadata.lockOwner = file.lockOwner
- metadata.lockOwnerEditor = file.lockOwnerEditor
- metadata.lockOwnerType = file.lockOwnerType
- metadata.lockOwnerDisplayName = file.lockOwnerDisplayName
- metadata.lockTime = file.lockTime
- metadata.lockTimeOut = file.lockTimeOut
- metadata.path = file.path
- metadata.permissions = file.permissions
- metadata.placePhotos = file.placePhotos
- metadata.quotaUsedBytes = file.quotaUsedBytes
- metadata.quotaAvailableBytes = file.quotaAvailableBytes
- metadata.richWorkspace = file.richWorkspace
- metadata.resourceType = file.resourceType
- metadata.serverUrl = file.serverUrl
- metadata.serveUrlFileName = file.serverUrl + "/" + file.fileName
- metadata.sharePermissionsCollaborationServices = file.sharePermissionsCollaborationServices
- for element in file.sharePermissionsCloudMesh {
- metadata.sharePermissionsCloudMesh.append(element)
- }
- for element in file.shareType {
- metadata.shareType.append(element)
- }
- for element in file.tags {
- metadata.tags.append(element)
- }
- metadata.size = file.size
- metadata.classFile = file.classFile
- // iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
- if (metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown") && metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue {
- metadata.classFile = NKCommon.TypeClassFile.document.rawValue
- }
- if let date = file.uploadDate {
- metadata.uploadDate = date as NSDate
+
+ if isShare || isMounted {
+ return true
} else {
- metadata.uploadDate = file.date as NSDate
- }
- metadata.urlBase = file.urlBase
- metadata.user = file.user
- metadata.userId = file.userId
- metadata.latitude = file.latitude
- metadata.longitude = file.longitude
- metadata.altitude = file.altitude
- metadata.height = Int(file.height)
- metadata.width = Int(file.width)
- metadata.livePhotoFile = file.livePhotoFile
- metadata.isFlaggedAsLivePhotoByServer = file.isFlaggedAsLivePhotoByServer
-
- // E2EE find the fileName for fileNameView
- if isDirectoryE2EE || file.e2eEncrypted {
- if let tableE2eEncryption = getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", file.account, file.serverUrl, file.fileName)) {
- metadata.fileNameView = tableE2eEncryption.fileName
- let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: file.contentType, directory: file.directory, account: file.account)
- metadata.contentType = results.mimeType
- metadata.iconName = results.iconName
- metadata.classFile = results.classFile
- }
+ return false
}
- return metadata
}
- func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatas: [tableMetadata]) -> Void) {
- var counter: Int = 0
- var isDirectoryE2EE: Bool = false
- let listServerUrl = ThreadSafeDictionary()
- var metadataFolder = tableMetadata()
- var metadatas: [tableMetadata] = []
-
- for file in files {
- if let key = listServerUrl[file.serverUrl] {
- isDirectoryE2EE = key
- } else {
- isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
- listServerUrl[file.serverUrl] = isDirectoryE2EE
- }
+ // MARK: - Realm Write
- let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+ func addMetadataIfNeededAsync(_ metadata: tableMetadata, sync: Bool = true) {
+ let detached = metadata.detachedCopy()
- if counter == 0 && useFirstAsMetadataFolder {
- metadataFolder = tableMetadata(value: metadata)
- } else {
- metadatas.append(metadata)
+ performRealmWrite(sync: sync) { realm in
+ if realm.object(ofType: tableMetadata.self, forPrimaryKey: metadata.ocId) == nil {
+ realm.add(detached)
}
-
- counter += 1
}
- completion(metadataFolder, metadatas)
}
-
- func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatasFolder: [tableMetadata], _ metadatas: [tableMetadata]) -> Void) {
- var counter: Int = 0
- var isDirectoryE2EE: Bool = false
- let listServerUrl = ThreadSafeDictionary()
+ func addAndReturnMetadata(_ metadata: tableMetadata) -> tableMetadata? {
+ let detached = metadata.detachedCopy()
- var metadataFolder = tableMetadata()
- var metadataFolders: [tableMetadata] = []
- var metadatas: [tableMetadata] = []
+ performRealmWrite { realm in
+ realm.add(detached, update: .all)
+ }
- for file in files {
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter("ocId == %@", metadata.ocId)
+ .first
+ .map { $0.detachedCopy() }
+ }
+ }
- if let key = listServerUrl[file.serverUrl] {
- isDirectoryE2EE = key
- } else {
- isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
- listServerUrl[file.serverUrl] = isDirectoryE2EE
- }
+ func addAndReturnMetadataAsync(_ metadata: tableMetadata) async -> tableMetadata? {
+ let detached = metadata.detachedCopy()
- let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+ await performRealmWriteAsync { realm in
+ realm.add(detached, update: .all)
+ }
- if counter == 0 && useMetadataFolder {
- metadataFolder = tableMetadata.init(value: metadata)
- } else {
- metadatas.append(metadata)
- if metadata.directory {
- metadataFolders.append(metadata)
- }
- }
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter("ocId == %@", metadata.ocId)
+ .first?
+ .detachedCopy()
+ }
+ }
- counter += 1
- }
-
- completion(metadataFolder, metadataFolders, metadatas)
- }
-
-// func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatasFolder: [tableMetadata], _ metadatas: [tableMetadata]) -> Void) {
-//
-// var counter: Int = 0
-// var isDirectoryE2EE: Bool = false
-// let listServerUrl = ThreadSafeDictionary()
-//
-// var metadataFolder = tableMetadata()
-// var metadataFolders: [tableMetadata] = []
-// var metadatas: [tableMetadata] = []
-//
-// for file in files {
-//
-// if let key = listServerUrl[file.serverUrl] {
-// isDirectoryE2EE = key
-// } else {
-// isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
-// listServerUrl[file.serverUrl] = isDirectoryE2EE
-// }
-//
-// let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
-//
-// if counter == 0 && useMetadataFolder {
-// metadataFolder = tableMetadata.init(value: metadata)
-// } else {
-// metadatas.append(metadata)
-// if metadata.directory {
-// metadataFolders.append(metadata)
-// }
-// }
-//
-// counter += 1
-// }
-//
-// completion(metadataFolder, metadataFolders, metadatas)
-// }
-
- func getMetadataDirectoryFrom(files: [NKFile]) -> tableMetadata? {
- guard let file = files.first else { return nil }
- let isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
- let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+ func addMetadata(_ metadata: tableMetadata, sync: Bool = true) {
+ let detached = metadata.detachedCopy()
- return metadata
+ performRealmWrite(sync: sync) { realm in
+ realm.add(detached, update: .all)
+ }
}
- func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatas: [tableMetadata]) {
- await withUnsafeContinuation({ continuation in
- convertFilesToMetadatas(files, useFirstAsMetadataFolder: useFirstAsMetadataFolder) { metadataFolder, metadatas in
- continuation.resume(returning: (metadataFolder, metadatas))
- }
- })
+ func addMetadataAsync(_ metadata: tableMetadata) async {
+ let detached = metadata.detachedCopy()
+
+ await performRealmWriteAsync { realm in
+ realm.add(detached, update: .all)
+ }
}
- func createMetadata(fileName: String, fileNameView: String, ocId: String, serverUrl: String, url: String, contentType: String, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil, directory: Bool = false, session: NCSession.Session, sceneIdentifier: String?) -> tableMetadata {
- let metadata = tableMetadata()
+ func addMetadatas(_ metadatas: [tableMetadata], sync: Bool = true) {
+ let detached = metadatas.map { $0.detachedCopy() }
- if isUrl {
- metadata.contentType = "text/uri-list"
- if let iconName = iconName {
- metadata.iconName = iconName
- } else {
- metadata.iconName = NKCommon.TypeClassFile.url.rawValue
- }
- metadata.classFile = NKCommon.TypeClassFile.url.rawValue
- } else {
- let (mimeType, classFile, iconName, _, _, _) = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: contentType, directory: directory, account: session.account)
- metadata.contentType = mimeType
- metadata.iconName = iconName
- metadata.classFile = classFile
- // iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
- if classFile == NKCommon.TypeClassFile.unknow.rawValue && (mimeType == "text/x-markdown" || mimeType == "text/markdown") {
- metadata.iconName = NKCommon.TypeIconFile.txt.rawValue
- metadata.classFile = NKCommon.TypeClassFile.document.rawValue
- }
+ performRealmWrite(sync: sync) { realm in
+ realm.add(detached, update: .all)
}
- if let iconUrl = iconUrl {
- metadata.iconUrl = iconUrl
- }
-
- let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
-
- metadata.account = session.account
- metadata.creationDate = Date() as NSDate
- metadata.date = Date() as NSDate
- metadata.directory = directory
- metadata.hasPreview = true
- metadata.etag = ocId
- metadata.fileName = fileName
- metadata.fileNameView = fileName
- metadata.name = name
- metadata.ocId = ocId
- metadata.ocIdTransfer = ocId
- metadata.permissions = "RGDNVW"
- metadata.serverUrl = serverUrl
- metadata.serveUrlFileName = serverUrl + "/" + fileName
- metadata.subline = subline
- metadata.uploadDate = Date() as NSDate
- metadata.url = url
- metadata.urlBase = session.urlBase
- metadata.user = session.user
- metadata.userId = session.userId
- metadata.sceneIdentifier = sceneIdentifier
- metadata.nativeFormat = !NCKeychain().formatCompatibility
-
- if !metadata.urlBase.isEmpty, metadata.serverUrl.hasPrefix(metadata.urlBase) {
- metadata.path = String(metadata.serverUrl.dropFirst(metadata.urlBase.count)) + "/"
- }
- return metadata
}
- func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
- let permissions = NCPermissions()
- var isShare = false
- var isMounted = false
+ func addMetadatasAsync(_ metadatas: [tableMetadata]) async {
+ let detached = metadatas.map { $0.detachedCopy() }
- if metadataFolder != nil {
- isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
- isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
- } else if let directory = getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
- isShare = metadata.permissions.contains(permissions.permissionShared) && !directory.permissions.contains(permissions.permissionShared)
- isMounted = metadata.permissions.contains(permissions.permissionMounted) && !directory.permissions.contains(permissions.permissionMounted)
+ await performRealmWriteAsync { realm in
+ realm.add(detached, update: .all)
}
+ }
- if isShare || isMounted {
- return true
- } else {
- return false
+ func deleteMetadataAsync(predicate: NSPredicate) async {
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter(predicate)
+ realm.delete(result)
}
}
- // MARK: - Set
+ func deleteMetadataAsync(id: String?) async {
+ guard let id else { return }
- @discardableResult
- func addMetadata(_ metadata: tableMetadata) -> tableMetadata {
- do {
- let realm = try Realm()
- try realm.write {
- return tableMetadata(value: realm.create(tableMetadata.self, value: metadata, update: .all))
- }
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@ OR fileId == %@", id, id)
+ realm.delete(result)
}
-
- return tableMetadata(value: metadata)
}
- func addMetadatas(_ metadatas: [tableMetadata]) {
- do {
- let realm = try Realm()
- try realm.write {
- for metadata in metadatas {
- realm.create(tableMetadata.self, value: metadata, update: .all)
- }
- }
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+ func deleteMetadataOcIds(_ ocIds: [String], sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId IN %@", ocIds)
+ realm.delete(result)
}
}
- func deleteMetadata(predicate: NSPredicate) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter(predicate)
- realm.delete(results)
- }
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+ func replaceMetadataAsync(id: String, metadata: tableMetadata) async {
+ let detached = metadata.detachedCopy()
+
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@ OR ocIdTransfer == %@", id, id)
+ realm.delete(result)
+ realm.add(detached, update: .all)
}
}
- func deleteMetadataOcId(_ ocId: String?) {
- guard let ocId else { return }
-
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter("ocId == %@", ocId)
- realm.delete(results)
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ // Asynchronously deletes an array of `tableMetadata` entries from the Realm database.
+ /// - Parameter metadatas: The `tableMetadata` objects to be deleted.
+ func deleteMetadatasAsync(_ metadatas: [tableMetadata]) async {
+ guard !metadatas.isEmpty else {
+ return
}
- }
+ let detached = metadatas.map { $0.detachedCopy() }
- func deleteMetadataOcIds(_ ocIds: [String]) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter("ocId IN %@", ocIds)
- realm.delete(results)
+ await performRealmWriteAsync { realm in
+ for detached in detached {
+ if let managed = realm.object(ofType: tableMetadata.self, forPrimaryKey: detached.ocId) {
+ realm.delete(managed)
+ }
}
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
}
}
- func deleteMetadatas(_ metadatas: [tableMetadata]) {
- do {
- let realm = try Realm()
- try realm.write {
- realm.delete(metadatas)
+ func renameMetadata(fileNameNew: String, ocId: String, status: Int = NCGlobal.shared.metadataStatusNormal) async {
+ await performRealmWriteAsync { realm in
+ guard let metadata = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first else {
+ return
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
- }
- }
- func renameMetadata(fileNameNew: String, ocId: String, status: Int = NCGlobal.shared.metadataStatusNormal) {
- do {
- let realm = try Realm()
- try realm.write {
- if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
- let fileNameView = result.fileNameView
- let fileIdMOV = result.livePhotoFile
- let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameView)
- let resultsType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameNew, mimeType: "", directory: result.directory, account: result.account)
-
- result.fileName = fileNameNew
- result.fileNameView = fileNameNew
- result.iconName = resultsType.iconName
- result.contentType = resultsType.mimeType
- result.classFile = resultsType.classFile
- result.status = status
-
- if status == NCGlobal.shared.metadataStatusNormal {
- result.sessionDate = nil
- } else {
- result.sessionDate = Date()
- }
-
- if result.directory,
- let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
- let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameNew)
-
- resultDirectory.serverUrl = serverUrlTo
- } else {
- let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
- let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameNew
-
- self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
-
- if result.isLivePhoto,
- let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
- let fileNameView = resultMOV.fileNameView
- let fileName = (fileNameNew as NSString).deletingPathExtension
- let ext = (resultMOV.fileName as NSString).pathExtension
- resultMOV.fileName = fileName + "." + ext
- resultMOV.fileNameView = fileName + "." + ext
-
- let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
- let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
-
- self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
+ let oldFileNameView = metadata.fileNameView
+ let account = metadata.account
+ let originalServerUrl = metadata.serverUrl
+
+ metadata.fileName = fileNameNew
+ metadata.fileNameView = fileNameNew
+ metadata.status = status
+ metadata.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
+
+ if metadata.directory {
+ let oldDirUrl = self.utilityFileSystem.createServerUrl(serverUrl: originalServerUrl, fileName: oldFileNameView)
+ let newDirUrl = self.utilityFileSystem.createServerUrl(serverUrl: originalServerUrl, fileName: fileNameNew)
+
+ if let dir = realm.objects(tableDirectory.self)
+ .filter("account == %@ AND serverUrl == %@", account, oldDirUrl)
+ .first {
+ dir.serverUrl = newDirUrl
}
+ } else {
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, userId: metadata.userId, urlBase: metadata.urlBase) + "/" + oldFileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, userId: metadata.userId, urlBase: metadata.urlBase) + "/" + fileNameNew
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func restoreMetadataFileName(ocId: String) {
- do {
- let realm = try Realm()
- try realm.write {
- if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first,
- let encodedURLString = result.serveUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
- let url = URL(string: encodedURLString) {
- let fileIdMOV = result.livePhotoFile
- let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileNameView)
- let lastPathComponent = url.lastPathComponent
- let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
- let fileNameView = result.fileNameView
-
- result.fileName = fileName
- result.fileNameView = fileName
- result.status = NCGlobal.shared.metadataStatusNormal
- result.sessionDate = nil
+ func restoreMetadataFileName(ocId: String, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first,
+ let encodedURLString = result.serverUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
+ let url = URL(string: encodedURLString) {
+ let fileIdMOV = result.livePhotoFile
+ let directoryServerUrl = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: result.fileNameView)
+ let lastPathComponent = url.lastPathComponent
+ let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
+ let fileNameView = result.fileNameView
- if result.directory,
- let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
- let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileName)
+ result.fileName = fileName
+ result.fileNameView = fileName
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
- resultDirectory.serverUrl = serverUrlTo
- } else {
- let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
- let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileName
+ if result.directory,
+ let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
+ let serverUrlTo = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: fileName)
- self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
+ resultDirectory.serverUrl = serverUrlTo
+ } else {
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileName
+
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
- if result.isLivePhoto,
- let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
- let fileNameView = resultMOV.fileNameView
- let fileName = (fileName as NSString).deletingPathExtension
- let ext = (resultMOV.fileName as NSString).pathExtension
- resultMOV.fileName = fileName + "." + ext
- resultMOV.fileNameView = fileName + "." + ext
+ if result.isLivePhoto,
+ let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
+ let fileNameView = resultMOV.fileNameView
+ let fileName = (fileName as NSString).deletingPathExtension
+ let ext = (resultMOV.fileName as NSString).pathExtension
+ resultMOV.fileName = fileName + "." + ext
+ resultMOV.fileNameView = fileName + "." + ext
- let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
- let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fileName + "." + ext
- self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
- }
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
}
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataServeUrlFileNameStatusNormal(ocId: String) {
- do {
- let realm = try Realm()
- try realm.write {
- if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
- result.serveUrlFileName = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileName)
- result.status = NCGlobal.shared.metadataStatusNormal
- result.sessionDate = nil
- }
+ /// Asynchronously restores the file name of a metadata entry and updates related file system and Realm entries.
+ /// - Parameter ocId: The object ID (ocId) of the file to restore.
+ func restoreMetadataFileNameAsync(ocId: String) async {
+ await performRealmWriteAsync { realm in
+ guard let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first,
+ let encodedURLString = result.serverUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
+ let url = URL(string: encodedURLString)
+ else {
+ return
+ }
+
+ let fileIdMOV = result.livePhotoFile
+ let directoryServerUrl = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: result.fileNameView)
+ let lastPathComponent = url.lastPathComponent
+ let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
+ let fileNameView = result.fileNameView
+
+ result.fileName = fileName
+ result.fileNameView = fileName
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
+
+ if result.directory,
+ let resultDirectory = realm.objects(tableDirectory.self)
+ .filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl)
+ .first {
+ let serverUrlTo = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: fileName)
+ resultDirectory.serverUrl = serverUrlTo
+ } else {
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileNameView
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId, userId: result.userId, urlBase: result.urlBase) + "/" + fileName
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+ }
+
+ if result.isLivePhoto,
+ let resultMOV = realm.objects(tableMetadata.self)
+ .filter("fileId == %@ AND account == %@", fileIdMOV, result.account)
+ .first {
+ let fileNameViewMOV = resultMOV.fileNameView
+ let baseName = (fileName as NSString).deletingPathExtension
+ let ext = (resultMOV.fileName as NSString).pathExtension
+ let fullFileName = baseName + "." + ext
+
+ resultMOV.fileName = fullFileName
+ resultMOV.fileNameView = fullFileName
+
+ let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fileNameViewMOV
+ let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId, userId: resultMOV.userId, urlBase: resultMOV.urlBase) + "/" + fullFileName
+ self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataEtagResource(ocId: String, etagResource: String?) {
- guard let etagResource else { return }
-
- do {
- let realm = try Realm()
- try realm.write {
- let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
- result?.etagResource = etagResource
+ func setMetadataServerUrlFileNameStatusNormal(ocId: String, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first {
+ result.serverUrlFileName = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: result.fileName)
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataLivePhotoByServer(account: String, ocId: String, livePhotoFile: String) {
- do {
- let realm = try Realm()
- try realm.write {
- if let result = realm.objects(tableMetadata.self).filter("account == %@ AND ocId == %@", account, ocId).first {
- result.isFlaggedAsLivePhotoByServer = true
- result.livePhotoFile = livePhotoFile
- }
+ func setMetadataServerUrlFileNameStatusNormalAsync(ocId: String) async {
+ await performRealmWriteAsync { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first {
+ result.serverUrlFileName = self.utilityFileSystem.createServerUrl(serverUrl: result.serverUrl, fileName: result.fileName)
+ result.status = NCGlobal.shared.metadataStatusNormal
+ result.sessionDate = nil
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func updateMetadatasFavorite(account: String, metadatas: [tableMetadata]) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter("account == %@ AND favorite == true", account)
- for result in results {
- result.favorite = false
- }
- realm.add(metadatas, update: .all)
+ func setMetadataLivePhotoByServerAsync(account: String,
+ ocId: String,
+ livePhotoFile: String) async {
+ await performRealmWriteAsync { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND ocId == %@", account, ocId)
+ .first {
+ result.isFlaggedAsLivePhotoByServer = true
+ result.livePhotoFile = livePhotoFile
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func updateMetadatasFiles(_ metadatas: [tableMetadata], serverUrl: String, account: String) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal))
- realm.delete(results)
- for metadata in metadatas {
- if realm.objects(tableMetadata.self).filter(NSPredicate(format: "ocId == %@ AND status != %d", metadata.ocId, NCGlobal.shared.metadataStatusNormal)).first != nil {
- continue
- }
- realm.add(tableMetadata(value: metadata), update: .all)
+ func updateMetadatasFavoriteAsync(account: String, metadatas: [tableMetadata]) async {
+ guard !metadatas.isEmpty else { return }
+
+ await performRealmWriteAsync { realm in
+ let oldFavorites = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND favorite == true", account)
+ for item in oldFavorites {
+ item.favorite = false
+ }
+ realm.add(metadatas, update: .all)
+ }
+ }
+
+ /// Asynchronously updates a list of `tableMetadata` entries in Realm for a given account and server URL.
+ ///
+ /// This function performs the following steps:
+ /// 1. Skips all entries with `status != metadataStatusNormal`.
+ /// 2. Deletes existing metadata entries with `status == metadataStatusNormal` that are not in the skip list.
+ /// 3. Copies matching `mediaSearch` from previously deleted metadata to the incoming list.
+ /// 4. Inserts or updates new metadata entries into Realm, except those in the skip list.
+ ///
+ /// - Parameters:
+ /// - metadatas: An array of incoming detached `tableMetadata` objects to insert or update.
+ /// - serverUrl: The server URL associated with the metadata entries.
+ /// - account: The account identifier used to scope the metadata update.
+ func updateMetadatasFilesAsync(_ metadatas: [tableMetadata], serverUrl: String, account: String) async {
+ await performRealmWriteAsync { realm in
+ let ocIdsToSkip = Set(
+ realm.objects(tableMetadata.self)
+ .filter("status != %d", NCGlobal.shared.metadataStatusNormal)
+ .map(\.ocId)
+ )
+
+ let resultsToDelete = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND serverUrl == %@ AND status == %d AND fileName != %@", account, serverUrl, NCGlobal.shared.metadataStatusNormal, NextcloudKit.shared.nkCommonInstance.rootFileName)
+ .filter { !ocIdsToSkip.contains($0.ocId) }
+ let metadatasCopy = Array(resultsToDelete).map { tableMetadata(value: $0) }
+
+ realm.delete(resultsToDelete)
+
+ for metadata in metadatas {
+ guard !ocIdsToSkip.contains(metadata.ocId) else {
+ continue
}
+ if let match = metadatasCopy.first(where: { $0.ocId == metadata.ocId }) {
+ metadata.mediaSearch = match.mediaSearch
+ }
+ realm.add(metadata.detachedCopy(), update: .all)
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataEncrypted(ocId: String, encrypted: Bool) {
- do {
- let realm = try Realm()
- try realm.write {
- let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
- result?.e2eEncrypted = encrypted
+ func setMetadataEncrypted(ocId: String, encrypted: Bool, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first
+ result?.e2eEncrypted = encrypted
+ }
+ }
+
+ func setMetadataEncryptedAsync(ocId: String, encrypted: Bool) async {
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first
+ result?.e2eEncrypted = encrypted
+ }
+ }
+
+ func setMetadataFileNameView(serverUrl: String, fileName: String, newFileNameView: String, account: String, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName)
+ .first
+ result?.fileNameView = newFileNameView
+ }
+ }
+
+ func setMetadataFileNameViewAsync(serverUrl: String, fileName: String, newFileNameView: String, account: String) async {
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName)
+ .first
+ result?.fileNameView = newFileNameView
+ }
+ }
+
+ func moveMetadata(ocId: String, serverUrlTo: String, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first {
+ result.serverUrl = serverUrlTo
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataFileNameView(serverUrl: String, fileName: String, newFileNameView: String, account: String) {
- do {
- let realm = try Realm()
- try realm.write {
- let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first
- result?.fileNameView = newFileNameView
+ func moveMetadataAsync(ocId: String, serverUrlTo: String) async {
+ await performRealmWriteAsync { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first {
+ result.serverUrl = serverUrlTo
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func moveMetadata(ocId: String, serverUrlTo: String) {
- do {
- let realm = try Realm()
- try realm.write {
- if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
- result.serverUrl = serverUrlTo
- }
+ func setLivePhotoFile(fileId: String, livePhotoFile: String) async {
+ await performRealmWriteAsync { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("fileId == %@", fileId)
+ .first
+ result?.livePhotoFile = livePhotoFile
+ }
+ }
+
+ func clearAssetLocalIdentifiersAsync(_ assetLocalIdentifiers: [String]) async {
+ await performRealmWriteAsync { realm in
+ let results = realm.objects(tableMetadata.self)
+ .filter("assetLocalIdentifier IN %@", assetLocalIdentifiers)
+ for result in results {
+ result.assetLocalIdentifier = ""
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func clearAssetLocalIdentifiers(_ assetLocalIdentifiers: [String]) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier IN %@", assetLocalIdentifiers)
- for result in results {
- result.assetLocalIdentifier = ""
- }
+ func setMetadataFavorite(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first
+ if let favorite {
+ result?.favorite = favorite
+ }
+ result?.storeFlag = saveOldFavorite
+ result?.status = status
+
+ if status == NCGlobal.shared.metadataStatusNormal {
+ result?.sessionDate = nil
+ } else {
+ result?.sessionDate = Date()
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- func setMetadataFavorite(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int) {
- do {
- let realm = try Realm()
- try realm.write {
- let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
- if let favorite {
- result?.favorite = favorite
- }
- result?.storeFlag = saveOldFavorite
- result?.status = status
+ /// Asynchronously sets the favorite status of a `tableMetadata` entry.
+ /// Optionally stores the previous favorite flag and updates the sync status.
+ func setMetadataFavoriteAsync(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int) async {
+ await performRealmWriteAsync { realm in
+ guard let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first else {
+ return
+ }
- if status == NCGlobal.shared.metadataStatusNormal {
- result?.sessionDate = nil
- } else {
- result?.sessionDate = Date()
- }
+ if let favorite {
+ result.favorite = favorite
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+
+ result.storeFlag = saveOldFavorite
+ result.status = status
+ result.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
}
}
- func setMetadataCopyMove(ocId: String, serverUrlTo: String, overwrite: String?, status: Int) {
- do {
- let realm = try Realm()
- try realm.write {
- let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
- result?.serverUrlTo = serverUrlTo
- result?.storeFlag = overwrite
- result?.status = status
+ func setMetadataCopyMove(ocId: String, serverUrlTo: String, overwrite: String?, status: Int, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ if let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first {
+ result.destination = serverUrlTo
+ result.storeFlag = overwrite
+ result.status = status
if status == NCGlobal.shared.metadataStatusNormal {
- result?.sessionDate = nil
+ result.sessionDate = nil
} else {
- result?.sessionDate = Date()
+ result.sessionDate = Date()
}
}
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
}
}
- // MARK: - GetMetadata
+ /// Asynchronously updates a `tableMetadata` entry to set copy/move status and target server URL.
+ func setMetadataCopyMoveAsync(ocId: String, destination: String, overwrite: String?, status: Int) async {
+ await performRealmWriteAsync { realm in
+ guard let result = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first else {
+ return
+ }
- func getMetadata(predicate: NSPredicate) -> tableMetadata? {
- do {
- let realm = try Realm()
- guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
- return tableMetadata(value: result)
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ result.destination = destination
+ result.storeFlag = overwrite
+ result.status = status
+ result.sessionDate = (status == NCGlobal.shared.metadataStatusNormal) ? nil : Date()
}
-
- return nil
}
- func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter(predicate)
- return Array(results.map { tableMetadata(value: $0) })
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func clearMetadatasUpload(account: String, sync: Bool = true) {
+ performRealmWrite(sync: sync) { realm in
+ let results = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusUploadError)
+ realm.delete(results)
}
- return []
}
- func getMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool = false) -> [tableMetadata]? {
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
- return Array(results.map { tableMetadata(value: $0) })
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func clearMetadatasUploadAsync(account: String) async {
+ await performRealmWriteAsync { realm in
+ let results = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusUploadError)
+ realm.delete(results)
}
- return nil
}
- func getMetadatas(predicate: NSPredicate, numItems: Int, sorted: String, ascending: Bool) -> [tableMetadata] {
- var counter: Int = 0
- var metadatas: [tableMetadata] = []
+ /// Syncs the remote and local metadata.
+ /// Returns true if there were changes (additions or deletions), false if everything was already up-to-date.
+ func mergeRemoteMetadatasAsync(remoteMetadatas: [tableMetadata], localMetadatas: [tableMetadata]) async -> Bool {
+ // Set of ocId
+ let remoteOcIds = Set(remoteMetadatas.map { $0.ocId })
+ let localOcIds = Set(localMetadatas.map { $0.ocId })
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
- for result in results where counter < numItems {
- metadatas.append(tableMetadata(value: result))
- counter += 1
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ // Calculate diffs
+ let toDeleteOcIds = localOcIds.subtracting(remoteOcIds)
+ let toAddOcIds = remoteOcIds.subtracting(localOcIds)
+
+ guard !toDeleteOcIds.isEmpty || !toAddOcIds.isEmpty else {
+ return false // No changes needed
}
- return metadatas
- }
- func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
- guard let ocId else { return nil }
+ let toDeleteKeys = Array(toDeleteOcIds)
- do {
- let realm = try Realm()
- guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
- return tableMetadata(value: result)
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ await performRealmWriteAsync { realm in
+ let toAdd = remoteMetadatas.filter { toAddOcIds.contains($0.ocId) }
+ let toDelete = toDeleteKeys.compactMap {
+ realm.object(ofType: tableMetadata.self, forPrimaryKey: $0)
+ }
+
+ realm.delete(toDelete)
+ realm.add(toAdd, update: .modified)
}
- return nil
+
+ return true
}
-// func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
-// guard let ocId else { return nil }
-//
-// do {
-// let realm = try Realm()
-// if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
-// return tableMetadata(value: result)
-// }
-// if let result = realm.objects(tableMetadata.self).filter("ocIdTransfer == %@", ocId).first {
-// return tableMetadata(value: result)
-// }
-// } catch let error as NSError {
-// NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-// }
-// return nil
-// }
-
- func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
- guard let ocId else { return nil }
+ // MARK: - Realm Read
+ func getAllTableMetadataAsync() async -> [tableMetadata] {
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self).map { tableMetadata(value: $0) }
+ } ?? []
+ }
+
+ func getMetadata(predicate: NSPredicate) -> tableMetadata? {
return performRealmRead { realm in
realm.objects(tableMetadata.self)
- .filter("ocId == %@ OR ocIdTransfer == %@", ocId, ocId)
+ .filter(predicate)
.first
- .map { tableMetadata(value: $0) }
+ .map { $0.detachedCopy() }
}
}
-
-// func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
-// guard let ocId else { return nil }
-//
-// return performRealmRead { realm in
-// // First, try to find by ocId
-// if let result = realm.objects(tableMetadata.self)
-// .filter("ocId == %@", ocId)
-// .first {
-// return tableMetadata(value: result)
-// }
-//
-// // If not found, try to find by ocIdTransfer
-// if let result = realm.objects(tableMetadata.self)
-// .filter("ocIdTransfer == %@", ocId)
-// .first {
-// return tableMetadata(value: result)
-// }
-//
-// // Not found
-// return nil
-// }
-// }
-
- func getMetadataFolder(session: NCSession.Session, serverUrl: String) -> tableMetadata? {
- var serverUrl = serverUrl
- var fileName = ""
- let serverUrlHome = utilityFileSystem.getHomeServer(session: session)
- if serverUrlHome == serverUrl {
- fileName = "."
- serverUrl = ".."
- } else {
- fileName = (serverUrl as NSString).lastPathComponent
- if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) {
- serverUrl = path
- }
+ func getMetadataAsync(predicate: NSPredicate) async -> tableMetadata? {
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .first
+ .map { $0.detachedCopy() }
}
+ }
+
+ func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
+ performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .map { $0.detachedCopy() }
+ } ?? []
+ }
+
+ func getMediaMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> ThreadSafeArray? {
do {
let realm = try Realm()
- guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", session.account, serverUrl, fileName).first else { return nil }
- return tableMetadata(value: result)
+ if let sorted {
+ var results: [tableMetadata] = []
+ switch sorted {//NCPreferences().mediaSortDate {
+ case "date":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.date as Date) > ($1.date as Date) }
+ case "creationDate":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.creationDate as Date) > ($1.creationDate as Date) }
+ case "uploadDate":
+ results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.uploadDate as Date) > ($1.uploadDate as Date) }
+ default:
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ }
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ } else {
+ let results = realm.objects(tableMetadata.self).filter(predicate)
+ return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+ }
} catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+// NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
}
return nil
}
- func getMetadataLivePhoto(metadata: tableMetadata) -> tableMetadata? {
- guard metadata.isLivePhoto else { return nil }
+ func getMetadatas(predicate: NSPredicate,
+ sortedByKeyPath: String,
+ ascending: Bool = false) -> [tableMetadata]? {
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
+ .map { $0.detachedCopy() }
+ }
+ }
- do {
- let realm = try Realm()
- guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
- metadata.account,
- metadata.serverUrl,
- metadata.livePhotoFile)).first else { return nil }
- return tableMetadata(value: result)
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func getMetadatasAsync(predicate: NSPredicate,
+ sortedByKeyPath: String,
+ ascending: Bool = false,
+ limit: Int? = nil) async -> [tableMetadata]? {
+ return await performRealmReadAsync { realm in
+ let results = realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .sorted(byKeyPath: sortedByKeyPath,
+ ascending: ascending)
+
+ if let limit {
+ let sliced = results.prefix(limit)
+ return sliced.map { $0.detachedCopy() }
+ } else {
+ return results.map { $0.detachedCopy() }
+ }
}
- return nil
}
- func getMetadataConflict(account: String, serverUrl: String, fileNameView: String, nativeFormat: Bool) -> tableMetadata? {
- let fileNameExtension = (fileNameView as NSString).pathExtension.lowercased()
- let fileNameNoExtension = (fileNameView as NSString).deletingPathExtension
- var fileNameConflict = fileNameView
+ func getMetadatas(predicate: NSPredicate,
+ numItems: Int,
+ sorted: String,
+ ascending: Bool) -> [tableMetadata] {
+ return performRealmRead { realm in
+ let results = realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .sorted(byKeyPath: sorted, ascending: ascending)
+ return results.prefix(numItems)
+ .map { $0.detachedCopy() }
+ } ?? []
+ }
- if fileNameExtension == "heic", !nativeFormat {
- fileNameConflict = fileNameNoExtension + ".jpg"
+ func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
+ guard let ocId else { return nil }
+
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first
+ .map { $0.detachedCopy() }
}
- return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@",
- account,
- serverUrl,
- fileNameConflict))
}
- // MARK: - GetResult(s)Metadata
+ func getMetadataFromOcIdAsync(_ ocId: String?) async -> tableMetadata? {
+ guard let ocId else { return nil }
- func getResultsMetadatasPredicate(_ predicate: NSPredicate, layoutForView: NCDBLayoutForView?, directoryOnTop: Bool = true) -> [tableMetadata] {
- do {
- let realm = try Realm()
- var results = realm.objects(tableMetadata.self).filter(predicate).freeze()
- let layout: NCDBLayoutForView = layoutForView ?? NCDBLayoutForView()
-
- if layout.sort == "fileName" {
- let sortedResults = results.sorted {
- let ordered = layout.ascending ? ComparisonResult.orderedAscending : ComparisonResult.orderedDescending
- // 1. favorite order
- if $0.favorite == $1.favorite {
- // 2. directory order TOP
- if directoryOnTop {
- if $0.directory == $1.directory {
- // 3. natural fileName
- return $0.fileNameView.localizedStandardCompare($1.fileNameView) == ordered
- } else {
- return $0.directory && !$1.directory
- }
- } else {
- return $0.fileNameView.localizedStandardCompare($1.fileNameView) == ordered
- }
- } else {
- return $0.favorite && !$1.favorite
- }
- }
- return sortedResults
- } else {
- if directoryOnTop {
- results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false).sorted(byKeyPath: "directory", ascending: false)
- } else {
- results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false)
- }
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter("ocId == %@", ocId)
+ .first
+ .map { $0.detachedCopy() }
+ }
+ }
+
+ func getMetadataFromOcIdAndocIdTransferAsync(_ ocId: String?) async -> tableMetadata? {
+ guard let ocId else {
+ return nil
+ }
+
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter("ocId == %@ OR ocIdTransfer == %@", ocId, ocId)
+ .first
+ .map { $0.detachedCopy() }
+ }
+ }
+
+ /// Asynchronously retrieves the metadata for a folder, based on its session and serverUrl.
+ /// Handles the home directory case rootFileName) and detaches the Realm object before returning.
+ func getMetadataFolderAsync(session: NCSession.Session, serverUrl: String) async -> tableMetadata? {
+ var serverUrl = serverUrl
+ var fileName = ""
+ let home = utilityFileSystem.getHomeServer(session: session)
+
+ if home == serverUrl {
+ fileName = NextcloudKit.shared.nkCommonInstance.rootFileName
+ } else {
+ fileName = (serverUrl as NSString).lastPathComponent
+ if let serverDirectoryUp = utilityFileSystem.serverDirectoryUp(serverUrl: serverUrl, home: home) {
+ serverUrl = serverDirectoryUp
}
- return Array(results)
+ }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter("account == %@ AND serverUrl == %@ AND fileName == %@", session.account, serverUrl, fileName)
+ .first
+ .map { $0.detachedCopy() }
}
- return []
}
- func getResultsMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool, arraySlice: Int) -> [tableMetadata] {
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending).prefix(arraySlice)
- return Array(results)
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func getMetadataLivePhoto(metadata: tableMetadata) -> tableMetadata? {
+ guard metadata.isLivePhoto else {
+ return nil
+ }
+ let detached = metadata.detachedCopy()
+
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
+ detached.account,
+ detached.serverUrl,
+ detached.livePhotoFile))
+ .first
+ .map { $0.detachedCopy() }
}
- return []
}
- func getResultMetadata(predicate: NSPredicate) -> tableMetadata? {
- do {
- let realm = try Realm()
- return realm.objects(tableMetadata.self).filter(predicate).first
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func getMetadataLivePhotoAsync(metadata: tableMetadata) async -> tableMetadata? {
+ guard metadata.isLivePhoto else {
+ return nil
+ }
+ let detached = metadata.detachedCopy()
+
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
+ detached.account,
+ detached.serverUrl,
+ detached.livePhotoFile))
+ .first
+ .map { $0.detachedCopy() }
}
- return nil
}
- func getResultMetadataFromFileName(_ fileName: String, serverUrl: String, sessionTaskIdentifier: Int) -> tableMetadata? {
- do {
- let realm = try Realm()
- return realm.objects(tableMetadata.self).filter("fileName == %@ AND serverUrl == %@ AND sessionTaskIdentifier == %d", fileName, serverUrl, sessionTaskIdentifier).first
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ func getMetadataConflict(account: String, serverUrl: String, fileNameView: String, nativeFormat: Bool) -> tableMetadata? {
+ let fileNameExtension = (fileNameView as NSString).pathExtension.lowercased()
+ let fileNameNoExtension = (fileNameView as NSString).deletingPathExtension
+ var fileNameConflict = fileNameView
+
+ if fileNameExtension == "heic", !nativeFormat {
+ fileNameConflict = fileNameNoExtension + ".jpg"
}
- return nil
+ return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@",
+ account,
+ serverUrl,
+ fileNameConflict))
}
- func getResultsMetadatasFromGroupfolders(session: NCSession.Session) -> Results? {
- var ocId: [String] = []
+ /// Asynchronously retrieves and sorts `tableMetadata` associated with groupfolders for a given session.
+ /// - Parameters:
+ /// - session: The `NCSession.Session` containing account and server information.
+ /// - layoutForView: An optional layout configuration used for sorting.
+ /// - Returns: An array of sorted and detached `tableMetadata` objects.
+ func getMetadatasFromGroupfoldersAsync(session: NCSession.Session, layoutForView: NCDBLayoutForView?) async -> [tableMetadata] {
let homeServerUrl = utilityFileSystem.getHomeServer(session: session)
- do {
- let realm = try Realm()
- let groupfolders = realm.objects(TableGroupfolders.self).filter("account == %@", session.account).sorted(byKeyPath: "mountPoint", ascending: true)
+ let detachedMetadatas: [tableMetadata] = await performRealmReadAsync { realm in
+ var ocIds: [String] = []
+
+ // Safely fetch and detach groupfolders
+ let groupfolders = realm.objects(TableGroupfolders.self)
+ .filter("account == %@", session.account)
+ .sorted(byKeyPath: "mountPoint", ascending: true)
+ .map { TableGroupfolders(value: $0) }
for groupfolder in groupfolders {
let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
let serverUrlFileName = homeServerUrl + mountPoint
- if let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", session.account, serverUrlFileName).first,
- let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first {
- ocId.append(result.ocId)
+ if let directory = realm.objects(tableDirectory.self)
+ .filter("account == %@ AND serverUrl == %@", session.account, serverUrlFileName)
+ .first,
+ let metadata = realm.objects(tableMetadata.self)
+ .filter("ocId == %@", directory.ocId)
+ .first {
+ ocIds.append(metadata.ocId)
}
}
- return realm.objects(tableMetadata.self).filter("ocId IN %@", ocId)
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ // Fetch and detach the corresponding metadatas
+ return realm.objects(tableMetadata.self)
+ .filter("ocId IN %@", ocIds)
+ .map { $0.detachedCopy() }
+ } ?? []
+
+ let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: session.account, metadatas: detachedMetadatas)
+ return sorted
+ }
+
+ func getRootContainerMetadata(accout: String) -> tableMetadata? {
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter("fileName == %@ AND account == %@", NextcloudKit.shared.nkCommonInstance.rootFileName, accout)
+ .first
+ .map { $0.detachedCopy() }
}
+ }
- return nil
+ func getRootContainerMetadataAsync(accout: String) async -> tableMetadata? {
+ return await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter("fileName == %@ AND account == %@", NextcloudKit.shared.nkCommonInstance.rootFileName, accout)
+ .first
+ .map { $0.detachedCopy() }
+ }
}
- func getTableMetadatasDirectoryFavoriteIdentifierRank(account: String) -> [String: NSNumber] {
- var listIdentifierRank: [String: NSNumber] = [:]
- var counter = 10 as Int64
+ func getMetadatasAsync(predicate: NSPredicate) async -> [tableMetadata] {
+ await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .map { $0.detachedCopy() }
+ } ?? []
+ }
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter("account == %@ AND directory == true AND favorite == true", account).sorted(byKeyPath: "fileNameView", ascending: true)
- for result in results {
+ func getTableMetadatasDirectoryFavoriteIdentifierRankAsync(account: String) async -> [String: NSNumber] {
+ let result = await performRealmReadAsync { realm in
+ var listIdentifierRank: [String: NSNumber] = [:]
+ var counter = Int64(10)
+
+ let results = realm.objects(tableMetadata.self)
+ .filter("account == %@ AND directory == true AND favorite == true", account)
+ .sorted(byKeyPath: "fileNameView", ascending: true)
+
+ results.forEach { item in
counter += 1
- listIdentifierRank[result.ocId] = NSNumber(value: Int64(counter))
+ listIdentifierRank[item.ocId] = NSNumber(value: counter)
}
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+
+ return listIdentifierRank
}
- return listIdentifierRank
+ return result ?? [:]
}
- @objc func clearMetadatasUpload(account: String) {
- do {
- let realm = try Realm()
- try realm.write {
- let results = realm.objects(tableMetadata.self).filter("account == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusUploadError)
- realm.delete(results)
- }
- } catch let error {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+ func getAssetLocalIdentifiersUploadedAsync() async -> [String]? {
+ return await performRealmReadAsync { realm in
+ let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
+ return results.map { $0.assetLocalIdentifier }
}
}
- func getAssetLocalIdentifiersUploaded() -> [String]? {
- var assetLocalIdentifiers: [String] = []
+ func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
+ guard let fileId else {
+ return nil
+ }
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
- for result in results {
- assetLocalIdentifiers.append(result.assetLocalIdentifier)
- }
- return assetLocalIdentifiers
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter("fileId == %@", fileId)
+ .first
+ .map { $0.detachedCopy() }
}
- return nil
}
- func getMetadataFromDirectory(account: String, serverUrl: String) -> Bool {
- do {
- let realm = try Realm()
- guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first,
- realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first != nil else { return false }
- return true
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ /// Asynchronously retrieves a `tableMetadata` object matching the given `fileId`, if available.
+ /// - Parameter fileId: The file identifier used to query the Realm database.
+ /// - Returns: A detached copy of the `tableMetadata` object, or `nil` if not found.
+ func getMetadataFromFileIdAsync(_ fileId: String?) async -> tableMetadata? {
+ guard let fileId else {
+ return nil
+ }
+
+ return await performRealmReadAsync { realm in
+ let object = realm.objects(tableMetadata.self)
+ .filter("fileId == %@", fileId)
+ .first
+ return object?.detachedCopy()
}
- return false
}
- func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
- guard let fileId else { return nil }
+ /// Asynchronously retrieves and sorts `tableMetadata` objects matching a given predicate and layout.
+ func getMetadatasAsync(predicate: NSPredicate,
+ withLayout layoutForView: NCDBLayoutForView?,
+ withAccount account: String) async -> [tableMetadata] {
+ let detachedMetadatas = await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .map { $0.detachedCopy() }
+ } ?? []
- do {
- let realm = try Realm()
- if let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first {
- return tableMetadata(value: result)
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: account, metadatas: detachedMetadatas)
+ return sorted
+ }
+
+ /// Asynchronously retrieves and sorts `tableMetadata` objects matching a given predicate and layout.
+ func getMetadatasAsyncDataSource(withServerUrl serverUrl: String,
+ withUserId userId: String,
+ withAccount account: String,
+ withLayout layoutForView: NCDBLayoutForView?,
+ withPreficate predicateSource: NSPredicate? = nil) async -> [tableMetadata] {
+ var predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName != %@ AND NOT (status IN %@)", account, serverUrl, NextcloudKit.shared.nkCommonInstance.rootFileName, NCGlobal.shared.metadataStatusHideInView)
+
+ if NCPreferences().getPersonalFilesOnly(account: account) {
+ predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName != %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@)", account, serverUrl, NextcloudKit.shared.nkCommonInstance.rootFileName, userId, NCGlobal.shared.metadataStatusHideInView)
}
- return nil
+
+ if let predicateSource {
+ predicate = predicateSource
+ }
+
+ let detachedMetadatas = await performRealmReadAsync { realm in
+ realm.objects(tableMetadata.self)
+ .filter(predicate)
+ .map { $0.detachedCopy() }
+ } ?? []
+
+ let cleanedMetadatas = filterAndNormalizeLivePhotos(from: detachedMetadatas)
+ let sorted = await self.sortedMetadata(layoutForView: layoutForView, account: account, metadatas: cleanedMetadatas)
+
+ return sorted
}
- func getResultMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
- guard let ocId else { return nil }
+ func getMetadatasAsync(predicate: NSPredicate,
+ withSort sortDescriptors: [RealmSwift.SortDescriptor] = [],
+ withLimit limit: Int? = nil) async -> [tableMetadata]? {
+ await performRealmReadAsync { realm in
+ var results = realm.objects(tableMetadata.self)
+ .filter(predicate)
- do {
- let realm = try Realm()
- return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+ if !sortDescriptors.isEmpty {
+ results = results.sorted(by: sortDescriptors)
+ }
+
+ if let limit {
+ let sliced = results.prefix(limit)
+ return sliced.map { $0.detachedCopy() }
+ } else {
+ return results.map { $0.detachedCopy() }
+ }
}
- return nil
+ }
+
+ func hasUploadingMetadataWithChunksOrE2EE() -> Bool {
+ return performRealmRead { realm in
+ realm.objects(tableMetadata.self)
+ .filter("status == %d AND (chunk > 0 OR e2eEncrypted == true)", NCGlobal.shared.metadataStatusUploading)
+ .first != nil
+ } ?? false
}
func getMetadataDirectoryAsync(serverUrl: String, account: String) async -> tableMetadata? {
@@ -1417,7 +1364,7 @@ extension NCManageDatabase {
guard let decodedBaseUrl = baseUrl.removingPercentEncoding else {
return nil
}
-
+
return performRealmRead { realm in
let object = realm.objects(tableMetadata.self)
.filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, decodedBaseUrl, fileName)
@@ -1425,182 +1372,4 @@ extension NCManageDatabase {
return object?.detachedCopy()
}
}
-
- func createMetadatasFolder(assets: [PHAsset],
- useSubFolder: Bool,
- session: NCSession.Session, completion: @escaping ([tableMetadata]) -> Void) {
- var foldersCreated: Set = []
- var metadatas: [tableMetadata] = []
- let serverUrlBase = getAccountAutoUploadDirectory(session: session)
- let fileNameBase = getAccountAutoUploadFileName(account: session.account)
- let predicate = NSPredicate(format: "account == %@ AND serverUrl BEGINSWITH %@ AND directory == true", session.account, serverUrlBase)
-
- func createMetadata(serverUrl: String, fileName: String, metadata: tableMetadata?) {
- guard !foldersCreated.contains(serverUrl + "/" + fileName) else {
- return
- }
- foldersCreated.insert(serverUrl + "/" + fileName)
-
- if let metadata {
- metadata.status = NCGlobal.shared.metadataStatusWaitCreateFolder
- metadata.sessionSelector = NCGlobal.shared.selectorUploadAutoUpload
- metadata.sessionDate = Date()
- metadatas.append(tableMetadata(value: metadata))
- } else {
- let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName,
- fileNameView: fileName,
- ocId: NSUUID().uuidString,
- serverUrl: serverUrl,
- url: "",
- contentType: "httpd/unix-directory",
- directory: true,
- session: session,
- sceneIdentifier: nil)
- metadata.status = NCGlobal.shared.metadataStatusWaitCreateFolder
- metadata.sessionSelector = NCGlobal.shared.selectorUploadAutoUpload
- metadata.sessionDate = Date()
- metadatas.append(metadata)
- }
- }
-
- let metadatasFolder = getMetadatas(predicate: predicate)
- let targetPath = serverUrlBase + "/" + fileNameBase
- let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
- createMetadata(serverUrl: serverUrlBase, fileName: fileNameBase, metadata: metadata)
-
- if useSubFolder {
- let autoUploadServerUrlBase = self.getAccountAutoUploadServerUrlBase(session: session)
- let autoUploadSubfolderGranularity = self.getAccountAutoUploadSubfolderGranularity()
- let folders = Set(assets.map { self.utilityFileSystem.createGranularityPath(asset: $0) }).sorted()
-
- for folder in folders {
- let componentsDate = folder.split(separator: "/")
- let year = componentsDate[0]
- let serverUrl = autoUploadServerUrlBase
- let fileName = String(year)
- let targetPath = serverUrl + "/" + fileName
- let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
-
- createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
-
- if autoUploadSubfolderGranularity >= NCGlobal.shared.subfolderGranularityMonthly {
- let month = componentsDate[1]
- let serverUrl = autoUploadServerUrlBase + "/" + year
- let fileName = String(month)
- let targetPath = serverUrl + "/" + fileName
- let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
-
- createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
-
- if autoUploadSubfolderGranularity == NCGlobal.shared.subfolderGranularityDaily {
- let day = componentsDate[2]
- let serverUrl = autoUploadServerUrlBase + "/" + year + "/" + month
- let fileName = String(day)
- let targetPath = serverUrl + "/" + fileName
- let metadata = metadatasFolder.first(where: { $0.serverUrl + "/" + $0.fileNameView == targetPath })
-
- createMetadata(serverUrl: serverUrl, fileName: fileName, metadata: metadata)
- }
-
- }
- return results
- } else {
- let results = realm.objects(tableMetadata.self).filter(predicate)
- if freeze {
- return results.freeze()
- }
- return results
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
- }
- return nil
- }
-
- func getCalculateCumulativeHash(for metadatas: [tableMetadata], account: String, serverUrl: String) -> String {
- let concatenatedEtags = metadatas.map { $0.etag }.joined(separator: "-")
- return sha256Hash(concatenatedEtags)
- }
-
- func getMediaMetadatas(predicate: NSPredicate) -> ThreadSafeArray? {
-
- do {
- let realm = try Realm()
- let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
- return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
- }
-
- return nil
- }
-
- func getMediaMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> ThreadSafeArray? {
-
- do {
- let realm = try Realm()
- if let sorted {
- var results: [tableMetadata] = []
- switch NCKeychain().mediaSortDate {
- case "date":
- results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.date as Date) > ($1.date as Date) }
- case "creationDate":
- results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.creationDate as Date) > ($1.creationDate as Date) }
- case "uploadDate":
- results = realm.objects(tableMetadata.self).filter(predicate).sorted { ($0.uploadDate as Date) > ($1.uploadDate as Date) }
- default:
- let results = realm.objects(tableMetadata.self).filter(predicate)
- return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
- }
- return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
- } else {
- let results = realm.objects(tableMetadata.self).filter(predicate)
- return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
- }
- return nil
- }
-
- func getAdvancedMetadatas(predicate: NSPredicate, page: Int = 0, limit: Int = 0, sorted: String, ascending: Bool) -> [tableMetadata] {
-
- var metadatas: [tableMetadata] = []
-
- do {
- let realm = try Realm()
- realm.refresh()
- let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
- if !results.isEmpty {
- if page == 0 || limit == 0 {
- return Array(results.map { tableMetadata.init(value: $0) })
- } else {
- let nFrom = (page - 1) * limit
- let nTo = nFrom + (limit - 1)
- for n in nFrom...nTo {
- if n == results.count {
- break
- }
- metadatas.append(tableMetadata.init(value: results[n]))
- }
- }
- }
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
- }
-
- return metadatas
- }
-
- func getResultMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
- guard let fileId else { return nil }
-
- do {
- let realm = try Realm()
- return realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first
- } catch let error as NSError {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
- }
- return nil
- }
}
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
index c9aae962b1..01003052ec 100644
--- a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
@@ -28,6 +28,10 @@ import XLForm
// MARK: -
@objc class NCCreateFormUploadDocuments: XLFormViewController, NCSelectDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, NCCreateFormUploadConflictDelegate {
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool, session: NCSession.Session) {
+
+ }
+
@IBOutlet weak var indicator: UIActivityIndicatorView!
@IBOutlet weak var collectionView: UICollectionView!
@@ -43,8 +47,8 @@ import XLForm
var fileName = ""
var fileNameExtension = ""
var titleForm = ""
- var listOfTemplate: [NKEditorTemplates] = []
- var selectTemplate: NKEditorTemplates?
+ var listOfTemplate: [NKEditorTemplate] = []
+ var selectTemplate: NKEditorTemplate?
let utilityFileSystem = NCUtilityFileSystem()
let utility = NCUtility()
@@ -112,7 +116,7 @@ import XLForm
XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
row.action.formSelector = #selector(changeDestinationFolder(_:))
- row.cellConfig["folderImage.image"] = UIImage(named: "folder")!.imageColor(NCBrandColor.shared.customer)
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder")!.withTintColor(NCBrandColor.shared.customer)
row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
@@ -275,58 +279,62 @@ import XLForm
@objc func save() {
- guard let selectTemplate = self.selectTemplate else { return }
- templateIdentifier = selectTemplate.identifier
-
- let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
- var fileName = rowFileName.value as? String
- if fileName?.isEmpty ?? false || fileName == nil {
- fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
- } else if fileName?.trimmingCharacters(in: .whitespaces).isEmpty ?? false {
- let alert = UIAlertController(title: "", message: NSLocalizedString("_please_enter_file_name_", comment: ""), preferredStyle: .alert)
- alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
- self.present(alert, animated: true)
- return
- }
-
- // Ensure fileName is not nil or empty
- guard var fileNameForm: String = fileName, !fileNameForm.isEmpty else { return }
-
- // Trim whitespaces and newlines
- fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
-
- fileName = FileAutoRenamer.rename(fileNameForm, account: session.account)
-
- let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false, account: session.account
- )
- if utility.editorsDirectEditing(account: session.account, contentType: result.mimeType).isEmpty {
- fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
- }
-
- // verify if already exists
- if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", session.account, self.serverUrl, fileNameForm)) != nil {
- NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
- return
-// }
-//
-// if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
-//
-// let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
-//
-// guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
-//
-// conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
-// conflict.alwaysNewFileNameNumber = true
-// conflict.serverUrl = serverUrl
-// conflict.metadatasUploadInConflict = [metadataForUpload]
-// conflict.delegate = self
-//
-// self.present(conflict, animated: true, completion: nil)
-
- } else {
-
- let fileNamePath = utilityFileSystem.getFileNamePath(String(describing: fileNameForm), serverUrl: serverUrl, session: session)
- createDocument(fileNamePath: fileNamePath, fileName: String(describing: fileNameForm))
+ Task {
+ guard let selectTemplate = self.selectTemplate else { return }
+ templateIdentifier = selectTemplate.identifier
+
+ let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ var fileName = rowFileName.value as? String
+ if fileName?.isEmpty ?? false || fileName == nil {
+ fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
+ } else if fileName?.trimmingCharacters(in: .whitespaces).isEmpty ?? false {
+ let alert = UIAlertController(title: "", message: NSLocalizedString("_please_enter_file_name_", comment: ""), preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
+ self.present(alert, animated: true)
+ return
+ }
+
+ // Ensure fileName is not nil or empty
+ guard var fileNameForm: String = fileName, !fileNameForm.isEmpty else { return }
+
+ // Trim whitespaces and newlines
+ fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ let fileAutoRenamer = FileAutoRenamer()
+ fileName = fileAutoRenamer.rename(filename: fileNameForm, isFolderPath: true)
+
+ let result = await NKTypeIdentifiers.shared.getInternalType(fileName: fileNameForm, mimeType: "", directory: false, account: session.account)
+
+ if utility.editorsDirectEditing(account: session.account, contentType: result.mimeType).isEmpty {
+ fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
+ }
+
+ // verify if already exists
+ if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", session.account, self.serverUrl, fileNameForm)) != nil {
+ NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
+ return
+ // }
+ //
+ // if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
+ //
+ // let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
+ //
+ // guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+ //
+ // conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+ // conflict.alwaysNewFileNameNumber = true
+ // conflict.serverUrl = serverUrl
+ // conflict.metadatasUploadInConflict = [metadataForUpload]
+ // conflict.delegate = self
+ //
+ // self.present(conflict, animated: true, completion: nil)
+
+ } else {
+
+ let fileNamePath = utilityFileSystem.getFileNamePath(String(describing: fileNameForm), serverUrl: serverUrl, session: session)
+ await NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileNameForm), fileNameExtension: self.fileNameExtension, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account)
+
+ }
}
}
@@ -335,7 +343,11 @@ import XLForm
if let metadatas, metadatas.count > 0 {
let fileName = metadatas[0].fileName
let fileNamePath = utilityFileSystem.getFileNamePath(fileName, serverUrl: serverUrl, session: session)
- createDocument(fileNamePath: fileNamePath, fileName: fileName)
+// createDocument(fileNamePath: fileNamePath, fileName: fileName)
+ Task {
+ await NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileName), editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account)
+ }
+
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.cancel()
@@ -351,54 +363,62 @@ import XLForm
if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
- var options = NKRequestOptions()
- if self.editorId == NCGlobal.shared.editorOnlyoffice {
- options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
- } else if editorId == NCGlobal.shared.editorText {
- options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
- }
-
- NextcloudKit.shared.textCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { account, url, _, error in
- guard error == .success, account == self.session.account, let url = url else {
- self.navigationItem.rightBarButtonItem?.isEnabled = true
- NCContentPresenter().showError(error: error)
- return
- }
-
- var results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false, account: self.session.account)
- // FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
- if results.mimeType.isEmpty {
- results.mimeType = "text/x-markdown"
+ Task {
+ var options = NKRequestOptions()
+ if self.editorId == NCGlobal.shared.editorOnlyoffice {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
+ } else if editorId == NCGlobal.shared.editorText {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
}
-
- self.dismiss(animated: true, completion: {
- let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName, fileNameView: fileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: results.mimeType, session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
- if let viewController = self.appDelegate.activeViewController {
-// NCViewer().view(viewController: viewController, metadata: metadata)
- NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
+
+ let results = await NextcloudKit.shared.textCreateFileAsync(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: self.session.account,
+ path: fileNamePath,
+ name: "textCreateFile")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
}
- })
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+ let metadata = await NCManageDatabase.shared.createMetadataAsync(fileName: fileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: controller) {
+ controller.navigationController?.pushViewController(vc, animated: true)
+ }
}
}
if self.editorId == NCGlobal.shared.editorCollabora {
-
- NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier, account: session.account) { account, url, _, error in
- guard error == .success, account == self.session.account, let url = url else {
- self.navigationItem.rightBarButtonItem?.isEnabled = true
- NCContentPresenter().showError(error: error)
- return
- }
-
- self.dismiss(animated: true, completion: {
- let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension
- let metadata = NCManageDatabase.shared.createMetadata(fileName: createFileName, fileNameView: createFileName, ocId: UUID, serverUrl: self.serverUrl, url: url, contentType: "", session: self.session, sceneIdentifier: self.appDelegate.sceneIdentifier)
- AnalyticsHelper.shared.trackCreateFile(metadata: metadata)
- if let viewController = self.appDelegate.activeViewController {
-// NCViewer().view(viewController: viewController, metadata: metadata)
- NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata])
+ Task {
+
+ let results = await NextcloudKit.shared.createRichdocumentsAsync(path: fileNamePath, templateId: templateIdentifier, account: session.account) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: self.session.account,
+ path: fileNamePath,
+ name: "CreateRichdocuments")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
}
- })
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+
+ let metadata = await NCManageDatabase.shared.createMetadataAsync(fileName: fileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: controller) {
+ controller.navigationController?.pushViewController(vc, animated: true)
+ }
}
}
}
@@ -432,7 +452,7 @@ import XLForm
for template in templates {
- let temp = NKEditorTemplates()
+ var temp = NKEditorTemplate()
temp.identifier = template.identifier
temp.ext = template.ext
@@ -452,7 +472,7 @@ import XLForm
if self.listOfTemplate.isEmpty {
- let temp = NKEditorTemplates()
+ var temp = NKEditorTemplate()
temp.identifier = ""
if self.editorId == NCGlobal.shared.editorText {
@@ -489,14 +509,14 @@ import XLForm
for template in templates! {
- let temp = NKEditorTemplates()
+ var temp = NKEditorTemplate()
temp.identifier = "\(template.templateId)"
- temp.delete = template.delete
+// temp.delete = template.delete
temp.ext = template.ext
temp.name = template.name
temp.preview = template.preview
- temp.type = template.type
+// temp.type = template.type
self.listOfTemplate.append(temp)
@@ -511,7 +531,7 @@ import XLForm
if self.listOfTemplate.isEmpty {
- let temp = NKEditorTemplates()
+ var temp = NKEditorTemplate()
temp.identifier = ""
if self.typeTemplate == NCGlobal.shared.templateDocument {