From 57472eab3c7b035392c6a5aa240593ecaa7d1ccf Mon Sep 17 00:00:00 2001 From: ilotterytea Date: Mon, 8 Dec 2025 21:53:36 +0500 Subject: upd: moved all /public/ files to the root folder --- 404.php | 43 ++ account/change_emoteset.php | 36 ++ account/delete.php | 50 ++ account/index.php | 306 ++++++++++ account/login/index.php | 99 ++++ account/login/twitch.php | 175 ++++++ account/register.php | 111 ++++ account/security.php | 52 ++ account/signout.php | 16 + badges.php | 50 ++ captcha.php | 59 ++ emotes/delete.php | 47 ++ emotes/index.php | 546 ++++++++++++++++++ emotes/rate.php | 63 ++ emotes/setmanip.php | 138 +++++ emotes/upload.php | 552 ++++++++++++++++++ emotesets.php | 164 ++++++ inbox.php | 77 +++ index.php | 90 +++ public/404.php | 43 -- public/account/change_emoteset.php | 36 -- public/account/delete.php | 50 -- public/account/index.php | 306 ---------- public/account/login/index.php | 99 ---- public/account/login/twitch.php | 175 ------ public/account/register.php | 111 ---- public/account/security.php | 52 -- public/account/signout.php | 16 - public/badges.php | 50 -- public/captcha.php | 59 -- public/emotes/delete.php | 47 -- public/emotes/index.php | 546 ------------------ public/emotes/rate.php | 63 -- public/emotes/setmanip.php | 138 ----- public/emotes/upload.php | 552 ------------------ public/emotesets.php | 164 ------ public/inbox.php | 77 --- public/index.php | 90 --- public/report/index.php | 124 ---- public/report/list.php | 81 --- public/report/send.php | 45 -- public/rules.php | 50 -- public/software.php | 83 --- public/static/favicon.ico | Bin 1150 -> 0 bytes public/static/img/404/1.webp | Bin 279856 -> 0 bytes public/static/img/brand/big.webp | Bin 12592 -> 0 bytes public/static/img/brand/mini.webp | Bin 3244 -> 0 bytes public/static/img/counter/0.png | Bin 37146 -> 0 bytes public/static/img/counter/1.png | Bin 32791 -> 0 bytes public/static/img/counter/2.png | Bin 37615 -> 0 bytes public/static/img/counter/3.png | Bin 22557 -> 0 bytes public/static/img/counter/4.png | Bin 35197 -> 0 bytes public/static/img/counter/5.png | Bin 20349 -> 0 bytes public/static/img/counter/6.png | Bin 32075 -> 0 bytes public/static/img/counter/7.png | Bin 22028 -> 0 bytes public/static/img/counter/8.png | Bin 34637 -> 0 bytes public/static/img/counter/9.png | Bin 142919 -> 0 bytes public/static/img/defaults/profile_picture.png | Bin 29739 -> 0 bytes public/static/img/icons/bin.png | Bin 475 -> 0 bytes public/static/img/icons/clock.png | Bin 882 -> 0 bytes public/static/img/icons/connect.png | Bin 748 -> 0 bytes public/static/img/icons/connections/twitch.webp | Bin 5142 -> 0 bytes public/static/img/icons/disconnect.png | Bin 796 -> 0 bytes public/static/img/icons/door_in.png | Bin 693 -> 0 bytes public/static/img/icons/door_out.png | Bin 688 -> 0 bytes public/static/img/icons/emotes/emote.png | Bin 4877 -> 0 bytes public/static/img/icons/emotes/emote_folder.png | Bin 4885 -> 0 bytes public/static/img/icons/emotes/emote_go.png | Bin 4980 -> 0 bytes public/static/img/icons/emoticon_happy.png | Bin 731 -> 0 bytes public/static/img/icons/eye.png | Bin 750 -> 0 bytes public/static/img/icons/heart.png | Bin 749 -> 0 bytes public/static/img/icons/inbox/0.png | Bin 587 -> 0 bytes public/static/img/icons/inbox/1.png | Bin 781 -> 0 bytes public/static/img/icons/inbox/2.png | Bin 641 -> 0 bytes public/static/img/icons/link_break.png | Bin 657 -> 0 bytes public/static/img/icons/new_emote.png | Bin 963 -> 0 bytes public/static/img/icons/no.png | Bin 587 -> 0 bytes public/static/img/icons/pencil.png | Bin 450 -> 0 bytes public/static/img/icons/ratings/-1.png | Bin 573 -> 0 bytes public/static/img/icons/ratings/1.png | Bin 883 -> 0 bytes public/static/img/icons/ratings/brimstone.webp | Bin 8400 -> 0 bytes public/static/img/icons/star.png | Bin 670 -> 0 bytes public/static/img/icons/table.png | Bin 566 -> 0 bytes public/static/img/icons/tag_blue.png | Bin 586 -> 0 bytes public/static/img/icons/user.png | Bin 741 -> 0 bytes public/static/img/icons/world.png | Bin 923 -> 0 bytes public/static/img/icons/yes.png | Bin 781 -> 0 bytes public/static/img/software/tinyrino/icon.png | Bin 6845 -> 0 bytes .../static/img/software/tinyrino/screenshots/1.png | Bin 57921 -> 0 bytes .../static/img/software/tinyrino/screenshots/2.png | Bin 197187 -> 0 bytes public/static/style.css | 640 --------------------- public/static/txt/RULES | 2 - public/system/emotes/index.php | 250 -------- public/system/emotes/verdict.php | 80 --- public/system/index.php | 69 --- public/users.php | 587 ------------------- report/index.php | 124 ++++ report/list.php | 81 +++ report/send.php | 45 ++ rules.php | 50 ++ software.php | 83 +++ static/favicon.ico | Bin 0 -> 1150 bytes static/img/404/1.webp | Bin 0 -> 279856 bytes static/img/brand/big.webp | Bin 0 -> 12592 bytes static/img/brand/mini.webp | Bin 0 -> 3244 bytes static/img/counter/0.png | Bin 0 -> 37146 bytes static/img/counter/1.png | Bin 0 -> 32791 bytes static/img/counter/2.png | Bin 0 -> 37615 bytes static/img/counter/3.png | Bin 0 -> 22557 bytes static/img/counter/4.png | Bin 0 -> 35197 bytes static/img/counter/5.png | Bin 0 -> 20349 bytes static/img/counter/6.png | Bin 0 -> 32075 bytes static/img/counter/7.png | Bin 0 -> 22028 bytes static/img/counter/8.png | Bin 0 -> 34637 bytes static/img/counter/9.png | Bin 0 -> 142919 bytes static/img/defaults/profile_picture.png | Bin 0 -> 29739 bytes static/img/icons/bin.png | Bin 0 -> 475 bytes static/img/icons/clock.png | Bin 0 -> 882 bytes static/img/icons/connect.png | Bin 0 -> 748 bytes static/img/icons/connections/twitch.webp | Bin 0 -> 5142 bytes static/img/icons/disconnect.png | Bin 0 -> 796 bytes static/img/icons/door_in.png | Bin 0 -> 693 bytes static/img/icons/door_out.png | Bin 0 -> 688 bytes static/img/icons/emotes/emote.png | Bin 0 -> 4877 bytes static/img/icons/emotes/emote_folder.png | Bin 0 -> 4885 bytes static/img/icons/emotes/emote_go.png | Bin 0 -> 4980 bytes static/img/icons/emoticon_happy.png | Bin 0 -> 731 bytes static/img/icons/eye.png | Bin 0 -> 750 bytes static/img/icons/heart.png | Bin 0 -> 749 bytes static/img/icons/inbox/0.png | Bin 0 -> 587 bytes static/img/icons/inbox/1.png | Bin 0 -> 781 bytes static/img/icons/inbox/2.png | Bin 0 -> 641 bytes static/img/icons/link_break.png | Bin 0 -> 657 bytes static/img/icons/new_emote.png | Bin 0 -> 963 bytes static/img/icons/no.png | Bin 0 -> 587 bytes static/img/icons/pencil.png | Bin 0 -> 450 bytes static/img/icons/ratings/-1.png | Bin 0 -> 573 bytes static/img/icons/ratings/1.png | Bin 0 -> 883 bytes static/img/icons/ratings/brimstone.webp | Bin 0 -> 8400 bytes static/img/icons/star.png | Bin 0 -> 670 bytes static/img/icons/table.png | Bin 0 -> 566 bytes static/img/icons/tag_blue.png | Bin 0 -> 586 bytes static/img/icons/user.png | Bin 0 -> 741 bytes static/img/icons/world.png | Bin 0 -> 923 bytes static/img/icons/yes.png | Bin 0 -> 781 bytes static/img/software/tinyrino/icon.png | Bin 0 -> 6845 bytes static/img/software/tinyrino/screenshots/1.png | Bin 0 -> 57921 bytes static/img/software/tinyrino/screenshots/2.png | Bin 0 -> 197187 bytes static/style.css | 640 +++++++++++++++++++++ static/txt/RULES | 2 + system/emotes/index.php | 250 ++++++++ system/emotes/verdict.php | 80 +++ system/index.php | 69 +++ users.php | 587 +++++++++++++++++++ 154 files changed, 4685 insertions(+), 4685 deletions(-) create mode 100644 404.php create mode 100644 account/change_emoteset.php create mode 100644 account/delete.php create mode 100644 account/index.php create mode 100644 account/login/index.php create mode 100644 account/login/twitch.php create mode 100644 account/register.php create mode 100644 account/security.php create mode 100644 account/signout.php create mode 100644 badges.php create mode 100644 captcha.php create mode 100644 emotes/delete.php create mode 100644 emotes/index.php create mode 100644 emotes/rate.php create mode 100644 emotes/setmanip.php create mode 100644 emotes/upload.php create mode 100644 emotesets.php create mode 100644 inbox.php create mode 100644 index.php delete mode 100644 public/404.php delete mode 100644 public/account/change_emoteset.php delete mode 100644 public/account/delete.php delete mode 100644 public/account/index.php delete mode 100644 public/account/login/index.php delete mode 100644 public/account/login/twitch.php delete mode 100644 public/account/register.php delete mode 100644 public/account/security.php delete mode 100644 public/account/signout.php delete mode 100644 public/badges.php delete mode 100644 public/captcha.php delete mode 100644 public/emotes/delete.php delete mode 100644 public/emotes/index.php delete mode 100644 public/emotes/rate.php delete mode 100644 public/emotes/setmanip.php delete mode 100644 public/emotes/upload.php delete mode 100644 public/emotesets.php delete mode 100644 public/inbox.php delete mode 100644 public/index.php delete mode 100644 public/report/index.php delete mode 100644 public/report/list.php delete mode 100644 public/report/send.php delete mode 100644 public/rules.php delete mode 100644 public/software.php delete mode 100644 public/static/favicon.ico delete mode 100644 public/static/img/404/1.webp delete mode 100644 public/static/img/brand/big.webp delete mode 100644 public/static/img/brand/mini.webp delete mode 100644 public/static/img/counter/0.png delete mode 100644 public/static/img/counter/1.png delete mode 100644 public/static/img/counter/2.png delete mode 100644 public/static/img/counter/3.png delete mode 100644 public/static/img/counter/4.png delete mode 100644 public/static/img/counter/5.png delete mode 100644 public/static/img/counter/6.png delete mode 100644 public/static/img/counter/7.png delete mode 100644 public/static/img/counter/8.png delete mode 100644 public/static/img/counter/9.png delete mode 100644 public/static/img/defaults/profile_picture.png delete mode 100644 public/static/img/icons/bin.png delete mode 100644 public/static/img/icons/clock.png delete mode 100644 public/static/img/icons/connect.png delete mode 100644 public/static/img/icons/connections/twitch.webp delete mode 100644 public/static/img/icons/disconnect.png delete mode 100644 public/static/img/icons/door_in.png delete mode 100644 public/static/img/icons/door_out.png delete mode 100644 public/static/img/icons/emotes/emote.png delete mode 100644 public/static/img/icons/emotes/emote_folder.png delete mode 100644 public/static/img/icons/emotes/emote_go.png delete mode 100644 public/static/img/icons/emoticon_happy.png delete mode 100644 public/static/img/icons/eye.png delete mode 100644 public/static/img/icons/heart.png delete mode 100644 public/static/img/icons/inbox/0.png delete mode 100644 public/static/img/icons/inbox/1.png delete mode 100644 public/static/img/icons/inbox/2.png delete mode 100644 public/static/img/icons/link_break.png delete mode 100644 public/static/img/icons/new_emote.png delete mode 100644 public/static/img/icons/no.png delete mode 100644 public/static/img/icons/pencil.png delete mode 100644 public/static/img/icons/ratings/-1.png delete mode 100644 public/static/img/icons/ratings/1.png delete mode 100644 public/static/img/icons/ratings/brimstone.webp delete mode 100644 public/static/img/icons/star.png delete mode 100644 public/static/img/icons/table.png delete mode 100644 public/static/img/icons/tag_blue.png delete mode 100644 public/static/img/icons/user.png delete mode 100644 public/static/img/icons/world.png delete mode 100644 public/static/img/icons/yes.png delete mode 100644 public/static/img/software/tinyrino/icon.png delete mode 100644 public/static/img/software/tinyrino/screenshots/1.png delete mode 100644 public/static/img/software/tinyrino/screenshots/2.png delete mode 100644 public/static/style.css delete mode 100644 public/static/txt/RULES delete mode 100644 public/system/emotes/index.php delete mode 100644 public/system/emotes/verdict.php delete mode 100644 public/system/index.php delete mode 100644 public/users.php create mode 100644 report/index.php create mode 100644 report/list.php create mode 100644 report/send.php create mode 100644 rules.php create mode 100644 software.php create mode 100644 static/favicon.ico create mode 100644 static/img/404/1.webp create mode 100644 static/img/brand/big.webp create mode 100644 static/img/brand/mini.webp create mode 100644 static/img/counter/0.png create mode 100644 static/img/counter/1.png create mode 100644 static/img/counter/2.png create mode 100644 static/img/counter/3.png create mode 100644 static/img/counter/4.png create mode 100644 static/img/counter/5.png create mode 100644 static/img/counter/6.png create mode 100644 static/img/counter/7.png create mode 100644 static/img/counter/8.png create mode 100644 static/img/counter/9.png create mode 100644 static/img/defaults/profile_picture.png create mode 100644 static/img/icons/bin.png create mode 100644 static/img/icons/clock.png create mode 100644 static/img/icons/connect.png create mode 100644 static/img/icons/connections/twitch.webp create mode 100644 static/img/icons/disconnect.png create mode 100644 static/img/icons/door_in.png create mode 100644 static/img/icons/door_out.png create mode 100644 static/img/icons/emotes/emote.png create mode 100644 static/img/icons/emotes/emote_folder.png create mode 100644 static/img/icons/emotes/emote_go.png create mode 100644 static/img/icons/emoticon_happy.png create mode 100644 static/img/icons/eye.png create mode 100644 static/img/icons/heart.png create mode 100644 static/img/icons/inbox/0.png create mode 100644 static/img/icons/inbox/1.png create mode 100644 static/img/icons/inbox/2.png create mode 100644 static/img/icons/link_break.png create mode 100644 static/img/icons/new_emote.png create mode 100644 static/img/icons/no.png create mode 100644 static/img/icons/pencil.png create mode 100644 static/img/icons/ratings/-1.png create mode 100644 static/img/icons/ratings/1.png create mode 100644 static/img/icons/ratings/brimstone.webp create mode 100644 static/img/icons/star.png create mode 100644 static/img/icons/table.png create mode 100644 static/img/icons/tag_blue.png create mode 100644 static/img/icons/user.png create mode 100644 static/img/icons/world.png create mode 100644 static/img/icons/yes.png create mode 100644 static/img/software/tinyrino/icon.png create mode 100644 static/img/software/tinyrino/screenshots/1.png create mode 100644 static/img/software/tinyrino/screenshots/2.png create mode 100644 static/style.css create mode 100644 static/txt/RULES create mode 100644 system/emotes/index.php create mode 100644 system/emotes/verdict.php create mode 100644 system/index.php create mode 100644 users.php diff --git a/404.php b/404.php new file mode 100644 index 0000000..cd7e12e --- /dev/null +++ b/404.php @@ -0,0 +1,43 @@ + + + + + (Error) <?php echo sprintf("%s - %s", $reason, INSTANCE_NAME) ?> + + + + + +
+
+ +
+

