Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@
['name' => 'Context#transfer', 'url' => '/api/2/contexts/{contextId}/transfer', 'verb' => 'PUT'],
['name' => 'Context#updateContentOrder', 'url' => '/api/2/contexts/{contextId}/pages/{pageId}', 'verb' => 'PUT'],

['name' => 'FederationColumnOCS#index', 'url' => '/api/2/federation/tables/{tableId}/columns', 'verb' => 'GET'],

['name' => 'FederationRowOCS#index', 'url' => '/api/2/federation/tables/{tableId}/rows', 'verb' => 'GET'],
['name' => 'FederationRowOCS#create', 'url' => '/api/2/federation/tables/{tableId}/rows', 'verb' => 'POST'],
['name' => 'FederationRowOCS#update', 'url' => '/api/2/federation/tables/{tableId}/rows/{rowId}', 'verb' => 'PUT'],
['name' => 'FederationRowOCS#delete', 'url' => '/api/2/federation/tables/{tableId}/rows/{rowId}', 'verb' => 'DELETE'],

['name' => 'RowOCS#createRow', 'url' => '/api/2/{nodeCollection}/{nodeId}/rows', 'verb' => 'POST', 'requirements' => ['nodeCollection' => '(tables|views)', 'nodeId' => '(\d+)']],
['name' => 'RowOCS#updateRow', 'url' => '/api/2/{nodeCollection}/{nodeId}/rows/{rowId}', 'verb' => 'PUT', 'requirements' => ['nodeCollection' => '(tables|views)', 'nodeId' => '(\d+)']],
['name' => 'RowOCS#deleteRow', 'url' => '/api/2/{nodeCollection}/{nodeId}/rows/{rowId}', 'verb' => 'DELETE', 'requirements' => ['nodeCollection' => '(tables|views)', 'nodeId' => '(\d+)']],
]
];
18 changes: 18 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@
use OCA\Tables\Event\TableDeletedEvent;
use OCA\Tables\Event\TableOwnershipTransferredEvent;
use OCA\Tables\Event\ViewDeletedEvent;
use OCA\Tables\Federation\FederationProvider;
use OCA\Tables\Listener\AddMissingIndicesListener;
use OCA\Tables\Listener\AnalyticsDatasourceListener;
use OCA\Tables\Listener\BeforeTemplateRenderedListener;
use OCA\Tables\Listener\LoadAdditionalListener;
use OCA\Tables\Listener\ReceiverCleanupListener;
use OCA\Tables\Listener\ResourceTypeRegisterListener;
use OCA\Tables\Listener\TablesReferenceListener;
use OCA\Tables\Listener\UserDeletedListener;
use OCA\Tables\Listener\WhenRowDeletedAuditLogListener;
use OCA\Tables\Listener\WhenTableDeletedAuditLogListener;
use OCA\Tables\Listener\WhenTableTransferredAuditLogListener;
use OCA\Tables\Listener\WhenViewDeletedAuditLogListener;
use OCA\Tables\Middleware\FederationMiddleware;
use OCA\Tables\Middleware\PermissionMiddleware;
use OCA\Tables\Middleware\ShareControlMiddleware;
use OCA\Tables\Reference\ContentReferenceProvider;
Expand All @@ -42,7 +45,11 @@
use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\Federation\ICloudFederationProvider;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\OCM\Events\LocalOCMDiscoveryEvent;
use OCP\Server;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\UserDeletedEvent;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -94,6 +101,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(UserDeletedEvent::class, ReceiverCleanupListener::class);
$context->registerEventListener(GroupDeletedEvent::class, ReceiverCleanupListener::class);
$context->registerEventListener(CircleDestroyedEvent::class, ReceiverCleanupListener::class);
$context->registerEventListener(LocalOCMDiscoveryEvent::class, ResourceTypeRegisterListener::class);

$context->registerSearchProvider(SearchTablesProvider::class);

Expand All @@ -104,10 +112,20 @@ public function register(IRegistrationContext $context): void {

$context->registerMiddleware(PermissionMiddleware::class);
$context->registerMiddleware(ShareControlMiddleware::class);
$context->registerMiddleware(FederationMiddleware::class);

$context->registerUserMigrator(TablesMigrator::class);
}

public function boot(IBootContext $context): void {
$context->injectFn([$this, 'registerCloudFederationProviderManager']);
}

