Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6cc0c08
feat(socketapii): add file actions option to list of options for files.
camilasan Jun 23, 2025
a2562f8
feat(declarativeui): style file actions window.
camilasan Sep 3, 2025
fd85cc6
feat(declarativeui): improve the looks of the file actions Window.
camilasan Sep 3, 2025
fe117db
fix(declarativeui/fileactions): file actions context menu.
camilasan Sep 4, 2025
c26b19d
feat(declarativeui): add logging category to FileActionsModel.
camilasan Sep 17, 2025
f0163a3
feat(declarativeui/fileactions): return default icon when server does…
camilasan Sep 18, 2025
0ed16df
fix(declarativeui/fileactions): parse response with declarative UI el…
camilasan Sep 30, 2025
be34cc1
refactor(declarativeui): remove unused DeclarativeUi class.
camilasan Oct 1, 2025
554601c
fix(declarativeui): files license.
camilasan Oct 1, 2025
fef645d
refactor(declarativeui): remove Declarative UI menu item.
camilasan Oct 1, 2025
06c2905
fix(capabilities): update capabilities with new json key + adjust mim…
camilasan Jan 20, 2026
6eec098
fix(declarativeui): use EnforcedPlainTextLabel instead of Label.
camilasan Jan 21, 2026
fe480c9
fix(capabilities): an empty mimetype_filters means the action is avai…
camilasan Jan 21, 2026
405cf97
fix(declarativeui/fileactions): handle the new params format.
camilasan Jan 21, 2026
2c89c94
style(integration): rename from declarativeui to integration.
camilasan Jan 31, 2026
5447b43
fix(integratio): return true if there isn't an empty client_integrati…
camilasan Feb 1, 2026
92c51a8
feat(integration): do not list file actions if the server doesn't sup…
camilasan Feb 1, 2026
414b33d
style(tray): modernize ActivityListModel::data.
camilasan Feb 1, 2026
448004a
fix(integration): use ActivityData function to retrieve server icon.
camilasan Feb 2, 2026
cc07a49
refactor(tray): modernize Activity::relativeServerFileTypeIconPath.
camilasan Feb 2, 2026
2821f2f
feat(integration): align icons and make sure they all have the same s…
camilasan Feb 2, 2026
cd03ad3
fix(integration): remove unused files and variables.
camilasan Feb 3, 2026
792b00e
fix(integration): missing reference on non-trivial type.
camilasan Feb 3, 2026
82def6c
feat(fileprovider): add file actions to context menu.
camilasan Feb 16, 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
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ SPDX-FileCopyrightText = "2016 Nextcloud GmbH"
SPDX-License-Identifier = "LicenseRef-NextcloudTrademarks"

