From 4a3a8b636be1132a590cae0a96216fbeeef83cf0 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Tue, 22 Sep 2020 23:32:48 +0200 Subject: [PATCH] index fertig bis auf rank --- api/config_example.php | 7 - api/functions.php | 450 +++++++++++++++++++ api/index.php | 357 ++++++++++++++- client/scripts/database.js | 799 +++++++++++++++++++++++++++++++++ client/scripts/datetime.js | 87 ++++ client/scripts/regatten.js.php | 67 ++- client/scripts/strings.js.php | 47 ++ content/index.php | 54 ++- index.php | 2 + server/page/headerfooter.php | 1 + server/page/menus.php | 29 ++ server/page/wrapper.php | 2 + server/scripts.php | 37 ++ server/scripts/index.js | 323 +++++++++++++ server/templates.php | 6 +- server/templates/button.html | 4 +- server/templates/card.html | 6 +- server/templates/error.html | 4 +- server/templates/table.html | 6 +- 19 files changed, 2215 insertions(+), 73 deletions(-) create mode 100644 api/functions.php create mode 100644 client/scripts/database.js create mode 100644 client/scripts/datetime.js create mode 100644 server/scripts.php create mode 100644 server/scripts/index.js diff --git a/api/config_example.php b/api/config_example.php index bcd2ceb..585ada0 100644 --- a/api/config_example.php +++ b/api/config_example.php @@ -27,13 +27,6 @@ define('DB_TABLE_NEWS', 'news'); define('DB_TABLE_UPDATETIMES', '_updatetimes'); - // PERMISSIONS - define('PERM_ALL', 0); - define('PERM_REGISTERED', 1); - define('PERM_READ', 2); - define('PERM_WRITE', 4); - define('PERM_ADMIN', 8); - // OUTGOING MAILS - Credentials for outgoing mails define('MAIL_SMTP_HOST', 'ssl://ostertun.net'); // SMTP Server address define('MAIL_SMTP_PORT', 465); // port to use diff --git a/api/functions.php b/api/functions.php new file mode 100644 index 0000000..a5cc263 --- /dev/null +++ b/api/functions.php @@ -0,0 +1,450 @@ += "' . date('Y-m-d', $from) . '") AND (`date` <= "' . date('Y-m-d', $to) . '") ORDER BY `date`'); + } + + function get_regatta_years($mysqli) { + $query = 'SELECT DISTINCT(YEAR(`date`)) as year FROM ' . BOATCLASS . DB_TABLE_SUFFIX_REGATTAS . ' ORDER BY `date`;'; + $response = mysqli_query($mysqli, $query); + + if ($response !== false) { + $result = array(); + if ($response->num_rows > 0) { + while ($row = $response->fetch_assoc()) { + $result[] = $row['year']; + } + } + return $result; + } else { + logE("functions", "get_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli)); + return false; + } + } + + function get_result_calculated($mysqli, $regatta_id) { + $regatta = get_regatta($mysqli, $regatta_id); + if ($regatta === false) { + return false; + } + $results = get_result($mysqli, $regatta_id); + if ($results !== false) { + + // *** Replace , with . + foreach ($results as $key => $value) { + for ($i = 1; $i <= $regatta['races']; $i ++) { + $results[$key]['race' . $i] = str_replace(',', '.', $results[$key]['race' . $i]); + } + } + + // *** Calculation *** + $gemeldet = count($results); + + $sortarray = array(); + foreach ($results as $key => $value) { + $results[$key]['finished'] = false; + $results[$key]['values'] = array(); + $results[$key]['values_all'] = array(); + $results[$key]['texts'] = array(); + $copy = array(); + for ($i = 1; $i <= $regatta['races']; $i ++) { + if (is_numeric($value['race' . $i])) { + $results[$key]['values'][$i] = $value['race' . $i]; + $results[$key]['texts'][$i] = $value['race' . $i]; + $copy[$i] = $value['race' . $i]; + $results[$key]['finished'] = true; + } else { + switch ($value['race' . $i]) { + // Nicht gestartet + case 'DNC': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; break; // Did not come + case 'DNS': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; break; // Did not started + // Startfehler + case 'OCS': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; /*$results[$key]['finished'] = true;*/ break; // On course site +// Muss v. Hand case 'ZFP': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; $results[$key]['finished'] = true; break; // Z-Flag penalty (20% nach 30.2) + case 'UFD': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; /*$results[$key]['finished'] = true;*/ break; // Uniform Flag Disqualified (disqu. nach 30.3) + case 'BFD': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; /*$results[$key]['finished'] = true;*/ break; // Black Flag Disqualified (disqu. nach 30.4) + // Nicht durch Ziel gegangen + case 'DNF': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; break; // Did not finish + case 'RET': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; break; // Retired (Aufgegeben) + case 'RAF': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; /*$results[$key]['finished'] = true;*/ break; // Retired after finish + // Disqualifizierun + case 'DSQ': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; /*$results[$key]['finished'] = true;*/ break; // Disqualified + case 'DNE': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = -1; /*$results[$key]['finished'] = true;*/ break; // Disqualified, not excludable (disqu. kann nach 90.3(b) nicht gestrichen werden) + case 'DGM': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = -2; /*$results[$key]['finished'] = true;*/ break; // Disqualification Gross Missconduct (kann nach 69.1(b)(2) nicht gestr. werden, grobes Fehlverhalten) + // Wiedergutmachung +// Muss v. Hand case 'RDG': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; $results[$key]['finished'] = true; break; // Redress given (Wiedergutmachung gewährt) + // Strafen +// Muss v. Hand case 'SCP': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; $results[$key]['finished'] = true; break; // Wertungsstrafe nach 44.3(a) (20%) +// Muss v. Hand case 'DPI': $results[$key]['values'][$i] = $gemeldet + 1; $copy[$i] = $gemeldet + 1; $results[$key]['finished'] = true; break; // Punktstrafe nach Ermessen der Jury + // Unbekannt + default: $results[$key]['values'][$i] = 0; $copy[$i] = 0; break; + } + + if ($results[$key]['values'][$i] != 0) { + $results[$key]['texts'][$i] = $value['race' . $i] . ' (' . $results[$key]['values'][$i] . ')'; + } else { + $results[$key]['texts'][$i] = $value['race' . $i] . ' (Unknown - 0)'; + } + } + } + $results[$key]['values_all'] = $results[$key]['values']; + for ($s = 0; $s < $regatta['streicher']; $s ++) { + $max = max($copy); + for ($i = 1; $i <= $regatta['races']; $i ++) { + if ($copy[$i] == $max) { + $copy[$i] = 0; + break; + } + } + } + $brutto = $netto = 0; + for ($i = 1; $i <= $regatta['races']; $i ++) { + $brutto += $results[$key]['values_all'][$i]; + if ($copy[$i] == -1) { $results[$key]['values'][$i] = $gemeldet + 1; } + elseif ($copy[$i] == -2) { $results[$key]['values'][$i] = $gemeldet + 1; } + else { $results[$key]['values'][$i] = $copy[$i]; } + if ($results[$key]['values'][$i] == 0) { + $results[$key]['texts'][$i] = '[' . $results[$key]['texts'][$i] . ']'; + } + $netto += $results[$key]['values'][$i]; + } + $results[$key]['brutto'] = $brutto; + $results[$key]['netto'] = $netto; + + if ($results[$key]['finished']) { + $sortarray[$key] = 0; + } else { + $sortarray[$key] = 1; + } + $sortarray[$key] /*.*/= sprintf("%08.2f", $netto); + $temp = $results[$key]['values']; + sort($temp); + $i = 0; + foreach ($temp as $val) { + if ($i < $regatta['races']) { + $sortarray[$key] .= sprintf("%07.2f", $val); + } + $i ++; + } + for ($i = $regatta['races']; $i > 0; $i --) { + $sortarray[$key] .= sprintf("%07.2f", $results[$key]['values_all'][$i]); + } + $results[$key]['sortvalue'] = $sortarray[$key]; + } + array_multisort($sortarray, $results); + $i = 1; + foreach ($results as $key => $value) { + if (($i > 1) and ($sortarray[$key] == $sortarray[$lastkey])) { + $results[$key]['place'] = $results[$lastkey]['place']; + } else { + $results[$key]['place'] = $i; + } + $i ++; + $lastkey = $key; + } + unset ($sortarray); + + return $results; + } else { + return false; + } + } + + function update_result_cache($mysqli, $regatta_id) { + $regatta = get_regatta($mysqli, $regatta_id); + if ($regatta === false) return; + $results = get_result_calculated($mysqli, $regatta['id']); + if ($results === false) return; + + // count finished boats + $fb = 0; + foreach ($results as $result) { + if ($result['finished']) { + $fb ++; + } + } + + db_update_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_REGATTAS, ['finishedBoats' => $fb], '`id`="' . $regatta['id'] . '"', 1); + + foreach ($results as $result) { + if ($fb == 0) { + $rlp = 0; + } else { + $rlp = 100 * $regatta['rlf'] * (($fb + 1 - $result['place']) / $fb); + } + db_update_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_RESULTS, ['place' => $result['place'], 'rlp' => $rlp], '`id`="' . $result['id'] . '"', 1); + } + } + + function get_ranking($mysqli, $from, $to, $jugend = false, $jugstrict = false) { + global $rankNoResults, $_CLASSES; + $rankNoResults = array(); + + $sailors = get_sailor($mysqli); + $regattas = get_regattas_range($mysqli, $from, $to); + + if (($sailors !== false) and ($regattas !== false)) { + foreach ($sailors as $key => $sailor) { + $sailors[$key]['regattas'] = array(); + $sailors[$key]['tmp_rlp'] = array(); + } + + foreach ($regattas as $regatta) { + $date = strtotime($regatta['date']); + + // regatta has to be min. 2 days to be ranking-regatta + if ($regatta['length'] < 2) { + continue; + } + + $results = get_result($mysqli, $regatta['id']); + if ($results === false) { + continue; + } + + if (count($results) <= 0) { + if (strtotime('+' . ($regatta['length']-1) . ' days', $date) <= time()) { + if (!$regatta['canceled']) { + $rankNoResults[] = $regatta; + } + } + continue; + } + + // in one race there must be at least 10 boats started + $ok = false; + for ($i = 1; $i <= $regatta['races']; $i ++) { + $temp = 0; + foreach ($results as $result) { + if (($result['race' . $i] != 'DNC') and ($result['race' . $i] != 'DNS')) { + $temp ++; + } + } + if ($temp >= 10) { + $ok = true; + break; + } + } + if (!$ok) { + continue; + } + + $fb = $regatta['finishedBoats']; + + // add regatta to each sailor + foreach ($results as $result) { + if ($result['rlp'] == 0) { + continue; + } + // check if crew is youth + //if ($jugend) { + // $crew = explode(',', $result['crew']); + // $okay = true; + // foreach ($crew as $sailor) { + // if (($sailor == '') or !isset($sailors[$sailor])) continue; + // $sailor = $sailors[$sailor]; + // if ((($sailor['year'] !== null) and ($sailor['year'] < (date('Y', $date) - $_CLASSES[BOATCLASS]['youth-age']))) or + // (($sailor['year'] === null) and ($jugstrict))) { + // $okay = false; + // break; + // } + // } + // if (!$okay) continue; + //} + // calc m + if ($regatta['m'] > 0) { + $m = $regatta['m']; + } elseif ($regatta['races'] <= 4) { + $m = $regatta['races']; + } else { + if (($regatta['length'] > 2) and ($regatta['races'] >= 6)) { + $m = 5; + } else { + $m = 4; + } + } + $rlp = $result['rlp']; + $sailors[$result['steuermann']]['regattas'][$regatta['id']] = array( + 'regatta' => $regatta['id'], + 'boat' => $result['boat'], + 'crew' => $result['crew'], + 'place' => $result['place'], + 'fb' => $fb, + 'rlp' => $rlp, + 'used' => 0, + 'm' => $m + ); + for ($i = 0; $i < $m; $i ++) { + array_push($sailors[$result['steuermann']]['tmp_rlp'], array($regatta['id'], $rlp)); + } + } + } + + foreach ($sailors as $key => $sailor) { + if ($sailor['german'] == 0) { + unset($sailors[$key]); + } elseif ($jugend) { + if ((($sailor['year'] !== null) and ($sailor['year'] < (date('Y', $to) - $_CLASSES[BOATCLASS]['youth-age']))) or + (($sailor['year'] === null) and ($jugstrict))) { + unset($sailors[$key]); + } + } + } + + $sortarray = array(); + + foreach ($sailors as $key => $sailor) { + // sort rlps desc + $sort = array(); + foreach ($sailor['tmp_rlp'] as $key2 => $value) { + $sort[$key2] = $value[1]; + } + array_multisort($sort, SORT_DESC, $sailors[$key]['tmp_rlp']); + // calc mean. rlp + $sum = 0; + $cnt = 0; + foreach ($sailors[$key]['tmp_rlp'] as $value) { + $sum += $value[1]; + $sailors[$key]['regattas'][$value[0]]['used'] ++; + $cnt ++; + if ($cnt >= 9) { + break; + } + } + unset($sailors[$key]['tmp_rlp']); + if ($cnt > 0) { + $rlp = $sum / $cnt; + $sailors[$key]['rlp'] = $rlp; + $sailors[$key]['m'] = $cnt; + } else { + unset($sailors[$key]); + continue; + } + + if ($rlp == 0) { + $sortarray[$key] = $cnt; + } else { + $sortarray[$key] = $cnt * 1000 + $rlp; + } + } + array_multisort($sortarray, SORT_DESC, $sailors); + unset($sortarray); + + $i = 1; + foreach ($sailors as $key => $sailor) { + $sailors[$key]['rank'] = $i; + $i ++; + } + + return $sailors; + } else { + return false; + } + } + + function get_trim_boat($mysqli, $id = false) { + return get_db_entry($mysqli, DB_TABLE_TRIM_BOATS, $id); + } + + function get_trim_boat_users($mysqli, $id) { + $result = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`boat` = "' . mysqli_real_escape_string($mysqli, $id) . '"'); + if ($result === false) + return false; + else + return $result; + } + + function get_trim_user_boats($mysqli, $id) { + $boats = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`user` = "' . mysqli_real_escape_string($mysqli, $id) . '"'); + if ($boats === false) { + return false; + } else { + $result = []; + foreach ($boats as $value) { + $result[$value['boat']] = get_trim_boat($mysqli, $value['boat']); + } + return $result; + } + } + + function trim_is_boat_user($mysqli, $user, $boat) { + $res = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`user` = "' . mysqli_real_escape_string($mysqli, $user) . '" AND `boat` = "' . mysqli_real_escape_string($mysqli, $boat) . '"'); + return ($res !== false) and (count($res) == 1); + } + + function get_trim_trim($mysqli, $id = false) { + return get_db_entry($mysqli, DB_TABLE_TRIM_TRIMS, $id); + } + + function get_trim_boat_trims($mysqli, $id) { + $result = db_get_data($mysqli, DB_TABLE_TRIM_TRIMS, '*', '`boat` = "' . mysqli_real_escape_string($mysqli, $id) . '"'); + if ($result === false) { + return false; + } else { + return $result; + } + } + +?> \ No newline at end of file diff --git a/api/index.php b/api/index.php index 4102512..9eb94c3 100644 --- a/api/index.php +++ b/api/index.php @@ -5,6 +5,7 @@ require_once(__DIR__ . '/../server/log.php'); require_once(__DIR__ . '/database.php'); require_once(__DIR__ . '/login.php'); + require_once(__DIR__ . '/functions.php'); $request = false; if (isset($_GET['request'])) { @@ -76,15 +77,14 @@ } else { $user_id = false; } - $perm = get_perm($mysqli, $user_id); - function has_perm($permission) { - global $perm; - return ($perm & $permission) == $permission; + function isLoggedIn() { + global $user_id; + return $user_id !== false; } - function checkPermission($perm) { - if (!has_perm($perm)) done(DONE_UNAUTHORIZED, ['error' => 'permission denied']); + function checkLoggedIn() { + if (!isLoggedIn()) done(DONE_UNAUTHORIZED, ['error' => 'permission denied']); } function checkRequest($param) { @@ -98,6 +98,35 @@ }, $array); } + $whereString = false; + if (isset($_REQUEST['index'], $_REQUEST['value'])) { + $whereString = '`' . mysqli_real_escape_string($mysqli, $_REQUEST['index']) . '`="' . mysqli_real_escape_string($mysqli, $_REQUEST['value']) . '"'; + } + + function sendEntries($table) { + global $mysqli, $whereString; + $response = db_get_data($mysqli, $table, '*', $whereString); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, $table, '*', '`changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + } + + function sendEntry($table) { + global $mysqli; + checkRequest('id'); + $response = db_get_data($mysqli, $table, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + } + switch ($action) { case 'login': @@ -110,11 +139,325 @@ break; case 'logout': - checkPermission(PERM_REGISTERED); + checkLoggedIn(); auth_logout($mysqli, $_REQUEST['auth']['id']); done(DONE_OKAY); break; + case 'get_update_time': + $times = array(); + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . DB_TABLE_CLUBS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['clubs'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . BOATCLASS . DB_TABLE_SUFFIX_BOATS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['boats'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . BOATCLASS . DB_TABLE_SUFFIX_SAILORS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['sailors'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . BOATCLASS . DB_TABLE_SUFFIX_REGATTAS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['regattas'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . BOATCLASS . DB_TABLE_SUFFIX_RESULTS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['results'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . BOATCLASS . DB_TABLE_SUFFIX_PLANNING . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['plannings'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . DB_TABLE_TRIM_BOATS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['trim_boats'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . DB_TABLE_TRIM_USERS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['trim_users'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . DB_TABLE_TRIM_TRIMS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['trim_trims'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + $response = db_get_data($mysqli, DB_TABLE_UPDATETIMES, '`update`', '`table` = "' . DB_TABLE_USERS . '"', 1); + if (($response !== false) and (count($response) > 0)) { + $times['users'] = strtotime(array_values($response)[0]['update']); + } else { + done(DONE_DATABASE); + } + done(DONE_OKAY, $times); + break; + + case 'get_clubs': + sendEntries(DB_TABLE_CLUBS); + break; + + case 'get_club': + sendEntry(DB_TABLE_CLUBS); + break; + + case 'get_boats': + sendEntries(BOATCLASS . DB_TABLE_SUFFIX_BOATS); + break; + + case 'get_boat': + sendEntry(BOATCLASS . DB_TABLE_SUFFIX_BOATS); + break; + + case 'get_sailors': + sendEntries(BOATCLASS . DB_TABLE_SUFFIX_SAILORS); + break; + + case 'get_sailor': + sendEntry(BOATCLASS . DB_TABLE_SUFFIX_SAILORS); + break; + + case 'get_years': + $response = get_regatta_years($mysqli); + if ($response === false) done(DONE_DATABASE); + foreach ($response as $key => $value) + $response[$key] = ['year' => $value]; + done(DONE_OKAY, ['data' => $response]); + break; + + case 'get_regattas': + sendEntries(BOATCLASS . DB_TABLE_SUFFIX_REGATTAS); + break; + + case 'get_regatta': + sendEntry(BOATCLASS . DB_TABLE_SUFFIX_REGATTAS); + break; + + case 'get_results': + sendEntries(BOATCLASS . DB_TABLE_SUFFIX_RESULTS); + break; + + case 'get_result': + sendEntry(BOATCLASS . DB_TABLE_SUFFIX_RESULTS); + break; + + case 'get_plannings': + $response = db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_PLANNING, '*', $whereString); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_PLANNING, '*', '`changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_map(function ($entry) { + global $user_id; + if (($user_id === false) or ($entry['user'] != $user_id)) { + unset($entry['gemeldet'], $entry['bezahlt']); + } + return $entry; + }, $response); + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + break; + + case 'get_planning': + checkRequest('id'); + $response = db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_PLANNING, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + if (($user_id === false) or ($response['user'] != $user_id)) { + unset($response['gemeldet'], $response['bezahlt']); + } + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + break; + + case 'get_trim_boats': + checkLoggedIn(); + $users = db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'boat', '`user`="' . $user_id . '"'); + $boats = implode(',', array_column($users, 'boat')); + if ($boats == '') { + done(DONE_OKAY, array('data' => [], 'keys' => [])); + } + $response = db_get_data($mysqli, DB_TABLE_TRIM_BOATS, '*', '`id` IN (' . $boats . ')' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, DB_TABLE_TRIM_BOATS, '*', '`id` IN (' . $boats . ') AND `changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + break; + + case 'get_trim_boat': + checkLoggedIn(); + checkRequest('id'); + $response = db_get_data($mysqli, DB_TABLE_TRIM_BOATS, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + if (count(db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'id', '`user`="' . $user_id . '" AND `boat`="' . $response['id'] . '"')) != 1) + done(DONE_BAD_REQUEST, ['error' => 'id not found']); + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + break; + + case 'get_trim_users': + checkLoggedIn(); + $users = db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'boat', '`user`="' . $user_id . '"'); + $boats = implode(',', array_column($users, 'boat')); + if ($boats == '') { + done(DONE_OKAY, array('data' => [], 'keys' => [])); + } + $response = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`boat` IN (' . $boats . ')' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`boat` IN (' . $boats . ') AND `changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + break; + + case 'get_trim_user': + checkLoggedIn(); + checkRequest('id'); + $response = db_get_data($mysqli, DB_TABLE_TRIM_USERS, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + if (count(db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'id', '`user`="' . $user_id . '" AND `boat`="' . $response['boat'] . '"')) != 1) + done(DONE_BAD_REQUEST, ['error' => 'id not found']); + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + break; + + case 'get_trim_trims': + checkLoggedIn(); + $users = db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'boat', '`user`="' . $user_id . '"'); + $boats = implode(',', array_column($users, 'boat')); + if ($boats == '') { + done(DONE_OKAY, array('data' => [], 'keys' => [])); + } + $response = db_get_data($mysqli, DB_TABLE_TRIM_TRIMS, '*', '`boat` IN (' . $boats . ')' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, DB_TABLE_TRIM_TRIMS, '*', '`boat` IN (' . $boats . ') AND `changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + break; + + case 'get_trim_trim': + checkLoggedIn(); + checkRequest('id'); + $response = db_get_data($mysqli, DB_TABLE_TRIM_TRIMS, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + if (count(db_get_data($mysqli, DB_TABLE_TRIM_USERS, 'id', '`user`="' . $user_id . '" AND `boat`="' . $response['boat'] . '"')) != 1) + done(DONE_BAD_REQUEST, ['error' => 'id not found']); + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + break; + + case 'get_users': + $followFields = ''; + for ($i = 1; $i <= 5; $i ++) $followFields .= ',' . BOATCLASS . '_sailor' . $i . ' AS sailor' . $i; + $response = db_get_data($mysqli, DB_TABLE_USERS, 'id,username,email' . $followFields, $whereString); + if ($response === false) done(DONE_DATABASE); + $keys = array_keys($response); + if (isset($_REQUEST['changed-after'])) { + $response = db_get_data($mysqli, DB_TABLE_USERS, 'id,username,email,' . $followFields, '`changed` > "' . mysqli_real_escape_string($mysqli, date('Y-m-d H:i:s', $_REQUEST['changed-after'])) . '"' . ($whereString ? (' AND ' . $whereString) : '')); + if ($response === false) done(DONE_DATABASE); + } + $response = array_map(function ($entry) { + global $user_id; + if ($entry['id'] != $user_id) { + $entry = ['id' => $entry['id'], 'username' => $entry['username']]; + } + return $entry; + }, $response); + $response = array_values($response); + done(DONE_OKAY, array('data' => replaceChanged($response), 'keys' => $keys)); + break; + + case 'get_user': + checkRequest('id'); + $followFields = ''; + for ($i = 1; $i <= 5; $i ++) $followFields .= ',' . BOATCLASS . '_sailor' . $i . ' AS sailor' . $i; + $response = db_get_data($mysqli, DB_TABLE_USERS, 'id,username,email' . $followFields, '`id` = "' . mysqli_real_escape_string($mysqli, $_REQUEST['id']) . '"'); + if ($response === false) done(DONE_DATABASE); + if (count($response) != 1) done(DONE_BAD_REQUEST, ['error' => 'id not found']); + $response = array_values($response)[0]; + if ($response['id'] != $user_id) { + $response = ['id' => $response['id'], 'username' => $response['username']]; + } + unset($response['changed']); + done(DONE_OKAY, ['data' => $response]); + break; + + case 'add_subscription': + checkRequest('subscription'); + $data = [ + 'auth' => PUSH_AUTH, + 'subscription' => $_REQUEST['subscription'] + ]; + $ch = curl_init('https://push.ostertun.net/add_subscription'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + $result = curl_exec($ch); + curl_close($ch); + if ($result == "OK") + done(DONE_OKAY); + else { + logE('add_subscription', $result); + done(DONE_SERVER_ERROR); + } + break; + + case 'remove_subscription': + checkRequest('subscription'); + $data = [ + 'auth' => PUSH_AUTH, + 'subscription' => $_REQUEST['subscription'] + ]; + $ch = curl_init('https://push.ostertun.net/remove_subscription'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + $result = curl_exec($ch); + curl_close($ch); + if ($result == "OK") + done(DONE_OKAY); + else { + logE('remove_subscription', $result); + done(DONE_SERVER_ERROR); + } + break; + default: done(DONE_BAD_REQUEST, ['error' => 'action invalid']); diff --git a/client/scripts/database.js b/client/scripts/database.js new file mode 100644 index 0000000..4bd023a --- /dev/null +++ b/client/scripts/database.js @@ -0,0 +1,799 @@ +const DB_VERSION = 3; + +const USER_ID = localStorage.getItem('auth_user'); +const USER_NAME = localStorage.getItem('auth_username'); + +var canUseLocalDB = false; +var syncTimer = null; +var updateSyncStatusTimer; +var syncInProgress = 0; +var db; +var user = null; + +var getJSON = function(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhr.responseType = 'json'; + xhr.timeout = 60000; + xhr.onload = function() { + callback(xhr.status, xhr.response); + }; + xhr.ontimeout = function () { + console.log("getJSON: timeout"); + callback(0, null); + } + xhr.onerror = function () { + console.log("getJSON: error"); + callback(0, null); + } + if (USER_ID != null) { + authString = 'auth[id]=' + localStorage.getItem('auth_id'); + authString += '&auth[hash]=' + encodeURIComponent(localStorage.getItem('auth_hash')); + xhr.send(authString); + } else { + xhr.send(); + } +}; + +function isLoggedIn() { + return USER_ID !== null; +} + +function search(string, fields) { + var keywords = string.split(' '); + for (kid in keywords) { + var keyword = keywords[kid].toLowerCase(); + var found = false; + for (fid in fields) { + var field = fields[fid].toLowerCase(); + if (field.indexOf(keyword) >= 0) { + found = true; + break; + } + } + if (!found) return false; + } + return true; +} + +function dbGetData(table, id = null) { + return new Promise(function(resolve) { + if (canUseLocalDB) { + if (id == null) { + db.transaction(table).objectStore(table).getAll().onsuccess = function (event) { + resolve(event.target.result); + }; + } else { + var request = db.transaction(table).objectStore(table).get(id.toString()); + request.onsuccess = function (event) { + resolve(typeof request.result != 'undefined' ? request.result : null); + } + } + } else { + if (id == null) { + getJSON(QUERY_URL + 'get_' + table, function (code, data) { + if (code == 200) { + resolve(data.data); + } else { + console.log("Something went wrong (HTTP " + code + ")"); + fail(strings.error_network, 5000); + resolve([]); + } + }); + } else { + getJSON(QUERY_URL + 'get_' + table.substr(0, table.length - 1) + '?id=' + id, function (code, data) { + if (code == 200) { + resolve(data.data); + } else { + console.log("Something went wrong (HTTP " + code + ")"); + fail(strings.error_network, 5000); + resolve(null); + } + }); + } + } + }); +} + +function dbGetDataIndex(table, indexName, value) { + return new Promise(function(resolve) { + if (canUseLocalDB) { + var request = db.transaction(table).objectStore(table).index(indexName).getAll(IDBKeyRange.only(value.toString())); + request.onsuccess = function (event) { + resolve(request.result); + } + } else { + getJSON(QUERY_URL + 'get_' + table + '?index=' + indexName + '&value=' + value, function (code, data) { + if (code == 200) { + resolve(data.data); + } else { + console.log("Something went wrong (HTTP " + code + ")"); + fail(strings.error_network, 5000); + resolve([]); + } + }); + } + }); +} + +function dbGetRegattasRange(minDate, maxDate) { + return new Promise(async function(resolve) { + var regattas = await dbGetData('regattas'); + var result = []; + for (id in regattas) { + var regatta = regattas[id]; + var dateFrom = parseDate(regatta['date']); + var dateTo = parseDate(regatta['date']); + dateTo.setDate(dateTo.getDate() + parseInt(regatta['length']) - 1); + if ((minDate <= dateTo) && (maxDate >= dateFrom)) { + regatta['dateFrom'] = dateFrom; + regatta['dateTo'] = dateTo; + result.push(regatta); + } + } + result.sort(function (a, b) { + if (a['date'] < b['date']) return -1; + if (a['date'] > b['date']) return 1; + return 0; + }); + resolve(result); + }); +} + +var compareResultsRaceCount; +function compareResults (a, b) { + if (a['netto'] != b['netto']) return (a['netto'] < b['netto']) ? -1 : 1; + var tempA = [...a['values']]; + tempA.sort(); + var tempB = [...b['values']]; + tempB.sort(); + for (var i = 0; i < compareResultsRaceCount; i ++) { + if (tempA[i] != tempB[i]) return (tempA[i] < tempB[i]) ? -1 : 1; + } + for (var i = compareResultsRaceCount - 1; i >= 0; i --) { + if (a['values_all'][i] != b['values_all'][i]) return (a['values_all'][i] < b['values_all'][i]) ? -1 : 1; + } + return 0; +} + +function dbGetResultCalculated(regatta) { + return new Promise(async function(resolve) { + var results = await dbGetDataIndex('results', 'regatta', regatta.id); + if (results.length > 0) { + + var gemeldet = results.length; + + for (id in results) { + results[id]['finished'] = false; + results[id]['values'] = []; + results[id]['values_all'] = []; + results[id]['texts'] = []; + var copy = []; + + for (var i = 0; i < regatta['races']; i ++) { + var race = results[id]['race' + (i + 1)].replace(',', '.'); + + if (!isNaN(race)) { + copy[i] = results[id]['values'][i] = parseFloat(race); + results[id]['texts'][i] = race; + results[id]['finished'] = true; + } else { + switch (race.toUpperCase()) { + // Nicht gestartet + case 'DNC': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Did not come + case 'DNS': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Did not started + // Startfehler + case 'OCS': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // On course site + case 'UFD': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Uniform Flag Disqualified (disqu. nach 30.3) + case 'BFD': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Black Flag Disqualified (disqu. nach 30.4) + // Nicht durch Ziel gegangen + case 'DNF': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Did not finish + case 'RET': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Retired (Aufgegeben) + case 'RAF': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Retired after finish + // Disqualifizierun + case 'DSQ': results[id]['values'][i] = gemeldet + 1; copy[i] = gemeldet + 1; break; // Disqualified + case 'DNE': results[id]['values'][i] = gemeldet + 1; copy[i] = -1; break; // Disqualified, not excludable (disqu. kann nach 90.3(b) nicht gestrichen werden) + case 'DGM': results[id]['values'][i] = gemeldet + 1; copy[i] = -2; break; // Disqualification Gross Missconduct (kann nach 69.1(b)(2) nicht gestr. werden, grobes Fehlverhalten) + // Unbekannt + default: results[id]['values'][i] = 0; copy[i] = 0; break; + } + + if (results[id]['values'][i] != 0) { + results[id]['texts'][i] = race + ' (' + results[id]['values'][i] + ')'; + } else { + results[id]['texts'][i] = race + ' (Unknown - 0)'; + } + } + } + + results[id]['values_all'] = [...results[id]['values']]; + for (var s = 0; s < regatta['streicher']; s ++) { + var max = Math.max(...copy); + for (var i = 0; i < regatta['races']; i ++) { + if (copy[i] == max) { + copy[i] = 0; + break; + } + } + } + + var brutto = 0; + var netto = 0; + for (var i = 0; i < regatta['races']; i ++) { + brutto += results[id]['values_all'][i]; + if (copy[i] == -1) { results[id]['values'][i] = gemeldet + 1; } + else if (copy[i] == -2) { results[id]['values'][i] = gemeldet + 1; } + else { results[id]['values'][i] = copy[i]; } + if (results[id]['values'][i] == 0) { + results[id]['texts'][i] = '[' + results[id]['texts'][i] + ']'; + } + netto += results[id]['values'][i]; + } + results[id]['brutto'] = brutto; + results[id]['netto'] = netto; + } + + compareResultsRaceCount = regatta['races']; + + results.sort(compareResults); + + var place = 1; + for (id in results) { + if ((id > 0) && (compareResults(results[id], results[id - 1]) == 0)) { + results[id]['place'] = results[id - 1]['place']; + } else { + results[id]['place'] = place; + } + place ++; + } + + resolve(results); + + } else { + resolve([]); + } + }); +} + +async function updateSyncStatus() { // TODO +// var syncStatus = document.getElementById('syncstatus'); +// var lastSync = await dbGetData('update_times', 'last_sync'); +// lastSync = new Date(lastSync.time * 1000); +// var now = new Date(); +// var diff = Math.round((now - lastSync) / 1000); +// var txt = ''; +// +// if (diff < 30) { // 30 sec +// txt = 'jetzt'; +// } else if (diff < 3600) { // 60 min +// diff = Math.round(diff / 60); +// txt = 'vor ' + diff + ' ' + (diff == 1 ? 'Minute' : 'Minuten'); +// } else if (diff < 86400) { // 24 std +// diff = Math.round(diff / 3600); +// txt = 'vor ' + diff + ' ' + (diff == 1 ? 'Stunde' : 'Stunden'); +// } else { +// diff = Math.round(diff / 86400); +// txt = 'vor ' + diff + ' ' + (diff == 1 ? 'Tag' : 'Tagen'); +// } +// +// var btn = ' Sync'; +// syncStatus.innerHTML = 'Zuletzt aktualisiert: ' + txt + btn; +} + +async function runPageScript() { + if (canUseLocalDB) { + updateSyncStatus(); + } + if (typeof updateSyncStatusTimer == 'undefined') { // TODO +// var syncStatus = document.getElementById('syncstatus'); + if (canUseLocalDB) { + updateSyncStatusTimer = window.setInterval(updateSyncStatus, 10000); + } else { +// syncStatus.innerHTML = 'Keine Offline-Nutzung möglich.'; + updateSyncStatusTimer = null; + } +// syncStatus.style.display = 'block'; + } + + if (typeof siteScript === 'function') { + siteScript(); + } + hideLoader(); +} + +function sync() { + if (!canUseLocalDB) return false; + if (syncInProgress > 0) return false; + + var now = Math.floor(Date.now() / 1000); + + db.transaction('update_times').objectStore('update_times').getAll().onsuccess = function (event) { + var localTimes = {}; + event.target.result.forEach(function (entry) { + localTimes[entry['table']] = entry['time']; + }); + + syncInProgress = 10; + var syncOkay = true; + console.log("Sync Start"); + + var interval = window.setInterval(function () { + if (syncInProgress <= 0) { + window.clearInterval(interval); + if (syncOkay) { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'last_sync', time: now }); + } + console.log("Sync Stop"); + + runPageScript(); + } + }, 100); + + getJSON(QUERY_URL + 'get_update_time', function (code, serverTimes) { + if (code == 200) { + + // CLUBS + if (localTimes['clubs'] < serverTimes['clubs']) { + getJSON(QUERY_URL + 'get_clubs?changer-after=' + localTimes['clubs'], function (code, data) { + if (code == 200) { + var os = db.transaction('clubs', 'readwrite').objectStore('clubs'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'clubs', time: serverTimes['clubs'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // BOATS + if (localTimes['boats'] < serverTimes['boats']) { + getJSON(QUERY_URL + 'get_boats?changer-after=' + localTimes['boats'], function (code, data) { + if (code == 200) { + var os = db.transaction('boats', 'readwrite').objectStore('boats'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'boats', time: serverTimes['boats'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // SAILORS + if (localTimes['sailors'] < serverTimes['sailors']) { + getJSON(QUERY_URL + 'get_sailors?changer-after=' + localTimes['sailors'], function (code, data) { + if (code == 200) { + var os = db.transaction('sailors', 'readwrite').objectStore('sailors'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'sailors', time: serverTimes['sailors'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // REGATTAS + if (localTimes['regattas'] < serverTimes['regattas']) { + getJSON(QUERY_URL + 'get_regattas?changer-after=' + localTimes['regattas'], function (code, data) { + if (code == 200) { + var os = db.transaction('regattas', 'readwrite').objectStore('regattas'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = async function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + // update years + var regattas = await dbGetData('regattas'); + var years = {}; + for (id in regattas) { + var entry = regattas[id]; + var date = parseDate(entry['date']); + var y = date.getFullYear(); + years[y] = y; + } + var osYears = db.transaction('years', 'readwrite').objectStore('years'); + osYears.clear().onsuccess = function (event) { + for (var y in years) { + osYears.put({ year: y }); + } + } + + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'regattas', time: serverTimes['regattas'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // RESULTS + if (localTimes['results'] < serverTimes['results']) { + getJSON(QUERY_URL + 'get_results?changer-after=' + localTimes['results'], function (code, data) { + if (code == 200) { + var os = db.transaction('results', 'readwrite').objectStore('results'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'results', time: serverTimes['results'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // PLANNINGS + if (localTimes['plannings'] < serverTimes['plannings']) { + getJSON(QUERY_URL + 'get_plannings?changer-after=' + localTimes['plannings'], function (code, data) { + if (code == 200) { + var os = db.transaction('plannings', 'readwrite').objectStore('plannings'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'plannings', time: serverTimes['plannings'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + if (isLoggedIn()) { + // TRIM_BOATS + if (localTimes['trim_boats'] < serverTimes['trim_boats']) { + getJSON(QUERY_URL + 'get_trim_boats?changer-after=' + localTimes['trim_boats'], function (code, data) { + if (code == 200) { + var os = db.transaction('trim_boats', 'readwrite').objectStore('trim_boats'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'trim_boats', time: serverTimes['trim_boats'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // TRIM_USERS + if (localTimes['trim_users'] < serverTimes['trim_users']) { + getJSON(QUERY_URL + 'get_trim_users?changer-after=' + localTimes['trim_users'], function (code, data) { + if (code == 200) { + var os = db.transaction('trim_users', 'readwrite').objectStore('trim_users'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'trim_users', time: serverTimes['trim_users'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + // TRIM_TRIMS + if (localTimes['trim_trims'] < serverTimes['trim_trims']) { + getJSON(QUERY_URL + 'get_trim_trims?changer-after=' + localTimes['trim_trims'], function (code, data) { + if (code == 200) { + var os = db.transaction('trim_trims', 'readwrite').objectStore('trim_trims'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'trim_trims', time: serverTimes['trim_trims'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + } else { + syncInProgress -= 3; + } + + // USERS + if (localTimes['users'] < serverTimes['users']) { + getJSON(QUERY_URL + 'get_users?changer-after=' + localTimes['users'], function (code, data) { + if (code == 200) { + var os = db.transaction('users', 'readwrite').objectStore('users'); + console.log(data); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'users', time: serverTimes['users'] }); + syncInProgress --; + } + }; + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + } + }); + } else { + syncInProgress --; + } + + } else { + console.log("Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress = 0; + } + }); + }; +} + +function checkSync() { + if (!canUseLocalDB) return; + var osUpdateTimes = db.transaction('update_times').objectStore('update_times'); + osUpdateTimes.get('last_sync').onsuccess = function (event) { + var lastSync = event.target.result.time; + var now = Math.floor(Date.now() / 1000); + if ((lastSync + 600) < now) { // sync max all 10 minutes + sync(); + } + }; +} + +function initDatabase() { + if (window.indexedDB) { + var request = window.indexedDB.open('regatten_app_db_' + BOATCLASS, DB_VERSION); + request.onerror = function (event) { + console.log("Cannot open DB: " + event.target.errorCode); + + runPageScript(); + }; + request.onsuccess = function (event) { + console.log("Database loaded"); + db = event.target.result; + + db.onversionchange = function (event) { + if (syncTimer != null) window.clearInterval(syncTimer); + if (updateSyncStatusTimer != null) window.clearInterval(updateSyncStatusTimer); +// TODO document.getElementById('syncstatus').innerHTML = ''; + canUseLocalDB = false; + db.close(); + location.reload; + } + + db.onerror = function (event) { + console.log("DB Error: " + event.target.errorCode); + }; + + canUseLocalDB = true; + + db.transaction('update_times').objectStore('update_times').get('last_sync').onsuccess = function (event) { + var lastSync = event.target.result.time; + if (lastSync > 0) { + runPageScript(); + } + }; + + checkSync(); + + syncTimer = window.setInterval(checkSync, 300000); // 5 min + + window.ononline = function () { + checkSync(); + } + }; + request.onupgradeneeded = function (event) { + var db = event.target.result; + var upgradeTransaction = event.target.transaction; + var oldVersion = event.oldVersion; + var newVersion = event.newVersion; + + console.log("Datenbank Version Upgrade von " + oldVersion + " auf " + newVersion); + + if ((oldVersion < 1) && (newVersion >= 1)) { + console.log('to version 1'); + var osClubs = db.createObjectStore('clubs', { keyPath: 'id' }); + var osBoats = db.createObjectStore('boats', { keyPath: 'id' }); + var osSailors = db.createObjectStore('sailors', { keyPath: 'id' }); + var osRegattas = db.createObjectStore('regattas', { keyPath: 'id' }); + var osResults = db.createObjectStore('results', { keyPath: 'id' }); + osResults.createIndex('regatta', 'regatta', { unique: false }); + var osPlannings = db.createObjectStore('plannings', { keyPath: 'id' }); + osPlannings.createIndex('user', 'user', { unique: false }); + osPlannings.createIndex('regatta', 'regatta', { unique: false }); + var osTrimBoats = db.createObjectStore('trim_boats', { keyPath: 'id' }); + var osTrimUsers = db.createObjectStore('trim_users', { keyPath: 'id' }); + osTrimUsers.createIndex('boat', 'boat', { unique: false }); + var osTrimTrims = db.createObjectStore('trim_trims', { keyPath: 'id' }); + osTrimTrims.createIndex('boat', 'boat', { unique: false }); + var osUpdateTimes = db.createObjectStore('update_times', { keyPath: 'table' }); + osUpdateTimes.add({ table: 'last_sync', time: 0 }); + osUpdateTimes.add({ table: 'clubs', time: 0 }); + osUpdateTimes.add({ table: 'boats', time: 0 }); + osUpdateTimes.add({ table: 'sailors', time: 0 }); + osUpdateTimes.add({ table: 'regattas', time: 0 }); + osUpdateTimes.add({ table: 'results', time: 0 }); + osUpdateTimes.add({ table: 'plannings', time: 0 }); + osUpdateTimes.add({ table: 'trim_boats', time: 0 }); + osUpdateTimes.add({ table: 'trim_users', time: 0 }); + osUpdateTimes.add({ table: 'trim_trims', time: 0 }); + } + + if ((oldVersion < 2) && (newVersion >= 2)) { + console.log('to version 2'); + var osUsers = db.createObjectStore('users', { keyPath: 'id' }); + osUsers.createIndex('username', 'username', { unique: true }); + var osUpdateTimes = upgradeTransaction.objectStore('update_times'); + osUpdateTimes.add({ table: 'users', time: 0 }); + } + + if ((oldVersion < 3) && (newVersion >= 3)) { + console.log('to version 3'); + var osYears = db.createObjectStore('years', { keyPath: 'year' }); + var osUpdateTimes = upgradeTransaction.objectStore('update_times'); + osUpdateTimes.put({ table: 'regattas', time: 0 }); + } + + var osUpdateTimes = upgradeTransaction.objectStore('update_times'); + osUpdateTimes.put({ table: 'last_sync', time: 0 }); + } + } else { + runPageScript(); + } +} diff --git a/client/scripts/datetime.js b/client/scripts/datetime.js new file mode 100644 index 0000000..ac5e354 --- /dev/null +++ b/client/scripts/datetime.js @@ -0,0 +1,87 @@ +function parseDate(string) { + var year, month, day; + if (string.includes('.')) { + var split = string.split('.'); + if (split.length != 3) return null; + year = parseInt(split[2]); + month = parseInt(split[1]); + day = parseInt(split[0]); + } else if (string.includes('/')) { + var split = string.split('/'); + if (split.length != 3) return null; + year = parseInt(split[2]); + month = parseInt(split[0]); + day = parseInt(split[1]); + } else if (string.includes('-')) { + var split = string.split('-'); + if (split.length != 3) return null; + if (split[2].length > 2) { + year = parseInt(split[2]); + month = parseInt(split[1]); + day = parseInt(split[0]); + } else { + year = parseInt(split[0]); + month = parseInt(split[1]); + day = parseInt(split[2]); + } + } else { + return null; + } + + if (isNaN(year) || isNaN(month) || isNaN(day)) return null; + if (year.toString().length == 2) year = (year < 70 ? 2000 : 1900) + year; + if ((year < 1970) || (year > 3000)) return null; + if ((month < 1) || (month > 12)) return null; + if ((day < 1) || (day > 31)) return null; + + var date = new Date(Date.UTC(year, month - 1, day)); + return date; +} + +function getToday() { + var date = new Date(); + date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + return date; +} + +function formatDate(format, date = null) { + if (date === null) { + date = new Date(); + } else { + date = new Date(date.valueOf()); + date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + } + + format = format.replace("M", "%1%"); + format = format.replace("F", "%2%"); + format = format.replace("D", "%3%"); + format = format.replace("l", "%4%"); + + var tmp = date.getFullYear().toString(); + var tmp2 = tmp.substr(2); + format = format.replace("Y", tmp); + format = format.replace('y', tmp2); + + tmp = (date.getMonth() + 1).toString(); + tmp2 = (tmp.length > 1 ? tmp : ('0' + tmp)); + format = format.replace('n', tmp); + format = format.replace('m', tmp2); + + tmp = date.getDate().toString(); + tmp2 = (tmp.length > 1 ? tmp : ('0' + tmp)); + format = format.replace('j', tmp); + format = format.replace('d', tmp2); + + tmp = date.getDay(); + tmp2 = (tmp == 0 ? 7 : tmp); + format = format.replace('w', tmp); + format = format.replace('N', tmp2); + + format = format.replace('%1%', strings.months_short[date.getMonth()]); + format = format.replace('%2%', strings.months_long[date.getMonth()]); + + format = format.replace('%3%', strings.weekdays_short[date.getDay()]); + format = format.replace('%4%', strings.weekdays_long[date.getDay()]); + + return format; +} diff --git a/client/scripts/regatten.js.php b/client/scripts/regatten.js.php index 1885768..b920241 100644 --- a/client/scripts/regatten.js.php +++ b/client/scripts/regatten.js.php @@ -6,7 +6,8 @@ ?> -const apiUrl = '/api/'; +const QUERY_URL = '/api/'; +const BOATCLASS = ''; var randomId = function() { return '_' + Math.random().toString(36).substr(2, 9); } @@ -181,33 +182,53 @@ var logout = function() { }); } -var initRegatten = function() { - loggedin = (localStorage.getItem('auth_id') !== null); - - if (loggedin) { - var auth = { - id: localStorage.getItem('auth_id'), - hash: localStorage.getItem('auth_hash') - } - var user = { - id: localStorage.getItem('auth_user'), - name: localStorage.getItem('auth_username') - } - if ((auth.hash === null) || (user.id === null) || (user.name === null)) { - logoutClearStorage(); - return; +function resetDb() { + $('#menu-developer').hideMenu(); + if (canUseLocalDB) { + showLoader(); + var request = window.indexedDB.deleteDatabase('regatten_app_db_' + BOATCLASS); + request.onerror = function (event) { + console.log("Cannot open DB: " + event.target.errorCode); + toastError('There was an error deleting your database.
Please report this to dev@regatten.net'); + hideLoader(); + }; + request.onsuccess = function (event) { + console.log('DB deleted'); + toastInfo('The database was deleted. Please reload or close this tab.
At the next visit, a new database will be created.'); + hideLoader(); } + } else { + toastWarn('Your device does not support storing data locally. All data is fetched directly from our server.
As a result, you can not reset your database.'); } +} + +function resetCache() { + $('#menu-developer').hideMenu(); + navigator.serviceWorker.getRegistrations().then(function (registrations) { + for (let registration of registrations) { + console.log('Unregister sW:', registration); + registration.unregister(); + } + }); + caches.keys().then((keyList) => { + return Promise.all(keyList.map((key) => { + console.log('Cache deleted:', key); + return caches.delete(key); + })); + }); + toastInfo('The serviceWorker and the cache were deleted. A new serviceWorker will be generated on the next refresh.'); +} + +var initRegatten = function() { + showLoader(); - if (loggedin) { + initDatabase(); + + if (isLoggedIn) { $('.show-notloggedin').css('display', 'none'); - $('.replace-userid-href').attr('href', $('.replace-userid-href').attr('href').replace('%USERID%', user.id)); - $('.replace-username').html(user.name); + $('.replace-userid-href').attr('href', $('.replace-userid-href').attr('href').replace('%USERID%', USER_ID)); + $('.replace-username').html(USER_NAME); } else { $('.show-loggedin').css('display', 'none'); } - - if (typeof siteScript !== 'undefined') { - siteScript(); - } } \ No newline at end of file diff --git a/client/scripts/strings.js.php b/client/scripts/strings.js.php index ae61288..d53b9d5 100644 --- a/client/scripts/strings.js.php +++ b/client/scripts/strings.js.php @@ -8,4 +8,51 @@ const strings = { inetMsgOffline: "Keine Internet-Verbindung erkannt", inetMsgOnline: "Du bist wieder online", + error_network: "Verbindung fehlgeschlagen.
Stelle sicher, dass Du mit dem Internet verbunden bist und versuche es erneut!", + months_short: [ + 'Jan.', + 'Feb.', + 'März', + 'Apr.', + 'Mai', + 'Juni', + 'Juli', + 'Aug.', + 'Sep.', + 'Okt.', + 'Nov.', + 'Dez.' + ], + months_long: [ + 'Januar', + 'Februar', + 'März', + 'April', + 'Mai', + 'Juni', + 'Juli', + 'August', + 'September', + 'Oktober', + 'November', + 'Dezember' + ], + weekdays_short: [ + 'So', + 'Mo', + 'Di', + 'Mi', + 'Do', + 'Fr', + 'Sa' + ], + weekdays_long: [ + 'Sonntag', + 'Montag', + 'Dienstag', + 'Mittwoch', + 'Donnerstag', + 'Freitag', + 'Samstag' + ], } \ No newline at end of file diff --git a/content/index.php b/content/index.php index 9baa79b..3b4130a 100644 --- a/content/index.php +++ b/content/index.php @@ -7,30 +7,52 @@ $sp['output'] .= $tpl->load('card', [$content]); // Favorites - $content = "