+ [ Back to home ] +
+ +
+ " alt=""> +
+
+
+ + + \ No newline at end of file diff --git a/account/change_emoteset.php b/account/change_emoteset.php new file mode 100644 index 0000000..c2fc209 --- /dev/null +++ b/account/change_emoteset.php @@ -0,0 +1,36 @@ +prepare("SELECT id FROM acquired_emote_sets WHERE emote_set_id = ? AND user_id = ?"); +$stmt->execute([$emote_set_id, $user_id]); + +if ($stmt->rowCount() == 0) { + generate_alert("/404.php", "You don't own emote set ID $emote_set_id", 403); + exit; +} + +$_SESSION["user_active_emote_set_id"] = $emote_set_id; + +header("Location: " . $_POST["redirect"] ?? "/"); \ No newline at end of file diff --git a/account/delete.php b/account/delete.php new file mode 100644 index 0000000..ec8c040 --- /dev/null +++ b/account/delete.php @@ -0,0 +1,50 @@ +prepare("DELETE FROM user_badges WHERE user_id = ?")->execute([$id]); +} + +if ($profile) { + $db->prepare("DELETE FROM users WHERE id = ?")->execute([$id]); + + session_unset(); + session_destroy(); + + setcookie("secret_key", "", time() - 1000); +} + +header("Location: /account"); \ No newline at end of file diff --git a/account/index.php b/account/index.php new file mode 100644 index 0000000..2b9e790 --- /dev/null +++ b/account/index.php @@ -0,0 +1,306 @@ +prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + + if ($stmt->rowCount() == 0) { + $stmt = $db->prepare("UPDATE users SET username = ? WHERE id = ?"); + $stmt->execute([$username, $_SESSION["user_id"]]); + } else { + generate_alert("/account", "The username has already taken"); + exit; + } + } + + if (isset($_FILES["pfp"]) && !empty($_FILES["pfp"]["tmp_name"])) { + $pfp = $_FILES["pfp"]; + + if ( + $err = create_image_bundle( + $pfp["tmp_name"], + $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/avatars/" . $_SESSION["user_id"], + ACCOUNT_PFP_MAX_SIZE[0], + ACCOUNT_PFP_MAX_SIZE[1], + true, + true + ) + ) { + generate_alert("/account", sprintf("Error occurred while processing the profile picture (%d)", $err)); + exit; + } + } + + if (isset($_FILES["banner"]) && !empty($_FILES["banner"]["tmp_name"])) { + $banner = $_FILES["banner"]; + + if ( + $err = create_image_bundle( + $banner["tmp_name"], + $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/banners/" . $_SESSION["user_id"], + ACCOUNT_BANNER_MAX_SIZE[0], + ACCOUNT_BANNER_MAX_SIZE[1], + true, + true + ) + ) { + generate_alert("/account", sprintf("Error occurred while processing the profile banner (%d)", $err)); + exit; + } + } + + if (isset($_FILES["badge"]) && !empty($_FILES["badge"]["tmp_name"])) { + $badge = $_FILES["badge"]; + $badge_id = bin2hex(random_bytes(16)); + if ( + $err = create_image_bundle( + $badge["tmp_name"], + $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/badges/" . $badge_id, + ACCOUNT_BADGE_MAX_SIZE[0], + ACCOUNT_BADGE_MAX_SIZE[1], + true, + true + ) + ) { + generate_alert("/account", sprintf("Error occurred while processing the personal badge (%d)", $err)); + exit; + } + + $db->prepare("DELETE FROM user_badges WHERE badge_id != ? AND user_id = ?")->execute([$badge_id, $_SESSION["user_id"]]); + $db->prepare("INSERT INTO badges(id, uploaded_by) VALUES (?, ?)")->execute([$badge_id, $_SESSION["user_id"]]); + $db->prepare("INSERT INTO user_badges(badge_id, user_id) VALUES (?, ?)")->execute([$badge_id, $_SESSION["user_id"]]); + } + + $db = null; + generate_alert("/account", "Your changes have been applied!", 200); + exit; +} + +?> + + + + + Account management - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ + +
+ +
+

Account management

+ +
+

Profile

+

Profile picture

+ '; + } else { + echo "

You don't have profile picture

"; + } + ?> +
+ + + + Remove profile picture + + +
+ +

Profile banner

+ '; + } else { + echo "

You don't have profile banner

"; + } + ?> +
+ + + + Remove banner + + +
+ +

Personal badge

+ prepare("SELECT badge_id FROM user_badges WHERE user_id = ?"); + $stmt->execute([$_SESSION["user_id"]]); + + $has_badge = false; + + if ($row = $stmt->fetch()) { + echo '
'; + echo ''; + echo ''; + echo ''; + echo '
'; + $has_badge = true; + } else { + echo "

You don't have personal badge

"; + } + ?> +
+ + + + Remove badge + + +
+ +

Username

+ "> + + +
+ +
+ +
+

Connections

+
+ prepare("SELECT * FROM connections WHERE user_id = ?"); + $stmt->execute([$_SESSION["user_id"]]); + $connections = $stmt->fetchAll(); + $platforms = ["twitch"]; + + foreach ($platforms as $platform) { + $connection = null; + $key = array_search($platform, array_column($connections, "platform")); + + if (!is_bool($key)) { + $connection = $connections[$key]; + } + + echo "
"; + echo "
"; + + echo "
"; + echo "" . ucfirst($platform) . ""; + + // TODO: check if connection is still alive + if ($connection == null) { + echo "Not connected"; + } else { + echo "" . $connection["alias_id"] . ""; + } + + echo "
"; + + echo "
"; + + if ($connection == null) { + echo ""; + echo 'Connect'; + echo ""; + } else { + echo ""; + echo 'Disconnect'; + echo ""; + } + + echo "
"; + } + ?> +
+
+ +
+ +
+

Security & Privacy

+
+ prepare("SELECT CASE WHEN password IS NOT NULL THEN 1 ELSE 0 END as set_password FROM users WHERE id = ?"); + $stmt->execute([$_SESSION["user_id"]]); + $set_password = $stmt->fetch()[0]; + if ($set_password): ?> + + + + + +
+
+ prepare("SELECT private_profile FROM user_preferences WHERE id = ?"); + $stmt->execute([$_SESSION["user_id"]]); + if (intval($stmt->fetch()[0]) == 1) { + echo 'checked'; + } + ?>> + +

Enabling this feature will hide your authorship of uploaded emotes and + actions.

+ +
+
+ + +
+ + +
+ + Delete + me +
+
+
+
+ + + + + \ No newline at end of file diff --git a/account/login/index.php b/account/login/index.php new file mode 100644 index 0000000..ace116d --- /dev/null +++ b/account/login/index.php @@ -0,0 +1,99 @@ +prepare("SELECT secret_key, password FROM users WHERE username = ? AND password IS NOT NULL"); + $stmt->execute([$username]); + + if ($row = $stmt->fetch()) { + if (password_verify($password, $row["password"])) { + setcookie("secret_key", $row["secret_key"], $remember ? (time() + ACCOUNT_COOKIE_MAX_LIFETIME) : 0, "/"); + header("Location: /account"); + exit; + } else { + generate_alert("/account/login", "Passwords do not match!", 403); + exit; + } + } else { + generate_alert("/account/login", "User not found or is not accessable", 404); + exit; + } +} +?> + + + + + Login - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+ +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + Register + +
+
+
+
+ + +
+ Login with Twitch +

Logging in via Twitch gives you the ability to use + emotes in your Twitch chat. +

+
+ +
+
+
+ + + \ No newline at end of file diff --git a/account/login/twitch.php b/account/login/twitch.php new file mode 100644 index 0000000..38fd6cc --- /dev/null +++ b/account/login/twitch.php @@ -0,0 +1,175 @@ +prepare("SELECT c.id, + CASE WHEN ( + SELECT u.password FROM users u WHERE u.id = c.user_id + ) IS NOT NULL + THEN 1 ELSE 0 + END AS set_password + FROM connections c + WHERE c.user_id = ? + "); + $stmt->execute([$_SESSION["user_id"]]); + + if ($row = $stmt->fetch()) { + if ($row["set_password"]) { + $db->prepare("DELETE FROM connections WHERE user_id = ? AND platform = 'twitch'")->execute([$_SESSION["user_id"]]); + generate_alert("/account", "Successfully disconnected from Twitch!", 200); + } else { + generate_alert("/account", "You must set a password before deleting any connections", 403); + } + } else { + generate_alert("/account", "No Twitch connection found", 404); + } + exit; +} + +$client_id = TWITCH_CLIENT_ID; +$client_secret = TWITCH_SECRET_KEY; +$redirect_uri = TWITCH_REDIRECT_URI; + +if (isset($_GET["error"])) { + header("Location: /account/login"); + exit; +} + +if (!isset($_GET["code"])) { + header("Location: https://id.twitch.tv/oauth2/authorize?client_id=$client_id&redirect_uri=$redirect_uri&response_type=code"); + exit; +} + +$code = $_GET["code"]; + +// obtaining twitch token +$request = curl_init(); +curl_setopt($request, CURLOPT_URL, "https://id.twitch.tv/oauth2/token"); +curl_setopt($request, CURLOPT_POST, 1); +curl_setopt( + $request, + CURLOPT_POSTFIELDS, + "client_id=$client_id&client_secret=$client_secret&code=$code&grant_type=authorization_code&redirect_uri=$redirect_uri" +); +curl_setopt($request, CURLOPT_RETURNTRANSFER, true); + +$response = curl_exec($request); +curl_close($request); + +$response = json_decode($response, true); + +if (array_key_exists("status", $response)) { + header("Location: /account/login"); + exit; +} + +// identifying user +$request = curl_init(); +curl_setopt($request, CURLOPT_URL, "https://api.twitch.tv/helix/users"); +curl_setopt($request, CURLOPT_HTTPHEADER, [ + "Authorization: Bearer " . $response["access_token"], + "Client-Id: $client_id" +]); +curl_setopt($request, CURLOPT_RETURNTRANSFER, true); + +$twitch_user = curl_exec($request); +curl_close($request); + +$twitch_user = json_decode($twitch_user, true); + +if (empty($twitch_user["data"])) { + generate_alert("/account", "Failed to identify Twitch user", 500); + exit; +} + +$twitch_user = $twitch_user["data"][0]; + +// saving it +$twitch_access_token = $response["access_token"]; +$twitch_refresh_token = $response["refresh_token"]; +$twitch_expires_on = time() + intval($response["expires_in"]); + +// creating user if not exists +$stmt = $db->prepare("SELECT * FROM users u + INNER JOIN connections c ON c.alias_id = ? + WHERE c.user_id = u.id AND c.platform = 'twitch' +"); +$stmt->execute([$twitch_user["id"]]); + +$user_id = ""; +$user_secret_key = ""; +$user_name = ""; + +if ($row = $stmt->fetch()) { + if (isset($_SESSION["user_id"]) && $_SESSION["user_id"] != $row["id"]) { + generate_alert("/account", "There is another " . INSTANCE_NAME . " account associated with that Twitch account", 409); + exit; + } + + $user_name = $row["username"]; + $user_secret_key = $row["secret_key"]; + $user_id = $row["id"]; +} else { + $user_secret_key = generate_random_string(32); + $user_name = $twitch_user["login"]; + $user_id = bin2hex(random_bytes(16)); + + list($user_secret_key, $user_name, $user_id) = match (isset($_SESSION["user_id"])) { + true => [$_COOKIE["secret_key"], $_SESSION["user_name"], $_SESSION["user_id"]], + default => [generate_random_string(32), $twitch_user["login"], bin2hex(random_bytes(16))] + }; + + if (!isset($_SESSION["user_id"])) { + // checking for duplicates + $stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username = ?"); + $stmt->execute([$user_name]); + $duplicates = intval($stmt->fetch()[0]); + if ($duplicates > 0) { + $i = 1; + while (true) { + $stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username = ?"); + $stmt->execute(["$user_name$i"]); + + if ($stmt->fetch()[0] == 0) { + break; + } + + $i++; + } + $user_name .= $i; + } + + $stmt = $db->prepare("INSERT INTO users(id, username, secret_key) VALUES (?, ?, ?)"); + if (!$stmt->execute([$user_id, $user_name, $user_secret_key])) { + $db = null; + echo "Failed to create a user"; + exit; + } + } + + $stmt = $db->prepare("INSERT INTO connections(user_id, alias_id, platform, data) VALUES (?, ?, 'twitch', ?)"); + $stmt->execute([ + $user_id, + $twitch_user["id"], + sprintf("%s:%s:%s", $twitch_access_token, $twitch_refresh_token, $twitch_expires_on) + ]); +} + +$_SESSION["user_id"] = $user_id; +$_SESSION["user_name"] = $user_name; +setcookie("secret_key", $user_secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/"); + +$db = null; + +header("Location: /account"); \ No newline at end of file diff --git a/account/register.php b/account/register.php new file mode 100644 index 0000000..1da89a0 --- /dev/null +++ b/account/register.php @@ -0,0 +1,111 @@ + $username_length || $username_length > ACCOUNT_USERNAME_LENGTH[1]) { + generate_alert("/account/register.php", sprintf("Username must be between %d-%d characters long", ACCOUNT_USERNAME_LENGTH[0], ACCOUNT_USERNAME_LENGTH[1])); + exit; + } + + if (!preg_match(ACCOUNT_USERNAME_REGEX, $username)) { + generate_alert("/account/register.php", "Bad username"); + exit; + } + + $password = $_POST["password"]; + if (ACCOUNT_PASSWORD_MIN_LENGTH > strlen($password)) { + generate_alert("/account/register.php", "Password must be at least " . ACCOUNT_PASSWORD_MIN_LENGTH . " characters"); + exit; + } + + $db = new PDO(DB_URL, DB_USER, DB_PASS); + + $stmt = $db->prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + + if ($stmt->rowCount() != 0) { + generate_alert("/account/register.php", "The username has already been taken"); + exit; + } + + $secret_key = generate_random_string(ACCOUNT_SECRET_KEY_LENGTH); + $password = password_hash($password, PASSWORD_DEFAULT); + + $id = bin2hex(random_bytes(16)); + + $stmt = $db->prepare("INSERT INTO users(id, username, password, secret_key) VALUES (?, ?, ?, ?)"); + $stmt->execute([$id, $username, $password, $secret_key]); + + setcookie("secret_key", $secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/"); + header("Location: /account"); + exit; +} +?> + + + + + Register an account - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ + +
+ +
+ +
+
+
+ + +
+
+ + +
+
+ +
+
+

+ Since doesn't require email and password reset via email is + not supported, please remember your passwords! +

+
+
+
+
+
+ + + \ No newline at end of file diff --git a/account/security.php b/account/security.php new file mode 100644 index 0000000..5545b60 --- /dev/null +++ b/account/security.php @@ -0,0 +1,52 @@ +prepare("SELECT * FROM users WHERE id = ?"); +$stmt->execute([$_SESSION["user_id"]]); + +$user = $stmt->fetch(); +$current_password = $_POST["password-current"] ?? ""; + +if ($user["password"] != null && !password_verify($current_password, $user["password"])) { + generate_alert("/account", "Password is required to apply changes in 'Security' section"); + exit; +} + +if (!empty($_POST["password-new"])) { + $password = $_POST["password-new"]; + if (ACCOUNT_PASSWORD_MIN_LENGTH > strlen($password)) { + generate_alert("/account", "Your password must be at least " . ACCOUNT_PASSWORD_MIN_LENGTH . " characters"); + exit; + } + + $db->prepare("UPDATE users SET password = ? WHERE id = ?") + ->execute([password_hash($password, PASSWORD_DEFAULT), $user["id"]]); +} + +$private_profile = (int) (intval($_POST["make-private"] ?? "0") == 1); + +$db->prepare("UPDATE user_preferences SET private_profile = ? WHERE id = ?") + ->execute([$private_profile, $user["id"]]); + +if (intval($_POST["signout-everywhere"] ?? "0") == 1) { + $db->prepare("UPDATE users SET secret_key = ? WHERE id = ?") + ->execute([generate_random_string(ACCOUNT_SECRET_KEY_LENGTH), $_SESSION["user_id"]]); + + session_unset(); + session_destroy(); + + setcookie("secret_key", "", time() - 1000); +} + +generate_alert("/account", "Your changes have been applied!", 200); \ No newline at end of file diff --git a/account/signout.php b/account/signout.php new file mode 100644 index 0000000..f971d4a --- /dev/null +++ b/account/signout.php @@ -0,0 +1,16 @@ +prepare("SELECT + u.id, u.username, + r.name AS role_name, + r.badge_id AS role_badge_id, + ub.badge_id AS custom_badge_id, + co.alias_id AS connection_alias_id, + co.platform AS connection_platform + FROM users u + JOIN role_assigns ra ON ra.user_id = u.id + JOIN roles r ON r.id = ra.role_id + LEFT JOIN user_badges ub ON ub.user_id = u.id + LEFT JOIN connections co ON co.user_id = u.id + WHERE r.badge_id IS NOT NULL OR ub.badge_id IS NOT NULL +"); +$stmt->execute(); + +$rows = $stmt->fetchAll(); + +$badges = []; + +foreach ($rows as $row) { + $badge = [ + "id" => $row["id"], + "username" => $row["username"], + "role" => Role::from_array($row), + "custom_badge" => Badge::from_array($row, "custom"), + "connection" => match (isset($row["connection_alias_id"], $row["connection_platform"])) { + true => [ + "alias_id" => $row["connection_alias_id"], + "platform" => $row["connection_platform"] + ], + false => null + } + ]; + + array_push($badges, $badge); +} + +json_response([ + "status_code" => 200, + "message" => null, + "data" => $badges +]); \ No newline at end of file diff --git a/captcha.php b/captcha.php new file mode 100644 index 0000000..b454b7d --- /dev/null +++ b/captcha.php @@ -0,0 +1,59 @@ + 200, + "message" => "Solved!", + "data" => null + ]); + } else { + echo json_response([ + "status_code" => 400, + "message" => "Wrong answer!", + "data" => null + ], 400); + } + exit; +} + +$file_folder = $_SERVER["DOCUMENT_ROOT"] . '/static/img/captcha'; + +if (!CAPTCHA_ENABLE || ($_SESSION["captcha_solved"] ?? false) || !is_dir($file_folder)) { + $_SESSION["captcha_solved"] = true; + echo json_response([ + "status_code" => 200, + "message" => "No need to solve captcha", + "data" => null + ]); + exit; +} + +$files = scandir($file_folder); +array_splice($files, 0, 2); + +$filename = $files[random_int(0, count($files) - 1)]; +$filename = basename($filename, ".png"); + +$_SESSION["captcha_word"] = $filename; + +$image = generate_image_captcha( + CAPTCHA_SIZE[0], + CAPTCHA_SIZE[1], + random_int(1, 3), + $filename, + $file_folder +); + +echo json_response([ + "status_code" => 200, + "message" => null, + "data" => $image +]); \ No newline at end of file diff --git a/emotes/delete.php b/emotes/delete.php new file mode 100644 index 0000000..6252e45 --- /dev/null +++ b/emotes/delete.php @@ -0,0 +1,47 @@ +prepare("SELECT uploaded_by, code FROM emotes WHERE id = ?"); +$stmt->execute([$emote_id]); + +if ($row = $stmt->fetch()) { + if ($row["uploaded_by"] === $user_id) { + $unlink = intval($_POST["unlink"] ?? "0") == 1; + + if ($unlink) { + $stmt = $db->prepare("UPDATE emotes SET uploaded_by = NULL WHERE id = ? AND uploaded_by = ?"); + $stmt->execute([$emote_id, $user_id]); + generate_alert("/emotes/?id=$emote_id", 'Your authorship has been removed for the emote "' . $row["code"] . '"', 200); + } else { + $stmt = $db->prepare("DELETE FROM emotes WHERE id = ? AND uploaded_by = ?"); + $stmt->execute([$emote_id, $user_id]); + + $path = $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/emotes/$emote_id"; + array_map("unlink", glob("$path/*.*")); + rmdir($path); + + generate_alert("/emotes", 'Emote "' . $row["code"] . '" has been removed from the servers', 200); + } + } else { + generate_alert("/emotes", "You don't own the emote \"" . $row["code"] . "\"", 403); + } +} else { + generate_alert("/emotes", "Emote ID $emote_id not found", 404); +} \ No newline at end of file diff --git a/emotes/index.php b/emotes/index.php new file mode 100644 index 0000000..af14120 --- /dev/null +++ b/emotes/index.php @@ -0,0 +1,546 @@ +prepare("SELECT e.id, e.code, e.created_at, e.source, e.visibility, + COALESCE(COUNT(r.rate), 0) as total_rating, + COALESCE(ROUND(AVG(r.rate), 2), 0) AS average_rating, + CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by + FROM emotes e + LEFT JOIN user_preferences up ON up.id = e.uploaded_by + LEFT JOIN ratings AS r ON r.emote_id = e.id + WHERE e.id = ? + LIMIT 1 + "); + $stmt->execute([$user_id, $id]); + + $row = $stmt->fetch(); + + if ($row["id"]) { + // fetching emote tags + $stmt = $db->prepare("SELECT t.code FROM tags t + INNER JOIN tag_assigns ta ON ta.emote_id = ? + WHERE t.id = ta.tag_id + "); + $stmt->execute([$row["id"]]); + $tags = $stmt->fetchAll(PDO::FETCH_ASSOC); + $tags = array_column($tags, "code"); + + $row["tags"] = $tags; + $row["ext"] = "webp"; + $emote = Emote::from_array_with_user($row, $db); + } else { + generate_alert("/404.php", "Emote ID $id does not exists", 404); + exit; + } +} +// fetching all emotes +else { + $sort = $_GET["sort"] ?? "high_ratings"; + $sort = match ($sort) { + "low_ratings" => "rating ASC", + "recent" => "e.created_at DESC", + "oldest" => "e.created_at ASC", + default => "rating DESC" + }; + $page = max(1, intval($_GET["p"] ?? "1")); + $limit = 50; + $offset = ($page - 1) * $limit; + $search = $_GET["q"] ?? ""; + + // fetching emotes + $stmt = $db->prepare("SELECT e.*, + CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, + CASE WHEN EXISTS ( + SELECT 1 + FROM emote_set_contents ec + INNER JOIN emote_sets es ON es.id = ec.emote_set_id + JOIN acquired_emote_sets aes ON aes.emote_set_id = es.id + WHERE ec.emote_id = e.id AND es.id = ? + ) THEN 1 ELSE 0 END AS is_in_user_set, COALESCE(COUNT(r.rate), 0) AS rating + FROM emotes e + LEFT JOIN user_preferences up ON up.id = e.uploaded_by + LEFT JOIN ratings AS r ON r.emote_id = e.id + LEFT JOIN tag_assigns ta ON ta.emote_id = e.id + LEFT JOIN tags t ON t.id = ta.tag_id + WHERE (t.code = ? OR e.code LIKE ?) AND e.visibility = 1 + GROUP BY + e.id, e.code, e.created_at + ORDER BY $sort + LIMIT ? OFFSET ? + "); + + $sql_search = "%$search%"; + $user_emote_set_id = $_SESSION["user_active_emote_set_id"] ?? ""; + + $stmt->bindParam(1, $user_id, PDO::PARAM_STR); + $stmt->bindParam(2, $user_emote_set_id, PDO::PARAM_STR); + $stmt->bindParam(3, $search, PDO::PARAM_STR); + $stmt->bindParam(4, $sql_search, PDO::PARAM_STR); + $stmt->bindParam(5, $limit, PDO::PARAM_INT); + $stmt->bindParam(6, $offset, PDO::PARAM_INT); + + $stmt->execute(); + + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + $emotes = []; + + foreach ($rows as $row) { + array_push($emotes, Emote::from_array_with_user($row, $db)); + } + + $total_emotes = count($emotes); + $total_pages = ceil($total_emotes / $limit); +} + +if (CLIENT_REQUIRES_JSON) { + json_response([ + "status_code" => 200, + "message" => null, + "data" => $emotes ?? $emote + ]); + exit; +} +?> + + + + + <?php + echo ($emote != null ? "Emote " . $emote->get_code() : "Emotes") . ' - ' . INSTANCE_NAME + ?> + + + + + +
+
+ + +
+ +
+ +
+ + +
+
+ '; + + echo ''; + + $path = $_SERVER["DOCUMENT_ROOT"] . '/static/userdata/emotes/' . $emote->get_id() . "/{$size}x.webp"; + + echo '
'; + + if ($file_size = filesize($path)) { + $kb = sprintf("%.2f", $file_size / 1024); + echo "

{$kb}KB

"; + } + + if ($image_size = getimagesize($path)) { + echo "

$image_size[0]x$image_size[1]

"; + } + + echo '
'; + } + ?> +
+
+ +
+ +
+ prepare("SELECT id, code FROM emote_set_contents WHERE emote_set_id = ? AND emote_id = ?"); + $stmt->execute([$_SESSION["user_active_emote_set_id"], $emote->get_id()]); + + $added = false; + + if ($row = $stmt->fetch()) { + $added = true; + $emote_current_name = $row["code"] ?? $emote->get_code(); + } + } + + if (isset($_SESSION["user_role"]) && $_SESSION["user_role"]["permission_emoteset_own"]) { + echo '' ?> +
+ + " style="display: none;"> + + + +
+
+ + " style="display: none;"> + + + + + + + +
+ + + get_uploaded_by() === $_SESSION["user_id"]): ?> +
+ + +
+
+ + + +
+ +
+
+ prepare("SELECT rate FROM ratings WHERE user_id = ? AND emote_id = ?"); + $stmt->execute([$_SESSION["user_id"], $id]); + + if ($row = $stmt->fetch()) { + echo 'You gave '; + } else { + foreach (RATING_NAMES as $key => $value) { + echo '
'; + echo ''; + echo ""; + echo '
'; + } + } + } + if (REPORTS_ENABLE && $_SESSION["user_role"]["permission_report"]) { + echo "Report emote"; + } + } + ?> +
+ +

Log in to get additional features...

+ +
+ +
+ + get_tags())): ?> + + + + + + + + + + prepare("SELECT u.id, a.created_at FROM users u + INNER JOIN mod_actions a ON a.emote_id = ? + WHERE u.id = a.user_id"); + $stmt->execute([$emote->get_id()]); + + if ($row = $stmt->fetch()) { + $approver = User::get_user_by_id($db, $row["id"]); + + echo ''; + } + + if (RATING_ENABLE): ?> + + + get_rating()["total"] < RATING_EMOTE_MIN_VOTES) { + echo ''; + } else { + + $rating = $emote->get_rating()["average"]; + + // TODO: make it customizable + list($rating_classname, $rating_name) = match (true) { + in_range($rating, 0.75, 1.0) => [ + "gemerald", + " + + Shiny Gemerald! + + + + " + ], + in_range($rating, 0.25, 0.75) => ["gem", " Gem "], + in_range($rating, -0.25, 0.25) => ["iron", "Iron"], + in_range($rating, -0.75, -0.25) => ["coal", " Coal "], + in_range($rating, -1.0, -0.75) => [ + "brimstone", + " + + + + !!!AVOID THIS CANCER-GIVING BRIMSTONE!!! + + + + " + ] + }; + + echo ''; + } + ?> + + + + + + + get_source()): ?> + + + + + +
Tags + get_tags() as $tag) { + echo "$tag "; + } + ?> +
Uploaderget_uploaded_by()) { + $u = $emote->get_uploaded_by(); + $show_private_badge = $u->private_profile; + + $username = $u->username; + $link = "/users.php?id={$u->id}"; + $badge = $u->role; + $custom_badge = $u->custom_badge; + } + + echo ""; + echo $username; + echo ""; + + if ($show_private_badge) { + echo " (Private)"; + } + + if ($badge && $badge->badge) { + echo " ## {$badge->name}"; + } + + if ($custom_badge) { + echo " "; + } + + echo ', get_created_at()); + echo ' UTC">about ' . format_timestamp(time() - $emote->get_created_at()) . " ago"; + ?>
Approver'; + echo "{$approver->username}"; + + if ($approver->role && $approver->role->badge) { + echo " ## {$approver->role->name}"; + } + + if ($approver->custom_badge) { + echo " "; + } + + echo ', '; + echo format_timestamp(strtotime($row["created_at"]) - $emote->get_created_at()) . ' after upload'; + echo '
RatingNot rated (' . $emote->get_rating()["total"] . ')'; + echo "$rating_name"; + echo ' (' . $emote->get_rating()["total"] . ')'; + echo '
Visibilityget_visibility()) { + case 0: + echo 'Unlisted'; + break; + case 1: + echo 'Public'; + break; + case 2: + echo 'Pending approval (unlisted for a moment)'; + break; + default: + echo 'N/A'; + break; + } + ?>
Source + get_source() ?> +
+
+ +
+
+ prepare("SELECT users.id, users.username + FROM users + INNER JOIN emote_sets AS es ON es.owner_id = users.id + INNER JOIN emote_set_contents AS ec ON ec.emote_set_id = es.id + INNER JOIN acquired_emote_sets AS aes ON aes.emote_set_id = es.id + WHERE ec.emote_id = ? AND aes.is_default = TRUE"); + + $stmt->execute([$emote->get_id()]); + $count = $stmt->rowCount(); + + $db = null; + + if ($count > 0) { + echo "

Added in $count channels

"; + } else { + echo "No one has added this emote yet... :'("; + } + ?> +
+ fetch()) { + echo '' . $row["username"] . ''; + } + ?> +
+
+ +
+ +
+ 1) { + echo '' ?> +
+
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/emotes/rate.php b/emotes/rate.php new file mode 100644 index 0000000..1e8eb67 --- /dev/null +++ b/emotes/rate.php @@ -0,0 +1,63 @@ +prepare("SELECT id FROM emotes WHERE id = ?"); +$stmt->execute([$id]); +if ($stmt->rowCount() != 1) { + generate_alert("/emotes", "Emote ID $id does not exist", 404); + exit; +} + +// checking if user has already given a rate +$stmt = $db->prepare("SELECT id FROM ratings WHERE user_id = ? AND emote_id = ?"); +$stmt->execute([$_SESSION["user_id"], $id]); +if ($stmt->rowCount() != 0) { + generate_alert("/emotes?id=$id", "You have already given a rate for this emote!", 403); + exit; +} + +// giving a rate +$stmt = $db->prepare("INSERT INTO ratings(user_id, emote_id, rate) VALUES (?, ?, ?)"); +$stmt->execute([$_SESSION["user_id"], $id, clamp($rate, -2, 2)]); + +if (CLIENT_REQUIRES_JSON) { + $stmt = $db->prepare("SELECT * FROM ratings WHERE id = ?"); + $stmt->execute([$db->lastInsertId()]); + + json_response([ + "status_code" => 200, + "message" => "Rated!", + "data" => $stmt->fetch(PDO::FETCH_ASSOC) + ]); + exit; +} + +generate_alert("/emotes?id=$id", "Rated!", 200); diff --git a/emotes/setmanip.php b/emotes/setmanip.php new file mode 100644 index 0000000..129790d --- /dev/null +++ b/emotes/setmanip.php @@ -0,0 +1,138 @@ +prepare("SELECT id, code, uploaded_by, visibility, created_at FROM emotes WHERE id = ?"); +$stmt->execute([$emote_id]); +if ($stmt->rowCount() == 0) { + generate_alert("/emotes", "Emote not found", 404); + exit; +} +$emote = $stmt->fetch(PDO::FETCH_ASSOC); + +$user_id = $_SESSION["user_id"]; +$emote_set_id = $_POST["emote_set_id"]; + +// checking emote set +$stmt = $db->prepare("SELECT id FROM acquired_emote_sets WHERE emote_set_id = ? AND user_id = ?"); +$stmt->execute([$emote_set_id, $user_id]); + +if ($stmt->rowCount() == 0) { + generate_alert("/404.php", "You don't own emote set ID $emote_set_id", 403); + exit; +} + +// inserting emote +$stmt = $db->prepare("SELECT id FROM emote_set_contents WHERE emote_set_id = ? AND emote_id = ?"); +$stmt->execute([$emote_set_id, $emote_id]); + +$action = $_POST["action"]; +$payload = [ + "emote" => $emote, + "emoteset" => $_SESSION["user_active_emote_set"] +]; + +switch ($action) { + case "add": { + if ($stmt->rowCount() != 0) { + generate_alert("/emotes?id=$emote_id", "This emote has been already added!"); + exit; + } + + $stmt = $db->prepare("INSERT INTO emote_set_contents(emote_set_id, emote_id, added_by) VALUES (?, ?, ?)"); + $stmt->execute([$emote_set_id, $emote_id, $user_id]); + + if (ACCOUNT_LOG_ACTIONS) { + $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") + ->execute([$user_id, "EMOTESET_ADD", json_encode($payload)]); + } + + $db = null; + + generate_alert("/emotes?id=$emote_id", "This emote has been added to your set. Enjoy!", 200); + break; + } + case "remove": { + if ($row = $stmt->fetch()) { + $stmt = $db->prepare("DELETE FROM emote_set_contents WHERE id = ?"); + $stmt->execute([$row["id"]]); + } else { + generate_alert("/emotes?id=$emote_id", "This emote wasn't added!"); + $db = null; + exit; + } + + if (ACCOUNT_LOG_ACTIONS) { + $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") + ->execute([$user_id, "EMOTESET_REMOVE", json_encode($payload)]); + } + + $db = null; + + generate_alert("/emotes?id=$emote_id", "This emote has been removed from your set.", 200); + break; + } + case "alias": { + if (!isset($_POST["value"])) { + generate_alert("/emotes?id=$emote_id", "No value field"); + exit; + } + + $value = str_safe($_POST["value"], EMOTE_NAME_MAX_LENGTH); + + $stmt = $db->prepare("SELECT esc.code AS alias_code, e.code FROM emote_set_contents esc + INNER JOIN emotes e ON e.id = esc.emote_id + WHERE esc.emote_set_id = ? AND esc.emote_id = ?"); + $stmt->execute([$emote_set_id, $emote_id]); + + if (empty($value)) { + $value = null; + + if ($row = $stmt->fetch()) { + $payload["emote"]["original_code"] = $row["alias_code"]; + $payload["emote"]["code"] = $row["code"]; + } + } else { + $row = $stmt->fetch(); + $payload["emote"]["original_code"] = $row["alias_code"] ?? $row["code"]; + $payload["emote"]["code"] = $value; + } + + $stmt = $db->prepare("UPDATE emote_set_contents SET code = ? WHERE emote_set_id = ? AND emote_id = ?"); + $stmt->execute([$value, $emote_set_id, $emote_id]); + + if (ACCOUNT_LOG_ACTIONS) { + $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") + ->execute([$user_id, "EMOTESET_ALIAS", json_encode($payload)]); + } + + $db = null; + + generate_alert("/emotes?id=$emote_id", "Updated emote name!", 200); + break; + } + default: { + generate_alert("/emotes?id=$emote_id", "Unknown action"); + break; + } +} \ No newline at end of file diff --git a/emotes/upload.php b/emotes/upload.php new file mode 100644 index 0000000..644e4b6 --- /dev/null +++ b/emotes/upload.php @@ -0,0 +1,552 @@ +prepare("DELETE FROM emotes WHERE id = ?"); + $stmt->execute([$id]); + $db = null; + + array_map("unlink", glob("$path/*.*")); + rmdir($path); +} + +include "../../src/utils.php"; +include "../../src/images.php"; + +$max_width = EMOTE_MAX_SIZE[0]; +$max_height = EMOTE_MAX_SIZE[1]; + +if ($_SERVER['REQUEST_METHOD'] != "POST") { + include "../../src/partials.php"; + + echo '' ?> + + + + Upload an emote - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ + + +
+
+
+ +
+
+

Image*

+ + + + + +
+ + +
+ +

Emote name*

+ + +
+ +
+

test

+
+ + + + + + + + + + + + + + + +
Emote source: +
Tags [?]: +
+ +
+ + +
+ + +
+
+
+ + +
+ + +
+
+
+ + + + + + + prepare("INSERT INTO emotes(id, code, notes, source, uploaded_by, visibility) VALUES (?, ?, ?, ?, ?, ?)"); +$stmt->execute([$id, $code, $notes, $source, $uploaded_by, $visibility]); + +$path = "../static/userdata/emotes/$id"; + +if (!is_dir($path)) { + mkdir($path, 0777, true); +} + +if ($is_manual) { + $image_1x = $_FILES["file-1x"]; + $image_2x = $_FILES["file-2x"]; + $image_3x = $_FILES["file-3x"]; + + $file_1x = does_file_meet_requirements($image_1x["tmp_name"], $max_width / 4, $max_height / 4); + $file_2x = does_file_meet_requirements($image_2x["tmp_name"], $max_width / 2, $max_height / 2); + $file_3x = does_file_meet_requirements($image_3x["tmp_name"], $max_width, $max_height); + + if (!$file_1x[0] || !$file_2x[0] || !$file_3x[0]) { + generate_alert("/emotes/upload.php", "Files don't meet requirements"); + abort_upload($path, $db, $id); + exit; + } + + if ( + !move_uploaded_file($image_1x["tmp_name"], "$path/1x.$file_1x[1]") || + !move_uploaded_file($image_2x["tmp_name"], "$path/2x.$file_2x[1]") || + !move_uploaded_file($image_3x["tmp_name"], "$path/3x.$file_3x[1]") + ) { + generate_alert("/emotes/upload.php", "Failed to move the uploaded files"); + abort_upload($path, $db, $id); + exit; + } +} else { + $image = $_FILES["file"]; + // resizing the image + if ($err = create_image_bundle($image["tmp_name"], $path, $max_width, $max_height)) { + generate_alert("/emotes/upload.php", "Error occurred while processing images ($err)", 500); + abort_upload($path, $db, $id); + exit; + } + + if (EMOTE_STORE_ORIGINAL) { + $ext = get_file_extension($image["tmp_name"]) ?? ""; + move_uploaded_file($image["tmp_name"], "$path/original.$ext"); + } +} + +$tags = str_safe($_POST["tags"] ?? "", null); +$tags_processed = []; + +if (!empty($tags) && TAGS_ENABLE) { + $tags = explode(" ", $tags); + + $count = 0; + + foreach ($tags as $tag) { + if (TAGS_MAX_COUNT > 0 && $count >= TAGS_MAX_COUNT) { + break; + } + + if (!preg_match(TAGS_CODE_REGEX, $tag)) { + continue; + } + + $tag_id = null; + + $stmt = $db->prepare("SELECT id FROM tags WHERE code = ?"); + $stmt->execute([$tag]); + + if ($row = $stmt->fetch()) { + $tag_id = $row["id"]; + } else { + $tag_id = bin2hex(random_bytes(16)); + $db->prepare("INSERT INTO tags(id, code) VALUES (?, ?)")->execute([$tag_id, $tag]); + } + + $db->prepare("INSERT INTO tag_assigns(tag_id, emote_id) VALUES (?, ?)")->execute([$tag_id, $id]); + + $count++; + array_push($tags_processed, $tag); + } +} + +$emote_data = [ + "id" => $id, + "code" => $code, + "visibility" => $visibility, + "uploaded_by" => match ($uploaded_by == null) { + true => null, + false => [ + "id" => $uploaded_by, + "username" => $uploader_name + ] + }, + "notes" => $notes, + "source" => $source, + "tags" => $tags_processed +]; + +if (ACCOUNT_LOG_ACTIONS && $uploaded_by != null) { + $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") + ->execute([ + $uploaded_by, + "EMOTE_CREATE", + json_encode([ + "emote" => $emote_data + ]) + ]); +} + + +$db = null; + +if (CLIENT_REQUIRES_JSON) { + json_response([ + "status_code" => 201, + "message" => null, + "data" => $emote_data + ], 201); + exit; +} + +header("Location: /emotes?id=$id", true, 307); \ No newline at end of file diff --git a/emotesets.php b/emotesets.php new file mode 100644 index 0000000..635f4c4 --- /dev/null +++ b/emotesets.php @@ -0,0 +1,164 @@ +query("SELECT * FROM emote_sets WHERE is_global = TRUE LIMIT 1", PDO::FETCH_ASSOC); + + if ($rows->rowCount()) { + $emote_set = $rows->fetch(); + } else { + generate_alert("/404.php", "Global emoteset is not found", 404); + exit; + } +} +// featured emoteset +else if ($id == "featured") { + $rows = $db->query("SELECT * FROM emote_sets WHERE is_featured = TRUE LIMIT 1", PDO::FETCH_ASSOC); + + if ($rows->rowCount()) { + $emote_set = $rows->fetch(); + } else { + generate_alert("/404.php", "Featured emoteset is not found", 404); + exit; + } +} +// connected emoteset +else if (isset($_GET["alias_id"])) { + $alias_id = $_GET["alias_id"]; + $platform = $_GET["platform"] ?? "twitch"; + + $stmt = $db->prepare("SELECT es.* FROM emote_sets es + INNER JOIN connections co ON co.alias_id = ? AND co.platform = ? + INNER JOIN acquired_emote_sets aes ON aes.user_id = co.user_id + WHERE aes.is_default = TRUE + "); + $stmt->execute([$alias_id, $platform]); + + if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $emote_set = $row; + } else { + generate_alert("/404.php", "Emoteset is not found for alias ID $alias_id ($platform)", 404); + exit; + } +} +// specified emoteset +else if (!empty($id)) { + $stmt = $db->prepare("SELECT es.* FROM emote_sets es WHERE es.id = ?"); + $stmt->execute([$id]); + + if ($row = $stmt->fetch()) { + $emote_set = $row; + } else { + generate_alert("/404.php", "Emoteset ID $id is not found", 404); + exit; + } +} + +$user_id = $_SESSION["user_id"] ?? ""; +$emote_sets = null; + +// fetching emotes +if ($emote_set) { + $emote_set = Emoteset::from_array_extended($emote_set, $user_id, $db); +} elseif (!EMOTESET_PUBLIC_LIST) { + generate_alert("/404.php", "The public list of emotesets is disabled", 403); + exit; +} else { + $emote_sets = []; + foreach ($db->query("SELECT * FROM emote_sets", PDO::FETCH_ASSOC) as $row) { + array_push($emote_sets, Emoteset::from_array_extended($row, $user_id, $db)); + } +} + +if (CLIENT_REQUIRES_JSON) { + if ($emote_sets != null) { + json_response([ + "status_code" => 200, + "message" => null, + "data" => $emote_sets + ]); + exit; + } else if ($emote_set != null) { + json_response([ + "status_code" => 200, + "message" => null, + "data" => $emote_set + ]); + exit; + } else { + json_response([ + "status_code" => 404, + "message" => "Emoteset(s) not found", + "data" => null + ], 404); + exit; + } +} +?> + + + + + <?php + $title = match ($emote_set == null) { + true => count($emote_sets) . ' emotesets', + false => "Emoteset - {$emote_set->name}", + }; + + echo "$title - " . INSTANCE_NAME; + ?> + + + + + + +
+
+ +
+
+
+ +
+ emotes); + } else { + echo 'Nothing found...'; + } + ?> +
+
+
+
+
+ + + \ No newline at end of file diff --git a/inbox.php b/inbox.php new file mode 100644 index 0000000..f7742d1 --- /dev/null +++ b/inbox.php @@ -0,0 +1,77 @@ +prepare("SELECT * FROM inbox_messages WHERE recipient_id = ? ORDER BY sent_at DESC"); +$stmt->execute([$_SESSION["user_id"]]); + +$messages = $stmt->fetchAll(PDO::FETCH_ASSOC); + +$stmt = $db->prepare("UPDATE inbox_messages SET has_read = true WHERE recipient_id = ?"); +$stmt->execute([$_SESSION["user_id"]]); + +?> + + + + + Inbox - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+
+ +
+ + + + + + + '; + + echo ''; + echo ''; + + echo ''; + + echo ''; + } + ?> +
Contents
' . $message["contents"]; + echo ' (' . format_timestamp(time() - strtotime($message["sent_at"])) . ' ago)'; + echo ''; + if ($message["link"]) { + echo '[ View ]'; + } + echo '
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..e0746c7 --- /dev/null +++ b/index.php @@ -0,0 +1,90 @@ + + + + + <?php echo INSTANCE_NAME ?> + + + + + +
+
+

<?php echo INSTANCE_NAME; ?>

+ +
+ Emotes + + + Emotesets + + + + Users + + + Upload'; + } ?> + Account + Chat clients & Tools +
+ +
+ + +
+ +
+ query("SELECT COUNT(*) FROM emotes WHERE visibility = 1"); + $count = $results->fetch()[0]; + + foreach (str_split($count) as $c) { + echo "\"\""; + } + ?> +
+ +

+ '; + echo sprintf("%s v%s", TINYEMOTES_NAME, TINYEMOTES_VERSION); + echo ' '; + + if (TINYEMOTES_COMMIT != null) { + echo '(Commit '; + echo substr(TINYEMOTES_COMMIT, 0, 7); + echo ')'; + } + ?> +

+
+
+ + + \ No newline at end of file diff --git a/public/404.php b/public/404.php deleted file mode 100644 index cd7e12e..0000000 --- a/public/404.php +++ /dev/null @@ -1,43 +0,0 @@ - - - - - (Error) <?php echo sprintf("%s - %s", $reason, INSTANCE_NAME) ?> - - - - - -
-
- -
-

- [ Back to home ] -
- -
- " alt=""> -
-
-
- - - \ No newline at end of file diff --git a/public/account/change_emoteset.php b/public/account/change_emoteset.php deleted file mode 100644 index c2fc209..0000000 --- a/public/account/change_emoteset.php +++ /dev/null @@ -1,36 +0,0 @@ -prepare("SELECT id FROM acquired_emote_sets WHERE emote_set_id = ? AND user_id = ?"); -$stmt->execute([$emote_set_id, $user_id]); - -if ($stmt->rowCount() == 0) { - generate_alert("/404.php", "You don't own emote set ID $emote_set_id", 403); - exit; -} - -$_SESSION["user_active_emote_set_id"] = $emote_set_id; - -header("Location: " . $_POST["redirect"] ?? "/"); \ No newline at end of file diff --git a/public/account/delete.php b/public/account/delete.php deleted file mode 100644 index ec8c040..0000000 --- a/public/account/delete.php +++ /dev/null @@ -1,50 +0,0 @@ -prepare("DELETE FROM user_badges WHERE user_id = ?")->execute([$id]); -} - -if ($profile) { - $db->prepare("DELETE FROM users WHERE id = ?")->execute([$id]); - - session_unset(); - session_destroy(); - - setcookie("secret_key", "", time() - 1000); -} - -header("Location: /account"); \ No newline at end of file diff --git a/public/account/index.php b/public/account/index.php deleted file mode 100644 index 2b9e790..0000000 --- a/public/account/index.php +++ /dev/null @@ -1,306 +0,0 @@ -prepare("SELECT id FROM users WHERE username = ?"); - $stmt->execute([$username]); - - if ($stmt->rowCount() == 0) { - $stmt = $db->prepare("UPDATE users SET username = ? WHERE id = ?"); - $stmt->execute([$username, $_SESSION["user_id"]]); - } else { - generate_alert("/account", "The username has already taken"); - exit; - } - } - - if (isset($_FILES["pfp"]) && !empty($_FILES["pfp"]["tmp_name"])) { - $pfp = $_FILES["pfp"]; - - if ( - $err = create_image_bundle( - $pfp["tmp_name"], - $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/avatars/" . $_SESSION["user_id"], - ACCOUNT_PFP_MAX_SIZE[0], - ACCOUNT_PFP_MAX_SIZE[1], - true, - true - ) - ) { - generate_alert("/account", sprintf("Error occurred while processing the profile picture (%d)", $err)); - exit; - } - } - - if (isset($_FILES["banner"]) && !empty($_FILES["banner"]["tmp_name"])) { - $banner = $_FILES["banner"]; - - if ( - $err = create_image_bundle( - $banner["tmp_name"], - $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/banners/" . $_SESSION["user_id"], - ACCOUNT_BANNER_MAX_SIZE[0], - ACCOUNT_BANNER_MAX_SIZE[1], - true, - true - ) - ) { - generate_alert("/account", sprintf("Error occurred while processing the profile banner (%d)", $err)); - exit; - } - } - - if (isset($_FILES["badge"]) && !empty($_FILES["badge"]["tmp_name"])) { - $badge = $_FILES["badge"]; - $badge_id = bin2hex(random_bytes(16)); - if ( - $err = create_image_bundle( - $badge["tmp_name"], - $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/badges/" . $badge_id, - ACCOUNT_BADGE_MAX_SIZE[0], - ACCOUNT_BADGE_MAX_SIZE[1], - true, - true - ) - ) { - generate_alert("/account", sprintf("Error occurred while processing the personal badge (%d)", $err)); - exit; - } - - $db->prepare("DELETE FROM user_badges WHERE badge_id != ? AND user_id = ?")->execute([$badge_id, $_SESSION["user_id"]]); - $db->prepare("INSERT INTO badges(id, uploaded_by) VALUES (?, ?)")->execute([$badge_id, $_SESSION["user_id"]]); - $db->prepare("INSERT INTO user_badges(badge_id, user_id) VALUES (?, ?)")->execute([$badge_id, $_SESSION["user_id"]]); - } - - $db = null; - generate_alert("/account", "Your changes have been applied!", 200); - exit; -} - -?> - - - - - Account management - <?php echo INSTANCE_NAME ?> - - - - - -
-
- - -
- -
-

Account management

- -
-

Profile

-

Profile picture

- '; - } else { - echo "

You don't have profile picture

"; - } - ?> -
- - - - Remove profile picture - - -
- -

Profile banner

- '; - } else { - echo "

You don't have profile banner

"; - } - ?> -
- - - - Remove banner - - -
- -

Personal badge

- prepare("SELECT badge_id FROM user_badges WHERE user_id = ?"); - $stmt->execute([$_SESSION["user_id"]]); - - $has_badge = false; - - if ($row = $stmt->fetch()) { - echo '
'; - echo ''; - echo ''; - echo ''; - echo '
'; - $has_badge = true; - } else { - echo "

You don't have personal badge

"; - } - ?> -
- - - - Remove badge - - -
- -

Username

- "> - - -
- -
- -
-

Connections

-
- prepare("SELECT * FROM connections WHERE user_id = ?"); - $stmt->execute([$_SESSION["user_id"]]); - $connections = $stmt->fetchAll(); - $platforms = ["twitch"]; - - foreach ($platforms as $platform) { - $connection = null; - $key = array_search($platform, array_column($connections, "platform")); - - if (!is_bool($key)) { - $connection = $connections[$key]; - } - - echo "
"; - echo "
"; - - echo "
"; - echo "" . ucfirst($platform) . ""; - - // TODO: check if connection is still alive - if ($connection == null) { - echo "Not connected"; - } else { - echo "" . $connection["alias_id"] . ""; - } - - echo "
"; - - echo "
"; - - if ($connection == null) { - echo ""; - echo 'Connect'; - echo ""; - } else { - echo ""; - echo 'Disconnect'; - echo ""; - } - - echo "
"; - } - ?> -
-
- -
- -
-

Security & Privacy

-
- prepare("SELECT CASE WHEN password IS NOT NULL THEN 1 ELSE 0 END as set_password FROM users WHERE id = ?"); - $stmt->execute([$_SESSION["user_id"]]); - $set_password = $stmt->fetch()[0]; - if ($set_password): ?> - - - - - -
-
- prepare("SELECT private_profile FROM user_preferences WHERE id = ?"); - $stmt->execute([$_SESSION["user_id"]]); - if (intval($stmt->fetch()[0]) == 1) { - echo 'checked'; - } - ?>> - -

Enabling this feature will hide your authorship of uploaded emotes and - actions.

- -
-
- - -
- - -
- - Delete - me -
-
-
-
- - - - - \ No newline at end of file diff --git a/public/account/login/index.php b/public/account/login/index.php deleted file mode 100644 index ace116d..0000000 --- a/public/account/login/index.php +++ /dev/null @@ -1,99 +0,0 @@ -prepare("SELECT secret_key, password FROM users WHERE username = ? AND password IS NOT NULL"); - $stmt->execute([$username]); - - if ($row = $stmt->fetch()) { - if (password_verify($password, $row["password"])) { - setcookie("secret_key", $row["secret_key"], $remember ? (time() + ACCOUNT_COOKIE_MAX_LIFETIME) : 0, "/"); - header("Location: /account"); - exit; - } else { - generate_alert("/account/login", "Passwords do not match!", 403); - exit; - } - } else { - generate_alert("/account/login", "User not found or is not accessable", 404); - exit; - } -} -?> - - - - - Login - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
- -
- -
-
-
- - -
-
- - -
-
- - -
-
- - - Register - -
-
-
-
- - -
- Login with Twitch -

Logging in via Twitch gives you the ability to use - emotes in your Twitch chat. -

-
- -
-
-
- - - \ No newline at end of file diff --git a/public/account/login/twitch.php b/public/account/login/twitch.php deleted file mode 100644 index 38fd6cc..0000000 --- a/public/account/login/twitch.php +++ /dev/null @@ -1,175 +0,0 @@ -prepare("SELECT c.id, - CASE WHEN ( - SELECT u.password FROM users u WHERE u.id = c.user_id - ) IS NOT NULL - THEN 1 ELSE 0 - END AS set_password - FROM connections c - WHERE c.user_id = ? - "); - $stmt->execute([$_SESSION["user_id"]]); - - if ($row = $stmt->fetch()) { - if ($row["set_password"]) { - $db->prepare("DELETE FROM connections WHERE user_id = ? AND platform = 'twitch'")->execute([$_SESSION["user_id"]]); - generate_alert("/account", "Successfully disconnected from Twitch!", 200); - } else { - generate_alert("/account", "You must set a password before deleting any connections", 403); - } - } else { - generate_alert("/account", "No Twitch connection found", 404); - } - exit; -} - -$client_id = TWITCH_CLIENT_ID; -$client_secret = TWITCH_SECRET_KEY; -$redirect_uri = TWITCH_REDIRECT_URI; - -if (isset($_GET["error"])) { - header("Location: /account/login"); - exit; -} - -if (!isset($_GET["code"])) { - header("Location: https://id.twitch.tv/oauth2/authorize?client_id=$client_id&redirect_uri=$redirect_uri&response_type=code"); - exit; -} - -$code = $_GET["code"]; - -// obtaining twitch token -$request = curl_init(); -curl_setopt($request, CURLOPT_URL, "https://id.twitch.tv/oauth2/token"); -curl_setopt($request, CURLOPT_POST, 1); -curl_setopt( - $request, - CURLOPT_POSTFIELDS, - "client_id=$client_id&client_secret=$client_secret&code=$code&grant_type=authorization_code&redirect_uri=$redirect_uri" -); -curl_setopt($request, CURLOPT_RETURNTRANSFER, true); - -$response = curl_exec($request); -curl_close($request); - -$response = json_decode($response, true); - -if (array_key_exists("status", $response)) { - header("Location: /account/login"); - exit; -} - -// identifying user -$request = curl_init(); -curl_setopt($request, CURLOPT_URL, "https://api.twitch.tv/helix/users"); -curl_setopt($request, CURLOPT_HTTPHEADER, [ - "Authorization: Bearer " . $response["access_token"], - "Client-Id: $client_id" -]); -curl_setopt($request, CURLOPT_RETURNTRANSFER, true); - -$twitch_user = curl_exec($request); -curl_close($request); - -$twitch_user = json_decode($twitch_user, true); - -if (empty($twitch_user["data"])) { - generate_alert("/account", "Failed to identify Twitch user", 500); - exit; -} - -$twitch_user = $twitch_user["data"][0]; - -// saving it -$twitch_access_token = $response["access_token"]; -$twitch_refresh_token = $response["refresh_token"]; -$twitch_expires_on = time() + intval($response["expires_in"]); - -// creating user if not exists -$stmt = $db->prepare("SELECT * FROM users u - INNER JOIN connections c ON c.alias_id = ? - WHERE c.user_id = u.id AND c.platform = 'twitch' -"); -$stmt->execute([$twitch_user["id"]]); - -$user_id = ""; -$user_secret_key = ""; -$user_name = ""; - -if ($row = $stmt->fetch()) { - if (isset($_SESSION["user_id"]) && $_SESSION["user_id"] != $row["id"]) { - generate_alert("/account", "There is another " . INSTANCE_NAME . " account associated with that Twitch account", 409); - exit; - } - - $user_name = $row["username"]; - $user_secret_key = $row["secret_key"]; - $user_id = $row["id"]; -} else { - $user_secret_key = generate_random_string(32); - $user_name = $twitch_user["login"]; - $user_id = bin2hex(random_bytes(16)); - - list($user_secret_key, $user_name, $user_id) = match (isset($_SESSION["user_id"])) { - true => [$_COOKIE["secret_key"], $_SESSION["user_name"], $_SESSION["user_id"]], - default => [generate_random_string(32), $twitch_user["login"], bin2hex(random_bytes(16))] - }; - - if (!isset($_SESSION["user_id"])) { - // checking for duplicates - $stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username = ?"); - $stmt->execute([$user_name]); - $duplicates = intval($stmt->fetch()[0]); - if ($duplicates > 0) { - $i = 1; - while (true) { - $stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username = ?"); - $stmt->execute(["$user_name$i"]); - - if ($stmt->fetch()[0] == 0) { - break; - } - - $i++; - } - $user_name .= $i; - } - - $stmt = $db->prepare("INSERT INTO users(id, username, secret_key) VALUES (?, ?, ?)"); - if (!$stmt->execute([$user_id, $user_name, $user_secret_key])) { - $db = null; - echo "Failed to create a user"; - exit; - } - } - - $stmt = $db->prepare("INSERT INTO connections(user_id, alias_id, platform, data) VALUES (?, ?, 'twitch', ?)"); - $stmt->execute([ - $user_id, - $twitch_user["id"], - sprintf("%s:%s:%s", $twitch_access_token, $twitch_refresh_token, $twitch_expires_on) - ]); -} - -$_SESSION["user_id"] = $user_id; -$_SESSION["user_name"] = $user_name; -setcookie("secret_key", $user_secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/"); - -$db = null; - -header("Location: /account"); \ No newline at end of file diff --git a/public/account/register.php b/public/account/register.php deleted file mode 100644 index 1da89a0..0000000 --- a/public/account/register.php +++ /dev/null @@ -1,111 +0,0 @@ - $username_length || $username_length > ACCOUNT_USERNAME_LENGTH[1]) { - generate_alert("/account/register.php", sprintf("Username must be between %d-%d characters long", ACCOUNT_USERNAME_LENGTH[0], ACCOUNT_USERNAME_LENGTH[1])); - exit; - } - - if (!preg_match(ACCOUNT_USERNAME_REGEX, $username)) { - generate_alert("/account/register.php", "Bad username"); - exit; - } - - $password = $_POST["password"]; - if (ACCOUNT_PASSWORD_MIN_LENGTH > strlen($password)) { - generate_alert("/account/register.php", "Password must be at least " . ACCOUNT_PASSWORD_MIN_LENGTH . " characters"); - exit; - } - - $db = new PDO(DB_URL, DB_USER, DB_PASS); - - $stmt = $db->prepare("SELECT id FROM users WHERE username = ?"); - $stmt->execute([$username]); - - if ($stmt->rowCount() != 0) { - generate_alert("/account/register.php", "The username has already been taken"); - exit; - } - - $secret_key = generate_random_string(ACCOUNT_SECRET_KEY_LENGTH); - $password = password_hash($password, PASSWORD_DEFAULT); - - $id = bin2hex(random_bytes(16)); - - $stmt = $db->prepare("INSERT INTO users(id, username, password, secret_key) VALUES (?, ?, ?, ?)"); - $stmt->execute([$id, $username, $password, $secret_key]); - - setcookie("secret_key", $secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/"); - header("Location: /account"); - exit; -} -?> - - - - - Register an account - <?php echo INSTANCE_NAME ?> - - - - - -
-
- - -
- -
- -
-
-
- - -
-
- - -
-
- -
-
-

- Since doesn't require email and password reset via email is - not supported, please remember your passwords! -

-
-
-
-
-
- - - \ No newline at end of file diff --git a/public/account/security.php b/public/account/security.php deleted file mode 100644 index 5545b60..0000000 --- a/public/account/security.php +++ /dev/null @@ -1,52 +0,0 @@ -prepare("SELECT * FROM users WHERE id = ?"); -$stmt->execute([$_SESSION["user_id"]]); - -$user = $stmt->fetch(); -$current_password = $_POST["password-current"] ?? ""; - -if ($user["password"] != null && !password_verify($current_password, $user["password"])) { - generate_alert("/account", "Password is required to apply changes in 'Security' section"); - exit; -} - -if (!empty($_POST["password-new"])) { - $password = $_POST["password-new"]; - if (ACCOUNT_PASSWORD_MIN_LENGTH > strlen($password)) { - generate_alert("/account", "Your password must be at least " . ACCOUNT_PASSWORD_MIN_LENGTH . " characters"); - exit; - } - - $db->prepare("UPDATE users SET password = ? WHERE id = ?") - ->execute([password_hash($password, PASSWORD_DEFAULT), $user["id"]]); -} - -$private_profile = (int) (intval($_POST["make-private"] ?? "0") == 1); - -$db->prepare("UPDATE user_preferences SET private_profile = ? WHERE id = ?") - ->execute([$private_profile, $user["id"]]); - -if (intval($_POST["signout-everywhere"] ?? "0") == 1) { - $db->prepare("UPDATE users SET secret_key = ? WHERE id = ?") - ->execute([generate_random_string(ACCOUNT_SECRET_KEY_LENGTH), $_SESSION["user_id"]]); - - session_unset(); - session_destroy(); - - setcookie("secret_key", "", time() - 1000); -} - -generate_alert("/account", "Your changes have been applied!", 200); \ No newline at end of file diff --git a/public/account/signout.php b/public/account/signout.php deleted file mode 100644 index f971d4a..0000000 --- a/public/account/signout.php +++ /dev/null @@ -1,16 +0,0 @@ -prepare("SELECT - u.id, u.username, - r.name AS role_name, - r.badge_id AS role_badge_id, - ub.badge_id AS custom_badge_id, - co.alias_id AS connection_alias_id, - co.platform AS connection_platform - FROM users u - JOIN role_assigns ra ON ra.user_id = u.id - JOIN roles r ON r.id = ra.role_id - LEFT JOIN user_badges ub ON ub.user_id = u.id - LEFT JOIN connections co ON co.user_id = u.id - WHERE r.badge_id IS NOT NULL OR ub.badge_id IS NOT NULL -"); -$stmt->execute(); - -$rows = $stmt->fetchAll(); - -$badges = []; - -foreach ($rows as $row) { - $badge = [ - "id" => $row["id"], - "username" => $row["username"], - "role" => Role::from_array($row), - "custom_badge" => Badge::from_array($row, "custom"), - "connection" => match (isset($row["connection_alias_id"], $row["connection_platform"])) { - true => [ - "alias_id" => $row["connection_alias_id"], - "platform" => $row["connection_platform"] - ], - false => null - } - ]; - - array_push($badges, $badge); -} - -json_response([ - "status_code" => 200, - "message" => null, - "data" => $badges -]); \ No newline at end of file diff --git a/public/captcha.php b/public/captcha.php deleted file mode 100644 index b454b7d..0000000 --- a/public/captcha.php +++ /dev/null @@ -1,59 +0,0 @@ - 200, - "message" => "Solved!", - "data" => null - ]); - } else { - echo json_response([ - "status_code" => 400, - "message" => "Wrong answer!", - "data" => null - ], 400); - } - exit; -} - -$file_folder = $_SERVER["DOCUMENT_ROOT"] . '/static/img/captcha'; - -if (!CAPTCHA_ENABLE || ($_SESSION["captcha_solved"] ?? false) || !is_dir($file_folder)) { - $_SESSION["captcha_solved"] = true; - echo json_response([ - "status_code" => 200, - "message" => "No need to solve captcha", - "data" => null - ]); - exit; -} - -$files = scandir($file_folder); -array_splice($files, 0, 2); - -$filename = $files[random_int(0, count($files) - 1)]; -$filename = basename($filename, ".png"); - -$_SESSION["captcha_word"] = $filename; - -$image = generate_image_captcha( - CAPTCHA_SIZE[0], - CAPTCHA_SIZE[1], - random_int(1, 3), - $filename, - $file_folder -); - -echo json_response([ - "status_code" => 200, - "message" => null, - "data" => $image -]); \ No newline at end of file diff --git a/public/emotes/delete.php b/public/emotes/delete.php deleted file mode 100644 index 6252e45..0000000 --- a/public/emotes/delete.php +++ /dev/null @@ -1,47 +0,0 @@ -prepare("SELECT uploaded_by, code FROM emotes WHERE id = ?"); -$stmt->execute([$emote_id]); - -if ($row = $stmt->fetch()) { - if ($row["uploaded_by"] === $user_id) { - $unlink = intval($_POST["unlink"] ?? "0") == 1; - - if ($unlink) { - $stmt = $db->prepare("UPDATE emotes SET uploaded_by = NULL WHERE id = ? AND uploaded_by = ?"); - $stmt->execute([$emote_id, $user_id]); - generate_alert("/emotes/?id=$emote_id", 'Your authorship has been removed for the emote "' . $row["code"] . '"', 200); - } else { - $stmt = $db->prepare("DELETE FROM emotes WHERE id = ? AND uploaded_by = ?"); - $stmt->execute([$emote_id, $user_id]); - - $path = $_SERVER["DOCUMENT_ROOT"] . "/static/userdata/emotes/$emote_id"; - array_map("unlink", glob("$path/*.*")); - rmdir($path); - - generate_alert("/emotes", 'Emote "' . $row["code"] . '" has been removed from the servers', 200); - } - } else { - generate_alert("/emotes", "You don't own the emote \"" . $row["code"] . "\"", 403); - } -} else { - generate_alert("/emotes", "Emote ID $emote_id not found", 404); -} \ No newline at end of file diff --git a/public/emotes/index.php b/public/emotes/index.php deleted file mode 100644 index af14120..0000000 --- a/public/emotes/index.php +++ /dev/null @@ -1,546 +0,0 @@ -prepare("SELECT e.id, e.code, e.created_at, e.source, e.visibility, - COALESCE(COUNT(r.rate), 0) as total_rating, - COALESCE(ROUND(AVG(r.rate), 2), 0) AS average_rating, - CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by - FROM emotes e - LEFT JOIN user_preferences up ON up.id = e.uploaded_by - LEFT JOIN ratings AS r ON r.emote_id = e.id - WHERE e.id = ? - LIMIT 1 - "); - $stmt->execute([$user_id, $id]); - - $row = $stmt->fetch(); - - if ($row["id"]) { - // fetching emote tags - $stmt = $db->prepare("SELECT t.code FROM tags t - INNER JOIN tag_assigns ta ON ta.emote_id = ? - WHERE t.id = ta.tag_id - "); - $stmt->execute([$row["id"]]); - $tags = $stmt->fetchAll(PDO::FETCH_ASSOC); - $tags = array_column($tags, "code"); - - $row["tags"] = $tags; - $row["ext"] = "webp"; - $emote = Emote::from_array_with_user($row, $db); - } else { - generate_alert("/404.php", "Emote ID $id does not exists", 404); - exit; - } -} -// fetching all emotes -else { - $sort = $_GET["sort"] ?? "high_ratings"; - $sort = match ($sort) { - "low_ratings" => "rating ASC", - "recent" => "e.created_at DESC", - "oldest" => "e.created_at ASC", - default => "rating DESC" - }; - $page = max(1, intval($_GET["p"] ?? "1")); - $limit = 50; - $offset = ($page - 1) * $limit; - $search = $_GET["q"] ?? ""; - - // fetching emotes - $stmt = $db->prepare("SELECT e.*, - CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, - CASE WHEN EXISTS ( - SELECT 1 - FROM emote_set_contents ec - INNER JOIN emote_sets es ON es.id = ec.emote_set_id - JOIN acquired_emote_sets aes ON aes.emote_set_id = es.id - WHERE ec.emote_id = e.id AND es.id = ? - ) THEN 1 ELSE 0 END AS is_in_user_set, COALESCE(COUNT(r.rate), 0) AS rating - FROM emotes e - LEFT JOIN user_preferences up ON up.id = e.uploaded_by - LEFT JOIN ratings AS r ON r.emote_id = e.id - LEFT JOIN tag_assigns ta ON ta.emote_id = e.id - LEFT JOIN tags t ON t.id = ta.tag_id - WHERE (t.code = ? OR e.code LIKE ?) AND e.visibility = 1 - GROUP BY - e.id, e.code, e.created_at - ORDER BY $sort - LIMIT ? OFFSET ? - "); - - $sql_search = "%$search%"; - $user_emote_set_id = $_SESSION["user_active_emote_set_id"] ?? ""; - - $stmt->bindParam(1, $user_id, PDO::PARAM_STR); - $stmt->bindParam(2, $user_emote_set_id, PDO::PARAM_STR); - $stmt->bindParam(3, $search, PDO::PARAM_STR); - $stmt->bindParam(4, $sql_search, PDO::PARAM_STR); - $stmt->bindParam(5, $limit, PDO::PARAM_INT); - $stmt->bindParam(6, $offset, PDO::PARAM_INT); - - $stmt->execute(); - - $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); - $emotes = []; - - foreach ($rows as $row) { - array_push($emotes, Emote::from_array_with_user($row, $db)); - } - - $total_emotes = count($emotes); - $total_pages = ceil($total_emotes / $limit); -} - -if (CLIENT_REQUIRES_JSON) { - json_response([ - "status_code" => 200, - "message" => null, - "data" => $emotes ?? $emote - ]); - exit; -} -?> - - - - - <?php - echo ($emote != null ? "Emote " . $emote->get_code() : "Emotes") . ' - ' . INSTANCE_NAME - ?> - - - - - -
-
- - -
- -
- -
- - -
-
- '; - - echo ''; - - $path = $_SERVER["DOCUMENT_ROOT"] . '/static/userdata/emotes/' . $emote->get_id() . "/{$size}x.webp"; - - echo '
'; - - if ($file_size = filesize($path)) { - $kb = sprintf("%.2f", $file_size / 1024); - echo "

{$kb}KB

"; - } - - if ($image_size = getimagesize($path)) { - echo "

$image_size[0]x$image_size[1]

"; - } - - echo '
'; - } - ?> -
-
- -
- -
- prepare("SELECT id, code FROM emote_set_contents WHERE emote_set_id = ? AND emote_id = ?"); - $stmt->execute([$_SESSION["user_active_emote_set_id"], $emote->get_id()]); - - $added = false; - - if ($row = $stmt->fetch()) { - $added = true; - $emote_current_name = $row["code"] ?? $emote->get_code(); - } - } - - if (isset($_SESSION["user_role"]) && $_SESSION["user_role"]["permission_emoteset_own"]) { - echo '' ?> -
- - " style="display: none;"> - - - -
-
- - " style="display: none;"> - - - - - - - -
- - - get_uploaded_by() === $_SESSION["user_id"]): ?> -
- - -
-
- - - -
- -
-
- prepare("SELECT rate FROM ratings WHERE user_id = ? AND emote_id = ?"); - $stmt->execute([$_SESSION["user_id"], $id]); - - if ($row = $stmt->fetch()) { - echo 'You gave '; - } else { - foreach (RATING_NAMES as $key => $value) { - echo '
'; - echo ''; - echo ""; - echo '
'; - } - } - } - if (REPORTS_ENABLE && $_SESSION["user_role"]["permission_report"]) { - echo "Report emote"; - } - } - ?> -
- -

Log in to get additional features...

- -
- -
- - get_tags())): ?> - - - - - - - - - - prepare("SELECT u.id, a.created_at FROM users u - INNER JOIN mod_actions a ON a.emote_id = ? - WHERE u.id = a.user_id"); - $stmt->execute([$emote->get_id()]); - - if ($row = $stmt->fetch()) { - $approver = User::get_user_by_id($db, $row["id"]); - - echo ''; - } - - if (RATING_ENABLE): ?> - - - get_rating()["total"] < RATING_EMOTE_MIN_VOTES) { - echo ''; - } else { - - $rating = $emote->get_rating()["average"]; - - // TODO: make it customizable - list($rating_classname, $rating_name) = match (true) { - in_range($rating, 0.75, 1.0) => [ - "gemerald", - " - - Shiny Gemerald! - - - - " - ], - in_range($rating, 0.25, 0.75) => ["gem", " Gem "], - in_range($rating, -0.25, 0.25) => ["iron", "Iron"], - in_range($rating, -0.75, -0.25) => ["coal", " Coal "], - in_range($rating, -1.0, -0.75) => [ - "brimstone", - " - - - - !!!AVOID THIS CANCER-GIVING BRIMSTONE!!! - - - - " - ] - }; - - echo ''; - } - ?> - - - - - - - get_source()): ?> - - - - - -
Tags - get_tags() as $tag) { - echo "$tag "; - } - ?> -
Uploaderget_uploaded_by()) { - $u = $emote->get_uploaded_by(); - $show_private_badge = $u->private_profile; - - $username = $u->username; - $link = "/users.php?id={$u->id}"; - $badge = $u->role; - $custom_badge = $u->custom_badge; - } - - echo ""; - echo $username; - echo ""; - - if ($show_private_badge) { - echo " (Private)"; - } - - if ($badge && $badge->badge) { - echo " ## {$badge->name}"; - } - - if ($custom_badge) { - echo " "; - } - - echo ', get_created_at()); - echo ' UTC">about ' . format_timestamp(time() - $emote->get_created_at()) . " ago"; - ?>
Approver'; - echo "{$approver->username}"; - - if ($approver->role && $approver->role->badge) { - echo " ## {$approver->role->name}"; - } - - if ($approver->custom_badge) { - echo " "; - } - - echo ', '; - echo format_timestamp(strtotime($row["created_at"]) - $emote->get_created_at()) . ' after upload'; - echo '
RatingNot rated (' . $emote->get_rating()["total"] . ')'; - echo "$rating_name"; - echo ' (' . $emote->get_rating()["total"] . ')'; - echo '
Visibilityget_visibility()) { - case 0: - echo 'Unlisted'; - break; - case 1: - echo 'Public'; - break; - case 2: - echo 'Pending approval (unlisted for a moment)'; - break; - default: - echo 'N/A'; - break; - } - ?>
Source - get_source() ?> -
-
- -
-
- prepare("SELECT users.id, users.username - FROM users - INNER JOIN emote_sets AS es ON es.owner_id = users.id - INNER JOIN emote_set_contents AS ec ON ec.emote_set_id = es.id - INNER JOIN acquired_emote_sets AS aes ON aes.emote_set_id = es.id - WHERE ec.emote_id = ? AND aes.is_default = TRUE"); - - $stmt->execute([$emote->get_id()]); - $count = $stmt->rowCount(); - - $db = null; - - if ($count > 0) { - echo "

Added in $count channels

"; - } else { - echo "No one has added this emote yet... :'("; - } - ?> -
- fetch()) { - echo '' . $row["username"] . ''; - } - ?> -
-
- -
- -
- 1) { - echo '' ?> -
-
- -
- - -
- - - - \ No newline at end of file diff --git a/public/emotes/rate.php b/public/emotes/rate.php deleted file mode 100644 index 1e8eb67..0000000 --- a/public/emotes/rate.php +++ /dev/null @@ -1,63 +0,0 @@ -prepare("SELECT id FROM emotes WHERE id = ?"); -$stmt->execute([$id]); -if ($stmt->rowCount() != 1) { - generate_alert("/emotes", "Emote ID $id does not exist", 404); - exit; -} - -// checking if user has already given a rate -$stmt = $db->prepare("SELECT id FROM ratings WHERE user_id = ? AND emote_id = ?"); -$stmt->execute([$_SESSION["user_id"], $id]); -if ($stmt->rowCount() != 0) { - generate_alert("/emotes?id=$id", "You have already given a rate for this emote!", 403); - exit; -} - -// giving a rate -$stmt = $db->prepare("INSERT INTO ratings(user_id, emote_id, rate) VALUES (?, ?, ?)"); -$stmt->execute([$_SESSION["user_id"], $id, clamp($rate, -2, 2)]); - -if (CLIENT_REQUIRES_JSON) { - $stmt = $db->prepare("SELECT * FROM ratings WHERE id = ?"); - $stmt->execute([$db->lastInsertId()]); - - json_response([ - "status_code" => 200, - "message" => "Rated!", - "data" => $stmt->fetch(PDO::FETCH_ASSOC) - ]); - exit; -} - -generate_alert("/emotes?id=$id", "Rated!", 200); diff --git a/public/emotes/setmanip.php b/public/emotes/setmanip.php deleted file mode 100644 index 129790d..0000000 --- a/public/emotes/setmanip.php +++ /dev/null @@ -1,138 +0,0 @@ -prepare("SELECT id, code, uploaded_by, visibility, created_at FROM emotes WHERE id = ?"); -$stmt->execute([$emote_id]); -if ($stmt->rowCount() == 0) { - generate_alert("/emotes", "Emote not found", 404); - exit; -} -$emote = $stmt->fetch(PDO::FETCH_ASSOC); - -$user_id = $_SESSION["user_id"]; -$emote_set_id = $_POST["emote_set_id"]; - -// checking emote set -$stmt = $db->prepare("SELECT id FROM acquired_emote_sets WHERE emote_set_id = ? AND user_id = ?"); -$stmt->execute([$emote_set_id, $user_id]); - -if ($stmt->rowCount() == 0) { - generate_alert("/404.php", "You don't own emote set ID $emote_set_id", 403); - exit; -} - -// inserting emote -$stmt = $db->prepare("SELECT id FROM emote_set_contents WHERE emote_set_id = ? AND emote_id = ?"); -$stmt->execute([$emote_set_id, $emote_id]); - -$action = $_POST["action"]; -$payload = [ - "emote" => $emote, - "emoteset" => $_SESSION["user_active_emote_set"] -]; - -switch ($action) { - case "add": { - if ($stmt->rowCount() != 0) { - generate_alert("/emotes?id=$emote_id", "This emote has been already added!"); - exit; - } - - $stmt = $db->prepare("INSERT INTO emote_set_contents(emote_set_id, emote_id, added_by) VALUES (?, ?, ?)"); - $stmt->execute([$emote_set_id, $emote_id, $user_id]); - - if (ACCOUNT_LOG_ACTIONS) { - $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") - ->execute([$user_id, "EMOTESET_ADD", json_encode($payload)]); - } - - $db = null; - - generate_alert("/emotes?id=$emote_id", "This emote has been added to your set. Enjoy!", 200); - break; - } - case "remove": { - if ($row = $stmt->fetch()) { - $stmt = $db->prepare("DELETE FROM emote_set_contents WHERE id = ?"); - $stmt->execute([$row["id"]]); - } else { - generate_alert("/emotes?id=$emote_id", "This emote wasn't added!"); - $db = null; - exit; - } - - if (ACCOUNT_LOG_ACTIONS) { - $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") - ->execute([$user_id, "EMOTESET_REMOVE", json_encode($payload)]); - } - - $db = null; - - generate_alert("/emotes?id=$emote_id", "This emote has been removed from your set.", 200); - break; - } - case "alias": { - if (!isset($_POST["value"])) { - generate_alert("/emotes?id=$emote_id", "No value field"); - exit; - } - - $value = str_safe($_POST["value"], EMOTE_NAME_MAX_LENGTH); - - $stmt = $db->prepare("SELECT esc.code AS alias_code, e.code FROM emote_set_contents esc - INNER JOIN emotes e ON e.id = esc.emote_id - WHERE esc.emote_set_id = ? AND esc.emote_id = ?"); - $stmt->execute([$emote_set_id, $emote_id]); - - if (empty($value)) { - $value = null; - - if ($row = $stmt->fetch()) { - $payload["emote"]["original_code"] = $row["alias_code"]; - $payload["emote"]["code"] = $row["code"]; - } - } else { - $row = $stmt->fetch(); - $payload["emote"]["original_code"] = $row["alias_code"] ?? $row["code"]; - $payload["emote"]["code"] = $value; - } - - $stmt = $db->prepare("UPDATE emote_set_contents SET code = ? WHERE emote_set_id = ? AND emote_id = ?"); - $stmt->execute([$value, $emote_set_id, $emote_id]); - - if (ACCOUNT_LOG_ACTIONS) { - $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") - ->execute([$user_id, "EMOTESET_ALIAS", json_encode($payload)]); - } - - $db = null; - - generate_alert("/emotes?id=$emote_id", "Updated emote name!", 200); - break; - } - default: { - generate_alert("/emotes?id=$emote_id", "Unknown action"); - break; - } -} \ No newline at end of file diff --git a/public/emotes/upload.php b/public/emotes/upload.php deleted file mode 100644 index 644e4b6..0000000 --- a/public/emotes/upload.php +++ /dev/null @@ -1,552 +0,0 @@ -prepare("DELETE FROM emotes WHERE id = ?"); - $stmt->execute([$id]); - $db = null; - - array_map("unlink", glob("$path/*.*")); - rmdir($path); -} - -include "../../src/utils.php"; -include "../../src/images.php"; - -$max_width = EMOTE_MAX_SIZE[0]; -$max_height = EMOTE_MAX_SIZE[1]; - -if ($_SERVER['REQUEST_METHOD'] != "POST") { - include "../../src/partials.php"; - - echo '' ?> - - - - Upload an emote - <?php echo INSTANCE_NAME ?> - - - - - -
-
- - - -
-
-
- -
-
-

Image*

- - - - - -
- - -
- -

Emote name*

- - -
- -
-

test

-
- - - - - - - - - - - - - - - -
Emote source: -
Tags [?]: -
- -
- - -
- - -
-
-
- - -
- - -
-
-
- - - - - - - prepare("INSERT INTO emotes(id, code, notes, source, uploaded_by, visibility) VALUES (?, ?, ?, ?, ?, ?)"); -$stmt->execute([$id, $code, $notes, $source, $uploaded_by, $visibility]); - -$path = "../static/userdata/emotes/$id"; - -if (!is_dir($path)) { - mkdir($path, 0777, true); -} - -if ($is_manual) { - $image_1x = $_FILES["file-1x"]; - $image_2x = $_FILES["file-2x"]; - $image_3x = $_FILES["file-3x"]; - - $file_1x = does_file_meet_requirements($image_1x["tmp_name"], $max_width / 4, $max_height / 4); - $file_2x = does_file_meet_requirements($image_2x["tmp_name"], $max_width / 2, $max_height / 2); - $file_3x = does_file_meet_requirements($image_3x["tmp_name"], $max_width, $max_height); - - if (!$file_1x[0] || !$file_2x[0] || !$file_3x[0]) { - generate_alert("/emotes/upload.php", "Files don't meet requirements"); - abort_upload($path, $db, $id); - exit; - } - - if ( - !move_uploaded_file($image_1x["tmp_name"], "$path/1x.$file_1x[1]") || - !move_uploaded_file($image_2x["tmp_name"], "$path/2x.$file_2x[1]") || - !move_uploaded_file($image_3x["tmp_name"], "$path/3x.$file_3x[1]") - ) { - generate_alert("/emotes/upload.php", "Failed to move the uploaded files"); - abort_upload($path, $db, $id); - exit; - } -} else { - $image = $_FILES["file"]; - // resizing the image - if ($err = create_image_bundle($image["tmp_name"], $path, $max_width, $max_height)) { - generate_alert("/emotes/upload.php", "Error occurred while processing images ($err)", 500); - abort_upload($path, $db, $id); - exit; - } - - if (EMOTE_STORE_ORIGINAL) { - $ext = get_file_extension($image["tmp_name"]) ?? ""; - move_uploaded_file($image["tmp_name"], "$path/original.$ext"); - } -} - -$tags = str_safe($_POST["tags"] ?? "", null); -$tags_processed = []; - -if (!empty($tags) && TAGS_ENABLE) { - $tags = explode(" ", $tags); - - $count = 0; - - foreach ($tags as $tag) { - if (TAGS_MAX_COUNT > 0 && $count >= TAGS_MAX_COUNT) { - break; - } - - if (!preg_match(TAGS_CODE_REGEX, $tag)) { - continue; - } - - $tag_id = null; - - $stmt = $db->prepare("SELECT id FROM tags WHERE code = ?"); - $stmt->execute([$tag]); - - if ($row = $stmt->fetch()) { - $tag_id = $row["id"]; - } else { - $tag_id = bin2hex(random_bytes(16)); - $db->prepare("INSERT INTO tags(id, code) VALUES (?, ?)")->execute([$tag_id, $tag]); - } - - $db->prepare("INSERT INTO tag_assigns(tag_id, emote_id) VALUES (?, ?)")->execute([$tag_id, $id]); - - $count++; - array_push($tags_processed, $tag); - } -} - -$emote_data = [ - "id" => $id, - "code" => $code, - "visibility" => $visibility, - "uploaded_by" => match ($uploaded_by == null) { - true => null, - false => [ - "id" => $uploaded_by, - "username" => $uploader_name - ] - }, - "notes" => $notes, - "source" => $source, - "tags" => $tags_processed -]; - -if (ACCOUNT_LOG_ACTIONS && $uploaded_by != null) { - $db->prepare("INSERT INTO actions(user_id, action_type, action_payload) VALUES (?, ?, ?)") - ->execute([ - $uploaded_by, - "EMOTE_CREATE", - json_encode([ - "emote" => $emote_data - ]) - ]); -} - - -$db = null; - -if (CLIENT_REQUIRES_JSON) { - json_response([ - "status_code" => 201, - "message" => null, - "data" => $emote_data - ], 201); - exit; -} - -header("Location: /emotes?id=$id", true, 307); \ No newline at end of file diff --git a/public/emotesets.php b/public/emotesets.php deleted file mode 100644 index 635f4c4..0000000 --- a/public/emotesets.php +++ /dev/null @@ -1,164 +0,0 @@ -query("SELECT * FROM emote_sets WHERE is_global = TRUE LIMIT 1", PDO::FETCH_ASSOC); - - if ($rows->rowCount()) { - $emote_set = $rows->fetch(); - } else { - generate_alert("/404.php", "Global emoteset is not found", 404); - exit; - } -} -// featured emoteset -else if ($id == "featured") { - $rows = $db->query("SELECT * FROM emote_sets WHERE is_featured = TRUE LIMIT 1", PDO::FETCH_ASSOC); - - if ($rows->rowCount()) { - $emote_set = $rows->fetch(); - } else { - generate_alert("/404.php", "Featured emoteset is not found", 404); - exit; - } -} -// connected emoteset -else if (isset($_GET["alias_id"])) { - $alias_id = $_GET["alias_id"]; - $platform = $_GET["platform"] ?? "twitch"; - - $stmt = $db->prepare("SELECT es.* FROM emote_sets es - INNER JOIN connections co ON co.alias_id = ? AND co.platform = ? - INNER JOIN acquired_emote_sets aes ON aes.user_id = co.user_id - WHERE aes.is_default = TRUE - "); - $stmt->execute([$alias_id, $platform]); - - if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $emote_set = $row; - } else { - generate_alert("/404.php", "Emoteset is not found for alias ID $alias_id ($platform)", 404); - exit; - } -} -// specified emoteset -else if (!empty($id)) { - $stmt = $db->prepare("SELECT es.* FROM emote_sets es WHERE es.id = ?"); - $stmt->execute([$id]); - - if ($row = $stmt->fetch()) { - $emote_set = $row; - } else { - generate_alert("/404.php", "Emoteset ID $id is not found", 404); - exit; - } -} - -$user_id = $_SESSION["user_id"] ?? ""; -$emote_sets = null; - -// fetching emotes -if ($emote_set) { - $emote_set = Emoteset::from_array_extended($emote_set, $user_id, $db); -} elseif (!EMOTESET_PUBLIC_LIST) { - generate_alert("/404.php", "The public list of emotesets is disabled", 403); - exit; -} else { - $emote_sets = []; - foreach ($db->query("SELECT * FROM emote_sets", PDO::FETCH_ASSOC) as $row) { - array_push($emote_sets, Emoteset::from_array_extended($row, $user_id, $db)); - } -} - -if (CLIENT_REQUIRES_JSON) { - if ($emote_sets != null) { - json_response([ - "status_code" => 200, - "message" => null, - "data" => $emote_sets - ]); - exit; - } else if ($emote_set != null) { - json_response([ - "status_code" => 200, - "message" => null, - "data" => $emote_set - ]); - exit; - } else { - json_response([ - "status_code" => 404, - "message" => "Emoteset(s) not found", - "data" => null - ], 404); - exit; - } -} -?> - - - - - <?php - $title = match ($emote_set == null) { - true => count($emote_sets) . ' emotesets', - false => "Emoteset - {$emote_set->name}", - }; - - echo "$title - " . INSTANCE_NAME; - ?> - - - - - - -
-
- -
-
-
- -
- emotes); - } else { - echo 'Nothing found...'; - } - ?> -
-
-
-
-
- - - \ No newline at end of file diff --git a/public/inbox.php b/public/inbox.php deleted file mode 100644 index f7742d1..0000000 --- a/public/inbox.php +++ /dev/null @@ -1,77 +0,0 @@ -prepare("SELECT * FROM inbox_messages WHERE recipient_id = ? ORDER BY sent_at DESC"); -$stmt->execute([$_SESSION["user_id"]]); - -$messages = $stmt->fetchAll(PDO::FETCH_ASSOC); - -$stmt = $db->prepare("UPDATE inbox_messages SET has_read = true WHERE recipient_id = ?"); -$stmt->execute([$_SESSION["user_id"]]); - -?> - - - - - Inbox - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
-
- -
- - - - - - - '; - - echo ''; - echo ''; - - echo ''; - - echo ''; - } - ?> -
Contents
' . $message["contents"]; - echo ' (' . format_timestamp(time() - strtotime($message["sent_at"])) . ' ago)'; - echo ''; - if ($message["link"]) { - echo '[ View ]'; - } - echo '
-
-
-
-
-
- - - \ No newline at end of file diff --git a/public/index.php b/public/index.php deleted file mode 100644 index e0746c7..0000000 --- a/public/index.php +++ /dev/null @@ -1,90 +0,0 @@ - - - - - <?php echo INSTANCE_NAME ?> - - - - - -
-
-

<?php echo INSTANCE_NAME; ?>

- -
- Emotes - - - Emotesets - - - - Users - - - Upload'; - } ?> - Account - Chat clients & Tools -
- -
- - -
- -
- query("SELECT COUNT(*) FROM emotes WHERE visibility = 1"); - $count = $results->fetch()[0]; - - foreach (str_split($count) as $c) { - echo "\"\""; - } - ?> -
- -

- '; - echo sprintf("%s v%s", TINYEMOTES_NAME, TINYEMOTES_VERSION); - echo ' '; - - if (TINYEMOTES_COMMIT != null) { - echo '(Commit '; - echo substr(TINYEMOTES_COMMIT, 0, 7); - echo ')'; - } - ?> -

-
-
- - - \ No newline at end of file diff --git a/public/report/index.php b/public/report/index.php deleted file mode 100644 index e5014c4..0000000 --- a/public/report/index.php +++ /dev/null @@ -1,124 +0,0 @@ -prepare("SELECT * FROM reports WHERE id = ? AND sender_id = ?"); - $stmt->execute([$report_id, $_SESSION["user_id"]]); - - if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $report = $row; - - if (CLIENT_REQUIRES_JSON) { - json_response([ - "status_code" => 201, - "message" => null, - "data" => $report - ], 201); - exit; - } - } else { - generate_alert("/report", "Report ID #" . $_GET["id"] . " not found or not accessable"); - exit; - } -} - -$contents = ""; - -if ($contents == "") { - if (isset($_GET["user_id"])) { - $contents = "Hi! I want to report user ID #" . $_GET["user_id"] . " because..."; - } else if (isset($_GET["emote_id"])) { - $contents = "Hi! I want to report emote ID #" . $_GET["emote_id"] . " because..."; - } -} -?> - - - - - <?php echo ($report == null ? "Send a message to MODS" : "A message to MODS") . ' - ' . INSTANCE_NAME ?> - - - - - - -
-
- - -
- -
- - -
-
- - -
-
-
- -
-
-
-

Reported ago

-

Status: - Unresolved" : "Resolved" ?> -

-
- -
- -
- -
-
- - -
- -
-
- - - \ No newline at end of file diff --git a/public/report/list.php b/public/report/list.php deleted file mode 100644 index f02731a..0000000 --- a/public/report/list.php +++ /dev/null @@ -1,81 +0,0 @@ -prepare("SELECT * FROM reports WHERE sender_id = ? ORDER BY sent_at DESC"); -$stmt->execute([$_SESSION["user_id"]]); - -$reports = $stmt->fetchAll(PDO::FETCH_ASSOC); -?> - - - - - Report list - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
-
- -
- - - - - - - '; - - echo ''; - - echo ''; - - echo ''; - - echo ''; - } - ?> -
ContentsStatus
' . substr($report["contents"], 0, 20) . "..."; - echo ' (' . format_timestamp(time() - strtotime($report["sent_at"])) . ' ago)'; - echo ''; - echo $report["resolved_by"] == null ? "Unresolved" : "Resolved"; - echo ''; - echo '[ View ]'; - echo '
-
-
-
-
-
- - - \ No newline at end of file diff --git a/public/report/send.php b/public/report/send.php deleted file mode 100644 index ab136e1..0000000 --- a/public/report/send.php +++ /dev/null @@ -1,45 +0,0 @@ -prepare("INSERT INTO reports(sender_id, contents) VALUES (?, ?)"); -$stmt->execute([$_SESSION["user_id"], str_safe($_POST["contents"], 200)]); - -$report_id = $db->lastInsertId(); - -$stmt = $db->prepare("SELECT * FROM reports WHERE id = ?"); -$stmt->execute([$report_id]); - -if (CLIENT_REQUIRES_JSON) { - json_response([ - "status_code" => 201, - "message" => null, - "data" => $stmt->fetch(PDO::FETCH_ASSOC) - ], 201); - exit; -} - -generate_alert("/report?id=$report_id", "Thank you for your vigilance! MODS will take action as soon as possible.", 200); diff --git a/public/rules.php b/public/rules.php deleted file mode 100644 index 027b994..0000000 --- a/public/rules.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - The Rules of <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
- -
-

The Rules of

-
    - $line"; - } - if (empty($contents)) { - echo "No rules!"; - } - ?> -
-
-
-
-
- - - \ No newline at end of file diff --git a/public/software.php b/public/software.php deleted file mode 100644 index e45fd86..0000000 --- a/public/software.php +++ /dev/null @@ -1,83 +0,0 @@ - - [ - [ - "name" => "Tinyrino", - "author" => "ilotterytea", - "desc" => "Tinyrino is a fork of Chatterino7 (which is a fork of Chatterino 2). This fork supports TinyEmotes, a software that allows you to host your emotes on your own instances.", - "download_url" => "https://github.com/ilotterytea/tinyrino/releases", - "source_url" => "https://github.com/ilotterytea/tinyrino" - ] - ], - "Web extensions" => [], - "Chatbots" => [], - "Other tools" => [] -]; -?> - - - - - Software - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
- $sw) { - echo '
'; - echo ""; - echo '
'; - - if (empty($sw)) { - echo '

There are no software in this category! They will appear here as soon as they support TinyEmotes.

'; - } - - foreach ($sw as $s) { - $name_lower = strtolower($s["name"]); - echo '
'; - echo "
"; - - echo '
'; - echo '

' . $s["name"] . '

by ' . $s["author"] . '

'; - echo '

' . $s["desc"] . '

'; - - $screenshot_path = "./static/img/software/$name_lower/screenshots"; - if (is_dir($screenshot_path)) { - echo '
'; - foreach (new DirectoryIterator($screenshot_path) as $file) { - if ($file->isDot()) { - continue; - } - - echo ""; - } - echo '
'; - } - - echo '
'; - - echo '
'; - echo 'Download'; - echo '[ Source code ]'; - echo '
'; - } - - echo '
'; - } - ?> -
-
-
- - - \ No newline at end of file diff --git a/public/static/favicon.ico b/public/static/favicon.ico deleted file mode 100644 index ff8f9ad..0000000 Binary files a/public/static/favicon.ico and /dev/null differ diff --git a/public/static/img/404/1.webp b/public/static/img/404/1.webp deleted file mode 100644 index 7fc04a8..0000000 Binary files a/public/static/img/404/1.webp and /dev/null differ diff --git a/public/static/img/brand/big.webp b/public/static/img/brand/big.webp deleted file mode 100644 index c665251..0000000 Binary files a/public/static/img/brand/big.webp and /dev/null differ diff --git a/public/static/img/brand/mini.webp b/public/static/img/brand/mini.webp deleted file mode 100644 index ce0f91f..0000000 Binary files a/public/static/img/brand/mini.webp and /dev/null differ diff --git a/public/static/img/counter/0.png b/public/static/img/counter/0.png deleted file mode 100644 index 929946c..0000000 Binary files a/public/static/img/counter/0.png and /dev/null differ diff --git a/public/static/img/counter/1.png b/public/static/img/counter/1.png deleted file mode 100644 index f1c151d..0000000 Binary files a/public/static/img/counter/1.png and /dev/null differ diff --git a/public/static/img/counter/2.png b/public/static/img/counter/2.png deleted file mode 100644 index 7a5a2af..0000000 Binary files a/public/static/img/counter/2.png and /dev/null differ diff --git a/public/static/img/counter/3.png b/public/static/img/counter/3.png deleted file mode 100644 index c7c7598..0000000 Binary files a/public/static/img/counter/3.png and /dev/null differ diff --git a/public/static/img/counter/4.png b/public/static/img/counter/4.png deleted file mode 100644 index cd5b8b9..0000000 Binary files a/public/static/img/counter/4.png and /dev/null differ diff --git a/public/static/img/counter/5.png b/public/static/img/counter/5.png deleted file mode 100644 index 3455958..0000000 Binary files a/public/static/img/counter/5.png and /dev/null differ diff --git a/public/static/img/counter/6.png b/public/static/img/counter/6.png deleted file mode 100644 index c7f795d..0000000 Binary files a/public/static/img/counter/6.png and /dev/null differ diff --git a/public/static/img/counter/7.png b/public/static/img/counter/7.png deleted file mode 100644 index 925bb68..0000000 Binary files a/public/static/img/counter/7.png and /dev/null differ diff --git a/public/static/img/counter/8.png b/public/static/img/counter/8.png deleted file mode 100644 index 7cb611e..0000000 Binary files a/public/static/img/counter/8.png and /dev/null differ diff --git a/public/static/img/counter/9.png b/public/static/img/counter/9.png deleted file mode 100644 index f8ff704..0000000 Binary files a/public/static/img/counter/9.png and /dev/null differ diff --git a/public/static/img/defaults/profile_picture.png b/public/static/img/defaults/profile_picture.png deleted file mode 100644 index caaab1a..0000000 Binary files a/public/static/img/defaults/profile_picture.png and /dev/null differ diff --git a/public/static/img/icons/bin.png b/public/static/img/icons/bin.png deleted file mode 100644 index 375b8bf..0000000 Binary files a/public/static/img/icons/bin.png and /dev/null differ diff --git a/public/static/img/icons/clock.png b/public/static/img/icons/clock.png deleted file mode 100644 index e2672c2..0000000 Binary files a/public/static/img/icons/clock.png and /dev/null differ diff --git a/public/static/img/icons/connect.png b/public/static/img/icons/connect.png deleted file mode 100644 index 024138e..0000000 Binary files a/public/static/img/icons/connect.png and /dev/null differ diff --git a/public/static/img/icons/connections/twitch.webp b/public/static/img/icons/connections/twitch.webp deleted file mode 100644 index c2882b4..0000000 Binary files a/public/static/img/icons/connections/twitch.webp and /dev/null differ diff --git a/public/static/img/icons/disconnect.png b/public/static/img/icons/disconnect.png deleted file mode 100644 index b335cb1..0000000 Binary files a/public/static/img/icons/disconnect.png and /dev/null differ diff --git a/public/static/img/icons/door_in.png b/public/static/img/icons/door_in.png deleted file mode 100644 index 41676a0..0000000 Binary files a/public/static/img/icons/door_in.png and /dev/null differ diff --git a/public/static/img/icons/door_out.png b/public/static/img/icons/door_out.png deleted file mode 100644 index 2541d2b..0000000 Binary files a/public/static/img/icons/door_out.png and /dev/null differ diff --git a/public/static/img/icons/emotes/emote.png b/public/static/img/icons/emotes/emote.png deleted file mode 100644 index 3cf0a16..0000000 Binary files a/public/static/img/icons/emotes/emote.png and /dev/null differ diff --git a/public/static/img/icons/emotes/emote_folder.png b/public/static/img/icons/emotes/emote_folder.png deleted file mode 100644 index 2f8951f..0000000 Binary files a/public/static/img/icons/emotes/emote_folder.png and /dev/null differ diff --git a/public/static/img/icons/emotes/emote_go.png b/public/static/img/icons/emotes/emote_go.png deleted file mode 100644 index 36ae3f6..0000000 Binary files a/public/static/img/icons/emotes/emote_go.png and /dev/null differ diff --git a/public/static/img/icons/emoticon_happy.png b/public/static/img/icons/emoticon_happy.png deleted file mode 100644 index 6b7336e..0000000 Binary files a/public/static/img/icons/emoticon_happy.png and /dev/null differ diff --git a/public/static/img/icons/eye.png b/public/static/img/icons/eye.png deleted file mode 100644 index 564a1a9..0000000 Binary files a/public/static/img/icons/eye.png and /dev/null differ diff --git a/public/static/img/icons/heart.png b/public/static/img/icons/heart.png deleted file mode 100644 index d9ee53e..0000000 Binary files a/public/static/img/icons/heart.png and /dev/null differ diff --git a/public/static/img/icons/inbox/0.png b/public/static/img/icons/inbox/0.png deleted file mode 100644 index c149c2b..0000000 Binary files a/public/static/img/icons/inbox/0.png and /dev/null differ diff --git a/public/static/img/icons/inbox/1.png b/public/static/img/icons/inbox/1.png deleted file mode 100644 index 89c8129..0000000 Binary files a/public/static/img/icons/inbox/1.png and /dev/null differ diff --git a/public/static/img/icons/inbox/2.png b/public/static/img/icons/inbox/2.png deleted file mode 100644 index 7348aed..0000000 Binary files a/public/static/img/icons/inbox/2.png and /dev/null differ diff --git a/public/static/img/icons/link_break.png b/public/static/img/icons/link_break.png deleted file mode 100644 index 5235753..0000000 Binary files a/public/static/img/icons/link_break.png and /dev/null differ diff --git a/public/static/img/icons/new_emote.png b/public/static/img/icons/new_emote.png deleted file mode 100644 index 82d263e..0000000 Binary files a/public/static/img/icons/new_emote.png and /dev/null differ diff --git a/public/static/img/icons/no.png b/public/static/img/icons/no.png deleted file mode 100644 index c149c2b..0000000 Binary files a/public/static/img/icons/no.png and /dev/null differ diff --git a/public/static/img/icons/pencil.png b/public/static/img/icons/pencil.png deleted file mode 100644 index 0bfecd5..0000000 Binary files a/public/static/img/icons/pencil.png and /dev/null differ diff --git a/public/static/img/icons/ratings/-1.png b/public/static/img/icons/ratings/-1.png deleted file mode 100644 index 38f492a..0000000 Binary files a/public/static/img/icons/ratings/-1.png and /dev/null differ diff --git a/public/static/img/icons/ratings/1.png b/public/static/img/icons/ratings/1.png deleted file mode 100644 index 0b01c2b..0000000 Binary files a/public/static/img/icons/ratings/1.png and /dev/null differ diff --git a/public/static/img/icons/ratings/brimstone.webp b/public/static/img/icons/ratings/brimstone.webp deleted file mode 100644 index 98b0d62..0000000 Binary files a/public/static/img/icons/ratings/brimstone.webp and /dev/null differ diff --git a/public/static/img/icons/star.png b/public/static/img/icons/star.png deleted file mode 100644 index b88c857..0000000 Binary files a/public/static/img/icons/star.png and /dev/null differ diff --git a/public/static/img/icons/table.png b/public/static/img/icons/table.png deleted file mode 100644 index abcd936..0000000 Binary files a/public/static/img/icons/table.png and /dev/null differ diff --git a/public/static/img/icons/tag_blue.png b/public/static/img/icons/tag_blue.png deleted file mode 100644 index 9757fc6..0000000 Binary files a/public/static/img/icons/tag_blue.png and /dev/null differ diff --git a/public/static/img/icons/user.png b/public/static/img/icons/user.png deleted file mode 100644 index 79f35cc..0000000 Binary files a/public/static/img/icons/user.png and /dev/null differ diff --git a/public/static/img/icons/world.png b/public/static/img/icons/world.png deleted file mode 100644 index 68f21d3..0000000 Binary files a/public/static/img/icons/world.png and /dev/null differ diff --git a/public/static/img/icons/yes.png b/public/static/img/icons/yes.png deleted file mode 100644 index 89c8129..0000000 Binary files a/public/static/img/icons/yes.png and /dev/null differ diff --git a/public/static/img/software/tinyrino/icon.png b/public/static/img/software/tinyrino/icon.png deleted file mode 100644 index 8f5bce3..0000000 Binary files a/public/static/img/software/tinyrino/icon.png and /dev/null differ diff --git a/public/static/img/software/tinyrino/screenshots/1.png b/public/static/img/software/tinyrino/screenshots/1.png deleted file mode 100644 index 8e4d993..0000000 Binary files a/public/static/img/software/tinyrino/screenshots/1.png and /dev/null differ diff --git a/public/static/img/software/tinyrino/screenshots/2.png b/public/static/img/software/tinyrino/screenshots/2.png deleted file mode 100644 index ec21185..0000000 Binary files a/public/static/img/software/tinyrino/screenshots/2.png and /dev/null differ diff --git a/public/static/style.css b/public/static/style.css deleted file mode 100644 index 9aa1ca9..0000000 --- a/public/static/style.css +++ /dev/null @@ -1,640 +0,0 @@ -:root { - --background-color: #c4d1b5; - --background-color-hover: #e4eed8; - --background-color-disabled: #bec6b3; - --border-color: #95a186; - - --background-color-2: #cfdfbd; - --border-color-2: #849275; - - --foreground-color: #425514; - --foreground-color-hover: #1c220c; - - --body-background: #f2f8ee; - - --profile-background: radial-gradient(#f2f8eebb, #f2f8ee); - - /** NAVBAR */ - --navbar-background: linear-gradient(0deg, #b9caaf, #eaffdd); - --navbar-border-color: #94a58a; -} - -* { - padding: 0; - margin: 0; - - font-family: Arial, Helvetica, sans-serif; -} - -body { - background: var(--body-background); -} - -h1 { - font-size: 26px; -} - -h2 { - font-size: 20px; -} - -h3 { - font-size: 16px; -} - -div { - display: unset; -} - -table { - text-align: left; -} - -table.vertical th { - text-align: right; -} - -table.vertical.left th { - text-align: left; -} - -table.vertical th, -table.vertical td { - padding: 2px; -} - -a { - color: var(--foreground-color); - text-decoration: none; -} - -a:hover { - color: var(--foreground-color-hover); - text-decoration: underline; -} - -input { - padding: 2px; - border-radius: 4px; - border: 1px solid gray; -} - -input[type=file] { - max-width: 230px; -} - -form { - display: flex; - flex-direction: column; -} - -form:has(div) { - gap: 16px; -} - -label { - display: block; -} - -label.inline { - display: inline; -} - -textarea { - resize: vertical; - height: 100px; -} - -.container { - width: 100%; - min-height: 100vh; - display: flex; -} - -.wrapper { - flex-grow: 1; - display: flex; - flex-direction: column; -} - -.content { - flex-grow: 1; - display: flex; - flex-direction: column; - gap: 6px; - margin: 6px; -} - -.page { - display: flex; - flex-direction: row; -} - -.screenshots img { - height: 128px; -} - -.content.row>.content { - margin: 0; -} - -.sidebar { - max-width: 300px; -} - -/** ------------- - COUNTER ------------- -*/ -.counter img:not(:first-child) { - margin-left: 32px; -} - - -/** ------------ - NAVBAR ------------ -*/ - -.navbar { - background: var(--navbar-background); - border-bottom: 1px solid var(--navbar-border-color); - display: flex; - flex-direction: row; - padding: 4px; - gap: 16px; -} - -.navbar .links { - display: flex; - align-items: end; - gap: 4px; -} - -.navbar .brand { - display: flex; - flex-direction: row; - align-items: end; -} - -/** -------------- - BUTTONS -------------- -*/ - -button, -.button { - background: lightgray; - border: 1px solid gray; - border-radius: 4px; - padding: 2px 4px; - font-size: 14px; - text-decoration: none; - color: black; -} - -button:disabled { - color: gray; -} - -button:disabled:hover { - cursor: not-allowed; - background: lightgray; - color: gray; -} - -button:hover, -.button:hover { - background: #b9b9b9; - cursor: pointer; - color: black; - text-decoration: none; -} - -button.transparent, -.button.transparent { - background: unset; - border: unset; -} - -.gem:hover { - filter: saturate(2); -} - -.coal:hover { - filter: brightness(2); -} - -button.red, -.button.red { - background: #e97272; - border-color: #a85252; -} - -button.red:hover, -.button.red:hover { - background: #ec8d8d; -} - -button.green, -.button.green { - background: #6cbb6d; - border-color: #52a85d; -} - -button.green:hover, -.button.green:hover { - background: #85dd8a; -} - -button.purple, -.button.purple, -.twitch { - background: #9a7ad2 !important; - border-color: #6d5595 !important; -} - -button.purple:hover, -.button.purple:hover { - background: #ac88ea; -} - -button.big, -.button.big { - padding: 8px 24px; - font-size: 18px; -} - -/** ----------- - LIST ----------- -*/ - -.items { - display: flex; - flex-direction: column; - gap: 6px; -} - -.items.content { - flex-grow: 1; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 16px; -} - -.navtab { - position: relative; - width: 50%; - top: 2px; -} - -.full { - flex-grow: 1; -} - -.right { - justify-content: flex-end; -} - -/** ---------- - BOX ---------- -*/ - -.box { - background: var(--background-color); - border: 2px solid var(--border-color); - border-radius: 4px; - padding: 8px; -} - -.box.navtab { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom: unset; - margin: 0; -} - -.box:has(.navtab) { - background: unset; - border: unset; - border-radius: unset; - padding: 0; - - display: flex; - flex-direction: column; -} - -.box:has(.navtab) .content { - flex-grow: 1; - margin: 0; - padding: 16px; -} - -.box .content .box { - background: var(--background-color-2); - border-color: var(--border-color-2); -} - -.box .content a.box:hover { - background: linear-gradient(0deg, var(--background-color-hover), var(--background-color-2)); - cursor: pointer; -} - -.box hr { - border-color: var(--border-color); - border-width: 1px; -} - -.box.emote { - width: 96px; - height: 96px; -} - -.box.background { - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; - overflow: hidden; -} - -.box.background h1 { - color: white; - text-shadow: -1px 1px 4px black; -} - -.box.emote h1 { - font-size: 16px; - font-weight: 600; - - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.box.emote p { - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 10px; -} - -.box.emote:has(.emote-desc) img { - max-width: 64px; - max-height: 64px; -} - -.box.emote:has(.emote-desc.none) img { - max-width: 96px; - max-height: 96px; -} - -a.box { - text-decoration: none; - color: black; -} - -a.box:hover { - background: linear-gradient(0deg, var(--background-color-hover), var(--background-color-disabled)); - cursor: pointer; -} - -.emote-showcase { - flex-grow: 1; - display: flex; - justify-content: center; - align-items: center; - gap: 32px; - margin: 32px 0; -} - -.alert.red { - background: #e27777; - border-color: #9f5050; -} - -.emote-check { - position: relative; - left: 35px; - top: 10px; - width: 24px; - height: 24px; - z-index: 2; -} - -.emote:has(.emote-check) img { - margin-top: -15px; -} - -/** RATINGS */ -.rating.gemerald { - font-weight: bolder; - text-shadow: 0 0 5px blue; - color: #b4dbeb; -} - -.rating.gemerald img { - filter: hue-rotate(50deg) brightness(1.5); -} - -.rating.gem { - font-weight: bold; - color: #2e7b99; -} - -.rating.coal { - font-weight: bold; - color: #2d3335; - text-shadow: 0 0 2px black; -} - -.rating.brimstone { - font-weight: bolder; - color: orange; - text-shadow: 0 0 4px red; -} - -/** -------------- - ACCOUNTS -------------- -*/ - -.accman { - flex-grow: 0; - width: 400px; - - display: flex; - flex-direction: column; - gap: 16px; -} - -.badge { - padding: 2px 8px; - border-radius: 4px; - border-width: 1px; - border-style: solid; -} - -.badge img { - max-width: 16px; - max-height: 16px; - vertical-align: middle; -} - -/** ---------------------------------- - SOMETHING FROM TAILWINDCSS ---------------------------------- -*/ - -.row { - display: flex; - flex-direction: row; -} - -.column { - display: flex; - flex-direction: column; -} - -.grow { - flex-grow: 1; -} - -.small-gap { - gap: 8px; -} - -.big-gap { - gap: 32px; -} - -.no-gap { - gap: 0; -} - -.center { - justify-content: center; - align-items: center; -} - -.items-center { - align-items: center; -} - -.items-bottom { - align-items: end; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-bottom { - justify-content: end; -} - -.font-small { - font-size: 12px; -} - -.p-16 { - padding: 16px; -} - -.m-8 { - margin: 8px; -} - -.inline { - display: inline; -} - -.flex { - display: flex; -} - -.none { - display: none; -} - -.w-full { - width: 100%; -} - -.font-weight-normal, -.font-weight-normal th { - font-weight: normal; -} - -.rounded { - border-radius: 4px; -} - -/** -------------- - USER -------------- -*/ - -.background { - position: absolute; - background-position: center center; - top: 61px; - bottom: 0; - left: 0; - right: 0; - z-index: -1; -} - -.background-layer { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: var(--profile-background); -} - -/** -------------- - CHAT -------------- -*/ - -.chat-message { - background: linear-gradient(0deg, #202020, #303030); - color: #fff; - padding: 8px; -} - -.chat .chat-message:nth-child(even) { - background: linear-gradient(0deg, #353535, #454545); - - border-top: 1px solid #707070; - border-bottom: 1px solid #707070; -} - -.chat.rounded .chat-message:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -.chat.rounded .chat-message:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} \ No newline at end of file diff --git a/public/static/txt/RULES b/public/static/txt/RULES deleted file mode 100644 index 0ec5b9e..0000000 --- a/public/static/txt/RULES +++ /dev/null @@ -1,2 +0,0 @@ -Hey, admin! Write your rules in /static/txt/RULES file. -If you see this, VI VON \ No newline at end of file diff --git a/public/system/emotes/index.php b/public/system/emotes/index.php deleted file mode 100644 index 2a48408..0000000 --- a/public/system/emotes/index.php +++ /dev/null @@ -1,250 +0,0 @@ -prepare("SELECT e.*, -CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, -CASE WHEN up.private_profile = FALSE OR up.id = ? THEN u.username ELSE NULL END AS uploader_name, -r.name AS role_name, -r.badge_id AS role_badge_id, -ub.badge_id AS custom_badge_id -FROM emotes e -LEFT JOIN users u ON u.id = e.uploaded_by -LEFT JOIN user_preferences up ON up.id = u.id -LEFT JOIN role_assigns ra ON ra.user_id = u.id -LEFT JOIN roles r ON r.id = ra.role_id -LEFT JOIN user_badges ub ON ub.user_id = u.id -WHERE e.visibility = 2 -ORDER BY e.created_at DESC -LIMIT 25 -"); -$emote_results->execute([$current_user_id, $current_user_id]); - -$emote_results = $emote_results->fetchAll(PDO::FETCH_ASSOC); - -$emote = $emote_results[0] ?? null; - -if (isset($_GET["id"])) { - $stmt = $db->prepare("SELECT e.*, - CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, - CASE WHEN up.private_profile = FALSE OR up.id = ? THEN u.username ELSE NULL END AS uploader_name, - r.name AS role_name, - r.badge_id AS role_badge_id, - ub.badge_id AS custom_badge_id - FROM emotes e - LEFT JOIN users u ON u.id = e.uploaded_by - LEFT JOIN user_preferences up ON up.id = u.id - LEFT JOIN role_assigns ra ON ra.user_id = u.id - LEFT JOIN roles r ON r.id = ra.role_id - LEFT JOIN user_badges ub ON ub.user_id = u.id - WHERE e.visibility = 2 AND e.id = ? - LIMIT 1"); - - $stmt->execute([$current_user_id, $current_user_id, $_GET["id"]]); - $emote = $stmt->fetch(PDO::FETCH_ASSOC) ?? null; -} - -?> - - - - - System panel - <?php echo INSTANCE_NAME ?> - - - - - -
-
- - -
-
- -
- '; - echo ''; - echo '' . $row["code"] . ''; - echo ' by '; - - if ($row["uploader_name"] == null) { - echo ANONYMOUS_DEFAULT_NAME . '*'; - } else { - echo $row["uploader_name"]; - } - - echo ''; - } - - if (empty($emote_results)) { - echo 'Everything is clear. Good job!'; - } - ?> -
-
- -
- -
- '; - ?> -
-
-
- '; - - echo ''; - - $path = $_SERVER["DOCUMENT_ROOT"] . '/static/userdata/emotes/' . $emote["id"] . "/{$size}x.webp"; - - echo '
'; - - if ($file_size = filesize($path)) { - $kb = sprintf("%.2f", $file_size / 1024); - echo "

{$kb}KB

"; - } - - if ($image_size = getimagesize($path)) { - echo "

$image_size[0]x$image_size[1]

"; - } - - echo '
'; - } - ?> -
-
- - -
- - prepare("SELECT t.code FROM tags t - INNER JOIN tag_assigns ta ON ta.emote_id = ? - WHERE t.id = ta.tag_id - "); - $stmt->execute([$emote["id"]]); - - $tags = $stmt->fetchAll(PDO::FETCH_ASSOC); - $tags = array_column($tags, "code"); - - if (!empty($tags)) { - echo ''; - } - ?> - - - - - - - - - - - - - - -
Tags'; - foreach ($tags as $tag) { - echo "$tag "; - } - echo '
Uploader"; - echo $username; - echo ""; - - if ($emote["role_badge_id"]) { - echo ' ## ' . $emote['; - } - - if ($emote["custom_badge_id"]) { - echo ' '; - } - - echo ', about ' . format_timestamp(time() - strtotime($emote["created_at"])) . " ago"; - ?>
NotesEmpty' ?>
Source - " - target="_blank"> -
-
- -
- " style="display: none;"> - -
- -
- - -
-
- -
- -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/public/system/emotes/verdict.php b/public/system/emotes/verdict.php deleted file mode 100644 index df2f5ba..0000000 --- a/public/system/emotes/verdict.php +++ /dev/null @@ -1,80 +0,0 @@ -prepare("SELECT id, code, uploaded_by FROM emotes WHERE id = ? AND visibility = 2 LIMIT 1"); -$stmt->execute([$id]); - -if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $verdict = 2; - - switch ($action) { - case "approve": { - $db->prepare("UPDATE emotes SET visibility = 1 WHERE id = ?") - ->execute([$row["id"]]); - $verdict = 1; - break; - } - case "reject": { - $db->prepare("UPDATE emotes SET visibility = 0 WHERE id = ?") - ->execute([$row["id"]]); - $verdict = 0; - break; - } - default: { - generate_alert("/system/emotes", "Unknown action"); - exit; - } - } - - $comment = str_safe($_POST["comment"] ?? "", null, false); - - if ($comment == "") { - $comment = null; - } - - $db->prepare("INSERT INTO mod_actions(user_id, emote_id, verdict, comment) VALUES (?, ?, ?, ?)") - ->execute([$_SESSION["user_id"], $row["id"], $verdict, $comment]); - - if ($row["uploaded_by"] != null) { - $contents = match ($verdict) { - 0 => 'Your emote "' . $row["code"] . '" has been unlisted! Anyone can add it via a direct link.', - 1 => 'Your emote "' . $row["code"] . '" has been approved! Enjoy!', - default => 'We did something with your emote "' . $row["code"] . '"' - }; - - if ($comment != null) { - $contents .= " Mod's comment: $comment"; - } - - $db->prepare("INSERT INTO inbox_messages(recipient_id, message_type, contents, link) VALUES (?, ?, ?, ?)") - ->execute([$row["uploaded_by"], "1", $contents, "/emotes?id=" . $row["id"]]); - } - - generate_alert("/system/emotes", 'Emote "' . $row["code"] . '" has been ' . ($verdict == 0 ? 'unlisted' : 'set to public') . '!', 200); - exit; -} - -generate_alert("system/emotes", "Emote ID $id not found", 404); \ No newline at end of file diff --git a/public/system/index.php b/public/system/index.php deleted file mode 100644 index 95b17a5..0000000 --- a/public/system/index.php +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - System panel - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
-
- -
- Emotes'; - - $results = $db->query("SELECT COUNT(*) FROM emotes WHERE visibility = 2")->fetch()[0]; - - if ($results > 0) { - echo " ($results pending)"; - } - - echo ''; - } - - if (REPORTS_ENABLE && $_SESSION["user_role"]["permission_report_review"]) { - echo 'Reports'; - - $results = $db->query("SELECT COUNT(*) FROM reports WHERE resolved_by IS NULL")->fetch()[0]; - - if ($results > 0) { - echo " ($results pending)"; - } - - echo ''; - } - ?> -
-
-
-
-
- - - \ No newline at end of file diff --git a/public/users.php b/public/users.php deleted file mode 100644 index 6a6273e..0000000 --- a/public/users.php +++ /dev/null @@ -1,587 +0,0 @@ -prepare("SELECT id, username, joined_at, last_active_at - FROM users - WHERE username LIKE ? - ORDER BY last_active_at DESC LIMIT ? OFFSET ?"); - $stmt->bindParam(1, $search, PDO::PARAM_STR); - $stmt->bindParam(2, $limit, PDO::PARAM_INT); - $stmt->bindParam(3, $offset, PDO::PARAM_INT); - $stmt->execute(); - - $count_stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username LIKE ?"); - $count_stmt->execute([$search]); - - $total_users = $count_stmt->fetch()[0]; - $total_pages = ceil($total_users / $limit); - - if ($is_json) { - header("Content-Type: application/json"); - echo json_encode([ - "status_code" => 200, - "message" => null, - "data" => [ - "all_user_count" => intval($all_user_count), - "users" => $stmt->fetchAll(PDO::FETCH_ASSOC) - ] - ]); - exit; - } - - echo '' ?> - - - - User list - <?php echo INSTANCE_NAME ?> - - - - - -
-
- -
- -
-
- -
- '; - echo ''; - echo 'UsernameLast active'; - echo ''; - while ($row = $stmt->fetch()) { - $diff = time() - strtotime($row["last_active_at"]); - - $last_active = "moments"; - - if ($diff > 5) { - $last_active = format_timestamp($diff); - } - - echo ''; - echo ''; - echo '' . $row["username"] . ''; - echo "$last_active ago"; - echo ''; - } - echo ''; - } else { - echo '

Nothing found...

'; - } - ?> -
-
- 1) { - echo '' ?> - -
- -
- -
-
-
- - - - prepare("SELECT u.id FROM users u - INNER JOIN connections co ON co.alias_id = ? AND co.platform = ? - WHERE co.user_id = u.id - "); - $stmt->execute([$alias_id, $platform]); - - if ($row = $stmt->fetch()) { - $user = User::get_user_by_id($db, $row["id"]); - } -} -// fetching user by internal id -else if (isset($_GET["id"])) { - $user = User::get_user_by_id($db, $_GET["id"]); -} - -if (!$user) { - generate_alert("/404.php", "The user you requested cannot be found", 404); - exit; -} - -// User preferences -$stmt = $db->prepare("SELECT * FROM user_preferences WHERE id = ?"); -$stmt->execute([$user->id]); - -$user_preferences = $stmt->fetch(PDO::FETCH_ASSOC); - -$public_profile = !$user_preferences["private_profile"] || $user->id == ($_SESSION["user_id"] ?? ""); - -// fetching emote sets -$emote_sets = Emoteset::get_all_user_emotesets($db, $user->id); -$active_emote_set = null; -foreach ($emote_sets as $es) { - if ($es->is_default) { - $active_emote_set = $es; - break; - } -} - -// gathering uploaded emotes -$uploaded_emotes = []; - -if ($public_profile) { - $stmt = $db->prepare("SELECT e.id, e.code, e.uploaded_by, e.source, e.visibility - FROM emotes e - WHERE e.uploaded_by = ? - ORDER BY e.created_at ASC - "); - $stmt->execute([$user->id]); - - $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); - - foreach ($rows as $row) { - array_push($uploaded_emotes, Emote::from_array_with_user($row, $db)); - } -} - -// gathering actions -$actions = []; - -if ($public_profile) { - $stmt = $db->prepare("SELECT a.* FROM actions a WHERE a.user_id = ? ORDER BY a.created_at DESC LIMIT 15"); - $stmt->execute([$user->id]); - $actions = $stmt->fetchAll(PDO::FETCH_ASSOC); -} - -// TODO: add functionality - -// calculating contributions -$stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE uploaded_by = ?"); -$stmt->execute([$user->id]); -$contributions = intval($stmt->fetch()[0]); - -$stmt = $db->prepare("SELECT COUNT(*) FROM ratings WHERE user_id = ?"); -$stmt->execute([$user->id]); - -$contributions += intval($stmt->fetch()[0]); - -// getting status -$stmt = $db->prepare("SELECT * FROM roles r INNER JOIN role_assigns ra ON ra.user_id = ? WHERE ra.role_id = r.id"); -$stmt->execute([$user->id]); - -$role = $stmt->fetch(PDO::FETCH_ASSOC) ?? null; - -// getting reactions -$stmt = $db->prepare("SELECT rate, COUNT(*) AS c FROM ratings WHERE user_id = ? GROUP BY rate ORDER BY c DESC"); -$stmt->execute([$user->id]); - -$fav_reactions = $stmt->fetchAll(PDO::FETCH_ASSOC); - -// getting favorite emote -$fav_emote = 1; - -// getting custom badge -$stmt = $db->prepare("SELECT b.* FROM badges b - INNER JOIN user_badges ub ON ub.user_id = ? - WHERE b.id = ub.badge_id -"); -$stmt->execute([$user->id]); - -$custom_badge = null; -if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $custom_badge = $row; -} - -if ($is_json) { - $user_data = (array) $user; - - unset($user_data["private_profile"]); - - $user_data["stats"] = [ - "contributions" => $contributions, - "favorite_reaction_id" => $fav_reactions, - "favorite_emote_id" => $fav_emote - ]; - - $user_data["active_emote_set_id"] = $active_emote_set->id; - $user_data["emote_sets"] = $emote_sets; - $user_data["uploaded_emotes"] = $uploaded_emotes; - $user_data["actions"] = $actions; - - json_response([ - "status_code" => 200, - "message" => null, - "data" => $user_data - ]); - exit; -} -?> - - - - - <?php echo sprintf("%s - %s", $user->username, INSTANCE_NAME) ?> - - - - - -
-
-
- -
-
- -
- -
- -
- -
- emotes)) { - html_display_emotes($active_emote_set->emotes); - } else { - echo '

No emotes found... ' . ((($_SESSION["user_id"] ?? "") == $id) ? 'Start adding emotes and they will appear here! :)