[[annotations]]
path = ["theme/white/error.svg", "theme/black/error.svg","theme/white/warning.svg", "theme/black/warning.svg", "theme/white/info.svg", "theme/black/info.svg", "theme/info.svg", "theme/white/nextcloud/*.svg", "theme/black/nextcloud/*.svg", "theme/colored/nextcloud/*.svg", "theme/black/label.svg", "theme/white/label.svg", "theme/colored/user-status-*.svg", "theme/account.svg","theme/add.svg","theme/change.svg","theme/chevron-double-up.svg","theme/close.svg","theme/confirm.svg","theme/copy.svg","theme/delete.svg","theme/external.svg","theme/files.svg","theme/lock-broken.svg","theme/lock-http.svg","theme/lock-https.svg","theme/lock.svg","theme/magnifying-glass.svg","theme/more.svg","theme/network.svg","theme/public.svg","theme/reply.svg","theme/send.svg","theme/settings.svg","theme/share.svg","theme/sync-arrow.svg", "theme/black/account-group.svg","theme/black/clear.svg","theme/black/expand-less-black.svg","theme/black/folder-group.svg","theme/black/search.svg", "theme/colored/add-bordered.svg", "theme/colored/change-bordered.svg", "theme/colored/delete-bordered.svg", "theme/colored/delete.svg", "theme/*/activity.svg", "theme/*/add.svg","theme/*/bell.svg","theme/*/calendar.svg","theme/*/caret-down.svg","theme/*/change.svg","theme/*/close.svg","theme/*/comment.svg","theme/*/confirm.svg","theme/*/control-next.svg","theme/*/control-prev.svg","theme/*/edit.svg","theme/*/email.svg","theme/*/external.png","theme/*/external.svg","theme/*/external@2x.png","theme/*/folder.png","theme/*/folder.svg","theme/*/folder@2x.png","theme/*/more-apps.svg","theme/*/nc-assistant-app.svg","theme/*/settings.svg","theme/*/user.svg","theme/*/wizard-files.png","theme/*/wizard-files.svg","theme/*/wizard-files@2x.png","theme/*/wizard-groupware.png","theme/*/wizard-groupware.svg","theme/*/wizard-groupware@2x.png", "theme/cfapishellext_custom_states/0-locked.svg","theme/cfapishellext_custom_states/1-shared.svg","theme/cfapishellext_custom_states/1024-0-locked.png","theme/cfapishellext_custom_states/1024-1-shared.png","theme/cfapishellext_custom_states/128-0-locked.png","theme/cfapishellext_custom_states/128-1-shared.png","theme/cfapishellext_custom_states/24-0-locked.png","theme/cfapishellext_custom_states/24-1-shared.png","theme/cfapishellext_custom_states/256-0-locked.png","theme/cfapishellext_custom_states/256-1-shared.png","theme/cfapishellext_custom_states/32-0-locked.png","theme/cfapishellext_custom_states/32-1-shared.png","theme/cfapishellext_custom_states/40-0-locked.png","theme/cfapishellext_custom_states/40-1-shared.png","theme/cfapishellext_custom_states/48-0-locked.png","theme/cfapishellext_custom_states/48-1-shared.png","theme/cfapishellext_custom_states/512-0-locked.png","theme/cfapishellext_custom_states/512-1-shared.png","theme/cfapishellext_custom_states/64-0-locked.png","theme/cfapishellext_custom_states/64-1-shared.png"]
path = ["theme/white/error.svg", "theme/black/error.svg","theme/white/warning.svg", "theme/black/warning.svg", "theme/white/info.svg", "theme/black/info.svg", "theme/info.svg", "theme/white/nextcloud/*.svg", "theme/black/nextcloud/*.svg", "theme/colored/nextcloud/*.svg", "theme/black/label.svg", "theme/white/label.svg", "theme/colored/user-status-*.svg", "theme/account.svg","theme/add.svg","theme/change.svg","theme/chevron-double-up.svg","theme/close.svg","theme/confirm.svg","theme/copy.svg","theme/delete.svg","theme/external.svg","theme/files.svg","theme/lock-broken.svg","theme/lock-http.svg","theme/lock-https.svg","theme/lock.svg","theme/magnifying-glass.svg","theme/more.svg","theme/network.svg","theme/public.svg","theme/reply.svg","theme/send.svg","theme/settings.svg","theme/share.svg","theme/sync-arrow.svg", "theme/black/account-group.svg","theme/black/clear.svg","theme/black/expand-less-black.svg","theme/black/folder-group.svg","theme/black/search.svg", "theme/colored/add-bordered.svg", "theme/colored/change-bordered.svg", "theme/colored/delete-bordered.svg", "theme/colored/delete.svg", "theme/*/activity.svg", "theme/*/add.svg","theme/*/bell.svg","theme/*/calendar.svg","theme/*/caret-down.svg","theme/*/change.svg","theme/*/close.svg","theme/*/comment.svg","theme/*/confirm.svg","theme/*/control-next.svg","theme/*/control-prev.svg","theme/*/edit.svg","theme/*/email.svg","theme/*/external.png","theme/*/external.svg","theme/*/external@2x.png","theme/*/folder.png","theme/*/folder.svg","theme/*/folder@2x.png","theme/*/more-apps.svg","theme/*/nc-assistant-app.svg","theme/*/settings.svg","theme/*/user.svg","theme/*/wizard-files.png","theme/*/wizard-files.svg","theme/*/wizard-files@2x.png","theme/*/wizard-groupware.png","theme/*/wizard-groupware.svg","theme/*/wizard-groupware@2x.png", "theme/cfapishellext_custom_states/0-locked.svg","theme/cfapishellext_custom_states/1-shared.svg","theme/cfapishellext_custom_states/1024-0-locked.png","theme/cfapishellext_custom_states/1024-1-shared.png","theme/cfapishellext_custom_states/128-0-locked.png","theme/cfapishellext_custom_states/128-1-shared.png","theme/cfapishellext_custom_states/24-0-locked.png","theme/cfapishellext_custom_states/24-1-shared.png","theme/cfapishellext_custom_states/256-0-locked.png","theme/cfapishellext_custom_states/256-1-shared.png","theme/cfapishellext_custom_states/32-0-locked.png","theme/cfapishellext_custom_states/32-1-shared.png","theme/cfapishellext_custom_states/40-0-locked.png","theme/cfapishellext_custom_states/40-1-shared.png","theme/cfapishellext_custom_states/48-0-locked.png","theme/cfapishellext_custom_states/48-1-shared.png","theme/cfapishellext_custom_states/512-0-locked.png","theme/cfapishellext_custom_states/512-1-shared.png","theme/cfapishellext_custom_states/64-0-locked.png","theme/cfapishellext_custom_states/64-1-shared.png", "theme/backup.svg", "theme/convert_to_text.svg", "theme/file-open.svg"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2018-2025 Google LLC"
SPDX-License-Identifier = "Apache-2.0"
1 change: 1 addition & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@
<file>src/gui/ConflictItemFileInfo.qml</file>
<file>src/gui/macOS/ui/FileProviderSettings.qml</file>
<file>src/gui/macOS/ui/FileProviderFileDelegate.qml</file>
<file>src/gui/integration/FileActionsWindow.qml</file>
</qresource>
</RCC>
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
return nil
}

