up edit form

This commit is contained in:
Tykayn 2025-05-26 16:22:01 +02:00 committed by tykayn
parent fd72a1cedc
commit b1965abe06
6 changed files with 325 additions and 49 deletions

View file

@ -8,7 +8,7 @@ framework:
default_locale: 'fr'
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks: ['fr']
fallbacks: ['en']
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.

View file

@ -46,6 +46,7 @@ class PublicController extends AbstractController
return $this->render('public/edit.html.twig', [
'commerce' => $commerce,
'name' => $name,
'osm_kind' => $place->getOsmKind(),
"mapbox_token" => $_ENV['MAPBOX_TOKEN'],
"maptiler_token" => $_ENV['MAPTILER_TOKEN'],
]);
@ -74,14 +75,31 @@ class PublicController extends AbstractController
if ($request->isMethod('POST')) {
$status = "non modifié";
// Récupérer le type d'objet (node ou way)
$osm_kind = $request->request->get('osm_kind', 'node');
// Récupérer tous les tags du formulaire
$tags = [];
foreach ($request->request->all() as $key => $value) {
var_dump($key, $value);
if (strpos($key, 'commerce_tag_value__') === 0) {
$tagKey = str_replace('commerce_tag_value__', '', $key);
if (!empty($value)) {
// 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;
}
} elseif ($tagKey === 'contact:phone' || $tagKey === 'phone') {
// Nettoyer le numéro de téléphone
$value = preg_replace('/[^0-9+]/', '', $value);
} elseif (strpos($value, 'http://') === 0) {
$value = str_replace('http://', 'https://', $value);
}
$tags[$tagKey] = $value;
}
}
@ -102,7 +120,7 @@ class PublicController extends AbstractController
$tag = $changeset->addChild('tag');
$tag->addAttribute('k', 'comment');
$tag->addAttribute('v', 'Modification des tags via l\'interface web');
$tag->addAttribute('v', 'Modification des tags via l\'interface web #MonCommerceOSM');
$changesetResponse = $client->put('https://api.openstreetmap.org/api/0.6/changeset/create', [
'body' => $changesetXml->asXML(),
@ -114,19 +132,29 @@ class PublicController extends AbstractController
$newChangesetId = $changesetResponse->getBody()->getContents();
// 2. Modifier le nœud avec le nouveau changeset
// 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
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><osm version="0.6"></osm>');
$node = $xml->addChild('node');
$node->addAttribute('id', $osm_object_id);
$node->addAttribute('version', $version);
$node->addAttribute('changeset', $newChangesetId);
$node->addAttribute('lat', '49.6504926');
$node->addAttribute('lon', '-1.5722526');
$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']);
}
// Ajouter les tags
foreach ($tags as $key => $value) {
if (!empty($key) && !empty($value)) {
$tag = $node->addChild('tag');
$tag = $object->addChild('tag');
$tag->addAttribute('k', htmlspecialchars($key, ENT_XML1));
$tag->addAttribute('v', htmlspecialchars($value, ENT_XML1));
}
@ -134,9 +162,9 @@ class PublicController extends AbstractController
// Debug du XML généré
$xmlString = $xml->asXML();
var_dump($xmlString);
// echo('xml : <br>'.$xmlString);
$response = $client->put('https://api.openstreetmap.org/api/0.6/node/' . $osm_object_id, [
$response = $client->put("https://api.openstreetmap.org/api/0.6/{$osm_kind}/" . $osm_object_id, [
'body' => $xmlString,
'headers' => [
'Authorization' => 'Bearer ' . $osm_api_token,
@ -166,7 +194,7 @@ class PublicController extends AbstractController
}
// après envoi on récupère les données
$commerce = $this->motocultrice->get_osm_object_data($osm_object_id);
$commerce = $this->motocultrice->get_osm_object_data($osm_kind, $osm_object_id);
return $this->render('public/view.html.twig', [
'controller_name' => 'PublicController',
'commerce' => $commerce,

View file

@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManagerInterface;
class Motocultrice
{
private $overpassApiUrl = 'https://overpass-api.de/api/interpreter';
private $osmApiUrl = 'https://www.openstreetmap.org/api/0.6';
public function __construct(
private HttpClientInterface $client,
@ -141,4 +142,98 @@ QUERY;
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
}
public function formatOsmDataForSubmit(array $data): array
{
// Garder uniquement les tags essentiels
$essentialTags = [
'name',
'opening_hours',
'phone',
'contact:email',
'contact:phone',
'website',
'contact:website',
'wheelchair',
'addr:housenumber',
'addr:street',
'addr:city',
'addr:postcode',
'amenity',
'shop',
'tourism',
'source',
'ref:FR:SIRET'
];
$formattedData = [
'node' => [
'@attributes' => [
'id' => $data['@attributes']['id'],
'version' => $data['@attributes']['version'],
'changeset' => $data['@attributes']['changeset'],
'lat' => $data['@attributes']['lat'],
'lon' => $data['@attributes']['lon']
],
'tag' => []
]
];
// Filtrer et ajouter uniquement les tags essentiels
if (isset($data['tag'])) {
foreach ($data['tag'] as $tag) {
if (in_array($tag['@attributes']['k'], $essentialTags)) {
$formattedData['node']['tag'][] = $tag;
}
}
}
return $formattedData;
}
private function arrayToXml(array $data): string
{
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><osm></osm>');
if (isset($data['node'])) {
$node = $xml->addChild('node');
foreach ($data['node']['@attributes'] as $key => $value) {
$node->addAttribute($key, $value);
}
if (isset($data['node']['tag'])) {
foreach ($data['node']['tag'] as $tag) {
$tagElement = $node->addChild('tag');
$tagElement->addAttribute('k', $tag['@attributes']['k']);
$tagElement->addAttribute('v', $tag['@attributes']['v']);
}
}
}
return $xml->asXML();
}
public function submitOsmData(array $data): void
{
$formattedData = $this->formatOsmDataForSubmit($data);
$xmlData = $this->arrayToXml($formattedData);
try {
$response = $this->client->request('PUT',
"{$this->osmApiUrl}/node/{$data['@attributes']['id']}",
[
'body' => $xmlData,
'headers' => [
'Content-Type' => 'application/xml; charset=utf-8'
]
]
);
if ($response->getStatusCode() !== 200) {
throw new \Exception("Erreur lors de la soumission des données : " . $response->getContent());
}
} catch (\Exception $e) {
throw new \Exception("Erreur lors de la communication avec l'API OSM : " . $e->getMessage());
}
}
}

View file

@ -9,6 +9,10 @@
.hidden {
display: none;
}
input[type="checkbox"] {
width: 20px;
height: 20px;
}
</style>
{% endblock %}
@ -54,6 +58,7 @@
</div>
<form action="{{ path('app_public_submit', {'osm_object_id': commerce['@attributes'].id, 'version': commerce['@attributes'].version, 'changesetID': commerce['@attributes'].changeset }) }}" method="post" class="needs-validation">
<input type="hidden" name="osm_kind" value="{{ osm_kind }}">
<div class="mb-3">
<label for="commerce_id" class="form-label">{{ 'display.modify_commerce'|trans }}:
<strong>{{ commerce.tags_converted.name }}</strong>
@ -64,14 +69,125 @@
</div>
{% if commerce.tags_converted.image %}
{% if commerce.tags_converted.image is defined %}
<img src="{{ commerce.tags_converted.image }}" alt="Image du lieu" class="img-fluid mb-3">
{% endif %}
<h2>{{ 'display.tags'|trans }}</h2>
<fieldset>
{% for attributes in commerce.tag %}
{% for kv in attributes %}
<h2>{{ 'display.attributes'|trans }}</h2>
<div class="row mb-3">
<div class="col-md-5">
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__name">{{'display.keys.name'|trans}}</label>
</div>
<div class="col-md-5">
<input type="text" class="form-control" name="commerce_tag_value__name" value=" {% if commerce.tags_converted.name is defined %}{{ commerce.tags_converted.name }}{% endif %}">
</div>
</div>
{# cas d'une mairie
#}
{% if commerce.tags_converted.amenity is defined %}
{% if commerce.tags_converted.amenity == 'townhall' %}
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__building">{{'display.keys.building'|trans}}</label>
<select class="form-control" placeholder="{{'display.please_select'|trans}}" name="commerce_tag_value__building">
<option value="">{{'display.please_select'|trans}}</option>
<option value="main">{{'display.values.building.main'|trans}}</option>
<option value="annex">{{'display.values.building.annex'|trans}}</option>
</select>
</div>
</div>
{% endif %}
{% if commerce.tags_converted.amenity == 'restaurant' %}
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__cuisine">{{'display.keys.cuisine'|trans}}</label>
</div>
<div class="col-md-5">
<input type="text" class="form-control" name="commerce_tag_value__cuisine"
placeholder="française;italienne;japonaise"
value="{% if commerce.tags_converted.cuisine is defined %}{{ commerce.tags_converted.cuisine }}{% endif %}">
<small class="form-text text-muted">{{'display.help.cuisine'|trans}}</small>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
<div id="ask_angela">
<h2>{{ 'display.ask_angela'|trans }}</h2>
<p class="description">{{ 'display.ask_angela_description'|trans }}</p>
<p class="more-info">
<a href="https://www.openstreetmap.fr/contact/" target="_blank">{{ 'display.ask_angela_more_info'|trans }}</a>
</p>
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__angela">{{'display.ask_angela'|trans}}</label>
<input type="checkbox" name="commerce_tag_value__angela" value="yes">
</div>
</div>
</div>
<div id="wheelchair">
<h2>{{ 'display.wheelchair'|trans }}</h2>
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__wheelchair">{{'display.keys.wheelchair'|trans}}</label>
<select class="form-control" placeholder="{{'display.please_select'|trans}}" name="commerce_tag_value__wheelchair" value="{% if commerce.tags_converted.wheelchair is defined %}{{ commerce.tags_converted.wheelchair }}{% endif %}">
<option value="">{{'display.please_select'|trans}}</option>
<option value="yes">{{'display.values.wheelchair.yes'|trans}}</option>
<option value="limited">{{'display.values.wheelchair.limited'|trans}}</option>
<option value="no">{{'display.values.wheelchair.no'|trans}}</option>
</select>
</div>
</div>
</div>
<div id="opening_hours">
<h2>{{ 'display.opening_hours'|trans }}</h2>
<p class="description">{{ 'display.opening_hours_description'|trans }}</p>
<input type="checkbox" name="commerce_tag_value__opening_hours_1" value="yes">
Lundi de
<input type="number" name="commerce_tag_value__opening_hours_1_midday_hour" value="">
à
<input type="number" name="commerce_tag_value__opening_hours_1_midday_minute" value="">.
<input type="checkbox" name="commerce_tag_value__opening_hours_1_midday" value="yes">
et
<input type="number" name="commerce_tag_value__opening_hours_1_midday_hour" value="">
à
<input type="number" name="commerce_tag_value__opening_hours_1_midday_minute" value="">.
<input type="checkbox" name="commerce_tag_value__opening_hours_1_evening" value="yes">
<hr>
</div>
<div id="addresses">
<h2>{{ 'display.address'|trans }}</h2>
<div class="row mb-3">
<div class="col-md-5">
<label for="commerce_tag_value__addr:housenumber">{{'display.keys.addr:housenumber'|trans}}</label>
<label for="commerce_tag_value__addr:street">{{'display.keys.addr:street'|trans}}</label>
</div>
</div>
</div>
<fieldset>
{% for attributes in commerce.tag %}
{% for kv in attributes %}
{% if kv.k == 'opening_hours' %}
{{ 'display.keys.opening_hours'|trans }}
{% else %}
@ -87,7 +203,7 @@
{% endif %}
{% endfor %}
{% endfor %}
</fieldset>
</fieldset>
<button type="submit" class="btn btn-primary">{{ 'display.submit'|trans }}</button>
</form>

View file

@ -1,33 +1,63 @@
display:
title: "OSM My Shop"
home: "Home"
stats: "Statistics"
contact_humans: "Contact OSM France humans for help"
modify: "Modify"
welcome: "Welcome to the shops!"
modify_commerce: "Modify your shop"
tags: "Tags"
submit: "Submit"
last_modification: "Last modification"
days_ago: "%days% days ago"
by: "by"
view_on_osm: "View on OpenStreetMap"
title: 'OSM My Shop'
home: 'Home'
stats: 'Statistics'
welcome: 'Welcome to the shops!'
contact_humans: 'Contact OSM France humans for help'
modify: 'Modify'
modify_commerce: 'Modify your shop'
tags: 'Tags'
submit: 'Submit'
last_modification: 'Last modification'
days_ago: '%days% days ago'
by: 'by'
view_on_osm: 'View on OpenStreetMap'
please_select: 'Please select'
wheelchair: 'Wheelchair access'
ask_angela: 'Ask Angela'
ask_angela_description: 'This place is participating in the national campaign "Ask Angela" to help the prevention of harassment'
ask_angela_more_info: 'More information on the campaign'
address: 'Address'
attributes: 'Attributes'
opening_hours: 'Opening hours'
opening_hours_description: 'Good opening hours are important for customers to find you.'
values:
wheelchair:
yes: 'the place is fully accessible in wheelchair'
limited: 'the place is accessible in wheelchair but with some limitations'
no: 'the place is not accessible in wheelchair'
angela:
yes: 'Yes'
no: 'No'
building:
main: 'Main'
annex: 'Annex'
cuisine: 'Cuisine'
keys:
tags: "tags"
name: "Name"
address: "Address"
phone: "Phone"
contact:email: "Email"
contact:phone: "Contact phone"
address:housenumber: "Street number"
address:street: "Street"
address:city: "City"
address:postcode: "Postal code"
website: "Website"
contact:website: "Contact website"
opening_hours: "Opening hours"
shop: "Shop type"
tourism: "Tourism"
tags: 'tags'
name: 'Name'
address: 'Address'
phone: 'Phone'
building: 'Building'
contact:email: 'Email'
contact:phone: 'Contact phone'
address:housenumber: 'Street number'
address:street: 'Street'
address:city: 'City'
address:postcode: 'Postal code'
website: 'Website'
contact:website: 'Contact website'
opening_hours: 'Opening hours'
shop: 'Shop type'
tourism: 'Tourism'
wheelchair: 'Wheelchair access'
addr:housenumber: 'Street number'
addr:street: 'Street'
addr:city: 'City'
addr:postcode: 'Postal code'
amenity: 'Amenity'
source: 'Source'
ref:FR:SIRET: 'SIRET'
disclaimer:
title: "Warning"
text: "This site is a work in progress, this is a demonstration on a fictional object using OpenStreetMap API data."
title: 'Warning'
text: 'This site is a work in progress, this is a demonstration on a fictional object using OpenStreetMap API data.'

View file

@ -12,6 +12,12 @@ display:
days_ago: "il y a %days% jours"
by: "par"
view_on_osm: "Voir sur OpenStreetMap"
please_select: "Veuillez sélectionner"
values:
wheelchair: "Accès PMR"
yes: "le lieu est totalement praticable en fauteuil roulant"
limited: "le lieu est accessible en fauteuil roulant mais pas partout"
no: "le lieu n'est pas accessible en fauteuil roulant"
keys:
tags: "étiquettes"
name: "Nom"
@ -28,6 +34,7 @@ display:
opening_hours: "Horaires d'ouverture"
shop: "Type de commerce"
tourism: "Tourisme"
wheelchair: "Accès PMR"
disclaimer:
title: "Attention"
text: "Ce site est un travail en cours, ceci est une démonstration sur un objet fictif qui utilise les données de l'API OpenStreetMap."