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
cb07bba
feat(fileprovider): add file actions to context menu via xpc.
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 @@ -6,7 +6,7 @@
#ifndef AppProtocol_h
#define AppProtocol_h

#import <Foundation/Foundation.h>

Check failure on line 9 in shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/AppProtocol.h

View workflow job for this annotation

GitHub Actions / build

shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/AppProtocol.h:9:9 [clang-diagnostic-error]

'Foundation/Foundation.h' file not found

/**
* @brief The main app APIs exposed through XPC.
Expand All @@ -20,6 +20,12 @@
*/
- (void)reportSyncStatus:(NSString *)status forDomainIdentifier:(NSString *)domainIdentifier;

/**
* @brief Request the main app to open the file actions dialog for the given local file path.
* @param localPath The local file system path.
*/
- (void)openFileActionsForLocalPath:(NSString *)localPath;

@end

#endif /* AppProtocol_h */
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ let fpUiExtensionServiceName = NSFileProviderServiceName("com.nextcloud.desktopc
/// Get a server URL for the given local file provider item.
///
func itemServerPath(identifier: NSFileProviderItemIdentifier) async -> NSString?

/// Ask the main app to open file actions for a local path.
func openFileActions(forLocalPath localPath: NSString) async
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,9 @@ class FPUIExtensionServiceSource: NSObject, NSFileProviderServiceSource, NSXPCLi
let completePath = item.serverUrl + "/" + item.fileName
return completePath.replacingOccurrences(of: baseUrl, with: "") as NSString
}

func openFileActions(forLocalPath localPath: NSString) async {
logger.info("Forwarding request to open file actions for path \(localPath)")
fpExtension.app?.openFileActions(forLocalPath: localPath as String)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
case "com.nextcloud.desktopclient.FileProviderUIExt.EvictAction":
evict(itemsWithIdentifiers: itemIdentifiers, inDomain: domain);
extensionContext.completeRequest();
case "com.nextcloud.desktopclient.FileProviderUIExt.FileActionsAction":
openFileActions(itemIdentifiers: itemIdentifiers)
extensionContext.completeRequest()
default:
return
}
Expand All @@ -94,6 +97,38 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
self.view = NSView()
}

// MARK: - File actions

func openFileActions(itemIdentifiers: [NSFileProviderItemIdentifier]) {
guard let itemIdentifier = itemIdentifiers.first else {
logger?.error("File actions action called without item identifier.")
return
}

Task {
do {
guard let manager = NSFileProviderManager(for: domain) else {
logger?.error("Could not initialize file provider manager.")
return
}

let itemURL = try await manager.getUserVisibleURL(for: itemIdentifier)
let localPath = itemURL.path

guard !localPath.isEmpty else {
logger?.error("Got empty local path for file actions item.")
return
}

let connection = try await serviceResolver.getService(at: itemURL)
await connection.openFileActions(forLocalPath: localPath as NSString)
logger?.info("Requested opening file actions for item path: \(localPath)")
} catch {
logger?.error("Could not open file actions.", [.error: error])
}
}
}

// MARK: - Eviction

///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@
<key>NSExtensionFileProviderActionName</key>
<string>Share options</string>
</dict>

<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>SUBQUERY ( fileproviderItems, $fileproviderItem, $fileproviderItem.userInfo.displayFileActions == true ).@count &gt; 0</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>com.nextcloud.desktopclient.FileProviderUIExt.FileActionsAction</string>
<key>NSExtensionFileProviderActionName</key>
<string>File actions</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>SUBQUERY ( fileproviderItems, $fileproviderItem, $fileproviderItem.userInfo.downloaded == true ).@count &gt; 0</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
30 changes: 29 additions & 1 deletion 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 @@ -92,7 +92,8 @@
#if !DISABLE_ACCOUNT_MIGRATION
" --forcelegacyconfigimport : forcefully import account configurations from legacy clients (if available).\n"
#endif
" --reverse : use a reverse layout direction.\n";
" --reverse : use a reverse layout direction.\n"
" --open-file-actions <path> : open the file actions dialog for the given local path.\n";

QString applicationTrPath()
{
Expand Down Expand Up @@ -432,6 +433,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 Expand Up @@ -461,6 +465,7 @@
_gui->createTray();

handleEditLocallyFromOptions();
handleFileActionsFromOptions();

if (AccountSetupCommandLineManager::instance()->isCommandLineParsed()) {
AccountSetupCommandLineManager::instance()->setupAccountFromCommandLine();
Expand Down Expand Up @@ -769,6 +774,7 @@
}

handleEditLocallyFromOptions();
handleFileActionsFromOptions();

if (AccountSetupCommandLineManager::instance()->isCommandLineParsed()) {
AccountSetupCommandLineManager::instance()->setupAccountFromCommandLine();
Expand Down Expand Up @@ -864,6 +870,12 @@
qCInfo(lcApplication) << errorParsingLocalFileEditingUrl;
showHint(errorParsingLocalFileEditingUrl.toStdString());
}
} else if (option == QLatin1String("--open-file-actions")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
_fileActionsPath = QDir::fromNativeSeparators(it.next());
} else {
showHint("Path for file actions dialog not specified");
}
} else if (option == QStringLiteral("--overrideserverurl")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
const auto overrideUrl = it.next();
Expand Down Expand Up @@ -995,6 +1007,22 @@
_editFileLocallyUrl.clear();
}

void Application::handleFileActionsFromOptions()
{
if (_fileActionsPath.isEmpty()) {
return;
}

if (_gui.isNull()) {
qCWarning(lcApplication) << "Cannot open file actions dialog without GUI.";
_fileActionsPath.clear();
return;
}

_gui->slotShowFileActionsDialog(_fileActionsPath);
_fileActionsPath.clear();
}

QString enforcedLanguage()
{
const ConfigFile cfg;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ protected slots:
void setHelp();

void handleEditLocallyFromOptions();
void handleFileActionsFromOptions();

AccountManager::AccountsRestoreResult restoreLegacyAccount();
void setupConfigFile();
Expand Down Expand Up @@ -131,6 +132,7 @@ protected slots:
bool _debugMode = false;
bool _backgroundMode = false;
QUrl _editFileLocallyUrl;
QString _fileActionsPath;

ClientProxy _proxy;

Expand Down
Loading
Loading