Skip to content

Instantly share code, notes, and snippets.

@bls1999
Created January 13, 2021 01:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bls1999/cfa9105ab9d87800f32de65eea311ed4 to your computer and use it in GitHub Desktop.
Save bls1999/cfa9105ab9d87800f32de65eea311ed4 to your computer and use it in GitHub Desktop.
A quick jQuery/Ajax/PHP implementation of a simple RNG contest.
<!--
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