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
9a3fb94
fix: use signers in request signature controller
vitormattos Feb 17, 2026
8907bd3
fix: validate signers payload key in helper
vitormattos Feb 17, 2026
9c6e475
fix: migrate request signature service to signers
vitormattos Feb 17, 2026
b7a4577
fix: use signers in sign request deletion check
vitormattos Feb 17, 2026
461c993
refactor: add visible elements normalization service
vitormattos Feb 17, 2026
b84021a
fix: normalize visible elements in request modal
vitormattos Feb 17, 2026
5c3b473
fix: normalize envelope visible elements in sign pdf
vitormattos Feb 17, 2026
3975f3a
fix: normalize signer visible elements in sign sidebar
vitormattos Feb 17, 2026
384bc19
fix: send signers in signature request payload
vitormattos Feb 17, 2026
dffb4d2
test: cover nested visible elements payloads
vitormattos Feb 17, 2026
20edfbf
test: assert canonical signers payload in files store
vitormattos Feb 17, 2026
7fcb8bd
test: add visible elements service unit tests
vitormattos Feb 17, 2026
acfc11b
test: migrate validate helper fixtures to signers
vitormattos Feb 17, 2026
e777fad
test: migrate request signature service tests to signers
vitormattos Feb 17, 2026
cc9a9f1
test: migrate sign file service test payload to signers
vitormattos Feb 17, 2026
cc837ca
test: remove abstract identify method unit test
vitormattos Feb 17, 2026
da9400f
test: remove session service unit test
vitormattos Feb 17, 2026
d704bc2
docs: regenerate openapi schemas for signers payload
vitormattos Feb 17, 2026
9d312f9
fix: cs
vitormattos Feb 17, 2026
a3df3b7
fix: adjust files path
vitormattos Feb 17, 2026
663dac5
fix: replace users by sigrers
vitormattos Feb 17, 2026
0eff9d4
fix: add pending file
vitormattos Feb 17, 2026
1e1041e
refactor: improve tests
vitormattos Feb 17, 2026
a05efec
fix: replace setTimeout polling with async page await in PdfEditor
vitormattos Feb 17, 2026
ecd222e
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