<?php
namespace App\Controller;
use App\Entity\Enquiry;
use App\Entity\EnquiryCV;
use App\Form\EnquiryType;
use App\Form\EnquiryCVType;
use App\Annotation\CmsComponent;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\CreateAssessmentRequest;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
use Google\Cloud\RecaptchaEnterprise\V1\Client\RecaptchaEnterpriseServiceClient;
class EnquiryDefaultController extends AbstractController
{
private ?RecaptchaEnterpriseServiceClient $client = null;
private string $errorMessage = '';
public function __construct(
private EntityManagerInterface $em,
private \Swift_Mailer $mailer,
private string $email_noreply,
private string $email_primary,
private readonly string $recaptcha_key,
private readonly string $recaptcha_project_id,
private readonly string $projectRoot
) { }
/**
* @CmsComponent("Embed Enquiry Form", active=true, routeName="embed_enquiry")
*/
#[Route(path: '/cms-enquiry', name: 'embed_enquiry')]
public function embedEnquiry(Request $request, ): Response
{
$enquiry = new Enquiry();
$enquiry->setSubject('New Website Enquiry');
$form = $this->createForm(EnquiryType::class, $enquiry);
$form->handleRequest($request);
$error = false;
$success = false;
$errorMessage = '';
if ($form->isSubmitted()) {
if ($this->spamChecksPass($request)) {
if ($form->isValid()) {
$this->em->persist($enquiry);
$this->em->flush();
$success = true;
$this->sendEmail($enquiry);
} else {
$error = true;
$errorMessage = 'Error - Check the form for errors';
}
} else {
$error = true;
$errorMessage = $this->errorMessage;
}
}
return $this->render('@theme/enquiry/enquiry.html.twig', [
'enquiry' => $enquiry,
'error' => $error,
'success' => $success,
'errorMessage' => $errorMessage,
'form' => $form->createView(),
]);
}
/**
* @CmsComponent("Embed Enquiry Form & Map", active=true, routeName="embed_enquiry_map")
*/
#[Route(path: '/cms-enquiry-map', name: 'embed_enquiry_map')]
public function embedEnquiryMap(Request $request, ): Response
{
$enquiry = new Enquiry();
$enquiry->setSubject('New Website Enquiry');
$form = $this->createForm(EnquiryType::class, $enquiry);
$form->handleRequest($request);
$error = false;
$success = false;
$errorMessage = '';
if ($form->isSubmitted()) {
if ($this->spamChecksPass($request)) {
if ($form->isValid()) {
$this->em->persist($enquiry);
$this->em->flush();
$success = true;
$this->sendEmail($enquiry);
} else {
$error = true;
$errorMessage = 'Error - Check the form for errors';
}
} else {
$error = true;
$errorMessage = 'Error - Captcha Invalid';
}
}
return $this->render('@theme/enquiry/enquiry_map.html.twig', [
'enquiry' => $enquiry,
'error' => $error,
'success' => $success,
'errorMessage' => $errorMessage,
'form' => $form->createView(),
]);
}
/**
* @CmsComponent("Embed CV form", active=true, routeName="embed_enquiry_cv")
*/
#[Route(path: '/cms-enquiry-cv', name: 'embed_enquiry_cv')]
public function embedEnquiryCv(Request $request): Response
{
$enquiry = new EnquiryCV();
$form = $this->createForm(EnquiryCVType::class, $enquiry);
$form->handleRequest($request);
$error = false;
$success = false;
$errorMessage = '';
if ($form->isSubmitted()) {
if ($this->spamChecksPass($request)) {
if ($form->isValid()) {
$enquiry->uploadFile();
$this->em->persist($enquiry);
$this->em->flush();
$success = true;
$this->sendCVEmail($enquiry);
} else {
$error = true;
$errorMessage = 'Error - Check the form for errors';
}
} else {
$error = true;
$errorMessage = 'Error - Captcha Invalid';
}
}
return $this->render('@theme/enquiry/enquiry_cv.html.twig', [
'enquiry' => $enquiry,
'error' => $error,
'success' => $success,
'errorMessage' => $errorMessage,
'form' => $form->createView(),
]);
}
#[Route(path: '/email-test', name: 'email_test')]
public function emailTest(): Response
{
return $this->render('@theme/emails/enquiry-confirmed.html.twig');
}
private function sendEmail(Enquiry $enquiry): void
{
$message_to_client = (new \Swift_Message())
->setSubject('Enquiry Received via '.$this->getParameter('sitename').' website')
->setFrom($this->email_noreply)
->setTo($this->email_primary)
->setBody(
$this->renderView('@theme/emails/enquiry-to-client.html.twig', ['enquiry' => $enquiry]),
'text/html'
)
;
try {
$this->mailer->send($message_to_client);
} catch (\Throwable $th) {
// ignore
}
$this->sendConfirmationEmail($enquiry);
}
private function sendCVEmail(EnquiryCV $enquiry): void
{
$message_to_client = (new \Swift_Message())
->setSubject('Enquiry Received via '.$this->getParameter('sitename').' website')
->setFrom($this->email_noreply)
->setTo($this->email_primary)
->setBody(
$this->renderView('@theme/emails/enquiry-to-client-cv.html.twig', ['enquiry' => $enquiry]),
'text/html'
)
;
try {
$this->mailer->send($message_to_client);
} catch (\Throwable $th) {
// ignore
}
$this->sendConfirmationEmail($enquiry);
}
private function sendConfirmationEmail($enquiry): void
{
$message_to_user = (new \Swift_Message())
->setSubject('Enquiry sent to '.$this->getParameter('sitename').' confirmed')
->setFrom($this->email_noreply)
->setTo($enquiry->getEmail())
->setBody(
$this->renderView(
'@theme/emails/enquiry-confirmed.html.twig',
['enquiry' => $enquiry]
),
'text/html'
)
;
// try {
$this->mailer->send($message_to_user);
// } catch (\Throwable $th) {
// ignore
// }
}
private function spamChecksPass(Request $request): bool
{
// also test form execution time
try {
$posted = $request->request->All();
// check for captcha response
$recaptcha = $posted['g-recaptcha-response'] ?? null;
if (empty($recaptcha)) {
return false;
}
// check for form execution time
$loadedAt = (int) $posted['enquiry']['_loaded_at'] ?? 0;
if ($loadedAt && (time() - $loadedAt) < 3) {
return false;
}
// check for $honeypot field
$honeypot = $posted['enquiry']['website'] ?? null;
if (!empty($honeypot)) {
return false;
}
return $this->verifyRecaptcha($recaptcha);
} catch (\Throwable $th) {
return false;
}
}
private function verifyRecaptcha($token): bool
{
$this->errorMessage = '';
if (! $this->client) {
$this->client = new RecaptchaEnterpriseServiceClient([
'credentials' => $this->projectRoot . '/kasscaffolding-850ee80a9abf.json',
]);
$projectName = $this->client->projectName($this->recaptcha_project_id);
}
// Set the properties of the event to be tracked.
$event = (new Event())
->setSiteKey($this->recaptcha_key)
->setToken($token);
$assessment = (new Assessment())
->setEvent($event);
$request = (new CreateAssessmentRequest())
->setParent($projectName)
->setAssessment($assessment);
try {
$response = $this->client->createAssessment($request);
if ($response->getTokenProperties()->getValid() == false) {
$this->errorMessage = InvalidReason::name($response->getTokenProperties()->getInvalidReason());
return false;
}
$score = $response->getRiskAnalysis()->getScore();
if ($score < 0.5) {
$this->errorMessage = 'Low score: ' . $score;
return false;
}
} catch (\Exception $e) {
return false;
}
return true;
}
}