Skip to content

Commit

Permalink
feat: Semi-persistent local filtering on table
Browse files Browse the repository at this point in the history
Signed-off-by: Cleopatra Enjeck M <patrathewhiz@gmail.com>
  • Loading branch information
enjeck committed Oct 4, 2024
1 parent 7e6c26f commit 6c2f535
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 0 deletions.
1 change: 1 addition & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
['name' => 'view#create', 'url' => '/view', 'verb' => 'POST'],
['name' => 'view#update', 'url' => '/view/{id}', 'verb' => 'PUT'],
['name' => 'view#destroy', 'url' => '/view/{id}', 'verb' => 'DELETE'],
['name' => 'view#createTemporaryView', 'url' => '/view/{tableId}/temporary-view', 'verb' => 'POST'],

// columns
['name' => 'column#indexTableByView', 'url' => '/column/table/{tableId}/view/{viewId}', 'verb' => 'GET'],
Expand Down
9 changes: 9 additions & 0 deletions lib/Controller/ViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ public function update(int $id, array $data): DataResponse {
});
}

/**
* @NoAdminRequired
*/
public function createTemporaryView(int $tableId, array $data): DataResponse {
return $this->handleError(function () use ($tableId, $data) {
return $this->service->createTemporaryView($tableId, $data, $this->userId);
});
}

