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

feat: implement sign tab at right sidebar #2657

Merged
merged 35 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2b84e24
feat: implement sign tab
vitormattos Apr 2, 2024
688c356
fix: Lint
vitormattos Apr 3, 2024
bb833cc
chore: rewrite SignPDF to load as internal and external page
vitormattos Apr 8, 2024
c160b81
fix: Remove non-scoped css
vitormattos Apr 9, 2024
c648003
feat: Implement TopBar component
vitormattos Apr 9, 2024
2994cb9
chore: Add license header
vitormattos Apr 9, 2024
1b95e48
fix: Make possible open the sign page usign iframe
vitormattos Apr 10, 2024
df74db7
chore: Move logic to method
vitormattos Apr 10, 2024
8ae64e2
chore: Return signatureMethod at file list
vitormattos Apr 10, 2024
05471f1
chore: add initial state to signer signatures
vitormattos Apr 10, 2024
a4b95d1
fix: Able to close if not sent the request
vitormattos Apr 10, 2024
fa4c75b
feat: allow to replace signature at sign page
vitormattos Apr 10, 2024
26bb1a8
fix: linter
vitormattos Apr 10, 2024
6388b17
fix: linter
vitormattos Apr 10, 2024
772fc21
fix: prevent to access null value
vitormattos Apr 10, 2024
961782f
chore: Bump dependencies
vitormattos Apr 10, 2024
244a15d
chore: bump dependencies
vitormattos Apr 10, 2024
f373170
fix: integration test
vitormattos Apr 10, 2024
bcf1bff
chore: rename fileId to nodeId
vitormattos Apr 10, 2024
d12c64a
fix: these fields changed of name and places on previous commit
vitormattos Apr 10, 2024
61b7316
feat: show error messages at error page
vitormattos Apr 10, 2024
4c1945c
chore: Show error page as external
vitormattos Apr 10, 2024
9f98c47
chore: move route to have internal path
vitormattos Apr 10, 2024
43caf12
feat: Reduce usage of iframe
vitormattos Apr 10, 2024
cdc4b28
fix: Make possible edit and delete signature when unauthenticated
vitormattos Apr 10, 2024
790dcd3
chore: check if external action
vitormattos Apr 11, 2024
699df6a
fix: color
vitormattos Apr 11, 2024
90c5bfc
fix: color
vitormattos Apr 11, 2024
7cf2f1b
fix: core router use the name fileId and not fileId to this router
vitormattos Apr 11, 2024
e20f841
fix: Open modal as external
vitormattos Apr 11, 2024
fa38a86
fix: open iframe at app files
vitormattos Apr 11, 2024
eefb5cc
fix: show components when toggle sidebar
vitormattos Apr 11, 2024
b43d0cf
chore: Remove unused css
vitormattos Apr 11, 2024
2eeec54
fix: linter
vitormattos Apr 11, 2024
30671e8
chore: Remove unused file
vitormattos Apr 11, 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
8 changes: 4 additions & 4 deletions appinfo/routes/routesAccountController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
['name' => 'account#accountFileListToApproval', 'url' => '/api/{apiVersion}/account/files/approval/list', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#createSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements', 'verb' => 'POST', 'requirements' => $requirements],
['name' => 'account#getSignatureElements', 'url' => '/api/{apiVersion}/account/signature/elements', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#getSignatureElementPreview','url' => '/api/{apiVersion}/account/signature/elements/preview/{fileId}', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#getSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{elementId}', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#patchSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{elementId}', 'verb' => 'PATCH', 'requirements' => $requirements],
['name' => 'account#deleteSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{elementId}', 'verb' => 'DELETE', 'requirements' => $requirements],
['name' => 'account#getSignatureElementPreview','url' => '/api/{apiVersion}/account/signature/elements/preview/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#getSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'account#patchSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'PATCH', 'requirements' => $requirements],
['name' => 'account#deleteSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'DELETE', 'requirements' => $requirements],
],
];
Binary file removed img/application-pdf.png
Binary file not shown.
Binary file removed img/frame4.png
Binary file not shown.
22 changes: 13 additions & 9 deletions lib/Controller/AccountController.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,10 @@ public function getSignatureElements(): JSONResponse {
#[NoAdminRequired]
#[PublicPage]
#[NoCSRFRequired]
public function getSignatureElementPreview(int $fileId) {
public function getSignatureElementPreview(int $nodeId) {
try {
$node = $this->accountService->getFileByNodeIdAndSessionId(
$fileId,
$nodeId,
$this->sessionService->getSessionId()
);
} catch (DoesNotExistException $th) {
Expand All @@ -346,11 +346,11 @@ public function getSignatureElementPreview(int $fileId) {

#[NoAdminRequired]
#[NoCSRFRequired]
public function getSignatureElement(int $elementId): JSONResponse {
public function getSignatureElement(int $nodeId): JSONResponse {
$userId = $this->userSession->getUser()->getUID();
try {
return new JSONResponse(
$this->signerElementsService->getUserElementByElementId($userId, $elementId),
$this->signerElementsService->getUserElementByNodeId($userId, $nodeId),
Http::STATUS_OK
);
} catch (\Throwable $th) {
Expand All @@ -365,9 +365,9 @@ public function getSignatureElement(int $elementId): JSONResponse {

#[NoAdminRequired]
#[NoCSRFRequired]
public function patchSignatureElement($elementId, string $type = '', array $file = []): JSONResponse {
public function patchSignatureElement($nodeId, string $type = '', array $file = []): JSONResponse {
try {
$element['elementId'] = $elementId;
$element['nodeId'] = $nodeId;
if ($type) {
$element['type'] = $type;
}
Expand All @@ -394,10 +394,14 @@ public function patchSignatureElement($elementId, string $type = '', array $file

#[NoAdminRequired]
#[NoCSRFRequired]
public function deleteSignatureElement(int $elementId): JSONResponse {
$userId = $this->userSession->getUser()->getUID();
#[PublicPage]
public function deleteSignatureElement(int $nodeId): JSONResponse {
try {
$this->accountService->deleteSignatureElement($userId, $elementId);
$this->accountService->deleteSignatureElement(
user: $this->userSession->getUser(),
nodeId: $nodeId,
sessionId: $this->sessionService->getSessionId(),
);
} catch (\Throwable $th) {
return new JSONResponse(
[
Expand Down
3 changes: 1 addition & 2 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public function index(): TemplateResponse {
$this->initialState->provideInitialState('can_request_sign', false);
}

$this->provideSignerSignatues();
$this->initialState->provideInitialState('file_info', $this->fileService->formatFile());
$this->initialState->provideInitialState('identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings());
$this->initialState->provideInitialState('legal_information', $this->appConfig->getAppValue('legal_information'));
Expand Down Expand Up @@ -173,8 +174,6 @@ public function sign(string $uuid): TemplateResponse {
$this->initialState->provideInitialState('statusText', $file['statusText']);
$this->initialState->provideInitialState('signers', $file['signers']);
$this->provideSignerSignatues();
$signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId());
$this->initialState->provideInitialState('signature_methods', $signatureMethods);
$this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
$this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? '');
$this->initialState->provideInitialState('pdf',
Expand Down
12 changes: 6 additions & 6 deletions lib/Db/FileMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ public function getBySignerUuid(?string $uuid = null): File {
}

/**
* Return LibreSign file by fileId
* Return LibreSign file by nodeId
*/
public function getByFileId(?int $fileId = null): File {
$exists = array_filter($this->file, fn ($f) => $f->getNodeId() === $fileId || $f->getSignedNodeId() === $fileId);
public function getByFileId(?int $nodeId = null): File {
$exists = array_filter($this->file, fn ($f) => $f->getNodeId() === $nodeId || $f->getSignedNodeId() === $nodeId);
if (!empty($exists)) {
return current($exists);
}
foreach ($this->file as $file) {
if ($file->getNodeId() === $fileId) {
if ($file->getNodeId() === $nodeId) {
return $file;
}
}
Expand All @@ -136,8 +136,8 @@ public function getByFileId(?int $fileId = null): File {
->from($this->getTableName())
->where(
$qb->expr()->orX(
$qb->expr()->eq('node_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('signed_node_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))
$qb->expr()->eq('node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('signed_node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT))
)
);

Expand Down
119 changes: 3 additions & 116 deletions lib/Db/SignRequestMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,10 @@ public function getFilesAssociatedFilesWithMeFormatted(
$currentPageResults = $pagination->getCurrentPageResults();

$data = [];
$fileIds = [];

foreach ($currentPageResults as $row) {
$fileIds[] = $row['id'];
$data[] = $this->formatListRow($row);
}
$signers = $this->getByMultipleFileId($fileIds);
$identifyMethods = $this->getIdentifyMethodsFromSigners($signers);
$visibleElements = $this->getVisibleElementsFromSigners($signers);
$return['data'] = $this->associateAllAndFormat($user, $data, $signers, $identifyMethods, $visibleElements);
$return['data'] = $data;
$return['pagination'] = $pagination;
return $return;
}
Expand All @@ -379,7 +373,7 @@ public function getFilesAssociatedFilesWithMeFormatted(
* @param array<SignRequest> $signRequests
* @return FileElement[][]
*/
private function getVisibleElementsFromSigners(array $signRequests): array {
public function getVisibleElementsFromSigners(array $signRequests): array {
$signRequestIds = array_map(function (SignRequest $signRequest): int {
return $signRequest->getId();
}, $signRequests);
Expand Down Expand Up @@ -408,7 +402,7 @@ private function getVisibleElementsFromSigners(array $signRequests): array {
* @param array<SignRequest> $signRequests
* @return array<array-key, array<array-key, \OCP\AppFramework\Db\Entity&\OCA\Libresign\Db\IdentifyMethod>>
*/
private function getIdentifyMethodsFromSigners(array $signRequests): array {
public function getIdentifyMethodsFromSigners(array $signRequests): array {
$signRequestIds = array_map(function (SignRequest $signRequest): int {
return $signRequest->getId();
}, $signRequests);
Expand Down Expand Up @@ -531,113 +525,6 @@ private function getFilesAssociatedFilesWithMeStmt(string $userId, ?string $emai
return $pagination;
}

/**
* @param IUser $userId
* @param array $files
* @param array<SignRequest> $signers
* @param array<array-key, array<array-key, \OCP\AppFramework\Db\Entity&\OCA\Libresign\Db\IdentifyMethod>> $identifyMethods
* @param SignRequest[][]
*/
private function associateAllAndFormat(IUser $user, array $files, array $signers, array $identifyMethods, array $visibleElements): array {
foreach ($files as $key => $file) {
$totalSigned = 0;
foreach ($signers as $signerKey => $signer) {
if ($signer->getFileId() === $file['id']) {
/** @var array<IdentifyMethod> */
$identifyMethodsOfSigner = $identifyMethods[$signer->getId()] ?? [];
$data = [
'email' => array_reduce($identifyMethodsOfSigner, function (string $carry, IdentifyMethod $identifyMethod): string {
if ($identifyMethod->getIdentifierKey() === IdentifyMethodService::IDENTIFY_EMAIL) {
return $identifyMethod->getIdentifierValue();
}
return $carry;
}, ''),
'description' => $signer->getDescription(),
'displayName' =>
array_reduce($identifyMethodsOfSigner, function (string $carry, IdentifyMethod $identifyMethod): string {
if (!$carry && $identifyMethod->getMandatory()) {
return $identifyMethod->getIdentifierValue();
}
return $carry;
}, $signer->getDisplayName()),
'request_sign_date' => (new \DateTime())
->setTimestamp($signer->getCreatedAt())
->format('Y-m-d H:i:s'),
'signed' => null,
'signRequestId' => $signer->getId(),
'me' => array_reduce($identifyMethodsOfSigner, function (bool $carry, IdentifyMethod $identifyMethod) use ($user): bool {
if ($identifyMethod->getIdentifierKey() === IdentifyMethodService::IDENTIFY_ACCOUNT) {
if ($user->getUID() === $identifyMethod->getIdentifierValue()) {
return true;
}
} elseif ($identifyMethod->getIdentifierKey() === IdentifyMethodService::IDENTIFY_EMAIL) {
if (!$user->getEMailAddress()) {
return false;
}
if ($user->getEMailAddress() === $identifyMethod->getIdentifierValue()) {
return true;
}
}
return $carry;
}, false),
'visibleElements' => array_map(function (FileElement $visibleElement) use ($file) {
$element = [
'elementId' => $visibleElement->getId(),
'signRequestId' => $visibleElement->getSignRequestId(),
'type' => $visibleElement->getType(),
'coordinates' => [
'page' => $visibleElement->getPage(),
'urx' => $visibleElement->getUrx(),
'ury' => $visibleElement->getUry(),
'llx' => $visibleElement->getLlx(),
'lly' => $visibleElement->getLly()
]
];
$metadata = json_decode($file['metadata'], true);
$dimension = $metadata['d'][$element['coordinates']['page'] - 1];

$element['coordinates']['left'] = $element['coordinates']['llx'];
$element['coordinates']['height'] = abs($element['coordinates']['ury'] - $element['coordinates']['lly']);
$element['coordinates']['top'] = $dimension['h'] - $element['coordinates']['ury'];
$element['coordinates']['width'] = $element['coordinates']['urx'] - $element['coordinates']['llx'];

return $element;
}, $visibleElements[$signer->getId()] ?? []),
'identifyMethods' => array_map(function (IdentifyMethod $identifyMethod) use ($signer): array {
return [
'method' => $identifyMethod->getIdentifierKey(),
'value' => $identifyMethod->getIdentifierValue(),
'mandatory' => $identifyMethod->getMandatory(),
];
}, array_values($identifyMethodsOfSigner)),
];

if ($data['me']) {
$data['sign_uuid'] = $signer->getUuid();
$files[$key]['url'] = $this->urlGenerator->linkToRoute('libresign.page.getPdfFile', ['uuid' => $signer->getuuid()]);
}

if ($signer->getSigned()) {
$data['signed'] = $this->dateTimeFormatter->formatDateTime($signer->getSigned());
$totalSigned++;
}
ksort($data);
$files[$key]['signers'][] = $data;
unset($signers[$signerKey]);
}
}
if (empty($files[$key]['signers'])) {
$files[$key]['signers'] = [];
$files[$key]['statusText'] = $this->l10n->t('no signers');
} else {
$files[$key]['statusText'] = $this->fileMapper->getTextOfStatus((int) $files[$key]['status']);
}
unset($files[$key]['id']);
ksort($files[$key]);
}
return $files;
}

private function formatListRow(array $row): array {
$row['id'] = (int) $row['id'];
$row['status'] = (int) $row['status'];
Expand Down
8 changes: 4 additions & 4 deletions lib/Helper/ValidateHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,15 +295,15 @@ public function validateVisibleElementsRelation(array $list, SignRequest $signRe
if (!array_key_exists('documentElementId', $elements)) {
throw new LibresignException($this->l10n->t('Field %s not found', ['documentElementId']));
}
if (!array_key_exists('profileFileId', $elements)) {
throw new LibresignException($this->l10n->t('Field %s not found', ['profileFileId']));
if (!array_key_exists('profileNodeId', $elements)) {
throw new LibresignException($this->l10n->t('Field %s not found', ['profileNodeId']));
}
$this->validateSignerIsOwnerOfPdfVisibleElement($elements['documentElementId'], $signRequest);
if ($user instanceof IUser) {
try {
$this->userElementMapper->findOne(['file_id' => $elements['profileFileId'], 'user_id' => $user->getUID()]);
$this->userElementMapper->findOne(['file_id' => $elements['profileNodeId'], 'user_id' => $user->getUID()]);
} catch (\Throwable $th) {
throw new LibresignException($this->l10n->t('Field %s does not belong to user', $elements['profileFileId']));
throw new LibresignException($this->l10n->t('Field %s does not belong to user', $elements['profileNodeId']));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Middleware/GlobalInjectionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GlobalInjectionMiddleware extends Middleware {
public function afterController(Controller $controller, string $methodName, Response $response) {
if ($controller instanceof ViewController) {
$policy = new ContentSecurityPolicy();
$policy->allowEvalScript(true);
$policy->addAllowedFrameDomain("'self'");
$response->setContentSecurityPolicy($policy);
}
return $response;
Expand Down
29 changes: 24 additions & 5 deletions lib/Service/AccountService.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Http\Client\IClientService;
Expand Down Expand Up @@ -405,9 +406,12 @@ private function saveFileOfVisibleElementUsingSession(array $data, string $sessi
$rootSignatureFolder = $this->folderService->getFolder();
$folderName = $sessionId;
if ($rootSignatureFolder->nodeExists($folderName)) {
throw new \Exception($this->l10n->t('File already exists'));
/** @var Folder $folderToFile */
$folderToFile = $rootSignatureFolder->get($folderName);
} else {
/** @var Folder $folderToFile */
$folderToFile = $rootSignatureFolder->newFolder($folderName);
}
$folderToFile = $rootSignatureFolder->newFolder($folderName);
$filename = implode(
'_',
[
Expand Down Expand Up @@ -461,9 +465,24 @@ private function getFileRaw(array $data) {
return $content;
}

public function deleteSignatureElement(string $userId, int $elementId): void {
$element = $this->userElementMapper->findOne(['id' => $elementId, 'user_id' => $userId]);
$this->userElementMapper->delete($element);
public function deleteSignatureElement(?IUser $user, string $sessionId, int $nodeId): void {
if ($user instanceof IUser) {
$element = $this->userElementMapper->findOne([
'file_id' => $nodeId,
'user_id' => $user->getUID(),
]);
$this->userElementMapper->delete($element);
$file = $this->root->getById($element->getFileId());
if ($file) {
current($file)->delete();
}
} else {
$rootSignatureFolder = $this->folderService->getFolder();
$folderName = $sessionId;
if ($rootSignatureFolder->nodeExists($folderName)) {
$rootSignatureFolder->delete($folderName);
}
}
}

/**
Expand Down
Loading