454 lines
24 KiB
PHP
454 lines
24 KiB
PHP
<?php
|
|
/**
|
|
* admin/edit_site.php
|
|
*
|
|
* Corrige suite a l'audit du 20/09/2026 :
|
|
* - Le flux WMS cadastre (URL, nom de couche, attribution HTML) est
|
|
* desormais encode en JSON avec JSON_HEX_* au lieu d'addslashes(),
|
|
* qui n'est pas un encodeur JavaScript fiable et pouvait permettre
|
|
* une injection si un pays contenait une valeur cadastre malicieuse
|
|
* (le champ cadastre_attribution_html est explicitement du HTML,
|
|
* donc a risque si mal echappe).
|
|
* - Les erreurs techniques ($e->getMessage()) ne sont plus affichees
|
|
* a l'ecran : elles sont journalisees via error_log() et un message
|
|
* generique est montre a l'administrateur.
|
|
*
|
|
* Note : le controle par role (require_admin_role) est volontairement
|
|
* absent de cette version, la separation des privileges etant reportee
|
|
* a une migration dediee (voir admin_auth.php).
|
|
*
|
|
* Version complète :
|
|
* - coordonnées GPS avec step="any" ;
|
|
* - ajout dynamique de nouvelles cellules ;
|
|
* - héritage MCC/MNC depuis l'opérateur du site ;
|
|
* - INSERT avec exactement 14 paramètres et NOW() pour validated_at.
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/admin_auth.php';
|
|
require_once __DIR__ . '/../lib/db.php';
|
|
require_once __DIR__ . '/_nav.php';
|
|
|
|
require_admin_auth();
|
|
|
|
$db = pdo();
|
|
$user = current_admin_username();
|
|
$successMessage = null;
|
|
$errorMessage = null;
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
require_csrf_token();
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
if ($action === 'save_site') {
|
|
$siteId = (int)($_POST['site_id'] ?? 0);
|
|
$lat = (float)($_POST['lat'] ?? 0);
|
|
$lon = (float)($_POST['lon'] ?? 0);
|
|
$locationDesc = trim($_POST['location_desc'] ?? '');
|
|
$addressDesc = trim($_POST['address_desc'] ?? '');
|
|
|
|
$cellIds = $_POST['row_cell_pk'] ?? [];
|
|
$techs = $_POST['cell_tech'] ?? [];
|
|
$cellRadioIds = $_POST['cell_id'] ?? [];
|
|
$areaCodes = $_POST['cell_area_code'] ?? [];
|
|
$subIds = $_POST['cell_sub_id'] ?? [];
|
|
$subIds2 = $_POST['cell_sub_id2'] ?? [];
|
|
$bands = $_POST['cell_band'] ?? [];
|
|
$frequencies = $_POST['cell_frequency'] ?? [];
|
|
$azimuths = $_POST['cell_azimuth'] ?? [];
|
|
$bandwidths = $_POST['cell_bandwidth'] ?? [];
|
|
$arfcns = $_POST['cell_arfcn'] ?? [];
|
|
|
|
$newTechs = $_POST['new_cell_tech'] ?? [];
|
|
$newCellRadioIds = $_POST['new_cell_id'] ?? [];
|
|
$newAreaCodes = $_POST['new_cell_area_code'] ?? [];
|
|
$newSubIds = $_POST['new_cell_sub_id'] ?? [];
|
|
$newSubIds2 = $_POST['new_cell_sub_id2'] ?? [];
|
|
$newBands = $_POST['new_cell_band'] ?? [];
|
|
$newFrequencies = $_POST['new_cell_frequency'] ?? [];
|
|
$newAzimuths = $_POST['new_cell_azimuth'] ?? [];
|
|
$newBandwidths = $_POST['new_cell_bandwidth'] ?? [];
|
|
$newArfcns = $_POST['new_cell_arfcn'] ?? [];
|
|
|
|
if (!$siteId || !$lat || !$lon) {
|
|
$errorMessage = 'Coordonnees invalides.';
|
|
} else {
|
|
$siteMccMncStmt = $db->prepare('
|
|
SELECT o.mcc, o.mnc
|
|
FROM sites s
|
|
JOIN operators o ON o.id = s.operator_id
|
|
WHERE s.id = ?
|
|
');
|
|
$siteMccMncStmt->execute([$siteId]);
|
|
$siteMccMnc = $siteMccMncStmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$siteMccMnc) {
|
|
$errorMessage = 'Site introuvable, impossible de determiner le MCC/MNC.';
|
|
} else {
|
|
$siteMcc = $siteMccMnc['mcc'];
|
|
$siteMnc = $siteMccMnc['mnc'];
|
|
|
|
$db->beginTransaction();
|
|
try {
|
|
$db->prepare('UPDATE sites SET lat = ?, lon = ?, location_desc = ?, address_desc = ? WHERE id = ?')
|
|
->execute([$lat, $lon, $locationDesc, $addressDesc, $siteId]);
|
|
|
|
$updateCell = $db->prepare('
|
|
UPDATE cells
|
|
SET tech = ?, cell_id = ?, area_code = ?, sub_id = ?, sub_id2 = ?,
|
|
band = ?, frequency = ?, azimuth = ?, bandwidth = ?, arfcn = ?,
|
|
validated_by = ?, validated_at = NOW()
|
|
WHERE id = ? AND site_id = ?
|
|
');
|
|
|
|
foreach ($cellIds as $i => $cellPk) {
|
|
$cellPk = (int)$cellPk;
|
|
if (!$cellPk) {
|
|
continue;
|
|
}
|
|
|
|
$updateCell->execute([
|
|
$techs[$i] ?? null,
|
|
$cellRadioIds[$i] ?? null,
|
|
$areaCodes[$i] ?? null,
|
|
($subIds[$i] ?? '') !== '' ? $subIds[$i] : null,
|
|
($subIds2[$i] ?? '') !== '' ? $subIds2[$i] : null,
|
|
($bands[$i] ?? '') !== '' ? $bands[$i] : null,
|
|
($frequencies[$i] ?? '') !== '' ? $frequencies[$i] : null,
|
|
($azimuths[$i] ?? '') !== '' ? (int)$azimuths[$i] : null,
|
|
($bandwidths[$i] ?? '') !== '' ? $bandwidths[$i] : null,
|
|
($arfcns[$i] ?? '') !== '' ? $arfcns[$i] : null,
|
|
$user,
|
|
$cellPk,
|
|
$siteId,
|
|
]);
|
|
}
|
|
|
|
// 14 marqueurs (?) et 14 valeurs dans execute().
|
|
// validated_at est fourni par NOW(), donc aucune valeur
|
|
// supplémentaire ne doit être ajoutée dans execute().
|
|
$insertCell = $db->prepare('
|
|
INSERT INTO cells
|
|
(
|
|
site_id, mcc, mnc, tech, cell_id, area_code,
|
|
sub_id, sub_id2, band, frequency, azimuth,
|
|
bandwidth, arfcn, validated_by, validated_at
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
|
|
');
|
|
|
|
foreach ($newTechs as $i => $newTech) {
|
|
$newCellIdVal = trim((string)($newCellRadioIds[$i] ?? ''));
|
|
$newAreaCodeVal = trim((string)($newAreaCodes[$i] ?? ''));
|
|
|
|
// Ignore une ligne ajoutée mais non remplie.
|
|
if ($newCellIdVal === '' || $newAreaCodeVal === '') {
|
|
continue;
|
|
}
|
|
|
|
$insertCell->execute([
|
|
$siteId,
|
|
$siteMcc,
|
|
$siteMnc,
|
|
$newTech,
|
|
$newCellIdVal,
|
|
$newAreaCodeVal,
|
|
($newSubIds[$i] ?? '') !== '' ? $newSubIds[$i] : null,
|
|
($newSubIds2[$i] ?? '') !== '' ? $newSubIds2[$i] : null,
|
|
($newBands[$i] ?? '') !== '' ? $newBands[$i] : null,
|
|
($newFrequencies[$i] ?? '') !== '' ? $newFrequencies[$i] : null,
|
|
($newAzimuths[$i] ?? '') !== '' ? (int)$newAzimuths[$i] : null,
|
|
($newBandwidths[$i] ?? '') !== '' ? $newBandwidths[$i] : null,
|
|
($newArfcns[$i] ?? '') !== '' ? $newArfcns[$i] : null,
|
|
$user,
|
|
]);
|
|
}
|
|
|
|
$db->commit();
|
|
$successMessage = 'Site mis a jour.';
|
|
} catch (Throwable $e) {
|
|
if ($db->inTransaction()) {
|
|
$db->rollBack();
|
|
}
|
|
error_log('[edit_site] save_site: ' . $e->getMessage());
|
|
$errorMessage = "Une erreur est survenue lors de l'enregistrement. Consultez les journaux du serveur.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($action === 'delete_cell') {
|
|
$cellId = (int)($_POST['cell_id_pk'] ?? 0);
|
|
$siteId = (int)($_POST['site_id'] ?? 0);
|
|
|
|
$countStmt = $db->prepare('SELECT COUNT(*) FROM cells WHERE site_id = ?');
|
|
$countStmt->execute([$siteId]);
|
|
$remaining = (int)$countStmt->fetchColumn();
|
|
|
|
if ($remaining <= 1) {
|
|
$errorMessage = "Impossible de retirer la derniere cellule d'un site : supprimez plutot le site entier.";
|
|
} else {
|
|
try {
|
|
$db->prepare('DELETE FROM cells WHERE id = ? AND site_id = ?')->execute([$cellId, $siteId]);
|
|
$successMessage = 'Cellule retiree du site.';
|
|
} catch (Throwable $e) {
|
|
error_log('[edit_site] delete_cell: ' . $e->getMessage());
|
|
$errorMessage = 'Une erreur est survenue lors de la suppression.';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($action === 'delete_site') {
|
|
$siteId = (int)($_POST['site_id'] ?? 0);
|
|
try {
|
|
$db->prepare('DELETE FROM sites WHERE id = ?')->execute([$siteId]);
|
|
header('Location: edit_site.php?deleted=1');
|
|
exit;
|
|
} catch (Throwable $e) {
|
|
error_log('[edit_site] delete_site: ' . $e->getMessage());
|
|
$errorMessage = 'Une erreur est survenue lors de la suppression du site.';
|
|
}
|
|
}
|
|
}
|
|
|
|
$editingSiteId = isset($_GET['site_id']) ? (int)$_GET['site_id'] : null;
|
|
$editingSite = null;
|
|
$editingCells = [];
|
|
|
|
if ($editingSiteId) {
|
|
$siteStmt = $db->prepare('
|
|
SELECT s.*, o.brand AS operator_brand, o.mcc, o.mnc,
|
|
c.name AS country_name, c.default_lat, c.default_lon, c.default_zoom,
|
|
c.cadastre_wms_url, c.cadastre_wms_layer,
|
|
c.cadastre_attribution_html
|
|
FROM sites s
|
|
JOIN operators o ON o.id = s.operator_id
|
|
JOIN countries c ON c.id = s.country_id
|
|
WHERE s.id = ?
|
|
');
|
|
$siteStmt->execute([$editingSiteId]);
|
|
$editingSite = $siteStmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($editingSite) {
|
|
$cellsStmt = $db->prepare('SELECT * FROM cells WHERE site_id = ? ORDER BY tech, cell_id');
|
|
$cellsStmt->execute([$editingSiteId]);
|
|
$editingCells = $cellsStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
}
|
|
|
|
$mapLat = null;
|
|
$mapLon = null;
|
|
$coordsInvalid = false;
|
|
|
|
if ($editingSite) {
|
|
$siteLat = (float)$editingSite['lat'];
|
|
$siteLon = (float)$editingSite['lon'];
|
|
|
|
if ($siteLat === 0.0 && $siteLon === 0.0) {
|
|
$coordsInvalid = true;
|
|
$mapLat = (float)($editingSite['default_lat'] ?? 20);
|
|
$mapLon = (float)($editingSite['default_lon'] ?? 0);
|
|
} else {
|
|
$mapLat = $siteLat;
|
|
$mapLon = $siteLon;
|
|
}
|
|
}
|
|
|
|
$searchQuery = trim($_GET['q'] ?? '');
|
|
$countryFilter = $_GET['country'] ?? '';
|
|
$operatorFilter = $_GET['operator'] ?? '';
|
|
$sites = [];
|
|
|
|
if (!$editingSiteId) {
|
|
$sql = '
|
|
SELECT s.id, s.lat, s.lon, s.location_desc, s.address_desc,
|
|
o.brand AS operator_brand, c.name AS country_name,
|
|
GROUP_CONCAT(DISTINCT cl.tech ORDER BY cl.tech SEPARATOR ", ") AS techs,
|
|
COUNT(cl.id) AS cell_count
|
|
FROM sites s
|
|
JOIN operators o ON o.id = s.operator_id
|
|
JOIN countries c ON c.id = s.country_id
|
|
LEFT JOIN cells cl ON cl.site_id = s.id
|
|
WHERE 1=1
|
|
';
|
|
$params = [];
|
|
|
|
if ($searchQuery !== '') {
|
|
$sql .= ' AND (s.location_desc LIKE ? OR s.address_desc LIKE ? OR cl.cell_id LIKE ? OR cl.sub_id2 LIKE ?)';
|
|
$like = '%' . $searchQuery . '%';
|
|
array_push($params, $like, $like, $like, $like);
|
|
}
|
|
if ($countryFilter !== '') {
|
|
$sql .= ' AND c.iso2 = ?';
|
|
$params[] = $countryFilter;
|
|
}
|
|
if ($operatorFilter !== '') {
|
|
$sql .= ' AND o.id = ?';
|
|
$params[] = $operatorFilter;
|
|
}
|
|
|
|
$sql .= ' GROUP BY s.id ORDER BY s.created_at DESC LIMIT 200';
|
|
$stmt = $db->prepare($sql);
|
|
$stmt->execute($params);
|
|
$sites = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
$countries = $db->query('SELECT id, iso2, name FROM countries ORDER BY name')->fetchAll(PDO::FETCH_ASSOC);
|
|
$operators = $db->query('SELECT id, mcc, mnc, brand FROM operators ORDER BY brand')->fetchAll(PDO::FETCH_ASSOC);
|
|
$deletedNotice = isset($_GET['deleted']);
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>NetMonster Admin - Edition de site</title>
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin>
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin></script>
|
|
<link rel="stylesheet" href="assets/theme.css">
|
|
<style>
|
|
.search-row { display:flex; gap:.6rem; margin-bottom:1rem; flex-wrap:wrap; }
|
|
.search-row input[type=text] { flex:1; min-width:220px; }
|
|
table.sites-table td { vertical-align:middle; }
|
|
button.edit-btn { background:var(--primary); color:#fff; border:none; padding:.3rem .7rem; border-radius:4px; cursor:pointer; font-size:.78rem; }
|
|
.danger-zone { border:1px solid #f3c1bd; background:#fdeceb; border-radius:var(--radius-sm); padding:1rem; margin-top:1.4rem; }
|
|
.danger-zone h3 { margin:0 0 .5rem; font-size:.95rem; color:var(--danger-dark); }
|
|
#editMap { height:320px; width:100%; margin-bottom:1rem; background:#e8eaf0; border-radius:var(--radius-sm); }
|
|
button.add-cell-btn { background:var(--primary); color:#fff; border:none; padding:.4rem .9rem; border-radius:4px; cursor:pointer; font-size:.82rem; margin:.6rem 0 1rem; }
|
|
tr.new-cell-row td { background:#f3f8ff; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<?php render_admin_nav('edit_site.php', $user); ?>
|
|
<div class="page-wrap">
|
|
<?php if (!$editingSiteId): ?>
|
|
<h1>Edition d'un site valide</h1>
|
|
<p class="page-subtitle">Recherchez un site pour corriger ses coordonnees, sa description, ou les parametres radio de ses cellules.</p>
|
|
<?php if ($deletedNotice): ?><p class="msg-ok">Site supprime avec succes.</p><?php endif; ?>
|
|
<?php if ($successMessage): ?><p class="msg-ok"><?= htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?>
|
|
<div class="card">
|
|
<form class="search-row" method="get">
|
|
<input type="text" name="q" placeholder="Rechercher par lieu, adresse, Cell ID ou eNB..." value="<?= htmlspecialchars($searchQuery, ENT_QUOTES, 'UTF-8') ?>">
|
|
<select name="country"><option value="">Tous les pays</option><?php foreach ($countries as $c): ?><option value="<?= htmlspecialchars($c['iso2'], ENT_QUOTES, 'UTF-8') ?>" <?= $countryFilter === $c['iso2'] ? 'selected' : '' ?>><?= htmlspecialchars($c['name'], ENT_QUOTES, 'UTF-8') ?></option><?php endforeach; ?></select>
|
|
<select name="operator"><option value="">Tous les operateurs</option><?php foreach ($operators as $o): ?><option value="<?= $o['id'] ?>" <?= (string)$operatorFilter === (string)$o['id'] ? 'selected' : '' ?>><?= htmlspecialchars($o['brand'], ENT_QUOTES, 'UTF-8') ?></option><?php endforeach; ?></select>
|
|
<button type="submit" class="primary">Rechercher</button>
|
|
</form>
|
|
<table class="data-table sites-table">
|
|
<tr><th>Operateur</th><th>Pays</th><th>Technos</th><th>Cellules</th><th>Lieu</th><th>Lat/Lon</th><th></th></tr>
|
|
<?php foreach ($sites as $s): ?>
|
|
<tr><td><?= htmlspecialchars($s['operator_brand'], ENT_QUOTES, 'UTF-8') ?></td><td><?= htmlspecialchars($s['country_name'], ENT_QUOTES, 'UTF-8') ?></td><td><?= htmlspecialchars((string)($s['techs'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td><td><?= (int)$s['cell_count'] ?></td><td><?= htmlspecialchars($s['address_desc'] ?? $s['location_desc'] ?? '', ENT_QUOTES, 'UTF-8') ?></td><td><?= htmlspecialchars((string)$s['lat'], ENT_QUOTES, 'UTF-8') ?>, <?= htmlspecialchars((string)$s['lon'], ENT_QUOTES, 'UTF-8') ?></td><td><button class="edit-btn" onclick="window.location='?site_id=<?= $s['id'] ?>'">Editer</button></td></tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($sites)): ?><tr><td colspan="7" class="no-cadastre">Aucun site trouve.</td></tr><?php endif; ?>
|
|
</table>
|
|
</div>
|
|
<?php elseif ($editingSite): ?>
|
|
<h1>Edition du site — <?= htmlspecialchars($editingSite['operator_brand'], ENT_QUOTES, 'UTF-8') ?></h1>
|
|
<p class="page-subtitle"><a href="edit_site.php">← Retour a la liste</a></p>
|
|
<?php if ($successMessage): ?><p class="msg-ok"><?= htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?>
|
|
<?php if ($errorMessage): ?><p class="msg-err"><?= htmlspecialchars($errorMessage, ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?>
|
|
<?php if ($coordsInvalid): ?><p class="msg-err">Attention : les coordonnees enregistrees pour ce site sont a 0,0 (invalides). La carte est centree sur le pays par defaut ; corrigez la position ci-dessous.</p><?php endif; ?>
|
|
<div class="card">
|
|
<h2>Localisation</h2><div id="editMap"></div>
|
|
<form method="post" id="editSiteForm">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="save_site">
|
|
<input type="hidden" name="site_id" value="<?= (int)$editingSite['id'] ?>">
|
|
<div class="form-grid wide-3">
|
|
<div><label>Latitude</label><input type="number" step="any" name="lat" id="latInput" value="<?= htmlspecialchars((string)$editingSite['lat'], ENT_QUOTES, 'UTF-8') ?>" required></div>
|
|
<div><label>Longitude</label><input type="number" step="any" name="lon" id="lonInput" value="<?= htmlspecialchars((string)$editingSite['lon'], ENT_QUOTES, 'UTF-8') ?>" required></div>
|
|
<div><label>Operateur / pays (non modifiable ici)</label><input type="text" value="<?= htmlspecialchars($editingSite['operator_brand'] . ' - ' . $editingSite['country_name'], ENT_QUOTES, 'UTF-8') ?>" disabled></div>
|
|
</div>
|
|
<div class="form-grid" style="margin-top:.6rem"><div><label>Adresse / lieu</label><input type="text" name="address_desc" value="<?= htmlspecialchars($editingSite['address_desc'] ?? '', ENT_QUOTES, 'UTF-8') ?>"></div><div><label>Description (Location brute, optionnel)</label><input type="text" name="location_desc" value="<?= htmlspecialchars($editingSite['location_desc'] ?? '', ENT_QUOTES, 'UTF-8') ?>"></div></div>
|
|
<h2 style="margin-top:1.4rem">Cellules du site (<?= count($editingCells) ?>)</h2>
|
|
<table class="data-table" id="cellsTable">
|
|
<tr><th>Techno</th><th>Cell ID</th><th>Area code</th><th>Sub ID</th><th>eNB/RNC</th><th>Bande</th><th>Frequence</th><th>Azimut</th><th>Largeur</th><th>ARFCN</th><th></th></tr>
|
|
<?php foreach ($editingCells as $c): ?>
|
|
<tr>
|
|
<input type="hidden" name="row_cell_pk[]" value="<?= (int)$c['id'] ?>">
|
|
<td><select name="cell_tech[]"><?php foreach (['2G','3G','4G','5G','CDMA'] as $t): ?><option value="<?= $t ?>" <?= $c['tech'] === $t ? 'selected' : '' ?>><?= $t ?></option><?php endforeach; ?></select></td>
|
|
<td><input type="text" name="cell_id[]" value="<?= htmlspecialchars((string)$c['cell_id'], ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_area_code[]" value="<?= htmlspecialchars((string)$c['area_code'], ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_sub_id[]" value="<?= htmlspecialchars((string)($c['sub_id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_sub_id2[]" value="<?= htmlspecialchars((string)($c['sub_id2'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_band[]" value="<?= htmlspecialchars((string)($c['band'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_frequency[]" value="<?= htmlspecialchars((string)($c['frequency'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="number" min="0" max="359" name="cell_azimuth[]" value="<?= htmlspecialchars((string)($c['azimuth'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_bandwidth[]" value="<?= htmlspecialchars((string)($c['bandwidth'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><input type="text" name="cell_arfcn[]" value="<?= htmlspecialchars((string)($c['arfcn'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"></td>
|
|
<td><button type="button" class="reject-small" onclick="deleteCell(<?= (int)$c['id'] ?>, <?= (int)$editingSiteId ?>)">Retirer</button></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</table>
|
|
<button type="button" class="add-cell-btn" id="addCellBtn">+ Ajouter une bande</button>
|
|
<button type="submit" class="validate" style="margin-top:1rem">Enregistrer les modifications</button>
|
|
</form>
|
|
</div>
|
|
<div class="danger-zone"><h3>Zone sensible</h3><p class="page-subtitle" style="margin-bottom:.6rem">La suppression du site retire aussi TOUTES ses cellules de la base validee (irreversible).</p><form method="post" onsubmit="return confirm('Supprimer definitivement ce site et toutes ses cellules ?')"><?= csrf_field() ?><input type="hidden" name="action" value="delete_site"><input type="hidden" name="site_id" value="<?= (int)$editingSiteId ?>"><button type="submit" class="reject">Supprimer ce site</button></form></div>
|
|
<form method="post" id="deleteCellForm" style="display:none"><?= csrf_field() ?><input type="hidden" name="action" value="delete_cell"><input type="hidden" name="cell_id_pk" id="deleteCellPk"><input type="hidden" name="site_id" id="deleteCellSiteId"></form>
|
|
<script>
|
|
function deleteCell(cellPk, siteId) {
|
|
if (!confirm('Retirer cette cellule du site ?')) return;
|
|
document.getElementById('deleteCellPk').value = cellPk;
|
|
document.getElementById('deleteCellSiteId').value = siteId;
|
|
document.getElementById('deleteCellForm').submit();
|
|
}
|
|
|
|
let newCellIndex = 0;
|
|
document.getElementById('addCellBtn').addEventListener('click', function () {
|
|
const table = document.getElementById('cellsTable');
|
|
const row = document.createElement('tr');
|
|
row.className = 'new-cell-row';
|
|
row.innerHTML = `
|
|
<td><select name="new_cell_tech[${newCellIndex}]"><option value="2G">2G</option><option value="3G">3G</option><option value="4G" selected>4G</option><option value="5G">5G</option><option value="CDMA">CDMA</option></select></td>
|
|
<td><input type="text" name="new_cell_id[${newCellIndex}]" placeholder="Cell ID"></td>
|
|
<td><input type="text" name="new_cell_area_code[${newCellIndex}]" placeholder="Area code"></td>
|
|
<td><input type="text" name="new_cell_sub_id[${newCellIndex}]" placeholder="Sub ID"></td>
|
|
<td><input type="text" name="new_cell_sub_id2[${newCellIndex}]" placeholder="eNB/RNC"></td>
|
|
<td><input type="text" name="new_cell_band[${newCellIndex}]" placeholder="Bande (ex: B7)"></td>
|
|
<td><input type="text" name="new_cell_frequency[${newCellIndex}]" placeholder="Frequence"></td>
|
|
<td><input type="number" min="0" max="359" name="new_cell_azimuth[${newCellIndex}]" placeholder="Azimut"></td>
|
|
<td><input type="text" name="new_cell_bandwidth[${newCellIndex}]" placeholder="Largeur"></td>
|
|
<td><input type="text" name="new_cell_arfcn[${newCellIndex}]" placeholder="ARFCN"></td>
|
|
<td><button type="button" class="reject-small" onclick="this.closest('tr').remove()">Annuler</button></td>
|
|
`;
|
|
table.appendChild(row);
|
|
newCellIndex++;
|
|
});
|
|
|
|
try {
|
|
const editMap = L.map('editMap').setView([<?= $mapLat ?>, <?= $mapLon ?>], <?= $coordsInvalid ? 6 : 16 ?>);
|
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a> contributors'
|
|
}).addTo(editMap);
|
|
<?php if (!empty($editingSite['cadastre_wms_url']) && !empty($editingSite['cadastre_wms_layer'])): ?>
|
|
L.tileLayer.wms(
|
|
<?= json_encode($editingSite['cadastre_wms_url'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>,
|
|
{
|
|
layers: <?= json_encode($editingSite['cadastre_wms_layer'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>,
|
|
format: 'image/png',
|
|
transparent: true,
|
|
opacity: 0.6,
|
|
attribution: <?= json_encode($editingSite['cadastre_attribution_html'] ?? '', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>
|
|
}
|
|
).addTo(editMap);
|
|
<?php endif; ?>
|
|
const marker = L.marker([<?= $mapLat ?>, <?= $mapLon ?>], {draggable: true}).addTo(editMap);
|
|
marker.on('dragend', function (e) {
|
|
const pos = e.target.getLatLng();
|
|
document.getElementById('latInput').value = pos.lat.toFixed(6);
|
|
document.getElementById('lonInput').value = pos.lng.toFixed(6);
|
|
});
|
|
setTimeout(() => editMap.invalidateSize(), 100);
|
|
window.addEventListener('resize', () => editMap.invalidateSize());
|
|
} catch (err) {
|
|
document.getElementById('editMap').innerHTML = '<p style="padding:1rem;color:#b71c1c">Erreur de chargement de la carte.</p>';
|
|
console.error('Erreur Leaflet sur edit_site.php :', err);
|
|
}
|
|
</script>
|
|
<?php else: ?>
|
|
<p class="msg-err">Site introuvable.</p>
|
|
<p><a href="edit_site.php">← Retour a la liste</a></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</body>
|
|
</html>
|