<?php declare(strict_types=1);
namespace App\StartPlatz\Bundle\EventBundle\Controller;
/**
* CRITICAL DEPENDENCY WARNING - APPLICATION SYSTEM INTEGRATION
*
* This controller has HEAVY DEPENDENCIES on the Application system from StartupBundle.
* Event registration is the MOST IMPORTANT registration method for events and relies
* extensively on the Application infrastructure.
*
* DO NOT modify Application-related code without testing ALL event registration scenarios:
* - Anonymous (non-member) event registration
* - Member event registration
* - Paid event registration with Stripe
* - Free event registration
* - Multi-batch event registration
* - Event registration with validation workflows
*
* Critical dependencies:
* 1. Application Entity & Repository (30+ method calls)
* - createApplicationByApplyForm()
* - setApplication()
* - updateApplicationByMember()
* - setApplicationDone()
* - cleanApplicationData()
* - And many more...
*
* 2. ApplicationType Form
* - Used for all event registration forms
* - Field definitions must remain compatible
*
* 3. RheinlandPitchBundle Templates
* - StartPlatzRheinlandPitchBundle/Apply/_edit.registration.event.widget.html.twig
* - Shared validation and workflow logic
*
* 4. Shared Workflows
* - Email validation (validateEmail/validateLogin)
* - Member creation and assignment
* - Payment processing
* - Confirmation emails
*
* Key methods with Application dependencies:
* - eventSingleAction() - Main event page with registration
* - getOrCreateApplication() - Application creation/retrieval
* - updateApplicationWithMemberIdAndTeamId() - Member linking
* - validateEventApplication() - Validation workflows
* - setEventApplicationDone() - Completion logic
* - handleAction() - Payment and status management
*
* See also:
* - StartupBundle/Repository/ApplicationRepository.php
* - RheinlandPitchBundle/Controller/ApplyController.php
* - Documentation: /doc/claude-files/application-process.md#event-integration
*/
use App\StartPlatz\Bundle\AlphaBundle\Entity\Page;
use App\StartPlatz\Bundle\ApiBundle\MemberHubspotService;
use App\StartPlatz\Bundle\FeedbackBundle\CallbackService;
use App\StartPlatz\Bundle\FeedbackBundle\Form\ImportFormType;
use App\StartPlatz\Bundle\MailmanBundle\Entity\MailTemplate;
use App\StartPlatz\Bundle\MemberBundle\Entity\Member;
use App\StartPlatz\Bundle\MemberBundle\Entity\Option;
use App\StartPlatz\Bundle\MemberBundle\Entity\Product;
use App\StartPlatz\Bundle\MemberBundle\Entity\Service;
use App\StartPlatz\Bundle\MemberBundle\Entity\Team;
use App\StartPlatz\Bundle\MetaBundle\Entity\Attribute;
use App\StartPlatz\Bundle\MetaBundle\Entity\Tag;
use App\StartPlatz\Bundle\MonsumBundle\Entity\Customer;
use App\StartPlatz\Bundle\StartupBundle\Entity\Application;
use App\StartPlatz\Bundle\StartupBundle\Entity\Batch;
use App\StartPlatz\Bundle\StartupBundle\Entity\Program;
use App\StartPlatz\Bundle\StartupBundle\Form\ApplicationType;
use App\StartPlatz\Bundle\StatisticBundle\Entity\Statistic;
use App\StartPlatz\Bundle\UserBundle\Entity\User;
use App\StartPlatz\Bundle\UserBundle\LoginService;
use App\StartPlatz\Bundle\WebsiteBundle\Utility\Utility;
use DateTime;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use App\StartPlatz\Bundle\EventBundle\Entity\Event;
use App\StartPlatz\Bundle\EventBundle\Entity\EventRepository;
use App\StartPlatz\Bundle\EventBundle\Model\EventBaseRepository;
use App\StartPlatz\Bundle\EventBundle\Model\EventCommonRepository;
use App\StartPlatz\Bundle\WebsiteBundle\MenuTranslationService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Psr\Log\LoggerInterface;
use App\StartPlatz\Bundle\EventBundle\Service\MailTemplateService;
class DefaultController extends AbstractController
{
public function __construct(
private readonly SessionInterface $session,
private readonly FormFactoryInterface $formFactory,
private readonly MenuTranslationService $menuTranslationService,
private readonly CallbackService $callbackService,
private readonly MemberHubspotService $memberHubspotService,
private readonly MailerInterface $mailer,
private readonly LoginService $loginService,
private readonly EventCommonRepository $eventCommonRepository,
private readonly UrlGeneratorInterface $urlGenerator,
private readonly EventBaseRepository $eventBaseRepository,
private readonly LoggerInterface $logger,
private readonly MailTemplateService $mailTemplateService
) {
}
/**
* @return EventCommonRepository
*/
protected function getEventComRep()
{
return $this->eventCommonRepository;
}
/**
* @return EventBaseRepository
*/
protected function getEventBaseRepository()
{
return $this->eventBaseRepository;
}
/** @return EventRepository */
protected function getEventRepository(): EventRepository
{
$em = $this->getDoctrine()->getManager();
return $em->getRepository(Event::class);
}
/**
*/
public function showEventsAction($time = "upcoming", $term='', $title="", $text = '')
{
// sanitize variable from wordPress
$time = str_replace('“', '', (string) $time);
$term = str_replace('“', '', (string) $term);
$title = str_replace('“', '', (string) $title);
$text = str_replace('“', '', (string) $text);
$terms = explode('#', $term);
$events = $this->getEventRepository()->findPublicByTerms($terms, [], ($time == 'upcoming') ? 'future' : 'past');
return $this->render('@StartPlatzEventBundle/Default/show_events.html.twig', [
'events' => $events,
'title' => $title,
'templateVars' => [],
'term' => $term,
'text'=>$text,
]);
}
/**
*/
public function showFutureEventsAction($term = 'startup', $variant = 'default')
{
$term = ltrim((string) $term, '#');
$events = $this->getEventRepository()->findPublicEventsByTerm($term);
$view = '@StartPlatzEventBundle/Default/show_future_events.html.twig';
if ($variant == 'black-rows') {
$view = '@StartPlatzEventBundle/Default/show_future_events.black-rows.html.twig';
}
return $this->render($view, [
'events' => $events,
'term' => $term,
'templateVars' => [],
]);
}
/**
*/
public function showNextEventsAction(Request $request, $limit = 3)
{
$events = $this->getEventRepository()->findNextEvents($limit);
return $this->render('@StartPlatzEventBundle/Default/show_next_events.html.twig', [
'events' => $events,
'templateVars' => [],
'lang' => $this->menuTranslationService->getLang($request->server->get('REDIRECT_URL')),
]);
}
/**
*/
public function showListGroupEventsAction(Request $request, $limit = 15)
{
$events = $this->getEventRepository()->findNextEvents($limit);
return $this->render('@StartPlatzEvent/Default/show_list_group_events.html.twig', [
'events' => $events,
'templateVars' => [],
'lang' => $this->menuTranslationService->getLang($request->server->get('REDIRECT_URL')),
]);
}
/**
*/
public function showPastEventsAction($term)
{
$events = $this->getEventRepository()->findPastPublicEventsByTerm($term);
return $this->render('@StartPlatzEventBundle/Default/show_past_events.html.twig', [
'events' => $events,
'term' => $term,
'templateVars' => [],
]);
}
/**
* @Route("/events", name="events_list")
* @Route("/en/events", name="events_list_english")
* @Route("/fr/evenements", name="events_list_french")
* @Route("/events/bevorstehend/")
* @Route("/events/list")
* @Route("/en/events/list")
* @Route("/fr/evenements/list")
* @Route("/events/embed/list")
* @Route("/en/events/embed/list")
* @Route("/fr/evenements/embed/list") */
public function eventsAction(Request $request)
{
$templateVars = $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
$em = $this->getDoctrine()->getManager();
// Get the current path
$path = $request->getPathInfo();
// Check if the path starts with '/en/' or '/fr/'
if (strpos($path, '/en/') === 0) {
$lang = 'EN';
} elseif (strpos($path, '/fr/') === 0) {
$lang = 'FR';
} else {
$lang = 'DE';
}
// Check if the path contains '/en/'
$embed = false;
if (str_contains($path, '/embed/') ) {
$embed = true;
}
// Fetch u tags
$uTags = $em->getRepository(Tag::class)->findBy(['domain' => 'event', 'isEditableByUser' => 1], ['tag' => 'ASC']);
// Convert to key-value array
$uTagsArray = array_map(function (Tag $entity) {
return [
'tag' => $entity->getTag(), // Dies entspricht dem 'slug' in deiner URL-Struktur
'label' => $entity->getLabel()
];
}, $uTags);
// Fetch w tags
$wTags = $em->getRepository(Tag::class)->findBy(['domain' => 'event', 'isUsedInWordPress' => 1], ['tag' => 'ASC']);
// Convert to key-value array
$wTagsArray = array_map(function (Tag $entity) {
return [
'tag' => $entity->getTag(), // Dies entspricht dem 'slug' in deiner URL-Struktur
'label' => $entity->getLabel()
];
}, $wTags);
$additionalEvents = [];
$queryString = '';
// Check for location filter first (location-based filtering)
if (isset($templateVars['location']) and $location = $templateVars['location']) {
$futureEvents = $this->getEventRepository()->findPublicByLocationWithBatches($location, $this->getEventComRep()->setBaseCriteria($templateVars), 'future');
$pastEvents = $this->getEventRepository()->findPublicByLocationWithBatches($location, $this->getEventComRep()->setBaseCriteria($templateVars), 'past');
$moreFutureEvents = $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
}
// Check for tag filter (tag-based filtering)
elseif (isset($templateVars['tag']) and $tag = $templateVars['tag']) {
$futureEvents = $this->getEventRepository()->findPublicByTagWithBatches('#' . $tag, $this->getEventComRep()->setBaseCriteria($templateVars), 'future');
/*
* falls einer der zielgruppen tags == uTags gewählt ist
* dann hole auch die events für alle fTags und startplatz-ai-hub
*/
// Extrahiere alle 'tag' Werte aus dem $uTagsArray
$uTagsSlugs = array_column($uTagsArray, 'tag');
// Überprüfe, ob der gewählte $tag in den $uTagsSlugs enthalten ist
$isUTag = in_array($tag, $uTagsSlugs);
if ($isUTag) {
// Fetch f tags
$fTags = $em->getRepository(Tag::class)->findBy(['domain' => 'event', 'isFavorite' => 1], ['tag' => 'ASC']);
// Extract just the tags to a simple array
$fTagsList = array_map(function (Tag $entity) {
return $entity->getTag(); // Dies gibt nur den 'tag' zurück
}, $fTags);
$additionalEvents = $this->getEventRepository()->findPublicByTagsExcludingTag($fTagsList, '#' . $tag, $this->getEventComRep()->setBaseCriteria($templateVars), 'future');
}
$pastEvents = $this->getEventRepository()->findPublicByTagWithBatches('#' . $tag, $this->getEventComRep()->setBaseCriteria($templateVars), 'past');
$moreFutureEvents = $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
}
// No filter - show all events
else {
$futureEvents = $this->getEventRepository()->findPublicByWithBatches($queryString, $this->getEventComRep()->setBaseCriteria($templateVars), 'future');
$moreFutureEvents = $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars));
$pastEvents = $this->getEventRepository()->findPublicByWithBatches($queryString, $this->getEventComRep()->setBaseCriteria($templateVars), 'past');
}
if ($request->get('format') === 'json') {
$events = [];
foreach ($futureEvents as $event) {
$events[] = $event->toArray();
}
return Response::create(
json_encode($events),
200,
[
'Content-Type' => 'application/json',
'Access-Control-Allow-Origin' => '*',
]
);
}
$targetPath = $request->server->get('REDIRECT_URL');
if ($this->getUser()) {
if (!$email = $this->getUser()->getEmail()) {
$email = null;
}
} else {
$email = null;
}
$form = $this->setFeedbackForm($targetPath, $this->generateUrl('feedback_translation'), $email);
$eventTypes = $em->getRepository(Tag::class)->findBy(['domain'=>'event', 'isThema'=>1], ['tag'=>'ASC']);
$eventTypesTags = array_map(fn (Tag $entity) => $entity->getTag(), $eventTypes);
$eventTypesLabels = array_map(fn (Tag $entity) => $entity->getLabel(), $eventTypes);
$eventTypesTagLabel = array_combine($eventTypesTags, $eventTypesLabels);
// Fetch popular tags
$popularTags = $em->getRepository(Tag::class)->findBy(['domain' => 'event', 'isModul' => 1], ['tag' => 'ASC']);
// Convert to key-value array
$popularTagsArray = array_map(function (Tag $entity) {
return [
'tag' => $entity->getTag(), // Dies entspricht dem 'slug' in deiner URL-Struktur
'label' => $entity->getLabel()
];
}, $popularTags);
$view = '@StartPlatzEventBundle/Default/events.html.twig';
if ($embed){
$view = '@StartPlatzEventBundle/Default/events-embed.html.twig';
}
return $this->render($view, [
'templateVars' => $templateVars,
'lang' => $lang,
'embed' => $embed,
'nextConference' => $this->getEventRepository()->findNextPublicBy("Konferenz"),
'nextWorkshop' => $this->getEventRepository()->findNextPublicBy("Workshop"),
'futureEvents' => $futureEvents,
'pastEvents' => $pastEvents,
'events' => $this->getEventRepository()->findNextEvents(100),
'moreFutureEvents' => $moreFutureEvents,
'additionalEvents' => $additionalEvents,
'eventArten' => $eventTypes,
'eventTypesTagLabel' => $eventTypesTagLabel,
'popularTagsArray' => $popularTagsArray,
'uTagsArray' => $uTagsArray,
'wTagsArray' => $wTagsArray,
'form' => $form->createView(),
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
]);
}
/**
* @Route("/event-series/{seriesTag}", name="event_series")
*/
public function eventSeriesAction($seriesTag, Request $request )
{
$templateVars = $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
$em = $this->getDoctrine()->getManager();
$eventRepo = $em->getRepository(Event::class);
$batchRepo = $em->getRepository(Batch::class);
// Retrieve the UTM parameters from the URL
$utmParameters = [
'utmSource' => $request->query->get('utm_source'),
'utmMedium' => $request->query->get('utm_medium'),
'utmCampaign' => $request->query->get('utm_campaign'),
'utmTerm' => $request->query->get('utm_term'),
'utmContent' => $request->query->get('utm_content'),
];
// Fetch data related to the event series using $seriesName
// For example, using a repository method:
$nextEvent = $eventRepo->findNextEventInSeries($seriesTag, new \DateTime());
// Check if the result is null (no future events)
if ($nextEvent === null) {
// Fetch the most recent past event in the series
$nextEvent = $eventRepo->findMostRecentPastEventInSeries($seriesTag, new \DateTime());
}
// Initialize variables with default values
$noEventsAvailable = false; // Default to false, indicating events are available
$alternativeContent = ''; // Default empty string or a default alternative message
$nextBatch = null;
$futureEventsData = [];
$pastEventsData = [];
// Proceed only if there is an event (either upcoming or past)
if ($nextEvent !== null) {
// Fetch all future events in the series, excluding the $nextEvent
$futureEvents = $eventRepo->findAllFutureEventsExcluding($seriesTag, $nextEvent->getId());
// Fetch all past events in the series, excluding the $nextEvent
$pastEvents = $eventRepo->findAllPastEventsExcluding($seriesTag, $nextEvent->getId());
// If the main event is multi-batch, get the next batch
if ($nextEvent->isMultiBatchEvent()) {
$nextBatch = $batchRepo->findNextOpenBatchByEvent($nextEvent);
}
// Get batch data for future and past events
$futureBatches = $eventRepo->findAllFutureBatchesInSeries($seriesTag, $nextEvent->getId());
$pastBatches = $eventRepo->findAllPastBatchesInSeries($seriesTag, $nextEvent->getId());
// Merge events and batches data for display
$futureEventsData = $this->mergeEventsAndBatches($futureEvents, $futureBatches);
$pastEventsData = $this->mergeEventsAndBatches($pastEvents, $pastBatches);
} else {
// No events are available, display alternative content or a message
$noEventsAvailable = true;
$alternativeContent = "Sorry, there are no events available for this series.";
$nextEvent = new Event();
$futureEvents = [];
$pastEvents = [];
// $alternativeContent can also be fetched from a repository if needed
}
return $this->render('@StartPlatzEventBundle/Default/event-series.html.twig', [
'event' => $nextEvent,
'nextEvent' => $nextEvent, // The next or most recent event
'nextBatch' => $nextBatch, // The next batch for multi-batch events
'futureEvents' => $futureEvents, // Collection of future events, excluding $nextEvent
'pastEvents' => $pastEvents, // Collection of past events, excluding $nextEvent
'futureEventsData' => $futureEventsData, // Combined events and batches data for future
'pastEventsData' => $pastEventsData, // Combined events and batches data for past
'noEventsAvailable' => $noEventsAvailable, // Flag for no events availability
'alternativeContent' => $alternativeContent, // Alternative content when no events are available
'utmParameters' => $utmParameters,
'showTeaser' => $nextEvent->getTeaser() > '',
'batch' => $nextBatch ?: new Batch(),
'application' => new Application(),
'batchIsOpen' => $nextBatch ? true : false,
'seriesEvents' => $futureEvents,
'promotionUrl' => "/event-series/{$seriesTag}",
'templateVars' => $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request),
'speakers' => $this->getSpeakers($nextEvent),
'lang' => $lang ?? "DE", // Default to "DE" if $lang is not set
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases( $request->server->get('REQUEST_URI') ),
]);
}
/**
* Merge events and batches data for display in series pages
* Combines single-batch events with multi-batch event batches
*
* @param array $events Array of Event entities
* @param array $batches Array of batch data from raw SQL query
* @return array Combined and sorted data
*/
private function mergeEventsAndBatches(array $events, array $batches): array
{
$merged = [];
// Add single-batch events
foreach ($events as $event) {
if (!$event->isMultiBatchEvent()) {
$merged[] = [
'type' => 'event',
'event' => $event,
'date' => $event->getStartDate(),
'endDate' => $event->getEndDate(),
'title' => $event->getTitle(),
'location' => $event->getLocation(),
'slug' => $event->getSlug(),
'trainer' => $event->getTrainer()
];
}
}
// Add batches (which are from multi-batch events)
foreach ($batches as $batch) {
$merged[] = [
'type' => 'batch',
'batch_id' => $batch['batch_id'],
'event_id' => $batch['event_id'],
'date' => new \DateTime($batch['batch_date']),
'title' => $batch['event_title'],
'batch_name' => $batch['batch_name'],
'location' => $batch['location'],
'event_slug' => $batch['event_slug'],
'batch_slug' => $batch['batch_slug'],
'trainer' => $batch['trainer']
];
}
// Sort by date
usort($merged, function($a, $b) {
return $a['date'] <=> $b['date'];
});
return $merged;
}
private function setFeedbackForm ( $targetPath, $actionPath, $email = NULL ) {
return $this->formFactory->createNamedBuilder( 'feedback' )
->setAction( $actionPath )
->add('targetPath', HiddenType::class, array( 'data' => $targetPath, ) )
->add('email', TextType::class, array( 'data' => $email, 'label' => '-', 'required' => false))
->add('note', TextareaType::class, array( 'label' => '-', 'required' => true))
->add('save', SubmitType::class,array('label'=>'Save'))
->getForm();
}
/**
* @Route("/events/list/month/{year}/{month}", name="events_list_month")
*/
public function listMonthAction($year, $month, Request $request)
{
$templateVars = $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
if (!isset($templateVars['tag'])) {
$templateVars['tag'] = '';
}
$queryString = '';
if (isset($templateVars['tag']) and $tag = $templateVars['tag']) {
$events = $this->getEventBaseRepository()->getEventsByMonth($this->getEventComRep()->setBaseCriteria($templateVars), $year, $month);
} else {
$events = $this->getEventBaseRepository()->getEventsByMonth($this->getEventComRep()->setBaseCriteria($templateVars), $year, $month);
}
$targetPath = $request->server->get('REDIRECT_URL');
$lang = $this->menuTranslationService->getLang($targetPath);
$menuLinksDe2En = $this->menuTranslationService->getMenuLinksSwitcher();
return $this->render('@StartPlatzEventBundle/Default/list_month.html.twig', [
'templateVars' => $templateVars,
'events' => $events,
'month' => $month,
'year' => $year,
'targetPath' => $targetPath,
'moreFutureEvents' => $this->getEventBaseRepository()->countFutureEvents($this->getEventComRep()->setBaseCriteria($templateVars)),
'menuLinksDe2En' => $menuLinksDe2En,
'menuLinksEn2De' => array_flip($menuLinksDe2En) ,
'menuPhrases' => $this->menuTranslationService->getMenuPhrases($lang),
'menuLinks' => $this->menuTranslationService->getMenuLinks($lang),
'footerPhrases' => $this->menuTranslationService->getFooterPhrases($lang),
'footerLinks' => $this->menuTranslationService->getFooterLinks($lang),
'lang' => $lang,
]);
}
/** redirecting routes */
/**
* @Route("/event/corporate-services-event/")
*/
public function eventRedirectAction()
{
return $this->redirect($this->generateUrl('event_show_single', ['slug' => 'best-practice-digitalisierung']));
}
private function findEventBySlug($slug)
{
$eventRepository = $this->getEventRepository();
$event = $eventRepository->findOneBy(['slug' => $slug]);
if (!$event) {
$querySlug = '/' . $slug;
$event = $eventRepository->findOneBy(['slug' => $querySlug]);
if (!$event) {
return false;
} else {
$em = $this->getDoctrine()->getManager();
$event->setSlug($slug);
$em->persist($event);
$em->flush();
}
}
return $event;
}
private function findEventBySlugError($slug)
{
$eventRepository = $this->getEventRepository();
$event = $eventRepository->findOneBy(['slug' => $slug]);
if (!$event) {
$querySlug = '/' . $slug;
$event = $eventRepository->findOneBy(['slug' => $querySlug]);
if (!$event) {
$this->session->getFlashBag()->add('notice', 'ERROR Veranstaltung nicht gefunden');
return $this->redirectToRoute('events_list');
} else {
$em = $this->getDoctrine()->getManager();
$event->setSlug($slug);
$em->persist($event);
$em->flush();
}
}
return $event;
}
private function handleEventStatus(Event $event, Request $request)
{
// SEO-optimized handling: Archive events are displayed normally (no redirect)
// Template will show archive banner and hide registration forms
// This preserves 100% SEO juice and backlinks
if ($event->getStatus() != 'publish' and $event->getStatus() != 'archive' and !$request->get('preview')) {
// Draft and trash events require authentication
if (!$this->getUser()) {
$this->addFlash('notice', 'ERROR Veranstaltung ist nicht verfügbar');
return $this->redirectToRoute('events_list');
}
}
return null;
}
/**
* @Route("/events/repair/", name="events_repair_format")
*/
public function repairFormatAction()
{
// Format-Umwandlung - deprecated - we need to check by mysql research
$em = $this->getDoctrine()->getManager();
$events = $em->getRepository(Event::class)->repairContent();
$responseContent = '<html><head><title>Repair Progress</title></head><body><ul>';
foreach ($events as $event) {
$responseContent .= "<li>{$event}</li>";
}
$responseContent .= '</ul></body></html>';
return new Response($responseContent);
}
### section signup starts here ###
/**
* @Route("/ai-summer-school-single-ticket", name="ai-summer-school-single-ticket")
*/
public function aisummerschoolSingleTicketAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-single-ticket.html.twig', [
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
]);
}
/**
* @Route("/ai-summer-school-firmen-pakete", name="ai-summer-school-firmen-pakete")
*/
public function aisummerschoolFirmenPaketeAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-firmen-pakete.html.twig', [
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
]);
}
/**
* @Route("/ai-summer-school-stipendien", name="ai-summer-school-stipendien")
*/
public function aisummerschoolStipendienAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-stipendien.html.twig', [
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
]);
}
/**
* @Route("/ai-summer-school-volunteers", name="ai-summer-school-volunteers")
*/
public function aisummerschoolVolunteersAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-volunteers.html.twig', [
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
]);
}
/**
* @Route("/ai-summer-school-sponsoring", name="ai-summer-school-sponsoring")
*/
public function aisummerschoolSponsoringAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
return $this->render('@StartPlatzEventBundle/Default/ai-summer-school-sponsoring.html.twig', [
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
]);
}
/**
* @Route("/signup/{slug}", name="signup_show_single")
*/
public function signupSingleAction(Request $request, $slug)
{
$em = $this->getDoctrine()->getManager();
/** @var Program $program */
if (!$program = $em->getRepository(Program::class /*Startup Program*/)->findOneBy(['slug' => 'sign-up'])) {
$this->session->getFlashBag()->add('notice', 'ERROR Sorry, no program found');
return $this->redirect($this->generateUrl('apply_home', []));
}
/** @var Batch $batch */
if (!$batch = $em->getRepository(Batch::class)->findOneBy(['slug' => $slug, 'program' => $program])) {
$this->session->getFlashBag()->add('notice', 'ERROR Sorry, no batch found');
return $this->redirect($this->generateUrl('apply_home', []));
}
// Retrieve the UTM parameters from the URL
$utmParameters = [
'utmSource' => $request->query->get('utm_source'),
'utmMedium' => $request->query->get('utm_medium'),
'utmCampaign' => $request->query->get('utm_campaign'),
'utmTerm' => $request->query->get('utm_term'),
'utmContent' => $request->query->get('utm_content'),
];
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
$batchIsOpen = $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
$form = null;
$program = null;
$member = null;
$application = null;
$product = null;
$editFeedback = "";
$settings = [];
$action = "";
$customersArr = [];
if ($batchIsOpen) {
$program = $batch->getProgram();
$settings = $this->getProgramSettings($program, $batch);
$batch = $em->getRepository(Batch::class)->updateNumbers($batch);
/** @var User $user */
if ($user = $this->getUser()) {
$member = $em->getRepository(Member::class)->find($user->getMemberId());
}
$action = $request->get('action');
$application = $this->getOrCreateApplication($request, $batch, $program, $member, $lang, $settings, $utmParameters, null);
if ($batch->getPriceInEuroCent() > 0) {
$route = "signup_show_single";
$routeParameters["slug"] = $batch->getSlug();
$application = $this->handleAction($request, $member, $application, $batch, $lang, $route, $routeParameters, $settings);
if (is_string($application)) {
// Handle redirect
return $this->redirect($application);
}
}
if ($user && in_array($action, ["needsloginvalidation", "needsemailverification"])) {
$response = $this->validateEventApplication($application, $member, $settings, $batch, $lang);
$application = $response['data']['application'];
}
if ($batch->getPriceInEuroCent() > 0 && $batch->getProductId()) {
if (!$product = $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
$this->session->getFlashBag()->add('notice', 'ERROR configuration not found');
return $this->redirect($this->generateUrl('events_list'));
}
if ($application->getId() && isset($member) && $member->getId()) {
$customersArr = $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($member, $product->getAccount(), false);
}
}
$form = $this->createForm(ApplicationType::class, $application, ['settings' => $settings, 'program' => $program, 'batch' => $batch]);
$form->handleRequest($request);
##############################################################
## form validation starts here ##
##############################################################
if ($form->isSubmitted() && $form->isValid()) {
$application = $em->getRepository(Application::class)->cleanApplicationData($application);
$adminEmail = $application->getEmail();
if ($member) {
$memberToBeRegistered = $member;
$isExistingMember = true;
} else {
$response = $this->processRegistrationCheckMembershipExistence($application);
$isExistingMember = $response['data']['isExistingMember'];
$memberToBeRegistered = $response['data']['memberToBeRegistered'];
}
$memberToBeRegistered->setIsExistingMember($isExistingMember);
// fill application with memberId and teamId and discount information
$application = $this->updateApplicationWithMemberIdAndTeamId($application, $memberToBeRegistered, $batch);
if (!$batch->isMultipleApplicationsPerMember()) {
// check if there is already an application to this event == batch with that batchId and memberId
if ($existingApplication = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
// there are two applications - the latest application will always win
$fields = Utility::getEntityFieldsArray($application, ['id','assessments', 'votesRegistered', 'applicationStatus', 'editStatus', 'editMessage', 'lastChangeUser','history','extraFields','lastModified','createdAt']);
$storedData = Utility::fillDataByObject($existingApplication, $fields);
$updatedData = Utility::fillDataByObject($application, $fields);
$differences = array_diff_assoc($updatedData, $storedData);
$application = $existingApplication;
$application = $this->updateApplication($application, $differences);
$application->setIsExistingApplication(true);
}
}
if (isset($settings['extraFields'])) {
$application = $this->getExtraFieldsData($application, $settings, $form);
}
$dummyEvent = new Event();
$isPaidEvent = $this->checkPaidEvent($application, $batch, $dummyEvent);
$hasToPay = $this->checkPaymentException($application, $memberToBeRegistered, $dummyEvent, $settings, $batch);
if ($isPaidEvent && $hasToPay) {
// falls es bereits einen zugänglichen Monsum Account für diese Person gibt
$customersArr = $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered, $product->getAccount(), false);
$dummyEvent = new Event();
$application = $this->updateApplicationWithStepOneInformation($application, $memberToBeRegistered, $dummyEvent);
// Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId übergeben werden
$application = $em->getRepository(Application::class)->setApplication($application);
} else {
$route = 'signup_show_single';
$routeParameters = $request->get('_route_params');
$response = $this->processRegistrationWithoutPaymentSimpleOptimized($isExistingMember, $user, $memberToBeRegistered, $application, $settings, $lang, $adminEmail, $batch, $route, $routeParameters);
// Bei SignUps: Prüfe ob goToPage gesetzt ist
if ($batch->getGoToPage()) {
// Weiterleitung zu goToPage (kann URL oder interner Pfad sein)
return $this->redirect($batch->getGoToPage());
}
// Ansonsten auf SignUp-Seite bleiben - keine Weiterleitung
$application = $response['data']['application'];
$member = $response['data']['member'];
}
}
}
// Default to legacy template for backward compatibility
$view = '@StartPlatzEventBundle/Default/signup-single.html.twig';
// Check for pageTemplate field (new approach)
if ($batch->getPageTemplate()) {
switch ($batch->getPageTemplate()) {
case 'modern':
case 'flexible':
$view = '@StartPlatzEventBundle/Default/signup-single.modern.html.twig';
break;
case 'petition':
$view = '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
break;
case 'legacy':
default:
$view = '@StartPlatzEventBundle/Default/signup-single.html.twig';
break;
}
}
// Keep backward compatibility for existing petition SignUps using JSON settings
elseif (array_key_exists('signupTemplate', $settings)) {
$view = '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
}
// Build the metaData array using only the $batch object (landing page fields)
$metaData = [
// SEO Tag: Description (using landingPageDescription)
'description' => $batch->getLandingPageDescription(),
// Canonical URL (the domain remains lowercase)
'canonical' => 'https://www.startplatz.de/signup/' . urlencode($batch->getSlug()),
// Open Graph Tags for Social Sharing (using landingPage fields)
'ogTitle' => $batch->getLandingPageTitle(),
'ogDescription' => $batch->getLandingPageDescription(),
'ogImage' => $batch->getLandingPageSocialImage(),
'ogUrl' => 'https://www.startplatz.de/signup/' . urlencode($batch->getSlug()),
'ogType' => 'website',
'ogSiteName' => 'STARTPLATZ', // Protected trademark must be uppercase
// Twitter Card Tags (using landingPage fields)
'twitterCard' => 'summary', // or "summary_large_image" if preferred
'twitterTitle' => $batch->getLandingPageTitle(),
'twitterDescription'=> $batch->getLandingPageDescription(),
'twitterImage' => $batch->getLandingPageSocialImage(),
'twitterSite' => '@STARTPLATZ', // Protected trademark must be uppercase
// Title for the page (taken from landingPageTitle)
'title' => $batch->getLandingPageTitle(),
// Additional Landing Page Fields
'landingPageCss' => $batch->getLandingPageCss(),
'landingPageContent' => $batch->getLandingPageContent(),
'landingPageJs' => $batch->getLandingPageJs(),
'landingPageFooterTemplate' => $batch->getLandingPageFooterTemplate(),
];
return $this->render($view, [
'batch' => $batch,
'program' => $program,
'settings' => $settings,
'metaData' => $metaData,
'form' => $form ? $form->createView() : $form,
'application' => $application,
'action' => $action,
'batchIsOpen' => $batchIsOpen,
'customersArr' => $customersArr,
'member' => $member,
'isMultiBatchEvent' => false, // Signups have no event, so no multi-batch
'utmParameters' => $utmParameters,
'phrases' => $em->getRepository(Option::class)->getApplicationPhrases($program, $batch, false, $lang),
'editFeedback' => $editFeedback,
'templateVars' => [],
'preview' => $request->get('preview'),
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
'targetRoute' => "signup_show_single",
'targetRouteSlug' => $batch->getSlug(),
'targetPath' => $targetPath,
]);
}
### section signup ends here ###
private function getProgramSettings($program, $batch)
{
$em = $this->getDoctrine()->getManager();
$settings = $em->getRepository(Application::class)->getSettingsProgram($program, $batch); return $settings;
}
private function getOrCreateApplication(Request $request, $batch, $program, $member, $lang, $settings, $utmParameters, $event = null)
{
$em = $this->getDoctrine()->getManager();
$application = null;
// process:stripeStarted
// Check if the application ID is provided
if ($applicationId = $request->get('app')) {
$application = $em->getRepository(Application::class)->find($applicationId);
}
// Check if an application exists if there is a member
if ($member) {
$application = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
}
// check if stripe was not finished and therefore we need to delete that application
$action = $request->get('action');
if ($application && $application->getEditStatus() == 'process:stripeStarted' && $action != 'checkSuccess'){
$responseText = $em->getRepository(Application::class)->deleteApplication($application->getId());
$application = null;
$applicationId = null;
}
// If no application is found, create a new one
if (!$application) {
$priceAndDiscount = $em->getRepository(Member::class)->getPriceAndDiscountByBatch($member, $batch);
// Pass the UTM parameters along with other necessary arguments to the createApplicationByApplyForm method
$application = $em->getRepository(Application::class)->createApplicationByApplyForm(
$program,
$batch,
$lang,
$priceAndDiscount,
$utmParameters // This is the array you created earlier
);
if ($member) {
$application = $em->getRepository(Application::class)->updateApplicationByMember($application, $member);
}
} else {
// Update lang on existing application if it differs
// This ensures language preference is maintained throughout the user journey
if ($lang && $application->getLang() !== $lang) {
$application->setLang($lang);
$em->persist($application);
$em->flush();
}
}
// Fix for single events with event-level pricing:
// If batch has no price but event has price, use event price
if ($batch && $event && !$batch->getPriceInEuroCent() && $event->getPriceInEuroCent() > 0) {
$application->setPriceInEuroCent($event->getPriceInEuroCent());
$discountPercent = $application->getDiscountPercent() ?: 0;
$realPrice = $event->getPriceInEuroCent() - ($event->getPriceInEuroCent() * ($discountPercent / 100));
$application->setRealPriceInEuroCent($realPrice);
}
if ($member and $application->getApplicationStatus() == 'started') {
$priceAndDiscount = $em->getRepository(Member::class)->getPriceAndDiscountByBatch($member, $batch);
if ($application->getRealPriceinEuroCent() != $priceAndDiscount["realPriceinEuroCent"]) {
$application = $em->getRepository(Application::class)->setDiscountAndRealPrice($application, $priceAndDiscount);
if ($application->getRealPriceinEuroCent() == 0) {
$application = $this->setEventApplicationDone($application, $member, $batch, $lang, $settings);
}
}
}
// Gib das Application-Objekt zurück
return $application;
}
/**
* @Route("/allmeda/event/application/send-confirmation-mail/{applicationId}", name="allmeda_event_send-confirmation-mail")
* @Security("is_granted('ROLE_ADMIN')")
*/
public function sendConfirmationMailViaPublicFunction($applicationId)
{
$em = $this->getDoctrine()->getManager();
$application = $em->getRepository(Application::class)->find($applicationId);
$member = $em->getRepository(Member::class)->find($application->getMemberId());
$batch = $em->getRepository(Batch::class)->find($application->getBatchId());
$feedback = $this->sendConfirmationMailToApplicant(null, $member, [], $application, $batch);
$response = "SUCCESS Confirmation Mail has been sent";
return new Response($response);
}
private function setEventApplicationDone($application, $member, $batch, $lang, $settings)
{
$em = $this->getDoctrine()->getManager();
$application = $em->getRepository(Application::class)->setApplicationDone($application);
$phrases = $em->getRepository(Option::class)->getApplicationPhrases($batch->getProgram(), $batch, false, $lang);
switch ($lang) {
case "DE":
$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!";
break;
case "EN":
$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!";
break;
}
$this->session->getFlashBag()->add('notice', "SUCCESS {$message}");
$application = $em->getRepository(Application::class)->setApplication($application, $member);
$this->sendConfirmationMailToApplicant(null, $member, $settings, $application, $batch);
return $application;
}
private function updateApplicationWithMemberIdAndTeamId(Application $application, Member $memberToBeRegistered, $batch)
{
$em = $this->getDoctrine()->getManager();
if (!$application->getMemberId()) {
$application->setMemberId($memberToBeRegistered->getId());
if ($application->getRealPriceinEuroCent() > 0) {
$priceAndDiscount = $em->getRepository(Member::class)->getPriceAndDiscountByBatch($memberToBeRegistered, $batch);
$application = $em->getRepository(Application::class)->setDiscountAndRealPrice($application, $priceAndDiscount);
}
}
if (!$application->getTeamId()) {
$application->setTeamId($memberToBeRegistered->getTeamId());
$application->setStartupName($memberToBeRegistered->getTeamName());
}
if (!$application->getLinkedin() && $memberToBeRegistered->getLinkedin()) {
$application->setLinkedin($memberToBeRegistered->getLinkedin());
}
if (!$application->getPhone() && $memberToBeRegistered->getPhone()) {
$application->setPhone($memberToBeRegistered->getPhone());
}
if (!$memberToBeRegistered->getIsExistingMember()){
$application->setIsNewMember(true);
} else {
$application->setIsNewMember(false);
}
// Transfer UTM parameters if not already set in the Member object and if they exist in the Application object
$memberToBeRegistered = $em->getRepository(Member::class)->updateMemberWithUtmParametersFromApplication($memberToBeRegistered, $application);
return $application;
}
/**
* handleAction - Handles various actions for the application
*
* This function is called in the following contexts:
* 1. During Signup (Line 549)
* - With route "signup_show_single"
* - Parameters include batch slug, language, etc.
*
* 2. During Event Handling (Line 968)
* - With route "event_show_single"
* - Parameters include event slug, language, etc.
*/
private function handleAction(Request $request, $member,Application $application, Batch $batch, $lang, $route, $routeParameters, $settings)
{
$action = $request->get("action");
$em = $this->getDoctrine()->getManager();
switch ($action) {
case "checkSuccess":
$provider = $request->get('provider');
if ($provider == 'stripe'){
$application->setEditStatus("success:stripePaid");
}
$application->setApplicationStatus('applied');
$application->setHasPaid(true);
$application = $em->getRepository(Application::class)->setApplication($application);
switch ($lang) {
case "DE":
$message = "Danke für Deine Anmeldung.<br>Wir freuen uns auf Dich!";
break;
case "EN":
$message = "Thanks, Your application is now approved.<br>We are looking forward to having you!";
break;
}
if ($productNumber = $request->get('productnumber') or $provider == 'stripe'){
## productNumber is only given in case of sign up
switch ($lang) {
case "DE":
$message = "Danke für Deine Buchung.<br>Wir freuen uns auf Dich!";
break;
case "EN":
$message = "Thanks, Your booking was successful.<br>We are looking forward to having you!";
break;
}
$account = $request->get('account');
$customerHash = $request->get('customerId');
$subscriptionHash = $request->get('subscriptionId');
if (!$member) {
$member = $em->getRepository(Member::class)->find($application->getMemberId());
}
if (array_key_exists("setAiHubMembership", $settings)) {
// Hole den Status aus den Einstellungen
$aiMembershipStatus = $settings["setAiHubMembership"];
// Setze das Start- und Enddatum basierend auf dem aktuellen Datum und dem Status
$aiMembershipStartDate = new DateTime(); // Setze auf aktuelles Datum und Uhrzeit
$aiMembershipEndDate = clone $aiMembershipStartDate;
if ($aiMembershipStatus === 'trial') {
$aiMembershipEndDate->modify('+1 month');
} elseif ($aiMembershipStatus === 'active') {
$aiMembershipEndDate->modify('+3 months');
}
// Setze den Grund für die Mitgliedschaft
$reason = "Buy of {$productNumber}";
// Erstelle oder aktualisiere das Service-Objekt mit den neuen Daten
// (Du müsstest diese Logik entsprechend deiner Anwendung implementieren)
$service = new Service(); // oder hole ein existierendes Service-Objekt
$service->setStatus($aiMembershipStatus);
$service->setStartDate($aiMembershipStartDate);
$service->setEndDate($aiMembershipEndDate);
// Rufe die setAiHubMembership Funktion auf
$successContent[] = $em->getRepository(Member::class)->setAiHubMembership($service, $member, $reason);
}
$loginLink = null;
$targetPath = $this->generateUrl('x_membership_first-steps', ['productNumber' => $productNumber, 'account' => $account]);
$this->session->getFlashBag()->add('notice', $message);
if (!$user = $this->getUser()) {
$loginLink = $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
$user = $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
}
$feedback = $this->sendConfirmationMailToApplicant($user, null, $settings, $application, $batch);
if ($provider == 'stripe'){
$mailContent = [
'applicationId' => $application->getId(),
'memberId' => $application->getMemberId(),
'editStatus' => $application->getEditStatus(),
'applicationStatus' => $application->getApplicationStatus(),
'person' => $application->getPerson(),
'email' => $application->getEmail(),
'utmSource' => $application->getUtmSource(),
'batchSlug' => $application->getBatchSlug(),
];
$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);
return $application;
} else {
return isset($loginLink) ? $loginLink : $targetPath;
}
} else { // if productNumber is not defined
if (!$user = $this->getUser()) {
$user = $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
}
$feedback = $this->sendConfirmationMailToApplicant($user, null, $settings, $application, $batch);
}
break;
case "sendMagicLinkForPayment":
// create LoginLink and send email
$customerId = $request->get('customerId');
$feedbackUrl = $this->sendMagicLinkForPayment($application, $batch, $customerId, $route, $routeParameters);
return $feedbackUrl;
case "createConsumerMonsumAccount":
case "useExistingCustomerAccount":
$customerId = $request->get('customerId');
$checkoutUrl = $this->prepareMonsumCheckoutNewMonsumAccount($application, $batch, $action, $route, $routeParameters, $customerId);
return $checkoutUrl;
}
// Return the modified $application object to the main controller
return $application;
}
private function validateEventApplication(Application $application, Member $member, $settings, $batch, string $lang): array
{
$em = $this->getDoctrine()->getManager();
$message = "";
$status = "";
// Check if a confirmation email needs to be sent
$shouldSendConfirmationMail = false;
$feedback = null;
switch ($application->getEditStatus()) {
case "error:validateLogin":
$application->setEditStatus('success:validatedLogin');
$application->setEditMessage("Application has been validated by {$member->getEmail()}");
$application = $em->getRepository(Application::class)->setHistory($application, $application->getEditMessage());
$application = $em->getRepository(Application::class)->setApplication($application, $member);
$status = "success";
$shouldSendConfirmationMail = true;
break;
case "error:validateEmail":
$application->setEditStatus('success:validatedEmail');
$application->setEditMessage("Email of this Application has been validated by {$member->getEmail()}");
$application = $em->getRepository(Application::class)->setHistory($application, $application->getEditMessage());
if ($application->getApplicationStatus() == 'started') {
$application->setApplicationStatus('applied');
}
$application = $em->getRepository(Application::class)->setApplication($application, $member);
$status = "success";
$shouldSendConfirmationMail = true;
break;
}
if ($shouldSendConfirmationMail) {
// Logic to send confirmation mail
$feedback = $this->sendConfirmationMailToApplicant(null, $member, $settings, $application, $batch);
}
if ($status == "success") {
$application = $em->getRepository(Application::class)->setApplicationApplied($application, $member);
switch ($lang) {
case "DE":
$message = "SUCCESS Danke, Deine Anmeldung ist jetzt bestätigt. Wir freuen uns auf Dich!";
if ($feedback){
$message .= "<br>Wir haben Dir eine Bestätigungsmail geschickt. Bitte prüfe Deinen Posteingang.";
}
break;
case "EN":
$message = "SUCCESS Thanks, Your application is now approved. We are looking forward to having you!";
if ($feedback){
$message .= "<br>We have sent you a confirmation mail. Please check your inbox!";
}
break;
}
$this->session->getFlashBag()->add('notice', $message);
}
return [
'data' => [
'application' => $application,
],
'message' => $message,
'status' => $status,
];
}
private function checkPaidEvent(Application $application, $batch = false, $event = false): bool
{
// First check: Application has a price (Monsum payment)
if ($application->getRealPriceInEuroCent() !== null && $application->getRealPriceInEuroCent() > 0) {
return true;
}
// Always prioritize batch payment settings
if ($batch) {
// Check if batch has a price or Stripe price ID
if ($batch->getPriceInEuroCent() > 0 || $batch->getStripePriceId() > '') {
return true;
}
}
// Legacy fallback: Check event payment settings for backward compatibility
// This can be removed after all single-batch events are migrated
if ($event) {
if ($event->getStripePriceId() > '' || $event->getPriceInEuroCent() > 0) {
return true;
}
}
return false;
}
private function checkPaymentException(Application $application, $member = false, $event = false, $settings = false, $batch = false): bool
{
if ($member) {
// Check batch-specific KI Campus setting first for multi-batch events
$isFreeForKiCampus = false;
if ($batch && $batch->getIsFreeForKiCampus() !== null) {
// Use batch-specific setting if available
$isFreeForKiCampus = $batch->getIsFreeForKiCampus();
} elseif ($event) {
// Fall back to event setting
$isFreeForKiCampus = $event->getIsFreeForKiCampus();
}
$this->logger->info('checkPaymentException: KI Campus check', [
'member_id' => $member->getId(),
'member_email' => $member->getEmail(),
'member_tags' => $member->getTags(),
'has_ki_campus_tag' => str_contains($member->getTags() ?? '', "#ki-campus"),
'isFreeForKiCampus' => $isFreeForKiCampus,
'batch_setting' => $batch ? $batch->getIsFreeForKiCampus() : 'no batch',
'event_setting' => $event ? $event->getIsFreeForKiCampus() : 'no event',
]);
if ($isFreeForKiCampus) {
if (str_contains($member->getTags() ?? '', "#ki-campus")) {
$this->session->getFlashBag()->add('notice', "SUCCESS Du bist Mitglied im KI Campus und kannst kostenlos teilnehmen");
$this->logger->info('Payment exception granted: KI Campus member');
return false; // no payment necessary for ki campus members
}
}
// Check batch-specific Community setting first for multi-batch events
$isFreeForCommunity = false;
if ($batch && $batch->getIsFreeForCommunity() !== null) {
// Use batch-specific setting if available
$isFreeForCommunity = $batch->getIsFreeForCommunity();
} elseif ($event) {
// Fall back to event setting
$isFreeForCommunity = $event->getIsFreeForCommunity();
}
$this->logger->info('checkPaymentException: Community check', [
'member_id' => $member->getId(),
'member_email' => $member->getEmail(),
'member_tags' => $member->getTags(),
'isFreeForCommunity' => $isFreeForCommunity,
'batch_setting' => $batch ? $batch->getIsFreeForCommunity() : 'no batch',
'event_setting' => $event ? $event->getIsFreeForCommunity() : 'no event',
]);
if ($isFreeForCommunity) {
$communityTags = ['#physical-member', '#community-member', '#can-get-transponder'];
$memberTags = $member->getTags() ?? '';
foreach ($communityTags as $tag) {
if (str_contains($memberTags, $tag)) {
$this->session->getFlashBag()->add('notice',
"SUCCESS: Du bist Community-Mitglied und kannst kostenlos teilnehmen");
$this->logger->info('Payment exception granted: Community member', [
'member_id' => $member->getId(),
'matched_tag' => $tag
]);
return false; // no payment necessary for community members
}
}
}
if ($event && isset($settings['freeIfTagMatches'])) {
if (str_contains($member->getTags() ?? '', $settings['freeIfTagMatches'])) {
$tagName = str_replace('#', '', $settings['freeIfTagMatches']); // Entferne '#' falls vorhanden
$this->session->getFlashBag()->add('notice', "SUCCESS Du bist Mitglied und in unserer Datenbank mit dem Tag {$tagName} vermerkt und kannst daher kostenlos teilnehmen");
return false; // no payment necessary for members with matching tag
}
}
if ($application->getHasVolunteerAgreed()) {
$this->session->getFlashBag()->add('notice', "SUCCESS Du bist als Volunteer angemeldet und kannst kostenlos teilnehmen");
return false; // no payment necessary for volunteers
}
if ($application->getDiscountPercent() == 100) {
$this->session->getFlashBag()->add('notice', "SUCCESS Du hast einen 100% Discount Code und kannst kostenlos teilnehmen");
return false; // no payment necessary with 100% discount
}
}
$this->logger->info('checkPaymentException: Payment required', [
'member_exists' => $member ? true : false,
'reason' => 'No payment exception criteria met'
]);
return true; // payment necessary for all other cases
}
/**
* @Route("/event-confirmation/{slug}/", name="event_confirmation")
*/
public function eventConfirmationAction(Request $request, $slug)
{
$em = $this->getDoctrine()->getManager();
$event = $this->findEventBySlug($slug);
if (!$event) {
$this->addFlash('notice', 'ERROR Event not found');
return $this->redirectToRoute('events_list');
}
$seriesEvents = [];
if ($seriesTag = $event->getSeriesTag()) {
$seriesEvents = $this->getEventRepository()->findBySeriesTag($seriesTag, '', ['startDate' => 'DESC']);
}
$embed = $request->get('embed');
$batch = $em->getRepository(Batch::class)->findOneBy(['programId'=>27, 'eventId'=>$event->getId(), 'status'=>'active', 'access' =>'anonymous']);
$program = $batch->getProgram();
$action = $request->get('action');
$applicationId = $request->get('app');
$route = "event_confirmation";
$routeParameters["slug"] = $event->getSlug();
$settings = [];
$member = false;
$application = new Application();
// Load application first, then determine language
if ($applicationId){
if (!$application = $em->getRepository(Application::class)->find($applicationId)){
$this->addFlash('notice', 'ERROR Application with ID '.$applicationId.' not found');
return $this->redirectToRoute('events_list');
}
}
// Language detection: Priority is Application > Event > Default DE
// This ensures user's language choice persists through Stripe redirect
$lang = ($application && $application->getId() && $application->getLang())
? $application->getLang()
: ($event->getLang() ?? 'DE');
// Handle action after language is determined
if ($applicationId && $action){
$application = $this->handleAction($request, $member, $application, $batch, $lang, $route, $routeParameters, $settings);
if (is_string($application)) {
// Handle redirect
return $this->redirect($application);
}
}
// Generate the absolute base URL based on the route and parameters
$baseURL = $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
// UTM parameters for the promotion URL
$promotionUtmParameters = [
'utm_source' => 'affiliate',
'utm_medium' => is_numeric($application->getMemberId()) ? $application->getMemberId() : '',
'utm_campaign' => 'event:' . $event->getId(),
'utm_term' => '',
'utm_content' => '',
];
$promotionUrl = $em->getRepository(Batch::class)->generatePromotionUrl($baseURL, $promotionUtmParameters);
$view = '@StartPlatzEventBundle/Default/event-confirmation.html.twig';
if ($embed){
$view = '@StartPlatzEventBundle/Default/event-confirmation.embed.html.twig';
}
if ($event->getTwigFile() == 'winter-2025' ){
$view = '@StartPlatzEventBundle/Default/event-confirmation.winter-2025.html.twig';
}
if ($event->getTwigFile() == 'style-ecodynamics' ){
$view = '@StartPlatzEventBundle/Default/event-confirmation.style-ecodynamics.html.twig';
}
if ($event->getTwigFile() == 'style-ai-hub' ){
$view = '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
}
if ($event->getTwigFile() == 'style-startplatz' ){
$view = '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
}
return $this->render($view, [
'event' => $event,
'seriesEvents' => $seriesEvents,
'promotionUrl' => $promotionUrl,
'batch' => $batch,
'settings' => $settings,
'application' => $application,
'action' => $action,
'member' => $member,
'speakers' => $this->getSpeakers($event),
'phrases' => $em->getRepository(Option::class)->getApplicationPhrases($program, $batch, false, $lang),
'templateVars' => [],
'embed' => $embed,
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
'isMultiBatchEvent' => $event->isMultiBatchEvent(),
]);
}
/**
* @Route("/event/{slug}/", name="event_show_single")
* @Route("/event/{slug}/{date}", name="event_show_single_by_date", requirements={"date"="\d{4}-\d{2}-\d{2}"})
*/
public function eventSingleAction(Request $request, $slug, $date = null)
{
##event-single
if ($slug == 'ai-bootcamp-koeln-2024-01-18') return $this->redirect( $this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-2024-01-29')) );
if ($slug == 'ai-summer-school-koeln-2208') return $this->redirect( $this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-1109')) );
if ($slug == 'ai-summer-school-duesseldorf-2908') return $this->redirect( $this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-duesseldorf-2609')) );
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')) );
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')) );
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')) );
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')) );
if ($slug == 'ai-barcamp-generative-ki') return $this->redirect( $this->generateUrl('event_show_single', array('slug' => 'ai-barcamp-logistik')) );
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')) );
// Retrieve the UTM parameters from the URL
$utmParameters = [
'utmSource' => $request->query->get('utm_source') ?? 'homepage',
'utmMedium' => $request->query->get('utm_medium'),
'utmCampaign' => $request->query->get('utm_campaign'),
'utmTerm' => $request->query->get('utm_term'),
'utmContent' => $request->query->get('utm_content'),
];
$em = $this->getDoctrine()->getManager();
/** @var Event $event */
$event = $this->findEventBySlug($slug);
if (!$event) {
$this->addFlash('notice', 'ERROR Veranstaltung nicht gefunden');
return $this->redirectToRoute('events_list');
}
// Handle event visibility: redirect if not published or previewed without user session.
$response = $this->handleEventStatus($event, $request);
if ($response) {
return $response;
}
$seriesEvents = [];
if ($seriesTag = $event->getSeriesTag()) {
$seriesEvents = $this->getEventRepository()->findBySeriesTag($seriesTag, '', ['startDate' => 'DESC']);
}
// Language detection with priority: URL parameter > Event default
// This ensures ?lang=en parameter works for bilingual events
$lang = strtoupper($request->query->get('lang', '')) ?: ($event->getLang() ?? 'DE');
$embed = $request->get('embed');
$eventbriteId = $event->getEventbriteId();
$isEventbriteOnly = $event->getTicketing() > '';
$applicationUrl = $event->getApplicationUrl();
// Multi-batch aware logic
$futureBatches = [];
$allBatches = [];
if ($event->isMultiBatchEvent()) {
$allBatches = $em->getRepository(Batch::class)->findAllBatchesByEvent($event);
if ($date) {
// User selected a specific date
$batch = $em->getRepository(Batch::class)->findBatchByEventAndDate($event, $date);
if (!$batch) {
// Invalid date, redirect to main event page
$this->addFlash('notice', 'Der gewählte Termin ist nicht verfügbar.');
return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
}
} else {
// No date selected, show next upcoming batch
$batch = $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
}
// Get all future batches for the selector
$futureBatches = $em->getRepository(Batch::class)->findFutureBatchesByEvent($event, 10);
// Get past batches for archive display
$pastBatches = $em->getRepository(Batch::class)->findPastBatchesByEvent($event, 10);
} else {
// Single batch event - keep existing logic
// Try to find active batch first, then fall back to completed batch for display
$batch = $em->getRepository(Batch::class)->findOneBy(['programId'=>27, 'eventId'=>$event->getId(), 'status'=>'active', 'access' =>'anonymous']);
if (!$batch) {
// No active batch found, look for completed batch (for past events that need to display)
$batch = $em->getRepository(Batch::class)->findOneBy(['programId'=>27, 'eventId'=>$event->getId(), 'status'=>'completed', 'access' =>'anonymous']);
}
if ($batch) {
$allBatches = [$batch];
}
}
$batchIsOpen = $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
$form = null;
$program = null;
$application = null;
$product = null;
$editFeedback = "";
$settings = [];
$action = "";
$customersArr = [];
$member = null;
/** @var User $user */
if ($user = $this->getUser()) {
$member = $em->getRepository(Member::class)->find($user->getMemberId());
}
if ($batch && !$isEventbriteOnly) {
$program = $batch->getProgram();
}
if ($batchIsOpen && !$isEventbriteOnly) {
$settings = $this->getProgramSettings($program, $batch);
$batch = $em->getRepository(Batch::class)->updateNumbers($batch);
$action = $request->get('action');
$referral = $request->get('referral');
// do some exceptions
if ($member && isset($settings['batchType'])) {
if ($settings['batchType'] == 'memberDocumentation'){
// check if member
if ($em->getRepository(Application::class)->findApplicationsByAiFridayBatchSlugAndMemberId($member->getId()))
{
if (!$em->getRepository(Application::class)->findOneBy(['memberId'=>$member->getId(), 'batchId' => $batch->getId()])){
$memberTeam = $em->getRepository(Team::class)->find($member->getTeamId());
$adminEmail = $member->getEmail();
$application = $em->getRepository(Application::class)->createApplicationIfNotExists($program, $batch, $memberTeam, $member, $adminEmail);
$application->setBatchStatus('applicant');
$application->setApplicationStatus('applied');
$em->persist($application);
$em->flush();
$batch = $em->getRepository(Batch::class)->updateNumbers($batch);
}
}
}
}
$application = $this->getOrCreateApplication($request, $batch, $program, $member, $lang, $settings, $utmParameters, $event);
if ($referral && $application->getRealPriceinEuroCent() > 0) {
if ($recommendedByMember = $em->getRepository(Member::class)->find($referral)) {
if ($neededTag = $settings['isRecommendedBy'] ?? '') {
if (Utility::contains($recommendedByMember->getTags(), $neededTag)) {
$application->setRealPriceinEuroCent(0);
$application->setDiscountReason("geworben durch {$recommendedByMember->getName()}");
$application->setDiscountPercent(100);
}
}
}
}
if ($user && in_array($action, ["needsloginvalidation", "needsemailverification"])) {
$response = $this->validateEventApplication($application, $member, $settings, $batch, $lang);
$application = $response['data']['application'];
}
// Only check for Monsum product if we're not using Stripe
$hasStripeConfig = ($batch && $batch->getStripePriceId()) || ($event && $event->getStripePriceId());
if ($batch->getPriceInEuroCent() > 0 && $batch->getProductId() && !$hasStripeConfig) {
if (!$product = $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
$this->session->getFlashBag()->add('notice', 'ERROR configuration not found');
return $this->redirect($this->generateUrl('events_list'));
}
}
$form = $this->createForm(ApplicationType::class, $application, [
'settings' => $settings,
'program' => $program,
'batch' => $batch,
'csrf_protection' => $embed ? false : true,
]);
$form->handleRequest($request);
##############################################################
## form validation starts here ##
##############################################################
if ($form->isSubmitted() && $form->isValid()) {
$application = $em->getRepository(Application::class)->cleanApplicationData($application);
$adminEmail = $application->getEmail();
$email = $application->getEmail();
if (!Utility::validateEmail($email)){
$this->addFlash('notice', 'ERROR Incorrect email adress. Please check your email adress!');
} else {
// maxApplicants
if (isset($settings['maxApplicants'])) {
$applicants = $em->getRepository(Application::class)->findBy(['batchId' => $batch->getId(), 'applicationStatus' => 'applied']);
$currentApplicantsCount = count($applicants);
if ($currentApplicantsCount >= $settings['maxApplicants']) {
$this->addFlash('notice', 'ERROR Leider sind alle verfügbaren Plätze bereits vergeben.');
return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
}
}
// isErrorDetectionOn
if (isset($settings['isErrorDetectionOn']) and $settings['isErrorDetectionOn']) {
$this->addFlash('notice', 'ERROR There was a problem in processing your application. Please contact support@startplatz.de');
return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
}
if ($member) {
$memberToBeRegistered = $member;
$isExistingMember = true;
} else {
$response = $this->processRegistrationCheckMembershipExistence($application);
$isExistingMember = $response['data']['isExistingMember'];
$memberToBeRegistered = $response['data']['memberToBeRegistered'];
}
$memberToBeRegistered->setIsExistingMember($isExistingMember);
// fill application with memberId and teamId and discount information
$application = $this->updateApplicationWithMemberIdAndTeamId($application, $memberToBeRegistered, $batch);
if (!$batch->isMultipleApplicationsPerMember()) {
// check if there is already an application to this event == batch with that batchId and memberId
if ($existingApplication = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
// there are two applications - the latest application will always win
$fields = Utility::getEntityFieldsArray($application, ['id', 'assessments', 'votesRegistered', 'applicationStatus', 'editStatus', 'editMessage', 'lastChangeUser', 'history', 'extraFields', 'lastModified', 'createdAt']);
$storedData = Utility::fillDataByObject($existingApplication, $fields);
$updatedData = Utility::fillDataByObject($application, $fields);
$differences = array_diff_assoc($updatedData, $storedData);
$application = $existingApplication;
$application = $this->updateApplication($application, $differences);
$application->setIsExistingApplication(true);
}
}
if (isset($settings['extraFields'])) {
$application = $this->getExtraFieldsData($application, $settings, $form);
}
$isPaidEvent = $this->checkPaidEvent($application, $batch, $event);
// Debug logging for ki-campus payment exception
$this->logger->info('Payment exception check', [
'member_exists' => $member ? true : false,
'member_id' => $member ? $member->getId() : null,
'member_email' => $member ? $member->getEmail() : null,
'member_tags' => $member ? $member->getTags() : null,
'memberToBeRegistered_id' => $memberToBeRegistered->getId(),
'memberToBeRegistered_email' => $memberToBeRegistered->getEmail(),
'memberToBeRegistered_tags' => $memberToBeRegistered->getTags(),
'batch_isFreeForKiCampus' => $batch ? $batch->getIsFreeForKiCampus() : null,
'event_isFreeForKiCampus' => $event ? $event->getIsFreeForKiCampus() : null,
]);
// Always use memberToBeRegistered for payment exception check
$hasToPay = $this->checkPaymentException($application, $memberToBeRegistered, $event, $settings, $batch);
if ($isPaidEvent && $hasToPay) {
$application = $this->updateApplicationWithStepOneInformation($application, $memberToBeRegistered, $event);
// Always use batch payment settings first
$stripePriceId = null;
$stripeAccount = null;
if ($batch) {
// Use batch-specific Stripe settings
$stripePriceId = $batch->getStripePriceId();
$stripeAccount = $batch->getStripeAccount();
}
// Legacy fallback to event settings if batch has no payment info
if (!$stripePriceId && $event) {
$stripePriceId = $event->getStripePriceId();
}
if (!$stripeAccount && $event) {
$stripeAccount = $event->getStripeAccount();
}
if ($stripePriceId > '') {
// Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId übergeben werden
$application = $em->getRepository(Application::class)->setApplication($application);
$clientReferenceId = $memberToBeRegistered->getId();
$clientEmail = $memberToBeRegistered->getEmail();
$successUrlParameters = [
'slug' => $event->getSlug(),
'app' => $application->getId(),
'action' => "checkSuccess",
'provider' => 'stripe',
'embed' => $embed,
];
$route = 'event_confirmation';
$successPage = $this->generateUrl($route, $successUrlParameters, UrlGeneratorInterface::ABSOLUTE_URL);
//$successUrl = urlencode($successPage);
$successUrl = $successPage;
// Check STRIPE_MODE first, fallback to APP_ENV for backwards compatibility
$stripeMode = $_ENV['STRIPE_MODE'] ?? ($_ENV['APP_ENV'] === 'prod' ? 'live' : 'test');
$isLiveEnvironment = $stripeMode === 'live';
$stripeAccount = $stripeAccount ?: "STARTPLATZ";
$stripeApiKey = $em->getRepository(Option::class)->getStripeApiKey($stripeAccount, $isLiveEnvironment);
// In dev environment, use a test price ID instead of live one
if (!$isLiveEnvironment && $stripePriceId && strpos($stripePriceId, '_test_') === false) {
// This is a live price ID in test mode - use a generic test price
$originalPriceId = $stripePriceId;
$stripePriceId = 'price_1RnzWKHegJLrFARi6RzQ9D8Q'; // Generic test price for 90 EUR
// Log the mapping
file_put_contents('/var/www/html/var/log/stripe_debug_controller.log',
date('Y-m-d H:i:s') . " - Mapped live price $originalPriceId to test price $stripePriceId\n",
FILE_APPEND);
}
// Generate cancel URL - back to the event page
$cancelUrl = $this->generateUrl('event_show_single', ['slug' => $event->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL);
// Debug logging
$debugInfo = [
'stripePriceId' => $stripePriceId,
'clientReferenceId' => $clientReferenceId,
'clientEmail' => $clientEmail,
'stripeAccount' => $stripeAccount,
'successUrl' => $successUrl,
'cancelUrl' => $cancelUrl,
'isLiveEnvironment' => $isLiveEnvironment,
];
file_put_contents('/var/www/html/var/log/stripe_debug_controller.log',
date('Y-m-d H:i:s') . " - Payment parameters: " . json_encode($debugInfo, JSON_PRETTY_PRINT) . "\n",
FILE_APPEND);
$paymentLink = $this->callbackService->curl_stripe_paymentlink($stripePriceId, $clientReferenceId, $clientEmail, $stripeApiKey, $successUrl, $cancelUrl);
// Überprüfen, ob die Antwort ein gültiger Zahlungslink ist
if (filter_var($paymentLink, FILTER_VALIDATE_URL)) {
// Erfolgsfall: Weiterleitung des Benutzers zum Zahlungslink oder Anzeigen des Links
// Zum Beispiel könnten Sie den Benutzer weiterleiten:
// Note: Stripe payment links handle email prefilling differently
// The email is already passed during payment link creation
// Adding parameters to the URL can break the payment link
if ($user && $user->getIsAdmin()) {
$this->session->getFlashBag()->add('notice', "SUCCESS paymentLink: {$paymentLink}");
}
$mailContent = [
'applicationId' => $application->getId(),
'memberId' => $application->getMemberId(),
'editStatus' => $application->getEditStatus(),
'applicationStatus' => $application->getApplicationStatus(),
'person' => $application->getPerson(),
'email' => $application->getEmail(),
'utmSource' => $application->getUtmSource(),
'eventTitle' => $event->getTitle(),
'eventSlug' => $event->getSlug(),
];
$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);
// call stripe and let applicant pay for the ticket
if ($embed){
return $this->render('@StartPlatzEventBundle/Default/iframe_redirect.html.twig', [
'targetUrl' => $paymentLink
]);
} else {
return $this->redirect($paymentLink);
}
} else {
// Fehlerfall: Anzeigen der Fehlermeldung
// Loggen der Fehlermeldung oder Anzeigen einer Fehlerseite
// Zum Beispiel könnten Sie eine Fehlerseite rendern:
$this->session->getFlashBag()->add('notice', "ERROR paymentLink: {$paymentLink}");
$paymentLink = false;
}
} else {
// falls es bereits einen zugänglichen Monsum Account für diese Person gibt
if ($product) {
$customersArr = $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered, $product->getAccount(), false);
}
}
// Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId übergeben werden
$application = $em->getRepository(Application::class)->setApplication($application);
} else {
$route = 'event_show_single';
$routeParameters = $request->get('_route_params');
if (!array_key_exists('slug', $routeParameters)) {
$routeParameters['slug'] = $event->getSlug();
}
$response = $this->processRegistrationWithoutPaymentSimpleOptimized($isExistingMember, $user, $memberToBeRegistered, $application, $settings, $lang, $adminEmail, $batch, $route, $routeParameters);
if ($response['status'] == 'error') {
$targetPath = $response['data']['targetPath'];
return $this->redirect($targetPath);
}
$application = $response['data']['application'];
$member = $response['data']['member'];
if ($this->shouldSetAiHubMembership($settings, $application->getApplicationStatus())) {
$aiHubStatus = $settings["setAiHubMembership"];
$service = $this->configureServiceForAiHub($aiHubStatus, $event->getStartDate());
$reason = "participated in event with id={$event->getId()} and title={$event->getTitle()}";
$em->getRepository(Member::class)->setAiHubMembership($service, $member, $reason);
}
}
}
}
}
$promotionUrl = null;
if ($user && $batch) {
// Generate the absolute base URL based on the route and parameters
$baseURL = $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
// UTM parameters for the promotion URL
$promotionUtmParameters = [
'utm_source' => 'affiliate',
'utm_medium' => $member->getId(),
'utm_campaign' => 'event:' . $event->getId(),
'utm_term' => '',
'utm_content' => '',
];
$promotionUrl = $em->getRepository(Batch::class)->generatePromotionUrl($baseURL, $promotionUtmParameters);
}
// Überprüfen Sie den Wert des Teaserfeldes, um showTeaser zu setzen
$showTeaser = !empty($event->getTeaser());
$paymentLink = false;
$view = '@StartPlatzEventBundle/Default/event-single.html.twig';
if ($event->isSinglePage() or $embed){
$view = '@StartPlatzEventBundle/Default/event-single.lp.html.twig';
}
if ($event->getTwigFile() == 'winter-2025' ){
$view = '@StartPlatzEventBundle/Default/event-single.winter-2025.html.twig';
}
if ($event->getTwigFile() == 'style-ecodynamics' ){
$view = '@StartPlatzEventBundle/Default/event-single.style-ecodynamics.html.twig';
}
if ($event->getTwigFile() == 'style-ai-hub' ){
$view = '@StartPlatzEventBundle/Default/event-single.style-ai-hub.html.twig';
}
if ($event->getTwigFile() == 'style-startplatz' ){
$view = '@StartPlatzEventBundle/Default/event-single.style-ai-hub.html.twig';
}
return $this->render($view, [
'event' => $event,
'seriesEvents' => $seriesEvents,
'showTeaser' => $showTeaser,
'utmParameters' => $utmParameters,
'promotionUrl' => $promotionUrl,
'eventbriteId' => $eventbriteId,
'isEventbriteOnly' => $isEventbriteOnly,
'applicationUrl' => $applicationUrl,
'batch' => $batch,
'settings' => $settings,
'form' => $form ? $form->createView() : $form,
'application' => $application,
'action' => $action,
'batchIsOpen' => $batchIsOpen,
'customersArr' => $customersArr,
'member' => $member,
'paymentLink' => $paymentLink,
// Multi-batch event support
'futureBatches' => $futureBatches,
'pastBatches' => $pastBatches ?? [],
'allBatches' => $allBatches,
'selectedDate' => $date,
'isMultiBatchEvent' => $event->isMultiBatchEvent(),
'speakers' => $this->getSpeakers($event),
'phrases' => $em->getRepository(Option::class)->getApplicationPhrases($program, $batch, false, $lang),
'editFeedback' => $editFeedback,
'templateVars' => [],
'preview' => $request->get('preview'),
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
'isEnglish' => $lang === 'EN',
'embed' => $embed,
'targetRoute' => "event_show_single",
'targetRouteSlug' => $event->getSlug(),
]);
}
/**
* @Route("/api/event/participant-by-linkedin", name="add_participant-by-linkedin", methods={"POST"})
* */
public function addParticipantByLinkedIn(Request $request)
{
$authorizedIdentifier = 'IhrGeheimerIdentifier'; // Set your identifier here
// Check the identifier
if ($request->headers->get('X-CUSTOM-IDENTIFIER') !== $authorizedIdentifier) {
//return new Response('Unauthorized', Response::HTTP_UNAUTHORIZED);
}
$data = json_decode($request->getContent(), true);
$em = $this->getDoctrine()->getManager();
if (empty($data['email'])) {
return new Response('Required data missing: email', Response::HTTP_BAD_REQUEST);
}
if (empty($data['formName'])) {
return new Response('Required data missing: formName', Response::HTTP_BAD_REQUEST);
}
// Retrieve the UTM parameters from the URL
$utmParameters = [
'utmSource' => $data['utmSource'] ?? null,
'utmMedium' => $data['utmMedium'] ?? null,
'utmCampaign' => $data['utmCampaign'] ?? null,
'utmTerm' => $data['utmTerm'] ?? null,
'utmContent' => $data['utmContent'] ?? null,
];
$formName = $data['formName'];
// contains eventTitle
// search for event with event Titel latest startDate
if (!$event = $em->getRepository(Event::class)->findOneBy(['title'=>$formName], ['startDate'=>'DESC'])) {
return new Response('Event not found', Response::HTTP_NOT_FOUND);
}
$eventId = $event->getId();
if (!$batch = $em->getRepository(Batch::class)->findOneBy(['eventId' => $eventId],['start'=>'DESC'])) {
return new Response('Associated batch with event ID not found', Response::HTTP_NOT_FOUND);
}
if (!$em->getRepository(Batch::class)->isBatchApplicationOpen($batch)) {
return new Response('Batch application is closed', Response::HTTP_FORBIDDEN);
}
$member = $em->getRepository(Member::class)->createGuestMemberIfNotExists($data, false,$data['email'], "MA");
if (empty($member->getPhone()) && !empty($data['phone'])) {
$phone = Utility::cleanAndFormatPhoneNumber($data['phone']);
$member = $em->getRepository(Member::class)->setPhone($member, $phone, $member->getEmail());
}
/** @var Application $application */
$application = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
if (!$application) {
$application = $em->getRepository(Application::class)->createApplicationByApplyForm(
$batch->getProgram(),
$batch,
false,
[],
$utmParameters
);
}
$application = $em->getRepository(Application::class)->updateApplicationByMember($application, $member);
$application->setHasNewsletterPermissionGiven(1);
$extraFields['jobTitle'] = $data['jobTitle'] ?? 'n.a.';
$extraFields['company'] = $data['company'] ?? 'n.a.';
$extraFields['country'] = $data['country'] ?? 'n.a.';
if ($extraFields) {
$application->setExtraFields(json_encode($extraFields)) ;
}
$application = $em->getRepository(Application::class)->setApplicationApplied($application, $member);
$this->sendConfirmationMailToApplicant(null, $member, null, $application, $batch);
return new Response('Participant added', Response::HTTP_CREATED);
}
/**
* @Route("/api/event/participant-by-facebook", name="add_participant-by-facebook", methods={"POST"})
* @Route("/api/event/participant-by-tiktok", name="add_participant-by-tiktok", methods={"POST"})
* */
public function addParticipantByFacebook(Request $request)
{
set_time_limit(0);
$authorizedIdentifier = 'IhrGeheimerIdentifier'; // Set your identifier here
$em = $this->getDoctrine()->getManager();
try {
// Check the identifier
if ($request->headers->get('X-CUSTOM-IDENTIFIER') !== $authorizedIdentifier) {
//return new JsonResponse(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
}
// Parse and validate JSON payload
$data = json_decode($request->getContent(), true);
if (json_last_error() !== JSON_ERROR_NONE) {
return new JsonResponse([
'error' => 'Invalid JSON payload',
'details' => json_last_error_msg()
], Response::HTTP_BAD_REQUEST);
}
// Validate required fields
if (empty($data['email'])) {
return new JsonResponse([
'error' => 'Required data missing: email',
'field' => 'email'
], Response::HTTP_BAD_REQUEST);
}
if (empty($data['formName'])) {
return new JsonResponse([
'error' => 'Required data missing: formName',
'field' => 'formName'
], Response::HTTP_BAD_REQUEST);
}
// Save raw webhook data for debugging
$currentTimestamp = date('Y-m-d_H:i:s');
$em->getRepository(Option::class)->createOptionIfNotExists(
'sp.event.api.add-participant-by-facebook.' . $currentTimestamp,
$request->getContent(),
'facebook-import'
);
// Extract UTM parameters
$utmParameters = [
'utmSource' => $data['utmSource'] ?? null,
'utmMedium' => $data['utmMedium'] ?? null,
'utmCampaign' => $data['utmCampaign'] ?? null,
'utmTerm' => $data['utmTerm'] ?? null,
'utmContent' => $data['utmContent'] ?? null,
];
// Parse formName safely
$formName = $data['formName'];
$parts = explode("|", $formName);
if (count($parts) < 3) {
return new JsonResponse([
'error' => 'Invalid formName format',
'details' => 'Expected format: "Type | Name | Slug"',
'received' => $formName
], Response::HTTP_BAD_REQUEST);
}
// Extract and normalize slug
$slug = trim($parts[2], " /");
$slug = strtolower($slug);
// Begin database transaction
$em->getConnection()->beginTransaction();
try {
// Find the appropriate event
$event = null;
// Check if this is a multi-batch/evergreen event
if (str_contains(strtolower($parts[0]), 'evergreen')) {
$now = new \DateTime();
$event = $em->getRepository(Event::class)->findNextEventByPartOfSlug($slug, $now);
if (!$event) {
throw new \Exception("No upcoming evergreen event found with slug containing: {$slug}");
}
} else {
$event = $em->getRepository(Event::class)->findOneBy(['slug' => $slug]);
if (!$event) {
throw new \Exception("Event not found with slug: {$slug}");
}
}
// Find the appropriate batch
$batch = null;
if ($event->getIsMultiBatchEvent()) {
// Multi-batch event: find next open batch
$batch = $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
if (!$batch) {
// Fallback: try to find any open batch
$batches = $em->getRepository(Batch::class)->findBy([
'eventId' => $event->getId(),
'status' => 'active'
]);
foreach ($batches as $b) {
if ($em->getRepository(Batch::class)->isBatchApplicationOpen($b)) {
$batch = $b;
break;
}
}
}
if (!$batch) {
throw new \Exception('No open batches available for this multi-batch event');
}
} else {
// Single-batch event
$batch = $em->getRepository(Batch::class)->findOneBy(['eventId' => $event->getId()]);
if (!$batch) {
throw new \Exception('No batch found for this event');
}
}
// Verify batch is open for registration
if (!$em->getRepository(Batch::class)->isBatchApplicationOpen($batch)) {
throw new \Exception('Registration is closed for this batch');
}
// Create or update member
// Transform data to match expected field names
$memberData = $data;
if (isset($data['firstname'])) {
$memberData['firstName'] = $data['firstname'];
}
if (isset($data['lastname'])) {
$memberData['lastName'] = $data['lastname'];
}
$member = $em->getRepository(Member::class)->createGuestMemberIfNotExists(
$memberData,
false,
$data['email'],
"MA"
);
// Update phone if provided
if (empty($member->getPhone()) && !empty($data['phone'])) {
$phone = Utility::cleanAndFormatPhoneNumber($data['phone']);
$member = $em->getRepository(Member::class)->setPhone($member, $phone, $member->getEmail());
}
// Check for existing application
$application = $em->getRepository(Application::class)->findOneBy([
'batchId' => $batch->getId(),
'memberId' => $member->getId()
]);
// Create new application if needed
if (!$application) {
$application = $em->getRepository(Application::class)->createApplicationByApplyForm(
$batch->getProgram(),
$batch,
false,
[],
$utmParameters
);
} else {
// Log duplicate application attempt
$this->logger->info('Facebook webhook: Duplicate application attempt', [
'email' => $data['email'],
'eventSlug' => $slug,
'batchId' => $batch->getId()
]);
}
// Update application with member data
$application = $em->getRepository(Application::class)->updateApplicationByMember($application, $member);
$application->setHasNewsletterPermissionGiven(1);
$application = $em->getRepository(Application::class)->setApplicationApplied($application, $member);
// Commit transaction before sending emails
$em->getConnection()->commit();
// Send confirmation email (includes HubSpot sync)
// Disable newsletter registration for Facebook webhook participants
try {
$this->sendConfirmationMailToApplicant(null, $member, null, $application, $batch, false);
} catch (\Exception $emailException) {
// Log email error but don't fail the webhook
$this->logger->error('Facebook webhook: Failed to send confirmation email', [
'error' => $emailException->getMessage(),
'memberId' => $member->getId(),
'applicationId' => $application->getId()
]);
}
// Return success with details
return new JsonResponse([
'status' => 'success',
'message' => 'Participant added successfully',
'data' => [
'memberId' => $member->getId(),
'applicationId' => $application->getId(),
'eventId' => $event->getId(),
'eventSlug' => $event->getSlug(),
'batchId' => $batch->getId(),
'batchDate' => $batch->getStartDate() ? $batch->getStartDate()->format('Y-m-d') : null
]
], Response::HTTP_CREATED);
} catch (\Exception $e) {
// Rollback transaction on error
$em->getConnection()->rollback();
throw $e;
}
} catch (\Exception $e) {
// Log the error
$this->logger->error('Facebook webhook error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'payload' => $data ?? null
]);
// Return error response
return new JsonResponse([
'error' => 'Failed to process participant',
'details' => $e->getMessage()
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* @Route("/allmeda/events/participants/import/{batchId}", name="allmeda_participants_import")
* @Security("is_granted('ROLE_ADMIN')")
*/
public function importParticipantsAction(Request $request, $batchId)
{
$em = $this->getDoctrine()->getManager();
$templateVars = $em->getRepository(Application::class)->getTemplateVars($request);
$feedback = [];
$adminEmail = $this->getUser()->getEmail();
$responseContent = [];
$redirect = Utility::getRedirectTarget($request);
$batch = $em->getRepository(Batch::class)->find($batchId);
$fields = ['name', 'phone', 'email', 'utmSource','sendConfirmationMail'];
$importString = Utility::setDataList($fields);
$form = $this->createForm(ImportFormType::class, ['importString' => $importString]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// perform some action, such as saving the task to the database
$inputVars = $form->getData();
$importString = $inputVars['importString'];
$rows = explode(PHP_EOL, $importString);
$firstRow = array_shift($rows);
$fields = str_getcsv($firstRow, "\t");
// fields need to be
// $fields = ['name', 'phone', 'email', 'utmSource'];
foreach ($rows as $row) {
$feedback[] = str_getcsv($row, "\t");
}
if ($form->get('import')->isClicked()) {
$now = new DateTime();
// find virtual team for that batch
// http://dev.startplatz.de/apply-add-application/80488/653
// * @Route("/apply-add-application/{memberId}/{batchId}/", name="apply_add-application")
/** @var Program $program */
$program = $batch->getProgram();
// check if settings contains necessary setters
$batch = $em->getRepository(Batch::class)->checkLegacyData($batch);
$settings = $em->getRepository(Application::class)->getSettingsProgram($program, $batch);
$virtualTeam = array_key_exists('virtualTeam', $settings) ? $settings['virtualTeam'] : 'applicants-' . $program->getSlug() . '-' . $batch->getSlug();
$applicantsTeam = $em->getRepository(Team::class)->getVirtualTeamByName($virtualTeam);
foreach ($feedback as $row) {
$row = array_combine($fields, $row);
if (in_array('utmSource', $fields)){
$data['email']= $row['email'];
$data['name']= $row['name']?? null;
$data['firstName']= $row['firstName']?? null;
$data['lastName']= $row['lastName']?? null;
$data['phone']= $row['phone'] ?? null;
$data['utmSource'] = $row['utmSource'];
$utmParameters = [
'utmSource' => $data['utmSource'] ?? null,
'utmMedium' => $data['utmMedium'] ?? null,
'utmCampaign' => $data['utmCampaign'] ?? null,
'utmTerm' => $data['utmTerm'] ?? null,
'utmContent' => $data['utmContent'] ?? null,
];
if (!Utility::validateEmail($data['email'])) {
$this->session->getFlashBag()->add('notice', 'ERROR email is not valid, please check ' . $data['email']);
return $this->redirect($this->generateUrl($redirect->path, (array)$redirect->parameters));
}
$member = $em->getRepository(Member::class)->createGuestMemberIfNotExists($data, false,$data['email'], "MA");
$application = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $member->getId()]);
if (empty($member->getPhone()) && !empty($data['phone'])) {
$member = $em->getRepository(Member::class)->setPhone($member, $data['phone'], $member->getEmail());
}
if (!$application) {
$application = $em->getRepository(Application::class)->createApplicationByApplyForm(
$batch->getProgram(),
$batch,
false,
[],
$utmParameters
);
$application = $em->getRepository(Application::class)->updateApplicationByMember($application, $member);
$application = $em->getRepository(Application::class)->setApplicationApplied($application, $member);
if (array_key_exists('sendConfirmationMail', $row) && $row['sendConfirmationMail'] === 'yes') {
$this->sendConfirmationMailToApplicant(null, $member, null, $application, $batch);
$this->session->getFlashBag()->add('notice', 'SUCCESS confirmationMail has been sent to ' . $data['email']);
}
} else {
if (empty($application->getPhone()) && !empty($member->getPhone())) {
$application->setPhone($member->getPhone());
$em->persist($application);
$em->flush();
}
}
} else {
$email = $row['email'];
$firstName = $row['firstName'];
// find member by email
$member = $em->getRepository(Member::class)->createGuestMemberIfNotExists($row, $applicantsTeam, $adminEmail, $memberType = "MA");
$application = $em->getRepository(Application::class)->createApplicationIfNotExists($program, $batch, $applicantsTeam, $member, $adminEmail);
}
$responseContent[] = "Application created or updated for {$application->getPerson()} with email={$application->getEmail()}";
}
$batch = $em->getRepository(Batch::class)->updateNumbers($batch);
$this->session->getFlashBag()->add('notice', 'SUCCESS data has been imported ' . print_r($responseContent, true));
return $this->redirect($this->generateUrl($redirect->path, (array)$redirect->parameters));
}
} else {
$importString = 'leer';
}
return $this->render('@StartPlatzWebsiteBundle/Import/import.html.twig', [
'importString' => $importString,
'feedback' => $feedback,
'templateVars' => $templateVars,
'form' => $form->createView(),
'fields' => $fields,
'title' => "Import Applications Template: default",
'hint' => "Write 'yes' in the column 'sendConfirmationMail' if a confirmation mail should be sent",
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
]);
}
private function shouldSetAiHubMembership($settings, $applicationStatus)
{
return array_key_exists("setAiHubMembership", $settings) && $applicationStatus === 'applied';
}
private function configureServiceForAiHub($status, DateTime $startDate)
{
$service = new Service(); // oder hole ein existierendes Service-Objekt
$endDate = clone $startDate;
if ($status === 'trial') {
$endDate->modify('+1 month');
} elseif ($status === 'active') {
$endDate->modify('+3 months');
}
$service->setStatus($status);
$service->setStartDate($startDate);
$service->setEndDate($endDate);
return $service;
}
/**
* @Route("/allmeda/admin/event/add-participant/{id}/{batchId}", name="allmeda_admin_event_add_participant_modal", defaults={"batchId"=null})
* @Security("is_granted('ROLE_ADMIN')")
*/
public function addParticipantAction(Request $request, $id, $batchId = null)
{
$em = $this->getDoctrine()->getManager();
/** @var User $user */
$user = $this->getUser();
$adminEmail = $user->getEmail();
$returnPath = ['path' => 'admin_event_participants', 'parameters' => ['id'=>$id, 'batchId'=>$batchId]];
$redirectUrl = base64_encode(json_encode($returnPath));
if (!$event = $this->getEventRepository()->findOneBy(['id' => $id])) {
$this->addFlash('notice', 'ERROR Veranstaltung nicht gefunden');
return $this->redirectToRoute('admin_event_participants', ['id'=>$id, 'batchId'=>$batchId]);
}
// Retrieve the UTM parameters from the URL
$utmParameters = [
'utmSource' => $request->query->get('utm_source') ?? 'admin',
'utmMedium' => $request->query->get('utm_medium'),
'utmCampaign' => $request->query->get('utm_campaign'),
'utmTerm' => $request->query->get('utm_term'),
'utmContent' => $request->query->get('utm_content'),
];
$lang = 'DE';
if ($targetPath = $request->server->get('REDIRECT_URL')) {
$lang = $this->menuTranslationService->getLang($targetPath);
}
$eventbriteId = $event->getEventbriteId();
$isEventbriteOnly = $event->getTicketing() > '';
$applicationUrl = $event->getApplicationUrl();
// Handle batch selection - use provided batchId or find appropriate batch
if ($batchId) {
$batch = $em->getRepository(Batch::class)->find($batchId);
// Verify batch belongs to this event
if (!$batch || $batch->getEventId() != $event->getId()) {
$this->addFlash('notice', 'ERROR: Selected batch does not belong to this event');
// Fallback to finding next open batch
if ($event->isMultiBatchEvent()) {
$batch = $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
} else {
$batch = $em->getRepository(Batch::class)->findOneBy(['programId'=>27, 'eventId'=>$event->getId()]);
}
// Update batchId to the fallback batch's ID
if ($batch) {
$batchId = $batch->getId();
}
}
} else {
// No batch specified - find appropriate batch
if ($event->isMultiBatchEvent()) {
$batch = $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
} else {
$batch = $em->getRepository(Batch::class)->findOneBy(['programId'=>27, 'eventId'=>$event->getId()]);
}
// Set batchId to the found batch's ID
if ($batch) {
$batchId = $batch->getId();
}
}
$batchIsOpen = $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
$program = $batch->getProgram();
$settings = $this->getProgramSettings($program, $batch);
$member = false;
$application = $this->getOrCreateApplication($request, $batch, $program, $member, $lang, $settings, $utmParameters, null);
$form = $this->createForm(ApplicationType::class, $application, ['settings' => $settings, 'program' => $program, 'batch' => $batch]);
$form->handleRequest($request);
##############################################################
## form validation starts here ##
##############################################################
if ($form->isSubmitted() && $form->isValid()) {
$application = $em->getRepository(Application::class)->cleanApplicationData($application);
$adminEmail = $application->getEmail();
if ($member) {
$memberToBeRegistered = $member;
$isExistingMember = true;
} else {
$response = $this->processRegistrationCheckMembershipExistence($application);
$isExistingMember = $response['data']['isExistingMember'];
$memberToBeRegistered = $response['data']['memberToBeRegistered'];
}
$memberToBeRegistered->setIsExistingMember($isExistingMember);
// fill application with memberId and teamId and discount information
$application = $this->updateApplicationWithMemberIdAndTeamId($application, $memberToBeRegistered, $batch);
if (!$batch->isMultipleApplicationsPerMember()) {
// check if there is already an application to this event == batch with that batchId and memberId
if ($existingApplication = $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
// there are two applications - the latest application will always win
$fields = Utility::getEntityFieldsArray($application, ['id','assessments', 'votesRegistered', 'applicationStatus', 'editStatus', 'editMessage', 'lastChangeUser','history','extraFields','lastModified','createdAt']);
$storedData = Utility::fillDataByObject($existingApplication, $fields);
$updatedData = Utility::fillDataByObject($application, $fields);
$differences = array_diff_assoc($updatedData, $storedData);
$application = $existingApplication;
$application = $this->updateApplication($application, $differences);
$application->setIsExistingApplication(true);
}
}
if (isset($settings['extraFields'])) {
$application = $this->getExtraFieldsData($application, $settings, $form);
}
$settings['isNeedsLoginValidation'] = 0;
$settings['isNeedsEmailValidation'] = 0;
$settings['isSendConfirmationMail'] = 0;
$route = 'event_show_single';
$routeParameters = $request->get('_route_params');
if (!array_key_exists('slug', $routeParameters)) {
$routeParameters['slug'] = $event->getSlug();
}
$application->setApplicationStatus('applied');
$application = $em->getRepository(Application::class)->setApplication($application, $memberToBeRegistered);
$this->session->getFlashBag()->add('notice', "SUCCESS {$application->getPerson()} wurde hinzugefügt mit Status == {$application->getApplicationStatus()}");
return $this->redirectToRoute('admin_event_participants', ['id'=>$id, 'batchId'=>$batchId]);
}
return $this->render('@StartPlatzEvent/Admin/edit.add-participant.html.twig', [
'event' => $event,
'eventbriteId' => $eventbriteId,
'isEventbriteOnly' => $isEventbriteOnly,
'applicationUrl' => $applicationUrl,
'batch' => $batch,
'settings' => $settings,
'form' => $form ? $form->createView() : $form,
'application' => $application,
'batchIsOpen' => $batchIsOpen,
'member' => $member,
'speakers' => $this->getSpeakers($event),
'phrases' => $em->getRepository(Option::class)->getApplicationPhrases($program, $batch, false, $lang),
'templateVars' => [],
'preview' => $request->get('preview'),
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
'lang' => $lang,
'targetRoute' => "event_show_single",
'targetRouteSlug' => $event->getSlug(),
'targetPath' => $targetPath,
]);
}
public function updateApplication(Application $application, $differences)
{
foreach ($differences as $diff => $value) {
$method = 'set' . ucfirst((string) $diff);
if (method_exists($application, $method)) {
$application->$method($value);
}
}
return $application;
}
private function getExtraFieldsData(Application $application, $settings, $form)
{
$extraFields = $settings['extraFields'];
$data = [];
$i = 0;
foreach ($extraFields as $extraField) {
$data[$extraField['field']] = $form->get($extraField['field'])->getData();
$i++;
}
if ($data) {
$application->setExtraFields(json_encode($data)) ;
}
return $application;
}
private function getExtraFieldsAttributes($settings)
{
if (!isset($settings['extraFields'])) {
return $settings;
}
$em = $this->getDoctrine()->getManager();
$extraFields = $settings['extraFields'];
$i = 0;
foreach ($extraFields as $extraField) {
if ($extraField['type'] == 'choice') {
$choices = [];
if (isset($extraField['arr'])) {
$choices = $em->getRepository(Attribute::class)->findCodesByAttributeName($extraField['arr']);
}
$settings['extraFields'][$i]['choices'] = $choices;
}
$i++;
}
return $settings;
}
private function sendMagicLinkForPayment(Application $application, Batch $batch, $customerId, $route, $routeParameters)
{
if (!$application) {
$this->session->getFlashBag()->add('notice', 'ERROR application not found');
return $this->generateUrl($route, $routeParameters);
}
$em = $this->getDoctrine()->getManager();
if (!$product = $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
$this->session->getFlashBag()->add('notice', 'ERROR configuration not found');
return $this->generateUrl($route, $routeParameters);
}
if (!$memberToBeRegistered = $this->getExistingMemberAccount($application)) {
$this->session->getFlashBag()->add('notice', 'ERROR member not found');
return $this->generateUrl($route, $routeParameters);
}
if (!$customer = $em->getRepository(Customer::class)->findOneBy(['hash'=>$customerId])) {
$this->session->getFlashBag()->add('notice', 'ERROR customer not found');
return $this->generateUrl($route, $routeParameters);
}
$routeParameters["app"] = $application->getId();
$routeParameters['action'] = "useExistingCustomerAccount";
$routeParameters['customerId'] = $customerId;
$targetPath = $this->generateUrl($route, $routeParameters);
$loginLink = $this->loginService->getLoginLinkByTargetPath($memberToBeRegistered->getEmail(), $targetPath);
$mailTemplate = $em->getRepository(MailTemplate::class)->getPaymentLoginMailTemplateByBatch($batch, $application, $loginLink);
$feedback = Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
$routeParameters["app"] = $application->getId();
$routeParameters['action'] = "MagicLinkForPaymentSent";
$targetPath = $this->generateUrl($route, $routeParameters);
return $targetPath;
}
private function getSpeakers($event)
{
$em = $this->getDoctrine()->getManager();
$speakers = [];
if ($rows = json_decode((string) $event->getSpeakers())) {
foreach ($rows as $row) {
switch (strtolower((string) $row[1])) {
case "partner":
// find partner page
if ($partner = $em->getRepository(Page::class)->find($row[2])) {
$speakers['partners'][] = $partner;
}
break;
case "speaker":
// find member
if ($member = $em->getRepository(Member::class)->find($row[2])) {
$speakers['members'][] = $member;
}
break;
default:
$speakers['legacy'][] = $row;
break;
}
}
}
return $speakers;
}
private function prepareMonsumCheckoutNewMonsumAccount(Application $application, Batch $batch, $action, $route, $routeParameters, $customerId = 0)
{
if (!$application) {
$this->session->getFlashBag()->add('notice', 'ERROR application not found');
return $this->generateUrl($route, $routeParameters);
}
$em = $this->getDoctrine()->getManager();
if (!$product = $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
$this->session->getFlashBag()->add('notice', 'ERROR configuration not found');
return $this->generateUrl($route, $routeParameters);
}
if (!$memberToBeRegistered = $this->getExistingMemberAccount($application)) {
return $this->generateUrl($route, $routeParameters);
}
if ($action == "useExistingCustomerAccount") {
if (!$customerArray = $em->getRepository(Customer::class)->isMonsumCustomerValidByMemberAndCustomerId($memberToBeRegistered, $customerId)) {
$this->session->getFlashBag()->add('notice', 'ERROR customer account not found');
return $this->generateUrl($route, $routeParameters);
}
if (!$customer = $em->getRepository(Customer::class)->findOneBy(['customerId'=>$customerArray['customerId'] ])) {
$this->session->getFlashBag()->add('notice', 'ERROR customer account not found');
return $this->generateUrl($route, $routeParameters);
}
} else {
$team = $em->getRepository(Team::class)->createNewMonsumAccountTeamByMembershipApplicant($memberToBeRegistered);
$customer = $this->setMonsumAccountIfNotExists($memberToBeRegistered, $team, $application, $product->getAccount());
}
$customer = $em->getRepository(Customer::class)->setCheckoutData($customer, $product->getAccount(), $product->getProductNumber(), $memberToBeRegistered->getId());
$successUrlParameters = [
'slug' => $routeParameters['slug'],
'app' => $application->getId(),
'action' => "checkSuccess",
'provider' => 'monsum',
];
if ($product->getLandingPageSnippet() > '') {
$successUrlParameters['account'] = $product->getAccount();
$successUrlParameters['productnumber'] = $product->getProductNumber();
}
$successPage = $this->generateUrl($route, $successUrlParameters, UrlGeneratorInterface::ABSOLUTE_URL);
$successUrl = urlencode($successPage);
$successUrl = urlencode($successUrl); // that is strange but monsum wants double encoding
$subscription_title = $routeParameters['slug'];
$promocode = "";
switch ($application->getDiscountPercent()) {
case "100":
$promocode = "100_system";
break;
case "50":
$promocode = "community";
break;
case "20":
//$promocode = "20_system"; wegen AI ausser Kraft gesetzt
break;
}
if ($application->getUtmTerm()) {
$promocode = $application->getUtmTerm();
}
$accountHash = $customer->getAccountHash();
$customerHash = $customer->getHash();
$productNumber = $product->getProductNumber();
return "https://app.monsum.com/checkout/0/{$accountHash}/{$customerHash}/{$productNumber}&success_url={$successUrl}&promocode={$promocode}&subscription_title={$subscription_title}&x_applicationId={$application->getId()}";
}
private function prepareMonsumCheckout($application, $product, $member, $event, $customer)
{
$successPage = $this->generateUrl('event_show_single', [
'slug' => $event->getSlug(),
'app' => $application->getId(),
'action' => "checkSuccess",
'provider' => 'monsum',
], UrlGeneratorInterface::ABSOLUTE_URL);
$successUrl = urlencode($successPage);
$successUrl = urlencode($successUrl); // that is strange but monsum wants double encoding
$subscription_title = $event->getTitle();
$promocode = $member->getHasCommunityMembership() ? "community" : "";
$accountHash = $customer->getAccountHash();
$customerHash = $customer->getHash();
$productNumber = $product->getProductNumber();
return "https://app.monsum.com/checkout/0/{$accountHash}/{$customerHash}/{$productNumber}&success_url={$successUrl}&promocode={$promocode}&subscription_title={$subscription_title}&x_applicationId={$application->getId()}";
}
private function updateApplicationWithStepOneInformation(Application $application,Member $memberToBeRegistered, Event $event)
{
$status = "process";
// Check for Stripe payment configuration
$hasStripePayment = false;
if ($event->getStripePriceId() > '') {
$hasStripePayment = true;
}
// For multi-batch events, also check batch-specific Stripe settings
if ($event->isMultiBatchEvent() && $application->getBatchId()) {
$em = $this->getDoctrine()->getManager();
$batch = $em->getRepository(Batch::class)->find($application->getBatchId());
if ($batch && $batch->getStripePriceId() > '') {
$hasStripePayment = true;
}
}
if ($hasStripePayment){
$todo = "stripeStarted";
} else {
$todo = "do next step";
}
$message = "Next step is needed";
$application->setEditStatus("{$status}:{$todo}");
$application->setEditMessage($message);
$application->setApplicationStatus('started');
$application->setMemberId($memberToBeRegistered->getId());
return $application;
}
private function getOrCreateMemberAccount($application)
{
if (!$memberToBeRegistered = $this->getExistingMemberAccount($application)) {
$memberToBeRegistered = $this->createGuestMember($application);
}
return $memberToBeRegistered;
}
private function processRegistrationCheckMembershipExistence($application)
{
$em = $this->getDoctrine()->getManager();
$status = "success";
$message = "nothing to be reported";
// does a memberAccount exist with that email?
if ($memberToBeRegistered = $this->getExistingMemberAccount($application)) {
$isExistingMember = true;
} else {
$isExistingMember = false;
$memberToBeRegistered = $this->createGuestMember($application);
}
$response = [
'status' => $status,
'message' => $message,
'data' => [
'isExistingMember' => $isExistingMember,
'memberToBeRegistered' => $memberToBeRegistered,
],
];
return $response;
}
private function processRegistrationWithPayment($application, $memberToBeRegistered, $account)
{
$em = $this->getDoctrine()->getManager();
$status = "success";
$message = "nothing to be reported";
$data = [];
// does a customerAccount exist with that member?
if ($customer = $em->getRepository(Customer::class)->findPrimaryMonsumAccount($memberToBeRegistered, $account)) {
$isExistingCustomerAccount = true;
} else {
$isExistingCustomerAccount = false;
$team = $em->getRepository(Team::class)->createTeamByMembershipApplicant($memberToBeRegistered);
$customer = $this->setMonsumAccountIfNotExists($memberToBeRegistered, $team, $application, $account);
}
$response = [
'status' => $status,
'message' => $message,
'data' => [
'isExistingCustomerAccount' => $isExistingCustomerAccount,
'customer' => $customer,
],
];
return $response;
}
private function getExistingMemberAccount($application)
{
$em = $this->getDoctrine()->getManager();
$existingMember = false;
if ($application->getEmail()) {
$existingMember = $em->getRepository(Member::class)->findOneBy(['email' => $application->getEmail()]);
}
return $existingMember;
}
private function createGuestMember($application)
{
$em = $this->getDoctrine()->getManager();
$guestMember = false;
$data['email'] = $application->getEmail();
$data['firstName'] = $application->getFirstName();
$data['lastName'] = $application->getLastName();
$data['name'] = $application->getPerson();
$adminEmail = $application->getEmail();
// we create an guest membership, memberType needs to be only "MA"
$memberType = "MA";
$guestMember = $em->getRepository(Member::class)->createGuestMemberIfNotExists($data, false, $adminEmail, $memberType);
return $guestMember;
}
private function processRegistrationWithoutPaymentSimpleOptimized($isExistingMember, $user, $member, Application $application, $settings, $lang, $adminEmail, Batch $batch, $route, $routeParameters)
{
$em = $this->getDoctrine()->getManager();
$status = "error";
$message = "";
$action = "";
$mailTemplate = "";
$targetPath = "";
if ($user) {
$application->setApplicationStatus('applied');
$application = $em->getRepository(Application::class)->setApplication($application, $member);
if ($batch->getProgramId() == 30 && !in_array($batch->getGoToPage(), ['landingPage','editMentorsProfile'])) {
$status = "error";
$targetPath = $batch->getGoToPage();
$response = [
'status' => $status,
'message' => $message,
'data' => [
'targetPath' => $targetPath,
'application' => $application,
'member' => $member,
],
];
return $response;
}
$feedback = $this->sendConfirmationMailToApplicant($user, null, $settings, $application, $batch);
if ($lang == 'EN') {
$message = "Thank you for your registration.<br>We have sent you a confirmation email.";
} else {
$message = "Danke für Deine Registrierung.<br>Wir haben Dir eine Bestätigungsmail geschickt.";
}
$status = "success";
$response = [
'status' => $status,
'message' => $message,
'data' => [
'targetPath' => $targetPath,
'application' => $application,
'member' => $member,
],
];
$status = strtoupper($status);
$this->session->getFlashBag()->add('notice', "{$status} {$message}");
return $response;
}
if ($isExistingMember && $settings['isNeedsLoginValidation']) {
$status = "error";
$todo = "validateLogin";
$action = "needsLoginValidation";
$message = "This email address is already in use. Please login to make your application valid.";
} elseif ($isExistingMember && !$settings['isNeedsLoginValidation']) {
$status = "process";
$todo = "notValidatedLogin";
$action = "applied";
$message = "This email address is already in use. Application will be processed without login being validated";
} elseif (!$isExistingMember && $settings['isNeedsEmailValidation']) {
$status = "error";
$todo = "validateEmail";
$action = "needsEmailVerification";
$message = "Email is not yet registered. Email needs to be validated";
} elseif (!$isExistingMember && !$settings['isNeedsEmailValidation']) {
$status = "process";
$todo = "notValidateEmail";
$action = "registered";
$message = "Email is not yet registered. Application will be processed without email being validated";
}
$application = $em->getRepository(Application::class)->setEditStatus($application, $status, $todo, $message);
if ($batch->getProgramId() == 30 && !in_array($batch->getGoToPage(), ['landingPage','editMentorsProfile'])) {
$application->setApplicationStatus('applied');
$application = $em->getRepository(Application::class)->setApplication($application, $member);
$status = "error";
$targetPath = $batch->getGoToPage();
$loginLink = $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
$response = [
'status' => $status,
'message' => $message,
'data' => [
'targetPath' => $loginLink,
'application' => $application,
'member' => $member,
],
];
return $response;
}
$phrases = $em->getRepository(Option::class)->getApplicationPhrases($batch->getProgram(), $batch, false, $lang);
if ($status == 'process') {
$application->setApplicationStatus('applied');
switch ($lang) {
case "DE":
$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!";
break;
case "EN":
$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!";
break;
}
} else {
$application->setApplicationStatus('started');
switch ($lang) {
case "DE":
$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";
break;
case "EN":
$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.";
break;
}
}
$this->session->getFlashBag()->add('notice', "SUCCESS {$message}");
$application = $em->getRepository(Application::class)->setApplication($application, $member);
$routeParameters['action'] = $action;
$routeParameters['lang'] = $lang;
$targetPath = strtolower($this->generateUrl($route, $routeParameters));
$loginLink = $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
if ($isExistingMember && $settings['isNeedsLoginValidation']) {
$mailTemplate = $em->getRepository(MailTemplate::class)->getValidateLoginMailTemplateByBatch($batch, $application, $loginLink);
$feedback = Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
} elseif ($isExistingMember && !$settings['isNeedsLoginValidation']) {
$this->sendConfirmationMailToApplicant(null, $member, $settings, $application, $batch);
} elseif (!$isExistingMember && $settings['isNeedsEmailValidation']) {
$mailTemplate = $em->getRepository(MailTemplate::class)->getValidateEmailMailTemplateByBatch($batch, $application, $loginLink);
$feedback = Utility::sendAlertMailPerZapier($mailTemplate['recipientEmail'], $mailTemplate['mailText'], $mailTemplate['mailSubject'], $mailTemplate['replyToEmail'], $mailTemplate['fromName'], $mailTemplate['bodyType']);
} elseif (!$isExistingMember && !$settings['isNeedsEmailValidation']) {
$this->sendConfirmationMailToApplicant(null, $member, $settings, $application, $batch);
}
$response = [
'status' => $status,
'message' => $message,
'data' => [
'targetPath' => $targetPath,
'application' => $application,
'member' => $member,
],
];
return $response;
}
private function processRegistrationWithoutPayment($isMemberloggedIn, $member, $application, $settings, $request, $lang, $adminEmail, $batch)
{
$em = $this->getDoctrine()->getManager();
if ($existingMember = $em->getRepository(Member::class)->findOneBy(['email' => $application->getEmail()])) {
$status = "error";
$todo = "validateLogin";
if ($existingApplication = $em->getRepository(Application::class)->findOneBy(['memberId'=>$existingMember->getId(), 'batchId'=>$batch->getId()])) {
$application = $existingApplication;
$message = "You have already applied. Please login with the magic link.";
} else {
$application->setMemberId($existingMember->getId());
$message = "This email address is already in use. Please login to make your application valid.";
}
$application = $em->getRepository(Application::class)->setEditStatus($application, $status, $todo, $message);
if (!$application->getApplicationStatus()) {
$application->setApplicationStatus('started');
}
// set this application i.e. save to database
$application = $em->getRepository(Application::class)->setApplication($application);
$member = $em->getRepository(Member::class)->updateMemberWithApplication($existingMember, $application, $adminEmail);
$routeParameters = $request->get('_route_params');
$routeParameters["app"] = $application->getId();
$routeParameters['action'] = "needsLoginValidation";
$targetPath = strtolower($this->generateUrl('event_show_single', $routeParameters));
$loginLink = $this->loginService->getLoginLinkByTargetPath($existingMember->getEmail(), $targetPath);
$mailTemplate = $em->getRepository(MailTemplate::class)->getValidateLoginMailTemplateByBatch($batch, $application, $loginLink);
/*
if ($lang == 'DE'){
$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.' );
} else {
$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.' );
}
*/
$response = [
'status' => $status,
'message' => $message,
'data' => [
'mailTemplate' => $mailTemplate,
'targetPath' => $targetPath,
],
];
return $response;
} else {
$status = "error";
$todo = "validateEmail";
$message = "Email is not yet registered. Email needs to be validated";
$application = $em->getRepository(Application::class)->setErrorValidateEmail($application);
$data['email'] = $application->getEmail();
$data['firstName'] = $application->getFirstName();
$data['lastName'] = $application->getLastName();
$data['name'] = $application->getPerson();
$adminEmail = $application->getEmail();
if ($settings['isTeamNeeded']) {
$memberType = "GF";
} else {
$memberType = "MA";
}
$member = $em->getRepository(Member::class)->createGuestMemberIfNotExists($data, false, $adminEmail, $memberType);
$application->setMemberId($member->getId());
$application->setTeamId($member->getTeamId());
$application = $em->getRepository(Application::class)->setEditStatus($application, $status, $todo, $message);
if (!$application->getApplicationStatus()) {
$application->setApplicationStatus('started');
}
// set this application i.e. save to database
$application = $em->getRepository(Application::class)->setApplication($application);
$member = $em->getRepository(Member::class)->updateMemberWithApplication($member, $application, $adminEmail);
$routeParameters = $request->get('_route_params');
$routeParameters["app"] = $application->getId();
$routeParameters['action'] = "needsEmailVerification";
$targetPath = strtolower($this->generateUrl('event_show_single', $routeParameters));
$loginLink = $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
$mailTemplate = $em->getRepository(MailTemplate::class)->getValidateEmailMailTemplateByBatch($batch, $application, $loginLink);
/*
if ($lang == 'DE'){
$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.' );
} else {
$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.' );
}
*/
$response = [
'status' => $status,
'message' => $message,
'data' => [
'mailTemplate' => $mailTemplate,
'targetPath' => $targetPath,
],
];
return $response;
}
}
/**
* @Route("/event-ics/download/{eventId}", name="event_download")
*/
public function downloadCalendarEventByEventId($eventId)
{
$em = $this->getDoctrine()->getManager();
if (!$event = $em->getRepository(Event::class)->findOneBy(['id' => $eventId])) {
return "";
}
if (!$batch = $em->getRepository(Batch::class)->findOneBy(['eventId' => $eventId])) {
return "";
}
$zoomLink = $batch->getZoomlink();
$description = $zoomLink ? "Teilnehmen mit {$zoomLink}" : "";
$summary = $event->getTitle();
// Zeitzone für Berlin und UTC einrichten
$timezoneBerlin = new \DateTimeZone('Europe/Berlin');
$timezoneUTC = new \DateTimeZone('UTC');
// Start- und Endzeit des Events konvertieren
$startDate = $event->getStartDate();
$startDate->setTimezone($timezoneUTC);
$icsStartDate = $startDate->format('Ymd\THis\Z');
$endDate = $event->getEndDate();
$endDate->setTimezone($timezoneUTC);
$icsEndDate = $endDate->format('Ymd\THis\Z');
// Erstellen des iCal-Inhalts mit den konvertierten Zeiten
$icsContent = "BEGIN:VCALENDAR\r\n";
$icsContent .= "VERSION:2.0\r\n";
$icsContent .= "BEGIN:VEVENT\r\n";
$icsContent .= "DTSTART:{$icsStartDate}\r\n";
$icsContent .= "DTEND:{$icsEndDate}\r\n";
$icsContent .= "SUMMARY:{$summary}\r\n";
$icsContent .= "DESCRIPTION:{$description}\r\n";
$icsContent .= "END:VEVENT\r\n";
$icsContent .= "END:VCALENDAR";
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: attachment; filename="calendar_' . $event->getSlug() . '.ics"');
echo $icsContent;
exit;
}
/**
* Download ICS calendar file using Batch dates for multi-batch events
*
* @Route("/event-ics/download/batch/{batchId}", name="event_download_batch")
*/
public function downloadCalendarEventByBatchId($batchId)
{
$em = $this->getDoctrine()->getManager();
// Find batch
if (!$batch = $em->getRepository(Batch::class)->find($batchId)) {
return new Response("Batch not found", Response::HTTP_NOT_FOUND);
}
// Find event
if (!$event = $em->getRepository(Event::class)->find($batch->getEventId())) {
return new Response("Event not found", Response::HTTP_NOT_FOUND);
}
// Determine which dates to use: Batch dates for multi-batch events, Event dates otherwise
if ($event->isMultiBatchEvent() && $batch->getStartDate()) {
$startDate = clone $batch->getStartDate();
$endDate = $batch->getEndDate() ? clone $batch->getEndDate() : (clone $startDate)->modify('+2 hours');
} else {
$startDate = clone $event->getStartDate();
$endDate = clone $event->getEndDate();
}
// Prepare description
$zoomLink = $batch->getZoomlink();
$description = $zoomLink ? "Teilnehmen mit {$zoomLink}" : "";
$summary = $event->getTitle();
// Convert to UTC
$timezoneUTC = new \DateTimeZone('UTC');
$startDate->setTimezone($timezoneUTC);
$endDate->setTimezone($timezoneUTC);
$icsStartDate = $startDate->format('Ymd\THis\Z');
$icsEndDate = $endDate->format('Ymd\THis\Z');
// Generate ICS content
$icsContent = "BEGIN:VCALENDAR\r\n";
$icsContent .= "VERSION:2.0\r\n";
$icsContent .= "BEGIN:VEVENT\r\n";
$icsContent .= "DTSTART:{$icsStartDate}\r\n";
$icsContent .= "DTEND:{$icsEndDate}\r\n";
$icsContent .= "SUMMARY:{$summary}\r\n";
$icsContent .= "DESCRIPTION:{$description}\r\n";
$icsContent .= "END:VEVENT\r\n";
$icsContent .= "END:VCALENDAR";
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: attachment; filename="calendar_' . $event->getSlug() . '_batch_' . $batch->getId() . '.ics"');
echo $icsContent;
exit;
}
private function sendConfirmationMailToApplicant(User $user = null, Member $member = null, $settings, Application $application, Batch $batch, bool $allowNewsletterRegistration = true) {
if (isset($settings['isSendConfirmationMail']) && !$settings['isSendConfirmationMail']) {
return false;
}
if ($user === null && $member === null) {
return false;
}
$feedback = "no mail";
$em = $this->getDoctrine()->getManager();
if ($user === null) {
$user = $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
}
if ($member === null) {
$member = $em->getRepository(Member::class)->find($user->getMemberId());
}
$email = $member->getEmail();
if (!Utility::validateEmail($email)) {
return false;
}
if (!$user->getisNoBulkMail() && $allowNewsletterRegistration){
if (!$member->getHasKiNewsletter()){
$action = "on";
$payload = json_encode([
'firstName' => $member->getFirstName(),
'lastName' => $member->getLastName(),
'email' => $member->getEmail(),
'phone' => $member->getPhone(),
'linkedIn' => $member->getLinkedin(),
'utmSource' => 'Allmeda',
'utmMedium' => $application->getBatchSlug(),
'utmCampaign' => '',
'action' => $action
]);
##todo newsletter anmeldung neu denken
if ($batch->getProgramId() != 30) {
$response = Utility::curl_callback("https://hook.eu2.make.com/8258grek86fuwqjgi7dh3npvowxjvfk5", $payload);
if ($action == 'on') {
$action = true;
}
$member->setHasKiNewsletter((bool)$action);
$em->persist($member);
}
}
}
if (!$settings) {
$application->setStartupName("Test Startup");
$application->setEmail($user->getEmail());
}
$lang = $batch->getLang();
$program = $batch->getProgram();
$routeParameters = [
'programSlug' => $program->getSlug(),
'batchSlug' => $batch->getSlug(),
'lang' => $lang,
];
$targetPath = $this->generateUrl('apply_program_home', $routeParameters);
$loginLink = $this->loginService->getLoginLinkByTargetPath($application->getEmail(), $targetPath);
// Get event for multi-batch check
$event = null;
if ($batch->getEventId()) {
$event = $em->getRepository(Event::class)->find($batch->getEventId());
}
// Use MailTemplateService for confirmation mail
$confirmationMail = $this->mailTemplateService->renderConfirmationMail(
$batch,
$user,
$application,
$loginLink,
$event
);
$feedback = Utility::sendAlertMailPerZapier(
$application->getEmail(),
$confirmationMail['html'],
$confirmationMail['subject'],
$batch->getSenderMail(),
$batch->getFromName(),
'html'
);
$application->setHasConfirmationMailSent(true);
$application = $em->getRepository(Application::class)->setApplicationDoneWithoutWorkflow($application);
// Use MailTemplateService for admin notification mail
$adminMail = $this->mailTemplateService->renderAdminNotificationMail(
$batch,
$user,
$application
);
// Send admin notification mail
if ($batch->getRecipientMonitoringMail()) {
Utility::sendDirectMail(
$this->mailer,
$adminMail['subject'],
$application->getEmail(),
'',
$batch->getRecipientMonitoringMail(),
'',
$adminMail['body'],
false
);
}
// sanitize title field
if (!$em->getRepository(Member::class)->isTitleValid($member->getTitle())) {
// Titel sanitizen, z. B. auf null setzen oder einen Standardwert zuweisen
$member->setTitle("") ;
}
// Update member statistics and sync with hubspot
$this->memberHubspotService->updateMemberStatisticsAndSyncWithHubspot($member, false);
// Assuming updateMemberStatistics directly updates and persists the member entity
$em->flush();
$em->refresh($member);
if ($batch->getSkoolWebhook() > ''){
$feedback = $this->callbackService->sendPostRequest($batch->getSkoolWebhook(), $application->getEmail());
}
if ($batch->getZapierWebhook() > ''){
$payload = json_encode($em->getRepository(Event::class)->setPayloadSingleApplicationAndBatch($application, $batch));
$callbackUrl = $batch->getZapierWebhook();
$feedback = $this->callbackService->curl_callback($callbackUrl, $payload);
}
return $feedback;
}
/**
* @Route("/archiv/event/{slug}/", name="event_archiv_show")
*/
public function showAction(Request $request, $slug)
{
if ($slug == '22-rheinland-pitch-mai-2015-2015-04-27-1430300112') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '23-rheinland-pitch-juni-2015-finale-duesseldorf']));
}
if ($slug == 'rheinland-pitch') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '01-rheinland-pitch-april-2013']));
}
if ($slug == 'rheinland-pitch-mai') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '02-rheinland-pitch-mai-2013']));
}
if ($slug == 'rheinland-pitch-juni') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '03-rheinland-pitch-juni-2013']));
}
if ($slug == 'rheinland-pitch-august') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '05-rheinland-pitch-august-2013']));
}
if ($slug == 'rheinland-pitch-september') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '06-rheinland-pitch-september-2013']));
}
if ($slug == 'rheinland-pitch-oktober') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '07-rheinland-pitch-oktober-2013']));
}
if ($slug == 'rheinland-pitch-november') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '08-rheinland-pitch-november-2013']));
}
if ($slug == 'rheinland-pitch-januar') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '09-rheinland-pitch-januar-2014']));
}
if ($slug == 'rheinland-pitch-februar') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '10-rheinland-pitch-februar-2014']));
}
if ($slug == 'rheinland-pitch-marz') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '11-rheinland-pitch-maerz-2014']));
}
if ($slug == 'rheinland-pitch-april') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '12-rheinland-pitch-april-2014']));
}
if ($slug == 'rheinland-pitch-mai-3') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '13-rheinland-pitch-mai-2014']));
}
if ($slug == 'rheinland-pitch-juni-2') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '14-rheinland-pitch-juni-2014']));
}
if ($slug == '22-rheinland-pitch-mai-2015-2015-04-27') {
return $this->redirect($this->generateUrl('event_archiv_show', ['slug' => '22-rheinland-pitch-mai-2015']));
}
$templateVars = $this->getEventComRep()->getStartPlatzEventBaseTemplateVars($request);
if (!$event = $this->getEventRepository()->findOneBy(['slug' => $slug])) {
$querySlug = '/' . $slug;
if (!$event = $this->getEventRepository()->findOneBy(['slug' => $querySlug])) {
$this->session->getFlashBag()->add('notice', 'ERROR Veranstaltung nicht gefunden');
return $this->redirect($this->generateUrl('events_list'));
} else {
$em = $this->getDoctrine()->getManager();
$event->setSlug($slug);
$em->persist($event);
$em->flush();
}
}
if ($event->getStatus() != 'publish') {
if (!$this->getUser()) {
return $this->redirect($this->generateUrl('events_list', $templateVars));
}
}
//previous @StartPlatzEventBundle/Default/show.html.twig
return $this->render('@StartPlatzEventBundle/Default/event-single.html.twig', [
'templateVars' => $templateVars,
'event' => $event,
'batch' => false,
'speakers' => [],
'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
]);
}
/**
*/
public function eventsThisWeekAction()
{
$year = date('Y');
$month = date('m');
$moreFutureEvents = $this->getEventBaseRepository()->getEventsByWeek($this->getEventComRep()->setBaseCriteria([]), $year, $month);
return $this->render('@StartPlatzEventBundle/Default/events_this_week.html.twig', [
'moreFutureEvents' => $moreFutureEvents,
]);
}
private function getRedirects()
{
return [
'Best-Practice-Digitalisierung-Januar-2018' => 'best-practice-digitalisierung',
'best-practice-digitalisierung-3' => 'best-practice-digitalisierung',
'adwords-fuer-einsteiger-' => 'adwords-fuer-einsteiger-koeln-2017-01-24',
'adwords-fur-einsteiger-' => 'adwords-fuer-einsteiger-koeln-2016-06-06',
'adwords-fur-einsteiger-DUS' => 'adwords-fuer-einsteiger-dus-2016-06-07',
'-adwords-fuer-einsteiger' => 'adwords-fur-einsteiger',
'adwords-fur-einsteiger-2016-02-16' => 'adwords-fuer-einsteiger-koeln-2016-02-16',
'digital-networking-week-1611257320' => 'digital-networking-week',
'chatgpt3-how-to-prompt-duesseldorf' => 'chatgpt-how-to-prompt-duesseldorf'
];
}
private function setMonsumAccountIfNotExists(Member $member, Team $team, Application $application, $account)
{
$em = $this->getDoctrine()->getManager();
$adminEmail = $member->getEmail();
$responseContent = [];
$teamId = $team->getId();
$customer = null;
$monsumCustomer = null;
$settings = ['adminEmail' => $adminEmail, 'location' => $em->getRepository(Customer::class)->getLocationByAccount($account), 'account' => $account, 'accountHash' => $em->getRepository(Customer::class)->getAccountHashByAccount($account)];
$login = $em->getRepository(Customer::class)->getLoginByAccount($account);
$payload = json_encode(['SERVICE' => "customer.get", 'FILTER' => ['CUSTOMER_EXT_UID' => "{$team->getId()}"]]);
$feedback = $this->callbackService->curl_get_monsum_all($payload, $login);
$feedback = json_decode((string) $feedback);
if (isset($feedback->RESPONSE->CUSTOMERS[0])) {
$responseContent[] = "Team {$teamId} has already account in Monsum";
$customer = $feedback->RESPONSE->CUSTOMERS[0];
} else {
$responseContent[] = "New account will be created for team {$teamId} in Monsum";
$customerSettings = $em->getRepository(Customer::class)->getCustomerSettingsByTeamAndMember($team, $member);
$payload = json_encode(['SERVICE' => "customer.create", 'DATA' => $customerSettings]);
$feedback = $this->callbackService->curl_get_monsum_all($payload, $login);
$feedback = json_decode((string) $feedback);
$monsumResponse = $feedback->RESPONSE;
if (isset($monsumResponse->ERRORS)) {
$responseContent[] = "ERROR ======== ERROR";
$responseContent[] = $monsumResponse->ERRORS;
$responseContent[] = $feedback;
} else {
$responseContent[] = $monsumResponse;
if ($monsumResponse->STATUS == "success") {
$customerId = $monsumResponse->CUSTOMER_ID;
$payload = $em->getRepository(Customer::class)->getPayloadSingleCustomerByCustomerId($customerId);
$feedback = $this->callbackService->curl_get_monsum_all($payload, $login);
if ($feedback = json_decode((string) $feedback)) {
$customer = $feedback->RESPONSE->CUSTOMERS[0];
} else {
$responseContent[] = "ERROR: could not decode feedback from monsum";
$responseContent[] = $feedback;
}
}
}
}
if ($customer) {
$monsumCustomer = $em->getRepository(Customer::class)->createOrUpdateMonsumCustomerByTeam($customer, $settings, $team);
}
return $monsumCustomer;
}
private function prepareCustomerForMonsumCheckout(Application $application, Product $product, $memberToBeRegistered)
{
// see applyController $this->goToMonsum( ... )
$em = $this->getDoctrine()->getManager();
// save member, assign member to team, save application
$data['email'] = $application->getEmail();
$data['name'] = $application->getPerson();
$adminEmail = $application->getEmail();
if (!$memberToBeRegistered) {
$memberToBeRegistered = $this->createGuestMember($application);
}
$team = $em->getRepository(Team::class)->createNewMonsumAccountTeamByMembershipApplicant($memberToBeRegistered);
$customer = $this->setMonsumAccountIfNotExists($memberToBeRegistered, $team, $application, $product->getAccount());
$productNumber = $product->getProductNumber();
$customer = $em->getRepository(Customer::class)->setCheckoutData($customer, $product->getAccount(), $productNumber, $memberToBeRegistered->getId());
$response = [
'status' => 'success',
'message' => 'accounts created',
'data' => [
'memberToBeRegistered' => $memberToBeRegistered,
'customer' => $customer,
'team' => $team,
],
];
return $response;
}
private function legacyPaymentProcess($application, $isMemberloggedIn, $em, $batch, $member, $event){
if ($application->getRealPriceinEuroCent()){
if ($isMemberloggedIn){
// bei monsum zahlen
if (!$product = $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
$this->session->getFlashBag()->add('notice', 'ERROR configuration not found');
return $this->redirect($this->generateUrl('events_list'));
}
$account = $product->getAccount();
if (!$customer = $em->getRepository(Customer::class)->findPrimaryMonsumAccount($member, $account)) {
$this->session->getFlashBag()->add('notice', 'ERROR monsum customer not found');
return $this->redirect($this->generateUrl('events_list'));
//$team = $em->getRepository(Team::class)->createTeamByMembershipApplicant($member);
//$customer = $this->setMonsumAccountIfNotExists($member, $team, $application, $account);
}
$productNumber = $product->getProductNumber();
$customer = $em->getRepository(Customer::class)->setCheckoutData($customer, $account, $productNumber, $member->getId());
$unitPrice = $application->getRealPriceinEuroCent() / 100;
$login = $em->getRepository(Customer::class)->getLoginByAccount($customer->getAccount());
$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')}"]]);
// Set URL to call back
$callbackUrl = "https://app.monsum.com/api/1.0/api.php";
$feedback = Utility::curl_callback($callbackUrl, $payload, $login);
$feedback = json_decode((string) $feedback);
if (isset($feedback->RESPONSE->STATUS) and $feedback->RESPONSE->STATUS == "success") {
$subscriptionId = $feedback->RESPONSE->SUBSCRIPTION_ID;
$application->setEditStatus("sucess:subscriptionId=={$subscriptionId}");
$application = $em->getRepository(Application::class)->setHistory($application, $application->getEditMessage());
$application->setApplicationStatus('applied');
$em->persist($application);
$em->flush();
} else {
$application->setEditStatus("error:monsum-purchase-failed");
$em->persist($application);
$em->flush();
$action = "error";
}
}
}
}
}