src/Controller/PageGeneratorController.php line 44

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use Twig\Environment;
  4. use Symfony\Component\Routing\Annotation\Route;
  5. use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\HttpFoundation\RedirectResponse;
  9. use Symfony\Component\Finder\Finder;
  10. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  11. use Symfony\Component\EventDispatcher\GenericEvent;
  12. use Symfony\Component\Form\FormInterface;
  13. use Symfony\Component\Security\Core\Security;
  14. use Symfony\Component\Routing\RequestContext;
  15. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  16. use Sylius\Component\Channel\Context\ChannelContextInterface;
  17. use App\Repository\UserRepository;
  18. use App\Service\UserService;
  19. use Vanilla\JsConnect\JsConnect;
  20. use Vanilla\JsConnect\JsConnectJSONP;
  21. final class PageGeneratorController extends AbstractController
  22. {
  23. private const CONTACT_EMAIL = 'info@hephaestos.eu';
  24. /** @var Environment */
  25. private $twig;
  26. /** @var ChannelContextInterface */
  27. private $channelContext;
  28. private Jsconnect $jsconnect;
  29. public function __construct(Environment $twig, Jsconnect $jsconnect, ChannelContextInterface $channelContext)
  30. {
  31. $this->twig = $twig;
  32. $this->jsconnect = $jsconnect;
  33. $this->channelContext = $channelContext;
  34. }
  35. public function menuAction(): Response
  36. {
  37. return $this->render('@SyliusShop/Menu/_pageMenu.html.twig');
  38. }
  39. /**
  40. * Subscription page disabled for the beta-only release.
  41. */
  42. public function subscriptionAction(): Response
  43. {
  44. throw $this->createNotFoundException();
  45. }
  46. /**
  47. * @Route("/beta-testers", name="beta_testers")
  48. */
  49. public function betaTestersAction(Request $request, \Swift_Mailer $mailer): Response
  50. {
  51. $formData = [
  52. 'firstName' => '',
  53. 'lastName' => '',
  54. 'email' => '',
  55. 'company' => '',
  56. 'role' => '',
  57. 'showType' => '',
  58. 'operatingSystem' => '',
  59. 'hardware' => '',
  60. 'features' => '',
  61. 'usage' => '',
  62. 'feedback' => '',
  63. 'availability' => '',
  64. ];
  65. if ($request->isMethod('POST')) {
  66. $formData = array_map(
  67. static fn ($value): string => trim((string) $value),
  68. array_intersect_key($request->request->all(), $formData)
  69. ) + $formData;
  70. $honeypot = trim((string) $request->request->get('website', ''));
  71. $requiredFields = ['firstName', 'lastName', 'email', 'role', 'operatingSystem', 'usage', 'feedback'];
  72. $missingFields = array_filter($requiredFields, static fn (string $field): bool => '' === $formData[$field]);
  73. if ('' !== $honeypot) {
  74. $this->addFlash('success', 'beta.form.success');
  75. return $this->redirectToRoute('beta_testers');
  76. }
  77. if (!$this->isCsrfTokenValid('beta_tester_application', (string) $request->request->get('_token'))) {
  78. $this->addFlash('error', 'beta.form.csrf_error');
  79. } elseif ([] !== $missingFields || false === filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
  80. $this->addFlash('error', 'beta.form.error');
  81. } else {
  82. $messageBody = $this->renderView('Email/beta_tester_application.html.twig', [
  83. 'data' => $formData,
  84. ]);
  85. $message = (new \Swift_Message('Nouvelle candidature beta testeur Hephaestos'))
  86. ->setFrom([self::CONTACT_EMAIL => 'Hephaestos'])
  87. ->setTo([self::CONTACT_EMAIL])
  88. ->setReplyTo([$formData['email']])
  89. ->setBody($messageBody, 'text/html');
  90. try {
  91. if (0 === $mailer->send($message)) {
  92. throw new \RuntimeException('Beta email was not accepted by the transport.');
  93. }
  94. } catch (\Throwable $exception) {
  95. $this->addFlash('error', 'beta.form.mail_error');
  96. return $this->redirectToRoute('beta_testers');
  97. }
  98. $this->addFlash('success', 'beta.form.success');
  99. return $this->redirectToRoute('beta_testers');
  100. }
  101. }
  102. return $this->render('page_generator/beta_testers.html.twig', [
  103. 'formData' => $formData,
  104. ]);
  105. }
  106. /**
  107. * @Route("/formation/demande", name="formation_request")
  108. */
  109. public function formationRequestAction(Request $request, \Swift_Mailer $mailer): Response
  110. {
  111. $formData = [
  112. 'firstName' => '',
  113. 'lastName' => '',
  114. 'email' => '',
  115. 'company' => '',
  116. 'subject' => '',
  117. 'needs' => '',
  118. 'availability' => '',
  119. ];
  120. if ($request->isMethod('POST')) {
  121. $formData = array_map(
  122. static fn ($value): string => trim((string) $value),
  123. array_intersect_key($request->request->all(), $formData)
  124. ) + $formData;
  125. $honeypot = trim((string) $request->request->get('website', ''));
  126. $requiredFields = ['firstName', 'lastName', 'email', 'subject', 'needs'];
  127. $missingFields = array_filter($requiredFields, static fn (string $field): bool => '' === $formData[$field]);
  128. if ('' !== $honeypot) {
  129. $this->addFlash('success', 'formation.request.success');
  130. return $this->redirectToRoute('formation_request');
  131. }
  132. if (!$this->isCsrfTokenValid('formation_request', (string) $request->request->get('_token'))) {
  133. $this->addFlash('error', 'formation.request.csrf_error');
  134. } elseif ([] !== $missingFields || false === filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
  135. $this->addFlash('error', 'formation.request.error');
  136. } else {
  137. $messageBody = $this->renderView('Email/formation_request.html.twig', [
  138. 'data' => $formData,
  139. ]);
  140. $message = (new \Swift_Message('Nouvelle demande de formation Hephaestos'))
  141. ->setFrom([self::CONTACT_EMAIL => 'Hephaestos'])
  142. ->setTo([self::CONTACT_EMAIL])
  143. ->setReplyTo([$formData['email']])
  144. ->setBody($messageBody, 'text/html');
  145. try {
  146. if (0 === $mailer->send($message)) {
  147. throw new \RuntimeException('Training request email was not accepted by the transport.');
  148. }
  149. } catch (\Throwable $exception) {
  150. $this->addFlash('error', 'formation.request.mail_error');
  151. return $this->redirectToRoute('formation_request');
  152. }
  153. $this->addFlash('success', 'formation.request.success');
  154. return $this->redirectToRoute('formation_request');
  155. }
  156. }
  157. return $this->render('page_generator/formation_request.html.twig', [
  158. 'formData' => $formData,
  159. ]);
  160. }
  161. private function getChannel(): mixed
  162. {
  163. return $this->channelContext->getChannel();
  164. }
  165. /**
  166. * @Route("/old_version", name="old_version")
  167. */
  168. public function oldversionAction(Request $request): Response
  169. {
  170. $finderwindows64 = new Finder();
  171. $windowsfiles64 = $finderwindows64->files()->name('*.zip')->in($this->get('kernel')->getProjectDir() . "/public/versions/old/Windows/x64");
  172. $finderwindows32 = new Finder();
  173. $windowsfiles32 = $finderwindows32->files()->name('*.zip')->in($this->get('kernel')->getProjectDir() . "/public/versions/old/Windows/x86");
  174. $findermac = new Finder();
  175. $macfiles = $findermac->files()->name('*.zip')->in($this->get('kernel')->getProjectDir() . "/public/versions/old/OSX");
  176. return $this->render('page_generator/oldversion.html.twig', array('windowsfiles64' => $windowsfiles64,'windowsfiles32' => $windowsfiles32, 'macfiles' => $macfiles));
  177. }
  178. /**
  179. * @Route("/download", name="download")
  180. public function downloadAction(Request $request): Response
  181. {
  182. return $this->render('page_generator/download.html.twig');
  183. }
  184. */
  185. /**
  186. * @Route("/formation", name="formation")
  187. */
  188. public function formationAction(Request $request): Response
  189. {
  190. $locale = $request->getLocale();
  191. $template = str_starts_with((string) $locale, 'en')
  192. ? 'page_generator/formation.en.html.twig'
  193. : 'page_generator/formation.html.twig';
  194. return $this->render($template);
  195. }
  196. /**
  197. * @Route("/forum", name="forum")
  198. */
  199. public function forumAction(Request $request, UserService $userService): Response
  200. {
  201. $forumRoot = rtrim((string) $this->getParameter('forumdomain'), '/');
  202. $forumUrl = $forumRoot . '/';
  203. $user = $this->getUser();
  204. if ($user === null || !is_object($user) || !method_exists($user, 'isVerified') || !$user->isVerified()) {
  205. return new RedirectResponse($forumUrl);
  206. }
  207. $customer = $userService->getCustomerByUser($user);
  208. $forumName = method_exists($customer, 'getPseudo') ? trim((string) $customer->getPseudo()) : '';
  209. if ('' === $forumName) {
  210. $this->addFlash('error', 'customer.pseudo.required_for_forum');
  211. return $this->redirectToRoute('sylius_shop_account_profile_update');
  212. }
  213. $forumEmail = $this->resolveForumEmail($customer, $user);
  214. $ssoToken = $this->createForumSsoToken([
  215. 'sub' => (string) $user->getId(),
  216. 'name' => $forumName,
  217. 'email' => $forumEmail,
  218. ]);
  219. $forumSsoPath = '/app.php/hephaestos/sso/login';
  220. $forumSsoUrl = $forumRoot . $forumSsoPath . '?token=' . rawurlencode($ssoToken) . '&return=' . rawurlencode('/');
  221. return new RedirectResponse($forumSsoUrl);
  222. }
  223. private function resolveForumEmail(object $customer, object $user): string
  224. {
  225. $candidates = [
  226. method_exists($customer, 'getEmail') ? $customer->getEmail() : null,
  227. method_exists($user, 'getEmail') ? $user->getEmail() : null,
  228. method_exists($user, 'getUsername') ? $user->getUsername() : null,
  229. ];
  230. foreach ($candidates as $candidate) {
  231. $candidate = trim((string) $candidate);
  232. if ('' !== $candidate) {
  233. return $candidate;
  234. }
  235. }
  236. return 'forum-user-' . (method_exists($user, 'getId') ? (string) $user->getId() : 'hephaestos') . '@hephaestos.local';
  237. }
  238. private function resolveForumName(object $customer, object $user): string
  239. {
  240. $candidates = [
  241. method_exists($customer, 'getPseudo') ? $customer->getPseudo() : null,
  242. method_exists($customer, 'getFullName') ? $customer->getFullName() : null,
  243. trim(sprintf(
  244. '%s %s',
  245. method_exists($customer, 'getFirstName') ? (string) $customer->getFirstName() : '',
  246. method_exists($customer, 'getLastName') ? (string) $customer->getLastName() : ''
  247. )),
  248. method_exists($customer, 'getEmail') ? $customer->getEmail() : null,
  249. method_exists($user, 'getEmail') ? $user->getEmail() : null,
  250. method_exists($user, 'getUsername') ? $user->getUsername() : null,
  251. 'user-' . (method_exists($user, 'getId') ? (string) $user->getId() : 'hephaestos'),
  252. ];
  253. foreach ($candidates as $candidate) {
  254. $candidate = trim((string) $candidate);
  255. if ('' !== $candidate) {
  256. return $candidate;
  257. }
  258. }
  259. return 'Hephaestos user';
  260. }
  261. private function createForumSsoToken(array $identity): string
  262. {
  263. $now = time();
  264. $payload = [
  265. 'sub' => (string) ($identity['sub'] ?? ''),
  266. 'name' => (string) ($identity['name'] ?? ''),
  267. 'email' => (string) ($identity['email'] ?? ''),
  268. 'iat' => $now,
  269. 'exp' => $now + 120,
  270. 'nonce' => bin2hex(random_bytes(8)),
  271. ];
  272. $payloadJson = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  273. if (false === $payloadJson) {
  274. throw new \RuntimeException('Unable to encode forum SSO payload.');
  275. }
  276. $payloadB64 = rtrim(strtr(base64_encode($payloadJson), '+/', '-_'), '=');
  277. $secret = (string) $this->getParameter('forum_sso_secret');
  278. $signature = hash_hmac('sha256', $payloadB64, $secret);
  279. return $payloadB64 . '.' . $signature;
  280. }
  281. /**
  282. * @Route("/wiki", name="wiki")
  283. */
  284. public function wikiAction(Request $request): Response
  285. {
  286. return $this->render('page_generator/wiki.html.twig');
  287. }
  288. /**
  289. * @Route("/redirectlogin", name="redirectlogin")
  290. */
  291. public function redirectloginAction(Request $request): Response
  292. {
  293. $context = new RequestContext();
  294. $context->fromRequest(Request::createFromGlobals());
  295. $url = $this->generateUrl('sylius_shop_login', [
  296. '_locale' => 'fr',
  297. ], UrlGeneratorInterface::ABSOLUTE_URL);
  298. $js = '<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script type="text/javascript">jQuery(document).ready( function() {
  299. top.location = "' . $url . '";});</script>';
  300. $response = new Response();
  301. $response->prepare($request);
  302. $response->setContent($js);
  303. $response->send();
  304. return $this->redirect($url);
  305. //return $this->redirectToRoute('sylius_shop_login', [], 301);
  306. }
  307. /**
  308. * @Route("/redirectlogout", name="redirectlogout")
  309. */
  310. public function redirectlogoutAction(Request $request): Response
  311. {
  312. $context = new RequestContext();
  313. $context->fromRequest(Request::createFromGlobals());
  314. $url = $this->generateUrl('sylius_shop_logout', ['_locale' => 'fr',], UrlGeneratorInterface::ABSOLUTE_URL);
  315. $js = '<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script type="text/javascript">jQuery(document).ready( function() {
  316. top.location = "' . $url . '";});</script>';
  317. $response = new Response();
  318. $response->prepare($request);
  319. $response->setContent($js);
  320. $response->send();
  321. }
  322. /**
  323. * @Route("/redirectregister", name="redirectregister")
  324. */
  325. public function redirectregisterAction(Request $request): Response
  326. {
  327. $context = new RequestContext();
  328. $context->fromRequest(Request::createFromGlobals());
  329. $url = $this->generateUrl('sylius_shop_register', ['_locale' => 'fr',], UrlGeneratorInterface::ABSOLUTE_URL);
  330. $js = '<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script type="text/javascript">jQuery(document).ready( function() { top.location = "' . $url . '";});</script>';
  331. $response = new Response();
  332. $response->prepare($request);
  333. $response->setContent($js);
  334. $response->send();
  335. // return $this->redirect($url);
  336. //return $this->redirectToRoute('sylius_shop_register', [], 301);
  337. }
  338. /**
  339. * @Route("/electronique", name="electronique")
  340. */
  341. public function electroniqueAction(Request $request): Response
  342. {
  343. $code = array('Board_Taxons','DMX_HF','PWM_DIMMER_Taxon', 'HF_PROJECTOR__Taxon','books');
  344. return $this->render('page_generator/electronique.html.twig', array('code' => $code, 'maxresult' => -1));
  345. }
  346. /**
  347. * @Route("/features", name="features")
  348. */
  349. public function featuresAction(Request $request): Response
  350. {
  351. return $this->render('page_generator/features.html.twig');
  352. }
  353. /**
  354. * @Route("/about", name="about")
  355. */
  356. public function aboutAction(Request $request): Response
  357. {
  358. return $this->render('page_generator/about.html.twig');
  359. }
  360. /**
  361. * @Route("/shipping", name="shipping")
  362. */
  363. public function deliveryAction(Request $request): Response
  364. {
  365. return $this->render('page_generator/shipping.html.twig');
  366. }
  367. /**
  368. * @Route("/FAQ", name="FAQ")
  369. */
  370. public function FAQAction(Request $request): Response
  371. {
  372. return $this->render('page_generator/FAQ.html.twig');
  373. }
  374. /**
  375. * @Route("/privacy", name="privacy")
  376. */
  377. public function privacyAction(Request $request): Response
  378. {
  379. return $this->render('page_generator/privacy.html.twig');
  380. }
  381. /**
  382. * @Route("/return", name="return")
  383. */
  384. public function returnAction(Request $request): Response
  385. {
  386. return $this->render('page_generator/return.html.twig');
  387. }
  388. /**
  389. * @Route("/terms", name="terms")
  390. */
  391. public function termsAction(Request $request): Response
  392. {
  393. return $this->render('page_generator/terms.html.twig');
  394. }
  395. /**
  396. * @Route("/confirmationresend", name="confirmationresend", methods={"POST"})
  397. */
  398. public function resendVerificationTokenfromMailAction(Request $request): Response
  399. {
  400. if (!$this->isCsrfTokenValid('resend_verification_email', (string) $request->request->get('_token'))) {
  401. $this->addTranslatedFlash('error', 'Invalid security token.');
  402. return $this->redirectToRoute('sylius_shop_login');
  403. }
  404. $email = trim((string) $request->request->get('emailverif', ''));
  405. if ('' === $email || false === filter_var($email, FILTER_VALIDATE_EMAIL)) {
  406. // Neutral response to avoid user enumeration.
  407. $this->addTranslatedFlash('success', 'sylius.user.verify_email_request');
  408. return $this->redirectToRoute('sylius_shop_login');
  409. }
  410. $cooldownKey = 'resend_verification_last_' . sha1(strtolower($email) . '|' . (string) $request->getClientIp());
  411. $cooldownSeconds = 60;
  412. if ($request->hasSession()) {
  413. $lastAttemptAt = (int) $request->getSession()->get($cooldownKey, 0);
  414. if ($lastAttemptAt > 0 && (time() - $lastAttemptAt) < $cooldownSeconds) {
  415. $this->addTranslatedFlash('success', 'sylius.user.verify_email_request');
  416. return $this->redirectToRoute('sylius_shop_login');
  417. }
  418. $request->getSession()->set($cooldownKey, time());
  419. }
  420. $customer = $this->container->get('sylius.repository.customer')->findOneBy(['email' => $email]);
  421. $user = null !== $customer ? $customer->getUser() : null;
  422. if (null !== $user && null === $user->getVerifiedAt()) {
  423. $sender = $this->container->get('sylius.email_sender');
  424. $logpath = 'https://www.hephaestos.eu/build/img/baniere1024x768.png';
  425. $localeCode = $this->get('sylius.context.locale')->getLocaleCode();
  426. $channel = $this->container->get('sylius.context.channel')->getChannel();
  427. $sender->send(\Sylius\Bundle\UserBundle\Mailer\Emails::EMAIL_VERIFICATION_TOKEN, [$email], ['user' => $user, 'channel' => $channel, 'localeCode' => $localeCode, 'image_src' => $logpath]);
  428. }
  429. $this->addTranslatedFlash('success', 'sylius.user.verify_email_request');
  430. return $this->redirectToRoute('sylius_shop_login');
  431. }
  432. /**
  433. * {@inheritdoc}
  434. */
  435. protected function addTranslatedFlash(string $type, string $message): void
  436. {
  437. $translator = $this->container->get('translator');
  438. $this->container->get('session')->getFlashBag()->add($type, @$this->translator->trans($message, [], 'flashes'));
  439. }
  440. }