Skip to content

Commit

Permalink
Offline rename (#3086)
Browse files Browse the repository at this point in the history
* rename
* rename e2ee

---------

Signed-off-by: Marino Faggiana <marino@marinofaggiana.com>
  • Loading branch information
marinofaggiana authored Oct 3, 2024
1 parent 6b97d2c commit bcdb595
Show file tree
Hide file tree
Showing 21 changed files with 296 additions and 159 deletions.
2 changes: 1 addition & 1 deletion Brand/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ import Foundation
// Database Realm
//
let databaseName = "nextcloud.realm"
let databaseSchemaVersion: UInt64 = 359
let databaseSchemaVersion: UInt64 = 360
3 changes: 2 additions & 1 deletion File Provider Extension/FileProviderExtension+Actions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ extension FileProviderExtension {
NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false, account: metadata.account) { account, error in
if error == .success {
// Rename metadata
self.database.renameMetadata(fileNameTo: itemName, ocId: ocId, account: account)
self.database.renameMetadata(fileNameNew: itemName, ocId: ocId)
self.database.setMetadataServeUrlFileNameStatusNormal(ocId: ocId)

guard let metadata = self.database.getMetadataFromOcId(ocId) else {
return completionHandler(nil, NSFileProviderError(.noSuchItem))
Expand Down
111 changes: 103 additions & 8 deletions iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class tableMetadata: Object {
@objc dynamic var richWorkspace: String?
@objc dynamic var sceneIdentifier: String?
@objc dynamic var serverUrl = ""
@objc dynamic var serveUrlFileName = ""
@objc dynamic var session = ""
@objc dynamic var sessionDate: Date?
@objc dynamic var sessionError = ""
Expand Down Expand Up @@ -392,6 +393,7 @@ extension NCManageDatabase {
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)
Expand Down Expand Up @@ -514,6 +516,7 @@ extension NCManageDatabase {
metadata.ocIdTransfer = ocId
metadata.permissions = "RGDNVW"
metadata.serverUrl = serverUrl
metadata.serveUrlFileName = serverUrl + "/" + fileName
metadata.subline = subline
metadata.uploadDate = Date() as NSDate
metadata.url = url
Expand Down Expand Up @@ -552,18 +555,16 @@ extension NCManageDatabase {

@discardableResult
func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
let result = tableMetadata(value: metadata)

do {
let realm = try Realm()
try realm.write {
realm.add(result, update: .all)
realm.add(tableMetadata(value: metadata), update: .all)
}
} catch let error {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
return nil
}
return result
return tableMetadata(value: metadata)
}

func addMetadatas(_ metadatas: [tableMetadata]) {
Expand Down Expand Up @@ -614,17 +615,111 @@ extension NCManageDatabase {
}
}

func renameMetadata(fileNameTo: String, ocId: String, account: String) {
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 resultsType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameTo, mimeType: "", directory: result.directory, account: account)
result.fileName = fileNameTo
result.fileNameView = fileNameTo
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 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)
}
}
}
} 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

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)
}

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

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
}
}
} catch let error {
Expand Down
12 changes: 6 additions & 6 deletions iOSClient/Data/NCManageDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class NCManageDatabase: NSObject {

override init() {
func migrationSchema(_ migration: Migration, _ oldSchemaVersion: UInt64) {
if oldSchemaVersion < 359 {
if oldSchemaVersion < 360 {
migration.deleteData(forType: tableMetadata.className())
migration.enumerateObjects(ofType: tableDirectory.className()) { _, newObject in
newObject?["etag"] = ""
Expand All @@ -51,11 +51,11 @@ class NCManageDatabase: NSObject {
}

func compactDB(_ totalBytes: Int, _ usedBytes: Int) -> Bool {
// totalBytes refers to the size of the file on disk in bytes (data + free space)
// usedBytes refers to the number of bytes used by data in the file
// Compact if the file is over 100MB in size and less than 50% 'used'
let oneHundredMB = 100 * 1024 * 1024
return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
let usedPercentage = (Double(usedBytes) / Double(totalBytes)) * 100
/// Compact the database if more than 25% of the space is free
let shouldCompact = (usedPercentage < 75.0) && (totalBytes > 100 * 1024 * 1024)

return shouldCompact
}
var realm: Realm?
let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
Expand Down
34 changes: 19 additions & 15 deletions iOSClient/Extensions/UIAlertController+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ extension UIAlertController {
static func createFolder(serverUrl: String, account: String, markE2ee: Bool = false, sceneIdentifier: String? = nil, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: nil, preferredStyle: .alert)
let session = NCSession.shared.getSession(account: account)
let isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)

let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
guard let fileNameFolder = alertController.textFields?.first?.text else { return }
if markE2ee {
if NCNetworking.shared.isOffline {
return NCContentPresenter().showInfo(error: NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_offline_not_allowed_"))
}
Task {
let createFolderResults = await NCNetworking.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder, account: session.account)
if createFolderResults.error == .success {
Expand All @@ -50,6 +54,15 @@ extension UIAlertController {
NCContentPresenter().showError(error: createFolderResults.error)
}
}
} else if isDirectoryEncrypted {
if NCNetworking.shared.isOffline {
return NCContentPresenter().showInfo(error: NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_offline_not_allowed_"))
}
#if !EXTENSION
Task {
await NCNetworkingE2EECreateFolder().createFolder(fileName: fileNameFolder, serverUrl: serverUrl, withPush: true, sceneIdentifier: sceneIdentifier, session: session)
}
#endif
} else {
let metadataForCreateFolder = NCManageDatabase.shared.createMetadata(fileName: fileNameFolder,
fileNameView: fileNameFolder,
Expand Down Expand Up @@ -122,9 +135,7 @@ extension UIAlertController {
preferredStyle: .alert)
if canDeleteServer {
alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { (_: UIAlertAction) in
for metadata in selectedMetadatas {
NCNetworking.shared.deleteMetadata(metadata)
}
NCNetworking.shared.deleteMetadatas(selectedMetadatas, sceneIdentifier: sceneIdentifier)
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
completion(false)
})
Expand Down Expand Up @@ -202,32 +213,25 @@ extension UIAlertController {
let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert)

let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
guard let newFileName = alertController.textFields?.first?.text else { return }
guard let fileNameNew = alertController.textFields?.first?.text else { return }

// verify if already exists
if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, newFileName)) != nil {
if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil {
NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
return
}

NCActivityIndicator.shared.start()

NCNetworking.shared.renameMetadata(metadata, fileNameNew: newFileName) { error in

NCActivityIndicator.shared.stop()
NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew)

if error != .success {
NCContentPresenter().showError(error: error)
}
}
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl])
})

// text field is initially empty, no action
okAction.isEnabled = false
let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)

alertController.addTextField { textField in
textField.text = metadata.fileName
textField.text = metadata.fileNameView
textField.autocapitalizationType = .words
}

Expand Down
2 changes: 2 additions & 0 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ class NCFiles: NCCollectionViewCommon {
} else if self.dataSource.isEmpty() {
self.collectionView.reloadData()
}
} else if self.dataSource.isEmpty() {
self.collectionView.reloadData()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,8 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate {
let canDeleteServer = metadatas.allSatisfy { !$0.lock }

if canDeleteServer {
let copyMetadatas = metadatas
alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in
for metadata in copyMetadatas {
NCNetworking.shared.deleteMetadata(metadata)
}
NCNetworking.shared.deleteMetadatas(metadatas, sceneIdentifier: self.controller?.sceneIdentifier)
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
self.setEditMode(false)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
@objc func renameFile(_ notification: NSNotification) {
guard let userInfo = notification.userInfo as NSDictionary?,
let account = userInfo["account"] as? String,
account == session.account
let serverUrl = userInfo["serverUrl"] as? String,
let error = userInfo["error"] as? NKError,
account == session.account,
serverUrl == self.serverUrl
else { return }

if error != .success {
NCContentPresenter().showError(error: error)
}

reloadDataSource()
}

Expand Down
5 changes: 4 additions & 1 deletion iOSClient/Media/NCMedia+Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ extension NCMedia: NCMediaSelectTabBarDelegate {
let ocIds = self.fileSelect.map { $0 }
var alertStyle = UIAlertController.Style.actionSheet
var indexPaths: [IndexPath] = []
var metadatas: [tableMetadata] = []

if UIDevice.current.userInterfaceIdiom == .pad { alertStyle = .alert }

Expand All @@ -203,10 +204,12 @@ extension NCMedia: NCMediaSelectTabBarDelegate {

for ocId in ocIds {
if let metadata = self.database.getMetadataFromOcId(ocId) {
NCNetworking.shared.deleteMetadata(metadata)
metadatas.append(metadata)
}
}

NCNetworking.shared.deleteMetadatas(metadatas, sceneIdentifier: self.controller?.sceneIdentifier)

for index in indices {
let indexPath = IndexPath(row: index, section: 0)
if let cell = self.collectionView.cellForItem(at: indexPath) as? NCMediaCell,
Expand Down
3 changes: 1 addition & 2 deletions iOSClient/Menu/NCCollectionViewCommon+Menu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,7 @@ extension NCCollectionViewCommon {
//
// RENAME
//
if NCNetworking.shared.isOnline,
metadata.isRenameable {
if metadata.isRenameable {
actions.append(
NCMenuAction(
title: NSLocalizedString("_rename_", comment: ""),
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Menu/NCContextMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class NCContextMenu: NSObject {
}
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: alertStyle)
alertController.addAction(UIAlertAction(title: NSLocalizedString("_delete_file_", comment: ""), style: .destructive) { _ in
NCNetworking.shared.deleteMetadata(metadata)
NCNetworking.shared.deleteMetadatas([metadata], sceneIdentifier: sceneIdentifier)
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
})
alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { _ in })
Expand Down
Loading

0 comments on commit bcdb595

Please sign in to comment.