Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
94.12% |
32 / 34 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
| GatewayNotificationComposer | |
94.12% |
32 / 34 |
|
83.33% |
5 / 6 |
11.02 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| compose | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| composePayload | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
| render | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| signRequest | |
87.50% |
14 / 16 |
|
0.00% |
0 / 1 |
5.05 | |||
| signedFile | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | /** |
| 5 | * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors |
| 6 | * SPDX-License-Identifier: AGPL-3.0-or-later |
| 7 | */ |
| 8 | |
| 9 | namespace OCA\Libresign\Service\Notification; |
| 10 | |
| 11 | use OCA\Libresign\Db\File; |
| 12 | use OCA\Libresign\Db\SignRequest; |
| 13 | use OCP\IL10N; |
| 14 | use OCP\IURLGenerator; |
| 15 | |
| 16 | /** Builds fully localized notification content before a transport submission is claimed. */ |
| 17 | final class GatewayNotificationComposer { |
| 18 | public function __construct( |
| 19 | private NotificationHistoryService $history, |
| 20 | private IL10N $l10n, |
| 21 | private IURLGenerator $urlGenerator, |
| 22 | private WhatsAppBusinessTemplateOptions $templates, |
| 23 | ) { |
| 24 | } |
| 25 | |
| 26 | public function compose(string $eventType, SignRequest $request, File $file, string $method): string { |
| 27 | return $this->render($eventType, $request, $file, $method)[0]; |
| 28 | } |
| 29 | |
| 30 | public function composePayload(string $eventType, SignRequest $request, File $file, string $method, string $gatewayName): GatewayPayload { |
| 31 | [$message, $text, $url] = $this->render($eventType, $request, $file, $method); |
| 32 | $extra = $gatewayName === 'whatsappbusiness' |
| 33 | ? $this->templates->forNotification($text, $url) |
| 34 | : []; |
| 35 | return new GatewayPayload($gatewayName, $message, $extra); |
| 36 | } |
| 37 | |
| 38 | /** @return array{string, string, string} Full message, independent template text, action URL. */ |
| 39 | private function render(string $eventType, SignRequest $request, File $file, string $method): array { |
| 40 | return match ($eventType) { |
| 41 | 'sign_request' => $this->signRequest($request, $file, $method), |
| 42 | 'file_signed' => $this->signedFile($request, $file), |
| 43 | default => throw new \InvalidArgumentException('Unsupported gateway notification event'), |
| 44 | }; |
| 45 | } |
| 46 | |
| 47 | /** @return array{string, string, string} */ |
| 48 | private function signRequest(SignRequest $request, File $file, string $method): array { |
| 49 | $url = $this->urlGenerator->linkToRouteAbsolute('libresign.page.sign', ['uuid' => $request->getUuid()]); |
| 50 | $description = $request->getDescription(); |
| 51 | $hasDescription = is_string($description) && trim($description) !== ''; |
| 52 | $name = $file->getName(); |
| 53 | |
| 54 | if ($this->history->isFirst($request, $method)) { |
| 55 | if ($hasDescription) { |
| 56 | // TRANSLATORS Initial signature invitation with a sender's description. |
| 57 | // %1$s is that description, %2$s is the document name, and %3$s is the signing URL. |
| 58 | // Example with "Please review this agreement", "Agreement.pdf", and "https://example.invalid/sign": |
| 59 | // Please review this agreement |
| 60 | // |
| 61 | // There is a document for you to sign. Access the link below: |
| 62 | // Agreement.pdf: https://example.invalid/sign |
| 63 | // Keep the whole message translatable. Preserve the intended paragraph break; placeholders may be reordered for the target language. |
| 64 | $message = $this->l10n->t("%1\$s\n\nThere is a document for you to sign. Access the link below:\n%2\$s: %3\$s", [$description, $name, $url]); |
| 65 | // TRANSLATORS Initial invitation sent through a WhatsApp Business UTILITY template. |
| 66 | // %1$s is the sender's description; %2$s is the document name. The URL is not in this string. |
| 67 | // The translated text has paragraph breaks, but the first WhatsApp template value is normalized to one line. |
| 68 | // Example values sent to the approved template: |
| 69 | // {{1}} = Please review this agreement There is a document for you to sign. Access the link below: Agreement.pdf: |
| 70 | // {{2}} = https://example.invalid/sign |
| 71 | // Do not add the URL to this translated text or alter the positional template variables. |
| 72 | $text = $this->l10n->t("%1\$s\n\nThere is a document for you to sign. Access the link below:\n%2\$s:", [$description, $name]); |
| 73 | } else { |
| 74 | // TRANSLATORS Initial signature invitation without a sender's description. |
| 75 | // %1$s is the document name; %2$s is the signing URL. |
| 76 | // Example with "Agreement.pdf" and "https://example.invalid/sign": |
| 77 | // There is a document for you to sign. Access the link below: |
| 78 | // Agreement.pdf: https://example.invalid/sign |
| 79 | // The entire message is translatable; placeholders may be reordered for the target language. |
| 80 | $message = $this->l10n->t("There is a document for you to sign. Access the link below:\n%1\$s: %2\$s", [$name, $url]); |
| 81 | // TRANSLATORS Initial invitation without a description, for a WhatsApp Business UTILITY template. |
| 82 | // %1$s is the document name. The signing URL is a separate template variable, not part of this text. |
| 83 | // Example values sent to the approved template (the first one is a single line): |
| 84 | // {{1}} = There is a document for you to sign. Access the link below: Agreement.pdf: |
| 85 | // {{2}} = https://example.invalid/sign |
| 86 | // Translate the whole text while keeping the URL out of the first template value. |
| 87 | $text = $this->l10n->t("There is a document for you to sign. Access the link below:\n%1\$s:", [$name]); |
| 88 | } |
| 89 | } elseif ($hasDescription) { |
| 90 | // TRANSLATORS Updated signature invitation with a sender's description. |
| 91 | // %1$s is the description, %2$s is the document name, and %3$s is the signing URL. |
| 92 | // Example with "Please check the new version", "Agreement.pdf", and "https://example.invalid/sign": |
| 93 | // Please check the new version |
| 94 | // |
| 95 | // Changes have been made in a file that you have to sign. Access the link below: |
| 96 | // Agreement.pdf: https://example.invalid/sign |
| 97 | // Keep the paragraph break. The placeholders can be reordered to suit the language. |
| 98 | $message = $this->l10n->t("%1\$s\n\nChanges have been made in a file that you have to sign. Access the link below:\n%2\$s: %3\$s", [$description, $name, $url]); |
| 99 | // TRANSLATORS Updated invitation with a description, for a WhatsApp Business UTILITY template. |
| 100 | // %1$s is the sender's description; %2$s is the document name. The URL is supplied separately. |
| 101 | // Example values sent to the template after the translated text is normalized to one line: |
| 102 | // {{1}} = Please check the new version Changes have been made in a file that you have to sign. Access the link below: Agreement.pdf: |
| 103 | // {{2}} = https://example.invalid/sign |
| 104 | // Do not insert the separate URL into this translated text. |
| 105 | $text = $this->l10n->t("%1\$s\n\nChanges have been made in a file that you have to sign. Access the link below:\n%2\$s:", [$description, $name]); |
| 106 | } else { |
| 107 | // TRANSLATORS Updated signature invitation without a sender's description. |
| 108 | // %1$s is the document name and %2$s is the signing URL. |
| 109 | // Example with "Agreement.pdf" and "https://example.invalid/sign": |
| 110 | // Changes have been made in a file that you have to sign. Access the link below: |
| 111 | // Agreement.pdf: https://example.invalid/sign |
| 112 | // The full notification can be rearranged for the target language. |
| 113 | $message = $this->l10n->t("Changes have been made in a file that you have to sign. Access the link below:\n%1\$s: %2\$s", [$name, $url]); |
| 114 | // TRANSLATORS Updated invitation without a description, for a WhatsApp Business UTILITY template. |
| 115 | // %1$s is the document name, while the signing URL is supplied as the next template variable. |
| 116 | // Example values sent to the template (the first one is a single line): |
| 117 | // {{1}} = Changes have been made in a file that you have to sign. Access the link below: Agreement.pdf: |
| 118 | // {{2}} = https://example.invalid/sign |
| 119 | // The translated text must not include the separate signing URL. |
| 120 | $text = $this->l10n->t("Changes have been made in a file that you have to sign. Access the link below:\n%1\$s:", [$name]); |
| 121 | } |
| 122 | |
| 123 | return [$message, $text, $url]; |
| 124 | } |
| 125 | |
| 126 | /** @return array{string, string, string} */ |
| 127 | private function signedFile(SignRequest $request, File $file): array { |
| 128 | $url = $this->urlGenerator->linkToRouteAbsolute('libresign.page.validationFilePublic', ['uuid' => $file->getUuid()]); |
| 129 | $name = $file->getName(); |
| 130 | $signer = $request->getDisplayName(); |
| 131 | // TRANSLATORS Signature completion notification. |
| 132 | // %1$s is the signer's display name, %2$s is the document name, and %3$s is the public validation URL. |
| 133 | // Example with "Alex Example", "Agreement.pdf", and "https://example.invalid/validate": |
| 134 | // LibreSign: A file has been signed |
| 135 | // Alex Example signed the document. You can access it using the link below: |
| 136 | // Agreement.pdf: https://example.invalid/validate |
| 137 | // Keep the full notification and its line breaks translatable; the placeholders may be reordered. |
| 138 | $message = $this->l10n->t("LibreSign: A file has been signed\n%1\$s signed the document. You can access it using the link below:\n%2\$s: %3\$s", [$signer, $name, $url]); |
| 139 | // TRANSLATORS Signature completion for a WhatsApp Business UTILITY template. |
| 140 | // %1$s is the signer's display name, and %2$s is the document name. The validation URL is separate. |
| 141 | // Example values sent to the approved template (the first one is a single line): |
| 142 | // {{1}} = LibreSign: A file has been signed Alex Example signed the document. You can access it using the link below: Agreement.pdf: |
| 143 | // {{2}} = https://example.invalid/validate |
| 144 | // Do not include the separate validation URL in the translated text. |
| 145 | $text = $this->l10n->t("LibreSign: A file has been signed\n%1\$s signed the document. You can access it using the link below:\n%2\$s:", [$signer, $name]); |
| 146 | return [$message, $text, $url]; |
| 147 | } |
| 148 | } |