Skip to content

Commit 78b98a0

Browse files
feat(setup-check): add PopplerSetupCheck
Adds PopplerSetupCheck to verify pdfsig and pdfinfo utilities (optional). Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Check pdfsig installation and version - [x] Check pdfinfo installation and version - [x] Return info severity when missing (optional check) - [x] Return success when both tools are working - [x] Add unit tests covering all scenarios
1 parent 946097c commit 78b98a0

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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\SetupCheck;
10+
11+
use OCP\IL10N;
12+
use OCP\SetupCheck\ISetupCheck;
13+
use OCP\SetupCheck\SetupResult;
14+
15+
class PopplerSetupCheck implements ISetupCheck {
16+
private IL10N $l10n;
17+
18+
public function __construct(IL10N $l10n) {
19+
$this->l10n = $l10n;
20+
}
21+
22+
public function getName(): string {
23+
return $this->l10n->t('Poppler utils');
24+
}
25+
26+
public function getCategory(): string {
27+
return 'system';
28+
}
29+
30+
public function run(): SetupResult {
31+
$pdfsigOk = $this->checkPdfSig();
32+
$pdfinfoOk = $this->checkPdfinfo();
33+
34+
if ($pdfsigOk && $pdfinfoOk) {
35+
return SetupResult::success(
36+
$this->l10n->t('pdfsig version: %s, pdfinfo version: %s', [$pdfsigOk, $pdfinfoOk])
37+
);
38+
}
39+
40+
$messages = [];
41+
$tip = $this->l10n->t('Install the package poppler-utils on your operating system to enable PDF signature validation and page dimension detection.');
42+
43+
if (!$pdfsigOk) {
44+
$messages[] = $this->l10n->t('pdfsig not installed or not working');
45+
}
46+
if (!$pdfinfoOk) {
47+
$messages[] = $this->l10n->t('pdfinfo not installed or not working');
48+
}
49+
50+
// Since Poppler is optional, return info instead of error
51+
return SetupResult::info(implode('; ', $messages), $tip);
52+
}
53+
54+
private function checkPdfSig(): ?string {
55+
exec('pdfsig -v 2>&1', $output, $retval);
56+
if ($retval !== 0 || empty($output)) {
57+
return null;
58+
}
59+
$full = implode(PHP_EOL, $output);
60+
if (preg_match('/pdfsig version (?<version>.*)/', $full, $matches)) {
61+
return $matches['version'];
62+
}
63+
return null;
64+
}
65+
66+
private function checkPdfinfo(): ?string {
67+
exec('pdfinfo -v 2>&1', $output, $retval);
68+
if ($retval !== 0 || empty($output)) {
69+
return null;
70+
}
71+
$full = implode(PHP_EOL, $output);
72+
if (preg_match('/pdfinfo version (?<version>.*)/', $full, $matches)) {
73+
return $matches['version'];
74+
}
75+
return null;
76+
}
77+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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\Tests\Unit\SetupCheck;
10+
11+
use OCA\Libresign\SetupCheck\PopplerSetupCheck;
12+
use OCA\Libresign\SetupCheck\ExecMock;
13+
use OCP\IL10N;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
17+
class PopplerSetupCheckTest extends TestCase
18+
{
19+
/** @var IL10N|MockObject */
20+
private $l10n;
21+
22+
/** @var PopplerSetupCheck */
23+
private $check;
24+
25+
protected function setUp(): void
26+
{
27+
parent::setUp();
28+
29+
ExecMock::$commands = [];
30+
31+
$this->l10n = $this->createMock(IL10N::class);
32+
$this->l10n->method('t')
33+
->willReturnCallback(fn($text, $params = []) => vsprintf($text, $params));
34+
35+
$this->check = new PopplerSetupCheck($this->l10n);
36+
}
37+
38+
public function testBothToolsInstalled(): void
39+
{
40+
ExecMock::$commands['pdfsig -v 2>&1'] = [
41+
'output' => ['pdfsig version 25.03.0', 'extra line'],
42+
'result_code' => 0,
43+
];
44+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
45+
'output' => ['pdfinfo version 25.03.0'],
46+
'result_code' => 0,
47+
];
48+
49+
$result = $this->check->run();
50+
51+
$this->assertEquals('success', $result->getSeverity());
52+
$this->assertEquals(
53+
'pdfsig version: 25.03.0, pdfinfo version: 25.03.0',
54+
$result->getDescription()
55+
);
56+
}
57+
58+
public function testOnlyPdfsigInstalled(): void
59+
{
60+
ExecMock::$commands['pdfsig -v 2>&1'] = [
61+
'output' => ['pdfsig version 25.03.0'],
62+
'result_code' => 0,
63+
];
64+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
65+
'output' => [],
66+
'result_code' => 127,
67+
];
68+
69+
$result = $this->check->run();
70+
71+
$this->assertEquals('info', $result->getSeverity());
72+
$this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription());
73+
$this->assertStringContainsString('Install the package poppler-utils', $result->getLinkToDoc());
74+
}
75+
76+
public function testNoToolsInstalled(): void
77+
{
78+
ExecMock::$commands['pdfsig -v 2>&1'] = [
79+
'output' => [],
80+
'result_code' => 127,
81+
];
82+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
83+
'output' => [],
84+
'result_code' => 127,
85+
];
86+
87+
$result = $this->check->run();
88+
89+
$this->assertEquals('info', $result->getSeverity());
90+
$this->assertStringContainsString(
91+
'pdfsig not installed or not working; pdfinfo not installed or not working',
92+
$result->getDescription()
93+
);
94+
}
95+
96+
public function testPdfsigVersionParseFails(): void
97+
{
98+
ExecMock::$commands['pdfsig -v 2>&1'] = [
99+
'output' => ['pdfsig version 25.03.0'],
100+
'result_code' => 0,
101+
];
102+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
103+
'output' => ['pdfinfo: unknown option'],
104+
'result_code' => 0,
105+
];
106+
107+
$result = $this->check->run();
108+
109+
$this->assertEquals('info', $result->getSeverity());
110+
$this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription());
111+
$this->assertStringNotContainsString('pdfsig', $result->getDescription());
112+
}
113+
114+
public function testOnlyPdfinfoInstalledButParseFails(): void
115+
{
116+
ExecMock::$commands['pdfsig -v 2>&1'] = [
117+
'output' => [],
118+
'result_code' => 127,
119+
];
120+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
121+
'output' => ['pdfinfo: unknown option'],
122+
'result_code' => 0,
123+
];
124+
125+
$result = $this->check->run();
126+
127+
$this->assertEquals('info', $result->getSeverity());
128+
$this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription());
129+
$this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription());
130+
}
131+
132+
public function testBothToolsParseFails(): void
133+
{
134+
ExecMock::$commands['pdfsig -v 2>&1'] = [
135+
'output' => ['pdfsig: error'],
136+
'result_code' => 0,
137+
];
138+
ExecMock::$commands['pdfinfo -v 2>&1'] = [
139+
'output' => ['pdfinfo: error'],
140+
'result_code' => 0,
141+
];
142+
143+
$result = $this->check->run();
144+
145+
$this->assertEquals('info', $result->getSeverity());
146+
$this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription());
147+
$this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription());
148+
}
149+
150+
public function testGetName(): void
151+
{
152+
$this->l10n->expects($this->once())
153+
->method('t')
154+
->with('Poppler utils')
155+
->willReturn('Poppler utils');
156+
$this->assertEquals('Poppler utils', $this->check->getName());
157+
}
158+
159+
public function testGetCategory(): void
160+
{
161+
$this->assertEquals('system', $this->check->getCategory());
162+
}
163+
}

0 commit comments

Comments
 (0)