guard let appGroupIdentifier = extensionDictionary["NSExtensionFileProviderDocumentGroup"] as? String else {
return nil
if let fileProviderGroupIdentifier = extensionDictionary["NSExtensionFileProviderDocumentGroup"] as? String,
!fileProviderGroupIdentifier.isEmpty {

Check warning on line 22 in shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Extensions/FileManager+applicationGroupContainer.swift

View workflow job for this annotation

GitHub Actions / Lint

Wrap the opening brace of multiline statements. (wrapMultilineStatementBraces)

Check warning on line 22 in shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Extensions/FileManager+applicationGroupContainer.swift

View workflow job for this annotation

GitHub Actions / Lint

Indent code in accordance with the scope level. (indent)
return containerURL(forSecurityApplicationGroupIdentifier: fileProviderGroupIdentifier)
}

if let mainAppGroupIdentifier = Bundle.main.object(forInfoDictionaryKey: "SocketApiprefix") as? String,
!mainAppGroupIdentifier.isEmpty {

Check warning on line 27 in shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Extensions/FileManager+applicationGroupContainer.swift

View workflow job for this annotation

GitHub Actions / Lint

Wrap the opening brace of multiline statements. (wrapMultilineStatementBraces)

Check warning on line 27 in shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Extensions/FileManager+applicationGroupContainer.swift

View workflow job for this annotation

GitHub Actions / Lint

Indent code in accordance with the scope level. (indent)
return containerURL(forSecurityApplicationGroupIdentifier: mainAppGroupIdentifier)
}

return containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later

import FileProviderUI
import FileProvider
import NCDesktopClientSocketKit
import NextcloudKit
import NextcloudFileProviderKit
import OSLog

Expand Down Expand Up @@ -29,6 +32,9 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
var logger: FileProviderLogger!

var serviceResolver: ServiceResolver!

var socketClient: LocalSocketClient?
lazy var lineProcessor: LineProcessor = NoOpLineProcessor()

// MARK: - Lifecycle

Expand All @@ -44,6 +50,27 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
if serviceResolver == nil, let log {
serviceResolver = ServiceResolver(log: log)
}

setupSocketClientForSocketApi()
}

private func setupSocketClientForSocketApi() {
guard let socketApiPrefix = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String, !socketApiPrefix.isEmpty else {
logger?.error("No SocketApiPrefix found")
return
}

guard let containerUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: socketApiPrefix) else {
logger?.error("No containerURL available")
return
}

