• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 21008830184

14 Jan 2026 08:29PM UTC coverage: 44.286%. First build
21008830184

Pull #6436

github

web-flow
Merge 4bec3de23 into 9bd4c65c5
Pull Request #6436: feat: async parallel signing

390 of 923 new or added lines in 41 files covered. (42.25%)

7007 of 15822 relevant lines covered (44.29%)

4.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.38
/lib/Handler/FooterHandler.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Handler;
10

11
use OCA\Libresign\AppInfo\Application;
12
use OCA\Libresign\Db\File as FileEntity;
13
use OCA\Libresign\Exception\LibresignException;
14
use OCA\Libresign\Service\File\Pdf\PdfMetadataExtractor;
15
use OCA\Libresign\Vendor\Endroid\QrCode\Color\Color;
16
use OCA\Libresign\Vendor\Endroid\QrCode\Encoding\Encoding;
17
use OCA\Libresign\Vendor\Endroid\QrCode\ErrorCorrectionLevel;
18
use OCA\Libresign\Vendor\Endroid\QrCode\QrCode;
19
use OCA\Libresign\Vendor\Endroid\QrCode\RoundBlockSizeMode;
20
use OCA\Libresign\Vendor\Endroid\QrCode\Writer\PngWriter;
21
use OCA\Libresign\Vendor\Mpdf\Mpdf;
22
use OCA\Libresign\Vendor\Twig\Environment;
23
use OCA\Libresign\Vendor\Twig\Error\SyntaxError;
24
use OCA\Libresign\Vendor\Twig\Loader\FilesystemLoader;
25
use OCP\Files\File;
26
use OCP\IAppConfig;
27
use OCP\IL10N;
28
use OCP\ITempManager;
29
use OCP\IURLGenerator;
30
use OCP\L10N\IFactory;
31