' : '

'); - } - } else { - echo "

This user doesn't have active emote set.

"; - } - ?> -
-
- -
- -
- No emote sets found... ' . ((($_SESSION["user_id"] ?? "") == $id) ? 'Start adding emotes and you will have one! :)

' : '

'); - } - ?> -
-
- - -
- -
- This user has done nothing bad or good...

"; - } - - foreach ($actions as $action) { - echo '
'; - - list($action_name, $preposition, $icon_name) = match ($action["action_type"]) { - "EMOTESET_ADD" => ["added", "to", "yes.png"], - "EMOTESET_REMOVE" => ["removed", "from", "no.png"], - "EMOTESET_ALIAS" => ["renamed", "in", "pencil.png"], - "EMOTE_CREATE" => ["created", null, "new_emote.png"], - "EMOTE_DELETE" => ["deleted", null, "deleted_emote.png"], - "EMOTE_RENAME" => ["renamed", null, "renamed_emote.png"] - }; - - echo "
"; - - echo '
'; - echo '

'; - echo '' . $user->username . ' '; - - $payload = json_decode($action["action_payload"], true); - - list($action_root, $action_sub) = explode("_", $action["action_type"]); - - switch ($action_root) { - case "EMOTESET": { - $e_stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE id = ?"); - $e_stmt->execute([$payload["emote"]["id"]]); - - echo "$action_name emote rowCount() == 1) { - echo '/emotes?id=' . $payload["emote"]["id"] . '">'; - echo ' '; - } else { - echo '">'; - } - - if (isset($payload["emote"]["original_code"])) { - echo $payload["emote"]["original_code"] . ' to '; - echo "rowCount() == 1) { - echo '/emotes?id=' . $payload["emote"]["id"] . '">'; - echo ' '; - } else { - echo '">'; - } - - echo $payload["emote"]["code"] . ''; - } else { - echo $payload["emote"]["code"] . ''; - } - - $es_stmt = $db->prepare("SELECT COUNT(*) FROM emote_sets WHERE id = ?"); - $es_stmt->execute([$payload["emoteset"]["id"]]); - - echo " $preposition rowCount() == 1) { - echo '/emotesets.php?id=' . $payload["emoteset"]["id"]; - } - - echo '">' . $payload["emoteset"]["name"] . ''; - break; - } - case "EMOTE": { - $e_stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE id = ?"); - $e_stmt->execute([$payload["emote"]["id"]]); - - echo "$action_name emote rowCount() == 1) { - echo '/emotes?id=' . $payload["emote"]["id"] . '">'; - echo ' '; - } else { - echo '">'; - } - - echo $payload["emote"]["code"] . ''; - break; - } - default: { - echo "something that we don't know"; - break; - } - } - - echo '

