summaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/api/chrome.php2
-rw-r--r--pages/api/desktop.php3
-rw-r--r--pages/api/me.php28
-rw-r--r--pages/api/menu.php168
-rw-r--r--pages/api/pleasure-real.php35
-rw-r--r--pages/api/pleasure.php35
-rw-r--r--pages/computers.inc23
-rw-r--r--pages/login.inc21
-rw-r--r--pages/pleasure.inc15
-rw-r--r--pages/schedules.inc213
-rw-r--r--pages/toys.inc720
11 files changed, 1233 insertions, 30 deletions
diff --git a/pages/api/chrome.php b/pages/api/chrome.php
index 3b49ec7..c50599a 100644
--- a/pages/api/chrome.php
+++ b/pages/api/chrome.php
@@ -4,7 +4,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $i
if (isset($_GET["e"])) {
- header("Access-Control-Allow-Origin: chrome-extension://klenhenmabddpffgdaijdkjmfkkafbno");
+ header("Access-Control-Allow-Origin: chrome-extension://foioekefbipjjonpgpbjahmplcealhhg");
} else {
header("Access-Control-Allow-Origin: *");
}
diff --git a/pages/api/desktop.php b/pages/api/desktop.php
new file mode 100644
index 0000000..5d79440
--- /dev/null
+++ b/pages/api/desktop.php
@@ -0,0 +1,3 @@
+<?php
+
+die(); \ No newline at end of file
diff --git a/pages/api/me.php b/pages/api/me.php
index 215e1cf..4f86549 100644
--- a/pages/api/me.php
+++ b/pages/api/me.php
@@ -3,19 +3,37 @@
$app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true);
require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn;
-if (!$isLoggedIn && !$isLowerLoggedIn) header("Location: /-/login") and die();
-global $_PROFILE;
header("Content-Type: application/json");
+if (!$isLoggedIn && !$isLowerLoggedIn) die('{"valid": false}');
+global $_PROFILE;
+
if ($_PROFILE['login'] === "raindrops") {
- die('{"name": "Raindrops System", "id": "raindrops", "pluralkit": "gdapd"}');
+ die(json_encode([
+ "valid" => true,
+ "name" => "Raindrops System",
+ "id" => "raindrops",
+ "pluralkit" => "gdapd",
+ "avatar" => getAsset("gdapd"),
+ "email" => $_PROFILE["profile"]["email"]["email"]
+ ]));
} else if ($_PROFILE["login"] === "cloudburst") {
- die('{"name": "Cloudburst System", "id": "cloudburst", "pluralkit": "ynmuc"}');
+ die(json_encode([
+ "valid" => true,
+ "name" => "Cloudburst System",
+ "id" => "cloudburst",
+ "pluralkit" => "ynmuc",
+ "avatar" => getAsset("ynmuc"),
+ "email" => $_PROFILE["profile"]["email"]["email"]
+ ]));
} else {
die(json_encode([
+ "valid" => true,
"name" => $app["other"]["name"],
"id" => $app["other"]["slug"],
- "pluralkit" => $app["other"]["id"]
+ "pluralkit" => $app["other"]["id"],
+ "avatar" => getAsset($app["other"]["id"]),
+ "email" => $_PROFILE["profile"]["email"]["email"]
]));
} \ No newline at end of file
diff --git a/pages/api/menu.php b/pages/api/menu.php
new file mode 100644
index 0000000..10214d8
--- /dev/null
+++ b/pages/api/menu.php
@@ -0,0 +1,168 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $_PROFILE; global $isLowerLoggedIn; global $app;
+if (!$isLoggedIn && !$isLowerLoggedIn) header("Location: /-/login") and die();
+
+$available = json_decode(base64_decode($_GET["available"]), true);
+$isSexual = true;
+
+die(json_encode([
+ [
+ "title" => "Cold Haze",
+ "items" => [
+ [
+ "title" => "Front planner",
+ "link" => "coldhaze://-/fronting",
+ "show" => $available["coldHaze"],
+ "icon" => "icons://planner"
+ ],
+ [
+ "title" => "Profile scores",
+ "link" => "coldhaze://-/profiles",
+ "show" => $available["coldHaze"],
+ "icon" => "icons://scores"
+ ],
+ [
+ "title" => "Money tracker",
+ "link" => "coldhaze://-/money",
+ "show" => $available["coldHaze"] && $isLoggedIn,
+ "icon" => "icons://money"
+ ],
+ [
+ "title" => "Rules",
+ "link" => "coldhaze://-/rules",
+ "show" => $available["coldHaze"] && $isLoggedIn,
+ "icon" => "icons://rules"
+ ],
+ [
+ "title" => "Toys database",
+ "link" => "coldhaze://-/toys",
+ "show" => $available["coldHaze"] && $isLoggedIn && $isSexual,
+ "icon" => "icons://toys"
+ ],
+ [
+ "title" => "Documents",
+ "link" => "coldhaze://-/docs",
+ "show" => $available["coldHaze"] && $isLoggedIn,
+ "icon" => "icons://documents"
+ ],
+ [
+ "title" => "Devices",
+ "link" => "coldhaze://-/computers",
+ "show" => $available["coldHaze"] && $isLoggedIn,
+ "icon" => "icons://computers"
+ ],
+ [
+ "title" => "System travelling",
+ "link" => "coldhaze://-/travelling",
+ "show" => $available["coldHaze"],
+ "icon" => "icons://travelling"
+ ]
+ ]
+ ],
+ [
+ "title" => "Utilities",
+ "items" => [
+ [
+ "title" => "Backup viewer",
+ "link" => "local://backup",
+ "show" => true,
+ "icon" => "icons://backup"
+ ],
+ [
+ "title" => "SSH client",
+ "link" => "local://ssh",
+ "show" => true,
+ "icon" => "icons://ssh"
+ ]
+ ]
+ ],
+ [
+ "title" => "Booru",
+ "items" => [
+ [
+ "title" => "Home",
+ "link" => "booru://home/",
+ "show" => $available["booru"],
+ "icon" => "icons://home"
+ ],
+ [
+ "title" => "Search",
+ "link" => "booru://search/",
+ "show" => $available["booru"],
+ "icon" => "icons://search"
+ ],
+ [
+ "title" => "Followed tags",
+ "link" => "booru://followed/",
+ "show" => $available["booru"],
+ "icon" => "icons://followed"
+ ],
+ [
+ "title" => "Saved images",
+ "link" => "booru://saved/",
+ "show" => $available["booru"],
+ "icon" => "icons://saved"
+ ],
+ [
+ "title" => "Explicit gallery",
+ "link" => "booru://nsfw/g/",
+ "show" => $available["booru"] && $isSexual,
+ "icon" => "icons://nsfw"
+ ],
+ [
+ "title" => "Genitalia",
+ "link" => "booru://nsfw/sp2/",
+ "show" => $available["booru"] && $isSexual,
+ "icon" => "https://booru.equestria.dev/nsfw/icon/"
+ ]
+ ]
+ ],
+ [
+ "title" => "Other apps",
+ "items" => [
+ [
+ "title" => "Ponycon.info",
+ "link" => "ponycon://",
+ "show" => $available["ponycon"],
+ "icon" => "icons://ponycon"
+ ],
+ [
+ "title" => "Delta",
+ "link" => "delta://",
+ "show" => $available["delta"],
+ "icon" => "icons://delta"
+ ],
+ [
+ "title" => "Gitea",
+ "link" => "gitea://equestria.dev",
+ "show" => $available["gitea"],
+ "icon" => "icons://gitea"
+ ],
+ [
+ "title" => "JetBrains Hub",
+ "link" => "hub://hub/projects",
+ "show" => true,
+ "icon" => "icons://hub"
+ ],
+ [
+ "title" => "YouTrack",
+ "link" => "youtrack://",
+ "show" => $available["youtrack"],
+ "icon" => "icons://youtrack"
+ ],
+ [
+ "title" => "Plex",
+ "link" => "https://plex.equestria.dev",
+ "show" => true,
+ "icon" => "icons://plex"
+ ],
+ [
+ "title" => "Proxmox",
+ "link" => "https://admin.equestria.dev",
+ "show" => true,
+ "icon" => "icons://proxmox"
+ ]
+ ]
+ ]
+])); \ No newline at end of file
diff --git a/pages/api/pleasure-real.php b/pages/api/pleasure-real.php
new file mode 100644
index 0000000..2d631ff
--- /dev/null
+++ b/pages/api/pleasure-real.php
@@ -0,0 +1,35 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn;
+if (!$isLoggedIn && !$isLowerLoggedIn) header("Location: /-/login") and die();
+global $_PROFILE;
+
+if ($_PROFILE["login"] === "cloudburst") return;
+
+$frontRaindrops = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/fronters.json"), true)["members"];
+$frontOther = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/fronters.json"), true)["members"];
+
+if ($_PROFILE["login"] === "raindrops" && isset($frontRaindrops[0])) {
+ $pony = $frontRaindrops[0]["display_name"] ?? $frontRaindrops[0]["name"];
+} else if ($_PROFILE["login"] !== "raindrops" && isset($frontOther[0])) {
+ $pony = $frontOther[0]["display_name"] ?? $frontOther[0]["name"];
+} else {
+ $pony = "somepony";
+}
+
+$ntfy = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true)["ntfy"];
+
+file_get_contents('https://' . $ntfy["server"] . '/pleasure', false, stream_context_create([
+ 'http' => [
+ 'method' => 'POST',
+ 'header' =>
+ "Content-Type: text/plain\r\n" .
+ "Title: ๐Ÿฉ $pony wants to play for a bit\r\n" .
+ "Priority: high\r\n" .
+ "Tags: pleasure\r\n" .
+ "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]),
+ 'content' => "Hey, $pony wants to play and have fun for a bit, get up!"
+ ]
+]));
+
+die(); \ No newline at end of file
diff --git a/pages/api/pleasure.php b/pages/api/pleasure.php
new file mode 100644
index 0000000..b300a70
--- /dev/null
+++ b/pages/api/pleasure.php
@@ -0,0 +1,35 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn;
+if (!$isLoggedIn && !$isLowerLoggedIn) header("Location: /-/login") and die();
+global $_PROFILE;
+
+if ($_PROFILE["login"] === "cloudburst") return;
+
+$frontRaindrops = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/fronters.json"), true)["members"];
+$frontOther = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/fronters.json"), true)["members"];
+
+if ($_PROFILE["login"] === "raindrops" && isset($frontRaindrops[0])) {
+ $pony = $frontRaindrops[0]["display_name"] ?? $frontRaindrops[0]["name"];
+} else if ($_PROFILE["login"] !== "raindrops" && isset($frontOther[0])) {
+ $pony = $frontOther[0]["display_name"] ?? $frontOther[0]["name"];
+} else {
+ $pony = "somepony";
+}
+
+$ntfy = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true)["ntfy"];
+
+file_get_contents('https://' . $ntfy["server"] . '/pleasure', false, stream_context_create([
+ 'http' => [
+ 'method' => 'POST',
+ 'header' =>
+ "Content-Type: text/plain\r\n" .
+ "Title: [Test] ๐Ÿฉ $pony wants to play for a bit\r\n" .
+ "Priority: high\r\n" .
+ "Tags: pleasure\r\n" .
+ "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]),
+ 'content' => "[This is a test] Hey, $pony wants to play and have fun for a bit, get up!"
+ ]
+]));
+
+die(); \ No newline at end of file
diff --git a/pages/computers.inc b/pages/computers.inc
index d2b9cbd..67cb6ef 100644
--- a/pages/computers.inc
+++ b/pages/computers.inc
@@ -68,9 +68,9 @@ if (str_ends_with($computer["os"], "unknown")) {
<?php if ($computer["luna_version"] === "1.0.0"): ?>
<div class="alert alert-danger">
- <p><b>We are unable to give you access to this computer.</b></p>
- <p>This computer is running xsion 1.0.0. This version of Luna is considered malware because of inconspicuous behavior and is therefore not usable anymore and has been disabled. This means you cannot access this computer at the moment.</p>
- Update this computer to run Luna 1.1.0 or newer and try again.
+ <p><b>We are unable to give you access to this device.</b></p>
+ <p>This computer is running Luna version 1.0.0. This version of Luna is considered malware because of inconspicuous behavior and is therefore not usable anymore and has been disabled. This means you cannot access this computer at the moment.</p>
+ Update this device to run Luna 1.1.0 or newer and try again.
</div>
<?php else: ?>
<h4 style="margin-top: 20px;margin-bottom: 10px;">Overview</h4>
@@ -266,7 +266,7 @@ if (str_ends_with($computer["os"], "unknown")) {
<td style="padding-right: 10px; text-align: right;"><b>Remaining capacity:</b></td>
<td><?= $computer["battery"]["currentCapacity"] ?>/<?= $computer["battery"]["maxCapacity"] ?> <?= $computer["battery"]["capacityUnit"] ?> (designed for <?= $computer["battery"]["designedCapacity"] ?> <?= $computer["battery"]["capacityUnit"] ?><?php if ($computer["battery"]["designedCapacity"] > 0): ?>, <?= round(($computer["battery"]["maxCapacity"] / $computer["battery"]["designedCapacity"]) * 100, 2) ?>% left<?php endif; ?>)</td>
</tr>
- <?php else: ?><tr><td colspan="2">This computer does not contain a battery.</td></tr><?php endif; ?><?php else: ?><tr><td colspan="2">This operating system does not support reporting battery info.</td></tr><?php endif; ?>
+ <?php else: ?><tr><td colspan="2">This device does not contain a battery.</td></tr><?php endif; ?><?php else: ?><tr><td colspan="2">This operating system does not support reporting battery info.</td></tr><?php endif; ?>
<tr>
<td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Operating system</h4></td>
@@ -304,6 +304,9 @@ if (str_ends_with($computer["os"], "unknown")) {
</tr>
<?php endif; ?>
+ <?php if (count(array_filter($computer["versions"], function ($version, $software) {
+ return trim($version) !== "" && $software !== "systemOpensslLib" && $software !== "openssl" && $software !== "node" && $software !== "v8";
+ }, ARRAY_FILTER_USE_BOTH)) > 0): ?>
<tr>
<td colspan="2"><h4 style="margin-top: 20px;margin-bottom: 10px;">Software versions</h4></td>
</tr>
@@ -345,15 +348,15 @@ if (str_ends_with($computer["os"], "unknown")) {
?>:</b></td>
<td><?= $version ?></td>
</tr>
- <?php endif; endforeach; ?>
+ <?php endif; endforeach; endif; ?>
</tbody>
</table>
<?php if (!isset($computer["remote_control"]) || $computer["remote_control"] === true): ?>
<h4 style="margin-top: 20px;margin-bottom: 10px;" id="screens">Remote control</h4>
<p id="page-content">
- You can remotely control this computer. <a href="./<?= $id ?>/control">Open remote control.</a><br>
- <small class="text-muted">Note: The user of this computer will need to approve your connection request before you can see and control their screen.</small>
+ You can remotely control this device. <a href="./<?= $id ?>/control">Open remote device.</a><br>
+ <small class="text-muted">Note: The user of this device will need to approve your connection request before you can see and control their screen.</small>
</p>
<?php endif; ?>
@@ -864,8 +867,8 @@ if (str_ends_with($computer["os"], "unknown")) {
<?php endif; ?>
<?php else: ?>
<div>
- <h2>Computers</h2>
- <p>Click on a computer to view information about it (open windows, installed apps, ...).</p>
+ <h2>Devices</h2>
+ <p>Click on a device to view information about it</p>
<ul class="list-group">
<?php foreach (array_filter(scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/data/computers/metadata"), function ($i) {
@@ -900,7 +903,7 @@ if (str_ends_with($computer["os"], "unknown")) {
<div style="display: flex; align-items: center;">
<div>
<b><?= $computer["host"] ?></b> ยท <?= $computer["os"] ?><br>
- Owned by <?= $owner === "raindrops" ? "Raindrops System" : "Cloudburst System"; ?><br>
+ Computer ยท Owned by <?= $owner === "raindrops" ? "Raindrops System" : "Cloudburst System"; ?><br>
<?php if (timeAgo($computer["date"]) === "now"): ?>Online<?php else: ?>Offline since <?= timeAgo($computer["date"]) ?><?php endif; ?>
</div>
</div>
diff --git a/pages/login.inc b/pages/login.inc
index 313be91..1522ab3 100644
--- a/pages/login.inc
+++ b/pages/login.inc
@@ -1,25 +1,18 @@
<?php
-require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages;
-require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; global $readOnly; global $isNormallyLoggedIn;
-
if (isset($_GET["return"])) {
setcookie("PEH2_RETURN_PAGE", $_GET["return"], 0, "/");
}
+if (isset($_GET["immediate"])) {
+ header("Location: /auth/init");
+ die();
+}
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages;
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; global $readOnly; global $isNormallyLoggedIn;
-if (isset($_GET["mini"])) {
?>
-<style>
- #navigation-pane, #title-bar, #footer, #titlebar-separator, #mobile-navigation, #global-search-container {
- display: none !important;
- }
-
- body {
- margin-left: 0 !important;
- }
-</style>
-<?php } ?>
<br>
<div class="container">
diff --git a/pages/pleasure.inc b/pages/pleasure.inc
new file mode 100644
index 0000000..ec468d5
--- /dev/null
+++ b/pages/pleasure.inc
@@ -0,0 +1,15 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages;
+$emergencyHeader = true; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc';
+
+?>
+
+<br>
+<div class="container">
+ <div id="page-content">
+ <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/components/pleasure.inc"; ?>
+ </div>
+</div>
+
+<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/footer.inc'; ?>
diff --git a/pages/schedules.inc b/pages/schedules.inc
new file mode 100644
index 0000000..bfdaf6e
--- /dev/null
+++ b/pages/schedules.inc
@@ -0,0 +1,213 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages; global $app;
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; global $readOnly; global $isNormallyLoggedIn;
+
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/composer/vendor/autoload.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/composer/ical/ical.php";
+
+?>
+
+<style>
+ .day-gradient {
+ background-image: linear-gradient(180deg, rgba(7,15,36,1) 0%, rgba(36,56,83,1) 14%, rgba(165,126,57,1) 28%, rgba(37,109,201,1) 42%, rgba(47,165,208,1) 57%, rgba(252,120,15,1) 71%, rgba(38,66,97,1) 85%, rgba(7,15,36,1) 100%);
+ background-size: 100% 1000%;
+ }
+
+ .invert {
+ filter: invert(1) hue-rotate(180deg);
+ }
+
+ @media (max-width: 800px) {
+ #schedules {
+ grid-template-columns: 1fr !important;
+ }
+
+ .schedules-separator {
+ display: block !important;
+ }
+ }
+</style>
+
+<br>
+<div class="container">
+ <h2>Schedules</h2>
+
+ <div id="schedules" style="margin-top: 20px; display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 20px;">
+ <div>
+ <div id="live-time-other-outer" class="day-gradient" style="text-align: center; background-color: rgba(255, 255, 255, .1); padding: 20px 0; border-radius: 10px; margin-bottom: 20px;">
+ <b><?= $app["other"]["name"] ?></b><br>
+ <h3 id="live-time-other">--:--</h3>
+ </div>
+
+ <?php
+
+ $weekDay = (int)date('N');
+
+ if ($weekDay < 6 && !in_array(date('Y-m-d'), $app["other"]["work"]["off"])) {
+ $start = ($app["other"]["work"]["start"][0] * 3600) + ($app["other"]["work"]["start"][1] * 60);
+ $end = ($app["other"]["work"]["end"][0] * 3600) + ($app["other"]["work"]["end"][1] * 60);
+ $parts = explode("|", (new DateTime("now", new DateTimeZone("America/Chicago")))->format("H|i"));
+ $now = ((int)$parts[0] * 3600) + ((int)$parts[1] * 60);
+
+ if ($start < $now && $now < $end) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐Ÿ—„๏ธ</span> &nbsp;Currently at work</b></p>Started ' . timeAgo($now - $start, false, true, true) . '<br>Finishing ' . timeIn($end - $now, true) . '</div>');
+ } elseif ($now > $end) {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;Finally off work!</b></div>');
+ } elseif ($now < $start) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐Ÿ—„๏ธ</span> &nbsp;There\'s work today</b></p>Starting ' . timeIn($start - $now, true) . '<br>Working for ' . timeAgo($end - $start, false, true, true, false) . '</div>');
+ }
+ } else {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;No work today!</b></div>');
+ }
+
+ ?>
+
+ <ul>
+ <li><b>Anchorage:</b> <span id="live-time-other1">--:--</span></li>
+ <li><b>Vancouver:</b> <span id="live-time-other2">--:--</span></li>
+ </ul>
+
+ <hr style="display: none;" class="schedules-separator">
+ </div>
+
+ <div>
+ <div id="live-time-cloudburst-outer" class="day-gradient" style="text-align: center; background-color: rgba(255, 255, 255, .1); padding: 20px 0; border-radius: 10px; margin-bottom: 20px;">
+ <b>Cloudburst System</b><br>
+ <h3 id="live-time-cloudburst">--:--</h3>
+ </div>
+
+ <?php
+
+ $calendar = new \om\IcalParser();
+ $calendar->parseFile($_SERVER['DOCUMENT_ROOT'] . "/includes/data/calendar.ics");
+
+ $college = array_values(array_filter((array)$calendar->getEvents()->sorted(), function ($i) {
+ return strtotime(((array)$i["DTEND"])["date"]) > time() && date('Y-m-d', strtotime(((array)$i["DTEND"])["date"])) === date('Y-m-d') && trim(strtolower($i["SUMMARY"])) === "(cloudburst) college";
+ }));
+
+ if (count($college) > 0) {
+ $start = strtotime(((array)$college[0]["DTSTART"])["date"]);
+ $end = strtotime(((array)$college[0]["DTEND"])["date"]);
+ $now = time();
+
+ if ($start < $now && $now < $end) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐Ÿซ๏ธ</span> &nbsp;Currently in college</b></p>Started ' . timeAgo($now - $start, false, true, true) . '<br>Finishing ' . timeIn($end - $now, true) . '</div>');
+ } elseif ($now > $end) {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;Finally out of college!</b></div>');
+ } elseif ($now < $start) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐Ÿซ๏ธ</span> &nbsp;Getting ready for college</b></p>Starting ' . timeIn($start - $now, true) . '<br>In college for ' . timeAgo($end - $start, false, true, true, false) . '</div>');
+ }
+ } else {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;No college today!</b></div>');
+ }
+
+ ?>
+
+ <hr style="display: none;" class="schedules-separator">
+ </div>
+
+ <div>
+ <div id="live-time-raindrops-outer" class="day-gradient" style="text-align: center; background-color: rgba(255, 255, 255, .1); padding: 20px 0; border-radius: 10px; margin-bottom: 20px;">
+ <b>Raindrops System</b><br>
+ <h3 id="live-time-raindrops">--:--</h3>
+ </div>
+
+ <?php
+
+ $school = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/school.json"), true);
+
+ if (isset($school[date('Y-m-d')])) {
+ $today = $school[date('Y-m-d')];
+
+ if (count($college) > 0) {
+ $start = $today["firstClass"]["timestamp"] / 1000;
+ $end = strtotime($today["end"]);
+ $now = time();
+
+ if ($start < $now && $now < $end) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐ŸŽ“๏ธ</span> &nbsp;Currently in school</b></p>Started ' . timeAgo($now - $start, false, true, true) . '<br>Finishing ' . timeIn($end - $now, true) . '</div>');
+ } elseif ($now > $end) {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;Finally out of school!</b></div>');
+ } elseif ($now < $start) {
+ echo('<div class="alert alert-warning"><p><b><span class="invert">๐ŸŽ“๏ธ</span> &nbsp;Getting ready for school</b></p>Starting ' . timeIn($start - $now, true) . '<br>In school for ' . timeAgo($end - $start, false, true, true, false) . '</div>');
+ }
+ } else {
+ echo('<div class="alert alert-success"><b><span class="invert">๐ŸŽ‰</span> &nbsp;No school today!</b></div>');
+ }
+ }
+
+ ?>
+
+ <hr style="display: none;" class="schedules-separator">
+ </div>
+ </div>
+</div>
+
+<script>
+ function getDayPercentage(time) {
+ let hours = parseInt(time.split(":")[0]);
+ let minutes = parseInt(time.split(":")[1].split(" ")[0]);
+
+ if (time.split(":")[1].split(" ")[1] === "PM") hours += 12;
+ if (hours === 12 && time.split(":")[1].split(" ")[1] === "AM") hours = 0;
+ if (hours === 24 && time.split(":")[1].split(" ")[1] === "PM") hours = 12;
+
+ let timestamp = new Date("1970-01-01 " + hours + ":" + minutes + " UTC").getTime() / 1000;
+
+ return (timestamp / 86400) * 100;
+ }
+
+ function updateTime() {
+ let time1 = (new Intl.DateTimeFormat('en-US', {
+ timeZone: 'Europe/Paris',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: true
+ })).format(new Date());
+
+ document.getElementById("live-time-raindrops").innerText = time1;
+ document.getElementById("live-time-raindrops-outer").style.backgroundPositionY = getDayPercentage(time1) + "%";
+
+ let time2 = (new Intl.DateTimeFormat('en-US', {
+ timeZone: 'Europe/London',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: true
+ })).format(new Date());
+
+ document.getElementById("live-time-cloudburst").innerText = time2;
+ document.getElementById("live-time-cloudburst-outer").style.backgroundPositionY = getDayPercentage(time2) + "%";
+
+ let time3 = (new Intl.DateTimeFormat('en-US', {
+ timeZone: 'America/Chicago',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: true
+ })).format(new Date());
+
+ document.getElementById("live-time-other").innerText = time3;
+ document.getElementById("live-time-other-outer").style.backgroundPositionY = getDayPercentage(time3) + "%";
+
+ document.getElementById("live-time-other1").innerText = (new Intl.DateTimeFormat('en-US', {
+ timeZone: 'America/Anchorage',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: true
+ })).format(new Date());
+ document.getElementById("live-time-other2").innerText = (new Intl.DateTimeFormat('en-US', {
+ timeZone: 'America/Vancouver',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: true
+ })).format(new Date());
+ }
+
+ updateTime();
+
+ setInterval(() => {
+ updateTime();
+ }, 10000);
+</script>
+
+<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/footer.inc'; ?>
diff --git a/pages/toys.inc b/pages/toys.inc
new file mode 100644
index 0000000..afcd5ef
--- /dev/null
+++ b/pages/toys.inc
@@ -0,0 +1,720 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title;
+
+if (isset($_POST['deleteAction'])) {
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json"), true);
+
+ $selected = null;
+ $selectedIndex = -1;
+ $id = $_POST['action'];
+
+ foreach ($data as $index => $item) {
+ if ($item["id"] === $id) {
+ $selectedIndex = $index;
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ unset($data[$selectedIndex]);
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json", utf8_encode(json_encode($data)));
+ header("Location: /-/toys/?d&id=" . $id);
+ die();
+}
+
+if (isset($_POST['updateAction'])) {
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json"), true);
+
+ $selected = null;
+ $selectedIndex = -1;
+ $id = $_POST['action'];
+
+ foreach ($data as $index => $item) {
+ if ($item["id"] === $id) {
+ $selectedIndex = $index;
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ if (isset($_POST["sexual"])) {
+ $selected["sexual"] = true;
+ } else {
+ $selected["sexual"] = false;
+ }
+
+ if (isset($_POST["name"])) $selected["name"] = strip_tags(trim($_POST["name"]));
+ if (isset($_POST["usage"])) $selected["usage"] = strip_tags(trim($_POST["usage"]));
+ if (isset($_POST["irl"])) $selected["irl"] = strip_tags(trim($_POST["irl"]));
+ if (isset($_POST["image"])) $selected["image"] = strip_tags(trim($_POST["image"]));
+ if (isset($_POST["usage_img"])) $selected["usage_img"] = strip_tags(trim($_POST["usage_img"]));
+ if (isset($_POST["keywords"])) $selected["keywords"] = array_map(function ($i) {
+ return trim($i);
+ }, explode(",", strip_tags(trim($_POST["keywords"]))));
+ if (isset($_POST["description"])) $selected["description"] = strip_tags(trim($_POST["description"]));
+ if (isset($_POST["water"])) $selected["water"] = match ($_POST["water"]) {
+ "0" => "out",
+ "1" => "in",
+ "2" => "both",
+ "3" => "playground"
+ };
+
+ if (isset($_POST["relations"])) {
+ $ponies = [];
+
+ foreach ($_POST["relations"] as $relation => $d) {
+ $ponies[] = [
+ "members" => explode("-", $relation),
+ "deprecated" => isset($d["deprecated"]),
+ "sexual" => isset($d["sexual"])
+ ];
+ }
+
+ $selected["ponies"] = $ponies;
+ }
+
+ global $_PROFILE;
+ if ($_PROFILE['login'] === "raindrops" && isset($_POST["verified"])) {
+ $selected["verified"] = true;
+ } else {
+ unset($selected["verified"]);
+ }
+
+ if (isset($_POST["untested"])) {
+ $selected["untested"] = true;
+ } else {
+ unset($selected["untested"]);
+ }
+
+ $data[$selectedIndex] = $selected;
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json", utf8_encode(json_encode($data)));
+
+ header("Location: /-/toys/" . $id);
+ die();
+}
+
+if (isset($_POST['createAction'])) {
+ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/random.inc";
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json"), true);
+
+ if (!isset($_POST["name"]) || !isset($_POST["water"])) {
+ header("Location: /-/toys");
+ die();
+ }
+ if (trim($_POST["name"]) === "" || !is_numeric($_POST["water"])) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ $water = match ($_POST["water"]) {
+ "0" => "out",
+ "1" => "in",
+ "2" => "both",
+ "3" => "playground"
+ };
+ $name = strip_tags(trim($_POST["name"]));
+ $id = random();
+
+ $ponies = [];
+
+ if (isset($_POST["relations"])) {
+ foreach ($_POST["relations"] as $relation => $_) {
+ $ponies[] = [
+ "members" => explode("-", $relation),
+ "deprecated" => false
+ ];
+ }
+ }
+
+ if (isset($_POST["sexual"])) {
+ $sexual = true;
+ } else {
+ $sexual = false;
+ }
+
+ $data[] = [
+ "id" => $id,
+ "name" => $name,
+ "water" => $water,
+ "description" => null,
+ "ponies" => $ponies,
+ "usage" => null,
+ "irl" => null,
+ "image" => "",
+ "usage_img" => "",
+ "keywords" => [],
+ "sexual" => $sexual
+ ];
+
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json", utf8_encode(json_encode($data)));
+ header("Location: /-/toys/" . $id);
+ die();
+}
+
+global $pagename;
+$parts = explode("/", $pagename);
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json"), true);
+
+$selected = null;
+
+if (isset($parts[1])) {
+ $id = $parts[1];
+
+ foreach ($data as $item) {
+ if ($item["id"] === $id) {
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys/?nf&id=" . $id);
+ die();
+ } else {
+ $title = $selected["name"] . " ยท " . $title;
+ }
+}
+
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/util/keywords.inc';
+
+if (!file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json")) file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json", "[]");
+
+global $_PROFILE;
+global $pagename;
+$parts = explode("/", $pagename);
+
+?>
+
+<style>
+ @media (max-width: 800px) {
+ #toy-grid-img {
+ display: block !important;
+ }
+ }
+
+ @media (max-width: 900px) {
+ #toy-grid-img2 {
+ display: block !important;
+ }
+ }
+</style>
+
+ <script src="/assets/editor/fuse.js"></script>
+
+ <br>
+ <div class="container">
+ <div id="<?= isset($parts[1]) ? "page-content" : "" ?>">
+ <?php if (isset($_GET['nf'])): ?>
+ <div class="alert alert-danger alert-dismissible">
+ <button onclick='window.history.pushState({"html":null,"pageTitle":document.title},"", "/-/toys/");' type="button" class="btn-close" data-bs-dismiss="alert" style="filter: none !important;"></button>
+ <b>Error: </b> The requested toy (<code><?= strip_tags($_GET['id'] ?? "-") ?></code>) was not found, it may have been deleted or has never existed.
+ </div>
+ <?php endif; ?>
+
+ <?php if (isset($_GET['d'])): ?>
+ <div class="alert alert-success alert-dismissible">
+ <button onclick='window.history.pushState({"html":null,"pageTitle":document.title},"", "/-/toys");' type="button" class="btn-close" data-bs-dismiss="alert" style="filter: none !important;"></button>
+ <b>Success: </b> The toy with ID <code><?= strip_tags($_GET['id'] ?? "-") ?></code> has been successfully deleted.
+ </div>
+ <?php endif; ?>
+
+ <?php if (isset($parts[1])): ?>
+
+ <h2>
+ <span style="vertical-align: middle;"><?= $selected["name"] ?></span>
+ <a href="/-/toys" class="small btn btn-outline-light" style="float:right;margin-top:5px;vertical-align:middle;opacity:1 !important; color:white;">Back</a>
+ </h2>
+
+ <p>
+ <a onclick="event.target.blur();" data-bs-toggle="modal" data-bs-target="#editor" href="#">Edit</a> ยท
+ <?php if (!isset($selected["verified"])): ?><span class="badge bg-warning rounded-pill text-black">Unverified</span><?php endif; ?>
+ <?php if ($selected["water"] === "in"): ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <?php elseif ($selected["water"] === "out"): ?>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php endif; ?>
+ </p>
+
+ <div id="toy-grid-img" style="display: grid; grid-template-columns: 1fr <?php if (isset($selected["usage_img"]) && trim($selected["usage_img"]) !== ""): ?>2fr<?php endif; ?> !important; grid-gap: 20px;">
+ <?php if (isset($selected["usage_img"]) && trim($selected["usage_img"]) !== ""): ?>
+ <div>
+ <img src="<?= $selected["usage_img"] ?>" style="width: 100%; max-height: 100vh;">
+ </div>
+ <?php endif; ?>
+ <div>
+
+ <?php if (isset($selected["description"]) && trim($selected["description"]) !== ""): ?>
+ <?= replaceKeyWords(str_replace("\n", "<br>", strip_tags($selected["description"]))); ?>
+ <?php else: ?>
+ <p><i>No description provided for this toy. Enter edit mode to add a description to this toy.</i></p>
+ <?php endif; ?>
+
+ <hr>
+
+ <div style="margin-top:10px;"></div>
+
+ <div id="toy-grid-img2" style="display: grid; grid-template-columns: <?php if (isset($selected["usage_img"]) && $selected["usage_img"] !== ""): ?>3fr <?php if (isset($selected["image"]) && trim($selected["image"]) !== ""): ?>1fr<?php endif; ?><?php else: ?>6fr <?php if (isset($selected["image"]) && trim($selected["image"]) !== ""): ?>1fr<?php endif; ?><?php endif; ?> !important; grid-gap: 20px;"><div>
+ <b>Usage:</b><br>
+ <?php if (isset($selected["usage"]) && trim($selected["usage"]) !== ""): ?>
+ <?php
+
+ $lines = explode("\n", strip_tags($selected["usage"]));
+
+ if (count($lines) > 1) echo("<ul>");
+
+ foreach ($lines as $line) {
+ if (count($lines) > 1) echo("<li>");
+
+ $parts = explode(":", $line);
+
+ if (count($parts) > 1 && strlen($parts[0]) < 30) {
+ $p0 = $parts[0]; array_shift($parts);
+ echo(replaceKeyWords("<b>" . $p0 . ":</b>" . implode(":", $parts)));
+ } else {
+ echo(replaceKeyWords(implode(":", $parts)));
+ }
+
+ if (count($lines) > 1) echo("</li>");
+ }
+
+ if (count($lines) > 1) echo("</ul>");
+
+ ?>
+ <?php else: ?>
+ <p><i>No usage provided for this toy. Enter edit mode to add usage information to this toy.</i></p>
+ <?php endif; ?>
+ <div style="margin-top:10px;"></div>
+
+ <b>Instructions to obtain in real life:</b><br>
+ <?php if (isset($selected["irl"]) && trim($selected["irl"]) !== ""): ?>
+ <?= replaceKeyWords(strip_tags($selected["irl"])) ?>
+ <?php else: ?>
+ <p><i><?php if (isset($selected["image"]) && $selected["image"] !== ""): ?>This toy is obtainable in real life, but no instructions are provided.<?php else: ?>This toy is not obtainable in real life.<?php endif; ?></i></p>
+ <?php endif; ?>
+
+ </div>
+ <?php if (isset($selected["image"]) && trim($selected["image"]) !== ""): ?>
+ <div>
+ <img src="<?= $selected["image"] ?>" style="width: 100%; max-height: 100vh;">
+ </div>
+ <?php endif; ?>
+ </div>
+
+
+ <hr>
+
+ <h4>Similar toys</h4>
+ <div class="row">
+ <?php
+
+ $names = [];
+ $currentName = $selected["name"];
+ $namesByDistance = [];
+
+ foreach ($data as $action) {
+ if ($action["name"] !== $currentName) $names[$action["name"]] = [
+ "id" => $action["id"],
+ "water" => $action["water"],
+ "ponies" => $action["ponies"],
+ "sexual" => $action["sexual"],
+ "description" => $action["description"]
+ ];
+ }
+
+ foreach ($names as $name => $data) {
+ $namesByDistance[] = [
+ "name" => $name,
+ "distance" => levenshtein($currentName, $name),
+ "id" => $data["id"],
+ "description" => $data["description"],
+ "water" => $data["water"],
+ "ponies" => $data["ponies"],
+ "sexual" => $data["sexual"]
+ ];
+ }
+
+ uasort($namesByDistance, function ($a, $b) use ($selected) {
+ return $a["distance"] - $b["distance"];
+ });
+
+ foreach ($namesByDistance as $item) {
+ echo("<!-- " . $currentName . " <-> " . $item["name"] . " => " . $item["distance"] . ") -->");
+ }
+
+ $index = 0;
+ foreach ($namesByDistance as $item): if ($index < 3):
+ ?>
+ <div class="col-md-4" style="margin-bottom:20px;">
+ <a class="linked-card" href="/-/toys/<?= $item["id"] ?>"><div class="card">
+ <div class="card-body">
+ <h4 class="card-title"><?= $item["name"] ?></h4>
+ <p class="card-text">
+ <!--
+ <?php
+
+ $uniquePonies = [];
+
+ foreach ($item["ponies"] as $ponies) {
+ foreach ($ponies["members"] as $member) {
+ if (isset($uniquePonies[$member]) && !$uniquePonies[$member]) {
+ $uniquePonies[$member] = false;
+ } else {
+ $uniquePonies[$member] = $ponies["deprecated"];
+ }
+ }
+ }
+
+ foreach ($uniquePonies as $name => $deprecated): if (!$deprecated): ?>
+ <span data-bs-toggle="tooltip" title="<?= getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["display_name"] ?>" style="display: inline-block;">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:32px;">
+ </span>
+ <?php endif; endforeach; ?>
+ <?php foreach ($uniquePonies as $name => $deprecated): if ($deprecated): ?>
+ <span data-bs-toggle="tooltip" data-bs-html="true" title="<i><?= strip_tags(getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["name"]) ?></i>" style="opacity:.5;display: inline-block;">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:32px;">
+ </span>
+ <?php endif; endforeach; ?>-->
+ <?= strlen(strip_tags($item["description"])) > 100 ? substr(strip_tags($item["description"]), 0, 100) . "โ€ฆ" : strip_tags($item["description"]) ?>
+ </p>
+ <?php if ($item["water"] === "in"): ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <?php elseif ($item["water"] === "out"): ?>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php elseif ($item["water"] === "playground"): ?>
+ <span class="badge rounded-pill" style="background-color:#20c997;">In playground</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php endif; ?>
+ </div>
+ </div></a>
+ </div>
+ <?php $index++; endif; endforeach; ?>
+ </div>
+
+ <div class="modal fade" id="editor">
+ <div class="modal-dialog modal-xl">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title">Edit toy</h4>
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+ </div>
+
+ <div class="modal-body">
+ <form method="post" style="display:inline;">
+ <input type="text" placeholder="Toy name" name="name" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', $selected["name"]) ?>">
+ <select name="water" class="form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%3e%3c/svg%3e");'>
+ <option value="0" <?= $selected["water"] === "out" ? "selected" : "" ?>>Usable outside the water</option>
+ <option value="1" <?= $selected["water"] === "in" ? "selected" : "" ?>>Usable inside the water</option>
+ <option value="2" <?= $selected["water"] === "both" ? "selected" : "" ?>>Usable both inside and outside</option>
+ </select>
+
+ <label style="margin-left:5px; display: none;">
+ <input <?= ($selected["sexual"] ?? false) ? "checked" : "" ?> type="checkbox" name="sexual">
+ Sexual toy
+ </label>
+
+ <label style="margin-left:5px; margin-top: 12px;">
+ <input class="form-check-input" <?= ($selected["verified"] ?? false) ? "checked" : "" ?> <?= $_PROFILE['login'] !== "raindrops" ? "disabled" : "" ?> type="checkbox" name="verified">
+ Mark as verified
+ </label><br>
+
+ <label style="margin-left:5px; display: none;">
+ <input <?= ($selected["untested"] ?? false) ? "checked" : "" ?> type="checkbox" name="untested">
+ Mark as untested
+ </label>
+
+ <hr>
+
+ <textarea rows="5" name="description" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;margin-bottom:10px;" placeholder="Description"><?= strip_tags($selected["description"] ?? "") ?></textarea>
+
+ <textarea rows="5" placeholder="Toy usage" name="usage" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;margin-bottom:10px;"><?= strip_tags($selected["usage"] ?? "") ?></textarea>
+
+ <hr>
+
+ <input type="text" placeholder="Keywords (comma-separated)" name="keywords" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', implode(",", $selected["keywords"] ?? [])) ?>">
+
+ <input type="text" placeholder="Instructions to obtain IRL" name="irl" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', $selected["irl"] ?? "") ?>">
+
+ <input type="text" placeholder="URL to IRL image" name="image" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', $selected["image"] ?? "") ?>">
+
+ <input type="text" placeholder="URL to usage image" name="usage_img" class="form-control" style="color:white;background:#111;border-color:#222;" value="<?= str_replace('"', '&quot;', $selected["usage_img"] ?? "") ?>">
+
+ <br>
+ <input type="hidden" name="updateAction">
+ <input type="hidden" name="action" value="<?= $selected["id"] ?>">
+ <input type="submit" class="btn btn-primary" value="Save">
+ </form>
+ <form method="post" style="display:inline;">
+ <input type="hidden" name="deleteAction">
+ <input type="hidden" name="action" value="<?= $selected["id"] ?>">
+ <input type="submit" class="btn btn-danger" value="Delete">
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div></div>
+
+ <?php else: ?>
+
+ <h2>Toys database</h2>
+ <?php $complete = count(array_filter($data, function ($i) {
+ return (isset($i["description"]) && trim($i["description"]) === "") || !isset($i["description"]);
+ })); ?>
+ <p><?= count($data) ?> toys<?php if ($complete > 0): ?> (<?= $complete ?> incomplete)<?php endif; ?></p>
+
+ <input type="text" placeholder="Search for a toy..." class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" onchange="search();" onkeydown="search();" onkeyup="search();" id="search">
+
+ <div id="list">
+ <div class="list-group">
+ <?php
+
+ $init = [];
+ foreach ($data as $value) {
+ $init[$value["name"]] = $value;
+ }
+
+ ksort($init);
+
+ $sorted = array_values($init);
+
+ foreach ($sorted as $item): ?>
+ <a href="/-/toys/<?= $item["id"] ?>" id="action-<?= $item["id"] ?>" style="display:grid;grid-template-columns: 1fr 0.2fr;" class="list-group-item list-group-item-action action-listing">
+ <div>
+ <span class="<?= trim($item["description"]) === "" ? "text-danger" : "" ?>"><?= $item["name"] ?></span>
+ <?php if (!isset($item["verified"])): ?><span class="badge bg-warning rounded-pill text-black">Unverified</span><?php endif; ?>
+ </div>
+ </a>
+ <?php endforeach; ?>
+ </div>
+ </div>
+
+ <div id="search-results" class="list-group"></div>
+
+ <div id="page-content">
+ <hr>
+ <p>Not finding what you are looking for? <a onclick="event.target.blur(); document.getElementById('creator-title').focus();" href="#" data-bs-toggle="modal" data-bs-target="#creator">Add a toy.</a></p>
+ </div>
+
+ <script>
+ window.actions = JSON.parse(atob(`<?= base64_encode(json_encode(array_values(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys/toys.json"), true)))) ?>`));
+ </script>
+
+ <div class="modal fade" id="creator">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title">Add a new toy</h4>
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+ </div>
+
+ <div class="modal-body">
+ <form method="post">
+ <input id="creator-title" type="text" placeholder="Toy name" name="name" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;">
+ <select name="water" class="form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%3e%3c/svg%3e");'>
+ <option value="0" selected>Usable outside of the water</option>
+ <option value="1">Usable inside of the water</option>
+ <option value="2">Usable both inside and outside</option>
+ <option value="3">Usable in a playground</option>
+ </select>
+
+ <p style="margin-top:10px;">You can add additional data (description, how to use) after adding the toy.</p>
+ <input type="hidden" name="createAction">
+ <input type="submit" class="btn btn-primary" value="Add">
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <?php endif; ?>
+ </div>
+ </div>
+
+ <!--suppress JSUnresolvedFunction -->
+ <script>
+ Array.from(document.getElementsByClassName("checkbox-input")).forEach((el) => {
+ el.onchange = () => {
+ let parent = el.parentElement;
+
+ if (el.checked) {
+ parent.classList.add("checked");
+ } else {
+ parent.classList.remove("checked");
+ }
+ }
+ });
+
+ const fuse = new Fuse(window.actions, {
+ includeScore: true,
+ keys: [
+ {
+ name: 'name',
+ weight: 1
+ },
+ {
+ name: 'description',
+ weight: 1
+ },
+ {
+ name: 'example',
+ weight: 0.7
+ },
+ {
+ name: 'irl',
+ weight: 0.5
+ }
+ ]
+ })
+
+ function search() {
+ let query = document.getElementById("search").value;
+ let results = fuse.search(query).map((i) => {
+ return {
+ id: i.item.id,
+ score: i.score
+ };
+ });
+
+ let unfiltered = results;
+
+ results = results.filter((i) => {
+ return i.score < 0.7;
+ });
+
+ console.log("Before:", unfiltered, "After:", results);
+
+ document.getElementById("list").style.display = "none";
+ document.getElementById("search-results").style.display = "block";
+ document.getElementById("search-results").innerHTML = "";
+
+ for (let result of results) {
+ document.getElementById("search-results").innerHTML += document.getElementById("action-" + result.id).outerHTML;
+ }
+
+ console.log(results);
+
+ if (query.trim() === "") {
+ document.getElementById("list").style.display = "block";
+ document.getElementById("search-results").style.display = "none";
+ }
+ }
+
+ function changeMixed() {
+ let value = document.getElementById("editor-type").value;
+ console.log(value);
+
+ if (value === "2") {
+ Array.from(document.getElementsByClassName("creator-relation")).forEach((el) => {
+ el.classList.add("mixed");
+ });
+ } else {
+ Array.from(document.getElementsByClassName("creator-relation")).forEach((el) => {
+ el.classList.remove("mixed");
+ });
+ }
+ }
+ </script>
+
+ <style>
+ .modal-header {
+ border-bottom: 1px solid #353738;
+ }
+
+ .modal-content {
+ border: 1px solid rgba(255, 255, 255, .2);
+ background-color: #111;
+ }
+
+ .btn-close {
+ filter: invert(1);
+ }
+
+ .creator-relation {
+ border-radius: 10px;
+ padding: 5px 10px;
+ opacity: .5;
+ }
+
+ .creator-relation.checked {
+ background-color: rgba(255, 255, 255, .1);
+ opacity: 1;
+ }
+
+ .creator-relation:hover {
+ background-color: rgba(255, 255, 255, .1);
+ }
+
+ .creator-relation.checked:hover {
+ background-color: rgba(255, 255, 255, .25) !important;
+ }
+
+ .creator-relation.checked .deprecated {
+ display: block !important;
+ }
+
+ .creator-relation.mixed.checked .sexual {
+ display: block !important;
+ }
+
+ .list-group-item {
+ color: #fff;
+ background-color: #222;
+ border: 1px solid rgba(255, 255, 255, .125);
+ }
+
+ .list-group-item.disabled {
+ color: #fff;
+ background-color: #222;
+ border-color: rgba(255, 255, 255, .125);
+ opacity: .75;
+ }
+
+ .list-group-item:hover {
+ background-color: #252525;
+ color: #ddd;
+ }
+
+ .list-group-item:active, .list-group-item:focus {
+ background-color: #272727;
+ color: #bbb;
+ }
+
+ @media (max-width: 991px) {
+ .action-listing {
+ grid-template-columns: 1fr !important;
+ text-align: center !important;
+ }
+
+ .action-listing > * {
+ margin-bottom: 10px;
+ text-align: center !important;
+ }
+
+ .action-listing > *:nth-last-child(1) {
+ margin-bottom: 0 !important;
+ }
+
+ .action-listing img {
+ width: 32px !important;
+ }
+ }
+ </style>
+
+<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/footer.inc'; ?> \ No newline at end of file