Skip to content
Open
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
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Share your tables and views with users and groups within your cloud.
Have a good time and manage whatever you want.

]]></description>
<version>2.0.0-alpha.1</version>
<version>2.0.0-alpha.2</version>
<licence>agpl</licence>
<author mail="[email protected]">Florian Steffens</author>
<namespace>Tables</namespace>
Expand Down
2 changes: 2 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCA\Tables\Listener\WhenTableTransferredAuditLogListener;
use OCA\Tables\Listener\WhenViewDeletedAuditLogListener;
use OCA\Tables\Middleware\PermissionMiddleware;
use OCA\Tables\Middleware\ShareControlMiddleware;
use OCA\Tables\Reference\ContentReferenceProvider;
use OCA\Tables\Reference\ReferenceProvider;
use OCA\Tables\Search\SearchTablesProvider;
Expand Down Expand Up @@ -92,6 +93,7 @@ public function register(IRegistrationContext $context): void {
$context->registerCapability(Capabilities::class);

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

public function boot(IBootContext $context): void {
Expand Down
1 change: 1 addition & 0 deletions lib/Constants/ShareReceiverType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ class ShareReceiverType {
public const USER = 'user';
public const GROUP = 'group';
public const CIRCLE = 'circle';
public const LINK = 'link';
}
35 changes: 35 additions & 0 deletions lib/Controller/ACommonColumnsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Controller;

use OCA\Tables\Errors\BadRequestError;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Service\ColumnService;

abstract class ACommonColumnsController extends AOCSController {
protected ColumnService $service;

/**
* @throws PermissionError
* @throws NotFoundError
* @throws InternalError
* @throws BadRequestError
*/
protected function getColumnsFromTableOrView(string $nodeType, int $nodeId, ?string $overriddenUserid = null): array {
if ($nodeType === 'table') {
return $this->service->findAllByTable($nodeId, $overriddenUserid);
}
if ($nodeType === 'view') {
return $this->service->findAllByView($nodeId, $overriddenUserid);
}
throw new BadRequestError('Invalid node type provided');
}
}
98 changes: 65 additions & 33 deletions lib/Controller/ApiColumnsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
namespace OCA\Tables\Controller;

use OCA\Tables\AppInfo\Application;
use OCA\Tables\Db\Column;
use OCA\Tables\Dto\Column as ColumnDto;
use OCA\Tables\Errors\BadRequestError;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Middleware\Attribute\RequirePermission;
use OCA\Tables\ResponseDefinitions;
use OCA\Tables\Service\ColumnService;
use OCA\Tables\Service\ShareService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
Expand All @@ -24,15 +27,16 @@
/**
* @psalm-import-type TablesColumn from ResponseDefinitions
*/
class ApiColumnsController extends AOCSController {
private ColumnService $service;
class ApiColumnsController extends ACommonColumnsController {

public function __construct(
IRequest $request,
LoggerInterface $logger,
ColumnService $service,
IL10N $n,
string $userId) {
string $userId,
protected ShareService $shareService,
) {
parent::__construct($request, $logger, $n, $userId);
$this->service = $service;
}
Expand All @@ -44,38 +48,39 @@ public function __construct(
*
* @param int $nodeId Node ID
* @param 'table'|'view' $nodeType Node type
* @return DataResponse<Http::STATUS_OK, list<TablesColumn>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, list<TablesColumn>,
* array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: View deleted
* 400: Invalid input arguments
* 403: No permissions
* 404: Not found
*/
#[NoAdminRequired]
#[RequirePermission(permission: Application::PERMISSION_READ)]
public function index(int $nodeId, string $nodeType): DataResponse {
try {
if ($nodeType === 'table') {
$columns = $this->service->findAllByTable($nodeId);
} elseif ($nodeType === 'view') {
$columns = $this->service->findAllByView($nodeId);
} else {
$columns = null;
}
$columns = $this->getColumnsFromTableOrView($nodeType, $nodeId);
return new DataResponse($this->service->formatColumns($columns));
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError $e) {
return $this->handleError($e);
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (BadRequestError $e) {
return $this->handleBadRequestError($e);
}
}

/**
* [api v2] Get a column object
*
* @param int $id Column ID
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column returned
* 403: No permissions
Expand Down Expand Up @@ -111,10 +116,14 @@ public function show(int $id): DataResponse {
* @param float|null $numberMax Max
* @param 'progress'|'stars'|null $subtype Subtype for the new column
* @param string|null $description Description
* @param list<int>|null $selectedViewIds View IDs where this columns should be added
* @param array<string, mixed> $customSettings Custom settings for the column
* @param list<int>|null $selectedViewIds View IDs where this columns
* should be added
* @param array<string, mixed> $customSettings Custom settings for the
* column
*
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column created
* 403: No permission
Expand Down Expand Up @@ -161,14 +170,19 @@ public function createNumberColumn(int $baseNodeId, string $title, ?float $numbe
* @param string|null $textDefault Default
* @param string|null $textAllowedPattern Allowed regex pattern
* @param int|null $textMaxLength Max raw text length
* @param bool|null $textUnique Whether the text value must be unique, if column is a text
* @param bool|null $textUnique Whether the text value must be unique, if
* column is a text
* @param 'progress'|'stars'|null $subtype Subtype for the new column
* @param string|null $description Description
* @param list<int>|null $selectedViewIds View IDs where this columns should be added
* @param list<int>|null $selectedViewIds View IDs where this columns
* should be added
* @param boolean $mandatory Is mandatory
* @param 'table'|'view' $baseNodeType Context type of the column creation
* @param array<string, mixed> $customSettings Custom settings for the column
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @param array<string, mixed> $customSettings Custom settings for the
* column
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column created
* 403: No permission
Expand Down Expand Up @@ -210,16 +224,23 @@ public function createTextColumn(int $baseNodeId, string $title, ?string $textDe
*
* @param int $baseNodeId Context of the column creation
* @param string $title Title
* @param string $selectionOptions Json array{id: int, label: string} with options that can be selected, eg [{"id": 1, "label": "first"},{"id": 2, "label": "second"}]
* @param string|null $selectionDefault Json int|list<int> for default selected option(s), eg 5 or ["1", "8"]
* @param string $selectionOptions Json array{id: int, label: string} with
* options that can be selected, eg [{"id": 1, "label": "first"},{"id":
* 2, "label": "second"}]
* @param string|null $selectionDefault Json int|list<int> for default
* selected option(s), eg 5 or ["1", "8"]
* @param 'progress'|'stars'|null $subtype Subtype for the new column
* @param string|null $description Description
* @param list<int>|null $selectedViewIds View IDs where this columns should be added
* @param list<int>|null $selectedViewIds View IDs where this columns
* should be added
* @param boolean $mandatory Is mandatory
* @param 'table'|'view' $baseNodeType Context type of the column creation
* @param array<string, mixed> $customSettings Custom settings for the column
* @param array<string, mixed> $customSettings Custom settings for the
* column
*
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column created
* 403: No permission
Expand Down Expand Up @@ -259,15 +280,20 @@ public function createSelectionColumn(int $baseNodeId, string $title, string $se
*
* @param int $baseNodeId Context of the column creation
* @param string $title Title
* @param 'today'|'now'|null $datetimeDefault For a subtype 'date' you can set 'today'. For a main type or subtype 'time' you can set to 'now'.
* @param 'today'|'now'|null $datetimeDefault For a subtype 'date' you can
* set 'today'. For a main type or subtype 'time' you can set to 'now'.
* @param 'progress'|'stars'|null $subtype Subtype for the new column
* @param string|null $description Description
* @param list<int>|null $selectedViewIds View IDs where this columns should be added
* @param list<int>|null $selectedViewIds View IDs where this columns
* should be added
* @param boolean $mandatory Is mandatory
* @param 'table'|'view' $baseNodeType Context type of the column creation
* @param array<string, mixed> $customSettings Custom settings for the column
* @param array<string, mixed> $customSettings Custom settings for the
* column
*
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column created
* 403: No permission
Expand Down Expand Up @@ -304,19 +330,25 @@ public function createDatetimeColumn(int $baseNodeId, string $title, ?string $da
*
* @param int $baseNodeId Context of the column creation
* @param string $title Title
* @param string|null $usergroupDefault Json array{id: string, type: int}, eg [{"id": "admin", "type": 0}, {"id": "user1", "type": 0}]
* @param boolean $usergroupMultipleItems Whether you can select multiple users or/and groups
* @param string|null $usergroupDefault Json array{id: string, type: int},
* eg [{"id": "admin", "type": 0}, {"id": "user1", "type": 0}]
* @param boolean $usergroupMultipleItems Whether you can select multiple
* users or/and groups
* @param boolean $usergroupSelectUsers Whether you can select users
* @param boolean $usergroupSelectGroups Whether you can select groups
* @param boolean $usergroupSelectTeams Whether you can select teams
* @param boolean $showUserStatus Whether to show the user's status
* @param string|null $description Description
* @param list<int>|null $selectedViewIds View IDs where this columns should be added
* @param list<int>|null $selectedViewIds View IDs where this columns
* should be added
* @param boolean $mandatory Is mandatory
* @param 'table'|'view' $baseNodeType Context type of the column creation
* @param array<string, mixed> $customSettings Custom settings for the column
* @param array<string, mixed> $customSettings Custom settings for the
* column
*
* @return DataResponse<Http::STATUS_OK, TablesColumn, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TablesColumn,
* array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND,
* array{message: string}, array{}>
*
* 200: Column created
* 403: No permission
Expand Down
86 changes: 86 additions & 0 deletions lib/Controller/ApiPublicColumnsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Controller;

use InvalidArgumentException;
use OCA\Tables\Errors\BadRequestError;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\ResponseDefinitions;
use OCA\Tables\Service\ColumnService;
use OCA\Tables\Service\ShareService;
use OCA\Tables\Service\ValueObject\ShareToken;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

/**
* @psalm-import-type TablesPublicColumn from ResponseDefinitions
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use #[AssertShareToken] here too?

class ApiPublicColumnsController extends ACommonColumnsController {

public function __construct(
protected ColumnService $service,
protected ShareService $shareService,
IRequest $request,
LoggerInterface $logger,
IL10N $l,
) {
parent::__construct($request, $logger, $l, '');
}

/**
* [api v2] Get all columns for a table or a view shared by link
*
* Return an empty array if no columns were found
*
* @param string $token The share token
* @return DataResponse<Http::STATUS_OK, list<TablesPublicColumn>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 200: Columns are returned
* 400: Invalid request parameters
* 403: No permissions
* 404: Not found
* 500: Internal error
*/
#[PublicPage]
#[ApiRoute(verb: 'GET', url: '/api/2/public/{token}/columns')]
#[OpenAPI]
public function indexByPublicLink(string $token): DataResponse {
try {
$shareToken = new ShareToken($token);
} catch (InvalidArgumentException $e) {
return $this->handleBadRequestError(new BadRequestError(
'Invalid share token', $e->getCode(), $e
));
}

try {
$share = $this->shareService->findByToken($shareToken);
$columns = $this->getColumnsFromTableOrView($share->getNodeType(), $share->getNodeId(), '');

$formattedTableColumns = $this->service->formatColumnsForPublicShare($columns);
return new DataResponse($formattedTableColumns);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError $e) {
return $this->handleError($e);
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (BadRequestError $e) {
return $this->handleBadRequestError($e);
}
}
}
Loading
Loading