Skip to content
Merged
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
1 change: 1 addition & 0 deletions css/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@include icon-black-white('filter_set', 'deck', 1);
@include icon-black-white('attach', 'deck', 1);
@include icon-black-white('reply', 'deck', 1);
@include icon-black-white('notifications-dark', 'deck', 1);

.icon-toggle-compact-collapsed {
@include icon-color('toggle-view-expand', 'deck', $color-black);
Expand Down
75 changes: 75 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ Returns an array of board items
"deletedAt": 0,
"id": 10,
"lastModified": 1586269585,
"settings": {
"notify-due": "off",
"calendar": true
}
}
]
```
Expand Down Expand Up @@ -952,6 +956,77 @@ For now only `deck_file` is supported as an attachment type.
The following endpoints are available through the Nextcloud OCS endpoint, which is available at `/ocs/v2.php/apps/deck/api/v1.0/`.
This has the benefit that both the web UI as well as external integrations can use the same API.

## Config

Deck stores user and app configuration values globally and per board. The GET endpoint allows to fetch the current global configuration while board settings will be exposed through the board element on the regular API endpoints.

### GET /api/v1.0/config - Fetch app configuration values

#### Response

| Config key | Description | Value |
| --- | --- |
| calendar | Determines if the calendar/tasks integration through the CalDAV backend is enabled for the user (boolean) |
| groupLimit | Determines if creating new boards is limited to certain groups of the instance. The resulting output is an array of group objects with the id and the displayname (Admin only)|

```
{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 200,
"message": "OK"
},
"data": {
"calendar": true,
"groupLimit": [
{
"id": "admin",
"displayname": "admin"
}
]
}
}
}

```

### POST /api/v1.0/config/{id}/{key} - Set a config value


#### Request parameters

| Parameter | Type | Description |
| --------- | ------- | --------------------------------------- |
| id | Integer | The id of the board |
| key | String | The config key to set, prefixed with `board:{boardId}:` for board specific settings |
| value | String | The value that should be stored for the config key |

##### Board configuration

| Key | Value |
| --- | ----- |
| notify-due | `off`, `assigned` or `all` |
| calendar | Boolean |

#### Example request

```
curl -X POST 'https://admin:admin@nextcloud.local/ocs/v2.php/apps/deck/api/v1.0/config/calendar' -H 'Accept: application/json' -H "Content-Type: application/json" -H 'OCS-APIRequest: true' --data-raw '{"value":false}'

{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 200,
"message": "OK"
},
"data": false
}
}