'; - echo '[' . format_timestamp(time() - strtotime($action["created_at"])) . ' ago] '; - echo '
'; - } - ?> -
-
- - -
- -
- -
-
- -
-
-
-
- - - - - \ No newline at end of file diff --git a/report/index.php b/report/index.php new file mode 100644 index 0000000..e5014c4 --- /dev/null +++ b/report/index.php @@ -0,0 +1,124 @@ +prepare("SELECT * FROM reports WHERE id = ? AND sender_id = ?"); + $stmt->execute([$report_id, $_SESSION["user_id"]]); + + if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $report = $row; + + if (CLIENT_REQUIRES_JSON) { + json_response([ + "status_code" => 201, + "message" => null, + "data" => $report + ], 201); + exit; + } + } else { + generate_alert("/report", "Report ID #" . $_GET["id"] . " not found or not accessable"); + exit; + } +} + +$contents = ""; + +if ($contents == "") { + if (isset($_GET["user_id"])) { + $contents = "Hi! I want to report user ID #" . $_GET["user_id"] . " because..."; + } else if (isset($_GET["emote_id"])) { + $contents = "Hi! I want to report emote ID #" . $_GET["emote_id"] . " because..."; + } +} +?> + + + + + <?php echo ($report == null ? "Send a message to MODS" : "A message to MODS") . ' - ' . INSTANCE_NAME ?> + + + + + + +
+
+ + +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+

