diff --git a/src/CoreBundle/Controller/ValidationTokenController.php b/src/CoreBundle/Controller/ValidationTokenController.php
new file mode 100644
index 00000000000..c18f81000c0
--- /dev/null
+++ b/src/CoreBundle/Controller/ValidationTokenController.php
@@ -0,0 +1,119 @@
+tokenRepository->findOneBy([
+ 'type' => $this->validationTokenHelper->getTypeId($type),
+ 'hash' => $hash
+ ]);
+
+ if (!$token) {
+ throw $this->createNotFoundException('Invalid token.');
+ }
+
+ // Process the action related to the token type
+ $this->processAction($token);
+
+ // Remove the used token
+ $this->tokenRepository->remove($token, true);
+
+ // Register the token usage event
+ $this->registerTokenUsedEvent($token);
+
+ return $this->render('@ChamiloCore/Validation/success.html.twig', [
+ 'type' => $type,
+ ]);
+ }
+
+ #[Route('/test/generate-token/{type}/{resourceId}', name: 'test_generate_token')]
+ public function testGenerateToken(string $type, int $resourceId): Response
+ {
+ $typeId = $this->validationTokenHelper->getTypeId($type);
+ $token = new ValidationToken($typeId, $resourceId);
+ $this->tokenRepository->save($token, true);
+
+ $validationLink = $this->generateUrl('validate_token', [
+ 'type' => $type,
+ 'hash' => $token->getHash(),
+ ], \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL);
+
+ return new Response("Generated token: {$token->getHash()}
Validation link: {$validationLink}");
+ }
+
+ private function processAction(ValidationToken $token): void
+ {
+ switch ($token->getType()) {
+ case 1: // Assuming 1 is for 'ticket'
+ $this->processTicketValidation($token);
+ break;
+ case 2: // Assuming 2 is for 'user'
+ // Implement user validation logic here
+ break;
+ default:
+ throw new \InvalidArgumentException('Unrecognized token type');
+ }
+ }
+
+ private function processTicketValidation(ValidationToken $token): void
+ {
+ $ticketId = $token->getResourceId();
+
+ // Simulate ticket validation logic
+ // Here you would typically check if the ticket exists and is valid
+ // For now, we'll just print a message to simulate this
+ // Replace this with your actual ticket validation logic
+ $ticketValid = $this->validateTicket($ticketId);
+
+ if (!$ticketValid) {
+ throw new \RuntimeException('Invalid ticket.');
+ }
+
+ // If the ticket is valid, you can mark it as used or perform other actions
+ // For example, update the ticket status in the database
+ // $this->ticketRepository->markAsUsed($ticketId);
+ }
+
+ private function validateTicket(int $ticketId): bool
+ {
+ // Here you would implement the logic to check if the ticket is valid.
+ // This is a placeholder function to simulate validation.
+
+ // For testing purposes, let's assume all tickets are valid.
+ // In a real implementation, you would query your database or service.
+
+ return true; // Assume the ticket is valid for now
+ }
+
+ private function registerTokenUsedEvent(ValidationToken $token): void
+ {
+ $user = $this->security->getUser();
+ $userId = $user?->getId();
+ $this->trackEDefaultRepository->registerTokenUsedEvent($token, $userId);
+ }
+}
diff --git a/src/CoreBundle/Entity/TrackEDefault.php b/src/CoreBundle/Entity/TrackEDefault.php
index d3e69f00700..43a372bd432 100644
--- a/src/CoreBundle/Entity/TrackEDefault.php
+++ b/src/CoreBundle/Entity/TrackEDefault.php
@@ -68,7 +68,7 @@ public function getDefaultUserId()
return $this->defaultUserId;
}
- public function setCId(int $cId): self
+ public function setCId(?int $cId): self
{
$this->cId = $cId;
@@ -153,7 +153,7 @@ public function getDefaultValue()
return $this->defaultValue;
}
- public function setSessionId(int $sessionId): self
+ public function setSessionId(?int $sessionId): self
{
$this->sessionId = $sessionId;
diff --git a/src/CoreBundle/Entity/ValidationToken.php b/src/CoreBundle/Entity/ValidationToken.php
new file mode 100644
index 00000000000..f2c930a5397
--- /dev/null
+++ b/src/CoreBundle/Entity/ValidationToken.php
@@ -0,0 +1,96 @@
+type = $type;
+ $this->resourceId = $resourceId;
+ $this->hash = hash('sha256', uniqid((string) rand(), true));
+ $this->createdAt = new \DateTime();
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getType(): int
+ {
+ return $this->type;
+ }
+
+ public function setType(int $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function getResourceId(): int
+ {
+ return $this->resourceId;
+ }
+
+ public function setResourceId(int $resourceId): self
+ {
+ $this->resourceId = $resourceId;
+ return $this;
+ }
+
+ public function getHash(): string
+ {
+ return $this->hash;
+ }
+
+ public function getCreatedAt(): \DateTime
+ {
+ return $this->createdAt;
+ }
+
+ public function setCreatedAt(\DateTime $createdAt): self
+ {
+ $this->createdAt = $createdAt;
+ return $this;
+ }
+
+ /**
+ * Genera un enlace de validaciĆ³n.
+ */
+ public static function generateLink(int $type, int $resourceId): string
+ {
+ $token = new self($type, $resourceId);
+ return '/validate/' . $type . '/' . $token->getHash();
+ }
+}
diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20241211183300.php b/src/CoreBundle/Migrations/Schema/V200/Version20241211183300.php
new file mode 100644
index 00000000000..4b9ad08f5a0
--- /dev/null
+++ b/src/CoreBundle/Migrations/Schema/V200/Version20241211183300.php
@@ -0,0 +1,42 @@
+hasTable('validation_token')) {
+ $this->addSql("
+ CREATE TABLE validation_token (
+ id INT AUTO_INCREMENT NOT NULL,
+ type INT NOT NULL,
+ resource_id BIGINT NOT NULL,
+ hash CHAR(64) NOT NULL,
+ created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)',
+ INDEX idx_type_hash (type, hash),
+ PRIMARY KEY(id)
+ ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC
+ ");
+ }
+ }
+
+ public function down(Schema $schema): void
+ {
+ if ($schema->hasTable('validation_token')) {
+ $this->addSql('DROP TABLE validation_token');
+ }
+ }
+}
diff --git a/src/CoreBundle/Repository/TrackEDefaultRepository.php b/src/CoreBundle/Repository/TrackEDefaultRepository.php
index 0a10a41bbe7..bfaecf3b056 100644
--- a/src/CoreBundle/Repository/TrackEDefaultRepository.php
+++ b/src/CoreBundle/Repository/TrackEDefaultRepository.php
@@ -7,6 +7,7 @@
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\TrackEDefault;
+use Chamilo\CoreBundle\Entity\ValidationToken;
use DateTime;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
@@ -65,4 +66,22 @@ public function getUserCourseRegistrationAt(int $courseId, int $userId, ?int $se
return null;
}
+
+ /**
+ * Registers an event when a validation token is used.
+ */
+ public function registerTokenUsedEvent(ValidationToken $token, ?int $userId = null): void
+ {
+ $event = new TrackEDefault();
+ $event->setDefaultUserId($userId ?? 0);
+ $event->setCId(null);
+ $event->setDefaultDate(new \DateTime());
+ $event->setDefaultEventType('VALIDATION_TOKEN_USED');
+ $event->setDefaultValueType('validation_token');
+ $event->setDefaultValue(\json_encode(['hash' => $token->getHash()]));
+ $event->setSessionId(null);
+
+ $this->_em->persist($event);
+ $this->_em->flush();
+ }
}
diff --git a/src/CoreBundle/Repository/ValidationTokenRepository.php b/src/CoreBundle/Repository/ValidationTokenRepository.php
new file mode 100644
index 00000000000..9edb35630f0
--- /dev/null
+++ b/src/CoreBundle/Repository/ValidationTokenRepository.php
@@ -0,0 +1,37 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(ValidationToken $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/CoreBundle/Resources/views/Validation/success.html.twig b/src/CoreBundle/Resources/views/Validation/success.html.twig
new file mode 100644
index 00000000000..7b9aded6f11
--- /dev/null
+++ b/src/CoreBundle/Resources/views/Validation/success.html.twig
@@ -0,0 +1,6 @@
+{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
+
+{% block content %}
+
The token for {{ type }} has been successfully validated.
+{% endblock %} diff --git a/src/CoreBundle/ServiceHelper/ValidationTokenHelper.php b/src/CoreBundle/ServiceHelper/ValidationTokenHelper.php new file mode 100644 index 00000000000..b04549a61af --- /dev/null +++ b/src/CoreBundle/ServiceHelper/ValidationTokenHelper.php @@ -0,0 +1,48 @@ +tokenRepository->save($token, true); + + return $this->urlGenerator->generate('validate_token', [ + 'type' => $this->getTypeString($type), + 'hash' => $token->getHash(), + ], UrlGeneratorInterface::ABSOLUTE_URL); + } + + public function getTypeId(string $type): int + { + return match ($type) { + 'ticket' => 1, + 'user' => 2, + default => throw new \InvalidArgumentException('Unrecognized validation type'), + }; + } + + private function getTypeString(int $type): string + { + return match ($type) { + 1 => 'ticket', + 2 => 'user', + default => throw new \InvalidArgumentException('Unrecognized validation type'), + }; + } +}