Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
ContactController
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
2 / 2
14
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
13
1<?php
2
3/**
4 * The Contact Controller
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-09
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Controller\Frontend\Api;
21
22use phpMyFAQ\Controller\AbstractController;
23use phpMyFAQ\Core\Exception;
24use phpMyFAQ\Filter;
25use phpMyFAQ\Mail;
26use phpMyFAQ\StopWords;
27use phpMyFAQ\Translation;
28use phpMyFAQ\Utils;
29use Symfony\Component\HttpFoundation\JsonResponse;
30use Symfony\Component\HttpFoundation\Request;
31use Symfony\Component\HttpFoundation\Response;
32use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
33use Symfony\Component\Routing\Attribute\Route;
34
35final class ContactController extends AbstractController
36{
37    public function __construct(
38        private readonly StopWords $stopWords,
39        private readonly Mail $mailer,
40    ) {
41        parent::__construct();
42    }
43
44    /**
45     * @throws Exception
46     * @throws \JsonException
47     * @throws \Exception
48     *
49     */
50    #[Route(path: 'contact', name: 'api.private.contact', methods: ['POST'])]
51    public function create(Request $request): JsonResponse
52    {
53        $data = json_decode($request->getContent());
54
55        if (!$data instanceof \stdClass) {
56            throw new Exception('Invalid JSON data');
57        }
58
59        if (!property_exists($data, 'name')) {
60            throw new Exception('Missing name');
61        }
62
63        if (!property_exists($data, 'question')) {
64            throw new Exception('Missing question');
65        }
66
67        $author = trim((string) Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS));
68        $email = Filter::filterEmail($data->email);
69        $question = trim((string) Filter::filterVar($data->question, FILTER_SANITIZE_SPECIAL_CHARS));
70
71        if (!$email) {
72            throw new Exception('Invalid email address');
73        }
74
75        $email = Filter::filterVar($email, FILTER_SANITIZE_SPECIAL_CHARS, '');
76
77        if ($question === '' || $question === '0') {
78            throw new Exception('Empty question');
79        }
80
81        if (!$this->captchaCodeIsValid($request)) {
82            throw new Exception('Invalid captcha');
83        }
84
85        if ($author !== '' && $author !== '0' && $email !== '' && $this->stopWords->checkBannedWord($question)) {
86            $question = sprintf(
87                '%s: %s<br>%s: %s<br><br>%s',
88                Translation::getString(key: 'msgNewContentName'),
89                $author,
90                Translation::getString(key: 'msgNewContentMail'),
91                $email,
92                $question,
93            );
94
95            try {
96                $this->mailer->setReplyTo($email, $author);
97                $this->mailer->addTo($this->configuration->getAdminEmail());
98                $this->mailer->subject = Utils::resolveMarkers(
99                    text: 'Feedback: %sitename%',
100                    configuration: $this->configuration,
101                );
102                $this->mailer->message = $question;
103                $this->mailer->send();
104
105                return $this->json(['success' => Translation::get(key: 'msgMailContact')], Response::HTTP_OK);
106            } catch (Exception|TransportExceptionInterface $e) {
107                return $this->json(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
108            }
109        }
110
111        return $this->json(['error' => Translation::get(key: 'err_sendMail')], Response::HTTP_BAD_REQUEST);
112    }
113}