Skip to content

Commit 2d9d7fa

Browse files
committed
feat(Sharing): Add user status for shares
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent d765497 commit 2d9d7fa

29 files changed

Lines changed: 1014 additions & 39 deletions

apps/sharing/appinfo/info.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<name>Sharing</name>
1010
<summary>TODO</summary>
1111
<description>TODO</description>
12-
<version>2.0.0-dev.0</version>
12+
<version>2.0.0-dev.1</version>
1313
<licence>AGPL-3.0-or-later</licence>
1414
<author>Kate Döen</author>
1515
<namespace>Sharing</namespace>
@@ -32,5 +32,6 @@
3232
<command>\OCA\Sharing\Command\UpdateShareProperty</command>
3333
<command>\OCA\Sharing\Command\UpdateShareRecipientSecret</command>
3434
<command>\OCA\Sharing\Command\UpdateShareState</command>
35+
<command>\OCA\Sharing\Command\UpdateShareUserStatus</command>
3536
</commands>
3637
</info>

apps/sharing/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
'OCA\\Sharing\\Command\\UpdateShareProperty' => $baseDir . '/../lib/Command/UpdateShareProperty.php',
2424
'OCA\\Sharing\\Command\\UpdateShareRecipientSecret' => $baseDir . '/../lib/Command/UpdateShareRecipientSecret.php',
2525
'OCA\\Sharing\\Command\\UpdateShareState' => $baseDir . '/../lib/Command/UpdateShareState.php',
26+
'OCA\\Sharing\\Command\\UpdateShareUserStatus' => $baseDir . '/../lib/Command/UpdateShareUserStatus.php',
2627
'OCA\\Sharing\\Controller\\ApiV1Controller' => $baseDir . '/../lib/Controller/ApiV1Controller.php',
2728
'OCA\\Sharing\\Middleware\\ShareApiEnabledMiddleware' => $baseDir . '/../lib/Middleware/ShareApiEnabledMiddleware.php',
2829
'OCA\\Sharing\\Migration\\Version1000Date20250929161325' => $baseDir . '/../lib/Migration/Version1000Date20250929161325.php',
2930
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => $baseDir . '/../lib/Migration/Version1000Date20260731171922.php',
31+
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => $baseDir . '/../lib/Migration/Version1000Date20260826073021.php',
3032
'OCA\\Sharing\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
3133
);

apps/sharing/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ class ComposerStaticInitSharing
3838
'OCA\\Sharing\\Command\\UpdateShareProperty' => __DIR__ . '/..' . '/../lib/Command/UpdateShareProperty.php',
3939
'OCA\\Sharing\\Command\\UpdateShareRecipientSecret' => __DIR__ . '/..' . '/../lib/Command/UpdateShareRecipientSecret.php',
4040
'OCA\\Sharing\\Command\\UpdateShareState' => __DIR__ . '/..' . '/../lib/Command/UpdateShareState.php',
41+
'OCA\\Sharing\\Command\\UpdateShareUserStatus' => __DIR__ . '/..' . '/../lib/Command/UpdateShareUserStatus.php',
4142
'OCA\\Sharing\\Controller\\ApiV1Controller' => __DIR__ . '/..' . '/../lib/Controller/ApiV1Controller.php',
4243
'OCA\\Sharing\\Middleware\\ShareApiEnabledMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/ShareApiEnabledMiddleware.php',
4344
'OCA\\Sharing\\Migration\\Version1000Date20250929161325' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20250929161325.php',
4445
'OCA\\Sharing\\Migration\\Version1000Date20260731171922' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260731171922.php',
46+
'OCA\\Sharing\\Migration\\Version1000Date20260826073021' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20260826073021.php',
4547
'OCA\\Sharing\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
4648
);
4749

apps/sharing/lib/Command/GetShares.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Exception;
1313
use NCU\Sharing\Share;
1414
use NCU\Sharing\ShareState;
15+
use NCU\Sharing\ShareUserStatus;
1516
use NCU\Sharing\Source\IShareSourceType;
1617
use OC\Core\Command\Base;
1718
use Symfony\Component\Console\Input\InputInterface;
@@ -29,6 +30,7 @@ public function configure(): void {
2930
->addOption('filter-source-type-class', '', InputOption::VALUE_REQUIRED, 'Source type class to filter by')
3031
->addOption('filter-source-type-value', '', InputOption::VALUE_REQUIRED, 'Source type value to filter by')
3132
->addOption('filter-state', '', InputOption::VALUE_REQUIRED, 'State to filter by. Possible values: ' . implode(', ', array_map(static fn (UnitEnum $case) => $case->value, ShareState::cases())))
33+
->addOption('filter-user-status', '', InputOption::VALUE_REQUIRED, 'User status to filter by. Possible values: ' . implode(', ', array_map(static fn (UnitEnum $case) => $case->value, ShareUserStatus::cases())))
3234
->addOption('last-share-id', '', InputOption::VALUE_REQUIRED, 'Share ID to use as an offset')
3335
->addOption('limit', '', InputOption::VALUE_REQUIRED, 'Maximum number of shares to return');
3436
parent::configure();
@@ -53,6 +55,17 @@ public function execute(InputInterface $input, OutputInterface $output): int {
5355
}
5456
}
5557