Deine Favoriten

"; - $thead = "SeglerRangliste 2020"; + $content = '

Deine Favoriten

'; + $thead = 'SeglerRangliste'; $content .= $tpl->load('table', [$thead, 'html-id' => 'table-favorites', 'css-class' => 'mb-0 mt-3']); + $content .= '

'; + $content .= 'Du folgst keinen Seglern.
'; + $content .= 'Um jemandem zu folgen, gehe zur Segler-Liste und wähle bis zu fünf Segler aus.'; + $content .= '

'; $sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-favorites']); // Planning next - $content = "

Deine nächsten Regatten

"; - $thead = "DatumRegattaInformationenRLFSegler"; + $content = '

Deine nächsten Regatten

'; + $thead = 'DatumRegattaInformationenRLFSegler'; $content .= $tpl->load('table', [$thead, 'html-id' => 'table-yournext', 'css-class' => 'mb-0 mt-3']); + $content .= '

'; + $content .= 'Du fährst in den nächsten vier Wochen auf keine Regatta!'; + $content .= '

'; $sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-yournext']); + // Not logged in + $content = '

Nicht angemeldet

'; + $content .= '

'; + $content .= 'Um alle Funktionen dieser Seite nutzen zu können, logge Dich bitte ein.
'; + $content .= 'Solltest Du noch kein Benutzerkonto haben, kannst Du Dich kostenlos registrieren.'; + $content .= '

