Compare commits

...

51 Commits

Author SHA1 Message Date
d5fc01f78f remove member 0 2025-07-09 11:26:10 +02:00
1bcb980fd6 hide hybrid, better print 2025-07-08 17:50:57 +02:00
6193ae3edb ptn 2025-05-25 14:04:33 +02:00
0a4921646e little changes 2025-05-22 13:58:14 +02:00
abc3d30473 little fixes 2025-05-20 14:56:27 +02:00
8c4ae9626f fix tagged 2025-05-20 14:53:30 +02:00
a6189199ee untagged color 2025-05-20 14:33:09 +02:00
35f9760b24 Merge remote-tracking branch 'origin/HEAD' 2025-05-20 11:41:57 +02:00
f1dad26944 change hybrid colors 2025-05-20 11:38:54 +02:00
266ed7e24e ; 2025-05-20 11:13:02 +02:00
267303d4ce check file_exists 2025-05-20 00:30:25 +02:00
5f087b1689 no id for label 2025-05-19 22:43:02 +02:00
30b47dedde [style*="--pvid: ${pvid}"] 2025-05-19 19:15:58 +02:00
341b017c01 404 2025-05-19 18:15:11 +02:00
d9415c7fa6 change url 2025-05-19 17:11:28 +02:00
5a63ac033e schéma 2025-05-19 16:23:52 +02:00
25db944ae5 fix trunk vlan 2025-05-19 16:21:50 +02:00
2efdbb339a locale 2025-05-19 15:02:20 +02:00
e988e09b7f Merge remote-tracking branch 'origin/HEAD' 2025-05-19 14:55:24 +02:00
a36abb0ddf custom colors 2025-05-19 14:52:05 +02:00
3886890c06 favicon 2025-05-07 15:08:48 +02:00
ea88991a9d Revert "7 segments"
This reverts commit 6258874114.
2025-05-06 17:54:34 +02:00
6258874114 7 segments 2025-05-03 02:04:03 +02:00
c8c376f1dd hybrid 2025-04-30 15:33:15 +02:00
5c625b0ec2 hybrid return 2025-04-15 11:17:54 +02:00
93b7498b75 rename 2025-04-11 17:08:07 +02:00
c5c2dc5a10 tweak style 2025-04-10 17:26:17 +02:00
74c0812128 pointer 2025-04-03 14:19:51 +02:00
98f38c51d8 margin li 2025-04-02 16:48:57 +02:00
b54b4f6bfc TMI 2025-04-02 16:37:41 +02:00
832a0c1232 TMI 2025-04-02 16:00:47 +02:00
8dbc082b37 print tweaks 2025-04-02 15:32:54 +02:00
e74d49e34c flex 2025-04-02 12:09:53 +02:00
88228aedae split patterns 2025-04-02 09:30:53 +02:00
68fcc088c6 fix tagged pattern 2025-04-02 09:02:03 +02:00
75d16a5e41 text-shadow 2025-04-02 01:23:45 +02:00
70b3644b8d desature 2025-04-02 01:19:41 +02:00
c628ee81d7 shutdown style 2025-04-02 01:17:34 +02:00
dd419bd0fa 15deg 2025-04-02 01:11:25 +02:00
b4daa46e1b better regex for 'port hybrid vlan tagged' 2025-04-01 18:13:35 +02:00
9d7a44805e fix print issue on firefox 2025-04-01 18:09:38 +02:00
3dc55e372c style tweaks 2025-04-01 17:38:11 +02:00
a17114cde1 tweak style 2025-04-01 15:57:37 +02:00
06ce10d919 rj45 inverse 2025-04-01 08:50:38 +02:00
0e23934b4e url 2025-04-01 08:18:15 +02:00
a8a7b8692a style fixes 2025-04-01 02:14:35 +02:00
144edde8e1 rj45 2025-04-01 02:01:47 +02:00
9d5e23d8f0 color 2025-03-31 19:23:46 +02:00
b208841a45 O/2 2025-03-31 19:23:36 +02:00
fe8274828e fix style 2025-03-31 15:15:56 +02:00
5ec8109f1d undo border-collapse for trunk 2025-03-31 15:07:55 +02:00
9 changed files with 406 additions and 241 deletions

4
config.php Normal file
View File

@ -0,0 +1,4 @@
<?php
$basedir = __DIR__ . "/confs";
$locale = "fr_FR.UTF-8";

0
custom.css Normal file
View File

1
favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg role="img" focusable="false" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><path fill="#cfd8dc" d="M12.99999994 11.66666662c0 .73633333-.597 1.33333332-1.33333332 1.33333332H2.33333338c-.73633333 0-1.33333332-.597-1.33333332-1.33333332V2.33333338c0-.73633333.597-1.33333332 1.33333332-1.33333332h9.33333324c.73633333 0 1.33333332.597 1.33333332 1.33333332v9.33333324z"/><g fill="#455a64"><path d="M2.66666671 3.33333337h8.66666658v5.33333328H2.66666671z"/><path d="M4.00000003 7.33333333h5.99999994v2.33333331H4.00000003z"/><path d="M5.33333335 8.66666665h3.3333333v1.99999998h-3.3333333z"/></g><path fill="#fdd835" d="M3.6666667 4.00000003h.66666666v2.33333331H3.6666667zm.99999999 0h.66666666v2.33333331h-.66666666zm.99999999 0h.66666666v2.33333331h-.66666666zm.99999999 0h.66666666v2.33333331h-.66666666zm.99999999 0h.66666666v2.33333331h-.66666666zm.99999999 0h.66666666v2.33333331h-.66666666zm.99999999 0h.66666666v2.33333331h-.66666666z"/></svg>

After

Width:  |  Height:  |  Size: 984 B

View File

@ -1,18 +1,29 @@
<?php
require_once "config.php";
setlocale(LC_CTYPE, $locale);
if ($_SERVER["QUERY_STRING"] != "") {
include "switch.php";
exit;
}
?>
<!DOCTYPE HTML>
<html lang='fr'>
<head>
<title>Schémas des VLANs</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="container">
<h1>Schémas des VLANs</h1>
<div class="file-list">
<ul>
<?php
$basedir = __DIR__ . "/confs";
function recursive_ls($path) {
<head>
<title>Schémas des VLANs</title>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="icon" type="image/svg" href="favicon.svg">
</head>
<body>
<h1>Schémas des VLANs</h1>
<div class="file-list">
<ul>
<?php
function recursive_ls($path) {
global $basedir;
if (substr(basename($path), 0, 1) == '.') {
@ -36,16 +47,16 @@
}
if (substr($path, -4) == ".cfg") {
return "<li><a href='show.php?switch=".str_replace("$basedir/", "", $path)."' target='_blank'>" . basename($path) . "</a></li>\n";
return "<li><a href='?" . str_replace("$basedir/", "", $path) . "' target='_blank'>" . basename($path) . "</a></li>\n";
}
return "";
}
}
echo recursive_ls($basedir);
echo recursive_ls($basedir);
?>
</ul>
</div>
</div>
</ul>
</div>
</body>
</html>

40
rj45-inverse.svg Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916666"
version="1.1"
id="svg1"
xml:space="preserve"
sodipodi:docname="rj45-inverse.svg"
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="36.75"
inkscape:cx="9.9863946"
inkscape:cy="10"
inkscape:window-width="1680"
inkscape:window-height="979"
inkscape:window-x="1912"
inkscape:window-y="19"
inkscape:window-maximized="1"
inkscape:current-layer="layer1-2" /><defs
id="defs1" /><g
id="layer1-2"
transform="rotate(180,2.6458334,2.6458334)"><path
id="rect1-4"
style="fill:#34495e;fill-opacity:1;stroke-width:0.070003"
d="M 0,0 V 5.2916667 H 5.2916667 V 0 H 2.8222222 Z m 2.2381957,0.19616086 h 0.9264484 c 0.1791197,0 0.3233547,0.15265851 0.3233547,0.37450931 v 0.0861369 h 1.1373691 c 0.2740052,0 0.4168283,0.1937523 0.4152798,0.53312043 l -0.01368,2.9981332 C 5.0254188,4.5274292 4.8856927,4.7209964 4.6116875,4.7209964 H 0.67997917 c -0.27400514,0 -0.41696486,-0.1935672 -0.41541633,-0.5329357 L 0.27824324,1.1899275 C 0.27979177,0.85055938 0.41965443,0.65680703 0.69365957,0.65680703 H 1.9148412 v -0.0861369 c 0,-0.2218508 0.1442347,-0.37450927 0.3233545,-0.37450927 z"
sodipodi:nodetypes="ccccccssscsscssssscss" /></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