58+
/** @var ?string $filterUserStatus */
59+
$filterUserStatus = $input->getOption('filter-user-status');
60+
if ($filterUserStatus !== null) {
61+
try {
62+
$filterUserStatus = ShareUserStatus::from($filterUserStatus);
63+
} catch (ValueError $valueError) {
64+
$output->writeln($valueError->getMessage());
65+
return Base::FAILURE;
66+
}
67+
}
68+
5669
/** @var ?string $lastShareID */
5770
$lastShareID = $input->getOption('last-share-id');
5871
/** @var ?string $limit */
@@ -68,7 +81,7 @@ public function execute(InputInterface $input, OutputInterface $output): int {
6881
try {
6982
$this->dbConnection->beginTransaction();
7083

71-
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $lastShareID, $limit);
84+
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
7285
$this->dbConnection->commit();
7386

7487
$data = Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $shares);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OCA\Sharing\Command;
11+
12+
use NCU\Sharing\Share;
13+
use NCU\Sharing\ShareUserStatus;
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
18+
final class UpdateShareUserStatus extends SharingBase {
19+
#[\Override]
20+
public function configure(): void {
21+
$this
22+
->setName('sharing:update-share-user-status')
23+
->setDescription('Update the user status for a share.')
24+
->addArgument('id', InputArgument::REQUIRED, 'Share ID')
25+
->addArgument('user-status', InputArgument::REQUIRED, 'User status');
26+
parent::configure();
27+
}
28+
29+
#[\Override]
30+
public function execute(InputInterface $input, OutputInterface $output): int {
31+
/** @var string $id */
32+
$id = $input->getArgument('id');
33+
/** @var string $userStatus */
34+
$userStatus = $input->getArgument('user-status');
35+
$userStatus = ShareUserStatus::from($userStatus);
36+
37+
return $this->wrapExecution($input, $output, function () use ($id, $userStatus): Share {
38+
$share = $this->manager->getShare($this->accessContext, $id);
39+
return $this->manager->updateShareUserStatus($this->accessContext, $share, $userStatus);
40+
});
41+
}
42+
}

apps/sharing/lib/Controller/ApiV1Controller.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use NCU\Sharing\Share;
2727
use NCU\Sharing\ShareAccessContext;
2828
use NCU\Sharing\ShareState;
29+
use NCU\Sharing\ShareUserStatus;
2930
use NCU\Sharing\Source\IShareSourceType;
3031
use NCU\Sharing\Source\ShareSource;
3132
use OCA\Sharing\ResponseDefinitions;
@@ -51,6 +52,7 @@
5152
* @psalm-import-type SharingShare from ResponseDefinitions
5253
* @psalm-import-type SharingRecipient from ResponseDefinitions
5354
* @psalm-import-type SharingState from ResponseDefinitions
55+
* @psalm-import-type SharingUserStatus from ResponseDefinitions
5456
* @psalm-import-type SharingPermissionPreset from ResponseDefinitions
5557
*/
5658
final class ApiV1Controller extends OCSController {
@@ -200,6 +202,43 @@ public function updateShareState(string $id, string $state): DataResponse {
200202
}
201203
}
202204

