src/StartPlatz/Bundle/EventBundle/Controller/DefaultController.php line 190

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\StartPlatz\Bundle\EventBundle\Controller;
  3. /**
  4.  * CRITICAL DEPENDENCY WARNING - APPLICATION SYSTEM INTEGRATION
  5.  * 
  6.  * This controller has HEAVY DEPENDENCIES on the Application system from StartupBundle.
  7.  * Event registration is the MOST IMPORTANT registration method for events and relies
  8.  * extensively on the Application infrastructure.
  9.  * 
  10.  * DO NOT modify Application-related code without testing ALL event registration scenarios:
  11.  * - Anonymous (non-member) event registration
  12.  * - Member event registration  
  13.  * - Paid event registration with Stripe
  14.  * - Free event registration
  15.  * - Multi-batch event registration
  16.  * - Event registration with validation workflows
  17.  * 
  18.  * Critical dependencies:
  19.  * 1. Application Entity & Repository (30+ method calls)
  20.  *    - createApplicationByApplyForm()
  21.  *    - setApplication()
  22.  *    - updateApplicationByMember()
  23.  *    - setApplicationDone()
  24.  *    - cleanApplicationData()
  25.  *    - And many more...
  26.  * 
  27.  * 2. ApplicationType Form
  28.  *    - Used for all event registration forms
  29.  *    - Field definitions must remain compatible
  30.  * 
  31.  * 3. RheinlandPitchBundle Templates
  32.  *    - StartPlatzRheinlandPitchBundle/Apply/_edit.registration.event.widget.html.twig
  33.  *    - Shared validation and workflow logic
  34.  * 
  35.  * 4. Shared Workflows
  36.  *    - Email validation (validateEmail/validateLogin)
  37.  *    - Member creation and assignment
  38.  *    - Payment processing
  39.  *    - Confirmation emails
  40.  * 
  41.  * Key methods with Application dependencies:
  42.  * - eventSingleAction() - Main event page with registration
  43.  * - getOrCreateApplication() - Application creation/retrieval
  44.  * - updateApplicationWithMemberIdAndTeamId() - Member linking
  45.  * - validateEventApplication() - Validation workflows
  46.  * - setEventApplicationDone() - Completion logic
  47.  * - handleAction() - Payment and status management
  48.  * 
  49.  * See also:
  50.  * - StartupBundle/Repository/ApplicationRepository.php
  51.  * - RheinlandPitchBundle/Controller/ApplyController.php
  52.  * - Documentation: /doc/claude-files/application-process.md#event-integration
  53.  */
  54. use App\StartPlatz\Bundle\AlphaBundle\Entity\Page;
  55. use App\StartPlatz\Bundle\ApiBundle\MemberHubspotService;
  56. use App\StartPlatz\Bundle\FeedbackBundle\CallbackService;
  57. use App\StartPlatz\Bundle\FeedbackBundle\Form\ImportFormType;
  58. use App\StartPlatz\Bundle\MailmanBundle\Entity\MailTemplate;
  59. use App\StartPlatz\Bundle\MemberBundle\Entity\Member;
  60. use App\StartPlatz\Bundle\MemberBundle\Entity\Option;
  61. use App\StartPlatz\Bundle\MemberBundle\Entity\Product;
  62. use App\StartPlatz\Bundle\MemberBundle\Entity\Service;
  63. use App\StartPlatz\Bundle\MemberBundle\Entity\Team;
  64. use App\StartPlatz\Bundle\MetaBundle\Entity\Attribute;
  65. use App\StartPlatz\Bundle\MetaBundle\Entity\Tag;
  66. use App\StartPlatz\Bundle\MonsumBundle\Entity\Customer;
  67. use App\StartPlatz\Bundle\StartupBundle\Entity\Application;
  68. use App\StartPlatz\Bundle\StartupBundle\Entity\Batch;
  69. use App\StartPlatz\Bundle\StartupBundle\Entity\Program;
  70. use App\StartPlatz\Bundle\StartupBundle\Form\ApplicationType;
  71. use App\StartPlatz\Bundle\StatisticBundle\Entity\Statistic;
  72. use App\StartPlatz\Bundle\UserBundle\Entity\User;
  73. use App\StartPlatz\Bundle\UserBundle\LoginService;
  74. use App\StartPlatz\Bundle\WebsiteBundle\Utility\Utility;
  75. use DateTime;
  76. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  77. use Symfony\Component\Mailer\MailerInterface;
  78. use Symfony\Component\Routing\Annotation\Route;
  79. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
  80. use App\StartPlatz\Bundle\EventBundle\Entity\Event;
  81. use App\StartPlatz\Bundle\EventBundle\Entity\EventRepository;
  82. use App\StartPlatz\Bundle\EventBundle\Model\EventBaseRepository;
  83. use App\StartPlatz\Bundle\EventBundle\Model\EventCommonRepository;
  84. use App\StartPlatz\Bundle\WebsiteBundle\MenuTranslationService;
  85. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  86. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  87. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  88. use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  89. use Symfony\Component\Form\Extension\Core\Type\TextType;
  90. use Symfony\Component\Form\FormFactoryInterface;
  91. use Symfony\Component\HttpFoundation\Request;
  92. use Symfony\Component\HttpFoundation\Response;
  93. use Symfony\Component\HttpFoundation\JsonResponse;
  94. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  95. use Psr\Log\LoggerInterface;
  96. use App\StartPlatz\Bundle\EventBundle\Service\MailTemplateService;
  97. class DefaultController extends AbstractController
  98. {
  99.     public function __construct(
  100.         private readonly SessionInterface $session,
  101.         private readonly FormFactoryInterface $formFactory,
  102.         private readonly MenuTranslationService $menuTranslationService,
  103.         private readonly CallbackService $callbackService,
  104.         private readonly MemberHubspotService $memberHubspotService,
  105.         private readonly MailerInterface $mailer,
  106.         private readonly LoginService $loginService,
  107.         private readonly EventCommonRepository $eventCommonRepository,
  108.         private readonly UrlGeneratorInterface  $urlGenerator,
  109.         private readonly EventBaseRepository $eventBaseRepository,
  110.         private readonly LoggerInterface $logger,
  111.         private readonly MailTemplateService $mailTemplateService
  112.     ) {
  113.     }
  114.     /**
  115.      * @return EventCommonRepository
  116.      */
  117.     protected function getEventComRep()
  118.     {
  119.         return $this->eventCommonRepository;
  120.     }
  121.     /**
  122.      * @return EventBaseRepository
  123.      */
  124.     protected function getEventBaseRepository()
  125.     {
  126.         return $this->eventBaseRepository;
  127.     }
  128.     /** @return EventRepository */
  129.     protected function getEventRepository(): EventRepository
  130.     {
  131.         $em $this->getDoctrine()->getManager();
  132.         return $em->getRepository(Event::class);
  133.     }
  134.     /**
  135.      */
  136.     public function showEventsAction($time "upcoming"$term=''$title=""$text '')
  137.     {
  138.         // sanitize variable from wordPress
  139.         $time str_replace('&#8220;''', (string) $time);
  140.         $term str_replace('&#8220;''', (string) $term);
  141.         $title str_replace('&#8220;''', (string) $title);
  142.         $text str_replace('&#8220;''', (string) $text);
  143.         $terms explode('#'$term);
  144.         $events $this->getEventRepository()->findPublicByTerms($terms, [], ($time == 'upcoming') ? 'future' 'past');
  145.         return $this->render('@StartPlatzEventBundle/Default/show_events.html.twig', [
  146.             'events' => $events,
  147.             'title'  => $title,
  148.             'templateVars' => [],
  149.             'term' => $term,
  150.             'text'=>$text,
  151.         ]);
  152.     }
  153.     /**
  154.      */
  155.     public function showFutureEventsAction($term 'startup'$variant 'default')
  156.     {
  157.         $term ltrim((string) $term'#');
  158.         $events $this->getEventRepository()->findPublicEventsByTerm($term);
  159.         $view '@StartPlatzEventBundle/Default/show_future_events.html.twig';
  160.         if ($variant == 'black-rows') {
  161.             $view '@StartPlatzEventBundle/Default/show_future_events.black-rows.html.twig';
  162.         }
  163.         return $this->render($view, [
  164.             'events' => $events,
  165.             'term'   => $term,
  166.             'templateVars' => [],
  167.         ]);
  168.     }
  169.     /**
  170.      */
  171.     public function showNextEventsAction(Request $request$limit 3)
  172.     {
  173.         $events $this->getEventRepository()->findNextEvents($limit);
  174.         return $this->render('@StartPlatzEventBundle/Default/show_next_events.html.twig', [
  175.             'events' => $events,
  176.             'templateVars' => [],
  177.             'lang' =>  $this->menuTranslationService->getLang($request->server->get('REDIRECT_URL')),
  178.         ]);
  179.     }
  180.     /**
  181.      */
  182.     public function showListGroupEventsAction(Request $request$limit 15)
  183.     {
  184.         $events $this->getEventRepository()->findNextEvents($limit);
  185.         return $this->render('@StartPlatzEvent/Default/show_list_group_events.html.twig', [
  186.             'events' => $events,
  187.             'templateVars' => [],
  188.             'lang' =>  $this->menuTranslationService->getLang($request->server->get('REDIRECT_URL')),
  189.         ]);
  190.     }
  191.     /**
  192.      */
  193.     public function showPastEventsAction($term)
  194.     {
  195.         $events $this->getEventRepository()->findPastPublicEventsByTerm($term);
  196.         return $this->render('@StartPlatzEventBundle/Default/show_past_events.html.twig', [
  197.             'events' => $events,
  198.             'term' => $term,
  199.             'templateVars' => [],
  200.         ]);
  201.     }
  202.     /**
  203.      * @Route("/events", name="events_list")
  204.      * @Route("/en/events", name="events_list_english")
  205.      * @Route("/fr/evenements", name="events_list_french")
  206.      * @Route("/events/bevorstehend/")
  207.      * @Route("/events/list")
  208.      * @Route("/en/events/list")
  209.      * @Route("/fr/evenements/list")
  210.      * @Route("/events/embed/list")
  211.      * @Route("/en/events/embed/list")
  212.      * @Route("/fr/evenements/embed/list")     */
  213.     public function eventsAction(Request $request)
  214.     {
  215.         $templateVars $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
  216.         $em $this->getDoctrine()->getManager();
  217.         // Get the current path
  218.         $path $request->getPathInfo();
  219.         // Check if the path starts with '/en/' or '/fr/'
  220.         if (strpos($path'/en/') === 0) {
  221.             $lang 'EN';
  222.         } elseif (strpos($path'/fr/') === 0) {
  223.             $lang 'FR';
  224.         } else {
  225.             $lang 'DE';
  226.         }
  227.         // Check if the path contains  '/en/'
  228.         $embed false;
  229.         if (str_contains($path'/embed/') ) {
  230.             $embed true;
  231.         }
  232.         // Fetch u tags
  233.         $uTags $em->getRepository(Tag::class)->findBy(['domain' => 'event''isEditableByUser' => 1], ['tag' => 'ASC']);
  234.         // Convert to key-value array
  235.         $uTagsArray array_map(function (Tag $entity) {
  236.             return [
  237.                 'tag' => $entity->getTag(),  // Dies entspricht dem 'slug' in deiner URL-Struktur
  238.                 'label' => $entity->getLabel()
  239.             ];
  240.         }, $uTags);
  241.         // Fetch w tags
  242.         $wTags $em->getRepository(Tag::class)->findBy(['domain' => 'event''isUsedInWordPress' => 1], ['tag' => 'ASC']);
  243.         // Convert to key-value array
  244.         $wTagsArray array_map(function (Tag $entity) {
  245.             return [
  246.                 'tag' => $entity->getTag(),  // Dies entspricht dem 'slug' in deiner URL-Struktur
  247.                 'label' => $entity->getLabel()
  248.             ];
  249.         }, $wTags);
  250.         $additionalEvents = [];
  251.         $queryString '';
  252.         // Check for location filter first (location-based filtering)
  253.         if (isset($templateVars['location']) and $location $templateVars['location']) {
  254.             $futureEvents $this->getEventRepository()->findPublicByLocationWithBatches($location$this->getEventComRep()->setBaseCriteria($templateVars), 'future');
  255.             $pastEvents   $this->getEventRepository()->findPublicByLocationWithBatches($location$this->getEventComRep()->setBaseCriteria($templateVars), 'past');
  256.             $moreFutureEvents $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
  257.         }
  258.         // Check for tag filter (tag-based filtering)
  259.         elseif (isset($templateVars['tag']) and $tag $templateVars['tag']) {
  260.             $futureEvents $this->getEventRepository()->findPublicByTagWithBatches('#' $tag$this->getEventComRep()->setBaseCriteria($templateVars), 'future');
  261. /*
  262.  * falls einer der zielgruppen tags == uTags gewählt ist
  263.  * dann hole auch die events für alle fTags und startplatz-ai-hub
  264.  */
  265. // Extrahiere alle 'tag' Werte aus dem $uTagsArray
  266.             $uTagsSlugs array_column($uTagsArray'tag');
  267. // Ãœberprüfe, ob der gewählte $tag in den $uTagsSlugs enthalten ist
  268.             $isUTag in_array($tag$uTagsSlugs);
  269.             if ($isUTag) {
  270.                 // Fetch f tags
  271.                 $fTags $em->getRepository(Tag::class)->findBy(['domain' => 'event''isFavorite' => 1], ['tag' => 'ASC']);
  272.                 // Extract just the tags to a simple array
  273.                 $fTagsList array_map(function (Tag $entity) {
  274.                     return $entity->getTag(); // Dies gibt nur den 'tag' zurück
  275.                 }, $fTags);
  276.                 $additionalEvents $this->getEventRepository()->findPublicByTagsExcludingTag($fTagsList'#' $tag$this->getEventComRep()->setBaseCriteria($templateVars), 'future');
  277.             }
  278.             $pastEvents   $this->getEventRepository()->findPublicByTagWithBatches('#' $tag$this->getEventComRep()->setBaseCriteria($templateVars), 'past');
  279.             $moreFutureEvents $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
  280.         }
  281.         // No filter - show all events
  282.         else {
  283.             $futureEvents $this->getEventRepository()->findPublicByWithBatches($queryString$this->getEventComRep()->setBaseCriteria($templateVars), 'future');
  284.             $moreFutureEvents $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
  285.             $pastEvents   $this->getEventRepository()->findPublicByWithBatches($queryString$this->getEventComRep()->setBaseCriteria($templateVars), 'past');
  286.         }
  287.         if ($request->get('format') === 'json') {
  288.             $events = [];
  289.             foreach ($futureEvents as $event) {
  290.                 $events[] = $event->toArray();
  291.             }
  292.             return Response::create(
  293.                 json_encode($events),
  294.                 200,
  295.                 [
  296.                     'Content-Type' => 'application/json',
  297.                     'Access-Control-Allow-Origin' => '*',
  298.                 ]
  299.             );
  300.         }
  301.         $targetPath $request->server->get('REDIRECT_URL');
  302.         if ($this->getUser()) {
  303.             if (!$email $this->getUser()->getEmail()) {
  304.                 $email null;
  305.             }
  306.         } else {
  307.             $email null;
  308.         }
  309.         $form $this->setFeedbackForm($targetPath$this->generateUrl('feedback_translation'), $email);
  310.         $eventTypes $em->getRepository(Tag::class)->findBy(['domain'=>'event''isThema'=>1], ['tag'=>'ASC']);
  311.         $eventTypesTags   array_map(fn (Tag $entity) => $entity->getTag(), $eventTypes);
  312.         $eventTypesLabels array_map(fn (Tag $entity) => $entity->getLabel(), $eventTypes);
  313.         $eventTypesTagLabel array_combine($eventTypesTags$eventTypesLabels);
  314.         // Fetch popular tags
  315.         $popularTags $em->getRepository(Tag::class)->findBy(['domain' => 'event''isModul' => 1], ['tag' => 'ASC']);
  316.         // Convert to key-value array
  317.         $popularTagsArray array_map(function (Tag $entity) {
  318.             return [
  319.                 'tag' => $entity->getTag(),  // Dies entspricht dem 'slug' in deiner URL-Struktur
  320.                 'label' => $entity->getLabel()
  321.             ];
  322.         }, $popularTags);
  323.         $view '@StartPlatzEventBundle/Default/events.html.twig';
  324.         if ($embed){
  325.             $view '@StartPlatzEventBundle/Default/events-embed.html.twig';
  326.         }
  327.         return $this->render($view, [
  328.             'templateVars' => $templateVars,
  329.             'lang'           => $lang,
  330.             'embed'          => $embed,
  331.             'nextConference' => $this->getEventRepository()->findNextPublicBy("Konferenz"),
  332.             'nextWorkshop'   => $this->getEventRepository()->findNextPublicBy("Workshop"),
  333.             'futureEvents'   => $futureEvents,
  334.             'pastEvents'     => $pastEvents,
  335.             'events'         => $this->getEventRepository()->findNextEvents(100),
  336.             'moreFutureEvents' => $moreFutureEvents,
  337.             'additionalEvents' => $additionalEvents,
  338.             'eventArten'         => $eventTypes,
  339.             'eventTypesTagLabel' => $eventTypesTagLabel,
  340.             'popularTagsArray'   => $popularTagsArray,
  341.             'uTagsArray'         => $uTagsArray,
  342.             'wTagsArray'         => $wTagsArray,
  343.             'form'           => $form->createView(),
  344.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  345.         ]);
  346.     }
  347.     /**
  348.      * @Route("/event-series/{seriesTag}", name="event_series")
  349.      */
  350.     public function eventSeriesAction($seriesTagRequest $request )
  351.     {
  352.         $templateVars $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
  353.         $em $this->getDoctrine()->getManager();
  354.         $eventRepo $em->getRepository(Event::class);
  355.         $batchRepo $em->getRepository(Batch::class);
  356.         // Retrieve the UTM parameters from the URL
  357.         $utmParameters = [
  358.             'utmSource'   => $request->query->get('utm_source'),
  359.             'utmMedium'   => $request->query->get('utm_medium'),
  360.             'utmCampaign' => $request->query->get('utm_campaign'),
  361.             'utmTerm'     => $request->query->get('utm_term'),
  362.             'utmContent'  => $request->query->get('utm_content'),
  363.         ];
  364.         // Fetch data related to the event series using $seriesName
  365.         // For example, using a repository method:
  366.         $nextEvent $eventRepo->findNextEventInSeries($seriesTag, new \DateTime());
  367.         // Check if the result is null (no future events)
  368.         if ($nextEvent === null) {
  369.             // Fetch the most recent past event in the series
  370.             $nextEvent $eventRepo->findMostRecentPastEventInSeries($seriesTag, new \DateTime());
  371.         }
  372.         // Initialize variables with default values
  373.         $noEventsAvailable false// Default to false, indicating events are available
  374.         $alternativeContent ''// Default empty string or a default alternative message
  375.         $nextBatch null;
  376.         $futureEventsData = [];
  377.         $pastEventsData = [];
  378.         // Proceed only if there is an event (either upcoming or past)
  379.         if ($nextEvent !== null) {
  380.             // Fetch all future events in the series, excluding the $nextEvent
  381.             $futureEvents $eventRepo->findAllFutureEventsExcluding($seriesTag$nextEvent->getId());
  382.             // Fetch all past events in the series, excluding the $nextEvent
  383.             $pastEvents $eventRepo->findAllPastEventsExcluding($seriesTag$nextEvent->getId());
  384.             // If the main event is multi-batch, get the next batch
  385.             if ($nextEvent->isMultiBatchEvent()) {
  386.                 $nextBatch $batchRepo->findNextOpenBatchByEvent($nextEvent);
  387.             }
  388.             // Get batch data for future and past events
  389.             $futureBatches $eventRepo->findAllFutureBatchesInSeries($seriesTag$nextEvent->getId());
  390.             $pastBatches $eventRepo->findAllPastBatchesInSeries($seriesTag$nextEvent->getId());
  391.             // Merge events and batches data for display
  392.             $futureEventsData $this->mergeEventsAndBatches($futureEvents$futureBatches);
  393.             $pastEventsData $this->mergeEventsAndBatches($pastEvents$pastBatches);
  394.         } else {
  395.             // No events are available, display alternative content or a message
  396.             $noEventsAvailable true;
  397.             $alternativeContent "Sorry, there are no events available for this series.";
  398.             $nextEvent = new Event();
  399.             $futureEvents = [];
  400.             $pastEvents = [];
  401.             // $alternativeContent can also be fetched from a repository if needed
  402.         }
  403.         return $this->render('@StartPlatzEventBundle/Default/event-series.html.twig', [
  404.             'event'             => $nextEvent,
  405.             'nextEvent'         => $nextEvent,          // The next or most recent event
  406.             'nextBatch'         => $nextBatch,          // The next batch for multi-batch events
  407.             'futureEvents'      => $futureEvents,    // Collection of future events, excluding $nextEvent
  408.             'pastEvents'        => $pastEvents,        // Collection of past events, excluding $nextEvent
  409.             'futureEventsData'  => $futureEventsData,  // Combined events and batches data for future
  410.             'pastEventsData'    => $pastEventsData,    // Combined events and batches data for past
  411.             'noEventsAvailable' => $noEventsAvailable,  // Flag for no events availability
  412.             'alternativeContent' => $alternativeContent// Alternative content when no events are available
  413.             'utmParameters'     => $utmParameters,
  414.             'showTeaser'         => $nextEvent->getTeaser() > '',
  415.             'batch'              => $nextBatch ?: new Batch(),
  416.             'application'        => new Application(),
  417.             'batchIsOpen'        => $nextBatch true false,
  418.             'seriesEvents'       => $futureEvents,
  419.             'promotionUrl'       => "/event-series/{$seriesTag}",
  420.             'templateVars'       => $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request),
  421.             'speakers'           => $this->getSpeakers($nextEvent),
  422.             'lang' => $lang ?? "DE",  // Default to "DE" if $lang is not set
  423.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases$request->server->get('REQUEST_URI')  ),
  424.         ]);
  425.     }
  426.     /**
  427.      * Merge events and batches data for display in series pages
  428.      * Combines single-batch events with multi-batch event batches
  429.      * 
  430.      * @param array $events Array of Event entities
  431.      * @param array $batches Array of batch data from raw SQL query
  432.      * @return array Combined and sorted data
  433.      */
  434.     private function mergeEventsAndBatches(array $events, array $batches): array
  435.     {
  436.         $merged = [];
  437.         
  438.         // Add single-batch events
  439.         foreach ($events as $event) {
  440.             if (!$event->isMultiBatchEvent()) {
  441.                 $merged[] = [
  442.                     'type' => 'event',
  443.                     'event' => $event,
  444.                     'date' => $event->getStartDate(),
  445.                     'endDate' => $event->getEndDate(),
  446.                     'title' => $event->getTitle(),
  447.                     'location' => $event->getLocation(),
  448.                     'slug' => $event->getSlug(),
  449.                     'trainer' => $event->getTrainer()
  450.                 ];
  451.             }
  452.         }
  453.         
  454.         // Add batches (which are from multi-batch events)
  455.         foreach ($batches as $batch) {
  456.             $merged[] = [
  457.                 'type' => 'batch',
  458.                 'batch_id' => $batch['batch_id'],
  459.                 'event_id' => $batch['event_id'],
  460.                 'date' => new \DateTime($batch['batch_date']),
  461.                 'title' => $batch['event_title'],
  462.                 'batch_name' => $batch['batch_name'],
  463.                 'location' => $batch['location'],
  464.                 'event_slug' => $batch['event_slug'],
  465.                 'batch_slug' => $batch['batch_slug'],
  466.                 'trainer' => $batch['trainer']
  467.             ];
  468.         }
  469.         
  470.         // Sort by date
  471.         usort($merged, function($a$b) {
  472.             return $a['date'] <=> $b['date'];
  473.         });
  474.         
  475.         return $merged;
  476.     }
  477.   
  478.     private function setFeedbackForm $targetPath$actionPath$email NULL ) {
  479.         return $this->formFactory->createNamedBuilder'feedback' )
  480.             ->setAction$actionPath )
  481.             ->add('targetPath'HiddenType::class, array( 'data' => $targetPath, ) )
  482.             ->add('email'TextType::class,  array( 'data' => $email'label' => '-''required' => false))
  483.             ->add('note'TextareaType::class, array( 'label' => '-''required' => true))
  484.             ->add('save'SubmitType::class,array('label'=>'Save'))
  485.             ->getForm();
  486.     }
  487.     /**
  488.      * @Route("/events/list/month/{year}/{month}", name="events_list_month")
  489.      */
  490.     public function listMonthAction($year$monthRequest $request)
  491.     {
  492.         $templateVars $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
  493.         if (!isset($templateVars['tag'])) {
  494.             $templateVars['tag'] = '';
  495.         }
  496.         $queryString '';
  497.         if (isset($templateVars['tag']) and $tag $templateVars['tag']) {
  498.             $events $this->getEventBaseRepository()->getEventsByMonth($this->getEventComRep()->setBaseCriteria($templateVars), $year$month);
  499.         } else {
  500.             $events $this->getEventBaseRepository()->getEventsByMonth($this->getEventComRep()->setBaseCriteria($templateVars), $year$month);
  501.         }
  502.         $targetPath $request->server->get('REDIRECT_URL');
  503.         $lang $this->menuTranslationService->getLang($targetPath);
  504.         $menuLinksDe2En $this->menuTranslationService->getMenuLinksSwitcher();
  505.         return $this->render('@StartPlatzEventBundle/Default/list_month.html.twig', [
  506.             'templateVars' => $templateVars,
  507.             'events' => $events,
  508.             'month' => $month,
  509.             'year' => $year,
  510.             'targetPath'      => $targetPath,
  511.             'moreFutureEvents' => $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars)),
  512.             'menuLinksDe2En' => $menuLinksDe2En,
  513.             'menuLinksEn2De' => array_flip($menuLinksDe2En) ,
  514.             'menuPhrases'    => $this->menuTranslationService->getMenuPhrases($lang),
  515.             'menuLinks'      => $this->menuTranslationService->getMenuLinks($lang),
  516.             'footerPhrases'  => $this->menuTranslationService->getFooterPhrases($lang),
  517.             'footerLinks'    => $this->menuTranslationService->getFooterLinks($lang),
  518.             'lang'           => $lang,
  519.         ]);
  520.     }
  521.     /** redirecting routes */
  522.     /**
  523.      * @Route("/event/corporate-services-event/")
  524.      */
  525.     public function eventRedirectAction()
  526.     {
  527.         return $this->redirect($this->generateUrl('event_show_single', ['slug' => 'best-practice-digitalisierung']));
  528.     }
  529.     private function findEventBySlug($slug)
  530.     {
  531.         $eventRepository $this->getEventRepository();
  532.         $event $eventRepository->findOneBy(['slug' => $slug]);
  533.         if (!$event) {
  534.             $querySlug '/' $slug;
  535.             $event $eventRepository->findOneBy(['slug' => $querySlug]);
  536.             if (!$event) {
  537.                 return false;
  538.             } else {
  539.                 $em $this->getDoctrine()->getManager();
  540.                 $event->setSlug($slug);
  541.                 $em->persist($event);
  542.                 $em->flush();
  543.             }
  544.         }
  545.         return $event;
  546.     }
  547.     private function findEventBySlugError($slug)
  548.     {
  549.         $eventRepository $this->getEventRepository();
  550.         $event $eventRepository->findOneBy(['slug' => $slug]);
  551.         if (!$event) {
  552.             $querySlug '/' $slug;
  553.             $event $eventRepository->findOneBy(['slug' => $querySlug]);
  554.             if (!$event) {
  555.                 $this->session->getFlashBag()->add('notice''ERROR Veranstaltung nicht gefunden');
  556.                 return $this->redirectToRoute('events_list');
  557.             } else {
  558.                 $em $this->getDoctrine()->getManager();
  559.                 $event->setSlug($slug);
  560.                 $em->persist($event);
  561.                 $em->flush();
  562.             }
  563.         }
  564.         return $event;
  565.     }
  566.     private function handleEventStatus(Event $eventRequest $request)
  567.     {
  568.         // SEO-optimized handling: Archive events are displayed normally (no redirect)
  569.         // Template will show archive banner and hide registration forms
  570.         // This preserves 100% SEO juice and backlinks
  571.         if ($event->getStatus() != 'publish' and $event->getStatus() != 'archive' and !$request->get('preview')) {
  572.             // Draft and trash events require authentication
  573.             if (!$this->getUser()) {
  574.                 $this->addFlash('notice''ERROR Veranstaltung ist nicht verfügbar');
  575.                 return $this->redirectToRoute('events_list');
  576.             }
  577.         }
  578.         return null;
  579.     }
  580.     /**
  581.      * @Route("/events/repair/", name="events_repair_format")
  582.      */
  583.     public function repairFormatAction()
  584.     {
  585.         // Format-Umwandlung - deprecated - we need to check by mysql research
  586.         $em $this->getDoctrine()->getManager();
  587.         $events $em->getRepository(Event::class)->repairContent();
  588.         $responseContent '<html><head><title>Repair Progress</title></head><body><ul>';
  589.         foreach ($events as $event) {
  590.             $responseContent .= "<li>{$event}</li>";
  591.         }
  592.         $responseContent .= '</ul></body></html>';
  593.         return new Response($responseContent);
  594.     }
  595.     ### section signup starts here ###
  596.     /**
  597.      * @Route("/ai-summer-school-single-ticket", name="ai-summer-school-single-ticket")
  598.      */
  599.     public function aisummerschoolSingleTicketAction(Request $request)
  600.     {
  601.         $em $this->getDoctrine()->getManager();
  602.         $lang 'DE';
  603.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  604.             $lang $this->menuTranslationService->getLang($targetPath);
  605.         }
  606.         return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-single-ticket.html.twig', [
  607.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  608.             'lang'            => $lang,
  609.         ]);
  610.     }
  611.     /**
  612.      * @Route("/ai-summer-school-firmen-pakete", name="ai-summer-school-firmen-pakete")
  613.      */
  614.     public function aisummerschoolFirmenPaketeAction(Request $request)
  615.     {
  616.         $em $this->getDoctrine()->getManager();
  617.         $lang 'DE';
  618.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  619.             $lang $this->menuTranslationService->getLang($targetPath);
  620.         }
  621.         return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-firmen-pakete.html.twig', [
  622.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  623.             'lang'            => $lang,
  624.         ]);
  625.     }
  626.     /**
  627.      * @Route("/ai-summer-school-stipendien", name="ai-summer-school-stipendien")
  628.      */
  629.     public function aisummerschoolStipendienAction(Request $request)
  630.     {
  631.         $em $this->getDoctrine()->getManager();
  632.         $lang 'DE';
  633.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  634.             $lang $this->menuTranslationService->getLang($targetPath);
  635.         }
  636.         return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-stipendien.html.twig', [
  637.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  638.             'lang'            => $lang,
  639.         ]);
  640.     }
  641.     /**
  642.      * @Route("/ai-summer-school-volunteers", name="ai-summer-school-volunteers")
  643.      */
  644.     public function aisummerschoolVolunteersAction(Request $request)
  645.     {
  646.         $em $this->getDoctrine()->getManager();
  647.         $lang 'DE';
  648.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  649.             $lang $this->menuTranslationService->getLang($targetPath);
  650.         }
  651.         return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-volunteers.html.twig', [
  652.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  653.             'lang'            => $lang,
  654.         ]);
  655.     }
  656.     /**
  657.      * @Route("/ai-summer-school-sponsoring", name="ai-summer-school-sponsoring")
  658.      */
  659.     public function aisummerschoolSponsoringAction(Request $request)
  660.     {
  661.         $em $this->getDoctrine()->getManager();
  662.         $lang 'DE';
  663.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  664.             $lang $this->menuTranslationService->getLang($targetPath);
  665.         }
  666.         return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-sponsoring.html.twig', [
  667.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  668.             'lang'            => $lang,
  669.         ]);
  670.     }
  671.     /**
  672.      * @Route("/signup/{slug}", name="signup_show_single")
  673.      */
  674.     public function signupSingleAction(Request $request$slug)
  675.     {
  676.         $em $this->getDoctrine()->getManager();
  677.         /** @var Program $program */
  678.         if (!$program $em->getRepository(Program::class /*Startup Program*/)->findOneBy(['slug' => 'sign-up'])) {
  679.             $this->session->getFlashBag()->add('notice''ERROR Sorry, no program found');
  680.             return $this->redirect($this->generateUrl('apply_home', []));
  681.         }
  682.         /** @var Batch $batch */
  683.         if (!$batch $em->getRepository(Batch::class)->findOneBy(['slug' => $slug'program' => $program])) {
  684.             $this->session->getFlashBag()->add('notice''ERROR Sorry, no batch found');
  685.             return $this->redirect($this->generateUrl('apply_home', []));
  686.         }
  687.         // Retrieve the UTM parameters from the URL
  688.         $utmParameters = [
  689.             'utmSource'   => $request->query->get('utm_source'),
  690.             'utmMedium'   => $request->query->get('utm_medium'),
  691.             'utmCampaign' => $request->query->get('utm_campaign'),
  692.             'utmTerm'     => $request->query->get('utm_term'),
  693.             'utmContent'  => $request->query->get('utm_content'),
  694.         ];
  695.         $lang 'DE';
  696.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  697.             $lang $this->menuTranslationService->getLang($targetPath);
  698.         }
  699.         $batchIsOpen $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
  700.         $form    null;
  701.         $program null;
  702.         $member  null;
  703.         $application null;
  704.         $product     null;
  705.         $editFeedback "";
  706.         $settings     = [];
  707.         $action       "";
  708.         $customersArr = [];
  709.         if ($batchIsOpen) {
  710.             $program $batch->getProgram();
  711.             $settings $this->getProgramSettings($program$batch);
  712.             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  713.             /** @var User $user */
  714.             if ($user $this->getUser()) {
  715.                 $member $em->getRepository(Member::class)->find($user->getMemberId());
  716.             }
  717.             $action   $request->get('action');
  718.             $application $this->getOrCreateApplication($request$batch$program$member$lang$settings$utmParametersnull);
  719.             if ($batch->getPriceInEuroCent() > 0) {
  720.                 $route                   "signup_show_single";
  721.                 $routeParameters["slug"] = $batch->getSlug();
  722.                 $application $this->handleAction($request$member$application$batch$lang$route$routeParameters$settings);
  723.                 if (is_string($application)) {
  724.                     // Handle redirect
  725.                     return $this->redirect($application);
  726.                 }
  727.             }
  728.             if ($user && in_array($action, ["needsloginvalidation""needsemailverification"])) {
  729.                 $response $this->validateEventApplication($application$member$settings$batch$lang);
  730.                 $application $response['data']['application'];
  731.             }
  732.             if ($batch->getPriceInEuroCent() > && $batch->getProductId()) {
  733.                 if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  734.                     $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  735.                     return $this->redirect($this->generateUrl('events_list'));
  736.                 }
  737.                 if ($application->getId() && isset($member) && $member->getId()) {
  738.                     $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($member$product->getAccount(), false);
  739.                 }
  740.             }
  741.             $form $this->createForm(ApplicationType::class, $application, ['settings' => $settings'program' => $program'batch' => $batch]);
  742.             $form->handleRequest($request);
  743.             ##############################################################
  744.             ##   form validation starts here                            ##
  745.             ##############################################################
  746.             if ($form->isSubmitted() && $form->isValid()) {
  747.                 $application $em->getRepository(Application::class)->cleanApplicationData($application);
  748.                 $adminEmail  $application->getEmail();
  749.                 if ($member) {
  750.                     $memberToBeRegistered $member;
  751.                     $isExistingMember     true;
  752.                 } else {
  753.                     $response $this->processRegistrationCheckMembershipExistence($application);
  754.                     $isExistingMember     $response['data']['isExistingMember'];
  755.                     $memberToBeRegistered $response['data']['memberToBeRegistered'];
  756.                 }
  757.                 $memberToBeRegistered->setIsExistingMember($isExistingMember);
  758.                 // fill application with memberId and teamId and discount information
  759.                 $application $this->updateApplicationWithMemberIdAndTeamId($application$memberToBeRegistered$batch);
  760.                 if (!$batch->isMultipleApplicationsPerMember()) {
  761.                     // check if there is already an application to this event == batch with that batchId and memberId
  762.                     if ($existingApplication $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
  763.                         // there are two applications - the latest application will always win
  764.                         $fields     Utility::getEntityFieldsArray($application, ['id','assessments''votesRegistered''applicationStatus''editStatus''editMessage''lastChangeUser','history','extraFields','lastModified','createdAt']);
  765.                         $storedData Utility::fillDataByObject($existingApplication$fields);
  766.                         $updatedData Utility::fillDataByObject($application$fields);
  767.                         $differences array_diff_assoc($updatedData$storedData);
  768.                         $application $existingApplication;
  769.                         $application $this->updateApplication($application$differences);
  770.                         $application->setIsExistingApplication(true);
  771.                     }
  772.                 }
  773.                 if (isset($settings['extraFields'])) {
  774.                     $application $this->getExtraFieldsData($application$settings$form);
  775.                 }
  776.                 $dummyEvent = new Event();
  777.                 $isPaidEvent $this->checkPaidEvent($application$batch$dummyEvent);
  778.                 $hasToPay    $this->checkPaymentException($application$memberToBeRegistered$dummyEvent$settings$batch);
  779.                 if ($isPaidEvent && $hasToPay) {
  780.                     // falls es bereits einen zugänglichen Monsum Account für diese Person gibt
  781.                     $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered$product->getAccount(), false);
  782.                     $dummyEvent = new Event();
  783.                     $application  $this->updateApplicationWithStepOneInformation($application$memberToBeRegistered$dummyEvent);
  784.                     // Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId Ã¼bergeben werden
  785.                     $application $em->getRepository(Application::class)->setApplication($application);
  786.                 } else {
  787.                     $route 'signup_show_single';
  788.                     $routeParameters $request->get('_route_params');
  789.                     $response $this->processRegistrationWithoutPaymentSimpleOptimized($isExistingMember$user$memberToBeRegistered$application$settings$lang$adminEmail$batch$route$routeParameters);
  790.                     // Bei SignUps: Prüfe ob goToPage gesetzt ist
  791.                     if ($batch->getGoToPage()) {
  792.                         // Weiterleitung zu goToPage (kann URL oder interner Pfad sein)
  793.                         return $this->redirect($batch->getGoToPage());
  794.                     }
  795.                     // Ansonsten auf SignUp-Seite bleiben - keine Weiterleitung
  796.                     $application  $response['data']['application'];
  797.                     $member       $response['data']['member'];
  798.                 }
  799.             }
  800.         }
  801.         // Default to legacy template for backward compatibility
  802.         $view '@StartPlatzEventBundle/Default/signup-single.html.twig';
  803.         // Check for pageTemplate field (new approach)
  804.         if ($batch->getPageTemplate()) {
  805.             switch ($batch->getPageTemplate()) {
  806.                 case 'modern':
  807.                 case 'flexible':
  808.                     $view '@StartPlatzEventBundle/Default/signup-single.modern.html.twig';
  809.                     break;
  810.                 case 'petition':
  811.                     $view '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
  812.                     break;
  813.                 case 'legacy':
  814.                 default:
  815.                     $view '@StartPlatzEventBundle/Default/signup-single.html.twig';
  816.                     break;
  817.             }
  818.         }
  819.         // Keep backward compatibility for existing petition SignUps using JSON settings
  820.         elseif (array_key_exists('signupTemplate'$settings)) {
  821.             $view '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
  822.         }
  823. // Build the metaData array using only the $batch object (landing page fields)
  824.         $metaData = [
  825.             // SEO Tag: Description (using landingPageDescription)
  826.             'description'       => $batch->getLandingPageDescription(),
  827.             // Canonical URL (the domain remains lowercase)
  828.             'canonical'         => 'https://www.startplatz.de/signup/' urlencode($batch->getSlug()),
  829.             // Open Graph Tags for Social Sharing (using landingPage fields)
  830.             'ogTitle'           => $batch->getLandingPageTitle(),
  831.             'ogDescription'     => $batch->getLandingPageDescription(),
  832.             'ogImage'           => $batch->getLandingPageSocialImage(),
  833.             'ogUrl'             => 'https://www.startplatz.de/signup/' urlencode($batch->getSlug()),
  834.             'ogType'            => 'website',
  835.             'ogSiteName'        => 'STARTPLATZ'// Protected trademark must be uppercase
  836.             // Twitter Card Tags (using landingPage fields)
  837.             'twitterCard'       => 'summary'// or "summary_large_image" if preferred
  838.             'twitterTitle'      => $batch->getLandingPageTitle(),
  839.             'twitterDescription'=> $batch->getLandingPageDescription(),
  840.             'twitterImage'      => $batch->getLandingPageSocialImage(),
  841.             'twitterSite'       => '@STARTPLATZ'// Protected trademark must be uppercase
  842.             // Title for the page (taken from landingPageTitle)
  843.             'title'             => $batch->getLandingPageTitle(),
  844.             // Additional Landing Page Fields
  845.             'landingPageCss'            => $batch->getLandingPageCss(),
  846.             'landingPageContent'        => $batch->getLandingPageContent(),
  847.             'landingPageJs'             => $batch->getLandingPageJs(),
  848.             'landingPageFooterTemplate' => $batch->getLandingPageFooterTemplate(),
  849.         ];
  850.         return $this->render($view, [
  851.             'batch'          => $batch,
  852.             'program'        => $program,
  853.             'settings'       => $settings,
  854.             'metaData'       => $metaData,
  855.             'form'           => $form $form->createView() : $form,
  856.             'application'    => $application,
  857.             'action'         => $action,
  858.             'batchIsOpen'    => $batchIsOpen,
  859.             'customersArr'   => $customersArr,
  860.             'member'         => $member,
  861.             'isMultiBatchEvent' => false,  // Signups have no event, so no multi-batch
  862.             'utmParameters'  => $utmParameters,
  863.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  864.             'editFeedback'   => $editFeedback,
  865.             'templateVars'   => [],
  866.             'preview'             => $request->get('preview'),
  867.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  868.             'lang'            => $lang,
  869.             'targetRoute'     => "signup_show_single",
  870.             'targetRouteSlug' => $batch->getSlug(),
  871.             'targetPath'      => $targetPath,
  872.         ]);
  873.     }
  874.     ### section signup ends here ###
  875.     private function getProgramSettings($program$batch)
  876.     {
  877.         $em $this->getDoctrine()->getManager();
  878.         $settings $em->getRepository(Application::class)->getSettingsProgram($program$batch);        return $settings;
  879.     }
  880.     private function getOrCreateApplication(Request $request$batch$program$member$lang$settings$utmParameters$event null)
  881.     {
  882.         $em $this->getDoctrine()->getManager();
  883.         $application null;
  884.         // process:stripeStarted
  885.         // Check if the application ID is provided
  886.         if ($applicationId $request->get('app')) {
  887.             $application $em->getRepository(Application::class)->find($applicationId);
  888.         }
  889.         // Check if an application exists if there is a member
  890.         if ($member) {
  891.             $application $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
  892.         }
  893.         // check if stripe was not finished and therefore we need to delete that application
  894.         $action $request->get('action');
  895.         if ($application && $application->getEditStatus() == 'process:stripeStarted' && $action != 'checkSuccess'){
  896.             $responseText $em->getRepository(Application::class)->deleteApplication($application->getId());
  897.             $application null;
  898.             $applicationId null;
  899.         }
  900.         // If no application is found, create a new one
  901.         if (!$application) {
  902.             $priceAndDiscount $em->getRepository(Member::class)->getPriceAndDiscountByBatch($member$batch);
  903.             // Pass the UTM parameters along with other necessary arguments to the createApplicationByApplyForm method
  904.             $application $em->getRepository(Application::class)->createApplicationByApplyForm(
  905.                 $program,
  906.                 $batch,
  907.                 $lang,
  908.                 $priceAndDiscount,
  909.                 $utmParameters  // This is the array you created earlier
  910.             );
  911.             if ($member) {
  912.                 $application $em->getRepository(Application::class)->updateApplicationByMember($application$member);
  913.             }
  914.         } else {
  915.             // Update lang on existing application if it differs
  916.             // This ensures language preference is maintained throughout the user journey
  917.             if ($lang && $application->getLang() !== $lang) {
  918.                 $application->setLang($lang);
  919.                 $em->persist($application);
  920.                 $em->flush();
  921.             }
  922.         }
  923.         
  924.         // Fix for single events with event-level pricing: 
  925.         // If batch has no price but event has price, use event price
  926.         if ($batch && $event && !$batch->getPriceInEuroCent() && $event->getPriceInEuroCent() > 0) {
  927.             $application->setPriceInEuroCent($event->getPriceInEuroCent());
  928.             $discountPercent $application->getDiscountPercent() ?: 0;
  929.             $realPrice $event->getPriceInEuroCent() - ($event->getPriceInEuroCent() * ($discountPercent 100));
  930.             $application->setRealPriceInEuroCent($realPrice);
  931.         }
  932.         if ($member and $application->getApplicationStatus() == 'started') {
  933.             $priceAndDiscount $em->getRepository(Member::class)->getPriceAndDiscountByBatch($member$batch);
  934.             if ($application->getRealPriceinEuroCent() != $priceAndDiscount["realPriceinEuroCent"]) {
  935.                 $application $em->getRepository(Application::class)->setDiscountAndRealPrice($application$priceAndDiscount);
  936.                 if ($application->getRealPriceinEuroCent() == 0) {
  937.                     $application $this->setEventApplicationDone($application$member$batch$lang$settings);
  938.                 }
  939.             }
  940.         }
  941.         // Gib das Application-Objekt zurück
  942.         return $application;
  943.     }
  944.     /**
  945.      * @Route("/allmeda/event/application/send-confirmation-mail/{applicationId}", name="allmeda_event_send-confirmation-mail")
  946.      * @Security("is_granted('ROLE_ADMIN')")
  947.      */
  948.     public function sendConfirmationMailViaPublicFunction($applicationId)
  949.     {
  950.         $em $this->getDoctrine()->getManager();
  951.         $application $em->getRepository(Application::class)->find($applicationId);
  952.         $member $em->getRepository(Member::class)->find($application->getMemberId());
  953.         $batch $em->getRepository(Batch::class)->find($application->getBatchId());
  954.         $feedback $this->sendConfirmationMailToApplicant(null$member, [], $application$batch);
  955.         $response "SUCCESS Confirmation Mail has been sent";
  956.         return new Response($response);
  957.     }
  958.     private function setEventApplicationDone($application$member$batch$lang$settings)
  959.     {
  960.         $em $this->getDoctrine()->getManager();
  961.         $application $em->getRepository(Application::class)->setApplicationDone($application);
  962.         $phrases $em->getRepository(Option::class)->getApplicationPhrases($batch->getProgram(), $batchfalse$lang);
  963.         switch ($lang) {
  964.             case "DE":
  965.                 $message $phrases['event_application_thankyou_sent_confirmationmail'] ?? "Danke für Deine Anmeldung.<br>Wir haben Dir eine Bestätigungsmail geschickt. Wir freuen uns auf Dich!";
  966.                 break;
  967.             case "EN":
  968.                 $message $phrases['event_application_thankyou_sent_confirmationmail'] ?? "Thanks, Your application is now approved.<br>We have sent you a confirmation mal and we are looking forward to having you!";
  969.                 break;
  970.         }
  971.         $this->session->getFlashBag()->add('notice'"SUCCESS {$message}");
  972.         $application $em->getRepository(Application::class)->setApplication($application$member);
  973.         $this->sendConfirmationMailToApplicant(null$member$settings$application$batch);
  974.         return $application;
  975.     }
  976.     private function updateApplicationWithMemberIdAndTeamId(Application $applicationMember $memberToBeRegistered$batch)
  977.     {
  978.         $em $this->getDoctrine()->getManager();
  979.         if (!$application->getMemberId()) {
  980.             $application->setMemberId($memberToBeRegistered->getId());
  981.             if ($application->getRealPriceinEuroCent() > 0) {
  982.                 $priceAndDiscount $em->getRepository(Member::class)->getPriceAndDiscountByBatch($memberToBeRegistered$batch);
  983.                 $application $em->getRepository(Application::class)->setDiscountAndRealPrice($application$priceAndDiscount);
  984.             }
  985.         }
  986.         if (!$application->getTeamId()) {
  987.             $application->setTeamId($memberToBeRegistered->getTeamId());
  988.             $application->setStartupName($memberToBeRegistered->getTeamName());
  989.         }
  990.         if (!$application->getLinkedin() && $memberToBeRegistered->getLinkedin()) {
  991.             $application->setLinkedin($memberToBeRegistered->getLinkedin());
  992.         }
  993.         if (!$application->getPhone() && $memberToBeRegistered->getPhone()) {
  994.             $application->setPhone($memberToBeRegistered->getPhone());
  995.         }
  996.         if (!$memberToBeRegistered->getIsExistingMember()){
  997.             $application->setIsNewMember(true);
  998.         } else {
  999.             $application->setIsNewMember(false);
  1000.         }
  1001.         // Transfer UTM parameters if not already set in the Member object and if they exist in the Application object
  1002.         $memberToBeRegistered $em->getRepository(Member::class)->updateMemberWithUtmParametersFromApplication($memberToBeRegistered$application);
  1003.         return $application;
  1004.     }
  1005.     /**
  1006.      * handleAction - Handles various actions for the application
  1007.      *
  1008.      * This function is called in the following contexts:
  1009.      * 1. During Signup (Line 549)
  1010.      *    - With route "signup_show_single"
  1011.      *    - Parameters include batch slug, language, etc.
  1012.      *
  1013.      * 2. During Event Handling (Line 968)
  1014.      *    - With route "event_show_single"
  1015.      *    - Parameters include event slug, language, etc.
  1016.      */
  1017.     private function handleAction(Request $request$member,Application $applicationBatch $batch$lang$route$routeParameters$settings)
  1018.     {
  1019.         $action $request->get("action");
  1020.         $em $this->getDoctrine()->getManager();
  1021.         switch ($action) {
  1022.             case "checkSuccess":
  1023.                 $provider $request->get('provider');
  1024.                 if ($provider == 'stripe'){
  1025.                     $application->setEditStatus("success:stripePaid");
  1026.                 }
  1027.                 $application->setApplicationStatus('applied');
  1028.                 $application->setHasPaid(true);
  1029.                 $application $em->getRepository(Application::class)->setApplication($application);
  1030.                 switch ($lang) {
  1031.                     case "DE":
  1032.                         $message "Danke für Deine Anmeldung.<br>Wir freuen uns auf Dich!";
  1033.                         break;
  1034.                     case "EN":
  1035.                         $message "Thanks, Your application is now approved.<br>We are looking forward to having you!";
  1036.                         break;
  1037.                 }
  1038.                 if ($productNumber $request->get('productnumber') or $provider == 'stripe'){
  1039.                     ## productNumber is only given in case of sign up
  1040.                     switch ($lang) {
  1041.                         case "DE":
  1042.                             $message "Danke für Deine Buchung.<br>Wir freuen uns auf Dich!";
  1043.                             break;
  1044.                         case "EN":
  1045.                             $message "Thanks, Your booking was successful.<br>We are looking forward to having you!";
  1046.                             break;
  1047.                     }
  1048.                     $account      $request->get('account');
  1049.                     $customerHash $request->get('customerId');
  1050.                     $subscriptionHash $request->get('subscriptionId');
  1051.                     if (!$member) {
  1052.                         $member $em->getRepository(Member::class)->find($application->getMemberId());
  1053.                     }
  1054.                     if (array_key_exists("setAiHubMembership"$settings)) {
  1055.                         // Hole den Status aus den Einstellungen
  1056.                         $aiMembershipStatus $settings["setAiHubMembership"];
  1057.                         // Setze das Start- und Enddatum basierend auf dem aktuellen Datum und dem Status
  1058.                         $aiMembershipStartDate = new DateTime();  // Setze auf aktuelles Datum und Uhrzeit
  1059.                         $aiMembershipEndDate = clone $aiMembershipStartDate;
  1060.                         if ($aiMembershipStatus === 'trial') {
  1061.                             $aiMembershipEndDate->modify('+1 month');
  1062.                         } elseif ($aiMembershipStatus === 'active') {
  1063.                             $aiMembershipEndDate->modify('+3 months');
  1064.                         }
  1065.                         // Setze den Grund für die Mitgliedschaft
  1066.                         $reason "Buy of {$productNumber}";
  1067.                         // Erstelle oder aktualisiere das Service-Objekt mit den neuen Daten
  1068.                         // (Du müsstest diese Logik entsprechend deiner Anwendung implementieren)
  1069.                         $service = new Service();  // oder hole ein existierendes Service-Objekt
  1070.                         $service->setStatus($aiMembershipStatus);
  1071.                         $service->setStartDate($aiMembershipStartDate);
  1072.                         $service->setEndDate($aiMembershipEndDate);
  1073.                         // Rufe die setAiHubMembership Funktion auf
  1074.                         $successContent[] = $em->getRepository(Member::class)->setAiHubMembership($service$member$reason);
  1075.                     }
  1076.                     $loginLink null;
  1077.                     $targetPath $this->generateUrl('x_membership_first-steps', ['productNumber' => $productNumber'account' => $account]);
  1078.                     $this->session->getFlashBag()->add('notice'$message);
  1079.                     if (!$user $this->getUser()) {
  1080.                         $loginLink $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
  1081.                         $user $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
  1082.                     }
  1083.                     $feedback $this->sendConfirmationMailToApplicant($usernull$settings$application$batch);
  1084.                     if ($provider == 'stripe'){
  1085.                         $mailContent = [
  1086.                             'applicationId' => $application->getId(),
  1087.                             'memberId' => $application->getMemberId(),
  1088.                             'editStatus' => $application->getEditStatus(),
  1089.                             'applicationStatus' => $application->getApplicationStatus(),
  1090.                             'person' => $application->getPerson(),
  1091.                             'email' => $application->getEmail(),
  1092.                             'utmSource' => $application->getUtmSource(),
  1093.                             'batchSlug' => $application->getBatchSlug(),
  1094.                         ];
  1095.                         $response Utility::sendDirectMail($this->mailer"stripe step two {$application->getBatchId()} {$application->getEmail()}"'support@startplatz.de''Monitoring Stripe''lorenz.graef@startplatz.de''Lorenz'print_r($mailContent,true), false);
  1096.                         return $application;
  1097.                     } else {
  1098.                         return isset($loginLink) ? $loginLink $targetPath;
  1099.                     }
  1100.                 } else { // if productNumber is not defined
  1101.                     if (!$user $this->getUser()) {
  1102.                         $user $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
  1103.                     }
  1104.                     $feedback $this->sendConfirmationMailToApplicant($usernull$settings$application$batch);
  1105.                 }
  1106.                 break;
  1107.             case "sendMagicLinkForPayment":
  1108.                 // create LoginLink and send email
  1109.                 $customerId  $request->get('customerId');
  1110.                 $feedbackUrl $this->sendMagicLinkForPayment($application$batch$customerId$route$routeParameters);
  1111.                 return $feedbackUrl;
  1112.             case "createConsumerMonsumAccount":
  1113.             case "useExistingCustomerAccount":
  1114.                 $customerId  $request->get('customerId');
  1115.                 $checkoutUrl $this->prepareMonsumCheckoutNewMonsumAccount($application$batch$action$route$routeParameters$customerId);
  1116.                 return $checkoutUrl;
  1117.         }
  1118.         // Return the modified $application object to the main controller
  1119.         return $application;
  1120.     }
  1121.     private function validateEventApplication(Application $applicationMember $member$settings$batchstring $lang): array
  1122.     {
  1123.         $em $this->getDoctrine()->getManager();
  1124.         $message "";
  1125.         $status "";
  1126.         // Check if a confirmation email needs to be sent
  1127.         $shouldSendConfirmationMail false;
  1128.         $feedback null;
  1129.         switch ($application->getEditStatus()) {
  1130.             case "error:validateLogin":
  1131.                 $application->setEditStatus('success:validatedLogin');
  1132.                 $application->setEditMessage("Application has been validated by {$member->getEmail()}");
  1133.                 $application $em->getRepository(Application::class)->setHistory($application$application->getEditMessage());
  1134.                 $application $em->getRepository(Application::class)->setApplication($application$member);
  1135.                 $status "success";
  1136.                 $shouldSendConfirmationMail true;
  1137.                 break;
  1138.             case "error:validateEmail":
  1139.                 $application->setEditStatus('success:validatedEmail');
  1140.                 $application->setEditMessage("Email of this Application has been validated by {$member->getEmail()}");
  1141.                 $application $em->getRepository(Application::class)->setHistory($application$application->getEditMessage());
  1142.                 if ($application->getApplicationStatus() == 'started') {
  1143.                     $application->setApplicationStatus('applied');
  1144.                 }
  1145.                 $application $em->getRepository(Application::class)->setApplication($application$member);
  1146.                 $status "success";
  1147.                 $shouldSendConfirmationMail true;
  1148.                 break;
  1149.         }
  1150.         if ($shouldSendConfirmationMail) {
  1151.             // Logic to send confirmation mail
  1152.             $feedback $this->sendConfirmationMailToApplicant(null$member$settings$application$batch);
  1153.         }
  1154.         if ($status == "success") {
  1155.             $application $em->getRepository(Application::class)->setApplicationApplied($application$member);
  1156.             switch ($lang) {
  1157.                 case "DE":
  1158.                     $message "SUCCESS Danke, Deine Anmeldung ist jetzt bestätigt. Wir freuen uns auf Dich!";
  1159.                     if ($feedback){
  1160.                         $message .= "<br>Wir haben Dir eine Bestätigungsmail geschickt. Bitte prüfe Deinen Posteingang.";
  1161.                     }
  1162.                     break;
  1163.                 case "EN":
  1164.                     $message "SUCCESS Thanks, Your application is now approved. We are looking forward to having you!";
  1165.                     if ($feedback){
  1166.                         $message .= "<br>We have sent you a confirmation mail. Please check your inbox!";
  1167.                     }
  1168.                     break;
  1169.             }
  1170.             $this->session->getFlashBag()->add('notice'$message);
  1171.         }
  1172.         return [
  1173.             'data' => [
  1174.                 'application' => $application,
  1175.             ],
  1176.             'message' => $message,
  1177.             'status' => $status,
  1178.         ];
  1179.     }
  1180.     private function checkPaidEvent(Application $application$batch false$event false): bool
  1181.     {
  1182.         // First check: Application has a price (Monsum payment)
  1183.         if ($application->getRealPriceInEuroCent() !== null && $application->getRealPriceInEuroCent() > 0) {
  1184.             return true;
  1185.         }
  1186.         
  1187.         // Always prioritize batch payment settings
  1188.         if ($batch) {
  1189.             // Check if batch has a price or Stripe price ID
  1190.             if ($batch->getPriceInEuroCent() > || $batch->getStripePriceId() > '') {
  1191.                 return true;
  1192.             }
  1193.         }
  1194.         
  1195.         // Legacy fallback: Check event payment settings for backward compatibility
  1196.         // This can be removed after all single-batch events are migrated
  1197.         if ($event) {
  1198.             if ($event->getStripePriceId() > '' || $event->getPriceInEuroCent() > 0) {
  1199.                 return true;
  1200.             }
  1201.         }
  1202.         return false;
  1203.     }
  1204.     private function checkPaymentException(Application $application$member false$event false$settings false$batch false): bool
  1205.     {
  1206.         if ($member) {
  1207.             // Check batch-specific KI Campus setting first for multi-batch events
  1208.             $isFreeForKiCampus false;
  1209.             if ($batch && $batch->getIsFreeForKiCampus() !== null) {
  1210.                 // Use batch-specific setting if available
  1211.                 $isFreeForKiCampus $batch->getIsFreeForKiCampus();
  1212.             } elseif ($event) {
  1213.                 // Fall back to event setting
  1214.                 $isFreeForKiCampus $event->getIsFreeForKiCampus();
  1215.             }
  1216.             
  1217.             $this->logger->info('checkPaymentException: KI Campus check', [
  1218.                 'member_id' => $member->getId(),
  1219.                 'member_email' => $member->getEmail(),
  1220.                 'member_tags' => $member->getTags(),
  1221.                 'has_ki_campus_tag' => str_contains($member->getTags() ?? ''"#ki-campus"),
  1222.                 'isFreeForKiCampus' => $isFreeForKiCampus,
  1223.                 'batch_setting' => $batch $batch->getIsFreeForKiCampus() : 'no batch',
  1224.                 'event_setting' => $event $event->getIsFreeForKiCampus() : 'no event',
  1225.             ]);
  1226.             
  1227.             if ($isFreeForKiCampus) {
  1228.                 if (str_contains($member->getTags() ?? ''"#ki-campus")) {
  1229.                     $this->session->getFlashBag()->add('notice'"SUCCESS Du bist Mitglied im KI Campus und kannst kostenlos teilnehmen");
  1230.                     $this->logger->info('Payment exception granted: KI Campus member');
  1231.                     return false// no payment necessary for ki campus members
  1232.                 }
  1233.             }
  1234.             // Check batch-specific Community setting first for multi-batch events
  1235.             $isFreeForCommunity false;
  1236.             if ($batch && $batch->getIsFreeForCommunity() !== null) {
  1237.                 // Use batch-specific setting if available
  1238.                 $isFreeForCommunity $batch->getIsFreeForCommunity();
  1239.             } elseif ($event) {
  1240.                 // Fall back to event setting
  1241.                 $isFreeForCommunity $event->getIsFreeForCommunity();
  1242.             }
  1243.             
  1244.             $this->logger->info('checkPaymentException: Community check', [
  1245.                 'member_id' => $member->getId(),
  1246.                 'member_email' => $member->getEmail(),
  1247.                 'member_tags' => $member->getTags(),
  1248.                 'isFreeForCommunity' => $isFreeForCommunity,
  1249.                 'batch_setting' => $batch $batch->getIsFreeForCommunity() : 'no batch',
  1250.                 'event_setting' => $event $event->getIsFreeForCommunity() : 'no event',
  1251.             ]);
  1252.             
  1253.             if ($isFreeForCommunity) {
  1254.                 $communityTags = ['#physical-member''#community-member''#can-get-transponder'];
  1255.                 $memberTags $member->getTags() ?? '';
  1256.                 
  1257.                 foreach ($communityTags as $tag) {
  1258.                     if (str_contains($memberTags$tag)) {
  1259.                         $this->session->getFlashBag()->add('notice'
  1260.                             "SUCCESS: Du bist Community-Mitglied und kannst kostenlos teilnehmen");
  1261.                         $this->logger->info('Payment exception granted: Community member', [
  1262.                             'member_id' => $member->getId(),
  1263.                             'matched_tag' => $tag
  1264.                         ]);
  1265.                         return false// no payment necessary for community members
  1266.                     }
  1267.                 }
  1268.             }
  1269.             if ($event && isset($settings['freeIfTagMatches'])) {
  1270.                 if (str_contains($member->getTags() ?? ''$settings['freeIfTagMatches'])) {
  1271.                     $tagName str_replace('#'''$settings['freeIfTagMatches']); // Entferne '#' falls vorhanden
  1272.                     $this->session->getFlashBag()->add('notice'"SUCCESS Du bist Mitglied und in unserer Datenbank mit dem Tag {$tagName} vermerkt und kannst daher kostenlos teilnehmen");
  1273.                     return false// no payment necessary for members with matching tag
  1274.                 }
  1275.             }
  1276.             if ($application->getHasVolunteerAgreed()) {
  1277.                 $this->session->getFlashBag()->add('notice'"SUCCESS Du bist als Volunteer angemeldet und kannst kostenlos teilnehmen");
  1278.                 return false// no payment necessary for volunteers
  1279.             }
  1280.             if ($application->getDiscountPercent() == 100) {
  1281.                 $this->session->getFlashBag()->add('notice'"SUCCESS Du hast einen 100% Discount Code und kannst kostenlos teilnehmen");
  1282.                 return false// no payment necessary with 100% discount
  1283.             }
  1284.         }
  1285.         $this->logger->info('checkPaymentException: Payment required', [
  1286.             'member_exists' => $member true false,
  1287.             'reason' => 'No payment exception criteria met'
  1288.         ]);
  1289.         return true// payment necessary for all other cases
  1290.     }
  1291.     /**
  1292.      * @Route("/event-confirmation/{slug}/", name="event_confirmation")
  1293.      */
  1294.     public function eventConfirmationAction(Request $request$slug)
  1295.     {
  1296.         $em $this->getDoctrine()->getManager();
  1297.         $event $this->findEventBySlug($slug);
  1298.         if (!$event) {
  1299.             $this->addFlash('notice''ERROR Event not found');
  1300.             return $this->redirectToRoute('events_list');
  1301.         }
  1302.         $seriesEvents = [];
  1303.         if ($seriesTag $event->getSeriesTag()) {
  1304.             $seriesEvents $this->getEventRepository()->findBySeriesTag($seriesTag'', ['startDate' => 'DESC']);
  1305.         }
  1306.         $embed $request->get('embed');
  1307.         $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'active''access' =>'anonymous']);
  1308.         $program $batch->getProgram();
  1309.         $action   $request->get('action');
  1310.         $applicationId $request->get('app');
  1311.         $route                   "event_confirmation";
  1312.         $routeParameters["slug"] = $event->getSlug();
  1313.         $settings = [];
  1314.         $member   false;
  1315.         $application = new Application();
  1316.         // Load application first, then determine language
  1317.         if ($applicationId){
  1318.             if (!$application $em->getRepository(Application::class)->find($applicationId)){
  1319.                 $this->addFlash('notice''ERROR Application with ID '.$applicationId.' not found');
  1320.                 return $this->redirectToRoute('events_list');
  1321.             }
  1322.         }
  1323.         // Language detection: Priority is Application > Event > Default DE
  1324.         // This ensures user's language choice persists through Stripe redirect
  1325.         $lang = ($application && $application->getId() && $application->getLang())
  1326.             ? $application->getLang()
  1327.             : ($event->getLang() ?? 'DE');
  1328.         // Handle action after language is determined
  1329.         if ($applicationId && $action){
  1330.             $application $this->handleAction($request$member$application$batch$lang$route$routeParameters$settings);
  1331.             if (is_string($application)) {
  1332.                 // Handle redirect
  1333.                 return $this->redirect($application);
  1334.             }
  1335.         }
  1336.         // Generate the absolute base URL based on the route and parameters
  1337.         $baseURL $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
  1338.         // UTM parameters for the promotion URL
  1339.         $promotionUtmParameters = [
  1340.             'utm_source'   => 'affiliate',
  1341.             'utm_medium'   => is_numeric($application->getMemberId()) ? $application->getMemberId() : '',
  1342.             'utm_campaign' => 'event:' $event->getId(),
  1343.             'utm_term'     => '',
  1344.             'utm_content'  => '',
  1345.         ];
  1346.         $promotionUrl $em->getRepository(Batch::class)->generatePromotionUrl($baseURL$promotionUtmParameters);
  1347.         $view '@StartPlatzEventBundle/Default/event-confirmation.html.twig';
  1348.         if ($embed){
  1349.             $view '@StartPlatzEventBundle/Default/event-confirmation.embed.html.twig';
  1350.         }
  1351.         if ($event->getTwigFile() == 'winter-2025' ){
  1352.             $view '@StartPlatzEventBundle/Default/event-confirmation.winter-2025.html.twig';
  1353.         }
  1354.         if ($event->getTwigFile() == 'style-ecodynamics' ){
  1355.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ecodynamics.html.twig';
  1356.         }
  1357.         if ($event->getTwigFile() == 'style-ai-hub' ){
  1358.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
  1359.         }
  1360.         if ($event->getTwigFile() == 'style-startplatz' ){
  1361.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
  1362.         }
  1363.         return $this->render($view, [
  1364.             'event'         => $event,
  1365.             'seriesEvents'  => $seriesEvents,
  1366.             'promotionUrl'   => $promotionUrl,
  1367.             'batch'          => $batch,
  1368.             'settings'       => $settings,
  1369.             'application'    => $application,
  1370.             'action'         => $action,
  1371.             'member'         => $member,
  1372.             'speakers'       => $this->getSpeakers($event),
  1373.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  1374.             'templateVars'   => [],
  1375.             'embed'          => $embed,
  1376.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  1377.             'lang'            => $lang,
  1378.             'isMultiBatchEvent' => $event->isMultiBatchEvent(),
  1379.         ]);
  1380.     }
  1381.     /**
  1382.      * @Route("/event/{slug}/", name="event_show_single")
  1383.      * @Route("/event/{slug}/{date}", name="event_show_single_by_date", requirements={"date"="\d{4}-\d{2}-\d{2}"})
  1384.      */
  1385.     public function eventSingleAction(Request $request$slug$date null)
  1386.     {
  1387.         ##event-single
  1388.         if ($slug == 'ai-bootcamp-koeln-2024-01-18') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-2024-01-29')) );
  1389.         if ($slug == 'ai-summer-school-koeln-2208') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-1109')) );
  1390.         if ($slug == 'ai-summer-school-duesseldorf-2908') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-duesseldorf-2609')) );
  1391.         if ($slug == 'ai-disruptme-marketing-in-unternehmen-koeln-2023-10-19') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-marketing-in-unternehmen-koeln-2024-01-31')) );
  1392.         if ($slug == 'ai-disruptme-publisher-koeln-2023-10-18') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-publisher-duesseldorf-2024-03-20')) );
  1393.         if ($slug == 'ai-disruptme-travel-tourismus-duesseldorf-2023-11-09') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-travel-tourismus-duesseldorf-2024-02-22')) );
  1394.         if ($slug == 'genai-ki-app-no-code-backend-fuer-ki-anwendung-2024-05-28') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'genai-ki-anwendung-bauen-frontend-ux-ui-2024-05-21')) );
  1395.         if ($slug == 'ai-barcamp-generative-ki') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-barcamp-logistik')) );
  1396.         if ($slug == 'genai-generative-ki-zertifikat-ki-kompetenz-erwerben-2025-05-20') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ki-kompetenzpflicht-zertifikat-artikel-4-2025-05-20')) );
  1397.         // Retrieve the UTM parameters from the URL
  1398.         $utmParameters = [
  1399.             'utmSource'   => $request->query->get('utm_source') ?? 'homepage',
  1400.             'utmMedium'   => $request->query->get('utm_medium'),
  1401.             'utmCampaign' => $request->query->get('utm_campaign'),
  1402.             'utmTerm'     => $request->query->get('utm_term'),
  1403.             'utmContent'  => $request->query->get('utm_content'),
  1404.         ];
  1405.         $em $this->getDoctrine()->getManager();
  1406.         /** @var Event $event */
  1407.         $event $this->findEventBySlug($slug);
  1408.         if (!$event) {
  1409.             $this->addFlash('notice''ERROR Veranstaltung nicht gefunden');
  1410.             return $this->redirectToRoute('events_list');
  1411.         }
  1412.         // Handle event visibility: redirect if not published or previewed without user session.
  1413.         $response $this->handleEventStatus($event$request);
  1414.         if ($response) {
  1415.             return $response;
  1416.         }
  1417.         $seriesEvents = [];
  1418.         if ($seriesTag $event->getSeriesTag()) {
  1419.             $seriesEvents $this->getEventRepository()->findBySeriesTag($seriesTag'', ['startDate' => 'DESC']);
  1420.         }
  1421.         // Language detection with priority: URL parameter > Event default
  1422.         // This ensures ?lang=en parameter works for bilingual events
  1423.         $lang strtoupper($request->query->get('lang''')) ?: ($event->getLang() ?? 'DE');
  1424.         $embed $request->get('embed');
  1425.         $eventbriteId   $event->getEventbriteId();
  1426.         $isEventbriteOnly $event->getTicketing() > '';
  1427.         $applicationUrl $event->getApplicationUrl();
  1428.         
  1429.         // Multi-batch aware logic
  1430.         $futureBatches = [];
  1431.         $allBatches = [];
  1432.         
  1433.         if ($event->isMultiBatchEvent()) {
  1434.             $allBatches $em->getRepository(Batch::class)->findAllBatchesByEvent($event);
  1435.             
  1436.             if ($date) {
  1437.                 // User selected a specific date
  1438.                 $batch $em->getRepository(Batch::class)->findBatchByEventAndDate($event$date);
  1439.                 if (!$batch) {
  1440.                     // Invalid date, redirect to main event page
  1441.                     $this->addFlash('notice''Der gewählte Termin ist nicht verfügbar.');
  1442.                     return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  1443.                 }
  1444.             } else {
  1445.                 // No date selected, show next upcoming batch
  1446.                 $batch $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
  1447.             }
  1448.             
  1449.             // Get all future batches for the selector
  1450.             $futureBatches $em->getRepository(Batch::class)->findFutureBatchesByEvent($event10);
  1451.             // Get past batches for archive display
  1452.             $pastBatches $em->getRepository(Batch::class)->findPastBatchesByEvent($event10);
  1453.         } else {
  1454.             // Single batch event - keep existing logic
  1455.             // Try to find active batch first, then fall back to completed batch for display
  1456.             $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'active''access' =>'anonymous']);
  1457.             if (!$batch) {
  1458.                 // No active batch found, look for completed batch (for past events that need to display)
  1459.                 $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'completed''access' =>'anonymous']);
  1460.             }
  1461.             if ($batch) {
  1462.                 $allBatches = [$batch];
  1463.             }
  1464.         }
  1465.         
  1466.         $batchIsOpen $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
  1467.         $form    null;
  1468.         $program null;
  1469.         $application null;
  1470.         $product     null;
  1471.         $editFeedback "";
  1472.         $settings     = [];
  1473.         $action       "";
  1474.         $customersArr = [];
  1475.         $member null;
  1476.         /** @var User $user */
  1477.         if ($user $this->getUser()) {
  1478.             $member $em->getRepository(Member::class)->find($user->getMemberId());
  1479.         }
  1480.         if ($batch && !$isEventbriteOnly) {
  1481.             $program $batch->getProgram();
  1482.         }
  1483.         if ($batchIsOpen && !$isEventbriteOnly) {
  1484.             $settings $this->getProgramSettings($program$batch);
  1485.             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  1486.             $action   $request->get('action');
  1487.             $referral $request->get('referral');
  1488.             // do some exceptions
  1489.             if ($member && isset($settings['batchType'])) {
  1490.                 if ($settings['batchType'] == 'memberDocumentation'){
  1491.                     // check if member
  1492.                     if ($em->getRepository(Application::class)->findApplicationsByAiFridayBatchSlugAndMemberId($member->getId()))
  1493.                     {
  1494.                         if (!$em->getRepository(Application::class)->findOneBy(['memberId'=>$member->getId(), 'batchId' => $batch->getId()])){
  1495.                             $memberTeam  $em->getRepository(Team::class)->find($member->getTeamId());
  1496.                             $adminEmail  $member->getEmail();
  1497.                             $application $em->getRepository(Application::class)->createApplicationIfNotExists($program$batch$memberTeam$member$adminEmail);
  1498.                             $application->setBatchStatus('applicant');
  1499.                             $application->setApplicationStatus('applied');
  1500.                             $em->persist($application);
  1501.                             $em->flush();
  1502.                             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  1503.                         }
  1504.                     }
  1505.                 }
  1506.             }
  1507.             $application $this->getOrCreateApplication($request$batch$program$member$lang$settings$utmParameters$event);
  1508.             if ($referral && $application->getRealPriceinEuroCent() > 0) {
  1509.                 if ($recommendedByMember $em->getRepository(Member::class)->find($referral)) {
  1510.                     if ($neededTag $settings['isRecommendedBy'] ?? '') {
  1511.                         if (Utility::contains($recommendedByMember->getTags(), $neededTag)) {
  1512.                             $application->setRealPriceinEuroCent(0);
  1513.                             $application->setDiscountReason("geworben durch {$recommendedByMember->getName()}");
  1514.                             $application->setDiscountPercent(100);
  1515.                         }
  1516.                     }
  1517.                 }
  1518.             }
  1519.             if ($user && in_array($action, ["needsloginvalidation""needsemailverification"])) {
  1520.                 $response $this->validateEventApplication($application$member$settings$batch$lang);
  1521.                 $application $response['data']['application'];
  1522.             }
  1523.             // Only check for Monsum product if we're not using Stripe
  1524.             $hasStripeConfig = ($batch && $batch->getStripePriceId()) || ($event && $event->getStripePriceId());
  1525.             
  1526.             if ($batch->getPriceInEuroCent() > && $batch->getProductId() && !$hasStripeConfig) {
  1527.                 if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  1528.                     $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  1529.                     return $this->redirect($this->generateUrl('events_list'));
  1530.                 }
  1531.             }
  1532.             $form $this->createForm(ApplicationType::class, $application, [
  1533.                 'settings' => $settings,
  1534.                 'program' => $program,
  1535.                 'batch' => $batch,
  1536.                 'csrf_protection' => $embed false true,
  1537.                 ]);
  1538.             $form->handleRequest($request);
  1539.             ##############################################################
  1540.             ##   form validation starts here                            ##
  1541.             ##############################################################
  1542.             if ($form->isSubmitted() && $form->isValid()) {
  1543.                 $application $em->getRepository(Application::class)->cleanApplicationData($application);
  1544.                 $adminEmail $application->getEmail();
  1545.                 $email $application->getEmail();
  1546.                 if (!Utility::validateEmail($email)){
  1547.                     $this->addFlash('notice''ERROR Incorrect email adress. Please check your email adress!');
  1548.                 } else {
  1549.                     // maxApplicants
  1550.                     if (isset($settings['maxApplicants'])) {
  1551.                         $applicants $em->getRepository(Application::class)->findBy(['batchId' => $batch->getId(), 'applicationStatus' => 'applied']);
  1552.                         $currentApplicantsCount count($applicants);
  1553.                         if ($currentApplicantsCount >= $settings['maxApplicants']) {
  1554.                             $this->addFlash('notice''ERROR Leider sind alle verfügbaren Plätze bereits vergeben.');
  1555.                             return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  1556.                         }
  1557.                     }
  1558.                     // isErrorDetectionOn
  1559.                     if (isset($settings['isErrorDetectionOn']) and $settings['isErrorDetectionOn']) {
  1560.                         $this->addFlash('notice''ERROR There was a problem in processing your application. Please contact support@startplatz.de');
  1561.                         return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  1562.                     }
  1563.                     if ($member) {
  1564.                         $memberToBeRegistered $member;
  1565.                         $isExistingMember true;
  1566.                     } else {
  1567.                         $response $this->processRegistrationCheckMembershipExistence($application);
  1568.                         $isExistingMember $response['data']['isExistingMember'];
  1569.                         $memberToBeRegistered $response['data']['memberToBeRegistered'];
  1570.                     }
  1571.                     $memberToBeRegistered->setIsExistingMember($isExistingMember);
  1572.                     // fill application with memberId and teamId and discount information
  1573.                     $application $this->updateApplicationWithMemberIdAndTeamId($application$memberToBeRegistered$batch);
  1574.                     if (!$batch->isMultipleApplicationsPerMember()) {
  1575.                         // check if there is already an application to this event == batch with that batchId and memberId
  1576.                         if ($existingApplication $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
  1577.                             // there are two applications - the latest application will always win
  1578.                             $fields Utility::getEntityFieldsArray($application, ['id''assessments''votesRegistered''applicationStatus''editStatus''editMessage''lastChangeUser''history''extraFields''lastModified''createdAt']);
  1579.                             $storedData Utility::fillDataByObject($existingApplication$fields);
  1580.                             $updatedData Utility::fillDataByObject($application$fields);
  1581.                             $differences array_diff_assoc($updatedData$storedData);
  1582.                             $application $existingApplication;
  1583.                             $application $this->updateApplication($application$differences);
  1584.                             $application->setIsExistingApplication(true);
  1585.                         }
  1586.                     }
  1587.                     if (isset($settings['extraFields'])) {
  1588.                         $application $this->getExtraFieldsData($application$settings$form);
  1589.                     }
  1590.                     $isPaidEvent $this->checkPaidEvent($application$batch$event);
  1591.                     // Debug logging for ki-campus payment exception
  1592.                     $this->logger->info('Payment exception check', [
  1593.                         'member_exists' => $member true false,
  1594.                         'member_id' => $member $member->getId() : null,
  1595.                         'member_email' => $member $member->getEmail() : null,
  1596.                         'member_tags' => $member $member->getTags() : null,
  1597.                         'memberToBeRegistered_id' => $memberToBeRegistered->getId(),
  1598.                         'memberToBeRegistered_email' => $memberToBeRegistered->getEmail(),
  1599.                         'memberToBeRegistered_tags' => $memberToBeRegistered->getTags(),
  1600.                         'batch_isFreeForKiCampus' => $batch $batch->getIsFreeForKiCampus() : null,
  1601.                         'event_isFreeForKiCampus' => $event $event->getIsFreeForKiCampus() : null,
  1602.                     ]);
  1603.                     
  1604.                     // Always use memberToBeRegistered for payment exception check
  1605.                     $hasToPay $this->checkPaymentException($application$memberToBeRegistered$event$settings$batch);
  1606.                     if ($isPaidEvent && $hasToPay) {
  1607.                         $application $this->updateApplicationWithStepOneInformation($application$memberToBeRegistered$event);
  1608.                         
  1609.                         // Always use batch payment settings first
  1610.                         $stripePriceId null;
  1611.                         $stripeAccount null;
  1612.                         
  1613.                         if ($batch) {
  1614.                             // Use batch-specific Stripe settings
  1615.                             $stripePriceId $batch->getStripePriceId();
  1616.                             $stripeAccount $batch->getStripeAccount();
  1617.                         }
  1618.                         
  1619.                         // Legacy fallback to event settings if batch has no payment info
  1620.                         if (!$stripePriceId && $event) {
  1621.                             $stripePriceId $event->getStripePriceId();
  1622.                         }
  1623.                         if (!$stripeAccount && $event) {
  1624.                             $stripeAccount $event->getStripeAccount();
  1625.                         }
  1626.                         
  1627.                         if ($stripePriceId '') {
  1628.                             // Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId Ã¼bergeben werden
  1629.                             $application $em->getRepository(Application::class)->setApplication($application);
  1630.                             $clientReferenceId $memberToBeRegistered->getId();
  1631.                             $clientEmail $memberToBeRegistered->getEmail();
  1632.                             $successUrlParameters = [
  1633.                                 'slug' => $event->getSlug(),
  1634.                                 'app' => $application->getId(),
  1635.                                 'action' => "checkSuccess",
  1636.                                 'provider' => 'stripe',
  1637.                                 'embed' => $embed,
  1638.                             ];
  1639.                             $route 'event_confirmation';
  1640.                             $successPage $this->generateUrl($route$successUrlParametersUrlGeneratorInterface::ABSOLUTE_URL);
  1641.                             //$successUrl = urlencode($successPage);
  1642.                             $successUrl $successPage;
  1643.                             // Check STRIPE_MODE first, fallback to APP_ENV for backwards compatibility
  1644.                             $stripeMode $_ENV['STRIPE_MODE'] ?? ($_ENV['APP_ENV'] === 'prod' 'live' 'test');
  1645.                             $isLiveEnvironment $stripeMode === 'live';
  1646.                             $stripeAccount $stripeAccount ?: "STARTPLATZ";
  1647.                             $stripeApiKey  $em->getRepository(Option::class)->getStripeApiKey($stripeAccount$isLiveEnvironment);
  1648.                             
  1649.                             // In dev environment, use a test price ID instead of live one
  1650.                             if (!$isLiveEnvironment && $stripePriceId && strpos($stripePriceId'_test_') === false) {
  1651.                                 // This is a live price ID in test mode - use a generic test price
  1652.                                 $originalPriceId $stripePriceId;
  1653.                                 $stripePriceId 'price_1RnzWKHegJLrFARi6RzQ9D8Q'// Generic test price for 90 EUR
  1654.                                 
  1655.                                 // Log the mapping
  1656.                                 file_put_contents('/var/www/html/var/log/stripe_debug_controller.log'
  1657.                                     date('Y-m-d H:i:s') . " - Mapped live price $originalPriceId to test price $stripePriceId\n"
  1658.                                     FILE_APPEND);
  1659.                             }
  1660.                             // Generate cancel URL - back to the event page
  1661.                             $cancelUrl $this->generateUrl('event_show_single', ['slug' => $event->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL);
  1662.                             
  1663.                             // Debug logging
  1664.                             $debugInfo = [
  1665.                                 'stripePriceId' => $stripePriceId,
  1666.                                 'clientReferenceId' => $clientReferenceId,
  1667.                                 'clientEmail' => $clientEmail,
  1668.                                 'stripeAccount' => $stripeAccount,
  1669.                                 'successUrl' => $successUrl,
  1670.                                 'cancelUrl' => $cancelUrl,
  1671.                                 'isLiveEnvironment' => $isLiveEnvironment,
  1672.                             ];
  1673.                             file_put_contents('/var/www/html/var/log/stripe_debug_controller.log'
  1674.                                 date('Y-m-d H:i:s') . " - Payment parameters: " json_encode($debugInfoJSON_PRETTY_PRINT) . "\n"
  1675.                                 FILE_APPEND);
  1676.                             $paymentLink $this->callbackService->curl_stripe_paymentlink($stripePriceId$clientReferenceId$clientEmail$stripeApiKey$successUrl$cancelUrl);
  1677.                             // Ãœberprüfen, ob die Antwort ein gültiger Zahlungslink ist
  1678.                             if (filter_var($paymentLinkFILTER_VALIDATE_URL)) {
  1679.                                 // Erfolgsfall: Weiterleitung des Benutzers zum Zahlungslink oder Anzeigen des Links
  1680.                                 // Zum Beispiel könnten Sie den Benutzer weiterleiten:
  1681.                                 // Note: Stripe payment links handle email prefilling differently
  1682.                                 // The email is already passed during payment link creation
  1683.                                 // Adding parameters to the URL can break the payment link
  1684.                                 if ($user && $user->getIsAdmin()) {
  1685.                                     $this->session->getFlashBag()->add('notice'"SUCCESS paymentLink: {$paymentLink}");
  1686.                                 }
  1687.                                 $mailContent = [
  1688.                                     'applicationId' => $application->getId(),
  1689.                                     'memberId' => $application->getMemberId(),
  1690.                                     'editStatus' => $application->getEditStatus(),
  1691.                                     'applicationStatus' => $application->getApplicationStatus(),
  1692.                                     'person' => $application->getPerson(),
  1693.                                     'email' => $application->getEmail(),
  1694.                                     'utmSource' => $application->getUtmSource(),
  1695.                                     'eventTitle' => $event->getTitle(),
  1696.                                     'eventSlug' => $event->getSlug(),
  1697.                                 ];
  1698.                                 $response Utility::sendDirectMail($this->mailer"stripe {$event->getId()} step one {$application->getId()} {$application->getEmail()}"'support@startplatz.de''Monitoring Stripe''lorenz.graef@startplatz.de''Lorenz'print_r($mailContent,true), false);
  1699.                                 // call stripe and let applicant pay for the ticket
  1700.                                 if ($embed){
  1701.                                     return $this->render('@StartPlatzEventBundle/Default/iframe_redirect.html.twig', [
  1702.                                         'targetUrl' => $paymentLink
  1703.                                     ]);
  1704.                                 } else {
  1705.                                     return $this->redirect($paymentLink);
  1706.                                 }
  1707.                             } else {
  1708.                                 // Fehlerfall: Anzeigen der Fehlermeldung
  1709.                                 // Loggen der Fehlermeldung oder Anzeigen einer Fehlerseite
  1710.                                 // Zum Beispiel könnten Sie eine Fehlerseite rendern:
  1711.                                 $this->session->getFlashBag()->add('notice'"ERROR paymentLink: {$paymentLink}");
  1712.                                 $paymentLink false;
  1713.                             }
  1714.                         } else {
  1715.                             // falls es bereits einen zugänglichen Monsum Account für diese Person gibt
  1716.                             if ($product) {
  1717.                                 $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered$product->getAccount(), false);
  1718.                             }
  1719.                         }
  1720.                         // Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId Ã¼bergeben werden
  1721.                         $application $em->getRepository(Application::class)->setApplication($application);
  1722.                     } else {
  1723.                         $route 'event_show_single';
  1724.                         $routeParameters $request->get('_route_params');
  1725.                         if (!array_key_exists('slug'$routeParameters)) {
  1726.                             $routeParameters['slug'] = $event->getSlug();
  1727.                         }
  1728.                         $response $this->processRegistrationWithoutPaymentSimpleOptimized($isExistingMember$user$memberToBeRegistered$application$settings$lang$adminEmail$batch$route$routeParameters);
  1729.                         if ($response['status'] == 'error') {
  1730.                             $targetPath $response['data']['targetPath'];
  1731.                             return $this->redirect($targetPath);
  1732.                         }
  1733.                         $application $response['data']['application'];
  1734.                         $member $response['data']['member'];
  1735.                         if ($this->shouldSetAiHubMembership($settings$application->getApplicationStatus())) {
  1736.                             $aiHubStatus $settings["setAiHubMembership"];
  1737.                             $service $this->configureServiceForAiHub($aiHubStatus$event->getStartDate());
  1738.                             $reason "participated in event with id={$event->getId()} and title={$event->getTitle()}";
  1739.                             $em->getRepository(Member::class)->setAiHubMembership($service$member$reason);
  1740.                         }
  1741.                     }
  1742.                 }
  1743.             }
  1744.         }
  1745.         $promotionUrl null;
  1746.         if ($user && $batch) {
  1747.             // Generate the absolute base URL based on the route and parameters
  1748.             $baseURL $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
  1749.             // UTM parameters for the promotion URL
  1750.             $promotionUtmParameters = [
  1751.                 'utm_source' => 'affiliate',
  1752.                 'utm_medium' => $member->getId(),
  1753.                 'utm_campaign' => 'event:' $event->getId(),
  1754.                 'utm_term'     => '',
  1755.                 'utm_content'  => '',
  1756.             ];
  1757.             $promotionUrl $em->getRepository(Batch::class)->generatePromotionUrl($baseURL$promotionUtmParameters);
  1758.         }
  1759.         // Ãœberprüfen Sie den Wert des Teaserfeldes, um showTeaser zu setzen
  1760.         $showTeaser = !empty($event->getTeaser());
  1761.         $paymentLink false;
  1762.         $view '@StartPlatzEventBundle/Default/event-single.html.twig';
  1763.         if ($event->isSinglePage() or $embed){
  1764.             $view '@StartPlatzEventBundle/Default/event-single.lp.html.twig';
  1765.         }
  1766.         if ($event->getTwigFile() == 'winter-2025' ){
  1767.             $view '@StartPlatzEventBundle/Default/event-single.winter-2025.html.twig';
  1768.         }
  1769.         if ($event->getTwigFile() == 'style-ecodynamics' ){
  1770.             $view '@StartPlatzEventBundle/Default/event-single.style-ecodynamics.html.twig';
  1771.         }
  1772.         if ($event->getTwigFile() == 'style-ai-hub' ){
  1773.             $view '@StartPlatzEventBundle/Default/event-single.style-ai-hub.html.twig';
  1774.         }
  1775.         if ($event->getTwigFile() == 'style-startplatz' ){
  1776.             $view '@StartPlatzEventBundle/Default/event-single.style-ai-hub.html.twig';
  1777.         }
  1778.         return $this->render($view, [
  1779.             'event'         => $event,
  1780.             'seriesEvents'  => $seriesEvents,
  1781.             'showTeaser'    => $showTeaser,
  1782.             'utmParameters'  => $utmParameters,
  1783.             'promotionUrl'   => $promotionUrl,
  1784.             'eventbriteId'   => $eventbriteId,
  1785.             'isEventbriteOnly' => $isEventbriteOnly,
  1786.             'applicationUrl' => $applicationUrl,
  1787.             'batch'          => $batch,
  1788.             'settings'       => $settings,
  1789.             'form'           => $form $form->createView() : $form,
  1790.             'application'    => $application,
  1791.             'action'         => $action,
  1792.             'batchIsOpen'    => $batchIsOpen,
  1793.             'customersArr'   => $customersArr,
  1794.             'member'         => $member,
  1795.             'paymentLink'    => $paymentLink,
  1796.             
  1797.             // Multi-batch event support
  1798.             'futureBatches'  => $futureBatches,
  1799.             'pastBatches'    => $pastBatches ?? [],
  1800.             'allBatches'     => $allBatches,
  1801.             'selectedDate'   => $date,
  1802.             'isMultiBatchEvent' => $event->isMultiBatchEvent(),
  1803.             'speakers'       => $this->getSpeakers($event),
  1804.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  1805.             'editFeedback'   => $editFeedback,
  1806.             'templateVars'   => [],
  1807.             'preview'             => $request->get('preview'),
  1808.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  1809.             'lang'           => $lang,
  1810.             'isEnglish'      => $lang === 'EN',
  1811.             'embed'          => $embed,
  1812.             'targetRoute'     => "event_show_single",
  1813.             'targetRouteSlug' => $event->getSlug(),
  1814.         ]);
  1815.     }
  1816.     /**
  1817.      * @Route("/api/event/participant-by-linkedin", name="add_participant-by-linkedin", methods={"POST"})
  1818.      * */
  1819.     public function addParticipantByLinkedIn(Request $request)
  1820.     {
  1821.         $authorizedIdentifier 'IhrGeheimerIdentifier'// Set your identifier here
  1822.         // Check the identifier
  1823.         if ($request->headers->get('X-CUSTOM-IDENTIFIER') !== $authorizedIdentifier) {
  1824.             //return new Response('Unauthorized', Response::HTTP_UNAUTHORIZED);
  1825.         }
  1826.         $data json_decode($request->getContent(), true);
  1827.         $em $this->getDoctrine()->getManager();
  1828.         if (empty($data['email'])) {
  1829.             return new Response('Required data missing: email'Response::HTTP_BAD_REQUEST);
  1830.         }
  1831.         if (empty($data['formName'])) {
  1832.             return new Response('Required data missing: formName'Response::HTTP_BAD_REQUEST);
  1833.         }
  1834.         // Retrieve the UTM parameters from the URL
  1835.         $utmParameters = [
  1836.             'utmSource'   => $data['utmSource'] ?? null,
  1837.             'utmMedium'   => $data['utmMedium'] ?? null,
  1838.             'utmCampaign' => $data['utmCampaign'] ?? null,
  1839.             'utmTerm'     => $data['utmTerm'] ?? null,
  1840.             'utmContent'  => $data['utmContent'] ?? null,
  1841.         ];
  1842.         $formName $data['formName'];
  1843.         // contains eventTitle
  1844.         // search for event with event Titel latest startDate
  1845.         if (!$event $em->getRepository(Event::class)->findOneBy(['title'=>$formName], ['startDate'=>'DESC'])) {
  1846.             return new Response('Event not found'Response::HTTP_NOT_FOUND);
  1847.         }
  1848.         $eventId $event->getId();
  1849.         if (!$batch $em->getRepository(Batch::class)->findOneBy(['eventId' => $eventId],['start'=>'DESC'])) {
  1850.             return new Response('Associated batch with event ID not found'Response::HTTP_NOT_FOUND);
  1851.         }
  1852.         if (!$em->getRepository(Batch::class)->isBatchApplicationOpen($batch)) {
  1853.             return new Response('Batch application is closed'Response::HTTP_FORBIDDEN);
  1854.         }
  1855.         $member $em->getRepository(Member::class)->createGuestMemberIfNotExists($datafalse,$data['email'], "MA");
  1856.         if (empty($member->getPhone()) && !empty($data['phone'])) {
  1857.             $phone Utility::cleanAndFormatPhoneNumber($data['phone']);
  1858.             $member $em->getRepository(Member::class)->setPhone($member$phone$member->getEmail());
  1859.         }
  1860.         /** @var Application $application */
  1861.         $application $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
  1862.         if (!$application) {
  1863.             $application $em->getRepository(Application::class)->createApplicationByApplyForm(
  1864.                 $batch->getProgram(),
  1865.                 $batch,
  1866.                 false,
  1867.                 [],
  1868.                 $utmParameters
  1869.             );
  1870.         }
  1871.         $application $em->getRepository(Application::class)->updateApplicationByMember($application$member);
  1872.         $application->setHasNewsletterPermissionGiven(1);
  1873.         $extraFields['jobTitle'] = $data['jobTitle'] ?? 'n.a.';
  1874.         $extraFields['company'] = $data['company'] ?? 'n.a.';
  1875.         $extraFields['country'] = $data['country'] ?? 'n.a.';
  1876.         if ($extraFields) {
  1877.             $application->setExtraFields(json_encode($extraFields)) ;
  1878.         }
  1879.         $application $em->getRepository(Application::class)->setApplicationApplied($application$member);
  1880.         $this->sendConfirmationMailToApplicant(null$membernull$application$batch);
  1881.         return new Response('Participant added'Response::HTTP_CREATED);
  1882.     }
  1883.     /**
  1884.      * @Route("/api/event/participant-by-facebook", name="add_participant-by-facebook", methods={"POST"})
  1885.      * @Route("/api/event/participant-by-tiktok", name="add_participant-by-tiktok", methods={"POST"})
  1886.      * */
  1887.     public function addParticipantByFacebook(Request $request)
  1888.     {
  1889.         set_time_limit(0);
  1890.         $authorizedIdentifier 'IhrGeheimerIdentifier'// Set your identifier here
  1891.         $em $this->getDoctrine()->getManager();
  1892.         
  1893.         try {
  1894.             // Check the identifier
  1895.             if ($request->headers->get('X-CUSTOM-IDENTIFIER') !== $authorizedIdentifier) {
  1896.                 //return new JsonResponse(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
  1897.             }
  1898.             // Parse and validate JSON payload
  1899.             $data json_decode($request->getContent(), true);
  1900.             if (json_last_error() !== JSON_ERROR_NONE) {
  1901.                 return new JsonResponse([
  1902.                     'error' => 'Invalid JSON payload',
  1903.                     'details' => json_last_error_msg()
  1904.                 ], Response::HTTP_BAD_REQUEST);
  1905.             }
  1906.             // Validate required fields
  1907.             if (empty($data['email'])) {
  1908.                 return new JsonResponse([
  1909.                     'error' => 'Required data missing: email',
  1910.                     'field' => 'email'
  1911.                 ], Response::HTTP_BAD_REQUEST);
  1912.             }
  1913.             if (empty($data['formName'])) {
  1914.                 return new JsonResponse([
  1915.                     'error' => 'Required data missing: formName',
  1916.                     'field' => 'formName'
  1917.                 ], Response::HTTP_BAD_REQUEST);
  1918.             }
  1919.             // Save raw webhook data for debugging
  1920.             $currentTimestamp date('Y-m-d_H:i:s');
  1921.             $em->getRepository(Option::class)->createOptionIfNotExists(
  1922.                 'sp.event.api.add-participant-by-facebook.' $currentTimestamp
  1923.                 $request->getContent(), 
  1924.                 'facebook-import'
  1925.             );
  1926.             // Extract UTM parameters
  1927.             $utmParameters = [
  1928.                 'utmSource'   => $data['utmSource'] ?? null,
  1929.                 'utmMedium'   => $data['utmMedium'] ?? null,
  1930.                 'utmCampaign' => $data['utmCampaign'] ?? null,
  1931.                 'utmTerm'     => $data['utmTerm'] ?? null,
  1932.                 'utmContent'  => $data['utmContent'] ?? null,
  1933.             ];
  1934.             // Parse formName safely
  1935.             $formName $data['formName'];
  1936.             $parts explode("|"$formName);
  1937.             
  1938.             if (count($parts) < 3) {
  1939.                 return new JsonResponse([
  1940.                     'error' => 'Invalid formName format',
  1941.                     'details' => 'Expected format: "Type | Name | Slug"',
  1942.                     'received' => $formName
  1943.                 ], Response::HTTP_BAD_REQUEST);
  1944.             }
  1945.             // Extract and normalize slug
  1946.             $slug trim($parts[2], " /");
  1947.             $slug strtolower($slug);
  1948.             // Begin database transaction
  1949.             $em->getConnection()->beginTransaction();
  1950.             
  1951.             try {
  1952.                 // Find the appropriate event
  1953.                 $event null;
  1954.                 
  1955.                 // Check if this is a multi-batch/evergreen event
  1956.                 if (str_contains(strtolower($parts[0]), 'evergreen')) {
  1957.                     $now = new \DateTime();
  1958.                     $event $em->getRepository(Event::class)->findNextEventByPartOfSlug($slug$now);
  1959.                     
  1960.                     if (!$event) {
  1961.                         throw new \Exception("No upcoming evergreen event found with slug containing: {$slug}");
  1962.                     }
  1963.                 } else {
  1964.                     $event $em->getRepository(Event::class)->findOneBy(['slug' => $slug]);
  1965.                     
  1966.                     if (!$event) {
  1967.                         throw new \Exception("Event not found with slug: {$slug}");
  1968.                     }
  1969.                 }
  1970.                 // Find the appropriate batch
  1971.                 $batch null;
  1972.                 
  1973.                 if ($event->getIsMultiBatchEvent()) {
  1974.                     // Multi-batch event: find next open batch
  1975.                     $batch $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
  1976.                     
  1977.                     if (!$batch) {
  1978.                         // Fallback: try to find any open batch
  1979.                         $batches $em->getRepository(Batch::class)->findBy([
  1980.                             'eventId' => $event->getId(),
  1981.                             'status' => 'active'
  1982.                         ]);
  1983.                         
  1984.                         foreach ($batches as $b) {
  1985.                             if ($em->getRepository(Batch::class)->isBatchApplicationOpen($b)) {
  1986.                                 $batch $b;
  1987.                                 break;
  1988.                             }
  1989.                         }
  1990.                     }
  1991.                     
  1992.                     if (!$batch) {
  1993.                         throw new \Exception('No open batches available for this multi-batch event');
  1994.                     }
  1995.                 } else {
  1996.                     // Single-batch event
  1997.                     $batch $em->getRepository(Batch::class)->findOneBy(['eventId' => $event->getId()]);
  1998.                     
  1999.                     if (!$batch) {
  2000.                         throw new \Exception('No batch found for this event');
  2001.                     }
  2002.                 }
  2003.                 // Verify batch is open for registration
  2004.                 if (!$em->getRepository(Batch::class)->isBatchApplicationOpen($batch)) {
  2005.                     throw new \Exception('Registration is closed for this batch');
  2006.                 }
  2007.                 // Create or update member
  2008.                 // Transform data to match expected field names
  2009.                 $memberData $data;
  2010.                 if (isset($data['firstname'])) {
  2011.                     $memberData['firstName'] = $data['firstname'];
  2012.                 }
  2013.                 if (isset($data['lastname'])) {
  2014.                     $memberData['lastName'] = $data['lastname'];
  2015.                 }
  2016.                 
  2017.                 $member $em->getRepository(Member::class)->createGuestMemberIfNotExists(
  2018.                     $memberData
  2019.                     false
  2020.                     $data['email'], 
  2021.                     "MA"
  2022.                 );
  2023.                 // Update phone if provided
  2024.                 if (empty($member->getPhone()) && !empty($data['phone'])) {
  2025.                     $phone Utility::cleanAndFormatPhoneNumber($data['phone']);
  2026.                     $member $em->getRepository(Member::class)->setPhone($member$phone$member->getEmail());
  2027.                 }
  2028.                 // Check for existing application
  2029.                 $application $em->getRepository(Application::class)->findOneBy([
  2030.                     'batchId' => $batch->getId(), 
  2031.                     'memberId' => $member->getId()
  2032.                 ]);
  2033.                 // Create new application if needed
  2034.                 if (!$application) {
  2035.                     $application $em->getRepository(Application::class)->createApplicationByApplyForm(
  2036.                         $batch->getProgram(),
  2037.                         $batch,
  2038.                         false,
  2039.                         [],
  2040.                         $utmParameters
  2041.                     );
  2042.                 } else {
  2043.                     // Log duplicate application attempt
  2044.                     $this->logger->info('Facebook webhook: Duplicate application attempt', [
  2045.                         'email' => $data['email'],
  2046.                         'eventSlug' => $slug,
  2047.                         'batchId' => $batch->getId()
  2048.                     ]);
  2049.                 }
  2050.                 // Update application with member data
  2051.                 $application $em->getRepository(Application::class)->updateApplicationByMember($application$member);
  2052.                 $application->setHasNewsletterPermissionGiven(1);
  2053.                 $application $em->getRepository(Application::class)->setApplicationApplied($application$member);
  2054.                 // Commit transaction before sending emails
  2055.                 $em->getConnection()->commit();
  2056.                 // Send confirmation email (includes HubSpot sync)
  2057.                 // Disable newsletter registration for Facebook webhook participants
  2058.                 try {
  2059.                     $this->sendConfirmationMailToApplicant(null$membernull$application$batchfalse);
  2060.                 } catch (\Exception $emailException) {
  2061.                     // Log email error but don't fail the webhook
  2062.                     $this->logger->error('Facebook webhook: Failed to send confirmation email', [
  2063.                         'error' => $emailException->getMessage(),
  2064.                         'memberId' => $member->getId(),
  2065.                         'applicationId' => $application->getId()
  2066.                     ]);
  2067.                 }
  2068.                 // Return success with details
  2069.                 return new JsonResponse([
  2070.                     'status' => 'success',
  2071.                     'message' => 'Participant added successfully',
  2072.                     'data' => [
  2073.                         'memberId' => $member->getId(),
  2074.                         'applicationId' => $application->getId(),
  2075.                         'eventId' => $event->getId(),
  2076.                         'eventSlug' => $event->getSlug(),
  2077.                         'batchId' => $batch->getId(),
  2078.                         'batchDate' => $batch->getStartDate() ? $batch->getStartDate()->format('Y-m-d') : null
  2079.                     ]
  2080.                 ], Response::HTTP_CREATED);
  2081.                 
  2082.             } catch (\Exception $e) {
  2083.                 // Rollback transaction on error
  2084.                 $em->getConnection()->rollback();
  2085.                 throw $e;
  2086.             }
  2087.             
  2088.         } catch (\Exception $e) {
  2089.             // Log the error
  2090.             $this->logger->error('Facebook webhook error', [
  2091.                 'error' => $e->getMessage(),
  2092.                 'trace' => $e->getTraceAsString(),
  2093.                 'payload' => $data ?? null
  2094.             ]);
  2095.             // Return error response
  2096.             return new JsonResponse([
  2097.                 'error' => 'Failed to process participant',
  2098.                 'details' => $e->getMessage()
  2099.             ], Response::HTTP_INTERNAL_SERVER_ERROR);
  2100.         }
  2101.     }
  2102.     /**
  2103.      * @Route("/allmeda/events/participants/import/{batchId}", name="allmeda_participants_import")
  2104.      * @Security("is_granted('ROLE_ADMIN')")
  2105.      */
  2106.     public function importParticipantsAction(Request $request$batchId)
  2107.     {
  2108.         $em $this->getDoctrine()->getManager();
  2109.         $templateVars $em->getRepository(Application::class)->getTemplateVars($request);
  2110.         $feedback = [];
  2111.         $adminEmail $this->getUser()->getEmail();
  2112.         $responseContent = [];
  2113.         $redirect Utility::getRedirectTarget($request);
  2114.         $batch $em->getRepository(Batch::class)->find($batchId);
  2115.         $fields = ['name''phone''email''utmSource','sendConfirmationMail'];
  2116.         $importString Utility::setDataList($fields);
  2117.         $form $this->createForm(ImportFormType::class, ['importString' => $importString]);
  2118.         $form->handleRequest($request);
  2119.         if ($form->isSubmitted() && $form->isValid()) {
  2120.             // perform some action, such as saving the task to the database
  2121.             $inputVars $form->getData();
  2122.             $importString $inputVars['importString'];
  2123.             $rows explode(PHP_EOL$importString);
  2124.             $firstRow array_shift($rows);
  2125.             $fields str_getcsv($firstRow"\t");
  2126.             // fields need to be
  2127.             // $fields = ['name', 'phone', 'email', 'utmSource'];
  2128.             foreach ($rows as $row) {
  2129.                 $feedback[] = str_getcsv($row"\t");
  2130.             }
  2131.             if ($form->get('import')->isClicked()) {
  2132.                 $now = new DateTime();
  2133.                 // find virtual team for that batch
  2134.                 // http://dev.startplatz.de/apply-add-application/80488/653
  2135.                 //      * @Route("/apply-add-application/{memberId}/{batchId}/", name="apply_add-application")
  2136.                 /** @var Program $program */
  2137.                 $program $batch->getProgram();
  2138.                 // check if settings contains necessary setters
  2139.                 $batch $em->getRepository(Batch::class)->checkLegacyData($batch);
  2140.                 $settings $em->getRepository(Application::class)->getSettingsProgram($program$batch);
  2141.                 $virtualTeam array_key_exists('virtualTeam'$settings) ? $settings['virtualTeam'] : 'applicants-' $program->getSlug() . '-' $batch->getSlug();
  2142.                 $applicantsTeam $em->getRepository(Team::class)->getVirtualTeamByName($virtualTeam);
  2143.                 foreach ($feedback as $row) {
  2144.                     $row array_combine($fields$row);
  2145.                     if (in_array('utmSource'$fields)){
  2146.                         $data['email']= $row['email'];
  2147.                         $data['name']= $row['name']?? null;
  2148.                         $data['firstName']= $row['firstName']?? null;
  2149.                         $data['lastName']= $row['lastName']?? null;
  2150.                         $data['phone']= $row['phone'] ?? null;
  2151.                         $data['utmSource'] = $row['utmSource'];
  2152.                         $utmParameters = [
  2153.                             'utmSource'   => $data['utmSource'] ?? null,
  2154.                             'utmMedium'   => $data['utmMedium'] ?? null,
  2155.                             'utmCampaign' => $data['utmCampaign'] ?? null,
  2156.                             'utmTerm'     => $data['utmTerm'] ?? null,
  2157.                             'utmContent'  => $data['utmContent'] ?? null,
  2158.                         ];
  2159.                         if (!Utility::validateEmail($data['email'])) {
  2160.                             $this->session->getFlashBag()->add('notice''ERROR email is not valid, please check ' $data['email']);
  2161.                             return $this->redirect($this->generateUrl($redirect->path, (array)$redirect->parameters));
  2162.                         }
  2163.                         $member $em->getRepository(Member::class)->createGuestMemberIfNotExists($datafalse,$data['email'], "MA");
  2164.                         $application $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
  2165.                         if (empty($member->getPhone()) && !empty($data['phone'])) {
  2166.                             $member $em->getRepository(Member::class)->setPhone($member$data['phone'], $member->getEmail());
  2167.                         }
  2168.                         if (!$application) {
  2169.                             $application $em->getRepository(Application::class)->createApplicationByApplyForm(
  2170.                                 $batch->getProgram(),
  2171.                                 $batch,
  2172.                                 false,
  2173.                                 [],
  2174.                                 $utmParameters
  2175.                             );
  2176.                             $application $em->getRepository(Application::class)->updateApplicationByMember($application$member);
  2177.                             $application $em->getRepository(Application::class)->setApplicationApplied($application$member);
  2178.                             if (array_key_exists('sendConfirmationMail'$row) && $row['sendConfirmationMail'] === 'yes') {
  2179.                                 $this->sendConfirmationMailToApplicant(null$membernull$application$batch);
  2180.                                 $this->session->getFlashBag()->add('notice''SUCCESS confirmationMail has been sent to ' $data['email']);
  2181.                             }
  2182.                         } else {
  2183.                             if (empty($application->getPhone()) && !empty($member->getPhone())) {
  2184.                                 $application->setPhone($member->getPhone());
  2185.                                 $em->persist($application);
  2186.                                 $em->flush();
  2187.                             }
  2188.                         }
  2189.                     } else {
  2190.                         $email $row['email'];
  2191.                         $firstName $row['firstName'];
  2192.                         // find member by email
  2193.                         $member $em->getRepository(Member::class)->createGuestMemberIfNotExists($row$applicantsTeam$adminEmail$memberType "MA");
  2194.                         $application $em->getRepository(Application::class)->createApplicationIfNotExists($program$batch$applicantsTeam$member$adminEmail);
  2195.                     }
  2196.                     $responseContent[] = "Application created or updated for {$application->getPerson()} with email={$application->getEmail()}";
  2197.                 }
  2198.                 $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  2199.                 $this->session->getFlashBag()->add('notice''SUCCESS data has been imported ' print_r($responseContenttrue));
  2200.                 return $this->redirect($this->generateUrl($redirect->path, (array)$redirect->parameters));
  2201.             }
  2202.         } else {
  2203.             $importString 'leer';
  2204.         }
  2205.         return $this->render('@StartPlatzWebsiteBundle/Import/import.html.twig', [
  2206.             'importString' => $importString,
  2207.             'feedback' => $feedback,
  2208.             'templateVars' => $templateVars,
  2209.             'form' => $form->createView(),
  2210.             'fields' => $fields,
  2211.             'title' => "Import Applications Template: default",
  2212.             'hint'  => "Write 'yes' in the column 'sendConfirmationMail' if a confirmation mail should be sent",
  2213.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  2214.         ]);
  2215.     }
  2216.     private function shouldSetAiHubMembership($settings$applicationStatus)
  2217.     {
  2218.         return array_key_exists("setAiHubMembership"$settings) && $applicationStatus === 'applied';
  2219.     }
  2220.     private function configureServiceForAiHub($statusDateTime $startDate)
  2221.     {
  2222.         $service = new Service();  // oder hole ein existierendes Service-Objekt
  2223.         $endDate = clone $startDate;
  2224.         if ($status === 'trial') {
  2225.             $endDate->modify('+1 month');
  2226.         } elseif ($status === 'active') {
  2227.             $endDate->modify('+3 months');
  2228.         }
  2229.         $service->setStatus($status);
  2230.         $service->setStartDate($startDate);
  2231.         $service->setEndDate($endDate);
  2232.         return $service;
  2233.     }
  2234.     /**
  2235.      * @Route("/allmeda/admin/event/add-participant/{id}/{batchId}", name="allmeda_admin_event_add_participant_modal", defaults={"batchId"=null})
  2236.      * @Security("is_granted('ROLE_ADMIN')")
  2237.      */
  2238.     public function addParticipantAction(Request $request$id$batchId null)
  2239.     {
  2240.         $em $this->getDoctrine()->getManager();
  2241.         /** @var User $user */
  2242.         $user $this->getUser();
  2243.         $adminEmail $user->getEmail();
  2244.         $returnPath = ['path' => 'admin_event_participants''parameters' => ['id'=>$id'batchId'=>$batchId]];
  2245.         $redirectUrl base64_encode(json_encode($returnPath));
  2246.         if (!$event $this->getEventRepository()->findOneBy(['id' => $id])) {
  2247.             $this->addFlash('notice''ERROR Veranstaltung nicht gefunden');
  2248.             return $this->redirectToRoute('admin_event_participants', ['id'=>$id'batchId'=>$batchId]);
  2249.         }
  2250.         // Retrieve the UTM parameters from the URL
  2251.         $utmParameters = [
  2252.             'utmSource' => $request->query->get('utm_source') ?? 'admin',
  2253.             'utmMedium'   => $request->query->get('utm_medium'),
  2254.             'utmCampaign' => $request->query->get('utm_campaign'),
  2255.             'utmTerm'     => $request->query->get('utm_term'),
  2256.             'utmContent'  => $request->query->get('utm_content'),
  2257.         ];
  2258.         $lang 'DE';
  2259.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  2260.             $lang $this->menuTranslationService->getLang($targetPath);
  2261.         }
  2262.         $eventbriteId   $event->getEventbriteId();
  2263.         $isEventbriteOnly $event->getTicketing() > '';
  2264.         $applicationUrl $event->getApplicationUrl();
  2265.         
  2266.         // Handle batch selection - use provided batchId or find appropriate batch
  2267.         if ($batchId) {
  2268.             $batch $em->getRepository(Batch::class)->find($batchId);
  2269.             // Verify batch belongs to this event
  2270.             if (!$batch || $batch->getEventId() != $event->getId()) {
  2271.                 $this->addFlash('notice''ERROR: Selected batch does not belong to this event');
  2272.                 // Fallback to finding next open batch
  2273.                 if ($event->isMultiBatchEvent()) {
  2274.                     $batch $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
  2275.                 } else {
  2276.                     $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId()]);
  2277.                 }
  2278.                 // Update batchId to the fallback batch's ID
  2279.                 if ($batch) {
  2280.                     $batchId $batch->getId();
  2281.                 }
  2282.             }
  2283.         } else {
  2284.             // No batch specified - find appropriate batch
  2285.             if ($event->isMultiBatchEvent()) {
  2286.                 $batch $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
  2287.             } else {
  2288.                 $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId()]);
  2289.             }
  2290.             // Set batchId to the found batch's ID
  2291.             if ($batch) {
  2292.                 $batchId $batch->getId();
  2293.             }
  2294.         }
  2295.         
  2296.         $batchIsOpen $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
  2297.         $program $batch->getProgram();
  2298.         $settings $this->getProgramSettings($program$batch);
  2299.         $member      false;
  2300.         $application $this->getOrCreateApplication($request$batch$program$member$lang$settings$utmParametersnull);
  2301.         $form $this->createForm(ApplicationType::class, $application, ['settings' => $settings'program' => $program'batch' => $batch]);
  2302.         $form->handleRequest($request);
  2303.         ##############################################################
  2304.         ##   form validation starts here                            ##
  2305.         ##############################################################
  2306.         if ($form->isSubmitted() && $form->isValid()) {
  2307.             $application $em->getRepository(Application::class)->cleanApplicationData($application);
  2308.             $adminEmail  $application->getEmail();
  2309.             if ($member) {
  2310.                 $memberToBeRegistered $member;
  2311.                 $isExistingMember     true;
  2312.             } else {
  2313.                 $response $this->processRegistrationCheckMembershipExistence($application);
  2314.                 $isExistingMember     $response['data']['isExistingMember'];
  2315.                 $memberToBeRegistered $response['data']['memberToBeRegistered'];
  2316.             }
  2317.             $memberToBeRegistered->setIsExistingMember($isExistingMember);
  2318.             // fill application with memberId and teamId and discount information
  2319.             $application $this->updateApplicationWithMemberIdAndTeamId($application$memberToBeRegistered$batch);
  2320.             if (!$batch->isMultipleApplicationsPerMember()) {
  2321.                 // check if there is already an application to this event == batch with that batchId and memberId
  2322.                 if ($existingApplication $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
  2323.                     // there are two applications - the latest application will always win
  2324.                     $fields     Utility::getEntityFieldsArray($application, ['id','assessments''votesRegistered''applicationStatus''editStatus''editMessage''lastChangeUser','history','extraFields','lastModified','createdAt']);
  2325.                     $storedData Utility::fillDataByObject($existingApplication$fields);
  2326.                     $updatedData Utility::fillDataByObject($application$fields);
  2327.                     $differences array_diff_assoc($updatedData$storedData);
  2328.                     $application $existingApplication;
  2329.                     $application $this->updateApplication($application$differences);
  2330.                     $application->setIsExistingApplication(true);
  2331.                 }
  2332.             }
  2333.             if (isset($settings['extraFields'])) {
  2334.                 $application $this->getExtraFieldsData($application$settings$form);
  2335.             }
  2336.             $settings['isNeedsLoginValidation'] = 0;
  2337.             $settings['isNeedsEmailValidation'] = 0;
  2338.             $settings['isSendConfirmationMail'] = 0;
  2339.             $route 'event_show_single';
  2340.             $routeParameters $request->get('_route_params');
  2341.             if (!array_key_exists('slug'$routeParameters)) {
  2342.                 $routeParameters['slug'] = $event->getSlug();
  2343.             }
  2344.             $application->setApplicationStatus('applied');
  2345.             $application $em->getRepository(Application::class)->setApplication($application$memberToBeRegistered);
  2346.             $this->session->getFlashBag()->add('notice'"SUCCESS {$application->getPerson()} wurde hinzugefügt mit Status == {$application->getApplicationStatus()}");
  2347.             return $this->redirectToRoute('admin_event_participants', ['id'=>$id'batchId'=>$batchId]);
  2348.         }
  2349.         return $this->render('@StartPlatzEvent/Admin/edit.add-participant.html.twig', [
  2350.             'event'         => $event,
  2351.             'eventbriteId'   => $eventbriteId,
  2352.             'isEventbriteOnly' => $isEventbriteOnly,
  2353.             'applicationUrl' => $applicationUrl,
  2354.             'batch'          => $batch,
  2355.             'settings'       => $settings,
  2356.             'form'           => $form $form->createView() : $form,
  2357.             'application'    => $application,
  2358.             'batchIsOpen'    => $batchIsOpen,
  2359.             'member'         => $member,
  2360.             'speakers'       => $this->getSpeakers($event),
  2361.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  2362.             'templateVars'   => [],
  2363.             'preview'             => $request->get('preview'),
  2364.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  2365.             'lang'           => $lang,
  2366.             'targetRoute'     => "event_show_single",
  2367.             'targetRouteSlug' => $event->getSlug(),
  2368.             'targetPath'      => $targetPath,
  2369.             ]);
  2370.     }
  2371.     public function updateApplication(Application $application$differences)
  2372.     {
  2373.         foreach ($differences as $diff => $value) {
  2374.             $method 'set' ucfirst((string) $diff);
  2375.             if (method_exists($application$method)) {
  2376.                 $application->$method($value);
  2377.             }
  2378.         }
  2379.         return $application;
  2380.     }
  2381.     private function getExtraFieldsData(Application $application$settings$form)
  2382.     {
  2383.         $extraFields $settings['extraFields'];
  2384.         $data = [];
  2385.         $i 0;
  2386.         foreach ($extraFields as $extraField) {
  2387.             $data[$extraField['field']] = $form->get($extraField['field'])->getData();
  2388.             $i++;
  2389.         }
  2390.         if ($data) {
  2391.             $application->setExtraFields(json_encode($data)) ;
  2392.         }
  2393.         return $application;
  2394.     }
  2395.     private function getExtraFieldsAttributes($settings)
  2396.     {
  2397.         if (!isset($settings['extraFields'])) {
  2398.             return $settings;
  2399.         }
  2400.         $em $this->getDoctrine()->getManager();
  2401.         $extraFields $settings['extraFields'];
  2402.         $i 0;
  2403.         foreach ($extraFields as $extraField) {
  2404.             if ($extraField['type'] == 'choice') {
  2405.                 $choices = [];
  2406.                 if (isset($extraField['arr'])) {
  2407.                     $choices $em->getRepository(Attribute::class)->findCodesByAttributeName($extraField['arr']);
  2408.                 }
  2409.                 $settings['extraFields'][$i]['choices'] = $choices;
  2410.             }
  2411.             $i++;
  2412.         }
  2413.         return $settings;
  2414.     }
  2415.     private function sendMagicLinkForPayment(Application $applicationBatch $batch$customerId$route$routeParameters)
  2416.     {
  2417.         if (!$application) {
  2418.             $this->session->getFlashBag()->add('notice''ERROR application not found');
  2419.             return $this->generateUrl($route$routeParameters);
  2420.         }
  2421.         $em $this->getDoctrine()->getManager();
  2422.         if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  2423.             $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  2424.             return $this->generateUrl($route$routeParameters);
  2425.         }
  2426.         if (!$memberToBeRegistered $this->getExistingMemberAccount($application)) {
  2427.             $this->session->getFlashBag()->add('notice''ERROR member not found');
  2428.             return $this->generateUrl($route$routeParameters);
  2429.         }
  2430.         if (!$customer $em->getRepository(Customer::class)->findOneBy(['hash'=>$customerId])) {
  2431.             $this->session->getFlashBag()->add('notice''ERROR customer not found');
  2432.             return $this->generateUrl($route$routeParameters);
  2433.         }
  2434.         $routeParameters["app"]        = $application->getId();
  2435.         $routeParameters['action']     = "useExistingCustomerAccount";
  2436.         $routeParameters['customerId'] = $customerId;
  2437.         $targetPath $this->generateUrl($route$routeParameters);
  2438.         $loginLink $this->loginService->getLoginLinkByTargetPath($memberToBeRegistered->getEmail(), $targetPath);
  2439.         $mailTemplate $em->getRepository(MailTemplate::class)->getPaymentLoginMailTemplateByBatch($batch$application$loginLink);
  2440.         $feedback Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
  2441.         $routeParameters["app"]        = $application->getId();
  2442.         $routeParameters['action']     = "MagicLinkForPaymentSent";
  2443.         $targetPath $this->generateUrl($route$routeParameters);
  2444.         return $targetPath;
  2445.     }
  2446.     private function getSpeakers($event)
  2447.     {
  2448.         $em $this->getDoctrine()->getManager();
  2449.         $speakers = [];
  2450.         if ($rows json_decode((string) $event->getSpeakers())) {
  2451.             foreach ($rows as $row) {
  2452.                 switch (strtolower((string) $row[1])) {
  2453.                     case "partner":
  2454.                         // find partner page
  2455.                         if ($partner $em->getRepository(Page::class)->find($row[2])) {
  2456.                             $speakers['partners'][] = $partner;
  2457.                         }
  2458.                         break;
  2459.                     case "speaker":
  2460.                         // find member
  2461.                         if ($member $em->getRepository(Member::class)->find($row[2])) {
  2462.                             $speakers['members'][] = $member;
  2463.                         }
  2464.                         break;
  2465.                     default:
  2466.                         $speakers['legacy'][] = $row;
  2467.                         break;
  2468.                 }
  2469.             }
  2470.         }
  2471.         return $speakers;
  2472.     }
  2473.     private function prepareMonsumCheckoutNewMonsumAccount(Application $applicationBatch $batch$action$route$routeParameters$customerId 0)
  2474.     {
  2475.         if (!$application) {
  2476.             $this->session->getFlashBag()->add('notice''ERROR application not found');
  2477.             return $this->generateUrl($route$routeParameters);
  2478.         }
  2479.         $em $this->getDoctrine()->getManager();
  2480.         if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  2481.             $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  2482.             return $this->generateUrl($route$routeParameters);
  2483.         }
  2484.         if (!$memberToBeRegistered $this->getExistingMemberAccount($application)) {
  2485.             return $this->generateUrl($route$routeParameters);
  2486.         }
  2487.         if ($action == "useExistingCustomerAccount") {
  2488.             if (!$customerArray $em->getRepository(Customer::class)->isMonsumCustomerValidByMemberAndCustomerId($memberToBeRegistered$customerId)) {
  2489.                 $this->session->getFlashBag()->add('notice''ERROR customer account not found');
  2490.                 return $this->generateUrl($route$routeParameters);
  2491.             }
  2492.             if (!$customer $em->getRepository(Customer::class)->findOneBy(['customerId'=>$customerArray['customerId'] ])) {
  2493.                 $this->session->getFlashBag()->add('notice''ERROR customer account not found');
  2494.                 return $this->generateUrl($route$routeParameters);
  2495.             }
  2496.         } else {
  2497.             $team     $em->getRepository(Team::class)->createNewMonsumAccountTeamByMembershipApplicant($memberToBeRegistered);
  2498.             $customer $this->setMonsumAccountIfNotExists($memberToBeRegistered$team$application$product->getAccount());
  2499.         }
  2500.         $customer $em->getRepository(Customer::class)->setCheckoutData($customer$product->getAccount(), $product->getProductNumber(), $memberToBeRegistered->getId());
  2501.         $successUrlParameters = [
  2502.             'slug' => $routeParameters['slug'],
  2503.             'app' => $application->getId(),
  2504.             'action' => "checkSuccess",
  2505.             'provider' => 'monsum',
  2506.         ];
  2507.         if ($product->getLandingPageSnippet() > '') {
  2508.             $successUrlParameters['account']       = $product->getAccount();
  2509.             $successUrlParameters['productnumber'] = $product->getProductNumber();
  2510.         }
  2511.         $successPage $this->generateUrl($route$successUrlParametersUrlGeneratorInterface::ABSOLUTE_URL);
  2512.         $successUrl urlencode($successPage);
  2513.         $successUrl urlencode($successUrl); // that is strange but monsum wants double encoding
  2514.         $subscription_title $routeParameters['slug'];
  2515.         $promocode "";
  2516.         switch ($application->getDiscountPercent()) {
  2517.             case "100":
  2518.                 $promocode "100_system";
  2519.                 break;
  2520.             case "50":
  2521.                 $promocode "community";
  2522.                 break;
  2523.             case "20":
  2524.                 //$promocode = "20_system"; wegen AI ausser Kraft gesetzt
  2525.                 break;
  2526.         }
  2527.         if ($application->getUtmTerm()) {
  2528.             $promocode $application->getUtmTerm();
  2529.         }
  2530.         $accountHash   $customer->getAccountHash();
  2531.         $customerHash  $customer->getHash();
  2532.         $productNumber $product->getProductNumber();
  2533.         return "https://app.monsum.com/checkout/0/{$accountHash}/{$customerHash}/{$productNumber}&success_url={$successUrl}&promocode={$promocode}&subscription_title={$subscription_title}&x_applicationId={$application->getId()}";
  2534.     }
  2535.     private function prepareMonsumCheckout($application$product$member$event$customer)
  2536.     {
  2537.         $successPage $this->generateUrl('event_show_single', [
  2538.             'slug' => $event->getSlug(),
  2539.             'app' => $application->getId(),
  2540.             'action' => "checkSuccess",
  2541.             'provider' => 'monsum',
  2542.         ], UrlGeneratorInterface::ABSOLUTE_URL);
  2543.         $successUrl urlencode($successPage);
  2544.         $successUrl urlencode($successUrl); // that is strange but monsum wants double encoding
  2545.         $subscription_title $event->getTitle();
  2546.         $promocode $member->getHasCommunityMembership() ? "community" "";
  2547.         $accountHash   $customer->getAccountHash();
  2548.         $customerHash  $customer->getHash();
  2549.         $productNumber $product->getProductNumber();
  2550.         return "https://app.monsum.com/checkout/0/{$accountHash}/{$customerHash}/{$productNumber}&success_url={$successUrl}&promocode={$promocode}&subscription_title={$subscription_title}&x_applicationId={$application->getId()}";
  2551.     }
  2552.     private function updateApplicationWithStepOneInformation(Application $application,Member $memberToBeRegisteredEvent $event)
  2553.     {
  2554.         $status  "process";
  2555.         // Check for Stripe payment configuration
  2556.         $hasStripePayment false;
  2557.         if ($event->getStripePriceId() > '') {
  2558.             $hasStripePayment true;
  2559.         }
  2560.         
  2561.         // For multi-batch events, also check batch-specific Stripe settings
  2562.         if ($event->isMultiBatchEvent() && $application->getBatchId()) {
  2563.             $em $this->getDoctrine()->getManager();
  2564.             $batch $em->getRepository(Batch::class)->find($application->getBatchId());
  2565.             if ($batch && $batch->getStripePriceId() > '') {
  2566.                 $hasStripePayment true;
  2567.             }
  2568.         }
  2569.         if ($hasStripePayment){
  2570.             $todo    "stripeStarted";
  2571.         } else {
  2572.             $todo    "do next step";
  2573.         }
  2574.         $message "Next step is needed";
  2575.         $application->setEditStatus("{$status}:{$todo}");
  2576.         $application->setEditMessage($message);
  2577.         $application->setApplicationStatus('started');
  2578.         $application->setMemberId($memberToBeRegistered->getId());
  2579.         return $application;
  2580.     }
  2581.     private function getOrCreateMemberAccount($application)
  2582.     {
  2583.         if (!$memberToBeRegistered $this->getExistingMemberAccount($application)) {
  2584.             $memberToBeRegistered  =  $this->createGuestMember($application);
  2585.         }
  2586.         return $memberToBeRegistered;
  2587.     }
  2588.     private function processRegistrationCheckMembershipExistence($application)
  2589.     {
  2590.         $em $this->getDoctrine()->getManager();
  2591.         $status  "success";
  2592.         $message "nothing to be reported";
  2593.         // does a memberAccount exist with that email?
  2594.         if ($memberToBeRegistered $this->getExistingMemberAccount($application)) {
  2595.             $isExistingMember true;
  2596.         } else {
  2597.             $isExistingMember false;
  2598.             $memberToBeRegistered $this->createGuestMember($application);
  2599.         }
  2600.         $response = [
  2601.             'status'  => $status,
  2602.             'message' => $message,
  2603.             'data'    => [
  2604.                 'isExistingMember'          => $isExistingMember,
  2605.                 'memberToBeRegistered'      => $memberToBeRegistered,
  2606.             ],
  2607.         ];
  2608.         return $response;
  2609.     }
  2610.     private function processRegistrationWithPayment($application$memberToBeRegistered$account)
  2611.     {
  2612.         $em $this->getDoctrine()->getManager();
  2613.         $status  "success";
  2614.         $message "nothing to be reported";
  2615.         $data    = [];
  2616.         // does a customerAccount exist with that member?
  2617.         if ($customer $em->getRepository(Customer::class)->findPrimaryMonsumAccount($memberToBeRegistered$account)) {
  2618.             $isExistingCustomerAccount true;
  2619.         } else {
  2620.             $isExistingCustomerAccount false;
  2621.             $team $em->getRepository(Team::class)->createTeamByMembershipApplicant($memberToBeRegistered);
  2622.             $customer $this->setMonsumAccountIfNotExists($memberToBeRegistered$team$application$account);
  2623.         }
  2624.         $response = [
  2625.             'status'  => $status,
  2626.             'message' => $message,
  2627.             'data'    => [
  2628.                 'isExistingCustomerAccount' => $isExistingCustomerAccount,
  2629.                 'customer'                  => $customer,
  2630.             ],
  2631.         ];
  2632.         return $response;
  2633.     }
  2634.     private function getExistingMemberAccount($application)
  2635.     {
  2636.         $em $this->getDoctrine()->getManager();
  2637.         $existingMember false;
  2638.         if ($application->getEmail()) {
  2639.             $existingMember $em->getRepository(Member::class)->findOneBy(['email' => $application->getEmail()]);
  2640.         }
  2641.         return $existingMember;
  2642.     }
  2643.     private function createGuestMember($application)
  2644.     {
  2645.         $em $this->getDoctrine()->getManager();
  2646.         $guestMember false;
  2647.         $data['email']     = $application->getEmail();
  2648.         $data['firstName'] = $application->getFirstName();
  2649.         $data['lastName']  = $application->getLastName();
  2650.         $data['name']      = $application->getPerson();
  2651.         $adminEmail        $application->getEmail();
  2652.         // we create an guest membership, memberType needs to be only "MA"
  2653.         $memberType "MA";
  2654.         $guestMember $em->getRepository(Member::class)->createGuestMemberIfNotExists($datafalse$adminEmail$memberType);
  2655.         return $guestMember;
  2656.     }
  2657.     private function processRegistrationWithoutPaymentSimpleOptimized($isExistingMember$user$memberApplication $application$settings$lang$adminEmailBatch $batch$route$routeParameters)
  2658.     {
  2659.         $em $this->getDoctrine()->getManager();
  2660.         $status "error";
  2661.         $message "";
  2662.         $action  "";
  2663.         $mailTemplate "";
  2664.         $targetPath   "";
  2665.         if ($user) {
  2666.             $application->setApplicationStatus('applied');
  2667.             $application $em->getRepository(Application::class)->setApplication($application$member);
  2668.             if ($batch->getProgramId() == 30 && !in_array($batch->getGoToPage(), ['landingPage','editMentorsProfile'])) {
  2669.                 $status  "error";
  2670.                 $targetPath $batch->getGoToPage();
  2671.                 $response = [
  2672.                     'status' => $status,
  2673.                     'message' => $message,
  2674.                     'data' => [
  2675.                         'targetPath'   => $targetPath,
  2676.                         'application'  => $application,
  2677.                         'member'       => $member,
  2678.                     ],
  2679.                 ];
  2680.                 return $response;
  2681.             }
  2682.             $feedback $this->sendConfirmationMailToApplicant($usernull$settings$application$batch);
  2683.             if ($lang == 'EN') {
  2684.                 $message "Thank you for your registration.<br>We have sent you a confirmation email.";
  2685.             } else {
  2686.                 $message "Danke für Deine Registrierung.<br>Wir haben Dir eine Bestätigungsmail geschickt.";
  2687.             }
  2688.             $status  "success";
  2689.             $response = [
  2690.                 'status' => $status,
  2691.                 'message' => $message,
  2692.                 'data' => [
  2693.                     'targetPath'   => $targetPath,
  2694.                     'application'  => $application,
  2695.                     'member'       => $member,
  2696.                 ],
  2697.             ];
  2698.             $status strtoupper($status);
  2699.             $this->session->getFlashBag()->add('notice'"{$status} {$message}");
  2700.             return $response;
  2701.         }
  2702.         if ($isExistingMember && $settings['isNeedsLoginValidation']) {
  2703.             $status  "error";
  2704.             $todo    "validateLogin";
  2705.             $action  "needsLoginValidation";
  2706.             $message "This email address is already in use. Please login to make your application valid.";
  2707.         } elseif ($isExistingMember && !$settings['isNeedsLoginValidation']) {
  2708.             $status  "process";
  2709.             $todo    "notValidatedLogin";
  2710.             $action  "applied";
  2711.             $message "This email address is already in use. Application will be processed without login being validated";
  2712.         } elseif (!$isExistingMember && $settings['isNeedsEmailValidation']) {
  2713.             $status  "error";
  2714.             $todo    "validateEmail";
  2715.             $action  "needsEmailVerification";
  2716.             $message "Email is not yet registered. Email needs to be validated";
  2717.         } elseif (!$isExistingMember && !$settings['isNeedsEmailValidation']) {
  2718.             $status  "process";
  2719.             $todo    "notValidateEmail";
  2720.             $action  "registered";
  2721.             $message "Email is not yet registered. Application will be processed without email being validated";
  2722.         }
  2723.         $application $em->getRepository(Application::class)->setEditStatus($application$status$todo$message);
  2724.         if ($batch->getProgramId() == 30 && !in_array($batch->getGoToPage(), ['landingPage','editMentorsProfile'])) {
  2725.             $application->setApplicationStatus('applied');
  2726.             $application $em->getRepository(Application::class)->setApplication($application$member);
  2727.             $status  "error";
  2728.             $targetPath $batch->getGoToPage();
  2729.             $loginLink $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
  2730.             $response = [
  2731.                 'status' => $status,
  2732.                 'message' => $message,
  2733.                 'data' => [
  2734.                     'targetPath'   => $loginLink,
  2735.                     'application'  => $application,
  2736.                     'member'       => $member,
  2737.                 ],
  2738.             ];
  2739.             return $response;
  2740.         }
  2741.         $phrases $em->getRepository(Option::class)->getApplicationPhrases($batch->getProgram(), $batchfalse$lang);
  2742.         if ($status == 'process') {
  2743.             $application->setApplicationStatus('applied');
  2744.             switch ($lang) {
  2745.                 case "DE":
  2746.                     $message $phrases['event_application_thankyou_sent_confirmationmail'] ?? "Danke für Deine Anmeldung.<br>Wir haben Dir eine Bestätigungsmail geschickt. Wir freuen uns auf Dich!";
  2747.                     break;
  2748.                 case "EN":
  2749.                     $message $phrases['event_application_thankyou_sent_confirmationmail'] ?? "Thanks, Your application is now approved.<br>We have sent you a confirmation mal and we are looking forward to having you!";
  2750.                     break;
  2751.             }
  2752.         } else {
  2753.             $application->setApplicationStatus('started');
  2754.             switch ($lang) {
  2755.                 case "DE":
  2756.                     $message "Danke für Deine Registrierung. Du bist vorgemerkt<br>Wir haben Dir eine Mail zur Bestätigung Deiner E-Mail Adresse geschickt. Bitte klicke den Bestätigungslink in dieser Mail, um Dich endgültig anzumelden";
  2757.                     break;
  2758.                 case "EN":
  2759.                     $message "Thank you for your registration. You are pre-registered.<br>We have sent you an email to confirm your email address. Please click the confirmation link in that email to complete your registration.";
  2760.                     break;
  2761.             }
  2762.         }
  2763.         $this->session->getFlashBag()->add('notice'"SUCCESS {$message}");
  2764.         $application $em->getRepository(Application::class)->setApplication($application$member);
  2765.         $routeParameters['action'] = $action;
  2766.         $routeParameters['lang']   = $lang;
  2767.         $targetPath strtolower($this->generateUrl($route$routeParameters));
  2768.         $loginLink $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
  2769.         if ($isExistingMember && $settings['isNeedsLoginValidation']) {
  2770.             $mailTemplate $em->getRepository(MailTemplate::class)->getValidateLoginMailTemplateByBatch($batch$application$loginLink);
  2771.             $feedback Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
  2772.         } elseif ($isExistingMember && !$settings['isNeedsLoginValidation']) {
  2773.             $this->sendConfirmationMailToApplicant(null$member$settings$application$batch);
  2774.         } elseif (!$isExistingMember && $settings['isNeedsEmailValidation']) {
  2775.             $mailTemplate $em->getRepository(MailTemplate::class)->getValidateEmailMailTemplateByBatch($batch$application$loginLink);
  2776.             $feedback Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
  2777.         } elseif (!$isExistingMember && !$settings['isNeedsEmailValidation']) {
  2778.             $this->sendConfirmationMailToApplicant(null$member$settings$application$batch);
  2779.         }
  2780.         $response = [
  2781.             'status' => $status,
  2782.             'message' => $message,
  2783.             'data' => [
  2784.                 'targetPath'   => $targetPath,
  2785.                 'application'  => $application,
  2786.                 'member'       => $member,
  2787.             ],
  2788.         ];
  2789.         return $response;
  2790.     }
  2791.     private function processRegistrationWithoutPayment($isMemberloggedIn$member$application$settings$request$lang$adminEmail$batch)
  2792.     {
  2793.         $em $this->getDoctrine()->getManager();
  2794.         if ($existingMember $em->getRepository(Member::class)->findOneBy(['email' => $application->getEmail()])) {
  2795.             $status "error";
  2796.             $todo   "validateLogin";
  2797.             if ($existingApplication $em->getRepository(Application::class)->findOneBy(['memberId'=>$existingMember->getId(), 'batchId'=>$batch->getId()])) {
  2798.                 $application $existingApplication;
  2799.                 $message "You have already applied. Please login with the magic link.";
  2800.             } else {
  2801.                 $application->setMemberId($existingMember->getId());
  2802.                 $message "This email address is already in use. Please login to make your application valid.";
  2803.             }
  2804.             $application $em->getRepository(Application::class)->setEditStatus($application$status$todo$message);
  2805.             if (!$application->getApplicationStatus()) {
  2806.                 $application->setApplicationStatus('started');
  2807.             }
  2808.             // set this application i.e. save to database
  2809.             $application $em->getRepository(Application::class)->setApplication($application);
  2810.             $member $em->getRepository(Member::class)->updateMemberWithApplication($existingMember$application$adminEmail);
  2811.             $routeParameters $request->get('_route_params');
  2812.             $routeParameters["app"]    = $application->getId();
  2813.             $routeParameters['action'] = "needsLoginValidation";
  2814.             $targetPath strtolower($this->generateUrl('event_show_single'$routeParameters));
  2815.             $loginLink $this->loginService->getLoginLinkByTargetPath($existingMember->getEmail(), $targetPath);
  2816.             $mailTemplate $em->getRepository(MailTemplate::class)->getValidateLoginMailTemplateByBatch($batch$application$loginLink);
  2817.             /*
  2818.                 if ($lang == 'DE'){
  2819.                     $this->session->getFlashBag()->add('notice', 'SUCCESS Danke für Deine Anmeldung. Du bist vorgemerkt. Wir haben Dir eine Mail zur Ãœberprüfung geschickt. Sobald Du Deine Anmeldung bestätigt hast, wird die Anmeldung gültig.' );
  2820.                 } else {
  2821.                     $this->session->getFlashBag()->add('notice', 'SUCCESS Thank you for your registration. You are registered. As soon as you have confirmed your registration, the registration will be valid.' );
  2822.                 }
  2823.             */
  2824.             $response = [
  2825.                 'status'  => $status,
  2826.                 'message' => $message,
  2827.                 'data'    => [
  2828.                     'mailTemplate' => $mailTemplate,
  2829.                     'targetPath'   => $targetPath,
  2830.                 ],
  2831.             ];
  2832.             return $response;
  2833.         } else {
  2834.             $status "error";
  2835.             $todo   "validateEmail";
  2836.             $message "Email is not yet registered. Email needs to be validated";
  2837.             $application $em->getRepository(Application::class)->setErrorValidateEmail($application);
  2838.             $data['email']     = $application->getEmail();
  2839.             $data['firstName'] = $application->getFirstName();
  2840.             $data['lastName']  = $application->getLastName();
  2841.             $data['name']      = $application->getPerson();
  2842.             $adminEmail        $application->getEmail();
  2843.             if ($settings['isTeamNeeded']) {
  2844.                 $memberType "GF";
  2845.             } else {
  2846.                 $memberType "MA";
  2847.             }
  2848.             $member $em->getRepository(Member::class)->createGuestMemberIfNotExists($datafalse$adminEmail$memberType);
  2849.             $application->setMemberId($member->getId());
  2850.             $application->setTeamId($member->getTeamId());
  2851.             $application $em->getRepository(Application::class)->setEditStatus($application$status$todo$message);
  2852.             if (!$application->getApplicationStatus()) {
  2853.                 $application->setApplicationStatus('started');
  2854.             }
  2855.             // set this application i.e. save to database
  2856.             $application $em->getRepository(Application::class)->setApplication($application);
  2857.             $member $em->getRepository(Member::class)->updateMemberWithApplication($member$application$adminEmail);
  2858.             $routeParameters $request->get('_route_params');
  2859.             $routeParameters["app"]    = $application->getId();
  2860.             $routeParameters['action'] = "needsEmailVerification";
  2861.             $targetPath strtolower($this->generateUrl('event_show_single'$routeParameters));
  2862.             $loginLink $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
  2863.             $mailTemplate $em->getRepository(MailTemplate::class)->getValidateEmailMailTemplateByBatch($batch$application$loginLink);
  2864.             /*
  2865.             if ($lang == 'DE'){
  2866.                 $this->session->getFlashBag()->add('notice', 'SUCCESS Danke für Deine Anmeldung. Du bist vorgemerkt. Wir haben Dir eine Mail zur Ãœberprüfung geschickt. Sobald Du Deine E-Mail Adresse bestätigt hast, wird die Anmeldung gültig.' );
  2867.             } else {
  2868.                 $this->session->getFlashBag()->add('notice', 'SUCCESS Thank you for your registration. You are registered. As soon as you have confirmed your e-mail address, the registration will be valid.' );
  2869.             }
  2870.             */
  2871.             $response = [
  2872.                 'status'  => $status,
  2873.                 'message' => $message,
  2874.                 'data'    => [
  2875.                     'mailTemplate' => $mailTemplate,
  2876.                     'targetPath'   => $targetPath,
  2877.                 ],
  2878.             ];
  2879.             return $response;
  2880.         }
  2881.     }
  2882.     /**
  2883.      * @Route("/event-ics/download/{eventId}", name="event_download")
  2884.      */
  2885.     public function downloadCalendarEventByEventId($eventId)
  2886.     {
  2887.         $em $this->getDoctrine()->getManager();
  2888.         if (!$event $em->getRepository(Event::class)->findOneBy(['id' => $eventId])) {
  2889.             return "";
  2890.         }
  2891.         if (!$batch $em->getRepository(Batch::class)->findOneBy(['eventId' => $eventId])) {
  2892.             return "";
  2893.         }
  2894.         $zoomLink  $batch->getZoomlink();
  2895.         $description $zoomLink "Teilnehmen mit {$zoomLink}"";
  2896.         $summary $event->getTitle();
  2897.         // Zeitzone für Berlin und UTC einrichten
  2898.         $timezoneBerlin = new \DateTimeZone('Europe/Berlin');
  2899.         $timezoneUTC = new \DateTimeZone('UTC');
  2900.         // Start- und Endzeit des Events konvertieren
  2901.         $startDate $event->getStartDate();
  2902.         $startDate->setTimezone($timezoneUTC);
  2903.         $icsStartDate $startDate->format('Ymd\THis\Z');
  2904.         $endDate $event->getEndDate();
  2905.         $endDate->setTimezone($timezoneUTC);
  2906.         $icsEndDate $endDate->format('Ymd\THis\Z');
  2907.         // Erstellen des iCal-Inhalts mit den konvertierten Zeiten
  2908.         $icsContent "BEGIN:VCALENDAR\r\n";
  2909.         $icsContent .= "VERSION:2.0\r\n";
  2910.         $icsContent .= "BEGIN:VEVENT\r\n";
  2911.         $icsContent .= "DTSTART:{$icsStartDate}\r\n";
  2912.         $icsContent .= "DTEND:{$icsEndDate}\r\n";
  2913.         $icsContent .= "SUMMARY:{$summary}\r\n";
  2914.         $icsContent .= "DESCRIPTION:{$description}\r\n";
  2915.         $icsContent .= "END:VEVENT\r\n";
  2916.         $icsContent .= "END:VCALENDAR";
  2917.         header('Content-Type: text/calendar; charset=utf-8');
  2918.         header('Content-Disposition: attachment; filename="calendar_' $event->getSlug() . '.ics"');
  2919.         echo $icsContent;
  2920.         exit;
  2921.     }
  2922.     /**
  2923.      * Download ICS calendar file using Batch dates for multi-batch events
  2924.      *
  2925.      * @Route("/event-ics/download/batch/{batchId}", name="event_download_batch")
  2926.      */
  2927.     public function downloadCalendarEventByBatchId($batchId)
  2928.     {
  2929.         $em $this->getDoctrine()->getManager();
  2930.         // Find batch
  2931.         if (!$batch $em->getRepository(Batch::class)->find($batchId)) {
  2932.             return new Response("Batch not found"Response::HTTP_NOT_FOUND);
  2933.         }
  2934.         // Find event
  2935.         if (!$event $em->getRepository(Event::class)->find($batch->getEventId())) {
  2936.             return new Response("Event not found"Response::HTTP_NOT_FOUND);
  2937.         }
  2938.         // Determine which dates to use: Batch dates for multi-batch events, Event dates otherwise
  2939.         if ($event->isMultiBatchEvent() && $batch->getStartDate()) {
  2940.             $startDate = clone $batch->getStartDate();
  2941.             $endDate $batch->getEndDate() ? clone $batch->getEndDate() : (clone $startDate)->modify('+2 hours');
  2942.         } else {
  2943.             $startDate = clone $event->getStartDate();
  2944.             $endDate = clone $event->getEndDate();
  2945.         }
  2946.         // Prepare description
  2947.         $zoomLink $batch->getZoomlink();
  2948.         $description $zoomLink "Teilnehmen mit {$zoomLink}"";
  2949.         $summary $event->getTitle();
  2950.         // Convert to UTC
  2951.         $timezoneUTC = new \DateTimeZone('UTC');
  2952.         $startDate->setTimezone($timezoneUTC);
  2953.         $endDate->setTimezone($timezoneUTC);
  2954.         $icsStartDate $startDate->format('Ymd\THis\Z');
  2955.         $icsEndDate $endDate->format('Ymd\THis\Z');
  2956.         // Generate ICS content
  2957.         $icsContent "BEGIN:VCALENDAR\r\n";
  2958.         $icsContent .= "VERSION:2.0\r\n";
  2959.         $icsContent .= "BEGIN:VEVENT\r\n";
  2960.         $icsContent .= "DTSTART:{$icsStartDate}\r\n";
  2961.         $icsContent .= "DTEND:{$icsEndDate}\r\n";
  2962.         $icsContent .= "SUMMARY:{$summary}\r\n";
  2963.         $icsContent .= "DESCRIPTION:{$description}\r\n";
  2964.         $icsContent .= "END:VEVENT\r\n";
  2965.         $icsContent .= "END:VCALENDAR";
  2966.         header('Content-Type: text/calendar; charset=utf-8');
  2967.         header('Content-Disposition: attachment; filename="calendar_' $event->getSlug() . '_batch_' $batch->getId() . '.ics"');
  2968.         echo $icsContent;
  2969.         exit;
  2970.     }
  2971.     private function sendConfirmationMailToApplicant(User $user nullMember $member null$settingsApplication $applicationBatch $batchbool $allowNewsletterRegistration true) {
  2972.         if (isset($settings['isSendConfirmationMail']) && !$settings['isSendConfirmationMail']) {
  2973.             return false;
  2974.         }
  2975.         if ($user === null && $member === null) {
  2976.             return false;
  2977.         }
  2978.         $feedback "no mail";
  2979.         $em $this->getDoctrine()->getManager();
  2980.         if ($user === null) {
  2981.             $user $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
  2982.         }
  2983.         if ($member === null) {
  2984.             $member $em->getRepository(Member::class)->find($user->getMemberId());
  2985.         }
  2986.         $email $member->getEmail();
  2987.         if (!Utility::validateEmail($email)) {
  2988.             return false;
  2989.         }
  2990.         if (!$user->getisNoBulkMail() && $allowNewsletterRegistration){
  2991.             if (!$member->getHasKiNewsletter()){
  2992.                 $action "on";
  2993.                 $payload json_encode([
  2994.                     'firstName'   => $member->getFirstName(),
  2995.                     'lastName'    => $member->getLastName(),
  2996.                     'email'       => $member->getEmail(),
  2997.                     'phone'       => $member->getPhone(),
  2998.                     'linkedIn'    => $member->getLinkedin(),
  2999.                     'utmSource'   => 'Allmeda',
  3000.                     'utmMedium'   => $application->getBatchSlug(),
  3001.                     'utmCampaign' => '',
  3002.                     'action'      => $action
  3003.                 ]);
  3004.                 ##todo newsletter anmeldung neu denken
  3005.                 if ($batch->getProgramId() != 30) {
  3006.                     $response Utility::curl_callback("https://hook.eu2.make.com/8258grek86fuwqjgi7dh3npvowxjvfk5"$payload);
  3007.                     if ($action == 'on') {
  3008.                         $action true;
  3009.                     }
  3010.                     $member->setHasKiNewsletter((bool)$action);
  3011.                     $em->persist($member);
  3012.                 }
  3013.             }
  3014.         }
  3015.         if (!$settings) {
  3016.             $application->setStartupName("Test Startup");
  3017.             $application->setEmail($user->getEmail());
  3018.         }
  3019.         $lang $batch->getLang();
  3020.         $program $batch->getProgram();
  3021.         $routeParameters = [
  3022.             'programSlug' => $program->getSlug(),
  3023.             'batchSlug' => $batch->getSlug(),
  3024.             'lang'      => $lang,
  3025.         ];
  3026.         $targetPath $this->generateUrl('apply_program_home'$routeParameters);
  3027.         $loginLink  $this->loginService->getLoginLinkByTargetPath($application->getEmail(), $targetPath);
  3028.         // Get event for multi-batch check
  3029.         $event null;
  3030.         if ($batch->getEventId()) {
  3031.             $event $em->getRepository(Event::class)->find($batch->getEventId());
  3032.         }
  3033.         // Use MailTemplateService for confirmation mail
  3034.         $confirmationMail $this->mailTemplateService->renderConfirmationMail(
  3035.             $batch,
  3036.             $user,
  3037.             $application,
  3038.             $loginLink,
  3039.             $event
  3040.         );
  3041.         $feedback Utility::sendAlertMailPerZapier(
  3042.             $application->getEmail(), 
  3043.             $confirmationMail['html'], 
  3044.             $confirmationMail['subject'], 
  3045.             $batch->getSenderMail(), 
  3046.             $batch->getFromName(), 
  3047.             'html'
  3048.         );
  3049.         $application->setHasConfirmationMailSent(true);
  3050.         $application $em->getRepository(Application::class)->setApplicationDoneWithoutWorkflow($application);
  3051.         // Use MailTemplateService for admin notification mail
  3052.         $adminMail $this->mailTemplateService->renderAdminNotificationMail(
  3053.             $batch,
  3054.             $user,
  3055.             $application
  3056.         );
  3057.         // Send admin notification mail
  3058.         if ($batch->getRecipientMonitoringMail()) {
  3059.             Utility::sendDirectMail(
  3060.                 $this->mailer
  3061.                 $adminMail['subject'], 
  3062.                 $application->getEmail(), 
  3063.                 ''
  3064.                 $batch->getRecipientMonitoringMail(), 
  3065.                 ''
  3066.                 $adminMail['body'], 
  3067.                 false
  3068.             );
  3069.         }
  3070.         // sanitize title field
  3071.         if (!$em->getRepository(Member::class)->isTitleValid($member->getTitle())) {
  3072.             // Titel sanitizen, z. B. auf null setzen oder einen Standardwert zuweisen
  3073.             $member->setTitle("") ;
  3074.         }
  3075.         // Update member statistics and sync with hubspot
  3076.         $this->memberHubspotService->updateMemberStatisticsAndSyncWithHubspot($memberfalse);
  3077.         // Assuming updateMemberStatistics directly updates and persists the member entity
  3078.         $em->flush();
  3079.         $em->refresh($member);
  3080.         if ($batch->getSkoolWebhook() > ''){
  3081.             $feedback $this->callbackService->sendPostRequest($batch->getSkoolWebhook(), $application->getEmail());
  3082.         }
  3083.         if ($batch->getZapierWebhook() > ''){
  3084.             $payload json_encode($em->getRepository(Event::class)->setPayloadSingleApplicationAndBatch($application$batch));
  3085.             $callbackUrl $batch->getZapierWebhook();
  3086.             $feedback $this->callbackService->curl_callback($callbackUrl$payload);
  3087.         }
  3088.         return $feedback;
  3089.     }
  3090.     /**
  3091.      * @Route("/archiv/event/{slug}/", name="event_archiv_show")
  3092.      */
  3093.     public function showAction(Request $request$slug)
  3094.     {
  3095.         if ($slug == '22-rheinland-pitch-mai-2015-2015-04-27-1430300112') {
  3096.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '23-rheinland-pitch-juni-2015-finale-duesseldorf']));
  3097.         }
  3098.         if ($slug == 'rheinland-pitch') {
  3099.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '01-rheinland-pitch-april-2013']));
  3100.         }
  3101.         if ($slug == 'rheinland-pitch-mai') {
  3102.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '02-rheinland-pitch-mai-2013']));
  3103.         }
  3104.         if ($slug == 'rheinland-pitch-juni') {
  3105.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '03-rheinland-pitch-juni-2013']));
  3106.         }
  3107.         if ($slug == 'rheinland-pitch-august') {
  3108.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '05-rheinland-pitch-august-2013']));
  3109.         }
  3110.         if ($slug == 'rheinland-pitch-september') {
  3111.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '06-rheinland-pitch-september-2013']));
  3112.         }
  3113.         if ($slug == 'rheinland-pitch-oktober') {
  3114.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '07-rheinland-pitch-oktober-2013']));
  3115.         }
  3116.         if ($slug == 'rheinland-pitch-november') {
  3117.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '08-rheinland-pitch-november-2013']));
  3118.         }
  3119.         if ($slug == 'rheinland-pitch-januar') {
  3120.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '09-rheinland-pitch-januar-2014']));
  3121.         }
  3122.         if ($slug == 'rheinland-pitch-februar') {
  3123.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '10-rheinland-pitch-februar-2014']));
  3124.         }
  3125.         if ($slug == 'rheinland-pitch-marz') {
  3126.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '11-rheinland-pitch-maerz-2014']));
  3127.         }
  3128.         if ($slug == 'rheinland-pitch-april') {
  3129.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '12-rheinland-pitch-april-2014']));
  3130.         }
  3131.         if ($slug == 'rheinland-pitch-mai-3') {
  3132.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '13-rheinland-pitch-mai-2014']));
  3133.         }
  3134.         if ($slug == 'rheinland-pitch-juni-2') {
  3135.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '14-rheinland-pitch-juni-2014']));
  3136.         }
  3137.         if ($slug == '22-rheinland-pitch-mai-2015-2015-04-27') {
  3138.             return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '22-rheinland-pitch-mai-2015']));
  3139.         }
  3140.         $templateVars $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
  3141.         if (!$event $this->getEventRepository()->findOneBy(['slug' => $slug])) {
  3142.             $querySlug '/' $slug;
  3143.             if (!$event $this->getEventRepository()->findOneBy(['slug' => $querySlug])) {
  3144.                 $this->session->getFlashBag()->add('notice''ERROR Veranstaltung nicht gefunden');
  3145.                 return $this->redirect($this->generateUrl('events_list'));
  3146.             } else {
  3147.                 $em $this->getDoctrine()->getManager();
  3148.                 $event->setSlug($slug);
  3149.                 $em->persist($event);
  3150.                 $em->flush();
  3151.             }
  3152.         }
  3153.         if ($event->getStatus() != 'publish') {
  3154.             if (!$this->getUser()) {
  3155.                 return $this->redirect($this->generateUrl('events_list'$templateVars));
  3156.             }
  3157.         }
  3158.         //previous @StartPlatzEventBundle/Default/show.html.twig
  3159.         return $this->render('@StartPlatzEventBundle/Default/event-single.html.twig', [
  3160.             'templateVars' => $templateVars,
  3161.             'event'        => $event,
  3162.             'batch'        => false,
  3163.             'speakers'     => [],
  3164.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  3165.         ]);
  3166.     }
  3167.     /**
  3168.      */
  3169.     public function eventsThisWeekAction()
  3170.     {
  3171.         $year date('Y');
  3172.         $month date('m');
  3173.         $moreFutureEvents $this->getEventBaseRepository()->getEventsByWeek($this->getEventComRep()->setBaseCriteria([]), $year$month);
  3174.         return $this->render('@StartPlatzEventBundle/Default/events_this_week.html.twig', [
  3175.             'moreFutureEvents' => $moreFutureEvents,
  3176.         ]);
  3177.     }
  3178.     private function getRedirects()
  3179.     {
  3180.         return [
  3181.             'Best-Practice-Digitalisierung-Januar-2018' => 'best-practice-digitalisierung',
  3182.             'best-practice-digitalisierung-3' => 'best-practice-digitalisierung',
  3183.             'adwords-fuer-einsteiger-' => 'adwords-fuer-einsteiger-koeln-2017-01-24',
  3184.             'adwords-fur-einsteiger-'  => 'adwords-fuer-einsteiger-koeln-2016-06-06',
  3185.             'adwords-fur-einsteiger-DUS' => 'adwords-fuer-einsteiger-dus-2016-06-07',
  3186.             '-adwords-fuer-einsteiger' => 'adwords-fur-einsteiger',
  3187.             'adwords-fur-einsteiger-2016-02-16' => 'adwords-fuer-einsteiger-koeln-2016-02-16',
  3188.             'digital-networking-week-1611257320' => 'digital-networking-week',
  3189.             'chatgpt3-how-to-prompt-duesseldorf' => 'chatgpt-how-to-prompt-duesseldorf'
  3190.         ];
  3191.     }
  3192.     private function setMonsumAccountIfNotExists(Member $memberTeam $teamApplication $application$account)
  3193.     {
  3194.         $em $this->getDoctrine()->getManager();
  3195.         $adminEmail $member->getEmail();
  3196.         $responseContent = [];
  3197.         $teamId $team->getId();
  3198.         $customer null;
  3199.         $monsumCustomer null;
  3200.         $settings = ['adminEmail' => $adminEmail'location' => $em->getRepository(Customer::class)->getLocationByAccount($account), 'account' => $account'accountHash' => $em->getRepository(Customer::class)->getAccountHashByAccount($account)];
  3201.         $login $em->getRepository(Customer::class)->getLoginByAccount($account);
  3202.         $payload json_encode(['SERVICE' => "customer.get"'FILTER' => ['CUSTOMER_EXT_UID' => "{$team->getId()}"]]);
  3203.         $feedback $this->callbackService->curl_get_monsum_all($payload$login);
  3204.         $feedback json_decode((string) $feedback);
  3205.         if (isset($feedback->RESPONSE->CUSTOMERS[0])) {
  3206.             $responseContent[] = "Team {$teamId} has already account in Monsum";
  3207.             $customer $feedback->RESPONSE->CUSTOMERS[0];
  3208.         } else {
  3209.             $responseContent[] = "New account will be created for team {$teamId} in Monsum";
  3210.             $customerSettings $em->getRepository(Customer::class)->getCustomerSettingsByTeamAndMember($team$member);
  3211.             $payload json_encode(['SERVICE' => "customer.create"'DATA' => $customerSettings]);
  3212.             $feedback $this->callbackService->curl_get_monsum_all($payload$login);
  3213.             $feedback json_decode((string) $feedback);
  3214.             $monsumResponse $feedback->RESPONSE;
  3215.             if (isset($monsumResponse->ERRORS)) {
  3216.                 $responseContent[] = "ERROR ======== ERROR";
  3217.                 $responseContent[] = $monsumResponse->ERRORS;
  3218.                 $responseContent[] = $feedback;
  3219.             } else {
  3220.                 $responseContent[] = $monsumResponse;
  3221.                 if ($monsumResponse->STATUS == "success") {
  3222.                     $customerId $monsumResponse->CUSTOMER_ID;
  3223.                     $payload $em->getRepository(Customer::class)->getPayloadSingleCustomerByCustomerId($customerId);
  3224.                     $feedback $this->callbackService->curl_get_monsum_all($payload$login);
  3225.                     if ($feedback json_decode((string) $feedback)) {
  3226.                         $customer $feedback->RESPONSE->CUSTOMERS[0];
  3227.                     } else {
  3228.                         $responseContent[] = "ERROR: could not decode feedback from monsum";
  3229.                         $responseContent[] = $feedback;
  3230.                     }
  3231.                 }
  3232.             }
  3233.         }
  3234.         if ($customer) {
  3235.             $monsumCustomer $em->getRepository(Customer::class)->createOrUpdateMonsumCustomerByTeam($customer$settings$team);
  3236.         }
  3237.         return $monsumCustomer;
  3238.     }
  3239.     private function prepareCustomerForMonsumCheckout(Application $applicationProduct $product$memberToBeRegistered)
  3240.     {
  3241.         // see applyController $this->goToMonsum( ... )
  3242.         $em $this->getDoctrine()->getManager();
  3243.         // save member, assign member to team, save application
  3244.         $data['email'] = $application->getEmail();
  3245.         $data['name']  = $application->getPerson();
  3246.         $adminEmail    $application->getEmail();
  3247.         if (!$memberToBeRegistered) {
  3248.             $memberToBeRegistered $this->createGuestMember($application);
  3249.         }
  3250.         $team     $em->getRepository(Team::class)->createNewMonsumAccountTeamByMembershipApplicant($memberToBeRegistered);
  3251.         $customer $this->setMonsumAccountIfNotExists($memberToBeRegistered$team$application$product->getAccount());
  3252.         $productNumber $product->getProductNumber();
  3253.         $customer $em->getRepository(Customer::class)->setCheckoutData($customer$product->getAccount(), $productNumber$memberToBeRegistered->getId());
  3254.         $response = [
  3255.             'status'  => 'success',
  3256.             'message' => 'accounts created',
  3257.             'data'    => [
  3258.                 'memberToBeRegistered'      => $memberToBeRegistered,
  3259.                 'customer'                  => $customer,
  3260.                 'team'                      => $team,
  3261.             ],
  3262.         ];
  3263.         return $response;
  3264.     }
  3265.     private function legacyPaymentProcess($application$isMemberloggedIn$em$batch$member$event){
  3266.         if ($application->getRealPriceinEuroCent()){
  3267.             if ($isMemberloggedIn){
  3268.                 // bei monsum zahlen
  3269.                 if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  3270.                     $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  3271.                     return $this->redirect($this->generateUrl('events_list'));
  3272.                 }
  3273.                 $account $product->getAccount();
  3274.                 if (!$customer $em->getRepository(Customer::class)->findPrimaryMonsumAccount($member$account)) {
  3275.                     $this->session->getFlashBag()->add('notice''ERROR monsum customer not found');
  3276.                     return $this->redirect($this->generateUrl('events_list'));
  3277.                     //$team = $em->getRepository(Team::class)->createTeamByMembershipApplicant($member);
  3278.                     //$customer = $this->setMonsumAccountIfNotExists($member, $team, $application, $account);
  3279.                 }
  3280.                 $productNumber $product->getProductNumber();
  3281.                 $customer $em->getRepository(Customer::class)->setCheckoutData($customer$account$productNumber$member->getId());
  3282.                 $unitPrice $application->getRealPriceinEuroCent() / 100;
  3283.                 $login $em->getRepository(Customer::class)->getLoginByAccount($customer->getAccount());
  3284.                 $payload json_encode(['SERVICE' => "subscription.create"'DATA' => ['CUSTOMER_ID'    => "{$customer->getCustomerId()}"'ARTICLE_NUMBER' => "31011"'UNIT_PRICE'     => "{$unitPrice}"'TITLE'          => "Teilnahme an STARTPLATZ Event"'DESCRIPTION'    => "{$event->getTitle()} am {$event->getStartDate()->format('d.m.Y')}"'START_DATE'     => "{$event->getStartDate()->format('Y-m-d')}"]]);
  3285.                 // Set URL to call back
  3286.                 $callbackUrl "https://app.monsum.com/api/1.0/api.php";
  3287.                 $feedback Utility::curl_callback($callbackUrl$payload$login);
  3288.                 $feedback json_decode((string) $feedback);
  3289.                 if (isset($feedback->RESPONSE->STATUS) and $feedback->RESPONSE->STATUS == "success") {
  3290.                     $subscriptionId $feedback->RESPONSE->SUBSCRIPTION_ID;
  3291.                     $application->setEditStatus("sucess:subscriptionId=={$subscriptionId}");
  3292.                     $application $em->getRepository(Application::class)->setHistory($application$application->getEditMessage());
  3293.                     $application->setApplicationStatus('applied');
  3294.                     $em->persist($application);
  3295.                     $em->flush();
  3296.                 } else {
  3297.                     $application->setEditStatus("error:monsum-purchase-failed");
  3298.                     $em->persist($application);
  3299.                     $em->flush();
  3300.                     $action "error";
  3301.                 }
  3302.             }
  3303.         }
  3304.     }
  3305. }