```

## Comments

### GET /cards/{cardId}/comments - List comments
Expand Down
4 changes: 4 additions & 0 deletions img/notifications-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion lib/DAV/CalendarPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ class CalendarPlugin implements ICalendarProvider {

/** @var DeckCalendarBackend */
private $backend;
/** @var ConfigService */
private $configService;
/** @var bool */
private $calendarIntegrationEnabled;

public function __construct(DeckCalendarBackend $backend, ConfigService $configService) {
$this->backend = $backend;
$this->configService = $configService;
$this->calendarIntegrationEnabled = $configService->get('calendar');
}

Expand All @@ -50,9 +53,12 @@ public function fetchAllForCalendarHome(string $principalUri): array {
return [];
}

$configService = $this->configService;
return array_map(function (Board $board) use ($principalUri) {
return new Calendar($principalUri, 'board-' . $board->getId(), $board, $this->backend);
}, $this->backend->getBoards());
}, array_filter($this->backend->getBoards(), function ($board) use ($configService) {
return $configService->isCalendarEnabled($board->getId());
}));
}

public function hasCalendarInCalendarHome(string $principalUri, string $calendarUri): bool {
Expand Down
73 changes: 53 additions & 20 deletions lib/Db/AssignedUsersMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,35 @@
*
*/

declare(strict_types=1);

namespace OCA\Deck\Db;

use OCA\Deck\NotFoundException;
use OCA\Deck\Service\CirclesService;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;

class AssignedUsersMapper extends DeckMapper implements IPermissionMapper {
class AssignedUsersMapper extends QBMapper implements IPermissionMapper {

/** @var CardMapper */
private $cardMapper;
/** @var IUserManager */
private $userManager;
/**
* @var IGroupManager
*/
/** @var IGroupManager */
private $groupManager;
/** @var CirclesService */
private $circleService;

public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager, IGroupManager $groupManager) {
public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager, IGroupManager $groupManager, CirclesService $circleService) {
parent::__construct($db, 'deck_assigned_users', AssignedUsers::class);
$this->cardMapper = $cardMapper;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->circleService = $circleService;
}

/**
Expand All @@ -51,19 +59,25 @@ public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserMana
* @return array|Entity
*/
public function find($cardId) {
$sql = 'SELECT * FROM `*PREFIX*deck_assigned_users` ' .
'WHERE `card_id` = ?';
$users = $this->findEntities($sql, [$cardId]);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_assigned_users')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId)));
/** @var AssignedUsers[] $users */
$users = $this->findEntities($qb);
foreach ($users as &$user) {
$this->mapParticipant($user);
}
return $users;
}

public function findByUserId($uid) {
$sql = 'SELECT * FROM `*PREFIX*deck_assigned_users` ' .
'WHERE `participant` = ?';
return $this->findEntities($sql, [$uid]);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_assigned_users')
->where($qb->expr()->eq('participant', $qb->createNamedParameter($uid)));
/** @var AssignedUsers[] $users */
return $this->findEntities($qb);
}


Expand All @@ -81,24 +95,43 @@ public function findBoardId($cardId) {
* @param Entity $entity
* @return null|Entity
*/
public function insert(Entity $entity) {
public function insert(Entity $entity): Entity {
$origin = $this->getOrigin($entity);
if ($origin !== null) {
/** @var AssignedUsers $assignment */
$assignment = parent::insert($entity);
$this->mapParticipant($assignment);
return $assignment;
if ($origin === null) {
throw new NotFoundException('No origin found for assignment');
}
return null;
/** @var AssignedUsers $assignment */
$assignment = parent::insert($entity);
$this->mapParticipant($assignment);
return $assignment;
}

public function mapParticipant(AssignedUsers &$assignment) {
public function mapParticipant(AssignedUsers $assignment): void {
$self = $this;
$assignment->resolveRelation('participant', function () use (&$self, &$assignment) {
return $self->getOrigin($assignment);
});
}

public function isUserAssigned($cardId, $userId): bool {
$assignments = $this->find($cardId);
/** @var AssignedUsers $assignment */
foreach ($assignments as $assignment) {
$origin = $this->getOrigin($assignment);
if ($origin instanceof User && $assignment->getParticipant() === $userId) {
return true;
}
if ($origin instanceof Group && $this->groupManager->isInGroup($userId, $assignment->getParticipant())) {
return true;
}
if ($origin instanceof Circle && $this->circleService->isUserInCircle($assignment->getParticipant(), $userId)) {
return true;
}
}

return false;
}

private function getOrigin(AssignedUsers $assignment) {
if ($assignment->getType() === AssignedUsers::TYPE_USER) {
$origin = $this->userManager->get($assignment->getParticipant());
Expand All @@ -109,7 +142,7 @@ private function getOrigin(AssignedUsers $assignment) {
return $origin ? new Group($origin) : null;
}
if ($assignment->getType() === AssignedUsers::TYPE_CIRCLE) {
$origin = $this->groupManager->get($assignment->getParticipant());
$origin = $this->circleService->getCircle($assignment->getParticipant());
return $origin ? new Circle($origin) : null;
}
return null;
Expand Down
3 changes: 3 additions & 0 deletions lib/Db/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Board extends RelationalEntity {
protected $deletedAt = 0;
protected $lastModified = 0;

protected $settings = [];

public function __construct() {
$this->addType('id', 'integer');
$this->addType('shared', 'integer');
Expand All @@ -49,6 +51,7 @@ public function __construct() {
$this->addRelation('users');
$this->addRelation('permissions');
$this->addRelation('stacks');
$this->addRelation('settings');
$this->addResolvable('owner');
$this->shared = -1;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/Db/CardMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

use Exception;
use OCP\AppFramework\Db\Entity;

use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
Expand Down Expand Up @@ -82,7 +81,8 @@ public function update(Entity $entity, $updateModified = true): Entity {
}

// make sure we only reset the notification flag if the duedate changes
if (in_array('duedate', $entity->getUpdatedFields(), true)) {
$updatedFields = $entity->getUpdatedFields();
if (isset($updatedFields['duedate']) && $updatedFields['duedate']) {
try {
/** @var Card $existing */
$existing = $this->find($entity->getId());
Expand Down Expand Up @@ -243,6 +243,7 @@ public function findOverdue() {
$qb->select('id','title','duedate','notified')
->from('deck_cards')
->where($qb->expr()->lt('duedate', $qb->createFunction('NOW()')))
->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false)))
->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
Expand Down
4 changes: 4 additions & 0 deletions lib/Db/RelationalObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ public function getObjectSerialization() {
throw new \Exception('jsonSerialize is not implemented on ' . get_class($this));
}
}

public function getPrimaryKey(): string {
return $this->primaryKey;
}
}
Loading