src/Controller/EnquiryDefaultController.php line 41

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Enquiry;
  4. use App\Entity\EnquiryCV;
  5. use App\Form\EnquiryType;
  6. use App\Form\EnquiryCVType;
  7. use App\Annotation\CmsComponent;
  8. use Doctrine\ORM\EntityManagerInterface;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Google\Cloud\RecaptchaEnterprise\V1\Event;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\Routing\Annotation\Route;
  13. use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
  14. use Google\Cloud\RecaptchaEnterprise\V1\CreateAssessmentRequest;
  15. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  16. use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
  17. use Google\Cloud\RecaptchaEnterprise\V1\Client\RecaptchaEnterpriseServiceClient;
  18. class EnquiryDefaultController extends AbstractController
  19. {
  20. private ?RecaptchaEnterpriseServiceClient $client = null;
  21. private string $errorMessage = '';
  22. public function __construct(
  23. private EntityManagerInterface $em,
  24. private \Swift_Mailer $mailer,
  25. private string $email_noreply,
  26. private string $email_primary,
  27. private readonly string $recaptcha_key,
  28. private readonly string $recaptcha_project_id,
  29. private readonly string $projectRoot
  30. ) { }
  31. /**
  32. * @CmsComponent("Embed Enquiry Form", active=true, routeName="embed_enquiry")
  33. */
  34. #[Route(path: '/cms-enquiry', name: 'embed_enquiry')]
  35. public function embedEnquiry(Request $request, ): Response
  36. {
  37. $enquiry = new Enquiry();
  38. $enquiry->setSubject('New Website Enquiry');
  39. $form = $this->createForm(EnquiryType::class, $enquiry);
  40. $form->handleRequest($request);
  41. $error = false;
  42. $success = false;
  43. $errorMessage = '';
  44. if ($form->isSubmitted()) {
  45. if ($this->spamChecksPass($request)) {
  46. if ($form->isValid()) {
  47. $this->em->persist($enquiry);
  48. $this->em->flush();
  49. $success = true;
  50. $this->sendEmail($enquiry);
  51. } else {
  52. $error = true;
  53. $errorMessage = 'Error - Check the form for errors';
  54. }
  55. } else {
  56. $error = true;
  57. $errorMessage = $this->errorMessage;
  58. }
  59. }
  60. return $this->render('@theme/enquiry/enquiry.html.twig', [
  61. 'enquiry' => $enquiry,
  62. 'error' => $error,
  63. 'success' => $success,
  64. 'errorMessage' => $errorMessage,
  65. 'form' => $form->createView(),
  66. ]);
  67. }
  68. /**
  69. * @CmsComponent("Embed Enquiry Form & Map", active=true, routeName="embed_enquiry_map")
  70. */
  71. #[Route(path: '/cms-enquiry-map', name: 'embed_enquiry_map')]
  72. public function embedEnquiryMap(Request $request, ): Response
  73. {
  74. $enquiry = new Enquiry();
  75. $enquiry->setSubject('New Website Enquiry');
  76. $form = $this->createForm(EnquiryType::class, $enquiry);
  77. $form->handleRequest($request);
  78. $error = false;
  79. $success = false;
  80. $errorMessage = '';
  81. if ($form->isSubmitted()) {
  82. if ($this->spamChecksPass($request)) {
  83. if ($form->isValid()) {
  84. $this->em->persist($enquiry);
  85. $this->em->flush();
  86. $success = true;
  87. $this->sendEmail($enquiry);
  88. } else {
  89. $error = true;
  90. $errorMessage = 'Error - Check the form for errors';
  91. }
  92. } else {
  93. $error = true;
  94. $errorMessage = 'Error - Captcha Invalid';
  95. }
  96. }
  97. return $this->render('@theme/enquiry/enquiry_map.html.twig', [
  98. 'enquiry' => $enquiry,
  99. 'error' => $error,
  100. 'success' => $success,
  101. 'errorMessage' => $errorMessage,
  102. 'form' => $form->createView(),
  103. ]);
  104. }
  105. /**
  106. * @CmsComponent("Embed CV form", active=true, routeName="embed_enquiry_cv")
  107. */
  108. #[Route(path: '/cms-enquiry-cv', name: 'embed_enquiry_cv')]
  109. public function embedEnquiryCv(Request $request): Response
  110. {
  111. $enquiry = new EnquiryCV();
  112. $form = $this->createForm(EnquiryCVType::class, $enquiry);
  113. $form->handleRequest($request);
  114. $error = false;
  115. $success = false;
  116. $errorMessage = '';
  117. if ($form->isSubmitted()) {
  118. if ($this->spamChecksPass($request)) {
  119. if ($form->isValid()) {
  120. $enquiry->uploadFile();
  121. $this->em->persist($enquiry);
  122. $this->em->flush();
  123. $success = true;
  124. $this->sendCVEmail($enquiry);
  125. } else {
  126. $error = true;
  127. $errorMessage = 'Error - Check the form for errors';
  128. }
  129. } else {
  130. $error = true;
  131. $errorMessage = 'Error - Captcha Invalid';
  132. }
  133. }
  134. return $this->render('@theme/enquiry/enquiry_cv.html.twig', [
  135. 'enquiry' => $enquiry,
  136. 'error' => $error,
  137. 'success' => $success,
  138. 'errorMessage' => $errorMessage,
  139. 'form' => $form->createView(),
  140. ]);
  141. }
  142. #[Route(path: '/email-test', name: 'email_test')]
  143. public function emailTest(): Response
  144. {
  145. return $this->render('@theme/emails/enquiry-confirmed.html.twig');
  146. }
  147. private function sendEmail(Enquiry $enquiry): void
  148. {
  149. $message_to_client = (new \Swift_Message())
  150. ->setSubject('Enquiry Received via '.$this->getParameter('sitename').' website')
  151. ->setFrom($this->email_noreply)
  152. ->setTo($this->email_primary)
  153. ->setBody(
  154. $this->renderView('@theme/emails/enquiry-to-client.html.twig', ['enquiry' => $enquiry]),
  155. 'text/html'
  156. )
  157. ;
  158. try {
  159. $this->mailer->send($message_to_client);
  160. } catch (\Throwable $th) {
  161. // ignore
  162. }
  163. $this->sendConfirmationEmail($enquiry);
  164. }
  165. private function sendCVEmail(EnquiryCV $enquiry): void
  166. {
  167. $message_to_client = (new \Swift_Message())
  168. ->setSubject('Enquiry Received via '.$this->getParameter('sitename').' website')
  169. ->setFrom($this->email_noreply)
  170. ->setTo($this->email_primary)
  171. ->setBody(
  172. $this->renderView('@theme/emails/enquiry-to-client-cv.html.twig', ['enquiry' => $enquiry]),
  173. 'text/html'
  174. )
  175. ;
  176. try {
  177. $this->mailer->send($message_to_client);
  178. } catch (\Throwable $th) {
  179. // ignore
  180. }
  181. $this->sendConfirmationEmail($enquiry);
  182. }
  183. private function sendConfirmationEmail($enquiry): void
  184. {
  185. $message_to_user = (new \Swift_Message())
  186. ->setSubject('Enquiry sent to '.$this->getParameter('sitename').' confirmed')
  187. ->setFrom($this->email_noreply)
  188. ->setTo($enquiry->getEmail())
  189. ->setBody(
  190. $this->renderView(
  191. '@theme/emails/enquiry-confirmed.html.twig',
  192. ['enquiry' => $enquiry]
  193. ),
  194. 'text/html'
  195. )
  196. ;
  197. // try {
  198. $this->mailer->send($message_to_user);
  199. // } catch (\Throwable $th) {
  200. // ignore
  201. // }
  202. }
  203. private function spamChecksPass(Request $request): bool
  204. {
  205. // also test form execution time
  206. try {
  207. $posted = $request->request->All();
  208. // check for captcha response
  209. $recaptcha = $posted['g-recaptcha-response'] ?? null;
  210. if (empty($recaptcha)) {
  211. return false;
  212. }
  213. // check for form execution time
  214. $loadedAt = (int) $posted['enquiry']['_loaded_at'] ?? 0;
  215. if ($loadedAt && (time() - $loadedAt) < 3) {
  216. return false;
  217. }
  218. // check for $honeypot field
  219. $honeypot = $posted['enquiry']['website'] ?? null;
  220. if (!empty($honeypot)) {
  221. return false;
  222. }
  223. return $this->verifyRecaptcha($recaptcha);
  224. } catch (\Throwable $th) {
  225. return false;
  226. }
  227. }
  228. private function verifyRecaptcha($token): bool
  229. {
  230. $this->errorMessage = '';
  231. if (! $this->client) {
  232. $this->client = new RecaptchaEnterpriseServiceClient([
  233. 'credentials' => $this->projectRoot . '/kasscaffolding-850ee80a9abf.json',
  234. ]);
  235. $projectName = $this->client->projectName($this->recaptcha_project_id);
  236. }
  237. // Set the properties of the event to be tracked.
  238. $event = (new Event())
  239. ->setSiteKey($this->recaptcha_key)
  240. ->setToken($token);
  241. $assessment = (new Assessment())
  242. ->setEvent($event);
  243. $request = (new CreateAssessmentRequest())
  244. ->setParent($projectName)
  245. ->setAssessment($assessment);
  246. try {
  247. $response = $this->client->createAssessment($request);
  248. if ($response->getTokenProperties()->getValid() == false) {
  249. $this->errorMessage = InvalidReason::name($response->getTokenProperties()->getInvalidReason());
  250. return false;
  251. }
  252. $score = $response->getRiskAnalysis()->getScore();
  253. if ($score < 0.5) {
  254. $this->errorMessage = 'Low score: ' . $score;
  255. return false;
  256. }
  257. } catch (\Exception $e) {
  258. return false;
  259. }
  260. return true;
  261. }
  262. }