Skip to content

Commit 38390e7

Browse files
committed
feat: add default_action field to files-actions-menu registration
fixes #748 Signed-off-by: Jaap van der Plas <jvdplas@gmail.com>
1 parent 2c9093f commit 38390e7

7 files changed

Lines changed: 122 additions & 3 deletions

File tree

lib/Controller/OCSUiController.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,21 @@ public function registerFileActionMenu(string $name, string $displayName, string
6161
}
6262

6363
/**
64+
* @param string $defaultAction One of '', 'default', or 'hidden'
6465
* @throws OCSBadRequestException
6566
*/
6667
#[AppAPIAuth]
6768
#[PublicPage]
6869
#[NoCSRFRequired]
6970
public function registerFileActionMenuV2(string $name, string $displayName, string $actionHandler,
7071
string $icon = '', string $mime = 'file', int $permissions = 31,
71-
int $order = 0): DataResponse {
72+
int $order = 0, string $defaultAction = ''): DataResponse {
73+
$normalizedDefault = $defaultAction === '' ? null : $defaultAction;
74+
if ($normalizedDefault !== null && !in_array($normalizedDefault, ['default', 'hidden'], true)) {
75+
throw new OCSBadRequestException("Invalid defaultAction '$defaultAction' — must be '', 'default', or 'hidden'");
76+
}
7277
$result = $this->filesActionsMenuService->registerFileActionMenu(
73-
$this->request->getHeader('EX-APP-ID'), $name, $displayName, $actionHandler, $icon, $mime, $permissions, $order, '2.0');
78+
$this->request->getHeader('EX-APP-ID'), $name, $displayName, $actionHandler, $icon, $mime, $permissions, $order, '2.0', $normalizedDefault);
7479
if (!$result) {
7580
throw new OCSBadRequestException('File Action Menu entry could not be registered');
7681
}

lib/Db/UI/FilesActionsMenu.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* @method string getIcon()
2727
* @method string getActionHandler()
2828
* @method string getVersion()
29+
* @method string|null getDefaultAction()
2930
* @method void setAppid(string $appid)
3031
* @method void setName(string $name)
3132
* @method void setDisplayName(string $displayName)
@@ -35,6 +36,7 @@
3536
* @method void setIcon(string $icon)
3637
* @method void setActionHandler(string $actionHandler)
3738
* @method void setVersion(string $version)
39+
* @method void setDefaultAction(string|null $defaultAction)
3840
*/
3941
class FilesActionsMenu extends Entity implements JsonSerializable {
4042
protected $appid;
@@ -46,6 +48,7 @@ class FilesActionsMenu extends Entity implements JsonSerializable {
4648
protected $icon;
4749
protected $actionHandler;
4850
protected $version;
51+
protected $defaultAction;
4952

5053
/**
5154
* @param array $params
@@ -60,6 +63,7 @@ public function __construct(array $params = []) {
6063
$this->addType('icon', 'string');
6164
$this->addType('actionHandler', 'string');
6265
$this->addType('version', 'string');
66+
$this->addType('defaultAction', 'string');
6367

6468
if (isset($params['id'])) {
6569
$this->setId($params['id']);
@@ -91,6 +95,9 @@ public function __construct(array $params = []) {
9195
if (isset($params['version'])) {
9296
$this->setVersion($params['version']);
9397
}
98+
if (array_key_exists('default_action', $params)) {
99+
$this->setDefaultAction($params['default_action']);
100+
}
94101
}
95102

96103
public function jsonSerialize(): array {
@@ -105,6 +112,7 @@ public function jsonSerialize(): array {
105112
'icon' => $this->getIcon(),
106113
'action_handler' => $this->getActionHandler(),
107114
'version' => $this->getVersion(),
115+
'default_action' => $this->getDefaultAction(),
108116
];
109117
}
110118
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\AppAPI\Migration;
11+
12+
use Closure;
13+
use OCP\DB\ISchemaWrapper;
14+
use OCP\DB\Types;
15+
use OCP\Migration\Attributes\AddColumn;
16+
use OCP\Migration\Attributes\ColumnType;
17+
use OCP\Migration\IOutput;
18+
use OCP\Migration\SimpleMigrationStep;
19+
20+
#[AddColumn('ex_ui_files_actions', 'default_action', ColumnType::STRING, 'DefaultType for ExApp file actions')]
21+
class Version035000Date20260518120000 extends SimpleMigrationStep {
22+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
23+
/** @var ISchemaWrapper $schema */
24+
$schema = $schemaClosure();
25+
26+
if ($schema->hasTable('ex_ui_files_actions')) {
27+
$table = $schema->getTable('ex_ui_files_actions');
28+
29+
if (!$table->hasColumn('default_action')) {
30+
$table->addColumn('default_action', Types::STRING, [
31+
'notnull' => false,
32+
'length' => 16,
33+
'default' => null,
34+
]);
35+
}
36+
}
37+
38+
return $schema;
39+
}
40+
}

