Sfoglia il codice sorgente

Configures software website and FAQ URLs

Configures the software website URL for each environment.
Adds FAQ URL to the shop service for trial requests.
Uses bound parameters in Dolibarr SQL queries to prevent injection vulnerabilities.
Implements ShopRequestData interface for ShopRequest classes.
Ensures reserved subdomains are correctly validated.
Olivier Massot 5 mesi fa
parent
commit
5731d36d66

+ 2 - 0
.env

@@ -116,3 +116,5 @@ BLACKFIRE_CLIENT_TOKEN=8cfbeb263d044da9678dc2612531504da3790c308da7448e35724a5da
 BLACKFIRE_SERVER_ID=1171e53b-459b-41da-a292-80ff68cee8c2
 BLACKFIRE_SERVER_TOKEN=dbd1cfbea015fe83cccfc189a36ca3c16f3a1b43b94f50032a15e41e53548e8b
 ###< BlackFire configuration ###
+
+FAQ_URL=https://ressources.opentalent.fr/space/FAQ/2496592/Tutoriels+vid%C3%A9os

+ 2 - 0
config/services.yaml

@@ -25,8 +25,10 @@ services:
             $publicLegacyBaseUrl: '%env(PUBLIC_API_LEG_BASE_URL)%'
             $baseUrl: '%env(API_BASE_URL)%'
             $publicBaseUrl: '%env(PUBLIC_API_BASE_URL)%'
+            $softwareWebsiteUrl: '%env(SOFTWARE_WEBSITE_URL)%'
             $opentalentMailReport: 'mail.report@opentalent.fr'
             $fileStorageDir: '%kernel.project_dir%/var/files/storage'
+            $faqUrl: '%env(FAQ_URL)%'
 
     # makes classes in src/ available to be used as services
     # this creates a service per class whose id is the fully-qualified class name

+ 4 - 0
env/.env.docker

@@ -22,6 +22,10 @@ PUBLIC_API_BASE_URL=https://local.ap2i.opentalent.fr
 TYPO3_BASE_URI=http://docker.sub.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://local.logiciels.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.prod

@@ -15,6 +15,10 @@ PUBLIC_API_BASE_URL=https://ap2i.opentalent.fr/api
 TYPO3_BASE_URI=http://ohcluses.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.opentalent.fr
+###
+
 ###> dolibarr client ###
 DOLIBARR_API_BASE_URI=https://prod-erp.2iopenservice.com/api/index.php/
 ###< dolibarr client ###

+ 4 - 0
env/.env.staging

@@ -27,6 +27,10 @@ ELASTICSEARCH_PORT=9200
 TYPO3_BASE_URI=https://none
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=none
+###
+
 ### Internal requests (@see doc/internal_requests.md)
 INTERNAL_FILES_DOWNLOAD_URI=https://none
 ###

+ 4 - 0
env/.env.test

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test1

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test1.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test1.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test1.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test2

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test2.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test2.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test2.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test3

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test3.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test3.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test3.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test4

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test4.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test4.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test4.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test5

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test5.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test5.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test5.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test6

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test6.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test6.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test6.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test7

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test7.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test7.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test7.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test8

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test8.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test8.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test8.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 4 - 0
env/.env.test9

@@ -17,6 +17,10 @@ PUBLIC_API_BASE_URL=https://ap2i.test9.opentalent.fr/api
 TYPO3_BASE_URI=https://sub.test9.opentalent.fr
 ###< typo3 client ###
 
+### Site logiciels
+SOFTWARE_WEBSITE_URL=https://logiciels.test9.opentalent.fr
+###
+
 ###> symfony/mercure-bundle ###
 # See https://symfony.com/doc/current/mercure.html#configuration
 # The URL of the Mercure hub, used by the app to publish updates (can be a local URL)

+ 1 - 1
src/ApiResources/Shop/NewStructureArtistPremiumTrialRequest.php

@@ -31,7 +31,7 @@ use Symfony\Component\Validator\Constraints as Assert;
         ),
     ]
 )]