39
rj45.svg Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916666"
version="1.1"
id="svg1"
xml:space="preserve"
sodipodi:docname="rj45.svg"
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="36.75"
inkscape:cx="10"
inkscape:cy="10"
inkscape:window-width="1680"
inkscape:window-height="979"
inkscape:window-x="1912"
inkscape:window-y="19"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
id="layer1"><path
id="rect1"
style="fill:#34495e;fill-opacity:1;stroke-width:0.070003"
d="M 0,0 V 5.2916667 H 5.2916667 V 0 H 2.8222222 Z m 2.2381957,0.19616086 h 0.9264484 c 0.1791197,0 0.3233547,0.15265851 0.3233547,0.37450931 v 0.0861369 h 1.1373691 c 0.2740052,0 0.4168283,0.1937523 0.4152798,0.53312043 l -0.01368,2.9981332 C 5.0254188,4.5274292 4.8856927,4.7209964 4.6116875,4.7209964 H 0.67997917 c -0.27400514,0 -0.41696486,-0.1935672 -0.41541633,-0.5329357 L 0.27824324,1.1899275 C 0.27979177,0.85055938 0.41965443,0.65680703 0.69365957,0.65680703 H 1.9148412 v -0.0861369 c 0,-0.2218508 0.1442347,-0.37450927 0.3233545,-0.37450927 z"
sodipodi:nodetypes="ccccccssscsscssssscss" /></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

125
show.php
View File