lib/Service/UI/FilesActionsMenuService.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ public function __construct(
4444
* @param int $permissions
4545
* @param int $order
4646
* @param string $version
47+
* @param string|null $defaultAction
4748
* @return FilesActionsMenu|null
4849
*/
4950
public function registerFileActionMenu(string $appId, string $name, string $displayName, string $actionHandler,
50-
string $icon, string $mime, int $permissions, int $order, string $version): ?FilesActionsMenu {
51+
string $icon, string $mime, int $permissions, int $order, string $version,
52+
?string $defaultAction = null): ?FilesActionsMenu {
5153
try {
5254
$fileActionMenu = $this->mapper->findByAppidName($appId, $name);
5355
} catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) {
@@ -64,6 +66,7 @@ public function registerFileActionMenu(string $appId, string $name, string $disp
6466
'permissions' => $permissions,
6567
'order' => $order,
6668
'version' => $version,
69+
'default_action' => $defaultAction,
6770
]);
6871
if ($fileActionMenu !== null) {
6972
$newFileActionMenu->setId($fileActionMenu->getId());

src/filesplugin.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ export function registerFileAction33(fileAction, iconProvider) {
151151
execBatch: async ({ nodes }) => execBatch(nodes),
152152
}
153153

154+
if (fileAction.default_action) {
155+
action.default = fileAction.default_action
156+
}
157+
154158
registerFileAction(action)
155159
}
156160

src/filesplugin.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,32 @@ describe('registerFileAction33', () => {
416416
expect(action.enabled({ nodes: [mockNode] })).toBe(false)
417417
})
418418

419+
it('does not set default when default_action is missing', () => {
420+
const action = getRegisteredAction({
421+
appid: 'testapp', action_handler: 'handler', name: 'test',
422+
display_name: 'Test', mime: 'image/jpeg', order: 0,
423+
})
424+
expect(action.default).toBeUndefined()
425+
})
426+
427+
it('passes through default_action="default"', () => {
428+
const action = getRegisteredAction({
429+
appid: 'testapp', action_handler: 'handler', name: 'test',
430+
display_name: 'Test', mime: 'image/jpeg', order: 0,
431+
default_action: 'default',
432+
})
433+
expect(action.default).toBe('default')
434+
})
435+
436+
it('passes through default_action="hidden"', () => {
437+
const action = getRegisteredAction({
438+
appid: 'testapp', action_handler: 'handler', name: 'test',
439+
display_name: 'Test', mime: 'image/jpeg', order: 0,
440+
default_action: 'hidden',
441+
})
442+
expect(action.default).toBe('hidden')
443+
})
444+
419445
it('enabled returns false for empty nodes', () => {
420446
const action = getRegisteredAction({
421447
appid: 'testapp',

tests/php/Controller/OCSUiControllerTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use OCA\AppAPI\Service\UI\StylesService;
1717
use OCA\AppAPI\Service\UI\TopMenuService;
1818
use OCP\AppFramework\Http;
19+
use OCP\AppFramework\OCS\OCSBadRequestException;
1920
use OCP\AppFramework\OCS\OCSNotFoundException;
2021
use OCP\IRequest;
2122
use OCP\Server;
@@ -193,6 +194,38 @@ public function testFileActionsV2HasV2Version(): void {
193194
self::assertSame('image', $data['mime']);
194195
self::assertSame('31', (string)$data['permissions']);
195196
self::assertSame('2.0', $data['version']);
197+
self::assertNull($data['default_action']);
198+
}
199+
200+
public function testFileActionsV2DefaultActionRoundTrip(): void {
201+
$this->controller->registerFileActionMenuV2(
202+
name: 'ui_action_v2', displayName: 'V2 Action', actionHandler: '/handler2',
203+
icon: '', mime: 'image', permissions: 31, order: 0, defaultAction: 'default',
204+
);
205+
$data = self::asArray($this->controller->getFileActionMenu('ui_action_v2')->getData());
206+
self::assertSame('default', $data['default_action']);
207+
208+
$this->controller->registerFileActionMenuV2(
209+
name: 'ui_action_v2', displayName: 'V2 Action', actionHandler: '/handler2',
210+
icon: '', mime: 'image', permissions: 31, order: 0, defaultAction: 'hidden',
211+
);
212+
$data = self::asArray($this->controller->getFileActionMenu('ui_action_v2')->getData());
213+
self::assertSame('hidden', $data['default_action']);
214+
215+
$this->controller->registerFileActionMenuV2(
216+
name: 'ui_action_v2', displayName: 'V2 Action', actionHandler: '/handler2',
217+
icon: '', mime: 'image', permissions: 31, order: 0, defaultAction: '',
218+
);
219+
$data = self::asArray($this->controller->getFileActionMenu('ui_action_v2')->getData());
220+
self::assertNull($data['default_action']);
221+
}
222+
223+
public function testFileActionsV2RejectsUnknownDefaultAction(): void {
224+
$this->expectException(OCSBadRequestException::class);
225+
$this->controller->registerFileActionMenuV2(
226+
name: 'ui_action_v2', displayName: 'V2 Action', actionHandler: '/handler2',
227+
icon: '', mime: 'image', permissions: 31, order: 0, defaultAction: 'bogus',
228+
);
196229
}
197230

198231
public function testInitialStateRoundTrip(): void {

0 commit comments

Comments
 (0)