871 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			871 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
require_once "check_maintenance.php";
 | 
						|
 | 
						|
//ini_set('display_startup_errors', '1');
 | 
						|
 | 
						|
require_once "globals.php";
 | 
						|
 | 
						|
require_once "privilege_levels.php";
 | 
						|
 | 
						|
if (!file_exists(INSTALL_INDICATOR)) {
 | 
						|
    exit();
 | 
						|
}
 | 
						|
 | 
						|
if (!isset($_REQUEST["action"])) {
 | 
						|
    exit();
 | 
						|
}
 | 
						|
 | 
						|
require_once "common_func.php";
 | 
						|
 | 
						|
require_once "class/ReqHandler.php";
 | 
						|
 | 
						|
require_once "class/UserMgr.php";
 | 
						|
 | 
						|
require_once "class/GroupMgr.php";
 | 
						|
 | 
						|
require_once "class/GameMgr.php";
 | 
						|
 | 
						|
require_once "class/TestMgr.php";
 | 
						|
 | 
						|
require_once "class/ReportBuilder.php";
 | 
						|
 | 
						|
// ------------------------
 | 
						|
 | 
						|
$userMgr = new UserMgr();
 | 
						|
 | 
						|
// ------------------------
 | 
						|
 | 
						|
$result = "";
 | 
						|
$success = false;
 | 
						|
 | 
						|
// user-related variables
 | 
						|
$user = null;
 | 
						|
$nickname = "";
 | 
						|
$privilege = PRIVILEGE_NONE;
 | 
						|
 | 
						|
// --------
 | 
						|
 | 
						|
// create request handler
 | 
						|
$rh = new ReqHandler();
 | 
						|
 | 
						|
// action dump callback
 | 
						|
function dump_actions(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    return $rh->dump_actions();
 | 
						|
}
 | 
						|
 | 
						|
$rh->add("dump_actions", [], PRIVILEGE_QUIZMASTER, "dump_actions", RESP_PLAIN, "Dump all registered actions.");
 | 
						|
 | 
						|
/* ------------ ACTIONS AVAILABLE WITHOUT LOGGING IN ---------- */
 | 
						|
 | 
						|
// login the user
 | 
						|
function login(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $userMgr;
 | 
						|
    global $user;
 | 
						|
 | 
						|
    $nickname = $params["nickname"];
 | 
						|
    $password = $params["password"];
 | 
						|
 | 
						|
    $user = $userMgr->getUser($nickname);
 | 
						|
    if (($user !== null) && $user->checkPassword($password)) {
 | 
						|
        session_start();
 | 
						|
        $_SESSION["nickname"] = $nickname;
 | 
						|
        $result = "OK";
 | 
						|
    } else {
 | 
						|
        $result = "FAIL";
 | 
						|
    }
 | 
						|
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
$rh->add("login", ["nickname", "password"], PRIVILEGE_NONE, "login", RESP_PLAIN, "Log in the user.");
 | 
						|
 | 
						|
// 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"])) || ($userMgr->getUser($_SESSION["nickname"]) === null)) {
 | 
						|
    goto process_and_print;
 | 
						|
}
 | 
						|
 | 
						|
$user = $userMgr->getUser($_SESSION["nickname"]);
 | 
						|
$nickname = $user->getNickname();
 | 
						|
$privilege = $user->getPrivilege();
 | 
						|
$groupMgr = new GroupMgr();
 | 
						|
$gameMgr = new GameMgr();
 | 
						|
$testMgr = new TestMgr();
 | 
						|
 | 
						|
/* ---------- ACTIONS REQUIRING BEING LOGGED IN ------------ */
 | 
						|
 | 
						|