/**
* @NoAdminRequired
*/
Expand Down
26 changes: 26 additions & 0 deletions lib/Db/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
* @method setSort(string $sort)
* @method getOwnerDisplayName(): string
* @method setOwnerDisplayName(string $ownerDisplayName)
* @method getRows(): string
* @method setRows(string $rows)
* @method getColumnValues(): string
* @method setColumnValues(string $columnValues)
*/
class View extends Entity implements JsonSerializable {
protected ?string $title = null;
Expand All @@ -73,6 +77,8 @@ class View extends Entity implements JsonSerializable {
protected ?int $rowsCount = 0;
protected ?string $ownership = null;
protected ?string $ownerDisplayName = null;
protected ?string $rows = null;
protected ?string $columnValues = null;

public function __construct() {
$this->addType('id', 'integer');
Expand All @@ -87,6 +93,16 @@ public function getColumnsArray(): array {
return $this->getArray($this->getColumns());
}


public function getRowsArray(): array {
return $this->getArray($this->getRows());
}

public function getColumnValuesArray(): array {
return $this->getArray($this->getColumnValues());
}


/**
* @psalm-suppress MismatchingDocblockReturnType
* @return list<array{columnId: int, mode: 'ASC'|'DESC'}>
Expand Down Expand Up @@ -125,6 +141,14 @@ public function setColumnsArray(array $array):void {
$this->setColumns(\json_encode($array));
}

public function setRowsArray(array $array):void {
$this->setRows(\json_encode($array));
}

public function setColumnValuesArray(array $array):void {
$this->setColumnValues(\json_encode($array));
}

public function setSortArray(array $array):void {
$this->setSort(\json_encode($array));
}
Expand Down Expand Up @@ -168,6 +192,8 @@ public function jsonSerialize(): array {
'hasShares' => !!$this->hasShares,
'rowsCount' => $this->rowsCount ?: 0,
'ownerDisplayName' => $this->ownerDisplayName,
'rows' => $this->getRowsArray(),
'columnValues' => $this->getColumnValuesArray(),
];
$serialisedJson['filter'] = $this->getFilterArray();

Expand Down
129 changes: 129 additions & 0 deletions lib/Service/ViewService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
use OCA\Tables\Db\Table;
use OCA\Tables\Db\View;
use OCA\Tables\Db\ViewMapper;
use OCA\Tables\Db\ColumnMapper;
use OCA\Tables\Db\Row2;
use OCA\Tables\Db\Row2Mapper;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
Expand All @@ -40,6 +43,9 @@ class ViewService extends SuperService {

private RowService $rowService;

private ColumnMapper $columnMapper;
private Row2Mapper $row2Mapper;

protected UserHelper $userHelper;

protected FavoritesService $favoritesService;
Expand All @@ -54,6 +60,8 @@ public function __construct(
LoggerInterface $logger,
?string $userId,
ViewMapper $mapper,
ColumnMapper $columnMapper,
Row2Mapper $row2Mapper,
ShareService $shareService,
RowService $rowService,
UserHelper $userHelper,
Expand All @@ -67,6 +75,8 @@ public function __construct(
$this->mapper = $mapper;
$this->shareService = $shareService;
$this->rowService = $rowService;
$this->columnMapper = $columnMapper;
$this->row2Mapper = $row2Mapper;
$this->userHelper = $userHelper;
$this->favoritesService = $favoritesService;
$this->eventDispatcher = $eventDispatcher;
Expand Down Expand Up @@ -550,4 +560,123 @@ public function search(string $term, int $limit = 100, int $offset = 0, ?string
return [];
}
}

/**
* @param int $tableId
* @param array $data
* @param string|null $userId
* @return View
* @throws InternalError
* @throws PermissionError
* @throws InvalidArgumentException
*/
public function createTemporaryView(int $tableId, array $data, ?string $userId = null): View {
$userId = $this->permissionsService->preCheckUserId($userId);

try {
// $table = $this->tableService->find($tableId);

// // security
// if (!$this->permissionsService->canReadTable($table, $userId)) {
// throw new PermissionError('PermissionError: can not read table with id ' . $tableId);
// }
$this->logger->warning((string)$tableId.' , '.$userId);

$view = new View();
$view->setTableId($tableId);
$view->setOwnership($userId);

$updatableParameter = ['title', 'emoji', 'description', 'columns', 'sort', 'filter'];

foreach ($data as $key => $value) {
if (!in_array($key, $updatableParameter)) {
throw new InvalidArgumentException('Invalid parameter: ' . $key);
}

switch ($key) {
case 'title':
$view->setTitle($value);
break;
case 'emoji':
$view->setEmoji($value);
break;
case 'description':
$view->setDescription($value);
break;
case 'columns':
$view->setColumnsArray($value);
break;
case 'sort':
$view->setSortArray($value);
break;
case 'filter':
$view->setFilterArray($value);
break;
}
}
$rows = $this->findRowsByView($tableId, $userId, $view);
$view->setRowsArray($rows);
$columnValues = $this->findColumnsByView($tableId, $view, $userId);
$view->setColumnValuesArray($columnValues);

// $this->enhanceTemporaryView($view, $userId);

return $view;
} catch (NotFoundError $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new InternalError('Table not found: ' . $tableId);
} catch (PermissionError $e) {
$this->logger->debug('permission error during creating temporary view', ['exception' => $e]);
throw $e;
} catch (Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new InternalError($e->getMessage());
}
}

/**
* @param int $tableId
* @param string $userId
* @param View view
* @param int|null $limit
* @param int|null $offset
* @return Row2[]
* @throws DoesNotExistException
* @throws InternalError
* @throws MultipleObjectsReturnedException
* @throws PermissionError
*/
public function findRowsByView(int $tableId, string $userId, View $view, ?int $limit = null, ?int $offset = null): array {
try {
if ($this->permissionsService->canReadRowsByElementId($tableId, 'table', $userId)) {
$columnsArray = $view->getColumnsArray();
$filterColumns = $this->columnMapper->findAll($columnsArray);
$tableColumns = $this->columnMapper->findAllByTable($view->getTableId());

return $this->row2Mapper->findAll($tableColumns, $filterColumns, $view->getTableId(), $limit, $offset, $view->getFilterArray(), $view->getSortArray(), $userId);
} else {
throw new PermissionError('no read access to table id = '.$tableId);
}
} catch (Exception $e) {
$this->logger->error($e->getMessage());
throw new InternalError($e->getMessage());
}
}


/**
* @param int $tableId
* @param View view
* @param string|null $userId
* @return array
* @throws NotFoundError
* @throws PermissionError
* @throws InternalError
*/
public function findColumnsByView(int $tableId, View $view, ?string $userId = null): array {
$viewColumnIds = $view->getColumnsArray();
$viewColumns = $this->columnMapper->findAll($viewColumnIds);
return $viewColumns;
}

}
69 changes: 69 additions & 0 deletions src/modules/main/sections/Temp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!--
- SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<!-- <div v-if="!view" class="icon-loading" />
<div v-else class="main-view-view"> -->
<div>
<ElementTitle :active-element="view" :is-table="false" :view-setting.sync="localViewSetting" />
<div class="table-wrapper">
<EmptyView v-if="columns.length === 0" :view="view" />
<TableView v-else
:rows="rows"
:columns="columns"
:element="view"
:view-setting.sync="localViewSetting"
:is-view="true"
:can-read-rows="true"
:can-create-rows="true"
:can-edit-rows="true"
:can-delete-rows="false"
:can-create-columns="false"
:can-edit-columns="false"
:can-delete-columns="false"
:can-delete-table="false" />
</div>
</div>
</template>

<script>
import TableView from '../partials/TableView.vue'
import EmptyView from './EmptyView.vue'
import permissionsMixin from '../../../shared/components/ncTable/mixins/permissionsMixin.js'
import ElementTitle from './ElementTitle.vue'
export default {
components: {
EmptyView,
TableView,
ElementTitle,
},
mixins: [permissionsMixin],
props: {
view: {
type: Object,
default: null,
},
columns: {
type: Array,
default: null,
},
rows: {
type: Array,
default: null,
},
},
data() {
return {
localLoading: false,
lastActiveViewId: null,
localViewSetting: {},
}
},
}
</script>
Loading

0 comments on commit 6c2f535

Please sign in to comment.