2025-05-22 19:13:43 +02:00
< ? php
namespace App\Controller ;
2025-05-26 11:32:53 +02:00
use App\Entity\Stats ;
use App\Entity\Place ;
2025-05-26 11:55:44 +02:00
use App\Service\Motocultrice ;
2025-05-26 11:32:53 +02:00
use Doctrine\ORM\EntityManagerInterface ;
2025-05-22 19:13:43 +02:00
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ;
use Symfony\Component\HttpFoundation\Response ;
use Symfony\Component\Routing\Annotation\Route ;
use GuzzleHttp\Client ;
use Symfony\Component\HttpFoundation\Request ;
2025-05-28 16:24:34 +02:00
use Symfony\Component\Mime\Email ;
use Symfony\Component\Mailer\MailerInterface ;
2025-05-27 19:13:35 +02:00
2025-05-22 19:13:43 +02:00
class PublicController extends AbstractController
{
2025-05-27 19:13:35 +02:00
private $hide_filled_inputs = true ;
2025-05-26 11:32:53 +02:00
public function __construct (
2025-05-26 11:55:44 +02:00
private EntityManagerInterface $entityManager ,
2025-05-28 16:24:34 +02:00
private Motocultrice $motocultrice ,
private MailerInterface $mailer
2025-05-26 11:32:53 +02:00
) {
}
2025-05-28 16:24:34 +02:00
#[Route('/propose-email/{email}/{type}/{id}', name: 'app_public_propose_email')]
public function proposeEmail ( string $email , string $type , int $id ) : Response
{
$data = $this -> motocultrice -> get_osm_object_data ( $type , $id );
// Récupérer le code postal depuis les tags, sinon mettre -1
$zipCode = isset ( $data [ 'tags_converted' ][ 'addr:postcode' ]) ? ( int ) $data [ 'tags_converted' ][ 'addr:postcode' ] : - 1 ;
$place_name = $data [ 'tags_converted' ][ 'name' ];
// Vérifier si une Place existe déjà avec le même osm_kind et osmId
$existingPlace = $this -> entityManager -> getRepository ( Place :: class ) -> findOneBy ([
'osm_kind' => $type ,
'osmId' => $id
]);
if ( $existingPlace ) {
// Mettre à jour l'email de la Place existante
$existingPlace -> setEmail ( $email ) -> setLastContactAttemptDate ( new \DateTime ());
$this -> entityManager -> flush ();
$debug = '' ;
if ( $this -> getParameter ( 'kernel.environment' ) !== 'prod' ) {
$debug = 'Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this -> generateUrl ( 'app_public_edit' , [
'zipcode' => $zipCode ,
'name' => $place_name ,
'uuid' => $existingPlace -> getUuidForUrl ()
], true );
}
$this -> addFlash ( 'success' , 'L\'email a été mis à jour. Un email vous sera envoyé avec le lien de modification. ' . $debug );
} else {
// Créer une nouvelle entité Place
$place = new Place ();
$place -> setEmail ( $email )
-> setOsmId ( $id )
-> setOsmKind ( $type )
-> setAskedHumainsSupport ( false )
-> setOptedOut ( false )
-> setDead ( false )
-> setNote ( '' )
-> setModifiedDate ( new \DateTime ())
-> setZipCode ( $zipCode )
-> setPlaceCount ( 0 )
-> setLastContactAttemptDate ( new \DateTime ())
-> setUuidForUrl ( uniqid ());
$this -> entityManager -> persist ( $place );
$this -> entityManager -> flush ();
$debug = '' ;
if ( $this -> getParameter ( 'kernel.environment' ) !== 'prod' ) {
$debug = 'Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this -> generateUrl ( 'app_public_edit' , [
'zipcode' => $zipCode ,
'name' => $place_name ,
'uuid' => $place -> getUuidForUrl ()
], true );
}
$this -> addFlash ( 'success' , 'Un email vous sera envoyé avec le lien de modification. ' . $debug );
}
// Envoyer l'email
$destinataire = $this -> getParameter ( 'kernel.environment' ) === 'prod' ? $email : 'contact+essai_osm_commerce@cipherbliss.com' ;
$message = ( new Email ())
-> from ( 'contact@osm-commerce.fr' )
-> to ( $destinataire )
-> subject ( 'Votre lien de modification OpenStreetMap' )
-> text ( 'Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this -> generateUrl ( 'app_public_edit' , [
'zipcode' => $zipCode ,
'name' => $place_name ,
'uuid' => $existingPlace ? $existingPlace -> getUuidForUrl () : $place -> getUuidForUrl ()
], true ));
$this -> mailer -> send ( $message );
return $this -> redirectToRoute ( 'app_public_index' );
}
2025-05-22 19:13:43 +02:00
#[Route('/', name: 'app_public_index')]
public function index () : Response
{
2025-05-28 16:24:34 +02:00
return $this -> render ( 'public/home.html.twig' , [
2025-05-22 19:13:43 +02:00
'controller_name' => 'PublicController' ,
2025-05-28 16:24:34 +02:00
2025-05-22 19:13:43 +02:00
]);
}
2025-05-26 12:57:10 +02:00
#[Route('/edit/{zipcode}/{name}/{uuid}', name: 'app_public_edit')]
public function edit_with_uuid ( $zipcode , $name , $uuid ) : Response
{
$place = $this -> entityManager -> getRepository ( Place :: class ) -> findOneBy ([ 'uuid_for_url' => $uuid ]);
if ( ! $place ) {
2025-05-26 23:51:46 +02:00
$this -> addFlash ( 'warning' , 'Ce lien de modification n\'existe pas.' );
2025-05-26 12:57:10 +02:00
return $this -> redirectToRoute ( 'app_public_index' );
}
2025-05-26 23:51:46 +02:00
if ( $place -> getOsmKind () === 'relation' ) {
$this -> addFlash ( 'warning' , 'Les objets OSM de type "relation" ne sont pas gérés dans cet outil.' );
return $this -> redirectToRoute ( 'app_public_index' );
}
// récupérer les tags de base
$base_tags = $this -> motocultrice -> base_tags ;
$base_tags = array_fill_keys ( $base_tags , '' );
$commerce_overpass = $this -> motocultrice -> get_osm_object_data ( $place -> getOsmKind (), $place -> getOsmId ());
// Fusionner les tags de base avec les tags existants
$commerce_overpass [ 'tags_converted' ] = array_merge ( $base_tags , $commerce_overpass [ 'tags_converted' ]);
// Trier les tags par ordre alphabétique des clés
ksort ( $commerce_overpass [ 'tags_converted' ]);
2025-05-26 12:57:10 +02:00
return $this -> render ( 'public/edit.html.twig' , [
2025-05-26 23:51:46 +02:00
'commerce_overpass' => $commerce_overpass ,
2025-05-26 12:57:10 +02:00
'name' => $name ,
2025-05-26 23:51:46 +02:00
'commerce' => $place ,
2025-05-27 19:13:35 +02:00
'hide_filled_inputs' => $this -> hide_filled_inputs ,
2025-05-27 12:17:46 +02:00
'excluded_tags_to_render' => $this -> motocultrice -> excluded_tags_to_render ,
2025-05-26 16:22:01 +02:00
'osm_kind' => $place -> getOsmKind (),
2025-05-26 13:07:49 +02:00
" mapbox_token " => $_ENV [ 'MAPBOX_TOKEN' ],
" maptiler_token " => $_ENV [ 'MAPTILER_TOKEN' ],
2025-05-26 12:57:10 +02:00
]);
}
2025-05-26 11:32:53 +02:00
#[Route('/dashboard', name: 'app_public_dashboard')]
public function dashboard () : Response
{
// get stats
$stats = $this -> entityManager -> getRepository ( Stats :: class ) -> findAll ();
2025-05-26 23:51:46 +02:00
$places = $this -> entityManager -> getRepository ( Place :: class ) -> findBy ([], [ 'zip_code' => 'ASC' , 'name' => 'ASC' ]);
2025-05-26 11:32:53 +02:00
return $this -> render ( 'public/dashboard.html.twig' , [
'controller_name' => 'PublicController' ,
'stats' => $stats ,
'places' => $places ,
]);
}
2025-05-22 19:13:43 +02:00
#[Route('/modify/{osm_object_id}/{version}/{changesetID}', name: 'app_public_submit')]
public function submit ( $osm_object_id , $version , $changesetID ) : Response
{
// Récupérer les données POST
$request = Request :: createFromGlobals ();
// Vérifier si des données ont été soumises
if ( $request -> isMethod ( 'POST' )) {
$status = " non modifié " ;
2025-05-26 16:22:01 +02:00
// Récupérer le type d'objet (node ou way)
$osm_kind = $request -> request -> get ( 'osm_kind' , 'node' );
2025-05-22 19:13:43 +02:00
// Récupérer tous les tags du formulaire
$tags = [];
2025-05-27 11:18:29 +02:00
$request_post = $request -> request -> all ();
$request_post = $this -> motocultrice -> map_post_values ( $request_post );
foreach ( $request_post as $key => $value ) {
2025-05-27 19:13:35 +02:00
2025-05-22 19:13:43 +02:00
if ( strpos ( $key , 'commerce_tag_value__' ) === 0 ) {
$tagKey = str_replace ( 'commerce_tag_value__' , '' , $key );
if ( ! empty ( $value )) {
2025-05-26 16:22:01 +02:00
// Validation des données selon le type de tag
if ( $tagKey === 'addr:postcode' ) {
// Vérifier que c'est bien un code postal français (5 chiffres)
if ( ! preg_match ( '/^\d{5}$/' , $value )) {
$status = " Erreur : Le code postal doit être composé de 5 chiffres " ;
continue ;
}
}
2025-05-28 16:24:34 +02:00
$tags [ $tagKey ] = trim ( $value );
2025-05-22 19:13:43 +02:00
}
}
}
// Récupérer le token OSM depuis les variables d'environnement
$osm_api_token = $_ENV [ 'APP_OSM_BEARER' ];
2025-05-26 23:51:46 +02:00
$exception = false ;
$exception_message = " " ;
2025-05-22 19:13:43 +02:00
try {
$client = new Client ();
// 1. Créer un nouveau changeset
$changesetXml = new \SimpleXMLElement ( '<?xml version="1.0" encoding="UTF-8"?><osm version="0.6"></osm>' );
$changeset = $changesetXml -> addChild ( 'changeset' );
$tag = $changeset -> addChild ( 'tag' );
$tag -> addAttribute ( 'k' , 'created_by' );
$tag -> addAttribute ( 'v' , 'OSM Mon Commerce Web Editor' );
$tag = $changeset -> addChild ( 'tag' );
$tag -> addAttribute ( 'k' , 'comment' );
2025-05-26 16:22:01 +02:00
$tag -> addAttribute ( 'v' , 'Modification des tags via l\'interface web #MonCommerceOSM' );
2025-05-22 19:13:43 +02:00
$changesetResponse = $client -> put ( 'https://api.openstreetmap.org/api/0.6/changeset/create' , [
'body' => $changesetXml -> asXML (),
'headers' => [
'Authorization' => 'Bearer ' . $osm_api_token ,
'Content-Type' => 'application/xml'
]
]);
$newChangesetId = $changesetResponse -> getBody () -> getContents ();
2025-05-26 16:22:01 +02:00
// Récupérer les données actuelles de l'objet
$currentObjectData = $this -> motocultrice -> get_osm_object_data ( $osm_kind , $osm_object_id );
// 2. Modifier l'objet avec le nouveau changeset
2025-05-22 19:13:43 +02:00
$xml = new \SimpleXMLElement ( '<?xml version="1.0" encoding="UTF-8"?><osm version="0.6"></osm>' );
2025-05-26 16:22:01 +02:00
$object = $xml -> addChild ( $osm_kind );
$object -> addAttribute ( 'id' , $osm_object_id );
$object -> addAttribute ( 'version' , $version );
$object -> addAttribute ( 'changeset' , $newChangesetId );
// Ajouter les coordonnées pour les nodes
if ( $osm_kind === 'node' ) {
if ( ! isset ( $currentObjectData [ '@attributes' ][ 'lat' ]) || ! isset ( $currentObjectData [ '@attributes' ][ 'lon' ])) {
throw new \Exception ( " Impossible de récupérer les coordonnées du nœud " );
}
$object -> addAttribute ( 'lat' , $currentObjectData [ '@attributes' ][ 'lat' ]);
$object -> addAttribute ( 'lon' , $currentObjectData [ '@attributes' ][ 'lon' ]);
}
2025-05-22 19:13:43 +02:00
// Ajouter les tags
foreach ( $tags as $key => $value ) {
if ( ! empty ( $key ) && ! empty ( $value )) {
2025-05-26 16:22:01 +02:00
$tag = $object -> addChild ( 'tag' );
2025-05-22 19:13:43 +02:00
$tag -> addAttribute ( 'k' , htmlspecialchars ( $key , ENT_XML1 ));
$tag -> addAttribute ( 'v' , htmlspecialchars ( $value , ENT_XML1 ));
}
}
// Debug du XML généré
$xmlString = $xml -> asXML ();
2025-05-26 16:22:01 +02:00
// echo('xml : <br>'.$xmlString);
2025-05-22 19:13:43 +02:00
2025-05-26 16:22:01 +02:00
$response = $client -> put ( " https://api.openstreetmap.org/api/0.6/ { $osm_kind } / " . $osm_object_id , [
2025-05-22 19:13:43 +02:00
'body' => $xmlString ,
'headers' => [
'Authorization' => 'Bearer ' . $osm_api_token ,
'Content-Type' => 'application/xml'
]
]);
// 3. Fermer le changeset
$client -> put ( 'https://api.openstreetmap.org/api/0.6/changeset/' . $newChangesetId . '/close' , [
'headers' => [
'Authorization' => 'Bearer ' . $osm_api_token
]
]);
if ( $response -> getStatusCode () === 200 ) {
$status = " Les tags ont été mis à jour avec succès " ;
} else {
$status = " Erreur lors de la mise à jour des tags " ;
}
} catch ( \Exception $e ) {
$status = " Erreur lors de la communication avec l'API OSM: " . $e -> getMessage ();
2025-05-26 23:51:46 +02:00
$exception = true ;
$exception_message = $e -> getMessage ();
2025-05-22 19:13:43 +02:00
// Debug de la réponse en cas d'erreur
if ( method_exists ( $e , 'getResponse' )) {
var_dump ( $e -> getResponse () -> getBody () -> getContents ());
}
}
}
// après envoi on récupère les données
2025-05-26 16:22:01 +02:00
$commerce = $this -> motocultrice -> get_osm_object_data ( $osm_kind , $osm_object_id );
2025-05-27 19:13:35 +02:00
2025-05-22 19:13:43 +02:00
return $this -> render ( 'public/view.html.twig' , [
'controller_name' => 'PublicController' ,
'commerce' => $commerce ,
2025-05-27 19:13:35 +02:00
2025-05-22 19:13:43 +02:00
'status' => $status ,
2025-05-26 23:51:46 +02:00
'exception' => $exception ,
'exception_message' => $exception_message ,
2025-05-26 13:07:49 +02:00
'mapbox_token' => $_ENV [ 'MAPBOX_TOKEN' ],
'maptiler_token' => $_ENV [ 'MAPTILER_TOKEN' ],
2025-05-22 19:13:43 +02:00
]);
}
#[Route('/request_email_to_modify/{osm_object_id}', name: 'app_public_request_email')]
public function request_email ( $osm_object_id ) : Response
{
2025-05-27 00:05:10 +02:00
if ( $this -> getRequest () -> isMethod ( 'POST' )) {
$email = $this -> getRequest () -> request -> get ( 'email' );
try {
// TODO: Implémenter l'envoi réel du mail
$this -> addFlash (
'success' ,
'Un email vous a été envoyé avec les instructions pour modifier ce lieu.'
);
} catch ( \Exception $e ) {
$this -> addFlash (
'error' ,
'Une erreur est survenue lors de l\'envoi de l\'email. Veuillez réessayer plus tard.'
);
}
return $this -> redirectToRoute ( 'app_public_index' );
}
2025-05-22 19:13:43 +02:00
// TODO envoyer un email
return $this -> render ( 'public/request_email.html.twig' , [
'controller_name' => 'PublicController' ,
'commerce_id' => $osm_object_id ,
]);
}
}