Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
33.33% covered (danger)
33.33%
13 / 39
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
FooterTemplateController
33.33% covered (danger)
33.33%
13 / 39
25.00% covered (danger)
25.00%
1 / 4
39.63
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFooterTemplate
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 saveFooterTemplate
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
4.13
 previewPdf
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Controller;
10
11use OCA\Libresign\AppInfo\Application;
12use OCA\Libresign\Service\FooterService;
13use OCP\AppFramework\Http;
14use OCP\AppFramework\Http\Attribute\ApiRoute;
15use OCP\AppFramework\Http\Attribute\NoAdminRequired;
16use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
17use OCP\AppFramework\Http\DataDownloadResponse;
18use OCP\AppFramework\Http\DataResponse;
19use OCP\IRequest;
20
21/**
22 * @psalm-import-type LibresignErrorResponse from \OCA\Libresign\ResponseDefinitions
23 * @psalm-import-type LibresignFooterTemplateResponse from \OCA\Libresign\ResponseDefinitions
24 */
25final class FooterTemplateController extends AEnvironmentAwareController {
26    public function __construct(
27        IRequest $request,
28        private FooterService $footerService,
29    ) {
30        parent::__construct(Application::APP_ID, $request);
31    }
32
33    /**
34     * Get footer template
35     *
36     * Returns the current footer template if set, otherwise returns the default template.
37     *
38     * @return DataResponse<Http::STATUS_OK, LibresignFooterTemplateResponse, array{}>|DataResponse<Http::STATUS_FORBIDDEN, LibresignErrorResponse, array{}>
39     *
40     * 200: OK
41     * 403: Forbidden
42     */
43    #[NoAdminRequired]
44    #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/footer-template', requirements: ['apiVersion' => '(v1)'])]
45    public function getFooterTemplate(): DataResponse {
46        if (!$this->footerService->isPreviewAllowed()) {
47            return new DataResponse([
48                'error' => 'Footer template is disabled by policy for the current user.',
49            ], Http::STATUS_FORBIDDEN);
50        }
51
52        $previewSettings = $this->footerService->getPreviewSettings();
53
54        return new DataResponse([
55            'template' => $this->footerService->getTemplate(),
56            'isDefault' => $this->footerService->isDefaultTemplate(),
57            'template_variables' => $this->footerService->getTemplateVariablesMetadata(),
58            'preview_width' => $previewSettings['preview_width'],
59            'preview_height' => $previewSettings['preview_height'],
60            'preview_zoom' => $previewSettings['preview_zoom'],
61        ]);
62    }
63
64    /**
65     * Save footer template and render preview
66     *
67     * Saves the footer template and returns the rendered PDF preview.
68     *
69     * @param string $template The Twig template to save (empty to reset to default)
70     * @param int $width Width of preview in points (default: 595 - A4 width)
71     * @param int $height Height of preview in points (default: 50)
72     * @return DataDownloadResponse<Http::STATUS_OK, 'application/pdf', array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN, LibresignErrorResponse, array{}>
73     *
74     * 200: OK
75     * 400: Bad request
76     * 403: Forbidden
77     */
78    #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/footer-template', requirements: ['apiVersion' => '(v1)'])]
79    public function saveFooterTemplate(string $template = '', int $width = 595, int $height = 50) {
80        if (!$this->footerService->isPreviewAllowed()) {
81            return new DataResponse([
82                'error' => 'Footer template is disabled by policy for the current user.',
83            ], Http::STATUS_FORBIDDEN);
84        }
85
86        try {
87            // Validate and render before persisting: a rejected template must not
88            // replace the global footer used by other signing requests.
89            $pdf = $this->footerService->renderPreviewPdf(
90                $template !== '' ? $template : $this->footerService->getDefaultTemplate(),
91                $width,
92                $height,
93            );
94            $this->footerService->saveTemplate($template, $width, $height);
95
96            return new DataDownloadResponse($pdf, 'footer-preview.pdf', 'application/pdf');
97        } catch (\Exception $e) {
98            return new DataResponse([
99                'error' => $e->getMessage(),
100            ], Http::STATUS_BAD_REQUEST);
101        }
102    }
103
104    /**
105     * Preview footer template as PDF
106     *
107     * @param string $template Template to preview
108     * @param int $width Width of preview in points (default: 595 - A4 width)
109     * @param int $height Height of preview in points (default: 50)
110     * @param ?bool $writeQrcodeOnFooter Whether to force QR code rendering in footer preview (null uses policy)
111     * @return DataDownloadResponse<Http::STATUS_OK, 'application/pdf', array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN, LibresignErrorResponse, array{}>
112     *
113     * 200: OK
114     * 400: Bad request
115     * 403: Forbidden
116     */
117    #[NoAdminRequired]
118    #[NoCSRFRequired]
119    #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/footer-template/preview-pdf', requirements: ['apiVersion' => '(v1)'])]
120    public function previewPdf(string $template = '', int $width = 595, int $height = 50, ?bool $writeQrcodeOnFooter = null) {
121        if (!$this->footerService->isPreviewAllowed()) {
122            return new DataResponse([
123                'error' => 'Footer preview is disabled by policy for the current user.',
124            ], Http::STATUS_FORBIDDEN);
125        }
126
127        try {
128            $pdf = $this->footerService->renderPreviewPdf($template, $width, $height, $writeQrcodeOnFooter);
129            return new DataDownloadResponse($pdf, 'footer-preview.pdf', 'application/pdf');
130        } catch (\Exception $e) {
131            return new DataResponse([
132                'error' => $e->getMessage(),
133            ], Http::STATUS_BAD_REQUEST);
134        }
135    }
136}