Updated 18 files and added 10 files (automated)
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 613 B |
@ -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
|
||||
]));
|
@ -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'; ?>
|