Created
January 13, 2021 01:36
-
-
Save bls1999/cfa9105ab9d87800f32de65eea311ed4 to your computer and use it in GitHub Desktop.
A quick jQuery/Ajax/PHP implementation of a simple RNG contest.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
MIT License | |
Copyright (c) 2021 Ben Schwartz | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
* * * * * | |
-- Purpose -- | |
The following script is a MyBB-integrated JS/PHP RNG program that generates a random number of entries (ranging 1-100) in a contest. It's intended to be used by the contest host to keep track of entries. | |
The page can be accessed by the contest host and any of the "privileged" usergroups (for purposes of auditing). The data is stored in a file on the server. A mixture of jQuery, Ajax, and vanilla JS is used for the logic. | |
-- Page PHP File -- | |
--> | |
<?php | |
ini_set('error_reporting', '1'); | |
error_reporting(E_ALL | E_STRICT); | |
// mybb things | |
define('IN_MYBB', 1); | |
define('NO_ONLINE', 1); | |
require_once $_SERVER['DOCUMENT_ROOT'] . "/path/to/forum/global.php"; | |
add_breadcrumb("Contest Page", "/path/to/forum/showthread.php?tid=threadidhere"); | |
add_breadcrumb("This Page", "thisfile.php"); | |
$contest_host = $mybb->user['uid'] == 1; | |
$usergroup_id = (int) $mybb->user['usergroup']; | |
$privileged = in_array($usergroup_id, [4, 6, 9, 13]); | |
$ret = new stdClass(); | |
$ret->success = false; | |
try { | |
if (empty($mybb->user['uid']) || $mybb->user['uid'] == 0 || (!$privileged && !$contest_host)) { | |
error_no_permission(); | |
die(); | |
} | |
if ($_SERVER['REQUEST_METHOD'] === 'GET') { | |
if ($_GET['mode'] === 'raw') { // get data | |
$data = file_get_contents(__DIR__ . '/path/to/datafile.json'); | |
if ($data === false) { | |
throw new Exception('Could not access data.'); | |
} | |
$obj = empty($data) ? new stdClass() : json_decode($data); | |
$obj->success = true; | |
// show data | |
header('Content-type: text/plain'); | |
die(json_encode($obj, JSON_UNESCAPED_UNICODE)); | |
} elseif (empty($_GET['mode'])) { // templates for showing on mybb | |
eval('$headerinclude = "'.$templates->get('headerinclude').'";'); | |
eval('$header = "'.$templates->get('header').'";'); | |
eval('$footer = "'.$templates->get('footer').'";'); | |
eval('$page = "'.$templates->get('rng_contest').'";'); | |
output_page($page); | |
die(); | |
} | |
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
verify_post_check(@$_POST['my_post_key']); // ensure no CSRF | |
if ($contest_host) { // only let the contest host do this | |
if (empty($_POST['data']) | |
|| strlen($_POST['data']) > 20000 | |
|| is_null(json_decode($_POST['data'])) | |
|| $_SERVER['CONTENT_TYPE'] !== 'application/x-www-form-urlencoded; charset=UTF-8' | |
) { | |
throw new Exception('Invalid data passed.'); | |
} | |
// put the file | |
$obj = new stdClass(); | |
$obj->users = json_decode($_POST['data']); | |
file_put_contents(__DIR__ . '/path/to/datafile.json', json_encode($obj, JSON_UNESCAPED_UNICODE)); | |
// send back to user | |
$ret->success = true; | |
} else { | |
throw new Exception('You aren\'t the contest host.'); | |
} | |
} | |
} catch (Exception $e) { | |
$ret->error = $e->getMessage(); | |
} finally { | |
die(json_encode($ret)); | |
} | |
?> | |
<!-- | |
-- Page Template w/ name "rng_contest" -- | |
--> | |
<html> | |
<head> | |
<title>RNG Contest Processing Corner</title> | |
<script src="https://code.jquery.com/jquery-latest.min.js"></script> | |
<link type="text/css" rel="stylesheet" href="{$mybb->settings['homeurl']}/style/scores-table.css" /> | |
{$headerinclude} | |
<script type="text/javascript"> | |
var users; | |
$(function () { | |
getUsers(); | |
$('button#go').click(function () { | |
// show loading screen | |
$(this).attr('disabled', 'disabled'); | |
$('textarea#participants, div#nodata, div#hasdata, div#go_bt').hide(); | |
$('span#loading').show(); | |
if (users == null) { | |
console.log(users); | |
outputTable(doInit()); | |
} else { | |
outputTable(doCalc(users)); | |
} | |
}); | |
}); | |
function doInit() | |
{ | |
var names = $('textarea#participants').val(); | |
if (names.trim() === '') { | |
alert('Error: You must enter some participants!') | |
$('button#go').removeAttr('disabled'); | |
$('textarea#participants, div#nodata, div#go_bt').show(); | |
$('span#loading').hide(); | |
return; | |
} | |
// split participants into array by new line, then push each to master array | |
var usersNew = names.split("\n"); | |
var added = []; | |
for (var name in usersNew) { | |
// remove duplicates | |
added[name] = usersNew[name] = usersNew[name].trim(); | |
if (added.indexOf(usersNew[name]) != name) { | |
delete usersNew[name]; | |
continue; | |
} | |
// add to list | |
usersNew[name] = {"name": usersNew[name], "entries": 0}; | |
} | |
// perform rng operation | |
return doCalc(usersNew); | |
} | |
function doCalc(users) | |
{ | |
for (var i in users) { | |
var participant = users[i]; | |
var entriesToAward = Math.ceil(Math.random() * 100); | |
if (entriesToAward >= 1 && entriesToAward <= 100) { | |
participant.awarded = entriesToAward; | |
participant.entries += entriesToAward; | |
users[i] = participant; | |
} else { | |
return alert('Malformed data.'); | |
} | |
} | |
return users.sort(userSort); | |
} | |
function showExistingData(users) | |
{ | |
outputTable(users.sort(userSort), false); | |
} | |
function userSort(a, b) | |
{ | |
if (a.entries > b.entries) { | |
return -1; | |
} else if (a.entries < b.entries) { | |
return 1; | |
} else if (a.name > b.name) { | |
return -1; | |
} else if (a.name < b.name) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
function outputTable(info, save = true) | |
{ | |
// make sure we're getting data | |
if (info == null) { | |
return; | |
} | |
// reset table | |
var rank = 0; | |
$('table#usersList tbody').html( | |
`<tr> | |
<th class='noborder' style='text-align: left'>Rank</th> | |
<th class='noborder' style='text-align: left'>Name</th> | |
<th class='noborder' style='text-align: left; width: 259px'>` + (!save ? 'Last ' : '') + `Gained</th> | |
<th class='noborder'>Total</th> | |
</tr>` | |
); | |
for (var i in info) { | |
var user = info[i]; | |
$("table#usersList").find('tbody') | |
.append($('<tr>') | |
.append($('<td>') | |
.attr('class', 'noborder') | |
.text(++rank) | |
) | |
.append($('<td>') | |
.attr('class', 'noborder') | |
.text(user.name) | |
) | |
.append($('<td>') | |
.attr('class', 'noborder') | |
.addClass(save ? 'success' : '') | |
.text(user.awarded > 0 ? '+ ' + user.awarded : '') | |
) | |
.append($('<td>') | |
.attr('class', 'noborder') | |
.text(user.entries) | |
) | |
); | |
} | |
// save and show | |
if (save) { | |
saveData(info); | |
} else { | |
$('span#loading').hide(); | |
$('table#participantsTable').show(); | |
} | |
} | |
function getUsers() | |
{ | |
$.ajax({ | |
data: {'mode':'raw'}, | |
dataType: 'text', | |
url: window.location.href, | |
success: function(ret) { | |
console.log(ret); | |
if (ret == null) { | |
$('span#loading') | |
.addClass('error') | |
.text('Error: Something weird happened...'); | |
return; | |
} else if (ret.trim() == '') { | |
$('div#go_bt, div#nodata').show(); | |
$('span#loading').hide(); | |
} | |
ret = JSON.parse(ret); | |
if (ret.success == null || !ret.success) { | |
$('span#loading') | |
.addClass('error') | |
.text('Error: ' + (ret.error != null ? ret.error : 'Something weird happened...')); | |
} else if (ret.success && ret.users == null) { | |
$('div#go_bt, div#nodata').show(); | |
$('span#loading').hide(); | |
} else { | |
users = ret.users; | |
$('div#go_bt, div#hasdata').show(); | |
showExistingData(users); | |
} | |
}, | |
error: function(err) { | |
console.log(err); | |
$('span#loading').addClass('error').text('yikes!'); | |
} | |
}); | |
} | |
function saveData(data) | |
{ | |
// remove null values from data | |
data = JSON.stringify(data.filter(function (val) { | |
return val != null && val != 'null'; | |
})); | |
// send data to server | |
$.ajax({ | |
data: { | |
'data': data, | |
'my_post_key': my_post_key | |
}, | |
dataType: 'text', | |
type: 'POST', | |
url: window.location.href, | |
success: function(ret) { | |
if (ret == null || ret.trim() === '') { | |
$('span#loading') | |
.addClass('error') | |
.text('Error: Something weird happened...'); | |
return; | |
} | |
// parse | |
try { | |
ret = JSON.parse(ret); | |
} catch { | |
$('span#loading') | |
.addClass('error') | |
.text('Error: Loaded data not in expected format; could not parse. An attempted CSRF may have been prevented.'); | |
} | |
if (ret.success) { | |
$('table#participantsTable').show(); | |
$('span#loading') | |
.addClass('success') | |
.text('Done!'); | |
} else { | |
$('span#loading') | |
.addClass('error') | |
.text('Error: ' + (ret.error != null ? ret.error : 'Something weird happened...')); | |
} | |
}, | |
error: function(err) { | |
console.log(err); | |
$('span#loading').addClass('error').text('yikes!'); | |
} | |
}); | |
} | |
</script> | |
<style type="text/css"> | |
button#go { | |
cursor: pointer; | |
padding: 4px 8px; | |
font: bold 11.5px Tahoma,Calibri,Verdana,Geneva,sans-serif; | |
border: 1px solid #52544E; | |
border-radius: 5px; | |
color: #424242; | |
background: #e9e9e9 url(/btnimg.png) repeat-x top left; | |
} | |
button#go:hover { | |
border: 1px solid #80827c; | |
color: #55C546; | |
} | |
span#loading { | |
font-weight: bold; | |
} | |
.success { | |
color: green; | |
} | |
.error { | |
color: red; | |
} | |
div#nodata, div#hasdata, div#go_bt, table#participantsTable { | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
{$header} | |
<br /> | |
<div id="content"> | |
<div class="wrapper"> | |
<navigation> | |
<br /> | |
<table border="0" cellspacing="{$theme['borderwidth']}" cellpadding="{$theme['tablespace']}" class="tborder" style="text-align: left;"> | |
<tr> | |
<td class="thead" style="text-align: left;"><strong>Process RNG Entries</strong></td> | |
</tr> | |
<tr> | |
<td class="trow1" style="text-align: left;"> | |
<p> | |
<div id="nodata"> | |
Welcome! Enter the participants below (separate by new line):<br /><br /> | |
<textarea id="participants" rows="8" cols="75" placeholder="Super awesome people that could win fabulous prizes :)"></textarea> | |
</div> | |
<div id="hasdata"> | |
Welcome back! Click "Generate RNG" to process the RNG operation for today. | |
</div> | |
<div id="go_bt"> | |
<br /> | |
<button id="go">Generate RNG</button> | |
</div> | |
<span id="loading">Loading...</span> | |
</p> | |
</td> | |
</tr> | |
</table> | |
<br /> | |
<table id="participantsTable" border="0" cellspacing="{$theme['borderwidth']}" cellpadding="{$theme['tablespace']}" class="tborder" style="text-align: left;"> | |
<tbody> | |
<tr> | |
<td class='thead' style='text-align: left;'><strong>Participants</strong></td> | |
</tr> | |
<tr> | |
<td class='trow1' style='text-align: left;'> | |
<table id="usersList" class='noborder' width='100%'> | |
<tr> | |
<th class='noborder' style='text-align: left'>Rank</th> | |
<th class='noborder' style='text-align: left'>Name</th> | |
<th class='noborder' style='text-align: left'>Gained</th> | |
<th class='noborder'>Total</th> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<debugstuff> | |
</div> | |
</div> | |
<br /> | |
{$footer} | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment