Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable30] feat: rewrite file list #3898

Merged
merged 48 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e424bb7
feat: rewrite file list
vitormattos Sep 20, 2024
f32e23f
chore: start to implement FileEntry component
vitormattos Sep 23, 2024
09fba66
fix: linter
vitormattos Sep 24, 2024
eb7b5d1
feat: add FileEntryName component
vitormattos Sep 24, 2024
bd55cc4
chore: add component EntryAction
vitormattos Sep 24, 2024
7732407
feat: trigger opened menu on right click
vitormattos Sep 25, 2024
2a9c598
chore: make FileEntry at table mode to work
vitormattos Sep 25, 2024
eaad006
chore: remove checkbox
vitormattos Sep 25, 2024
4b42a5f
fix: header sorting arrow icon
v-bianchi Sep 25, 2024
78dd038
feat: add status dot to file entry component
v-bianchi Sep 25, 2024
fb0a129
fix: linter
vitormattos Sep 25, 2024
4329294
feat: open file details side bar
v-bianchi Sep 26, 2024
ab208f1
feat: implement file filtering logic WIP
v-bianchi Sep 26, 2024
00085ab
feat: filter files according to status and modified values
v-bianchi Sep 30, 2024
4354bad
chore: update OpenAPI documentation
v-bianchi Sep 30, 2024
5d54c55
feat: implement file sorting
v-bianchi Sep 30, 2024
5a69c8c
chore: implement buttons to reques to sign
vitormattos Oct 14, 2024
9840cfa
chore: move methods to store
vitormattos Oct 14, 2024
6a7f8da
chore: make sign menu to work
vitormattos Oct 19, 2024
2306a52
chore: implement validate
vitormattos Oct 19, 2024
cf36e84
chore: start to implement delete
vitormattos Oct 21, 2024
c9e86cb
fix: prevent to be funcion when isn't
vitormattos Oct 24, 2024
2c43b92
fix: lint
vitormattos Oct 24, 2024
7c85cb5
chore: confirm before delete
vitormattos Oct 24, 2024
2b0eee1
chore: indent
vitormattos Oct 24, 2024
b788e02
chore: make reactive the conditional to display actions
vitormattos Oct 24, 2024
1f7a2bd
fix: sing from action
vitormattos Oct 24, 2024
a0dcaf3
chore: only delete from backend when the file exists at LibreSign side
vitormattos Oct 24, 2024
4a4ed53
fix: way to get signer uuid
vitormattos Oct 24, 2024
f112904
feat: implement order
vitormattos Oct 24, 2024
5bce873
feat: implement lazy loading of files with pagination
v-bianchi Oct 28, 2024
ef1b10a
feat: integrate file list filters and pagination query params WIP
v-bianchi Oct 31, 2024
bcc894d
fix: prevent infinity loop when use IntersectionObserver
vitormattos Oct 31, 2024
ad9c5bc
fix: linter
vitormattos Oct 31, 2024
0fd434c
chore: remove timeline
vitormattos Oct 31, 2024
2388b31
chore: phpcs fix
vitormattos Oct 31, 2024
0b4b535
fix: SPDX
vitormattos Oct 31, 2024
55c4f02
fix: prevent add duplicated element to array
vitormattos Oct 31, 2024
320acdb
fix: copy pdfworker to LibreSign
vitormattos Oct 31, 2024
60f8ed8
feat: integrate files filtering and pagination, fix IntersectionObserver
v-bianchi Oct 31, 2024
ca1ee55
fix: integration tests
vitormattos Oct 31, 2024
55177fa
refactor: make more readdable
vitormattos Oct 31, 2024
03fc7bb
refactor: make more readdable
vitormattos Oct 31, 2024
1422c9e
fix: query string order
vitormattos Oct 31, 2024
b2a2df7
chore: remove delete from filter
vitormattos Oct 31, 2024
31f5028
fix: order url parameters of pagination
vitormattos Nov 1, 2024
bda9c90
feat: make possible delete file too
vitormattos Nov 5, 2024
9f959ee
feat: Save file to LibeSign every when a new file is uploaded
vitormattos Nov 5, 2024
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
74 changes: 71 additions & 3 deletions lib/Controller/FileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use OCA\Files_Sharing\SharedStorage;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Db\File as FileEntity;
use OCA\Libresign\Db\SignRequestMapper;
use OCA\Libresign\Exception\LibresignException;
use OCA\Libresign\Helper\JSActions;
Expand All @@ -19,6 +20,7 @@
use OCA\Libresign\Service\AccountService;
use OCA\Libresign\Service\FileService;
use OCA\Libresign\Service\IdentifyMethodService;
use OCA\Libresign\Service\RequestSignatureService;
use OCA\Libresign\Service\SessionService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
Expand Down Expand Up @@ -59,6 +61,7 @@ public function __construct(
private SessionService $sessionService,
private SignRequestMapper $signRequestMapper,
private IdentifyMethodService $identifyMethodService,
private RequestSignatureService $requestSignatureService,
private AccountService $accountService,
private IRootFolder $root,
private IPreview $preview,
Expand Down Expand Up @@ -192,9 +195,13 @@ public function validate(?string $type = null, $identifier = null): DataResponse
*
* @param string|null $signer_uuid Signer UUID
* @param string|null $nodeId The nodeId (also called fileId). Is the id of a file at Nextcloud
* @param int|null $status Status could be one of 0 = draft, 1 = able to sign, 2 = partial signed, 3 = signed, 4 = deleted.
* @param list<int>|null $status Status could be none or many of 0 = draft, 1 = able to sign, 2 = partial signed, 3 = signed, 4 = deleted.
* @param int|null $page the number of page to return
* @param int|null $length Total of elements to return
* @param int|null $start Start date of signature request (UNIX timestamp)
* @param int|null $end End date of signature request (UNIX timestamp)
* @param string|null $sortBy Name of the column to sort by
* @param string|null $sortDirection Ascending or descending order
* @return DataResponse<Http::STATUS_OK, array{pagination: LibresignPagination, data: ?LibresignFile[]}, array{}>
*
* 200: OK
Expand All @@ -207,16 +214,26 @@ public function list(
?int $length = null,
?string $signer_uuid = null,
?string $nodeId = null,
?int $status = null,
?array $status = null,
?int $start = null,
?int $end = null,
?string $sortBy = null,
?string $sortDirection = null,
): DataResponse {
$filter = array_filter([
'signer_uuid' => $signer_uuid,
'nodeId' => $nodeId,
'status' => $status,
'start' => $start,
'end' => $end,
], static function ($var) { return $var !== null; });
$sort = [
'sortBy' => $sortBy,
'sortDirection' => $sortDirection,
];
$return = $this->fileService
->setMe($this->userSession->getUser())
->listAssociatedFilesOfSignFlow($page, $length, $filter);
->listAssociatedFilesOfSignFlow($page, $length, $filter, $sort);
return new DataResponse($return, Http::STATUS_OK);
}

Expand Down Expand Up @@ -356,6 +373,15 @@ public function save(array $file, string $name = '', array $settings = []): Data
'file' => $file,
'settings' => $settings
]);
$data = [
'file' => [
'fileNode' => $node,
],
'name' => $name,
'userManager' => $this->userSession->getUser(),
'status' => FileEntity::STATUS_DRAFT,
];
$file = $this->requestSignatureService->save($data);

