diff options
author | Minteck <contact@minteck.org> | 2022-11-11 23:47:49 +0100 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-11-11 23:47:49 +0100 |
commit | 209356b8ade1920b50d1d3a1a5e121c6623d167b (patch) | |
tree | 5301396987d1510f715a0b1c24754873af19e1dc /pages | |
parent | 2c4ae43e688a9873e86211ea0e7aeb9ba770dd77 (diff) | |
download | pluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.tar.gz pluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.tar.bz2 pluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.zip |
Update
Diffstat (limited to 'pages')
-rw-r--r-- | pages/about.inc | 2 | ||||
-rw-r--r-- | pages/byfront.inc | 188 | ||||
-rw-r--r-- | pages/computers.inc | 856 | ||||
-rw-r--r-- | pages/debug.inc | 4 | ||||
-rw-r--r-- | pages/docs.inc | 6 | ||||
-rw-r--r-- | pages/edit.inc | 2 | ||||
-rw-r--r-- | pages/parser.inc | 4 | ||||
-rw-r--r-- | pages/prefix.inc | 4 | ||||
-rw-r--r-- | pages/rules-old.inc | 253 | ||||
-rw-r--r-- | pages/rules.inc | 243 | ||||
-rw-r--r-- | pages/stats.inc | 172 | ||||
-rw-r--r-- | pages/together.inc | 1 | ||||
-rw-r--r-- | pages/travelling.inc | 9 |
13 files changed, 1291 insertions, 453 deletions
diff --git a/pages/about.inc b/pages/about.inc index d6b0eeb..3e89cf5 100644 --- a/pages/about.inc +++ b/pages/about.inc @@ -48,7 +48,7 @@ function prettySize($bytes) { if ($bytes > 1024) { if ($bytes > 1024**2) { if ($bytes > 1024**3) { - return round($bytes / 1024**3, 1) . " MB"; + return round($bytes / 1024**3, 1) . " GB"; } else { return round($bytes / 1024**2, 1) . " MB"; } diff --git a/pages/byfront.inc b/pages/byfront.inc index 280d981..77556a2 100644 --- a/pages/byfront.inc +++ b/pages/byfront.inc @@ -11,52 +11,100 @@ $travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includ <div id="page-content"> <h2>Members by last fronted</h2> - <h4>Cloudburst System</h4> - <?php $system = "ynmuc"; $members = array_map(function ($i) use ($system) { - $i["_lastFronted"] = -1; - $id = $i["id"]; - $memberData = $i; - - $fronters = array_map(function ($item) { - return $item["id"]; - }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["members"]); - - if (in_array($id, $fronters)) { - $i["_lastFronted"] = time(); - } else { - $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/switches.json"), true); - - $thisMember = array_filter($switches, function ($item) use ($memberData) { - return in_array($memberData["id"], $item["members"]); - }); - - $thisMember = array_values($thisMember); - $frontingEnd = null; - - if (count($thisMember) > 0) { - $thisIndex = array_search($thisMember[0], $switches); - - $frontingStart = $thisMember[0]; - $frontingEnd = $switches[$thisIndex - 1]; + <?php $members = [ + ...array_map(function ($i) { + $system = "ynmuc"; + $i["_lastFronted"] = -1; + $id = $i["id"]; + $memberData = $i; + + $fronters = array_map(function ($item) { + return $item["id"]; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["members"]); + + if (in_array($id, $fronters)) { + $i["_lastFronted"] = time(); + } else { + $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/switches.json"), true); + + $thisMember = array_filter($switches, function ($item) use ($memberData) { + return in_array($memberData["id"], $item["members"]); + }); + + $thisMember = array_values($thisMember); + $frontingEnd = null; + + if (count($thisMember) > 0) { + $thisIndex = array_search($thisMember[0], $switches); + + $frontingStart = $thisMember[0]; + $frontingEnd = $switches[$thisIndex - 1]; + } + + if ($frontingEnd !== null && isset($frontingStart)) { + $i["_lastFronted"] = strtotime($frontingEnd["timestamp"]); + } } - if ($frontingEnd !== null && isset($frontingStart)) { - $i["_lastFronted"] = strtotime($frontingEnd["timestamp"]); + return $i; + }, array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "ynmuc"; + }))), + ...array_map(function ($i) { + $system = "gdapd"; + $i["_lastFronted"] = -1; + $id = $i["id"]; + $memberData = $i; + + $fronters = array_map(function ($item) { + return $item["id"]; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["members"]); + + if (in_array($id, $fronters)) { + $i["_lastFronted"] = time(); + } else { + $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/switches.json"), true); + + $thisMember = array_filter($switches, function ($item) use ($memberData) { + return in_array($memberData["id"], $item["members"]); + }); + + $thisMember = array_values($thisMember); + $frontingEnd = null; + + if (count($thisMember) > 0) { + $thisIndex = array_search($thisMember[0], $switches); + + $frontingStart = $thisMember[0]; + $frontingEnd = $switches[$thisIndex - 1]; + } + + if ($frontingEnd !== null && isset($frontingStart)) { + $i["_lastFronted"] = strtotime($frontingEnd["timestamp"]); + } } - } - return $i; - }, array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "ynmuc"; - }))); uasort($members, function ($a, $b) { + return $i; + }, array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "gdapd"; + }))) + ]; uasort($members, function ($a, $b) { return $b["_lastFronted"] - $a["_lastFronted"]; - }); foreach ($members as $member): ?> - <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1fr 2fr max-content;"> + }); $members = array_reverse($members); foreach ($members as $member): if ($member["_lastFronted"] !== -1 && $member["_lastFronted"] !== time()): ?> + <div class="relation" style="background-color:<?php if ($member["_lastFronted"] > time() - (86400 * 14)): ?>rgba(255, 255, 255, .1)<?php else: ?><?php if ($member["_lastFronted"] > time() - (86400 * 30)): ?>rgba(255, 227, 0, 0.17)<?php else: ?>rgba(255,55,55,0.17)<?php endif; ?>;<?php endif; ?>;margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1fr 2fr max-content;<?php if ($travelling[$member['id']]["travelling"]): ?>opacity: 0.75;<?php endif; ?>"> <a class="relation-intro" style="background-color:rgba(255, 255, 255, .05);border-right:1px solid rgba(255, 255, 255, .1);margin:-10px;padding:10px;border-top-left-radius:10px;border-bottom-left-radius:10px;color: white;display:flex;align-items:center;text-decoration: none;" href="/<?= $member["name"] ?>"> <img src="<?= getAsset($member['system'], $member["id"], "heads") ?>" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> - <?php if ($member["_metadata"]["shared_memory"] !== 2): ?> - <span class="badge text-black bg-warning rounded-pill">Not sharing memory</span> - <?php endif; ?> + <span style="display: inline-block;margin-left: auto;"> + <?php if ($member["_metadata"]["shared_memory"] !== 2): ?> + <span class="badge text-black bg-warning rounded-pill">Memory</span> + <?php endif; ?> + <?php if ($travelling[$member['id']]["travelling"]): ?> + <span class="badge text-white bg-secondary rounded-pill">Travelling</span> + <?php endif; ?> + <?php if ($member["_lastFronted"] < time() - (86400 * 30)): ?> + <span class="badge text-white bg-danger rounded-pill">Must front</span> + <?php endif; ?> + </span> </a> <div class="relation-item" style="display:flex;align-items:center;margin-left:10px;padding:0 20px;"> @@ -67,67 +115,7 @@ $travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includ <?php endif; ?> </div> </div> - <?php endforeach; ?> - - <br> - <h4>Raindrops System</h4> - <?php $system = "gdapd"; $members = array_map(function ($i) use ($system) { - $i["_lastFronted"] = -1; - $id = $i["id"]; - $memberData = $i; - - $fronters = array_map(function ($item) { - return $item["id"]; - }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["members"]); - - if (in_array($id, $fronters)) { - $i["_lastFronted"] = time(); - } else { - $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/switches.json"), true); - - $thisMember = array_filter($switches, function ($item) use ($memberData) { - return in_array($memberData["id"], $item["members"]); - }); - - $thisMember = array_values($thisMember); - $frontingEnd = null; - - if (count($thisMember) > 0) { - $thisIndex = array_search($thisMember[0], $switches); - - $frontingStart = $thisMember[0]; - $frontingEnd = $switches[$thisIndex - 1]; - } - - if ($frontingEnd !== null && isset($frontingStart)) { - $i["_lastFronted"] = strtotime($frontingEnd["timestamp"]); - } - } - - return $i; - }, array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "gdapd"; - }))); uasort($members, function ($a, $b) { - return $b["_lastFronted"] - $a["_lastFronted"]; - }); foreach ($members as $member): ?> - <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1fr 2fr max-content;"> - <a class="relation-intro" style="background-color:rgba(255, 255, 255, .05);border-right:1px solid rgba(255, 255, 255, .1);margin:-10px;padding:10px;border-top-left-radius:10px;border-bottom-left-radius:10px;color: white;display:flex;align-items:center;text-decoration: none;" href="/<?= $member["name"] ?>"> - <img src="<?= getAsset($member['system'], $member["id"], "heads") ?>" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> - <?php if ($member["_metadata"]["shared_memory"] !== 2): ?> - <span class="badge text-black bg-warning rounded-pill">Not sharing memory</span> - <?php endif; ?> - </a> - - <div class="relation-item" style="display:flex;align-items:center;margin-left:10px;padding:0 20px;"> - <?php if ($member["_lastFronted"] === -1): ?> - Never fronted - <?php else: ?> - Last fronted <?= timeAgo($member["_lastFronted"]) ?><?php if ($member["_lastFronted"] !== time()): ?> (<?= date('l j F Y', $member["_lastFronted"]) ?>)<?php endif; ?> - <?php endif; ?> - </div> - </div> - <?php endforeach; ?> - </div> + <?php endif; endforeach; ?> <style> @media (max-width: 991px) { diff --git a/pages/computers.inc b/pages/computers.inc new file mode 100644 index 0000000..c2d6437 --- /dev/null +++ b/pages/computers.inc @@ -0,0 +1,856 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $pagename; +$computer = []; +$parts = explode("/", $pagename); + +if (isset($parts[1])) { + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/computers/metadata/" . $parts[1] . ".json")) { + $computer = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/computers/metadata/" . $parts[1] . ".json"), true); + $id = $parts[1]; + $owner = explode("-", $id)[0]; + + if (count($parts) > 2) { + $title = "Remote control · " . $computer["host"] . " · " . $title; + } else { + $title = $computer["host"] . " · " . $title; + } + } else { + header("Location: /-/computers"); + die(); + } +} + +if (isset($parts[2]) && $parts[2] !== "control") { + header("Location: /-/computers/" . $parts[1]); + die(); +} + +require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; + +$parts = array_values(array_filter($parts, function ($i) { + return $i !== "-"; +})); + +if (count($parts) === 2 || count($parts) === 3) { + array_unshift($parts, null); +} + +?> + +<br> +<div class="container"> + <?php if (isset($parts[2]) && !isset($parts[3])): $id = $parts[2]; ?> + <div> + <h2> + <span style="vertical-align: middle;"><?= $computer["host"] ?></span> + <a href="/-/computers" class="small btn btn-outline-light" style="float:right;margin-top:5px;vertical-align:middle;opacity:1 !important; color:white;">Back</a> + </h2> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Overview</h4> + <style> + @media (max-width: 700px) { + #g-0 { + grid-template-columns: 1fr !important; + } + + #g-0 img, #g-0 table { + display: inline-block; + margin-left: auto; + margin-right: auto; + } + + #g-0 table { + margin-top: 20px; + } + } + </style> + <div style="display: grid; grid-template-columns: 256px 1fr; grid-column-gap: 30px;" id="g-0"> + <div style="display: flex; align-items: center;"> + <img style="width: 256px;" src="https://ponies.equestria.horse/api/data?f=computers/screens/<?= $id . "-" . $computer["screens"][0]["id"] ?>.jpg"> + </div> + <div style="display: flex; align-items: center;"> + <table> + <tbody> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Owner:</b></td> + <td><?= $owner === "raindrops" ? "Raindrops System" : "Cloudburst System" ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Luna version:</b></td> + <td><?= $computer["luna_version"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>System:</b></td> + <td><?= $computer["os"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>OS kernel:</b></td> + <td><?= $computer["kernel"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Serial:</b></td> + <td><?= $computer["serial"] ?> (<?= $computer["serial_source"] === "hardware" ? "logic board" : "OS" ?>)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Last seen:</b></td> + <td><?= date('j M, g:ia (T)', strtotime($computer["date"])) ?>, <?= timeAgo($computer["date"]) ?></td> + </tr> + </tbody> + </table> + </div> + </div> + + <table><tbody> + <tr> + <td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Processors and graphics</h4></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Main processor:</b></td> + <td><?= trim($computer["cpu"]["manufacturer"] . " " . $computer["cpu"]["brand"] . " " . $computer["cpu"]["model"]) ?> (<?= $computer["cpu"]["speed"] ?> GHz)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Processor threading:</b></td> + <td> + <?= $computer["cpu"]["processors"] ?> processor<?= $computer["cpu"]["processors"] > 1 ? "s" : "" ?>, + <?= $computer["cpu"]["physicalCores"] ?> core<?= $computer["cpu"]["physicalCores"] > 1 ? "s" : "" ?><?= isset($computer["cpu"]["efficiencyCores"]) ? " (" . $computer["cpu"]["efficiencyCores"] . " efficiency, " . $computer["cpu"]["performanceCores"] . " performance)" : "" ?>, + <?= $computer["cpu"]["cores"] ?> thread<?= $computer["cpu"]["cores"] > 1 ? "s" : "" ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Hardware virtualization:</b></td> + <td><?= $computer["cpu"]["virtualization"] ? "Capable" : "Disabled" ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Level 1 cache:</b></td> + <td><?= round($computer["cpu"]["cache"]["l1i"] / 1024, 2) ?> KiB (instruction), <?= round($computer["cpu"]["cache"]["l1d"] / 1024, 2) ?> KiB (data)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Level 2 cache:</b></td> + <td><?= isset($computer["cpu"]["cache"]["l2"]) ? round($computer["cpu"]["cache"]["l2"] / 1024**2, 2) . " MiB" : "Not supported" ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Level 3 cache:</b></td> + <td><?= isset($computer["cpu"]["cache"]["l3"]) ? round($computer["cpu"]["cache"]["l3"] / 1024**2, 2) . " MiB" : "Not supported" ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Graphics processor<?= count($computer["gpu"]["controllers"]) > 1 ? "s" : "" ?>:</b></td> + <td> + <?php $index = 0; foreach ($computer["gpu"]["controllers"] as $controller): ?> + <?= $controller["bus"] ?> <?= $controller["model"] ?> (<?= $controller["cores"] ?> cores, <?= $controller["vramDynamic"] ? "dynamic graphics memory" : $controller["vram"] . " MiB" ?>) + <?= $index < count($computer["gpu"]["controllers"]) - 1 ? "<br>" : "" ?> + <?php $index++; endforeach; ?> + </td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Display<?= count($computer["gpu"]["displays"]) > 1 ? "s" : "" ?>:</b></td> + <td> + <?php $index = 0; foreach ($computer["gpu"]["displays"] as $display): ?> + <?= $display["connection"] ?> <?= $display["vendor"] ?> <?= $display["model"] ?> (<?= $display["currentResX"] ?>x<?= $display["currentResY"] ?><?= $display["currentResX"] !== $display["resolutionX"] ? ", native " . $display["resolutionX"] . "x" . $display["resolutionY"] : "" ?>, <?= $display["currentRefreshRate"] ?> Hz) + <?= $index < count($computer["gpu"]["controllers"]) - 1 ? "<br>" : "" ?> + <?php $index++; endforeach; ?> + </td> + </tr> + + <tr> + <td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">System memory</h4></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Physical memory:</b></td> + <td><?= round($computer["ram"]["total"] / 1024**3, 2) ?> GiB (<?= round($computer["ram"]["used"] / 1024**2, 2) ?> MiB used, <?= round($computer["ram"]["free"] / 1024**2, 2) ?> MiB free)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Memory usage:</b></td> + <td><?= round($computer["ram"]["active"] / 1024**2, 2) ?> MiB active, <?= round($computer["ram"]["available"] / 1024**2, 2) ?> MiB available, <?= round($computer["ram"]["buffcache"] / 1024**2, 2) ?> MiB cache</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Swap memory:</b></td> + <td><?= round($computer["ram"]["swaptotal"] / 1024**3, 2) ?> GiB (<?= round($computer["ram"]["swapused"] / 1024**2, 2) ?> MiB used, <?= round($computer["ram"]["swapfree"] / 1024**2, 2) ?> MiB free)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>RAM chips:</b></td> + <td> + <?php $index = 0; foreach ($computer["ram_chips"] as $chip): ?> + <?= $chip["manufacturer"] ?> <?= round($chip["size"] / 1024**2, 2) ?> MiB <?= $chip["type"] ?> + <?= $index < count($computer["ram_chips"]) - 1 ? "<br>" : "" ?> + <?php $index++; endforeach; ?> + </td> + </tr> + + <tr> + <td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Battery</h4></td> + </tr> + <?php if ($computer["battery"]["hasBattery"]): ?> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Battery model:</b></td> + <td><?= preg_replace("/ +/", " ", trim($computer["battery"]["manufacturer"] . " " . $computer["battery"]["type"] . " " . $computer["battery"]["model"])) ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Charge level:</b></td> + <td><?= $computer["battery"]["percent"] ?>%<?= $computer["battery"]["acConnected"] ? ", plugged in" : "" ?> (<?= round($computer["battery"]["voltage"], 2) ?> V)</td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Cycles:</b></td> + <td><?= $computer["battery"]["cycleCount"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Remaining capacity:</b></td> + <td><?= $computer["battery"]["currentCapacity"] ?>/<?= $computer["battery"]["maxCapacity"] ?> <?= $computer["battery"]["capacityUnit"] ?> (designed for <?= $computer["battery"]["designedCapacity"] ?> <?= $computer["battery"]["capacityUnit"] ?>, <?= round(($computer["battery"]["maxCapacity"] / $computer["battery"]["designedCapacity"]) * 100, 2) ?>% left)</td> + </tr> + <?php else: ?><tr><td colspan="2">This computer does not contain a battery.</td></tr><?php endif; ?> + + <tr> + <td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Operating system</h4></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>System:</b></td> + <td><?= $computer["os_info"]["distro"] ?> <?= $computer["os_info"]["release"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>CPU architecture:</b></td> + <td><?= $computer["os_info"]["arch"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Kernel version:</b></td> + <td><?= $computer["os_info"]["kernel"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Code page:</b></td> + <td><?= $computer["os_info"]["codepage"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Build number:</b></td> + <td><?= $computer["os_info"]["build"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Serial number:</b></td> + <td><?= $computer["os_info"]["serial"] ?></td> + </tr> + <tr> + <td style="padding-right: 10px; text-align: right;"><b>Startup interface:</b></td> + <td><?= $computer["os_info"]["uefi"] ? "UEFI" : "BIOS (legacy)" ?></td> + </tr> + + <tr> + <td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Software versions</h4></td> + </tr> + <?php foreach ($computer["versions"] as $software => $version): if (trim($version) !== "" && $software !== "systemOpensslLib" && $software !== "openssl" && $software !== "node" && $software !== "v8"): ?> + <tr> + <td style="padding-right: 10px; text-align: right;"><b><?= + match ($software) { + "kernel" => "OS kernel", + "systemOpenssl" => $computer["versions"]["systemOpensslLib"], + "npm" => "NPM", + "yarn" => "Yarn", + "gulp" => "Gulp", + "grunt" => "Grunt", + "git" => "Git", + "tsc" => "TypeScript", + "mysql" => "MySQL", + "redis" => "Redis", + "mongodb" => "MongoDB", + "apache" => "Apache HTTPD", + "php" => "PHP", + "docker" => "Docker", + "postfix" => "Postfix SMTP Server", + "postgresql" => "PostgreSQL", + "perl" => "Perl", + "python" => "Python (legacy)", + "python3" => "Python", + "pip" => "PIP (legacy)", + "pip3" => "PIP", + "java" => "Java", + "gcc" => "C compiler", + "virtualbox" => "VirtualBox", + "bash" => "Bash", + "zsh" => "zsh", + "fish" => "Fish", + "powershell" => "PowerShell", + "dotnet" => ".NET", + default => $software, + } + ?>:</b></td> + <td><?= $version ?></td> + </tr> + <?php endif; endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;" id="screens">Screens</h4> + <p id="page-content"> + You can remotely control this computer. <a href="./<?= $id ?>/control">Open remote control.</a> + </p> + <div style="display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px;"> + <?php foreach ($computer["screens"] as $screen): ?> + <div class="card"> + <div class="card-body"> + <img style="width: 100%;" src="https://ponies.equestria.horse/api/data?f=computers/screens/<?= $id . "-" . $screen["id"] ?>.jpg"> + </div> + </div> + <?php endforeach; ?> + </div> + + <h4 style="margin-top: 20px;margin-bottom: 10px;" id="windows">Windows</h4> + <div style="display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px;"> + <?php foreach ($computer["windows"] as $window): ?> + <div class="card"> + <div class="card-body" style="display: flex; align-items: center;"> + <div style="width: 100%;"> + <img style="max-width: 100%; max-height: 100%;" src="https://ponies.equestria.horse/api/data?f=computers/windows/<?= $id . "-" . sha1($window["gid"]) ?>.jpg"> + <div style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden; width: 100%; text-align: center; margin-top: 7px; margin-bottom: -7px;"><?= $window["name"] ?></div> + </div> + </div> + </div> + <?php endforeach; ?> + </div> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Sessions</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">TTY</th> + <th scope="col">Command</th> + <th scope="col">User</th> + <th scope="col">Open</th> + <th scope="col">Type</th> + </tr> + </thead> + <tbody> + <?php foreach ($computer["users"] as $user): ?> + <tr> + <th scope="row"><?= $user["tty"] ?></th> + <td><?= str_replace("�", "", $user["command"]) ?></td> + <td><?= $user["user"] ?></td> + <td><?= $user["date"] . " " . $user["time"] ?></td> + <td><?= trim($user["ip"]) === "" ? "Local" : "Remote (" . $user["ip"] . ")" ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Filesystems</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">Device</th> + <th scope="col">Mount point</th> + <th scope="col">Type</th> + <th scope="col">Used</th> + <th scope="col">Free</th> + <th scope="col">Total</th> + </tr> + </thead> + <tbody> + <?php + + function prettySize($bytes) { + if ($bytes > 1024) { + if ($bytes > 1024**2) { + if ($bytes > 1024**3) { + return round($bytes / 1024**3, 1) . " GB"; + } else { + return round($bytes / 1024**2, 1) . " MB"; + } + } else { + return round($bytes / 1024, 1) . " KB"; + } + } else { + return $bytes . " B"; + } + } + + uasort($computer["filesystems"], function ($a, $b) { + return $b["use"] - $a["use"]; + }); + + foreach ($computer["filesystems"] as $fs): ?> + <tr> + <th scope="row"><?= $fs["fs"] ?></th> + <td><?= $fs["mount"] ?></td> + <td><?= $fs["type"] ?></td> + <td><?= prettySize($fs["used"]) ?> (<?= $fs["use"] ?>%)</td> + <td><?= prettySize($fs["available"]) ?></td> + <td><?= prettySize($fs["size"]) ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Audio devices</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">Type</th> + <th scope="col">Manufacturer</th> + <th scope="col">Name</th> + <th scope="col">Default</th> + <th scope="col">Connection</th> + </tr> + </thead> + <tbody> + <?php foreach ($computer["audio"] as $device): ?> + <tr> + <th scope="row"><?= trim($device["type"]) === "" ? "Device" : $device["type"] ?></th> + <td><?= $device["manufacturer"] ?></td> + <td><?= $device["name"] ?></td> + <td><?= $device["default"] ? "Yes" : "" ?></td> + <td><?= trim($device["channel"]) === "" ? "Virtual" : $device["channel"] ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Network cards</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">Interface</th> + <th scope="col">State</th> + <th scope="col">IP addresses</th> + <th scope="col">MAC address</th> + <th scope="col">Type</th> + <th scope="col">Internal</th> + <th scope="col">Virtual</th> + <th scope="col">DHCP</th> + </tr> + </thead> + <tbody> + <?php + + uasort($computer["network"], function ($a, $b) { + $statA = match ($a["operstate"]) { + "up" => 300, + "down" => 200, + default => 100 + }; + $statB = match ($b["operstate"]) { + "up" => 300, + "down" => 200, + default => 100 + }; + + return $statB - $statA; + }); + + foreach ($computer["network"] as $iface): ?> + <tr <?= $iface["operstate"] !== "up" ? 'style="opacity:.5;"' : "" ?>> + <th scope="row"><?= $iface["ifaceName"] ?></th> + <td><?= $iface["operstate"] === "up" ? "Active" : ($iface["operstate"] === "down" ? "Inactive" : "") ?></td> + <td><?= trim($iface["ip4"] !== "") ? $iface["ip4"] : "" ?><?= trim($iface["ip6"] !== "") ? (trim($iface["ip4"] !== "") ? ", " : "") . $iface["ip6"] : "" ?></td> + <td><?= trim($iface["mac"] !== "") ? $iface["mac"] : "" ?></td> + <td><?= $iface["type"] === "wireless" ? "Wireless" : "Wired" ?></td> + <td><?= $iface["internal"] ? "Yes" : "" ?></td> + <td><?= $iface["virtual"] ? "Yes" : "" ?></td> + <td><?= $iface["dhcp"] ? "Yes" : "" ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Network connections</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">State</th> + <th scope="col">Protocol</th> + <th scope="col">Source</th> + <th scope="col">Destination</th> + <th scope="col">Process</th> + </tr> + </thead> + <tbody> + <?php foreach ($computer["connections"] as $connection): ?> + <tr> + <th scope="row"><?= match ($connection["state"]) { + "ESTABLISHED" => "Connected", + "LISTEN" => "Listening", + "CLOSING" => "Terminating", + "UNKNOWN" => "?", + "LAST_ACK", "FIN_WAIT2" => "Waiting", + "TIME_WAIT" => "Waiting", + "FIN_WAIT1" => "Closing", + "SYN_RECV" => "Received", + "SYN_SENT" => "Attempting", + default => $connection["state"], + } ?></th> + <td><?= match ($connection["protocol"]) { + "tcp4" => "TCP over IPv4", + "tcp6" => "TCP over IPv6", + "tcp46" => "TCP over IPv4 and IPv6", + "udp4" => "UDP over IPv4", + "udp6" => "UDP over IPv6", + "udp46" => "UDP over IPv4 and IPv6", + default => $connection["protocol"], + } ?></td> + <td><?= $connection["localAddress"] === "*" ? "<all>" : match ($connection["localAddress"]) { + "127.0.0.1" => "<local IPv4>", + "::1" => "<local IPv6>", + default => $connection["localAddress"], + } ?> (port <?= $connection["localPort"] ?>)</td> + <td><?php if ($connection["state"] !== "LISTEN"): ?><?= $connection["peerAddress"] === "*" ? "<all>" : match ($connection["peerAddress"]) { + "127.0.0.1" => "<local IPv4>", + "::1" => "<local IPv6>", + default => $connection["localAddress"], + } ?> (<?= $connection["peerPort"] ?>)<?php endif; ?></td> + <td><?php + + $f = array_values(array_filter($computer["processes"], function ($i) use ($connection) { + return $i["pid"] === $connection["pid"]; + })); + + if (count($f) > 0) { + echo($f[0]["name"] . " [" . $connection["pid"] . "]"); + } else { + echo($connection["pid"]); + } + + ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <h4 style="margin-top: 20px;margin-bottom: 10px;">Processes</h4> + <table class="table"> + <thead> + <tr> + <th scope="col">PID</th> + <th scope="col">Name</th> + <th scope="col">CPU</th> + <th scope="col">RAM</th> + <th scope="col">User</th> + <th scope="col">Started</th> + </tr> + </thead> + <tbody> + <?php foreach ($computer["processes"] as $process): ?> + <tr> + <th scope="row"><?= $process["pid"] ?></th> + <td><?= $process["name"] ?></td> + <td><?= $process["cpu"] ?>%</td> + <td><?= $process["ram"] ?>%</td> + <td><?= $process["user"] ?></td> + <td><?= timeAgo($process["date"]) ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + </div> + <?php elseif (isset($parts[3]) && $parts[3] === "control"): $id = $parts[2]; ?> + <div> + <h2> + <span style="vertical-align: middle;">Remote controlling <?= $computer["host"] ?></span> + <a href="/-/computers/<?= $id ?>" class="small btn btn-outline-light" style="float:right;margin-top:5px;vertical-align:middle;opacity:1 !important; color:white;">Back</a> + </h2> + + <div style="display: flex; align-items: center;"> + <img id="display" style="max-width: 100%; max-height: 100%; height: 100%; width: 100%; opacity: .5" src="https://ponies.equestria.horse/api/data?f=computers/screens/<?= $id . "-" . $computer["screens"][0]["id"] ?>.jpg"> + </div> + + <div id="commands" style="background: #151515; border: 1px solid #333; margin-top: 10px; border-radius: 10px; font-family: 'JetBrains Mono', monospace; font-size: 14px;"> + <div id="output" style="height: 200px; padding: 10px 20px; overflow: auto;"> + <pre id="output-text"></pre> + <div id="loader" style="display: none;"> + <img src="/assets/editor/load.svg" style="width: 36px; vertical-align: middle; margin-left: -8px; margin-top: -8px;" id="loader-img" alt=""> + <span id="loader-time" style="vertical-align: middle; opacity: .5; display: inline-block; margin-top: -8px; margin-left: -8px;"> + <span id="loader-time-inner"></span> + <span id="loader-time-stopper"> + | <a id="loader-time-stopper-link" style="cursor: pointer; text-decoration: underline;" onclick="stopCommand();">Stop</a> + </span> + </span> + </div> + </div> + <div id="entry" style="border-top: 1px solid #333;"> + <input type="text" id="command-entry" placeholder="Enter a command" style="width: 100%; color: white; background: transparent; border: none; padding: 10px 20px; outline: none;"> + </div> + </div> + + <script> + function stopCommand() { + ws.send(JSON.stringify({ + type: "halt" + })); + } + + function scrollToBottom() { + document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight; + } + + window.addEventListener("load", () => { + document.getElementById("command-entry").value = ""; + }); + + document.getElementById("command-entry").onkeydown = (event) => { + if (event.key === "Enter") { + event.preventDefault(); + let command = document.getElementById("command-entry").value; + document.getElementById("command-entry").value = ""; + document.getElementById("command-entry").disabled = true; + document.getElementById("loader-time").style.display = "none"; + document.getElementById("loader").style.display = ""; + document.getElementById("output-text").innerHTML += "\n$ " + command.replaceAll(">", ">").replaceAll("<", "<") + "\n"; + scrollToBottom(); + ws.send(JSON.stringify({ + type: "command", + value: btoa(command) + })); + } + } + + document.getElementById("title-bar-action-1").style.display = "none"; + window.controlling = false; + window.commandRunInterval = null; + + function connect() { + window.ws = new WebSocket("wss://ponies.equestria.horse/_Computers-RemoteControl-EntryPoint/socket"); + window.baseLatency = null; + + ws.addEventListener('open', (data) => { + console.log(data); + ws.send(JSON.stringify({ + type: "client", + id: "<?= $id ?>", + token: "<?= $_COOKIE['PEH2_SESSION_TOKEN'] ?>" + })) + }); + + ws.addEventListener('message', async (_data) => { + let data = JSON.parse(await (_data.data.text())); + + if (data.type === "image") { + document.getElementById("display").style.opacity = "1"; + document.getElementById("display").src = data.url; + + if (baseLatency) { + ws.send(JSON.stringify({ + type: "set_latency", + value: Math.abs((new Date().getTime() - data.time) - baseLatency) + })); + + if (Math.abs((new Date().getTime() - data.time) - baseLatency) > 200) { + ws.send(JSON.stringify({ + type: "reduce_quality" + })); + } else if (Math.abs((new Date().getTime() - data.time) - baseLatency) < 150) { + ws.send(JSON.stringify({ + type: "increase_quality" + })); + } + } else { + window.baseLatency = Math.abs(new Date().getTime() - data.time); + } + } else if (data.type === "command_stop") { + if (data.code !== 0) { + if (data.signal) { + if (data.code) { + document.getElementById("output-text").innerHTML += "<span style='color: #ff8c8c;'>Command exited with code " + data.code.toString().replaceAll(">", ">").replaceAll("<", "<") + " (caused by " + data.signal.toString().replaceAll(">", ">").replaceAll("<", "<") + ")</span>"; + } else { + document.getElementById("output-text").innerHTML += "<span style='color: #ff8c8c;'>Command exited with signal " + data.signal.toString().replaceAll(">", ">").replaceAll("<", "<"); + } + } else { + document.getElementById("output-text").innerHTML += "<span style='color: #ff8c8c;'>Command exited with code " + data.code.toString().replaceAll(">", ">").replaceAll("<", "<") + "</span>"; + } + } + + clearInterval(window.commandRunInterval); + document.getElementById("command-entry").disabled = false; + document.getElementById("loader").style.display = "none"; + scrollToBottom(); + } else if (data.type === "command_start") { + document.getElementById("loader-time").style.display = "inline-block"; + document.getElementById("loader-time-inner").innerText = ""; + window.commandStartTime = new Date().getTime(); + window.commandRunInterval = setInterval(() => { + document.getElementById("loader-time-inner").innerText = ((new Date().getTime() - window.commandStartTime) / 1000).toFixed(2) + "s"; + }, 10); + scrollToBottom(); + } else if (data.type === "command_stdout" || data.type === "command_stderr") { + if (data.type === "command_stderr") { + document.getElementById("output-text").innerHTML += "<span style='color: #ffec8c;'>" + atob(data.value).replaceAll(">", ">").replaceAll("<", "<") + "</span>"; + } else { + document.getElementById("output-text").innerHTML += atob(data.value).replaceAll(">", ">").replaceAll("<", "<"); + } + + scrollToBottom(); + } else { + console.log(data); + } + }); + + ws.addEventListener('close', (data) => { + document.getElementById("display").style.opacity = "0.5"; + console.log(data); + + setTimeout(() => { + connect(); + }, 1000); + }); + } + + connect(); + + document.getElementById("display").addEventListener("mousemove", (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "move_cursor", + x: event.offsetX / document.getElementById("display").width, + y: event.offsetY / document.getElementById("display").height + })); + }); + + document.getElementById("display").addEventListener("click", (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "click", + button: 0 + })); + }); + + document.getElementById("display").addEventListener("contextmenu", (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "click", + button: 1 + })); + }); + + document.getElementById("display").addEventListener("auxclick", (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "click", + button: 2 + })); + }); + + document.body.onkeydown = document.getElementById("display").onkeydown = (event) => { + if (window.controlling) event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "keyboard", + shift: event.shiftKey, + ctrl: event.ctrlKey, + meta: event.metaKey, + alt: event.altKey, + key: event.key + })); + } + + document.getElementById("display").ondragstart = (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "move_cursor", + x: event.offsetX / document.getElementById("display").width, + y: event.offsetY / document.getElementById("display").height + })); + if (window.controlling) ws.send(JSON.stringify({ + type: "start_drag" + })); + } + + document.getElementById("display").ondragend = (event) => { + event.preventDefault(); + + if (window.controlling) ws.send(JSON.stringify({ + type: "move_cursor", + x: event.offsetX / document.getElementById("display").width, + y: event.offsetY / document.getElementById("display").height + })); + if (window.controlling) ws.send(JSON.stringify({ + type: "stop_drag" + })); + } + + function enableControl() { + document.getElementById("title-bar-action-1").style.display = "inline-block"; + document.getElementById("title-bar-action-0").style.display = "none"; + window.controlling = true; + } + + function disableControl() { + document.getElementById("title-bar-action-1").style.display = "none"; + document.getElementById("title-bar-action-0").style.display = "inline-block"; + window.controlling = false; + } + </script> + <?php else: ?> + <div> + <h2>Computers</h2> + <p>Click on a computer to view information about it (open windows, installed apps, ...).</p> + + <ul class="list-group"> + <?php foreach (array_filter(scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/data/computers/metadata"), function ($i) { + return !str_starts_with($i, "."); + }) as $file): $computer = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/computers/metadata/" . $file), true); $id = substr($file, 0, -5); $owner = explode("-", $id)[0]; ?> + <a href="/-/computers/<?= $id ?>" class="list-group-item list-group-item-action" style="display: grid; grid-template-columns: 100px 1fr; grid-column-gap: 10px;"> + <div style="display: flex; align-items: center;"> + <img style="width: 100px;" src="https://ponies.equestria.horse/api/data?f=computers/screens/<?= $id . "-" . $computer["screens"][0]["id"] ?>.jpg"> + </div> + <div style="display: flex; align-items: center;"> + <div> + <b><?= $computer["host"] ?></b> · <?= $computer["os"] ?><br> + Owned by <?= $owner === "raindrops" ? "Raindrops System" : "Cloudburst System"; ?><br> + Last seen <?= timeAgo($computer["date"]) ?> + </div> + </div> + </a> + <?php endforeach; ?> + </ul> + </div> + <?php endif; ?> +</div> + +<style> + .list-group-item { + color: #fff; + background-color: #222; + border: 1px solid rgba(255, 255, 255, .125); + } + + .list-group-item.disabled { + color: #fff; + background-color: #222; + border-color: rgba(255, 255, 255, .125); + opacity: .75; + } + + .list-group-item:hover { + background-color: #252525; + color: #ddd; + } + + .list-group-item:active, .list-group-item:focus { + background-color: #272727; + color: #bbb; + } + + .modal-header { + border-bottom: 1px solid #353738; + } + + .modal-content { + border: 1px solid rgba(255, 255, 255, .2); + background-color: #111; + } + + .btn-close { + filter: invert(1); + } + + .btn-outline-light:hover { + color: black !important; + } + + table { + color: white !important; + } + + table * { + border-color: rgba(255, 255, 255, .25) !important; + } +</style> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.inc'; ?> diff --git a/pages/debug.inc b/pages/debug.inc index 77001d0..e316a6f 100644 --- a/pages/debug.inc +++ b/pages/debug.inc @@ -76,7 +76,7 @@ function itemToName(string $item): string { <li><b>Longest operation:</b> <?= itemToName(array_search(max(array_values($data["times"])), $data["times"])) ?? "-" ?> (<?= round(max(array_values($data["times"])) * 1000) ?> ms, <?= round((max(array_values($data["times"])) / $data["duration"]) * 100, 2) ?>%)</li> </ul> - <h4>Processing times</h4> + <h4 id="times">Processing times</h4> <div> <?php foreach ($data["times"] as $item => $time): ?><span class="element tooltip-nohelp" title="<b><?= itemToName($item) ?></b><br>(<?= round($time * 1000) ?> ms)" data-bs-toggle="tooltip" data-bs-html="true" style="opacity:.75;display:inline-block;background:#<?= substr(md5($item), 0, 6) ?>;height:8px;margin:4px 0;width:<?= ($time / $data["duration"]) * 100 ?>%"></span><?php endforeach; ?> </div> @@ -86,7 +86,7 @@ function itemToName(string $item): string { <?php endforeach; ?> </ul> - <h4>Reported failures</h4> + <h4 id="failures">Reported failures</h4> <?php if (count($data["restored"]) < 1): ?> <p><i>The data updater has not reported any update failure in the last run.</i></p> <?php else: ?> diff --git a/pages/docs.inc b/pages/docs.inc index 0322f32..6d01489 100644 --- a/pages/docs.inc +++ b/pages/docs.inc @@ -22,7 +22,7 @@ if ($select === "add") { die(); } elseif (isset($select)) { if (ctype_alnum($select) && file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/docs/" . $select . ".json")) { - $id = $select; + $id = $_documentId = $select; $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/docs/" . $select . ".json"), true); $titleBase = " · " . $title . " · Cold Haze"; $title = $data["name"] . " · " . $title; @@ -141,7 +141,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; saving = true; try { - await window.fetch("/api/docs?id=<?= $id ?>", { + await window.fetch("/api/docs?id=<?= $_documentId ?>", { method: "POST", body: JSON.stringify({ content: data, name: document.getElementById("document-name").innerText, category: document.getElementById("category").innerText }) }); @@ -289,7 +289,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <?php endforeach; ?></div> <hr> - <h4>Marked for deletion</h4><div class="list-group"> + <h4 id="deletable">Marked for deletion</h4><div class="list-group"> <?php foreach ($deletable as $item): ?> <a href="/-/docs/<?= $item["id"] ?>" id="document-<?= $item["id"] ?>" class="list-group-item list-group-item-action document-listing <?php if (strip_tags($item["contents"]) === "/delete"): ?>opacity-75<?php endif; ?>"> <?= $item["name"] ?> diff --git a/pages/edit.inc b/pages/edit.inc index 8be6730..4333044 100644 --- a/pages/edit.inc +++ b/pages/edit.inc @@ -21,7 +21,7 @@ $parts = explode("/", $_GET['_']); array_shift($parts); array_shift($parts); $system = $parts[0]; -$member = ($parts[1] ?? null) === "" ? null : $parts[1]; +$member = !isset($parts[1]) || $parts[1] === "" ? null : $parts[1]; if ($system !== "cloudburst" && $system !== "raindrops") header("Location: /?error=Invalid system name: " . $system) and die(); $systemCommonName = $system === "cloudburst" ? "Cloudburst System" : "Raindrops System"; diff --git a/pages/parser.inc b/pages/parser.inc index dc07744..283006b 100644 --- a/pages/parser.inc +++ b/pages/parser.inc @@ -11,6 +11,10 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <h2>Message parser</h2> <p>Enter a message here, and we will tell you which system member this message came from.</p> + <div class="alert alert-warning"> + <b>Notice:</b> The prefix generator and message parser pages will be removed from Cold Haze starting November 1<sup>st</sup>, due to being unused, difficult to maintain and containing outdated information. Feel free to ask a system member if you need help figuring out who sent a specific message. + </div> + <div style="display:grid;grid-template-columns:1fr 2fr;grid-gap:10px;"> <select id="system" title="Existing prefixes from..." class="tooltip-nohelp form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' onchange="update();"> <option value="all">(all systems)</option> diff --git a/pages/prefix.inc b/pages/prefix.inc index d7d8a40..0b1f3a3 100644 --- a/pages/prefix.inc +++ b/pages/prefix.inc @@ -11,6 +11,10 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <h2>Prefix generator</h2> <p>This prefix generator will take into account the prefixes from the existing members to try to generate new original prefixes for a potential new member.</p> + <div class="alert alert-warning"> + <b>Notice:</b> The prefix generator and message parser pages will be removed from Cold Haze starting November 1<sup>st</sup>, due to being unused, difficult to maintain and containing outdated information. Feel free to ask a system member if you need help figuring out who sent a specific message. + </div> + <div style="display:grid;grid-template-columns:1fr 1fr 1fr;grid-gap:10px;"> <select id="system" title="Existing prefixes from..." class="tooltip-nohelp form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' onchange="update();"> <optgroup label="General Options"> diff --git a/pages/rules-old.inc b/pages/rules-old.inc new file mode 100644 index 0000000..21ae7e6 --- /dev/null +++ b/pages/rules-old.inc @@ -0,0 +1,253 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; + +if (isset($_POST["updateRules"])) { + header("Content-Type: text/plain"); + + if (!isset($_POST['payload'])) { + header("Location: /-/rules"); + die(); + } + + foreach ($_POST['payload'] as $index => $rule) { + if (!isset($rule["name"]) || !isset($rule["content"]) && !is_numeric($index)) { + header("Location: /-/rules"); + die(); + } + + if (trim($rule["name"]) === "") { + unset($_POST["payload"][$index]); + continue; + } + + if (!isset($rule["approved"])) $rule["approved"] = []; + if (isset($rule["approved"][0])) $rule["approved"][0] = true; else $rule["approved"][0] = false; + if (isset($rule["approved"][1])) $rule["approved"][1] = true; else $rule["approved"][1] = false; + + $_POST["payload"][$index] = $rule; + } + + file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json", utf8_encode(json_encode($_POST["payload"]))); + + header("Location: /-/rules"); + die(); +} + +require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; + +?> + +<br> +<div class="container"> + <div id="page-content"> + <h2>Systems rules</h2> + <p>Click on a rule to view additional details. <a onclick="event.target.blur();" href="#" data-bs-toggle="modal" data-bs-target="#editor">Edit rules</a></p> + + <?php + + $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json"), true); + + $protectorCloudburst = array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "ynmuc" && $i["_metadata"]["protector"]; + }))[0]; + $protectorRaindrops = array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "gdapd" && $i["_metadata"]["protector"]; + }))[0]; + + $pcName = getMiniName($protectorCloudburst["display_name"] ?? $protectorCloudburst["name"]); + $prName = getMiniName($protectorRaindrops["display_name"] ?? $protectorRaindrops["name"]); + + ?> + + <ul class="list-group"> + <?php $index = 1; foreach ($rules as $rule): ?> + <li class="list-group-item rule-outer"> + <details> + <summary class="rule"> + <b><?= $index ?>. <?= strip_tags($rule["name"]) ?></b> + <?php if (in_array(false, $rule["approved"])): ?> + <span class="badge bg-warning text-black rounded-pill">Unapproved</span> + <?php endif; ?> + </summary> + <?php if (in_array(false, $rule["approved"])): ?> + <div style="margin-top:10px;" class="alert alert-warning"> + <b>This rule has not yet been approved.</b> All rules need to be approved by the leaders from both systems. This rule is still missing approval from <?php + + if ($rule["approved"][0] === false) { + if ($rule["approved"][1] === false) { + echo($pcName . " and " . $prName); + } else { + echo($pcName); + } + } else if ($rule["approved"][1] === false) { + echo($prName); + } + + ?>. + </div> + <?php endif; ?> + <div <?= !in_array(false, $rule["approved"]) ? 'style="margin-top:10px;"' : '' ?> class="list-group-item"> + <?= strip_tags($rule["content"]) ?> + </div> + </details> + </li> + <?php $index++; endforeach; ?> + </ul> + </div> +</div> + +<div class="modal fade" id="editor"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Rules editor</h4> + <button type="button" class="btn-close" data-bs-dismiss="modal"></button> + </div> + + <div class="modal-body"> + <p>Rules with an empty name are automatically deleted. Buttons to add new rules and save the changes are at the bottom of the list.</p> + <hr> + <form method="post"> + <div id="rules-editor"> + <?php $index = 1; foreach ($rules as $rule): ?> + <div <?= $index === 1 ? 'id="default-rule"' : '' ?>> + <p><b <?= $index === 1 ? 'id="default-rule--number"' : '' ?>>Rule #<?= $index ?>:</b></p> + <input <?= $index === 1 ? 'id="default-rule--name"' : '' ?> type="text" placeholder="Rule name" class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" name="payload[<?= $index - 1 ?>][name]" value="<?= str_replace('"', """, strip_tags($rule["name"])) ?>"> + + <textarea <?= $index === 1 ? 'id="default-rule--content"' : '' ?> name="payload[<?= $index - 1 ?>][content]" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;" placeholder="Rule details"><?= strip_tags($rule["content"] ?? "") ?></textarea> + + <label style="margin-top:10px;margin-left:5px;"> + <input <?= $index === 1 ? 'id="default-rule--approval-1"' : '' ?> <?= ($rule["approved"][0] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][0]"> + Approved by <?= $pcName ?> + </label><br> + <label style="margin-left:5px;"> + <input <?= $index === 1 ? 'id="default-rule--approval-2"' : '' ?> <?= ($rule["approved"][1] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][1]"> + Approved by <?= $prName ?> + </label><br> + + <hr> + </div> + <?php $index++; endforeach; ?> + </div> + <input type="submit" value="Save" class="btn btn-primary"> + <a onclick="editorNewRule();" class="btn btn-secondary">New rule</a> + <input type="hidden" name="updateRules"> + </form> + </div> + </div> + </div> +</div> + +<script> + window.numberOfRules = <?= count($rules) ?>; + + Array.from(document.getElementsByClassName("rule-outer")).forEach((el) => { + let details = el.children[0]; + + el.onclick = () => { + Array.from(document.getElementsByClassName("rule-outer")).forEach((sel) => { + if (el === sel) return; + sel.children[0].open = false; + sel.classList.remove("open"); + }); + + details.open = !details.open; + + if (details.open) { + el.classList.add("open"); + } else { + el.classList.remove("open"); + } + } + }) + + function editorNewRule() { + let id = Math.random(36).toString().split(".")[1]; + + document.getElementById("default-rule").id = "added-" + id + "-1"; + document.getElementById("default-rule--number").id = "added-" + id + "-2"; + document.getElementById("default-rule--name").id = "added-" + id + "-3"; + document.getElementById("default-rule--content").id = "added-" + id + "-4"; + document.getElementById("default-rule--approval-1").id = "added-" + id + "-5"; + document.getElementById("default-rule--approval-2").id = "added-" + id + "-6"; + + let child = document.createElement("div"); + child.id = "temp-" + id; + window.numberOfRules++; + + document.getElementById("rules-editor").appendChild(child); + document.getElementById("temp-" + id).outerHTML = document.getElementById("added-" + id + "-1").outerHTML; + + document.getElementById("added-" + id + "-1").id = "default-rule"; + document.getElementById("added-" + id + "-2").id = "default-rule--number"; + document.getElementById("added-" + id + "-3").id = "default-rule--name"; + document.getElementById("added-" + id + "-4").id = "default-rule--content"; + document.getElementById("added-" + id + "-5").id = "default-rule--approval-1"; + document.getElementById("added-" + id + "-6").id = "default-rule--approval-2"; + + document.getElementById("added-" + id + "-3").name = "payload[" + (numberOfRules - 1) + "][name]"; + document.getElementById("added-" + id + "-4").name = "payload[" + (numberOfRules - 1) + "][content]"; + document.getElementById("added-" + id + "-5").name = "payload[" + (numberOfRules - 1) + "][approved][0]"; + document.getElementById("added-" + id + "-6").name = "payload[" + (numberOfRules - 1) + "][approved][1]"; + + document.getElementById("added-" + id + "-5").checked = false; + document.getElementById("added-" + id + "-6").checked = false; + document.getElementById("added-" + id + "-3").value = ""; + document.getElementById("added-" + id + "-4").value = ""; + document.getElementById("added-" + id + "-2").innerText = "Rule #" + numberOfRules + ":"; + } +</script> + +<style> + .list-group-item { + color: #fff; + background-color: #222; + border: 1px solid rgba(255, 255, 255, .125); + } + + .list-group-item.disabled { + color: #fff; + background-color: #222; + border-color: rgba(255, 255, 255, .125); + opacity: .75; + } + + .rule-outer:hover { + background-color: #252525; + color: #ddd; + } + + .rule-outer:active, .rule-outer:focus { + background-color: #272727; + color: #bbb; + } + + .rule-outer.open { + background-color: #333; + } + + .rule { + list-style: none; + pointer-events: none; + } + + .rule-outer { + cursor: pointer; + } + + .modal-header { + border-bottom: 1px solid #353738; + } + + .modal-content { + border: 1px solid rgba(255, 255, 255, .2); + background-color: #111; + } + + .btn-close { + filter: invert(1); + } +</style> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.inc'; ?> diff --git a/pages/rules.inc b/pages/rules.inc index 21ae7e6..eee3508 100644 --- a/pages/rules.inc +++ b/pages/rules.inc @@ -1,39 +1,6 @@ <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; - -if (isset($_POST["updateRules"])) { - header("Content-Type: text/plain"); - - if (!isset($_POST['payload'])) { - header("Location: /-/rules"); - die(); - } - - foreach ($_POST['payload'] as $index => $rule) { - if (!isset($rule["name"]) || !isset($rule["content"]) && !is_numeric($index)) { - header("Location: /-/rules"); - die(); - } - - if (trim($rule["name"]) === "") { - unset($_POST["payload"][$index]); - continue; - } - - if (!isset($rule["approved"])) $rule["approved"] = []; - if (isset($rule["approved"][0])) $rule["approved"][0] = true; else $rule["approved"][0] = false; - if (isset($rule["approved"][1])) $rule["approved"][1] = true; else $rule["approved"][1] = false; - - $_POST["payload"][$index] = $rule; - } - - file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json", utf8_encode(json_encode($_POST["payload"]))); - - header("Location: /-/rules"); - die(); -} - require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; ?> @@ -41,164 +8,95 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <br> <div class="container"> <div id="page-content"> - <h2>Systems rules</h2> - <p>Click on a rule to view additional details. <a onclick="event.target.blur();" href="#" data-bs-toggle="modal" data-bs-target="#editor">Edit rules</a></p> + <h2>General rules</h2> + <p>Click on a rule in the list to view the history of that rule, including all amendments. Note that older versions of a rule cannot be enforced anymore and are provided only for informational purposes.</p> <?php - $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json"), true); + $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/newrules.json"), true); - $protectorCloudburst = array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "ynmuc" && $i["_metadata"]["protector"]; - }))[0]; - $protectorRaindrops = array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "gdapd" && $i["_metadata"]["protector"]; - }))[0]; + $catindex = 0; + foreach ($rules as $category): ?> - $pcName = getMiniName($protectorCloudburst["display_name"] ?? $protectorCloudburst["name"]); - $prName = getMiniName($protectorRaindrops["display_name"] ?? $protectorRaindrops["name"]); - - ?> + <?php if ($catindex === 0): ?> <ul class="list-group"> - <?php $index = 1; foreach ($rules as $rule): ?> <li class="list-group-item rule-outer"> <details> <summary class="rule"> - <b><?= $index ?>. <?= strip_tags($rule["name"]) ?></b> - <?php if (in_array(false, $rule["approved"])): ?> - <span class="badge bg-warning text-black rounded-pill">Unapproved</span> + <b><?= $category['title'] ?><?php if (count($category['rules'][0]['amendments']) > 0): ?>-<?= $category['rules'][0]['amendments'][count($category['rules'][0]['amendments']) - 1]['id'] ?>:</b> <?= $category['rules'][0]['amendments'][count($category['rules'][0]['amendments']) - 1]['text'] ?><?php else: ?>:</b> <?= $category['rules'][0]['text'] ?><?php endif; ?> + </summary> + + <ul class="list-group" style="margin-top:10px;"> + <?php if (count($category['rules'][0]['amendments']) > 0): ?> + <?php $aindex = 0; foreach (array_reverse($category['rules'][0]['amendments']) as $amendment): ?> + + <?php if ($aindex === 0): ?> + <li class="list-group-item"><b>Curent version:</b> <?= $amendment['text'] ?></li> + <?php else: ?> + <li class="list-group-item"><b>Amendment Preamble-<?= $amendment['id'] ?>:</b> <?= $amendment['text'] ?></li> + <?php endif; ?> + + <?php $aindex++; endforeach; ?> + <li class="list-group-item"><b>Original rule:</b> <?= $category['rules'][0]['text'] ?></li> + <?php else: ?> + <li class="list-group-item"> + <i>This rule was never amended.</i> + </li> <?php endif; ?> + </ul> + </details> + </li> + </ul> + + <?php else: + + if ($category["title"] === "@punishments") { + $catindex = "P"; + $category["title"] = "Planned punishments"; + } + + ?> + + <h4 style="margin-top: 15px;">Articles <?= $catindex ?>.1 to <?= $catindex ?>.<?= count($category['rules']) ?>: <?= $category["title"] ?></h4> + + <ul class="list-group"> + <?php $ruleindex = 1; foreach ($category['rules'] as $rule): ?> + <li class="list-group-item rule-outer"> + <details> + <summary class="rule"> + <b>Article <?= $catindex ?>.<?= $ruleindex ?><?php if (count($rule['amendments']) > 0): ?>-<?= $rule['amendments'][count($rule['amendments']) - 1]['id'] ?>:</b> <?= $rule['amendments'][count($rule['amendments']) - 1]['text'] ?><?php else: ?>:</b> <?= $rule['text'] ?><?php endif; ?> </summary> - <?php if (in_array(false, $rule["approved"])): ?> - <div style="margin-top:10px;" class="alert alert-warning"> - <b>This rule has not yet been approved.</b> All rules need to be approved by the leaders from both systems. This rule is still missing approval from <?php - - if ($rule["approved"][0] === false) { - if ($rule["approved"][1] === false) { - echo($pcName . " and " . $prName); - } else { - echo($pcName); - } - } else if ($rule["approved"][1] === false) { - echo($prName); - } - - ?>. - </div> - <?php endif; ?> - <div <?= !in_array(false, $rule["approved"]) ? 'style="margin-top:10px;"' : '' ?> class="list-group-item"> - <?= strip_tags($rule["content"]) ?> - </div> + + <ul class="list-group" style="margin-top:10px;"> + <?php if (count($rule['amendments']) > 0): ?> + <?php $aindex = 0; foreach (array_reverse($rule['amendments']) as $amendment): ?> + + <?php if ($aindex === 0): ?> + <li class="list-group-item"><b>Curent version:</b> <?= $amendment['text'] ?></li> + <?php else: ?> + <li class="list-group-item"><b>Amendment Preamble-<?= $amendment['id'] ?>:</b> <?= $amendment['text'] ?></li> + <?php endif; ?> + + <?php $aindex++; endforeach; ?> + <li class="list-group-item"><b>Original rule:</b> <?= $rule['text'] ?></li> + <?php else: ?> + <li class="list-group-item"> + <i>This rule was never amended.</i> + </li> + <?php endif; ?> + </ul> </details> </li> - <?php $index++; endforeach; ?> + <?php $ruleindex++; endforeach; ?> </ul> - </div> -</div> -<div class="modal fade" id="editor"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h4 class="modal-title">Rules editor</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <p>Rules with an empty name are automatically deleted. Buttons to add new rules and save the changes are at the bottom of the list.</p> - <hr> - <form method="post"> - <div id="rules-editor"> - <?php $index = 1; foreach ($rules as $rule): ?> - <div <?= $index === 1 ? 'id="default-rule"' : '' ?>> - <p><b <?= $index === 1 ? 'id="default-rule--number"' : '' ?>>Rule #<?= $index ?>:</b></p> - <input <?= $index === 1 ? 'id="default-rule--name"' : '' ?> type="text" placeholder="Rule name" class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" name="payload[<?= $index - 1 ?>][name]" value="<?= str_replace('"', """, strip_tags($rule["name"])) ?>"> - - <textarea <?= $index === 1 ? 'id="default-rule--content"' : '' ?> name="payload[<?= $index - 1 ?>][content]" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;" placeholder="Rule details"><?= strip_tags($rule["content"] ?? "") ?></textarea> - - <label style="margin-top:10px;margin-left:5px;"> - <input <?= $index === 1 ? 'id="default-rule--approval-1"' : '' ?> <?= ($rule["approved"][0] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][0]"> - Approved by <?= $pcName ?> - </label><br> - <label style="margin-left:5px;"> - <input <?= $index === 1 ? 'id="default-rule--approval-2"' : '' ?> <?= ($rule["approved"][1] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][1]"> - Approved by <?= $prName ?> - </label><br> - - <hr> - </div> - <?php $index++; endforeach; ?> - </div> - <input type="submit" value="Save" class="btn btn-primary"> - <a onclick="editorNewRule();" class="btn btn-secondary">New rule</a> - <input type="hidden" name="updateRules"> - </form> - </div> - </div> + <?php endif; ?> + + <?php $catindex++; endforeach; ?> </div> </div> -<script> - window.numberOfRules = <?= count($rules) ?>; - - Array.from(document.getElementsByClassName("rule-outer")).forEach((el) => { - let details = el.children[0]; - - el.onclick = () => { - Array.from(document.getElementsByClassName("rule-outer")).forEach((sel) => { - if (el === sel) return; - sel.children[0].open = false; - sel.classList.remove("open"); - }); - - details.open = !details.open; - - if (details.open) { - el.classList.add("open"); - } else { - el.classList.remove("open"); - } - } - }) - - function editorNewRule() { - let id = Math.random(36).toString().split(".")[1]; - - document.getElementById("default-rule").id = "added-" + id + "-1"; - document.getElementById("default-rule--number").id = "added-" + id + "-2"; - document.getElementById("default-rule--name").id = "added-" + id + "-3"; - document.getElementById("default-rule--content").id = "added-" + id + "-4"; - document.getElementById("default-rule--approval-1").id = "added-" + id + "-5"; - document.getElementById("default-rule--approval-2").id = "added-" + id + "-6"; - - let child = document.createElement("div"); - child.id = "temp-" + id; - window.numberOfRules++; - - document.getElementById("rules-editor").appendChild(child); - document.getElementById("temp-" + id).outerHTML = document.getElementById("added-" + id + "-1").outerHTML; - - document.getElementById("added-" + id + "-1").id = "default-rule"; - document.getElementById("added-" + id + "-2").id = "default-rule--number"; - document.getElementById("added-" + id + "-3").id = "default-rule--name"; - document.getElementById("added-" + id + "-4").id = "default-rule--content"; - document.getElementById("added-" + id + "-5").id = "default-rule--approval-1"; - document.getElementById("added-" + id + "-6").id = "default-rule--approval-2"; - - document.getElementById("added-" + id + "-3").name = "payload[" + (numberOfRules - 1) + "][name]"; - document.getElementById("added-" + id + "-4").name = "payload[" + (numberOfRules - 1) + "][content]"; - document.getElementById("added-" + id + "-5").name = "payload[" + (numberOfRules - 1) + "][approved][0]"; - document.getElementById("added-" + id + "-6").name = "payload[" + (numberOfRules - 1) + "][approved][1]"; - - document.getElementById("added-" + id + "-5").checked = false; - document.getElementById("added-" + id + "-6").checked = false; - document.getElementById("added-" + id + "-3").value = ""; - document.getElementById("added-" + id + "-4").value = ""; - document.getElementById("added-" + id + "-2").innerText = "Rule #" + numberOfRules + ":"; - } -</script> - <style> .list-group-item { color: #fff; @@ -229,7 +127,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; .rule { list-style: none; - pointer-events: none; } .rule-outer { diff --git a/pages/stats.inc b/pages/stats.inc index 19d4453..bd537f9 100644 --- a/pages/stats.inc +++ b/pages/stats.inc @@ -263,178 +263,6 @@ $switchesCloudburst = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . } }); </script> - - <br> - <h3>Time at front in the last 30 days</h3> - <canvas id="graph-01" style="width: 100%; height: 300px; max-height: 100%;"></canvas> - <script> - function toDuration(seconds, remainder) { - if (seconds > 60) { - if (seconds > 3600) { - if (seconds > 86400) { - if (remainder) { - return Math.floor(seconds / 86400) + " d. " + Math.floor((seconds / 3600) - Math.floor(seconds / 86400) * 24) + " hr. " + Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) / 60) + " min. " + Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) - (Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) / 60)) * 60) + " sec."; - } else { - return Math.round(seconds / 86400) + " d."; - } - } else { - if (remainder) { - return Math.floor(seconds / 3600) + " hr. " + Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) / 60) + " min. " + Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) - (Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) / 60)) * 60) + " sec."; - } else { - return Math.round(seconds / 3600) + " hr."; - } - } - } else { - if (remainder) { - return Math.round(seconds / 60) + " min. " + Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) - (Math.floor((seconds - (Math.floor(seconds / 3600) * 3600)) / 60)) * 60) + " sec."; - } else { - return Math.round(seconds / 60) + " min."; - } - } - } else { - return seconds + " sec."; - } - } - - const ctx1 = document.getElementById('graph-01').getContext('2d'); - window.graph01 = { - label: "Members", - data: JSON.parse(`<?= json_encode(array_map(function ($i) { - return $i["time"]; - }, array_values($members))) ?>`), - backgroundColor: JSON.parse(`<?= json_encode(array_map(function ($i) { - return "#" . $i["color"] . "33"; - }, array_values($members))) ?>`), - borderColor: JSON.parse(`<?= json_encode(array_map(function ($i) { - return "#" . $i["color"]; - }, array_values($members))) ?>`), - borderWidth: 1 - }; - const graph1 = new Chart(ctx1, { - type: 'bar', - data: { - labels: JSON.parse(`<?= json_encode(array_map(function ($i) { - return $i["name"]; - }, array_values($members))) ?>`), - datasets: [ - window.graph01 - ] - }, - options: { - animation: { - duration: 0 - }, - scales: { - y: { - beginAtZero: true, - ticks: { - callback: function(label) { - return toDuration(label); - } - } - }, - x: { - grid: { - color: "rgba(255,255,255,0.25)" - } - } - }, - elements: { - point:{ - radius: 0 - } - }, - plugins: { - legend: { - display: false - }, - tooltip: { - callbacks: { - label: function(tooltipItem) { - return toDuration(tooltipItem.raw, true); - } - } - } - } - } - }); - </script> - - <br> - <h3>Time at front per month</h3> - <canvas id="graph-02" style="width: 100%; height: 300px; max-height: 100%;"></canvas> - <script> - const ctx2 = document.getElementById('graph-02').getContext('2d'); - window.chart02 = JSON.parse(`<?= json_encode(array_values(array_map(function ($i) use ($frontersMonthMembers) { - $name = getMemberWithoutSystem(array_search($i, $frontersMonthMembers))["name"]; - $month = (int)implode("-", explode("-", array_search($i, $frontersMonthMembers))); - - $k = array_filter($i, function ($i) { - return isset($i["name"]) && isset($i["color"]); - }); - - if (count($k) > 0) { - return [ - "label" => $k[array_keys($k)[0]]["name"], - "data" => array_values(array_map(function ($j) use ($name, $month) { - return $name === "twilight" && $month < 202208 ? 0 : $j["duration"]; - }, $i)), - "borderColor" => "#" . $k[array_keys($k)[0]]["color"] - ]; - } else { - return [ - "label" => getMemberWithoutSystem(array_search($i, $frontersMonthMembers))["display_name"], - "data" => array_values(array_map(function ($j) { - return $j["duration"]; - }, $i)), - "borderColor" => "#" . getMemberWithoutSystem(array_search($i, $frontersMonthMembers))["color"] - ]; - } - }, $frontersMonthMembers))) ?>`); - console.log(JSON.parse(`<?= json_encode(array_map(function ($i) { - return date("M Y", strtotime($i . "-00T00:00:00.000Z")); - }, array_keys($frontersMonthRectified))) ?>`)); - const graph2 = new Chart(ctx2, { - type: 'line', - data: { - labels: JSON.parse(`<?= json_encode(array_map(function ($i) { - return date("M Y", strtotime($i . "-00T00:00:00.000Z")); - }, array_keys($frontersMonthRectified))) ?>`), - datasets: window.chart02 - }, - options: { - animation: { - duration: 0 - }, - scales: { - y: { - beginAtZero: true, - grid: { - color: "rgba(255,255,255,0.25)" - }, - ticks: { - callback: function(label) { - return toDuration(label); - } - } - } - }, - plugins: { - legend: { - display: false - }, - tooltip: { - intersect: false, - callbacks: { - label: function(tooltipItem) { - return tooltipItem.dataset.label + ": " + toDuration(tooltipItem.raw, true); - } - } - } - } - } - }); - </script> </div> </div> diff --git a/pages/together.inc b/pages/together.inc index 87faf6a..3799630 100644 --- a/pages/together.inc +++ b/pages/together.inc @@ -1,5 +1,6 @@ <?php +$_GET['old'] = ""; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; diff --git a/pages/travelling.inc b/pages/travelling.inc index e808063..b7b772b 100644 --- a/pages/travelling.inc +++ b/pages/travelling.inc @@ -116,7 +116,14 @@ global $travelling; <div class="container"> <div> <h2>System travelling</h2> - <?php foreach (scoreOrderGlobal() as $member): ?> + <?php foreach ([ + ...array_values(array_filter(scoreOrderGlobal(), function ($i) use ($travelling) { + return $travelling[$i['id']]["travelling"]; + })), + ...array_values(array_filter(scoreOrderGlobal(), function ($i) use ($travelling) { + return !$travelling[$i['id']]["travelling"]; + })) + ] as $member): ?> <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1fr 2fr max-content;"> <a class="relation-intro" style="background-color:rgba(255, 255, 255, .05);border-right:1px solid rgba(255, 255, 255, .1);margin:-10px;padding:10px;border-top-left-radius:10px;border-bottom-left-radius:10px;color: white;display:flex;align-items:center;text-decoration: none;" href="/<?= $member["name"] ?>"> <img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> |