- Introduced `Storage/VIO.php` for monitoring VIO Fibre Channel status with client balancing checks and table sorting. - Updated `/include/en.php` and `/include/fr.php` to include new translations for ServiceNow and VIO-related navigation elements. - Improved SQL query and filtering logic in `/X/Inventory.php` to exclude VIO entries and add XenSam version data. - Enhanced `/reports/heartbeat.php` with a new mechanism for handling server heartbeat status, allowing better granularity and readability.
1582 lines
32 KiB
PHP
1582 lines
32 KiB
PHP
<?php ob_start(); ?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
|
|
<title>Web Infra Reports IT - Local Administrators</title>
|
|
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
|
|
|
|
<script src="/js/jquery-3.6.1.min.js"></script>
|
|
|
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
|
<link rel="stylesheet" href="/css/bootstrap-icons/bootstrap-icons.css">
|
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
|
|
<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/libs/js-xlsx/xlsx.core.min.js"></script>
|
|
|
|
<style>
|
|
html,
|
|
body {height: 100%;overflow: hidden;}
|
|
|
|
#content {
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#content > .row {
|
|
height: 100%;
|
|
}
|
|
|
|
#content .col.py-3 {
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Cards
|
|
============================================================ */
|
|
|
|
.summary-card {
|
|
min-height: 92px;
|
|
}
|
|
|
|
body.bg-dark .card {
|
|
--bs-card-bg: #2b3035;
|
|
--bs-card-color: #f8f9fa;
|
|
--bs-card-border-color: #495057;
|
|
|
|
background-color: #2b3035;
|
|
color: #f8f9fa;
|
|
border-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .card .text-muted {
|
|
color: #adb5bd !important;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Navigation tabs
|
|
============================================================ */
|
|
|
|
.nav-tabs .nav-link {
|
|
font-weight: 600;
|
|
}
|
|
|
|
body.bg-dark .nav-tabs {
|
|
border-bottom-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .nav-tabs .nav-link {
|
|
color: #adb5bd;
|
|
}
|
|
|
|
body.bg-dark .nav-tabs .nav-link:hover {
|
|
border-color: #495057 #495057 transparent;
|
|
color: #fff;
|
|
}
|
|
|
|
body.bg-dark .nav-tabs .nav-link.active {
|
|
background-color: #2b3035;
|
|
color: #fff;
|
|
border-color: #495057 #495057 #2b3035;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Tables
|
|
============================================================ */
|
|
|
|
.report-table {
|
|
font-size: .90rem;
|
|
}
|
|
|
|
.report-table td,
|
|
.report-table th {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.server-name {
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.admin-name {
|
|
font-family: Consolas, "Courier New", monospace;
|
|
font-size: .88rem;
|
|
}
|
|
|
|
/*
|
|
* Bootstrap Table applique la hauteur sur son container.
|
|
* Le scroll vertical se fera dans cette zone.
|
|
*/
|
|
.fixed-table-body {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Badges / boutons administrateurs
|
|
============================================================ */
|
|
|
|
.admin-badge {
|
|
display: inline-block;
|
|
margin: 2px 3px 2px 0;
|
|
padding: .30rem .50rem;
|
|
|
|
border: 0;
|
|
border-radius: .35rem;
|
|
|
|
background-color: #e9ecef;
|
|
color: #212529;
|
|
|
|
font-family: Consolas, "Courier New", monospace;
|
|
font-size: .78rem;
|
|
|
|
cursor: pointer;
|
|
}
|
|
|
|
.admin-badge:hover {
|
|
background-color: #ced4da;
|
|
}
|
|
|
|
body.bg-dark .admin-badge {
|
|
background-color: #495057;
|
|
color: #f8f9fa;
|
|
}
|
|
|
|
body.bg-dark .admin-badge:hover {
|
|
background-color: #6c757d;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Modal
|
|
============================================================ */
|
|
|
|
body.bg-dark .modal-content {
|
|
background-color: #2b3035;
|
|
color: #f8f9fa;
|
|
border-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .modal-header,
|
|
body.bg-dark .modal-footer {
|
|
border-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .btn-close {
|
|
filter: invert(1);
|
|
}
|
|
|
|
.modal-admin-name {
|
|
font-family: Consolas, "Courier New", monospace;
|
|
}
|
|
|
|
.server-modal-item {
|
|
font-family: Consolas, "Courier New", monospace;
|
|
font-size: .92rem;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Bootstrap Table / dark mode
|
|
============================================================ */
|
|
|
|
body.bg-dark .fixed-table-toolbar .search input {
|
|
background-color: #212529;
|
|
color: #f8f9fa;
|
|
border-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .fixed-table-toolbar .search input::placeholder {
|
|
color: #adb5bd;
|
|
}
|
|
|
|
body.bg-dark .pagination .page-link {
|
|
background-color: #212529;
|
|
border-color: #495057;
|
|
color: #f8f9fa;
|
|
}
|
|
|
|
body.bg-dark .pagination .active .page-link {
|
|
background-color: #0d6efd;
|
|
border-color: #0d6efd;
|
|
}
|
|
|
|
body.bg-dark .dropdown-menu {
|
|
background-color: #2b3035;
|
|
border-color: #495057;
|
|
}
|
|
|
|
body.bg-dark .dropdown-item {
|
|
color: #f8f9fa;
|
|
}
|
|
|
|
body.bg-dark .dropdown-item:hover {
|
|
background-color: #495057;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Responsive
|
|
============================================================ */
|
|
|
|
@media (max-width: 767.98px) {
|
|
|
|
.summary-card {
|
|
min-height: auto;
|
|
}
|
|
|
|
.report-table {
|
|
font-size: .80rem;
|
|
}
|
|
|
|
.admin-badge {
|
|
font-size: .72rem;
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body class="bg-light text-dark">
|
|
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/include/all.php"; ?>
|
|
|
|
<?php
|
|
|
|
/* ============================================================================
|
|
Helpers
|
|
========================================================================== */
|
|
|
|
function local_admin_h($value)
|
|
{
|
|
return htmlspecialchars(
|
|
(string)$value,
|
|
ENT_QUOTES | ENT_SUBSTITUTE,
|
|
'UTF-8'
|
|
);
|
|
}
|
|
|
|
|
|
/* ============================================================================
|
|
Lecture MySQL
|
|
========================================================================== */
|
|
|
|
$sql = "
|
|
SELECT
|
|
`Server`,
|
|
`Admins`
|
|
FROM infra.localadmins
|
|
ORDER BY `Server`
|
|
";
|
|
|
|
$rows = Invoke_Entry01($sql);
|
|
|
|
if (!is_array($rows)) {
|
|
$rows = [];
|
|
}
|
|
|
|
|
|
/* ============================================================================
|
|
Préparation des données
|
|
========================================================================== */
|
|
|
|
$servers = [];
|
|
$admins = [];
|
|
|
|
$serversWithoutAdmins = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
$server = trim((string)($row['Server'] ?? ''));
|
|
$rawAdmins = trim((string)($row['Admins'] ?? ''));
|
|
|
|
if ($server === '') {
|
|
continue;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Découpage des admins sur |
|
|
------------------------------------------------------------------------ */
|
|
|
|
$serverAdmins = [];
|
|
|
|
if ($rawAdmins !== '') {
|
|
|
|
foreach (explode('|', $rawAdmins) as $admin) {
|
|
|
|
$admin = trim($admin);
|
|
|
|
if ($admin === '') {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Évite les doublons dans une même ligne serveur.
|
|
*/
|
|
$key = strtolower($admin);
|
|
|
|
$serverAdmins[$key] = $admin;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Tri des admins du serveur
|
|
------------------------------------------------------------------------ */
|
|
|
|
natcasesort($serverAdmins);
|
|
|
|
|
|
if (count($serverAdmins) === 0) {
|
|
$serversWithoutAdmins++;
|
|
}
|
|
|
|
|
|
$servers[] = [
|
|
'server' => $server,
|
|
'admins' => array_values($serverAdmins)
|
|
];
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Construction de la vue inverse :
|
|
admin -> serveurs
|
|
------------------------------------------------------------------------ */
|
|
|
|
foreach ($serverAdmins as $admin) {
|
|
|
|
$key = strtolower($admin);
|
|
|
|
if (!isset($admins[$key])) {
|
|
|
|
$admins[$key] = [
|
|
'name' => $admin,
|
|
'servers' => []
|
|
];
|
|
|
|
}
|
|
|
|
/*
|
|
* Tableau associatif pour éviter les doublons de serveurs.
|
|
*/
|
|
$admins[$key]['servers'][strtolower($server)] = $server;
|
|
}
|
|
}
|
|
|
|
|
|
/* ============================================================================
|
|
Tri des serveurs
|
|
========================================================================== */
|
|
|
|
usort($servers, function ($a, $b) {
|
|
|
|
return strnatcasecmp(
|
|
$a['server'],
|
|
$b['server']
|
|
);
|
|
|
|
});
|
|
|
|
|
|
/* ============================================================================
|
|
Tri des admins et de leurs serveurs
|
|
========================================================================== */
|
|
|
|
foreach ($admins as &$admin) {
|
|
|
|
natcasesort($admin['servers']);
|
|
|
|
$admin['servers'] = array_values($admin['servers']);
|
|
}
|
|
|
|
unset($admin);
|
|
|
|
|
|
uasort($admins, function ($a, $b) {
|
|
|
|
return strnatcasecmp(
|
|
$a['name'],
|
|
$b['name']
|
|
);
|
|
|
|
});
|
|
|
|
|
|
/* ============================================================================
|
|
Compteurs
|
|
========================================================================== */
|
|
|
|
$totalServers = count($servers);
|
|
$totalAdmins = count($admins);
|
|
|
|
|
|
/*
|
|
* Nombre total d'affectations :
|
|
*
|
|
* 1 admin sur 10 serveurs = 10 affectations.
|
|
*/
|
|
$totalAssignments = 0;
|
|
|
|
foreach ($admins as $admin) {
|
|
$totalAssignments += count($admin['servers']);
|
|
}
|
|
|
|
|
|
/* ============================================================================
|
|
JSON utilisé par la modal
|
|
========================================================================== */
|
|
|
|
$adminsForJavascript = [];
|
|
|
|
foreach ($admins as $key => $admin) {
|
|
|
|
$adminsForJavascript[$key] = [
|
|
'name' => $admin['name'],
|
|
'servers' => $admin['servers']
|
|
];
|
|
}
|
|
|
|
?>
|
|
|
|
<div class="container-fluid" id="content">
|
|
|
|
<div class="row flex-nowrap">
|
|
|
|
<!-- =====================================================================
|
|
Navbar
|
|
===================================================================== -->
|
|
|
|
<div
|
|
class="col-auto col-md-2 col-xl-2 px-sm-2 px-0 bg-dark vh-100 position-sticky top-0"
|
|
style="-ms-flex:0 0 230px; flex:0 0 230px;"
|
|
>
|
|
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/navbar.html"; ?>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- =====================================================================
|
|
Contenu
|
|
===================================================================== -->
|
|
|
|
<div class="col py-3">
|
|
|
|
|
|
<!-- ===================================================================
|
|
Titre
|
|
=================================================================== -->
|
|
|
|
<h2>
|
|
|
|
<span class="badge text-bg-secondary w-100 text-wrap">
|
|
|
|
<i class="bi bi-shield-lock me-1"></i>
|
|
|
|
Local Administrators —
|
|
|
|
<span class="text-light">
|
|
<?php echo $totalServers; ?> serveurs
|
|
</span>
|
|
|
|
—
|
|
|
|
<span class="text-info">
|
|
<?php echo $totalAdmins; ?> administrateurs
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</h2>
|
|
|
|
|
|
<div class="container-fluid px-0">
|
|
|
|
|
|
<!-- =================================================================
|
|
Résumé
|
|
================================================================= -->
|
|
|
|
<div class="row g-2 mt-1 mb-4">
|
|
|
|
|
|
<!-- Serveurs -->
|
|
|
|
<div class="col-6 col-md-3">
|
|
|
|
<div class="card shadow-sm summary-card">
|
|
|
|
<div class="card-body text-center">
|
|
|
|
<div class="fs-3 fw-bold">
|
|
|
|
<?php echo $totalServers; ?>
|
|
|
|
</div>
|
|
|
|
<div class="text-muted small">
|
|
|
|
Serveurs
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Administrateurs -->
|
|
|
|
<div class="col-6 col-md-3">
|
|
|
|
<div class="card shadow-sm summary-card">
|
|
|
|
<div class="card-body text-center">
|
|
|
|
<div class="fs-3 fw-bold text-info">
|
|
|
|
<?php echo $totalAdmins; ?>
|
|
|
|
</div>
|
|
|
|
<div class="text-muted small">
|
|
|
|
Administrateurs distincts
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Affectations -->
|
|
|
|
<div class="col-6 col-md-3">
|
|
|
|
<div class="card shadow-sm summary-card">
|
|
|
|
<div class="card-body text-center">
|
|
|
|
<div class="fs-3 fw-bold text-primary">
|
|
|
|
<?php echo $totalAssignments; ?>
|
|
|
|
</div>
|
|
|
|
<div class="text-muted small">
|
|
|
|
Affectations admin / serveur
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Sans admin -->
|
|
|
|
<div class="col-6 col-md-3">
|
|
|
|
<div class="card shadow-sm summary-card">
|
|
|
|
<div class="card-body text-center">
|
|
|
|
<div
|
|
class="fs-3 fw-bold <?php echo $serversWithoutAdmins > 0 ? 'text-warning' : 'text-success'; ?>"
|
|
>
|
|
|
|
<?php echo $serversWithoutAdmins; ?>
|
|
|
|
</div>
|
|
|
|
<div class="text-muted small">
|
|
|
|
Serveurs sans admin renseigné
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- =================================================================
|
|
Onglets
|
|
================================================================= -->
|
|
|
|
<ul
|
|
class="nav nav-tabs"
|
|
id="localAdminsTabs"
|
|
role="tablist"
|
|
>
|
|
|
|
|
|
<!-- Par serveur -->
|
|
|
|
<li class="nav-item" role="presentation">
|
|
|
|
<button
|
|
class="nav-link active"
|
|
id="servers-tab"
|
|
data-bs-toggle="tab"
|
|
data-bs-target="#servers-panel"
|
|
type="button"
|
|
role="tab"
|
|
>
|
|
|
|
<i class="bi bi-hdd-rack me-1"></i>
|
|
|
|
Par serveur
|
|
|
|
<span class="badge text-bg-secondary ms-1">
|
|
|
|
<?php echo $totalServers; ?>
|
|
|
|
</span>
|
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
|
|
<!-- Par administrateur -->
|
|
|
|
<li class="nav-item" role="presentation">
|
|
|
|
<button
|
|
class="nav-link"
|
|
id="admins-tab"
|
|
data-bs-toggle="tab"
|
|
data-bs-target="#admins-panel"
|
|
type="button"
|
|
role="tab"
|
|
>
|
|
|
|
<i class="bi bi-people me-1"></i>
|
|
|
|
Par administrateur
|
|
|
|
<span class="badge text-bg-secondary ms-1">
|
|
|
|
<?php echo $totalAdmins; ?>
|
|
|
|
</span>
|
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
<!-- =================================================================
|
|
Contenu onglets
|
|
================================================================= -->
|
|
|
|
<div class="tab-content pt-3">
|
|
|
|
|
|
<!-- ===============================================================
|
|
PAR SERVEUR
|
|
=============================================================== -->
|
|
|
|
<div
|
|
class="tab-pane fade show active"
|
|
id="servers-panel"
|
|
role="tabpanel"
|
|
>
|
|
|
|
<table
|
|
id="tableServers"
|
|
class="table table-bordered table-hover table-sm report-table"
|
|
data-search="true"
|
|
data-search-highlight="true"
|
|
data-sortable="true"
|
|
data-show-columns="true"
|
|
data-show-export="true"
|
|
data-export-types='["csv","excel"]'
|
|
>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th
|
|
data-field="server"
|
|
data-sortable="true"
|
|
>
|
|
Serveur
|
|
</th>
|
|
|
|
<th
|
|
data-field="admins"
|
|
data-sortable="false"
|
|
>
|
|
Administrateurs locaux
|
|
</th>
|
|
|
|
<th
|
|
data-field="count"
|
|
data-sortable="true"
|
|
data-align="center"
|
|
>
|
|
Nombre
|
|
</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
<?php foreach ($servers as $row) : ?>
|
|
|
|
<tr>
|
|
|
|
<!-- Serveur -->
|
|
|
|
<td class="server-name">
|
|
|
|
<?php echo local_admin_h($row['server']); ?>
|
|
|
|
</td>
|
|
|
|
|
|
<!-- Admins -->
|
|
|
|
<td>
|
|
|
|
<?php if (count($row['admins']) === 0) : ?>
|
|
|
|
<span class="badge bg-warning text-dark">
|
|
|
|
Aucun administrateur renseigné
|
|
|
|
</span>
|
|
|
|
<?php else : ?>
|
|
|
|
<?php foreach ($row['admins'] as $admin) : ?>
|
|
|
|
<button
|
|
type="button"
|
|
class="admin-badge js-show-admin"
|
|
data-admin-key="<?php echo local_admin_h(strtolower($admin)); ?>"
|
|
title="Afficher les serveurs où cet administrateur est présent"
|
|
>
|
|
|
|
<?php echo local_admin_h($admin); ?>
|
|
|
|
</button>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
<?php endif; ?>
|
|
|
|
</td>
|
|
|
|
|
|
<!-- Nombre -->
|
|
|
|
<td class="text-center">
|
|
|
|
<?php echo count($row['admins']); ?>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- ===============================================================
|
|
PAR ADMINISTRATEUR
|
|
=============================================================== -->
|
|
|
|
<div
|
|
class="tab-pane fade"
|
|
id="admins-panel"
|
|
role="tabpanel"
|
|
>
|
|
|
|
<table
|
|
id="tableAdmins"
|
|
class="table table-bordered table-hover table-sm report-table"
|
|
data-search="true"
|
|
data-search-highlight="true"
|
|
data-sortable="true"
|
|
data-show-columns="true"
|
|
data-show-export="true"
|
|
data-export-types='["csv","excel"]'
|
|
>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th
|
|
data-field="admin"
|
|
data-sortable="true"
|
|
>
|
|
Administrateur
|
|
</th>
|
|
|
|
<th
|
|
data-field="count"
|
|
data-sortable="true"
|
|
data-align="center"
|
|
>
|
|
Serveurs
|
|
</th>
|
|
|
|
<th
|
|
data-field="action"
|
|
data-sortable="false"
|
|
data-align="center"
|
|
data-switchable="false"
|
|
>
|
|
Détail
|
|
</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
<?php foreach ($admins as $key => $admin) : ?>
|
|
|
|
<tr>
|
|
|
|
<!-- Admin -->
|
|
|
|
<td class="admin-name">
|
|
|
|
<?php echo local_admin_h($admin['name']); ?>
|
|
|
|
</td>
|
|
|
|
|
|
<!-- Nombre de serveurs -->
|
|
|
|
<td class="text-center">
|
|
|
|
<?php echo count($admin['servers']); ?>
|
|
|
|
</td>
|
|
|
|
|
|
<!-- Bouton détail -->
|
|
|
|
<td class="text-center">
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-outline-primary js-show-admin"
|
|
data-admin-key="<?php echo local_admin_h($key); ?>"
|
|
>
|
|
|
|
<i class="bi bi-list-ul me-1"></i>
|
|
|
|
Voir les serveurs
|
|
|
|
</button>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- =========================================================================
|
|
Modal détail administrateur
|
|
========================================================================= -->
|
|
|
|
<div
|
|
class="modal fade"
|
|
id="adminServersModal"
|
|
tabindex="-1"
|
|
aria-labelledby="adminServersModalLabel"
|
|
aria-hidden="true"
|
|
>
|
|
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
|
|
<div class="modal-content">
|
|
|
|
|
|
<!-- Header -->
|
|
|
|
<div class="modal-header">
|
|
|
|
<h5
|
|
class="modal-title"
|
|
id="adminServersModalLabel"
|
|
>
|
|
|
|
<i class="bi bi-person-lock me-1"></i>
|
|
|
|
<span id="modalAdminName" class="modal-admin-name"></span>
|
|
|
|
</h5>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn-close"
|
|
data-bs-dismiss="modal"
|
|
aria-label="Fermer"
|
|
></button>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Body -->
|
|
|
|
<div class="modal-body">
|
|
|
|
<div class="mb-3">
|
|
|
|
<span class="badge text-bg-primary">
|
|
|
|
<span id="modalServerCount">0</span>
|
|
|
|
serveur(s)
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
class="list-group"
|
|
id="modalServerList"
|
|
>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Footer -->
|
|
|
|
<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>
|
|
|
|
/* ============================================================================
|
|
Données PHP -> Javascript
|
|
========================================================================== */
|
|
|
|
const localAdmins = <?php
|
|
|
|
echo json_encode(
|
|
$adminsForJavascript,
|
|
JSON_UNESCAPED_UNICODE |
|
|
JSON_UNESCAPED_SLASHES |
|
|
JSON_HEX_TAG |
|
|
JSON_HEX_APOS |
|
|
JSON_HEX_AMP |
|
|
JSON_HEX_QUOT
|
|
);
|
|
|
|
?>;
|
|
|
|
|
|
/* ============================================================================
|
|
Initialisation
|
|
========================================================================== */
|
|
|
|
$(function () {
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Dark mode
|
|
------------------------------------------------------------------------ */
|
|
|
|
function isDark() {
|
|
|
|
return document.body.classList.contains('bg-dark');
|
|
|
|
}
|
|
|
|
|
|
function syncTheme($table) {
|
|
|
|
$table.toggleClass(
|
|
'table-dark',
|
|
isDark()
|
|
);
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Debounce
|
|
------------------------------------------------------------------------ */
|
|
|
|
function debounce(func, wait) {
|
|
|
|
let timeout;
|
|
|
|
return function () {
|
|
|
|
const context = this;
|
|
const args = arguments;
|
|
|
|
clearTimeout(timeout);
|
|
|
|
timeout = setTimeout(
|
|
function () {
|
|
|
|
func.apply(
|
|
context,
|
|
args
|
|
);
|
|
|
|
},
|
|
wait
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Calcul dynamique de la hauteur du tableau
|
|
------------------------------------------------------------------------ */
|
|
|
|
function updateTableHeight($table) {
|
|
|
|
if (!$table || $table.length === 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Impossible de calculer correctement la géométrie
|
|
* d'un tableau placé dans un onglet masqué.
|
|
*/
|
|
if (!$table.is(':visible')) {
|
|
return;
|
|
}
|
|
|
|
|
|
const $bootstrapTable =
|
|
$table.closest('.bootstrap-table');
|
|
|
|
|
|
if ($bootstrapTable.length === 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Le fixed-table-container commence juste sous la toolbar
|
|
* Bootstrap Table : recherche, export, choix colonnes...
|
|
*
|
|
* C'est donc le meilleur point de départ pour calculer
|
|
* l'espace restant jusqu'en bas de l'écran.
|
|
*/
|
|
const $container =
|
|
$bootstrapTable.find('.fixed-table-container');
|
|
|
|
|
|
if ($container.length === 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
const rect =
|
|
$container[0].getBoundingClientRect();
|
|
|
|
|
|
/*
|
|
* Petite marge en bas.
|
|
*/
|
|
const bottomMargin = 15;
|
|
|
|
|
|
let availableHeight =
|
|
window.innerHeight
|
|
- rect.top
|
|
- bottomMargin;
|
|
|
|
|
|
/*
|
|
* Evite une hauteur négative ou minuscule.
|
|
*/
|
|
availableHeight = Math.max(
|
|
availableHeight,
|
|
250
|
|
);
|
|
|
|
|
|
$table.bootstrapTable(
|
|
'resetView',
|
|
{
|
|
height: availableHeight
|
|
}
|
|
);
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Bootstrap Table
|
|
------------------------------------------------------------------------ */
|
|
|
|
function initTable($table) {
|
|
|
|
if ($table.length === 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
if (!$table.data('bt-initialized')) {
|
|
|
|
$table.bootstrapTable();
|
|
|
|
$table.data(
|
|
'bt-initialized',
|
|
true
|
|
);
|
|
|
|
} else {
|
|
|
|
$table.bootstrapTable(
|
|
'resetView'
|
|
);
|
|
|
|
}
|
|
|
|
|
|
syncTheme($table);
|
|
|
|
|
|
/*
|
|
* Laisse Bootstrap Table construire son DOM
|
|
* avant de mesurer la hauteur disponible.
|
|
*/
|
|
setTimeout(
|
|
function () {
|
|
|
|
updateTableHeight($table);
|
|
|
|
},
|
|
50
|
|
);
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Première table visible au chargement
|
|
------------------------------------------------------------------------ */
|
|
|
|
initTable(
|
|
$('#tableServers')
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Lorsqu'on change d'onglet
|
|
------------------------------------------------------------------------ */
|
|
|
|
$('button[data-bs-toggle="tab"]').on(
|
|
'shown.bs.tab',
|
|
function (event) {
|
|
|
|
/*
|
|
* Lecture directe de data-bs-target.
|
|
* Plus fiable que jQuery .data() avec les attributs Bootstrap 5.
|
|
*/
|
|
const target =
|
|
event.target.getAttribute('data-bs-target');
|
|
|
|
if (!target) {
|
|
return;
|
|
}
|
|
|
|
|
|
const $table =
|
|
$(target).find('table.report-table');
|
|
|
|
|
|
/*
|
|
* On laisse Bootstrap terminer complètement
|
|
* l'affichage de l'onglet avant de toucher à Bootstrap Table.
|
|
*/
|
|
setTimeout(
|
|
function () {
|
|
|
|
function initTable($table) {
|
|
|
|
if ($table.length === 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Important :
|
|
* ne jamais initialiser Bootstrap Table
|
|
* pendant que la table est cachée.
|
|
*/
|
|
if (!$table.is(':visible')) {
|
|
return;
|
|
}
|
|
|
|
|
|
if (!$table.data('bt-initialized')) {
|
|
|
|
/*
|
|
* Première ouverture de l'onglet :
|
|
* Bootstrap Table construit son DOM.
|
|
*/
|
|
$table.bootstrapTable();
|
|
|
|
$table.data(
|
|
'bt-initialized',
|
|
true
|
|
);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Table déjà initialisée :
|
|
* on force Bootstrap Table à recalculer son affichage.
|
|
*/
|
|
$table.bootstrapTable(
|
|
'resetView'
|
|
);
|
|
|
|
}
|
|
|
|
|
|
syncTheme($table);
|
|
|
|
|
|
/*
|
|
* Une fois le DOM Bootstrap Table construit,
|
|
* calcul de la hauteur disponible.
|
|
*/
|
|
setTimeout(
|
|
function () {
|
|
|
|
$table.bootstrapTable(
|
|
'resetView'
|
|
);
|
|
|
|
updateTableHeight(
|
|
$table
|
|
);
|
|
|
|
},
|
|
50
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
10
|
|
);
|
|
|
|
}
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Changement clair / sombre
|
|
------------------------------------------------------------------------ */
|
|
|
|
$('#lightSwitch').on(
|
|
'change',
|
|
function () {
|
|
|
|
setTimeout(
|
|
function () {
|
|
|
|
$('table.report-table').each(
|
|
function () {
|
|
|
|
syncTheme(
|
|
$(this)
|
|
);
|
|
|
|
}
|
|
);
|
|
|
|
},
|
|
0
|
|
);
|
|
|
|
}
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Modal administrateur
|
|
------------------------------------------------------------------------ */
|
|
|
|
const adminModal = new bootstrap.Modal(
|
|
document.getElementById('adminServersModal')
|
|
);
|
|
|
|
|
|
$(document).on(
|
|
'click',
|
|
'.js-show-admin',
|
|
function () {
|
|
|
|
const key = String(
|
|
$(this).data('admin-key') || ''
|
|
).toLowerCase();
|
|
|
|
|
|
if (!localAdmins[key]) {
|
|
return;
|
|
}
|
|
|
|
|
|
const admin =
|
|
localAdmins[key];
|
|
|
|
|
|
/* Nom */
|
|
|
|
$('#modalAdminName').text(
|
|
admin.name
|
|
);
|
|
|
|
|
|
/* Compteur */
|
|
|
|
$('#modalServerCount').text(
|
|
admin.servers.length
|
|
);
|
|
|
|
|
|
/* Liste */
|
|
|
|
const $list =
|
|
$('#modalServerList');
|
|
|
|
|
|
$list.empty();
|
|
|
|
|
|
admin.servers.forEach(
|
|
function (server) {
|
|
|
|
$('<div>')
|
|
.addClass(
|
|
'list-group-item server-modal-item'
|
|
)
|
|
.text(server)
|
|
.appendTo($list);
|
|
|
|
}
|
|
);
|
|
|
|
|
|
/*
|
|
* Thème sombre des éléments créés dynamiquement.
|
|
*/
|
|
if (isDark()) {
|
|
|
|
$list
|
|
.find('.list-group-item')
|
|
.addClass(
|
|
'bg-dark text-light border-secondary'
|
|
);
|
|
|
|
}
|
|
|
|
|
|
adminModal.show();
|
|
|
|
}
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Le modal peut être ouvert puis le thème changé
|
|
------------------------------------------------------------------------ */
|
|
|
|
$('#lightSwitch').on(
|
|
'change',
|
|
function () {
|
|
|
|
setTimeout(
|
|
function () {
|
|
|
|
const dark =
|
|
isDark();
|
|
|
|
|
|
$('#modalServerList .list-group-item')
|
|
.toggleClass(
|
|
'bg-dark text-light border-secondary',
|
|
dark
|
|
);
|
|
|
|
},
|
|
0
|
|
);
|
|
|
|
}
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Resize navigateur
|
|
------------------------------------------------------------------------ */
|
|
|
|
$(window).on(
|
|
'resize',
|
|
debounce(
|
|
function () {
|
|
|
|
$('table.report-table:visible').each(
|
|
function () {
|
|
|
|
const $table =
|
|
$(this);
|
|
|
|
|
|
if ($table.data('bt-initialized')) {
|
|
|
|
updateTableHeight(
|
|
$table
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
);
|
|
|
|
},
|
|
150
|
|
)
|
|
);
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
Sécurité :
|
|
deuxième calcul après rendu complet de la page
|
|
------------------------------------------------------------------------ */
|
|
|
|
setTimeout(
|
|
function () {
|
|
|
|
$('table.report-table:visible').each(
|
|
function () {
|
|
|
|
const $table =
|
|
$(this);
|
|
|
|
|
|
if ($table.data('bt-initialized')) {
|
|
|
|
updateTableHeight(
|
|
$table
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
);
|
|
|
|
},
|
|
250
|
|
);
|
|
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|