32
class FooterHandler {
33
        private QrCode $qrCode;
34
        private const MIN_QRCODE_SIZE = 100;
35
        private const POINT_TO_MILIMETER = 0.3527777778;
36

37
        public function __construct(
38
                private IAppConfig $appConfig,
39
                private PdfMetadataExtractor $pdfMetadataExtractor,
40
                private IURLGenerator $urlGenerator,
41
                private IL10N $l10n,
42
                private IFactory $l10nFactory,
43
                private ITempManager $tempManager,
44
                private TemplateVariables $templateVars,
45
        ) {
46
        }
59✔
47

48
        public function getFooter(array $dimensions): string {
49
                $add_footer = (bool)$this->appConfig->getValueBool(Application::APP_ID, 'add_footer', true);
8✔
50
                if (!$add_footer) {
8✔
51
                        return '';
2✔
52
                }
53

54
                $htmlFooter = $this->getRenderedHtmlFooter();
6✔
55
                foreach ($dimensions as $dimension) {
6✔
56
                        if (!isset($pdf)) {
6✔
57
                                $pdf = new Mpdf([
6✔
58
                                        'tempDir' => $this->tempManager->getTempBaseDir(),
6✔
59
                                        'orientation' => 'P',
6✔
60
                                        'margin_left' => 0,
6✔
61
                                        'margin_right' => 0,
6✔
62
                                        'margin_top' => 0,
6✔
63
                                        'margin_bottom' => 0,
6✔
64
                                        'margin_header' => 0,
6✔
65
                                        'margin_footer' => 0,
6✔
66
                                        'format' => [
6✔
67
                                                $dimension['w'] * self::POINT_TO_MILIMETER,
6✔
68
                                                $dimension['h'] * self::POINT_TO_MILIMETER,
6✔
69
                                        ],
6✔
70
                                ]);
6✔
71
                                $pdf->SetDirectionality($this->templateVars->getDirection());
6✔
72
                        }
73
                        $pdf->AddPage(
6✔
74
                                orientation: 'P',
6✔
75
                                newformat: [
6✔
76
                                        $dimension['w'] * self::POINT_TO_MILIMETER,
6✔
77
                                        $dimension['h'] * self::POINT_TO_MILIMETER,
6✔
78
                                ],
6✔
79
                        );
6✔
80

81
                        $pdf->SetHTMLFooter($htmlFooter);
6✔
82
                }
83

84
                return $pdf->Output('', 'S');
6✔
85
        }
86

87
        public function getMetadata(File $file, FileEntity $fileEntity): array {
88
                $metadata = $fileEntity->getMetadata();
×
89
                if (!is_array($metadata) || !isset($metadata['d'])) {
×
NEW
90
                        $metadata = $this->pdfMetadataExtractor
×
91
                                ->setFile($file)
×
92
                                ->getPageDimensions();
×
93
                }
94
                return $metadata;
×
95
        }
96

97
        private function getRenderedHtmlFooter(): string {
98
                try {
99
                        $twigEnvironment = new Environment(
6✔
100
                                new FilesystemLoader(),
6✔
101
                        );
6✔
102
                        return $twigEnvironment
6✔
103
                                ->createTemplate($this->getTemplate())
6✔
104
                                ->render($this->prepareTemplateVars());
6✔
105
                } catch (SyntaxError $e) {
×
106
                        throw new LibresignException($e->getMessage());
×
107
                }
108
        }
109

110
        public function setTemplateVar(string $name, mixed $value): self {
111
                $this->templateVars->merge([$name => $value]);
7✔
112
                return $this;
7✔
113
        }
114

115
        private function prepareTemplateVars(): array {
116
                if (!$this->templateVars->getSignedBy()) {
6✔
117
                        $this->templateVars->setSignedBy(
6✔
118
                                $this->appConfig->getValueString(Application::APP_ID, 'footer_signed_by', $this->l10n->t('Digitally signed by LibreSign.'))
6✔
119
                        );
6✔
120
                }
121

122
                if (!$this->templateVars->getDirection()) {
6✔
123
                        $this->templateVars->setDirection(
6✔
124
                                $this->l10nFactory->getLanguageDirection($this->l10n->getLanguageCode())
6✔
125
                        );
6✔
126
                }
127

128
                if (!$this->templateVars->getLinkToSite()) {
6✔
129
                        $this->templateVars->setLinkToSite(
6✔
130
                                $this->appConfig->getValueString(Application::APP_ID, 'footer_link_to_site', 'https://libresign.coop')
6✔
131
                        );
6✔
132
                }
133

134
                if (!$this->templateVars->getValidationSite() && $this->templateVars->getUuid()) {
6✔
135
                        $validationSite = $this->appConfig->getValueString(Application::APP_ID, 'validation_site');
4✔
136
                        if ($validationSite) {
4✔
137
                                $this->templateVars->setValidationSite(
4✔
138
                                        rtrim($validationSite, '/') . '/' . $this->templateVars->getUuid()
4✔
139
                                );
4✔
140
                        } else {
141
                                $this->templateVars->setValidationSite(
×
142
                                        $this->urlGenerator->linkToRouteAbsolute('libresign.page.validationFileWithShortUrl', [
×
143
                                                'uuid' => $this->templateVars->getUuid(),
×
144
                                        ])
×
145
                                );
×
146
                        }
147
                }
148

149
                if (!$this->templateVars->getValidateIn()) {
6✔
150
                        $validateIn = $this->appConfig->getValueString(Application::APP_ID, 'footer_validate_in', 'Validate in %s.');
6✔
151
                        if ($validateIn === 'Validate in %s.') {
6✔
152
                                $this->templateVars->setValidateIn($this->l10n->t('Validate in %s.', ['%s']));
4✔
153
                        } else {
154
                                $this->templateVars->setValidateIn($validateIn);
2✔
155
                        }
156
                }
157

158
                if ($this->appConfig->getValueBool(Application::APP_ID, 'write_qrcode_on_footer', true) && $this->templateVars->getValidationSite()) {
6✔
159
                        $this->templateVars->setQrcode($this->getQrCodeImageBase64($this->templateVars->getValidationSite()));
2✔
160
                }
161

162
                $vars = $this->templateVars->toArray();
6✔
163
                foreach ($vars as $key => $value) {
6✔
164
                        if (is_string($value)) {
6✔
165
                                $vars[$key] = htmlentities($value, ENT_NOQUOTES | ENT_SUBSTITUTE | ENT_HTML401);
6✔
166
                        }
167
                }
168

169
                return $vars;
6✔
170
        }
171

172
        public function getTemplate(): string {
173
                $footerTemplate = $this->appConfig->getValueString(Application::APP_ID, 'footer_template', '');
9✔
174
                if ($footerTemplate) {
9✔
175
                        return $footerTemplate;
7✔
176
                }
177
                return $this->getDefaultTemplate();
2✔
178
        }
179

180
        public function getDefaultTemplate(): string {
181
                return (string)file_get_contents(__DIR__ . '/Templates/footer.twig');
2✔
182
        }
183

184
        private function getQrCodeImageBase64(string $text): string {
185
                $this->qrCode = new QrCode(
2✔
186
                        data: $text,
2✔
187
                        encoding: new Encoding('UTF-8'),
2✔
188
                        errorCorrectionLevel: ErrorCorrectionLevel::Low,
2✔
189
                        size: self::MIN_QRCODE_SIZE,
2✔
190
                        margin: 4,
2✔
191
                        roundBlockSizeMode: RoundBlockSizeMode::Margin,
2✔
192
                        foregroundColor: new Color(0, 0, 0),
2✔
193
                        backgroundColor: new Color(255, 255, 255)
2✔
194
                );
2✔
195
                $writer = new PngWriter();
2✔
196
                $result = $writer->write($this->qrCode);
2✔
197
                $qrcode = base64_encode($result->getString());
2✔
198

199
                $this->templateVars->setQrcodeSize($this->qrCode->getSize() + $this->qrCode->getMargin() * 2);
2✔
200

201
                return $qrcode;
2✔
202
        }
203

204
        public function getTemplateVariablesMetadata(): array {
205
                return $this->templateVars->getVariablesMetadata();
1✔
206
        }
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc