Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a4790b5
nmc 2023 - privacy policy localization changes added
TSI-amrutwaghmare May 10, 2023
a42ac8f
NMC 2023 german translation update (+35 squashed commits)
TSI-amrutwaghmare May 10, 2023
3a18ea6
nmc 2023 - scan cluster localisation changes
TSI-amrutwaghmare May 19, 2023
1a1d810
nmc 2023 - sharing feature localisation strings update
TSI-amrutwaghmare Jun 6, 2023
e3514b5
nmc 2023 - localisation update for share text field
TSI-amrutwaghmare Jun 7, 2023
0fae51c
NMC 2023 - update missing localised strings for german languages
TSI-amrutwaghmare Feb 27, 2024
0de80ca
nmc 2023 - privacy policy localization changes added
TSI-amrutwaghmare May 10, 2023
6b37df4
NMC 1990 Settings cluster Localization strings added
TSI-shwetawaikar May 11, 2023
294e459
nmc 2023 - E2e and onboarding Internet not available strings update
TSI-amrutwaghmare May 24, 2023
4b6b523
nmc 2023 - sharing feature localisation strings update
TSI-amrutwaghmare Jun 6, 2023
c1f0efe
nmc 2023 - localisation update for share text field
TSI-amrutwaghmare Jun 7, 2023
35b93c0
nmc 2023 - more tab localisation changes
TSI-amrutwaghmare Jun 9, 2023
9f90b86
nmc 2023 - image video upload localisation related changes
TSI-amrutwaghmare Jun 12, 2023
ff443a5
NMC 2023 "Details" string changed
TSI-shwetawaikar Jun 23, 2023
e9d81d0
NMC 2023 - share strings update
TSI-amrutwaghmare Jun 24, 2023
4bbc820
NMC 2023 - (nmc 2397) Strings update
TSI-amrutwaghmare Jul 11, 2023
b642253
NMC 2023 - Localisation changes for auto upload description
TSI-amrutwaghmare Dec 26, 2023
981ef9f
NMC 2023 - update missing localised strings for german languages
TSI-amrutwaghmare Feb 27, 2024
64b75b7
NMC 2023 german translation update
TSI-amrutwaghmare Aug 26, 2024
096fc6f
NMC 2023 - update missing localised strings for english and german la…
harshada-15-tsys Apr 7, 2025
dfb297a
NMC 2023 - Updated missing localised strings for English and DE language
harshada-15-tsys Apr 16, 2025
29d9089
NMC-2023 - Localizations for EN and DE updated for New sharing design…
harshada-15-tsys Sep 30, 2025
a712005
NMC 2023 german translation update (+35 squashed commits)
TSI-amrutwaghmare May 10, 2023
b0f341e
nmc 2023 - sharing feature localisation strings update
TSI-amrutwaghmare Jun 6, 2023
aecf7be
nmc 2023 - localisation update for share text field
TSI-amrutwaghmare Jun 7, 2023
4f98dea
NMC 2023 - update missing localised strings for german languages
TSI-amrutwaghmare Feb 27, 2024
67a70ca
nmc 2023 - privacy policy localization changes added
TSI-amrutwaghmare May 10, 2023
96b55cc
nmc 2023 - E2e and onboarding Internet not available strings update
TSI-amrutwaghmare May 24, 2023
d97fdeb
nmc 2023 - sharing feature localisation strings update
TSI-amrutwaghmare Jun 6, 2023
07870fb
nmc 2023 - localisation update for share text field
TSI-amrutwaghmare Jun 7, 2023
5909edd
nmc 2023 - more tab localisation changes
TSI-amrutwaghmare Jun 9, 2023
c2f2b62
nmc 2023 - image video upload localisation related changes
TSI-amrutwaghmare Jun 12, 2023
5e2e6d5
NMC 2023 "Details" string changed
TSI-shwetawaikar Jun 23, 2023
eee08b8
NMC 2023 - share strings update
TSI-amrutwaghmare Jun 24, 2023
147356d
NMC 2023 - (nmc 2397) Strings update
TSI-amrutwaghmare Jul 11, 2023
b879d4e
NMC 2023 - Localisation changes for auto upload description
TSI-amrutwaghmare Dec 26, 2023
e72f4bd
NMC 2023 - update missing localised strings for german languages
TSI-amrutwaghmare Feb 27, 2024
d923222
NMC 2023 german translation update
TSI-amrutwaghmare Aug 26, 2024
108fb96
NMC 2023 - update missing localised strings for english and german la…
harshada-15-tsys Apr 7, 2025
d2bbdb7
NMC 2023 - Updated missing localised strings for English and DE language
harshada-15-tsys Apr 16, 2025
9e00432
NMC-2023 - Localizations for EN and DE updated for New sharing design…
harshada-15-tsys Sep 30, 2025
d4d5b2f
NMC 2023 - Localisation changes
harshada-15-tsys Dec 17, 2025
1657a6f
Merge branch 'master' into nmc/2023-localization
harshada-15-tsys Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions File Provider Extension/FileProviderData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class FileProviderData: NSObject {
case workingSet
}