Reported ago

+

Status: + Unresolved" : "Resolved" ?> +

+
+ +
+ +
+ +
+
+ + +
+ +
+
+ + + \ No newline at end of file diff --git a/report/list.php b/report/list.php new file mode 100644 index 0000000..f02731a --- /dev/null +++ b/report/list.php @@ -0,0 +1,81 @@ +prepare("SELECT * FROM reports WHERE sender_id = ? ORDER BY sent_at DESC"); +$stmt->execute([$_SESSION["user_id"]]); + +$reports = $stmt->fetchAll(PDO::FETCH_ASSOC); +?> + + + + + Report list - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+
+ +
+ + + + + + + '; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + } + ?> +
ContentsStatus
' . substr($report["contents"], 0, 20) . "..."; + echo ' (' . format_timestamp(time() - strtotime($report["sent_at"])) . ' ago)'; + echo ''; + echo $report["resolved_by"] == null ? "Unresolved" : "Resolved"; + echo ''; + echo '[ View ]'; + echo '
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/report/send.php b/report/send.php new file mode 100644 index 0000000..ab136e1 --- /dev/null +++ b/report/send.php @@ -0,0 +1,45 @@ +prepare("INSERT INTO reports(sender_id, contents) VALUES (?, ?)"); +$stmt->execute([$_SESSION["user_id"], str_safe($_POST["contents"], 200)]); + +$report_id = $db->lastInsertId(); + +$stmt = $db->prepare("SELECT * FROM reports WHERE id = ?"); +$stmt->execute([$report_id]); + +if (CLIENT_REQUIRES_JSON) { + json_response([ + "status_code" => 201, + "message" => null, + "data" => $stmt->fetch(PDO::FETCH_ASSOC) + ], 201); + exit; +} + +generate_alert("/report?id=$report_id", "Thank you for your vigilance! MODS will take action as soon as possible.", 200); diff --git a/rules.php b/rules.php new file mode 100644 index 0000000..027b994 --- /dev/null +++ b/rules.php @@ -0,0 +1,50 @@ + + + + + + The Rules of <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+ +
+