-class NewStructureArtistPremiumTrialRequest
+class NewStructureArtistPremiumTrialRequest implements ShopRequestData
 {
     /**
      * Id 'bidon' ajouté par défaut pour permettre la construction

+ 9 - 0
src/ApiResources/Shop/ShopRequestData.php

@@ -0,0 +1,9 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Shop;
+
+interface ShopRequestData
+{
+
+}

+ 22 - 9
src/Service/Dolibarr/DolibarrUtils.php

@@ -59,11 +59,13 @@ class DolibarrUtils
     /**
      * Exécute une requête SQL sur la DB Dolibarr.
      *
+     * @param string $sql The SQL query to execute
+     * @param array $params The parameters to bind to the query
      * @throws \Doctrine\DBAL\Exception
      */
-    protected function executeQuery(string $sql): void
+    protected function executeQuery(string $sql, array $params = []): void
     {
-        $this->dolibarrConnection->executeQuery($sql);
+        $this->dolibarrConnection->executeQuery($sql, $params);
     }
 
     /**
@@ -79,12 +81,13 @@ class DolibarrUtils
         $apiUserId = 8;
 
         $this->executeQuery(
-            "DELETE FROM llx_societe_commerciaux WHERE fk_soc = $societyId"
+            "DELETE FROM llx_societe_commerciaux WHERE fk_soc = ?",
+            [$societyId]
         );
 
         $this->executeQuery(
-            "INSERT INTO llx_societe_commerciaux (fk_soc, fk_user) 
-                   VALUES ($societyId, $apiUserId)"
+            "INSERT INTO llx_societe_commerciaux (fk_soc, fk_user) VALUES (?, ?)",
+            [$societyId, $apiUserId]
         );
     }
 
@@ -100,10 +103,20 @@ class DolibarrUtils
         $now = DatesUtils::new('now', $tz)->format('Y-m-d H:i:s');
         $apiUserId = 8;
 
-        $this->executeQuery(
-            "INSERT INTO llx_actioncomm (fk_soc, ref, code, label, note, datep, datep2, datec, fk_user_author, fk_user_mod, fk_user_action, percent) 
-                   VALUES ($societyId, -1, 'AC_OT_ONLINE_STORE', '$title', '$message', '$now', '$now', '$now', $apiUserId, $apiUserId, $apiUserId, -1)"
-        );
+        $sql = "INSERT INTO llx_actioncomm (fk_soc, ref, code, label, note, datep, datep2, datec, fk_user_author, fk_user_mod, fk_user_action, percent) 
+                   VALUES (?, -1, 'AC_OT_ONLINE_STORE', ?, ?, ?, ?, ?, ?, ?, ?, -1)";
+
+        $this->executeQuery($sql, [
+            $societyId,
+            $title,
+            $message,
+            $now,
+            $now,
+            $now,
+            $apiUserId,
+            $apiUserId,
+            $apiUserId
+        ]);
     }
 
     /**

+ 17 - 10
src/Service/Shop/ShopService.php

@@ -56,6 +56,8 @@ class ShopService
         private LoggerInterface $logger,
         private MessageBusInterface $messageBus,
         private Trial $trial,
+        private string $faqUrl,
+        private readonly string $softwareWebsiteUrl
     ) {
         $this->phoneNumberUtil = PhoneNumberUtil::getInstance();
     }
@@ -84,13 +86,11 @@ class ShopService
      *
      * @param array<string, mixed> $data
      */
-    protected function controlShopRequestData(ShopRequestType $type, NewStructureArtistPremiumTrialRequest|array $data): void
+    protected function controlShopRequestData(ShopRequestType $type, array $data): void
     {
         // @phpstan-ignore-next-line identical.alwaysTrue
         if ($type === ShopRequestType::NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL) {
-            /** @var NewStructureArtistPremiumTrialRequest $request */
-            $request = $data;
-            $this->validateNewStructureArtistPremiumTrialRequest($request);
+            $this->validateNewStructureArtistPremiumTrialRequest($data);
         } else {
             throw new \RuntimeException('request type not supported');
         }
@@ -145,8 +145,9 @@ class ShopService
     protected function sendRequestValidationLink(ShopRequest $shopRequest): void
     {
         $validationUrl = UrlBuilder::concat(
-            $this->publicBaseUrl,
-            ['/api/public/shop/validate', $shopRequest->getToken()]
+            $this->softwareWebsiteUrl,
+            ['/shop/try/validation'],
+            ['token' => $shopRequest->getToken()]
         );
 
         $data = $shopRequest->getData();
@@ -231,15 +232,21 @@ class ShopService
      * Vérifie la validité d'une requête d'essai artist premium pour une nouvelle structure.
      */
     protected function validateNewStructureArtistPremiumTrialRequest(
-        NewStructureArtistPremiumTrialRequest $request,
+        array $data,
     ): void {
+        $trialRequestObj = $this->serializer->deserialize(
+            json_encode($data),
+            NewStructureArtistPremiumTrialRequest::class,
+            'json'
+        );
+
         // Validate phone number
-        if (!$this->phoneNumberUtil->isPossibleNumber($request->getRepresentativePhone())) {
+        if (!$this->phoneNumberUtil->isPossibleNumber($trialRequestObj->getRepresentativePhone())) {
             throw new \RuntimeException('Invalid phone number');
         }
 
         // Check if organization already exists
-        $organizationCreationRequest = $this->createOrganizationCreationRequestFromTrialRequest($request);
+        $organizationCreationRequest = $this->createOrganizationCreationRequestFromTrialRequest($trialRequestObj);
         $this->organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
     }
 
@@ -312,7 +319,7 @@ class ShopService
         $model
             ->setTrialRequest($trialRequest)
             ->setAccountCreationUrl(UrlBuilder::concat($this->publicBaseUrl, ['/account/create']))
-            ->setFaqUrl(UrlBuilder::concat($this->publicBaseUrl, ['/faq/opentalent-artist']))
+            ->setFaqUrl($this->faqUrl)
             ->setSenderId(AccessIdsEnum::ADMIN_2IOPENSERVICE->value);
 
         // Send the email to the representative

+ 1 - 1
src/Service/Typo3/SubdomainService.php

@@ -77,7 +77,7 @@ class SubdomainService
     {
         $reservedSubdomains = $this->parameterBag->get('opentalent.subdomains')['reserved'];
         $subRegexes = array_map(
-            function (string $s) { return '(\b'.trim($s, '^$/\s').'\b)'; },
+            function (string $s) { return '(^'.trim($s, '^$/\s').'$)'; },
             $reservedSubdomains
         );
 

+ 0 - 1
src/State/Provider/Shop/ShopRequestProvider.php

@@ -61,7 +61,6 @@ final class ShopRequestProvider implements ProviderInterface
         $this->shopService->processShopRequest($shopRequest);
 
         // Return a success response
-        // TODO: redirect to a confirmation page
         return new Response('Request validated successfully', Response::HTTP_OK);
     }
 }

+ 2 - 2
templates/emails/shop/new-structure-artist-premium-trial-sales-admin.html.twig

@@ -39,11 +39,11 @@
         </tr>
         <tr>
             <td style="border: 1px solid #ddd; padding: 8px;">Type de structure</td>
-            <td style="border: 1px solid #ddd; padding: 8px;">{{ trialRequest.structureType }}</td>
+            <td style="border: 1px solid #ddd; padding: 8px;">{{ trialRequest.structureType.value }}</td>
         </tr>
         <tr style="background-color: #f2f2f2;">
             <td style="border: 1px solid #ddd; padding: 8px;">Statut juridique</td>
-            <td style="border: 1px solid #ddd; padding: 8px;">{{ trialRequest.legalStatus }}</td>
+            <td style="border: 1px solid #ddd; padding: 8px;">{{ trialRequest.legalStatus.value }}</td>
         </tr>
         <tr>
             <td style="border: 1px solid #ddd; padding: 8px;">Identifiant de la structure</td>