whoops thats not september

This commit is contained in:
pizzaboxer 2023-01-01 13:44:27 +00:00
parent dcc8e61201
commit 6b7d941c70
195 changed files with 9203 additions and 2791 deletions

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

8
.gitignore vendored
View File

@ -1,4 +1,4 @@
/thumbs/assets/*
/thumbs/avatars/*
/asset/files/*
/api/private/config.php
thumbs/assets/*
thumbs/avatars/*
asset/files/*
api/private/config.php

8
XD.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
pageBuilder::buildHeader();
?>
Do not run <code>game:HttpGet("http://polygon.pizzaboxer.xyz/XD")</code> in studio
please no
Plz
<?php pageBuilder::buildFooter();

View File

@ -1,6 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
users::requireAdmin();
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("System");
Users::RequireAdmin();
function getSetupUsage($clients)
{
@ -15,32 +16,40 @@ function getSetupUsage($clients)
}
else
{
$usage = system::getFolderSize("/var/www/pizzaboxer.xyz/setup$clients/", true);
$usage = System::GetFolderSize("/var/www/pizzaboxer.xyz/setup$clients/", true);
}
return $usage;
}
$servermemory = system::getMemoryUsage();
$usersOnline = users::getUsersOnline();
$pendingRenders = polygon::getPendingRenders();
$thumbPing = polygon::getServerPing(1);
$roles =
[
Users::STAFF_MODERATOR => "Moderator",
Users::STAFF_ADMINISTRATOR => "Administrator",
Users::STAFF_CATALOG => "Catalog Manager"
];
$servermemory = System::GetMemoryUsage();
$usersOnline = Users::GetUsersOnline();
$pendingRenders = Polygon::GetPendingRenders();
$thumbPing = Polygon::GetServerPing(1);
$usage = (object)
[
"Memory" => (object)
[
"Total" => system::getFileSize($servermemory->total),
"SytemUsage" => system::getFileSize($servermemory->total-$servermemory->free),
"PHPUsage" => system::getFileSize(memory_get_usage(true))
"Total" => System::GetFileSize($servermemory->total),
"SytemUsage" => System::GetFileSize($servermemory->total-$servermemory->free),
"PHPUsage" => System::GetFileSize(memory_get_usage(true))
],
"Disk" => (object)
[
"Total" => system::getFileSize(disk_total_space("/")),
"SystemUsage" => system::getFileSize(disk_total_space("/")-disk_free_space("/")),
"PolygonUsage" => system::getFolderSize("/var/www/pizzaboxer.xyz/polygon/"),
"SetupUsage" => system::getFileSize(getSetupUsage([2009, 2010, 2011, 2012]))
"Total" => System::GetFileSize(disk_total_space("/")),
"SystemUsage" => System::GetFileSize(disk_total_space("/")-disk_free_space("/")),
"PolygonUsage" => System::GetFolderSize("/var/www/pizzaboxer.xyz/polygon/"),
"ThumbnailUsage" => System::GetFolderSize("/var/www/pizzaboxer.xyz/polygoncdn/"),
"SetupUsage" => System::GetFileSize(getSetupUsage([2009, 2010, 2011, 2012]))
]
];
@ -51,45 +60,51 @@ pageBuilder::buildHeader();
<!--h1 style="position:absolute;opacity:0.5;font-size:10rem;z-index:10000000">THIS IS NOT <br> MULTAKOS SCREENSHOT LOL</h1-->
<h2 class="font-weight-normal"><?=SITE_CONFIG["site"]["name"]?> Administration</h2>
<div class="row">
<div class="col-md-7 p-0 divider-right">
<div class="pl-3">
<h3 class="pb-2 font-weight-normal">You are <?=vowel([1 => "Moderator", 2 => "Administrator"][SESSION["adminLevel"]])?> - Choose an action</h3>
<div class="row mx-1 mb-4">
<div class="col-md-4 py-2 pl-0">
<div class="col-md-7 divider-right">
<h3 class="pb-2 font-weight-normal">You are <?=vowel($roles[SESSION["adminLevel"]])?></h3>
<div class="row px-3 mb-2">
<?php if(Users::IsAdmin([Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR])) { ?>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-danger btn-lg btn-block px-0" href="/admin/moderate-user"><i class="fal fa-gavel"></i> Moderate user</a>
</div>
<div class="col-md-4 py-2 pl-0">
<?php } if(Users::IsAdmin()) { ?>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-danger btn-lg btn-block px-0" href="/admin/moderate-assets"><i class="fal fa-file-exclamation"></i> Moderate assets</a>
</div>
<div class="col-md-4 py-2 pl-0">
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/render-queue"><i class="fal fa-images"></i> Render queue</a>
</div>
<?php if(SESSION["adminLevel"] >= 2) { ?>
<div class="col-md-4 py-2 pl-0">
<?php } if(Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) { ?>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/site-banners"><i class="fal fa-bullhorn"></i> Site banners</a>
</div>
<div class="col-md-4 py-2 pl-0">
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="#" onclick="polygon.buildModal({header: 'coming soon', body: 'Sample Text', buttons: [{class:'btn btn-primary px-4', dismiss:true, text:'OK'}]});"><i class="fal fa-rss-square"></i> Newsfeed</a>
</div>
<div class="col-md-4 py-2 pl-0">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/staff-logs"><i class="fal fa-book"></i> Staff Logs</a>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/staff-audit"><i class="fal fa-book"></i> Audit log</a>
</div>
<div class="col-md-4 py-2 pl-0">
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/error-log"><i class="fal fa-exclamation-triangle"></i> Error log</a>
</div>
<?php } if(Users::IsAdmin([Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR])) { ?>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/create-asset"><i class="fal fa-file-plus"></i> Create asset</a>
</div>
<?php } if(SESSION["userId"] == 1){ ?>
<div class="col-md-4 py-2 pl-0">
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/give-asset"><i class="fal fa-gift"></i> Give asset</a>
</div>
<?php } if(Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) { ?>
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/give-currency"><i class="fal fa-pizza-slice"></i> Give <?=SITE_CONFIG["site"]["currency"]?></a>
</div>
<?php } ?>
<div class="col-md-4 py-2 pl-0">
<div class="col-md-4 py-2 px-1">
<a class="btn btn-outline-success btn-lg btn-block px-0" href="#" onclick="polygon.buildModal({header: 'Credentials', body: '<span>You\'ll have to enter these yourself!</span> <br> Username: <code>ProjectPolygon</code> <br> Password: <code>962e8f89341b4e5f208076b5d06fb1b6</code>', buttons: [{class:'btn btn-primary px-4', attributes: [{'attr': 'onclick', 'val': 'window.location = \'https://stats.pizzaboxer.xyz\''}], text:'Continue'}]})"><i class="fal fa-chart-pie"></i> Statistics</a>
</div>
<?php } ?>
</div>
</div>
</div>
<div class="col-md-5 p-0">
<div class="px-4">
<div class="col-md-5">
<h3 class="pb-3 font-weight-normal">Website / Server Info</h3>
<div class="card w-100 mt-2">
<div class="card-body text-center">
@ -106,7 +121,8 @@ pageBuilder::buildHeader();
<div class="card w-100 mt-2">
<div class="card-body text-center">
<h3 class="font-weight-normal"><i class="fal fa-hdd"></i> <?=$usage->Disk->SystemUsage?> / <?=$usage->Disk->Total?> Used</h3>
<small><?=SITE_CONFIG["site"]["name"]?> is using <?=$usage->Disk->PolygonUsage?></small><br>
<small><?=SITE_CONFIG["site"]["name"]?> is using <?=$usage->Disk->PolygonUsage?></small><br>
<small>Thumbnail CDN is using <?=$usage->Disk->ThumbnailUsage?></small><br>
<small>Client setup (2009-2012) is using <?=$usage->Disk->SetupUsage?> total</small>
</div>
@ -131,7 +147,6 @@ pageBuilder::buildHeader();
<?php if($usersOnline == 1) { ?><small>dead much?</small><?php } ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,5 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged" => true, "secure" => true]);
$wearing = isset($_POST["wearing"]) && $_POST["wearing"] == "true";
@ -14,8 +16,8 @@ if($wearing)
}
else
{
$type_str = catalog::getTypeByNum($type);
if(!catalog::getTypeByNum($type)) api::respond(400, false, "Invalid asset type");
$type_str = Catalog::GetTypeByNum($type);
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
$query = $pdo->prepare("SELECT COUNT(*) FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :uid AND assets.type = :type AND wearing = 0");
$query->bindParam(":type", $type, PDO::PARAM_INT);
}
@ -23,6 +25,8 @@ $query->bindParam(":uid", $userId, PDO::PARAM_INT);
$query->execute();
$pages = ceil($query->fetchColumn()/8);
if($page > $pages) $page = $pages;
if(!is_numeric($page) || $page < 1) $page = 1;
$offset = ($page - 1)*8;
if(!$pages) api::respond(200, true, $wearing ? 'You are not currently wearing anything' : 'You don\'t have any unequipped '.($type_str.(!str_ends_with($type_str, 's') ? 's' : '').' to wear'));

View File

@ -8,7 +8,7 @@ $bodyPart = $_POST["BodyPart"] ?? false;
$color = $_POST["Color"] ?? false;
if(!$color || !in_array($bodyPart, ["Head", "Torso", "Left Arm", "Right Arm", "Left Leg", "Right Leg"])) api::respond(400, false, "Bad Request");
$brickcolor = users::hex2bc(rgbtohex($color));
$brickcolor = Users::hex2bc(rgbtohex($color));
if(!$brickcolor) api::respond(200, false, "Invalid body color #".rgbtohex($color));
$bodyColors->{$bodyPart} = $brickcolor;
$bodyColors = json_encode($bodyColors);

View File

@ -2,6 +2,6 @@
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
polygon::requestRender("Avatar", SESSION["userId"]);
Polygon::RequestRender("Avatar", SESSION["userId"]);
api::respond(200, true, "OK");

107
api/account/get-feed.php Normal file
View File

@ -0,0 +1,107 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true]);
$FeedResults = db::run(
"SELECT feed.*, users.username FROM feed
INNER JOIN users ON users.id = feed.userId WHERE userId = :uid
OR groupId IS NULL AND userId IN
(
SELECT (CASE WHEN requesterId = :uid THEN receiverId ELSE requesterId END) FROM friends
WHERE requesterId = :uid OR receiverId = :uid AND status = 1
)
OR groupId IN
(
SELECT groups_members.GroupID FROM groups_members
INNER JOIN groups_ranks ON groups_ranks.GroupID = groups_members.GroupID AND groups_ranks.Rank = groups_members.Rank
WHERE groups_members.UserID = :uid AND groups_ranks.permissions LIKE '%\"CanViewGroupStatus\":true%'
)
ORDER BY feed.id DESC LIMIT 15",
[":uid" => SESSION["userId"]]
);
$feed = [];
$news = [];
/*$news[] =
[
"header" => '<h4 class="font-weight-normal">lol</h4>',
"message" => 'fucked your mom'
];*/
/*$news[] =
[
"header" => '<h4 class="font-weight-normal">this isn\'t dead!!!! (probably)</h4>',
"message" => "ive been more inclined to work on polygon now after like 4 months, so i guess development has resumed <br><br> 2fa has been implemented, and next on the roadmap is the catalog system. so yeah, stay tuned for that"
];*/
/* $news[] =
[
"header" => "",
"img" => "https://media.discordapp.net/attachments/745025397749448814/835635922590629888/HDKolobok-256px-3.gif",
// "message" => "What you know about KOLONBOK. ™ "
"message" => "KOLONBOK. ™ Has fix 2009"
]; */
/* $news[] =
[
"header" => "Groups have been released!",
"img" => "/img/ProjectPolygon.png",
"message" => "Groups have now been fully released, with more functionality than you could ever imagine. Groups don't cost anything to make, and you can join up to 20 of them. <br> If you haven't yet, come join the <a href=\"/groups?gid=1\">official group</a>!"
]; */
while($row = $FeedResults->fetch(PDO::FETCH_OBJ))
{
$timestamp = timeSince($row->timestamp);
if($row->groupId == NULL)
{
$feed[] =
[
"userName" => $row->username,
"img" => Thumbnails::GetAvatar($row->userId, 100, 100),
"header" => "<p class=\"m-0\"><a href=\"/user?ID={$row->userId}\">{$row->username}</a> - <small>{$timestamp}</small></p>",
"message" => Polygon::FilterText($row->text)
];
}
else
{
$GroupInfo = Groups::GetGroupInfo($row->groupId, true, true);
$feed[] =
[
"userName" => $GroupInfo->name,
"img" => Thumbnails::GetAssetFromID($GroupInfo->emblem, 420, 420),
"header" => "<p class=\"m-0\"><a href=\"/groups?gid={$GroupInfo->id}\">{$GroupInfo->name}</a> - <small>posted by <a href=\"/user?ID={$row->userId}\">{$row->username}</a></small> - <small>{$timestamp}</small></p>",
"message" => Polygon::FilterText($row->text)
];
}
}
$FeedCount = $FeedResults->rowCount();
if($FeedCount < 15)
{
$feed[] =
[
"userName" => "Your feed is currently empty!",
"img" => "/img/feed/friends.png",
"header" => "<h4 class=\"font-weight-normal\">Looks like your feed's empty</h4>",
"message" => "If you haven't made any friends yet, <a href='/browse'>go make some</a>! <br> If you already have some, why don't you kick off the discussion?"
];
if($FeedCount < 14)
{
$feed[] =
[
"userName" => "Customize your character",
"img" => "/img/feed/cart.png",
"header" => "<h4 class=\"font-weight-normal\">Customize your character</h4>",
"message" => "Log in every day and earn 10 pizzas. Pizzas can be used to buy clothing in our <a href=\"/catalog\">catalog</a>. You can also create your own clothing on the <a href=\"/develop\">Build page</a>."
];
}
}
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "feed" => $feed, "news" => $news]);

View File

@ -1,5 +1,8 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Games");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true]);
$userid = SESSION["userId"];
@ -16,10 +19,10 @@ $query->execute();
while($game = $query->fetch(PDO::FETCH_OBJ))
$items[] =
[
"game_name" => polygon::filterText($game->name),
"game_name" => Polygon::FilterText($game->name),
"game_id" => $game->id,
"game_thumbnail" => Thumbnails::GetAvatar($game->hoster, 250, 250),
"playing" => games::getPlayersInServer($game->id)->rowCount()
"playing" => Games::GetPlayersInServer($game->id)->rowCount()
];
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $items]);

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userid = SESSION["userId"];
@ -31,7 +32,7 @@ while($transaction = $query->fetch(PDO::FETCH_OBJ))
$transactions[] =
[
"type" => $type == "Sales" ? "Sold" : "Purchased",
"date" => date('n/j/y', $transaction->timestamp),
"date" => date('j/n/y', $transaction->timestamp),
"member_name" => $transaction->username,
"member_id" => $memberID,
"member_avatar" => Thumbnails::GetAvatar($memberID, 48, 48),

View File

@ -1,60 +0,0 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "logged_in" => true]);
$userid = SESSION["userId"];
$query = $pdo->prepare("
SELECT * FROM feed
WHERE userId = :uid
OR userId IN (SELECT receiverId FROM friends WHERE requesterId = :uid AND status = 1)
OR userId IN (SELECT requesterId FROM friends WHERE receiverId = :uid AND status = 1)
ORDER BY id DESC LIMIT 15");
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
$query->execute();
$feed = [];
$news = [];
/*$news[] =
[
"header" => '<h4 class="font-weight-normal">lol</h4>',
"message" => 'fucked your mom'
];*/
/*$news[] =
[
"header" => '<h4 class="font-weight-normal">this isn\'t dead!!!! (probably)</h4>',
"message" => "ive been more inclined to work on polygon now after like 4 months, so i guess development has resumed <br><br> 2fa has been implemented, and next on the roadmap is the catalog system. so yeah, stay tuned for that"
];*/
/* $news[] =
[
"header" => "",
"img" => "https://media.discordapp.net/attachments/745025397749448814/835635922590629888/HDKolobok-256px-3.gif",
// "message" => "What you know about KOLONBOK. ™ "
"message" => "KOLONBOK. ™ Has fix 2009"
]; */
while($row = $query->fetch(PDO::FETCH_OBJ))
{
$feed[] =
[
"userName" => users::getUserNameFromUid($row->userId),
"img" => Thumbnails::GetAvatar($row->userId, 100, 100),
"header" => '<p class="m-0"><a href="/user?ID='.$row->userId.'">'.users::getUserNameFromUid($row->userId).'</a> - <small>'.timeSince('@'.$row->timestamp).'</small></p>',
"message" => polygon::filterText($row->text)
];
}
if($query->rowCount() < 15)
{
$feed[] =
[
"userName" => "Your feed is currently empty!",
"img" => "/img/feed-starter.png",
"header" => '<h4 class="font-weight-normal">Looks like your feed\'s empty</h4>',
"message" => "If you haven't made any friends yet, <a href='/browse'>go make some</a>! <br> If you already have some, why don't you kick off the discussion?"
];
}
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "feed" => $feed, "news" => $news]);

View File

@ -1,20 +1,21 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Auth");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST['currentpwd']) || !isset($_POST['newpwd']) || !isset($_POST['confnewpwd'])) api::respond(400, false, "Bad Request");
$userid = SESSION["userId"];
$row = (object)SESSION["userInfo"];
$currentpwd = new auth($_POST['currentpwd']);
$newpwd = new auth($_POST['newpwd']);
$currentpwd = new Auth($_POST['currentpwd']);
$newpwd = new Auth($_POST['newpwd']);
if($row->lastpwdchange+1800 > time()) api::respond(429, false, "Please wait ".ceil((($row->lastpwdchange+1800)-time())/60)." minutes before attempting to change your password again");
if(!$currentpwd->verifyPassword($row->password)) api::respond(400, false, "Your current password does not match");
if(!$currentpwd->VerifyPassword($row->password)) api::respond(400, false, "Your current password does not match");
if($_POST['currentpwd'] == $_POST['newpwd']) api::respond(400, false, "Your new password cannot be the same as your current one");
if(strlen(preg_replace('/[0-9]/', "", $_POST['newpwd'])) < 6) api::respond(400, false, "Your new password is too weak. Make sure it contains at least six non-numeric characters");
if(strlen(preg_replace('/[^0-9]/', "", $_POST['newpwd'])) < 2) api::respond(400, false, "Your new password is too weak. Make sure it contains at least two numbers");
if($_POST['newpwd'] != $_POST['confnewpwd']) api::respond(400, false, "Confirmation password does not match");
$newpwd->updatePassword($userid);
$newpwd->UpdatePassword($userid);
api::respond(200, true, "OK");

View File

@ -1,5 +1,4 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
users::updatePing();
Users::UpdatePing();
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "friendRequests" => (int)SESSION["friendRequests"]]);

View File

@ -8,10 +8,11 @@ $userid = SESSION["userId"];
$filter = (int)($_POST['filter'] == 'true');
$debugging = (int)(isset($_POST['debugging']) && $_POST['debugging'] == 'true');
if(!in_array($_POST['theme'], ["light", "dark"])) api::respond(200, false, "Invalid theme");
if(!in_array($_POST['theme'], ["light", "dark", "hitius", "2014"])) api::respond(200, false, "Invalid theme");
if(!strlen($_POST['blurb'])) api::respond(200, false, "Your blurb can't be empty!");
if(strlen($_POST['blurb']) > 1000) api::respond(200, false, "Your blurb is too large!");
if(!strlen($_POST['blurb'])) api::respond(200, false, "Your blurb can't be empty");
if(strlen($_POST['blurb']) > 1000) api::respond(200, false, "Your blurb is too large");
if(Polygon::IsExplicitlyFiltered($_POST["blurb"])) api::respond(200, false, "Your blurb contains inappropriate text");
db::run(
"UPDATE users SET blurb = :blurb, filter = :filter, theme = :theme, debugging = :debugging WHERE id = :uid",

View File

@ -0,0 +1,44 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
Polygon::ImportClass("Discord");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userId = SESSION["userId"];
$status = $_POST['status'] ?? false;
if(!strlen($status)) api::respond(200, false, "Your status cannot be empty");
if(strlen($status) > 140) api::respond(200, false, "Your status cannot be more than 140 characters");
//ratelimit
$query = db::run(
"SELECT timestamp FROM feed WHERE userId = :uid AND groupID IS NULL AND timestamp+300 > UNIX_TIMESTAMP()",
[":uid" => $userId]
);
if($query->rowCount())
api::respond(200, false, "Please wait ".GetReadableTime($query->fetchColumn(), ["RelativeTime" => "5 minutes"])." before updating your status");
db::run("INSERT INTO feed (userId, timestamp, text) VALUES (:uid, UNIX_TIMESTAMP(), :status)", [":uid" => $userId, ":status" => $status]);
db::run("UPDATE users SET status = :status WHERE id = :uid", [":uid" => $userId, ":status" => $status]);
if(time() < strtotime("2021-09-07 00:00:00") && stripos($status, "#bezosgang") !== false && !Catalog::OwnsAsset(SESSION["userId"], 2802))
{
db::run(
"INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (2802, :uid, UNIX_TIMESTAMP())",
[":uid" => SESSION["userId"]]
);
}
Discord::SendToWebhook(
[
"username" => SESSION["userName"],
"content" => $status,
"avatar_url" => Thumbnails::GetAvatar(SESSION["userId"], 420, 420)
],
Discord::WEBHOOK_KUSH
);
api::respond(200, true, "OK");

View File

@ -1,28 +0,0 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userId = SESSION["userId"];
$status = $_POST['status'] ?? false;
if(!$status) api::respond(405, false, "Your status cannot be empty");
if(strlen($status) > 140) api::respond(405, false, "Your status cannot be more than 140 characters");
//ratelimit
$query = db::run("SELECT timestamp FROM feed WHERE userId = :uid AND timestamp+60 > UNIX_TIMESTAMP()", [":uid" => $userId]);
if($query->rowCount()) api::respond(400, false, "Please wait ".(($query->fetchColumn()+60)-time())." seconds before updating your status");
db::run("INSERT INTO feed (userId, timestamp, text) VALUES (:uid, UNIX_TIMESTAMP(), :status)", [":uid" => $userId, ":status" => $status]);
db::run("UPDATE users SET status = :status WHERE id = :uid", [":uid" => $userId, ":status" => $status]);
// $status = str_ireplace("http://", "", $status);
// $status = str_ireplace("https://", "", $status);
polygon::sendKushFeed([
"username" => SESSION["userName"],
"content" => $status,
"avatar_url" => Thumbnails::GetAvatar(SESSION["userId"], 420, 420)
]);
api::respond(200, true, "OK");

View File

@ -1,26 +0,0 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "admin_ratelimit" => true, "secure" => true]);
if(!isset($_POST["text"]) || !isset($_POST["bg-color"]) || !isset($_POST["text-color"])){ api::respond(400, false, "Invalid Request"); }
if($_POST["text-color"] != "dark" && $_POST["text-color"] != "light"){ api::respond(400, false, "Invalid Request"); }
if(!trim($_POST["text"])){ api::respond(400, false, "You haven't set the banner text"); }
if(strlen($_POST["text"]) > 128){ api::respond(400, false, "The banner text must be less than 128 characters"); }
if(!trim($_POST["bg-color"])){ api::respond(400, false, "You haven't set a background color"); }
if(!ctype_xdigit(ltrim($_POST["bg-color"], "#")) || strlen($_POST["bg-color"]) != 7){ api::respond(400, false, "That doesn't appear to be a valid hex color"); }
if($pdo->query("SELECT COUNT(*) FROM announcements WHERE activated")->fetchColumn() > 5){ api::respond(400, false, "There's too many banners currently active!"); }
$userId = SESSION["userId"];
$text = trim($_POST["text"]);
$color = trim($_POST["bg-color"]);
$textcolor = "text-".trim($_POST["text-color"]);
$query = $pdo->prepare("INSERT INTO announcements (createdBy, text, bgcolor, textcolor) VALUES (:uid, :text, :bgc, :tc)");
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
$query->bindParam(":text", $text, PDO::PARAM_STR);
$query->bindParam(":bgc", $color, PDO::PARAM_STR);
$query->bindParam(":tc", $textcolor, PDO::PARAM_STR);
$query->execute();
users::logStaffAction("[ Banners ] Created site banner with text: ".$text);
api::respond(200, true, "Banner has been created");

View File

@ -1,6 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "admin_ratelimit" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Forum");
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR], "admin_ratelimit" => true, "secure" => true]);
if(!isset($_POST['postType'])){ api::respond(400, false, "Bad Request"); }
if(!in_array($_POST['postType'], ["thread", "reply"])){ api::respond(400, false, "Bad Request"); }
@ -9,12 +10,12 @@ if(!is_numeric($_POST['postId'])){ api::respond(400, false, "Bad Request"); }
$userid = SESSION["userId"];
$isThread = $_POST['postType'] == "thread";
$threadInfo = $isThread ? forum::getThreadInfo($_POST['postId']) : forum::getReplyInfo($_POST['postId']);
$threadInfo = $isThread ? Forum::GetThreadInfo($_POST['postId']) : Forum::GetReplyInfo($_POST['postId']);
if(!$threadInfo){ api::respond(400, false, "Post does not exist"); }
$query = $isThread ? $pdo->prepare("UPDATE forum_threads SET deleted = 1 WHERE id = :id") : $pdo->prepare("UPDATE forum_replies SET deleted = 1 WHERE id = :id");
$query->bindParam(":id", $_POST['postId'], PDO::PARAM_INT);
if($query->execute()){ users::logStaffAction("[ Forums ] Deleted forum ".($isThread?"thread":"reply")." ID ".$_POST['postId']); api::respond(200, true, "OK"); }
if($query->execute()){ Users::LogStaffAction("[ Forums ] Deleted forum ".($isThread?"thread":"reply")." ID ".$_POST['postId']); api::respond(200, true, "OK"); }
else{ api::respond(500, false, "Internal Server Error"); }

View File

