Add Veeam and NetBackup support to inventory and reporting

- Introduced Veeam and NetBackup version tracking in `Inventory.php` for backup validation.
- Enhanced backup report in `Backups.php` with improved SQL queries and support for short hostname normalization.
- Updated `navbar.html` and related navigation components for better usability and accessibility.
- Refactored ActiveDirectory scripts for better modularity and event handling.
- Added `AGENTS.md` and `modele.php` as templates for documentation and new page creation.
This commit is contained in:
e025532
2026-09-24 15:36:09 +02:00
parent c185d099d6
commit db223b805c
7 changed files with 847 additions and 127 deletions

278
modele_full.php Normal file
View File

@@ -0,0 +1,278 @@
<?php
/**
* Catalogue de composants à copier dans les prochaines pages.
* Sources : modele.php, index.php, AD/LocalAdmins.php,
* Network/dhcp_servers.php, Hyper-V/Clusters.php et reports/Backups.php.
* Toutes les données ci-dessous sont fictives. Aucun appel à une base.
*/
ob_start();
require $_SERVER['DOCUMENT_ROOT'] . '/include/all.php';
$commonMarkup = ob_get_clean();
$pageTitle = 'Modèles de composants';
$escape = static function ($value) {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
};
// Remplacer ce jeu de démonstration par les données de votre future page.
$demoRows = [
['DEMO-SRV-01', 'Windows', 'Disponible', 'success', 24],
['DEMO-SRV-02', 'Linux', 'Disponible', 'success', 42],
['DEMO-SRV-03', 'Windows', 'À vérifier', 'warning', 76],
['DEMO-SRV-04', 'Linux', 'Indisponible', 'danger', 0],
['DEMO-SRV-05', 'Windows', 'Disponible', 'success', 31],
['DEMO-SRV-06', 'Linux', 'Disponible', 'success', 58],
];
?>
<!DOCTYPE html>
<html lang="<?= $escape($lang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= $escape($pageTitle) ?> - Web Infra Reports IT</title>
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap-icons-1.13.1/bootstrap-icons.css">
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
<script src="/js/jquery-3.6.1.min.js"></script>
<script src="/js/bootstrap.bundle.min.js"></script>
<!-- Dépendances du tableau avec recherche, tri, pagination et export. -->
<script src="/js/bootstrap-table.min.js"></script>
<script src="/js/bootstrap-table-fr-FR.min.js"></script>
<script src="/js/tableExport.min.js"></script>
<script src="/js/bootstrap-table-export.min.js"></script>
<script src="/js/chart.min.js"></script>
<style>
.page-sidebar { flex: 0 0 230px; overflow-y: auto; }
.page-content { min-width: 0; }
.demo-section { scroll-margin-top: 1rem; }
.demo-kpi { border-radius: 14px; box-shadow: 0 4px 14px rgba(15, 23, 42, .06); }
.demo-icon { width: 48px; height: 48px; display: grid; place-items: center; border-radius: 14px; font-size: 1.5rem; }
.demo-chart { position: relative; height: 240px; }
.page-content .card, .demo-modal .modal-content { color: inherit; }
body.bg-dark .page-content .card,
body.bg-dark .demo-modal .modal-content,
body.bg-dark .page-content .list-group-item { background-color: #2b3035; color: #f8f9fa; border-color: #495057; }
body.bg-dark .page-content .text-muted { color: #adb5bd !important; }
body.bg-dark .page-content .form-control,
body.bg-dark .page-content .form-select,
body.bg-dark .page-content .page-link,
body.bg-dark .page-content .dropdown-menu { background-color: #212529; color: #f8f9fa; border-color: #495057; }
body.bg-dark .page-content .dropdown-item { color: #f8f9fa; }
body.bg-dark .page-content .dropdown-item:hover { background-color: #495057; }
body.bg-dark .page-content .nav-tabs .nav-link.active { background-color: #2b3035; color: #f8f9fa; border-color: #495057; }
.page-content .accordion-item, .page-content .accordion-button { background-color: transparent; color: inherit; }
.page-content .accordion-item { border-color: rgba(128, 128, 128, .35); }
.page-content .accordion-button:not(.collapsed) { background-color: rgba(128, 128, 128, .12); }
body.bg-dark .page-content .accordion-button::after,
body.bg-dark .demo-modal .btn-close { filter: invert(1) grayscale(1); }
</style>
</head>
<body class="bg-light text-dark">
<?= $commonMarkup ?>
<div class="container-fluid">
<div class="row flex-nowrap">
<div class="col-auto col-md-2 col-xl-2 px-sm-2 px-0 bg-dark vh-100 position-sticky top-0 page-sidebar">
<?php require $_SERVER['DOCUMENT_ROOT'] . '/navbar.html'; ?>
</div>
<main class="col py-3 page-content" aria-labelledby="page-title">
<h1 id="page-title"><span class="badge text-bg-secondary w-100 text-wrap"><?= $escape($pageTitle) ?></span></h1>
<p class="text-muted">Exemples réutilisables — données fictives et actions de démonstration.</p>
<nav class="d-flex flex-wrap gap-2 mb-4" aria-label="Composants de la page">
<a class="btn btn-sm btn-outline-primary" href="#synthese">Synthèse</a>
<a class="btn btn-sm btn-outline-primary" href="#tableau">Tableau</a>
<a class="btn btn-sm btn-outline-primary" href="#boutons">Boutons</a>
<a class="btn btn-sm btn-outline-primary" href="#formulaire">Formulaire</a>
<a class="btn btn-sm btn-outline-primary" href="#indicateurs">Indicateurs</a>
<a class="btn btn-sm btn-outline-primary" href="#organisation">Onglets et accordéon</a>
</nav>
<!-- 1. CARTES DE SYNTHÈSE : reprendre une colonne pour chaque indicateur. -->
<section id="synthese" class="demo-section mb-4" aria-labelledby="synthese-title">
<h2 id="synthese-title" class="h4">Cartes de synthèse</h2>
<div class="row g-3">
<?php foreach ([['Total', 6, 'primary', 'hdd-rack'], ['Disponibles', 4, 'success', 'check-circle'], ['À vérifier', 1, 'warning', 'exclamation-triangle'], ['Indisponibles', 1, 'danger', 'x-circle']] as $kpi): ?>
<div class="col-sm-6 col-xl-3">
<div class="card demo-kpi h-100"><div class="card-body d-flex align-items-center gap-3">
<span class="demo-icon text-bg-<?= $escape($kpi[2]) ?>"><i class="bi bi-<?= $escape($kpi[3]) ?>" aria-hidden="true"></i></span>
<div><div class="text-muted"><?= $escape($kpi[0]) ?></div><div class="fs-2 fw-bold"><?= $escape($kpi[1]) ?></div></div>
</div></div>
</div>
<?php endforeach; ?>
</div>
</section>
<!-- 2. TABLEAU : identifiant unique requis pour chaque bootstrap-table. -->
<section id="tableau" class="card demo-section mb-4" aria-labelledby="tableau-title">
<div class="card-header"><h2 id="tableau-title" class="h5 mb-0"><i class="bi bi-table me-2" aria-hidden="true"></i>Tableau avec recherche, tri et export</h2></div>
<div class="card-body">
<table id="demo-table" class="table table-bordered table-hover table-sm"
data-toggle="table" data-locale="<?= $lang === 'fr' ? 'fr-FR' : 'en-US' ?>"
data-search="true" data-show-columns="true" data-pagination="true"
data-page-size="5" data-page-list="[5, 10, 25]" data-show-export="true"
data-export-types='["csv", "json"]' data-export-data-type="all">
<caption>Inventaire fictif : six serveurs de démonstration.</caption>
<thead><tr>
<th scope="col" data-field="server" data-sortable="true">Serveur</th>
<th scope="col" data-field="os" data-sortable="true">Système</th>
<th scope="col" data-field="status" data-sortable="true">État</th>
<th scope="col" data-field="usage" data-sortable="true">Utilisation (%)</th>
<th scope="col" data-field="actions" data-force-hide="true">Action</th>
</tr></thead>
<tbody>
<?php foreach ($demoRows as $row): ?>
<tr>
<td class="fw-bold"><?= $escape($row[0]) ?></td>
<td><?= $escape($row[1]) ?></td>
<td><span class="badge text-bg-<?= $escape($row[3]) ?>"><?= $escape($row[2]) ?></span></td>
<td><?= $escape($row[4]) ?></td>
<td><button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#demo-modal" data-demo-server="<?= $escape($row[0]) ?>"><i class="bi bi-eye me-1" aria-hidden="true"></i>Détails</button></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
<!-- 3. BOUTONS : les actions de cet exemple affichent seulement un message. -->
<section id="boutons" class="card demo-section mb-4" aria-labelledby="boutons-title">
<div class="card-header"><h2 id="boutons-title" class="h5 mb-0">Boutons et fenêtre modale</h2></div>
<div class="card-body">
<div class="d-flex flex-wrap gap-2 mb-3">
<button type="button" class="btn btn-primary" data-demo-action="Action principale"><i class="bi bi-plus-circle me-1" aria-hidden="true"></i>Principal</button>
<button type="button" class="btn btn-secondary" data-demo-action="Action secondaire">Secondaire</button>
<button type="button" class="btn btn-success" data-demo-action="Validation"><i class="bi bi-check-lg me-1" aria-hidden="true"></i>Valider</button>
<button type="button" class="btn btn-warning" data-demo-action="Vérification">Vérifier</button>
<button type="button" class="btn btn-danger" data-demo-action="Action sensible simulée">Action sensible</button>
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#demo-modal">Ouvrir la modale</button>
</div>
<div class="d-flex flex-wrap align-items-center gap-2">
<button type="button" class="btn btn-sm btn-outline-secondary" data-demo-action="Petit bouton">Petit bouton</button>
<button type="button" class="btn btn-primary" disabled>Désactivé</button>
<button type="button" class="btn btn-primary" disabled><span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>Chargement…</button>
<div class="dropdown">
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Autres actions</button>
<ul class="dropdown-menu">
<li><button type="button" class="dropdown-item" data-demo-action="Première option">Première option</button></li>
<li><button type="button" class="dropdown-item" data-demo-action="Seconde option">Seconde option</button></li>
</ul>
</div>
</div>
<p id="demo-action-result" class="mt-3 mb-0" role="status" aria-live="polite"></p>
</div>
</section>
<!-- 4. FORMULAIRE : validation navigateur et aperçu local, sans envoi. -->
<section id="formulaire" class="card demo-section mb-4" aria-labelledby="formulaire-title">
<div class="card-header"><h2 id="formulaire-title" class="h5 mb-0">Formulaire</h2></div>
<div class="card-body">
<form id="demo-form" onsubmit="return false;">
<div class="row g-3">
<div class="col-md-6"><label for="demo-name" class="form-label">Nom <span class="text-danger">*</span></label><input id="demo-name" class="form-control" required maxlength="80" placeholder="Exemple : DEMO-SRV-07"></div>
<div class="col-md-6"><label for="demo-system" class="form-label">Système</label><select id="demo-system" class="form-select"><option>Windows</option><option>Linux</option><option>Autre</option></select></div>
<div class="col-md-6"><label for="demo-date" class="form-label">Date</label><input type="date" id="demo-date" class="form-control"></div>
<div class="col-md-6"><label for="demo-capacity" class="form-label">Capacité</label><div class="input-group"><input type="number" id="demo-capacity" class="form-control" min="0" value="100"><span class="input-group-text">Go</span></div></div>
<div class="col-12"><label for="demo-comment" class="form-label">Commentaire</label><textarea id="demo-comment" class="form-control" rows="2" maxlength="500"></textarea></div>
<div class="col-md-6"><div class="form-check"><input type="checkbox" class="form-check-input" id="demo-notify"><label class="form-check-label" for="demo-notify">Inclure dans le rapport</label></div></div>
<div class="col-md-6"><div class="form-check form-switch"><input type="checkbox" class="form-check-input" id="demo-enabled" checked><label class="form-check-label" for="demo-enabled">Actif</label></div></div>
<div class="col-12 d-flex gap-2"><button type="submit" class="btn btn-primary">Prévisualiser</button><button type="reset" class="btn btn-outline-secondary">Réinitialiser</button></div>
</div>
<p id="demo-form-result" class="mt-3 mb-0" role="status" aria-live="polite"></p>
</form>
</div>
</section>
<!-- 5. ALERTES, BADGES, PROGRESSION ET GRAPHIQUE. -->
<section id="indicateurs" class="demo-section mb-4" aria-labelledby="indicateurs-title">
<h2 id="indicateurs-title" class="h4">Alertes et indicateurs</h2>
<div class="row g-3">
<div class="col-xl-7"><div class="card h-100"><div class="card-body">
<div class="alert alert-info"><i class="bi bi-info-circle me-2" aria-hidden="true"></i>Information : exemple de message contextuel.</div>
<div class="alert alert-success">Succès : vérification terminée.</div>
<div class="alert alert-warning">Attention : un élément nécessite une vérification.</div>
<div class="alert alert-danger">Erreur : exemple de message d’échec.</div>
<div class="d-flex flex-wrap gap-2 mb-3"><span class="badge text-bg-success">Conforme</span><span class="badge text-bg-warning">À vérifier</span><span class="badge text-bg-danger">Erreur</span><span class="badge text-bg-secondary">Inconnu</span></div>
<div id="demo-progress-label" class="mb-1">Capacité utilisée : 65 %</div>
<div class="progress"><div class="progress-bar progress-bar-striped" role="progressbar" style="width:65%" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100" aria-labelledby="demo-progress-label">65 %</div></div>
</div></div></div>
<div class="col-xl-5"><div class="card h-100"><div class="card-header"><h3 class="h5 mb-0">Répartition des serveurs</h3></div><div class="card-body">
<div class="demo-chart"><canvas id="demo-chart" role="img" aria-label="4 serveurs disponibles, 1 à vérifier et 1 indisponible">4 disponibles, 1 à vérifier, 1 indisponible.</canvas></div>
<ul class="list-unstyled mt-3 mb-0"><li><span class="badge text-bg-success me-2">4</span>Disponibles</li><li><span class="badge text-bg-warning me-2">1</span>À vérifier</li><li><span class="badge text-bg-danger me-2">1</span>Indisponibles</li></ul>
</div></div></div>
</div>
</section>
<!-- 6. ONGLETS ET ACCORDÉON : adapter ensemble les id et data-bs-target. -->
<section id="organisation" class="card demo-section mb-4" aria-labelledby="organisation-title">
<div class="card-header"><h2 id="organisation-title" class="h5 mb-0">Onglets et accordéon</h2></div>
<div class="card-body">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation"><button id="demo-summary-tab" class="nav-link active" type="button" role="tab" data-bs-toggle="tab" data-bs-target="#demo-summary-panel" aria-controls="demo-summary-panel" aria-selected="true">Résumé</button></li>
<li class="nav-item" role="presentation"><button id="demo-details-tab" class="nav-link" type="button" role="tab" data-bs-toggle="tab" data-bs-target="#demo-details-panel" aria-controls="demo-details-panel" aria-selected="false">Détails</button></li>
</ul>
<div class="tab-content pt-3">
<div id="demo-summary-panel" class="tab-pane fade show active" role="tabpanel" aria-labelledby="demo-summary-tab" tabindex="0"><p>Zone de synthèse : placer ici les informations principales.</p></div>
<div id="demo-details-panel" class="tab-pane fade" role="tabpanel" aria-labelledby="demo-details-tab" tabindex="0"><dl class="row"><dt class="col-sm-3">Environnement</dt><dd class="col-sm-9">Démonstration</dd><dt class="col-sm-3">Source</dt><dd class="col-sm-9">Données fictives intégrées à la page</dd></dl></div>
</div>
<div class="accordion" id="demo-accordion">
<div class="accordion-item">
<h3 class="accordion-header" id="demo-accordion-title"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demo-accordion-panel" aria-expanded="false" aria-controls="demo-accordion-panel">Informations complémentaires</button></h3>
<div id="demo-accordion-panel" class="accordion-collapse collapse" aria-labelledby="demo-accordion-title" data-bs-parent="#demo-accordion"><div class="accordion-body">Ce bloc peut accueillir un tableau secondaire, une liste ou un détail technique.</div></div>
</div>
</div>
</div>
</section>
</main>
</div>
</div>
<!-- MODALE : placée hors de la grille pour éviter les contraintes de positionnement. -->
<div class="modal fade demo-modal" id="demo-modal" tabindex="-1" aria-labelledby="demo-modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable"><div class="modal-content">
<div class="modal-header"><h2 class="modal-title fs-5" id="demo-modal-title">Détail de démonstration</h2><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button></div>
<div class="modal-body"><p id="demo-modal-description" class="mb-0"></p></div>
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button></div>
</div></div>
</div>
<script src="/js/switch.js"></script>
<script>
// Interactions de démonstration uniquement : aucun appel réseau ni enregistrement.
document.querySelectorAll('[data-demo-action]').forEach(function (button) {
button.addEventListener('click', function () {
document.getElementById('demo-action-result').textContent = button.dataset.demoAction + ' : démonstration exécutée.';
});
});
document.getElementById('demo-modal').addEventListener('show.bs.modal', function (event) {
var server = event.relatedTarget ? event.relatedTarget.getAttribute('data-demo-server') : null;
document.getElementById('demo-modal-title').textContent = server || 'Détail de démonstration';
document.getElementById('demo-modal-description').textContent = server
? 'Informations fictives pour ' + server + '. Ajouter ici le contenu détaillé.'
: 'Exemple de fenêtre modale avec un titre, un contenu et un bouton de fermeture.';
});
document.getElementById('demo-form').addEventListener('submit', function (event) {
event.preventDefault();
document.getElementById('demo-form-result').textContent = 'Aperçu : '
+ document.getElementById('demo-name').value + ' — '
+ document.getElementById('demo-system').value + ' — '
+ document.getElementById('demo-capacity').value + ' Go — '
+ (document.getElementById('demo-enabled').checked ? 'Actif' : 'Inactif')
+ '. Aucune donnée enregistrée.';
});
document.getElementById('demo-form').addEventListener('reset', function () {
document.getElementById('demo-form-result').textContent = '';
});
// API Chart.js 2.x, correspondant au fichier local js/chart.min.js.
new Chart(document.getElementById('demo-chart').getContext('2d'), {
type: 'doughnut',
data: {
labels: ['Disponibles', 'À vérifier', 'Indisponibles'],
datasets: [{ data: [4, 1, 1], backgroundColor: ['#198754', '#ffc107', '#dc3545'], borderWidth: 0 }]
},
options: { responsive: true, maintainAspectRatio: false, legend: { display: false } }
});
// Conserver le thème du tableau après une recherche, un tri ou une pagination.
$('#demo-table').on('post-body.bs.table', function () {
$(this).toggleClass('table-dark', document.body.classList.contains('bg-dark'));
});
</script>
</body>
</html>