Compare commits
	
		
			No commits in common. "master" and "live_debug" have entirely different histories.
		
	
	
		
			master
			...
			live_debug
		
	
		
@ -31,7 +31,7 @@ class Game extends AutoStoring
 | 
			
		||||
 | 
			
		||||
    // -------
 | 
			
		||||
 | 
			
		||||
    static public function genPublicId(): string
 | 
			
		||||
    static private function genPublicId(): string
 | 
			
		||||
    {
 | 
			
		||||
        return uniqid("p");
 | 
			
		||||
    }
 | 
			
		||||
@ -386,7 +386,7 @@ class GameMgr
 | 
			
		||||
            "game_file_present" => false,
 | 
			
		||||
            "properties" => $properties,
 | 
			
		||||
            "public" => false,
 | 
			
		||||
            "public_id" => Game::genPublicId(),
 | 
			
		||||
            "public_id" => self::genPublicId(),
 | 
			
		||||
            "version" => Game::CURRENT_GAME_VERSION
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
@ -430,34 +430,5 @@ class GameMgr
 | 
			
		||||
        return $games;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sanitize games and return game IDs.
 | 
			
		||||
    function sanitizeGames(array $games) : array {
 | 
			
		||||
        $sanitized = [];
 | 
			
		||||
        foreach ($games as $game) {
 | 
			
		||||
            // explode game identifier
 | 
			
		||||
            [$name, $id] = explode("#", $game);
 | 
			
		||||
 | 
			
		||||
            // fetch game
 | 
			
		||||
            if ($id !== null) {
 | 
			
		||||
                $records = $this->db->findBy([["name", "=", $name], "AND", ["_id", "=", (int)$id]]);
 | 
			
		||||
            } else {
 | 
			
		||||
                $records = $this->db->findBy(["name", "=", $name]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // put game ID into sanitized list only if identifier is not ambiguous
 | 
			
		||||
            if (count($records) === 1) {
 | 
			
		||||
                $sanitized[] = $records[0]["_id"];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $sanitized;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Resolve group IDs to full group identifiers.
 | 
			
		||||
    function resolveGames(array &$gameIds): void {
 | 
			
		||||
        $a = $this->db->findBy([["_id", "IN", $gameIds]]); // no caching here...
 | 
			
		||||
        $gameIds = array_map(fn($r) => $r["name"] . "#" . $r["_id"] ,$a);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -------
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -192,7 +192,7 @@ class Group extends AutoStoring
 | 
			
		||||
            alter_array_contents($this->games, $gameid, null);
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($gameids_remove as $gameid) { // remove games
 | 
			
		||||
            alter_array_contents($this->games, null, $gameid);
 | 
			
		||||
            alter_array_contents($this->members, null, $gameid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->storeMods(); // store changes
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ require_once "common_func.php";
 | 
			
		||||
 | 
			
		||||
class ReportBuilder
 | 
			
		||||
{
 | 
			
		||||
    static public function getStatsByFilters(int $gameid, string $filter, string $groups, bool $bestOnly)
 | 
			
		||||
    static public function getStatsByFilters(int $gameid, string $filter, string $groups, string $ordering)
 | 
			
		||||
    {
 | 
			
		||||
        $groupMgr = new GroupMgr();
 | 
			
		||||
        $testMgr = new TestMgr();
 | 
			
		||||
@ -18,7 +18,7 @@ class ReportBuilder
 | 
			
		||||
        $groupFilter = ["nickname", "IN", $nicknames];
 | 
			
		||||
 | 
			
		||||
        // get IDs
 | 
			
		||||
        $tests = $testMgr->getResultsByGameId($gameid, $filter, "", true, $bestOnly, $groupFilter);
 | 
			
		||||
        $tests = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $groupFilter);
 | 
			
		||||
        $ids = array_map(fn($test) => $test["_id"], $tests);
 | 
			
		||||
 | 
			
		||||
        // generate stats
 | 
			
		||||
@ -71,25 +71,12 @@ class Answer
 | 
			
		||||
    {
 | 
			
		||||
        return "\\answer" . $this->type . "{" . $this->ratio . "}{" . $this->text . "}\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get answer ratio.
 | 
			
		||||
    public function getRatio(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->ratio;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Challenge
 | 
			
		||||
{
 | 
			
		||||
    private string $question;
 | 
			
		||||
    private array $answers;
 | 
			
		||||
    private int $fillCount;
 | 
			
		||||
    private int $skipCount;
 | 
			
		||||
 | 
			
		||||
    // Sort answers by ratio.
 | 
			
		||||
    private function sortAnswers() : void {
 | 
			
		||||
        usort($this->answers, function($a, $b) {return $a->getRatio() < $b->getRatio();});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function __construct(array $data)
 | 
			
		||||
    {
 | 
			
		||||
@ -100,10 +87,6 @@ class Challenge
 | 
			
		||||
            $type = $answer === $data["correct_answer"] ? Answer::CORRECT : Answer::INCORRECT;
 | 
			
		||||
            $this->answers[] = new Answer($type, $answer, $ratio);
 | 
			
		||||
        }
 | 
			
		||||
        $this->fillCount = array_sum($data["player_answers"]); // get fill count
 | 
			
		||||
        $this->skipCount = $data["skipped"];
 | 
			
		||||
 | 
			
		||||
        $this->sortAnswers(); // sort answers by fill ratio
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getQuestion(): string
 | 
			
		||||
@ -116,22 +99,10 @@ class Challenge
 | 
			
		||||
        return $this->answers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFillCount(): int {
 | 
			
		||||
        return $this->fillCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSkipCount(): int {
 | 
			
		||||
        return $this->skipCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSubmissionCount() : int {
 | 
			
		||||
        return $this->fillCount + $this->skipCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Generate TeX representation.
 | 
			
		||||
    public function genTeX(): string
 | 
			
		||||
    {
 | 
			
		||||
        $tex = "\\begin{question}{" . $this->question . "}{" . $this->fillCount . "}\n";
 | 
			
		||||
        $tex = "\\begin{question}{" . $this->question . "}\n";
 | 
			
		||||
        foreach ($this->answers as &$answer) {
 | 
			
		||||
            $tex .= $answer->genTeX();
 | 
			
		||||
        }
 | 
			
		||||
@ -145,10 +116,6 @@ class ReportSection
 | 
			
		||||
    private string $title;
 | 
			
		||||
    private array $challenges;
 | 
			
		||||
 | 
			
		||||
    private function getNumberOfSubmissions() : int {
 | 
			
		||||
        return count($this->challenges) > 0 ? $this->challenges[0]->getSubmissionCount() : 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function __construct(string $title, array $challenges)
 | 
			
		||||
    {
 | 
			
		||||
        $this->title = $title;
 | 
			
		||||
@ -168,7 +135,7 @@ class ReportSection
 | 
			
		||||
    // Generate TeX representation of this report.
 | 
			
		||||
    function genTeX(): string
 | 
			
		||||
    {
 | 
			
		||||
        $tex = "\\begin{quiz}{" . $this->title . "}{" . $this->getNumberOfSubmissions() . "}\n";
 | 
			
		||||
        $tex = "\\begin{quiz}{" . $this->title . "}\n";
 | 
			
		||||
        foreach ($this->challenges as $challenge) {
 | 
			
		||||
            $tex .= $challenge->genTeX();
 | 
			
		||||
        }
 | 
			
		||||
@ -181,13 +148,11 @@ class Report
 | 
			
		||||
{
 | 
			
		||||
    private string $title;
 | 
			
		||||
    private array $sections;
 | 
			
		||||
    private array $comments;
 | 
			
		||||
 | 
			
		||||
    function __construct(string $title)
 | 
			
		||||
    {
 | 
			
		||||
        $this->title = $title;
 | 
			
		||||
        $this->sections = [];
 | 
			
		||||
        $this->comments = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -197,28 +162,13 @@ class Report
 | 
			
		||||
        $this->sections[] = $section;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addComment(string $comment): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->comments[] = $comment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Generate TeX representation.
 | 
			
		||||
    function genTeX(): string
 | 
			
		||||
    {
 | 
			
		||||
        $tex = "";
 | 
			
		||||
        // generate content
 | 
			
		||||
        foreach ($this->sections as $section) {
 | 
			
		||||
            $tex .= $section->genTeX() . "\n\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // add comments if any
 | 
			
		||||
        if ($this->comments != []) {
 | 
			
		||||
            $tex .= "\\huge\\noindent A következő oldal érzékeny információkat tartalmazhat!\\newline Nem publikálandó!\n\\clearpage\n\n";
 | 
			
		||||
            $tex .= "\\clearpage\n\small\n\\noindent ";
 | 
			
		||||
            $tex .= join("\\\\\n", $this->comments);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // escape LaTeX control characters
 | 
			
		||||
        $tex = TeXUtils::processCodeInserts($tex);
 | 
			
		||||
        $tex = TeXUtils::escape($tex);
 | 
			
		||||
        return $tex;
 | 
			
		||||
 | 
			
		||||
@ -406,7 +406,7 @@ class TestMgr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get test results by game ID.
 | 
			
		||||
    function getResultsByGameId(string $gameid, string $filter, string $orderby, bool $exclude_challenge_data, bool $best_ones_only, array ...$furtherFilters): array
 | 
			
		||||
    function getResultsByGameId(string $gameid, string $filter, string $orderby, bool $exclude_challenge_data, array ...$furtherFilters): array
 | 
			
		||||
    {
 | 
			
		||||
        $qb = $this->db->createQueryBuilder();
 | 
			
		||||
        $qb = $qb->where(["gameid", "=", (int)$gameid]);
 | 
			
		||||
@ -448,34 +448,6 @@ class TestMgr
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $test_data_array = $qb->getQuery()->fetch();
 | 
			
		||||
 | 
			
		||||
        // if only the best results should be included, then...
 | 
			
		||||
        if ($best_ones_only) {
 | 
			
		||||
            // filter out ongoing ones
 | 
			
		||||
            $tests = array_filter($test_data_array, fn($test) => $test["state"] === Test::TEST_CONCLUDED);
 | 
			
		||||
 | 
			
		||||
            // sort by result
 | 
			
		||||
            usort($tests, fn($a, $b) => $a["summary"]["percentage"] > $b["summary"]["percentage"]);
 | 
			
		||||
 | 
			
		||||
            // gather best tests by username here
 | 
			
		||||
            $best_test_ids = [];
 | 
			
		||||
            foreach ($tests as $test) {
 | 
			
		||||
                $nickname = $test["nickname"];
 | 
			
		||||
                if (!in_array($nickname, $best_test_ids)) {
 | 
			
		||||
                    $best_test_ids[$nickname] = $test["_id"];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // just keep values, drop the keys (nicknames)
 | 
			
		||||
            $best_test_ids = array_values($best_test_ids);
 | 
			
		||||
 | 
			
		||||
            // remove non-best results
 | 
			
		||||
            $test_data_array = array_filter($test_data_array, fn($test) => in_array($test["_id"], $best_test_ids));
 | 
			
		||||
 | 
			
		||||
            // renumber results
 | 
			
		||||
            $test_data_array = array_values($test_data_array);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $test_data_array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -515,7 +487,6 @@ class TestMgr
 | 
			
		||||
                        "correct_answer" => $correct_answer,
 | 
			
		||||
                        "player_answers" => array_fill(0, count($challenge["answers"]), 0),
 | 
			
		||||
                        "answer_count" => count($challenge["answers"]),
 | 
			
		||||
                        "skipped" => 0
 | 
			
		||||
                    ];
 | 
			
		||||
                    $aggregated[$challenge_indices[$idhash]] = $challenge_info; // insert challenge info
 | 
			
		||||
                }
 | 
			
		||||
@ -524,12 +495,9 @@ class TestMgr
 | 
			
		||||
                $challenge_idx = $challenge_indices[$idhash];
 | 
			
		||||
 | 
			
		||||
                // add up player answer
 | 
			
		||||
                $player_answer = trim($challenge["player_answer"]);
 | 
			
		||||
                if (($player_answer !== "") && ($player_answer != -1)) { // player answered
 | 
			
		||||
                if (trim($challenge["player_answer"]) !== "") {
 | 
			
		||||
                    $answer_idx = array_search($challenge["answers"][$challenge["player_answer"]], $aggregated[$challenge_idx]["answers"]); // transform player answer index to report answer index
 | 
			
		||||
                    $aggregated[$challenge_idx]["player_answers"][(int)$answer_idx]++;
 | 
			
		||||
                } else { // player has not answered or provided an unprocessable answer
 | 
			
		||||
                    $aggregated[$challenge_idx]["skipped"]++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
    "rakibtg/sleekdb": "2.15",
 | 
			
		||||
    "ext-http": "*",
 | 
			
		||||
    "ext-mbstring" : "*",
 | 
			
		||||
    "ext-zip": "*",
 | 
			
		||||
    "ext-fileinfo": "*"
 | 
			
		||||
    "ext-zip": "*"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -146,10 +146,7 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
 | 
			
		||||
        <select id="report_output_type">
 | 
			
		||||
            <option value="pdf">PDF</option>
 | 
			
		||||
            <option value="tex">TeX</option>
 | 
			
		||||
        </select><br>
 | 
			
		||||
        <input type="checkbox" id="best_only"><label for="best_only" style="font-size: 12px">Csak legjobb eredmények felhasználónként</label>
 | 
			
		||||
        <br>
 | 
			
		||||
        <br>
 | 
			
		||||
        </select>
 | 
			
		||||
        <input type="button" value="Előállítás" onclick="generate_game_report_by_groups()">
 | 
			
		||||
        <input type="button" value="Bezárás" onclick="hide('report_generator')">
 | 
			
		||||
    </section>
 | 
			
		||||
 | 
			
		||||
@ -67,23 +67,13 @@ if (!get_autologin_state() || ($user_data["privilege"] !== PRIVILEGE_QUIZMASTER)
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <label for="group_members">Tagok:</label><br>
 | 
			
		||||
                    <input type="button" value="+" onclick="open_element_change_window('add', 'member')">
 | 
			
		||||
                    <input type="button" value="-" onclick="open_element_change_window('remove', 'member')">
 | 
			
		||||
                    <input type="button" value="+" onclick="open_member_change_window('add')">
 | 
			
		||||
                    <input type="button" value="-" onclick="open_member_change_window('remove')">
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <textarea id="group_members" readonly></textarea>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <label for="group_games">Játékok:</label><br>
 | 
			
		||||
                    <input type="button" value="+" onclick="open_element_change_window('add', 'game')">
 | 
			
		||||
                    <input type="button" value="-" onclick="open_element_change_window('remove', 'game')">
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <textarea id="group_games" readonly></textarea>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </table>
 | 
			
		||||
        <section style="display: block; text-align: right">
 | 
			
		||||
            <input type="button" value="" id="group_editor_submit_btn">
 | 
			
		||||
@ -92,13 +82,13 @@ if (!get_autologin_state() || ($user_data["privilege"] !== PRIVILEGE_QUIZMASTER)
 | 
			
		||||
    </section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<section class="window" shown="false" id="element_manager_window">
 | 
			
		||||
<section class="window" shown="false" id="member_manager_window">
 | 
			
		||||
    <section class="window-inner">
 | 
			
		||||
        <section id="element_manager_window_title">
 | 
			
		||||
        <section id="member_manager_window_title">
 | 
			
		||||
        </section>
 | 
			
		||||
        <textarea id="add_remove_element_area"></textarea><br>
 | 
			
		||||
        <input type="button" value="OK" onclick="change_group_elements()">
 | 
			
		||||
        <input type="button" value="Mégse" onclick="hide('element_manager_window')">
 | 
			
		||||
        <textarea id="add_remove_member_area"></textarea><br>
 | 
			
		||||
        <input type="button" value="OK" onclick="change_group_members()">
 | 
			
		||||
        <input type="button" value="Mégse" onclick="hide('member_manager_window')">
 | 
			
		||||
    </section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -286,12 +286,9 @@ function patch_through_image(string $gameid, string $img_url)
 | 
			
		||||
    $image_fetch_url = $game_dir . DIRECTORY_SEPARATOR . $img_url;
 | 
			
		||||
 | 
			
		||||
    $img_fp = fopen($image_fetch_url, "r");
 | 
			
		||||
    $content_type = mime_content_type($image_fetch_url);
 | 
			
		||||
    if ($img_fp === false) {
 | 
			
		||||
        $img_fp = fopen(MISSING_IMAGE_PLACEHOLDER, "r");
 | 
			
		||||
        $content_type = mime_content_type(MISSING_IMAGE_PLACEHOLDER);
 | 
			
		||||
    }
 | 
			
		||||
    header("Content-Type: $content_type");
 | 
			
		||||
    fpassthru($img_fp);
 | 
			
		||||
    fclose($img_fp);
 | 
			
		||||
}
 | 
			
		||||
@ -488,7 +485,6 @@ function get_results_by_gameid(ReqHandler &$rh, array $params): array
 | 
			
		||||
    $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);
 | 
			
		||||
 | 
			
		||||
@ -520,9 +516,9 @@ function get_results_by_gameid(ReqHandler &$rh, array $params): array
 | 
			
		||||
        // execute filtering
 | 
			
		||||
        $game_results = null;
 | 
			
		||||
        if ($group_filter !== []) {
 | 
			
		||||
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $best_only, $group_filter);
 | 
			
		||||
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $group_filter);
 | 
			
		||||
        } else {
 | 
			
		||||
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true, $best_only);
 | 
			
		||||
            $game_results = $testMgr->getResultsByGameId($gameid, $filter, $ordering, true);
 | 
			
		||||
        }
 | 
			
		||||
        $result = $game_results;
 | 
			
		||||
    }
 | 
			
		||||
@ -538,7 +534,6 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
 | 
			
		||||
    $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
 | 
			
		||||
@ -570,20 +565,11 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
 | 
			
		||||
    // assemble report
 | 
			
		||||
    $report = new Report($game->getName());
 | 
			
		||||
    foreach ($groups as $groupname) {
 | 
			
		||||
        $stats = ReportBuilder::getStatsByFilters($gameid, $filter, $groupname, $best_only);
 | 
			
		||||
        $stats = ReportBuilder::getStatsByFilters($gameid, $filter, $groupname, "");
 | 
			
		||||
        $section = new ReportSection($groupname, $stats);
 | 
			
		||||
        $report->addSection($section);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // add comments
 | 
			
		||||
    $report->addComment("gameid: ${gameid}");
 | 
			
		||||
    $groupsJoined = join(", ", $groups);
 | 
			
		||||
    $report->addComment("groups: ${groupsJoined}");
 | 
			
		||||
    $report->addComment("filter: ${filter}");
 | 
			
		||||
    $report->addComment("best_only: ${params["best_only"]}");
 | 
			
		||||
    $requestURL = $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
 | 
			
		||||
    $report->addComment("\\scriptsize \\path{" . $requestURL . "}");
 | 
			
		||||
 | 
			
		||||
    // generate latex
 | 
			
		||||
    $report->saveTeX($buildDir);
 | 
			
		||||
 | 
			
		||||
@ -608,8 +594,8 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
 | 
			
		||||
        foreach ($files as $file) {
 | 
			
		||||
            $zip->addFile($file, basename($file));
 | 
			
		||||
        }
 | 
			
		||||
        $zip->addFromString("request.txt", "gameid: ${gameid}\ngroups: " . $groupsJoined . "\nfilter: ${filter}\nouttype: ${outtype}\nbest_only: ${params["best_only"]}");
 | 
			
		||||
        $zip->addFromString("request.url", $requestURL);
 | 
			
		||||
        $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";
 | 
			
		||||
    }
 | 
			
		||||
@ -724,14 +710,10 @@ function delete_groups(ReqHandler &$rh, array $params): string
 | 
			
		||||
function get_all_groups(ReqHandler &$rh, array $params): array
 | 
			
		||||
{
 | 
			
		||||
    global $groupMgr;
 | 
			
		||||
    global $gameMgr;
 | 
			
		||||
 | 
			
		||||
    $groups = $groupMgr->getAllGroups();
 | 
			
		||||
    $a = [];
 | 
			
		||||
    foreach ($groups as $g) {
 | 
			
		||||
        $r = $g->toArray();
 | 
			
		||||
        $gameMgr->resolveGames($r["games"]);
 | 
			
		||||
        $a[] = $r;
 | 
			
		||||
        $a[] = $g->toArray();
 | 
			
		||||
    }
 | 
			
		||||
    return $a;
 | 
			
		||||
}
 | 
			
		||||
@ -824,29 +806,20 @@ function get_game_groups(ReqHandler &$rh, array $params): array
 | 
			
		||||
    return $groups;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function change_group_members_or_games(ReqHandler &$rh, array $params): string
 | 
			
		||||
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()) || $user->hasQuizmasterPrivilege()) {
 | 
			
		||||
        if ($group->isUserContributor($user->getNickname())) {
 | 
			
		||||
            $add = explode_list(trim($params["add"]));
 | 
			
		||||
            $add = $userMgr->sanitizeNicknames($add);
 | 
			
		||||
            $remove = explode_list(trim($params["remove"]));
 | 
			
		||||
 | 
			
		||||
            $action = $params["action"];
 | 
			
		||||
            if ($action === "change_group_members") {
 | 
			
		||||
                global $userMgr;
 | 
			
		||||
                $add = $userMgr->sanitizeNicknames($add);
 | 
			
		||||
                $remove = $userMgr->sanitizeNicknames($remove);
 | 
			
		||||
                $group->changeMembers($add, $remove);
 | 
			
		||||
            } else if ($action === "change_group_games") {
 | 
			
		||||
                global $gameMgr;
 | 
			
		||||
                $add = $gameMgr->sanitizeGames($add);
 | 
			
		||||
                $remove = $gameMgr->sanitizeGames($remove);
 | 
			
		||||
                $group->changeGames($add, $remove);
 | 
			
		||||
            }
 | 
			
		||||
            $remove = $userMgr->sanitizeNicknames($remove);
 | 
			
		||||
            $group->changeMembers($add, $remove);
 | 
			
		||||
            return "OK";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -867,7 +840,7 @@ $rh->add("update_group", ["groupname", "description", "owner", "editors", "id"],
 | 
			
		||||
$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", "change_group_games"], ["groupid", "add", "remove"], PRIVILEGE_QUIZMASTER, "change_group_members_or_games", RESP_PLAIN, "Change group members or games.");
 | 
			
		||||
$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.");
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ function list_all_games() {
 | 
			
		||||
            let tdChkBox = document.createElement("td");
 | 
			
		||||
            tdChkBox.appendChild(chkbox);
 | 
			
		||||
            tdChkBox.classList.add("checkbox");
 | 
			
		||||
            let tdGameName = create_table_cell(g["name"] + "<span style='color:gray'>#" + g["_id"] + "</span>");
 | 
			
		||||
            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);
 | 
			
		||||
@ -373,13 +373,12 @@ function list_results_by_game(game) {
 | 
			
		||||
 | 
			
		||||
function generate_game_report_by_groups() {
 | 
			
		||||
    let gameid = EDITED_GAME["_id"];
 | 
			
		||||
    let filter = encodeURIComponent(document.getElementById("report_filter").value.trim());
 | 
			
		||||
    let groups = encodeURIComponent(document.getElementById("report_groups").value.trim());
 | 
			
		||||
    let best_only = document.getElementById("best_only").checked ? "true" : "false";
 | 
			
		||||
    let filter = document.getElementById("report_filter").value.trim()
 | 
			
		||||
    let groups = document.getElementById("report_groups").value.trim()
 | 
			
		||||
    let outtype = document.getElementById("report_output_type").value;
 | 
			
		||||
 | 
			
		||||
    let report_url = `interface.php?action=generate_report_by_groups&gameid=${gameid}&groups=${groups}&filter=${filter}&outtype=${outtype}&best_only=${best_only}`;
 | 
			
		||||
    //report_url = report_url.replaceAll("#", "%23");
 | 
			
		||||
    let report_url = `interface.php?action=generate_report_by_groups&gameid=${gameid}&groups=${groups}&filter=${filter}&outtype=${outtype}`;
 | 
			
		||||
    report_url = report_url.replace("#", "%23");
 | 
			
		||||
    window.open(report_url, "_blank");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,6 @@ function create_edit_group(group = null) {
 | 
			
		||||
    let group_ownerF = document.getElementById("group_owner");
 | 
			
		||||
    let group_editorsF = document.getElementById("group_editors");
 | 
			
		||||
    let group_membersF = document.getElementById("group_members");
 | 
			
		||||
    let group_gamesF = document.getElementById("group_games");
 | 
			
		||||
 | 
			
		||||
    if (!update) { // create a new group
 | 
			
		||||
        groupnameF.value = "";
 | 
			
		||||
@ -54,7 +53,6 @@ function create_edit_group(group = null) {
 | 
			
		||||
        group_ownerF.readOnly = true;
 | 
			
		||||
        group_editorsF.value = "";
 | 
			
		||||
        group_membersF.value = "";
 | 
			
		||||
        group_gamesF.value = "";
 | 
			
		||||
        hide("group_editor_additional_fields");
 | 
			
		||||
    } else { // update and existing one
 | 
			
		||||
        groupnameF.value = group["groupname"];
 | 
			
		||||
@ -64,7 +62,6 @@ function create_edit_group(group = null) {
 | 
			
		||||
        group_ownerF.readOnly = false;
 | 
			
		||||
        group_editorsF.value = group["editors"].join(", ");
 | 
			
		||||
        group_membersF.value = group["users"].join(", ");
 | 
			
		||||
        group_gamesF.value = group["games"].join(", ");
 | 
			
		||||
        show("group_editor_additional_fields");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -126,48 +123,47 @@ function delete_groups() {
 | 
			
		||||
 | 
			
		||||
function print_group_name(group_data) {
 | 
			
		||||
    let record = group_data["groupname"];
 | 
			
		||||
    let seqNumStyle = (group_data["unique"]) ? "style='color:lightgray'" : "";
 | 
			
		||||
    record += `<span ${seqNumStyle}>#` + group_data["_id"] + `</span>`;
 | 
			
		||||
    if (!group_data["unique"]) {
 | 
			
		||||
        record += "#" + group_data["_id"];
 | 
			
		||||
    }
 | 
			
		||||
    return record;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let ACTION = "";
 | 
			
		||||
let TYPE = ""
 | 
			
		||||
let MEMBER_ACTION = "";
 | 
			
		||||
 | 
			
		||||
function open_element_change_window(action, type) {
 | 
			
		||||
    ACTION = action;
 | 
			
		||||
    TYPE = type;
 | 
			
		||||
 | 
			
		||||
    let winCap = document.getElementById("element_manager_window_title");
 | 
			
		||||
 | 
			
		||||
    let typeStr = (type === "member") ? "Tagok" : "Játékok";
 | 
			
		||||
    let opStr = (action === "add") ? "hozzáadása" : "eltávolítása";
 | 
			
		||||
function open_member_change_window(action) {
 | 
			
		||||
    MEMBER_ACTION = action;
 | 
			
		||||
    let winCap = document.getElementById("member_manager_window_title");
 | 
			
		||||
 | 
			
		||||
    // print window title
 | 
			
		||||
    winCap.innerText = typeStr + " " + opStr;
 | 
			
		||||
    if (action === "add") {
 | 
			
		||||
        winCap.innerText = "Tagok hozzáadása";
 | 
			
		||||
    } else if (action === "remove") {
 | 
			
		||||
        winCap.innerText = "Tagok eltávolítása";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show("element_manager_window");
 | 
			
		||||
    show("member_manager_window");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function change_group_elements() {
 | 
			
		||||
    let elementListTA = document.getElementById("add_remove_element_area");
 | 
			
		||||
function change_group_members() {
 | 
			
		||||
    let userListTA = document.getElementById("add_remove_member_area");
 | 
			
		||||
 | 
			
		||||
    let req = {
 | 
			
		||||
        action: `change_group_${TYPE}s`,
 | 
			
		||||
        action: "change_group_members",
 | 
			
		||||
        groupid: EDITED_GROUP["_id"],
 | 
			
		||||
        add: [],
 | 
			
		||||
        remove: []
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (ACTION === "add") {
 | 
			
		||||
        req["add"] = elementListTA.value;
 | 
			
		||||
    } else if (ACTION === "remove") {
 | 
			
		||||
        req["remove"] = elementListTA.value;
 | 
			
		||||
    if (MEMBER_ACTION === "add") {
 | 
			
		||||
        req["add"] = userListTA.value;
 | 
			
		||||
    } else if (MEMBER_ACTION === "remove") {
 | 
			
		||||
        req["remove"] = userListTA.value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request(req).then(resp => {
 | 
			
		||||
        elementListTA.value = "";
 | 
			
		||||
        hide("element_manager_window");
 | 
			
		||||
        userListTA.value = "";
 | 
			
		||||
        hide("member_manager_window");
 | 
			
		||||
        hide("group_editor_window");
 | 
			
		||||
        list_all_groups();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -31,24 +31,11 @@ function autoconvert_datetime(str) {
 | 
			
		||||
    return filter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function count_selected() {
 | 
			
		||||
    let n = 0;
 | 
			
		||||
    document.getElementsByName("game_select").forEach((chk) => {
 | 
			
		||||
       if (chk.checked) {
 | 
			
		||||
           n++;
 | 
			
		||||
       }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    document.getElementById("selected_n").innerText = n.toString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fetch_results() {
 | 
			
		||||
 | 
			
		||||
    let filterF = document.getElementById("filter");
 | 
			
		||||
    let orderbyF = document.getElementById("orderby");
 | 
			
		||||
    let groupsF = document.getElementById("groups");
 | 
			
		||||
    let best_onlyChk = document.getElementById("best_only");
 | 
			
		||||
    let records_nS = document.getElementById("records_n");
 | 
			
		||||
 | 
			
		||||
    let filter = autoconvert_datetime(filterF.value.trim());
 | 
			
		||||
 | 
			
		||||
@ -59,8 +46,7 @@ function fetch_results() {
 | 
			
		||||
        gameid: GAMEID,
 | 
			
		||||
        filter: filter.trim(),
 | 
			
		||||
        orderby: orderbyF.value.trim(),
 | 
			
		||||
        groups: groupsF.value.trim(),
 | 
			
		||||
        best_only: best_onlyChk.checked ? "true" : "false"
 | 
			
		||||
        groups: groupsF.value.trim()
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    request(req).then(resp => {
 | 
			
		||||
@ -73,7 +59,7 @@ function fetch_results() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        records_nS.innerText = results.length;
 | 
			
		||||
        // let n = results.length;
 | 
			
		||||
 | 
			
		||||
        results.forEach((record) => {
 | 
			
		||||
            let row = document.createElement("tr");
 | 
			
		||||
@ -101,9 +87,6 @@ function fetch_results() {
 | 
			
		||||
            selectChk.type = "checkbox";
 | 
			
		||||
            selectChk.name = "game_select";
 | 
			
		||||
            selectChk.record = record;
 | 
			
		||||
            selectChk.addEventListener("input", () => {
 | 
			
		||||
               count_selected();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            let selection_cell = create_cell();
 | 
			
		||||
            selection_cell.append(selectChk);
 | 
			
		||||
@ -226,7 +209,6 @@ function toggle_test_selection() {
 | 
			
		||||
    game_selectChks.forEach((chk) => {
 | 
			
		||||
        chk.checked = !chk.checked;
 | 
			
		||||
    });
 | 
			
		||||
    count_selected();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function delete_tests() {
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,12 @@
 | 
			
		||||
% !TeX spellcheck = hu_HU
 | 
			
		||||
% !TeX encoding = UTF-8
 | 
			
		||||
% !TeX program = xelatex
 | 
			
		||||
 | 
			
		||||
\documentclass[10pt]{article}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%% Importing packages
 | 
			
		||||
% General things
 | 
			
		||||
\usepackage[magyar]{babel}
 | 
			
		||||
\usepackage[a4paper,margin=10mm,footskip=5mm]{geometry}
 | 
			
		||||
\usepackage[a4paper,margin=1in]{geometry}
 | 
			
		||||
\usepackage{fontspec}
 | 
			
		||||
\usepackage[fontsize=7pt]{fontsize}
 | 
			
		||||
\usepackage{titlesec}
 | 
			
		||||
\usepackage{url}
 | 
			
		||||
\usepackage{datetime2}
 | 
			
		||||
 | 
			
		||||
% For frame layout and content
 | 
			
		||||
\usepackage{xcolor}
 | 
			
		||||
@ -35,10 +28,6 @@
 | 
			
		||||
\definecolor{colpbb}{HTML}{A0A0A0} % Progressbar background
 | 
			
		||||
\definecolor{colanc}{HTML}{176767} % Correct answer background
 | 
			
		||||
\definecolor{colani}{HTML}{D3E5E5} % Incorrect answer background
 | 
			
		||||
\definecolor{colsbg}{HTML}{AA8A7D} % Group title background
 | 
			
		||||
\definecolor{colsfg}{HTML}{EFEFEF} % Group title text color
 | 
			
		||||
\definecolor{colqsb}{HTML}{176767} % Quiz sequence number background
 | 
			
		||||
\definecolor{colqsf}{HTML}{EFEFEF} % Quiz sequence number text color
 | 
			
		||||
 | 
			
		||||
% Setting up outer frames
 | 
			
		||||
\mdfsetup{
 | 
			
		||||
@ -63,16 +52,16 @@
 | 
			
		||||
 | 
			
		||||
% Replacing ToC text
 | 
			
		||||
\addto\captionsmagyar{%
 | 
			
		||||
    \renewcommand{\contentsname}%
 | 
			
		||||
    {\huge Csoportok}%
 | 
			
		||||
  \renewcommand{\contentsname}%
 | 
			
		||||
    {\huge Kurzusok}%
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
% Add dotfill to ToC
 | 
			
		||||
\makeatletter
 | 
			
		||||
\patchcmd{\l@section}{\hfil}{%
 | 
			
		||||
    \leaders\hbox{$\m@th
 | 
			
		||||
  \leaders\hbox{$\m@th
 | 
			
		||||
    \mkern \@dotsep mu\hbox{.}\mkern \@dotsep
 | 
			
		||||
    mu$}\hfill}{}{}
 | 
			
		||||
  mu$}\hfill}{}{}
 | 
			
		||||
\makeatother
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -94,76 +83,39 @@
 | 
			
		||||
\newcommand{\unnumsec}[1]{\section*{#1}%
 | 
			
		||||
\addcontentsline{toc}{section}{#1}}
 | 
			
		||||
 | 
			
		||||
% Set section title style
 | 
			
		||||
\titleformat{\unnumsec}{\Large\bfseries}{\thesection}{1em}{}
 | 
			
		||||
 | 
			
		||||
% Macros for answers
 | 
			
		||||
\newcommand{\answerCorrect}[2]{\progressbar{#1} & \begin{minipage}{0.8\columnwidth}\begin{mdframed}[backgroundcolor=colanc]\color{coltxl}{#2}\end{mdframed}\end{minipage}\\}
 | 
			
		||||
\newcommand{\answerIncorrect}[2]{\progressbar[filledcolor=colpbs]{#1} & \begin{minipage}{0.8\columnwidth}\begin{mdframed}[backgroundcolor=colani]\color{coltxd}{#2}\end{mdframed}\end{minipage}\\}
 | 
			
		||||
 | 
			
		||||
% Macros for questions.
 | 
			
		||||
\newmdenv[backgroundcolor=colqsb,align=left,innerleftmargin=0.2em,innerrightmargin=0.2em]{qsn} % Question sequence number
 | 
			
		||||
 | 
			
		||||
% Environment for questions. The parameter is the question itself, the contents of the environment should consist of \answer*{}{} macros and nothing else
 | 
			
		||||
\newenvironment{question}[2]{
 | 
			
		||||
    \begin{center}
 | 
			
		||||
        \begin{mdframed}
 | 
			
		||||
            \raisebox{0.05em} {
 | 
			
		||||
                \begin{minipage}{0.6cm}
 | 
			
		||||
                    \begin{qsn}[userdefinedwidth=0.5cm]
 | 
			
		||||
                        \begin{flushright}
 | 
			
		||||
                            \color{colqsf}\arabic{qc}.
 | 
			
		||||
                        \end{flushright}
 | 
			
		||||
                    \end{qsn}
 | 
			
		||||
                \end{minipage}
 | 
			
		||||
            }
 | 
			
		||||
            %
 | 
			
		||||
            {\color{coltit}\large #1\par}\medskip
 | 
			
		||||
            %
 | 
			
		||||
            {\hspace{1em}\small #2~kitöltő}\vspace{0.6em}\par
 | 
			
		||||
 | 
			
		||||
            \stepcounter{qc}
 | 
			
		||||
 | 
			
		||||
            \begin{tabular}{c c}
 | 
			
		||||
            }{
 | 
			
		||||
            \end{tabular}
 | 
			
		||||
 | 
			
		||||
        \end{mdframed}
 | 
			
		||||
    \end{center}
 | 
			
		||||
\newenvironment{question}[1]{
 | 
			
		||||
\begin{center}
 | 
			
		||||
    \begin{mdframed}
 | 
			
		||||
    {\color{coltit}\large #1\par}\medskip
 | 
			
		||||
        \begin{tabular}{c c}
 | 
			
		||||
}{
 | 
			
		||||
        \end{tabular}
 | 
			
		||||
    \end{mdframed}
 | 
			
		||||
\end{center}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
% An environment to create a quiz containing questions. The parameter is either the title of the quiz, or the name of the course
 | 
			
		||||
\newenvironment{quiz}[2]{
 | 
			
		||||
    % Create highlighted group title
 | 
			
		||||
    \begin{mdframed}[backgroundcolor=colsbg]
 | 
			
		||||
    {
 | 
			
		||||
        \color{colsfg}
 | 
			
		||||
        \unnumsec{#1}
 | 
			
		||||
        {\small #2~kitöltő}
 | 
			
		||||
        \vspace{0.4em}
 | 
			
		||||
    }
 | 
			
		||||
    \end{mdframed}
 | 
			
		||||
 | 
			
		||||
    % Reset question counter
 | 
			
		||||
    \setcounter{qc}{1}
 | 
			
		||||
    }{
 | 
			
		||||
\newenvironment{quiz}[1]{
 | 
			
		||||
    \unnumsec{#1}
 | 
			
		||||
}{
 | 
			
		||||
    \clearpage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
\begin{document}
 | 
			
		||||
    \begin{center}
 | 
			
		||||
        \Huge \IfFileExists{title.tex}{\input{title.tex}}{Kvíz eredmények}\\
 | 
			
		||||
        \normal \DTMnow
 | 
			
		||||
    \end{center}
 | 
			
		||||
\begin{center}
 | 
			
		||||
    \Huge \IfFileExists{stats/title.tex}{\input{title.tex}}{Kvíz eredmények}
 | 
			
		||||
\end{center}
 | 
			
		||||
 | 
			
		||||
    \Large
 | 
			
		||||
    \tableofcontents
 | 
			
		||||
    \normalsize
 | 
			
		||||
    \clearpage
 | 
			
		||||
\Large
 | 
			
		||||
\tableofcontents
 | 
			
		||||
\normalsize
 | 
			
		||||
\clearpage
 | 
			
		||||
 | 
			
		||||
    % Create question counter
 | 
			
		||||
    \newcounter{qc}
 | 
			
		||||
 | 
			
		||||
    \inputIfFileExists{content.tex}
 | 
			
		||||
\inputIfFileExists{content.tex}
 | 
			
		||||
 | 
			
		||||
\end{document}
 | 
			
		||||
 | 
			
		||||
@ -52,52 +52,50 @@ if (!$gameMgr->getGame($game_id)->isUserContributorOrOwner($user_data["nickname"
 | 
			
		||||
    <input type="text" placeholder="Rendezés" id="orderby" style="font-family: 'Monaco', monospace; width: 30em;">
 | 
			
		||||
    <input type="button" value="Szűrés" onclick="fetch_results()">
 | 
			
		||||
    <input type="button" value="Jelentés előállítása" onclick="generate_report()">
 | 
			
		||||
    <input type="button" value="Kijelöltek törlése" onclick="delete_tests()"><br>
 | 
			
		||||
    <input type="checkbox" id="best_only"><label for="best_only" style="font-size: 12px">Csak legjobb eredmények felhasználónként</label>
 | 
			
		||||
    <input type="button" value="Kijelöltek törlése" onclick="delete_tests()">
 | 
			
		||||
</section>
 | 
			
		||||
<section id="table_section">
 | 
			
		||||
    <table class="management">
 | 
			
		||||
        <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th>
 | 
			
		||||
                <input type="checkbox" onclick="toggle_test_selection()">
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>#<br>
 | 
			
		||||
                <code>
 | 
			
		||||
                    [_id]
 | 
			
		||||
                </code>
 | 
			
		||||
            </th>
 | 
			
		||||
            <th></th>
 | 
			
		||||
            <th>
 | 
			
		||||
                Felhasználónév<br>
 | 
			
		||||
                <code>
 | 
			
		||||
                    [nickname]
 | 
			
		||||
                </code>
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>Eredmény<br>
 | 
			
		||||
                <code>
 | 
			
		||||
                    [summary.percentage]
 | 
			
		||||
                </code>
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>Kezdés ideje<br>
 | 
			
		||||
                <code>
 | 
			
		||||
                    [start_time]
 | 
			
		||||
                </code>
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>Befejezés ideje<br>
 | 
			
		||||
                <code>
 | 
			
		||||
                    [end_time]
 | 
			
		||||
                </code>
 | 
			
		||||
            </th>
 | 
			
		||||
<section>
 | 
			
		||||
    <section id="table_section">
 | 
			
		||||
        <table class="management">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th>
 | 
			
		||||
                    <input type="checkbox" onclick="toggle_test_selection()">
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>#<br>
 | 
			
		||||
                    <code>
 | 
			
		||||
                        [_id]
 | 
			
		||||
                    </code>
 | 
			
		||||
                </th>
 | 
			
		||||
                <th></th>
 | 
			
		||||
                <th>
 | 
			
		||||
                    Felhasználónév<br>
 | 
			
		||||
                    <code>
 | 
			
		||||
                        [nickname]
 | 
			
		||||
                    </code>
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>Eredmény<br>
 | 
			
		||||
                    <code>
 | 
			
		||||
                        [summary.percentage]
 | 
			
		||||
                    </code>
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>Kezdés ideje<br>
 | 
			
		||||
                    <code>
 | 
			
		||||
                        [start_time]
 | 
			
		||||
                    </code>
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>Befejezés ideje<br>
 | 
			
		||||
                    <code>
 | 
			
		||||
                        [end_time]
 | 
			
		||||
                    </code>
 | 
			
		||||
                </th>
 | 
			
		||||
 | 
			
		||||
        </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody id="results_display">
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="summary_section">
 | 
			
		||||
    <span id="records_n">0</span> találat, <span id="selected_n">0</span> kijelölve
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody id="results_display">
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </section>
 | 
			
		||||
</section>
 | 
			
		||||
<script>
 | 
			
		||||
    let GAMEID = <?="$game_id"?>;
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,9 @@
 | 
			
		||||
section#table_section {
 | 
			
		||||
    position: sticky;
 | 
			
		||||
    height: calc(100vh - 6em);
 | 
			
		||||
    height: calc(100vh - 4em);
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#summary_section {
 | 
			
		||||
    margin-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.management {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    border-collapse: collapse;
 | 
			
		||||
 | 
			
		||||
@ -404,19 +404,4 @@ section.bad-answer section.answer input[type="radio"]:checked+label:not(.correct
 | 
			
		||||
section#further-info {
 | 
			
		||||
    font-size: 0.8em;
 | 
			
		||||
    padding: 0.4em 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="button"] {
 | 
			
		||||
    background-color: #176767;
 | 
			
		||||
    color: white;
 | 
			
		||||
    border: none;
 | 
			
		||||
    padding: 3pt;
 | 
			
		||||
    margin: 1pt;
 | 
			
		||||
    font-weight: bolder;
 | 
			
		||||
    border-radius: 2pt;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="button"]:hover {
 | 
			
		||||
    background-color: #408d8d;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user