@ -1,125 +0,0 @@
<?php
$basedir = __DIR__ . "/confs/";
$path = realpath($basedir . filter_input(INPUT_GET, "switch", FILTER_SANITIZE_STRING));
if (strpos($path, $basedir) !== 0 || substr($path, -4) != ".cfg") {
header('HTTP/1.1 404 Not Found');
die();
}
$conf = file_get_contents($path);
preg_match("/ sysname ([\w-]+)/", $conf, $sysname);
preg_match("/ip address ([\d.]+)/", $conf, $address);
preg_match_all("/(?<=\n)vlan (?P<pvid>\d+)[\r\n]+(?: name (?P<name>.+)[\r\n]+| description (?P<description>.+)[\r\n]+| .*[\r\n]+)*/", $conf, $vlans, PREG_SET_ORDER);
preg_match_all("/(?<=\n)interface [\w-]+(?P<member>\d+)\/0\/(?P<port>\d+)[\r\n]+(?: port hybrid (?:pvid )?vlan (?:(?P<tagged>\d+) tagged|(?P<untagged>\d+)(?: \d+)* untagged)[\r\n]+| port (?:access |trunk |hybrid |pvid |vlan )*(?P<pvid>\d+)[\r\n]+| voice-vlan (?P<voice_vlan>\d+) enable[\r\n]+| .*[\r\n]+)*(?<!#)/", $conf, $interfaces, PREG_SET_ORDER);
$stack = array();
foreach ($interfaces as $interface) {
if (!isset($stack[$interface["member"]])) $stack[$interface["member"]] = array();
$interface["style"] = "";
if (!empty($interface["pvid"])) $interface["style"] .= "--pvid: {$interface["pvid"]}; ";
if (!empty($interface["tagged"])) $interface["style"] .= "--tagged: {$interface["tagged"]}; ";
if (!empty($interface["untagged"])) $interface["style"] .= "--untagged: {$interface["untagged"]}; ";
$stack[$interface["member"]][$interface["port"]] = $interface;
}
/*echo ("<!--");
var_dump($interfaces);
echo ("-->");*/
?>
<!DOCTYPE HTML>
<html lang='fr'>
<head>
<title><?= $sysname[1] ?? "Switch sans nom" ?> - Tableau des VLANs</title>
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<header>
<h1>
<div><?= $sysname[1] ?? "Switch sans nom" ?></div>
<small><a href="https://<?= $address[1] ?>" target="_blank" class="link"><?= $address[1] ?></a></small>
</h1>
</header>
<main>
<table>
<caption>
<h2>Interfaces</h2>
</caption>
<tbody>
<?php
function display_interface($interface, $odd)
{
if ($interface["port"] % 2 == $odd) {
echo "<td class='{$interface[0]}" . (isset($interface["voice_vlan"]) ? " voice_vlan" : "") . "' title='{$interface[0]}' style='{$interface["style"]}'>{$interface["port"]}</td>\n";
}
}
foreach ($stack as $member => $interfaces) {
echo "<tr>\n<th>$member</th>\n<td>\n<table class='member'>\n<tbody>\n<tr>\n";
foreach ($interfaces as $interface) display_interface($interface, 1);
echo "</tr>\n<tr>\n";
foreach ($interfaces as $interface) display_interface($interface, 0);
echo "</tr>\n</tbody>\n</table>\n</td>\n</tr>\n";
}
?>
</tbody>
</table>
<table class='legend'>
<caption>
<h2>Légende</h2>
</caption>
<thead>
<tr>
<th>PVID</th>
<th>Nom</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<?php
foreach ($vlans as $vlan) {
if (isset($vlan["pvid"]) and $vlan["pvid"] != 1) {
$name = $vlan["name"] ?? "";
$description = $vlan["description"] ?? "";
echo "<tr title='{$vlan[0]}'><td class='interface vlan' style='--pvid: {$vlan["pvid"]}'>{$vlan["pvid"]}</td><td>$name</td><td>$description</td></tr>";
}
}
?>
<tr>
<td class='interface trunk'></td>
<td colspan='2'>Trunk</td>
</tr>
<tr>
<td class='interface hybrid' style='--tagged:60; --untagged:0'></td>
<td colspan='2'>Hybride (tagged/untagged)</td>
</tr>
<tr>
<td class='interface poe'></td>
<td colspan='2'>Power on Ethernet</td>
</tr>
<tr>
<td class='interface voice_vlan'></td>
<td colspan='2'>ToIP (voice-vlan)</td>
</tr>
<tr>
<td class='interface shutdown'></td>
<td colspan='2'>Interface désactivée</td>
</tr>
</tbody>
</table>
</main>
<footer>
<label id="colorSliderLabel" for="colorSlider" class="no-print">Changer les couleurs</label>
<input id="colorSlider" type="range" min="0" max="2000000" step="0.000000001" value="1353651.53435435"
oninput="document.documentElement.style.setProperty('--k', this.value);" class="no-print"/>
<a href="<?=str_replace(__DIR__."/", "", $path) ?>" target="_blank" class="link no-print">Télécharger la configuration</a>
<a href="index.php" class="link no-print">← Retour à la liste</a>
</footer>
</div>
</body>
</html>

196
style.css
View File

