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

LibreSign / libresign / 19928466437

04 Dec 2025 12:09PM UTC coverage: 41.374%. First build
19928466437

Pull #5988

github

web-flow
Merge 63ce2c8de into aa586ed2c
Pull Request #5988: refactor: footer handler reduce coupling

1 of 5 new or added lines in 2 files covered. (20.0%)

5132 of 12404 relevant lines covered (41.37%)

4.15 hits per line

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

83.53
/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\PdfParserService;
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 FileEntity $fileEntity;
35
        private const MIN_QRCODE_SIZE = 100;
36
        private const POINT_TO_MILIMETER = 0.3527777778;
37
        private array $templateVars = [];
38

39
        public function __construct(
40
                private IAppConfig $appConfig,
41
                private PdfParserService $pdfParserService,
42
                private IURLGenerator $urlGenerator,
43
                private IL10N $l10n,
44
                private IFactory $l10nFactory,
45
                private ITempManager $tempManager,
46
        ) {
47
        }
53✔
48

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

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

83
                        $pdf->SetHTMLFooter($htmlFooter);
4✔
84
                }
85

86
                return $pdf->Output('', 'S');
4✔
87
        }
88

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

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

112
        public function setTemplateVar(string $name, mixed $value): self {
113
                $this->templateVars[$name] = $value;
×
114
                return $this;
×
115
        }
116

117
        private function getTemplateVars(): array {
118
                $this->templateVars['signedBy'] = $this->appConfig->getValueString(Application::APP_ID, 'footer_signed_by', $this->l10n->t('Digitally signed by LibreSign.'));
4✔
119

120
                $this->templateVars['direction'] = $this->l10nFactory->getLanguageDirection($this->l10n->getLanguageCode());
4✔
121

122
                $this->templateVars['linkToSite'] = $this->appConfig->getValueString(Application::APP_ID, 'footer_link_to_site', 'https://libresign.coop');
4✔
123

124
                $this->templateVars['validationSite'] = $this->appConfig->getValueString(Application::APP_ID, 'validation_site');
4✔
125
                if ($this->templateVars['validationSite']) {
4✔
126
                        $this->templateVars['validationSite'] = rtrim($this->templateVars['validationSite'], '/') . '/' . $this->fileEntity->getUuid();
4✔
127
                } else {
128
                        $this->templateVars['validationSite'] = $this->urlGenerator->linkToRouteAbsolute('libresign.page.validationFileWithShortUrl', [
×
129
                                'uuid' => $this->fileEntity->getUuid(),
×
130
                        ]);
×
131
                }
132

133
                $this->templateVars['validateIn'] = $this->appConfig->getValueString(Application::APP_ID, 'footer_validate_in', 'Validate in %s.');
4✔
134
                if ($this->templateVars['validateIn'] === 'Validate in %s.') {
4✔
135
                        $this->templateVars['validateIn'] = $this->l10n->t('Validate in %s.', ['%s']);
2✔
136
                }
137

138
                foreach ($this->templateVars as $key => $value) {
4✔
139
                        if (is_string($value)) {
4✔
140
                                $this->templateVars[$key] = htmlentities($value, ENT_NOQUOTES | ENT_SUBSTITUTE | ENT_HTML401);
4✔
141
                        }
142
                }
143

144
                if ($this->appConfig->getValueBool(Application::APP_ID, 'write_qrcode_on_footer', true)) {
4✔
145
                        $this->templateVars['qrcode'] = $this->getQrCodeImageBase64($this->templateVars['validationSite']);
1✔
146
                }
147

148
                return $this->templateVars;
4✔
149
        }
150

151
        private function getTemplate(): string {
152
                $footerTemplate = $this->appConfig->getValueString(Application::APP_ID, 'footer_template', '');
4✔
153
                if ($footerTemplate) {
4✔
154
                        return $footerTemplate;
4✔
155
                }
156
                return (string)file_get_contents(__DIR__ . '/Templates/footer.twig');
×
157
        }
158

159
        private function getQrCodeImageBase64(string $text): string {
160
                $this->qrCode = new QrCode(
1✔
161
                        data: $text,
1✔
162
                        encoding: new Encoding('UTF-8'),
1✔
163
                        errorCorrectionLevel: ErrorCorrectionLevel::Low,
1✔
164
                        size: self::MIN_QRCODE_SIZE,
1✔
165
                        margin: 4,
1✔
166
                        roundBlockSizeMode: RoundBlockSizeMode::Margin,
1✔
167
                        foregroundColor: new Color(0, 0, 0),
1✔
168
                        backgroundColor: new Color(255, 255, 255)
1✔
169
                );
1✔
170
                $writer = new PngWriter();
1✔
171
                $result = $writer->write($this->qrCode);
1✔
172
                $qrcode = base64_encode($result->getString());
1✔
173

174
                $this->templateVars['qrcodeSize'] = $this->qrCode->getSize() + $this->qrCode->getMargin() * 2;
1✔
175

176
                return $qrcode;
1✔
177
        }
178
}
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