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
6 changes: 6 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@
['name' => 'board_ocs#createForTeam', 'url' => '/api/v{apiVersion}/boards/team', 'verb' => 'POST'],
['name' => 'board_ocs#addAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl', 'verb' => 'POST'],

['name' => 'board_view_api#all', 'url' => '/api/v{apiVersion}/views', 'verb' => 'GET'],
['name' => 'board_view_api#index', 'url' => '/api/v{apiVersion}/boards/{boardId}/views', 'verb' => 'GET'],
['name' => 'board_view_api#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/views', 'verb' => 'POST'],
['name' => 'board_view_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/views/{viewId}', 'verb' => 'PUT'],
['name' => 'board_view_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/views/{viewId}', 'verb' => 'DELETE'],

['name' => 'card_ocs#create', 'url' => '/api/v{apiVersion}/cards', 'verb' => 'POST'],
['name' => 'card_ocs#update', 'url' => '/api/v{apiVersion}/cards/{cardId}', 'verb' => 'PUT'],
['name' => 'card_ocs#assignLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'POST'],
Expand Down
53 changes: 53 additions & 0 deletions lib/Controller/BoardViewApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

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

namespace OCA\Deck\Controller;

use OCA\Deck\Service\BoardViewService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

class BoardViewApiController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
private BoardViewService $boardViewService,
private $userId,
) {
parent::__construct($appName, $request);
}

#[NoAdminRequired]
public function index(int $boardId): DataResponse {
return new DataResponse($this->boardViewService->findAll($boardId));
}

#[NoAdminRequired]
public function all(): DataResponse {
return new DataResponse($this->boardViewService->findAllForUser());
}

#[NoAdminRequired]
public function create(int $boardId, string $name, array $filters): DataResponse {
return new DataResponse($this->boardViewService->create($boardId, $name, $filters));
}

#[NoAdminRequired]
public function update(int $boardId, int $viewId, string $name, array $filters): DataResponse {
return new DataResponse($this->boardViewService->update($boardId, $viewId, $name, $filters));
}

#[NoAdminRequired]
public function delete(int $boardId, int $viewId): DataResponse {
$this->boardViewService->delete($boardId, $viewId);
return new DataResponse([]);
}
}
34 changes: 34 additions & 0 deletions lib/Db/BoardView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

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

namespace OCA\Deck\Db;

class BoardView extends RelationalEntity implements \JsonSerializable {
protected $boardId;
protected $name;
protected $filters;
protected $owner;
protected $createdAt;
protected $lastModifiedAt;

public function __construct() {
$this->addType('id', 'integer');
$this->addType('boardId', 'integer');
$this->addType('createdAt', 'integer');
$this->addType('lastModifiedAt', 'integer');
}

public function jsonSerialize(): array {
$json = parent::jsonSerialize();
if (isset($json['filters']) && is_string($json['filters'])) {
$json['filters'] = json_decode($json['filters'], true) ?? [];
}
return $json;
}
}
55 changes: 55 additions & 0 deletions lib/Db/BoardViewMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

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

namespace OCA\Deck\Db;

use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;

/** @template-extends QBMapper<BoardView> */
class BoardViewMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'deck_board_views', BoardView::class);
}

/**
* @return BoardView[]
*/
public function findAll(int $boardId, string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId)))
->orderBy('name', 'ASC');
return $this->findEntities($qb);
}

/**
* @return BoardView[]
*/
public function findAllForUser(string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId)))
->orderBy('name', 'ASC');
return $this->findEntities($qb);
}

public function find(int $id, string $userId): BoardView {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId)));
return $this->findEntity($qb);
}
}
58 changes: 58 additions & 0 deletions lib/Migration/Version11003Date20260821120000.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

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

namespace OCA\Deck\Migration;

use Closure;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version11003Date20260821120000 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
$schema = $schemaClosure();

if (!$schema->hasTable('deck_board_views')) {
$table = $schema->createTable('deck_board_views');
$table->addColumn('id', 'integer', [
'autoincrement' => true,
'notnull' => true,
'length' => 4,
]);
$table->addColumn('board_id', 'integer', [
'notnull' => true,
'length' => 4,
'default' => 0,
]);
$table->addColumn('name', 'string', [
'notnull' => true,
'length' => 255,
]);
$table->addColumn('filters', 'text', [
'notnull' => true,
]);
$table->addColumn('owner', 'string', [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('created_at', 'integer', [
'notnull' => true,
'length' => 4,
'default' => 0,
]);
$table->addColumn('last_modified_at', 'integer', [
'notnull' => true,
'length' => 4,
'default' => 0,
]);
$table->setPrimaryKey(['id']);
$table->addIndex(['board_id', 'owner'], 'deck_board_views_idx_board_owner');
}
return $schema;
}
}
82 changes: 82 additions & 0 deletions lib/Service/BoardViewService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

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