The Rules of

+
    + $line"; + } + if (empty($contents)) { + echo "No rules!"; + } + ?> +
+
+
+
+
+ + + \ No newline at end of file diff --git a/software.php b/software.php new file mode 100644 index 0000000..e45fd86 --- /dev/null +++ b/software.php @@ -0,0 +1,83 @@ + + [ + [ + "name" => "Tinyrino", + "author" => "ilotterytea", + "desc" => "Tinyrino is a fork of Chatterino7 (which is a fork of Chatterino 2). This fork supports TinyEmotes, a software that allows you to host your emotes on your own instances.", + "download_url" => "https://github.com/ilotterytea/tinyrino/releases", + "source_url" => "https://github.com/ilotterytea/tinyrino" + ] + ], + "Web extensions" => [], + "Chatbots" => [], + "Other tools" => [] +]; +?> + + + + + Software - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+ $sw) { + echo '
'; + echo ""; + echo '
'; + + if (empty($sw)) { + echo '

There are no software in this category! They will appear here as soon as they support TinyEmotes.

'; + } + + foreach ($sw as $s) { + $name_lower = strtolower($s["name"]); + echo '
'; + echo "
"; + + echo '
'; + echo '

' . $s["name"] . '

by ' . $s["author"] . '

'; + echo '

' . $s["desc"] . '

'; + + $screenshot_path = "./static/img/software/$name_lower/screenshots"; + if (is_dir($screenshot_path)) { + echo '
'; + foreach (new DirectoryIterator($screenshot_path) as $file) { + if ($file->isDot()) { + continue; + } + + echo ""; + } + echo '
'; + } + + echo '
'; + + echo '
'; + echo 'Download'; + echo '[ Source code ]'; + echo '
'; + } + + echo '
'; + } + ?> +
+
+
+ + + \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..ff8f9ad Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/img/404/1.webp b/static/img/404/1.webp new file mode 100644 index 0000000..7fc04a8 Binary files /dev/null and b/static/img/404/1.webp differ diff --git a/static/img/brand/big.webp b/static/img/brand/big.webp new file mode 100644 index 0000000..c665251 Binary files /dev/null and b/static/img/brand/big.webp differ diff --git a/static/img/brand/mini.webp b/static/img/brand/mini.webp new file mode 100644 index 0000000..ce0f91f Binary files /dev/null and b/static/img/brand/mini.webp differ diff --git a/static/img/counter/0.png b/static/img/counter/0.png new file mode 100644 index 0000000..929946c Binary files /dev/null and b/static/img/counter/0.png differ diff --git a/static/img/counter/1.png b/static/img/counter/1.png new file mode 100644 index 0000000..f1c151d Binary files /dev/null and b/static/img/counter/1.png differ diff --git a/static/img/counter/2.png b/static/img/counter/2.png new file mode 100644 index 0000000..7a5a2af Binary files /dev/null and b/static/img/counter/2.png differ diff --git a/static/img/counter/3.png b/static/img/counter/3.png new file mode 100644 index 0000000..c7c7598 Binary files /dev/null and b/static/img/counter/3.png differ diff --git a/static/img/counter/4.png b/static/img/counter/4.png new file mode 100644 index 0000000..cd5b8b9 Binary files /dev/null and b/static/img/counter/4.png differ diff --git a/static/img/counter/5.png b/static/img/counter/5.png new file mode 100644 index 0000000..3455958 Binary files /dev/null and b/static/img/counter/5.png differ diff --git a/static/img/counter/6.png b/static/img/counter/6.png new file mode 100644 index 0000000..c7f795d Binary files /dev/null and b/static/img/counter/6.png differ diff --git a/static/img/counter/7.png b/static/img/counter/7.png new file mode 100644 index 0000000..925bb68 Binary files /dev/null and b/static/img/counter/7.png differ diff --git a/static/img/counter/8.png b/static/img/counter/8.png new file mode 100644 index 0000000..7cb611e Binary files /dev/null and b/static/img/counter/8.png differ diff --git a/static/img/counter/9.png b/static/img/counter/9.png new file mode 100644 index 0000000..f8ff704 Binary files /dev/null and b/static/img/counter/9.png differ diff --git a/static/img/defaults/profile_picture.png b/static/img/defaults/profile_picture.png new file mode 100644 index 0000000..caaab1a Binary files /dev/null and b/static/img/defaults/profile_picture.png differ diff --git a/static/img/icons/bin.png b/static/img/icons/bin.png new file mode 100644 index 0000000..375b8bf Binary files /dev/null and b/static/img/icons/bin.png differ diff --git a/static/img/icons/clock.png b/static/img/icons/clock.png new file mode 100644 index 0000000..e2672c2 Binary files /dev/null and b/static/img/icons/clock.png differ diff --git a/static/img/icons/connect.png b/static/img/icons/connect.png new file mode 100644 index 0000000..024138e Binary files /dev/null and b/static/img/icons/connect.png differ diff --git a/static/img/icons/connections/twitch.webp b/static/img/icons/connections/twitch.webp new file mode 100644 index 0000000..c2882b4 Binary files /dev/null and b/static/img/icons/connections/twitch.webp differ diff --git a/static/img/icons/disconnect.png b/static/img/icons/disconnect.png new file mode 100644 index 0000000..b335cb1 Binary files /dev/null and b/static/img/icons/disconnect.png differ diff --git a/static/img/icons/door_in.png b/static/img/icons/door_in.png new file mode 100644 index 0000000..41676a0 Binary files /dev/null and b/static/img/icons/door_in.png differ diff --git a/static/img/icons/door_out.png b/static/img/icons/door_out.png new file mode 100644 index 0000000..2541d2b Binary files /dev/null and b/static/img/icons/door_out.png differ diff --git a/static/img/icons/emotes/emote.png b/static/img/icons/emotes/emote.png new file mode 100644 index 0000000..3cf0a16 Binary files /dev/null and b/static/img/icons/emotes/emote.png differ diff --git a/static/img/icons/emotes/emote_folder.png b/static/img/icons/emotes/emote_folder.png new file mode 100644 index 0000000..2f8951f Binary files /dev/null and b/static/img/icons/emotes/emote_folder.png differ diff --git a/static/img/icons/emotes/emote_go.png b/static/img/icons/emotes/emote_go.png new file mode 100644 index 0000000..36ae3f6 Binary files /dev/null and b/static/img/icons/emotes/emote_go.png differ diff --git a/static/img/icons/emoticon_happy.png b/static/img/icons/emoticon_happy.png new file mode 100644 index 0000000..6b7336e Binary files /dev/null and b/static/img/icons/emoticon_happy.png differ diff --git a/static/img/icons/eye.png b/static/img/icons/eye.png new file mode 100644 index 0000000..564a1a9 Binary files /dev/null and b/static/img/icons/eye.png differ diff --git a/static/img/icons/heart.png b/static/img/icons/heart.png new file mode 100644 index 0000000..d9ee53e Binary files /dev/null and b/static/img/icons/heart.png differ diff --git a/static/img/icons/inbox/0.png b/static/img/icons/inbox/0.png new file mode 100644 index 0000000..c149c2b Binary files /dev/null and b/static/img/icons/inbox/0.png differ diff --git a/static/img/icons/inbox/1.png b/static/img/icons/inbox/1.png new file mode 100644 index 0000000..89c8129 Binary files /dev/null and b/static/img/icons/inbox/1.png differ diff --git a/static/img/icons/inbox/2.png b/static/img/icons/inbox/2.png new file mode 100644 index 0000000..7348aed Binary files /dev/null and b/static/img/icons/inbox/2.png differ diff --git a/static/img/icons/link_break.png b/static/img/icons/link_break.png new file mode 100644 index 0000000..5235753 Binary files /dev/null and b/static/img/icons/link_break.png differ diff --git a/static/img/icons/new_emote.png b/static/img/icons/new_emote.png new file mode 100644 index 0000000..82d263e Binary files /dev/null and b/static/img/icons/new_emote.png differ diff --git a/static/img/icons/no.png b/static/img/icons/no.png new file mode 100644 index 0000000..c149c2b Binary files /dev/null and b/static/img/icons/no.png differ diff --git a/static/img/icons/pencil.png b/static/img/icons/pencil.png new file mode 100644 index 0000000..0bfecd5 Binary files /dev/null and b/static/img/icons/pencil.png differ diff --git a/static/img/icons/ratings/-1.png b/static/img/icons/ratings/-1.png new file mode 100644 index 0000000..38f492a Binary files /dev/null and b/static/img/icons/ratings/-1.png differ diff --git a/static/img/icons/ratings/1.png b/static/img/icons/ratings/1.png new file mode 100644 index 0000000..0b01c2b Binary files /dev/null and b/static/img/icons/ratings/1.png differ diff --git a/static/img/icons/ratings/brimstone.webp b/static/img/icons/ratings/brimstone.webp new file mode 100644 index 0000000..98b0d62 Binary files /dev/null and b/static/img/icons/ratings/brimstone.webp differ diff --git a/static/img/icons/star.png b/static/img/icons/star.png new file mode 100644 index 0000000..b88c857 Binary files /dev/null and b/static/img/icons/star.png differ diff --git a/static/img/icons/table.png b/static/img/icons/table.png new file mode 100644 index 0000000..abcd936 Binary files /dev/null and b/static/img/icons/table.png differ diff --git a/static/img/icons/tag_blue.png b/static/img/icons/tag_blue.png new file mode 100644 index 0000000..9757fc6 Binary files /dev/null and b/static/img/icons/tag_blue.png differ diff --git a/static/img/icons/user.png b/static/img/icons/user.png new file mode 100644 index 0000000..79f35cc Binary files /dev/null and b/static/img/icons/user.png differ diff --git a/static/img/icons/world.png b/static/img/icons/world.png new file mode 100644 index 0000000..68f21d3 Binary files /dev/null and b/static/img/icons/world.png differ diff --git a/static/img/icons/yes.png b/static/img/icons/yes.png new file mode 100644 index 0000000..89c8129 Binary files /dev/null and b/static/img/icons/yes.png differ diff --git a/static/img/software/tinyrino/icon.png b/static/img/software/tinyrino/icon.png new file mode 100644 index 0000000..8f5bce3 Binary files /dev/null and b/static/img/software/tinyrino/icon.png differ diff --git a/static/img/software/tinyrino/screenshots/1.png b/static/img/software/tinyrino/screenshots/1.png new file mode 100644 index 0000000..8e4d993 Binary files /dev/null and b/static/img/software/tinyrino/screenshots/1.png differ diff --git a/static/img/software/tinyrino/screenshots/2.png b/static/img/software/tinyrino/screenshots/2.png new file mode 100644 index 0000000..ec21185 Binary files /dev/null and b/static/img/software/tinyrino/screenshots/2.png differ diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..9aa1ca9 --- /dev/null +++ b/static/style.css @@ -0,0 +1,640 @@ +:root { + --background-color: #c4d1b5; + --background-color-hover: #e4eed8; + --background-color-disabled: #bec6b3; + --border-color: #95a186; + + --background-color-2: #cfdfbd; + --border-color-2: #849275; + + --foreground-color: #425514; + --foreground-color-hover: #1c220c; + + --body-background: #f2f8ee; + + --profile-background: radial-gradient(#f2f8eebb, #f2f8ee); + + /** NAVBAR */ + --navbar-background: linear-gradient(0deg, #b9caaf, #eaffdd); + --navbar-border-color: #94a58a; +} + +* { + padding: 0; + margin: 0; + + font-family: Arial, Helvetica, sans-serif; +} + +body { + background: var(--body-background); +} + +h1 { + font-size: 26px; +} + +h2 { + font-size: 20px; +} + +h3 { + font-size: 16px; +} + +div { + display: unset; +} + +table { + text-align: left; +} + +table.vertical th { + text-align: right; +} + +table.vertical.left th { + text-align: left; +} + +table.vertical th, +table.vertical td { + padding: 2px; +} + +a { + color: var(--foreground-color); + text-decoration: none; +} + +a:hover { + color: var(--foreground-color-hover); + text-decoration: underline; +} + +input { + padding: 2px; + border-radius: 4px; + border: 1px solid gray; +} + +input[type=file] { + max-width: 230px; +} + +form { + display: flex; + flex-direction: column; +} + +form:has(div) { + gap: 16px; +} + +label { + display: block; +} + +label.inline { + display: inline; +} + +textarea { + resize: vertical; + height: 100px; +} + +.container { + width: 100%; + min-height: 100vh; + display: flex; +} + +.wrapper { + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.content { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 6px; + margin: 6px; +} + +.page { + display: flex; + flex-direction: row; +} + +.screenshots img { + height: 128px; +} + +.content.row>.content { + margin: 0; +} + +.sidebar { + max-width: 300px; +} + +/** +------------ + COUNTER +------------ +*/ +.counter img:not(:first-child) { + margin-left: 32px; +} + + +/** +----------- + NAVBAR +----------- +*/ + +.navbar { + background: var(--navbar-background); + border-bottom: 1px solid var(--navbar-border-color); + display: flex; + flex-direction: row; + padding: 4px; + gap: 16px; +} + +.navbar .links { + display: flex; + align-items: end; + gap: 4px; +} + +.navbar .brand { + display: flex; + flex-direction: row; + align-items: end; +} + +/** +------------- + BUTTONS +------------- +*/ + +button, +.button { + background: lightgray; + border: 1px solid gray; + border-radius: 4px; + padding: 2px 4px; + font-size: 14px; + text-decoration: none; + color: black; +} + +button:disabled { + color: gray; +} + +button:disabled:hover { + cursor: not-allowed; + background: lightgray; + color: gray; +} + +button:hover, +.button:hover { + background: #b9b9b9; + cursor: pointer; + color: black; + text-decoration: none; +} + +button.transparent, +.button.transparent { + background: unset; + border: unset; +} + +.gem:hover { + filter: saturate(2); +} + +.coal:hover { + filter: brightness(2); +} + +button.red, +.button.red { + background: #e97272; + border-color: #a85252; +} + +button.red:hover, +.button.red:hover { + background: #ec8d8d; +} + +button.green, +.button.green { + background: #6cbb6d; + border-color: #52a85d; +} + +button.green:hover, +.button.green:hover { + background: #85dd8a; +} + +button.purple, +.button.purple, +.twitch { + background: #9a7ad2 !important; + border-color: #6d5595 !important; +} + +button.purple:hover, +.button.purple:hover { + background: #ac88ea; +} + +button.big, +.button.big { + padding: 8px 24px; + font-size: 18px; +} + +/** +---------- + LIST +---------- +*/ + +.items { + display: flex; + flex-direction: column; + gap: 6px; +} + +.items.content { + flex-grow: 1; + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 16px; +} + +.navtab { + position: relative; + width: 50%; + top: 2px; +} + +.full { + flex-grow: 1; +} + +.right { + justify-content: flex-end; +} + +/** +--------- + BOX +--------- +*/ + +.box { + background: var(--background-color); + border: 2px solid var(--border-color); + border-radius: 4px; + padding: 8px; +} + +.box.navtab { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: unset; + margin: 0; +} + +.box:has(.navtab) { + background: unset; + border: unset; + border-radius: unset; + padding: 0; + + display: flex; + flex-direction: column; +} + +.box:has(.navtab) .content { + flex-grow: 1; + margin: 0; + padding: 16px; +} + +.box .content .box { + background: var(--background-color-2); + border-color: var(--border-color-2); +} + +.box .content a.box:hover { + background: linear-gradient(0deg, var(--background-color-hover), var(--background-color-2)); + cursor: pointer; +} + +.box hr { + border-color: var(--border-color); + border-width: 1px; +} + +.box.emote { + width: 96px; + height: 96px; +} + +.box.background { + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; + overflow: hidden; +} + +.box.background h1 { + color: white; + text-shadow: -1px 1px 4px black; +} + +.box.emote h1 { + font-size: 16px; + font-weight: 600; + + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.box.emote p { + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 10px; +} + +.box.emote:has(.emote-desc) img { + max-width: 64px; + max-height: 64px; +} + +.box.emote:has(.emote-desc.none) img { + max-width: 96px; + max-height: 96px; +} + +a.box { + text-decoration: none; + color: black; +} + +a.box:hover { + background: linear-gradient(0deg, var(--background-color-hover), var(--background-color-disabled)); + cursor: pointer; +} + +.emote-showcase { + flex-grow: 1; + display: flex; + justify-content: center; + align-items: center; + gap: 32px; + margin: 32px 0; +} + +.alert.red { + background: #e27777; + border-color: #9f5050; +} + +.emote-check { + position: relative; + left: 35px; + top: 10px; + width: 24px; + height: 24px; + z-index: 2; +} + +.emote:has(.emote-check) img { + margin-top: -15px; +} + +/** RATINGS */ +.rating.gemerald { + font-weight: bolder; + text-shadow: 0 0 5px blue; + color: #b4dbeb; +} + +.rating.gemerald img { + filter: hue-rotate(50deg) brightness(1.5); +} + +.rating.gem { + font-weight: bold; + color: #2e7b99; +} + +.rating.coal { + font-weight: bold; + color: #2d3335; + text-shadow: 0 0 2px black; +} + +.rating.brimstone { + font-weight: bolder; + color: orange; + text-shadow: 0 0 4px red; +} + +/** +------------- + ACCOUNTS +------------- +*/ + +.accman { + flex-grow: 0; + width: 400px; + + display: flex; + flex-direction: column; + gap: 16px; +} + +.badge { + padding: 2px 8px; + border-radius: 4px; + border-width: 1px; + border-style: solid; +} + +.badge img { + max-width: 16px; + max-height: 16px; + vertical-align: middle; +} + +/** +--------------------------------- + SOMETHING FROM TAILWINDCSS +--------------------------------- +*/ + +.row { + display: flex; + flex-direction: row; +} + +.column { + display: flex; + flex-direction: column; +} + +.grow { + flex-grow: 1; +} + +.small-gap { + gap: 8px; +} + +.big-gap { + gap: 32px; +} + +.no-gap { + gap: 0; +} + +.center { + justify-content: center; + align-items: center; +} + +.items-center { + align-items: center; +} + +.items-bottom { + align-items: end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-bottom { + justify-content: end; +} + +.font-small { + font-size: 12px; +} + +.p-16 { + padding: 16px; +} + +.m-8 { + margin: 8px; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.none { + display: none; +} + +.w-full { + width: 100%; +} + +.font-weight-normal, +.font-weight-normal th { + font-weight: normal; +} + +.rounded { + border-radius: 4px; +} + +/** +------------- + USER +------------- +*/ + +.background { + position: absolute; + background-position: center center; + top: 61px; + bottom: 0; + left: 0; + right: 0; + z-index: -1; +} + +.background-layer { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: var(--profile-background); +} + +/** +------------- + CHAT +------------- +*/ + +.chat-message { + background: linear-gradient(0deg, #202020, #303030); + color: #fff; + padding: 8px; +} + +.chat .chat-message:nth-child(even) { + background: linear-gradient(0deg, #353535, #454545); + + border-top: 1px solid #707070; + border-bottom: 1px solid #707070; +} + +.chat.rounded .chat-message:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.chat.rounded .chat-message:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} \ No newline at end of file diff --git a/static/txt/RULES b/static/txt/RULES new file mode 100644 index 0000000..0ec5b9e --- /dev/null +++ b/static/txt/RULES @@ -0,0 +1,2 @@ +Hey, admin! Write your rules in /static/txt/RULES file. +If you see this, VI VON \ No newline at end of file diff --git a/system/emotes/index.php b/system/emotes/index.php new file mode 100644 index 0000000..2a48408 --- /dev/null +++ b/system/emotes/index.php @@ -0,0 +1,250 @@ +prepare("SELECT e.*, +CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, +CASE WHEN up.private_profile = FALSE OR up.id = ? THEN u.username ELSE NULL END AS uploader_name, +r.name AS role_name, +r.badge_id AS role_badge_id, +ub.badge_id AS custom_badge_id +FROM emotes e +LEFT JOIN users u ON u.id = e.uploaded_by +LEFT JOIN user_preferences up ON up.id = u.id +LEFT JOIN role_assigns ra ON ra.user_id = u.id +LEFT JOIN roles r ON r.id = ra.role_id +LEFT JOIN user_badges ub ON ub.user_id = u.id +WHERE e.visibility = 2 +ORDER BY e.created_at DESC +LIMIT 25 +"); +$emote_results->execute([$current_user_id, $current_user_id]); + +$emote_results = $emote_results->fetchAll(PDO::FETCH_ASSOC); + +$emote = $emote_results[0] ?? null; + +if (isset($_GET["id"])) { + $stmt = $db->prepare("SELECT e.*, + CASE WHEN up.private_profile = FALSE OR up.id = ? THEN e.uploaded_by ELSE NULL END AS uploaded_by, + CASE WHEN up.private_profile = FALSE OR up.id = ? THEN u.username ELSE NULL END AS uploader_name, + r.name AS role_name, + r.badge_id AS role_badge_id, + ub.badge_id AS custom_badge_id + FROM emotes e + LEFT JOIN users u ON u.id = e.uploaded_by + LEFT JOIN user_preferences up ON up.id = u.id + LEFT JOIN role_assigns ra ON ra.user_id = u.id + LEFT JOIN roles r ON r.id = ra.role_id + LEFT JOIN user_badges ub ON ub.user_id = u.id + WHERE e.visibility = 2 AND e.id = ? + LIMIT 1"); + + $stmt->execute([$current_user_id, $current_user_id, $_GET["id"]]); + $emote = $stmt->fetch(PDO::FETCH_ASSOC) ?? null; +} + +?> + + + + + System panel - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ + +
+
+ +
+ '; + echo ''; + echo '' . $row["code"] . ''; + echo ' by '; + + if ($row["uploader_name"] == null) { + echo ANONYMOUS_DEFAULT_NAME . '*'; + } else { + echo $row["uploader_name"]; + } + + echo ''; + } + + if (empty($emote_results)) { + echo 'Everything is clear. Good job!'; + } + ?> +
+
+ +
+ +
+ '; + ?> +
+
+
+ '; + + echo ''; + + $path = $_SERVER["DOCUMENT_ROOT"] . '/static/userdata/emotes/' . $emote["id"] . "/{$size}x.webp"; + + echo '
'; + + if ($file_size = filesize($path)) { + $kb = sprintf("%.2f", $file_size / 1024); + echo "

{$kb}KB

"; + } + + if ($image_size = getimagesize($path)) { + echo "

$image_size[0]x$image_size[1]

"; + } + + echo '
'; + } + ?> +
+
+ + +
+ + prepare("SELECT t.code FROM tags t + INNER JOIN tag_assigns ta ON ta.emote_id = ? + WHERE t.id = ta.tag_id + "); + $stmt->execute([$emote["id"]]); + + $tags = $stmt->fetchAll(PDO::FETCH_ASSOC); + $tags = array_column($tags, "code"); + + if (!empty($tags)) { + echo ''; + } + ?> + + + + + + + + + + + + + + +
Tags'; + foreach ($tags as $tag) { + echo "$tag "; + } + echo '
Uploader"; + echo $username; + echo ""; + + if ($emote["role_badge_id"]) { + echo ' ## ' . $emote['; + } + + if ($emote["custom_badge_id"]) { + echo ' '; + } + + echo ', about ' . format_timestamp(time() - strtotime($emote["created_at"])) . " ago"; + ?>
NotesEmpty' ?>
Source + " + target="_blank"> +
+
+ +
+ " style="display: none;"> + +
+ +
+ + +
+
+ +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/system/emotes/verdict.php b/system/emotes/verdict.php new file mode 100644 index 0000000..df2f5ba --- /dev/null +++ b/system/emotes/verdict.php @@ -0,0 +1,80 @@ +prepare("SELECT id, code, uploaded_by FROM emotes WHERE id = ? AND visibility = 2 LIMIT 1"); +$stmt->execute([$id]); + +if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $verdict = 2; + + switch ($action) { + case "approve": { + $db->prepare("UPDATE emotes SET visibility = 1 WHERE id = ?") + ->execute([$row["id"]]); + $verdict = 1; + break; + } + case "reject": { + $db->prepare("UPDATE emotes SET visibility = 0 WHERE id = ?") + ->execute([$row["id"]]); + $verdict = 0; + break; + } + default: { + generate_alert("/system/emotes", "Unknown action"); + exit; + } + } + + $comment = str_safe($_POST["comment"] ?? "", null, false); + + if ($comment == "") { + $comment = null; + } + + $db->prepare("INSERT INTO mod_actions(user_id, emote_id, verdict, comment) VALUES (?, ?, ?, ?)") + ->execute([$_SESSION["user_id"], $row["id"], $verdict, $comment]); + + if ($row["uploaded_by"] != null) { + $contents = match ($verdict) { + 0 => 'Your emote "' . $row["code"] . '" has been unlisted! Anyone can add it via a direct link.', + 1 => 'Your emote "' . $row["code"] . '" has been approved! Enjoy!', + default => 'We did something with your emote "' . $row["code"] . '"' + }; + + if ($comment != null) { + $contents .= " Mod's comment: $comment"; + } + + $db->prepare("INSERT INTO inbox_messages(recipient_id, message_type, contents, link) VALUES (?, ?, ?, ?)") + ->execute([$row["uploaded_by"], "1", $contents, "/emotes?id=" . $row["id"]]); + } + + generate_alert("/system/emotes", 'Emote "' . $row["code"] . '" has been ' . ($verdict == 0 ? 'unlisted' : 'set to public') . '!', 200); + exit; +} + +generate_alert("system/emotes", "Emote ID $id not found", 404); \ No newline at end of file diff --git a/system/index.php b/system/index.php new file mode 100644 index 0000000..95b17a5 --- /dev/null +++ b/system/index.php @@ -0,0 +1,69 @@ + + + + + + System panel - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+
+ +
+ Emotes'; + + $results = $db->query("SELECT COUNT(*) FROM emotes WHERE visibility = 2")->fetch()[0]; + + if ($results > 0) { + echo " ($results pending)"; + } + + echo ''; + } + + if (REPORTS_ENABLE && $_SESSION["user_role"]["permission_report_review"]) { + echo 'Reports'; + + $results = $db->query("SELECT COUNT(*) FROM reports WHERE resolved_by IS NULL")->fetch()[0]; + + if ($results > 0) { + echo " ($results pending)"; + } + + echo ''; + } + ?> +
+
+
+
+
+ + + \ No newline at end of file diff --git a/users.php b/users.php new file mode 100644 index 0000000..6a6273e --- /dev/null +++ b/users.php @@ -0,0 +1,587 @@ +prepare("SELECT id, username, joined_at, last_active_at + FROM users + WHERE username LIKE ? + ORDER BY last_active_at DESC LIMIT ? OFFSET ?"); + $stmt->bindParam(1, $search, PDO::PARAM_STR); + $stmt->bindParam(2, $limit, PDO::PARAM_INT); + $stmt->bindParam(3, $offset, PDO::PARAM_INT); + $stmt->execute(); + + $count_stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username LIKE ?"); + $count_stmt->execute([$search]); + + $total_users = $count_stmt->fetch()[0]; + $total_pages = ceil($total_users / $limit); + + if ($is_json) { + header("Content-Type: application/json"); + echo json_encode([ + "status_code" => 200, + "message" => null, + "data" => [ + "all_user_count" => intval($all_user_count), + "users" => $stmt->fetchAll(PDO::FETCH_ASSOC) + ] + ]); + exit; + } + + echo '' ?> + + + + User list - <?php echo INSTANCE_NAME ?> + + + + + +
+
+ +
+ +
+
+ +
+ '; + echo ''; + echo 'UsernameLast active'; + echo ''; + while ($row = $stmt->fetch()) { + $diff = time() - strtotime($row["last_active_at"]); + + $last_active = "moments"; + + if ($diff > 5) { + $last_active = format_timestamp($diff); + } + + echo ''; + echo ''; + echo '' . $row["username"] . ''; + echo "$last_active ago"; + echo ''; + } + echo ''; + } else { + echo '

Nothing found...

'; + } + ?> +
+
+ 1) { + echo '' ?> + +
+ +
+ +
+
+
+ + + + prepare("SELECT u.id FROM users u + INNER JOIN connections co ON co.alias_id = ? AND co.platform = ? + WHERE co.user_id = u.id + "); + $stmt->execute([$alias_id, $platform]); + + if ($row = $stmt->fetch()) { + $user = User::get_user_by_id($db, $row["id"]); + } +} +// fetching user by internal id +else if (isset($_GET["id"])) { + $user = User::get_user_by_id($db, $_GET["id"]); +} + +if (!$user) { + generate_alert("/404.php", "The user you requested cannot be found", 404); + exit; +} + +// User preferences +$stmt = $db->prepare("SELECT * FROM user_preferences WHERE id = ?"); +$stmt->execute([$user->id]); + +$user_preferences = $stmt->fetch(PDO::FETCH_ASSOC); + +$public_profile = !$user_preferences["private_profile"] || $user->id == ($_SESSION["user_id"] ?? ""); + +// fetching emote sets +$emote_sets = Emoteset::get_all_user_emotesets($db, $user->id); +$active_emote_set = null; +foreach ($emote_sets as $es) { + if ($es->is_default) { + $active_emote_set = $es; + break; + } +} + +// gathering uploaded emotes +$uploaded_emotes = []; + +if ($public_profile) { + $stmt = $db->prepare("SELECT e.id, e.code, e.uploaded_by, e.source, e.visibility + FROM emotes e + WHERE e.uploaded_by = ? + ORDER BY e.created_at ASC + "); + $stmt->execute([$user->id]); + + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($rows as $row) { + array_push($uploaded_emotes, Emote::from_array_with_user($row, $db)); + } +} + +// gathering actions +$actions = []; + +if ($public_profile) { + $stmt = $db->prepare("SELECT a.* FROM actions a WHERE a.user_id = ? ORDER BY a.created_at DESC LIMIT 15"); + $stmt->execute([$user->id]); + $actions = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +// TODO: add functionality + +// calculating contributions +$stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE uploaded_by = ?"); +$stmt->execute([$user->id]); +$contributions = intval($stmt->fetch()[0]); + +$stmt = $db->prepare("SELECT COUNT(*) FROM ratings WHERE user_id = ?"); +$stmt->execute([$user->id]); + +$contributions += intval($stmt->fetch()[0]); + +// getting status +$stmt = $db->prepare("SELECT * FROM roles r INNER JOIN role_assigns ra ON ra.user_id = ? WHERE ra.role_id = r.id"); +$stmt->execute([$user->id]); + +$role = $stmt->fetch(PDO::FETCH_ASSOC) ?? null; + +// getting reactions +$stmt = $db->prepare("SELECT rate, COUNT(*) AS c FROM ratings WHERE user_id = ? GROUP BY rate ORDER BY c DESC"); +$stmt->execute([$user->id]); + +$fav_reactions = $stmt->fetchAll(PDO::FETCH_ASSOC); + +// getting favorite emote +$fav_emote = 1; + +// getting custom badge +$stmt = $db->prepare("SELECT b.* FROM badges b + INNER JOIN user_badges ub ON ub.user_id = ? + WHERE b.id = ub.badge_id +"); +$stmt->execute([$user->id]); + +$custom_badge = null; +if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $custom_badge = $row; +} + +if ($is_json) { + $user_data = (array) $user; + + unset($user_data["private_profile"]); + + $user_data["stats"] = [ + "contributions" => $contributions, + "favorite_reaction_id" => $fav_reactions, + "favorite_emote_id" => $fav_emote + ]; + + $user_data["active_emote_set_id"] = $active_emote_set->id; + $user_data["emote_sets"] = $emote_sets; + $user_data["uploaded_emotes"] = $uploaded_emotes; + $user_data["actions"] = $actions; + + json_response([ + "status_code" => 200, + "message" => null, + "data" => $user_data + ]); + exit; +} +?> + + + + + <?php echo sprintf("%s - %s", $user->username, INSTANCE_NAME) ?> + + + + + +
+
+
+ +
+
+ +
+ +
+ +
+ +
+ emotes)) { + html_display_emotes($active_emote_set->emotes); + } else { + echo '

No emotes found... ' . ((($_SESSION["user_id"] ?? "") == $id) ? 'Start adding emotes and they will appear here! :)

