summaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2022-11-11 23:47:49 +0100
committerMinteck <contact@minteck.org>2022-11-11 23:47:49 +0100
commit209356b8ade1920b50d1d3a1a5e121c6623d167b (patch)
tree5301396987d1510f715a0b1c24754873af19e1dc /pages
parent2c4ae43e688a9873e86211ea0e7aeb9ba770dd77 (diff)
downloadpluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.tar.gz
pluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.tar.bz2
pluralconnect-209356b8ade1920b50d1d3a1a5e121c6623d167b.zip
Update
Diffstat (limited to 'pages')
-rw-r--r--pages/about.inc2
-rw-r--r--pages/byfront.inc188
-rw-r--r--pages/computers.inc856
-rw-r--r--pages/debug.inc4
-rw-r--r--pages/docs.inc6
-rw-r--r--pages/edit.inc2
-rw-r--r--pages/parser.inc4
-rw-r--r--pages/prefix.inc4
-rw-r--r--pages/rules-old.inc253
-rw-r--r--pages/rules.inc243
-rw-r--r--pages/stats.inc172
-rw-r--r--pages/together.inc1
-rw-r--r--pages/travelling.inc9
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;">&nbsp;<?= $member["display_name"] ?? $member["name"] ?>
- <?php if ($member["_metadata"]["shared_memory"] !== 2): ?>
- &nbsp;<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;">&nbsp;<?= $member["display_name"] ?? $member["name"] ?>
- <?php if ($member["_metadata"]["shared_memory"] !== 2): ?>
- &nbsp;<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"] === "*" ? "&lt;all&gt;" : match ($connection["localAddress"]) {
+ "127.0.0.1" => "&lt;local IPv4&gt;",
+ "::1" => "&lt;local IPv6&gt;",
+ default => $connection["localAddress"],
+ } ?> (port <?= $connection["localPort"] ?>)</td>
+ <td><?php if ($connection["state"] !== "LISTEN"): ?><?= $connection["peerAddress"] === "*" ? "&lt;all&gt;" : match ($connection["peerAddress"]) {
+ "127.0.0.1" => "&lt;local IPv4&gt;",
+ "::1" => "&lt;local IPv6&gt;",
+ 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(">", "&gt;").replaceAll("<", "&lt;") + "\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(">", "&gt;").replaceAll("<", "&lt;") + " (caused by " + data.signal.toString().replaceAll(">", "&gt;").replaceAll("<", "&lt;") + ")</span>";
+ } else {
+ document.getElementById("output-text").innerHTML += "<span style='color: #ff8c8c;'>Command exited with signal " + data.signal.toString().replaceAll(">", "&gt;").replaceAll("<", "&lt;");
+ }
+ } else {
+ document.getElementById("output-text").innerHTML += "<span style='color: #ff8c8c;'>Command exited with code " + data.code.toString().replaceAll(">", "&gt;").replaceAll("<", "&lt;") + "</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(">", "&gt;").replaceAll("<", "&lt;") + "</span>";
+ } else {
+ document.getElementById("output-text").innerHTML += atob(data.value).replaceAll(">", "&gt;").replaceAll("<", "&lt;");
+ }
+
+ 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"] ?>&nbsp;
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=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%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=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%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('"', "&quot;", 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('"', "&quot;", 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;">&nbsp;<?= $member["display_name"] ?? $member["name"] ?>