initial
This commit is contained in:
commit
dc9c7e5ded
22
autologin.php
Normal file
22
autologin.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "usermgr.php";
|
||||
|
||||
// attempt to auto-login
|
||||
$autologin_user_data = [];
|
||||
$auto_logged_in = false;
|
||||
if ((session_status() === PHP_SESSION_ACTIVE) && isset($_SESSION["nickname"])) {
|
||||
$autologin_user_data = get_user($_SESSION["nickname"]);
|
||||
$auto_logged_in = count($autologin_user_data) != 0;
|
||||
}
|
||||
|
||||
function get_autologin_state() : bool {
|
||||
global $auto_logged_in;
|
||||
return $auto_logged_in;
|
||||
}
|
||||
|
||||
function get_autologin_user_data() : array {
|
||||
global $autologin_user_data;
|
||||
return $autologin_user_data;
|
||||
}
|
||||
14
common_func.php
Normal file
14
common_func.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
function explode_list(string $str) : array {
|
||||
return explode(",", str_replace(" ", "", $str));
|
||||
}
|
||||
|
||||
function alter_array_contents(array &$a, $add, $remove) {
|
||||
if (($add !== null) && !array_search($add, $a)) { // if user was not assigned to the corresponding group
|
||||
$a[] = $add;
|
||||
}
|
||||
if (($remove !== null) && (($i = array_search($remove, $a)) !== false)) { // only perform deleting if user is assigned to the passed group
|
||||
array_splice($a, $i, 1);
|
||||
}
|
||||
}
|
||||
7
composer.json
Normal file
7
composer.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"require": {
|
||||
"rakibtg/sleekdb": "2.15",
|
||||
"ext-http": "*",
|
||||
"ext-json": "*"
|
||||
}
|
||||
}
|
||||
33
default_frame.php
Normal file
33
default_frame.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state()) {
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/o.js"></script>
|
||||
<script src="js/default_frame.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css">
|
||||
</head>
|
||||
<body>
|
||||
<section id="game_list_panel">
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
list_available_games();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
examples/minta_quiz_táblázat.xlsx
Normal file
BIN
examples/minta_quiz_táblázat.xlsx
Normal file
Binary file not shown.
66
game_manager_frame.php
Normal file
66
game_manager_frame.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) && ($user_data["privilege"] !== PRIVILEGE_QUIZMASTER))) {
|
||||
exit(); // this page is only available for quizmasters
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<script src="js/o.js"></script>
|
||||
<script src="js/hintbox.js"></script>
|
||||
<script src="js/usermgr.js"></script>
|
||||
<script src="js/gamemgr.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<section id="table_section">
|
||||
<table class="management">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Név</th>
|
||||
<th>Leírás</th>
|
||||
<th>Tulajdonos</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="game_manager_table">
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<input type="button" value="Új játék" onclick="create_edit_game();">
|
||||
<input type="button" value="Játék(ok) törlése" onclick="delete_games()">
|
||||
</section>
|
||||
<section class="window" shown="false" id="game_editor_window">
|
||||
<section class="window-inner">
|
||||
<section style="text-align: right">
|
||||
<span>Név: <input type="text" id="game_name"></span><br>
|
||||
<span>Leírás: <input type="text" id="game_description"></span><br>
|
||||
<span>Tulajdonos: <input type="text" id="game_owner" readonly></span><br>
|
||||
<span>Szerkesztők: <input type="text" id="game_contributors"></span><br>
|
||||
<span>Kérdés-fájl: <input type="button" id="download_challenges_btn" value="Letöltés CSV-ként" shown="false" onclick="download_challenges()">
|
||||
<input type="button" value="Új feltöltése" id="show_game_file_upload" onclick="show_hide_gamefile_upload(true)">
|
||||
<input type="file" id="game_file" shown="false">
|
||||
<input type="button" value="Mégse" id="cancel_game_file_upload" shown="false" onclick="show_hide_gamefile_upload(false);"></span><br>
|
||||
<span>Csoportok: <input type="text" id="game_groups"></span><br>
|
||||
</section>
|
||||
<span><input type="button" value="" id="game_editor_submit_btn"><input type="button" value="Mégse" onclick="hide('game_editor_window')"></span>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
list_all_games();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
186
gamemgr.php
Normal file
186
gamemgr.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "common_func.php";
|
||||
|
||||
$gamedb = new \SleekDB\Store(GAMEDB, DATADIR, ["timeout" => false]);
|
||||
|
||||
const DEFAULT_GAME_PROPERTIES = [
|
||||
"forward_only" => false, // player may traverse back and forth between challenges
|
||||
"time_limit" => -1, // no time limit; otherwise, this field indicates time limit in seconds
|
||||
"repeatable" => false // this test can be taken multiple times
|
||||
];
|
||||
|
||||
function create_game(string $name, string $owner, string $description = "", array $properties = DEFAULT_GAME_PROPERTIES, array $contributors = [], array $challenges = []): bool
|
||||
{
|
||||
global $testdb;
|
||||
$game_data = [
|
||||
"name" => $name,
|
||||
"owner" => $owner,
|
||||
"contributors" => $contributors,
|
||||
"description" => $description,
|
||||
"game_file_present" => false,
|
||||
"properties" => $properties,
|
||||
"groups" => [],
|
||||
];
|
||||
$game_data = $testdb->insert($game_data);
|
||||
|
||||
// prepare game context
|
||||
$id = $game_data["_id"];
|
||||
$current_game_media_dir = GAMEMEDIA_DIR . DIRECTORY_SEPARATOR . $id;
|
||||
mkdir($current_game_media_dir);
|
||||
save_challenges($id, []);
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_game(string $gameid): array
|
||||
{
|
||||
global $testdb;
|
||||
return $testdb->findById($gameid);
|
||||
}
|
||||
|
||||
function update_game(array $game_data)
|
||||
{
|
||||
global $testdb;
|
||||
$testdb->update($game_data);
|
||||
}
|
||||
|
||||
function delete_game(string $gameid)
|
||||
{
|
||||
global $testdb;
|
||||
$game_data = get_game($gameid);
|
||||
if (count($game_data) != 0) {
|
||||
foreach ($game_data["groups"] as $groupid) {
|
||||
change_group_game_assignments($groupid, null, $gameid);
|
||||
}
|
||||
$testdb->deleteById($gameid);
|
||||
}
|
||||
}
|
||||
|
||||
function change_game_group_assignments(string $gid, $groupname_add, $groupname_remove)
|
||||
{
|
||||
$game_data = get_game($gid);
|
||||
if (count($game_data) != 0) {
|
||||
alter_array_contents($game_data["groups"], $groupname_add, $groupname_remove);
|
||||
update_game($game_data); // update user
|
||||
}
|
||||
}
|
||||
|
||||
function get_all_games()
|
||||
{
|
||||
global $testdb;
|
||||
return $testdb->findAll();
|
||||
}
|
||||
|
||||
function get_all_game_data_by_contributor_nickname(string $nickname): array
|
||||
{
|
||||
global $testdb;
|
||||
$game_headers = [];
|
||||
if ($nickname !== "*") {
|
||||
$game_data_array = $testdb->findBy([["owner", "=", $nickname], "OR", ["contributors", "CONTAINS", $nickname]]);
|
||||
} else {
|
||||
$game_data_array = $testdb->findAll();
|
||||
}
|
||||
foreach ($game_data_array as $game_data) {
|
||||
$game_headers[] = $game_data;
|
||||
}
|
||||
return $game_headers;
|
||||
}
|
||||
|
||||
function import_challenges_from_csv(string $csv_path, string $gameid)
|
||||
{
|
||||
$game_data = get_game($gameid);
|
||||
if (count($game_data) === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$challenges = [];
|
||||
|
||||
// load filled CSV file
|
||||
$f = fopen($csv_path, "r");
|
||||
if (!$f) { // failed to open file
|
||||
return;
|
||||
}
|
||||
while ($csvline = fgetcsv($f)) {
|
||||
if (count($csvline) >= 3) {
|
||||
$ch = [
|
||||
"question" => $csvline[0],
|
||||
"image_url" => $csvline[1],
|
||||
"correct_answer" => $csvline[2],
|
||||
"answers" => array_filter(array_slice($csvline, 2), function ($v) {
|
||||
return trim($v) !== "";
|
||||
})
|
||||
];
|
||||
$challenges[] = $ch;
|
||||
}
|
||||
}
|
||||
fclose($f);
|
||||
|
||||
// save challenges
|
||||
save_challenges($gameid, $challenges);
|
||||
|
||||
// update game with game file present
|
||||
$game_data["game_file_present"] = true;
|
||||
update_game($game_data);
|
||||
}
|
||||
|
||||
function is_user_contributor_to_game(string $gameid, string $nickname): bool
|
||||
{
|
||||
$game_data = get_game($gameid);
|
||||
if (count($game_data) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($nickname, $game_data["contributors"]) || ($game_data["owner"] === $nickname);
|
||||
}
|
||||
|
||||
function is_user_owner_of_the_game(string $gameid, string $nickname): bool
|
||||
{
|
||||
$game_data = get_game($gameid);
|
||||
if (count($game_data) === 0) {
|
||||
return false;
|
||||
}
|
||||
return $game_data["owner"] === $nickname;
|
||||
}
|
||||
|
||||
function save_challenges(string $gameid, array $challenges)
|
||||
{
|
||||
file_put_contents(get_game_file_by_gameid($gameid), json_encode($challenges)); // store challenges in JSON-format
|
||||
}
|
||||
|
||||
function load_challenges(string $gameid)
|
||||
{
|
||||
return json_decode(file_get_contents(get_game_file_by_gameid($gameid)), true);
|
||||
}
|
||||
|
||||
function export_challenges_to_csv($f, string $gameid)
|
||||
{
|
||||
$game_data = get_game($gameid);
|
||||
if ((count($game_data) === []) || (!$game_data["game_file_present"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load challenges
|
||||
$challenges = load_challenges($gameid);
|
||||
|
||||
// populate CSV file
|
||||
foreach ($challenges as $ch) {
|
||||
$csvline = [
|
||||
$ch["question"],
|
||||
$ch["image_url"],
|
||||
];
|
||||
$csvline = array_merge($csvline, $ch["answers"]);
|
||||
fputcsv($f, $csvline);
|
||||
}
|
||||
}
|
||||
|
||||
function get_contributors(string $gameid): array
|
||||
{
|
||||
$game_data = get_game($gameid);
|
||||
return $game_data["contributors"];
|
||||
}
|
||||
|
||||
function get_game_file_by_gameid(string $gameid)
|
||||
{
|
||||
return GAMEMEDIA_DIR . DIRECTORY_SEPARATOR . $gameid . DIRECTORY_SEPARATOR . GAME_FILE;
|
||||
}
|
||||
34
globals.php
Normal file
34
globals.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
require_once "vendor/autoload.php";
|
||||
|
||||
const DATADIR = "appdata";
|
||||
const GAMEMEDIA_DIR = DATADIR . DIRECTORY_SEPARATOR . "game_media";
|
||||
const GAME_FILE = "challenges.json";
|
||||
const USERDB = "users";
|
||||
const GROUPDB = "groups";
|
||||
const GAMEDB = "games";
|
||||
const TESTDB = "tests";
|
||||
const INSTALL_INDICATOR = "INSTALLED";
|
||||
const QUIZMASTER_NICKNAME = "quizmaster";
|
||||
const LOGIN_URL = "login.php";
|
||||
const MAIN_URL = "main.php";
|
||||
const SESSION_NAME = "spreadquiz_sid";
|
||||
|
||||
session_name(SESSION_NAME);
|
||||
|
||||
// autoload session
|
||||
if ((session_status() === PHP_SESSION_NONE) && isset($_COOKIE[SESSION_NAME])) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
// initialize data directory
|
||||
function init_datadir() {
|
||||
if (!file_exists(DATADIR)) {
|
||||
mkdir(DATADIR);
|
||||
mkdir(GAMEMEDIA_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
62
group_manager_frame.php
Normal file
62
group_manager_frame.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state() || ($user_data["privilege"] !== PRIVILEGE_QUIZMASTER)) {
|
||||
exit(); // this page is only available for quizmasters
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<script src="js/o.js"></script>
|
||||
<script src="js/groupmgr.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<section id="table_section">
|
||||
<table class="management">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Név</th>
|
||||
<th>Leírás</th>
|
||||
<th>Tulajdonos</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="group_manager_table">
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<input type="button" value="Új csoport" onclick="create_edit_group();">
|
||||
<input type="button" value="Csoport(ok) törlése" onclick="delete_groups();">
|
||||
</section>
|
||||
<section class="window" shown="false" id="group_editor_window">
|
||||
<section class="window-inner">
|
||||
<section style="text-align: right">
|
||||
<span>Név: <input type="text" id="groupname"></span><br>
|
||||
<span>Leírás: <input type="text" id="group_description"></span><br>
|
||||
<span>Tulajdonos: <input type="text" id="group_owner" readonly></span><br>
|
||||
<span>Szerkesztők: <input type="text" id="group_editors"></span><br>
|
||||
<span>Tagok: <textarea id="group_members" readonly></textarea></span>
|
||||
</section>
|
||||
<span><input type="button" value="" id="group_editor_submit_btn"><input type="button" value="Mégse" onclick="hide('group_editor_window')"></span>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
list_all_groups();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
192
groupmgr.php
Normal file
192
groupmgr.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "common_func.php";
|
||||
require_once "usermgr.php";
|
||||
|
||||
$groupdb = new \SleekDB\Store(GROUPDB, DATADIR, ["timeout" => false]);
|
||||
|
||||
function create_group(string $groupname, string $owner, string $description = "", array $editors = []): bool
|
||||
{
|
||||
global $groupdb;
|
||||
|
||||
// test name uniqueness
|
||||
$unique = clear_unique_in_siblings($groupname);
|
||||
|
||||
// initialize group data
|
||||
$group_data = [
|
||||
"groupname" => $groupname,
|
||||
"unique" => $unique,
|
||||
"owner" => $owner,
|
||||
"description" => $description,
|
||||
"editors" => $editors,
|
||||
"users" => [],
|
||||
"games" => []
|
||||
];
|
||||
$groupdb->insert($group_data); // insert group
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_group(string $groupid): array
|
||||
{
|
||||
global $groupdb;
|
||||
return $groupdb->findById($groupid);
|
||||
}
|
||||
|
||||
function update_group(array $group_data)
|
||||
{
|
||||
global $groupdb;
|
||||
$groupdb->update($group_data);
|
||||
}
|
||||
|
||||
function delete_group(string $groupid)
|
||||
{
|
||||
global $groupdb;
|
||||
$group_data = get_group($groupid);
|
||||
if (count($group_data) !== 0) {
|
||||
foreach ($group_data["users"] as $nickname) { // remove all references to this group
|
||||
change_user_group_assignments($nickname, null, $groupid);
|
||||
}
|
||||
}
|
||||
$groupdb->deleteById($groupid);
|
||||
}
|
||||
|
||||
function get_all_groups()
|
||||
{
|
||||
global $groupdb;
|
||||
return $groupdb->findAll();
|
||||
}
|
||||
|
||||
function change_group_user_assignments(string $groupid, $nickname_add, $nickname_remove)
|
||||
{
|
||||
$group_data = get_group($groupid);
|
||||
if (count($group_data) != 0) {
|
||||
// --------- UPDATE group assignments (DEV ONLY!)------
|
||||
|
||||
$group_data["editors"] = $group_data["editors"] ?: [];
|
||||
|
||||
// ---------
|
||||
|
||||
alter_array_contents($group_data["users"], $nickname_add, $nickname_remove); // add to local storage
|
||||
alter_array_contents($group_data["editors"], null, $nickname_remove); // removed users must be excluded from the editors' list as well
|
||||
update_group($group_data); // update user
|
||||
if ($nickname_add !== null) {
|
||||
change_user_group_assignments($nickname_add, $groupid, null); // add to user's storage remote
|
||||
}
|
||||
if ($nickname_remove !== null) {
|
||||
change_user_group_assignments($nickname_remove, null, $groupid); // remove from user's storage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function change_group_game_assignments(string $groupid, $gameid_add, $gameid_remove)
|
||||
{
|
||||
$group_data = get_group($groupid);
|
||||
if (count($group_data) != 0) {
|
||||
alter_array_contents($group_data["games"], $gameid_add, $gameid_remove); // add to local storage
|
||||
update_group($group_data); // update user
|
||||
if ($gameid_add !== null) {
|
||||
change_game_group_assignments($gameid_add, $groupid, null); // add to user's storage remote
|
||||
}
|
||||
if ($gameid_remove !== null) {
|
||||
change_game_group_assignments($gameid_remove, null, $groupid); // remove from user's storage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function search_groups(string $needle): array
|
||||
{
|
||||
if (strlen($needle) < 3) {
|
||||
return [];
|
||||
}
|
||||
|
||||
global $groupdb;
|
||||
$parts = explode("#", $needle, 2);
|
||||
$groupname = $parts[0];
|
||||
$group_data_array = [];
|
||||
if (count($parts) === 1) {
|
||||
$group_data_array = $groupdb->findBy(["groupname", "LIKE", "%$groupname%"]);
|
||||
} else if (count($parts) > 1) {
|
||||
$groupid = $parts[1];
|
||||
$group_data_array = $groupdb->findBy([["groupname", "LIKE", "%$groupname%"], "AND", ["_id", "LIKE", "%$groupid%"]]);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($group_data_array as $group_data) {
|
||||
$results[] = [
|
||||
"groupname" => $group_data["groupname"],
|
||||
"_id" => $group_data["_id"],
|
||||
"unique" => $group_data["unique"]
|
||||
];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
function clear_unique_in_siblings($groupname): bool
|
||||
{
|
||||
// make test on name uniqueness
|
||||
global $groupdb;
|
||||
$twins = $groupdb->findBy(["groupname", "=", "$groupname"]);
|
||||
$unique = count($twins) == 0;
|
||||
if (count($twins) === 1) { // if fails, then also indicate in the original group that its name is no longer unique
|
||||
$twins[0]["unique"] = false;
|
||||
update_group($twins[0]);
|
||||
}
|
||||
return $unique;
|
||||
}
|
||||
|
||||
function get_groupids_by_compounds(array $compounds): array
|
||||
{
|
||||
global $groupdb;
|
||||
$groupids = [];
|
||||
foreach ($compounds as $compound) {
|
||||
if (trim($compound) === "") { // skip empty entries
|
||||
continue;
|
||||
}
|
||||
|
||||
// fetch the group
|
||||
$parts = explode("#", $compound);
|
||||
$group_data = [];
|
||||
if (count($parts) === 1) {
|
||||
$fetch_cmd = ["groupname", "=", $parts[0]];
|
||||
$group_data = $groupdb->findBy($fetch_cmd);
|
||||
if (count($group_data) == 1) { // too many hits
|
||||
$group_data = $group_data[0];
|
||||
} else {
|
||||
$group_data = [];
|
||||
}
|
||||
} else {
|
||||
$group_data = $groupdb->findById($parts[1]);
|
||||
}
|
||||
if ($group_data !== []) {
|
||||
$groupids[] = $group_data["_id"];
|
||||
}
|
||||
}
|
||||
return $groupids;
|
||||
}
|
||||
|
||||
function is_user_editor_to_group(string $groupid, string $nickname): bool
|
||||
{
|
||||
$group_data = get_group($groupid);
|
||||
if (count($group_data) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($nickname, $group_data["editors"]) || ($group_data["owner"] === $nickname);
|
||||
}
|
||||
|
||||
function get_group_unique_name(string $groupid): string
|
||||
{
|
||||
$group_data = get_group($groupid);
|
||||
if (count($group_data) !== 0) {
|
||||
return $group_data["groupname"] . (!$group_data["unique"] ? "#" . $groupid : "");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function resolve_groupids(array &$groups): void
|
||||
{
|
||||
for ($i = 0; $i < count($groups); $i++) {
|
||||
$groups[$i] = get_group_unique_name($groups[$i]);
|
||||
}
|
||||
}
|
||||
10
index.php
Normal file
10
index.php
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
</head>
|
||||
<body>
|
||||
<?="asd" ?>
|
||||
</body>
|
||||
</html>
|
||||
24
install.php
Normal file
24
install.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
|
||||
if (file_exists(INSTALL_INDICATOR)) {
|
||||
echo "SpreadQuiz already installed!";
|
||||
exit();
|
||||
}
|
||||
|
||||
init_datadir(); // create data directory
|
||||
|
||||
// auto-create databases
|
||||
require_once "usermgr.php";
|
||||
require_once "groupmgr.php";
|
||||
require_once "gamemgr.php";
|
||||
|
||||
// create "quizmaster" (admin) user
|
||||
$pw = uniqid();
|
||||
add_user(QUIZMASTER_NICKNAME, $pw, "");
|
||||
change_privilege_level(QUIZMASTER_NICKNAME, PRIVILEGE_QUIZMASTER);
|
||||
echo "Quizmaster account: quizmaster, $pw\n";
|
||||
|
||||
// deploy install indicator
|
||||
touch(INSTALL_INDICATOR);
|
||||
324
interface.php
Normal file
324
interface.php
Normal file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
|
||||
if (!file_exists(INSTALL_INDICATOR)) {
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_REQUEST["action"])) {
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once "common_func.php";
|
||||
|
||||
// load databases only if something meaningful have arrived
|
||||
require_once "usermgr.php";
|
||||
require_once "groupmgr.php";
|
||||
require_once "gamemgr.php";
|
||||
|
||||
$action = $_REQUEST["action"];
|
||||
|
||||
$result = "";
|
||||
|
||||
// no-login accessible actions
|
||||
switch ($action) {
|
||||
case "login":
|
||||
{
|
||||
$nickname = $_REQUEST["nickname"];
|
||||
$password = $_REQUEST["password"];
|
||||
if (check_user_credentials($nickname, $password)) {
|
||||
session_start();
|
||||
$_SESSION["nickname"] = $nickname;
|
||||
$result = "OK";
|
||||
} else {
|
||||
$result = "FAIL";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// exit script if there's no live session or nickname is missing or the referenced user is non-existent
|
||||
if ((session_status() != PHP_SESSION_ACTIVE) || (!isset($_SESSION["nickname"])) || (count(get_user($_SESSION["nickname"])) == 0)) {
|
||||
goto print_result;
|
||||
}
|
||||
|
||||
$user_data = get_user($_SESSION["nickname"]);
|
||||
$nickname = $user_data["nickname"];
|
||||
$privilege = $user_data["privilege"];
|
||||
|
||||
// login-requiring actions
|
||||
switch ($action) {
|
||||
case "logout":
|
||||
{
|
||||
$_SESSION = []; // clean up session data
|
||||
setcookie(SESSION_NAME, "", -1); // invalidate cookie
|
||||
}
|
||||
break;
|
||||
case "get_user_info":
|
||||
{
|
||||
$user_data_filtered = $user_data;
|
||||
unset($user_data_filtered["password"]);
|
||||
$result = json_encode($user_data_filtered);
|
||||
}
|
||||
break;
|
||||
case "get_available_games":
|
||||
{
|
||||
$games_by_groups = [];
|
||||
$groupids = $user_data["groups"];
|
||||
foreach ($groupids as $groupid) {
|
||||
$group_data = get_group($groupid);
|
||||
$game_collection = [
|
||||
"groupname" => $group_data["groupname"],
|
||||
"description" => $group_data["description"],
|
||||
"games" => []
|
||||
];
|
||||
$gameids = $group_data["games"];
|
||||
foreach ($gameids as $gameid) {
|
||||
$game = get_game($gameid);
|
||||
$game_collection["games"][] = $game;
|
||||
}
|
||||
$games_by_groups[] = $game_collection;
|
||||
}
|
||||
$result = json_encode($games_by_groups);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// creator or quizmaster actions
|
||||
if (($privilege !== PRIVILEGE_CREATOR) && ($privilege !== PRIVILEGE_QUIZMASTER)) {
|
||||
goto print_result;
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case "create_game":
|
||||
case "update_game":
|
||||
{
|
||||
$update = $action === "update_game";
|
||||
$data = json_decode($_REQUEST["data"], true) ?: [];
|
||||
if (($data === []) || (trim($data["name"] ?: "") === "")) { // no further processing
|
||||
goto print_result; // ~exit...
|
||||
}
|
||||
$gameid = $data["_id"];
|
||||
$name = $data["name"];
|
||||
$description = $data["description"];
|
||||
$contributors = explode_list($data["contributors"] ?: "");
|
||||
$owner = $update ? trim($data["owner"] ?: $nickname) : $nickname;
|
||||
$groups = explode_list($data["groups"] ?: "");
|
||||
|
||||
$groupids = get_groupids_by_compounds($groups); // convert group compounds to _ids
|
||||
|
||||
// remove group ID's this user cannot edit
|
||||
$groupids_with_editor_access = [];
|
||||
foreach ($groupids as $groupid) {
|
||||
if (is_user_editor_to_group($groupid, $nickname)) {
|
||||
$groupids_with_editor_access[] = $groupid;
|
||||
}
|
||||
}
|
||||
$groupids_with_editor_access = $groupid;
|
||||
|
||||
if (!$update) {
|
||||
create_game($name, $owner, $description);
|
||||
} else if (is_user_contributor_to_game($gameid, $nickname)) {
|
||||
$game_data = get_game($gameid);
|
||||
if (count($game_data) !== 0) {
|
||||
// group management
|
||||
$old_groupids = $game_data["groups"]; // retain old groupids
|
||||
$new_groupids = $groupids; // get new groupids
|
||||
$groupids_add = array_diff($new_groupids, $old_groupids); // groups this user needs to be added to
|
||||
$groupids_remove = array_diff($old_groupids, $new_groupids); // groups this user need to be removed from
|
||||
foreach ($groupids_add as $groupid) { // execute insertion and removal
|
||||
change_group_game_assignments($groupid, $gameid, null);
|
||||
}
|
||||
foreach ($groupids_remove as $groupid) {
|
||||
change_group_game_assignments($groupid, null, $gameid);
|
||||
}
|
||||
|
||||
// re-fetch game data
|
||||
$game_data = get_game($gameid);
|
||||
|
||||
// update game header data
|
||||
$game_data["name"] = $name;
|
||||
$game_data["description"] = $description;
|
||||
if (($game_data["owner"] === $nickname) || ($privilege === PRIVILEGE_QUIZMASTER)) {
|
||||
$game_data["owner"] = $owner;
|
||||
}
|
||||
$game_data["contributors"] = array_intersect($contributors, get_all_nicknames());
|
||||
update_game($game_data);
|
||||
|
||||
// update game file if supplied
|
||||
if (isset($_FILES["game_file"])) {
|
||||
import_challenges_from_csv($_FILES["game_file"]["tmp_name"], $data["_id"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "get_all_game_headers":
|
||||
{
|
||||
$requester_nickname = ($privilege === PRIVILEGE_QUIZMASTER) ? "*" : $nickname; // "*" means every game
|
||||
$game_headers = get_all_game_data_by_contributor_nickname($requester_nickname);
|
||||
foreach ($game_headers as &$game_header) {
|
||||
resolve_groupids($game_header["groups"]);
|
||||
}
|
||||
$result = json_encode($game_headers);
|
||||
}
|
||||
break;
|
||||
case "delete_games":
|
||||
{
|
||||
$gameids = explode_list(trim($_REQUEST["ids"] ?: ""));
|
||||
foreach ($gameids as $gameid) {
|
||||
if (($gameid !== "") && (is_user_owner_of_the_game($gameid, $nickname))) { // only the owner may delete a game
|
||||
delete_game($gameid);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "export_game_file_csv":
|
||||
{
|
||||
$gameid = trim($_REQUEST["gameid"] ?: "");
|
||||
if (($gameid !== "") && is_user_contributor_to_game($gameid, $nickname)) {
|
||||
$f = tmpfile();
|
||||
header("Content-Type: text/csv");
|
||||
header("Content-Disposition: attachment; filename=\"challenges_$gameid.csv\"\r\n");
|
||||
export_challenges_to_csv($f, $gameid);
|
||||
fseek($f, 0);
|
||||
fpassthru($f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// quizmaster actions
|
||||
if ($privilege !== PRIVILEGE_QUIZMASTER) {
|
||||
goto print_result;
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case "create_group":
|
||||
case "update_group":
|
||||
{
|
||||
$update = $action === "update_group";
|
||||
$groupname = trim($_REQUEST["groupname"] ?: "");
|
||||
$description = trim($_REQUEST["description"] ?: "");
|
||||
$editors = explode_list(trim($_REQUEST["editors"] ?: ""));
|
||||
$owner = (!$update) ? $user_data["nickname"] : trim($_REQUEST["owner"]);
|
||||
if ($owner === "") {
|
||||
$owner = $user_data["nickname"];
|
||||
}
|
||||
if ($groupname != "") {
|
||||
switch ($action) {
|
||||
case "create_group":
|
||||
create_group($groupname, $owner, $description, $editors);
|
||||
break;
|
||||
case "update_group":
|
||||
{
|
||||
$gid = $_REQUEST["id"];
|
||||
$group = get_group($gid);
|
||||
if (count($group) !== 0) {
|
||||
$group["unique"] = clear_unique_in_siblings($groupname); // manage unique flag in case of renaming
|
||||
$group["groupname"] = $groupname;
|
||||
$group["description"] = $description;
|
||||
$group["editors"] = array_intersect($editors, $group["users"]); // a user cannot be an editor if not part of the group
|
||||
$group["owner"] = $owner;
|
||||
update_group($group);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "delete_groups":
|
||||
{
|
||||
$groups = explode_list($_REQUEST["ids"] ?: "");
|
||||
foreach ($groups as $g) {
|
||||
delete_group($g);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "get_all_groups":
|
||||
$result = json_encode(get_all_groups());
|
||||
break;
|
||||
case "search_groups":
|
||||
{
|
||||
$needle = $_REQUEST["needle"] ?: "";
|
||||
$result = json_encode(search_groups($needle));
|
||||
}
|
||||
break;
|
||||
case "create_user":
|
||||
case "update_user":
|
||||
{
|
||||
$update = $action === "update_user";
|
||||
$target_nickname = trim($_REQUEST["nickname"] ?: "");
|
||||
$password = trim($_REQUEST["password"] ?: "");
|
||||
$groups = explode_list($_REQUEST["groups"] ?: "");
|
||||
$realname = trim($_REQUEST["realname"] ?: "");
|
||||
$privilege = trim($_REQUEST["privilege"] ?: PRIVILEGE_PLAYER);
|
||||
|
||||
$groupids = get_groupids_by_compounds($groups); // convert group compounds to _ids
|
||||
|
||||
if (($target_nickname !== "")) {
|
||||
if ((!$update) && ($password !== "")) { // CREATE
|
||||
add_user($target_nickname, $password, $realname, $groupids, $privilege);
|
||||
} else if ($update) { // UPDATE
|
||||
$user_data = get_user($target_nickname); // load user data
|
||||
|
||||
// group management
|
||||
$old_groupids = $user_data["groups"]; // retain old groupids
|
||||
$new_groupids = $groupids; // get new groupids
|
||||
$groupids_add = array_diff($new_groupids, $old_groupids); // groups this user needs to be added to
|
||||
$groupids_remove = array_diff($old_groupids, $new_groupids); // groups this user need to be removed from
|
||||
foreach ($groupids_add as $groupid) { // execute insertion and removal
|
||||
change_group_user_assignments($groupid, $target_nickname, null);
|
||||
}
|
||||
foreach ($groupids_remove as $groupid) {
|
||||
change_group_user_assignments($groupid, null, $target_nickname);
|
||||
}
|
||||
|
||||
// re-fetch user
|
||||
$user_data = get_user($target_nickname); // load user data
|
||||
|
||||
// further field update
|
||||
$user_data["realname"] = $realname;
|
||||
$user_data["privilege"] = $privilege;
|
||||
|
||||
// password replacement, if requested
|
||||
if ($password !== "") {
|
||||
$user_data["password"] = password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
update_user($user_data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case "delete_users":
|
||||
{
|
||||
$users = explode_list($_REQUEST["users"] ?: "");
|
||||
foreach ($users as $g) {
|
||||
delete_user($g);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "get_all_users":
|
||||
{
|
||||
$user_data_filtered = get_all_users();
|
||||
for ($i = 0; $i < count($user_data_filtered); $i++) {
|
||||
unset($user_data_filtered[$i]["password"]); // remove password from records
|
||||
resolve_groupids($user_data_filtered[$i]["groups"]); // resolve group IDs
|
||||
}
|
||||
$result = json_encode($user_data_filtered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
print_result:
|
||||
|
||||
if ($result !== "") {
|
||||
echo $result;
|
||||
}
|
||||
29
js/default_frame.js
Normal file
29
js/default_frame.js
Normal file
@ -0,0 +1,29 @@
|
||||
function list_available_games() {
|
||||
let game_list_panel = document.getElementById("game_list_panel");
|
||||
|
||||
let req = {
|
||||
action: "get_available_games"
|
||||
}
|
||||
request(req).then(resp => {
|
||||
let games_by_groups = JSON.parse(resp);
|
||||
games_by_groups.forEach((game_collection) => {
|
||||
let group_box = document.createElement("section");
|
||||
group_box.classList.add("group-box");
|
||||
let group_box_caption = document.createElement("span");
|
||||
group_box_caption.classList.add("group-box-caption");
|
||||
group_box_caption.innerHTML = game_collection["groupname"];
|
||||
let group_box_inner = document.createElement("section");
|
||||
group_box_inner.classList.add("group-box-inner");
|
||||
group_box.append(group_box_caption, group_box_inner);
|
||||
|
||||
game_collection["games"].forEach((game) => {
|
||||
let game_box = document.createElement("section");
|
||||
game_box.classList.add("game-box");
|
||||
game_box.innerHTML = game["name"];
|
||||
group_box_inner.appendChild(game_box);
|
||||
});
|
||||
|
||||
game_list_panel.appendChild(group_box);
|
||||
});
|
||||
});
|
||||
}
|
||||
175
js/gamemgr.js
Normal file
175
js/gamemgr.js
Normal file
@ -0,0 +1,175 @@
|
||||
function list_all_games() {
|
||||
let req = {action: "get_all_game_headers"};
|
||||
let tbody = document.getElementById("game_manager_table");
|
||||
tbody.innerHTML = "";
|
||||
request(req).then(resp => {
|
||||
let games = JSON.parse(resp);
|
||||
for (let i = 0; i < games.length; i++) {
|
||||
let g = games[i];
|
||||
let row = document.createElement("tr");
|
||||
let chkbox = document.createElement("input");
|
||||
chkbox.type = "checkbox";
|
||||
chkbox.name = "game_chkbox";
|
||||
chkbox.game = g;
|
||||
let tdChkBox = document.createElement("td");
|
||||
tdChkBox.appendChild(chkbox);
|
||||
tdChkBox.classList.add("checkbox");
|
||||
let tdGameName = create_table_cell(g["name"]);
|
||||
let tdGameDescription = create_table_cell(g["description"]);
|
||||
let tdOwner = create_table_cell(g["owner"]);
|
||||
row.append(tdChkBox, tdGameName, tdGameDescription, tdOwner);
|
||||
tbody.appendChild(row);
|
||||
|
||||
let edit_group_action = () => {
|
||||
create_edit_game(g);
|
||||
};
|
||||
|
||||
tdGameName.addEventListener("click", edit_group_action);
|
||||
tdGameDescription.addEventListener("click", edit_group_action);
|
||||
tdOwner.addEventListener("click", edit_group_action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var EDITED_GAME = null;
|
||||
|
||||
function create_edit_game(game = null) {
|
||||
EDITED_GAME = game;
|
||||
let updating = game !== null;
|
||||
|
||||
let nameF = document.getElementById("game_name");
|
||||
let descriptionF = document.getElementById("game_description");
|
||||
let submit_btn = document.getElementById("game_editor_submit_btn");
|
||||
let ownerF = document.getElementById("game_owner");
|
||||
let contributorsF = document.getElementById("game_contributors");
|
||||
let gameFileF = document.getElementById("game_file");
|
||||
let download_challenges_btn = document.getElementById("download_challenges_btn");
|
||||
let show_game_file_upload_btn = document.getElementById("show_game_file_upload");
|
||||
let cancel_game_file_upload_btn = document.getElementById("cancel_game_file_upload");
|
||||
let groupF = document.getElementById("game_groups");
|
||||
|
||||
if (!updating) { // creating a new game
|
||||
nameF.value = "";
|
||||
descriptionF.value = "";
|
||||
submit_btn.value = "Létrehozás"
|
||||
ownerF.value = USERDATA["nickname"];
|
||||
ownerF.readOnly = true;
|
||||
contributorsF.value = "";
|
||||
groupF.value = "";
|
||||
} else { // editing an existing one
|
||||
nameF.value = game["name"];
|
||||
descriptionF.value = game["description"];
|
||||
submit_btn.value = "Mentés"
|
||||
ownerF.value = game["owner"];
|
||||
ownerF.readOnly = false;
|
||||
contributorsF.value = game["contributors"].join(", ");
|
||||
groupF.value = game["groups"].join(", ");
|
||||
}
|
||||
gameFileF.value = "";
|
||||
|
||||
let game_file_present = updating && game["game_file_present"];
|
||||
if (game_file_present) {
|
||||
show(download_challenges_btn);
|
||||
}
|
||||
show_hide_gamefile_upload(false);
|
||||
|
||||
submit_btn.onclick = () => {
|
||||
let game_name = document.getElementById("game_name").value.trim();
|
||||
if (game_name !== "") {
|
||||
let reqData = {
|
||||
name: game_name,
|
||||
description: descriptionF.value.trim(),
|
||||
owner: updating ? ownerF.value.trim() : USERDATA["nickname"],
|
||||
contributors: contributorsF.value.trim(),
|
||||
groups: groupF.value.trim()
|
||||
};
|
||||
if (updating) {
|
||||
reqData["_id"] = game["_id"];
|
||||
}
|
||||
let req = {
|
||||
action: updating ? "update_game" : "create_game",
|
||||
data: JSON.stringify(reqData)
|
||||
};
|
||||
if (gameFileF.files.length > 0) { // append game file if selected
|
||||
req["game_file"] = gameFileF.files[0];
|
||||
}
|
||||
request(req).then(resp => {
|
||||
list_all_games();
|
||||
});
|
||||
hide("game_editor_window");
|
||||
}
|
||||
};
|
||||
|
||||
show("game_editor_window");
|
||||
}
|
||||
|
||||
function show_hide_gamefile_upload(en) {
|
||||
if (en) {
|
||||
// hide("download_challenges_btn");
|
||||
hide("show_game_file_upload");
|
||||
show("game_file");
|
||||
show("cancel_game_file_upload");
|
||||
} else {
|
||||
// show("download_challenges_btn");
|
||||
show("show_game_file_upload");
|
||||
hide("game_file");
|
||||
hide("cancel_game_file_upload");
|
||||
document.getElementById("game_file").value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function download_challenges() {
|
||||
let action = "export_game_file_csv";
|
||||
let gameid = EDITED_GAME["_id"];
|
||||
window.open(`interface.php?action=${action}&gameid=${gameid}`, "_blank");
|
||||
}
|
||||
|
||||
function get_selected_games() {
|
||||
let selected_chkboxes = document.getElementsByName("game_chkbox");
|
||||
let selected_games = [];
|
||||
selected_chkboxes.forEach((chkbox) => {
|
||||
if (chkbox.checked) {
|
||||
selected_games.push(chkbox.game);
|
||||
}
|
||||
});
|
||||
return selected_games;
|
||||
}
|
||||
|
||||
function delete_games() {
|
||||
let games = get_selected_games();
|
||||
if (games.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let game_names = [];
|
||||
let game_ids = [];
|
||||
games.forEach((g) => {
|
||||
game_names.push(g["name"]);
|
||||
game_ids.push(g["_id"]);
|
||||
});
|
||||
let msg = "Biztosan törölni kívánja a következő játéko(ka)t?\n\n" + game_names.join(", ") + "\n\n"
|
||||
+ "A törlés nem vonható vissza!";
|
||||
if (confirm(msg)) {
|
||||
let req = {action: "delete_games", ids: game_ids.join(",")};
|
||||
request(req).then(resp => {
|
||||
list_all_games();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// function hint_all_groups(target_element_id) {
|
||||
// const hintbox_insert_fn = (record) => {
|
||||
// let targetF = document.getElementById(target_element_id);
|
||||
// let groups = explode_sanitize_string_list(targetF.value);
|
||||
// groups.pop();
|
||||
// groups.push(record);
|
||||
// targetF.value = groups.join(", ");
|
||||
// close_hintbox(true);
|
||||
// }
|
||||
//
|
||||
// let req = {
|
||||
// action: "search_groups",
|
||||
// }
|
||||
//
|
||||
// open_hintbox_at(target_element_id, req, print_group_name, hintbox_insert_fn);
|
||||
// }
|
||||
123
js/groupmgr.js
Normal file
123
js/groupmgr.js
Normal file
@ -0,0 +1,123 @@
|
||||
function list_all_groups() {
|
||||
let req = {action: "get_all_groups"};
|
||||
let tbody = document.getElementById("group_manager_table");
|
||||
tbody.innerHTML = "";
|
||||
request(req).then(resp => {
|
||||
let groups = JSON.parse(resp);
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let g = groups[i];
|
||||
let row = document.createElement("tr");
|
||||
let chkbox = document.createElement("input");
|
||||
chkbox.type = "checkbox";
|
||||
chkbox.name = "group_chkbox";
|
||||
chkbox.group = g;
|
||||
let tdChkBox = document.createElement("td");
|
||||
tdChkBox.appendChild(chkbox);
|
||||
tdChkBox.classList.add("checkbox");
|
||||
let tdGroupName = create_table_cell(print_group_name(g));
|
||||
let tdGroupDescription = create_table_cell(g["description"]);
|
||||
let tdOwner = create_table_cell(g["owner"]);
|
||||
row.append(tdChkBox, tdGroupName, tdGroupDescription, tdOwner);
|
||||
tbody.appendChild(row);
|
||||
|
||||
let edit_group_action = () => {
|
||||
create_edit_group(g);
|
||||
};
|
||||
|
||||
tdGroupName.addEventListener("click", edit_group_action);
|
||||
tdGroupDescription.addEventListener("click", edit_group_action);
|
||||
tdOwner.addEventListener("click", edit_group_action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function create_edit_group(group = null) {
|
||||
let update = group !== null;
|
||||
|
||||
let groupnameF = document.getElementById("groupname");
|
||||
let group_descriptionF = document.getElementById("group_description");
|
||||
let submit_btn = document.getElementById("group_editor_submit_btn");
|
||||
let group_ownerF = document.getElementById("group_owner");
|
||||
let group_editorsF = document.getElementById("group_editors");
|
||||
let group_membersF = document.getElementById("group_members");
|
||||
|
||||
if (!update) { // create a new group
|
||||
groupnameF.value = "";
|
||||
group_descriptionF.value = "";
|
||||
submit_btn.value = "Létrehozás"
|
||||
group_ownerF.value = USERDATA["nickname"];
|
||||
group_ownerF.readOnly = true;
|
||||
group_editorsF.value = "";
|
||||
group_membersF.value = "";
|
||||
} else { // update and existing one
|
||||
groupnameF.value = group["groupname"];
|
||||
group_descriptionF.value = group["description"];
|
||||
submit_btn.value = "Mentés"
|
||||
group_ownerF.value = group["owner"];
|
||||
group_ownerF.readOnly = false;
|
||||
group_editorsF.value = group["editors"].join(", ");
|
||||
group_membersF.value = group["users"].join(", ");
|
||||
}
|
||||
|
||||
submit_btn.onclick = () => {
|
||||
let groupname = document.getElementById("groupname").value.trim();
|
||||
if (groupname !== "") {
|
||||
let req = {
|
||||
action: update ? "update_group" : "create_group",
|
||||
groupname: groupname,
|
||||
description: group_descriptionF.value.trim(),
|
||||
editors: group_editorsF.value.trim()
|
||||
};
|
||||
if (update) {
|
||||
req["id"] = group["_id"];
|
||||
}
|
||||
request(req).then(resp => {
|
||||
list_all_groups();
|
||||
});
|
||||
hide("group_editor_window");
|
||||
}
|
||||
};
|
||||
|
||||
show("group_editor_window");
|
||||
}
|
||||
|
||||
function get_selected_groups() {
|
||||
let selected_chkboxes = document.getElementsByName("group_chkbox");
|
||||
let selected_groups = [];
|
||||
selected_chkboxes.forEach((chkbox) => {
|
||||
if (chkbox.checked) {
|
||||
selected_groups.push(chkbox.group);
|
||||
}
|
||||
});
|
||||
return selected_groups;
|
||||
}
|
||||
|
||||
function delete_groups() {
|
||||
let groups = get_selected_groups();
|
||||
if (groups.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let group_names = [];
|
||||
let group_ids = [];
|
||||
groups.forEach((g) => {
|
||||
group_names.push(g["groupname"]);
|
||||
group_ids.push(g["_id"]);
|
||||
});
|
||||
let msg = "Biztosan törölni kívánja a következő csoporto(ka)t?\n\n" + group_names.join(", ") + "\n\n"
|
||||
+ "A törlés nem vonható vissza!";
|
||||
if (confirm(msg)) {
|
||||
let req = {action: "delete_groups", ids: group_ids.join(",")};
|
||||
request(req).then(resp => {
|
||||
list_all_groups();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function print_group_name(group_data) {
|
||||
let record = group_data["groupname"];
|
||||
if (!group_data["unique"]) {
|
||||
record += "#" + group_data["_id"];
|
||||
}
|
||||
return record;
|
||||
}
|
||||
70
js/hintbox.js
Normal file
70
js/hintbox.js
Normal file
@ -0,0 +1,70 @@
|
||||
function explode_sanitize_string_list(list, explodeAt = ",") {
|
||||
let elements = list.split(explodeAt);
|
||||
let sanitized = [];
|
||||
elements.forEach((e) => {
|
||||
if (e.trim() !== "") {
|
||||
sanitized.push(e);
|
||||
}
|
||||
});
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
const HINTBOX_ID = "HINTBOX";
|
||||
|
||||
function close_hintbox(close_if_focused = false) {
|
||||
setTimeout(() => {
|
||||
if (close_if_focused || (document.getElementById(HINTBOX_ID).firstChild !== document.activeElement)) {
|
||||
hide(HINTBOX_ID);
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
function open_hintbox(x, y, contents, print_fun, insert_fun) {
|
||||
let hbw = document.getElementById(HINTBOX_ID);
|
||||
if (hbw === null) {
|
||||
hbw = document.createElement("section");
|
||||
hbw.id = HINTBOX_ID;
|
||||
hbw.classList.add("hintbox-window");
|
||||
let lb = document.createElement("select");
|
||||
lb.size = 5;
|
||||
lb.style.width = "100%";
|
||||
hbw.appendChild(lb);
|
||||
document.body.appendChild(hbw);
|
||||
}
|
||||
let lb = hbw.firstChild;
|
||||
|
||||
hbw.style.left = `${x}px`;
|
||||
hbw.style.top = `${y}px`;
|
||||
|
||||
lb.innerHTML = "";
|
||||
contents.forEach((record) => {
|
||||
let line = print_fun(record);
|
||||
let opt = document.createElement("option");
|
||||
opt.value = line;
|
||||
opt.innerText = line;
|
||||
lb.appendChild(opt);
|
||||
});
|
||||
|
||||
lb.ondblclick = () => {
|
||||
insert_fun(lb.value);
|
||||
};
|
||||
show(HINTBOX_ID);
|
||||
}
|
||||
|
||||
function open_hintbox_at(target_element_id, req_data, print_fn, insert_fn) {
|
||||
let targetF = document.getElementById(target_element_id);
|
||||
|
||||
let bbox = targetF.getBoundingClientRect();
|
||||
let list_str = targetF.value.trim().split(",");
|
||||
if (list_str.length > 0) {
|
||||
let last_le_str = list_str[list_str.length - 1].trim();
|
||||
if (last_le_str.length > 2) {
|
||||
req_data["needle"] = last_le_str; // auto-insert needle
|
||||
request(req_data).then(resp => {
|
||||
let groups = JSON.parse(resp);
|
||||
let x = bbox.x + bbox.width;
|
||||
let y = bbox.y;
|
||||
open_hintbox(x, y, groups, print_fn, insert_fn);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
21
js/o.js
Normal file
21
js/o.js
Normal file
@ -0,0 +1,21 @@
|
||||
function o(input) {
|
||||
var o;
|
||||
if (typeof input === "string")
|
||||
o = document.getElementById(input);
|
||||
else if (typeof input === "object")
|
||||
o = input;
|
||||
return o;
|
||||
}
|
||||
|
||||
// megjelenítés / eltüntetés
|
||||
function show(obj) {
|
||||
o(obj).setAttribute("shown", "true");
|
||||
}
|
||||
|
||||
function hide(obj) {
|
||||
o(obj).setAttribute("shown", "false");
|
||||
}
|
||||
|
||||
function toggle_show(obj) {
|
||||
o(obj).setAttribute("shown", o(obj).getAttribute("shown") === "false");
|
||||
}
|
||||
31
js/req.js
Normal file
31
js/req.js
Normal file
@ -0,0 +1,31 @@
|
||||
// kérés indítása a szerver felé (eredeti: KL.)
|
||||
function request(data, url = "interface.php", method = "POST") {
|
||||
return new Promise((resolve, reject) => {
|
||||
var fd;
|
||||
|
||||
// ha van adat megadva...
|
||||
if (data != null) {
|
||||
fd = new FormData();
|
||||
|
||||
// mezők hozzáfűzése a kéréshez
|
||||
for (let prop in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, prop)) {
|
||||
fd.append(prop, data[prop]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// kérés feladása
|
||||
fetch(url, {
|
||||
method: method,
|
||||
body: fd,
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => resolve(data))
|
||||
.catch((error) => {
|
||||
console.error('Error: ', error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
52
js/spreadquiz.js
Normal file
52
js/spreadquiz.js
Normal file
@ -0,0 +1,52 @@
|
||||
function login() {
|
||||
let nicknameF = document.getElementById("nickname"); // fetch fields
|
||||
let pwF = document.getElementById("password");
|
||||
let nickname = nicknameF.value; // extract values
|
||||
let pw = pwF.value;
|
||||
|
||||
let loginReq = {
|
||||
action: "login",
|
||||
nickname: nickname,
|
||||
password: pw
|
||||
};
|
||||
|
||||
request(loginReq).then(resp => {
|
||||
if (resp === "OK") {
|
||||
location.href = "main.php"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function open_in_content_frame(url) {
|
||||
document.getElementById("content_frame").src = url;
|
||||
}
|
||||
|
||||
var USERDATA = {};
|
||||
function load_userdata() {
|
||||
let req = {action: "get_user_info"};
|
||||
request(req).then(resp => {
|
||||
USERDATA = JSON.parse(resp);
|
||||
});
|
||||
}
|
||||
|
||||
load_userdata();
|
||||
|
||||
function create_table_cell(content, styleClass = "") {
|
||||
if (content.trim() === "") {
|
||||
content = "<i>(üres)</i>";
|
||||
}
|
||||
let td = document.createElement("td");
|
||||
td.innerHTML = content;
|
||||
if (styleClass !== "") {
|
||||
td.classList.add(styleClass);
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
// ---------------
|
||||
|
||||
function highlight_row(nickname) {
|
||||
let hl_on = document.getElementById("user_chk_" + nickname).checked;
|
||||
let row = document.getElementById("row_" + nickname);
|
||||
row.setAttribute("highlight", hl_on ? "true" : "false");
|
||||
}
|
||||
30
js/testground.js
Normal file
30
js/testground.js
Normal file
@ -0,0 +1,30 @@
|
||||
function populate_test(test_id) {
|
||||
let test_display = document.getElementById("test_display");
|
||||
|
||||
let req = {
|
||||
action: "get_test",
|
||||
id: test_id
|
||||
}
|
||||
request(req).then(resp => {
|
||||
let test_data = JSON.parse(resp);
|
||||
test_data["challenges"].forEach((challenge) => {
|
||||
let challenge_box = document.createElement("section");
|
||||
challenge_box.classList.add("challenge");
|
||||
let question = document.createElement("span");
|
||||
question.classList.add("question");
|
||||
question.innerHTML = challenge["question"];
|
||||
let answer_container = document.createElement("section");
|
||||
answer_container.classList.add("answer-container");
|
||||
challenge_box.append(question, answer_container);
|
||||
|
||||
challenge["answers"].forEach((answer) => {
|
||||
let answer_section = document.createElement("section");
|
||||
answer_section.classList.add("answer");
|
||||
answer_section.innerHTML = answer;
|
||||
answer_container.appendChild(answer_section);
|
||||
});
|
||||
|
||||
test_display.appendChild(challenge_box);
|
||||
});
|
||||
});
|
||||
}
|
||||
164
js/usermgr.js
Normal file
164
js/usermgr.js
Normal file
@ -0,0 +1,164 @@
|
||||
function list_all_users() {
|
||||
let tbody = document.getElementById("user_manager_table_body");
|
||||
tbody.innerHTML = "";
|
||||
let req = {action: "get_all_users"};
|
||||
request(req).then(resp => {
|
||||
let users = JSON.parse(resp);
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
let u = users[i];
|
||||
let row = document.createElement("tr");
|
||||
let chkbox = document.createElement("input");
|
||||
chkbox.type = "checkbox";
|
||||
chkbox.name = "user_chkbox";
|
||||
chkbox.user = u;
|
||||
let tdChkBox = document.createElement("td");
|
||||
tdChkBox.appendChild(chkbox);
|
||||
tdChkBox.classList.add("checkbox");
|
||||
let tdNickName = create_table_cell(u["nickname"]);
|
||||
let tdRealName = create_table_cell(u["realname"]);
|
||||
let tdGroups = create_table_cell(u["groups"].join(", "));
|
||||
let tdPrivilege = create_table_cell(u["privilege"]);
|
||||
row.append(tdChkBox, tdNickName, tdRealName, tdGroups, tdPrivilege);
|
||||
tbody.appendChild(row);
|
||||
|
||||
let edit_user_action = () => {
|
||||
if (get_selected_users().length === 0) {
|
||||
edit_user(u);
|
||||
}
|
||||
};
|
||||
|
||||
tdNickName.addEventListener("click", edit_user_action);
|
||||
tdRealName.addEventListener("click", edit_user_action);
|
||||
tdGroups.addEventListener("click", edit_user_action);
|
||||
tdPrivilege.addEventListener("click", edit_user_action)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function create_new_user() {
|
||||
const generateRandomString = () => {
|
||||
return Math.floor(Math.random() * Date.now()).toString(36);
|
||||
};
|
||||
|
||||
let nicknameF = document.getElementById("nickname");
|
||||
let realnameF = document.getElementById("realname");
|
||||
let passwordF = document.getElementById("password");
|
||||
let groupsF = document.getElementById("groups");
|
||||
let privilegeF = document.getElementById("privilege");
|
||||
let submit_btn = document.getElementById("user_editor_submit_btn");
|
||||
|
||||
nicknameF.value = "";
|
||||
nicknameF.readOnly = false;
|
||||
realnameF.value = "";
|
||||
passwordF.type = "text";
|
||||
passwordF.value = generateRandomString();
|
||||
passwordF.readOnly = true;
|
||||
groupsF.value = "";
|
||||
submit_btn.value = "Létrehozás"
|
||||
|
||||
submit_btn.onclick = () => {
|
||||
let nickname = nicknameF.value.trim();
|
||||
if (nickname !== "") {
|
||||
let req = {
|
||||
action: "create_user",
|
||||
nickname: nickname,
|
||||
realname: realnameF.value.trim(),
|
||||
password: passwordF.value,
|
||||
groups: groupsF.value.trim(),
|
||||
privilege: privilegeF.value
|
||||
};
|
||||
request(req).then(resp => {
|
||||
list_all_users();
|
||||
});
|
||||
hide("user_editor_window");
|
||||
}
|
||||
};
|
||||
|
||||
show("user_editor_window");
|
||||
}
|
||||
|
||||
function edit_user(user) {
|
||||
let nicknameF = document.getElementById("nickname");
|
||||
let realnameF = document.getElementById("realname");
|
||||
let passwordF = document.getElementById("password");
|
||||
let groupsF = document.getElementById("groups");
|
||||
let privilegeF = document.getElementById("privilege");
|
||||
let submit_btn = document.getElementById("user_editor_submit_btn");
|
||||
|
||||
nicknameF.value = user["nickname"];
|
||||
nicknameF.readOnly = true;
|
||||
realnameF.value = user["realname"];
|
||||
passwordF.type = "password";
|
||||
passwordF.value = "";
|
||||
passwordF.readOnly = false;
|
||||
groupsF.value = user["groups"].join(", ");
|
||||
privilegeF.value = user["privilege"];
|
||||
submit_btn.value = "Mentés"
|
||||
|
||||
submit_btn.onclick = () => {
|
||||
let nickname = nicknameF.value.trim();
|
||||
if (nickname !== "") {
|
||||
let req = {
|
||||
action: "update_user",
|
||||
nickname: nickname,
|
||||
realname: realnameF.value.trim(),
|
||||
password: passwordF.value,
|
||||
groups: groupsF.value.trim(),
|
||||
privilege: privilegeF.value
|
||||
};
|
||||
request(req).then(resp => {
|
||||
list_all_users();
|
||||
});
|
||||
hide("user_editor_window");
|
||||
}
|
||||
};
|
||||
|
||||
show("user_editor_window");
|
||||
}
|
||||
|
||||
function get_selected_users() {
|
||||
let selected_chkboxes = document.getElementsByName("user_chkbox");
|
||||
let selected_users = [];
|
||||
selected_chkboxes.forEach((chkbox) => {
|
||||
if (chkbox.checked) {
|
||||
selected_users.push(chkbox.user);
|
||||
}
|
||||
});
|
||||
return selected_users;
|
||||
}
|
||||
|
||||
function delete_users() {
|
||||
let users = get_selected_users();
|
||||
if (users.length === 0) {
|
||||
return;
|
||||
}
|
||||
let user_nicknames = [];
|
||||
users.forEach((u) => {
|
||||
user_nicknames.push(u["nickname"]);
|
||||
});
|
||||
let msg = "Biztosan törölni kívánja a következő felhasználó(ka)t?\n\n" + user_nicknames.join(", ") + "\n\n"
|
||||
+ "A törlés nem vonható vissza!";
|
||||
if (confirm(msg)) {
|
||||
let req = {action: "delete_users", users: user_nicknames.join(",")};
|
||||
request(req).then(resp => {
|
||||
list_all_users();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hint_all_groups(target_element_id) {
|
||||
const hintbox_insert_fn = (record) => {
|
||||
let targetF = document.getElementById(target_element_id);
|
||||
let groups = explode_sanitize_string_list(targetF.value);
|
||||
groups.pop();
|
||||
groups.push(record);
|
||||
targetF.value = groups.join(", ");
|
||||
close_hintbox(true);
|
||||
}
|
||||
|
||||
let req = {
|
||||
action: "search_groups",
|
||||
}
|
||||
|
||||
open_hintbox_at(target_element_id, req, print_group_name, hintbox_insert_fn);
|
||||
}
|
||||
24
login.php
Normal file
24
login.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
if (get_autologin_state()) {
|
||||
header("Location: " . MAIN_URL);
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz :: Bejelentkezés</title>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" placeholder="Felhasználónév" id="nickname">
|
||||
<input type="password" placeholder="Jelszó" id="password">
|
||||
<input type="button" value="Belépés" onclick="login()">
|
||||
</body>
|
||||
</html>
|
||||
50
main.php
Normal file
50
main.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once "globals.php";
|
||||
require_once "usermgr.php";
|
||||
|
||||
require_once "autologin.php";
|
||||
|
||||
// if not logged in, then redirect to login page
|
||||
$logged_in = get_autologin_state();
|
||||
if (!$logged_in) {
|
||||
header("Location: " . LOGIN_URL);
|
||||
}
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
$privilege = $user_data["privilege"];
|
||||
|
||||
?>
|
||||
|
||||
<?php if ($logged_in) { ?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<section id="screen_panel">
|
||||
<section id="content_pane">
|
||||
<iframe id="content_frame" src="default_frame.php"></iframe>
|
||||
</section>
|
||||
<section id="info_pane">
|
||||
<section id="user_info" class="info-pane-element"><?= $user_data["nickname"]; ?></section>
|
||||
<?php if ($privilege != PRIVILEGE_PLAYER) { ?>
|
||||
<section id="action_panel" class="info-pane-element">
|
||||
<?php if (($privilege === PRIVILEGE_CREATOR) || ($privilege === PRIVILEGE_QUIZMASTER)) { ?>
|
||||
<input type="button" value="Tartalmak kezelése" onclick="open_in_content_frame('game_manager_frame.php')">
|
||||
<?php } ?>
|
||||
<?php if ($privilege === PRIVILEGE_QUIZMASTER) { ?>
|
||||
<input type="button" value="Felhasználók kezelése" onclick="open_in_content_frame('user_manager_frame.php')">
|
||||
<input type="button" value="Csoportok kezelése" onclick="open_in_content_frame('group_manager_frame.php')">
|
||||
<?php } ?>
|
||||
</section>
|
||||
<?php } ?>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
<?php } ?>
|
||||
197
style/spreadquiz.css
Normal file
197
style/spreadquiz.css
Normal file
@ -0,0 +1,197 @@
|
||||
*[shown="false"] {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
*[shown="true"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ----------------- */
|
||||
|
||||
section#screen_panel {
|
||||
display: block;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
section#content_pane, section#info_pane {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
section#content_pane {
|
||||
left: 0;
|
||||
width: 80vw;
|
||||
background-color: lightcyan;
|
||||
}
|
||||
|
||||
section#info_pane {
|
||||
right: 0;
|
||||
width: 20vw;
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
.info-pane-element {
|
||||
display: block;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
section#user_info {
|
||||
position: relative;
|
||||
top: 0;
|
||||
background-color: aquamarine;
|
||||
font-size: 18pt;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section#action_panel {
|
||||
background-color: antiquewhite;
|
||||
}
|
||||
|
||||
iframe#content_frame {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0 transparent;
|
||||
}
|
||||
|
||||
section#table_section {
|
||||
position: sticky;
|
||||
height: calc(100vh - 4em);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
table.management {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.management thead th {
|
||||
padding: 0.5em 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: darkgray;
|
||||
}
|
||||
|
||||
table.management tbody td {
|
||||
border: 1pt lightgrey solid;
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.management tbody tr[highlight="true"] {
|
||||
background-color: antiquewhite;
|
||||
}
|
||||
|
||||
table.management tbody tr[highlight="false"] td input {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
section#user_manager_action_bar {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
section.window {
|
||||
position: fixed;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(128, 128, 128, 0.7);
|
||||
padding: 4em;
|
||||
}
|
||||
|
||||
section.window-inner {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
border: 2pt solid darkslategray;
|
||||
background-color: whitesmoke;
|
||||
padding: 1em;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
td.checkbox {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.hintbox-window {
|
||||
position: fixed;
|
||||
display: block;
|
||||
width: 15em;
|
||||
height: 8em;
|
||||
}
|
||||
|
||||
/* ----- */
|
||||
|
||||
section#game_list_panel {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
section.group-box {
|
||||
margin: 1em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
span.group-box-caption {
|
||||
display: block;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 1em;
|
||||
background-color: chartreuse;
|
||||
}
|
||||
|
||||
section.group-box-inner {
|
||||
display: inline-table;
|
||||
padding: 1em;
|
||||
collapse: 1em;
|
||||
border-spacing: 1em;
|
||||
}
|
||||
|
||||
section.game-box {
|
||||
display: table-cell;
|
||||
width: 8em;
|
||||
height: 8em;
|
||||
border: 2pt dashed gray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
section#test_area {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
section.challenge {
|
||||
|
||||
}
|
||||
|
||||
span.question {
|
||||
|
||||
}
|
||||
|
||||
section.answer-container {
|
||||
|
||||
}
|
||||
|
||||
section.answer {
|
||||
|
||||
}
|
||||
31
testground.php
Normal file
31
testground.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state() || !isset($_REQUEST["testid"])) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$testid = $_REQUEST["testid"];
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/o.js"></script>
|
||||
<script src="js/testground.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css">
|
||||
</head>
|
||||
<body>
|
||||
<section id="test_display">
|
||||
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
93
testmgr.php
Normal file
93
testmgr.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "common_func.php";
|
||||
|
||||
require_once "gamemgr.php";
|
||||
require_once "usermgr.php";
|
||||
|
||||
$testdb = new \SleekDB\Store(TESTDB, DATADIR, ["timeout" => false]);
|
||||
|
||||
const TEST_ONGOING = "ongoing";
|
||||
const TEST_CONCLUDED = "concluded";
|
||||
|
||||
function create_or_continue_test(string $gameid, string $nickname): string
|
||||
{
|
||||
global $testdb;
|
||||
|
||||
// get game and user data
|
||||
$game_data = get_game($gameid);
|
||||
$user_data = get_user($nickname);
|
||||
if ((count($game_data) === 0) || (count($user_data) === 0)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// check if this user has permission to take this test
|
||||
// if the intersection of user's groups and game's assigned groups is zero, then the user has no access to this game
|
||||
if (count(array_intersect($game_data["groups"], $user_data["groups"])) === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// check if the user had taken this test before
|
||||
$fetch_criteria = [["gameid", "=", $gameid], "AND", ["nickname", "=", $nickname]];
|
||||
$previous_tests = $testdb->findBy($fetch_criteria);
|
||||
if (count($previous_tests) > 0) { // if there are previous attempts, then...
|
||||
// update timed tests to see if they had expired
|
||||
update_timed_tests($previous_tests);
|
||||
|
||||
// re-fetch tests, look only for ongoing
|
||||
$ongoing_tests = $testdb->findBy([$fetch_criteria, "AND", ["state", "=", TEST_ONGOING]]);
|
||||
if (count($ongoing_tests) !== 0) { // if there's an ongoing test
|
||||
return $ongoing_tests[0]["_id"];
|
||||
} else { // there's no ongoing test
|
||||
if ($game_data["properties"]["repeatable"]) { // test is repeatable...
|
||||
return create_test($game_data, $user_data);
|
||||
} else { // test is non-repeatable, cannot be attempted more times
|
||||
return "";
|
||||
}
|
||||
}
|
||||
} else { // there were no previous attempts
|
||||
return create_test($game_data, $user_data);
|
||||
}
|
||||
}
|
||||
|
||||
function create_test(array $game_data, array $user_data) : string
|
||||
{
|
||||
$gameid = $game_data["_id"];
|
||||
|
||||
// fill basic data
|
||||
$game_data = [
|
||||
"gameid" => $gameid,
|
||||
"nickname" => $user_data["nickname"],
|
||||
];
|
||||
|
||||
// fill challenges
|
||||
$challenges = load_challenges($gameid);
|
||||
|
||||
// shuffle answers
|
||||
foreach ($challenges as &$ch) {
|
||||
shuffle($ch["answers"]);
|
||||
}
|
||||
|
||||
$game_data["challenges"] = $challenges;
|
||||
|
||||
// involve properties
|
||||
}
|
||||
|
||||
function update_timed_tests(array $test_data_array)
|
||||
{
|
||||
$now = time();
|
||||
foreach ($test_data_array as $test_data) {
|
||||
// look for unprocessed expired tests
|
||||
if (($test_data["state"] === TEST_ONGOING) && ($test_data["end_limit_time"] < $now)) {
|
||||
$test_data["state"] = TEST_CONCLUDED;
|
||||
update_test($test_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update_test(array $test_data)
|
||||
{
|
||||
global $testdb;
|
||||
$testdb->update($test_data);
|
||||
}
|
||||
104
user_manager_frame.php
Normal file
104
user_manager_frame.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state() || ($user_data["privilege"] !== PRIVILEGE_QUIZMASTER)) {
|
||||
exit(); // this page is only available for quizmasters
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SpreadQuiz</title>
|
||||
<script src="js/req.js"></script>
|
||||
<script src="js/spreadquiz.js"></script>
|
||||
<script src="js/o.js"></script>
|
||||
<script src="js/groupmgr.js"></script>
|
||||
<script src="js/hintbox.js"></script>
|
||||
<script src="js/usermgr.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<section id="table_section">
|
||||
<table id="user_manager_table" class="management">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Felhasználónév</th>
|
||||
<th>Név</th>
|
||||
<th>Csoportok</th>
|
||||
<th>Jogosultság</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="user_manager_table_body">
|
||||
<!-- --><?php
|
||||
// function create_cell(string $content, string $class = "")
|
||||
// {
|
||||
// if (trim($content) === "") {
|
||||
// $content = "<i>(üres)</i>";
|
||||
// }
|
||||
// return "<td class='$class'>$content</td>";
|
||||
// }
|
||||
//
|
||||
// function create_table_row(array $keys, array $record)
|
||||
// {
|
||||
// $tr = "<tr>";
|
||||
// foreach ($keys as $k) {
|
||||
// $tr .= "<td>${record[$k]}</td>";
|
||||
// }
|
||||
// $tr .= "</tr>";
|
||||
// return $tr;
|
||||
// }
|
||||
//
|
||||
// $users = get_all_users();
|
||||
// foreach ($users as $u) {
|
||||
// $nickname = $u["nickname"];
|
||||
// $tr = "<tr id='row_$nickname'>";
|
||||
// $tr .= create_cell("<input type='checkbox' id='user_chk_$nickname' onchange='highlight_row(\"$nickname\")'>", "checkbox");
|
||||
// $tr .= create_cell($u["nickname"]);
|
||||
// $tr .= create_cell($u["realname"]);
|
||||
// $tr .= create_cell(implode(",", $u["groups"]));
|
||||
// $tr .= create_cell($u["privilege"]);
|
||||
// $tr .= "</tr>";
|
||||
// echo $tr;
|
||||
// }
|
||||
// ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<input type="button" value="Új felhasználó" onclick="create_new_user()">
|
||||
<input type="button" value="Felhasználó(k) törlése" onclick="delete_users()">
|
||||
<input type="button" value="Felhasználók importálása CSV-ből">
|
||||
</section>
|
||||
<section class="window" shown="false" id="user_editor_window">
|
||||
<section class="window-inner">
|
||||
<section style="text-align: right">
|
||||
<span>Felhasználónév: <input type="text" id="nickname"></span><br>
|
||||
<span>Teljes név: <input type="text" id="realname"></span><br>
|
||||
<span>Jelszó: <input type="text" id="password" readonly></span><br>
|
||||
<span>Csoportok: <input type="text" id="groups" oninput="hint_all_groups('groups')" onblur="close_hintbox()"></span><br>
|
||||
<span>Jogosultság:
|
||||
<select id="privilege">
|
||||
<option value="player">játékos</option>
|
||||
<option value="creator">szerkesztő</option>
|
||||
<option value="admin">kvízmester</option>
|
||||
</select>
|
||||
</span>
|
||||
</section>
|
||||
<span><input type="button" value="" id="user_editor_submit_btn"><input type="button" value="Mégse" onclick="hide('user_editor_window')"></span>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
list_all_users();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
113
usermgr.php
Normal file
113
usermgr.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "common_func.php";
|
||||
|
||||
$userdb = new \SleekDB\Store(USERDB, DATADIR, ["timeout" => false]);
|
||||
|
||||
const PRIVILEGE_PLAYER = "player";
|
||||
const PRIVILEGE_CREATOR = "creator";
|
||||
const PRIVILEGE_QUIZMASTER = "admin"; // TODO: refactor!
|
||||
|
||||
function add_user(string $nickname, string $password, string $realname, array $groups = [], string $privilege = PRIVILEGE_PLAYER): bool
|
||||
{
|
||||
global $userdb;
|
||||
if (count(get_user($nickname)) != 0) { // user exists
|
||||
return false;
|
||||
}
|
||||
|
||||
$user_data = [
|
||||
"nickname" => $nickname,
|
||||
"password" => password_hash($password, PASSWORD_DEFAULT),
|
||||
"realname" => $realname,
|
||||
"groups" => $groups,
|
||||
"privilege" => $privilege
|
||||
];
|
||||
$userdb->insert($user_data);
|
||||
return true; // user registration successful
|
||||
}
|
||||
|
||||
function delete_user(string $nickname)
|
||||
{
|
||||
global $userdb;
|
||||
if ($nickname == QUIZMASTER_NICKNAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user_data = get_user($nickname);
|
||||
if (count($user_data) !== 0) {
|
||||
foreach ($user_data["groups"] as $groupid) {
|
||||
change_group_user_assignments($groupid, null, $nickname);
|
||||
}
|
||||
$userdb->deleteBy(["nickname", "=", $nickname]);
|
||||
}
|
||||
}
|
||||
|
||||
function get_user(string $nickname): array
|
||||
{
|
||||
global $userdb;
|
||||
$user_data_array = $userdb->findBy(["nickname", "=", $nickname]);
|
||||
return count($user_data_array) != 0 ? $user_data_array[0] : [];
|
||||
}
|
||||
|
||||
function update_user(array $user_data)
|
||||
{
|
||||
global $userdb;
|
||||
return $userdb->update($user_data);
|
||||
}
|
||||
|
||||
function change_password(string $nickname, string $old, string $new): bool
|
||||
{
|
||||
$user_data = get_user($nickname);
|
||||
if (count($user_data) != 0) {
|
||||
if (password_verify($old, $user_data["password"])) {
|
||||
$user_data["password"] = password_hash($new, PASSWORD_DEFAULT);
|
||||
update_user($user_data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function change_user_group_assignments(string $nickname, $groupname_add, $groupname_remove)
|
||||
{
|
||||
$user_data = get_user($nickname);
|
||||
if (count($user_data) != 0) {
|
||||
alter_array_contents($user_data["groups"], $groupname_add, $groupname_remove);
|
||||
update_user($user_data); // update user
|
||||
}
|
||||
}
|
||||
|
||||
function change_privilege_level(string $nickname, string $privilege)
|
||||
{
|
||||
$user_data = get_user($nickname);
|
||||
if (count($user_data) != 0) {
|
||||
$user_data["privilege"] = $privilege;
|
||||
update_user($user_data);
|
||||
}
|
||||
}
|
||||
|
||||
function check_user_credentials(string $nickname, string $password): bool
|
||||
{
|
||||
$user_data = get_user($nickname);
|
||||
if (count($user_data) != 0) {
|
||||
return password_verify($password, $user_data["password"]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function get_all_users(): array
|
||||
{
|
||||
global $userdb;
|
||||
return $userdb->findAll();
|
||||
}
|
||||
|
||||
function get_all_nicknames() : array {
|
||||
$nicknames = [];
|
||||
$user_data_array = get_all_users();
|
||||
foreach ($user_data_array as $user_data) {
|
||||
$nicknames[] = $user_data["nickname"];
|
||||
}
|
||||
return $nicknames;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user