@ -1,12 +1,14 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "logged_in" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "admin" => [Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "logged_in" => true, "secure" => true]);
$type = $_POST["type"] ?? false;
$page = $_POST["page"] ?? 1;
$assets = [];
if(!catalog::getTypeByNum($type)) api::respond(400, false, "Invalid asset type");
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
$query = $pdo->prepare("SELECT COUNT(*) FROM assets WHERE creator = 2 AND type = :type ORDER BY id DESC");
$query->bindParam(":type", $type, PDO::PARAM_INT);
@ -21,7 +23,7 @@ $query->execute();
while($asset = $query->fetch(PDO::FETCH_OBJ))
{
$info = catalog::getItemInfo($asset->id);
$info = Catalog::GetAssetInfo($asset->id);
$assets[] =
[

View File

@ -1,17 +1,24 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
$page = $_POST["page"] ?? 1;
$assets = [];
$query = $pdo->query("SELECT COUNT(*) FROM assets WHERE NOT approved AND type != 1");
$query = $pdo->query("SELECT COUNT(*) FROM assets WHERE NOT approved AND (type != 1 || (SELECT COUNT(*) FROM polygon.groups WHERE emblem = assets.id))");
$pages = ceil($query->fetchColumn()/18);
$offset = ($page - 1)*18;
if(!$pages) api::respond(200, true, "There are no assets to approve");
$query = $pdo->prepare("SELECT assets.*, users.username FROM assets INNER JOIN users ON creator = users.id WHERE NOT approved AND type != 1 LIMIT 18 OFFSET :offset");
$query = $pdo->prepare(
"SELECT assets.*, users.username FROM assets
INNER JOIN users ON creator = users.id
WHERE NOT approved AND (type != 1 || (SELECT COUNT(*) FROM polygon.groups WHERE emblem = assets.id))
LIMIT 18 OFFSET :offset"
);
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
$query->execute();
@ -26,7 +33,7 @@ while($asset = $query->fetch(PDO::FETCH_OBJ))
"texture_id" => $asset->imageID,
"creator_id" => $asset->creator,
"creator_name" => $asset->username,
"type" => catalog::getTypeByNum($asset->type),
"type" => Catalog::GetTypeByNum($asset->type),
"created" => date("j/n/y G:i A", $asset->created),
"price" => $asset->sale ? $asset->price ? '<i class="fal fa-pizza-slice"></i> '.$asset->price : "Free" : "Off-Sale"
];

View File

@ -26,13 +26,13 @@ $output_array = [];
if($emergency)
{
$webhook .= sprintf("[%s] Git Pull intiated by %s\n", date('d/m/Y h:i:s A'), "[[[EMERGENCY]]]");
$webhook .= sprintf("[%s] Git Pull intiated by %s on %s\n", date('d/m/Y h:i:s A'), "[[[OVERRIDE]]]", $_SERVER["HTTP_HOST"]);
}
else
{
require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
if(!SESSION || !SESSION["adminLevel"]) die(http_response_code(404));
$webhook .= sprintf("[%s] Git Pull executed by %s\n", date('d/m/Y h:i:s A'), SESSION["userName"]);
if(!Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) die(http_response_code(404));
$webhook .= sprintf("[%s] Git Pull executed by %s on %s\n", date('d/m/Y h:i:s A'), SESSION["userName"], $_SERVER["HTTP_HOST"]);
}
exec("git pull 2>&1", $output_array, $exitcode);
@ -46,4 +46,5 @@ $webhook .= "```yaml\n";
$webhook .= $output;
$webhook .= "```";
// sendSystemWebhook($webhook);
require $_SERVER["DOCUMENT_ROOT"]."/api/private/components/Discord.php";
Discord::SendToWebhook(["content" => $webhook], Discord::WEBHOOK_POLYGON, false);

View File

@ -1,6 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "admin_ratelimit" => true, "secure" => true]);
api::initialize(["method" => "POST", "admin" => Users::STAFF_ADMINISTRATOR, "admin_ratelimit" => true, "secure" => true]);
if(SESSION["userId"] != 1){ api::respond(400, false, "Insufficient admin level"); }
if(!isset($_POST["username"]) || !isset($_POST["amount"]) || !isset($_POST["reason"])){ api::respond(400, false, "Invalid Request"); }
@ -13,7 +13,7 @@ if($_POST["amount"] > 500 || $_POST["amount"] < -500){ api::respond(400, false,
if(!trim($_POST["reason"])){ api::respond(400, false, "You must set a reason"); }
$amount = $_POST["amount"];
$userInfo = users::getUserInfoFromUserName($_POST["username"]);
$userInfo = Users::GetInfoFromName($_POST["username"]);
if(!$userInfo){ api::respond(400, false, "That user doesn't exist"); }
if(($userInfo->currency + $_POST["amount"]) < 0){ api::respond(400, false, "That'll make the user go bankrupt!"); }
@ -22,5 +22,5 @@ $query->bindParam(":amount", $amount, PDO::PARAM_INT);
$query->bindParam(":uid", $userInfo->id, PDO::PARAM_INT);
$query->execute();
users::logStaffAction("[ Currency ] Gave ".$_POST["amount"]." ".SITE_CONFIG["site"]["currency"]." to ".$userInfo->username." ( user ID ".$userInfo->id." ) ( Reason: ".$_POST["reason"]." )");
Users::LogStaffAction("[ Currency ] Gave ".$_POST["amount"]." ".SITE_CONFIG["site"]["currency"]." to ".$userInfo->username." ( user ID ".$userInfo->id." ) ( Reason: ".$_POST["reason"]." )");
api::respond(200, true, "Gave ".$_POST["amount"]." ".SITE_CONFIG["site"]["currency"]." to ".$userInfo->username);

View File

@ -1,12 +1,13 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
$assetId = $_POST['assetID'] ?? false;
$action = $_POST['action'] ?? false;
$action_sql = $action == "approve" ?: 2;
$reason = $_POST['reason'] ?? false;
$asset = catalog::getItemInfo($assetId);
$asset = Catalog::GetAssetInfo($assetId);
if(!in_array($action, ["approve", "decline"])) api::respond(400, false, "Invalid request");
if(!$asset) api::respond(400, false, "Asset does not exist");
@ -17,5 +18,5 @@ $query->bindParam(":id", $asset->id, PDO::PARAM_INT);
$query->bindParam(":image", $asset->imageID, PDO::PARAM_INT);
$query->execute();
users::logStaffAction('[ Asset Moderation ] '.ucfirst($action).'d "'.$asset->name.'" [ID '.$asset->id.']'.($reason ? ' with reason: '.$reason : ''));
Users::LogStaffAction('[ Asset Moderation ] '.ucfirst($action).'d "'.$asset->name.'" [ID '.$asset->id.']'.($reason ? ' with reason: '.$reason : ''));
api::respond(200, true, '"'.htmlspecialchars($asset->name).'" has been '.$action.'d');

View File

@ -1,6 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "admin_ratelimit" => true, "secure" => true]);
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR], "admin_ratelimit" => true, "secure" => true]);
if(!isset($_POST["username"]) || !isset($_POST["banType"]) || !isset($_POST["moderationNote"]) || !isset($_POST["until"])){ api::respond(400, false, "Bad Request"); }
if($_POST["banType"] < 1 || $_POST["banType"] > 4){ api::respond(400, false, "Bad Request"); }
@ -11,7 +11,7 @@ if($_POST["banType"] == 2 && empty($_POST["until"])){ api::respond(200, false, "
$banType = $_POST["banType"];
$staffNote = isset($_POST["staffNote"]) && $_POST["staffNote"] ? $_POST["staffNote"] : "";
$userId = SESSION["userId"];
$bannerInfo = users::getUserInfoFromUserName($_POST["username"]);
$bannerInfo = Users::GetInfoFromName($_POST["username"]);
$reason = $_POST["moderationNote"];
$bannedUntil = $_POST["banType"] == 2 ? strtotime($_POST["until"]." ".date('G:i:s')) : 0;
@ -19,14 +19,14 @@ if(!$bannerInfo){ api::respond(200, false, "User does not exist"); }
if($banType == 4)
{
if(!users::getUserModeration($bannerInfo->id)){ api::respond(200, false, "That user isn't banned!"); }
users::undoUserModeration($bannerInfo->id, true);
if(!Users::GetUserModeration($bannerInfo->id)){ api::respond(200, false, "That user isn't banned!"); }
Users::UndoUserModeration($bannerInfo->id, true);
}
else
{
// if($bannerInfo->id == $userId){ api::respond(200, false, "You cannot moderate yourself!"); }
// if($bannerInfo->adminlevel){ api::respond(200, false, "You cannot moderate a staff member"); }
if(users::getUserModeration($bannerInfo->id)){ api::respond(200, false, "That user is already banned!"); }
if(Users::GetUserModeration($bannerInfo->id)){ api::respond(200, false, "That user is already banned!"); }
if($banType == 2 && $bannedUntil < strtotime('tomorrow')){ api::respond(200, false, "Ban time must be at least 1 day long"); }
$query = $pdo->prepare("INSERT INTO bans (userId, bannerId, timeStarted, timeEnds, reason, banType, note) VALUES (:bid, :uid, UNIX_TIMESTAMP(), :ends, :reason, :type, :note)");
@ -55,5 +55,5 @@ $staff =
4 => "Unbanned ".$bannerInfo->username
];
users::logStaffAction("[ User Moderation ] ".$staff[$banType]." ( user ID ".$bannerInfo->id." )");
Users::LogStaffAction("[ User Moderation ] ".$staff[$banType]." ( user ID ".$bannerInfo->id." )");
api::respond(200, true, $bannerInfo->username." has been ".$text[$banType]);

View File

@ -1,6 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "secure" => true]);
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR], "secure" => true]);
if(!isset($_POST["banType"]) || !isset($_POST["moderationNote"]) || !isset($_POST["until"])){ api::respond(400, false, "Invalid Request"); }
if($_POST["banType"] < 1 || $_POST["banType"] > 3){ api::respond(400, false, "Invalid Request"); }

View File

@ -1,6 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
$renderType = $_POST['renderType'] ?? false;
$assetID = $_POST['assetID'] ?? false;
@ -11,24 +12,24 @@ if(!$assetID || !is_numeric($assetID)) api::respond(400, false, "Bad Request");
if($renderType == "Asset")
{
$asset = catalog::getItemInfo($assetID);
$asset = Catalog::GetAssetInfo($assetID);
if(!$asset) api::respond(200, false, "The asset you requested does not exist");
switch($asset->type)
{
case 4: polygon::requestRender("Mesh", $assetID); break; // mesh
case 8: case 19: polygon::requestRender("Model", $assetID); break; // hat/gear
case 11: case 12: polygon::requestRender("Clothing", $assetID); break; // shirt/pants
case 17: polygon::requestRender("Head", $assetID); break; // head
case 10: polygon::requestRender("UserModel", $assetID); break; // user generated model
case 4: Polygon::RequestRender("Mesh", $assetID); break; // mesh
case 8: case 19: Polygon::RequestRender("Model", $assetID); break; // hat/gear
case 11: case 12: Polygon::RequestRender("Clothing", $assetID); break; // shirt/pants
case 17: Polygon::RequestRender("Head", $assetID); break; // head
case 10: Polygon::RequestRender("UserModel", $assetID); break; // user generated model
default: api::respond(200, false, "This asset cannot be re-rendered");
}
}
else if($renderType == "Avatar")
{
$user = users::getUserInfoFromUid($assetID);
$user = Users::GetInfoFromID($assetID);
if(!$user) api::respond(200, false, "The user you requested does not exist");
polygon::requestRender("Avatar", $assetID);
Polygon::RequestRender("Avatar", $assetID);
}
users::logStaffAction("[ Render ] Re-rendered $renderType ID $assetID");
Users::LogStaffAction("[ Render ] Re-rendered $renderType ID $assetID");
api::respond(200, true, "Render request has been successfully submitted! See render status <a href='/admin/render-queue'>here</a>");

View File