'; + + $sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-notloggedin']); + // Next - $content = "

Nächste Regatten

"; - $thead = "DatumRegattaInformationenRLF"; + $content = '

Nächste Regatten

'; + $thead = 'DatumRegattaInformationenRLF'; $content .= $tpl->load('table', [$thead, 'html-id' => 'table-next', 'css-class' => 'mb-0 mt-3']); + $content .= '

'; + $content .= 'Keine Regatten in den nächsten zwei Wochen!'; + $content .= '

'; $sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-next']); // Last - $content = "

Letzte Regatten

"; - $thead = "DatumRegattaErgebnisseRLF"; + $content = '

Letzte Regatten

'; + $thead = 'DatumRegattaErgebnisseRLF'; $content .= $tpl->load('table', [$thead, 'html-id' => 'table-last', 'css-class' => 'mb-0 mt-3']); + $content .= '

'; + $content .= 'Keine Regatten in den letzten zwei Wochen!'; + $content .= '

'; $sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-last']); @@ -46,20 +68,6 @@ $sp['output'] .= $tpl->load('card', [$content]); - $sp['scripts'] = ' - - '; + $sp['scripts'] = '' . $scripts->load('index'); ?> \ No newline at end of file diff --git a/index.php b/index.php index 8585582..cdad4b6 100644 --- a/index.php +++ b/index.php @@ -3,6 +3,7 @@ require_once(__DIR__ . '/server/config.php'); require_once(__DIR__ . '/server/log.php'); require_once(__DIR__ . '/server/templates.php'); + require_once(__DIR__ . '/server/scripts.php'); $request = false; if (isset($_GET['request'])) { @@ -36,6 +37,7 @@ ]; $tpl = new Templates(__DIR__ . '/server/templates/'); + $scripts = new Scripts(__DIR__ . '/server/scripts/'); require_once(__DIR__ . '/content/' . $site . '.php'); diff --git a/server/page/headerfooter.php b/server/page/headerfooter.php index 120e37d..79c39d6 100644 --- a/server/page/headerfooter.php +++ b/server/page/headerfooter.php @@ -7,6 +7,7 @@ else echo ''; } ?> + + +