Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cb8217c
fix: use signers in request signature controller
vitormattos Feb 17, 2026
3162333
fix: validate signers payload key in helper
vitormattos Feb 17, 2026
2ceeb9e
fix: migrate request signature service to signers
vitormattos Feb 17, 2026
613274c
fix: use signers in sign request deletion check
vitormattos Feb 17, 2026
c2ff903
refactor: add visible elements normalization service
vitormattos Feb 17, 2026
fde4093
fix: normalize visible elements in request modal
vitormattos Feb 17, 2026
0c6f9df
fix: normalize envelope visible elements in sign pdf
vitormattos Feb 17, 2026
b442a44
fix: normalize signer visible elements in sign sidebar
vitormattos Feb 17, 2026
cf466b6
fix: send signers in signature request payload
vitormattos Feb 17, 2026
8f0d8ef
test: cover nested visible elements payloads
vitormattos Feb 17, 2026
f60b231
test: assert canonical signers payload in files store
vitormattos Feb 17, 2026
932afef
test: add visible elements service unit tests
vitormattos Feb 17, 2026
f8f5a41
test: migrate validate helper fixtures to signers
vitormattos Feb 17, 2026
19259ce
test: migrate request signature service tests to signers
vitormattos Feb 17, 2026
8f56ddb
test: migrate sign file service test payload to signers
vitormattos Feb 17, 2026
1444dee
test: remove abstract identify method unit test
vitormattos Feb 17, 2026
494ed87
test: remove session service unit test
vitormattos Feb 17, 2026
62ef416
docs: regenerate openapi schemas for signers payload
vitormattos Feb 17, 2026
fdae689
fix: cs
vitormattos Feb 17, 2026
e7396e6
fix: adjust files path
vitormattos Feb 17, 2026
f9d560b
fix: replace users by sigrers
vitormattos Feb 17, 2026
bb1091c
fix: add pending file
vitormattos Feb 17, 2026
934ddfa
refactor: improve tests
vitormattos Feb 17, 2026
2fb0d51
fix: replace setTimeout polling with async page await in PdfEditor
vitormattos Feb 17, 2026
914510a
fix: replace users by signers
vitormattos Feb 17, 2026
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
27 changes: 14 additions & 13 deletions lib/Controller/RequestSignatureController.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ public function __construct(
/**
* Request signature
*
* Request that a file be signed by a group of people.
* Each user in the users array can optionally include a 'signing_order' field
* Request that a file be signed by a list of signers.
* Each signer in the signers array can optionally include a 'signingOrder' field
* to control the order of signatures when ordered signing flow is enabled.
* When the created entity is an envelope (`nodeType` = `envelope`),
* the returned `data` includes `filesCount` and `files` as a list of
* envelope child files.
*
* @param LibresignNewSigner[] $users Collection of users who must sign the document. Each user can have: identify, displayName, description, notify, signing_order
* @param LibresignNewSigner[] $signers Collection of signers who must sign the document. Each signer can have: identify, displayName, description, notify, signingOrder
* @param string $name The name of file to sign
* @param LibresignFolderSettings $settings Settings to define how and where the file should be stored
* @param LibresignNewFile $file File object.
Expand All @@ -79,8 +79,8 @@ public function __construct(
#[RequireManager]
#[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/request-signature', requirements: ['apiVersion' => '(v1)'])]
public function request(
array $users,
string $name,
array $signers = [],
string $name = '',
array $settings = [],
array $file = [],
array $files = [],
Expand All @@ -96,7 +96,7 @@ public function request(
$files,
$name,
$settings,
$users,
$signers,
$status,
$callback,
$signatureFlow
Expand Down Expand Up @@ -127,9 +127,9 @@ public function request(
/**
* Updates signatures data
*
* Is necessary to inform the UUID of the file and a list of people
* It is necessary to inform the UUID of the file and a list of signers.
*
* @param LibresignNewSigner[]|null $users Collection of users who must sign the document
* @param LibresignNewSigner[]|null $signers Collection of signers who must sign the document
* @param string|null $uuid UUID of sign request. The signer UUID is what the person receives via email when asked to sign. This is not the file UUID.
* @param LibresignVisibleElement[]|null $visibleElements Visible elements on document
* @param LibresignNewFile|array<empty>|null $file File object.
Expand All @@ -148,7 +148,7 @@ public function request(
#[RequireManager]
#[ApiRoute(verb: 'PATCH', url: '/api/{apiVersion}/request-signature', requirements: ['apiVersion' => '(v1)'])]
public function updateSign(
?array $users = [],
?array $signers = [],
?string $uuid = null,
?array $visibleElements = null,
?array $file = [],
Expand All @@ -160,6 +160,7 @@ public function updateSign(
): DataResponse {
try {
$user = $this->userSession->getUser();
$signers = is_array($signers) ? $signers : [];

if (empty($uuid)) {
return $this->createSignatureRequest(
Expand All @@ -168,7 +169,7 @@ public function updateSign(
$files,
$name,
$settings,
$users,
$signers,
$status,
null,
$signatureFlow,
Expand All @@ -179,7 +180,7 @@ public function updateSign(
$data = [
'uuid' => $uuid,
'file' => $file,
'users' => $users,
'signers' => $signers,
'userManager' => $user,
'status' => $status,
'visibleElements' => $visibleElements,
Expand Down Expand Up @@ -221,7 +222,7 @@ private function createSignatureRequest(
array $files,
string $name,
array $settings,
array $users,
array $signers,
?int $status,
?string $callback,
?string $signatureFlow,
Expand All @@ -238,7 +239,7 @@ private function createSignatureRequest(
$data = [
'file' => $file,
'name' => $name,
'users' => $users,
'signers' => $signers,
'status' => $status,
'callback' => $callback,
'userManager' => $user,
Expand Down
6 changes: 3 additions & 3 deletions lib/Helper/ValidateHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,19 +535,19 @@ public function validateFileStatus(array $data): void {
}

public function validateIdentifySigners(array $data): void {
if (empty($data['users'])) {
if (empty($data['signers'])) {
return;
}

$this->validateSignersDataStructure($data);

foreach ($data['users'] as $signer) {
foreach ($data['signers'] as $signer) {
$this->validateSignerData($signer);
}
}

private function validateSignersDataStructure(array $data): void {
if (empty($data) || !array_key_exists('users', $data) || !is_array($data['users']) || empty($data['users'])) {
if (empty($data) || !array_key_exists('signers', $data) || !is_array($data['signers']) || empty($data['signers'])) {
throw new LibresignException($this->l10n->t('No signers'));
}
}
Expand Down
70 changes: 35 additions & 35 deletions lib/Service/RequestSignatureService.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function saveFiles(array $data): array {
'name' => $data['name'],
'userManager' => $data['userManager'],
'settings' => $data['settings'],
'users' => $data['users'] ?? [],
'signers' => $data['signers'] ?? [],
'status' => $data['status'] ?? FileStatus::DRAFT->value,
'visibleElements' => $data['visibleElements'] ?? [],
'signatureFlow' => $data['signatureFlow'] ?? null,
Expand All @@ -136,15 +136,15 @@ public function save(array $data): FileEntity {
}

private function propagateSignersToChildren(FileEntity $envelope, array $data): void {
if ($envelope->getNodeType() !== 'envelope' || empty($data['users'])) {
if ($envelope->getNodeType() !== 'envelope' || empty($data['signers'])) {
return;
}

$children = $this->fileMapper->getChildrenFiles($envelope->getId());

$dataWithoutNotification = $data;
foreach ($dataWithoutNotification['users'] as &$user) {
$user['notify'] = 0;
foreach ($dataWithoutNotification['signers'] as &$signer) {
$signer['notify'] = 0;
}

foreach ($children as $child) {
Expand Down Expand Up @@ -189,7 +189,7 @@ public function saveEnvelope(array $data): array {
$files[] = $fileEntity;
}

if (!empty($data['users'])) {
if (!empty($data['signers'])) {
$this->sequentialSigningService->setFile($envelope);
$this->associateToSigners($data, $envelope);
$this->propagateSignersToChildren($envelope, $data);
Expand Down Expand Up @@ -447,7 +447,7 @@ private function removeExtensionFromName(string $name, array $metadata): string
return $result ?? $name;
}

private function deleteIdentifyMethodIfNotExits(array $users, FileEntity $file): void {
private function deleteIdentifyMethodIfNotExits(array $signers, FileEntity $file): void {
$signRequests = $this->signRequestMapper->getByFileId($file->getId());
foreach ($signRequests as $key => $signRequest) {
$identifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($signRequest->getId());
Expand All @@ -458,7 +458,7 @@ private function deleteIdentifyMethodIfNotExits(array $users, FileEntity $file):
foreach ($identifyMethods as $methodName => $list) {
foreach ($list as $method) {
$exists[$key]['identify'][$methodName] = $method->getEntity()->getIdentifierValue();
if (!$this->identifyMethodExists($users, $method)) {
if (!$this->identifyMethodExists($signers, $method)) {
$this->unassociateToUser($file->getId(), $signRequest->getId());
continue 3;
}
Expand All @@ -467,10 +467,10 @@ private function deleteIdentifyMethodIfNotExits(array $users, FileEntity $file):
}
}

private function identifyMethodExists(array $users, IIdentifyMethod $identifyMethod): bool {
foreach ($users as $user) {
if (!empty($user['identifyMethods'])) {
foreach ($user['identifyMethods'] as $data) {
private function identifyMethodExists(array $signers, IIdentifyMethod $identifyMethod): bool {
foreach ($signers as $signer) {
if (!empty($signer['identifyMethods'])) {
foreach ($signer['identifyMethods'] as $data) {
if ($identifyMethod->getEntity()->getIdentifierKey() !== $data['method']) {
continue;
}
Expand All @@ -479,7 +479,7 @@ private function identifyMethodExists(array $users, IIdentifyMethod $identifyMet
}
}
} else {
foreach ($user['identify'] as $method => $value) {
foreach ($signer['identify'] as $method => $value) {
if ($identifyMethod->getEntity()->getIdentifierKey() !== $method) {
continue;
}
Expand All @@ -499,27 +499,27 @@ private function identifyMethodExists(array $users, IIdentifyMethod $identifyMet
*/
private function associateToSigners(array $data, FileEntity $file): array {
$return = [];
if (!empty($data['users'])) {
$this->deleteIdentifyMethodIfNotExits($data['users'], $file);
if (!empty($data['signers'])) {
$this->deleteIdentifyMethodIfNotExits($data['signers'], $file);
$this->identifyMethod->clearCache();

$this->sequentialSigningService->resetOrderCounter();
$fileStatus = $data['status'] ?? null;

foreach ($data['users'] as $user) {
$userProvidedOrder = isset($user['signingOrder']) ? (int)$user['signingOrder'] : null;
foreach ($data['signers'] as $signer) {
$userProvidedOrder = isset($signer['signingOrder']) ? (int)$signer['signingOrder'] : null;
$signingOrder = $this->sequentialSigningService->determineSigningOrder($userProvidedOrder);
$signerStatus = $user['status'] ?? null;
$shouldNotify = !isset($user['notify']) || $user['notify'] !== 0;
$signerStatus = $signer['status'] ?? null;
$shouldNotify = !isset($signer['notify']) || $signer['notify'] !== 0;

if (isset($user['identifyMethods'])) {
foreach ($user['identifyMethods'] as $identifyMethod) {
if (isset($signer['identifyMethods'])) {
foreach ($signer['identifyMethods'] as $identifyMethod) {
$return[] = $this->signRequestService->createOrUpdateSignRequest(
identifyMethods: [
$identifyMethod['method'] => $identifyMethod['value'],
],
displayName: $user['displayName'] ?? '',
description: $user['description'] ?? '',
displayName: $signer['displayName'] ?? '',
description: $signer['description'] ?? '',
notify: $shouldNotify,
fileId: $file->getId(),
signingOrder: $signingOrder,
Expand All @@ -529,9 +529,9 @@ private function associateToSigners(array $data, FileEntity $file): array {
}
} else {
$return[] = $this->signRequestService->createOrUpdateSignRequest(
identifyMethods: $user['identify'],
displayName: $user['displayName'] ?? '',
description: $user['description'] ?? '',
identifyMethods: $signer['identify'],
displayName: $signer['displayName'] ?? '',
description: $signer['description'] ?? '',
notify: $shouldNotify,
fileId: $file->getId(),
signingOrder: $signingOrder,
Expand Down Expand Up @@ -574,7 +574,7 @@ private function saveVisibleElements(array $data, FileEntity $file): array {

public function validateNewRequestToFile(array $data): void {
$this->validateNewFile($data);
$this->validateUsers($data);
$this->validateSigners($data);
$this->validateHelper->validateFileStatus($data);
}

Expand All @@ -585,22 +585,22 @@ public function validateNewFile(array $data): void {
$this->validateHelper->validateNewFile($data);
}

public function validateUsers(array $data): void {
if (empty($data['users'])) {
public function validateSigners(array $data): void {
if (empty($data['signers'])) {
if (($data['status'] ?? FileStatus::ABLE_TO_SIGN->value) === FileStatus::DRAFT->value) {
return;
}
throw new \Exception($this->l10n->t('Empty users list'));
throw new \Exception($this->l10n->t('Empty signers list'));
}
if (!is_array($data['users'])) {
// TRANSLATION This message will be displayed when the request to API with the key users has a value that is not an array
throw new \Exception($this->l10n->t('User list needs to be an array'));
if (!is_array($data['signers'])) {
// TRANSLATION This message will be displayed when the request to API with the key signers has a value that is not an array
throw new \Exception($this->l10n->t('Signers list needs to be an array'));
}
foreach ($data['users'] as $user) {
if (!array_key_exists('identify', $user)) {
foreach ($data['signers'] as $signer) {
if (!array_key_exists('identify', $signer)) {
throw new \Exception('Identify key not found');
}
$this->identifyMethod->setAllEntityData($user);
$this->identifyMethod->setAllEntityData($signer);
}
}

Expand Down
8 changes: 4 additions & 4 deletions lib/Service/SignFileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,16 @@ public function canDeleteRequestSignature(array $data): void {
if ($signed) {
throw new \Exception($this->l10n->t('Document already signed'));
}
array_walk($data['users'], function ($user) use ($signatures): void {
$exists = array_filter($signatures, function (SignRequestEntity $signRequest) use ($user) {
array_walk($data['signers'], function ($signer) use ($signatures): void {
$exists = array_filter($signatures, function (SignRequestEntity $signRequest) use ($signer) {
$identifyMethod = $this->identifyMethodService->getIdentifiedMethod($signRequest->getId());
if ($identifyMethod->getName() === 'email') {
return $identifyMethod->getEntity()->getIdentifierValue() === $user['email'];
return $identifyMethod->getEntity()->getIdentifierValue() === $signer['email'];
}
return false;
});
if (!$exists) {
throw new \Exception($this->l10n->t('No signature was requested to %s', $user['email']));
throw new \Exception($this->l10n->t('No signature was requested to %s', $signer['email']));
}
});
}
Expand Down
20 changes: 9 additions & 11 deletions openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -7628,7 +7628,7 @@
"post": {
"operationId": "request_signature-request",
"summary": "Request signature",
"description": "Request that a file be signed by a group of people. Each user in the users array can optionally include a 'signing_order' field to control the order of signatures when ordered signing flow is enabled. When the created entity is an envelope (`nodeType` = `envelope`), the returned `data` includes `filesCount` and `files` as a list of envelope child files.",
"description": "Request that a file be signed by a list of signers. Each signer in the signers array can optionally include a 'signingOrder' field to control the order of signatures when ordered signing flow is enabled. When the created entity is an envelope (`nodeType` = `envelope`), the returned `data` includes `filesCount` and `files` as a list of envelope child files.",
"tags": [
"request_signature"
],
Expand All @@ -7641,25 +7641,23 @@
}
],
"requestBody": {
"required": true,
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"users",
"name"
],
"properties": {
"users": {
"signers": {
"type": "array",
"description": "Collection of users who must sign the document. Each user can have: identify, displayName, description, notify, signing_order",
"default": [],
"description": "Collection of signers who must sign the document. Each signer can have: identify, displayName, description, notify, signingOrder",
"items": {
"$ref": "#/components/schemas/NewSigner"
}
},
"name": {
"type": "string",
"default": "",
"description": "The name of file to sign"
},
"settings": {
Expand Down Expand Up @@ -7818,7 +7816,7 @@
"patch": {
"operationId": "request_signature-update-sign",
"summary": "Updates signatures data",
"description": "Is necessary to inform the UUID of the file and a list of people",
"description": "It is necessary to inform the UUID of the file and a list of signers.",
"tags": [
"request_signature"
],
Expand All @@ -7837,11 +7835,11 @@
"schema": {
"type": "object",
"properties": {
"users": {
"signers": {
"type": "array",
"nullable": true,
"default": [],
"description": "Collection of users who must sign the document",
"description": "Collection of signers who must sign the document",
"items": {
"$ref": "#/components/schemas/NewSigner"
}
Expand Down
Loading
Loading