@ -1,18 +1,21 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST", "admin" => true, "secure" => true]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Image");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "admin" => [Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "secure" => true]);
$file = $_FILES["file"] ?? false;
$name = $_POST["name"] ?? false;
$type = $_POST["type"] ?? false;
$uploadas = $_POST["creator"] ?? "Polygon";
$creator = users::getUidFromUserName($uploadas);
$creator = Users::GetIDFromName($uploadas);
if(!$file) api::respond(200, false, "You must select a file");
if(!$name) api::respond(200, false, "You must specify a name");
if(strlen($name) > 50) api::respond(200, false, "Name cannot be longer than 50 characters");
if(!$creator) api::respond(400, false, "The user you're trying to create as does not exist");
if(polygon::filterText($name, false, false, true) != $name) api::respond(400, false, "The name contains inappropriate text");
if(Polygon::FilterText($name, false, false, true) != $name) api::respond(400, false, "The name contains inappropriate text");
//$lastCreation = $pdo->query("SELECT created FROM assets WHERE creator = 2 ORDER BY id DESC")->fetchColumn();
//if($lastCreation+60 > time()) api::respond(400, false, "Please wait ".(60-(time()-$lastCreation))." seconds before creating a new asset");
@ -21,72 +24,72 @@ if($type == 1) //image - this is for textures and stuff
{
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(400, false, "Must be a .png or .jpg file");
polygon::importLibrary("class.upload");
Polygon::ImportLibrary("class.upload");
$image = new Upload($file);
if(!$image->uploaded) api::respond(500, false, "Failed to process image - please contact an admin");
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
$image->image_convert = 'png';
$imageId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
image::process($image, ["name" => "$imageId", "resize" => false, "dir" => "/asset/files/"]);
$imageId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
Image::Process($image, ["name" => "$imageId", "resize" => false, "dir" => "/asset/files/"]);
Thumbnails::UploadAsset($image, $imageId, 60, 62, ["keepRatio" => true, "align" => "C"]);
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
}
elseif($type == 3) // audio
{
if(!in_array($file["type"], ["audio/mpeg", "audio/ogg", "audio/mid", "audio/wav", "video/ogg"])) api::respond(400, false, "Must be an mpeg, wav, ogg or midi audio. - ".$file["type"]);
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "audioType" => $file["type"], "approved" => 1]);
if(!in_array($file["type"], ["audio/mpeg", "audio/ogg", "audio/mid", "audio/wav"])) api::respond(400, false, "Must be an mpeg, wav, ogg or midi audio. - ".$file["type"]);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "audioType" => $file["type"], "approved" => 1]);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
image::renderfromimg("audio", $assetId);
Image::RenderFromStaticImage("audio", $assetId);
}
elseif($type == 4) //mesh
{
if(!str_ends_with($file["name"], ".mesh")) api::respond(400, false, "Must be a .mesh file");
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
polygon::requestRender("Mesh", $assetId);
Polygon::RequestRender("Mesh", $assetId);
}
elseif($type == 5) //lua
{
if(!str_ends_with($file["name"], ".lua")) api::respond(400, false, "Must be a .lua file");
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
image::renderfromimg("Script", $assetId);
Image::RenderFromStaticImage("Script", $assetId);
}
elseif($type == 8) //hat
{
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be a .rbxm or .xml file");
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
polygon::requestRender("Model", $assetId);
Polygon::RequestRender("Model", $assetId);
}
elseif($type == 17) //head
{
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be a .rbxm or .xml file");
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
polygon::requestRender("Head", $assetId);
Polygon::RequestRender("Head", $assetId);
}
elseif($type == 18) //faces are literally just decals lmao (with a minor alteration to the xml)
{
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(400, false, "Must be a .png or .jpg file");
polygon::importLibrary("class.upload");
Polygon::ImportLibrary("class.upload");
$image = new Upload($file);
if(!$image->uploaded) api::respond(500, false, "Failed to process image - please contact an admin");
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
$image->image_convert = 'png';
$imageId = catalog::createAsset(["type" => 1, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
image::process($image, ["name" => "$imageId", "resize" => false, "dir" => "/asset/files/"]);
$imageId = Catalog::CreateAsset(["type" => 1, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1]);
Image::Process($image, ["name" => "$imageId", "resize" => false, "dir" => "/asset/files/"]);
Thumbnails::UploadAsset($image, $imageId, 60, 62, ["keepRatio" => true, "align" => "C"]);
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
$itemId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "imageID" => $imageId, "approved" => 1]);
$itemId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "imageID" => $imageId, "approved" => 1]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, catalog::generateGraphicXML("Face", $imageId));
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("Face", $imageId));
Thumbnails::UploadAsset($image, $itemId, 420, 230);
Thumbnails::UploadAsset($image, $itemId, 420, 420);
@ -101,10 +104,10 @@ elseif($type == 19) //gear
{
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be a .rbxm or .xml file");
$assetId = catalog::createAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1, "gear_attributes" => '{"melee":false,"powerup":false,"ranged":false,"navigation":false,"explosive":false,"musical":false,"social":false,"transport":false,"building":false}']);
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => "", "approved" => 1, "gear_attributes" => '{"melee":false,"powerup":false,"ranged":false,"navigation":false,"explosive":false,"musical":false,"social":false,"transport":false,"building":false}']);
copy($file["tmp_name"], $_SERVER['DOCUMENT_ROOT']."/asset/files/".$assetId);
polygon::requestRender("Model", $assetId);
Polygon::RequestRender("Model", $assetId);
}
users::logStaffAction("[ Asset creation ] Created \"$name\" [ID ".($itemId ?? $assetId ?? $imageId)."]");
api::respond_custom(["status" => 200, "success" => true, "message" => "<a href='/item?ID=".($itemId ?? $assetId ?? $imageId)."'>".catalog::getTypeByNum($type)."</a> successfully created!"]);
Users::LogStaffAction("[ Asset creation ] Created \"$name\" [ID ".($itemId ?? $assetId ?? $imageId)."]");
api::respond_custom(["status" => 200, "success" => true, "message" => "<a href='/item?ID=".($itemId ?? $assetId ?? $imageId)."'>".Catalog::GetTypeByNum($type)."</a> successfully created!"]);

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
api::initialize();
if(!isset($_GET['assetID'])) api::respond(400, false, "Bad Request");
@ -29,7 +30,7 @@ while($row = $query->fetch(PDO::FETCH_OBJ))
"commenter_name" => $row->username,
"commenter_id" => $row->author,
"commenter_avatar" => Thumbnails::GetAvatar($row->author, 110, 110),
"content" => nl2br(polygon::filterText($row->content))
"content" => nl2br(Polygon::FilterText($row->content))
];
}

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST['assetID']) || !isset($_POST['content']));
@ -8,7 +9,7 @@ $uid = SESSION["userId"];
$id = $_POST['assetID'];
$content = $_POST['content'];
$item = catalog::getItemInfo($id);
$item = Catalog::GetAssetInfo($id);
if(!$item) api::respond(400, false, "Asset does not exist");
if(!$item->comments) api::respond(400, false, "Comments are unavailable for this asset");
if(!strlen($content)) api::respond(400, false, "Comment cannot be empty");
@ -17,8 +18,7 @@ if(strlen($content) > 100) api::respond(400, false, "Comment cannot be longer th
$query = $pdo->prepare("SELECT time FROM asset_comments WHERE time+60 > UNIX_TIMESTAMP() AND author = :uid");
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
$query->execute();
$lastComment = $query->fetchColumn();
if($lastComment) api::respond(400, false, "Please wait ".(60-(time()-$lastComment))." seconds before posting a new comment");
if($query->rowCount()) api::respond(400, false, "Please wait ".GetReadableTime($query->fetchColumn(), ["RelativeTime" => "1 minute"])." before posting a new comment");
$query = $pdo->prepare("INSERT INTO asset_comments (author, content, assetID, time) VALUES (:uid, :content, :aid, UNIX_TIMESTAMP())");
$query->bindParam(":uid", $uid, PDO::PARAM_INT);

View File

@ -1,5 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
function getPrice($price)
@ -11,12 +13,14 @@ $uid = SESSION["userId"];
$id = $_POST['id'] ?? false;
$price = $_POST['price'] ?? 0;
$item = catalog::getItemInfo($id);
$item = Catalog::GetAssetInfo($id);
if(!$item) api::respond(400, false, "Asset does not exist");
if(catalog::ownsAsset($uid, $id)) api::respond(400, false, "User already owns asset");
if(Catalog::OwnsAsset($uid, $id)) api::respond(400, false, "User already owns asset");
if(!$item->sale) api::respond(400, false, "Asset is off-sale");
if(SESSION["currency"] - $item->price < 0) api::respond(400, false, "User cannot afford asset");
if($item->price != $price)
{
die(json_encode(
[
"status" => 200,
@ -28,12 +32,23 @@ if($item->price != $price)
"footer" => 'Your balance after this transaction will be <i class="fal fa-pizza-slice"></i> '.(SESSION["currency"] - $item->price),
"newprice" => $item->price
]));
}
$query = $pdo->prepare("UPDATE users SET currency = currency - :price WHERE id = :uid; UPDATE users SET currency = currency + :price WHERE id = :seller");
$query->bindParam(":price", $item->price, PDO::PARAM_INT);
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
$query->bindParam(":seller", $item->creator, PDO::PARAM_INT);
$query->execute();
$IsAlt = false;
foreach(Users::GetAlternateAccounts($item->creator) as $alt)
{
if($alt["userid"] == $uid) $IsAlt = true;
}
if(!$IsAlt)
{
$query = $pdo->prepare("UPDATE users SET currency = currency - :price WHERE id = :uid; UPDATE users SET currency = currency + :price WHERE id = :seller");
$query->bindParam(":price", $item->price, PDO::PARAM_INT);
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
$query->bindParam(":seller", $item->creator, PDO::PARAM_INT);
$query->execute();
}
$query = $pdo->prepare("INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (:aid, :uid, UNIX_TIMESTAMP())");
$query->bindParam(":aid", $id, PDO::PARAM_INT);
@ -47,6 +62,14 @@ $query->bindParam(":aid", $id, PDO::PARAM_INT);
$query->bindParam(":price", $item->price, PDO::PARAM_INT);
$query->execute();
if(time() < strtotime("2021-09-07 00:00:00") && $id == 2692 && !Catalog::OwnsAsset(SESSION["userId"], 2800))
{
db::run(
"INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (2800, :uid, UNIX_TIMESTAMP())",
[":uid" => SESSION["userId"]]
);
}
die(json_encode(
[
"status" => 200,
@ -54,6 +77,6 @@ die(json_encode(
"message" => "OK",
"header" => "Purchase Complete!",
"image" => Thumbnails::GetAsset($item, 110, 110),
"text" => "You have successfully purchased the ".htmlspecialchars($item->name)." ".catalog::getTypeByNum($item->type)." from ".$item->username." for ".getPrice($item->price),
"text" => "You have successfully purchased the ".htmlspecialchars($item->name)." ".Catalog::GetTypeByNum($item->type)." from ".$item->username." for ".getPrice($item->price),
"buttons" => [['class' => 'btn btn-primary continue-shopping', 'dismiss' => true, 'text' => 'Continue Shopping']],
]));

View File

@ -1,5 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userid = SESSION["userId"];
@ -7,7 +9,7 @@ $type = $_POST["type"] ?? false;
$page = $_POST["page"] ?? 1;
$assets = [];
if(!catalog::getTypeByNum($type)) api::respond(400, false, "Invalid asset type");
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
$query = $pdo->prepare("SELECT * FROM assets WHERE creator = :uid AND type = :type ORDER BY id DESC");
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
@ -16,7 +18,7 @@ $query->execute();
while($asset = $query->fetch(PDO::FETCH_OBJ))
{
$info = catalog::getItemInfo($asset->id);
$info = Catalog::GetAssetInfo($asset->id);
$assets[] =
[

View File

@ -1,5 +1,8 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Image");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userid = SESSION["userId"];
@ -7,17 +10,17 @@ $file = $_FILES["file"] ?? false;
$name = $_POST["name"] ?? false;
$type = $_POST["type"] ?? false;
if(!$file) api::respond(400, false, "You must select a file");
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(400, false, "Must be a .png or .jpg file");
if(!$name) api::respond(400, false, "You must specify a name");
if(polygon::filterText($name, false, false, true) != $name) api::respond(400, false, "The name contains inappropriate text");
if(!in_array($type, [2, 11, 12, 13])) api::respond(400, false, "You can't upload that type of content!");
if(!$file) api::respond(200, false, "You must select a file");
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(200, false, "Must be a .png or .jpg file");
if(!$name) api::respond(200, false, "You must specify a name");
if(Polygon::IsExplicitlyFiltered($name)) api::respond(200, false, "The name contains inappropriate text");
if(!in_array($type, [2, 11, 12, 13])) api::respond(200, false, "You can't upload that type of content!");
$query = $pdo->prepare("SELECT created FROM assets WHERE creator = :uid ORDER BY id DESC");
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
$query->execute();
$lastCreation = $query->fetchColumn();
if($lastCreation+30 > time()) api::respond(400, false, "Please wait ".(30-(time()-$lastCreation))." seconds before creating a new asset");
if($lastCreation+30 > time()) api::respond(200, false, "Please wait ".(30-(time()-$lastCreation))." seconds before creating a new asset");
// tshirts are a bit messy but straightforward:
// the image asset itself must be 128x128 with the texture resized to preserve aspect ratio
@ -57,35 +60,35 @@ if($lastCreation+30 > time()) api::respond(400, false, "Please wait ".(30-(time(
// Decal/Face |yes (s)| |yes (s)| yes (s) | yes (s) | | yes (s) | yes (s) | yes (s) | yes (s) |
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
polygon::importLibrary("class.upload");
Polygon::ImportLibrary("class.upload");
$image = new Upload($file);
if(!$image->uploaded) api::respond(200, false, "Failed to process image - please contact an admin");
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
$image->image_convert = 'png';
$imageId = catalog::createAsset(["type" => 1, "creator" => SESSION["userId"], "name" => $name, "description" => catalog::getTypeByNum($type)." Image"]);
$imageId = Catalog::CreateAsset(["type" => 1, "creator" => SESSION["userId"], "name" => $name, "description" => Catalog::GetTypeByNum($type)." Image"]);
if($type == 2) //tshirt
{
image::process($image, ["name" => "$imageId", "keepRatio" => true, "align" => "T", "x" => 128, "y" => 128, "dir" => "/asset/files/"]);
Image::Process($image, ["name" => "$imageId", "keepRatio" => true, "align" => "T", "x" => 128, "y" => 128, "dir" => "/asset/files/"]);
Thumbnails::UploadAsset($image, $imageId, 60, 62, ["keepRatio" => true, "align" => "T"]);
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "T"]);
$itemId = catalog::createAsset(["type" => 2, "creator" => SESSION["userId"], "name" => $name, "description" => "T-Shirt", "imageID" => $imageId]);
$itemId = Catalog::CreateAsset(["type" => 2, "creator" => SESSION["userId"], "name" => $name, "description" => "T-Shirt", "imageID" => $imageId]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, catalog::generateGraphicXML("T-Shirt", $imageId));
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("T-Shirt", $imageId));
//process initial tshirt thumbnail
$template = imagecreatefrompng($_SERVER['DOCUMENT_ROOT']."/img/tshirt-template.png");
$shirtdecal = image::resize(SITE_CONFIG['paths']['thumbs_assets']."/$imageId-420x420.png", 250, 250);
$shirtdecal = Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$imageId-420x420.png", 250, 250);
imagesavealpha($template, true);
imagesavealpha($shirtdecal, true);
image::merge($template, $shirtdecal, 85, 85, 0, 0, 250, 250, 100);
Image::MergeLayers($template, $shirtdecal, 85, 85, 0, 0, 250, 250, 100);
imagepng($template, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png");
image::resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 100, 100, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-100x100.png");
image::resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 110, 110, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-110x110.png");
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 100, 100, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-100x100.png");
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 110, 110, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-110x110.png");
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-100x100.png");
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-110x110.png");
@ -93,31 +96,31 @@ if($type == 2) //tshirt
}
elseif($type == 11 || $type == 12) //shirt / pants
{
image::process($image, ["name" => "$imageId", "x" => 585, "y" => 559, "dir" => "/asset/files/"]);
Image::Process($image, ["name" => "$imageId", "x" => 585, "y" => 559, "dir" => "/asset/files/"]);
Thumbnails::UploadAsset($image, $imageId, 60, 62, ["keepRatio" => true, "align" => "C"]);
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
$itemId = catalog::createAsset(["type" => $type, "creator" => SESSION["userId"], "name" => $name, "description" => catalog::getTypeByNum($type), "imageID" => $imageId]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, catalog::generateGraphicXML(catalog::getTypeByNum($type), $imageId));
polygon::requestRender("Clothing", $itemId);
$itemId = Catalog::CreateAsset(["type" => $type, "creator" => SESSION["userId"], "name" => $name, "description" => Catalog::GetTypeByNum($type), "imageID" => $imageId]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML(Catalog::GetTypeByNum($type), $imageId));
Polygon::RequestRender("Clothing", $itemId);
}
elseif($type == 13) //decal
{
image::process($image, ["name" => "$imageId", "x" => 256, "scaleY" => true, "dir" => "/asset/files/"]);
Image::Process($image, ["name" => "$imageId", "x" => 256, "scaleY" => true, "dir" => "/asset/files/"]);
Thumbnails::UploadAsset($image, $imageId, 60, 62, ["keepRatio" => true, "align" => "C"]);
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
$itemId = catalog::createAsset(["type" => 13, "creator" => SESSION["userId"], "name" => $name, "description" => "Decal", "imageID" => $imageId]);
$itemId = Catalog::CreateAsset(["type" => 13, "creator" => SESSION["userId"], "name" => $name, "description" => "Decal", "imageID" => $imageId]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, catalog::generateGraphicXML("Decal", $imageId));
image::process($image, ["name" => "$itemId-48x48.png", "x" => 48, "y" => 48, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-75x75.png", "x" => 75, "y" => 75, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-100x100.png", "x" => 100, "y" => 100, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-110x110.png", "x" => 110, "y" => 110, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-250x250.png", "x" => 250, "y" => 250, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-352x352.png", "x" => 352, "y" => 352, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-420x230.png", "x" => 420, "y" => 230, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$itemId-420x420.png", "x" => 420, "y" => 420, "dir" => "/thumbs/assets/"]);
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("Decal", $imageId));
Thumbnails::UploadAsset($image, $itemId, 48, 48);
Thumbnails::UploadAsset($image, $itemId, 75, 75);
Thumbnails::UploadAsset($image, $itemId, 100, 100);
Thumbnails::UploadAsset($image, $itemId, 110, 110);
Thumbnails::UploadAsset($image, $itemId, 250, 250);
Thumbnails::UploadAsset($image, $itemId, 352, 352);
Thumbnails::UploadAsset($image, $itemId, 420, 230);
Thumbnails::UploadAsset($image, $itemId, 420, 420);
}
api::respond_custom(["status" => 200, "success" => true, "message" => catalog::getTypeByNum($type)." successfully created!"]);
api::respond_custom(["status" => 200, "success" => true, "message" => Catalog::GetTypeByNum($type)." successfully created!"]);

View File

@ -0,0 +1,25 @@
<?php
require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
api::initialize(["method" => "GET", "api" => "DiscordBot"]);
if (isset($_GET["Token"]) && isset($_GET["DiscordID"]))
{
$userInfo = db::run("SELECT * FROM users WHERE discordKey = :key", [":key" => $_GET["Token"]])->fetch(PDO::FETCH_OBJ);
if (!$userInfo) api::respond(200, false, "InvalidKey"); // check if verification key is valid
if ($userInfo->discordID != NULL) api::respond(200, false, "AlreadyVerified"); // check if mercury account is already verified
db::run(
"UPDATE users SET discordID = :id, discordVerifiedTime = UNIX_TIMESTAMP() WHERE discordKey = :key",
[":id" => $_GET["DiscordID"], ":key" => $_GET["Token"]]
);
api::respond(200, true, $userInfo->username);
}
else if (isset($_GET["DiscordID"]))
{
$username = db::run("SELECT username FROM users WHERE discordID = :id", [":id" => $_GET["DiscordID"]]);
if (!$username->rowCount()) api::respond(200, false, "NotVerified"); // check if discord account is already verified
api::respond(200, true, $username->fetchColumn());
}
api::respond(400, false, "Bad Request");

28
api/discord/whois.php Normal file
View File

@ -0,0 +1,28 @@
<?php
require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
api::initialize(["method" => "GET", "api" => "DiscordBot"]);
if (isset($_GET["UserName"]))
{
$userInfo = db::run(
"SELECT id, username, blurb, adminlevel, jointime, lastonline, discordID FROM users WHERE username = :name",
[":name" => $_GET["UserName"]]
)->fetch(PDO::FETCH_OBJ);
if (!$userInfo) api::respond(200, false, "DoesntExist");
}
else if (isset($_GET["DiscordID"]))
{
$userInfo = db::run(
"SELECT id, username, blurb, adminlevel, jointime, lastonline, discordID FROM users WHERE discordID = :id",
[":id" => $_GET["DiscordID"]]
)->fetch(PDO::FETCH_OBJ);
if (!$userInfo) api::respond(200, false, "NotVerified");
}
else
{
api::respond(400, false, "Bad Request");
}
$userInfo->blurb = str_ireplace(["@everyone", "@here"], ["[everyone]", "[here]"], $userInfo->blurb);
$userInfo->blurb = preg_replace("/<(@[0-9]+)>/i", "[$1]", $userInfo->blurb);
api::respond(200, true, $userInfo);

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
$userid = SESSION["userId"];
@ -25,7 +26,7 @@ while($row = $query->fetch(PDO::FETCH_OBJ))
{
$friends[] =
[
"username" => users::getUserNameFromUid($row->requesterId),
"username" => Users::GetNameFromID($row->requesterId),
"userid" => $row->requesterId,
"avatar" => Thumbnails::GetAvatar($row->requesterId, 250, 250),
"friendid" => $row->id

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
$url = $_SERVER['HTTP_REFERER'] ?? false;
@ -9,7 +10,7 @@ $order = strpos($url, "/home") ? "lastonline DESC" : "id";
$limit = strpos($url, "/friends") ? 18 : 6;
$self = str_ends_with($url, "/user") || str_ends_with($url, "/friends") || strpos($url, "/home");
if(!users::getUserInfoFromUid($userId)) api::respond(400, false, "User does not exist");
if(!Users::GetInfoFromID($userId)) api::respond(400, false, "User does not exist");
$query = $pdo->prepare("SELECT COUNT(*) FROM friends WHERE :uid IN (requesterId, receiverId) AND status = 1");
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
@ -18,7 +19,7 @@ $query->execute();
$pages = ceil($query->fetchColumn()/$limit);
$offset = ($page - 1)*$limit;
if(!$pages) api::respond(200, true, ($self ? "You do" : users::getUserNameFromUid($userId)." does")."n't have any friends");
if(!$pages) api::respond(200, true, ($self ? "You do" : Users::GetNameFromID($userId)." does")."n't have any friends");
$query = $pdo->prepare("
SELECT friends.*, users.username, users.id AS userId, users.status, users.lastonline FROM friends
@ -40,7 +41,7 @@ while($row = $query->fetch(PDO::FETCH_OBJ))
"userid" => $row->userId,
"avatar" => Thumbnails::GetAvatar($row->userId, 250, 250),
"friendid" => $row->id,
"status" => polygon::filterText($row->status)
"status" => Polygon::FilterText($row->status)
];
}

View File

@ -1,6 +1,8 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST"]);
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true]);
$client = $_POST["client"] ?? "false";
$creator = $_POST["creator"] ?? false;
@ -40,13 +42,14 @@ while($server = $servers->fetch(PDO::FETCH_OBJ))
{
$gears = [];
foreach(json_decode($server->allowed_gears, true) as $gear_attr => $gear_val)
if($gear_val) $gears[] = ["name" => catalog::$gear_attr_display[$gear_attr]["text_sel"], "icon" => catalog::$gear_attr_display[$gear_attr]["icon"]];
if($gear_val) $gears[] = ["name" => Catalog::$GearAttributesDisplay[$gear_attr]["text_sel"], "icon" => Catalog::$GearAttributesDisplay[$gear_attr]["icon"]];
$items[] =
[
"server_name" => polygon::filterText($server->name),
"server_name" => Polygon::FilterText($server->name),
"server_description" => strlen($server->description) ? Polygon::FilterText($server->description) : "No description available.",
"server_id" => $server->id,
"server_thumbnail" => Thumbnails::GetAvatar($server->hoster, 420, 420),
"hoster_name" => users::getUserNameFromUid($server->hoster),
"hoster_name" => Users::GetNameFromID($server->hoster),
"hoster_id" => $server->hoster,
"date" => date('n/d/Y g:i:s A', $server->created),
"version" => $server->version,

View File

@ -49,14 +49,17 @@ $query->bindParam(":sid", $serverID, PDO::PARAM_INT);
$query->bindParam(":teleport", $isTeleport, PDO::PARAM_INT);
$query->execute();
$Protocol = "https";
if($serverInfo->version == 2009) $Protocol = "http";
api::respond_custom([
"status" => 200,
"success" => true,
"message" => "OK",
"version" => $serverInfo->version,
"joinScriptUrl" => "http://chef.pizzaboxer.xyz/game/join?ticket=".$ticket,
"joinScriptUrl" => "{$Protocol}://{$_SERVER['HTTP_HOST']}/game/join?ticket={$ticket}",
// these last few params are for teleportservice and lack any function - just ignore
"authenticationUrl" => "http://chef.pizzaboxer.xyz/Login/Negotiate.ashx",
"authenticationTicket" => "unusedplzignore",
"authenticationUrl" => "{$Protocol}://{$_SERVER['HTTP_HOST']}/Login/Negotiate.ashx",
"authenticationTicket" => "0",
"status" => 2
]);

View File

@ -0,0 +1,52 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$Page = $_POST["Page"] ?? 1;
$Members = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if($Rank->Level == 0) api::respond(200, false, "You are not a member of this group");
if(!$Rank->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
$MemberCount = db::run(
"SELECT COUNT(*) FROM groups_members
WHERE GroupID = :GroupID AND Rank < :RankLevel AND NOT Pending",
[":GroupID" => $GroupID, ":RankLevel" => $Rank->Level]
)->fetchColumn();
$Pages = ceil($MemberCount/12);
$Offset = ($Page - 1)*12;
if(!$Pages) api::respond(200, true, "This group does not have any members.");
$MembersQuery = db::run(
"SELECT users.username, users.id, Rank FROM groups_members
INNER JOIN users ON users.id = groups_members.UserID
WHERE GroupID = :GroupID AND Rank < :RankLevel AND NOT Pending
ORDER BY Joined DESC LIMIT 12 OFFSET $Offset",
[":GroupID" => $GroupID, ":RankLevel" => $Rank->Level]
);
while($Member = $MembersQuery->fetch(PDO::FETCH_OBJ))
{
$Members[] =
[
"UserName" => $Member->username,
"UserID" => $Member->id,
"RoleLevel" => $Member->Rank,
"Avatar" => Thumbnails::GetAvatar($Member->id, 250, 250)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "count" => $MemberCount, "items" => $Members]));

View File

@ -0,0 +1,44 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST"]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$Roles = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if($Rank->Level == 0) api::respond(200, false, "You are not a member of this group");
if(!$Rank->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
if($Rank->Level == 255)
{
$RolesQuery = db::run(
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID ORDER BY Rank ASC",
[":GroupID" => $GroupID]
);
}
else
{
$RolesQuery = db::run(
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID AND Rank < :MyRank ORDER BY Rank ASC",
[":GroupID" => $GroupID, ":MyRank" => $Rank->Level]
);
}
while($Role = $RolesQuery->fetch(PDO::FETCH_OBJ))
{
$Roles[] =
[
"Name" => htmlspecialchars($Role->Name),
"Description" => htmlspecialchars($Role->Description),
"Rank" => $Role->Rank,
"Permissions" => json_decode($Role->Permissions)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "items" => $Roles]));

View File

@ -0,0 +1,110 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Recipient"])) api::respond(400, false, "Recipient is not set");
if(!isset($_POST["Type"])) api::respond(400, false, "Type is not set");
if(!in_array($_POST["Type"], ["ally", "enemy"])) api::respond(400, false, "Type is not valid");
$GroupID = $_POST["GroupID"] ?? false;
$RecipientName = $_POST["Recipient"] ?? false;
$Type = $_POST["Type"] ?? false;
$Groups = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$Recipient = db::run("SELECT * FROM groups WHERE name = :GroupName", [":GroupName" => $RecipientName]);
$RecipientInfo = $Recipient->fetch(PDO::FETCH_OBJ);
if(!$Recipient->rowCount()) api::respond(200, false, "No group with that name exists");
$MyRank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to manage this group's relationships");
if($RecipientInfo->id == $GroupID)
{
if($Type == "ally") api::respond(200, false, "You cannot send an ally request to your own group");
else if($Type == "enemy") api::respond(200, false, "You cannot declare your own group as an enemy");
}
$Relationship = db::run(
"SELECT * FROM groups_relationships WHERE :GroupID IN (Declarer, Recipient) AND :Recipient IN (Declarer, Recipient) AND Status != 2",
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
);
$RelationshipInfo = $Relationship->fetch(PDO::FETCH_OBJ);
if($Relationship->rowCount())
{
if($RelationshipInfo->Type == "Allies")
{
if($RelationshipInfo->Status == 0)
{
if($RelationshipInfo->Declarer == $GroupID)
{
api::respond(200, false, "You already have an outgoing ally request to this group");
}
else
{
api::respond(200, false, "You already have an incoming ally request from this group");
}
}
else if($RelationshipInfo->Status == 1)
{
api::respond(200, false, "You are already allies with this group!");
}
}
else if($RelationshipInfo->Type == "Enemies")
{
api::respond(200, false, "You are already enemies with this group!");
}
}
if($Type == "ally")
{
$LastRequest = db::run("SELECT Declared FROM groups_relationships WHERE Declarer = :GroupID AND Declared+3600 > UNIX_TIMESTAMP()", [":GroupID" => $GroupID]);
if($LastRequest->rowCount())
api::respond(429, false, "Please wait ".GetReadableTime($LastRequest->fetchColumn(), ["RelativeTime" => "1 hour"])." before sending a new ally request");
db::run(
"INSERT INTO groups_relationships (Type, Declarer, Recipient, Status, Declared)
VALUES (\"Allies\", :GroupID, :Recipient, 0, UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
);
Groups::LogAction(
$GroupID, "Send Ally Request",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> sent an ally request to <a href=\"/groups?gid=%d\">%s</a>",
SESSION["userId"], SESSION["userName"], $RecipientInfo->id, htmlspecialchars($RecipientInfo->name)
)
);
api::respond(200, true, "Ally request has been sent to ".Polygon::FilterText($RecipientInfo->name));
}
else if($Type == "enemy")
{
$LastRequest = db::run("SELECT Declared FROM groups_relationships WHERE Declarer = :GroupID AND Declared+3600 > UNIX_TIMESTAMP()", [":GroupID" => $GroupID]);
if($LastRequest->rowCount())
api::respond(429, false, "Please wait ".GetReadableTime($LastRequest->fetchColumn(), ["RelativeTime" => "1 hour"])." before sending a new ally request");
db::run(
"INSERT INTO groups_relationships (Type, Declarer, Recipient, Status, Declared, Established)
VALUES (\"Enemies\", :GroupID, :Recipient, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
);
Groups::LogAction(
$GroupID, "Create Enemy",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> declared <a href=\"/groups?gid=%d\">%s</a> as an enemy",
SESSION["userId"], SESSION["userName"], $RecipientInfo->id, htmlspecialchars($RecipientInfo->name)
)
);
api::respond(200, true, Polygon::FilterText($RecipientInfo->name)." is now your enemy!");
}
api::respond(200, false, "An unexpected error occurred");

View File

@ -0,0 +1,57 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["UserID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["UserID"])) api::respond(400, false, "GroupID is not a number");
if(isset($_POST["RoleLevel"]) && !is_numeric($_POST["RoleLevel"])) api::respond(400, false, "RoleLevel is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$UserID = $_POST["UserID"] ?? false;
$RoleLevel = $_POST["RoleLevel"] ?? false;
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$MyRole = Groups::GetUserRank(SESSION["userId"], $GroupID);
$UserRole = Groups::GetUserRank($UserID, $GroupID);
if($MyRole->Level == 0) api::respond(200, false, "You are not a member of this group");
if(!$MyRole->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
if($UserRole->Level == 0) api::respond(200, false, "That user is not a member of this group");
if($RoleLevel !== false)
{
if(!Groups::GetRankInfo($GroupID, $RoleLevel)) api::respond(200, false, "That role does not exist");
if($RoleLevel == 0 || $RoleLevel == 255) api::respond(200, false, "That role cannot be manually assigned to a member");
if($UserRole->Level == $RoleLevel) api::respond(200, false, "The role you tried to assign is the user's current role");
if($MyRole->Level <= $RoleLevel) api::respond(200, false, "You can only assign roles lower than yours");
if($MyRole->Level <= $UserRole->Level) api::respond(200, false, "You can only modify the role of a user who is a role lower than yours");
db::run(
"UPDATE groups_members SET Rank = :RoleLevel WHERE GroupID = :GroupID AND UserID = :UserID",
[":GroupID" => $GroupID, ":UserID" => $UserID, ":RoleLevel" => $RoleLevel]
);
$UserName = Users::GetNameFromID($UserID);
$RoleName = Groups::GetRankInfo($GroupID, $RoleLevel)->Name;
$Action = $RoleLevel > $UserRole->Level ? "promoted" : "demoted";
Groups::LogAction(
$GroupID, "Change Rank",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> %s <a href=\"/user?ID=%d\">%s</a> from %s to %s",
SESSION["userId"], SESSION["userName"], $Action, $UserID, $UserName, htmlspecialchars($UserRole->Name), htmlspecialchars($RoleName)
)
);
api::respond(200, true, "$UserName has been $Action to " . htmlspecialchars($RoleName));
}
api::respond(200, true, "OK");

View File

@ -0,0 +1,104 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Recipient"])) api::respond(400, false, "Recipient is not set");
if(!is_numeric($_POST["Recipient"])) api::respond(400, false, "Recipient is not a number");
if(!isset($_POST["Action"])) api::respond(400, false, "Action is not set");
if(!in_array($_POST["Action"], ["accept", "decline"])) api::respond(400, false, "Action is not valid");
$GroupID = $_POST["GroupID"] ?? false;
$Recipient = $_POST["Recipient"] ?? false;
$Action = $_POST["Action"] ?? false;
$Groups = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
if(!Groups::GetGroupInfo($Recipient)) api::respond(200, false, "Recipient group does not exist");
$MyRank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to manage this group's relationships");
$Relationship = db::run(
"SELECT groups_relationships.*, groups.name FROM groups_relationships
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
WHERE :GroupID IN (Declarer, Recipient) AND :Recipient IN (Declarer, Recipient) AND Status != 2",
[":GroupID" => $GroupID, ":Recipient" => $Recipient]
);
$RelationshipInfo = $Relationship->fetch(PDO::FETCH_OBJ);
if(!$Relationship->rowCount()) api::respond(200, false, "You are not in a relationship with this group");
if($Action == "accept")
{
if($RelationshipInfo->Type == "Enemies") api::respond(200, false, "You cannot accept an enemy relationship");
if($RelationshipInfo->Status != 0) api::respond(200, false, "You are already in a relationship with this group");
db::run(
"UPDATE groups_relationships SET Status = 1, Established = UNIX_TIMESTAMP() WHERE ID = :RelationshipID",
[":RelationshipID" => $RelationshipInfo->ID]
);
Groups::LogAction(
$GroupID, "Accept Ally Request",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> accepted an ally request from <a href=\"/groups?gid=%d\">%s</a>",
SESSION["userId"], SESSION["userName"], $Recipient, htmlspecialchars($RelationshipInfo->name)
)
);
api::respond(200, true, "You have accepted {$RelationshipInfo->name}'s ally request");
}
else if($Action == "decline")
{
db::run(
"UPDATE groups_relationships SET Status = 2, Broken = UNIX_TIMESTAMP() WHERE ID = :RelationshipID",
[":RelationshipID" => $RelationshipInfo->ID]
);
if($RelationshipInfo->Type == "Allies")
{
if($RelationshipInfo->Status == 0)
{
Groups::LogAction(
$GroupID, "Decline Ally Request",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> declined an ally request from <a href=\"/groups?gid=%d\">%s</a>",
SESSION["userId"], SESSION["userName"], $Recipient, htmlspecialchars($RelationshipInfo->name)
)
);
api::respond(200, true, "You have declined ".Polygon::FilterText($RelationshipInfo->name)."'s ally request");
}
else if($RelationshipInfo->Status == 1)
{
Groups::LogAction(
$GroupID, "Delete Ally",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> removed <a href=\"/groups?gid=%d\">%s</a> as an ally",
SESSION["userId"], SESSION["userName"], $Recipient, htmlspecialchars($RelationshipInfo->name)
)
);
api::respond(200, true, "You are no longer allies with ".Polygon::FilterText($RelationshipInfo->name));
}
}
else if($RelationshipInfo->Type == "Enemies")
{
Groups::LogAction(
$GroupID, "Delete Enemy",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> removed <a href=\"/groups?gid=%d\">%s</a> as an enemy",
SESSION["userId"], SESSION["userName"], $Recipient, htmlspecialchars($RelationshipInfo->name)
)
);
api::respond(200, true, "You are no longer enemies with ".Polygon::FilterText($RelationshipInfo->name));
}
}
api::respond(200, false, "An unexpected error occurred");

View File

@ -0,0 +1,167 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Roles"])) api::respond(400, false, "Roles is not set");
$GroupID = $_POST["GroupID"];
$Roles = json_decode($_POST["Roles"]);
if(!$Roles) api::respond(400, false, "Roles is not valid JSON");
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$MyRole = Groups::GetUserRank(SESSION["userId"], $GroupID);
if($MyRole->Level == 0) api::respond(200, false, "You are not a member of this group");
if($MyRole->Level != 255) api::respond(200, false, "You are not allowed to perform this action");
function FindRolesWithRank($Rank)
{
global $Roles;
$Count = 0;
foreach ($Roles as $Role)
{
if (!isset($Role->Rank)) continue;
if ($Role->Rank == $Rank) $Count += 1;
}
return $Count;
}
function FindRoleWithRank($Rank)
{
global $Roles;
foreach ($Roles as $Role)
{
if (!isset($Role->Rank)) continue;
if ($Role->Rank == $Rank) return $Role;
}
return false;
}
$Permissions =
[
"CanViewGroupWall",
"CanViewGroupStatus",
"CanPostOnGroupWall",
"CanPostGroupStatus",
"CanDeleteGroupWallPosts",
"CanAcceptJoinRequests",
"CanKickLowerRankedMembers",
"CanRoleLowerRankedMembers",
"CanManageRelationships",
"CanCreateAssets",
"CanConfigureAssets",
"CanSpendFunds",
"CanManageGames",
"CanManageGroupAdmin",
"CanViewAuditLog"
];
if(FindRolesWithRank(0) == 0) api::respond(200, false, "You can not remove the Guest role");
if(FindRolesWithRank(255) == 0) api::respond(200, false, "You can not remove the Owner role");
if(count($Roles) < 3) api::respond(200, false, "There must be at least three roles");
if(count($Roles) > 10) api::respond(200, false, "There must be no more than ten roles");
foreach($Roles as $Role)
{
if(!isset($Role->Name) || !isset($Role->Description) || !isset($Role->Rank) || !isset($Role->Permissions))
api::respond(200, false, "Roles are missing parameters");
if($Role->Rank < 0 || $Role->Rank > 255) api::respond(200, false, "Each role must have a rank number between 0 and 255");
if(FindRolesWithRank($Role->Rank) > 1) api::respond(200, false, "Each role must have a unique rank number");
$CurrentRole = Groups::GetRankInfo($GroupID, $Role->Rank);
if($CurrentRole === false) $Role->Action = "Create";
else $Role->Action = "Update";
if($Role->Rank == 0)
{
if($Role->Name != $CurrentRole->Name || $Role->Description != $CurrentRole->Description)
api::respond(200, false, "You can not modify the Guest role");
}
if($Role->Rank == 255 && $Role->Permissions != $CurrentRole->Permissions)
api::respond(200, false, "You can not modify the permissions of the Owner role");
if(strlen($Role->Name) < 3) api::respond(200, false, "Role names must be at least 3 characters long");
if(strlen($Role->Name) > 15) api::respond(200, false, "Role names must be no longer than 15 characters");
if(strlen($Role->Description) < 3) api::respond(200, false, "Role description must at least 3 characters long");
if(strlen($Role->Description) > 64) api::respond(200, false, "Role description must be no longer than 64 characters");
foreach ($Permissions as $Permission)
{
if(!isset($Role->Permissions->$Permission)) api::respond(200, false, "Role is missing a permission");
if(!is_bool($Role->Permissions->$Permission)) api::respond(200, false, "Role permission property must have a boolean value");
}
if(count((array)$Role->Permissions) != count($Permissions)) api::respond(200, false, "Role permissions contains an incorrect number of permissions");
}
foreach($Roles as $Role)
{
if($Role->Action == "Create")
{
// if(SESSION["userId"] == 1) echo "Creating Role {$Role->Rank}\r\n";
db::run(
"INSERT INTO groups_ranks (GroupID, Name, Description, Rank, Permissions, Created)
VALUES (:GroupID, :Name, :Description, :Rank, :Permissions, UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":Name" => $Role->Name, ":Description" => $Role->Description, ":Rank" => $Role->Rank, ":Permissions" => json_encode($Role->Permissions)]
);
}
}
$GroupRoles = Groups::GetGroupRanks($GroupID, true);
while($ExistingRole = $GroupRoles->fetch(PDO::FETCH_OBJ))
{
$Role = FindRoleWithRank($ExistingRole->Rank);
if($Role == false)
{
// if(SESSION["userId"] == 1) echo "Deleting Role {$ExistingRole->Rank}\r\n";
// for this one we gotta move the members with a role thats being deleted to the lowest rank
// slight issue with this is for a brief period, members assigned the role thats being deleted
// will have no role - if the timing is just right this could mess up the view of the group page
// delete the rank by the oldest id - so that we dont accidentally delete the new one
db::run(
"DELETE FROM groups_ranks WHERE GroupID = :GroupID AND Rank = :Rank ORDER BY id ASC LIMIT 1",
[":GroupID" => $GroupID, ":Rank" => $ExistingRole->Rank]
);
$NewRank = db::run(
"SELECT Rank FROM polygon.groups_ranks WHERE GroupID = :GroupID AND Rank != 0 ORDER BY Rank ASC LIMIT 1",
[":GroupID" => $GroupID]
)->fetchColumn();
// if(SESSION["userId"] == 1) echo "Updating existing members to {$NewRank}\r\n";
db::run(
"UPDATE groups_members SET Rank = :NewRank WHERE GroupID = :GroupID AND Rank = :Rank",
[":GroupID" => $GroupID, ":Rank" => $ExistingRole->Rank, ":NewRank" => $NewRank]
);
}
else if(isset($Role->Action) && $Role->Action == "Update")
{
// if(SESSION["userId"] == 1) echo "Updating Role {$Role->Rank}\r\n";
db::run(
"UPDATE groups_ranks SET Name = :Name, Description = :Description, Permissions = :Permissions
WHERE GroupID = :GroupID AND Rank = :Rank",
[":GroupID" => $GroupID, ":Name" => $Role->Name, ":Description" => $Role->Description, ":Rank" => $Role->Rank, ":Permissions" => json_encode($Role->Permissions)]
);
}
}
die(json_encode(["status" => 200, "success" => true, "message" => "Group roles have successfully been updated"]));

View File

@ -0,0 +1,40 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["PostID"])) api::respond(400, false, "PostID is not set");
if(!is_numeric($_POST["PostID"])) api::respond(400, false, "PostID is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$PostID = $_POST["PostID"] ?? false;
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$Rank->Permissions->CanDeleteGroupWallPosts) api::respond(200, false, "You are not allowed to delete wall posts on this group");
$PostInfo = db::run(
"SELECT * FROM groups_wall WHERE id = :PostID AND :GroupID = :GroupID",
[":PostID" => $PostID, ":GroupID" => $GroupID]
)->fetch(PDO::FETCH_OBJ);
if(!$PostInfo) api::respond(200, false, "Wall post does not exist");
Groups::LogAction(
$GroupID, "Delete Post",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> deleted post \"%s\" by <a href=\"/user?ID=%d\">%s</a>",
SESSION["userId"], SESSION["userName"], htmlspecialchars($PostInfo->Content), $PostInfo->PosterID, Users::GetNameFromID($PostInfo->PosterID)
)
);
db::run(
"DELETE FROM groups_wall WHERE id = :PostID AND :GroupID = :GroupID",
[":PostID" => $PostID, ":GroupID" => $GroupID]
);
api::respond(200, true, "OK");

75
api/groups/get-audit.php Normal file
View File

@ -0,0 +1,75 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$Filter = $_POST["Filter"] ?? "All Actions";
$Page = $_POST["Page"] ?? 1;
$Logs = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$MyRank = Groups::GetUserRank(SESSION["userId"] ?? 0, $GroupID);
if(!$MyRank->Permissions->CanViewAuditLog) api::respond(200, false, "You cannot audit this group");
if($Filter == "All Actions")
{
$LogCount = db::run(
"SELECT COUNT(*) FROM groups_audit WHERE GroupID = :GroupID",
[":GroupID" => $GroupID]
)->fetchColumn();
}
else
{
$LogCount = db::run(
"SELECT COUNT(*) FROM groups_audit WHERE GroupID = :GroupID AND Category = :Action",
[":GroupID" => $GroupID, ":Action" => $Filter]
)->fetchColumn();
}
$Pages = ceil($LogCount/20);
$Offset = ($Page - 1)*20;
if(!$Pages) api::respond(200, true, "This group does not have any logs for this action.");
if($Filter == "All Actions")
{
$LogsQuery = db::run(
"SELECT groups_audit.*, users.username FROM groups_audit
INNER JOIN users ON users.id = UserId
WHERE GroupID = :GroupID
ORDER BY Time DESC LIMIT 20 OFFSET $Offset",
[":GroupID" => $GroupID]
);
}
else
{
$LogsQuery = db::run(
"SELECT groups_audit.*, users.username FROM groups_audit
INNER JOIN users ON users.id = UserId
WHERE GroupID = :GroupID AND Category = :Action
ORDER BY Time DESC LIMIT 20 OFFSET $Offset",
[":GroupID" => $GroupID, ":Action" => $Filter]
);
}
while($Log = $LogsQuery->fetch(PDO::FETCH_OBJ))
{
$Logs[] =
[
"Date" => date('j/n/y G:i', $Log->Time),
"UserName" => $Log->username,
"UserID" => $Log->UserID,
"UserAvatar" => Thumbnails::GetAvatar($Log->UserID, 250, 250),
"Rank" => Polygon::FilterText($Log->Rank),
"Description" => Polygon::FilterText($Log->Description, false)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "items" => $Logs]));

View File

@ -0,0 +1,52 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["RankLevel"])) api::respond(400, false, "RankLevel is not set");
if(!is_numeric($_POST["RankLevel"])) api::respond(400, false, "RankLevel is not a number");
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$RankID = $_POST["RankLevel"] ?? false;
$Page = $_POST["Page"] ?? 1;
$Members = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
if(!Groups::GetRankInfo($GroupID, $RankID)) api::respond(200, false, "Group rank does not exist");
$MemberCount = db::run(
"SELECT COUNT(*) FROM groups_members WHERE GroupID = :GroupID AND Rank = :RankID AND NOT Pending",
[":GroupID" => $GroupID, ":RankID" => $RankID]
)->fetchColumn();
$Pages = ceil($MemberCount/12);
$Offset = ($Page - 1)*12;
if(!$Pages) api::respond(200, true, "This group does not have any members of this rank.");
$MembersQuery = db::run(
"SELECT users.username, users.id, Rank FROM groups_members
INNER JOIN users ON users.id = groups_members.UserID
WHERE GroupID = :GroupID AND Rank = :RankID AND NOT Pending
ORDER BY Joined DESC LIMIT 12 OFFSET $Offset",
[":GroupID" => $GroupID, ":RankID" => $RankID]
);
while($Member = $MembersQuery->fetch(PDO::FETCH_OBJ))
{
$Members[] =
[
"UserName" => $Member->username,
"UserID" => $Member->id,
"RoleLevel" => $Member->Rank,
"Avatar" => Thumbnails::GetAvatar($Member->id, 250, 250)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "count" => $MemberCount, "items" => $Members]));

View File