205+
/**
206+
* Update the user status for a share.
207+
*
208+
* @param string $id ID of the share
209+
* @param SharingUserStatus $userStatus New user status for the share
210+
* @return DataResponse<Http::STATUS_OK, SharingShare, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, string, array{}>
211+
*
212+
* 200: User status for share updated successfully
213+
* 400: Invalid share user status
214+
* 404: Share not found
215+
*/
216+
#[NoAdminRequired]
217+
#[ApiRoute(verb: 'PUT', url: '/api/v1/share/{id}/user-status')]
218+
public function updateShareUserStatus(string $id, string $userStatus): DataResponse {
219+
try {
220+
$shareUserStatus = ShareUserStatus::from($userStatus);
221+
} catch (ValueError $valueError) {
222+
return new DataResponse($valueError->getMessage(), Http::STATUS_BAD_REQUEST);
223+
}
224+
225+
try {
226+
try {
227+
$this->dbConnection->beginTransaction();
228+
229+
$share = $this->manager->getShare($this->accessContext, $id);
230+
$share = $this->manager->updateShareUserStatus($this->accessContext, $share, $shareUserStatus);
231+
$this->dbConnection->commit();
232+
return new DataResponse($share->format($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager));
233+
} catch (Exception $exception) {
234+
$this->dbConnection->rollBack();
235+
throw $exception;
236+
}
237+
} catch (ShareNotFoundException $shareNotFoundException) {
238+
return new DataResponse($shareNotFoundException->getHint(), Http::STATUS_NOT_FOUND);
239+
}
240+
}
241+
203242
/**
204243
* Add a new source to a share.
205244
*
@@ -560,6 +599,7 @@ public function getShare(string $id, ?string $secret = null, array $arguments =
560599
* @param ?class-string<IShareSourceType> $filterSourceTypeClass Source type class to filter by.
561600
* @param ?non-empty-string $filterSourceTypeValue Source type value to filter by.
562601
* @param ?SharingState $filterState State to filter by.
602+
* @param ?SharingUserStatus $filterUserStatus User status to filter by.
563603
* @param ?string $lastShareID The ID of the previous share. This is used as an offset and only shares with higher IDs are returned.
564604
* @param int<1, 100> $limit The number of shares to return.
565605
* @return DataResponse<Http::STATUS_OK, list<SharingShare>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, string, array{}>
@@ -569,7 +609,7 @@ public function getShare(string $id, ?string $secret = null, array $arguments =
569609
*/
570610
#[NoAdminRequired]
571611
#[ApiRoute(verb: 'GET', url: '/api/v1/shares')]
572-
public function getShares(?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $filterState, ?string $lastShareID, int $limit = 100): DataResponse {
612+
public function getShares(?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $filterState, ?string $filterUserStatus, ?string $lastShareID, int $limit = 100): DataResponse {
573613
/** @psalm-suppress DocblockTypeContradiction */
574614
if ($limit < 1) {
575615
return new DataResponse('The limit is too low.', Http::STATUS_BAD_REQUEST);
@@ -597,10 +637,18 @@ public function getShares(?string $filterSourceTypeClass, ?string $filterSourceT
597637
}
598638
}
599639

640+
if ($filterUserStatus !== null) {
641+
try {
642+
$filterUserStatus = ShareUserStatus::from($filterUserStatus);
643+
} catch (ValueError $valueError) {
644+
return new DataResponse($valueError->getMessage(), Http::STATUS_BAD_REQUEST);
645+
}
646+
}
647+
600648
try {
601649
$this->dbConnection->beginTransaction();
602650

603-
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $lastShareID, $limit);
651+
$shares = $this->manager->getShares($this->accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
604652
$this->dbConnection->commit();
605653
return new DataResponse(Share::formatMultiple($this->registry, $this->l10nFactory, $this->urlGenerator, $this->userManager, $shares));
606654
} catch (Exception $exception) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Sharing\Migration;
11+
12+
use Closure;
13+
use OCP\DB\ISchemaWrapper;
14+
use OCP\DB\Types;
15+
use OCP\Migration\IOutput;
16+
use OCP\Migration\SimpleMigrationStep;
17+
use Override;
18+
19+
final class Version1000Date20260826073021 extends SimpleMigrationStep {
20+
#[Override]
21+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
22+
$schema = $schemaClosure();
23+
24+
if (!$schema->hasTable('sharing_share_user_status')) {
25+
$shareTable = $schema->getTable('sharing_share');
26+
27+
$userStatusTable = $schema->createTable('sharing_share_user_status');
28+
$userStatusTable->addColumn('share_id', Types::BIGINT);
29+
$userStatusTable->addColumn('user_id', Types::STRING, ['length' => 64]);
30+
$userStatusTable->addColumn('status', Types::STRING, ['length' => 16]);
31+
$userStatusTable->setPrimaryKey(['share_id', 'user_id']);
32+
$userStatusTable->addForeignKeyConstraint($shareTable->getName(), ['share_id'], ['id'], ['onDelete' => 'CASCADE']);
33+
}
34+
35+
return $schema;
36+
}
37+
}

apps/sharing/lib/ResponseDefinitions.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
*
6363
* @psalm-type SharingState = 'active'|'draft'|'deleted'
6464
*
65+
* @psalm-type SharingUserStatus = 'pending'|'accepted'|'rejected'
66+
*
6567
* @psalm-type SharingProperty = array{
6668
* class: class-string<ISharePropertyType>,
6769
* display_name: non-empty-string,
@@ -125,6 +127,7 @@
125127
* // Unix time in milliseconds
126128
* last_updated: numeric-string,
127129
* state: SharingState,
130+
* user_status: ?SharingUserStatus,
128131
* sources: list<SharingSource>,
129132
* recipients: list<SharingRecipient>,
130133
* properties: list<SharingPropertyDate|SharingPropertyEnum|SharingPropertyBoolean|SharingPropertyPassword|SharingPropertyString>,

0 commit comments

Comments
 (0)