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.4.0-dev.0</version>
<version>2.4.0-dev.5</version>
<licence>AGPL-3.0-or-later</licence>
<author>Nextcloud GmbH and Nextcloud contributors</author>
<namespace>Tables</namespace>
Expand Down
7 changes: 5 additions & 2 deletions lib/Db/ContextMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protected function getFindContextBaseQuery(?string $userId): IQueryBuilder {
$qb->select(
'c.*',
'r.id as node_rel_id', 'r.node_id', 'r.node_type', 'r.permissions',
'p.id as page_id', 'p.page_type',
'p.id as page_id', 'p.page_type', 'p.title as page_title', 'p.emoji as page_emoji', 'p.description as page_description',
'pc.id as content_id', 'pc.order',
'n.display_mode as display_mode_default',
's.id as share_id', 's.receiver', 's.receiver_type'
Expand Down Expand Up @@ -131,7 +131,10 @@ protected function formatResultRows(array $rows, ?string $userId) {
$carry[$item['page_id']] ??= ['content' => []];
$carry[$item['page_id']]['id'] = (int)$item['page_id'];
$carry[$item['page_id']]['page_type'] = $item['page_type'];
if ($item['node_rel_id'] !== null) {
$carry[$item['page_id']]['title'] = $item['page_title'];
$carry[$item['page_id']]['emoji'] = $item['page_emoji'];
$carry[$item['page_id']]['description'] = $item['page_description'];
if ($item['node_rel_id'] !== null && $item['content_id'] !== null) {
$carry[$item['page_id']]['content'][$item['content_id']] = [
'order' => (int)$item['order'],
'node_rel_id' => (int)$item['node_rel_id']
Expand Down
13 changes: 13 additions & 0 deletions lib/Db/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@
* @method setContextId(int $value): void
* @method getPageType(): string
* @method setPageType(string $value): void
* @method getTitle(): ?string
* @method setTitle(?string $value): void
* @method getEmoji(): ?string
* @method setEmoji(?string $value): void
* @method getDescription(): ?string
* @method setDescription(?string $value): void
*/
class Page extends Entity implements \JsonSerializable {
public const TYPE_STARTPAGE = 'startpage';
public const TYPE_GRID = 'grid';

protected ?int $contextId = null;
protected ?string $pageType = null;
protected ?string $title = null;
protected ?string $emoji = null;
protected ?string $description = null;

public function __construct() {
$this->addType('id', 'integer');
Expand All @@ -31,6 +41,9 @@ public function jsonSerialize(): array {
'id' => $this->getId(),
'contextId' => $this->getContextId(),
'pageType' => $this->getPageType(),
'title' => $this->getTitle(),
'emoji' => $this->getEmoji(),
'description' => $this->getDescription(),
];
}
}
62 changes: 62 additions & 0 deletions lib/Db/Widget.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

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

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* A widget on a grid page: its type, optional title and its place on the grid.
*
* @method getPageId(): int
* @method setPageId(int $value): void
* @method getType(): string
* @method setType(string $value): void
* @method getTitle(): ?string
* @method setTitle(?string $value): void
* @method getGridX(): int
* @method setGridX(int $value): void
* @method getGridY(): int
* @method setGridY(int $value): void
* @method getGridWidth(): int
* @method setGridWidth(int $value): void
* @method getGridHeight(): int
* @method setGridHeight(int $value): void
*/
class Widget extends Entity implements \JsonSerializable {
protected ?int $pageId = null;
protected ?string $type = null;
protected ?string $title = null;
protected ?int $gridX = null;
protected ?int $gridY = null;
protected ?int $gridWidth = null;
protected ?int $gridHeight = null;

public function __construct() {
$this->addType('id', 'integer');
$this->addType('page_id', 'integer');
$this->addType('grid_x', 'integer');
$this->addType('grid_y', 'integer');
$this->addType('grid_width', 'integer');
$this->addType('grid_height', 'integer');
}

public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'pageId' => $this->getPageId(),
'type' => $this->getType(),
'title' => $this->getTitle(),
'gridX' => $this->getGridX(),
'gridY' => $this->getGridY(),
'gridWidth' => $this->getGridWidth(),
'gridHeight' => $this->getGridHeight(),
];
}
}
78 changes: 78 additions & 0 deletions lib/Db/WidgetMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

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

namespace OCA\Tables\Db;

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

/** @template-extends QBMapper<Widget> */
class WidgetMapper extends QBMapper {
protected string $table = 'tables_contexts_widget';

public function __construct(IDBConnection $db) {
parent::__construct($db, $this->table, Widget::class);
}

/**
* @param int[] $pageIds
* @return Widget[] ordered by page and grid position
* @throws Exception
*/
public function findByPageIds(array $pageIds): array {
if ($pageIds === []) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->table)
->where($qb->expr()->in('page_id', $qb->createNamedParameter($pageIds, IQueryBuilder::PARAM_INT_ARRAY)))
->orderBy('page_id', 'ASC')
->addOrderBy('grid_y', 'ASC')
->addOrderBy('grid_x', 'ASC')
->addOrderBy('id', 'ASC');

return $this->findEntities($qb);
}

/**
* @return int[]
* @throws Exception
*/
public function findIdsByPageId(int $pageId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('id')
->from($this->table)
->where($qb->expr()->eq('page_id', $qb->createNamedParameter($pageId, IQueryBuilder::PARAM_INT)));

$result = $qb->executeQuery();
$ids = [];
while ($row = $result->fetchAssociative()) {
$ids[] = (int)$row['id'];
}
$result->closeCursor();
return $ids;
}

/**
* @param int[] $widgetIds
* @throws Exception
*/
public function deleteByIds(array $widgetIds): void {
if ($widgetIds === []) {
return;
}
$qb = $this->db->getQueryBuilder();
$qb->delete($this->table)
->where($qb->expr()->in('id', $qb->createNamedParameter($widgetIds, IQueryBuilder::PARAM_INT_ARRAY)))
->executeStatement();
}
}
33 changes: 33 additions & 0 deletions lib/Db/WidgetSetting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

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

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* One setting of a widget. The value is JSON encoded.
*
* @method getWidgetId(): int
* @method setWidgetId(int $value): void
* @method getName(): string
* @method setName(string $value): void
* @method getValue(): ?string
* @method setValue(?string $value): void
*/
class WidgetSetting extends Entity {
protected ?int $widgetId = null;
protected ?string $name = null;
protected ?string $value = null;

public function __construct() {
$this->addType('id', 'integer');
$this->addType('widget_id', 'integer');
}
}
88 changes: 88 additions & 0 deletions lib/Db/WidgetSettingMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);

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

namespace OCA\Tables\Db;

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

/**
* Reads and writes widget settings as name => value maps. Values are stored JSON
* encoded, one row per setting.
*
* @psalm-type TablesWidgetSettingValue = string|int|float|bool|null
* @template-extends QBMapper<WidgetSetting>
*/
class WidgetSettingMapper extends QBMapper {
protected string $table = 'tables_contexts_widget_setting';

public function __construct(IDBConnection $db) {
parent::__construct($db, $this->table, WidgetSetting::class);
}

/**
* @param int[] $widgetIds
* @return array<int, array<string, TablesWidgetSettingValue>> settings by widget id
* @throws Exception
*/
public function findSettingsByWidgetIds(array $widgetIds): array {
if ($widgetIds === []) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('widget_id', 'name', 'value')
->from($this->table)
->where($qb->expr()->in('widget_id', $qb->createNamedParameter($widgetIds, IQueryBuilder::PARAM_INT_ARRAY)))
->orderBy('id', 'ASC');

$result = $qb->executeQuery();
$settings = [];
while ($row = $result->fetchAssociative()) {
$settings[(int)$row['widget_id']][(string)$row['name']] = $row['value'] === null
? null
: json_decode((string)$row['value'], true);
}
$result->closeCursor();
return $settings;
}

/**
* Replaces all settings of a widget.
*
* @param array<string, TablesWidgetSettingValue> $settings
* @throws Exception
* @throws \JsonException
*/
public function replaceSettings(int $widgetId, array $settings): void {
$this->deleteByWidgetIds([$widgetId]);
foreach ($settings as $name => $value) {
$setting = new WidgetSetting();
$setting->setWidgetId($widgetId);
$setting->setName((string)$name);
$setting->setValue($value === null ? null : json_encode($value, JSON_THROW_ON_ERROR));
$this->insert($setting);
}
}

/**
* @param int[] $widgetIds
* @throws Exception
*/
public function deleteByWidgetIds(array $widgetIds): void {
if ($widgetIds === []) {
return;
}
$qb = $this->db->getQueryBuilder();
$qb->delete($this->table)
->where($qb->expr()->in('widget_id', $qb->createNamedParameter($widgetIds, IQueryBuilder::PARAM_INT_ARRAY)))
->executeStatement();
}
}
31 changes: 31 additions & 0 deletions lib/Db/WidgetSource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

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

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* The node of its application a data widget shows. The widget id is the key;
* the inherited id is not used.
*
* @method getWidgetId(): int
* @method setWidgetId(int $value): void
* @method getNodeRelId(): int
* @method setNodeRelId(int $value): void
*/
class WidgetSource extends Entity {
protected ?int $widgetId = null;
protected ?int $nodeRelId = null;

public function __construct() {
$this->addType('widget_id', 'integer');
$this->addType('node_rel_id', 'integer');
}
}
Loading
Loading