@ -0,0 +1,82 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Type"])) api::respond(400, false, "Type is not set");
if(!in_array($_POST["Type"], ["Pending Allies", "Allies", "Enemies"])) api::respond(400, false, "Type is not valid");
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$Type = $_POST["Type"] ?? false;
$Page = $_POST["Page"] ?? 1;
$Groups = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
if($Type == "Pending Allies")
{
if(!SESSION) api::respond(200, false, "You are not allowed to get this group's pending allies");
$MyRank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to get this group's pending allies");
$GroupsCount = db::run(
"SELECT COUNT(*) FROM groups_relationships WHERE Recipient = :GroupID AND Type = \"Allies\" AND Status = 0",
[":GroupID" => $GroupID]
)->fetchColumn();
}
else
{
$GroupsCount = db::run(
"SELECT COUNT(*) FROM groups_relationships WHERE :GroupID IN (Declarer, Recipient) AND Type = :Type AND Status = 1",
[":GroupID" => $GroupID, ":Type" => $Type]
)->fetchColumn();
}
$Pages = ceil($GroupsCount/12);
$Offset = ($Page - 1)*12;
if(!$Pages) api::respond(200, true, "This group does not have any $Type");
if($Type == "Pending Allies")
{
$GroupsQuery = db::run(
"SELECT groups.name, groups.id, groups.emblem,
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
FROM groups_relationships
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
WHERE Recipient = :GroupID AND Type = \"Allies\" AND Status = 0
ORDER BY Declared DESC LIMIT 12 OFFSET $Offset",
[":GroupID" => $GroupID]
);
}
else
{
$GroupsQuery = db::run(
"SELECT groups.name, groups.id, groups.emblem,
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
FROM groups_relationships
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
WHERE :GroupID IN (Declarer, Recipient) AND Type = :Type AND Status = 1
ORDER BY Established DESC LIMIT 12 OFFSET $Offset",
[":GroupID" => $GroupID, ":Type" => $Type]
);
}
while($Group = $GroupsQuery->fetch(PDO::FETCH_OBJ))
{
$Groups[] =
[
"Name" => Polygon::FilterText($Group->name),
"ID" => $Group->id,
"MemberCount" => $Group->MemberCount,
"Emblem" => Thumbnails::GetAssetFromID($Group->emblem, 420, 420)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "items" => $Groups]));

53
api/groups/get-wall.php Normal file
View File

@ -0,0 +1,53 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$Page = $_POST["Page"] ?? 1;
$Wall = [];
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
if(SESSION) $Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
else $Rank = Groups::GetRankInfo($GroupID, 0);
if(!$Rank->Permissions->CanViewGroupWall) api::respond(200, false, "You are not allowed to view this group wall");
$PostCount = db::run(
"SELECT COUNT(*) FROM groups_wall WHERE GroupID = :GroupID AND NOT Deleted",
[":GroupID" => $GroupID]
)->fetchColumn();
$Pages = ceil($PostCount/15);
$Offset = ($Page - 1)*15;
if(!$Pages) api::respond(200, true, "This group does not have any wall posts.");
$PostQuery = db::run(
"SELECT groups_wall.id, users.username AS PosterName, PosterID, Content, TimePosted FROM groups_wall
INNER JOIN users ON users.id = PosterID WHERE GroupID = :GroupID AND NOT Deleted
ORDER BY TimePosted DESC LIMIT 15 OFFSET $Offset",
[":GroupID" => $GroupID]
);
while($Post = $PostQuery->fetch(PDO::FETCH_OBJ))
{
$Wall[] =
[
"id" => $Post->id,
"username" => $Post->PosterName,
"userid" => $Post->PosterID,
"content" => nl2br(Polygon::FilterText($Post->Content)),
"time" => date('j/n/Y g:i:s A', $Post->TimePosted),
"avatar" => Thumbnails::GetAvatar($Post->PosterID, 420, 420)
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "count" => $PostCount, "items" => $Wall]));

30
api/groups/join-group.php Normal file
View File

@ -0,0 +1,30 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
$GroupID = $_POST["GroupID"] ?? false;
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
if(Groups::CheckIfUserInGroup(SESSION["userId"], $GroupID)) api::respond(200, false, "You are already in this group");
if(Groups::GetUserGroups(SESSION["userId"])->rowCount() >= 20) api::respond(200, false, "You have reached the maximum number of groups");
$RateLimit = db::run("SELECT Joined FROM groups_members WHERE UserID = :UserID AND Joined+300 > UNIX_TIMESTAMP()", [":UserID" => SESSION["userId"]]);
if($RateLimit->rowCount())
api::respond(200, false, "Please wait ".GetReadableTime($RateLimit->fetchColumn(), ["RelativeTime" => "5 minutes"])." before joining a new group");
$RankLevel = db::run(
"SELECT Rank FROM groups_ranks WHERE GroupID = :GroupID AND Rank != 0 ORDER BY Rank ASC LIMIT 1",
[":GroupID" => $GroupID]
)->fetchColumn();
db::run(
"INSERT INTO groups_members (GroupID, UserID, Rank, Joined) VALUES (:GroupID, :UserID, :RankLevel, UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"], ":RankLevel" => $RankLevel]
);
api::respond(200, true, "OK");

View File

@ -0,0 +1,21 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
$GroupID = $_POST["GroupID"] ?? false;
$GroupInfo = Groups::GetGroupInfo($GroupID);
if(!$GroupInfo) api::respond(200, false, "Group does not exist");
if($GroupInfo->creator == SESSION["userId"]) api::respond(200, false, "You are the creator of this group");
if(!Groups::CheckIfUserInGroup(SESSION["userId"], $GroupID)) api::respond(200, false, "You are not in this group");
db::run(
"DELETE FROM groups_members WHERE GroupID = :GroupID AND UserID = :UserID",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"]]
);
api::respond(200, true, "OK");

56
api/groups/post-shout.php Normal file
View File

@ -0,0 +1,56 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
Polygon::ImportClass("Thumbnails");
Polygon::ImportClass("Discord");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Content"])) api::respond(400, false, "Content is not set");
$GroupID = $_POST["GroupID"] ?? false;
$Content = $_POST["Content"] ?? false;
$GroupInfo = Groups::GetGroupInfo($GroupID);
if(!$GroupInfo) api::respond(200, false, "Group does not exist");
$Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$Rank->Permissions->CanPostGroupStatus) api::respond(200, false, "You are not allowed to post on this group wall");
if(strlen($Content) < 3) api::respond(200, false, "Group shout must be at least 3 characters long");
if(strlen($Content) > 255) api::respond(200, false, "Group shout can not be longer than 64 characters");
$LastPost = db::run(
"SELECT timestamp FROM feed WHERE groupId = :GroupID AND userId = :UserID AND timestamp+300 > UNIX_TIMESTAMP()",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"]]
);
if($LastPost->rowCount())
api::respond(200, false, "Please wait ".GetReadableTime($LastPost->fetchColumn(), ["RelativeTime" => "5 minutes"])." before posting a group shout");
Groups::LogAction(
$GroupID, "Post Shout",
sprintf(
"<a href=\"/user?ID=%d\">%s</a> changed the group status to: %s",
SESSION["userId"], SESSION["userName"], htmlspecialchars($Content)
)
);
db::run(
"INSERT INTO feed (groupId, userId, text, timestamp) VALUES (:GroupID, :UserID, :Content, UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"], ":Content" => $Content]
);
Discord::SendToWebhook(
[
"username" => $GroupInfo->name,
"content" => $Content."\n(Posted by ".SESSION["userName"].")",
"avatar_url" => Thumbnails::GetAssetFromID($GroupInfo->emblem, 420, 420)
],
Discord::WEBHOOK_KUSH
);
api::respond(200, true, "OK");

38
api/groups/post-wall.php Normal file
View File

@ -0,0 +1,38 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Groups");
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
if(!isset($_POST["Content"])) api::respond(400, false, "Content is not set");
$GroupID = $_POST["GroupID"] ?? false;
$Content = $_POST["Content"] ?? false;
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
$Rank = Groups::GetUserRank(SESSION["userId"], $GroupID);
if(!$Rank->Permissions->CanPostOnGroupWall) api::respond(200, false, "You are not allowed to post on this group wall");
if(strlen($Content) < 3) api::respond(200, false, "Wall post must be at least 3 characters long");
if(strlen($Content) > 255) api::respond(200, false, "Wall post can not be longer than 255 characters");
$LastPost = db::run(
"SELECT TimePosted FROM groups_wall
WHERE GroupID = :GroupID AND PosterID = :UserID AND TimePosted+60 > UNIX_TIMESTAMP()
ORDER BY TimePosted DESC LIMIT 1",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"]]
);
if(SESSION["userId"] != 1 && $LastPost->rowCount())
api::respond(200, false, "Please wait ".(60-(time()-$LastPost->fetchColumn()))." seconds before posting a new wall post");
db::run(
"INSERT INTO groups_wall (GroupID, PosterID, Content, TimePosted) VALUES (:GroupID, :UserID, :Content, UNIX_TIMESTAMP())",
[":GroupID" => $GroupID, ":UserID" => SESSION["userId"], ":Content" => $Content]
);
api::respond(200, true, "OK");

View File