public function registerCloudFederationProviderManager(ICloudFederationProviderManager $manager): void {
$manager->addCloudFederationProvider(
FederationProvider::PROVIDER_ID,
'Tables Federation',
static fn (): ICloudFederationProvider => Server::get(FederationProvider::class),
);
}
}
1 change: 1 addition & 0 deletions lib/Constants/ShareReceiverType.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ class ShareReceiverType {
public const GROUP = 'group';
public const CIRCLE = 'circle';
public const LINK = 'link';
public const REMOTE = 'remote';
}
7 changes: 7 additions & 0 deletions lib/Controller/Api1Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCA\Tables\Model\ViewUpdateInput;
use OCA\Tables\ResponseDefinitions;
use OCA\Tables\Service\ColumnService;
use OCA\Tables\Service\FederationService;
use OCA\Tables\Service\ImportService;
use OCA\Tables\Service\RelationService;
use OCA\Tables\Service\RowService;
Expand Down Expand Up @@ -88,6 +89,7 @@ public function __construct(
LoggerInterface $logger,
IL10N $l10N,
?string $userId,
private FederationService $federationService,
) {
parent::__construct(Application::APP_ID, $request);
$this->tableService = $service;
Expand Down Expand Up @@ -770,6 +772,11 @@ public function updateShareDisplayMode(int $shareId, int $displayMode, string $t
#[CORS]
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
public function indexTableColumns(int $tableId, ?int $viewId): DataResponse {
$table = $this->tableService->find($tableId, true);
if ($table->isFederated()) {
return new DataResponse($this->federationService->getColumns($table));
}

try {
if ($viewId) {
$view = $this->viewService->find($viewId, false, $this->userId);
Expand Down
49 changes: 49 additions & 0 deletions lib/Controller/FederationColumnOCSController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Controller;

use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Federation\FederationShareContext;
use OCA\Tables\Middleware\Attribute\AssertFederationIsAccessible;
use OCA\Tables\Service\ColumnService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

class FederationColumnOCSController extends AOCSController {
public function __construct(
IRequest $request,
LoggerInterface $logger,
IL10N $n,
private readonly FederationShareContext $federationShareContext,
private readonly ColumnService $columnService,
) {
parent::__construct($request, $logger, $n, '');
}

#[PublicPage]
#[NoAdminRequired]
#[NoCSRFRequired]
#[AssertFederationIsAccessible]
public function index(int $tableId): DataResponse {
try {
$share = $this->federationShareContext->getShare();
$columns = $this->columnService->findAllByTable($tableId, $share->getSender());
return new DataResponse($columns);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError|\Exception $e) {
return $this->handleError($e);
}
}
}
121 changes: 121 additions & 0 deletions lib/Controller/FederationRowOCSController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Controller;

use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Federation\FederationShareContext;
use OCA\Tables\Middleware\Attribute\AssertFederationIsAccessible;
use OCA\Tables\Model\RowDataInput;
use OCA\Tables\Service\RowService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

class FederationRowOCSController extends AOCSController {
public function __construct(
IRequest $request,
LoggerInterface $logger,
IL10N $n,
private readonly FederationShareContext $federationShareContext,
private readonly RowService $rowService,
) {
parent::__construct($request, $logger, $n, '');
}

#[PublicPage]
#[NoAdminRequired]
#[NoCSRFRequired]
#[AssertFederationIsAccessible]
public function index(int $tableId, ?int $limit = null, ?int $offset = null): DataResponse {
try {
$share = $this->federationShareContext->getShare();
if (!$share->getPermissionRead()) {
throw new PermissionError('No permission to read rows');
}
$rows = $this->rowService->findAllByTable($tableId, $share->getSender(), $limit, $offset);
return new DataResponse($rows);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError|\Exception $e) {
return $this->handleError($e);
}
}

#[PublicPage]
#[NoAdminRequired]
#[NoCSRFRequired]
#[AssertFederationIsAccessible]
public function create(int $tableId, array $data): DataResponse {
try {
$share = $this->federationShareContext->getShare();
if (!$share->getPermissionCreate()) {
throw new PermissionError('No permission to create rows');
}
$newRowData = new RowDataInput();
foreach ($data as $key => $value) {
$newRowData->add((int)$key, $value);
}
$row = $this->rowService->create($tableId, null, $newRowData, $share->getSender());
return new DataResponse($row->jsonSerialize());
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError|\Exception $e) {
return $this->handleError($e);
}
}

#[PublicPage]
#[NoAdminRequired]
#[NoCSRFRequired]
#[AssertFederationIsAccessible]
public function update(int $tableId, int $rowId, array $data): DataResponse {
try {
$share = $this->federationShareContext->getShare();
if (!$share->getPermissionUpdate()) {
throw new PermissionError('No permission to update rows');
}
$row = $this->rowService->updateSet($rowId, null, $data, $share->getSender(), $tableId);
return new DataResponse($row->jsonSerialize());
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError|\Exception $e) {
return $this->handleError($e);
}
}

#[PublicPage]
#[NoAdminRequired]
#[NoCSRFRequired]
#[AssertFederationIsAccessible]
public function delete(int $tableId, int $rowId): DataResponse {
try {
$share = $this->federationShareContext->getShare();
if (!$share->getPermissionDelete()) {
throw new PermissionError('No permission to delete rows');
}
$row = $this->rowService->delete($rowId, null, $share->getSender(), $tableId);
return new DataResponse($row->jsonSerialize());
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (InternalError|\Exception $e) {
return $this->handleError($e);
}
}
}
8 changes: 8 additions & 0 deletions lib/Controller/RowController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

use OCA\Tables\AppInfo\Application;
use OCA\Tables\Middleware\Attribute\RequirePermission;
use OCA\Tables\Service\FederationService;
use OCA\Tables\Service\RowService;
use OCA\Tables\Service\TableService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
Expand All @@ -24,13 +26,19 @@ public function __construct(
protected LoggerInterface $logger,
private RowService $service,
private ?string $userId,
private TableService $tableService,
private FederationService $federationService,
) {
parent::__construct(Application::APP_ID, $request);
}

#[NoAdminRequired]
#[RequirePermission(permission: Application::PERMISSION_READ, type: Application::NODE_TYPE_TABLE, idParam: 'tableId')]
public function index(int $tableId): DataResponse {
$table = $this->tableService->find($tableId, true);
if ($table->isFederated()) {
return new DataResponse($this->federationService->getRows($table));
}
return $this->handleError(function () use ($tableId) {
return $this->service->findAllByTable($tableId, $this->userId);
});
Expand Down
Loading
Loading