@ -1,24 +1,26 @@
:root {
--k: 1353651.53435435;
--hue: 58.3;
--saturation: 90%;
--lightness: 65%;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
overflow: visible !important;
}
body {
margin: 0;
padding: 2rem 1rem;
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
display: flex;
flex-direction: column;
height: 100vh;
}
h1 {
@ -29,6 +31,7 @@ h1 {
h2 {
color: #34495e;
text-align: center;
margin-bottom: 0.5rem;
}
@ -40,6 +43,7 @@ h2 {
transition: background-color 0.3s;
margin-bottom: 0.5em;
font-weight: bold;
cursor: pointer;
}
.file-list summary:hover {
@ -59,7 +63,7 @@ h2 {
}
.file-list li {
margin-left: 0.5rem;
margin-left: 1.2rem;
}
.file-list a::before {
@ -93,119 +97,140 @@ h2 {
}
main {
flex-grow: 2;
display: flex;
flex-flow: wrap;
align-items: center;
justify-content: space-around;
gap: 1rem;
margin-bottom: 2rem;
}
ul {
list-style: none;
}
details {
cursor: pointer;
}
main > table {
margin: auto;
}
td {
text-align: left;
}
.member {
display: flex;
align-items: center;
border: 6px outset #476079;
border-radius: 4px;
background-color: #34495e;
margin-bottom: 0.5rem;
print-color-adjust: exact;
}
.member-id {
background: #888;
align-content: center;
color: lightgreen;
text-shadow: 0 1px 3px #fffb;
font-weight: bold;
border: 2px inset black;
font-family: monospace;
margin: 1em 0.5em;
padding: 0.12em 0.4em;
}
.interfaces {
border-spacing: 0;
margin: 0;
border: 8px solid #34495e;
border-radius: 6px;
}
.legend {
border-collapse: collapse;
border-spacing: 0;
}
.legend tbody {
border: 2px solid black;
}
.member td,
.legend td {
border: 2px inset #2c3e50;
background-color: white;
}
.interface {
position: relative;
text-align: center;
vertical-align: middle;
min-width: 2em;
height: 2em;
mix-blend-mode: darken;
/*! padding: 0; */
print-color-adjust: exact;
cursor: help;
transition: 0.2s background-color;
border: 3px inset #476079;
border-image-width: 5px;
border-image-slice: 3;
border-image-source: url(rj45.svg);
border-image-repeat: stretch;
background-color: white;
print-color-adjust: exact;
background-size: cover;
background-position: center;
background-origin: border-box;
}
.interface:hover {
[title] {
cursor: help;
}
.member tr:nth-child(even) .interface {
border-image-source: url(rj45-inverse.svg);
}
.interface:not(.vlan):hover {
background-color: #ddd;
}
.vlan {
--pvid: 0;
--pvid-color: hsl(
calc(var(--hue) * var(--pvid)) var(--saturation) var(--lightness)
);
color: #ecf0f1;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
background-color: hsl(calc(var(--k) * var(--pvid)) 97% 70%);
text-shadow: 1px 2px 2px black;
background-color: var(--pvid-color);
}
.vlan:hover {
background-color: hsl(calc(var(--k) * var(--pvid)) 65% 70%);
--saturation: 65%;
--lightness: 75%;
}
.trunk {
.trunk:not(.shutdown) {
font-weight: bold;
border: 4px solid !important;
border-image-slice: 1 !important;
border-image-source: linear-gradient(
127deg,
red,
background-image: linear-gradient(
140deg,
red 18%,
orange,
yellow,
green,
blue,
violet
) !important;
padding: 0;
}
.hybrid {
background-image: linear-gradient(
135deg,
hsl(calc(var(--k) * var(--tagged)) 100% 60%) 50%,
hsl(calc(var(--k) * var(--untagged)) 100% 60%) 50%
magenta,
violet 82%
);
}
/*.hybrid {
--tagged: 0;
--tagged-color: hsl(calc(var(--hue) * var(--tagged)) var(--saturation) var(--lightness));
--untagged: var(--pvid);
--untagged-color: hsl(calc(var(--hue) * var(--untagged)) var(--saturation) var(--lightness));
background-image: linear-gradient(145deg, transparent 65%, var(--tagged-color) 65%);
}*/
.shutdown:not([class*="loopback-detection action shutdown"]) {
background-color: lightgray !important;
background-image: none !important;
background-color: lightgray;
color: gray !important;
font-weight: normal;
text-shadow: none;
}
.poe::before {
content: "⚡";
font-size: 0.65em;
position: absolute;
top: -0.2em;
right: -0.25em;
}
.voice_vlan::after {
.voice_vlan::before {
content: "📞";
font-size: 0.65em;
position: absolute;
bottom: -0.15em;
left: 0;
bottom: -0.4em;
left: -0.4em;
}
.poe::after {
content: "⚡";
position: absolute;
top: -0.5em;
right: -0.5em;
}
.legend {
border-spacing: 0;
}
.legend td:not(.interface) {
border: 1px solid #2c3e50;
text-align: left;
padding: 0 0.3em;
}
footer {
@ -218,7 +243,22 @@ footer {
width: 33%;
}
input[type="color"] {
opacity: 0%;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
@media print {
body {
margin: auto;
background-color: unset;
}
.no-print {
display: none;
}

155
switch.php Normal file
View File

@ -0,0 +1,155 @@
<?php
$path = realpath($basedir . DIRECTORY_SEPARATOR . ltrim(urldecode($_SERVER["QUERY_STRING"]), '/'));
if (
strpos($path, $basedir) !== 0
|| substr($path, -4) != ".cfg"
|| !file_exists($path)
) {
http_response_code(404);
die("Fichier non trouvé");
}
$conf = file_get_contents($path);
if ($conf === false) {
http_response_code(404);
die("Fichier non trouvé");
}
preg_match("/ sysname ([\w-]+)/", $conf, $sysname);
preg_match("/ip address ([\d.]+)/", $conf, $address);
$startPtn = "(?<=[\r\n])";
$NL = "(?:[\r\n]+)";
$vlanPvidPtn = "vlan (?P<pvid>\d+)$NL";
$vlanNamePtn = "$startPtn name (?P<name>.+)$NL";
$vlanDescriptionPtn = "$startPtn description (?P<description>.+)$NL";
$otherPtn = "$startPtn .*$NL";
$endPtn = "(?<!#)";
preg_match_all("/$startPtn$vlanPvidPtn(?:$vlanNamePtn|$vlanDescriptionPtn|$otherPtn)*$endPtn/", $conf, $vlans, PREG_SET_ORDER);
$interfaceAddressPtn = "interface [\w-]+(?P<member>\d+)\/0\/(?P<port>\d+)$NL";
$pvidPtn = "$startPtn port (?:access|trunk pvid|hybrid pvid) vlan (?P<pvid>\d+)$NL";
$portHybridPtn = "$startPtn port hybrid vlan (?:(?P<tagged>\d+)(?: (?:to|\d+))* tagged|(?P<untagged>\d+)(?: \d+)* untagged)$NL";
$voiceVlanPtn = "$startPtn voice-vlan (?P<voice_vlan>\d+) enable$NL";
preg_match_all("/$startPtn$interfaceAddressPtn(?:$pvidPtn|$portHybridPtn|$voiceVlanPtn|$otherPtn)*$endPtn/", $conf, $interfaces, PREG_SET_ORDER);
$stack = array();
foreach ($interfaces as $interface) {
if ($interface["member"] == 0) continue;
if (!isset($stack[$interface["member"]])) $stack[$interface["member"]] = [[], []];
$interface["style"] = "";
if (!empty($interface["pvid"])) $interface["style"] .= "--pvid: {$interface["pvid"]};";
if (!empty($interface["tagged"])) $interface["style"] .= " --tagged: {$interface["tagged"]};";
if (!empty($interface["untagged"])) $interface["style"] .= " --untagged: {$interface["untagged"]};";
if (!empty($interface["voice_vlan"])) $interface["style"] .= " --voice-vlan: {$interface["voice_vlan"]};";
$stack[$interface["member"]][1 - $interface["port"] % 2][$interface["port"]] = $interface;
}
?>
<!DOCTYPE HTML>
<html lang='fr'>
<head>
<title><?= $sysname[1] ?? "Switch sans nom" ?> - Schéma des VLANs</title>
<link rel="icon" type="image/svg" href="favicon.svg">
<link href="style.css" rel="stylesheet" />
<link href="custom.css" rel="stylesheet" />
<style id="customColors"></style>
</head>
<body>
<header>
<h1>
<div><?= $sysname[1] ?? "Switch sans nom" ?></div>
<small><a href="https://<?= $address[1] ?>" target="_blank" class="link"><?= $address[1] ?></a></small>
</h1>
</header>
<main>
<div class="stack">
<h2>Interfaces</h2>
<?php
foreach ($stack as $member_id => $lines) {
echo "<div class='member'>\n<span class='member-id'>$member_id</span>\n<table class='interfaces'>\n<tbody>\n";
foreach ($lines as $interfaces) {
ksort($interfaces);
echo "<tr>\n";
foreach ($interfaces as $interface) {
echo "<td class='{$interface[0]}" . (isset($interface["voice_vlan"]) ? " voice_vlan" : "") . "' title='{$interface[0]}' style='{$interface["style"]}'>{$interface["port"]}</td>\n";
};
echo "</tr>\n";
}
echo "</tr>\n</tbody>\n</table>\n</div>\n";
}
?>
</div>
<table class='legend'>
<caption>
<h2>Légende</h2>
</caption>
<thead>
<tr>
<th>PVID</th>
<th>Nom</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<?php
foreach ($vlans as $vlan) {
if (isset($vlan["pvid"]) and $vlan["pvid"] != 1) {
$name = $vlan["name"] ?? "";
$description = $vlan["description"] ?? "";
echo "<tr title='{$vlan[0]}'><td class='interface vlan {$vlan["pvid"]}' style='--pvid: {$vlan["pvid"]};'>{$vlan["pvid"]}<input type='color' oninput='changeColor({$vlan["pvid"]}, this.value)' title='Changer la couleur' /></td><td>$name</td><td>$description</td></tr>";
}
}
?>
<tr>
<td class='interface trunk'></td>
<td colspan='2'>Trunk</td>
</tr>
<!--<tr>
<td class='interface hybrid' style='--tagged:60; --untagged:0'></td>
<td colspan='2'>Hybride (PVID / tagged)</td>
</tr>-->
<tr>
<td class='interface poe'></td>
<td colspan='2'>Power on Ethernet</td>
</tr>
<tr>
<td class='interface voice_vlan'></td>
<td colspan='2'>ToIP (voice-vlan)</td>
</tr>
<tr>
<td class='interface shutdown'></td>
<td colspan='2'>Interface désactivée</td>
</tr>
</tbody>
</table>
</main>
<footer>
<label for="colorSlider" class="no-print">Changer les couleurs (ou cliquez dans la légende)</label>
<input id="colorSlider" type="range" min="0" max="360" step="0.000000001" value="58.3"
oninput="document.documentElement.style.setProperty('--hue', this.value);" class="no-print" />
<a href="<?= str_replace(__DIR__ . "/", "", $path) ?>" target="_blank" class="link no-print">Télécharger la configuration</a>
<a href="." class="link no-print">← Retour à la liste</a>
</footer>
<script>
function changeColor(pvid, color) {
for (let i = customColors.sheet.cssRules.length - 1; i >= 0; i--) {
if (
(customColors.sheet.cssRules[i].selectorText == `[style*="--pvid: ${pvid};"]`) ||
(customColors.sheet.cssRules[i].selectorText == `[style*="--tagged: ${pvid};"]`) ||
(customColors.sheet.cssRules[i].selectorText == `[style*="--untagged: ${pvid};"]`)
) {
customColors.sheet.deleteRule(i)
}
}
customColors.sheet.insertRule(`[style*="--pvid: ${pvid};"] { --pvid-color: ${color} }`)
customColors.sheet.insertRule(`[style*="--tagged: ${pvid};"] { --tagged-color: ${color} }`)
customColors.sheet.insertRule(`[style*="--untagged: ${pvid};"] { --untagged-color: ${color} }`)
}
</script>
</body>
</html>