@ -1,5 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
$categories =
[
0=>"Bricks",
@ -92,7 +93,7 @@ $query->execute();
</div>
<?php } ?>
<div id="ToolboxItems">
<?php while($row = $query->fetch(PDO::FETCH_OBJ)) { $name = polygon::filterText($row->name); ?>
<?php while($row = $query->fetch(PDO::FETCH_OBJ)) { $name = Polygon::FilterText($row->name); ?>
<a class="ToolboxItem" title="<?=$name?>" href="javascript:insertContent(<?=$row->id?>)" ondragstart="dragRBX(<?=$row->id?>)" onmouseover="this.style.borderStyle='outset'" onmouseout="this.style.borderStyle='solid'" style="border-style: solid;display:inline-block;height:60px;width:60px;cursor:pointer;">
<img width="60" src="<?=Thumbnails::GetAsset($row, 75, 75)?>" border="0" id="img" alt="<?=$name?>">
</a>

View File

@ -0,0 +1,40 @@
<?php
class Auth
{
// i wonder if its worth putting the plain password only in the constructor
// for the sake of efficiency - usually it works out well, however in the
// change password api you end up having to instantiate two auth objects
// oh well
// by the way, this is like the only OOP thing in the entirety of polygon
// (apart from third party libaries). maybe i should change that. todo?
private $plaintext = "";
private $key = "";
function CreatePassword()
{
return \ParagonIE\PasswordLock\PasswordLock::hashAndEncrypt($this->plaintext, $this->key);
}
function VerifyPassword($storedtext)
{
if(strpos($storedtext, "$2y$10") !== false) //standard bcrypt - used since 04/09/2020
return password_verify($this->plaintext, $storedtext);
elseif(strpos($storedtext, "def50200") !== false) //argon2id w/ encryption - used since 26/02/2021
return \ParagonIE\PasswordLock\PasswordLock::decryptAndVerify($this->plaintext, $storedtext, $this->key);
}
function UpdatePassword($userId)
{
$pwhash = $this->createPassword();
db::run("UPDATE users SET password = :hash, lastpwdchange = UNIX_TIMESTAMP() WHERE id = :id", [":hash" => $pwhash, ":id" => $userId]);
}
function __construct($plaintext)
{
if(!class_exists('Defuse\Crypto\Key')) Polygon::ImportLibrary("PasswordLock");
$this->plaintext = $plaintext;
$this->key = \Defuse\Crypto\Key::loadFromAsciiSafeString(SITE_CONFIG["keys"]["passwordEncryption"]);
}
}

View File

@ -0,0 +1,137 @@
<?php
class Catalog
{
public static array $types =
[
1 => "Image", // (internal use only - this is used for asset images)
2 => "T-Shirt",
3 => "Audio",
4 => "Mesh", // (internal use only)
5 => "Lua", // (internal use only - use this for corescripts and linkedtool scripts)
6 => "HTML", // (deprecated - dont use)
7 => "Text", // (deprecated - dont use)
8 => "Hat",
9 => "Place", // (unused as of now)
10 => "Model",
11 => "Shirt",
12 => "Pants",
13 => "Decal",
16 => "Avatar", // (deprecated - dont use)
17 => "Head",
18 => "Face",
19 => "Gear",
21 => "Badge" // (unused as of now)
];
static function GetTypeByNum($type)
{
return self::$types[$type] ?? false;
}
public static array $GearAttributesDisplay =
[
"melee" => ["text_sel" => "Melee", "text_item" => "Melee Weapon", "icon" => "far fa-sword"],
"powerup" => ["text_sel" => "Power ups", "text_item" => "Power Up", "icon" => "far fa-arrow-alt-up"],
"ranged" => ["text_sel" => "Ranged", "text_item" => "Ranged Weapon", "icon" => "far fa-bow-arrow"],
"navigation" => ["text_sel" => "Navigation", "text_item" => "Melee", "icon" => "far fa-compass"],
"explosive" => ["text_sel" => "Explosives", "text_item" => "Explosive", "icon" => "far fa-bomb"],
"musical" => ["text_sel" => "Musical", "text_item" => "Musical", "icon" => "far fa-music"],
"social" => ["text_sel" => "Social", "text_item" => "Social Item", "icon" => "far fa-laugh"],
"transport" => ["text_sel" => "Transport", "text_item" => "Personal Transport", "icon" => "far fa-motorcycle"],
"building" => ["text_sel" => "Building", "text_item" => "Melee", "icon" => "far fa-hammer"]
];
public static array $GearAttributes =
[
"melee" => false,
"powerup" => false,
"ranged" => false,
"navigation" => false,
"explosive" => false,
"musical" => false,
"social" => false,
"transport" => false,
"building" => false
];
static function ParseGearAttributes()
{
$gears = self::$GearAttributes;
foreach($gears as $gear => $enabled) $gears[$gear] = isset($_POST["gear_$gear"]) && $_POST["gear_$gear"] == "on";
self::$GearAttributes = $gears;
}
static function GetAssetInfo($id)
{
return db::run(
"SELECT assets.*, users.username,
(SELECT COUNT(*) FROM ownedAssets WHERE assetId = assets.id AND userId != assets.creator) AS sales_total,
(SELECT COUNT(*) FROM ownedAssets WHERE assetId = assets.id AND userId != assets.creator AND timestamp > :sda) AS sales_week
FROM assets INNER JOIN users ON creator = users.id WHERE assets.id = :id",
[":sda" => strtotime('7 days ago', time()), ":id" => $id])->fetch(PDO::FETCH_OBJ);
}
static function CreateAsset($options)
{
global $pdo;
$columns = array_keys($options);
$querystring = "INSERT INTO assets (".implode(", ", $columns).", created, updated) ";
array_walk($columns, function(&$value, $_){ $value = ":$value"; });
$querystring .= "VALUES (".implode(", ", $columns).", UNIX_TIMESTAMP(), UNIX_TIMESTAMP())";
$query = $pdo->prepare($querystring);
foreach($options as $option => $val) $query->bindParam(":$option", $options[$option], is_numeric($val) ? PDO::PARAM_INT : PDO::PARAM_STR);
$query->execute();
$aid = $pdo->lastInsertId();
$uid = $options["creator"] ?? SESSION["userId"];
db::run("INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (:aid, :uid, UNIX_TIMESTAMP())", [":aid" => $aid, ":uid" => $uid]);
return $aid;
}
static function OwnsAsset($uid, $aid)
{
return db::run("SELECT COUNT(*) FROM ownedAssets WHERE assetId = :aid AND userId = :uid", [":aid" => $aid, ":uid" => $uid])->fetchColumn();
}
static function GenerateGraphicXML($type, $assetID)
{
$strings =
[
"T-Shirt" => ["class" => "ShirtGraphic", "contentName" => "Graphic", "stringName" => "Shirt Graphic"],
"Decal" => ["class" => "Decal", "contentName" => "Texture", "stringName" => "Decal"],
"Face" => ["class" => "Decal", "contentName" => "Texture", "stringName" => "face"],
"Shirt" => ["class" => "Shirt", "contentName" => "ShirtTemplate", "stringName" => "Shirt"],
"Pants" => ["class" => "Pants", "contentName" => "PantsTemplate", "stringName" => "Pants"]
];
ob_start(); ?>
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<External>null</External>
<External>nil</External>
<Item class="<?=$strings[$type]["class"]?>" referent="RBX0">
<Properties>
<?php if($type == "Decal" || $type == "Face") { ?>
<token name="Face">5</token>
<string name="Name"><?=$strings[$type]["stringName"]?></string>
<float name="Shiny">20</float>
<float name="Specular">0</float>
<Content name="Texture">
<url>%ASSETURL%<?=$assetID?></url>
</Content>
<?php } else { ?>
<Content name="<?=$strings[$type]["contentName"]?>">
<url>%ASSETURL%<?=$assetID?></url>
</Content>
<string name="Name"><?=$strings[$type]["stringName"]?></string>
<?php } ?>
<bool name="archivable">true</bool>
</Properties>
</Item>
</roblox>
<?php return ob_get_clean();
}
}

View File

@ -0,0 +1,61 @@
<?php
class Discord
{
const WEBHOOK_POLYGON = "https://discord.com/api/webhooks/";
const WEBHOOK_KUSH = "https://discord.com/api/webhooks/";
static function GetUserInfo($UserID)
{
$ch = curl_init();
curl_setopt_array($ch,
[
CURLOPT_URL => "https://discord.com/api/v8/users/$UserID",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bot"]
]);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpcode != 200) return false;
$response = json_decode($response);
if ($response == NULL) return false;
return (object)
[
"username" => $response->username,
"tag" => $response->discriminator,
"id" => $response->id,
"avatar" => "https://cdn.discordapp.com/avatars/{$response->id}/{$response->avatar}.png",
"color" => $response->accent_color,
"banner" => $response->banner,
"banner_color" => $response->banner_color
];
}
static function SendToWebhook($Payload, $Webhook, $EscapeContent = true)
{
// example payload:
// $payload = ["username" => "test", "content" => "test", "avatar_url" => "https://polygon.pizzaboxer.xyz/thumbs/avatar?id=1&x=100&y=100"];
if($EscapeContent)
{
$Payload["content"] = str_ireplace(["\\", "`"], ["\\\\", "\\`"], $Payload["content"]);
$Payload["content"] = str_ireplace(["@everyone", "@here"], ["`@everyone`", "`@here`"], $Payload["content"]);
$Payload["content"] = preg_replace("/(<@[0-9]+>)/i", "`$1`", $Payload["content"]);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $Webhook);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['payload_json' => json_encode($Payload)]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}

View File

@ -0,0 +1,115 @@
<?php
class ErrorHandler
{
private $Exception;
private $Type;
private $String;
private $File;
private $Line;
private function GetType($Type)
{
switch ($Type)
{
case E_ERROR: case E_USER_ERROR: return "Fatal error";
case E_WARNING: case E_USER_WARNING: return "Warning";
case E_NOTICE: case E_USER_NOTICE: return "Warning";
case E_DEPRECATED: case E_USER_DEPRECATED: return "Deprecated";
default: return "Unknown error type $Type";
}
}
private function GetVerboseMessage()
{
$VerboseMessage = "";
if($this->Type == "Exception")
{
$VerboseMessage .= sprintf("Fatal Error: Uncaught Exception: %s in %s:%d\n", $this->Exception->getMessage(), $this->Exception->getFile(), $this->Exception->getLine());
$VerboseMessage .= "Stack trace:\n";
$VerboseMessage .= sprintf("%s\n", $this->Exception->getTraceAsString());
$VerboseMessage .= sprintf(" thrown in %s on line %d", $this->Exception->getFile(), $this->Exception->getLine());
}
else
{
$VerboseMessage .= sprintf("%s: %s in %s on line %s", $this->Type, $this->String, $this->File, $this->Line);
}
return $VerboseMessage;
}
private function WriteLog()
{
$LogFile = $_SERVER['DOCUMENT_ROOT']."/api/private/ErrorLog.json";
$LogID = generateUUID();
if(!file_exists($LogFile)) file_put_contents($LogFile, "[]");
$Log = json_decode(file_get_contents($LogFile), true);
$Log[$LogID] =
[
"Timestamp" => time(),
"GETParameters" => $_GET,
"Message" => $this->GetVerboseMessage()
];
file_put_contents($LogFile, json_encode($Log));
return $LogID;
}
public function HandleError($Type, $String, $File, $Line)
{
$this->Type = $this->GetType($Type);
$this->String = $String;
$this->File = $File;
$this->Line = $Line;
$LogID = $this->WriteLog();
if(headers_sent())
{
die("An unexpected error occurred! More info: $LogID");
}
else
{
redirect("/error?id=$LogID");
}
}
public function HandleException($Exception)
{
$this->Type = "Exception";
$this->Exception = $Exception;
$LogID = $this->WriteLog();
if(headers_sent())
{
die("An unexpected error occurred! More info: $LogID");
}
else
{
redirect("/error?id=$LogID");
}
}
public function __construct()
{
set_error_handler([$this, "HandleError"]);
set_exception_handler([$this, "HandleException"]);
}
public static function GetLog($LogID = false)
{
$LogFile = $_SERVER['DOCUMENT_ROOT']."/api/private/ErrorLog.json";
if(!file_exists($LogFile)) file_put_contents($LogFile, "[]");
$Log = json_decode(file_get_contents($LogFile), true);
if($LogID !== false) return $Log[$LogID] ?? false;
return $Log;
}
}

View File

@ -0,0 +1,89 @@
<?php
class Forum
{
static function GetThreadInfo($id)
{
return db::run("SELECT * FROM forum_threads WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
}
static function GetReplyInfo($id)
{
return db::run("SELECT * FROM forum_replies WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
}
static function GetThreadReplies($id)
{
return db::run("SELECT COUNT(*) FROM forum_replies WHERE threadId = :id AND NOT deleted", [":id" => $id])->fetchColumn() ?: "-";
}
static function GetSubforumInfo($id)
{
return db::run("SELECT * FROM forum_subforums WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
}
static function GetSubforumThreadCount($id, $includeReplies = false)
{
$threads = db::run("SELECT COUNT(*) FROM forum_threads WHERE subforumid = :id", [":id" => $id])->fetchColumn();
if(!$includeReplies) return $threads ?: '-';
$replies = db::run("SELECT COUNT(*) from forum_replies WHERE threadId IN (SELECT id FROM forum_threads WHERE subforumid = :id)", [":id" => $id])->fetchColumn();
$total = $threads + $replies;
return $total ?: '-';
}
}
class pagination
{
// this is ugly and sucks
// really this is only for the forums
// everything else uses standard next and back pagination
public static int $page = 1;
public static int $pages = 1;
public static string $url = '/';
public static array $pager = [1 => 1, 2 => 1, 3 => 1];
public static function initialize()
{
self::$pager[1] = self::$page-1; self::$pager[2] = self::$page; self::$pager[3] = self::$page+1;
if(self::$page <= 2){ self::$pager[1] = self::$page; self::$pager[2] = self::$page+1; self::$pager[3] = self::$page+2; }
if(self::$page == 1){ self::$pager[1] = self::$page+1; }
if(self::$page >= self::$pages-1){ self::$pager[1] = self::$pages-3; self::$pager[2] = self::$pages-2; self::$pager[3] = self::$pages-1; }
if(self::$page == self::$pages){ self::$pager[1] = self::$pages-1; self::$pager[2] = self::$pages-2; }
if(self::$page == self::$pages-1){ self::$pager[1] = self::$pages-2; self::$pager[2] = self::$pages-1; }
}
public static function insert()
{
if(self::$pages <= 1) return;
?>
<nav>
<ul class="pagination justify-content-end mb-0">
<li class="page-item<?=self::$page<=1?' disabled':''?>">
<a class="page-link" <?=self::$page>1?'href="'.self::$url.(self::$page-1).'"':''?>aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
<span class="sr-only">Previous</span>
</a>
</li>
<li class="page-item<?=self::$page==1?' active':''?>"><a class="page-link"<?=self::$page!=1?' href="'.self::$url.'1" ':''?>>1</a></li>
<?php if(self::$pages > 2){ if(self::$page > 3){ ?><li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">&hellip;</a></li><?php } ?>
<?php for($i=1; $i<4; $i++){ if(self::$page == $i-1 || self::$pages-self::$page == $i-2) break; ?>
<li class="page-item<?=self::$page==self::$pager[$i]?' active':''?>"><a class="page-link"<?=self::$page!=self::$pager[$i]?' href="'.self::$url.self::$pager[$i].'" ':''?>><?=number_format(self::$pager[$i])?></a></li>
<?php } ?>
<?php if(self::$page < self::$pages-2){ ?><li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">&hellip;</a></li><?php } } ?>
<li class="page-item<?=self::$page==self::$pages?' active':''?>"><a class="page-link"<?=self::$page!=self::$pages?' href="'.self::$url.self::$pages.'" ':''?>><?=number_format(self::$pages)?></a></li>
<li class="page-item<?=self::$page>self::$pages?' disabled':''?>">
<a class="page-link" <?=self::$page<self::$pages?'href="'.self::$url.(self::$page+1).'"':''?>aria-label="Next">
<span aria-hidden="true">&raquo;</span>
<span class="sr-only">Previous</span>
</a>
</li>
</ul>
</nav>
<?php
}
}

View File

@ -0,0 +1,24 @@
<?php
class Games
{
static function GetServerInfo($id)
{
return db::run("
SELECT selfhosted_servers.*,
users.username,
users.jointime,
(SELECT COUNT(*) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = selfhosted_servers.id AND valid) AS players,
(ping+35 > UNIX_TIMESTAMP()) AS online
FROM selfhosted_servers INNER JOIN users ON users.id = hoster WHERE selfhosted_servers.id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
}
static function GetPlayersInServer($serverID)
{
return db::run("
SELECT users.* FROM selfhosted_servers
INNER JOIN client_sessions ON client_sessions.ping+35 > UNIX_TIMESTAMP() AND serverID = selfhosted_servers.id AND valid
INNER JOIN users ON users.id = uid
WHERE selfhosted_servers.id = :id GROUP BY client_sessions.uid", [":id" => $serverID]);
}
}

View File

@ -0,0 +1,162 @@
<?php
class Groups
{
static function GetGroupInfo($GroupID, $Barebones = false, $Force = false)
{
if($Barebones)
{
$GroupInfo = db::run("SELECT * FROM groups WHERE groups.id = :id", [":id" => $GroupID])->fetch(PDO::FETCH_OBJ);
}
else
{
$GroupInfo = db::run(
"SELECT groups.*,
(SELECT COUNT(*) FROM groups_members WHERE GroupID = :id AND NOT Pending) AS MemberCount,
users.username AS ownername FROM groups
INNER JOIN users ON users.id = groups.owner
WHERE groups.id = :id",
[":id" => $GroupID]
)->fetch(PDO::FETCH_OBJ);
}
if(!$Force && $GroupInfo && $GroupInfo->deleted) return false;
return $GroupInfo;
}
static function GetGroupStatus($GroupID)
{
return db::run(
"SELECT feed.*, users.username FROM feed
INNER JOIN users ON users.id = feed.userId
WHERE groupId = :GroupID ORDER BY id DESC LIMIT 1",
[":GroupID" => $GroupID]
)->fetch(PDO::FETCH_OBJ);
}
static function GetLastGroupUserJoined($UserID)
{
$GroupID = db::run(
"SELECT GroupID FROM groups_members WHERE UserID = :UserID ORDER BY Joined DESC LIMIT 1",
[":UserID" => $UserID]
)->fetchColumn();
return self::GetGroupInfo($GroupID);
}
static function GetRankInfo($GroupID, $RankLevel)
{
$RankInfo = db::run(
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID AND Rank = :RankLevel",
[":GroupID" => $GroupID, ":RankLevel" => $RankLevel]
)->fetch(PDO::FETCH_OBJ);
if(!$RankInfo) return false;
return (object) [
"Name" => $RankInfo->Name,
"Description" => $RankInfo->Description,
"Level" => $RankInfo->Rank,
"Permissions" => json_decode($RankInfo->Permissions)
];
}
static function GetGroupRanks($GroupID, $includeGuest = false)
{
if($includeGuest)
return db::run("SELECT * FROM groups_ranks WHERE GroupID = :id ORDER BY Rank ASC", [":id" => $GroupID]);
else
return db::run("SELECT * FROM groups_ranks WHERE GroupID = :id AND Rank != 0 ORDER BY Rank ASC", [":id" => $GroupID]);
}
static function CheckIfUserInGroup($UserID, $GroupID)
{
return db::run(
"SELECT * FROM groups_members WHERE UserID = :UserID AND GroupID = :GroupID",
[":UserID" => $UserID, ":GroupID" => $GroupID]
)->rowCount();
}
static function GetUserRank($UserID, $GroupID)
{
$RankLevel = db::run(
"SELECT Rank FROM groups_members WHERE UserID = :UserID And GroupID = :GroupID",
[":UserID" => $UserID, ":GroupID" => $GroupID]
)->fetchColumn();
if(!$RankLevel) return self::GetRankInfo($GroupID, 0);
return self::GetRankInfo($GroupID, $RankLevel);
}
static function GetUserGroups($UserID)
{
return db::run(
"SELECT groups.* FROM groups_members
INNER JOIN groups ON groups.id = groups_members.GroupID
WHERE groups_members.UserID = :UserID
ORDER BY groups_members.Joined DESC",
[":UserID" => $UserID]
);
}
static function LogAction($GroupID, $Category, $Description)
{
// small note: when using this, you gotta be very careful about what you pass into the description
// the description must be sanitized when inserted into the db, not when fetched from an api
// this is because the description may contain hyperlinks or other html elements
// also here's a list of categories:
// Delete Post
// Remove Member
// Accept Join Request
// Decline Join Request
// Post Shout
// Change Rank
// Buy Ad
// Send Ally Request
// Create Enemy
// Accept Ally Request
// Decline Ally Request
// Delete Ally
// Delete Enemy
// Add Group Place
// Delete Group Place
// Create Items
// Configure Items
// Spend Group Funds
// Change Owner
// Delete
// Adjust Currency Amounts
// Abandon
// Claim
// Rename
// Change Description
// Create Group Asset
// Update Group Asset
// Configure Group Asset
// Revert Group Asset
// Create Group Developer Product
// Configure Group Game
// Lock
// Unlock
// Create Pass
// Create Badge
// Configure Badge
// Save Place
// Publish Place
// Invite to Clan
// Kick from Clan
// Cancel Clan Invite
// Buy Clan
if(!SESSION) return false;
$MyRank = self::GetUserRank(SESSION["userId"], $GroupID);
db::run(
"INSERT INTO groups_audit (GroupID, Category, Time, UserID, Rank, Description)
VALUES (:GroupID, :Category, UNIX_TIMESTAMP(), :UserID, :Rank, :Description)",
[":GroupID" => $GroupID, ":Category" => $Category, ":UserID" => SESSION["userId"], ":Rank" => $MyRank->Name, ":Description" => $Description]
);
}
}

View File

@ -0,0 +1,51 @@
<?php
class Gzip
{
// this is to compress models and places to help conserve space
// this should be used only for models and places, nothing else
//http://stackoverflow.com/questions/6073397/how-do-you-create-a-gz-file-using-php
static function Compress(string $inFilename, int $level = 9): string
{
// Is the file gzipped already?
$extension = pathinfo($inFilename, PATHINFO_EXTENSION);
if ($extension == "gz") { return $inFilename; }
// Open input file
$inFile = fopen($inFilename, "rb");
if ($inFile === false) { throw new \Exception("Unable to open input file: $inFilename"); }
// Open output file
$gzFilename = $inFilename.".gz";
$gzFile = gzopen($gzFilename, "wb".$level);
if ($gzFile === false)
{
fclose($inFile);
throw new \Exception("Unable to open output file: $gzFilename");
}
// Stream copy
$length = 65536 * 1024; // 512 kB
while (!feof($inFile)) { gzwrite($gzFile, fread($inFile, $length)); }
// Close files
fclose($inFile);
gzclose($gzFile);
// Return the new filename
//delete original
unlink($inFilename);
rename($gzFilename, $inFilename);
return $gzFilename;
}
static function Decompress($filename, $buffer_size = 8192)
{
$buffer = "";
$file = gzopen($filename, 'rb');
while(!gzeof($file)) { $buffer .= gzread($file, $buffer_size); }
gzclose($file);
return $buffer;
}
}

View File

@ -0,0 +1,73 @@
<?php
class Image
{
static function Process($handle, $options)
{
$image = $options["image"] ?? true;
$resize = $options["resize"] ?? true;
$keepRatio = $options["keepRatio"] ?? false;
$scaleX = $options["scaleX"] ?? false;
$scaleY = $options["scaleY"] ?? false;
$handle->file_new_name_ext = "";
$handle->file_new_name_body = $options["name"];
if($image)
{
$handle->image_convert = "png";
$handle->image_resize = $resize;
if($resize)
{
if($keepRatio) $handle->image_ratio_fill = $options["align"];
if($scaleX) $handle->image_ratio_x = true; else $handle->image_x = $options["x"];
if($scaleY) $handle->image_ratio_y = true; else $handle->image_y = $options["y"];
}
}
if(strlen($options["name"]) && file_exists(ROOT.$options["dir"].$options["name"]))
unlink(ROOT.$options["dir"].$options["name"]);
$handle->process(ROOT.$options["dir"]);
if(!$handle->processed) return $handle->error;
return true;
}
static function Resize($file, $w, $h, $path = false)
{
list($width, $height) = getimagesize($file);
$src = imagecreatefrompng($file);
$dst = imagecreatetruecolor($w, $h);
imagealphablending($dst, false);
imagesavealpha($dst, true);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
// this resize function is used in conjunction with an imagepng function
// to resize an existing image and upload - having to do this eve
if($path) imagepng($dst, $path);
return $dst;
}
static function MergeLayers($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct)
{
$cut = imagecreatetruecolor($src_w, $src_h);
imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct);
}
// pre rendered thumbnails (scripts and audios) are all rendered with the same size
// so this just sorta cleans up the whole thing
static function RenderFromStaticImage($img, $assetID)
{
Image::Resize(ROOT."/thumbs/$img.png", 75, 75, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-75x75.png");
Image::Resize(ROOT."/thumbs/$img.png", 100, 100, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-100x100.png");
Image::Resize(ROOT."/thumbs/$img.png", 110, 110, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-110x110.png");
Image::Resize(ROOT."/thumbs/$img.png", 250, 250, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-250x250.png");
Image::Resize(ROOT."/thumbs/$img.png", 352, 352, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-352x352.png");
Image::Resize(ROOT."/thumbs/$img.png", 420, 230, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-420x230.png");
Image::Resize(ROOT."/thumbs/$img.png", 420, 420, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-420x420.png");
}
}

View File

@ -0,0 +1,18 @@
<?php
class RBXClient
{
static function CryptGetSignature($data)
{
openssl_sign($data, $signature, openssl_pkey_get_private("file://".ROOT."/../polygon_private.pem"));
return base64_encode($signature);
}
static function CryptSignScript($data, $assetID = false)
{
if($assetID) $data = "%{$assetID}%\n{$data}";
else $data = "\n{$data}";
$signedScript = "%" . self::CryptGetSignature($data) . "%{$data}";
return $signedScript;
}
}

View File

@ -0,0 +1,30 @@
<?php
class System
{
static function GetFileSize($bytes, $binaryPrefix = true)
{
$unit=array('B','KB','MB','GB','TB','PB');
if (!$bytes) return '0 ' . $unit[0];
if ($binaryPrefix) return round($bytes/pow(1024,($i=floor(log($bytes,1024)))),2) .' '. ($unit[$i] ?? 'B');
return round($bytes/pow(1000,($i=floor(log($bytes,1000)))),2) .' '. ($unit[$i] ?? 'B');
}
static function GetFolderSize($path, $raw = false)
{
$io = popen("du -sb $path", "r");
$size = (int)filter_var(explode($path, fgets($io, 4096), 2)[0], FILTER_SANITIZE_NUMBER_INT);
pclose($io);
if($raw) return $size;
return self::getFileSize($size);
}
static function GetMemoryUsage()
{
$lines = explode("\n", file_get_contents('/proc/meminfo'));
$total = (int) filter_var($lines[0], FILTER_SANITIZE_NUMBER_INT);
$free = (int) filter_var($lines[1], FILTER_SANITIZE_NUMBER_INT);
return (object)["total" => $total*1024, "free" => $free*1024];
}
}

View File

@ -0,0 +1,112 @@
<?php
class Thumbnails
{
// this is for use with the new polygon cdn
// currently this just calculates the sha1 hash
// of the user's current thumbnail on the fly
// from what ive seen it doesnt affect performance
// too much but ideally i would have the hash cached
// in the database, but for now this should do fine
private static string $BaseURL = "https://polygoncdn.pizzaboxer.xyz/";
private static array $StatusThumbnails =
[
"pending-100x100.png" => "0180a01964362301c67cc47344ff34c2041573c0",
"pending-110x110.png" => "e3dd8134956391d4b29070f3d4fc8db1a604f160",
"pending-250x250.png" => "d2c46fc832fb48e1d24935893124d21f16cb5824",
"pending-352x352.png" => "a4ce4cc7e648fba21da9093bcacf1c33c3903ab9",
"pending-420x420.png" => "2f4e0764e8ba3946f52e2b727ce5986776a8a0de",
"pending-48x48.png" => "4e3da1b2be713426b48ddddbd4ead386aadec461",
"pending-75x75.png" => "6ab927863f95d37af1546d31e3bf8b096cc9ed4a",
"rendering-100x100.png" => "b67cc4a3d126f29a0c11e7cba3843e6aceadb769",
"rendering-110x110.png" => "d059575ffed532648d3dcf6b1429defcc98fc8b1",
"rendering-250x250.png" => "9794c31aa3c4779f9cb2c541cedf2c25fa3397fe",
"rendering-352x352.png" => "f523775cc3da917e15c3b15e4165fee2562c0ff1",
"rendering-420x420.png" => "a9e786b5c339f29f9016d21858bf22c54146855c",
"rendering-48x48.png" => "d7a9b5d7044636d3011541634aee43ca4a86ade6",
"rendering-75x75.png" => "fa2ec2e53a4d50d9103a6e4370a3299ba5391544",
"unapproved-100x100.png" => "d4b4b1f0518597bafcd9cf342b6466275db34bbc",
"unapproved-110x110.png" => "7ad17e54cf834efd298d76c8799f58daf9c6829f",
"unapproved-250x250.png" => "cddec9d17ee3afc5da51d2fbf8011e562362e39a",
"unapproved-352x352.png" => "509b6c7bdb121e4185662987096860dd7f54ae11",
"unapproved-420x420.png" => "f31bc4f3d5008732f91ac90608d4e77fcd8d8d2b",
"unapproved-48x48.png" => "82da22ba47414d25ee544a253f6129d106cf17ef",
"unapproved-75x75.png" => "13ad6ad9ab4f84f03c58165bc8468a181d07339c"
];
private static function GetCDNLocation($hash)
{
return self::$BaseURL.$hash.".png";
}
static function GetStatus($status, $x, $y)
{
return self::GetCDNLocation(self::$StatusThumbnails["{$status}-{$x}x{$y}.png"]);
}
static function UploadToCDN($filepath)
{
$hash = sha1_file($filepath);
file_put_contents($_SERVER["DOCUMENT_ROOT"]."/../polygoncdn/".$hash.".png", file_get_contents($filepath));
}
static function GetAsset($sqlResult, $x, $y, $force = false)
{
// for this we need to pass in an sql pdo result
// this is so we can check if the asset is under review or disapproved
// passing in the sql result here saves us from having to do another query
// if we implement hash caching then we'd also use this for that
$assetID = $sqlResult->id;
$filepath = SITE_CONFIG['paths']['thumbs_assets']."/{$assetID}-{$x}x{$y}.png";
if(!file_exists($filepath)) return self::GetStatus("rendering", $x, $y);
if($force || $sqlResult->approved == 1) return self::GetCDNLocation(sha1_file($filepath));
if($sqlResult->approved == 0) return self::GetStatus("pending", $x, $y);
if($sqlResult->approved == 2) return self::GetStatus("unapproved", $x, $y);
}
static function GetAssetFromID($AssetID, $x, $y, $force = false)
{
// primarily used for fetching group emblems
// we dont need to block this as group emblems are fine to show publicly
$AssetInfo = db::run("SELECT * FROM assets WHERE id = :id", [":id" => $AssetID]);
if(!$AssetInfo->rowCount()) return false;
return self::GetAsset($AssetInfo->fetch(PDO::FETCH_OBJ), $x, $y, $force);
}
static function GetAvatar($avatarID, $x, $y)
{
if(!SESSION) return self::GetStatus("rendering", $x, $y);
$filepath = SITE_CONFIG['paths']['thumbs_avatars']."/{$avatarID}-{$x}x{$y}.png";
if(!file_exists($filepath)) return self::GetStatus("rendering", $x, $y);
return self::GetCDNLocation(sha1_file($filepath));
}
static function UploadAsset($handle, $assetID, $x, $y, $additionalOptions = [])
{
Polygon::ImportClass("Image");
$options = ["name" => "{$assetID}-{$x}x{$y}.png", "x" => $x, "y" => $y, "dir" => "/thumbs/assets/"];
$options = array_merge($options, $additionalOptions);
Image::Process($handle, $options);
self::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/{$assetID}-{$x}x{$y}.png");
}
static function UploadAvatar($handle, $avatarID, $x, $y)
{
Polygon::ImportClass("Image");
Image::Process($handle, ["name" => "{$avatarID}-{$x}x{$y}.png", "x" => $x, "y" => $y, "dir" => "/thumbs/avatars/"]);
self::UploadToCDN(SITE_CONFIG['paths']['thumbs_avatars']."/{$avatarID}-{$x}x{$y}.png");
}
}

View File

@ -0,0 +1,47 @@
<?php
class TwoFactorAuth
{
static function Initialize()
{
require ROOT.'/api/private/vendors/2fa/FixedBitNotation.php';
require ROOT.'/api/private/vendors/2fa/GoogleQrUrl.php';
require ROOT.'/api/private/vendors/2fa/GoogleAuthenticatorInterface.php';
require ROOT.'/api/private/vendors/2fa/GoogleAuthenticator.php';
return new \Google\Authenticator\GoogleAuthenticator();
}
static function Toggle()
{
if(!SESSION) return false;
db::run(
"UPDATE users SET twofa = :2fa WHERE id = :uid",
[":2fa" => (int)!SESSION["2fa"], ":uid" => SESSION["userId"]]
);
}
static function GenerateRecoveryCodes()
{
if(!SESSION) return false;
$codes = str_split(bin2hex(random_bytes(60)), 12);
db::run(
"UPDATE users SET twofaRecoveryCodes = :json WHERE id = :uid",
[":json" => json_encode(array_fill_keys($codes, true)), ":uid" => SESSION["userId"]]
);
return $codes;
}
static function GenerateNewSecret($GoogleAuthenticator)
{
if(!SESSION) return false;
$secret = $GoogleAuthenticator->generateSecret();
db::run(
"UPDATE users SET twofaSecret = :secret WHERE id = :uid",
[":secret" => $secret, ":uid" => SESSION["userId"]]
);
return $secret;
}
}

View File

@ -1,6 +1,4 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/config.php';
try
{
$pdo = new PDO(
@ -17,4 +15,17 @@ catch(PDOException $e)
echo "Project Polygon is currently undergoing maintenance. We will be back soon!";
if(isset($_GET['showError'])) echo $e->getMessage();
die();
}
class db
{
static function run($sql, $params = false)
{
global $pdo;
if(!$params) return $pdo->query($sql);
$query = $pdo->prepare($sql);
$query->execute($params);
return $query;
}
}

View File

@ -11,15 +11,13 @@ class pageBuilder
];
// this is separate from js dependencies as these MUST be loaded at the bottom
// when core.js is moved to its own file instead of being plopped directly into
// the html, this wont be necessary
public static array $polygonScripts = [];
public static array $polygonScripts = ["/js/polygon/core.js?t=35"];
public static array $CSSdependencies =
[
"/css/fontawesome-pro-v5.15.2/css/all.css",
"/css/toastr.css",
"/css/polygon.css?t=4"
"/css/polygon.css?t=15"
];
public static array $pageConfig =
@ -28,7 +26,7 @@ class pageBuilder
"og:site_name" => SITE_CONFIG["site"]["name"],
"og:url" => "https://polygon.pizzaboxer.xyz",
"og:description" => "yeah its a website about shapes and squares and triangles and stuff and ummmmm",
"og:image" => "https://chef.pizzaboxer.xyz/img/ProjectPolygon.png",
"og:image" => "https://polygon.pizzaboxer.xyz/img/ProjectPolygon.png",
"includeNav" => true,
"includeFooter" => true,
"app-attributes" => ""
@ -51,10 +49,32 @@ class pageBuilder
static function buildHeader()
{
$theme = "light";
// ideally i should probably have this loaded in from
// core.php instead of doing the php query on the fly here
global $pdo, $announcements, $markdown;
if(SESSION && SESSION["adminLevel"]) $pendingAssets = db::run("SELECT COUNT(*) FROM assets WHERE NOT approved AND type != 1")->fetchColumn();
if(SESSION)
{
if(SESSION["adminLevel"])
{
$pendingAssets = db::run("SELECT COUNT(*) FROM assets WHERE NOT approved AND (type != 1 || (SELECT COUNT(*) FROM polygon.groups WHERE emblem = assets.id))")->fetchColumn();
}
$theme = SESSION["userInfo"]["theme"];
if($theme == "dark") self::$CSSdependencies[] = "/css/polygon-dark.css?t=4";
else if($theme == "2013") self::$CSSdependencies[] = "/css/polygon-2013.css";
else if($theme == "hitius") self::$CSSdependencies[] = "/css/polygon-hitius.css";
else if($theme == "2014")
{
self::$CSSdependencies[] = "/css/polygon-2014.css?t=".time();
self::$JSdependencies[] = "/js/polygon/Navigation2014.js?t=".time();
self::$pageConfig["app-attributes"] .= " id=\"navContent\"";
}
}
ob_start();
?>
<!DOCTYPE html>
<html lang="en">
@ -72,44 +92,122 @@ class pageBuilder
<meta property="og:type" content="Website">
<meta property="og:image" content="<?=self::$pageConfig["og:image"]?>">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<?php foreach(self::$CSSdependencies as $url){ ?>
<?php foreach(self::$CSSdependencies as $url){ ?>
<link rel="stylesheet" href="<?=$url?>">
<?php } foreach(self::$JSdependencies as $url){ ?>
<?php } foreach(self::$JSdependencies as $url){ ?>
<script type="text/javascript" src="<?=$url?>"></script>
<?php } ?>
<?php } ?>
<script>
var polygon = {};
polygon.user =
{
<?php if(SESSION) { ?>
<?php if(SESSION) { ?>
logged_in: true,
name: "<?=SESSION["userName"]?>",
id: <?=SESSION["userId"]?>,
money: <?=SESSION["currency"]?>,
<?php } else { ?>
<?php } else { ?>
logged_in: false,
<?php } ?>
<?php } ?>
};
</script>
<?php if(SESSION && SESSION["userInfo"]["theme"] == "dark") { ?>
<link rel="stylesheet" href="/css/polygon-dark.css?t=<?=time()?>">
<?php } ?>
</head>
<body>
<?php if(self::$pageConfig["includeNav"]) { ?>
<nav class="navbar navbar-expand-lg navbar-dark navbar-orange navbar-top py-0">
<div class="container">
<a class="navbar-brand" href="/"><?=SITE_CONFIG["site"]["name"]?></a>
<?php if($theme == "2014") { ?>
<body class="layout-2014 mt-5">
<div class="nav-container no-gutter-ads">
<div class="navigation" id="navigation" onselectstart="return false;">
<div class="navigation-container">
<ul>
<li>
<div class="user">
<div class="menu-item">
<div class="username"><a href="/user?ID=<?=SESSION["userId"]?>"><?=SESSION["userName"]?></a></div>
</div>
</div>
</li>
<li class="nav2014-my-roblox">
<a class="menu-item" href="/home">
<span class="icon"></span>Home
</a>
</li>
<li class="nav2014-profile">
<a class="menu-item" href="/user">
<span class="icon"></span>Profile
</a>
</li>
<!--li class="nav2014-messages">
<a class="menu-item" href="/user">
<span class="icon"></span>Messages
</a>
</li-->
<li class="nav2014-friends">
<a class="menu-item" href="/friends">
<span class="icon"></span>Friends
<span class="notification-icon text-light friend-requests-indicator<?=!SESSION["friendRequests"]?' d-none':''?>"><?=SESSION["friendRequests"]?></span>
</a>
</li>
<li class="nav2014-character">
<a class="menu-item" href="/my/character">
<span class="icon"></span>Character
</a>
</li>
<li class="nav2014-inventory">
<a class="menu-item" href="/my/stuff">
<span class="icon"></span>Inventory
</a>
</li>
<li class="nav2014-develop">
<a class="menu-item" href="/develop">
<span class="icon"></span>Develop
</a>
</li>
<li class="nav2014-groups">
<a class="menu-item" href="/groups">
<span class="icon"></span>Groups
</a>
</li>
<li class="nav2014-forum">
<a class="menu-item" href="/forum">
<span class="icon"></span>Forum
</a>
</li>
<?php if(SESSION["adminLevel"]) { ?>
<li class="nav2014-profile">
<a class="menu-item" href="/admin">
<span class="icon"></span>Admin
<span class="notification-icon text-light <?=!$pendingAssets?' d-none':''?>"><?=$pendingAssets?></span>
</a>
</li>
<?php } ?>
<!--li class="nav2014-blog">
<a class="menu-item" href="https://web.archive.org/web/20140821165031/http://blog.roblox.com/">
<span class="icon"></span>Blog
</a>
</li>
<li class="upgrade-now">
<a href="/web/20140821165031/http://www.roblox.com/Upgrades/BuildersClubMemberships.aspx" class="nav-button" id="builders-club-button">Upgrade Now</a>
</li>
<li class="nav2014-events">
<span class="events-text">Events</span>
</li>
<li class="nav2014-sponsor">
<a class="menu-item" href="/web/20140821165031/http://www.roblox.com/event/clanbattle" title="Clan Battle">
<img src="https://web.archive.org/web/20140821165031im_/http://images.rbxcdn.com/6d4be1b1b1e67971c837da0dae72e82d">
</a>
</li-->
</ul>
</div>
</div>
<nav class="navbar navbar-expand-lg navbar-dark navbar-orange header-2014 fixed-top py-0">
<div class="nav-icon" onselectstart="return false;"></div>
<a class="navbar-brand pt-0" href="/"><img src="/img/2013/roblox_logo.png"></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#primaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="primaryNavbar">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<ul class="navbar-nav header-links mr-auto">
<li class="nav-item">
<a class="nav-link" href="/games">Games</a>
</li>
<li class="nav-item">
@ -121,21 +219,106 @@ class pageBuilder
<li class="nav-item">
<a class="nav-link" href="/forum">Forum</a>
</li>
<div class="search">
<div class="search-input-container">
<input type="text" placeholder="Search">
</div>
<div class="search-icon"></div>
<div class="universal-search-dropdown">
<div class="universal-search-option" data-searchurl="/games?Keyword=">
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Games</div>
</div>
<div class="universal-search-option" data-searchurl="/browse?Category=People&SearchTextBox=">
<div class="universal-search-text">Search <span class="universal-search-string"></span> in People</div>
</div>
<div class="universal-search-option selected" data-searchurl="/browse?Category=Groups&SearchTextBox=">
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Groups</div>
</div>
<div class="universal-search-option" data-searchurl="/catalog?Keyword=">
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Catalog</div>
</div>
<div class="universal-search-option" data-searchurl="/catalog?CurrencyType=0&SortType=1&Category=6&Keyword=">
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Library</div>
</div>
</div>
</div>
</ul>
<div class="navbar-nav">
<div class="navbar-button-container">
<a class="btn btn-sm mr-4" data-toggle="tooltip" data-html="true" data-placement="bottom" title="<?=SESSION["currency"]?> <?=SITE_CONFIG["site"]["currency"]?> <br> Next stipend in <?=timeSince("@".SESSION["nextCurrencyStipend"], true, false, false, true)?>" href="/my/money"><i class="fas fa-pizza-slice mr-1"></i> <?=SESSION["currency"]?></a>
</div>
<div class="navbar-button-container dropdown-hover">
<a class="btn btn-sm" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-cog"></i></a>
<div class="dropdown-menu dropdown-menu-right mx-2" aria-labelledby="settings-dropdown">
<a class="dropdown-item" href="/my/account">Settings</a>
<a class="dropdown-item" href="/logout">Logout</a>
</div>
</div>
</div>
</div>
</nav>
</div>
<noscript>
<div id="ctl00_Announcement">
<div id="ctl00_SystemAlertDiv" class="SystemAlert" style="background-color:red">
<div id="ctl00_SystemAlertTextColor" class="SystemAlertText">
<div id="ctl00_LabelAnnouncement">Please enable Javascript to use all the features on this site.</div>
</div>
</div>
</div>
</noscript>
<?php foreach($announcements as $announcement) { ?>
<div id="ctl00_Announcement">
<div id="ctl00_SystemAlertDiv" class="SystemAlert" style="background-color:<?=$announcement["bgcolor"]?>">
<div id="ctl00_SystemAlertTextColor" class="SystemAlertText">
<div id="ctl00_LabelAnnouncement"><?=$markdown->line($announcement["text"])?></div>
</div>
</div>
</div>
<?php } /* foreach($announcements as $announcement) */ ?>
<div class="app container py-4 nav-content"<?=self::$pageConfig['app-attributes']?>>
<?php } else { ?>
<body>
<?php if(self::$pageConfig["includeNav"]) { ?>
<nav class="navbar navbar-expand-lg navbar-dark navbar-orange navbar-top py-0">
<div class="container">
<a class="navbar-brand" href="/"><?=SITE_CONFIG["site"]["name"]?></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#primaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="primaryNavbar">
<ul class="navbar-nav header-links mr-auto">
<?php if(SESSION) { ?>
<li class="nav-item">
<a class="nav-link" href="/games">Games</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/catalog">Catalog</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/develop">Develop</a>
</li>
<?php } ?>
<li class="nav-item">
<a class="nav-link" href="/forum">Forum</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/browse">People</a>
</li>
<!--li class="nav-item dropdown">
<?php if(SESSION) { ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="moreDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">More</a>
<div class="dropdown-menu mt-0" aria-labelledby="moreDropdown">
<a class="dropdown-item" href="/browse">People</a>
<div class="dropdown-divider"></div>
<?php if(SESSION) { ?><a class="dropdown-item" href="/discord">Discord</a><?php } ?>
<a class="dropdown-item" href="https://twitter.com/boxerpizza">Twitter</a>
<!--a class="dropdown-item" href="/browse">People</a>
<div class="dropdown-divider"></div-->
<a class="dropdown-item" href="/discord">Discord</a>
<!--a class="dropdown-item" href="https://twitter.com/boxerpizza">Twitter</a-->
</div>
</li-->
</li>
<?php } ?>
</ul>
<div class="navbar-nav">
<?php if(SESSION) { ?>
<?php if(SESSION) { ?>
<a class="nav-link mr-2" href="/user?ID=<?=SESSION["userId"]?>"><?=SESSION["userName"]?></a>
<div class="navbar-button-container">
<a class="btn btn-sm btn-light py-0 px-1 friend-requests-indicator<?=!SESSION["friendRequests"]?' d-none':''?>"><?=SESSION["friendRequests"]?></a>
@ -149,16 +332,16 @@ class pageBuilder
<div class="navbar-button-container">
<a class="btn btn-sm btn-light my-1 mr-2 px-3" href="/logout">Logout</a>
</div>
<?php } else { ?>
<a class="nav-link" href="/register">Sign Up</a>
<a class="nav-link darken px-0">or</a>
<?php } else { ?>
<a class="nav-link" href="/">Sign Up</a>
<span class="nav-link darken px-0">or</span>
<a class="btn btn-sm btn-light my-1 mx-2 px-4" href="/login">Login</a>
<?php } ?>
<?php } ?>
</div>
</div>
</div>
</nav>
<?php if(SESSION) { ?>
<?php if(SESSION) { ?>
<nav class="navbar navbar-expand-lg navbar-dark navbar-top bg-dark py-0">
<div class="container">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#secondaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
@ -175,6 +358,9 @@ class pageBuilder
<li class="nav-item">
<a class="nav-link py-1" href="/friends">Friends</a>
</li>
<li class="nav-item">
<a class="nav-link py-1" href="/groups">Groups</a>
</li>
<li class="nav-item">
<a class="nav-link py-1" href="/my/stuff">Inventory</a>
</li>
@ -184,29 +370,33 @@ class pageBuilder
<li class="nav-item">
<a class="nav-link py-1" href="/my/account">Account</a>
</li>
<?php if(SESSION && SESSION["adminLevel"]) { ?>
<?php if(SESSION && SESSION["adminLevel"]) { ?>
<li class="nav-item">
<a class="nav-link py-1" href="/admin">Admin <span class="btn btn-sm btn-outline-light py-0<?=!$pendingAssets?' d-none':''?>" style="margin-top:-3px"><?=$pendingAssets?></span></a>
</li>
<?php } ?>
<?php } ?>
</ul>
</div>
</div>
</nav>
<?php } foreach($announcements as $announcement){ ?>
<div class="alert py-2 mb-0 rounded-0 text-center text-<?=$announcement["textcolor"]?>" role="alert" style="background-color: <?=$announcement["bgcolor"]?>">
<?=$markdown->text($announcement["text"])?>
<?php } /* if(SESSION) */ ?>
</div>
<?php } } ?>
<noscript>
<div class="alert py-2 mb-0 rounded-0 text-center text-light bg-danger" role="alert">
disabling javascript breaks the ux in half so dont do it pls
</div>
</noscript>
<div class="app container py-4"<?=self::$pageConfig['app-attributes']?>>
<?php } static function buildFooter() { ?>
<?php foreach($announcements as $announcement) { ?>
<div class="alert py-2 mb-0 rounded-0 text-center text-<?=$announcement["textcolor"]?>" role="alert" style="background-color: <?=$announcement["bgcolor"]?>">
<?=$markdown->text($announcement["text"])?>
</div>
<?php if(self::$pageConfig["includeFooter"]) { ?>
<?php } /* foreach($announcements as $announcement) */ ?>
<?php } /* if(self::$pageConfig["includeNav"]) */ ?>
<div class="app container py-4"<?=self::$pageConfig['app-attributes']?>>
<?php } /* if($theme == "2014") */ ?>
<?php } static function buildFooter() { ?>
</div>
<?php if(self::$pageConfig["includeFooter"]) { ?>
<nav class="footer navbar navbar-light navbar-orange">
<div class="container py-2 text-light text-center">
<!--div class="row" style="width:100%">
@ -223,23 +413,24 @@ class pageBuilder
</div>
</div>
</nav>
<?php } ?>
<?php } ?>
<div class="global modal fade" tabindex="-1" role="dialog" aria-labelledby="primaryModalCenter" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header card-header py-2">
<h3 class="col-12 modal-title text-center font-weight-normal" id="primaryModalTitle"></h3>
</div>
<div class="modal-body text-center text-break" style="white-space: pre-line">
<div class="modal-header card-header bg-cardpanel py-2">
<h3 class="col-12 modal-title text-center font-weight-normal"></h3>
</div>
<div class="modal-body text-center text-break">
your smell
</div>
<div class="modal-footer text-center">
</div>
<div class="modal-footer text-center">
<div class="mx-auto">
</div>
</div>
</div>
</div>
</div>
<?php if(SESSION) { ?>
<div class="placelauncher modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content"></div>
@ -256,187 +447,17 @@ class pageBuilder
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<img src="/img/ProjectPolygon.png" class="img-fluid pl-3 py-3 pr-1" style="max-width: 150px">
<h2 class="font-weight-normal">Welcome to <?=SITE_CONFIG["site"]["name"]?>!</h2>
<h5 class="font-weight-normal">Seems like you don't have the <span class="year">2010</span> client installed</h5>
<a class="btn btn-success btn-block mx-auto mt-3 install" style="max-width:18rem">Download Client</a>
<h5 class="font-weight-normal">Seems like you don't have <span class="year">2010</span> installed</h5>
<a class="btn btn-success btn-block mx-auto mt-3 install" style="max-width:18rem">Download</a>
</div>
<div class="modal-footer text-center py-2">
<small class="mx-auto">If you do have the client installed, just ignore this</small>
</div>
</div>
</div>
<?php } ?>
<script src="/js/bootstrap.bundle.min.js"></script>
<script>
//core.js
$.ajaxSetup({ headers: { 'x-polygon-csrf': $('meta[name="polygon-csrf"]').attr('content') } });
/* todo - dont be lazy and work on this
polygon.ajax = function(url, method, data, trusted, successCallback, errorCallback)
{
var ajaxOptions = {type: method, data: data};
if(trusted)
{
ajaxOptions.url = window.location.origin + url;
ajaxOptions.headers: {'x-polygon-csrf': $('meta[name="polygon-csrf"]').attr('content')};
}
else { ajaxOptions.url = url; }
}*/
polygon.button =
{
busy: function(button)
{
$(button).attr("disabled", "disabled").find(".spinner-border").removeClass("d-none");
},
active: function(button)
{
$(button).removeAttr("disabled").find(".spinner-border").addClass("d-none");
}
};
polygon.insertAlert = function(options)
{
var alertCode = '';
if(options.alertClasses == undefined) options.alertClasses = '';
if(options.parentClasses) alertCode += '<div class="'+options.parentClasses+'">';
alertCode += '<div class="alert alert-danger '+options.alertClasses+' px-2 py-1" style="width: fit-content;" role="alert">'+options.text+'</div>';
if(options.parentClasses) alertCode += '</div>';
$(options.parent).append(alertCode);
}
polygon.buildModal = function(options)
{
if(options.options == undefined) options.options = "show";
if(options.fade == undefined) $(".global.modal").addClass("fade");
else if(!options.fade) $(".global.modal").removeClass("fade");
var footer = $(".global.modal .modal-footer .mx-auto");
$(".global.modal .modal-title").html(options.header);
$(".global.modal .modal-body").html(options.body);
footer.empty();
$.each(options.buttons, function(_, button)
{
var buttonCode = '<button type="button" class="'+button.class+' text-center mx-1"';
// todo - improve how attributes are handled
// right now its like {"attr": "data-whatever", "val": 1} instead of just being like {"data-whatever": 1}
if(button.attributes != undefined) $.each(button.attributes, function(_, attr){ buttonCode += ' '+attr.attr+'="'+attr.val+'"'; });
if(button.dismiss) buttonCode += ' data-dismiss="modal"';
buttonCode += '><h4 class="font-weight-normal pb-0">'+button.text+'</h4></button>';
footer.append(buttonCode);
});
if(options.footer) footer.append('<p class="text-muted mt-3 mb-0">'+options.footer+'</p>');
$(".global.modal").modal(options.options);
};
polygon.populate = function(data, template, container)
{
$.each(data, function(_, item)
{
var templateCode = $(template).clone();
templateCode.html(function(_, html)
{
console.log(html);
// todo - this isnt very flexible
for (let key in item) html = html.replace(new RegExp("\\$"+key, "g"), item[key]);
return html;
});
if(templateCode.find("img").attr("preload-src"))
templateCode.find("img").attr("src", templateCode.find("img").attr("preload-src"));
templateCode.appendTo(container);
});
}
polygon.populateControl = function(control, data)
{
return polygon.populate(data, "."+control+"-container .template .item", "."+control+"-container .items");
}
polygon.pagination =
{
register: function(control, callback)
{
var pagination = "."+control+"-container .pagination";
if(!$(pagination).length) return;
$(pagination+" .back").click(function(){ callback(+$(pagination+" .page").val()-1); });
$(pagination+" .next").click(function(){ callback(+$(pagination+" .page").val()+1); });
$(pagination+" .page").on("focusout keypress", this, function(event)
{
if(event.type == "keypress") if(event.which == 13) $(this).blur(); else return;
if($(this).val() == $(this).attr("data-last-page")) return;
$(this).attr("data-last-page", $(this).val());
callback($(this).val());
});
},
handle: function(control, page, pages)
{
var pagination = "."+control+"-container .pagination";
if(!$(pagination).length) return;
if(pages <= 1 || pages == undefined) return $(pagination).addClass("d-none");
$(pagination).removeClass("d-none");
$(pagination+" .pages").text(pages);
if($(pagination+" .page").prop("tagName") == "INPUT") $(pagination+" .page").val(page);
else $(pagination+" .page").text(page);
if(page <= 1) $(pagination+" .back").attr("disabled", "disabled");
else $(pagination+" .back").removeAttr("disabled");
if(page >= pages) $(pagination+" .next").attr("disabled", "disabled");
else $(pagination+" .next").removeAttr("disabled");
}
}
toastr.options =
{
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "10000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
}
$(function()
{
if(polygon.user.logged_in)
{
setInterval(function()
{
if(document.hidden) return;
$.post("/api/account/ping", function(data)
{
if(data.friendRequests) $(".friend-requests-indicator").text(data.friendRequests).removeClass("d-none");
else $(".friend-requests-indicator").addClass("d-none");
if(data.status == 401) window.location.reload();
});
}, 30000);
}
$("[data-toggle='tooltip']").tooltip();
});
</script>
<?php if(SESSION && SESSION["adminLevel"]){ ?>
<?php if(SESSION && SESSION["adminLevel"]){ ?>
<script>
//admin.js
if (polygon.admin == undefined) polygon.admin = {};
@ -515,18 +536,21 @@ class pageBuilder
$("body").on("click", ".post-delete-confirm", function(){ polygon.admin.forum.moderate_post($(this).attr("data-type"), $(this).attr("data-id")); });
$("body").on("click", ".request-render", function(){ polygon.admin.request_render($(this).attr("data-type"), $(this).attr("data-id")); });
</script>
<?php } ?>
<?php foreach(self::$polygonScripts as $url){ ?>
<?php } ?>
<?php foreach(self::$polygonScripts as $url){ ?>
<script type="text/javascript" src="<?=$url?>"></script>
<?php } ?>
<?php } ?>
<?=self::$additionalFooterStuff?>
</body>
</html>
<?php }
<?php
ob_end_flush();
}
static function errorCode($code)
static function errorCode($code, $customText = false)
{
http_response_code($code);
$text =
[
400 => ["title" => "Bad request", "text" => "There was a problem with your request"],
@ -534,6 +558,11 @@ class pageBuilder
420 => ["title" => "Website is currently under maintenance", "text" => "check back later"],
500 => ["title" => "Unexpected error with your request", "text" => "Please try again after a few moments"]
];
if(!isset($text[$code])) $code = 500;
if (is_array($customText) && count($customText)) $text[$code] = $customText;
self::buildHeader();
?>
<div class="card mx-auto" style="max-width:640px;">
@ -541,6 +570,9 @@ class pageBuilder
<img src="/img/error.png">
<h2 class="font-weight-normal"><?=$text[$code]["title"]?></h2>
<?=$text[$code]["text"]?>
<?php if (!is_array($customText) && !empty($customText) && $code == 500) { ?>
<pre class="mt-4"><?=$customText?></pre>
<?php } ?>
<hr>
<a class="btn btn-outline-primary mx-1 mt-1 py-1" onclick="window.history.back()">Go to Previous Page</a>
<a class="btn btn-outline-primary mx-1 mt-1 py-1" href="/">Return Home</a>

View File

@ -0,0 +1,44 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
define("SITE_CONFIG",
[
"database" =>
[
"host" => "127.0.0.1",
"schema" => "polygon",
"username" => "polygon",
"password" => ""
],
"site" =>
[
"name" => "Project Polygon",
"name_secondary" => "Polygon",
"currency" => "Pizzas",
"private" => true,
"games" => true,
"thumbserver" => true
],
"keys" => // DO NOT ALTER ANY OF THESE UNLESS NECESSARY
[
// use \Defuse\Crypto\Key::createNewRandomKey()->saveToAsciiSafeString(); for this
"passwordEncryption" => "",
"renderserverApi" => "",
],
"api" => // deprecated - use above
[
"renderserverKey" => ""
],
"paths" =>
[
"assets" => $_SERVER['DOCUMENT_ROOT']."/asset/files/",
"thumbs_assets" => $_SERVER['DOCUMENT_ROOT']."/thumbs/assets/",
"thumbs_avatars" => $_SERVER['DOCUMENT_ROOT']."/thumbs/avatars/"
]
]);

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
<?php
include $_SERVER['DOCUMENT_ROOT']."/api/private/db.php";
require $_SERVER["DOCUMENT_ROOT"].'/api/private/config.php';
require $_SERVER["DOCUMENT_ROOT"].'/api/private/components/db.php';
if(SITE_CONFIG["api"]["renderserverKey"] != ($_GET['accessKey'] ?? false)) die(http_response_code(403));
$query = $pdo->query("UPDATE servers SET ping = UNIX_TIMESTAMP() WHERE id = 1");

View File

@ -1,5 +1,7 @@
<?php
include $_SERVER['DOCUMENT_ROOT']."/api/private/db.php";
require $_SERVER["DOCUMENT_ROOT"].'/api/private/config.php';
require $_SERVER["DOCUMENT_ROOT"].'/api/private/components/db.php';
if(SITE_CONFIG["api"]["renderserverKey"] != ($_GET['accessKey'] ?? false)) die(http_response_code(403));
header('Content-type: text/javascript');
if(!SITE_CONFIG["site"]["thumbserver"]) die("fart");

View File

@ -16,8 +16,8 @@ for _,v in pairs(game.GuiRoot:GetChildren()) do v:Remove() end
if not game.Players.LocalPlayer then game.Players:CreateLocalPlayer(0) end
plr = game.Players.LocalPlayer
--plr.CharacterAppearance = "http://<?=$_SERVER['HTTP_HOST']?>/asset/characterfetch?userId=<?=$asset?>"
plr.CharacterAppearance = "<?=users::getCharacterAppearance($asset, -1)?>"
--plr.CharacterAppearance = "http://<?=$_SERVER['HTTP_HOST']?>/Asset/CharacterFetch.ashx?userId=<?=$asset?>"
plr.CharacterAppearance = "<?=Users::GetCharacterAppearance($asset, -1)?>"
plr:LoadCharacter()
wait(2)

View File

@ -1,5 +1,7 @@
<?php
include $_SERVER['DOCUMENT_ROOT']."/api/private/db.php";
require $_SERVER["DOCUMENT_ROOT"].'/api/private/config.php';
require $_SERVER["DOCUMENT_ROOT"].'/api/private/components/db.php';
if(SITE_CONFIG["api"]["renderserverKey"] != ($_GET['accessKey'] ?? false)) die(http_response_code(403));
$completetype = $_GET['type'] ?? false; //1 = success; 2 = error;

View File

@ -1,5 +1,8 @@
<?php
include $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
<?php include $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Image");
Polygon::ImportClass("Thumbnails");
if(SITE_CONFIG["api"]["renderserverKey"] != ($_GET['accessKey'] ?? false)) die(http_response_code(403));
$jobid = $_GET['jobID'] ?? false;
@ -16,7 +19,7 @@ if(!$data) die("doesnt exist");
$assetID = $data->assetID;
polygon::importLibrary("class.upload");
Polygon::ImportLibrary("class.upload");
$image = new Upload($_FILES["file"]);
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
@ -24,13 +27,13 @@ $image->image_convert = 'png';
if($data->renderType == "Avatar")
{
/* image::process($image, ["name" => "$assetID-420x420.png", "x" => 420, "y" => 420, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-352x352.png", "x" => 352, "y" => 352, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-250x250.png", "x" => 250, "y" => 250, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-110x110.png", "x" => 110, "y" => 110, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-100x100.png", "x" => 100, "y" => 100, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-75x75.png", "x" => 75, "y" => 75, "dir" => "/thumbs/avatars/"]);
image::process($image, ["name" => "$assetID-48x48.png", "x" => 48, "y" => 48, "dir" => "/thumbs/avatars/"]); */
/* Image::Process($image, ["name" => "$assetID-420x420.png", "x" => 420, "y" => 420, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-352x352.png", "x" => 352, "y" => 352, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-250x250.png", "x" => 250, "y" => 250, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-110x110.png", "x" => 110, "y" => 110, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-100x100.png", "x" => 100, "y" => 100, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-75x75.png", "x" => 75, "y" => 75, "dir" => "/thumbs/avatars/"]);
Image::Process($image, ["name" => "$assetID-48x48.png", "x" => 48, "y" => 48, "dir" => "/thumbs/avatars/"]); */
Thumbnails::UploadAvatar($image, $assetID, 420, 420);
Thumbnails::UploadAvatar($image, $assetID, 352, 352);
Thumbnails::UploadAvatar($image, $assetID, 250, 250);
@ -41,17 +44,17 @@ if($data->renderType == "Avatar")
}
else
{
$type = catalog::getItemInfo($assetID)->type;
$type = Catalog::GetAssetInfo($assetID)->type;
if(in_array($type, [4, 8, 10, 11, 12, 17, 19]))
{
/* image::process($image, ["name" => "$assetID-420x420.png", "x" => 420, "y" => 420, "dir" => "/thumbs/assets/"]);
if(in_array($type, [8, 19])) image::process($image, ["name" => "$assetID-420x230.png", "keepRatio" => true, "align" => "C", "x" => 420, "y" => 230, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-352x352.png", "x" => 352, "y" => 352, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-250x250.png", "x" => 250, "y" => 250, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-110x110.png", "x" => 110, "y" => 110, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-100x100.png", "x" => 100, "y" => 100, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-75x75.png", "x" => 75, "y" => 75, "dir" => "/thumbs/assets/"]);
image::process($image, ["name" => "$assetID-48x48.png", "x" => 48, "y" => 48, "dir" => "/thumbs/assets/"]); */
/* Image::Process($image, ["name" => "$assetID-420x420.png", "x" => 420, "y" => 420, "dir" => "/thumbs/assets/"]);
if(in_array($type, [8, 19])) Image::Process($image, ["name" => "$assetID-420x230.png", "keepRatio" => true, "align" => "C", "x" => 420, "y" => 230, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-352x352.png", "x" => 352, "y" => 352, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-250x250.png", "x" => 250, "y" => 250, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-110x110.png", "x" => 110, "y" => 110, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-100x100.png", "x" => 100, "y" => 100, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-75x75.png", "x" => 75, "y" => 75, "dir" => "/thumbs/assets/"]);
Image::Process($image, ["name" => "$assetID-48x48.png", "x" => 48, "y" => 48, "dir" => "/thumbs/assets/"]); */
if(in_array($type, [8, 19])) Thumbnails::UploadAsset($image, $assetID, 420, 230, ["align" => "C"]);
Thumbnails::UploadAsset($image, $assetID, 420, 420);

75
api/users/get-badges.php Normal file
View File

@ -0,0 +1,75 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize(["method" => "POST"]);
if(!isset($_POST['userID'])) api::respond(400, false, "Invalid Request - userID not set");
if(!is_numeric($_POST['userID'])) api::respond(400, false, "Invalid Request - userID is not numeric");
if(!isset($_POST['type'])) api::respond(400, false, "Invalid Request - badge type not set");
if(!in_array($_POST['type'], ["player", "polygon"])) api::respond(400, false, "Invalid Request - invalid badge type");
$selfProfile = isset($_SERVER['HTTP_REFERER']) && str_ends_with($_SERVER['HTTP_REFERER'], "/user");
$page = $_POST['page'] ?? 1;
$pages = 1;
$type = $_POST['type'] ?? false;
$userinfo = Users::GetInfoFromID($_POST['userID']);
if(!$userinfo) api::respond(400, false, "User does not exist");
$badges = [];
if($type == "polygon")
{
if($userinfo->adminlevel == Users::STAFF_ADMINISTRATOR)
$badges[] =
[
"name" => "Administrator",
"image" => "/img/ProjectPolygon.png",
"info" => "This badge identifies an account as belonging to a ".SITE_CONFIG["site"]["name"]." administrator. Only official ".SITE_CONFIG["site"]["name"]." administrators will possess this badge. If someone claims to be an admin, but does not have this badge, they are potentially trying to mislead you."
];
if($userinfo->adminlevel == Users::STAFF_MODERATOR)
$badges[] =
[
"name" => "Moderator",
"image" => "/img/badges/Moderator.png",
"info" => "Users with this badge are moderators. Moderators have special powers on ".SITE_CONFIG["site"]["name"]." that allow them to moderate users and catalog items that other users upload. Users who are exemplary citizens on ".SITE_CONFIG["site"]["name"]." over a long period of time may be invited to be moderators. This badge is granted by invitation only."
];
if($userinfo->adminlevel == Users::STAFF_CATALOG)
$badges[] =
[
"name" => "Catalog Manager",
"image" => "/img/badges/CatalogManager.png",
"info" => "Users with this badge are catalog managers. Catalog managers have special powers on ".SITE_CONFIG["site"]["name"]." that allow them to create and moderate catalog items that other users upload. Users who are exemplary citizens on ".SITE_CONFIG["site"]["name"]." over a long period of time may be invited to be catalog managers. This badge is granted by invitation only."
];
if(Users::GetFriendCount($userinfo->id) >= 20)
$badges[] =
[
"name" => "Friendship",
"image" => "/img/badges/Friends.png",
"info" => "This badge is given to players who have embraced the ".SITE_CONFIG["site"]["name"]." community and have made at least 20 friends. People who have this badge are good people to know and can probably help you out if you are having trouble."
];
if(time() >= strtotime("1 year", $userinfo->jointime))
$badges[] =
[
"name" => "Veteran",
"image" => "/img/badges/Veteran.png",
"info" => "This decoration is awarded to all citizens who have played ".SITE_CONFIG["site"]["name"]." for at least a year. It recognizes stalwart community members who have stuck with us over countless releases and have helped shape ".SITE_CONFIG["site"]["name"]." into the game that it is today. These medalists are the true steel, the core of the Polygonian history ... and its future."
];
}
else
{
// TODO: add when we get dedicated servers
}
if($badges == [])
{
$responsemsg = ($selfProfile?"You have":$userinfo->username." has")."n't earned any ";
$responsemsg .= $type == "polygon" ? SITE_CONFIG["site"]["name"]." badges" : "player badges";
api::respond(200, true, $responsemsg);
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $pages, "items" => $badges]));

56
api/users/get-groups.php Normal file
View File

@ -0,0 +1,56 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
if(!isset($_POST['userID'])) api::respond(400, false, "Invalid Request - userID not set");
if(!is_numeric($_POST['userID'])) api::respond(400, false, "Invalid Request - userID is not numeric");
$SelfProfile = isset($_SERVER['HTTP_REFERER']) && str_ends_with($_SERVER['HTTP_REFERER'], "/user");
$UserID = $_POST['userID'] ?? false;
$Page = $_POST['page'] ?? 1;
$Groups = [];
$UserInfo = Users::GetInfoFromID($UserID);
if(!$UserInfo) api::respond(400, false, "User does not exist");
$GroupsCount = db::run(
"SELECT COUNT(*) FROM groups_members
INNER JOIN groups ON groups.id = groups_members.GroupID
WHERE groups_members.UserID = :UserID AND NOT groups.deleted",
[":UserID" => $UserID]
)->fetchColumn();
if(!$GroupsCount)
{
api::respond(200, true, ($SelfProfile ? "You are" : "{$UserInfo->username} is")."n't in any groups");
}
$Pages = ceil($GroupsCount/8);
$Offset = ($Page - 1)*8;
$GroupsQuery = db::run(
"SELECT groups.name, groups.id, groups.emblem, groups_ranks.Name AS Role,
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
FROM groups_members
INNER JOIN groups_ranks ON groups_ranks.GroupID = groups_members.GroupID AND groups_ranks.Rank = groups_members.Rank
INNER JOIN groups ON groups.id = groups_members.GroupID
WHERE groups_members.UserID = :UserID AND NOT groups.deleted
GROUP BY id ORDER BY Joined DESC LIMIT 8 OFFSET $Offset",
[":UserID" => $UserID]
);
while($Group = $GroupsQuery->Fetch(PDO::FETCH_OBJ))
{
$Groups[] =
[
"Name" => Polygon::FilterText($Group->name),
"ID" => $Group->id,
"Role" => Polygon::FilterText($Group->Role),
"MemberCount" => $Group->MemberCount,
"Emblem" => Thumbnails::GetAssetFromID($Group->emblem, 420, 420)
];
}
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "items" => $Groups]);

View File

@ -1,5 +1,7 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
api::initialize(["method" => "POST"]);
$self = isset($_SERVER['HTTP_REFERER']) && (str_ends_with($_SERVER['HTTP_REFERER'], "/my/stuff") || str_ends_with($_SERVER['HTTP_REFERER'], "/user"));
@ -8,9 +10,10 @@ $type = $_POST["type"] ?? false;
$page = $_POST["page"] ?? 1;
$assets = [];
if(!catalog::getTypeByNum($type)) api::respond(400, false, "Invalid asset type");
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
if(!in_array($type, [17, 18, 19, 8, 2, 11, 12, 13, 10, 3])) api::respond(400, false, "Invalid asset type");
$type_str = catalog::getTypeByNum($type);
$type_str = Catalog::GetTypeByNum($type);
$query = $pdo->prepare("SELECT COUNT(*) FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :uid AND assets.type = :type");
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
@ -20,7 +23,7 @@ $query->execute();
$pages = ceil($query->fetchColumn()/18);
$offset = ($page - 1)*18;
if(!$pages) api::respond(200, true, ($self?'You have':users::getUserNameFromUid($userId).' has').' not purchased any '.plural($type_str));
if(!$pages) api::respond(200, true, ($self?'You have':Users::GetNameFromID($userId).' has').' not purchased any '.plural($type_str));
$query = $pdo->prepare("
SELECT assets.*, users.username,
@ -52,4 +55,4 @@ while($asset = $query->fetch(PDO::FETCH_OBJ))
];
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $pages, "assets" => $assets]));
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $pages, "items" => $assets]));

View File

@ -1,44 +0,0 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
api::initialize();
if(!isset($_GET['userID'])){ api::respond(400, false, "Invalid Request - userID not set"); }
if(!is_numeric($_GET['userID'])){ api::respond(400, false, "Invalid Request - userID is not numeric"); }
if(!isset($_GET['type'])){ api::respond(400, false, "Invalid Request - badge type not set"); }
if(!in_array($_GET['type'], ["user-badges", "polygon-badges"])){ api::respond(400, false, "Invalid Request - invalid badge type"); }
$selfProfile = isset($_SERVER['HTTP_REFERER']) && str_ends_with($_SERVER['HTTP_REFERER'], "/user");
$page = $_GET['page'] ?? 1;
$type = $_GET['type'] ?? false;
$userinfo = users::getUserInfoFromUid($_GET['userID']);
if(!$userinfo) api::respond(400, false, "User does not exist");
$badges = [];
if($_GET['type'] == "polygon-badges")
{
if($userinfo->adminlevel)
{
$badges[] = ["name" => "Administrator", "id" => 1];
}
if($userinfo->adminlevel == 2)
{
//$badges[] = ["badgeName" => "Administrator", "badgeId" => 2];
}
}
else
{
}
if($badges == [])
{
$responsemsg = ($selfProfile?"You have":$userinfo->username." has")."n't earned any ";
$responsemsg .= $type == "user-badges" ? "player-made badges" : "Polygon badges";
api::respond(200, true, $responsemsg);
}
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "badges" => $badges]));

View File

@ -1,19 +1,34 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Gzip");
Polygon::ImportClass("RBXClient");
header("Cache-Control: max-age=120");
$id = $_GET['ID'] ?? $_GET['id'] ?? false;
$assetHost = $_GET['host'] ?? $_SERVER['HTTP_HOST'];
$force = isset($_GET['force']);
$rblxasset = false;
$ExemptIDs =
[
69281057, // stamper speaker
69281292, // stamper boombox
];
$query = $pdo->prepare("SELECT * FROM assets WHERE id = :id");
$query->bindParam(":id", $id, PDO::PARAM_INT);
$query->execute();
$asset = $query->fetch(PDO::FETCH_OBJ);
$SwapIDs =
[
60059129 => 2599, // stamper rock
60051616 => 2600, // stamper funk
60049010 => 2601, // stamper electronic
];
if(!$asset || isset($_GET['forcerblxasset'])) $rblxasset = true;
$AssetID = $_GET['ID'] ?? $_GET['id'] ?? false;
$AssetHost = $_GET['host'] ?? $_SERVER['HTTP_HOST'];
$ForceRequest = isset($_GET['force']);
$RobloxAsset = false;
$AssetID = $SwapIDs[$AssetID] ?? $AssetID;
$Asset = db::run("SELECT * FROM assets WHERE id = :id", [":id" => $AssetID])->fetch(PDO::FETCH_OBJ);
if(!$Asset || isset($_GET['forcerblxasset'])) $RobloxAsset = true;
// so i dont have a url redirect in the client just yet
// meaning that we're gonna have to replace the roblox.com urls on the fly
@ -21,61 +36,79 @@ if(!$asset || isset($_GET['forcerblxasset'])) $rblxasset = true;
// this is absolutely gonna tank performance but for the meantime theres
// not much else i can do
if($rblxasset)
if($RobloxAsset)
{
// we're only interested in replacing the asset urls of models
$apidata = json_decode(file_get_contents("https://api.roblox.com/marketplace/productinfo?assetId=".$_GET['id']));
if(!in_array($apidata->AssetTypeId, [9, 10])) die(header("Location: https://assetdelivery.roblox.com/v1/asset/?".$_SERVER['QUERY_STRING']));
// $apidata = json_decode(file_get_contents("https://api.roblox.com/marketplace/productinfo?assetId=".$_GET['id']));
// if(!in_array($apidata->AssetTypeId, [9, 10])) die(header("Location: https://assetdelivery.roblox.com/v1/asset/?".$_SERVER['QUERY_STRING']));
// /asset/?id= just redirects to a url on roblox's cdn so we need to get that redirect url
$ch = curl_init();
curl_setopt_array($ch, [CURLOPT_URL => "https://assetdelivery.roblox.com/v1/asset/?".$_SERVER['QUERY_STRING'], CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false]);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpcode != 302) die(http_response_code($httpcode));
$assetData = file_get_contents(curl_getinfo($ch, CURLINFO_REDIRECT_URL));
$curl = curl_init();
curl_setopt_array($curl,
[
CURLOPT_URL => "https://assetdelivery.roblox.com/v1/asset/?".$_SERVER['QUERY_STRING'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER => ["User-Agent: Roblox/WinInet"]
]);
$AssetData = curl_exec($curl);
$HttpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if($HttpCode != 200) die(http_response_code($HttpCode));
if(!stripos($AssetData, 'roblox')) die(header("Location: https://assetdelivery.roblox.com/v1/asset/?".$_SERVER['QUERY_STRING']));
}
else
{
if(!$force && $asset->approved != 1) die(http_response_code(403));
if(!$force && !$asset->publicDomain && (!SESSION || SESSION["userId"] != $asset->creator)) die(http_response_code(403));
if(!file_exists("./files/$id")) die(http_response_code(404));
if(!file_exists("./files/".$Asset->id)) die(http_response_code(404));
$assetData = file_get_contents("./files/".$asset->id);
if($asset->type == 10 && !stripos($assetData, 'roblox')) $assetData = gzip::decompress("./files/".$asset->id);
if(!$ForceRequest)
{
if($Asset->approved != 1) die(http_response_code(403));
if(!$Asset->publicDomain && (!SESSION || !Catalog::OwnsAsset(SESSION["userId"], $Asset->id))) die(http_response_code(403));
}
$AssetData = file_get_contents("./files/".$Asset->id);
if($Asset->type == 10 && !stripos($AssetData, 'roblox')) $AssetData = Gzip::Decompress("./files/".$Asset->id);
}
// replace asset urls
if($force) $assetData = preg_replace("/%ASSETURL%([0-9]+)/i", "http://$assetHost/asset/?id=$1&force=true", $assetData);
else $assetData = str_replace("%ASSETURL%", "http://$assetHost/asset/?id=", $assetData);
if($ForceRequest) $AssetData = preg_replace("/%ASSETURL%([0-9]+)/i", "http://$AssetHost/asset/?id=$1&force=true", $AssetData);
else $AssetData = str_replace("%ASSETURL%", "http://$AssetHost/asset/?id=", $AssetData);
$assetData = str_ireplace(
["www.roblox.com/asset", "roblox.com/asset", "www.roblox.com/thumbs", "roblox.com/thumbs"],
["$assetHost/asset", "$assetHost/asset", "$assetHost/thumbs", "$assetHost/thumbs"],
$assetData);
if(!$rblxasset && $asset->type == 3 && isset($_GET['audiostream']))
// we need to make an exception for the stamper tool speaker as it needs to be able to load polygon assets
if($RobloxAsset && !in_array($AssetID, $ExemptIDs))
{
header('Content-Type: '.$asset->audioType);
header('Content-Disposition: attachment; filename="'.htmlentities($asset->name).str_replace(
["audio/mpeg", "audio/ogg", "audio/mid", "audio/wav"],
[".mp3", ".ogg", ".mid", ".wav"],
$asset->audioType).'"');
$AssetData = str_ireplace(
["http://www.roblox.com/asset", "http://roblox.com/asset", "http://www.roblox.com/thumbs", "http://roblox.com/thumbs"],
["https://assetdelivery.roblox.com/v1/asset", "https://assetdelivery.roblox.com/v1/asset", "http://$AssetHost/thumbs", "http://$AssetHost/thumbs"],
$AssetData
);
}
else
{
$AssetData = str_ireplace(
["www.roblox.com/asset", "roblox.com/asset", "www.roblox.com/thumbs", "roblox.com/thumbs"],
["$AssetHost/asset", "$AssetHost/asset", "$AssetHost/thumbs", "$AssetHost/thumbs"],
$AssetData
);
}
if(!$RobloxAsset && $Asset->type == 3 && isset($_GET['audiostream']))
{
$FileExtensions = ["audio/mpeg" => ".mp3", "audio/ogg" => ".ogg", "audio/mid" => ".mid", "audio/wav" => ".wav"];
header('Content-Type: '.$Asset->audioType);
header('Content-Disposition: attachment; filename="'.htmlentities($Asset->name).$FileExtensions[$Asset->audioType].'"');
}
else
{
header('Content-Type: binary/octet-stream');
if($rblxasset) header('Content-Disposition: attachment');
else header('Content-Disposition: attachment; filename="'.md5_file("./files/".$asset->id).'"');
if($RobloxAsset) header('Content-Disposition: attachment; filename="'.sha1($AssetData).'"');
else header('Content-Disposition: attachment; filename="'.sha1_file("./files/".$Asset->id).'"');
}
if(!$rblxasset && $asset->type == 5)
{
// all lua assets must be signed
$assetData = "%".$asset->id."%\n".$assetData;
openssl_sign($assetData, $signature, openssl_pkey_get_private("file://".$_SERVER['DOCUMENT_ROOT']."/../polygon_private.pem"));
$assetData = "%".base64_encode($signature)."%".$assetData;
}
if(!$RobloxAsset && $Asset->type == 5) $AssetData = RBXClient::CryptSignScript($AssetData, $Asset->id);
header('Content-Length: '.strlen($assetData));
die($assetData);
header('Content-Length: '.strlen($AssetData));
die($AssetData);

View File

@ -1,71 +1,135 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
users::requireLogin();
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Thumbnails");
$keyword = $_GET['SearchTextBox'] ?? false;
$category = $_GET['Category'] ?? "Users";
$page = $_GET['PageNumber'] ?? 1;
$keyword_sql = "%$keyword%";
$keyword = $_GET['SearchTextBox'] ?? "";
$keyword_sql = "%";
$query = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username LIKE :keywd AND NOT (SELECT COUNT(*) FROM bans WHERE userId = users.id AND NOT isDismissed)");
$query->bindParam(":keywd", $keyword_sql, PDO::PARAM_STR);
$query->execute();
if(strlen($keyword)) $keyword_sql = "%{$keyword}%";
$pages = ceil($query->fetchColumn()/15);
// if($keyword) $keyword_sql = "*{$keyword}*";
// else $keyword_sql = "*";
if($category == "Groups")
{
// WHERE MATCH (name) AGAINST (:keywd IN NATURAL LANGUAGE MODE)
$querycount = "SELECT COUNT(*) FROM groups WHERE name LIKE :keywd AND NOT deleted";
$querystring =
"SELECT *,
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
FROM groups WHERE name LIKE :keywd AND NOT deleted
ORDER BY MemberCount DESC";
pageBuilder::$pageConfig["title"] = "Browse Groups";
}
else
{
// WHERE MATCH (username) AGAINST (:keywd IN NATURAL LANGUAGE MODE)
$querycount =
"SELECT COUNT(*) FROM users WHERE username LIKE :keywd
AND NOT (SELECT COUNT(*) FROM bans WHERE userId = users.id AND NOT isDismissed)";
$querystring =
"SELECT * FROM users WHERE username LIKE :keywd
AND NOT (SELECT COUNT(*) FROM bans WHERE userId = users.id AND NOT isDismissed)
ORDER BY lastonline DESC";
pageBuilder::$pageConfig["title"] = "Browse People";
}
$count = db::run($querycount, [":keywd" => $keyword_sql])->fetchColumn();
$pages = ceil($count/15);
if($page > $pages) $page = $pages;
if(!is_numeric($page) || $page < 1) $page = 1;
$offset = ($page - 1)*15;
$query = $pdo->prepare("SELECT * FROM users WHERE username LIKE :keywd AND NOT (SELECT COUNT(*) FROM bans WHERE userId = users.id AND NOT isDismissed) ORDER BY lastonline DESC LIMIT 15 OFFSET $offset");
$query->bindParam(":keywd", $keyword_sql, PDO::PARAM_STR);
$query->execute();
$results = db::run(
"$querystring LIMIT 15 OFFSET $offset",
[":keywd" => $keyword_sql]
);
function buildURL($page)
{
global $keyword;
global $category;
$url = "?";
if($keyword) $url .= "SearchTextBox=$keyword&";
$url .= "Category=$category&";
$url .= "PageNumber=$page";
return $url;
}
pageBuilder::$pageConfig["title"] = "Browse People";
pageBuilder::buildHeader();
?>
<form>
<div class="form-group row">
<label for="SearchTextBox" class="col-sm-1 col-form-label">Search: </label>
<input type="text" class="form-control col-sm-7 mx-2" name="SearchTextBox" id="SearchTextBox" value="<?=htmlspecialchars($keyword)?>">
<button class="btn btn-light">Search Users</button>
<div class="form-group row m-0">
<div class="col-sm-9 px-1 mb-2">
<input type="text" class="form-control form-control-sm" name="SearchTextBox" id="SearchTextBox" value="<?=htmlspecialchars($keyword)?>" placeholder="Search...">
</div>
<div class="col-sm-3 px-0 d-inline-flex">
<div class="w-50 px-1 d-inline-block">
<button class="btn btn-sm btn-block btn-light px-1" name="Category" value="Users">Search Users</button>
</div>
<div class="w-50 px-1 d-inline-block">
<button class="btn btn-sm btn-block btn-light px-1" name="Category" value="Groups">Search Groups</button>
</div>
</div>
</div>
<?php if($query->rowCount()) { ?>
<table class="table table-hover">
<thead class="table-bordered bg-light">
<tr>
<th class="font-weight-normal py-2" scope="col" style="width:5%">Avatar</th>
<th class="font-weight-normal py-2" scope="col" style="width:20%">Name</th>
<th class="font-weight-normal py-2" scope="col" style="width:50%">Blurb</th>
<th class="font-weight-normal py-2" scope="col" style="width:20%">Location / Last Seen</th>
</tr>
</thead>
<tbody>
<?php while($row = $query->fetch(PDO::FETCH_OBJ)) { $status = users::getOnlineStatus($row->id); ?>
<tr>
<td><img src="<?=Thumbnails::GetAvatar($row->id, 100, 100)?>" title="<?=$row->username?>" alt="<?=$row->username?>" width="63" height="63"></td>
<td><a href="/user?ID=<?=$row->id?>"><?=$row->username?></a></td>
<td class="text-break"><?=polygon::filterText($row->blurb)?></td>
<td><span<?=$status["attributes"]?:''?>><?=$status["text"]?></span></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } else { ?>
<p class="text-center">No results matched your search query</p>
<?php } if($pages > 1) { ?>
<div class="pagination form-inline justify-content-center">
<a class="btn btn-light back<?=$page<=1?' disabled':'" href="'.buildURL($page-1)?>"><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></a>
<span class="px-3">Page <?=$page?> of <?=$pages?></span>
<a class="btn btn-light next<?=$page>=$pages?' disabled':'" href="'.buildURL($page+1)?>"><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></a>
</div>
<?php } ?>
</form>
<?php pageBuilder::buildFooter(); ?>
<?php if($results->rowCount()) { ?>
<table class="table table-hover">
<?php if($category != "Groups") { ?>
<thead class="bg-light">
<tr>
<th class="font-weight-normal py-2" scope="col" style="width:5%">Avatar</th>
<th class="font-weight-normal py-2" scope="col" style="width:20%">Name</th>
<th class="font-weight-normal py-2" scope="col" style="width:50%">Blurb</th>
<th class="font-weight-normal py-2" scope="col" style="width:20%">Location / Last Seen</th>
</tr>
</thead>
<tbody>
<?php while($row = $results->fetch(PDO::FETCH_OBJ)) { $status = Users::GetOnlineStatus($row->id); ?>
<tr>
<td><a href="/user?ID=<?=$row->id?>"><img src="<?=Thumbnails::GetAvatar($row->id, 100, 100)?>" title="<?=$row->username?>" alt="<?=$row->username?>" width="63" height="63"></a></td>
<td class="text-break"><a href="/user?ID=<?=$row->id?>"><?=$row->username?></a></td>
<td class="text-break"><?=Polygon::FilterText($row->blurb)?></td>
<td><span<?=$status["attributes"]?:''?>><?=$status["text"]?></span></td>
</tr>
<?php } ?>
</tbody>
<?php } elseif($category == "Groups") { ?>
<thead class="bg-light">
<tr>
<th class="font-weight-normal py-2" scope="col" style="width:5%"></th>
<th class="font-weight-normal py-2" scope="col" style="width:20%">Group</th>
<th class="font-weight-normal py-2" scope="col" style="width:70%">Description</th>
<th class="font-weight-normal py-2" scope="col" style="width:5%">Members</th>
</tr>
</thead>
<tbody>
<?php while($row = $results->fetch(PDO::FETCH_OBJ)) { $status = Users::GetOnlineStatus($row->id); ?>
<tr>
<td><a href="/groups?gid=<?=$row->id?>"><img src="<?=Thumbnails::GetAssetFromID($row->emblem, 420, 420)?>" title="<?=$row->name?>" alt="<?=$row->name?>" width="63" height="63"></a></td>
<td class="text-break"><a href="/groups?gid=<?=$row->id?>"><?=Polygon::FilterText($row->name)?></a></td>
<td class="text-break"><?=Polygon::FilterText($row->description)?></td>
<td><?=$row->MemberCount?></td>
</tr>
<?php } ?>
</tbody>
<?php } ?>
</table>
<?php } else { ?>
<p class="text-center">No results matched your search query</p>
<?php } if($pages > 1) { ?>
<div class="pagination form-inline justify-content-center">
<a class="btn btn-light back<?=$page<=1?' disabled':'" href="'.buildURL($page-1)?>"><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></a>
<span class="px-3">Page <?=$page?> of <?=$pages?></span>
<a class="btn btn-light next<?=$page>=$pages?' disabled':'" href="'.buildURL($page+1)?>"><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></a>
</div>
<?php } pageBuilder::buildFooter(); ?>

View File

@ -1,6 +1,8 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
// users::requireLogin();
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Polygon::ImportClass("Thumbnails");
Users::RequireLogin();
// this is a catastrophe
@ -47,7 +49,7 @@ if($cat == 3 && !isset($_GET['Category']))
if(!isset($cats[$cat]))
die(header("Location: /catalog"));
if($subcat && (isset($cats[$cat]["type"]) || !in_array($subcat, $cats[$cat]["subcategories"])))
if($subcat && ($cat == 1 || isset($cats[$cat]["type"]) || !in_array($subcat, $cats[$cat]["subcategories"])))
die(header("Location: /catalog?Category=".$cat));
if(!in_array($currency, [0, 1, 2]))
@ -95,7 +97,7 @@ $query->bindParam(":keywd", $keyword_sql, PDO::PARAM_STR);
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
$query->execute();
pageBuilder::$polygonScripts[] = "/js/polygon/catalog.js";
pageBuilder::$polygonScripts[] = "/js/polygon/catalog.js?t=1";
pageBuilder::$pageConfig["title"] = "Avatar Items, Virtual Avatars, Virtual Goods";
pageBuilder::buildHeader();
?>
@ -121,7 +123,7 @@ pageBuilder::buildHeader();
<div class="input-group-append">
<select class="form-control mb-2 categoriesForKeyword rounded-0" style="width:auto">
<?php if(isset($cats[$cat]["subcategories"]) && $subcat) { ?>
<option value="Custom" selected="selected"><?=plural(catalog::getTypeByNum($type))?></option>
<option value="Custom" selected="selected"><?=plural(Catalog::GetTypeByNum($type))?></option>
<?php } foreach($cats as $cat_id => $cat_data) { ?>
<option value="<?=$cat_id?>"<?=!isset($_GET['Subcategory']) && $cat==$cat_id?' selected':''?>><?=$cat_data["name"]?></option>
<?php } ?>
@ -137,7 +139,7 @@ pageBuilder::buildHeader();
<a class="btn btn-secondary btn-block text-left"<?=isset($_GET['Category'])?' href="#" role="button" id="browseByCategory" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"':''?>>
<h5 class="font-weight-normal m-0">Category <i class="mt-1 ml-2 mr-1 fas fa-caret-down float-right"></i></h5>
</a>
<div class="bg-light dropdown-menu w-100<?=!isset($_GET['Category'])?' d-block':''?>" style="min-width:5rem;" aria-labelledby="browseByCategory">
<div class="bg-cardpanel dropdown-menu w-100<?=!isset($_GET['Category'])?' d-block':''?>" style="min-width:5rem;" aria-labelledby="browseByCategory">
<?php foreach($cats as $dropdown_id => $dropdown_cat) { ?>
<a href="#category=<?=strtolower(str_replace(' ', '', $dropdown_cat["name"]))?>" class="dropdown-item assetTypeFilter" data-category="<?=$dropdown_id?>"><?=$dropdown_cat["name"]?></a>
<?php } ?>
@ -150,7 +152,7 @@ pageBuilder::buildHeader();
<h6 class="ml-2 font-weight-normal mb-1">Category</h6>
<div class="ml-3 filter-category">
<?php if(isset($cats[$cat]["subcategories"]) && $subcat) { ?>
<p class="m-0"><?=plural(catalog::getTypeByNum($type))?></p>
<p class="m-0"><?=plural(Catalog::GetTypeByNum($type))?></p>
<?php } foreach($cats as $cat_id => $cat_data) { ?>
<p class="m-0"><a href="#category=<?=$cat_data["name"]?>" class="assetTypeFilter<?=$cat==$cat_id?' text-dark text-decoration-none':''?>" data-keepfilters="true" data-category="<?=$cat_id?>"><?=$cat_data["name"]?></a></p>
<?php } ?>
@ -161,7 +163,7 @@ pageBuilder::buildHeader();
<div class="ml-3 filter-subcategory">
<p class="m-0"><a href="#category=All <?=$cats[$cat]["name"]?>" class="assetTypeFilter<?=!$subcat?' text-dark text-decoration-none':''?>" data-types="3">All <?=$cats[$cat]["name"]?></a></p>
<?php foreach($cats[$cat]["subcategories"] as $cat_id) { ?>
<p class="m-0"><a href="#category=<?=plural(catalog::getTypeByNum($cat_id))?>" class="assetTypeFilter<?=$subcat==$cat_id?' text-dark text-decoration-none':''?>" data-types="<?=$cat_id?>"><?=plural(catalog::getTypeByNum($cat_id))?></a></p>
<p class="m-0"><a href="#category=<?=plural(Catalog::GetTypeByNum($cat_id))?>" class="assetTypeFilter<?=$subcat==$cat_id?' text-dark text-decoration-none':''?>" data-types="<?=$cat_id?>"><?=plural(Catalog::GetTypeByNum($cat_id))?></a></p>
<?php } ?>
</div>
<div class="divider-bottom my-3"></div>
@ -188,63 +190,61 @@ pageBuilder::buildHeader();
<li class="breadcrumb-item text-dark active"><?=$cats[$cat]["name"]?></li>
<?php } else { ?>
<li class="breadcrumb-item text-dark"><a href="?SortType=1&SortCurrency=0&Category=<?=$cat?>"><?=$cats[$cat]["name"]?></a></li>
<li class="breadcrumb-item text-dark active"><?=plural(catalog::getTypeByNum($type))?></li>
<li class="breadcrumb-item text-dark active"><?=plural(Catalog::GetTypeByNum($type))?></li>
<?php } ?>
</ol>
</nav>
<?php if($query->rowCount()) { ?>
<div class="row">
<div class="col-xl-9 col-lg-8 col-md-6">
<p>Showing <?=number_format($offset+1)?> - <?=number_format($offset+$query->rowCount())?> of <?=number_format($results)?> results</p>
</div>
<div class="col-xl-3 col-lg-4 col-md-6 pr-2 mb-2 d-flex">
<label class="form-label form-label-sm" for="sortBy" style="width:6rem;">Sort by: </label>
<select class="Sort form-control form-control-sm" id="sortBy">
<option value="0"<?=$sort==0?' selected="selected"':''?>>Bestselling</option>
<option value="1"<?=$sort==1?' selected="selected"':''?>>Recently updated</option>
<option value="2"<?=$sort==2?' selected="selected"':''?>>Price (High to Low)</option>
<option value="3"<?=$sort==3?' selected="selected"':''?>>Price (Low to High)</option>
</select>
</div>
</div>
<?php } else { ?>
<p class="text-center">No results matched your criteria</p>
<?php } ?>
<div class="items row pl-2">
<?php while($item = $query->fetch(PDO::FETCH_OBJ)) { ?>
<div class="item col-xl-2 col-lg-3 col-md-3 col-sm-4 col-6 pb-3 px-2" style="line-height:normal">
<div class="card info hover">
<a href="/<?=encode_asset_name($item->name)?>-item?id=<?=$item->id?>"><img src="/thumbs/catalog-loading.png" preload-src="<?=Thumbnails::GetAsset($item, 420, 420)?>" class="card-img-top img-fluid p-2" title="<?=polygon::filterText($item->name)?>" alt="<?=polygon::filterText($item->name)?>"></a>
<div class="card-body pt-0 px-2 pb-2">
<p class="text-truncate text-primary m-0" title="<?=polygon::filterText($item->name)?>"><a href="/<?=encode_asset_name($item->name)?>-item?id=<?=$item->id?>"><?=polygon::filterText($item->name)?></a></p>
<?php if($item->sale) { ?><p class="m-0<?=$item->price?' text-success':''?>"><?=$item->price ? '<i class="fal fa-pizza-slice"></i> '.number_format($item->price):'Free'?></p><?php } ?>
</div>
<div class="catalog-container">
<?php if($query->rowCount()) { ?>
<div class="row">
<div class="col-xl-9 col-lg-8 col-md-6">
<p>Showing <?=number_format($offset+1)?> - <?=number_format($offset+$query->rowCount())?> of <?=number_format($results)?> results</p>
</div>
<div class="details-wrapper">
<div class="card details d-none">
<div class="col-xl-3 col-lg-4 col-md-6 pr-2 mb-2 d-flex">
<label class="form-label form-label-sm" for="sortBy" style="width:6rem;">Sort by: </label>
<select class="Sort form-control form-control-sm" id="sortBy">
<option value="0"<?=$sort==0?' selected="selected"':''?>>Bestselling</option>
<option value="1"<?=$sort==1?' selected="selected"':''?>>Recently updated</option>
<option value="2"<?=$sort==2?' selected="selected"':''?>>Price (High to Low)</option>
<option value="3"<?=$sort==3?' selected="selected"':''?>>Price (Low to High)</option>
</select>
</div>
</div>
<?php } else { ?>
<p class="text-center">No results matched your criteria</p>
<?php } ?>
<div class="items row pl-2">
<?php while($item = $query->fetch(PDO::FETCH_OBJ)) { ?>
<div class="item col-xl-2 col-lg-3 col-md-3 col-sm-4 col-6 pb-3 px-2" style="line-height:normal">
<div class="card info hover">
<a href="/<?=encode_asset_name($item->name)?>-item?id=<?=$item->id?>"><img src="<?=Thumbnails::GetStatus("rendering", 420, 420)?>" preload-src="<?=Thumbnails::GetAsset($item, 420, 420)?>" class="card-img-top img-fluid p-2" title="<?=Polygon::FilterText($item->name)?>" alt="<?=Polygon::FilterText($item->name)?>"></a>
<div class="card-body pt-0 px-2 pb-2">
<p class="text-truncate m-0"><small class="text-muted">Creator: <a href="/user?ID=<?=$item->creator?>"><?=$item->username?></a></small></p>
<p class="text-truncate m-0"><small class="text-muted">Updated: <span class="text-dark"><?=timeSince($item->updated)?></span></small></p>
<p class="text-truncate m-0"><small class="text-muted">Sales: <span class="text-dark"><?=number_format($item->sales)?></span></small></p>
<p class="text-truncate text-primary m-0" title="<?=Polygon::FilterText($item->name)?>"><a href="/<?=encode_asset_name($item->name)?>-item?id=<?=$item->id?>"><?=Polygon::FilterText($item->name)?></a></p>
<?php if($item->sale) { ?><p class="m-0<?=$item->price?' text-success':''?>"><?=$item->price ? '<i class="fal fa-pizza-slice"></i> '.number_format($item->price):'Free'?></p><?php } ?>
</div>
</div>
<div class="details-wrapper">
<div class="card details d-none">
<div class="card-body pt-0 px-2 pb-2">
<p class="text-truncate m-0"><small class="text-muted">Creator: <a href="/user?ID=<?=$item->creator?>"><?=$item->username?></a></small></p>
<p class="text-truncate m-0"><small class="text-muted">Updated: <span class="text-dark"><?=timeSince($item->updated)?></span></small></p>
<p class="text-truncate m-0"><small class="text-muted">Sales: <span class="text-dark"><?=number_format($item->sales)?></span></small></p>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
<?php if($pages > 1) { ?>
<div class="pagination form-inline justify-content-center">
<button type="button" class="btn btn-light mx-3 back"<?=$page<=1?' disabled':''?>><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></button>
<span>Page</span>
<input class="form-control form-control-sm text-center mx-1 page" type="text" style="width:30px" value="<?=$page?>">
<span>of <?=$pages?></span>
<button type="button" class="btn btn-light mx-3 next"<?=$page>=$pages?' disabled':''?>><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></button>
</div>
<?php } ?>
</div>
<?php if($pages > 1) { ?>
<div class="pagination form-inline justify-content-center">
<button type="button" class="btn btn-light mx-3 back"<?=$page<=1?' disabled':''?>><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></button>
<span>Page</span>
<input class="form-control form-control-sm text-center mx-1 page" type="text" style="width:30px" value="<?=$page?>">
<span>of <?=$pages?></span>
<button type="button" class="btn btn-light mx-3 next"<?=$page>=$pages?' disabled':''?>><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></button>
</div>
<?php } ?>
</div>
</div>
<script>
$(".items .item img").each(function(){ $(this).attr("src", $(this).attr("preload-src")); });
$(".items .item").hover(function(){ $(this).find(".details").removeClass("d-none"); }, function(){ $(this).find(".details").addClass("d-none"); });
</script>
<?php pageBuilder::buildFooter(); ?>

BIN
css/fonts/ssprobold.woff Normal file

Binary file not shown.

BIN
css/fonts/ssprolight.woff Normal file

Binary file not shown.

BIN
css/fonts/ssproregular.woff Normal file

Binary file not shown.

Binary file not shown.

89
css/polygon-2013.css Normal file
View File

@ -0,0 +1,89 @@
.btn
{
border-radius: 0!important;
}
.btn-success,.btn-success:link,.btn-success:active,.btn-success:visited
{
border-color:#007001!important;
background-color:#007001!important;
background-image:url(/img/2013/Buttons/StyleGuide/bg-btn-green.png);
color:white;
}
.btn-primary,.btn-primary:link,.btn-primary:active,.btn-primary:visited
{
border-color:#0852b7!important;
background-color:#0852b7!important;
background-image:url(/img/2013/Buttons/StyleGuide/bg-btn-blue.png);
color:white;
}
.btn-secondary,.btn-secondary:link,.btn-secondary:active,.btn-secondary:visited
{
border-color:#565656!important;
background-color:#565656!important;
background-image:url(/img/2013/Buttons/StyleGuide/bg-btn-gray.png);
color:white;
}
.btn-light,.btn-light:active,.btn-light:link,.btn-light:visited,.btn-light:hover
{
border:1px solid #777;
padding:0 6px;
color:#000;
text-decoration:none;
background-color:#ccc;
text-align:center;
font-weight:normal;
cursor:pointer;
background-position:top;
display:inline-block;
*zoom:1;
*display:inline;
}
.btn-light:hover
{
background-position:bottom;
border-color:#888;
text-decoration:none;
}
.btn-light.disabled
{
border:1px solid #ccc;
cursor:default;
color:#a7a7a7;
background-position:center;
}
.btn-light.btn-sm
{
height:18px;
line-height:18px;
font-size:11px;
background-image:url(/img/2013/StyleGuide/btn-control-small-tile.png);
}
.btn-light
{
height:21px;
line-height:21px;
font-size:12px;
background-image:url(/img/2013/StyleGuide/btn-control-medium-tile.png);
}
.btn-light
{
height:25px;
line-height:24px;
font-size:13px;
padding:0 7px;
background-image:url(/img/2013/StyleGuide/btn-control-large-tile.png);
}
a.btn-light.top-level
{
font-weight:bold;
}

829
css/polygon-2014.css Normal file
View File

@ -0,0 +1,829 @@
@font-face
{
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(/css/fonts/ssprolight.woff) format('woff');
}
@font-face
{
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(/css/fonts/ssproregular.woff) format('woff');
}
@font-face
{
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url(/css/fonts/ssprosemibold.woff) format('woff');
}
@font-face
{
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(/css/fonts/ssprobold.woff) format('woff');
}
h2.font-weight-normal
{
font-size: 32px;
font-weight: bold!important;
color: #343434;
letter-spacing: -1px;
}
body
{
font-family: 'Source Sans Pro', Arial, Helvetica, sans-serif;
color: #343434;
line-height: 1.428;
font-size: 14px;
}
div.SystemAlert
{
width: 906px;
margin: 0 auto 5px;
border: 1px solid gray;
text-align: center;
color: #FFF;
padding: 1px;
}
.SystemAlertText
{
font-size: 16px;
font-weight: bold;
padding: 2px;
}
@media screen and (min-width:1480px)
{
.nav-container .nav-icon
{
display:none;
}
}
.navigation
{
position:fixed;
top:0;
left:0;
width:175px;
height:100%;
font-size:16px;
-webkit-transition:-webkit-transform .5s,left .5s;
transition:transform .5s,left .5s;
-ms-transform:translate(-100%,0);
-webkit-transform:translate(-100%,0);
transform:translate(-100%,0);
overflow:hidden;
z-index:12;
background:#e1e2e6;
-ms-user-select:none;
-moz-user-select:none;
-webkit-user-select:none;
}
.navigation-container
{
height:100%;
width:175px;
overflow-y:auto;
overflow-x:hidden;
}
.navigation ul
{
margin:0;
padding:0;
list-style:none;
position:relative;
top:40px;
}
.navigation ul ul
{
top:0;
}
.navigation ul li .menu-item
{
text-decoration:none;
display:block;
padding:6px 7px;
text-shadow:0 0 1px rgba(255,255,255,0.1);
font-weight:inherit;
color:#444;
position:relative;
}
.navigation.mobile ul li .menu-item
{
padding:14px 7px;
}
.navigation ul li .menu-item:hover
{
color:#000;
background-color:#CACACA;
}
.navigation ul li .menu-item a
{
color:#444;
text-decoration:none;
}
.navigation li.expand.nav-open .expand-icon
{
background-position:right -981px;
}
.navigation ul ul li .menu-item:hover,.navigation ul .expand ul li .menu-item:hover,.navigation li .menu-item:hover
{
cursor:pointer;
}
.nav-open .navigation
{
left:0;
-ms-transform:translate(0,0);
-webkit-transform:translate(0,0);
transform:translate(0,0);
display:block;
z-index:12;
margin-right:15px;
}
.navigation .user .username
{
padding-left:10px;
width:175px;
}
.navigation .user .menu-item
{
padding:0;
height:36px;
line-height:36px;
border-bottom:1px solid #ccc;
}
.navigation.mobile .user .menu-item
{
height:50px;
line-height:50px;
padding:0;
}
.navigation .user a,.navigation .under-13
{
display:block;
float:left;
}
.navigation .under-13
{
line-height:initial;
margin-top:17px;
color:white;
}
.navigation .upgrade-now
{
margin:15px 0;
}
.navigation .upgrade-now .nav-button
{
margin-left:20px;
font-size:16px;
color:white;
border-radius:4px;
padding:5px 20px 6px 20px;
height:auto;
text-decoration:none;
}
.navigation .upgrade-now #upgrade-now-button
{
background-color:#00B259;
}
.navigation .upgrade-now #upgrade-now-button:hover
{
text-decoration:none;
background:#008C46;
}
.navigation .upgrade-now #builders-club-button
{
background-color:#2D528F;
}
.navigation .upgrade-now #builders-club-button:hover
{
text-decoration:none;
background:#27487E;
}
.navigation .icon
{
background:url('/img/2013/Icons/Navigation2014/Nav2014-icon-sprite-sheet.png') no-repeat;
padding:5px 16px;
margin-right:10px;
}
.navigation .notification-icon
{
position:absolute;
top:7px;
left:121px;
height:20px;
background:#2D528F;
padding:0 7px;
border-radius:15px;
}
.navigation.mobile .notification-icon
{
top:16px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-my-roblox .icon
{
background-position:-5px -3px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-profile .icon
{
background-position:-5px -32px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-messages .icon
{
background-position:-5px -59px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-friends .icon
{
background-position:-5px -90px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-character .icon
{
background-position:-5px -119px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-inventory .icon
{
background-position:-5px -147px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-trade .icon
{
background-position:-5px -176px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-develop .icon
{
background-position:-5px -206px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-groups .icon
{
background-position:-5px -235px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-blog .icon
{
background-position:-5px -263px;
}
.layout-2014 .nav-container #navigation .navigation-container .nav2014-forum .icon
{
background-position:-5px -291px;
}
.navigation .nav2014-events .events-text
{
font-size:14px;
color:#999;
font-weight:bold;
margin-left:7px;
}
.navigation .nav2014-sponsor .menu-item span,.navigation .nav2014-sponsor .menu-item img
{
margin-left:7px;
}
.nav-open-static .navigation
{
display:block;
margin-right:15px;
left:175px;
}
.navigation-container
{
height: 100%;
width: 175px;
overflow-y: auto;
overflow-x: hidden;
}
.navigation ul
{
margin: 0;
padding: 0;
list-style: none;
position: relative;
top: 40px;
}
.nav-icon
{
/* position: fixed; */
height: 33px;
width: 46px;
/* margin: 4px 5px 4px 5px; */
left: 5px;
top: 0;
z-index: 10014;
background: url('/img/2013/Icons/Navigation2014/Nav2014-icon-sprite-sheet.png') no-repeat 2px -319px;
-ms-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
border-radius: 4px;
margin-left: -6px;
margin-right: 22px;
cursor: pointer;
}
.header-2014
{
background-color: #2D528F;
color: #fff;
text-decoration: none;
min-height: 40px;
width: 100%;
min-width: 330px;
top: 0;
z-index: 10013;
-ms-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.header-2014 .header-links li
{
margin-right: 28px;
}
.header-2014 .header-links li a
{
font-size: 16px;
padding-top: 3px;
padding-bottom: 5px;
border-radius: 4px;
}
.header-2014 .header-links li a:hover
{
color: #fff!important;
background-color: #27487E;
}
.header-2014 .search
{
position: relative;
overflow: hidden;
height: 100%;
width: 360px;
-webkit-transition: width .15s;
transition: width .15s;
transition-timing-function: ease;
transition-timing-function: ease;
transition-timing-function: ease-out;
}
.header-2014 .search-input-container
{
margin-top: 3px;
background: #fff;
padding: 0 10px;
border-radius: 5px;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.header-2014 .search.universal-search-open .search-input-container
{
border-radius: 5px 5px 0 0;
border-bottom: 0;
}
.header-2014 .search input
{
font-size: 18px;
border: none;
border-radius: 5px;
padding: 0;
display: block;
width:100%;
outline: none;
}
.header-2014 .search .search-icon
{
background: url('/img/2013/Icons/Navigation2014/Nav2014-icon-sprite-sheet.png') no-repeat;
background-position: -1px -349px;
height: 100%;
display: block;
position: absolute;
right: 1px;
top: 0;
margin-top: 4px;
width: 37px;
}
.header-2014 .search .search-icon:hover
{
cursor: pointer;
}
.header-2014 .universal-search-dropdown
{
position: fixed;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: inherit;
top: 32px;
height: auto;
border-radius: 0 0 5px 5px;
border: 1px solid #ccc;
border-top-color: rgb(204, 204, 204);
border-top-style: solid;
border-top-width: 1px;
border-top: 0;
background: #fff;
display: none;
z-index: 1;
}
.header-2014 .search.universal-search-open .universal-search-dropdown
{
display: block;
}
.header-2014 .universal-search-option
{
width: 100%;
color: #444;
font-size: 16px;
border-top: 1px solid #ccc;
padding: 6px 0;
}
.header-2014 .universal-search-option:hover
{
cursor: pointer;
}
.header-2014 .universal-search-option.selected
{
background: #e1e2e6;
}
.header-2014 .universal-search-text
{
margin: 1px 10px 0;
}
.header-2014 .universal-search-text .universal-search-string
{
word-break: break-all;
}
.navbar-orange .navbar-button-container .btn
{
border: none;
border-radius: 0;
color: #fff;
}
.navbar-orange .navbar-button-container i
{
-moz-border-radius: 50px/50px;
-webkit-border-radius: 50px 50px;
border-radius: 50px/50px;
border: solid 1px;
background-color: #6684c3;
border-color: #6684c3;
color: white;
padding: 5px;
}
.navbar-orange .navbar-button-container .dropdown-menu
{
-webkit-transition: opacity .15s;
transition: opacity .15s;
transition-timing-function: ease;
transition-timing-function: ease;
transition-timing-function: ease-out;
opacity: 0;
min-width: 7rem;
border-radius: 10px;
box-shadow: 0px 0px 5px gray;
}
.navbar-orange .navbar-button-container .dropdown-menu::after
{
border-bottom: 8px solid #FFFFFF;
border-left: 9px solid rgba(0, 0, 0, 0);
border-right: 9px solid rgba(0, 0, 0, 0);
content: "";
display: inline-block;
right: 17%;
position: absolute;
top: -7px;
}
.dropdown-hover:hover > .dropdown-menu
{
display: block;
opacity: 1;
}
.navbar-orange .navbar-button-container .dropdown-menu .dropdown-item
{
padding: .5rem .75rem;
}
.dropdown-menu .dropdown-item:hover
{
background-color: #f0eef0;
}
.btn-success, .btn-outline-success
{
border-color:#007001!important;
background-color:#007001!important;
background-image:url(https://polygon.pizzaboxer.xyz/img/2013/Buttons/StyleGuide/bg-btn-green.png);
color:white;
}
.btn-primary, .btn-outline-primary
{
border-color:#0852b7!important;
background-color:#0852b7!important;
background-image:url(https://polygon.pizzaboxer.xyz/img/2013/Buttons/StyleGuide/bg-btn-blue.png);
color:white;
}
.btn-secondary, .btn-outline-secondary,
.btn-warning, .btn-outline-warning,
.btn-danger, .btn-outline-danger
{
border-color:#565656!important;
background-color:#565656!important;
background-image:url(https://polygon.pizzaboxer.xyz/img/2013/Buttons/StyleGuide/bg-btn-gray.png);
color:white;
}
.app .btn, .modal .btn
{
border-radius: 0!important;
padding:1px 13px 3px 13px;
height:34px;
min-width:35px;
font-size:20px;
background-position:left -96px;
zoom: 1;
text-align: center;
font-weight: normal;
text-decoration: none;
border-width: 1px;
border-style: solid;
cursor: pointer;
*display: inline;
*vertical-align: top;
transition: none!important;
}
.app .btn:hover, .app .btn:focus,
.modal .btn:hover, .modal .btn:focus
{
background-position:left -128px;
}
.app .btn-sm, .modal .btn-sm
{
padding:1px 7px 0 7px;
height:23px;
min-width:40px;
font-size:14px;
line-height:18px;
background-position:left -160px;
}
.app .btn-lg, .modal .btn-lg
{
height:50px;
min-width:40px;
font-size:14px;
line-height:18px;
background-position: left 0px;
}
.app a.btn-lg h4, .modal a.btn-lg h4
{
margin-top: 3px!important;
}
.app .btn-sm:hover, .app .btn-sm:focus,
.modal .btn-sm:hover, .modal .btn-sm:focus
{
background-position:left -181px;
}
.app .btn-lg:hover, .app .btn-lg:focus,
.modal .btn-lg:hover, .modal .btn-lg:focus
{
background-position:left -48px;
}
/* .btn-light,.btn-light:active,.btn-light:link,.btn-light:visited,.btn-light:hover,
.btn-outline-light,.btn-outline-light:active,.btn-outline-light:link,.btn-outline-light:visited,.btn-outline-light:hover
{
border:1px solid #777;
padding:0 6px;
color:#000;
text-decoration:none;
background-color:#ccc;
text-align:center;
font-weight:normal;
cursor:pointer;
background-position:top;
display:inline-block;
*zoom:1;
*display:inline;
}
.btn-light:hover, .btn-light:focus,
.btn-outline-light:hover, .btn-outline-light:focus
{
background-position:bottom;
border-color:#888;
text-decoration:none;
}
.btn-light.disabled, .btn-outline-light.disabled
{
border:1px solid #ccc;
cursor:default;
color:#a7a7a7;
background-position:center;
}
.btn-light, .btn-outline-light
{
height:27px!important;
line-height:24px!important;
font-size:13px!important;
padding:0 7px!important;
background-image:url(https://polygon.pizzaboxer.xyz/img/2013/StyleGuide/btn-control-large-tile.png);
}
a.btn-light.top-level
{
font-weight:bold;
} */
.app .btn-sm, .modal .btn-sm
{
padding:1px 7px 0 7px;
height:23px;
min-width:40px;
font-size:14px;
line-height:18px;
background-position:left -160px;
}
.btn-light, .btn-outline-light
{
background: url(/img/2013/Buttons/bg-form_btn_lg-tile.png) repeat-x top left;
background-position-x: left;
background-position-y: top;
border-color: #777!important;
padding: 4px 8px 5px!important;
height: 27px!important;
font-size:14px!important;
line-height:18px;
background-position: left 0px!important;
}
.btn-light.next, .btn-light.previous
{
padding-top: 2px !important;
}
.btn-light:hover, .btn-light:focus,
.btn-outline-light:hover, .btn-outline-light:focus
{
background-position: left -25px!important
}
.nav-tabs .nav-link
{
color: black;
background-color: #D6D6D6;
border-radius: 0!important;
border: 1px solid #9e9e9e;
margin: 4px 2px 0 1px;
font-weight: bold;
font-size: 15px;
border-bottom-width: 0;
padding: 9px 7px;
}
.nav-tabs .nav-link:hover
{
background-color: #e9e9e9;
border: 1px solid #ccc;
border-bottom-width: 0;
}
.nav-tabs .nav-link.active
{
background-color: #fff;
}
.nav-tabs.flex-column .nav-link
{
border: none;
margin: 1px 0px 0px 1px!important;
background-color: transparent!important;
font-weight: normal!important;
}
.nav-tabs.flex-column .nav-link:hover
{
border-bottom-width: 1px!important;
border-right-width: 0!important;
background-color: #EFEFEF!important;
z-index: 1;
}
.nav-tabs.flex-column .nav-link.active
{
background-color: #EFEFEF!important;
border: 1px solid #CCC!important;
border-right-color: rgb(204, 204, 204);
border-right-style: solid;
font-weight: bold!important;
border-right: none;
text-align: left;
z-index: 2;
}
input, select
{
padding-top: 0px!important;
padding-bottom: 0px!important;
height: revert!important;
}
.card.bg-cardpanel
{
border-radius: 0;
}
.footer
{
background-color: transparent;
}
.footer>.text-light
{
color: #2D528F!important;
}
a.text-light.px-2
{
color: #2D528F!important;
}

View File

@ -153,7 +153,7 @@ textarea { background-color: #202020!important; }
/* friend cards (profile) */
.friend-card>.card {
.friend-card>.card, .item>.card {
background-color: #111!important;
}
@ -161,4 +161,13 @@ textarea { background-color: #202020!important; }
.modal-body, .modal-footer {
background-color: #131313;
}
pre { color: white; }
.bg-cardpanel { background-color: #262626!important; }
.items .item-striped:nth-of-type(2n)
{
background-color: #262626;
}

20
css/polygon-hitius.css Normal file
View File

@ -0,0 +1,20 @@
.text-center {
transform: skewX(47deg);
}
.col-lg-6.col-md-8.px-3.divider-right {
float: left;
transform: scaleX(3);
}
.navbar-orange, .nav-item .dropdown-menu {
border: 39px groove #fb0;
background-color: #fff000 !important;
}
* {
font-family: fantasy;
}
.navbar.navbar-expand-lg.navbar-dark.navbar-top.bg-dark.py-0 {
width: 33333px;
}
.card-body {
height: 455px;
tr

View File

@ -15,20 +15,20 @@ body
.navbar-orange,
.nav-item .dropdown-menu
{
background-color: #eb4034;
background-color: #E04A32;
}
/* change the brand and text color */
.navbar-orange .navbar-brand,
.navbar-text,
.navbar-nav .nav-link,
.navbar-nav a.nav-link,
.nav-item .dropdown-menu .dropdown-item
{
color: rgba(255,255,255,1)!important;
}
/* change the color of active or hovered links */
.navbar-orange .nav-link:hover, .darken
.navbar-orange a.nav-link:hover,
.navbar-orange .nav-link.darken
{
color: rgba(255,255,255,0.75)!important;
}
@ -58,6 +58,17 @@ body
position:absolute; margin-left:25px;
}
.accordion .accordion-header .accordion-arrow
{
transition: 0.25s ease;
transform: rotate(-90deg);
}
.accordion .accordion-header.ui-state-active .accordion-arrow
{
transform: rotate(0deg);
}
@media (min-width: 768px)
{
.divider-right
@ -101,9 +112,13 @@ body
pointer-events: none;
}
.alert a { color: unset; }
.alert p { margin-bottom: 0; }
.nav-tabs.flex-column { border-bottom: none; }
.nav-tabs.flex-column
{
border-bottom: none;
}
.nav-tabs.flex-column .active
{
@ -112,7 +127,10 @@ body
border-top-right-radius: 0;
}
.items .item .details-wrapper { position: relative; }
.items .item .details-wrapper
{
position: relative;
}
.items .item .card.details
{
@ -126,12 +144,27 @@ body
}
.border-none { border: none; }
.border-none { border: none!important; }
.border-none:focus { box-shadow: none; }
.modal-header { border-bottom: none; }
.modal-footer { border-top: none; }
.items .item-striped:nth-of-type(2n)
{
background-color: #f0eef0;
}
.ColorPickerItem:hover
{
cursor: pointer;
border-color: #e1e1e1;
border-style: solid;
border-width: 2px;
}
.jumbo { width: 3rem; height: 3rem; }
.btn:focus { box-shadow: none; }
.dropdown-menu { margin-top: -1px; }
.gear-attribute i { width:18px }
.gear-attribute i { width:18px }
.bg-cardpanel { background-color: #f0eef0; }

View File

@ -1,6 +1,13 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
users::requireLogin();
if(!SESSION)
{
if(Polygon::IsClientBrowser())
redirect("/");
else
Users::RequireLogin();
}
$views =
[
@ -59,7 +66,7 @@ pageBuilder::buildHeader();
<?php if(isset($views[$view])) { ?>
<div class="col-md-2 p-0 divider-right">
<div class="dropdown show mr-3 mb-4">
<a class="btn btn-success btn-block py-1" href="#" role="button" id="buildNew" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="btn btn-success btn-block" href="#" role="button" id="buildNew" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<h5 class="font-weight-normal mb-1 pb-0">Build New <i class="mt-1 fas fa-caret-down"></i></h5>
</a>
<div class="bg-light dropdown-menu w-100" aria-labelledby="buildNew">

View File

@ -1,6 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
if(!SESSION || !SESSION["adminLevel"]) pageBuilder::errorCode(404);
Users::RequireAdmin([Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR]);
$views =
[
@ -103,7 +103,7 @@ pageBuilder::buildHeader();
<p>for regular asset creation (shirts, pants, etc) just use the Develop page</p>
<h1 class="font-weight-normal">Important</h1>
<p>make sure the asset URLs in your asset are represented as <code>%ASSETURL%</code></p>
<p>so for instance, <code>http://chef.pizzaboxer.xyz/asset/?id=1818</code> would be <code>%ASSETURL%1818</code></p>
<p>so for instance, <code>http://<?=$_SERVER['HTTP_HOST']?>/asset/?id=1818</code> would be <code>%ASSETURL%1818</code></p>
</div>
<?php } ?>
</div>
@ -112,7 +112,7 @@ pageBuilder::buildHeader();
<div class="creation">
<div class="row">
<div class="col-sm-2 col-3">
<a href="$item_url"><img src="$thumbnail" class="img-fluid"></a>
<a href="$item_url"><img preload-src="$thumbnail" class="img-fluid"></a>
</div>
<div class="col-5 pl-0">
<a href="$item_url">$name</a>

View File

@ -0,0 +1,47 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("ErrorHandler");
Users::RequireAdmin(Users::STAFF_ADMINISTRATOR);
$Log = array_reverse(ErrorHandler::GetLog());
$page = $_GET['Page'] ?? 1;
$count = 1;
$pages = ceil($count/15);
if($page > $pages) $page = $pages;
if(!is_numeric($page) || $page < 1) $page = 1;
$offset = ($page - 1)*15;
pageBuilder::$pageConfig["title"] = "Staff Logs";
pageBuilder::buildHeader();
?>
<h2 class="font-weight-normal pb-0">Error Log</h2>
<table class="table table-hover">
<thead class="bg-light">
<tr>
<th class="font-weight-normal py-2" scope="col" style="width:13%">ID</th>
<th class="font-weight-normal py-2" scope="col" style="width:59%">Error</th>
<th class="font-weight-normal py-2" scope="col" style="width:13%">Time</th>
<th class="font-weight-normal py-2" scope="col" style="width:15%">Parameters</th>
</tr>
</thead>
<tbody>
<?php foreach($Log as $ID => $Error) { ?>
<tr>
<td class="text-break"><?=$ID?></td>
<td><pre style="white-space: pre-wrap;"><?=$Error["Message"]?></pre></td>
<td><?=date('j/n/y G:i:s', $Error["Timestamp"])?></td>
<td><pre style="white-space: pre-wrap;"><?=var_dump($Error["GETParameters"])?></pre></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php if($pages > 1) { ?>
<div class="pagination form-inline justify-content-center">
<a class="btn btn-light back<?=$page<=1?' disabled':'" href="'.buildURL($page-1)?>"><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></a>
<span class="px-3">Page <?=$page?> of <?=$pages?></span>
<a class="btn btn-light next<?=$page>=$pages?' disabled':'" href="'.buildURL($page+1)?>"><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></a>
</div>
<?php } ?>
<?php pageBuilder::buildFooter(); ?>

View File

@ -0,0 +1,86 @@
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
Polygon::ImportClass("Catalog");
Users::RequireAdmin([Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR]);
$Alert = false;
$Conditions =
[
"AssetID" => ["Type" => "Integer"],
"UserID" => ["Type" => "Integer"]
];
function SetError($text)
{
global $Alert;
$Alert = ["text" => $text, "color" => "danger"];
}
if($_SERVER["REQUEST_METHOD"] == "POST")
{
$AssetID = $_POST["AssetID"] ?? "";
$Condition = $_POST["Condition"] ?? "";
$ConditionData = $_POST["ConditionData"] ?? "";
if(empty($AssetID)) SetError("Asset ID cannot be empty");
else if(!isset($Conditions[$Condition])) SetError("Condition is not valid");
else if(empty($ConditionData)) SetError("Condition data must be set");
else if($Conditions[$Condition]["Type"] == "Integer" && !is_numeric($ConditionData)) SetError("Condition data must be a number");
else if(!Catalog::GetAssetInfo($AssetID)) SetError("The asset you're trying to give does not exist");
if($Alert === false)
{
$ItemName = Catalog::GetAssetInfo($AssetID)->name;
$ConditionString = "";
$UserIDs = [];
$TagID = generateUUID();
if($Condition == "UserID")
{
$ConditionString = "had the user ID $ConditionData";
$UserIDs = db::run(
"SELECT id FROM users WHERE id = :ConditionData
AND NOT (SELECT COUNT(*) FROM ownedAssets WHERE userId = users.id AND assetId = :AssetID)",
[":AssetID" => $AssetID, ":ConditionData" => $ConditionData]
)->fetchAll(PDO::FETCH_COLUMN);
}
else if($Condition == "AssetID")
{
$ConditionString = "purchased an asset with ID $ConditionData";
$UserIDs = db::run(
"SELECT id FROM users WHERE id IN (SELECT userId FROM ownedAssets WHERE assetId = :ConditionData)
AND NOT (SELECT COUNT(*) FROM ownedAssets WHERE userId = users.id AND assetId = :AssetID)",
[":AssetID" => $AssetID, ":ConditionData" => $ConditionData]
)->fetchAll(PDO::FETCH_COLUMN);
}
foreach($UserIDs as $UserID)
{
db::run(
"INSERT INTO ownedAssets (assetId, userId, TagID, timestamp) VALUES (:AssetID, :UserID, :TagID, UNIX_TIMESTAMP())",
[":UserID" => $UserID, ":AssetID" => $AssetID, ":TagID" => $TagID]
);
}
$Alert = ["text" => sprintf("\"%s\" has been given to %d user(s) (Tag ID %s)", $ItemName, count($UserIDs), $TagID), "color" => "primary"];
Users::LogStaffAction(sprintf(
"[ Give Asset ] %s gave \"%s\" (ID %s) to %d user(s) who %s (Tag ID %s)",
SESSION["userName"], $ItemName, $AssetID, count($UserIDs), $ConditionString, $TagID
));
}
}
pageBuilder::$pageConfig["title"] = "Give Asset";
pageBuilder::buildHeader();
?>
<h2 class="font-weight-normal">Give Asset</h2>
<?php if($Alert !== false) { ?><div class="alert alert-<?=$Alert["color"]?> px-2 py-1" role="alert"><?=$Alert["text"]?></div><?php } ?>
<p class="mb-2"><i class="fas fa-exclamation-triangle text-warning"></i> Be careful about how you use this. Actions done here can be reverted, but may take a while to roll back.</p>
<form method="post">
<span>Give <input class="form-control form-control-sm d-inline text-center px-0" name="AssetID" placeholder="Asset ID" style="width:100px;"> to anyone who has <select class="form-control form-control-sm d-inline text-center px-0" name="Condition" style="width:150px;"><option value="UserID">the User ID</option><option value="AssetID">purchased an asset with ID</option></select> <input class="form-control form-control-sm d-inline text-center px-0" name="ConditionData" style="width:150px;"> <button class="btn btn-sm btn-success mx-2" type="submit">Give</button></span>
</form>
<?php pageBuilder::buildFooter(); ?>

View File

@ -1,6 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
if(!SESSION || SESSION && SESSION["userId"] != 1){ pageBuilder::errorCode(404); }
Users::RequireAdmin(Users::STAFF_ADMINISTRATOR);
pageBuilder::$pageConfig["title"] = "Give ".SITE_CONFIG["site"]["currency"];
pageBuilder::buildHeader();

View File

@ -1,13 +1,13 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
users::requireLogin();
if(!in_array(SESSION["userId"], [1,5])) pageBuilder::errorCode(404);
Users::RequireAdmin();
pageBuilder::$polygonScripts[] = "/js/polygon/admin/asset-moderation.js?t=".time();
pageBuilder::$pageConfig["title"] = "Moderate Assets";
pageBuilder::buildHeader();
?>
<h2 class="font-weight-normal">Asset Moderation</h2>
<p class="mb-2"><i class="far fa-info-circle text-primary"></i> To view the template of an asset, click on the thumbnail.</p>
<div class="inventory-container pt-2 p-0 pr-4">
<div class="text-center">
<div class="loading"><span class="spinner-border" style="width: 3rem; height: 3rem;" role="status"></span></div>
@ -15,14 +15,16 @@ pageBuilder::buildHeader();
<p class="no-items d-none">There are no assets to moderate. <a href="#" onclick="window.history.back()">Go back</a></p>
<div class="items row"></div>
<div class="pagination form-inline justify-content-center d-none">
<button type="button" class="btn btn-light back"><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></button>
<span class="px-3">Page <input class="form-control form-control-sm text-center mx-1 page" type="text" data-last-page="1" style="width:30px"> of <span class="pages">10</span></span>
<button type="button" class="btn btn-light next"><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></button>
<button type="button" class="btn btn-light mx-2 back"><h5 class="mb-0"><i class="fal fa-caret-left"></i></h5></button>
<span>Page</span>
<input class="form-control form-control-sm text-center mx-1 px-0 page" type="text" data-last-page="1" style="width:40px">
<span>of <span class="pages">10</span></span>
<button type="button" class="btn btn-light mx-2 next"><h5 class="mb-0"><i class="fal fa-caret-right"></i></h5></button>
</div>
<div class="template d-none">
<div class="item col-lg-2 col-md-3 col-sm-4 col-6 mb-3 pr-0">
<div class="card hover h-100" data-asset-id="$item_id" data-texture-id="$texture_id">
<a href="#" class="view-texture"><img src="$item_thumbnail" class="card-img-top img-fluid p-2" title="$item_name" alt="$item_name"></a>
<a href="#" class="view-texture"><img preload-src="$item_thumbnail" class="card-img-top img-fluid p-2" title="$item_name" alt="$item_name"></a>
<div class="card-body pt-0 px-2 pb-2" style="line-height:normal">
<p class="text-truncate text-primary m-0" title="$item_name"><a href="/item?ID=$item_id">$item_name</a></p>
<p class="text-truncate m-0"><small class="text-muted">Creator: <a href="/user?ID=$creator_id">$creator_name</a></small></p>

View File

@ -1,7 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
if(!SESSION || SESSION && !SESSION["adminLevel"]){ pageBuilder::errorCode(404); }
Users::RequireAdmin([Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR]);
$query = $pdo->query("SELECT * FROM bans ORDER BY id DESC");
pageBuilder::$CSSdependencies[] = "/css/bootstrap-datepicker.min.css";
@ -111,13 +110,13 @@ pageBuilder::buildHeader();
<?=date('j/n/Y', $row->timeStarted)?>
</td>
<td>
<a href="/user?ID=<?=$row->userId?>"><?=users::getUserNameFromUid($row->userId)?></a>
<a href="/user?ID=<?=$row->userId?>"><?=Users::GetNameFromID($row->userId)?></a>
</td>
<td>
<?=[1=>"Warning", 2=>"Ban", 3=>"Permanent ban"][$row->banType]?>
</td>
<td>
<a href="/user?ID=<?=$row->userId?>"><?=users::getUserNameFromUid($row->bannerId)?></a>
<a href="/user?ID=<?=$row->userId?>"><?=Users::GetNameFromID($row->bannerId)?></a>
</td>
<td title="<?=$row->banType == 2 ? date('j/n/Y g:i:s A \G\M\T', $row->timeEnds) : 'Not Applicable'?>">
<?=$row->banType == 2 ? date('j/n/Y', $row->timeEnds) : "N/A"?>
@ -126,11 +125,11 @@ pageBuilder::buildHeader();
<?=$row->isDismissed?"Yes":"No"?>
</td>
<td>
<button class="btn btn-outline-primary" data-title="Ban reason for <?=users::getUserNameFromUid($row->userId)?>" data-text="<?=htmlspecialchars($row->reason)?>" data-control="openModal">View</button>
<button class="btn btn-outline-primary" data-title="Ban reason for <?=Users::GetNameFromID($row->userId)?>" data-text="<?=htmlspecialchars($row->reason)?>" data-control="openModal">View</button>
</td>
<td>
<?php if($row->note){ ?>
<button class="btn btn-outline-primary" data-title="Staff note for <?=users::getUserNameFromUid($row->userId)?>" data-text="<?=htmlspecialchars($row->note)?>" data-control="openModal">View</button>
<button class="btn btn-outline-primary" data-title="Staff note for <?=Users::GetNameFromID($row->userId)?>" data-text="<?=htmlspecialchars($row->note)?>" data-control="openModal">View</button>
<?php } else { echo "N/A"; } ?>
</td>
</tr>

View File

@ -1,7 +1,6 @@
<?php
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
if(!SESSION || SESSION && !SESSION["adminLevel"]){ pageBuilder::errorCode(404); }
Users::RequireAdmin();
$query = $pdo->query("SELECT * FROM renderqueue ORDER BY timestampRequested DESC");
pageBuilder::$CSSdependencies[] = "/css/bootstrap-datepicker.min.css";

Some files were not shown because too many files have changed in this diff Show More