namespace OCA\Deck\Service;

use OCA\Deck\BadRequestException;
use OCA\Deck\Db\BoardView;
use OCA\Deck\Db\BoardViewMapper;
use OCP\AppFramework\Db\DoesNotExistException;

class BoardViewService {
public function __construct(
private BoardViewMapper $boardViewMapper,
private BoardService $boardService,
private ?string $userId,
) {
}

/**
* @return BoardView[]
*/
public function findAll(int $boardId): array {
$this->boardService->find($boardId);
return $this->boardViewMapper->findAll($boardId, $this->userId ?? '');
}

/**
* @return BoardView[]
*/
public function findAllForUser(): array {
return $this->boardViewMapper->findAllForUser($this->userId ?? '');
}

public function create(int $boardId, string $name, array $filters): BoardView {
$this->boardService->find($boardId);
if ($name === '') {
throw new BadRequestException('The name must not be empty');
}

$view = new BoardView();
$view->setBoardId($boardId);
$view->setName($name);
$view->setFilters(json_encode($filters));
$view->setOwner($this->userId ?? '');
$view->setCreatedAt(time());
$view->setLastModifiedAt(time());
return $this->boardViewMapper->insert($view);
}

public function update(int $boardId, int $viewId, string $name, array $filters): BoardView {
$this->boardService->find($boardId);
$view = $this->boardViewMapper->find($viewId, $this->userId ?? '');
if ($view->getBoardId() !== $boardId) {
throw new BadRequestException('The view does not belong to the given board');
}

if ($name !== '') {
$view->setName($name);
}
$view->setFilters(json_encode($filters));
$view->setLastModifiedAt(time());
return $this->boardViewMapper->update($view);
}

/**
* @throws DoesNotExistException
*/
public function delete(int $boardId, int $viewId): void {
$this->boardService->find($boardId);
$view = $this->boardViewMapper->find($viewId, $this->userId ?? '');
if ($view->getBoardId() !== $boardId) {
throw new BadRequestException('The view does not belong to the given board');
}
$this->boardViewMapper->delete($view);
}
}
20 changes: 18 additions & 2 deletions lib/Service/ConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public function getAll(): array {
$data = [
'calendar' => $this->isCalendarEnabled(),
'cardDetailsInModal' => $this->isCardDetailsInModal(),
'cardIdBadge' => $this->isCardIdBadgeEnabled()
'cardIdBadge' => $this->isCardIdBadgeEnabled(),
'defaultBoardView' => $this->getDefaultBoardView(),
];
if ($this->groupManager->isAdmin($userId)) {
$data['groupLimit'] = $this->get('groupLimit');
Expand All @@ -62,7 +63,7 @@ public function getAll(): array {
}

/**
* @return bool|array{id: string, displayname: string}[]
* @return bool|int|null|array{id: string, displayname: string}[]
* @throws NoPermissionException
*/
public function get(string $key) {
Expand Down Expand Up @@ -90,10 +91,21 @@ public function get(string $key) {
return false;
}
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', false);
case 'defaultBoardView':
return $this->getDefaultBoardView();
}
return false;
}

public function getDefaultBoardView(): ?int {
$userId = $this->getUserId();
if ($userId === null) {
return null;
}
$value = $this->config->getUserValue($userId, Application::APP_ID, 'defaultBoardView', '');
return $value === '' ? null : (int)$value;
}

public function isCalendarEnabled(?int $boardId = null): bool {
$userId = $this->getUserId();
if ($userId === null) {
Expand Down Expand Up @@ -181,6 +193,10 @@ public function set($key, $value) {
$this->config->setUserValue($userId, Application::APP_ID, 'cardIdBadge', (string)$value);
$result = $value;
break;
case 'defaultBoardView':
$this->config->setUserValue($userId, Application::APP_ID, 'defaultBoardView', $value === null ? '' : (string)(int)$value);
$result = $value === null ? '' : $value;
break;
case 'board':
// extra check that user only send one of the allowed board settings and not something random
$parts = explode(':', $key, 3);
Expand Down
Loading