' : '

'); + } + } else { + echo "

This user doesn't have active emote set.

"; + } + ?> +
+
+ +
+ +
+ No emote sets found... ' . ((($_SESSION["user_id"] ?? "") == $id) ? 'Start adding emotes and you will have one! :)

' : '

'); + } + ?> +
+
+ + +
+ +
+ This user has done nothing bad or good...

"; + } + + foreach ($actions as $action) { + echo '
'; + + list($action_name, $preposition, $icon_name) = match ($action["action_type"]) { + "EMOTESET_ADD" => ["added", "to", "yes.png"], + "EMOTESET_REMOVE" => ["removed", "from", "no.png"], + "EMOTESET_ALIAS" => ["renamed", "in", "pencil.png"], + "EMOTE_CREATE" => ["created", null, "new_emote.png"], + "EMOTE_DELETE" => ["deleted", null, "deleted_emote.png"], + "EMOTE_RENAME" => ["renamed", null, "renamed_emote.png"] + }; + + echo "
"; + + echo '
'; + echo '

'; + echo '' . $user->username . ' '; + + $payload = json_decode($action["action_payload"], true); + + list($action_root, $action_sub) = explode("_", $action["action_type"]); + + switch ($action_root) { + case "EMOTESET": { + $e_stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE id = ?"); + $e_stmt->execute([$payload["emote"]["id"]]); + + echo "$action_name emote rowCount() == 1) { + echo '/emotes?id=' . $payload["emote"]["id"] . '">'; + echo ' '; + } else { + echo '">'; + } + + if (isset($payload["emote"]["original_code"])) { + echo $payload["emote"]["original_code"] . ' to '; + echo "rowCount() == 1) { + echo '/emotes?id=' . $payload["emote"]["id"] . '">'; + echo ' '; + } else { + echo '">'; + } + + echo $payload["emote"]["code"] . ''; + } else { + echo $payload["emote"]["code"] . ''; + } + + $es_stmt = $db->prepare("SELECT COUNT(*) FROM emote_sets WHERE id = ?"); + $es_stmt->execute([$payload["emoteset"]["id"]]); + + echo " $preposition rowCount() == 1) { + echo '/emotesets.php?id=' . $payload["emoteset"]["id"]; + } + + echo '">' . $payload["emoteset"]["name"] . ''; + break; + } + case "EMOTE": { + $e_stmt = $db->prepare("SELECT COUNT(*) FROM emotes WHERE id = ?"); + $e_stmt->execute([$payload["emote"]["id"]]); + + echo "$action_name emote rowCount() == 1) { + echo '/emotes?id=' . $payload["emote"]["id"] . '">'; + echo ' '; + } else { + echo '">'; + } + + echo $payload["emote"]["code"] . ''; + break; + } + default: { + echo "something that we don't know"; + break; + } + } + + echo '

'; + echo '[' . format_timestamp(time() - strtotime($action["created_at"])) . ' ago] '; + echo '
'; + } + ?> +
+
+ + +
+ +
+ +
+
+ +
+
+
+
+ + + + + \ No newline at end of file -- cgit v1.2.3