let socketPath = containerUrl.appendingPathComponent("s", isDirectory: true)
let processor = NoOpLineProcessor()
// FIXME
// self.socketClient = LocalSocketClient(socketPath: socketPath.path, lineProcessor: processor)
// self.socketClient?.start()
// self.logger?.debug("Socket client started successfully")
}

func prepare(childViewController: NSViewController) {
Expand Down Expand Up @@ -73,6 +100,8 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
case "com.nextcloud.desktopclient.FileProviderUIExt.EvictAction":
evict(itemsWithIdentifiers: itemIdentifiers, inDomain: domain);
extensionContext.completeRequest();
case "com.nextcloud.desktopclient.FileProviderUIExt.FileActionsAction":
showFileActions(itemsWithIdentifiers: itemIdentifiers, inDomain: domain);
default:
return
}
Expand Down Expand Up @@ -129,4 +158,38 @@ class DocumentActionViewController: FPUIActionExtensionViewController {

semaphore.wait()
}

func showFileActions(itemsWithIdentifiers identifiers: [NSFileProviderItemIdentifier], inDomain domain: NSFileProviderDomain) {
guard let firstIdentifier = identifiers.first else {
logger?.error("No item identifiers provided.")
extensionContext.completeRequest()
return
}

Task {
do {
guard let manager = NSFileProviderManager(for: domain) else {
logger?.error("Could not get file provider domain manager.")
await MainActor.run { extensionContext.completeRequest() }
return
}
let itemURL = try await manager.getUserVisibleURL(for: firstIdentifier)
let localPath = itemURL.path
let command = "FILE_ACTIONS:\(localPath)\n"
// FIXME
// self.socketClient?.sendMessage(command)
// self.logger?.debug("FILE_ACTIONS sent for: \(localPath)")
await MainActor.run { extensionContext.completeRequest() }
} catch {
logger?.error("Error: \(error.localizedDescription)")
await MainActor.run { extensionContext.completeRequest() }
}
}
}
}

private final class NoOpLineProcessor: NSObject, LineProcessor {
func process(_ line: String) {
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<string>$(OC_APPLICATION_NAME) File Provider UI Extension</string>
<key>CFBundleIdentifier</key>
<string>$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)</string>
<key>SocketApiPrefix</key>
<string>$(DEVELOPMENT_TEAM).$(OC_APPLICATION_REV_DOMAIN)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionFileProviderActions</key>
Expand Down Expand Up @@ -57,6 +59,14 @@
<key>NSExtensionFileProviderActionName</key>
<string>Free up space</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>SUBQUERY ( fileproviderItems, $fileproviderItem, $fileproviderItem.userInfo != nil ).@count &gt; 0</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>com.nextcloud.desktopclient.FileProviderUIExt.FileActionsAction</string>
<key>NSExtensionFileProviderActionName</key>
<string>File Actions</string>
</dict>
</array>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.fileprovider-actionsui</string>
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ set(client_SRCS
wizard/linklabel.cpp
wizard/wizardproxysettingsdialog.h
wizard/wizardproxysettingsdialog.cpp
integration/fileactionsmodel.h
integration/fileactionsmodel.cpp
)

if (NOT DISABLE_ACCOUNT_MIGRATION)
Expand Down
3 changes: 3 additions & 0 deletions src/gui/application.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
Expand Down Expand Up @@ -432,6 +432,9 @@
connect(FolderMan::instance()->socketApi(), &SocketApi::fileActivityCommandReceived,
_gui.data(), &ownCloudGui::slotShowFileActivityDialog);

connect(FolderMan::instance()->socketApi(), &SocketApi::fileActionsCommandReceived,
_gui.data(), &ownCloudGui::slotShowFileActionsDialog);

// startup procedure.
connect(&_checkConnectionTimer, &QTimer::timeout, this, &Application::slotCheckConnection);
_checkConnectionTimer.setInterval(ConnectionValidator::DefaultCallingIntervalMsec); // check for connection every 32 seconds.
Expand Down
Loading
Loading