diff options
Diffstat (limited to 'pages')
-rw-r--r-- | pages/api/ponytown.php | 117 | ||||
-rw-r--r-- | pages/docs.inc | 2 | ||||
-rw-r--r-- | pages/home.old.inc | 12 | ||||
-rw-r--r-- | pages/money.inc | 55 | ||||
-rw-r--r-- | pages/ponytown.inc | 258 | ||||
-rw-r--r-- | pages/rules.inc | 2 | ||||
-rw-r--r-- | pages/stats.inc | 3 |
7 files changed, 430 insertions, 19 deletions
diff --git a/pages/api/ponytown.php b/pages/api/ponytown.php new file mode 100644 index 0000000..f41ac01 --- /dev/null +++ b/pages/api/ponytown.php @@ -0,0 +1,117 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.inc"; global $isLoggedIn; global $_PROFILE; global $isLowerLoggedIn; global $app; +if (!$isLoggedIn && !$isLowerLoggedIn) header("Location: /-/login") and die(); + +$request_raw = file_get_contents('php://input'); +$json_object = json_decode($request_raw, true); + +$select = $_GET['id'] ?? null; + +if (!isset($select)) { + peh_error("System member not found", 404); + return; +} + +if (getMemberWithoutSystem($select) === null) { + peh_error("System member not found", 404); + return; +} + +$member = getMemberWithoutSystem($select); + +if ($isLowerLoggedIn && $member["_system"] !== $app["other"]["id"]) { + peh_error("System member not found", 404); + return; +} + +if (!isset($json_object[0]) || !isset($json_object[1])) { + die("Missing data"); +} + +$errors = []; + +foreach ([1, 2] as $_) { + $input = $json_object[$_ - 1]; + + $mime = explode(";", substr($input, 5))[0]; + $file = base64_decode(explode(",", explode(";", substr($input, 5))[1])[1]); + + $image = @imagecreatefromstring($file); + $size = @getimagesizefromstring($file); + + if ($image === false) { + $errors[] = "0x{$_}000000F: Failed to open image #" . $_ . ", it is probably not using a supported format"; + } + + if ($size === false) { + $errors[] = "0x{$_}000000E: Failed to get metadata for image #" . $_ . ", it is probably corrupted"; + } + + if ($image === false || $size === false) continue; + + $foundColor = false; + + for ($i = 0; $i < $size[0]; $i++) { + if (imagecolorat($image, $i, 0) !== 2130706432) { + $foundColor = true; + } + } + + if (!$foundColor) { + $errors[] = "0x{$_}000001A: Image #" . $_ . " seems to contain padding (based on the first row of pixels)"; + } + + $foundColor = false; + + for ($i = 0; $i < $size[1]; $i++) { + if (imagecolorat($image, 0, $i) !== 2130706432) { + $foundColor = true; + } + } + + if (!$foundColor) { + $errors[] = "0x{$_}000001B: Image #" . $_ . " seems to contain padding (based on the first column of pixels)"; + } + + if ($_ === 1 && $size[0] > 70) { + $errors[] = "0x{$_}000002A: Image #" . $_ . " is wider than it should, are you sure you set zoom to 1x? Maybe you inverted the files?"; + } + + if ($_ === 1 && $size[1] > 70) { + $errors[] = "0x{$_}000002B: Image #" . $_ . " is higher than it should, are you sure you set zoom to 1x? Maybe you inverted the files?"; + } + + if ($_ === 2 && $size[0] > 35) { + $errors[] = "0x{$_}000002A: Image #" . $_ . " is wider than it should, are you sure you set zoom to 1x? Maybe you inverted the files?"; + } + + if ($_ === 2 && $size[1] > 35) { + $errors[] = "0x{$_}000002B: Image #" . $_ . " is higher than it should, are you sure you set zoom to 1x? Maybe you inverted the files?"; + } +} + +if (count($errors) === 0 && isset($_GET["real"])) { + foreach ([1, 2] as $_) { + $input = $json_object[$_ - 1]; + + $mime = explode(";", substr($input, 5))[0]; + $file = base64_decode(explode(",", explode(";", substr($input, 5))[1])[1]); + + $image = @imagecreatefromstring($file); + + imagealphablending($image, false); + imagesavealpha($image, true); + + if ($_ === 1) { + imagepng($image, $_SERVER['DOCUMENT_ROOT'] . "/assets/ponies/" . $member["id"] . ".png"); + } else { + imagepng($image, $_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member["name"] . ".png"); + } + } +} + +die(json_encode([ + "success" => count($errors) === 0, + "errors" => $errors +]));
\ No newline at end of file diff --git a/pages/docs.inc b/pages/docs.inc index 3b212df..2e43727 100644 --- a/pages/docs.inc +++ b/pages/docs.inc @@ -345,7 +345,7 @@ function showDocument($item) { ?> time = new Date(time).getTime(); } - let periods = ["sec.", "mn.", "hr.", "d.", "wk.", "mo.", "y.", "ages"]; + let periods = ["sec", "min", "hr", "d", "wk", "mo", "y", "ages"]; let lengths = ["60", "60", "24", "7", "4.35", "12", "100"]; diff --git a/pages/home.old.inc b/pages/home.old.inc index 4249947..f918549 100644 --- a/pages/home.old.inc +++ b/pages/home.old.inc @@ -47,18 +47,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; require_once $_SE </div> <hr style="border-color:rgba(255, 255, 255, .25);"> - <!--<?php global $isLoggedIn; if ($isLoggedIn && ((int)date('H') >= 20 || (int)date('H') < 6)): ?> - <a href="/-/emergency" style="text-decoration: none;margin-top:15px;display:block;"> - <div class="alert alert-danger"> - <b>Are you in need of help?</b> If you need immediate help from a loved one, you may want to enable the emergency alert by clicking here, even if that wakes up the <?= $_PROFILE['name'] === "Raindrops System" ? "Cloudburst System" : "Raindrops System" ?>. Use it as you need. - </div> - </a> - <?php endif; ?>--> - - <!--<div class="alert alert-info"> - <b>Coming soon:</b> Free hosting for Project Scout (the software powering Cold Haze), so you can have your own website for your system. If you are interested and/or want to pre-register, feel free to <a href="https://equestria.horse/contact" target="_blank">contact us</a>. - </div>--> - <div id="homepage-desktop" style="margin-top:10px;"> <?php cloudburst(false); ?> diff --git a/pages/money.inc b/pages/money.inc index b297231..257b87f 100644 --- a/pages/money.inc +++ b/pages/money.inc @@ -1,5 +1,8 @@ <?php +$minimumRaindrops = 200; +$minimumCloudburst = 500; + require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages; global $_PROFILE; $parts = explode("/", $_GET["_"]); @@ -70,7 +73,7 @@ if ((isset($_GET["create"]) || isset($_GET["delete"])) && isset($parts[2])) { "Title: " . (getMember($myId)["display_name"] ?? getMember($myId)["name"]) . " created a transaction to " . $account["name"] . " (" . ucfirst($account["owner"]) . ")\r\n" . "Tags: bits\r\n" . "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]), - 'content' => $account["currency"] === "gbp" ? "£" : "€" . abs((float)$_GET["amount"]) . " were " . ((float)$_GET["amount"] >= 0 ? "added" : "removed") . " just now" . (trim($_GET["description"]) !== "" ? ": " . $_GET["description"] : "") + 'content' => ($account["currency"] === "gbp" ? "£" : "€") . abs((float)$_GET["amount"]) . " were " . ((float)$_GET["amount"] >= 0 ? "added" : "removed") . " just now" . (trim($_GET["description"]) !== "" ? ": " . $_GET["description"] : "") ] ])); @@ -96,7 +99,7 @@ if ((isset($_GET["create"]) || isset($_GET["delete"])) && isset($parts[2])) { "Title: " . (getMember($myId)["display_name"] ?? getMember($myId)["name"]) . " deleted a transaction from " . $account["name"] . " (" . ucfirst($account["owner"]) . ")\r\n" . "Tags: bits\r\n" . "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]), - 'content' => $account["currency"] === "gbp" ? "£" : "€" . abs((float)$account["transactions"][(int)$_GET["id"]]["amount"]) . " " . ((float)$account["transactions"][(int)$_GET["id"]]["amount"] >= 0 ? "advance" : "withdrawal") . " created by " . (getMemberWithoutSystem($account["transactions"][(int)$_GET["id"]]["author"])["display_name"] ?? getMemberWithoutSystem($account["transactions"][(int)$_GET["id"]]["author"])["name"]) . " " . timeAgo($account["transactions"][(int)$_GET["id"]]["date"]) + 'content' => ($account["currency"] === "gbp" ? "£" : "€") . abs((float)$account["transactions"][(int)$_GET["id"]]["amount"]) . " " . ((float)$account["transactions"][(int)$_GET["id"]]["amount"] >= 0 ? "advance" : "withdrawal") . " created by " . (getMemberWithoutSystem($account["transactions"][(int)$_GET["id"]]["author"])["display_name"] ?? getMemberWithoutSystem($account["transactions"][(int)$_GET["id"]]["author"])["name"]) . " " . timeAgo($account["transactions"][(int)$_GET["id"]]["date"]) ] ])); @@ -297,6 +300,30 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </a> <?php endif; endforeach; ?> </div> + + <div class="progress" style="margin-top: 20px;"> + <?php + + $part1 = 0; + $part2 = 1; + $difference = 0; + + if ($allAccounts > $minimumCloudburst) { + $part2 = ($allAccounts - $minimumCloudburst) / $allAccounts; + $part1 = 1 - $part2; + $difference = $allAccounts - $minimumCloudburst; + } else { + $part1 = 1; + $part2 = 0; + $difference = $minimumCloudburst - $allAccounts; + } + + ?> + <div class="progress-bar bg-danger" style="width:<?= $part1 * 100 ?>%"></div> + <div class="progress-bar bg-success" style="width:<?= $part2 * 100 ?>%"></div> + </div> + <p style="text-align: center; margin-top: 5px;" class="<?= $allAccounts > $minimumCloudburst ? "" : "bold text-danger" ?>">£<?= number_format($difference, 2, '.', ',') ?><?php if ($allAccounts > $minimumCloudburst): ?> (<?= round($part2 * 100, 2) ?>%)<?php endif; ?> <?= $allAccounts > $minimumCloudburst ? "over" : "under" ?> the minimum</p> + <canvas id="history-cloudburst" style="margin-top: 10px; width: 100%; height: 200px; max-height: 100%;"></canvas> <?php @@ -391,6 +418,30 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </a> <?php endif; endforeach; ?> </div> + + <div class="progress" style="margin-top: 20px;"> + <?php + + $part1 = 0; + $part2 = 1; + $difference = 0; + + if ($allAccounts > $minimumRaindrops) { + $part2 = ($allAccounts - $minimumRaindrops) / $allAccounts; + $part1 = 1 - $part2; + $difference = $allAccounts - $minimumRaindrops; + } else { + $part1 = 1; + $part2 = 0; + $difference = $minimumRaindrops - $allAccounts; + } + + ?> + <div class="progress-bar bg-danger" style="width:<?= $part1 * 100 ?>%"></div> + <div class="progress-bar bg-success" style="width:<?= $part2 * 100 ?>%"></div> + </div> + <p style="text-align: center; margin-top: 5px;" class="<?= $allAccounts > $minimumRaindrops ? "" : "bold text-danger" ?>">€<?= number_format($difference, 2, '.', ',') ?><?php if ($allAccounts > $minimumCloudburst): ?> (<?= round($part2 * 100, 2) ?>%)<?php endif; ?> <?= $allAccounts > $minimumRaindrops ? "over" : "under" ?> the minimum</p> + <canvas id="history-raindrops" style="margin-top: 10px; width: 100%; height: 200px; max-height: 100%;"></canvas> <?php diff --git a/pages/ponytown.inc b/pages/ponytown.inc new file mode 100644 index 0000000..d055c9c --- /dev/null +++ b/pages/ponytown.inc @@ -0,0 +1,258 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages; global $isLowerLoggedIn; global $app; + +$parts = explode("/", $_GET["_"]); + +if (!isset($parts[2])) { + peh_error("System member not found", 404); + return; +} + +if (getMemberWithoutSystem($parts[2]) === null) { + peh_error("System member not found", 404); + return; +} + +$member = getMemberWithoutSystem($parts[2]); + +if ($isLowerLoggedIn && $member["_system"] !== $app["other"]["id"]) { + peh_error("System member not found", 404); + return; +} + +$title = ($member["display_name"] ?? $member["name"]) . " · " . $title; + +require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; + +$member = getMemberWithoutSystem($parts[2]); + +?> + +<br> +<div class="container"> + <div id="page-content"> + <h2>Pony Town uploader for <?= $member["display_name"] ?? $member["name"] ?></h2> + + <?php if (isset($_GET["updated"])): ?> + <div class="alert alert-success">Successfully uploaded a new Pony Town character for this member, it may take a few minutes to update across the website.</div> + <?php endif; ?> + + <div style="display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 20px; margin-top: 15px;"> + <div class="card"> + <div class="card-body"> + <h3>Current version</h3> + + <div style="display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 10px;"> + <div style="display: flex; align-items: center; justify-content: center;"> + <img src="data:image/png;base64,<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/ponies/" . $member["id"] . ".png") ? base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/assets/ponies/" . $member["id"] . ".png")) : "" ?>" style="width: 100%; border: 1px dashed rgba(255, 255, 255, .5); border-radius: 10px;"> + </div> + <div style="display: flex; align-items: center; justify-content: center;"> + <img src="data:image/png;base64,<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member["name"] . ".png") ? base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member["name"] . ".png")) : "" ?>" style="width: 100%; border: 1px dashed rgba(255, 255, 255, .5); border-radius: 10px;"> + </div> + </div> + + <?php + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/ponies/" . $member["id"] . ".png")) { + $info = getimagesize($_SERVER['DOCUMENT_ROOT'] . "/assets/ponies/" . $member["id"] . ".png"); + } else { + $info = [null, 61]; + } + + if ($info[1] > 60): ?> + <p class="text-warning" style="margin-top: 15px; margin-bottom: 0; text-align: center;"><b>This Pony Town character needs to be updated.</b><br>Cold Haze now relies on pixel-perfect versions of one's Pony Town character to work properly, and the current character for this member is not compliant or non-existant.</p> + <?php endif; ?> + </div> + </div> + + <div class="card"> + <div class="card-body"> + <h3>Upload a new version</h3> + + <div id="uploader"> + <ol> + <li>Open Pony Town [<a href="https://pony.town/" target="_blank">main</a>, <a href="https://event.pony.town/" target="_blank">event</a>, <a href="https://eventblue.pony.town/" target="_blank">eventblue</a>, <a href="https://eventgreen.pony.town/" target="_blank">eventgreen</a>, <a href="https://breezy.pony.town/" target="_blank">breezy</a>], login if needed, and select the right pony</li> + <li>Click on <img alt="edit" src="/assets/editor/ponytown/step1.png" style="height: 2rem;"></li> + <li>Focus on <img alt="Export" src="/assets/editor/ponytown/step2.png" style="height: 2rem;"> and click on <img alt="Image export zoom" src="/assets/editor/ponytown/step2b.png" style="height: 2rem;"></li> + <li>In the menu, select <img alt="1x" src="/assets/editor/ponytown/step3.png" style="height: 2rem;"></li> + <li>Click on <img alt="Image export settings" src="/assets/editor/ponytown/step4.png" style="height: 2rem;"> and uncheck everything, like this:<br><img alt="[all settings unchecked]" src="/assets/editor/ponytown/step4b.png" style="width: 25%;"></li> + <li>Click on <img alt="PNG" src="/assets/editor/ponytown/step5.png" style="height: 2rem;"> and download the file to your device</li> + <li>Upload this file here (yes this is a button):<br><input type="file" id="file-uploader"></li> + <li>Go back to Pony Town, click on <img alt="Image export settings" src="/assets/editor/ponytown/step4.png" style="height: 2rem;"> again</li> + <li>Check "Head only", and nothing else, like this:<br><img alt="[Head only checked]" src="/assets/editor/ponytown/step9.png" style="width: 25%;"></li> + <li>Click on <img alt="PNG" src="/assets/editor/ponytown/step5.png" style="height: 2rem;"> once again and download the file to your device</li> + <li>Upload this file (the second one) here (yes this is also a button):<br><input type="file" id="file-uploader2"></li> + <li>We will check if you used the correct settings and save it if everything is good.</li> + </ol> + </div> + + <div id="checker" style="display: none;"> + <img src="/assets/editor/load.svg" style="width: 36px;"><span style="vertical-align: middle;" id="checker-message">Reading files...</span> + </div> + + <div id="error" style="display: none;"> + <p>Hmm, I don't think this is working! Check the errors below and try again:</p> + <ul id="errors"></ul> + <span class="btn btn-primary" onclick="upload();">Back</span> + </div> + + <div id="confirm" style="display: none;"> + <div style="display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 10px;"> + <div style="display: flex; align-items: center; justify-content: center;"> + <img id="confirm-preview-1" style="width: 100%; border: 1px dashed rgba(255, 255, 255, .5); border-radius: 10px;"> + </div> + <div style="display: flex; align-items: center; justify-content: center;"> + <img id="confirm-preview-2" style="width: 100%; border: 1px dashed rgba(255, 255, 255, .5); border-radius: 10px;"> + </div> + </div> + + <div style="margin-top: 10px;"> + <span class="btn btn-primary" onclick="uploadActual();">Upload</span> + <span class="btn btn-outline-primary" onclick='document.getElementById("file-uploader").value = "";document.getElementById("file-uploader2").value = "";upload();'>Cancel</span> + </div> + </div> + </div> + </div> + + <script> + function errors(list) { + document.getElementById("file-uploader").value = ""; + document.getElementById("file-uploader2").value = ""; + + document.getElementById("uploader").style.display = "none"; + document.getElementById("error").style.display = ""; + document.getElementById("checker").style.display = "none"; + document.getElementById("confirm").style.display = "none"; + + document.getElementById("errors").innerHTML = `<li>${list.map(i => i.replaceAll("<", "<").replaceAll(">", ">")).join("</li><li>")}</li>`; + } + + let files = [ + null, + null + ]; + + function uploadCandidate() { + if (files[0] && files[1]) { + document.getElementById("checker-message").innerText = "Uploading files for review..."; + + let data = JSON.stringify(files); + + if (data.length > 1024 ** 2) { + errors(["0xFF000001: The data being sent to the server is too large, your images are huge!"]); + return; + } + + window.fetch("/api/ponytown/?id=<?= $member["id"] ?>", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: data + }).then((res) => { + res.json().then((data) => { + document.getElementById("checker-message").innerText = "Processing response..."; + console.log(data); + + if (data.success) { + document.getElementById("confirm-preview-1").src = files[0]; + document.getElementById("confirm-preview-2").src = files[1]; + + document.getElementById("uploader").style.display = "none"; + document.getElementById("checker").style.display = "none"; + document.getElementById("confirm").style.display = ""; + document.getElementById("error").style.display = "none"; + } else { + errors(data.errors); + } + }) + }) + } + } + + function uploadActual() { + if (files[0] && files[1]) { + document.getElementById("checker-message").innerText = "Uploading final files..."; + + let data = JSON.stringify(files); + + if (data.length > 1024 ** 2) { + errors(["0xFF000001: The data being sent to the server is too large, your images are huge!"]); + return; + } + + window.fetch("/api/ponytown/?id=<?= $member["id"] ?>&real", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: data + }).then((res) => { + res.json().then((data) => { + document.getElementById("checker-message").innerText = "Processing response..."; + console.log(data); + + if (data.success) { + document.getElementById("checker-message").innerText = "Reloading..."; + location.href = "/-/ponytown/<?= $member["id"] ?>/?updated"; + } else { + errors(data.errors); + } + }) + }) + } + } + + document.getElementById("file-uploader").onchange = document.getElementById("file-uploader2").onchange = upload = () => { + files = [ + null, + null + ]; + + if (document.getElementById("file-uploader").files.length >= 1 && document.getElementById("file-uploader2").files.length >= 1) { + document.getElementById("errors").innerHTML = ""; + + document.getElementById("uploader").style.display = "none"; + document.getElementById("checker").style.display = ""; + document.getElementById("confirm").style.display = "none"; + document.getElementById("error").style.display = "none"; + + let file1 = document.getElementById("file-uploader").files[0]; + let file2 = document.getElementById("file-uploader2").files[0]; + + let reader1 = new FileReader(); + let reader2 = new FileReader(); + + reader1.readAsDataURL(file1); + reader2.readAsDataURL(file2); + + reader1.onerror = () => { + errors(["Unable to read file #1, an internal error occurred"]); + } + reader2.onerror = () => { + errors(["Unable to read file #2, an internal error occurred"]); + } + + reader1.onload = () => { + files[0] = reader1.result; + uploadCandidate(); + } + + reader2.onload = () => { + files[1] = reader2.result; + uploadCandidate(); + } + } else { + document.getElementById("uploader").style.display = ""; + document.getElementById("checker").style.display = "none"; + document.getElementById("confirm").style.display = "none"; + document.getElementById("error").style.display = "none"; + } + } + </script> + </div> + </div> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.inc'; ?>
\ No newline at end of file diff --git a/pages/rules.inc b/pages/rules.inc index 7257cf1..ec007df 100644 --- a/pages/rules.inc +++ b/pages/rules.inc @@ -117,7 +117,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </style> <div class="modal fade" id="editor"> - <div class="modal-dialog"> + <div class="modal-dialog modal-xl"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title">Rules editor</h4> diff --git a/pages/stats.inc b/pages/stats.inc index 510c317..1b0fe31 100644 --- a/pages/stats.inc +++ b/pages/stats.inc @@ -196,9 +196,6 @@ $switchesCloudburst = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . $frontersMonthMembers[$key][$month] = $frontersMonthRectified[$month][$key] ?? [ "duration" => 0 ]; - /*$frontersMonthMembers[$key][$month] = array_map(function ($i) { - return $i["duration"]; - }, $frontersMonthMembers[$key][$month]);*/ } } |