function logout(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    $_SESSION = []; // clean up session data
 | 
						|
    setcookie(SESSION_NAME, "", -1); // invalidate cookie
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
function get_user_info(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    $user_data_filtered = $user->toArray();
 | 
						|
    unset($user_data_filtered["password"]);
 | 
						|
    return $user_data_filtered;
 | 
						|
}
 | 
						|
 | 
						|
function get_available_games(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $groupMgr;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $games_by_groups = [];
 | 
						|
    $groupids = $groupMgr->getUserGroupIDs($user->getNickname());
 | 
						|
    foreach ($groupids as $groupid) {
 | 
						|
        $group = $groupMgr->getGroup($groupid);
 | 
						|
        $game_collection = [
 | 
						|
            "groupname" => $group->getName(),
 | 
						|
            "description" => $group->getDescription(),
 | 
						|
            "games" => []
 | 
						|
        ];
 | 
						|
        $gameids = $group->getGames();
 | 
						|
        foreach ($gameids as $gameid) {
 | 
						|
            $game = $gameMgr->getGame($gameid);
 | 
						|
            if ($game->isGameFileIsPresent()) {
 | 
						|
                $a = $game->toArray(Game::OMIT_ADVANCED_FIELDS);
 | 
						|
                $game_collection["games"][] = $a;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $games_by_groups[] = $game_collection;
 | 
						|
    }
 | 
						|
 | 
						|
    return $games_by_groups;
 | 
						|
}
 | 
						|
 | 
						|
function start_or_continue_test(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $gameMgr;
 | 
						|
    global $testMgr;
 | 
						|
    global $groupMgr;
 | 
						|
 | 
						|
    if ($groupMgr->doesUserAccessGame($params["gameid"], $user->getNickname())) {
 | 
						|
        $game = $gameMgr->getGame($params["gameid"]);
 | 
						|
        $test = $testMgr->addOrContinueTest($game, $user);
 | 
						|
        return $test->getId();
 | 
						|
    } else {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function get_results_overview(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $testMgr;
 | 
						|
 | 
						|
    $concluded_tests = $testMgr->getConcludedTests($params["gameid"], $user->getNickname());
 | 
						|
    $overviews = [];
 | 
						|
    foreach ($concluded_tests as $ct) {
 | 
						|
        $overview = [
 | 
						|
            "testid" => $ct->getId(),
 | 
						|
            "start_time" => $ct->getStartTime(),
 | 
						|
            "end_time" => $ct->getEndTime(),
 | 
						|
            ...($ct->getSummary()->toArray())
 | 
						|
        ];
 | 
						|
        $overviews[] = $overview;
 | 
						|
    }
 | 
						|
    return $overviews;
 | 
						|
}
 | 
						|
 | 
						|
function change_password(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    $oldpass = $params["oldpass"];
 | 
						|
    $newpass = $params["newpass"];
 | 
						|
 | 
						|
    global $user;
 | 
						|
    $success = $user->changePassword($newpass, $oldpass);
 | 
						|
 | 
						|
    return $success ? "OK" : "FAIL";
 | 
						|
}
 | 
						|
 | 
						|
$rh->add("logout", [], PRIVILEGE_PLAYER, "logout", RESP_PLAIN, "Log out the user.");
 | 
						|
$rh->add("change_password", ["oldpass", "newpass"], PRIVILEGE_PLAYER, "change_password", RESP_PLAIN, "Change users password.");
 | 
						|
$rh->add("get_user_info", [], PRIVILEGE_PLAYER, "get_user_info", RESP_JSON, "Get user information.");
 | 
						|
$rh->add("get_available_games", [], PRIVILEGE_PLAYER, "get_available_games", RESP_JSON, "Get available games to the player.");
 | 
						|
$rh->add("start_or_continue_test", ["gameid"], PRIVILEGE_PLAYER, "start_or_continue_test", RESP_PLAIN, "Start new or continue an ongoing test.");
 | 
						|
$rh->add("get_results_overview", ["gameid"], PRIVILEGE_PLAYER, "get_results_overview", RESP_JSON, "Get a quick overview of player's results of a single game.");
 | 
						|
 | 
						|
 | 
						|
// test-related queries
 | 
						|
function does_test_belong_to_user(Test &$test): bool
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    return $test->getNickname() === $user->getNickname();
 | 
						|
}
 | 
						|
 | 
						|
function is_test_access_approved(Test &$test): bool
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $game = $gameMgr->getGame($test->getGameId());
 | 
						|
    return does_test_belong_to_user($test) || $game->isUserContributorOrOwner($user->getNickname()) || $user->hasQuizmasterPrivilege();
 | 
						|
}
 | 
						|
 | 
						|
function access_test_data(string $testid): Test|null
 | 
						|
{
 | 
						|
    global $testMgr;
 | 
						|
 | 
						|
    $testid = trim($testid);
 | 
						|
    $test = ($testid !== "") ? $testMgr->getTest($testid) : null;
 | 
						|
 | 
						|
    // fetch test data
 | 
						|
    if ($test === null) {
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    // check if access is approved to the specific test
 | 
						|
    if (!is_test_access_approved($test)) {
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    // update the test if timed
 | 
						|
    // update_timed_tests([$test_data]); FIXME!!!
 | 
						|
 | 
						|
    return $test;
 | 
						|
}
 | 
						|
 | 
						|
function exclude_correct_answers(array &$challenges): void
 | 
						|
{
 | 
						|
    foreach ($challenges as &$challenge) {
 | 
						|
        $challenge["correct_answer"] = -1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function get_player_test(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    $result = [];
 | 
						|
    $test = access_test_data($params["testid"]);
 | 
						|
 | 
						|
    if ($test !== null) {
 | 
						|
        $test_data_with_current_time = $test->toArray();
 | 
						|
        if ($test->isOngoing()) {
 | 
						|
            exclude_correct_answers($test_data_with_current_time["challenges"]);
 | 
						|
        }
 | 
						|
        $test_data_with_current_time["current_time"] = time();
 | 
						|
        $result = $test_data_with_current_time;
 | 
						|
    }
 | 
						|
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
function save_player_answer(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    $test = access_test_data($params["testid"]);
 | 
						|
    if ($test !== null) {
 | 
						|
        $test->saveAnswer($params["challenge_index"], $params["answer_index"]);
 | 
						|
        return "OK";
 | 
						|
    } else {
 | 
						|
        return "FAIL";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function submit_test(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    $test = access_test_data($params["testid"]);
 | 
						|
    if ($test !== null) {
 | 
						|
        $test->concludeTest();
 | 
						|
        return "OK";
 | 
						|
    } else {
 | 
						|
        return "FAIL";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function patch_through_image(string $gameid, string $img_url)
 | 
						|
{
 | 
						|
    global $gameMgr;
 | 
						|
    $game_dir = $gameMgr->getGame($gameid)->getGameDir();
 | 
						|
    $image_fetch_url = $game_dir . DIRECTORY_SEPARATOR . $img_url;
 | 
						|
 | 
						|
    $img_fp = fopen($image_fetch_url, "r");
 | 
						|
    if ($img_fp === false) {
 | 
						|
        $img_fp = fopen(MISSING_IMAGE_PLACEHOLDER, "r");
 | 
						|
    }
 | 
						|
    fpassthru($img_fp);
 | 
						|
    fclose($img_fp);
 | 
						|
}
 | 
						|
 | 
						|
function get_image(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    $img_url = trim($params["img_url"] ?? "");
 | 
						|
    if ($img_url !== "") {
 | 
						|
        $gameid = $params["gameid"];
 | 
						|
        patch_through_image($gameid, $img_url);
 | 
						|
    }
 | 
						|
 | 
						|
    return "";
 | 
						|
}
 | 
						|
 | 
						|
$rh->add("get_test", ["testid"], PRIVILEGE_PLAYER, "get_player_test", RESP_JSON, "Get player's test by ID.");
 | 
						|
$rh->add("save_answer", ["testid", "challenge_index", "answer_index"], PRIVILEGE_PLAYER, "save_player_answer", RESP_PLAIN, "Store player's answer.");
 | 
						|
$rh->add("submit_test", ["testid"], PRIVILEGE_PLAYER, "submit_test", RESP_PLAIN, "Finish player's test.");
 | 
						|
$rh->add("get_image", ["gameid", "img_url"], PRIVILEGE_PLAYER, "get_image", RESP_NONE, "Get image per game.");
 | 
						|
 | 
						|
// execute query if user has the player privilege level
 | 
						|
if ($privilege === PRIVILEGE_PLAYER) {
 | 
						|
    goto process_and_print;
 | 
						|
}
 | 
						|
 | 
						|
/* --------------- CREATOR LEVEL ACTIONS ---------------- */
 | 
						|
 | 
						|
$requester_nickname = $user->hasQuizmasterPrivilege() ? "*" : $nickname; // "*" means every game
 | 
						|
 | 
						|
function create_update_game(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $nickname;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $update = $params[ReqHandler::ACTION_KEY] === "update_game";
 | 
						|
    $data = json_decode($params["data"], true) ?? [];
 | 
						|
    if (($data === []) || (trim($data["name"] ?? "") === "")) { // no further processing
 | 
						|
        return []; // ~exit...
 | 
						|
    }
 | 
						|
 | 
						|
    // fetch fields
 | 
						|
    $gameid = $data["_id"];
 | 
						|
    $name = $data["name"];
 | 
						|
    $description = $data["description"];
 | 
						|
    $contributors = explode_list($data["contributors"] ?? "");
 | 
						|
    $owner = $update ? trim($data["owner"] ?? $nickname) : $nickname;
 | 
						|
    $properties = $data["properties"] ?? [];
 | 
						|
 | 
						|
    // result
 | 
						|
    $result = [];
 | 
						|
 | 
						|
    // create or update
 | 
						|
    if (!$update) { // CREATE
 | 
						|
        $gameMgr->addGame($name, $owner, $description);
 | 
						|
    } else { // UPDATE
 | 
						|
        $game = $gameMgr->getGame($gameid); // fetch game
 | 
						|
 | 
						|
        if (($game !== null) && $game->isUserContributorOrOwner($nickname) || $user->hasQuizmasterPrivilege()) {
 | 
						|
            // disable autostoring
 | 
						|
            $game->disableAutoStoring();
 | 
						|
 | 
						|
            // update game header data
 | 
						|
            $game->setName($name);
 | 
						|
            $game->setDescription($description);
 | 
						|
 | 
						|
            if ($game->isUserOwner($nickname) || $user->hasQuizmasterPrivilege()) {
 | 
						|
                $game->setOwner($owner);
 | 
						|
            }
 | 
						|
            $game->setContributors($contributors);
 | 
						|
            $game->setProperties($properties);
 | 
						|
 | 
						|
            // process game public flag: a game might be only public if not being time-constrained and is allowed to be taken multiple times
 | 
						|
            $public = ($properties["time_limit"] !== 0) || (!$properties["repeatable"]) ? false : $data["public"];
 | 
						|
            $game->setPublic($public);
 | 
						|
 | 
						|
            // store modifications
 | 
						|
            $game->storeMods();
 | 
						|
 | 
						|
            // re-enable auto-storing
 | 
						|
            $game->enableAutoStoring();
 | 
						|
 | 
						|
            // update game file if supplied
 | 
						|
            if (isset($_FILES["game_file"])) {
 | 
						|
                // decide weather it's a package or a plain table
 | 
						|
                $file = $_FILES["game_file"];
 | 
						|
                $challenge_import_status = [];
 | 
						|
 | 
						|
                // determine MIME type
 | 
						|
                $file_type = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));
 | 
						|
 | 
						|
                if ($file_type === "zip") { // a package was uploaded
 | 
						|
                    $zip = new ZipArchive;
 | 
						|
                    if ($zip->open($file["tmp_name"])) {
 | 
						|
 | 
						|
                        $game_dir = $game->getGameDir(); // get game directory
 | 
						|
                        //$game_files = glob($game_dir); // get list of existing game files
 | 
						|
                        // remove former files recursively
 | 
						|
                        $dir_iter = new RecursiveDirectoryIterator($game_dir, FilesystemIterator::SKIP_DOTS);
 | 
						|
                        foreach ($dir_iter as $dir_item) {
 | 
						|
                            $item_path = $dir_item->getPathname();
 | 
						|
                            $dir_item->isDir() ? rmdir($item_path) : unlink($item_path);
 | 
						|
                        }
 | 
						|
 | 
						|
                        // extract package contents to the game directory
 | 
						|
                        $zip->extractTo($game_dir . DIRECTORY_SEPARATOR);
 | 
						|
 | 
						|
                        // search for the CSV table file
 | 
						|
                        $csv_files = glob($game_dir . DIRECTORY_SEPARATOR . "*.csv") ?? [];
 | 
						|
                        if (count($csv_files) > 0) {
 | 
						|
                            $challenge_import_status = $game->importChallengesFromCSV($csv_files[0]);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } else if ($file_type === "csv") { // a plain table was uploaded
 | 
						|
                    $challenge_import_status = $game->importChallengesFromCSV($file["tmp_name"]);
 | 
						|
                }
 | 
						|
                $result = $challenge_import_status;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
function get_all_game_headers(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $requester_nickname;
 | 
						|
    global $gameMgr;
 | 
						|
    $games = $gameMgr->getAllGameDataByContributor($requester_nickname);
 | 
						|
    $a = [];
 | 
						|
    foreach ($games as &$game) {
 | 
						|
        $a[] = $game->toArray();
 | 
						|
    }
 | 
						|
    return $a;
 | 
						|
}
 | 
						|
 | 
						|
function get_challenges(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $gameid = $params["gameid"];
 | 
						|
    $game = $gameMgr->getGame($gameid);
 | 
						|
    $result = "";
 | 
						|
    if (($game !== null) && ($user->hasQuizmasterPrivilege() || ($game->isUserContributorOrOwner($user->getNickname())))) {
 | 
						|
        $result = file_get_contents($game->getGameFile());
 | 
						|
    }
 | 
						|
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
function delete_games(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $gameids = explode_list(trim($params["ids"]));
 | 
						|
    foreach ($gameids as $gameid) {
 | 
						|
        $game = $gameMgr->getGame($gameid);
 | 
						|
        if (($game !== null) && ($game->isUserOwner($user->getNickname()))) { // only the owner may delete a game
 | 
						|
            $gameMgr->deleteGame($gameid);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
function export_game_file_csv(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $gameMgr;
 | 
						|
 | 
						|
    $gameid = trim($params["gameid"]);
 | 
						|
    $game = $gameMgr->getGame($gameid);
 | 
						|
 | 
						|
    if (($game !== null) && ($game->isUserContributorOrOwner($user->getNickname()) || $user->hasQuizmasterPrivilege())) {
 | 
						|
        $f = tmpfile();
 | 
						|
        header("Content-Type: text/csv");
 | 
						|
        header("Content-Disposition: attachment; filename=\"challenges_$gameid.csv\"\r\n");
 | 
						|
        $game->exportChallengesToCSV($f);
 | 
						|
        fseek($f, 0);
 | 
						|
        fpassthru($f);
 | 
						|
    }
 | 
						|
    return "";
 | 
						|
}
 | 
						|
 | 
						|
function get_results_by_gameid(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $gameMgr;
 | 
						|
    global $testMgr;
 | 
						|
    global $user;
 | 
						|
 | 
						|
    $gameid = trim($params["gameid"]);
 | 
						|
    $filter = trim($params["filter"] ?? "");
 | 
						|
    $ordering = trim($params["orderby"] ?? "");
 | 
						|
    $groups = explode_list(trim($params["groups"] ?? ""));
 | 
						|
    $best_only = trim($params["best_only"] ?? "false") === "true";
 | 
						|
 | 
						|
    $game = $gameMgr->getGame($gameid);
 | 
						|
 | 
						|
    $result = [];
 | 
						|
    if (($game !== null) && ($game->isUserContributorOrOwner($user->getNickname()) || $user->hasQuizmasterPrivilege())) {
 | 
						|
 | 
						|
        // creating filter criteria on groups
 | 
						|
        $group_filter = [];
 | 
						|
        if ($groups !== []) {
 | 
						|
            global $groupMgr;
 | 
						|
            $n = 0;
 | 
						|
            foreach ($groups as $groupname) {
 | 
						|
                $group = $groupMgr->getGroupByUniqueName($groupname);
 | 
						|
                if ($group !== null) {
 | 
						|
                    if ($n > 0) { // place OR between each group criterion
 | 
						|
                        $group_filter[] = "OR";
 | 
						|
                    }
 | 
						|
 | 
						|
                    $nicknames = $group->getMembers();
 | 
						|
                    $group_filter[] = ["nickname", "IN", $nicknames];
 | 
						|
 | 
						|
                    $n++;
 | 
						|
                } else { // a group not found means a faulty query
 | 
						|
                    return [];
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // execute filtering
 | 
						|
        $game_results = null;
 | 
						|
        if ($group_filter !== []) {
 | 
						|
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $best_only, $group_filter);
 | 
						|
        } else {
 | 
						|
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $best_only);
 | 
						|
        }
 | 
						|
        $result = $game_results;
 | 
						|
    }
 | 
						|
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
function generate_report_by_groups(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $gameMgr;
 | 
						|
    global $user;
 | 
						|
 | 
						|
    $gameid = trim($params["gameid"]);
 | 
						|
    $filter = trim($params["filter"] ?? "");
 | 
						|
    $groups = explode_list(trim($params["groups"]));
 | 
						|
    $best_only = trim($params["best_only"] ?? "false") === "true";
 | 
						|
    $outtype = trim($params["outtype"] ?? "pdf");
 | 
						|
 | 
						|
    // only PDF and TEX are valid
 | 
						|
    if (!in_array($outtype, ["pdf", "tex"])) {
 | 
						|
        return "FAIL";
 | 
						|
    }
 | 
						|
 | 
						|
    $game = $gameMgr->getGame($gameid);
 | 
						|
 | 
						|
    // verify game and access
 | 
						|
    if (($game === null) || ((!$game->isUserContributorOrOwner($user->getNickname()) && !$user->hasQuizmasterPrivilege()))) {
 | 
						|
        return "FAIL";
 | 
						|
    }
 | 
						|
 | 
						|
    // create destination directory and copy TeX frame file
 | 
						|
    $repId = uniqid($outtype);
 | 
						|
    $buildDir = REPORT_PDF_OUTPUT_DIR . DIRECTORY_SEPARATOR . $repId;
 | 
						|
    mkdir($buildDir);
 | 
						|
 | 
						|
    $iter = new RecursiveDirectoryIterator(REPORT_TEMPLATE_DIR, FilesystemIterator::SKIP_DOTS);
 | 
						|
    foreach ($iter as $dir_item) {
 | 
						|
        if (!$iter->isDir()) {
 | 
						|
            $src = $dir_item->getPathname();
 | 
						|
            $dst = $buildDir . DIRECTORY_SEPARATOR . $dir_item->getFilename();
 | 
						|
            copy($src, $dst);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // assemble report
 | 
						|
    $report = new Report($game->getName());
 | 
						|
    foreach ($groups as $groupname) {
 | 
						|
        $stats = ReportBuilder::getStatsByFilters($gameid, $filter, $groupname, $best_only);
 | 
						|
        $section = new ReportSection($groupname, $stats);
 | 
						|
        $report->addSection($section);
 | 
						|
    }
 | 
						|
 | 
						|
    // generate latex
 | 
						|
    $report->saveTeX($buildDir);
 | 
						|
 | 
						|
    if ($outtype === "pdf") {
 | 
						|
        // run LuaLaTeX twice
 | 
						|
        chdir($buildDir);
 | 
						|
        $tex_cmd = TEX_ENGINE . " -interaction=nonstopmode report.tex";
 | 
						|
        exec($tex_cmd);
 | 
						|
        exec($tex_cmd);
 | 
						|
 | 
						|
        // rename output
 | 
						|
        $origOutput = "report.pdf";
 | 
						|
        $output = $repId . ".pdf";
 | 
						|
        rename($origOutput, $output);
 | 
						|
 | 
						|
        $contentType = "application/pdf";
 | 
						|
    } else if ($outtype === "tex") {
 | 
						|
        $output = $buildDir . DIRECTORY_SEPARATOR . $repId . ".zip";
 | 
						|
        $zip = new ZipArchive();
 | 
						|
        $zip->open($output, ZipArchive::CREATE | ZipArchive::OVERWRITE);
 | 
						|
        $files = glob($buildDir . DIRECTORY_SEPARATOR . "*.tex");
 | 
						|
        foreach ($files as $file) {
 | 
						|
            $zip->addFile($file, basename($file));
 | 
						|
        }
 | 
						|
        $zip->addFromString("request.txt", "gameid: ${gameid}\ngroups: " . join(", ", $groups) . "\nfilter: ${filter}\nouttype: ${outtype}\n");
 | 
						|
        $zip->addFromString("request.url", $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"]);
 | 
						|
        $zip->close();
 | 
						|
        $contentType = "application/zip";
 | 
						|
    }
 | 
						|
 | 
						|
    // set content type
 | 
						|
    header("Content-Type: ${contentType}");
 | 
						|
    header("Content-Length: " . filesize($output));
 | 
						|
    header("Content-Disposition: attachment; filename=" . basename($output));
 | 
						|
 | 
						|
    // deploy the generated PDF
 | 
						|
    $f = fopen($output, "r");
 | 
						|
    if ($f !== false) {
 | 
						|
        fpassthru($f);
 | 
						|
        fclose($f);
 | 
						|
    }
 | 
						|
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
function generate_detailed_game_stats(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $testMgr;
 | 
						|
 | 
						|
    $testids = json_decode(trim($params["testids"]), true);
 | 
						|
    $gameid = trim($params["gameid"]);
 | 
						|
    $stats = $testMgr->generateDetailedStats($gameid, $testids);
 | 
						|
    return $stats;
 | 
						|
}
 | 
						|
 | 
						|
function delete_tests(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $testMgr;
 | 
						|
    $ids = explode_list(trim($params["ids"]));
 | 
						|
    foreach ($ids as $id) {
 | 
						|
        $testMgr->deleteTest($id);
 | 
						|
    }
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
$rh->add(["create_game", "update_game"], ["data"], PRIVILEGE_CREATOR, "create_update_game", RESP_JSON, "Create or update game.");
 | 
						|
$rh->add("get_all_game_headers", [], PRIVILEGE_CREATOR, "get_all_game_headers", RESP_JSON, "Get all game headers.");
 | 
						|
$rh->add("get_challenges", [], PRIVILEGE_CREATOR, "get_challenges", RESP_PLAIN, "Get game challenges.");
 | 
						|
$rh->add("delete_games", ["ids"], PRIVILEGE_CREATOR, "delete_games", RESP_PLAIN, "Delete games.");
 | 
						|
$rh->add("export_game_file_csv", ["gameid"], PRIVILEGE_CREATOR, "export_game_file_csv", RESP_NONE, "Export game CSV file.");
 | 
						|
$rh->add("get_results_by_gameid", ["gameid"], PRIVILEGE_CREATOR, "get_results_by_gameid", RESP_JSON, "Get game results.");
 | 
						|
$rh->add("generate_detailed_stats", ["gameid", "testids"], PRIVILEGE_CREATOR, "generate_detailed_game_stats", RESP_JSON, "Generate detailed game stats.");
 | 
						|
$rh->add("generate_report_by_groups", ["gameid", "groups"], PRIVILEGE_CREATOR, "generate_report_by_groups", RESP_NONE, "Generate game reports for each specified group.");
 | 
						|
$rh->add("delete_tests", ["ids"], PRIVILEGE_CREATOR, "delete_tests", RESP_PLAIN, "Delete tests.");
 | 
						|
 | 
						|
// execute processing if user is a creator
 | 
						|
if ($privilege === PRIVILEGE_CREATOR) {
 | 
						|
    goto process_and_print;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ------------------------ QUIZMASTER ACTIONS --------------------- */
 | 
						|
 | 
						|
function create_update_group(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $user;
 | 
						|
    global $groupMgr;
 | 
						|
    global $userMgr;
 | 
						|
 | 
						|
    $update = $params[ReqHandler::ACTION_KEY] === "update_group";
 | 
						|
    $groupname = trim($params["groupname"]);
 | 
						|
    $description = trim($params["description"]);
 | 
						|
    $editors = (!$update) ? [] : explode_list(trim($params["editors"]));
 | 
						|
    $owner = (!$update) ? $user->getNickname() : (trim($params["owner"]) ?: $user->getNickname());
 | 
						|
 | 
						|
    $result = "FAIL";
 | 
						|
    if ($groupname != "") {
 | 
						|
        if (!$update) {
 | 
						|
            $groupMgr->addGroup($groupname, $owner, $description);
 | 
						|
            $result = "OK";
 | 
						|
        } else {
 | 
						|
            $gid = $params["id"];
 | 
						|
            $group = $groupMgr->getGroup($gid);
 | 
						|
            if ($group !== null) {
 | 
						|
                $group->disableAutoStoring();
 | 
						|
                $group->setName($groupname);
 | 
						|
                $group->setDescription($description);
 | 
						|
 | 
						|
                // only an existing user might be an owner of a group
 | 
						|
                if ($userMgr->getUser($owner) !== null) {
 | 
						|
                    $group->setOwner($owner);
 | 
						|
                }
 | 
						|
 | 
						|
                $editors = array_intersect($editors, $group->getMembers()); // a user cannot be an editor if not participant of the group
 | 
						|
                $group->setEditors($editors);
 | 
						|
 | 
						|
                $group->storeMods();
 | 
						|
                $group->enableAutoStoring();
 | 
						|
 | 
						|
                $result = "OK";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return $result;
 | 
						|
}
 | 
						|
 | 
						|
function delete_groups(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
 | 
						|
    $groups = explode_list($params["ids"] ?? "");
 | 
						|
    foreach ($groups as $g) {
 | 
						|
        $groupMgr->deleteGroup($g);
 | 
						|
    }
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
function get_all_groups(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
    $groups = $groupMgr->getAllGroups();
 | 
						|
    $a = [];
 | 
						|
    foreach ($groups as $g) {
 | 
						|
        $a[] = $g->toArray();
 | 
						|
    }
 | 
						|
    return $a;
 | 
						|
}
 | 
						|
 | 
						|
function search_groups(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
    $groups = $groupMgr->searchGroups($params["needle"]);
 | 
						|
    $a = [];
 | 
						|
    foreach ($groups as $g) {
 | 
						|
        $a[] = $g->toArray();
 | 
						|
    }
 | 
						|
    return $a;
 | 
						|
}
 | 
						|
 | 
						|
function create_update_user(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $userMgr;
 | 
						|
 | 
						|
    $update = $params[ReqHandler::ACTION_KEY] === "update_user";
 | 
						|
    $target_nickname = trim($params["nickname"]);
 | 
						|
    $password = trim($params["password"]);
 | 
						|
    $realname = trim($params["realname"]);
 | 
						|
    $privilege = trim($params["privilege"]);
 | 
						|
 | 
						|
    $success = false;
 | 
						|
    if (($target_nickname !== "")) {
 | 
						|
        if ((!$update) && ($password !== "")) { // CREATE
 | 
						|
            $success = $userMgr->addUser($target_nickname, $password, $realname, $privilege);
 | 
						|
        } else if ($update) { // UPDATE
 | 
						|
            $tuser = $userMgr->getUser($target_nickname); // load user data
 | 
						|
            if ($tuser !== null) {
 | 
						|
                // further field update
 | 
						|
                $tuser->disableAutoStoring();
 | 
						|
                $tuser->setRealname($realname);
 | 
						|
                $tuser->setPrivilege($privilege);
 | 
						|
 | 
						|
                // password replacement, if requested
 | 
						|
                if ($password !== "") {
 | 
						|
                    $tuser->changePassword(password_hash($password, PASSWORD_DEFAULT), "", false);
 | 
						|
                }
 | 
						|
 | 
						|
                $tuser->storeMods();
 | 
						|
                $tuser->enableAutoStoring();
 | 
						|
 | 
						|
                $success = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return $success ? "OK" : "FAIL";
 | 
						|
}
 | 
						|
 | 
						|
function delete_users(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $userMgr;
 | 
						|
    $nicknames = explode_list($params["users"]);
 | 
						|
    foreach ($nicknames as $nick) {
 | 
						|
        $userMgr->deleteUser($nick);
 | 
						|
    }
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
function get_all_users(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $userMgr;
 | 
						|
    $user_data_filtered = [];
 | 
						|
    $all_users = $userMgr->getAllUsers();
 | 
						|
    for ($i = 0; $i < count($all_users); $i++) {
 | 
						|
        $a = $all_users[$i]->toArray(); // convert user to array
 | 
						|
        unset($a["password"]); // remove password from records
 | 
						|
        $user_data_filtered[] = $a;
 | 
						|
    }
 | 
						|
    return $user_data_filtered;
 | 
						|
}
 | 
						|
 | 
						|
function get_user_groups(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
    $groups = $groupMgr->getUserGroupIDs($params["nickname"]);
 | 
						|
    $groupMgr->resolveGroupIds($groups);
 | 
						|
    return $groups;
 | 
						|
}
 | 
						|
 | 
						|
function get_game_groups(ReqHandler &$rh, array $params): array
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
    $groups = $groupMgr->getGameGroupIDs($params["gameid"]);
 | 
						|
    $groupMgr->resolveGroupIds($groups);
 | 
						|
    return $groups;
 | 
						|
}
 | 
						|
 | 
						|
function change_group_members(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    global $groupMgr;
 | 
						|
    global $user;
 | 
						|
    global $userMgr;
 | 
						|
 | 
						|
    $group = $groupMgr->getGroup($params["groupid"]);
 | 
						|
    if ($group !== null) {
 | 
						|
        if ($group->isUserContributor($user->getNickname())) {
 | 
						|
            $add = explode_list(trim($params["add"]));
 | 
						|
            $add = $userMgr->sanitizeNicknames($add);
 | 
						|
            $remove = explode_list(trim($params["remove"]));
 | 
						|
            $remove = $userMgr->sanitizeNicknames($remove);
 | 
						|
            $group->changeMembers($add, $remove);
 | 
						|
            return "OK";
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return "FAIL";
 | 
						|
}
 | 
						|
 | 
						|
function import_users_from_csv(ReqHandler &$rh, array $params): string
 | 
						|
{
 | 
						|
    if (!isset($_FILES["users_table"])) {
 | 
						|
 | 
						|
    }
 | 
						|
    return "OK";
 | 
						|
}
 | 
						|
 | 
						|
$rh->add("create_group", ["groupname", "description"], PRIVILEGE_QUIZMASTER, "create_update_group", RESP_PLAIN, "Create group.");
 | 
						|
$rh->add("update_group", ["groupname", "description", "owner", "editors", "id"], PRIVILEGE_QUIZMASTER, "create_update_group", RESP_PLAIN, "Update group.");
 | 
						|
$rh->add("delete_groups", ["ids"], PRIVILEGE_QUIZMASTER, "delete_groups", RESP_PLAIN, "Delete group.");
 | 
						|
$rh->add("get_all_groups", [], PRIVILEGE_QUIZMASTER, "get_all_groups", RESP_JSON, "Get all player groups.");
 | 
						|
$rh->add("search_groups", ["needle"], PRIVILEGE_QUIZMASTER, "search_groups", RESP_JSON, "Serach and fetch player groups.");
 | 
						|
$rh->add("change_group_members", ["groupid", "add", "remove"], PRIVILEGE_QUIZMASTER, "change_group_members", RESP_PLAIN, "Change group members.");
 | 
						|
 | 
						|
$rh->add(["create_user", "update_user"], ["nickname", "password", "realname", "privilege"], PRIVILEGE_QUIZMASTER, "create_update_user", RESP_PLAIN, "Create or update user.");
 | 
						|
$rh->add("delete_users", ["users"], PRIVILEGE_QUIZMASTER, "delete_users", RESP_PLAIN, "Delete users.");
 | 
						|
$rh->add("get_all_users", [], PRIVILEGE_QUIZMASTER, "get_all_users", RESP_JSON, "Get all users.");
 | 
						|
$rh->add("get_user_groups", ["nickname"], PRIVILEGE_QUIZMASTER, "get_user_groups", RESP_JSON, "Get user's groups.");
 | 
						|
$rh->add("get_game_groups", ["gameid"], PRIVILEGE_QUIZMASTER, "get_game_groups", RESP_JSON, "Get game's groups.");
 | 
						|
$rh->add("import_users_from_csv", [], PRIVILEGE_QUIZMASTER, "import_users_from_csv", RESP_JSON, "Get all users.");
 | 
						|
 | 
						|
//function test(ReqHandler &$rh, array $params): string
 | 
						|
//{
 | 
						|
//    $usrmgr = new UserMgr();
 | 
						|
//    $nicknames = $usrmgr->getAllNicknames();
 | 
						|
//    return join(", ", $nicknames);
 | 
						|
//}
 | 
						|
 | 
						|
//$rh->add("test", [], PRIVILEGE_QUIZMASTER, "test", RESP_PLAIN, "Test.");
 | 
						|
 | 
						|
// ----------
 | 
						|
 | 
						|
process_and_print:
 | 
						|
 | 
						|
[$result, $success] = $rh->process($privilege);
 | 
						|
 | 
						|
if ($success && ($result !== "")) {
 | 
						|
    echo $result;
 | 
						|
} |