Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.15% covered (success)
96.15%
25 / 26
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PdfController
96.15% covered (success)
96.15%
25 / 26
75.00% covered (warning)
75.00%
3 / 4
10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setFaqFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setServicesFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getById
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * The PDF Controller for the REST API
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2024-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2024-03-02
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Controller\Api;
21
22use OpenApi\Attributes as OA;
23use phpMyFAQ\Controller\AbstractController;
24use phpMyFAQ\Core\Exception;
25use phpMyFAQ\Faq;
26use phpMyFAQ\Filter;
27use phpMyFAQ\Services;
28use phpMyFAQ\User\CurrentUser;
29use stdClass;
30use Symfony\Component\HttpFoundation\JsonResponse;
31use Symfony\Component\HttpFoundation\Request;
32use Symfony\Component\HttpFoundation\Response;
33use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
34use Symfony\Component\Routing\Attribute\Route;
35
36final class PdfController extends AbstractController
37{
38    /** @var null|callable(\phpMyFAQ\Configuration): Faq */
39    private $faqFactory = null;
40    /** @var null|callable(\phpMyFAQ\Configuration): Services */
41    private $servicesFactory = null;
42
43    public function __construct()
44    {
45        parent::__construct();
46
47        if (!$this->isApiEnabled()) {
48            throw new UnauthorizedHttpException(challenge: 'API is not enabled');
49        }
50    }
51
52    /**
53     * @param callable(\phpMyFAQ\Configuration): Faq $faqFactory
54     */
55    public function setFaqFactory(callable $faqFactory): void
56    {
57        $this->faqFactory = $faqFactory;
58    }
59
60    /**
61     * @param callable(\phpMyFAQ\Configuration): Services $servicesFactory
62     */
63    public function setServicesFactory(callable $servicesFactory): void
64    {
65        $this->servicesFactory = $servicesFactory;
66    }
67
68    /**
69     * @throws Exception
70     */
71    #[OA\Get(
72        path: '/api/v4.0/pdf/{categoryId}/{faqId}',
73        operationId: 'getPdfById',
74        description: 'This endpoint returns the URL to the PDF of FAQ for the given FAQ ID and the language provided '
75        . 'by "Accept-Language".',
76        tags: ['Public Endpoints'],
77    )]
78    #[OA\Header(
79        header: 'Accept-Language',
80        description: 'The language code for the FAQ.',
81        schema: new OA\Schema(type: 'string'),
82    )]
83    #[OA\Parameter(
84        name: 'categoryId',
85        description: 'The category ID.',
86        in: 'path',
87        required: true,
88        schema: new OA\Schema(type: 'integer'),
89    )]
90    #[OA\Parameter(
91        name: 'faqId',
92        description: 'The FAQ ID.',
93        in: 'path',
94        required: true,
95        schema: new OA\Schema(type: 'integer'),
96    )]
97    #[OA\Response(
98        response: 200,
99        description: 'If the PDF of the FAQ exists.',
100        content: new OA\JsonContent(example: 'https://www.example.org/pdf.php?cat=3&id=142&artlang=de'),
101    )]
102    #[OA\Response(
103        response: 404,
104        description: "If there's no FAQ and PDF for the given FAQ ID.",
105        content: new OA\JsonContent(example: []),
106    )]
107    #[Route(path: 'v4.0/pdf/{categoryId}/{faqId}', name: 'api.pdf.getById', methods: ['GET'])]
108    public function getById(Request $request): JsonResponse
109    {
110        [$currentUser, $currentGroups] = CurrentUser::getCurrentUserGroupId($this->currentUser);
111
112        $faq = is_callable($this->faqFactory)
113            ? ($this->faqFactory)($this->configuration)
114            : new Faq($this->configuration);
115        $faq->setUser($currentUser);
116        $faq->setGroups($currentGroups);
117
118        $categoryId = (int) Filter::filterVar($request->attributes->get(key: 'categoryId'), FILTER_VALIDATE_INT);
119        $faqId = (int) Filter::filterVar($request->attributes->get(key: 'faqId'), FILTER_VALIDATE_INT);
120
121        $faq->getFaq($faqId);
122        $result = $faq->faqRecord;
123
124        if ((is_countable($result) ? count($result) : 0) === 0 || $result['solution_id'] === 42) {
125            $result = new stdClass();
126            return $this->json($result, Response::HTTP_NOT_FOUND);
127        }
128
129        $services = is_callable($this->servicesFactory)
130            ? ($this->servicesFactory)($this->configuration)
131            : new Services($this->configuration);
132        $services->setFaqId($faqId);
133        $services->setLanguage($this->configuration->getLanguage()->getLanguage());
134        $services->setCategoryId($categoryId);
135
136        $result = $services->getPdfApiLink();
137        return $this->json($result, Response::HTTP_OK);
138    }
139}