$name, "group" => $group]; if (DUPLICATE_NAME_IN_SAME_GROUP || !in_array($new_rec, $ep)) { $ep[] = $new_rec; } } } return $ep; } define("MINIMUM_NEEDLE_LENGTH", 3); define("HIDE_GROUP_OF_NON_DUPLICATES", true); // keresés a nevek között function search_in_names(array $records, string $needle, string $group = "", bool $show_only_exact_match = false, bool $hide_groups_of_non_duplicates = HIDE_GROUP_OF_NON_DUPLICATES): array { // keresés $needle = trim($needle); if (strlen($needle) < MINIMUM_NEEDLE_LENGTH) { return []; } $exact_match = []; $hitpos_array = []; $filter = function (array $var) use (&$hitpos_array, &$exact_match, $needle, $group, $show_only_exact_match): bool { // ha a csoporton belül keres if ($group !== "" && strtolower($var["group"]) !== strtolower($group)) { return false; } $hitpos = iconv_strpos(strtolower($var["name"]), strtolower($needle)); // pontos egyezés vizsgálata (hossz és kezdőindex vizsgálata) if ($show_only_exact_match && ($hitpos != 0 || strlen($var["name"]) !== strlen($needle))) { $hitpos = false; } // ha van egyezés... if ($hitpos !== false) { $hitpos_array[] = $hitpos; // megvizsgálja, hogy exact egyezés van-e? $exact_match[] = $var["name"] === $needle; } return $hitpos !== false; }; $filter_res = array_values(array_filter($records, $filter)); if (count($filter_res) > 0) { for ($i = 0; $i < count($filter_res); $i++) { $filter_res[$i]["hitpos"] = $hitpos_array[$i]; $filter_res[$i]["exact_match"] = $exact_match[$i]; $filter_res[$i]["duplicate"] = false; } } if ($hide_groups_of_non_duplicates) { // nevek rendezése hossz szerint (substr gyorsítása n*(n-1)-ről (n-1)+(n-2)+(n-3)... vizsgálatra) usort($filter_res, fn($a, $b) => strlen($a["name"]) - strlen($b["name"])); // ha a név nem szerepel több, mint egyszer, akkor a csoportot törli (security reasons) for ($i = 0; $i < count($filter_res); $i++) { if ($filter_res[$i]["duplicate"]) { continue; } $name_needle = $filter_res[$i]["name"]; // név, amit keresünk a többiben $is_duplicate = false; for ($j = $i + 1; $j < count($filter_res); $j++) { // ha van találat, akkor kiugrunk a keresésből if (stripos($filter_res[$j]["name"], $name_needle) !== false) { $filter_res[$j]["duplicate"] = true; $is_duplicate = true; break; } } $filter_res[$i]["duplicate"] = $is_duplicate; } // csoport törlése azokból a nevekből, amikből nem szerepel több for ($i = 0; $i < count($filter_res); $i++) { if (!$filter_res[$i]["duplicate"]) { $filter_res[$i]["group"] = ""; } } } // rendezés abc-rendbe usort($filter_res, fn($a, $b) => $a["name"] > $b["name"]); return $filter_res; } // hozzáillesztés és sorok felcserélése function append_and_shuffle(string $filename, string $str) { $file_contents = file_get_contents($filename, $str); // betöltés $file_contents = ($file_contents === false) ? $str : ($file_contents . $str); // új adat hozzáadása $lines = explode("\n", $file_contents); // szétszedés soronként $lines_trimmed = []; // üres sorok kihagyása foreach ($lines as $line) { $lt = trim($line); if ($lt !== "") { $lines_trimmed[] = $lt; } } shuffle($lines_trimmed); // sorok felcserélése $file_contents = implode("\n", $lines_trimmed) . "\n"; // összeállítás egy stringbe return file_put_contents($filename, $file_contents); // mentés } define("DATA_DIR", "data"); define("LOGIN_ID_FILE_NAME", "electors_masked.md"); define("VOTE_FILENAME", "votes.dat"); define("VOTE_COUNT_FILENAME", "vote_count.dat"); define("USED_ELECTOR_HASH_FILENAME", "electors_submitted_vote.dat"); define("VOTE_STATE_FILENAME", "vote_state.json"); define("VOTE_TIMESTAMP_FILENAME", "vote_timestamps.dat"); function submit_name(string $name, string $group, string $elector_id) { // default... $ret = [ "successful" => false, "index" => -1 ]; // --------------- $used_hash_filename = DATA_DIR . DIRECTORY_SEPARATOR . USED_ELECTOR_HASH_FILENAME; $votes_filename = DATA_DIR . DIRECTORY_SEPARATOR . VOTE_FILENAME; $vote_count_filename = DATA_DIR . DIRECTORY_SEPARATOR . VOTE_COUNT_FILENAME; $timestamp_filename = DATA_DIR . DIRECTORY_SEPARATOR . VOTE_TIMESTAMP_FILENAME; // --------------- // kölcsönös kizárás a számláló fájllal $fp = fopen($vote_count_filename, "r+"); flock($fp, LOCK_EX); // kölcsönös kizárás // megvizsgáljuk, hogy szavazhat-e a felhasználó $used_elector_hashes = file_get_contents($used_hash_filename); $elector_hash = sha1($elector_id); if (strpos($used_elector_hashes, sha1($elector_id)) === false) { // ha szavazhat... // személy kikeresése $p = read_electable_people(ELECTABLE_PEOPLE_DATABASE); $hits = search_in_names($p, $name, $group, true, false); // ha egzakt találat van, azaz a szavazó megfelelő nevet adott meg if (count($hits) === 1) { // felhasználó azonosítójának beírása a felhasznált hash-ek jegyzékébe copy($used_hash_filename, "$used_hash_filename.prev"); $hash_save_successful = append_and_shuffle($used_hash_filename, "$elector_hash\n") !== false; // szavazat mentése $name = $hits[0]["name"]; $group = $hits[0]["group"]; $record = "$name//$group\n"; copy($votes_filename, "$votes_filename.prev"); $vote_save_successful = append_and_shuffle($votes_filename, $record) !== false; // időbélyeg mentése copy($timestamp_filename, "$timestamp_filename.prev"); $timestamp = date("Y-m-d H:i:s"); $timestamping_successful = file_put_contents($timestamp_filename, "$timestamp\n", FILE_APPEND) !== false; $success = $hash_save_successful && $vote_save_successful && $timestamping_successful; if ($success) { // ha sikeres minden... // szavazatszám növelése $cnt = (int)(fread($fp, 255)); $cnt++; $ret["index"] = $cnt; fseek($fp, 0); fwrite($fp, $cnt); } else { // ha valamelyik nem sikerül, akkor visszaállunk a korábbi állapotba rename($used_hash_filename, "$used_hash_filename.err_" . time()); // hibás fájl eltárolása rename($votes_filename, "$votes_filename.err_" . time()); rename($timestamp_filename, "$timestamp_filename.err_" . time()); copy("$used_hash_filename.prev", $used_hash_filename); // régi állapot visszaállítása copy("$votes_filename.prev", $votes_filename); copy("$timestamp_filename.prev", $timestamp_filename); } $ret["successful"] = $success; } } fclose($fp); // lock elengedése és fájl bezárása return $ret; } // név és csoport szétválasztása (NEM UGYANAZ, MINT JS-ben!) function fetch_name_and_group(string $compound): array { $parts = explode("//", $compound); return ["name" => $parts[0], "group" => $parts[1]]; } // állapotfájl előállítása, ha nem létezik function load_state() { $vote_state_fname = DATA_DIR . DIRECTORY_SEPARATOR . VOTE_STATE_FILENAME; $state = []; if (!file_exists($vote_state_fname)) { $state = [ "state" => "closed", "open_for_submit" => false, "auto_manage" => false, "open_date" => "2022-08-01 08:00:00", "close_date" => "2022-08-01 20:00:00", "results_public" => false, "public_results_plot_filename" => "", "public_results_plot_filename_mobile" => "" ]; } else { $state = json_decode(file_get_contents($vote_state_fname), true); } return $state; } function save_state($state) { file_put_contents(DATA_DIR . DIRECTORY_SEPARATOR . VOTE_STATE_FILENAME, json_encode($state, JSON_PRETTY_PRINT)); } // jelentés generálása function generate_report($omit_votes = true) { $state = load_state(); $elector_hashes = explode("\n", file_get_contents(DATA_DIR . DIRECTORY_SEPARATOR . LOGIN_ID_FILE_NAME)); $total_elector_count = 0; foreach ($elector_hashes as $elector_hash) { if (trim($elector_hash) !== "") { $total_elector_count++; } } $vote_list_str = file_get_contents(DATA_DIR . DIRECTORY_SEPARATOR . VOTE_FILENAME); $vote_list = explode("\n", $vote_list_str); $votes_grouped = []; $total_votes = 0; foreach ($vote_list as $vote) { // üres sorok kihagyása if (strlen(trim($vote)) === 0) { continue; } $votes_grouped[$vote] = (($votes_grouped[$vote]) ?? 0) + 1; $total_votes++; } // rendezés szavazatok száma alapján uasort($votes_grouped, fn($a, $b) => ($b - $a)); // név és csoport szétválasztása $votes_structured = []; foreach ($votes_grouped as $id_compound => $vote_cnt) { $expl_vote = fetch_name_and_group($id_compound); $votes_structured[$expl_vote["name"]] = ["name" => $expl_vote["name"], "group" => $expl_vote["group"], "count" => $vote_cnt, "ratio" => ($vote_cnt / $total_votes)]; } $report = [ "total_votes" => $total_votes, "total_electors" => $total_elector_count, "state" => $state ]; if (!$omit_votes) { $report["records"] = array_values($votes_structured); } return $report; } function RGBToHSLV($r, $g, $b, bool $hsv = false) { $r /= 255; $g /= 255; $b /= 255; $x_max = max($r, $g, $b); $x_min = min($r, $g, $b); $c = $x_max - $x_min; $l = ($x_max + $x_min) / 2; $h = 0; if ($c === 0) { $h = 0; } else if ($x_max === $r) { $h = 60 * ($g - $b) / $c; } else if ($x_max === $g) { $h = 60 * (2 + ($b - $r) / $c); } else if ($x_max === $b) { $h = 60 * (4 + ($r - $g) / $c); } if ($hsv) { $s = (($x_max === 0)) ? 0 : ((float)$c / $x_max); return [$h, $s, $x_max]; } else { $s = (($l === 0) || ($l === 1)) ? 0 : (2 * ($x_max - $l) / (1 - abs(2 * $l - 1))); return [$h, $s, $l]; } } function HSVtoRGB($h, $s, $v) { $c = $v * $s; $h_ = $h / 60; $x = $c * (1 - abs(fmod($h_, 2) - 1)); $rgb1 = [0, 0, 0]; if ($h_ >= 0 && $h_ < 1) { $rgb1 = [$c, $x, 0]; } else if ($h_ >= 1 && $h_ < 2) { $rgb1 = [$x, $c, 0]; } else if ($h_ >= 2 && $h_ < 3) { $rgb1 = [0, $c, $x]; } else if ($h_ >= 3 && $h_ < 4) { $rgb1 = [0, $x, $c]; } else if ($h_ >= 4 && $h_ < 5) { $rgb1 = [$x, 0, $c]; } else if ($h_ >= 5 && $h_ < 6) { $rgb1 = [$c, 0, $x]; } $m = $v - $c; $rgb = [$rgb1[0] + $m, $rgb1[1] + $m, $rgb1[2] + $m]; return $rgb; } function RGBtoHex($r, $g, $b) { $r_str = dechex($r); $g_str = dechex($g); $b_str = dechex($b); $r_str = (strlen($r_str) === 1) ? "0$r_str" : $r_str; $g_str = (strlen($g_str) === 1) ? "0$g_str" : $g_str; $b_str = (strlen($b_str) === 1) ? "0$b_str" : $b_str; return "#$r_str$g_str$b_str"; } //function RGBtoHCL($r, $g, $b) //{ // //} define("SVG_REPORT_WIDTH", 700); define("SVG_REPORT_HEIGHT", 350); define("SVG_REPORT_STROKE_WIDTH", 30); define("SVG_REPORT_LEGEND_MARK_SIZE", 20); define("SVG_REPORT_HIGHLIGHT_STROKE_WIDTH", 40); define("SVG_REPORT_PLOT_THRESHOLD_PERCENT", 5); define("SVG_REPORT_LEGEND_FONT_SIZE", 20); define("SVG_REPORT_START_COLOR", "003F5C"); define("SVG_REPORT_END_COLOR", "F89A17"); define("SVG_REPORT_PIE_RADIUS", 110); define("SVG_REPORT_ANIMATION_DUR", 2); // SVG-kép generálása a jelentésből function generate_svg_plot($report, $mobile = false, $timing = 8): string { $pie_orig = [SVG_REPORT_WIDTH / 2, SVG_REPORT_HEIGHT / 2]; $pie_radius = SVG_REPORT_PIE_RADIUS; $cum_percent = 0; $legend_font_size = SVG_REPORT_LEGEND_FONT_SIZE; $last_segment_midpoint = [0, 0]; $last_segment_midpoint_angle = 0; // százalékértéknyi körív rajzolása $svg_curve = function ($percent) use (&$cum_percent, $pie_radius, $pie_orig, &$last_segment_midpoint, &$last_segment_midpoint_angle) { $x_rot = $cum_percent / 100.0 * 360; // x-tengely forgatása $start_angle = -$cum_percent / 100.0 * 2 * M_PI; // radián $end_angle = -($cum_percent + $percent) / 100.0 * 2 * M_PI; // radián $middle_point_angle = -($cum_percent + $percent / 2); // fok $start_x = $pie_orig[0] + cos($start_angle) * $pie_radius; $start_y = $pie_orig[1] + sin($start_angle) * $pie_radius; $end_x = $pie_orig[0] + cos($end_angle) * $pie_radius; $end_y = $pie_orig[1] + sin($end_angle) * $pie_radius; $last_segment_midpoint[0] = $pie_orig[0] + cos($middle_point_angle / 100.0 * 2 * M_PI) * $pie_radius; $last_segment_midpoint[1] = $pie_orig[1] + sin($middle_point_angle / 100.0 * 2 * M_PI) * $pie_radius; $last_segment_midpoint_angle = $middle_point_angle * 3.6; $cum_percent += $percent; return "M $start_x $start_y A $pie_radius $pie_radius $x_rot 0 0 $end_x $end_y\n"; }; // kurzor mozgatása $svg_move_cursor = function ($x, $y) { return "M $x $y\n"; }; // útvonal generálása $svg_path = function ($d, $color, $id, $class = "sector", $stroke_width = SVG_REPORT_STROKE_WIDTH) { return "\n"; }; // jelmagyarázat generálása $svg_legend = function ($color, $text, $x, $y, $mark_size = SVG_REPORT_LEGEND_MARK_SIZE) use ($legend_font_size, $pie_orig, $mobile) { $rect_x = $x; $rect_y = $y; $circ_radius = $mark_size / 2; $anchor_cond = ($x > $pie_orig[0] || $mobile); $text_x_offset_sign = $anchor_cond ? 1 : -1; $text_anchor = $anchor_cond ? "start" : "end"; $text_x = $x + $text_x_offset_sign * ($circ_radius + 0.5 * $legend_font_size); $text_y = $y; // + ($mark_size - 0.4 * $legend_font_size) / 2; //return " return " $text\n"; }; // színpaletta előállítása $generate_palette = function ($start_hex, $end_hex, $cnt): array { if (gettype($start_hex) === "string") { $start_hex = hexdec($start_hex); } if (gettype($end_hex) === "string") { $end_hex = hexdec($end_hex); } // szétválasztás komponensekre $start_rgb = [($start_hex & 0xFF0000) >> 16, ($start_hex & 0x00FF00) >> 8, ($start_hex & 0x0000FF)]; $end_rgb = [($end_hex & 0xFF0000) >> 16, ($end_hex & 0x00FF00) >> 8, ($end_hex & 0x0000FF)]; $start_hsv = RGBToHSLV($start_rgb[0], $start_rgb[1], $start_rgb[2], true); $end_hsv = RGBToHSLV($end_rgb[0], $end_rgb[1], $end_rgb[2], true); // mindig a hosszabb görbe választása $hue_diff = $end_hsv[0] - $start_hsv[0]; if (abs($hue_diff) < 180) { $start_hsv[0] -= 360; } // interpoláció $step = [($end_hsv[0] - $start_hsv[0]) / ($cnt - 1), ($end_hsv[1] - $start_hsv[1]) / ($cnt - 1), ($end_hsv[2] - $start_hsv[2]) / ($cnt - 1)]; $palette = []; for ($i = 0; $i < $cnt; $i++) { $hue = $start_hsv[0] + $step[0] * $i % 360; $hue += ($hue < 0) ? 360 : 0; $color = [$hue, $start_hsv[1] + $step[1] * $i, $start_hsv[2] + $step[2] * $i]; $color_rgb = HSVtoRGB($color[0], $color[1], $color[2]); //$palette[] = "hsl(" . (floor($color[0])) . "," . (floor($color[1])) . "%," . (floor($color[2])) . "%)"; //$palette[] = "rgb(" . (round($color_rgb[0] * 255)) . "," . (round($color_rgb[1] * 255)) . "," . (round($color_rgb[2] * 255)) . ")"; $palette[] = RGBtoHex((round($color_rgb[0] * 255)), (round($color_rgb[1] * 255)), (round($color_rgb[2] * 255))); } return $palette; }; // ------------------- // küszöbérték feletti blokkok számának meghatározása $sector_count = 0; foreach ($report["records"] as $record) { $sector_count++; if ($record["ratio"] * 100.0 < SVG_REPORT_PLOT_THRESHOLD_PERCENT) { break; } } // színpaletta előállítása $palette = $generate_palette(SVG_REPORT_START_COLOR, SVG_REPORT_END_COLOR, $sector_count); //$palette = [ "#003f5c", "#424d83", "#8e5092", "#d24f81", "#fa6756", "#f89a17" ]; // svg-kép előállítása $svg = ""; // stíluslap hozzáadása $hlsw = SVG_REPORT_HIGHLIGHT_STROKE_WIDTH; $animdur = SVG_REPORT_ANIMATION_DUR; $svg .= ""; $total = $report["total_votes"]; $last_legend_anchor = []; foreach ($report["records"] as $idx => $record) { // kördiagram cikkei $percent = $record["count"] * 100.0 / $total; $joined_sector = $percent < SVG_REPORT_PLOT_THRESHOLD_PERCENT; // az küszöbérték alatti szavaztokat egybe gyűjtjük if ($joined_sector) { $percent = 100.0 - $cum_percent; // az összes maradék helyet kitöltjük } //$color = "rgb(" . random_int(0, 255) . "," . random_int(0, 255) . "," . random_int(0, 255) . ")"; $color = $palette[$idx]; $anim_delay = $timing * ($sector_count - $idx - 1); //$anim_delay = SVG_REPORT_ANIMATION_DUR * 2 / 3 * ($sector_count - $idx - 1); $svg .= ""; $sector_id = "sector$idx"; $svg .= $svg_path($svg_curve($percent), $color, "$sector_id"); // körcikk $svg .= "" // szöveg rajta . number_format((float)$percent, 1, ',', '') . "%\n"; // jelmagyarázat $legend_anchor = []; if (!$mobile) { // normál megjelenítés $legend_anchor = $pie_orig; $legend_anchor[0] += ($last_segment_midpoint[0] - $pie_orig[0]) * (1 + 1.2 * SVG_REPORT_STROKE_WIDTH / $pie_radius); $legend_anchor[1] += ($last_segment_midpoint[1] - $pie_orig[1]) * (1 + 1.2 * SVG_REPORT_STROKE_WIDTH / $pie_radius); } else { // mobilos megjelenítés $legend_anchor[0] = $pie_orig[0] - SVG_REPORT_PIE_RADIUS; $legend_anchor[1] = $pie_orig[1] + SVG_REPORT_PIE_RADIUS + SVG_REPORT_HIGHLIGHT_STROKE_WIDTH * 1.5 + 1.5 * $legend_font_size * $idx; } if (!$joined_sector) { $legend_text = "${record['name']} (${record['count']})"; } else { $legend_text = "Többiek"; } $svg .= $svg_legend($color, $legend_text, $legend_anchor[0], $legend_anchor[1]); $svg .= ""; $last_legend_anchor = $legend_anchor; if ($joined_sector) { break; } } // viewBox beállítása $viewBox = []; if (!$mobile) { // asztali kép $viewBox = "0 0 " . SVG_REPORT_WIDTH . " " . SVG_REPORT_HEIGHT; $style = "style='width: " . SVG_REPORT_WIDTH . "px;'"; } else { // mobilos kép $vBx = SVG_REPORT_WIDTH / 2 - (SVG_REPORT_PIE_RADIUS + SVG_REPORT_HIGHLIGHT_STROKE_WIDTH + 1); $vBy = SVG_REPORT_HEIGHT / 2 - (SVG_REPORT_PIE_RADIUS + SVG_REPORT_HIGHLIGHT_STROKE_WIDTH + 1); $vBw = (SVG_REPORT_PIE_RADIUS + SVG_REPORT_HIGHLIGHT_STROKE_WIDTH) * 2; $vBh = $last_legend_anchor[1] + 2 * $legend_font_size; $viewBox = "$vBx $vBy $vBw $vBh"; $style = ""; } $svg = "" . $svg; $svg .= ""; return $svg; } // azonosító ellenőrzése function verify_id($id) { if (trim($id) === "") { return ["valid" => false, "used" => false]; } $ids = file_get_contents(DATA_DIR . DIRECTORY_SEPARATOR . LOGIN_ID_FILE_NAME); $used_hashes = file_get_contents(DATA_DIR . DIRECTORY_SEPARATOR . USED_ELECTOR_HASH_FILENAME); $needle = sha1($id) . "\n"; // teljes sort kersünk, mert különben elég lenne egy részét ismerni az adatnak $id_valid = strpos($ids, $needle) !== false; $id_used = strpos($used_hashes, $needle) !== false; return ["valid" => $id_valid, "used" => $id_used]; } // -------------------------------- define("CP_LOGIN_ID_FILENAME", "cp_login_hashes.dat"); // Vezérlőpult kezelése // bejelentkezés a vezérlőpultba function cp_verify_id($id) { if (trim($id) === "") { return false; } $ids = file_get_contents(DATA_DIR . DIRECTORY_SEPARATOR . CP_LOGIN_ID_FILENAME); return strpos($ids, sha1($id) . "\n") !== false; } define("STATE_LOCK_FILENAME", "state.lock"); define("PUBLIC_DIR", "public"); define("FULL_RESULT_RECORDS_FILENAME", "full_results.json"); // szavazásállapot módosítása function cp_modify_state($command) { $fp = fopen(DATA_DIR . DIRECTORY_SEPARATOR . STATE_LOCK_FILENAME, "w"); flock($fp, LOCK_EX); $state = load_state(); switch ($command) { case "open_election": $state["state"] = "open"; break; case "close_election": $state["state"] = "closed"; break; case "enable_submitting": $state["open_for_submit"] = true; break; case "disable_submitting": $state["open_for_submit"] = false; break; case "publish_results": { // grafikon rajzolása $state["results_public"] = true; $uniqid = uniqid(); $plot_filename_normal = PUBLIC_DIR . DIRECTORY_SEPARATOR . "results_$uniqid.svg"; $plot_filename_mobile = PUBLIC_DIR . DIRECTORY_SEPARATOR . "m_results_$uniqid.svg"; $report = generate_report(false); $svg_normal = generate_svg_plot($report); file_put_contents($plot_filename_normal, $svg_normal); $svg_mobile = generate_svg_plot($report, true); file_put_contents($plot_filename_mobile, $svg_mobile); $state["public_results_plot_filename"] = $plot_filename_normal; $state["public_results_plot_filename_mobile"] = $plot_filename_mobile; // szavazat-eredmények mentése $records_without_group = []; foreach ($report["records"] as $record) { // csoport törlése (privacy and like that) $records_without_group[] = ["name" => $record["name"], "count" => $record["count"], "ratio" => $record["ratio"]]; } file_put_contents(PUBLIC_DIR . DIRECTORY_SEPARATOR . FULL_RESULT_RECORDS_FILENAME, json_encode($records_without_group)); } break; case "unpublish_results": $state["results_public"] = false; unlink($state["public_results_plot_filename"]); unlink($state["public_results_plot_filename_mobile"]); $state["public_results_plot_filename"] = ""; $state["public_results_plot_filename_mobile"] = ""; break; } save_state($state); fclose($fp); return $state; } define("TEMPDIR", "temp"); // jelentés előállítása csv-ként function generate_csv_report($report) { $csv_report_filename = TEMPDIR . DIRECTORY_SEPARATOR . uniqid("report_") . ".csv"; $csvfp = fopen($csv_report_filename, "w"); if ($csvfp === false) { return false; } // fejléc mentése $header = ["Név", "Csoport", "Szavazatok", "Arány", "Össz_szavazat", "Össz_szavazó", "Részv_arány"]; fputcsv($csvfp, $header); // első sor mentése $records = $report["records"]; if (count($records) === 0) { // egy sort legalább hozzáadunk $records[0] = ["name" => "", "group" => "", "count" => "", "ratio" => ""]; } $total_votes = $report["total_votes"]; $total_electors = $report["total_electors"]; $firstLine = [$records[0]["name"], $records[0]["group"], $records[0]["count"], number_format($records[0]["ratio"] * 100, 2) . "%", $total_votes, $total_electors, number_format($total_votes / $total_electors * 100, 2) . "%"]; fputcsv($csvfp, $firstLine); // többi sor mentése for ($i = 1; $i < count($records); $i++) { $records[$i]["ratio"] = number_format($records[$i]["ratio"] * 100, 2) . "%"; fputcsv($csvfp, $records[$i]); } fclose($csvfp); return $csv_report_filename; } function cp_add_access_code(string $ac) { $hash = sha1($ac); file_put_contents(DATA_DIR . DIRECTORY_SEPARATOR . LOGIN_ID_FILE_NAME, "$hash\n", FILE_APPEND); } // -------------------------------- define("ELECTABLE_PEOPLE_DATABASE", "data/electable_people.md"); $action = json_decode($_POST["action"]) ?? "none"; $req_data = json_decode($_POST["data"] ?? "[]", true); $res = ""; $disable_jsoning = false; // Belépés nélküli feldolgozás, ha nem sikerül, akkor processed = false; $processed = true; switch ($action) { case "login": { $elector_id = $req_data["elector_id"] ?? ""; $res = verify_id($elector_id); if ($res["valid"]) { $state = load_state(); $res["open"] = $state["state"] === "open"; if ($res["open"]) { $res["open_for_submit"] = $state["open_for_submit"]; $res["results_public"] = $state["results_public"]; } } else if (cp_verify_id($elector_id)) { // megnézzük, hogy admin-e, ha igen, akkor a szavazás indítása előtt beléphet $res["valid"] = true; $res["used"] = false; $state = load_state(); $res["open"] = $state["state"] === "open"; $res["open_for_submit"] = true; $res["results_public"] = false; } } break; case "cp_login": { $cp_id = $req_data["cp_id"] ?? ""; $res = cp_verify_id($cp_id); } break; default: $processed = false; break; } // ha feldolgozta, akkor ugrás a végére (inkább a goto, mint 46 zárójel... :D) if ($processed) { goto exit_point; // ---> } // Belépéses (azonosítóhoz kötött) feldolgozás $elector_id = json_decode($_POST["elector_id"] ?? ""); $state = load_state(); $admin_login = cp_verify_id($elector_id); if (((verify_id($elector_id)["valid"]) || $admin_login) && $state["state"] === "open") { $processed = true; switch ($action) { case "search": { if (!$state["open_for_submit"] && !$admin_login) { break; } $needle = $req_data["needle"] ?? ""; $group = $req_data["group"] ?? ""; $p = read_electable_people(ELECTABLE_PEOPLE_DATABASE); $hits = search_in_names($p, $needle, $group); $res = $hits; } break; case "submit": { if (!$state["open_for_submit"] && !$admin_login) { break; } $name = $req_data["needle"] ?? ""; $group = $req_data["group"] ?? ""; $res = submit_name($name, $group, $elector_id); } break; case "results": { $mobile = $req_data["mobile"] ?? false; $state = load_state(); $res = ["available" => $state["results_public"]]; if ($state["results_public"]) { $res["plot_url"] = $mobile ? $state["public_results_plot_filename_mobile"] : $state["public_results_plot_filename"]; $res["details"] = json_decode(file_get_contents(PUBLIC_DIR . DIRECTORY_SEPARATOR . FULL_RESULT_RECORDS_FILENAME), true); } } break; case "none": break; default: $processed = false; break; } } if ($processed) { goto exit_point; } // Control panel parancsok $processed = true; $elector_id = json_decode($_POST["cp_id"] ?? ""); if (cp_verify_id($elector_id)) { switch ($action) { case "cp_short_info": { $res = generate_report(true); // az összesített szavazatokat hagyja ki! } break; case "cp_mod_state": { $command = $req_data["command"]; $res = cp_modify_state($command); } break; case "cp_generate_plot": { $disable_jsoning = true; $res = generate_svg_plot(generate_report(false), false, 0); } break; case "cp_csv_report": { $report = generate_report(false); // teljes jelentés generálása $res = generate_csv_report($report); // csv generálása } break; case "cp_add_access_code": { $ac = $req_data["access_code"] ?? ""; if ($ac !== "") { cp_add_access_code($ac); } } break; default: $processed = false; break; } } exit_point: echo $disable_jsoning ? $res : json_encode($res); //$p = read_electable_people("data/electable_people.md"); return;