return new DataResponse(
[
Expand All @@ -377,4 +403,46 @@ public function save(array $file, string $name = '', array $settings = []): Data
);
}
}

/**
* Delete File
*
* This will delete the file and all data
*
* @param integer $fileId Node id of a Nextcloud file
* @return DataResponse<Http::STATUS_OK, array{message: string}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, array{message: string}, array{}>|DataResponse<Http::STATUS_UNPROCESSABLE_ENTITY, array{action: integer, errors: string[]}, array{}>
*
* 200: OK
* 401: Failed
* 422: Failed
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[RequireManager]
#[ApiRoute(verb: 'DELETE', url: '/api/{apiVersion}/file/file_id/{fileId}', requirements: ['apiVersion' => '(v1)'])]
public function deleteAllRequestSignatureUsingFileId(int $fileId): DataResponse {
try {
$data = [
'userManager' => $this->userSession->getUser(),
'file' => [
'fileId' => $fileId
]
];
$this->validateHelper->validateExistingFile($data);
$this->fileService->delete($fileId);
} catch (\Throwable $th) {
return new DataResponse(
[
'message' => $th->getMessage(),
],
Http::STATUS_UNAUTHORIZED
);
}
return new DataResponse(
[
'message' => $this->l10n->t('Success')
],
Http::STATUS_OK
);
}
}
2 changes: 1 addition & 1 deletion lib/Db/AccountFileMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private function getUserAccountFile(array $filter = []): Pagination {
$qb->setMaxResults($filter['length']);
}

$pagination = new Pagination($qb);
$pagination = new Pagination($qb, $this->urlGenerator);
return $pagination;
}

Expand Down
29 changes: 25 additions & 4 deletions lib/Db/SignRequestMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,12 @@ public function getFilesAssociatedFilesWithMeFormatted(
array $filter,
?int $page = null,
?int $length = null,
?array $sort = [],
): array {
$filter['email'] = $user->getEMailAddress();
$filter['length'] = $length;
$filter['page'] = $page;
$pagination = $this->getFilesAssociatedFilesWithMeStmt($user->getUID(), $filter);
$pagination = $this->getFilesAssociatedFilesWithMeStmt($user->getUID(), $filter, $sort);
$pagination->setMaxPerPage($length);
$pagination->setCurrentPage($page);
$currentPageResults = $pagination->getCurrentPageResults();
Expand Down Expand Up @@ -489,7 +490,17 @@ private function getFilesAssociatedFilesWithMeQueryBuilder(string $userId, ?arra
}
if (!empty($filter['status'])) {
$qb->andWhere(
$qb->expr()->eq('f.status', $qb->createNamedParameter($filter['status'], IQueryBuilder::PARAM_INT))
$qb->expr()->in('f.status', $qb->createNamedParameter($filter['status'], IQueryBuilder::PARAM_INT_ARRAY))
);
}
if (!empty($filter['start'])) {
$qb->andWhere(
$qb->expr()->gte('f.created_at', $qb->createNamedParameter($filter['start'], IQueryBuilder::PARAM_INT))
);
}
if (!empty($filter['end'])) {
$qb->andWhere(
$qb->expr()->lte('f.created_at', $qb->createNamedParameter($filter['end'], IQueryBuilder::PARAM_INT))
);
}
if (isset($filter['length']) && isset($filter['page'])) {
Expand All @@ -500,7 +511,11 @@ private function getFilesAssociatedFilesWithMeQueryBuilder(string $userId, ?arra
return $qb;
}

private function getFilesAssociatedFilesWithMeStmt(string $userId, ?array $filter = []): Pagination {
private function getFilesAssociatedFilesWithMeStmt(
string $userId,
?array $filter = [],
?array $sort = [],
): Pagination {
$qb = $this->getFilesAssociatedFilesWithMeQueryBuilder($userId, $filter);
$qb->select(
'f.id',
Expand All @@ -511,6 +526,12 @@ private function getFilesAssociatedFilesWithMeStmt(string $userId, ?array $filte
'f.status',
'f.metadata',
);
if (!empty($sort) && in_array($sort['sortBy'], ['name', 'status', 'created_at'])) {
$qb->orderBy(
$qb->func()->lower('f.' . $sort['sortBy']),
$sort['sortDirection'] == 'asc' ? 'asc' : 'desc'
);
}
$qb->selectAlias('f.created_at', 'request_date');

$countQueryBuilderModifier = function (IQueryBuilder $qb): int {
Expand All @@ -522,7 +543,7 @@ private function getFilesAssociatedFilesWithMeStmt(string $userId, ?array $filte
return (int)$qb->executeQuery()->fetchOne();
};

$pagination = new Pagination($qb);
$pagination = new Pagination($qb, $this->urlGenerator);
return $pagination;
}

Expand Down
71 changes: 52 additions & 19 deletions lib/Helper/Pagination.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@

use OCA\Libresign\Db\PagerFantaQueryAdapter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IURLGenerator;
use Pagerfanta\Pagerfanta;

class Pagination extends Pagerfanta {
/** @var string */
private $rootPath;
private string $routeName;
public function __construct(
IQueryBuilder $queryBuilder,
private IURLGenerator $urlGenerator,
) {
$adapter = new PagerFantaQueryAdapter($queryBuilder);
parent::__construct($adapter);
Expand All @@ -25,27 +26,59 @@ public function __construct(
/**
* @return static
*/
public function setRootPath(string $rootPath = ''): self {
$this->rootPath = $rootPath;
public function setRouteName(string $routeName = ''): self {
$this->routeName = $routeName;
return $this;
}

public function getPagination(?int $page, ?int $length): array {
public function getPagination(int $page, int $length, array $filter = []): array {
$this->setMaxPerPage($length);
$pagination['total'] = $this->count();
if ($pagination['total'] > $length) {
$pagination['current'] = $this->rootPath . '?page=' . $page . '&length=' . $length;
$pagination['next'] = $this->hasNextPage() ? $this->rootPath . '?page=' . $this->getNextPage() . '&length=' . $length : null;
$pagination['prev'] = $this->hasPreviousPage() ? $this->rootPath . '?page=' . $this->getPreviousPage() . '&length=' . $length : null;
$pagination['last'] = $this->hasNextPage() ? $this->rootPath . '?page=' . $this->getNbPages() . '&length=' . $length : null;
$pagination['first'] = $this->hasPreviousPage() ? $this->rootPath . '?page=1&length=' . $length : null;
} else {
$pagination['current'] = null;
$pagination['next'] = null;
$pagination['prev'] = null;
$pagination['last'] = null;
$pagination['first'] = null;
$total = $this->count();
if ($total > $length) {
return [
'total' => $total,
'current' => $this->linkToRoute(true, $page, $length, $filter),
'next' => $this->linkToRoute($this->hasNextPage(), 'getNextPage', $length, $filter),
'prev' => $this->linkToRoute($this->hasPreviousPage(), 'getPreviousPage', $length, $filter),
'last' => $this->linkToRoute($this->hasNextPage(), 'getNbPages', $length, $filter),
'first' => $this->linkToRoute($this->hasPreviousPage(), 1, $length, $filter),
];
}
return $pagination;
return [
'total' => $total,
'current' => null,
'next' => null,
'prev' => null,
'last' => null,
'first' => null,
];
}

private function linkToRoute(bool $condition, int|string $page, int $length, array $filter): ?string {
if (!$condition) {
return null;
}
if (is_string($page)) {
$page = $this->$page();
}
$url = $this->urlGenerator->linkToRouteAbsolute(
$this->routeName,
array_merge(['page' => $page, 'length' => $length, 'apiVersion' => 'v1'], $filter)
);
$url = $this->sortParameters($url);
return $url;
}

/**
* This is necessary to fix problem at integration tests because the method linkToRoute change the order of parameters
*/
private function sortParameters(?string $url): ?string {
if (!$url) {
return $url;
}
parse_str(parse_url($url, PHP_URL_QUERY), $query);
ksort($query);
$url = strtok($url, '?') . '?' . http_build_query($query);
return $url;
}
}
4 changes: 2 additions & 2 deletions lib/Service/AccountFileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public function accountFileList(array $filter, ?int $page = null, ?int $length =
$page = $page ?? 1;
$length = $length ?? (int)$this->appConfig->getAppValue('length_of_page', '100');
$data = $this->accountFileMapper->accountFileList($filter, $page, $length);
$data['pagination']->setRootPath('/file/list');
$data['pagination']->setRouteName('ocs.libresign.File.list');
return [
'data' => $data['data'],
'pagination' => $data['pagination']->getPagination($page, $length)
'pagination' => $data['pagination']->getPagination($page, $length, $filter)
];
}
}
28 changes: 25 additions & 3 deletions lib/Service/FileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,12 @@ public function setFileByPath(string $path): self {
*
* @psalm-return array{data: array, pagination: array}
*/
public function listAssociatedFilesOfSignFlow($page = null, $length = null, array $filter = []): array {
public function listAssociatedFilesOfSignFlow(
$page = null,
$length = null,
array $filter = [],
array $sort = [],
): array {
$page = $page ?? 1;
$length = $length ?? (int)$this->appConfig->getAppValue('length_of_page', '100');

Expand All @@ -441,17 +446,18 @@ public function listAssociatedFilesOfSignFlow($page = null, $length = null, arra
$filter,
$page,
$length,
$sort,
);

$signers = $this->signRequestMapper->getByMultipleFileId(array_column($return['data'], 'id'));
$identifyMethods = $this->signRequestMapper->getIdentifyMethodsFromSigners($signers);
$visibleElements = $this->signRequestMapper->getVisibleElementsFromSigners($signers);
$return['data'] = $this->associateAllAndFormat($this->me, $return['data'], $signers, $identifyMethods, $visibleElements);

$return['pagination']->setRootPath('/file/list');
$return['pagination']->setRouteName('ocs.libresign.File.list');
return [
'data' => $return['data'],
'pagination' => $return['pagination']->getPagination($page, $length)
'pagination' => $return['pagination']->getPagination($page, $length, $filter)
];
}

Expand Down Expand Up @@ -585,4 +591,20 @@ public function getMyLibresignFile(int $nodeId): File {
],
);
}

public function delete(int $fileId): void {
$file = $this->fileMapper->getByFileId($fileId);
$this->fileElementService->deleteVisibleElements($file->getId());
$list = $this->signRequestMapper->getByFileId($file->getId());
foreach ($list as $signRequest) {
$this->signRequestMapper->delete($signRequest);
}
$this->fileMapper->delete($file);
if ($file->getSignedNodeId()) {
$signedNextcloudFile = $this->folderService->getFileById($file->getSignedNodeId());
$signedNextcloudFile->delete();
}
$nextcloudFile = $this->folderService->getFileById($fileId);
$nextcloudFile->delete();
}
}
Loading
Loading