diff options
author | Minteck <contact@minteck.org> | 2022-08-10 00:04:25 +0200 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-08-10 00:04:25 +0200 |
commit | 0417260bf158c5d7b62888dae2e202b0de115a05 (patch) | |
tree | 7f54ee56da118813f2d1b3a324d322d0c74fa51b /includes | |
download | pluralconnect-0417260bf158c5d7b62888dae2e202b0de115a05.tar.gz pluralconnect-0417260bf158c5d7b62888dae2e202b0de115a05.tar.bz2 pluralconnect-0417260bf158c5d7b62888dae2e202b0de115a05.zip |
Initial commit
Diffstat (limited to 'includes')
-rw-r--r-- | includes/banner.php | 222 | ||||
-rw-r--r-- | includes/edit.php | 174 | ||||
-rw-r--r-- | includes/footer.php | 57 | ||||
-rw-r--r-- | includes/header.php | 719 | ||||
-rw-r--r-- | includes/member.php | 84 | ||||
-rw-r--r-- | includes/refresh.php | 35 | ||||
-rw-r--r-- | includes/score.php | 74 | ||||
-rw-r--r-- | includes/session.php | 19 | ||||
-rw-r--r-- | includes/subsysbanner.php | 68 | ||||
-rw-r--r-- | includes/subsysedit.php | 172 | ||||
-rw-r--r-- | includes/sysbanner.php | 80 | ||||
-rw-r--r-- | includes/sysedit.php | 172 | ||||
-rw-r--r-- | includes/system.php | 61 | ||||
-rw-r--r-- | includes/system/compare.php | 189 | ||||
-rw-r--r-- | includes/system/history.php | 380 | ||||
-rw-r--r-- | includes/system/species.php | 53 | ||||
-rw-r--r-- | includes/system/subsystem.php | 124 | ||||
-rw-r--r-- | includes/system/tree.php | 114 |
18 files changed, 2797 insertions, 0 deletions
diff --git a/includes/banner.php b/includes/banner.php new file mode 100644 index 0000000..b5088df --- /dev/null +++ b/includes/banner.php @@ -0,0 +1,222 @@ +<?php + +global $memberData; +global $memberCommonName; +global $memberID; +global $systemCommonName; +global $systemID; +global $system; + +$subsystems = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystems.json"), true) ?? []; + +function getMember(string $id) { + global $systemID; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function memberPartOfSubsystem(array $member) { + global $subsystems; + $is = false; + + foreach ($subsystems as $subsystem) { + if (in_array($member["id"], $subsystem["members"])) { + $is = true; + } + } + + return $is; +} + +function getSubsystemFromMember(array $member) { + global $subsystems; + $ss = false; + + foreach ($subsystems as $subsystem) { + if (in_array($member["id"], $subsystem["members"])) { + $ss = $subsystem; + } + } + + return $ss; +} + +?> + +<style> + .bg-light, .bg-light * { + color: black !important; + } +</style> +<div id="system-info" style="border:1px solid #<?= $memberData["color"] ?>;background:rgba(255, 255, 255, .1);border-radius:10px;display:grid;grid-template-columns: 128px 1fr;"> + <div style="margin:10px;width:100%;display:flex;align-items: center;justify-content: center;"> + <img id="member-icon" src="<?= $memberData['avatar_url'] ?>" alt="" style="height:128px;border-radius:5px;"> + </div> + <div style="padding:10px 10px 10px 20px;text-align:center;"> + <h3 style="margin-bottom:0;"> + <?= $memberCommonName ?> + </h3> + <div style="margin-bottom:0.5rem;"> + <?php if ($metadata["host"] ?? false): ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Host</b><br>This pony is the one who fronts the most often in their system." class="badge rounded-pill bg-primary">Host</span> + <?php endif; ?> + <?php if ($metadata["fictive"] ?? false): ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fictive</b><br>This pony is based on the personality, look and behavior of a character that is fictional in this world." class="badge rounded-pill bg-info">Fictive</span> + <?php endif; ?> + <?php if (($metadata["little"] ?? 0) === 2): ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Little</b><br>This pony is mental younger, and therefore behaves and feels younger than the body is." class="badge rounded-pill bg-success">Little</span> + <?php endif; ?> + <?php if (($metadata["little"] ?? 0) === 1): ?> + <?php if ($metadata["regression"] !== null && $metadata["regression"] !== false): $regression = getMember($metadata["regression"]); ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Age regressor</b><br>This pony is capable of regressing their mental age, which causes them to become <?= getMiniName($regression["display_name"] ?? $regression["name"]) ?>, temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-secondary">Age regresses into <a href="/<?= $system ?>/<?= $regression["name"] ?>"><?= getMiniName($regression["display_name"] ?? $regression["name"]) ?></a></span> + <?php else: ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Age regressor</b><br>This pony is capable of regressing their mental age, temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-secondary">Age regressor</span> + <?php endif; ?> + <?php endif; ?> + <?php if ($metadata["median"] !== null && $metadata["median"] !== false): $source = getMember($metadata["median"]) ?> + <?php if ($metadata["little"] > 0): ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Age regressed</b><br>This pony has regressed their mental age, making them <?= getMiniName($memberData["display_name"] ?? $memberData["name"]) ?> instead of <?= getMiniName($source["display_name"] ?? $source["name"]) ?>, temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-warning">Age regressed from <a href="/<?= $system ?>/<?= $source["name"] ?>"><?= getMiniName($source["display_name"] ?? $source["name"]) ?></a></span> + <?php else: ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Facet</b><br>This pony is a facet of <?= getMiniName($source["display_name"] ?? $source["name"]) ?>, meaning they are not totally independent from <?= getMiniName($source["display_name"] ?? $source["name"]) ?>." class="badge rounded-pill bg-light">Facet of <a href="/<?= $system ?>/<?= $source["name"] ?>"><?= getMiniName($source["display_name"] ?? $source["name"]) ?></a></span> + <?php endif; ?> + <?php endif; ?> + <?php if ($metadata["not_talking"] ?? false): ?> + <span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Not talking</b><br>Although they are present in the system, this pony does not want to communicate with other members." class="badge rounded-pill bg-danger">Not talking</span> + <?php endif; ?> + <?php if (!($metadata["fictive"] ?? false) && !($metadata["host"] ?? false) && !($metadata["little"] ?? false) && !($metadata["not_talking"] ?? false)): ?> + + <?php endif; ?> + </div> + <div style="display:grid;grid-template-columns: repeat(<?php if (!$metadata["median"]): ?>5<?php else: ?>4<?php endif; ?>, 1fr);" id="member-card"> + <span> + <b>Prefixes: </b> + <?php $index = 0; foreach ($memberData['proxy_tags'] as $proxy): ?> + <code style="color: white;"><?= $proxy["prefix"] ?><?= $proxy["suffix"] !== "" && $proxy["suffix"] !== null ? "..." . $proxy["suffix"] : "" ?></code><?php if ($index + 2 <= count($memberData["proxy_tags"])) echo(", "); ?> + <?php $index++; endforeach; ?> + </span> + <span> + <b>Pronouns: </b> + <?= $memberData["pronouns"] ?> + </span> + <?php if (!$metadata["median"]): ?> + <span> + <b>Last fronted: </b> + <?php + + $fronters = array_map(function ($item) { + return $item["id"]; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["members"]); + + if (in_array($memberID, $fronters)) { + echo("Right now<br>(started <span data-bs-toggle=\"tooltip\" title=\"" . date("D j M Y, G:i:s (e)", strtotime(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["timestamp"])) . "\">" . trim(timeAgo(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["timestamp"])) . "</span>)"); + } else { + $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-switches.json"), true); + + $thisMember = array_filter($switches, function ($item) { + global $memberData; + return in_array($memberData["id"], $item["members"]); + }); + $thisMember = array_values($thisMember); + $thisIndex = array_search($thisMember[0], $switches); + + $frontingStart = $thisMember[0]; + $frontingEnd = $switches[$thisIndex - 1]; + + if ($frontingEnd === null) { + echo("A long time ago<br>-"); + } else { + echo('<span data-bs-toggle="tooltip" title="' . date("D j M Y, G:i:s (e)", strtotime($frontingEnd["timestamp"])) . '">' . timeAgo($frontingEnd["timestamp"]) . '</span>'); + + $seconds = (strtotime($frontingEnd["timestamp"]) - strtotime($frontingStart["timestamp"])); + if ($seconds > 60) { + if ($seconds > 3600) { + echo("<br>(for " . round($seconds / 3600) . " hours)"); + } else { + echo("<br>(for " . round($seconds / 60) . " minutes)"); + } + } else { + echo("<br>(for " . $seconds . " seconds)"); + } + } + } + + ?> + </span> + <?php endif; ?> + <span> + <span style="vertical-align: middle;position:relative;top:-5px;"><b>Species: </b></span> + <?php foreach ($metadata["species"] ?? [] as $species): ?> + <img data-bs-toggle="tooltip" title="<?php switch ($species) { + case "earth": + echo "Earth pony"; + break; + + case "alicorn": + echo "Alicorn"; + break; + + case "pegasus": + echo "Pegasus"; + break; + + case "batpony": + echo "Bat pony"; + break; + + case "unicorn": + echo "Unicorn"; + break; + + default: + echo $species; + break; + } ?>" style="width:32px;vertical-align: middle;position:relative;top:-5px;" src="/assets/species/<?= $species ?>.png" alt="<?= $species ?>"> + <?php endforeach; ?> + </span> + <span> + <b>System: </b><a class="member-link" href="/<?= $system ?>"><img style="width:24px;border-radius:5px;" src="/assets/uploads/<?= $system ?>.png"> <?= getMiniName($systemCommonName) ?></a> + <?php if (memberPartOfSubsystem($memberData) && getSubsystemFromMember($memberData)["source_type"] !== "member"): $subsystem = getSubsystemFromMember($memberData); ?> + <br><b>Subsystem: </b><a class="member-link" href="/<?= $system ?>/-/subsystem/<?= $subsystem["source"] ?>"><img style="width:24px;border-radius:5px;" src="/assets/uploads/ss-<?= $subsystem['source'] ?>.png"> <?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystem[source].json") ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystem[source].json"), true)["name"] : $subsystem["source"] ?></a> + <?php endif; ?> + </span> + </div> + <div style="display:grid;grid-template-columns: repeat(2, 1fr);margin-top:5px;"> + <?php + + if ($memberData["name"] === "scootaloo") { + if ((int)date('j') % 2 === 0) { + $metadata["marefriends"] = array_reverse($metadata["marefriends"]); + } + } + + ?> + <span> + <b>Marefriends: </b><?= count($metadata["marefriends"]) > 1 ? '<span class="list-separator-mobile"><br></span>' : '' ?> + <?php $index = 0; foreach ($metadata["marefriends"] as $marefriend): $mfSystem = explode("/", $marefriend)[0]; $mfMemberID = explode("/", $marefriend)[1]; $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem-members.json"), true), function ($item) { + global $mfMemberID; + return $item["id"] === $mfMemberID; + }); sort($mfMember); $mfMember = $mfMember[0]; ?> + <a class="member-link" href="/<?= $mfSystem === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $mfMember["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $mfMember['name'] . ".png") ? "-" . $mfMember['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($mfMember["display_name"] ?? $mfMember["name"]) ?></a><?php if ($index + 2 <= count($metadata["marefriends"])) echo('<span class="list-separator-desktop">, </span><span class="list-separator-mobile"><br></span>'); ?> + <?php $index++; endforeach; ?> + <?php if (count($metadata["marefriends"]) === 0): ?>-<?php endif; ?> + </span> + <span> + <b>Sisters: </b><?= count($metadata["sisters"]) > 1 ? '<span class="list-separator-mobile"><br></span>' : '' ?> + <?php $index = 0; foreach ($metadata["sisters"] as $marefriend): $mfSystem = explode("/", $marefriend)[0]; $mfMemberID = explode("/", $marefriend)[1]; $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem-members.json"), true), function ($item) { + global $mfMemberID; + return $item["id"] === $mfMemberID; + }); sort($mfMember); $mfMember = $mfMember[0]; ?> + <a class="member-link" href="/<?= $mfSystem === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $mfMember["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $mfMember['name'] . ".png") ? "-" . $mfMember['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($mfMember["display_name"] ?? $mfMember["name"]) ?></a><?php if ($index + 2 <= count($metadata["sisters"])) echo('<span class="list-separator-desktop">, </span><span class="list-separator-mobile"><br></span>'); ?> + <?php $index++; endforeach; ?> + <?php if (count($metadata["sisters"]) === 0): ?>-<?php endif; ?> + </span> + </div> + </div> +</div>
\ No newline at end of file diff --git a/includes/edit.php b/includes/edit.php new file mode 100644 index 0000000..839c130 --- /dev/null +++ b/includes/edit.php @@ -0,0 +1,174 @@ +<?php global $system; global $systemCommonName; global $systemID; global $member; global $memberData; global $memberCommonName; global $memberID; $title = "Editing " . $memberCommonName . " · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +$metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $systemID . "-" . $memberID . "-metadata.json"), true); + +?> + +<br> +<div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/banner.php"; ?> + <br> + + <p class="text-muted"> + <span id="editor-save-status">Saved</span> · <span id="editor-size"><?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-disclaimers.html") ? strlen(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-disclaimers.html")) : "0" ?></span> bytes + </p> + + <!--suppress HtmlFormInputWithoutLabel --> + <textarea id="page-editor"> + <?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-disclaimers.html") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-disclaimers.html") : "" ?> + </textarea> + + <script src="/assets/editor/editor.js"></script> + <script> + let editor; + ClassicEditor + .create( document.querySelector( '#page-editor' ), { + toolbar: [ + 'undo', 'redo', '|', 'removeFormat', '|', 'heading', '|', 'fontSize', 'fontColor', 'fontBackgroundColor', 'alignment', '|', 'bold', 'italic', 'underline', 'strikethrough', '|', 'subscript', 'superscript', '|', 'code', '|', 'outdent', 'indent', '|', 'bulletedList', 'numberedList', '|', 'link', 'imageUpload', 'mediaEmbed', 'blockQuote', 'insertTable', 'codeBlock', '|', 'horizontalLine' + ] + } ) + + .then( newEditor => { + editor = newEditor; + } ) + .catch( error => { + console.error( error ); + } ); + </script> + <style> + :root { + --ck-color-base-background: transparent; + } + + .ck-toolbar { + filter: invert(1); + } + + .ck-tooltip__text { + color: white !important; + } + + .ck-dropdown__panel { + background: #ddd !important; + } + + .ck-color-grid__tile { + filter: invert(1); + } + + .ck-balloon-rotator { + background-color: #ccc !important; + } + + .ck-balloon-panel { + filter: invert(1); + } + </style> + <script> + let lastSavedData = editor.getData(); + let lastFetchedData = editor.getData(); + let timeSinceLastModified = 0; + let saving = false; + + async function save() { + let data = editor.getData(); + document.getElementById("editor-save-status").innerHTML = "Saving..."; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.add("text-primary"); + saving = true; + + try { + await window.fetch("/api/save?system=<?= $systemID ?>&member=<?= $memberID ?>", { + method: "POST", + body: JSON.stringify({ content: data }) + }); + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + lastSavedData = data; + saving = false; + } catch (e) { + console.error(e); + document.getElementById("editor-save-status").innerHTML = "Failed to save"; + document.getElementById("editor-save-status").classList.add("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + } + + document.onclick = async () => { + if (saving) return; + + if (editor.getData() !== lastSavedData) { + await save(); + } + } + + setInterval(async () => { + if (saving) return; + + document.getElementById("editor-size").innerHTML = editor.getData().length; + + if (editor.getData() !== lastSavedData) { + document.getElementById("editor-save-status").innerHTML = "Modified"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.add("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + + if (editor.getData() !== lastFetchedData) { + lastFetchedData = editor.getData(); + timeSinceLastModified = 0; + } else { + timeSinceLastModified++; + } + + if (timeSinceLastModified > 20) { + await save(); + } + } else { + timeSinceLastModified = 0; + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + }, 100) + </script> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..c120941 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,57 @@ +<?php + +if (!function_exists("timeAgo")) { + function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; + } +} + +?> + +<hr> +<div class="container text-muted"> + © <?= date("Y") ?> <a href="https://equestria.horse" target="_blank" class="text-muted">Equestria.dev Developers</a><br> + PluralKit data updated <?= trim(timeAgo(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/refresh.json"), true)["timestamp"])) ?>, next update in <?php $t = 5 - round((time() - json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/refresh.json"), true)["timestamp"]) / 60); ?><?= $t > 1 ? $t . " minutes" : ($t > 0 ? "a minute" : "a few seconds") ?> + <br><br><br> +</div> + +<script> + let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + let tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl) + }); + + Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')).forEach((item) => { + if (!item.classList.contains("tooltip-nohelp")) { + item.style.cursor = "help"; + } + }) +</script> + +</body> +</html>
\ No newline at end of file diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..af12bcb --- /dev/null +++ b/includes/header.php @@ -0,0 +1,719 @@ +<?php global $title; + +require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/score.php"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; + +function getMiniName(string $name) { + $parts = explode(" ", $name); + + if (strlen($parts[0]) > 3 && !str_ends_with($parts[0], "e") && $parts[0] !== "Filly") { + if (str_contains($parts[0], "/")) { + return explode("/", $parts[0])[0]; + } else { + return $parts[0]; + } + } else { + return $name; + } +} + +function getSystemMember(string $system, string $id) { + $systemID = $system; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function getBrightness(string $hexCode) { + if (str_starts_with("#", $hexCode)) { + $hexCode = substr($hexCode, 1); + } + + $red = hexdec(substr($hexCode, 0, 2)); + $green = hexdec(substr($hexCode, 2, 2)); + $blue = hexdec(substr($hexCode, 4, 2)); + $brightness = $red + $green + $blue; + + return $brightness > 382; +} + +function showMembersFromList(array $list, string $id) { + foreach ($list as $member) { if ($member['name'] !== "unknown") { + echo('<!-- ' . ($member['display_name'] ?? $member['name']) . ' --> +<a href="/' . ($id === "gdapd" ? "raindrops" : "cloudburst") . '/' . $member['name'] . '" style="text-decoration:none !important;filter:none !important;"><div class="hpd-item-card" style="background-color:rgba(255, 255, 255, .1);border-radius:10px;text-align:center;display:flex;align-items:center;justify-content:center;padding:5px;"><div> +<img alt="" src="/assets/uploads/pt' . (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "") . '.png" style="height:48px;display:block;margin-left:auto;margin-right:auto;"> +<div style="text-decoration:none;color:white;margin-top:5px;">' . ($member['display_name'] ?? $member['name']) . '</div> +<div style="text-decoration:none !important;color:black !important;"><code style="text-decoration:none !important;color:white !important;">' . $member['proxy_tags'][0]['prefix'] . '</code></div> +</div></div></a>'); + }} +} + +function showSubsystem(array $data, string $parentSystem) { + $subsystemData = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$parentSystem-subsystem-$data[source].json"), true); + + echo('<!-- ' . $subsystemData["name"] . ' --> +<div id="hpd-cloudburst" style="background:rgba(255, 255, 255, .1);border-radius:10px;padding:10px;display:grid;grid-template-columns: 1fr;margin-bottom:10px;">'); + echo(' <div style="display:grid;grid-template-columns:repeat(6, 1fr);grid-gap:10px;">'); + + showMembersFromList(scoreOrder(array_map(function ($i) use ($parentSystem) { + return getSystemMember($parentSystem, $i); + }, $data["members"]), $parentSystem), $parentSystem); + + echo('</div> + +</div>'); +} + +function showSystem(string $id, string $name, string $color, bool $hideTitle) { + if ($hideTitle) { + echo('<!-- ' . $name . ' --> +<div id="hpd-' . ($id === "gdapd" ? "raindrops" : "cloudburst") . '" style="background:rgba(255, 255, 255, .1);border-radius:10px;padding:10px;display:grid;grid-template-columns: 1fr;margin-bottom:10px;">'); + } else { + echo('<!-- ' . $name . ' --> +<div id="hpd-' . ($id === "gdapd" ? "raindrops" : "cloudburst") . '" style="background:rgba(255, 255, 255, .1);border-radius:10px;padding:10px 10px 10px 20px;display:grid;grid-template-columns: 128px 1fr;margin-bottom:10px;">'); + } + + if (!$hideTitle) echo('<!-- System Name --> +<a style="display:flex;margin: -10px -20px;align-items:center;justify-content:center;text-align:center;padding: 10px 20px;border-radius: 10px;background: ' . $color . ';width: 148px;text-decoration:none;color:white;filter:none !important;" href="/' . ($id === "gdapd" ? "raindrops" : "cloudburst") . '" class="hpd-system"> +' . $name . ' +</a>'); + + if ($hideTitle) { + echo(' <div style="display:grid;grid-template-columns:repeat(6, 1fr);grid-gap:10px;">'); + } else { + echo(' <div style="display:grid;grid-template-columns:repeat(6, 1fr);padding-left:10px;grid-gap:10px;">'); + } + + showMembersFromList(scoreOrder(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$id-members.json"), true), $id), $id); + + echo('</div> + +</div>'); +} + +function cloudburst(bool $hideTitle): void { + showSystem("ynmuc", "Cloudburst System", "#5f08a9a6", $hideTitle); +} + +function raindrops(bool $hideTitle): void { + showSystem("gdapd", "Raindrops System", "#a95f08a6", $hideTitle); +} + +?> +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> + <title><?= $title ? $title . " · " : "" ?>Cuties and Plurality</title> + <link rel="shortcut icon" href="/assets/uploads/logo.jpg" type="image/jpg"> + <style> + nav.navbar { + background-color: black !important; + border-bottom: 1px solid rgba(255, 255, 255, .25); + } + + body { + background-color: black !important; + color: white; + } + + .hpd-item-card:hover { + background-color: rgba(255, 255, 255, .15) !important; + } + + .hpd-item-card:active, .hpd-item-card:focus { + background-color: rgba(255, 255, 255, .2) !important; + } + + .hpd-system:hover { + opacity: .9 !important; + } + + .hpd-system:active, .hpd-system:focus { + opacity: .8 !important; + } + + .hpd-link:hover { + background-color: rgba(255, 255, 255, .15) !important; + } + + .hpd-link:active, .hpd-link:focus { + background-color: rgba(255, 255, 255, .2) !important; + } + + .list-separator-mobile { + display: none; + } + + @media (max-width: 991px) { + #hpd-cloudburst > div, #hpd-raindrops > div { + grid-template-columns: repeat(3, 1fr) !important; + } + + .list-separator-desktop { + display: none; + } + + span.list-separator-mobile { + display: inline; + } + } + + @media (max-width: 768px) { + #hpd-cloudburst > div, #hpd-raindrops > div { + grid-template-columns: repeat(2, 1fr) !important; + } + } + + @media (max-width: 575px) { + #hpd-cloudburst > div, #hpd-raindrops > div { + grid-template-columns: repeat(1, 1fr) !important; + } + + .hpd-item-card img { + display: inline-block !important; + margin-right: 5px !important; + height: 32px !important; + } + + #hpd-cloudburst > div, #hpd-raindrops > div { + grid-gap: 5px !important; + } + + .hpd-item-card div { + display: inline-block !important; + } + + .hpd-item-card div:nth-child(3)::before { + content: "("; + padding-left: 5px; + color: white !important; + } + + .hpd-item-card div:nth-child(3)::after { + content: ")"; + color: white !important; + } + } + + .dropdown-menu { + background-color: #222; + } + + .dropdown-item:hover { + background-color: rgba(255, 255, 255, .1); + } + + .dropdown-item:active, .dropdown-item:focus { + background-color: rgba(255, 255, 255, .2); + } + + .dropdown-item { + color: white !important; + } + + .dropdown-icon { + filter: invert(1); + } + + .dropdown-toggle .dropdown-icon { + opacity: .5; + transition: 200ms opacity; + } + + .dropdown-toggle:hover .dropdown-icon, .dropdown-toggle:active .dropdown-icon, .dropdown-toggle:focus .dropdown-icon { + opacity: .75; + } + + dd { + margin-left: 20px; + } + + #system-info a { + color: white; + } + + #system-info a:hover { + opacity: .75; + } + + #system-info a:active, #system-info a:focus { + opacity: .5; + } + + @media (max-width: 991px) { + #member-card { + grid-template-columns: repeat(3, 1fr) !important; + } + + .species-name { + display: none; + } + } + + @media (max-width: 767px) { + #member-card { + grid-template-columns: 1fr !important; + text-align: left; + } + } + + #page-content a { + color: #afd0ff; + } + + #page-content a:hover { + opacity: .75; + } + + #page-content a:active, #page-content a:focus { + opacity: .5; + } + + .tooltip.show { + opacity: 1; + } + + .tooltip-inner { + background: #151515; + box-shadow: 3px 4px 10px #ffffff26; + } + + .alert { + filter: invert(1) hue-rotate(180deg); + } + + .member-link { + color: white !important; + text-decoration: none !important; + } + + .system-action { + border-radius: 10px; + color: white !important; + text-decoration: none !important; + cursor: pointer; + } + + .system-action:hover { + background:rgba(255, 255, 255, .1); + } + + .table-dark { + --bs-table-bg: #000000; + } + + .comparison { + display: grid; + grid-template-columns: 3fr repeat(2, 2fr) repeat(5, 1fr); + } + + .comparison-header { + border-bottom: 2px solid rgba(255, 255, 255, .25); + font-weight: bold; + } + + .comparison-item { + padding: 5px 10px; + text-align: center; + } + + .comparison-item-clickable:hover { + background-color: rgba(255, 255, 255, .1); + } + + .comparison-item-clickable:active, .comparison-item-clickable:focus { + background-color: rgba(255, 255, 255, .25); + } + + @media (min-width: 1400px) { + .comparison-header-l0 { + display: inline; + } + .comparison-header-l1 { + display: none; + } + .comparison-header-l2 { + display: none; + } + .comparison-header-l3 { + display: none; + } + .comparison-header-l4 { + display: none; + } + .comparison-header-l5 { + display: none; + } + .comparison-name-full { + display: inline; + } + .comparison-name-small { + display: none; + } + .comparison-colors { + display: inline; + } + .comparison-relations-count { + display: none; + } + .comparison-relations-full { + display: inline; + } + } + + @media (max-width: 1399px) { + .comparison-header-l0 { + display: none; + } + .comparison-header-l1 { + display: inline; + } + .comparison-header-l2 { + display: none; + } + .comparison-header-l3 { + display: none; + } + .comparison-header-l4 { + display: none; + } + .comparison-header-l5 { + display: none; + } + .comparison-name-full { + display: inline; + } + .comparison-name-small { + display: none; + } + .comparison-colors { + display: inline; + } + .comparison-relations-count { + display: none; + } + .comparison-relations-full { + display: inline; + } + } + + @media (max-width: 1199px) { + .comparison-header-l0 { + display: none; + } + .comparison-header-l1 { + display: none; + } + .comparison-header-l2 { + display: initial; + } + .comparison-header-l3 { + display: none; + } + .comparison-header-l4 { + display: none; + } + .comparison-header-l5 { + display: none; + } + .comparison-name-full { + display: none; + } + .comparison-name-small { + display: inline; + } + .comparison-colors { + display: inline; + } + .comparison-relations-count { + display: inline; + } + .comparison-relations-full { + display: none; + } + } + + @media (max-width: 991px) { + .comparison-header-l0 { + display: none; + } + .comparison-header-l1 { + display: none; + } + .comparison-header-l2 { + display: none; + } + .comparison-header-l3 { + display: initial; + } + .comparison-header-l4 { + display: none; + } + .comparison-header-l5 { + display: none; + } + .comparison-name-full { + display: none; + } + .comparison-name-small { + display: inline; + } + .comparison-colors { + display: none !important; + } + .comparison-relations-count { + display: inline; + } + .comparison-relations-full { + display: none; + } + } + + @media (max-width: 767px) { + .comparison-header-l0 { + display: none; + } + .comparison-header-l1 { + display: none; + } + .comparison-header-l2 { + display: none; + } + .comparison-header-l3 { + display: none; + } + .comparison-header-l4 { + display: initial; + } + .comparison-header-l5 { + display: none; + } + .comparison-name-full { + display: none; + } + .comparison-name-small { + display: none; + } + .comparison-colors { + display: none !important; + } + .comparison-relations-count { + display: inline; + } + .comparison-relations-full { + display: none; + } + } + + @media (max-width: 575px) { + .comparison-header-l0 { + display: none; + } + .comparison-header-l1 { + display: none; + } + .comparison-header-l2 { + display: none; + } + .comparison-header-l3 { + display: none; + } + .comparison-header-l4 { + display: none; + } + .comparison-header-l5 { + display: initial; + } + .comparison-name-full { + display: none; + } + .comparison-name-small { + display: none; + } + .comparison-colors { + display: none !important; + } + .comparison { + grid-template-columns: repeat(3, 2fr) repeat(5, 1fr) !important; + } + .comparison-relations-count { + display: inline; + } + .comparison-relations-full { + display: none; + } + } + + .tree-first-separator { + height: 14px !important; + top: 0 !important; + } + + .tree-l0-separator { + display: inline-block; + width: 20px; + margin-left: 35px; + border-bottom: 1px solid white; + border-left: 1px solid white; + height: 26px; + position: relative; + top: -12px; + } + + .tree-l1 .tree-l0-separator { + border-bottom: none !important; + } + + .tree-l1-separator { + display: inline-block; + width: 20px; + margin-left: 35px; + border-bottom: 1px solid white; + border-left: 1px solid white; + height: 26px; + position: relative; + top: -12px; + left: -10px; + } + + .tree-l1 .tree-l0-separator { + width: 30px; + } + + .tree-l1 .tree-inner { + position: relative; + left: -10px; + } + + .tree-inner { + display: inline-block; + } + </style> +</head> +<body> + <nav class="navbar navbar-expand-md bg-dark navbar-dark"> + <div class="container-fluid"> + <a class="navbar-brand" href="/"><img src="/assets/uploads/logo.jpg" alt="" style="width:32px;vertical-align: middle;margin-right:5px;"> <span style="vertical-align: middle;">Cuties and Plurality</span><a> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="collapsibleNavbar"> + <ul class="navbar-nav"> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> + <img src="<?= $isLoggedIn ? "/assets/icons/loggedin.svg" : "/assets/icons/global.svg" ?>" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Global</span> + </a> + <ul class="dropdown-menu"> + <?php if ($isLoggedIn): ?> + <li><a class="dropdown-item" href="/emergency"> + <img src="/assets/icons/emergency.svg" alt="" style="width:24px;vertical-align: middle;"> + <span class="text-danger" style="vertical-align: middle;">Emergency Alert</span> + </a></li> + <li><hr class="dropdown-divider"></li> + <?php endif; ?> + <li><a class="dropdown-item" href="/"> + <img src="/assets/icons/home.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Home</span> + </a></li> + <li><a class="dropdown-item" href="/disclaimers"> + <img src="/assets/icons/disclaimers.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Disclaimers</span> + </a></li> + <li><a class="dropdown-item" href="/relations"> + <img src="/assets/icons/relations.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Relations</span> + </a></li> + <li><a class="dropdown-item" href="/terminology"> + <img src="/assets/icons/terminology.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Terminology</span> + </a></li> + <li><hr class="dropdown-divider"></li> + <li><h5 class="dropdown-header">Tools</h5></li> + <li><a class="dropdown-item" href="/parser"> + <img src="/assets/icons/parser.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Message Parser</span> + </a></li> + <li><a class="dropdown-item" href="/prefix"> + <img src="/assets/icons/prefix.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Prefix Generator</span> + </a></li> + <li><hr class="dropdown-divider"></li> + <li><h5 class="dropdown-header">Administrator</h5></li> + <?php if ($isLoggedIn): ?> + <li><a class="dropdown-item" href="/fronting"> + <img src="/assets/icons/fronting.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Front Planner</span> + </a></li> + <li><a class="dropdown-item" href="/score"> + <img src="/assets/icons/score.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Score System Testing</span> + </a></li> + <li><a class="dropdown-item" href="/logout"> + <img src="/assets/icons/logout.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Logout</span> + </a></li> + <?php else: ?> + <li><a class="dropdown-item" href="/login"> + <img src="/assets/icons/login.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Login</span> + </a></li> + <?php endif; ?> + </ul> + </li> + <?php if (!isset($emergencyHeader) || !$emergencyHeader): ?> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="/cloudburst" role="button" data-bs-toggle="dropdown"> + <img src="/assets/uploads/cloudburst.png" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Cloudburst System</span> + </a> + <ul class="dropdown-menu"> + <li><a class="dropdown-item" href="/cloudburst"> + <img src="/assets/icons/about.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">About us</span> + </a></li> + <li><hr class="dropdown-divider"></li> + <li><h5 class="dropdown-header">Members (<?= count(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true)) - 1 ?>)</h5></li> + <?php foreach (scoreOrder(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true), "ynmuc") as $member): if ($member['name'] !== "unknown"): ?> + <li><a class="dropdown-item" href="/cloudburst/<?= $member['name'] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;"><?= $member['display_name'] ?? $member['name'] ?></span> + </a></li> + <?php endif; endforeach; ?> + </ul> + </li> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="/raindrops" role="button" data-bs-toggle="dropdown"> + <img src="/assets/uploads/raindrops.png" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">Raindrops System</span> + </a> + <ul class="dropdown-menu"> + <li><a class="dropdown-item" href="/raindrops"> + <img src="/assets/icons/about.svg" class="dropdown-icon" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;">About us</span> + </a></li> + <li><hr class="dropdown-divider"></li> + <li><h5 class="dropdown-header">Members (<?= count(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true)) - 1 ?>)</h5></li> + <?php foreach (scoreOrder(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true), "gdapd") as $member): if ($member['name'] !== "unknown"): ?> + <li><a class="dropdown-item" href="/raindrops/<?= $member['name'] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" alt="" style="width:24px;vertical-align: middle;"> + <span style="vertical-align: middle;"><?= $member['display_name'] ?? $member['name'] ?></span> + </a></li> + <?php endif; endforeach; ?> + </ul> + </li> + <?php endif; ?> + </ul> + </div> + </div> + </nav>
\ No newline at end of file diff --git a/includes/member.php b/includes/member.php new file mode 100644 index 0000000..ad7995f --- /dev/null +++ b/includes/member.php @@ -0,0 +1,84 @@ +<?php global $system; global $systemCommonName; global $systemID; global $member; global $memberData; global $memberCommonName; global $memberID; $title = $memberCommonName . " · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +$metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $systemID . "-" . $memberID . "-metadata.json"), true); + +?> + +<br> +<div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/banner.php"; ?> + <br> + + <?php global $isLoggedIn; if ($isLoggedIn): ?> + <div class="alert alert-dark"> + <details> + <summary>Private administrator information</summary> + <ul style="margin-bottom:0;"> + <li><b>ID:</b> <code><?= $memberID ?></code> (<code><?= $systemID . "/" . $memberID ?></code>, <?= $memberData["name"] ?>)</li> + <li><b>Reduced name:</b> <?= getMiniName($memberData["display_name"] ?? $member["name"]) ?></li> + <li><b>Shared memory access:</b> <code><?= $metadata["shared_memory"] ?></code> (<?= $metadata["shared_memory"] === 2 ? "Full direct access" : ($metadata["shared_memory"] === 0 ? "No direct access" : "Partial direct access") ?>)</li> + <li><b>Protector:</b> <code><?= $metadata["protector"] ? "1" : "0" ?></code> (<?= $metadata["protector"] ? "Yes" : "No" ?>)</li> + <li><b>Little:</b> <code><?= $metadata["little"] ?></code> (<?= $metadata["little"] === 2 ? "Is a little" : ($metadata["little"] === 1 ? "Is an age regressor" : "No") ?>)</li> + <li><b>Relations count:</b> <code><?= count($metadata["marefriends"]) + count($metadata["sisters"]) ?></code></li> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; $score = calculateScore($metadata, $memberData["display_name"] ?? $memberData["name"]); ?> + <li> + <b>Score breakdown:</b> <code><?= $score["total"] ?></code> + <details> + <summary>Show details</summary> + <ul> + <li><b>Host score:</b> <code><?= $score["host"] ?></code></li> + <li><b>Relationships score:</b> <code><?= $score["relations"] ?></code></li> + <li><b>Fictive score:</b> <code><?= $score["fictive"] ?></code></li> + <li><b>Median score:</b> <code><?= $score["median"] ?></code></li> + <li><b>Species score:</b> <code><?= $score["species"] ?></code></li> + <li><b>Little score:</b> <code><?= $score["little"] ?></code></li> + <li><b>Not talking score:</b> <code><?= $score["not_talking"] ?></code></li> + <li><b>Protector score:</b> <code><?= $score["protector"] ?></code></li> + <li><b>Name score:</b> <code><?= $score["name"] ?></code></li> + <li><b>Shared memory score:</b> <code><?= $score["shared_memory"] ?></code></li> + </ul> + </details> + </li> + </ul> + </details> + </div> + <?php endif; ?> + + <div id="page-content"> + <?php global $isLoggedIn; if ($isLoggedIn): ?> + <small style="opacity:.5;display:block;">(<a href="/edit/<?= $system ?>/<?= $memberData['name'] ?>">edit</a>)</small> + <?php endif; ?> + <?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-content.html") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$memberID-content.html") : "<i>This page is empty.</i>" ?> + </div> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/refresh.php b/includes/refresh.php new file mode 100644 index 0000000..c29136f --- /dev/null +++ b/includes/refresh.php @@ -0,0 +1,35 @@ +<?php + +$start = time(); +@mkdir("./data"); + +function getSystem(string $id) { + echo("System: $id\n"); + + echo(" Base system info\n"); + file_put_contents("./data/$id-general.json", file_get_contents("https://api.pluralkit.me/v2/systems/$id")); + sleep(1); + + echo(" System members\n"); + file_put_contents("./data/$id-members.json", file_get_contents("https://api.pluralkit.me/v2/systems/$id/members")); + sleep(1); + + echo(" Fronters\n"); + file_put_contents("./data/$id-fronters.json", file_get_contents("https://api.pluralkit.me/v2/systems/$id/fronters")); + sleep(1); + + echo(" Switches\n"); + file_put_contents("./data/$id-switches.json", file_get_contents("https://api.pluralkit.me/v2/systems/$id/switches")); + sleep(1); +} + +getSystem("gdapd"); // Raindrops +getSystem("ynmuc"); // Cloudburst +$time = (time() - $start); + +echo("Completed in " . $time . " seconds.\n"); + +file_put_contents("./data/refresh.json", json_encode([ + "timestamp" => time(), + "duration" => $time +]));
\ No newline at end of file diff --git a/includes/score.php b/includes/score.php new file mode 100644 index 0000000..c3108f2 --- /dev/null +++ b/includes/score.php @@ -0,0 +1,74 @@ +<?php + +function calculateScore($metadata, $name) { + $scoreHost = $metadata["host"] ? 10000 : 0; + $scoreFictive = $metadata["fictive"] ? 200 : 0; + $scoreLittle = $metadata["little"] === 2 ? 100 : ($metadata["little"] === 1 ? 50 : 0); + $scoreNotTalking = $metadata["not_talking"] ? -200 : 0; + $scoreMedian = $metadata["median"] !== false ? -50 : 0; + $scoreProtector = $metadata["protector"] ? 1000 : 0; + $scoreSharedMemory = $metadata["shared_memory"] === 0 ? 200 : ($metadata["shared_memory"] === 1 ? 50 : 0); + $scoreSpecies = (in_array("pegasus", $metadata["species"]) ? 100 : 0) + (in_array("unicorn", $metadata["species"]) ? 75 : 0) + (in_array("earth", $metadata["species"]) ? 50 : 0) + (in_array("alicorn", $metadata["species"]) ? 150 : 0) + (in_array("batpony", $metadata["species"]) ? 125 : 0); + $scoreName = strlen($name) * 5; + $relations = (count($metadata["marefriends"]) * ($metadata["little"] ? 1 : 2)) + count($metadata["sisters"]); + $scoreRelations = $relations * 50; + + $score = $scoreHost + $scoreFictive + $scoreLittle + $scoreNotTalking + $scoreProtector + $scoreSharedMemory + $scoreRelations + $scoreSpecies + $scoreName + $scoreMedian; + + return [ + "host" => $scoreHost, + "fictive" => $scoreFictive, + "little" => $scoreLittle, + "median" => $scoreMedian, + "not_talking" => $scoreNotTalking, + "name" => $scoreName, + "protector" => $scoreProtector, + "shared_memory" => $scoreSharedMemory, + "relations" => $scoreRelations, + "species" => $scoreSpecies, + "total" => $score + ]; +} + +function scoreOrder($members, $system) { + $ordered = []; + foreach ($members as $member) { + if ($member["name"] !== "unknown") { + $member["_metadata"] = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system-$member[id]-metadata.json"), true); + $member["_score"] = calculateScore(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system-$member[id]-metadata.json"), true), $member["display_name"] ?? $member["name"]); + $ordered[] = $member; + } + } + + uasort($ordered, function($a, $b) { + return $b["_score"]["total"] - $a["_score"]["total"]; + }); + + return $ordered; +} + +function scoreOrderGlobal() { + $ordered = []; + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true) as $member) { + if ($member["name"] !== "unknown") { + $member["_system"] = "gdapd"; + $member["_metadata"] = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $member["_score"] = calculateScore(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true), $member["display_name"] ?? $member["name"]); + $ordered[] = $member; + } + } + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true) as $member) { + if ($member["name"] !== "unknown") { + $member["_system"] = "ynmuc"; + $member["_metadata"] = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $member["_score"] = calculateScore(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true), $member["display_name"] ?? $member["name"]); + $ordered[] = $member; + } + } + + uasort($ordered, function($a, $b) { + return $b["_score"]["total"] - $a["_score"]["total"]; + }); + + return $ordered; +}
\ No newline at end of file diff --git a/includes/session.php b/includes/session.php new file mode 100644 index 0000000..27acf6b --- /dev/null +++ b/includes/session.php @@ -0,0 +1,19 @@ +<?php + +global $isLoggedIn; +global $_PROFILE; + +if (isset($_COOKIE['PEH2_SESSION_TOKEN'])) { + if (str_contains($_COOKIE['PEH2_SESSION_TOKEN'], ".") || str_contains($_COOKIE['PEH2_SESSION_TOKEN'], "/")) { + $isLoggedIn = false; + } + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])))) { + $_PROFILE = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN']))), true); + $isLoggedIn = true; + } else { + $isLoggedIn = false; + } +} else { + $isLoggedIn = false; +}
\ No newline at end of file diff --git a/includes/subsysbanner.php b/includes/subsysbanner.php new file mode 100644 index 0000000..be06b06 --- /dev/null +++ b/includes/subsysbanner.php @@ -0,0 +1,68 @@ +<?php + +global $memberData; +global $memberCommonName; +global $memberID; +global $systemCommonName; +global $systemID; +global $system; +global $subsystemData; +global $subsystemID; +global $subsystem; + +$fronters = array_map(function ($item) { + return $item["id"]; +}, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["members"]); + +?> + +<div id="system-info" style="background:rgba(255, 255, 255, .1);border-radius:10px;display:grid;grid-template-columns: 128px 1fr;"> + <img src="/assets/uploads/ss-<?= $subsystemID ?>.png" alt="" style="height:128px;border-top-left-radius:10px;border-bottom-left-radius:10px;"> + <div style="padding:10px 10px 10px 20px;text-align:center;"> + <div style="display: grid; grid-template-columns: 1fr;height:100%;grid-template-rows: max-content max-content 1fr;"> + <h3 style="height:max-content;"><?= $subsystemData["name"] ?></h3> + <div style="height:max-content;display:grid;grid-template-columns: repeat(4, 1fr);" id="member-card"> + <span> + <b>Current Fronter:</b> + <?php if (in_array($fronters[0], getSubsystemByID($subsystemID)["members"])): $member = getMember($fronters[0]); ?> + <a class="member-link" href="/<?= $system ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> + <?php else: ?> + <span class="text-muted">N/A</span> + <?php endif; ?> + </span> + <span> + <?php + + $arr = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-switches.json"), true), function ($i) use ($subsystemID) { + return in_array($i["members"][0], getSubsystemByID($subsystemID)["members"]); + }); + sort($arr); + $previousID = $arr[in_array($fronters[0], getSubsystemByID($subsystemID)["members"]) ? 1 : 0]["members"][0]; + $member = null; + + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true) as $members) { + if ($members["id"] === $previousID) { + $member = $members; + break; + } + } + + ?> + <b>Previous Fronter: </b><a class="member-link" href="/<?= $system ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> + </span> + <span> + <b>Members:</b> <?= count(getSubsystemByID($subsystemID)["members"]) ?> + </span> + <span> + <b>Parent System:</b> <a class="member-link" href="/<?= $system ?>"><img style="width:24px;border-radius:5px;" src="/assets/uploads/<?= $system ?>.png"> <?= getMiniName($systemCommonName) ?></a> + </span> + </div> + </div> + </div> +</div> + +<div id="system-actions" style="margin-top:10px;padding:5px 10px;background:rgba(255, 255, 255, .1);border-radius:10px;"> + <div style="padding: 5px 10px;text-align: center;"> + You are viewing the page of a subsystem of the <b><a style="color:white;text-decoration: none;" href="/<?= $system ?>"><?= $systemCommonName ?></a></b>. + </div> +</div>
\ No newline at end of file diff --git a/includes/subsysedit.php b/includes/subsysedit.php new file mode 100644 index 0000000..a19683d --- /dev/null +++ b/includes/subsysedit.php @@ -0,0 +1,172 @@ +<?php global $system; global $systemCommonName; global $systemID; global $subsystem; global $subsystemData; global $subsystemCommonName; global $subsystemID; $title = "Editing " . $subsystemCommonName . " · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +?> + +<br> +<div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/subsysbanner.php"; ?> + <br> + + <p class="text-muted"> + <span id="editor-save-status">Saved</span> · <span id="editor-size"><?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.html") ? strlen(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.html")) : "0" ?></span> bytes + </p> + + <!--suppress HtmlFormInputWithoutLabel --> + <textarea id="page-editor"> + <?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.html") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.html") : "" ?> + </textarea> + + <script src="/assets/editor/editor.js"></script> + <script> + let editor; + ClassicEditor + .create( document.querySelector( '#page-editor' ), { + toolbar: [ + 'undo', 'redo', '|', 'removeFormat', '|', 'heading', '|', 'fontSize', 'fontColor', 'fontBackgroundColor', 'alignment', '|', 'bold', 'italic', 'underline', 'strikethrough', '|', 'subscript', 'superscript', '|', 'code', '|', 'outdent', 'indent', '|', 'bulletedList', 'numberedList', '|', 'link', 'imageUpload', 'mediaEmbed', 'blockQuote', 'insertTable', 'codeBlock', '|', 'horizontalLine' + ] + } ) + + .then( newEditor => { + editor = newEditor; + } ) + .catch( error => { + console.error( error ); + } ); + </script> + <style> + :root { + --ck-color-base-background: transparent; + } + + .ck-toolbar { + filter: invert(1); + } + + .ck-tooltip__text { + color: white !important; + } + + .ck-dropdown__panel { + background: #ddd !important; + } + + .ck-color-grid__tile { + filter: invert(1); + } + + .ck-balloon-rotator { + background-color: #ccc !important; + } + + .ck-balloon-panel { + filter: invert(1); + } + </style> + <script> + let lastSavedData = editor.getData(); + let lastFetchedData = editor.getData(); + let timeSinceLastModified = 0; + let saving = false; + + async function save() { + let data = editor.getData(); + document.getElementById("editor-save-status").innerHTML = "Saving..."; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.add("text-primary"); + saving = true; + + try { + await window.fetch("/api/save?system=<?= $systemID ?>&subsystem=<?= $subsystemID ?>", { + method: "POST", + body: JSON.stringify({ content: data }) + }); + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + lastSavedData = data; + saving = false; + } catch (e) { + console.error(e); + document.getElementById("editor-save-status").innerHTML = "Failed to save"; + document.getElementById("editor-save-status").classList.add("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + } + + document.onclick = async () => { + if (saving) return; + + if (editor.getData() !== lastSavedData) { + await save(); + } + } + + setInterval(async () => { + if (saving) return; + + document.getElementById("editor-size").innerHTML = editor.getData().length; + + if (editor.getData() !== lastSavedData) { + document.getElementById("editor-save-status").innerHTML = "Modified"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.add("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + + if (editor.getData() !== lastFetchedData) { + lastFetchedData = editor.getData(); + timeSinceLastModified = 0; + } else { + timeSinceLastModified++; + } + + if (timeSinceLastModified > 20) { + await save(); + } + } else { + timeSinceLastModified = 0; + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + }, 100) + </script> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/sysbanner.php b/includes/sysbanner.php new file mode 100644 index 0000000..d76e601 --- /dev/null +++ b/includes/sysbanner.php @@ -0,0 +1,80 @@ +<?php + +global $memberData; +global $memberCommonName; +global $memberID; +global $systemCommonName; +global $systemID; +global $system; + +?> + +<div id="system-info" style="background:rgba(255, 255, 255, .1);border-radius:10px;display:grid;grid-template-columns: 128px 1fr;"> + <img src="/assets/uploads/<?= $system ?>.png" alt="" style="height:128px;border-top-left-radius:10px;border-bottom-left-radius:10px;"> + <div style="padding:10px 10px 10px 20px;text-align:center;"> + <div style="display: grid; grid-template-columns: 1fr;height:100%;grid-template-rows: max-content max-content 1fr;"> + <h3 style="height:max-content;"><?= $systemCommonName ?></h3> + <div style="height:max-content;display:grid;grid-template-columns: repeat(5, 1fr);" id="member-card"> + <span> + <b>Host: </b><?php + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + foreach ($members as $member) { + $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-$member[id]-metadata.json"), true); + if ($data["host"]): ?> + <a class="member-link" href="/<?= $system ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> + <?php endif; + } + + ?> + + </span> + <span> + <?php $member = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["members"][0]; ?> + <b>Current Fronter: </b><a class="member-link" href="/<?= $system ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> + </span> + <span> + <?php + + $previousID = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-switches.json"), true)[1]["members"][0]; + $member = null; + + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true) as $members) { + if ($members["id"] === $previousID) { + $member = $members; + break; + } + } + + ?> + <b>Previous Fronter: </b><a class="member-link" href="/<?= $system ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> + </span> + <span> + <b>Members: </b><?= count(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true)) - 1 ?> + </span> + <span> + <b>Last Switch: </b><span data-bs-toggle="tooltip" title="<?= date("D j M Y, G:i:s (e)", strtotime(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["timestamp"])) ?>"><?= timeAgo(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-fronters.json"), true)["timestamp"]) ?></span> + </span> + </div> + </div> + </div> +</div> + +<div id="system-actions" style="margin-top:10px;padding:5px 10px;background:rgba(255, 255, 255, .1);border-radius:10px;display:grid;grid-template-columns: 1fr 1fr 1fr 1fr;"> + <a title="Front history" style="display:inline-block;padding:5px 10px;text-align: center" class="system-action" href="/<?= $system ?>/-/history"> + <img src="/assets/icons/history.svg" style="vertical-align: middle;height: 24px;width: 24px;filter: invert(1)" alt=""> + <span style="vertical-align: middle;" class="list-separator-desktop">Front history</span> + </a> + <a title="Compare members" style="display:inline-block;padding:5px 10px;text-align: center" class="system-action" href="/<?= $system ?>/-/compare"> + <img src="/assets/icons/compare.svg" style="vertical-align: middle;height: 24px;width: 24px;filter: invert(1)" alt=""> + <span style="vertical-align: middle;" class="list-separator-desktop">Compare members</span> + </a> + <a title="System tree" style="display:inline-block;padding:5px 10px;text-align: center" class="system-action" href="/<?= $system ?>/-/tree"> + <img src="/assets/icons/tree.svg" style="vertical-align: middle;height: 24px;width: 24px;filter: invert(1)" alt=""> + <span style="vertical-align: middle;" class="list-separator-desktop">System tree</span> + </a> + <a title="Members by species" style="display:inline-block;padding:5px 10px;text-align: center" class="system-action" href="/<?= $system ?>/-/species"> + <img src="/assets/icons/species.svg" style="vertical-align: middle;height: 24px;width: 24px;filter: invert(1)" alt=""> + <span style="vertical-align: middle;" class="list-separator-desktop">Members by species</span> + </a> +</div>
\ No newline at end of file diff --git a/includes/sysedit.php b/includes/sysedit.php new file mode 100644 index 0000000..0e3c9a7 --- /dev/null +++ b/includes/sysedit.php @@ -0,0 +1,172 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = "Editing " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +?> + +<br> +<div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/sysbanner.php"; ?> + <br> + + <p class="text-muted"> + <span id="editor-save-status">Saved</span> · <span id="editor-size"><?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-disclaimers.html") ? strlen(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-disclaimers.html")) : "0" ?></span> bytes + </p> + + <!--suppress HtmlFormInputWithoutLabel --> + <textarea id="page-editor"> + <?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-disclaimers.html") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-disclaimers.html") : "" ?> + </textarea> + + <script src="/assets/editor/editor.js"></script> + <script> + let editor; + ClassicEditor + .create( document.querySelector( '#page-editor' ), { + toolbar: [ + 'undo', 'redo', '|', 'removeFormat', '|', 'heading', '|', 'fontSize', 'fontColor', 'fontBackgroundColor', 'alignment', '|', 'bold', 'italic', 'underline', 'strikethrough', '|', 'subscript', 'superscript', '|', 'code', '|', 'outdent', 'indent', '|', 'bulletedList', 'numberedList', '|', 'link', 'imageUpload', 'mediaEmbed', 'blockQuote', 'insertTable', 'codeBlock', '|', 'horizontalLine' + ] + } ) + + .then( newEditor => { + editor = newEditor; + } ) + .catch( error => { + console.error( error ); + } ); + </script> + <style> + :root { + --ck-color-base-background: transparent; + } + + .ck-toolbar { + filter: invert(1); + } + + .ck-tooltip__text { + color: white !important; + } + + .ck-dropdown__panel { + background: #ddd !important; + } + + .ck-color-grid__tile { + filter: invert(1); + } + + .ck-balloon-rotator { + background-color: #ccc !important; + } + + .ck-balloon-panel { + filter: invert(1); + } + </style> + <script> + let lastSavedData = editor.getData(); + let lastFetchedData = editor.getData(); + let timeSinceLastModified = 0; + let saving = false; + + async function save() { + let data = editor.getData(); + document.getElementById("editor-save-status").innerHTML = "Saving..."; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.add("text-primary"); + saving = true; + + try { + await window.fetch("/api/save?system=<?= $systemID ?>&member=null", { + method: "POST", + body: JSON.stringify({ content: data }) + }); + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + lastSavedData = data; + saving = false; + } catch (e) { + console.error(e); + document.getElementById("editor-save-status").innerHTML = "Failed to save"; + document.getElementById("editor-save-status").classList.add("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + } + + document.onclick = async () => { + if (saving) return; + + if (editor.getData() !== lastSavedData) { + await save(); + } + } + + setInterval(async () => { + if (saving) return; + + document.getElementById("editor-size").innerHTML = editor.getData().length; + + if (editor.getData() !== lastSavedData) { + document.getElementById("editor-save-status").innerHTML = "Modified"; + document.getElementById("editor-save-status").classList.remove("text-danger"); + document.getElementById("editor-save-status").classList.remove("text-muted"); + document.getElementById("editor-save-status").classList.add("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + + if (editor.getData() !== lastFetchedData) { + lastFetchedData = editor.getData(); + timeSinceLastModified = 0; + } else { + timeSinceLastModified++; + } + + if (timeSinceLastModified > 20) { + await save(); + } + } else { + timeSinceLastModified = 0; + document.getElementById("editor-save-status").innerHTML = "Saved"; + document.getElementById("editor-save-status").classList.add("text-muted"); + document.getElementById("editor-save-status").classList.remove("text-warning"); + document.getElementById("editor-save-status").classList.remove("text-primary"); + } + }, 100) + </script> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system.php b/includes/system.php new file mode 100644 index 0000000..606d63d --- /dev/null +++ b/includes/system.php @@ -0,0 +1,61 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function getMember(string $id) { + global $systemID; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +?> + +<br> +<div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/sysbanner.php"; ?> + <br> + + <div id="page-content"> + <?php global $isLoggedIn; if ($isLoggedIn): ?> + <small style="opacity:.5;display:block;">(<a href="/edit/<?= $system ?>">edit</a>)</small> + <?php endif; ?> + <?= file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-disclaimers.html") ?> + </div> + <?php if ($system === "cloudburst") cloudburst(true); else raindrops(true); ?> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system/compare.php b/includes/system/compare.php new file mode 100644 index 0000000..96eb5d9 --- /dev/null +++ b/includes/system/compare.php @@ -0,0 +1,189 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = "Compare members · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +$members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + +function getMember(string $id) { + global $systemID; + global $members; + + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +?> + + <br> + <div class="container"> + <h2>Compare members of the <?= $systemCommonName ?></h2> + <div class="comparison"> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Member</span> + <span class="comparison-header-l1">Member</span> + <span class="comparison-header-l2">Member</span> + <span class="comparison-header-l3">Member</span> + <span class="comparison-header-l4">Member</span> + <span class="comparison-header-l5">Mmbr.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Species</span> + <span class="comparison-header-l1">Species</span> + <span class="comparison-header-l2">Species</span> + <span class="comparison-header-l3">Species</span> + <span class="comparison-header-l4">Spec.</span> + <span class="comparison-header-l5">Spec.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Relations</span> + <span class="comparison-header-l1">Relations</span> + <span class="comparison-header-l2">Relations</span> + <span class="comparison-header-l3">Relations</span> + <span class="comparison-header-l4">Relt.</span> + <span class="comparison-header-l5">Relt.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Host</span> + <span class="comparison-header-l1">Host</span> + <span class="comparison-header-l2">Host</span> + <span class="comparison-header-l3">Host</span> + <span class="comparison-header-l4">Hst.</span> + <span class="comparison-header-l5">Hst.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Fictive</span> + <span class="comparison-header-l1">Fictive</span> + <span class="comparison-header-l2">Fictive</span> + <span class="comparison-header-l3">Fictive</span> + <span class="comparison-header-l4">Fic.</span> + <span class="comparison-header-l5">Fic.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Little</span> + <span class="comparison-header-l1">Little</span> + <span class="comparison-header-l2">Little</span> + <span class="comparison-header-l3">Little</span> + <span class="comparison-header-l4">Ltl.</span> + <span class="comparison-header-l5">Ltl.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Not talking</span> + <span class="comparison-header-l1">No talk</span> + <span class="comparison-header-l2">No talk</span> + <span class="comparison-header-l3">NT.</span> + <span class="comparison-header-l4">NT.</span> + <span class="comparison-header-l5">NT.</span> + </span> + <span class="comparison-header comparison-item"> + <span class="comparison-header-l0">Protector</span> + <span class="comparison-header-l1">Protector</span> + <span class="comparison-header-l2">Protect.</span> + <span class="comparison-header-l3">Protect.</span> + <span class="comparison-header-l4">Prt.</span> + <span class="comparison-header-l5">Prt.</span> + </span> + + <?php foreach (scoreOrder($members, $systemID) as $member): + + $metadata = $member["_metadata"]; + + if ($member["name"] === "scootaloo") { + if ((int)date('j') % 2 === 0) { + $metadata["marefriends"] = array_reverse($metadata["marefriends"]); + } + } + + ?> + <a title="<?= $member["display_name"] ?? $member["name"] ?>" data-bs-toggle="tooltip" class="member-link comparison-item comparison-item-clickable" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <span style="vertical-align: middle;"><span class="comparison-name-full"><?= $member["display_name"] ?? $member["name"] ?></span><span class="comparison-name-small"><?= getMiniName($member["display_name"] ?? $member["name"]) ?></span></span> + <span class="comparison-colors" style="background-color: #<?= $member["color"] ?? "ffffff" ?>; height: 16px; width: 16px;display: inline-block;vertical-align: middle;border-radius:2px;"></span> + </a> + <span class="comparison-item"> + <?php foreach ($metadata["species"] ?? [] as $species): ?> + <img data-bs-toggle="tooltip" title="<?php switch ($species) { + case "earth": + echo "Earth pony"; + break; + + case "alicorn": + echo "Alicorn"; + break; + + case "pegasus": + echo "Pegasus"; + break; + + case "batpony": + echo "Bat pony"; + break; + + case "unicorn": + echo "Unicorn"; + break; + + default: + echo $species; + break; + } ?>" style="width:24px;vertical-align: middle;position:relative;top:-5px;" src="/assets/species/<?= $species ?>.png" alt="<?= $species ?>"> + <?php endforeach; ?> + </span> + <span class="comparison-item"> + <?= count($metadata["marefriends"]) + count($metadata["sisters"]) === 0 ? "-" : "" ?> + <span class="comparison-relations-count"> + <?= count($metadata["marefriends"]) + count($metadata["sisters"]) > 0 ? count($metadata["marefriends"]) + count($metadata["sisters"]) : "" ?> + </span> + <span class="comparison-relations-full"> + <?php $index = 0; foreach ($metadata["marefriends"] as $marefriend): $mfSystem = explode("/", $marefriend)[0]; $mfMemberID = explode("/", $marefriend)[1]; $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem-members.json"), true), function ($item) { + global $mfMemberID; + return $item["id"] === $mfMemberID; + }); sort($mfMember); $mfMember = $mfMember[0]; ?><a title="<b><?= $mfMember["display_name"] ?? $mfMember["name"] ?></b><br>Marefriend" data-bs-toggle="tooltip" data-bs-html="true" class="member-link" href="/<?= $mfSystem === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $mfMember["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $mfMember['name'] . ".png") ? "-" . $mfMember['name'] : "" ?>.png" style="width:24px;"></a><?php $index++; endforeach; ?><?php $index = 0; foreach ($metadata["sisters"] as $marefriend): $mfSystem = explode("/", $marefriend)[0]; $mfMemberID = explode("/", $marefriend)[1]; $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem-members.json"), true), function ($item) { + global $mfMemberID; + return $item["id"] === $mfMemberID; + }); sort($mfMember); $mfMember = $mfMember[0]; ?><a title="<b><?= $mfMember["display_name"] ?? $mfMember["name"] ?></b><br>Sister" data-bs-toggle="tooltip" data-bs-html="true" class="member-link" href="/<?= $mfSystem === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $mfMember["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $mfMember['name'] . ".png") ? "-" . $mfMember['name'] : "" ?>.png" style="width:24px;"></a><?php $index++; endforeach; ?> + </span> + </span> + <span class="comparison-item"> + <?php if ($metadata["host"]): ?> + <img data-bs-toggle="tooltip" title="Yes" src="/assets/icons/complete.svg" alt="Yes" style="width:24px;"> + <?php else: ?> + <img data-bs-toggle="tooltip" title="No" src="/assets/icons/none.svg" alt="No" style="width:24px;"> + <?php endif; ?> + </span> + <span class="comparison-item"> + <?php if ($metadata["fictive"]): ?> + <img data-bs-toggle="tooltip" title="Yes" src="/assets/icons/complete.svg" alt="Yes" style="width:24px;"> + <?php else: ?> + <img data-bs-toggle="tooltip" title="No" src="/assets/icons/none.svg" alt="No" style="width:24px;"> + <?php endif; ?> + </span> + <span class="comparison-item"> + <?php if ($metadata["little"] >= 2): ?> + <img data-bs-toggle="tooltip" title="Yes" src="/assets/icons/complete.svg" alt="Yes" style="width:24px;"> + <?php elseif ($metadata["little"] === 1): ?> + <img data-bs-toggle="tooltip" title="Age regressor" src="/assets/icons/partial.svg" alt="Partial" style="width:24px;"> + <?php else: ?> + <img data-bs-toggle="tooltip" title="No" src="/assets/icons/none.svg" alt="No" style="width:24px;"> + <?php endif; ?> + </span> + <span class="comparison-item"> + <?php if ($metadata["not_talking"]): ?> + <img data-bs-toggle="tooltip" title="Yes" src="/assets/icons/complete.svg" alt="Yes" style="width:24px;"> + <?php else: ?> + <img data-bs-toggle="tooltip" title="No" src="/assets/icons/none.svg" alt="No" style="width:24px;"> + <?php endif; ?> + </span> + <span class="comparison-item"> + <?php if ($metadata["protector"]): ?> + <img data-bs-toggle="tooltip" title="Yes" src="/assets/icons/complete.svg" alt="Yes" style="width:24px;"> + <?php else: ?> + <img data-bs-toggle="tooltip" title="No" src="/assets/icons/none.svg" alt="No" style="width:24px;"> + <?php endif; ?> + </span> + <?php endforeach; ?> + </div> + </div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system/history.php b/includes/system/history.php new file mode 100644 index 0000000..ad4e30e --- /dev/null +++ b/includes/system/history.php @@ -0,0 +1,380 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = "Front history · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +function getMember(string $id) { + global $systemID; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +?> + + <br> + <div class="container" id="page-content"> + <?php + + $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-switches.json"), true); + uksort($switches, function ($a, $b) { + return strtotime($b["timestamp"]) - strtotime($a["timestamp"]); + }); + + function getSwitchesForDay(int $day) { + global $switches; + + $filtered = array_values(array_filter($switches, function ($i) use ($day) { + $diff = strtotime(date("Y-m-d")) - strtotime(explode("T", $i["timestamp"])[0]); + return $diff <= (86400 * $day) && $diff > (86400 * ($day - 1)); + })); + + uksort($filtered, function ($a, $b) { + return strtotime($b["timestamp"]) - strtotime($a["timestamp"]); + }); + + return $filtered; + } + + function getSwitchBefore(string $id) { + global $switches; + + $currentPassed = false; + $before = null; + + foreach ($switches as $switch) { + if ($currentPassed) { + $before = $switch; + break; + } else { + if ($switch["id"] === $id) { + $currentPassed = true; + } + } + } + + return $before; + } + + function isNotToday(int $timestamp, int $offset) { + if (date('Y-m-d', $timestamp) !== date('Y-m-d', time() - (86400 * $offset))) { + return true; + } else { + return false; + } + } + + $switches1 = getSwitchesForDay(0); + $switches2 = getSwitchesForDay(1); + $switches3 = getSwitchesForDay(2); + $switches4 = getSwitchesForDay(3); + $switches5 = getSwitchesForDay(4); + $switches6 = getSwitchesForDay(5); + $switches7 = getSwitchesForDay(6); + $switches8 = getSwitchesForDay(7); + $switches9 = getSwitchesForDay(8); + $switches10 = getSwitchesForDay(9); + + ?> + <h2>Front history in the <?= $systemCommonName ?></h2> + <h4>Today</h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches1); + + $fronters[] = [ + "member" => $switches2[0] ? $switches2[0]["members"][0] : ($switches3[0] ? $switches3[0]["members"][0] : ($switches4[0] ? $switches4[0]["members"][0] : ($switches5[0] ? $switches5[0]["members"][0] : ($switches6[0] ? $switches6[0]["members"][0] : ($switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0]))))))), + "date" => strtotime($switches2[0] ? $switches2[0]["timestamp"] : ($switches3[0] ? $switches3[0]["timestamp"] : ($switches4[0] ? $switches4[0]["timestamp"] : ($switches5[0] ? $switches5[0]["timestamp"] : ($switches6[0] ? $switches6[0]["timestamp"] : ($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"])))))))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 0) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;">Yesterday</h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches2); + + $fronters[] = [ + "member" => $switches3[0] ? $switches3[0]["members"][0] : ($switches4[0] ? $switches4[0]["members"][0] : ($switches5[0] ? $switches5[0]["members"][0] : ($switches6[0] ? $switches6[0]["members"][0] : ($switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0])))))), + "date" => strtotime($switches3[0] ? $switches3[0]["timestamp"] : ($switches4[0] ? $switches4[0]["timestamp"] : ($switches5[0] ? $switches5[0]["timestamp"] : ($switches6[0] ? $switches6[0]["timestamp"] : ($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"]))))))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 1) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 2)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches3); + + $fronters[] = [ + "member" => $switches4[0] ? $switches4[0]["members"][0] : ($switches5[0] ? $switches5[0]["members"][0] : ($switches6[0] ? $switches6[0]["members"][0] : ($switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0]))))), + "date" => strtotime($switches4[0] ? $switches4[0]["timestamp"] : ($switches5[0] ? $switches5[0]["timestamp"] : ($switches6[0] ? $switches6[0]["timestamp"] : ($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"])))))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 2) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 3)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches4); + + $fronters[] = [ + "member" => $switches5[0] ? $switches5[0]["members"][0] : ($switches6[0] ? $switches6[0]["members"][0] : ($switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0])))), + "date" => strtotime($switches5[0] ? $switches5[0]["timestamp"] : ($switches6[0] ? $switches6[0]["timestamp"] : ($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"]))))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 3) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 4)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches5); + + $fronters[] = [ + "member" => $switches6[0] ? $switches6[0]["members"][0] : ($switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0]))), + "date" => strtotime($switches6[0] ? $switches6[0]["timestamp"] : ($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"])))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 4) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 5)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches6); + + $fronters[] = [ + "member" => $switches7[0] ? $switches7[0]["members"][0] : ($switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0])), + "date" => strtotime($switches7[0] ? $switches7[0]["timestamp"] : ($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"]))) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 5) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 6)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches7); + + $fronters[] = [ + "member" => $switches8[0] ? $switches8[0]["members"][0] : ($switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0]), + "date" => strtotime($switches8[0] ? $switches8[0]["timestamp"] : ($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"])) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 6) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 7)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches8); + + $fronters[] = [ + "member" => $switches9[0] ? $switches9[0]["members"][0] : $switches10[0]["members"][0], + "date" => strtotime($switches9[0] ? $switches9[0]["timestamp"] : $switches10[0]["timestamp"]) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 7) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 8)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches9); + + $fronters[] = [ + "member" => $switches10[0] ? $switches10[0]["members"][0] : getSwitchBefore($switches9[count($switches9) - 1]["id"])["members"][0], + "date" => strtotime($switches10[0] ? $switches10[0]["timestamp"] : getSwitchBefore($switches9[count($switches9) - 1]["id"])["timestamp"]) + ]; + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 8) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + <h4 style="margin-top:15px;"><?= date('D j M', time() - (86400 * 9)) ?></h4> + <?php + + $fronters = array_map(function ($i) { + return [ + "member" => $i["members"][0], + "date" => strtotime($i["timestamp"]) + ]; + }, $switches10); + + $fronters = array_unique($fronters, SORT_REGULAR); + + foreach ($fronters as $fronter): $member = getMember($fronter["member"]); + ?> + <div class="fronter"> + <span class="fronter-date" style="opacity:.5;font-family: monospace;font-size:14px;vertical-align: middle;"> + <?= isNotToday($fronter["date"], 9) ? "00:00" : date('H:i', $fronter["date"]) ?> + </span> + <span class="fronter-profile" style="vertical-align: middle;"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + </span> + </div> + <?php endforeach; ?> + </div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system/species.php b/includes/system/species.php new file mode 100644 index 0000000..a2251d0 --- /dev/null +++ b/includes/system/species.php @@ -0,0 +1,53 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = "Members by species · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +$members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); +$members = scoreOrder($members, $systemID); + +function species(array $members, string $id, string $name) { global $systemID; ?> + <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 4fr;"> + <div 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;text-decoration: none;"> + <img src="/assets/species/<?= $id ?>.png" style="width:24px;"><span class="species-name"> <?= $name ?></span> (<?= count($members) ?>) + </div> + + <div class="relation-item" style="margin-left:10px;padding:0 20px;"> + <?php if (count($members) > 0): ?> + <?php $index = 0; foreach ($members as $member): ?> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a><?php if ($index + 2 <= count($members)) echo('<span class="list-separator-desktop">, </span><span class="list-separator-mobile"><br></span>'); $index++; endforeach; ?> + <?php else: ?>-<?php endif; ?> + </div> + </div> +<?php } + +?> + + <br> + <div class="container" id="page-content"> + <h2><?= $systemCommonName ?> members by species</h2> + <?php + + $earth = []; + $pegasus = []; + $unicorn = []; + $alicorn = []; + $batpony = []; + + foreach ($members as $member) { + foreach ($member["_metadata"]["species"] as $species) { + if ($species === "earth") $earth[] = $member; + if ($species === "pegasus") $pegasus[] = $member; + if ($species === "unicorn") $unicorn[] = $member; + if ($species === "alicorn") $alicorn[] = $member; + if ($species === "batpony") $batpony[] = $member; + } + } + + ?> + + <?php species($earth, "earth", "Earth ponies"); ?> + <?php species($pegasus, "pegasus", "Pegasi"); ?> + <?php species($unicorn, "unicorn", "Unicorns"); ?> + <?php species($alicorn, "alicorn", "Alicorns"); ?> + <?php species($batpony, "batpony", "Bat ponies"); ?> + </div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system/subsystem.php b/includes/system/subsystem.php new file mode 100644 index 0000000..5d28cba --- /dev/null +++ b/includes/system/subsystem.php @@ -0,0 +1,124 @@ +<?php global $system; global $systemCommonName; global $parts; global $systemID; + +$members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); + +$subsystemID = $parts[3]; + +$subsystems = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystems.json"), true) ?? []; + +function getMember(string $id) { + global $systemID; + global $members; + + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function memberHasSubsystem(array $member) { + global $subsystems; + $has = false; + + foreach ($subsystems as $subsystem) { + if ($subsystem["source_type"] === "member" && $subsystem["source"] === $member["id"]) { + $has = true; + } + } + + return $has; +} + +function memberPartOfSubsystem(array $member) { + global $subsystems; + $is = false; + + foreach ($subsystems as $subsystem) { + if (in_array($member["id"], $subsystem["members"])) { + $is = true; + } + } + + return $is; +} + +function getMemberSubsystem(array $member) { + global $subsystems; + $subsystem = null; + + foreach ($subsystems as $ss) { + if ($ss["source_type"] === "member" && $ss["source"] === $member["id"]) { + $subsystem = $ss; + } + } + + return $subsystem; +} + +function getSubsystemByID(string $id) { + global $subsystems; + $subsystem = null; + + foreach ($subsystems as $ss) { + if ($ss["source"] === $id) { + $subsystem = $ss; + } + } + + return $subsystem; +} + +function timeAgo($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$difference} {$period} {$tense} "; +} + +if (getSubsystemByID($subsystemID) === null) header("Location: /?error=Invalid subsystem ID") and die(); +$subsystemData = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.json"), true); + +$title = $subsystemData["name"] . " · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +?> + + <br> + <div class="container"> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/subsysbanner.php"; ?> + <br> + + <div id="page-content"> + <?php global $isLoggedIn; if ($isLoggedIn): ?> + <small style="opacity:.5;display:block;">(<a href="/edit/<?= $system ?>/<?= $subsystemID ?>">edit</a>)</small> + <?php endif; ?> + <?= file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystemID.html") ?> + </div> + <?php showSubsystem(getSubsystemByID($subsystemID), $systemID); ?> + </div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file diff --git a/includes/system/tree.php b/includes/system/tree.php new file mode 100644 index 0000000..04cbf83 --- /dev/null +++ b/includes/system/tree.php @@ -0,0 +1,114 @@ +<?php global $system; global $systemCommonName; global $systemID; $title = "System tree · " . $systemCommonName; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +$members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-members.json"), true); +$members = scoreOrder($members, $systemID); + +$subsystems = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystems.json"), true) ?? []; + +function getMember(string $id) { + global $systemID; + global $members; + + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function memberHasSubsystem(array $member) { + global $subsystems; + $has = false; + + foreach ($subsystems as $subsystem) { + if ($subsystem["source_type"] === "member" && $subsystem["source"] === $member["id"]) { + $has = true; + } + } + + return $has; +} + +function memberPartOfSubsystem(array $member) { + global $subsystems; + $is = false; + + foreach ($subsystems as $subsystem) { + if (in_array($member["id"], $subsystem["members"])) { + $is = true; + } + } + + return $is; +} + +function getMemberSubsystem(array $member) { + global $subsystems; + $subsystem = null; + + foreach ($subsystems as $ss) { + if ($ss["source_type"] === "member" && $ss["source"] === $member["id"]) { + $subsystem = $ss; + } + } + + return $subsystem; +} + +?> + + <br> + <div class="container" id="page-content"> + <h2>System tree for the <?= $systemCommonName ?></h2> + <a class="tree-root member-link" href="/<?= $system ?>"> + <img src="/assets/uploads/<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/" . $system . ".png") ? $system : "" ?>.png" style="width:24px;"> <span style="vertical-align: middle;"><?= $systemCommonName ?></span></a> + <?php $first = true; ?> + <?php foreach ($subsystems as $subsystem): if ($subsystem["source_type"] === "trait"): ?> + <div class="tree-l0"> + <div class="tree-l0-separator<?= $first ? " tree-first-separator" : "" ?>"> </div> + <div class="tree-inner"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/-/subsystem/<?= $subsystem["source"] ?>"> + <img src="/assets/uploads/ss-<?= $subsystem["source"] ?>.png" style="width:24px;border-radius:5px;"> <span style="vertical-align: middle;"><?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystem[source].json") ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID-subsystem-$subsystem[source].json"), true)["name"] : $subsystem["source"] ?></span> + </a> + </div> + <?php $ssfirst = true; foreach ($subsystem["members"] as $ssm): $ssmember = getMember($ssm); ?> + <div class="tree-l1"> + <div class="tree-l0-separator"> </div> + <div class="tree-l1-separator<?= $ssfirst ? " tree-first-separator" : "" ?>"> </div> + <div class="tree-inner"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $ssmember["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $ssmember['name'] . ".png") ? "-" . $ssmember['name'] : "" ?>.png" style="width:24px;"> <span style="vertical-align: middle;"><?= $ssmember["display_name"] ?? $ssmember["name"] ?></span> + </a> + </div> + </div> + <?php $ssfirst = false; endforeach; ?> + </div> + <?php $first = false; endif; endforeach; ?> + <?php foreach ($members as $member): if (!memberPartOfSubsystem($member)): ?> + <div class="tree-l0"> + <div class="tree-l0-separator<?= $first ? " tree-first-separator" : "" ?>"> </div> + <div class="tree-inner"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <span style="vertical-align: middle;"><?= $member["display_name"] ?? $member["name"] ?></span> + </a> + </div> + <?php if (memberHasSubsystem($member)): ?> + <?php $ssfirst = true; foreach (getMemberSubsystem($member)["members"] as $ssm): $ssmember = getMember($ssm); ?> + <div class="tree-l1"> + <div class="tree-l0-separator"> </div> + <div class="tree-l1-separator<?= $ssfirst ? " tree-first-separator" : "" ?>"> </div> + <div class="tree-inner"> + <a class="member-link" href="/<?= $systemID === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $ssmember["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $ssmember['name'] . ".png") ? "-" . $ssmember['name'] : "" ?>.png" style="width:24px;"> <span style="vertical-align: middle;"><?= $ssmember["display_name"] ?? $ssmember["name"] ?></span> + </a> + </div> + </div> + <?php $ssfirst = false;endforeach; ?> + <?php endif; ?> + </div> + <?php $first = false; endif; endforeach; ?> + </div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>
\ No newline at end of file |