Skip to content

Custom share permissions #3459

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 34 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
969f8ef
Add read permission indicator + refactor
mpivchev Apr 2, 2025
e92ac49
WIP
mpivchev Apr 8, 2025
4647e9e
Merge branch 'master' of https://github.com/nextcloud/ios into add-sh…
mpivchev Apr 9, 2025
b473eb3
WIP
mpivchev Apr 9, 2025
fefa242
WIP
mpivchev Apr 9, 2025
f8a77ea
Download and sync in advanced
mpivchev Apr 9, 2025
d6adbb2
Make download and sync button work
mpivchev Apr 9, 2025
935fdcf
Remove download from normal permissions
mpivchev Apr 10, 2025
c2b8fd1
Remove unneeded db param
mpivchev Apr 11, 2025
5889f64
Fix view only, allow edit, custom permissions
mpivchev Apr 14, 2025
219df5a
Fix link custom permissions being grayed out
mpivchev Apr 14, 2025
7fc9a8a
Merge branch 'master' of https://github.com/nextcloud/ios into add-sh…
mpivchev Apr 15, 2025
bd87c2b
Fix share screen not updating with sharees on first open
mpivchev Apr 15, 2025
e6098fb
Refactor
mpivchev Apr 15, 2025
4637451
Add capabilities check for download limit
mpivchev Apr 15, 2025
6aae638
WIP
mpivchev Apr 16, 2025
b2140cc
WIP
mpivchev May 2, 2025
e58020b
Merge branch 'master' of https://github.com/nextcloud/ios into add-sh…
mpivchev May 2, 2025
2288d34
Add quick options to link shares
mpivchev May 5, 2025
d5d5dbf
WIP
mpivchev May 6, 2025
1d6ef84
Add selected flag to file drop
mpivchev May 6, 2025
05f0485
Remove share permission on email shares
mpivchev May 6, 2025
559ef53
Refactor
mpivchev May 7, 2025
5a450ac
Merge branch 'master' of https://github.com/nextcloud/ios into add-sh…
mpivchev May 7, 2025
9186989
Fix shares not showing
mpivchev May 7, 2025
5375499
Fix ui
mpivchev May 7, 2025
151ff20
Refactor
mpivchev May 14, 2025
7d67a04
Merge branch 'master' of https://github.com/nextcloud/ios into add-sh…
mpivchev May 15, 2025
b2fca73
PR changes
mpivchev May 16, 2025
ebe9ab3
PR changes
mpivchev May 16, 2025
f736cd4
WIP
mpivchev May 19, 2025
64becc2
Prevent user from choosing themselves
mpivchev May 19, 2025
3cf367d
Do not show already existing sharees
mpivchev May 20, 2025
ff67bb1
WIP
mpivchev May 21, 2025
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
6 changes: 4 additions & 2 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,7 @@
F3CA337C2D0B2B6A00672333 /* AlbumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumModel.swift; sourceTree = "<group>"; };
F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenAwakeManager.swift; sourceTree = "<group>"; };
F3E173BF2C9B1067006D177A /* AwakeMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwakeMode.swift; sourceTree = "<group>"; };
F3F9C4552DC8CAB8001D3E11 /* NextcloudKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NextcloudKit; path = ../NextcloudKit; sourceTree = SOURCE_ROOT; };
F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = "<group>"; };
F700510022DF63AC003A3356 /* NCShare.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCShare.storyboard; sourceTree = "<group>"; };
F700510422DF6A89003A3356 /* NCShare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShare.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3271,6 +3272,7 @@
F7F67B9F1A24D27800EE80DA = {
isa = PBXGroup;
children = (
F3F9C4552DC8CAB8001D3E11 /* NextcloudKit */,
AA8E041E2D3114E200E7E89C /* README.md */,
F7B8B82F25681C3400967775 /* GoogleService-Info.plist */,
F7C1CDD91E6DFC6F005D92BE /* Brand */,
Expand Down Expand Up @@ -6209,8 +6211,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/scenee/FloatingPanel";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
kind = exactVersion;
version = 2.8.6;
};
};
F77333862927A72100466E35 /* XCRemoteSwiftPackageReference "OpenSSL" */ = {
Expand Down
9 changes: 3 additions & 6 deletions iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,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
Expand Down Expand Up @@ -99,8 +98,8 @@ class tableMetadata: Object {
@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<String>()
let shareType = List<Int>()
@objc dynamic var size: Int64 = 0
@objc dynamic var status: Int = 0
Expand Down Expand Up @@ -380,9 +379,7 @@ extension NCManageDatabase {
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)
}
Expand Down
12 changes: 7 additions & 5 deletions iOSClient/Data/NCManageDatabase+Share.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class tableShareV2: Object {
@objc dynamic var sendPasswordByTalk: Bool = false
@objc dynamic var serverUrl = ""

///
/// shareType - (int) 0 = user; 1 = group; 3 = public link; 4 = email; 6 = federated cloud share; 7 = circle; 10 = Talk conversation
///
/// See [OCS Share API documentation](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-share-api.html) for semantic definitions of the different possible values.
///
Expand Down Expand Up @@ -252,9 +254,9 @@ extension NCManageDatabase {
// There is currently only one share attribute “download” from the scope “permissions”. This attribute is only valid for user and group shares, not for public link shares.
func setAttibuteDownload(state: Bool) -> String? {
if state {
return nil
return "[{\"scope\":\"permissions\",\"key\":\"download\",\"value\":true}]"
} else {
return "[{\"scope\":\"permissions\",\"key\":\"download\",\"enabled\":false}]"
return "[{\"scope\":\"permissions\",\"key\":\"download\",\"value\":null}]"
}
}

Expand All @@ -264,10 +266,10 @@ extension NCManageDatabase {
if let json = try JSONSerialization.jsonObject(with: data) as? [Dictionary<String, Any>] {
for sub in json {
let key = sub["key"] as? String
let enabled = sub["enabled"] as? Bool
let enabled = (sub["value"] as? Bool) /* >= NC 30 */ ?? sub["enabled"] as? Bool // /* < NC 29 */
let scope = sub["scope"] as? String
if key == "download", scope == "permissions", let enabled = enabled {
return enabled
if key == "download", scope == "permissions" {
return enabled ?? false
}
}
}
Expand Down
68 changes: 50 additions & 18 deletions iOSClient/Menu/NCShare+Menu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extension NCShare {
let capabilities = NCCapabilities.shared.getCapabilities(account: self.metadata.account)
var actions = [NCMenuAction]()

if share.shareType == 3, canReshare {
if share.shareType == NCShareCommon().SHARE_TYPE_LINK, canReshare {
actions.append(
NCMenuAction(
title: NSLocalizedString("_share_add_sharelink_", comment: ""),
Expand Down Expand Up @@ -95,40 +95,69 @@ extension NCShare {
self.presentMenu(with: actions, sender: sender)
}

func toggleUserPermissionMenu(isDirectory: Bool, tableShare: tableShare, sender: Any?) {
func toggleQuickPermissionsMenu(isDirectory: Bool, share: tableShare, sender: Any?) {
var actions = [NCMenuAction]()
let permissions = NCPermissions()

actions.append(
NCMenuAction(
actions.append(contentsOf:
[NCMenuAction(
title: NSLocalizedString("_share_read_only_", comment: ""),
icon: utility.loadImage(named: "eye", colors: [NCBrandColor.shared.iconImageColor]),
selected: tableShare.permissions == (permissions.permissionReadShare + permissions.permissionShareShare) || tableShare.permissions == permissions.permissionReadShare,
selected: share.permissions == (permissions.permissionReadShare + permissions.permissionShareShare) || share.permissions == permissions.permissionReadShare,
on: false,
sender: sender,
action: { _ in
let canShare = permissions.isPermissionToCanShare(tableShare.permissions)
let permissions = permissions.getPermission(canEdit: false, canCreate: false, canChange: false, canDelete: false, canShare: canShare, isDirectory: isDirectory)
self.updateSharePermissions(share: tableShare, permissions: permissions)
let permissions = permissions.getPermissionValue(canCreate: false, canEdit: false, canDelete: false, canShare: false, isDirectory: isDirectory)
self.updateSharePermissions(share: share, permissions: permissions)
}
)
)

actions.append(
),
NCMenuAction(
title: isDirectory ? NSLocalizedString("_share_allow_upload_", comment: "") : NSLocalizedString("_share_editing_", comment: ""),
title: NSLocalizedString("_share_editing_", comment: ""),
icon: utility.loadImage(named: "pencil", colors: [NCBrandColor.shared.iconImageColor]),
selected: hasUploadPermission(tableShare: tableShare),
selected: hasUploadPermission(tableShare: share),
on: false,
sender: sender,
action: { _ in
let canShare = permissions.isPermissionToCanShare(tableShare.permissions)
let permissions = permissions.getPermission(canEdit: true, canCreate: true, canChange: true, canDelete: true, canShare: canShare, isDirectory: isDirectory)
self.updateSharePermissions(share: tableShare, permissions: permissions)
let permissions = permissions.getPermissionValue(canCreate: true, canEdit: true, canDelete: true, canShare: true, isDirectory: isDirectory)
self.updateSharePermissions(share: share, permissions: permissions)
}
)
),
NCMenuAction(
title: NSLocalizedString("_custom_permissions_", comment: ""),
icon: utility.loadImage(named: "ellipsis", colors: [NCBrandColor.shared.iconImageColor]),
sender: sender,
action: { _ in
guard
let advancePermission = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "NCShareAdvancePermission") as? NCShareAdvancePermission,
let navigationController = self.navigationController, !share.isInvalidated else { return }
advancePermission.networking = self.networking
advancePermission.share = tableShare(value: share)
advancePermission.oldTableShare = tableShare(value: share)
advancePermission.metadata = self.metadata

if let downloadLimit = try? self.database.getDownloadLimit(byAccount: self.metadata.account, shareToken: share.token) {
advancePermission.downloadLimit = .limited(limit: downloadLimit.limit, count: downloadLimit.count)
}

navigationController.pushViewController(advancePermission, animated: true)
}
)]
)

if isDirectory && (share.shareType == NCShareCommon().SHARE_TYPE_LINK /* public link */ || share.shareType == NCShareCommon().SHARE_TYPE_EMAIL) {
actions.insert(NCMenuAction(
title: NSLocalizedString("_share_file_drop_", comment: ""),
icon: utility.loadImage(named: "arrow.up.document", colors: [NCBrandColor.shared.iconImageColor]),
selected: share.permissions == permissions.permissionCreateShare,
on: false,
sender: sender,
action: { _ in
let permissions = permissions.getPermissionValue(canRead: false, canCreate: true, canEdit: false, canDelete: false, canShare: false, isDirectory: isDirectory)
self.updateSharePermissions(share: share, permissions: permissions)
}
), at: 2)
}

self.presentMenu(with: actions, sender: sender)
}

Expand All @@ -152,6 +181,9 @@ extension NCShare {
if let model = try database.getDownloadLimit(byAccount: metadata.account, shareToken: updatedShare.token) {
downloadLimit = .limited(limit: model.limit, count: model.count)
}
if let model = try database.getDownloadLimit(byAccount: metadata.account, shareToken: updatedShare.token) {
downloadLimit = .limited(limit: model.limit, count: model.count)
}
} catch {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Failed to get download limit from database!")
return
Expand Down
117 changes: 61 additions & 56 deletions iOSClient/Share/Advanced/NCShareAdvancePermission.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,59 +27,6 @@ import SVGKit
import CloudKit

class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDelegate, NCShareNavigationTitleSetting {
func dismissShareAdvanceView(shouldSave: Bool) {
guard shouldSave else {
guard oldTableShare?.hasChanges(comparedTo: share) != false else {
navigationController?.popViewController(animated: true)
return
}

let alert = UIAlertController(
title: NSLocalizedString("_cancel_request_", comment: ""),
message: NSLocalizedString("_discard_changes_info_", comment: ""),
preferredStyle: .alert)

alert.addAction(UIAlertAction(
title: NSLocalizedString("_discard_changes_", comment: ""),
style: .destructive,
handler: { _ in self.navigationController?.popViewController(animated: true) }))

alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_editing_", comment: ""), style: .default))
self.present(alert, animated: true)

return
}

Task {
// TODO: Apply share token to download limit object

if isNewShare {
let serverUrl = metadata.serverUrl + "/" + metadata.fileName

if share.shareType != NCShareCommon().SHARE_TYPE_LINK, metadata.e2eEncrypted,
NCCapabilities.shared.getCapabilities(account: metadata.account).capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {

if NCNetworkingE2EE().isInUpload(account: metadata.account, serverUrl: serverUrl) {
let error = NKError(errorCode: NCGlobal.shared.errorE2EEUploadInProgress, errorDescription: NSLocalizedString("_e2e_in_upload_", comment: ""))
return NCContentPresenter().showInfo(error: error)
}

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, addUserId: share.shareWith, removeUserId: nil, account: metadata.account)

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

networking?.createShare(share, downloadLimit: self.downloadLimit)
} else {
networking?.updateShare(share, downloadLimit: self.downloadLimit)
}
}

navigationController?.popViewController(animated: true)
}

let database = NCManageDatabase.shared

var oldTableShare: tableShare?
Expand Down Expand Up @@ -172,7 +119,7 @@ class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDeleg

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return NSLocalizedString("_permissions_", comment: "")
return NSLocalizedString("_custom_permissions_", comment: "")
} else if section == 1 {
return NSLocalizedString("_advanced_", comment: "")
} else { return nil }
Expand All @@ -186,7 +133,7 @@ class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDeleg
if section == 0 {
// check reshare permission, if restricted add note
let maxPermission = metadata.directory ? NCPermissions().permissionMaxFolderShare : NCPermissions().permissionMaxFileShare
return shareConfig.resharePermission != maxPermission ? shareConfig.permissions.count + 1 : shareConfig.permissions.count
return shareConfig.sharePermission != maxPermission ? shareConfig.permissions.count + 1 : shareConfig.permissions.count
} else if section == 1 {
return shareConfig.advanced.count
} else { return 0 }
Expand All @@ -208,7 +155,7 @@ class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDeleg
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let cellConfig = shareConfig.config(for: indexPath) else { return }
guard let cellConfig = cellConfig as? NCShareDetails else {
guard let cellConfig = cellConfig as? NCAdvancedPermission else {
cellConfig.didSelect(for: share)
tableView.reloadData()
return
Expand Down Expand Up @@ -259,8 +206,66 @@ class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDeleg
tableView.reloadData()
}
self.present(alertController, animated: true)
case .downloadAndSync:
share.downloadAndSync.toggle()
tableView.reloadData()
}
}

func dismissShareAdvanceView(shouldSave: Bool) {
guard shouldSave else {
guard oldTableShare?.hasChanges(comparedTo: share) != false else {
navigationController?.popViewController(animated: true)
return
}

let alert = UIAlertController(
title: NSLocalizedString("_cancel_request_", comment: ""),
message: NSLocalizedString("_discard_changes_info_", comment: ""),
preferredStyle: .alert)

alert.addAction(UIAlertAction(
title: NSLocalizedString("_discard_changes_", comment: ""),
style: .destructive,
handler: { _ in self.navigationController?.popViewController(animated: true) }))

alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_editing_", comment: ""), style: .default))
self.present(alert, animated: true)

return
}

Task {
if (share.shareType == NCShareCommon().SHARE_TYPE_LINK || share.shareType == NCShareCommon().SHARE_TYPE_EMAIL) && NCPermissions().isPermissionToCanShare(share.permissions) {
share.permissions = share.permissions - NCPermissions().permissionShareShare
}

if isNewShare {
let serverUrl = metadata.serverUrl + "/" + metadata.fileName

if share.shareType != NCShareCommon().SHARE_TYPE_LINK, metadata.e2eEncrypted,
NCCapabilities.shared.getCapabilities(account: metadata.account).capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {

if NCNetworkingE2EE().isInUpload(account: metadata.account, serverUrl: serverUrl) {
let error = NKError(errorCode: NCGlobal.shared.errorE2EEUploadInProgress, errorDescription: NSLocalizedString("_e2e_in_upload_", comment: ""))
return NCContentPresenter().showInfo(error: error)
}

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, addUserId: share.shareWith, removeUserId: nil, account: metadata.account)

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

networking?.createShare(share, downloadLimit: self.downloadLimit)
} else {
networking?.updateShare(share, downloadLimit: self.downloadLimit)
}
}

navigationController?.popViewController(animated: true)
}
}

// MARK: - NCShareDownloadLimitTableViewControllerDelegate
Expand Down
Loading
Loading