// MARK: -
// MARK: -

@discardableResult
func setupAccount(domain: NSFileProviderDomain? = nil,
Expand Down Expand Up @@ -175,7 +175,7 @@ class FileProviderData: NSObject {

if error == .success {
if let metadata = await NCManageDatabase.shared.getMetadataFromOcIdAsync(ocId) {
await NCManageDatabase.shared.addLocalFilesAsync(metadatas: [metadata])
await NCManageDatabase.shared.addLocalFileAsync(metadata: metadata)
}
}

Expand Down
61 changes: 23 additions & 38 deletions iOSClient/Data/NCManageDatabase+LocalFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,50 +29,35 @@ extension NCManageDatabase {

// MARK: - Realm Write

/// Adds or updates multiple local file entries corresponding to the given metadata array.
/// Uses async Realm read + single write transaction. Assumes `tableLocalFile` has a primary key.
/// - Parameters:
/// - metadatas: Array of `tableMetadata` to map into `tableLocalFile`.
/// - offline: Optional override for the `offline` flag applied to all items.
func addLocalFilesAsync(metadatas: [tableMetadata], offline: Bool? = nil) async {
guard !metadatas.isEmpty else {
return
}

// Extract ocIds for efficient lookup
let ocIds = metadatas.compactMap { $0.ocId }
guard !ocIds.isEmpty else {
return
/// - metadata: The `tableMetadata` containing file details.
/// - offline: Optional flag to mark the file as available offline.
/// - Returns: Nothing. Realm write is performed asynchronously.
func addLocalFileAsync(metadata: tableMetadata, offline: Bool? = nil) async {
// Read (non-blocking): safely detach from Realm thread
let existing: tableLocalFile? = performRealmRead { realm in
realm.objects(tableLocalFile.self)
.filter(NSPredicate(format: "ocId == %@", metadata.ocId))
.first
.map { tableLocalFile(value: $0) }
}

// Preload existing entries to avoid creating duplicates
let existingMap: [String: tableLocalFile] = await core.performRealmReadAsync { realm in
let existing = realm.objects(tableLocalFile.self)
.filter(NSPredicate(format: "ocId IN %@", ocIds))
return Dictionary(uniqueKeysWithValues:
existing.map { ($0.ocId, tableLocalFile(value: $0)) } // detached copy via value init
)
} ?? [:]
await performRealmWriteAsync { realm in
let addObject = existing ?? tableLocalFile()

await core.performRealmWriteAsync { realm in
for metadata in metadatas {
// Reuse existing object or create a new one
let local = existingMap[metadata.ocId] ?? tableLocalFile()

local.account = metadata.account
local.etag = metadata.etag
local.exifDate = NSDate()
local.exifLatitude = "-1"
local.exifLongitude = "-1"
local.ocId = metadata.ocId
local.fileName = metadata.fileName

if let offline {
local.offline = offline
}
addObject.account = metadata.account
addObject.etag = metadata.etag
addObject.exifDate = NSDate()
addObject.exifLatitude = "-1"
addObject.exifLongitude = "-1"
addObject.ocId = metadata.ocId
addObject.fileName = metadata.fileName

realm.add(local, update: .all)
if let offline {
addObject.offline = offline
}

realm.add(addObject, update: .all)
}
}

Expand Down
66 changes: 66 additions & 0 deletions iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,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 == NKTypeClassFile.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 == NKTypeClassFile.document.rawValue
}

var isAudioOrVideo: Bool {
return classFile == NKTypeClassFile.audio.rawValue || classFile == NKTypeClassFile.video.rawValue
}
Expand Down Expand Up @@ -340,6 +367,17 @@ 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), !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
Expand Down Expand Up @@ -882,6 +920,34 @@ extension NCManageDatabase {
.map { $0.detachedCopy() }
} ?? []
}

func getMediaMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> ThreadSafeArray<tableMetadata>? {

do {
let realm = try Realm()
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("Could not access database: \(error)")
}
return nil
}

func getMetadatas(predicate: NSPredicate,
sortedByKeyPath: String,
Expand Down
32 changes: 17 additions & 15 deletions iOSClient/NCBackgroundLocationUploadManager.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2025 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later
//
// NCBackgroundLocationUploadManager.swift
// Nextcloud
//
// Created by Marino Faggiana on 06/06/25.
// Copyright © 2025 Marino Faggiana. All rights reserved.
//

import CoreLocation
import NextcloudKit
Expand Down Expand Up @@ -102,23 +106,21 @@ class NCBackgroundLocationUploadManager: NSObject, CLLocationManagerDelegate {
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Must work only in background
guard isAppInBackground else {
return
}

// Open Realm
guard NCManageDatabase.shared.openRealmBackground() else {
nkLog(tag: self.global.logTagLocation, emoji: .error, message: "Failed to open Realm in Location Manager")
return
}

let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
let location = locations.last
nkLog(tag: self.global.logTagLocation, emoji: .start, message: "Triggered by location change: \(location?.coordinate.latitude ?? 0), \(location?.coordinate.longitude ?? 0)")

Task.detached {
await appDelegate.backgroundSync()
if database.openRealmBackground() {
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
let location = locations.last
nkLog(tag: self.global.logTagLocation, emoji: .start, message: "Triggered by location change: \(location?.coordinate.latitude ?? 0), \(location?.coordinate.longitude ?? 0)")

Task.detached {
if let tblAccount = await self.database.getActiveTableAccountAsync() {
await appDelegate.backgroundSync(tblAccount: tblAccount)
}
}
}
}

Expand Down
58 changes: 54 additions & 4 deletions iOSClient/NCGlobal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ final class NCGlobal: Sendable {
// Intro selector
//
let introLogin: Int = 0
let introSignup: Int = 1
let introSignUpWithProvider: Int = 1

// Avatar
//
let avatarSize: Int = 128 * Int(UIScreen.main.scale)
let avatarSizeRounded: Int = 128

// Preview size
//
let size1024: CGSize = CGSize(width: 1024, height: 1024)
Expand Down Expand Up @@ -138,7 +139,35 @@ final class NCGlobal: Sendable {
//
let buttonMoreMore = "more"
let buttonMoreLock = "moreLock"

let buttonMoreStop = "stop"

// Standard height sections header/footer
//
let heightButtonsView: CGFloat = 50
let heightHeaderTransfer: CGFloat = 50
let heightSection: CGFloat = 30
let heightFooter: CGFloat = 1
let heightFooterButton: CGFloat = 30
let endHeightFooter: CGFloat = 85


// Text - OnlyOffice - Collabora - QuickLook
//
let editorText = "text"
let editorOnlyoffice = "onlyoffice"
let editorCollabora = "collabora"
let editorQuickLook = "quicklook"

let onlyofficeDocx = "onlyoffice_docx"
let onlyofficeXlsx = "onlyoffice_xlsx"
let onlyofficePptx = "onlyoffice_pptx"

// Template
//
let templateDocument = "document"
let templateSpreadsheet = "spreadsheet"
let templatePresentation = "presentation"

// Rich Workspace
//
let fileNameRichWorkspace = "Readme.md"
Expand Down Expand Up @@ -221,6 +250,8 @@ final class NCGlobal: Sendable {
let selectorSaveAsScan = "saveAsScan"
let selectorOpenDetail = "openDetail"
let selectorSynchronizationOffline = "synchronizationOffline"
let selectorPrint = "print"
let selectorDeleteFile = "deleteFile"

// Metadata : Status
//
Expand Down Expand Up @@ -251,7 +282,6 @@ final class NCGlobal: Sendable {
let metadataStatusForScreenAwake = [-1, -2, 1, 2]
let metadataStatusHideInView = [1, 2, 3, 11]
let metadataStatusWaitWebDav = [10, 11, 12, 13, 14, 15]
let metadataStatusTransfers = [-2, -3, 2, 3, 10, 11, 12, 13, 14, 15]

let metadatasStatusInWaiting = [-1, 1, 10, 11, 12, 13, 14, 15]
let metadatasStatusInProgress = [-2, 2]
Expand All @@ -268,13 +298,17 @@ final class NCGlobal: Sendable {
let notificationCenterChangeTheming = "changeTheming" // userInfo: account
let notificationCenterRichdocumentGrabFocus = "richdocumentGrabFocus"
let notificationCenterReloadDataNCShare = "reloadDataNCShare"
let notificationCenterDidCreateShareLink = "didCreateShareLink"

let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView"
let notificationCenterReloadAvatar = "reloadAvatar"
let notificationCenterClearCache = "clearCache"
let notificationCenterCheckUserDelaultErrorDone = "checkUserDelaultErrorDone" // userInfo: account, controller
let notificationCenterServerDidUpdate = "serverDidUpdate" // userInfo: account
let notificationCenterNetworkReachability = "networkReachability"

let notificationCenterRenameFile = "renameFile" // userInfo: serverUrl, account, error

let notificationCenterMenuSearchTextPDF = "menuSearchTextPDF"
let notificationCenterMenuGotToPageInPDF = "menuGotToPageInPDF"

Expand All @@ -288,6 +322,7 @@ final class NCGlobal: Sendable {
let notificationCenterPlayerIsPlaying = "playerIsPlaying"
let notificationCenterPlayerStoppedPlaying = "playerStoppedPlaying"

let notificationCenterFavoriteStatusChanged = "favoriteStatusChanged"
let notificationCenterUserInteractionMonitor = "serInteractionMonitor"

// Networking Status
Expand All @@ -304,8 +339,9 @@ final class NCGlobal: Sendable {
let networkingStatusUploading = "statusUploading"
let networkingStatusUploaded = "statusUploaded"

let networkingStatusReloadAvatar = "statusReloadAvatar"

let networkingStatusReloadAvatar = "statusReloadAvatar"
let notificationCenterUpdateIcons = "updateIcons"

// TIP
//
Expand Down Expand Up @@ -388,6 +424,20 @@ final class NCGlobal: Sendable {
//
let taskDescriptionRetrievesProperties = "retrievesProperties"
let taskDescriptionSynchronization = "synchronization"
let taskDescriptionDeleteFileOrFolder = "deleteFileOrFolder"

// MoEngage App Version
//
let moEngageAppVersion = 854

// Filename Mask and Type
//
let keyFileNameMask = "fileNameMask"
let keyFileNameType = "fileNameType"
let keyFileNameAutoUploadMask = "fileNameAutoUploadMask"
let keyFileNameAutoUploadType = "fileNameAutoUploadType"
let keyFileNameOriginal = "fileNameOriginal"
let keyFileNameOriginalAutoUpload = "fileNameOriginalAutoUpload"

// LOG TAG
//
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class NCNetworkingE2EEUpload: NSObject {
metadata.status = NCGlobal.shared.metadataStatusNormal

await self.database.addMetadataAsync(metadata)
await self.database.addLocalFilesAsync(metadatas: [metadata])
await self.database.addLocalFileAsync(metadata: metadata)
utility.createImageFileFrom(metadata: metadata)

await NCNetworking.shared.transferDispatcher.notifyAllDelegates { delegate in
Expand Down
25 changes: 22 additions & 3 deletions iOSClient/Networking/NCConfigServer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2022 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later
//
// NCConfigServer.swift
// Nextcloud
//
// Created by Marino Faggiana on 05/12/22.
// Copyright © 2022 Marino Faggiana. All rights reserved.
//
// Author Marino Faggiana <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
import UIKit
Expand Down
Loading