Compare commits

...

9 Commits
v_1.4 ... v_1.6

Author SHA1 Message Date
Timon Ostertun
1b1176b952 Release v_1.6 2020-09-27 23:18:37 +02:00
Timon Ostertun
00c1c93b80 Added news 2020-09-27 23:17:56 +02:00
Timon Ostertun
876776eb5b Added notifications 2020-09-27 21:54:43 +02:00
Timon Ostertun
a92dc45bb7 Can subscribe to pushes 2020-09-27 14:35:25 +02:00
Timon Ostertun
2bf623733d Release v_1.5 2020-09-27 01:35:43 +02:00
Timon Ostertun
60caf85daa Added add_year, add_boatname 2020-09-27 01:33:45 +02:00
Timon Ostertun
eddcff9e39 Added contact 2020-09-27 00:53:53 +02:00
Timon Ostertun
967ad50755 Moved api backend to main website 2020-09-27 00:05:57 +02:00
Timon Ostertun
a437d05647 Small fixes 2020-09-26 23:42:00 +02:00
26 changed files with 750 additions and 1273 deletions

View File

@@ -12,7 +12,7 @@ RewriteRule ^(.*)server(.*)$ / [R=301,L,NC]
### CONTENT LOADER ### CONTENT LOADER
# Keep this subfolders untouched # Keep this subfolders untouched
RewriteRule ^(api)($|/) - [L] #RewriteRule ^(api)($|/) - [L]
# Show site # Show site
RewriteRule ^([^\.]*)$ index.php?request=$1 [QSA] RewriteRule ^([^\.]*)$ index.php?request=$1 [QSA]

View File

@@ -1,8 +0,0 @@
RewriteEngine on
# root directory:
RewriteBase /projects/RegattenApp/api/
# Show site
RewriteRule ^(.*)$ index.php?request=$1 [QSA]

View File

@@ -1,37 +0,0 @@
<?php
// DATABASE Credentials
define('DB_USER', 'regattenwebsite');
define('DB_PASS', 'RBpOv4YYtZKWIGcN');
define('DB_HOST', 'localhost');
define('DB_DATABASE', 'regattenwebsite');
define('DB_CHANGE_TIME', true);
define('DB_USE_UTF8', true); // use utf-8 in DB requests
// DATABASE Table names
define('DB_TABLE_USERS', 'users');
define('DB_TABLE_LOGINS', 'logins');
define('DB_TABLE_KEEPLOGGEDIN', 'keeploggedin');
define('DB_TABLE_RESET', 'rstpw');
define('DB_TABLE_CLUBS', 'regatta_clubs');
define('DB_TABLE_SUFFIX_BOATS', '_boats');
define('DB_TABLE_SUFFIX_SAILORS', '_sailors');
define('DB_TABLE_SUFFIX_PLANNING', '_planning');
define('DB_TABLE_SUFFIX_REGATTAS', '_regattas');
define('DB_TABLE_SUFFIX_RESULTS', '_results');
define('DB_TABLE_TRIM_BOATS', 'trim_boats');
define('DB_TABLE_TRIM_USERS', 'trim_users');
define('DB_TABLE_TRIM_TRIMS', 'trim_trims');
define('DB_TABLE_NEWS', 'news');
define('DB_TABLE_UPDATETIMES', '_updatetimes');
// OUTGOING MAILS - Credentials for outgoing mails
define('MAIL_SMTP_HOST', 'ssl://ostertun.net'); // SMTP Server address
define('MAIL_SMTP_PORT', 465); // port to use
define('MAIL_FROM_ADDRESS', 'no-reply@regatten.net'); // address to send mails from
define('MAIL_USERNAME', MAIL_FROM_ADDRESS); // if true: username
define('MAIL_PASSWORD', 'pVc05j_3'); // & password
?>

View File

@@ -1,157 +0,0 @@
<?php
/*
Mysql Database Support
----------------------
Required defines:
- DB_HOST (STRING)
- DB_USER (STRING)
- DB_PASS (STRING)
- DB_DATABASE (STRING)
- DB_USE_UTF8 (BOOL)
- DB_CHANGE_TIME (BOOL)
Required functions:
- logE (in /_global/log.php)
*/
$mysqli = mysqli_connect(DB_HOST, DB_USER, DB_PASS);
if ($mysqli === false) {
logE("database", "Could not connect to database\n" . mysqli_connect_error);
die('Error: Could not connect to database');
}
mysqli_select_db($mysqli, DB_DATABASE);
if (DB_USE_UTF8) {
mysqli_set_charset($mysqli, 'utf8');
}
function db_get_data($mysqli, $table, $fields = '*', $where = false, $limit = false) {
$rest = '';
if ($where != false) {
$rest .= ' WHERE ' . $where;
}
if ($limit != false) {
$rest .= sprintf(' LIMIT %d', $limit);
}
$query = 'SELECT ' . $fields . ' FROM ' . mysqli_real_escape_string($mysqli, $table) . $rest . ';';
$response = mysqli_query($mysqli, $query);
if ($response !== false) {
$result = array();
if ($response->num_rows > 0) {
$i = 0;
while ($row = $response->fetch_assoc()) {
if (isset($row['id'])) {
$id = $row['id'];
} else {
$id = $i;
$i ++;
}
foreach ($row as $key => $value) {
$result[$id][$key] = $value;
}
}
}
return $result;
} else {
logE("database", "get_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
return false;
}
}
function db_update_data($mysqli, $table, $data, $where, $limit = false) {
$rest = '';
if ($where != false) {
$rest .= ' WHERE ' . $where;
}
if ($limit != false) {
$rest .= sprintf(' LIMIT %d', $limit);
}
$set = '';
$first = true;
foreach ($data as $key => $value) {
if ($first) {
$first = false;
} else {
$set .= ', ';
}
if ($value === null) {
$set .= '`' . mysqli_real_escape_string($mysqli, $key) . '`=NULL';
} else {
$set .= '`' . mysqli_real_escape_string($mysqli, $key) . '`="' . mysqli_real_escape_string($mysqli, $value) . '"';
}
}
if (defined('DB_CHANGE_TIME')) $set .= ', `changed`=NOW()';
$query = 'UPDATE ' . mysqli_real_escape_string($mysqli, $table) . ' SET ' . $set . $rest . ';';
$response = mysqli_query($mysqli, $query);
if ($response === false) {
logE("database", "update_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
} elseif (defined('DB_CHANGE_TIME')) {
mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
}
return $response;
}
function db_insert_data($mysqli, $table, $data) {
$fields = '';
$values = '';
$first = true;
foreach ($data as $key => $value) {
if ($first) {
$first = false;
} else {
$fields .= ', ';
$values .= ', ';
}
$fields .= '`' . mysqli_real_escape_string($mysqli, $key) . '`';
if ($value === null) {
$values .= 'NULL';
} else {
$values .= '"' . mysqli_real_escape_string($mysqli, $value) . '"';
}
}
if (defined('DB_CHANGE_TIME')) {
$fields .= ', `changed`';
$values .= ', NOW()';
}
$query = 'INSERT INTO `' . mysqli_real_escape_string($mysqli, $table) . '` (' . $fields . ') VALUES (' . $values . ');';
$response = mysqli_query($mysqli, $query);
if ($response === false) {
logE("database", "insert_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
} else {
$response = mysqli_insert_id($mysqli);
if (defined('DB_CHANGE_TIME')) {
mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
}
}
return $response;
}
function db_delete_data($mysqli, $table, $where, $limit = false) {
$rest = '';
if ($where != false) {
$rest .= ' WHERE ' . $where;
}
if ($limit != false) {
$rest .= sprintf(' LIMIT %d', $limit);
}
$query = 'DELETE FROM `' . mysqli_real_escape_string($mysqli, $table) . '`' . $rest . ';';
$response = mysqli_query($mysqli, $query);
if ($response === false) {
logE("database", "delete_data\nInvalid request\n" . $query . "\n" . mysqli_error($mysqli));
} elseif (defined('DB_CHANGE_TIME')) {
mysqli_query($mysqli, 'UPDATE `_updatetimes` SET `update`=NOW() WHERE `table`="' . mysqli_real_escape_string($mysqli, $table) . '";');
}
return $response;
}
?>

View File

@@ -1,450 +0,0 @@
<?php
function get_db_entry($mysqli, $table, $id = false, $order = false) {
if ($id === false) {
return db_get_data($mysqli, $table, '*', ($order !== false ? ('1=1 ORDER BY ' . $order) : false));
} else {
$result = db_get_data($mysqli, $table, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $id) . '"', 1);
if (($result === false) or (count($result) != 1))
return false;
else
return array_values($result)[0];
}
}
function get_club($mysqli, $id = false) {
return get_db_entry($mysqli, DB_TABLE_CLUBS, $id, '`kurz` ASC');
}
function get_boat($mysqli, $id = false) {
return get_db_entry($mysqli, BOATCLASS . DB_TABLE_SUFFIX_BOATS, $id, '`sailnumber` ASC');
}
function get_sailor($mysqli, $id = false) {
return get_db_entry($mysqli, BOATCLASS . DB_TABLE_SUFFIX_SAILORS, $id, '`name` ASC');
}
function get_planning($mysqli, $userId = false, $regattaId = false) {
$where = '';
$limit = false;
if ($userId !== false) {
$where .= '(`user`="' . mysqli_real_escape_string($mysqli, $userId) . '")';
}
if (($userId !== false) and ($regattaId !== false)) {
$where .= ' AND ';
$limit = 1;
}
if ($regattaId !== false) {
$where .= '(`regatta`="' . mysqli_real_escape_string($mysqli, $regattaId) . '")';
}
if ($where == '') $where = false;
if ($limit === false) {
return db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_PLANNING, '*', $where);
} else {
$result = db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_PLANNING, '*', $where, 1);
if (($result === false) or (count($result) != 1))
return false;
else
return array_values($result)[0];
}
}
function get_regatta($mysqli, $id = false) {
return get_db_entry($mysqli, BOATCLASS . DB_TABLE_SUFFIX_REGATTAS, $id, '`date` ASC');
}
function get_result($mysqli, $regattaId = false) {
if ($regattaId === false) {
return db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_RESULTS);
} else {
return db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_RESULTS, '*', '`regatta` = "' . mysqli_real_escape_string($mysqli, $regattaId) . '"');
}
}
function get_regattas_range($mysqli, $from, $to) {
return db_get_data($mysqli, BOATCLASS . DB_TABLE_SUFFIX_REGATTAS, '*', '(`date` >= "' . 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;
}
}
?>

View File

@@ -1,466 +0,0 @@
<?php
require_once(__DIR__ . '/../server/config.php');
require_once(__DIR__ . '/config.php');
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'])) {
$request = explode('/', $_GET['request']);
}
if ($request === false) {
$request = array();
}
if (count($request) >= 1) {
$action = array_shift($request);
} else {
$action = '';
}
define('DONE_OKAY', 0);
define('DONE_EMPTY', 1);
define('DONE_DATABASE', 2);
define('DONE_UNAUTHORIZED', 3);
define('DONE_BAD_REQUEST', 4);
define('DONE_CONFLICT', 5);
define('DONE_SERVER_ERROR', 6);
function done($donecode, $content = null) {
switch ($donecode) {
case DONE_OKAY:
header('HTTP/1.0 200 OK');
break;
case DONE_EMPTY:
header('HTTP/1.0 204 No Content');
break;
case DONE_DATABASE:
header('HTTP/1.0 500 Internal Server Error');
if ($content === null) {
$content = array('error' => 'database error');
}
break;
case DONE_UNAUTHORIZED:
header('HTTP/1.0 401 Unauthorized');
if ($content === null) {
$content = array('error' => 'unauthorized');
}
break;
case DONE_BAD_REQUEST:
header('HTTP/1.0 400 Bad Request');
if ($content === null) {
$content = array('error' => 'bad request');
}
break;
case DONE_CONFLICT:
header('HTTP/1.0 409 Conflict');
break;
case DONE_SERVER_ERROR:
header('HTTP/1.0 500 Internal Server Error');
break;
default:
header('HTTP/1.0 500 Internal Server Error');
break;
}
header('Content-Type: application/json');
if ($content !== null) {
echo json_encode($content);
} else {
echo '{ }';
}
exit;
}
if (isset($_REQUEST['auth']['id'], $_REQUEST['auth']['hash'])) {
$user_id = auth_check($mysqli, $_REQUEST['auth']['id'], $_REQUEST['auth']['hash']);
} else {
$user_id = false;
}
function isLoggedIn() {
global $user_id;
return $user_id !== false;
}
function checkLoggedIn() {
if (!isLoggedIn()) done(DONE_UNAUTHORIZED, ['error' => 'permission denied']);
}
function checkRequest($param) {
if (!isset($_REQUEST[$param])) done(DONE_BAD_REQUEST, ['error' => 'missing parameter: ' . $param]);
}
function replaceChanged($array) {
return array_map(function ($entry) {
unset($entry['changed']);
return $entry;
}, $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':
checkRequest('username');
checkRequest('password');
checkRequest('device');
$auth = auth_login($mysqli, $_REQUEST['username'], $_REQUEST['password'], $_REQUEST['device']);
if ($auth === false) done(DONE_UNAUTHORIZED);
done(DONE_OKAY, $auth);
break;
case 'logout':
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']);
}
?>

View File

@@ -1,107 +0,0 @@
<?php
function get_user($mysqli, $username = null) {
if ($username === null) {
return db_get_data($mysqli, DB_TABLE_USERS);
} else {
$user = db_get_data($mysqli, DB_TABLE_USERS, '*', '`username` = "' . mysqli_real_escape_string($mysqli, $username) . '"', 1);
if (($user === false) or (count($user) != 1)) return false;
return array_values($user)[0];
}
}
function get_user_by_id($mysqli, $user_id) {
$res = db_get_data($mysqli, DB_TABLE_USERS, '*', '`id` = "' . mysqli_real_escape_string($mysqli, $user_id) . '"', 1);
if (($res !== false) and (count($res) == 1)) {
return array_values($res)[0];
}
return false;
}
//function signup($mysqli, $username, $email, $password) {
// if (($username == '') or ($email == '') or ($password == '')) {
// return 1;
// }
// if (get_user($mysqli, $username) !== false) {
// return 1;
// }
// $salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), true));
// $hashpassword = hash('sha512', $password . $salt);
//
// $user = array();
// $user['username'] = $username;
// $user['email'] = $email;
// $user['password'] = $hashpassword;
// $user['salt'] = $salt;
// if (db_insert_data($mysqli, DB_TABLE_USERS, $user) !== false) {
// $values = array();
// $values['USERNAME'] = $username;
// $message = createMail('signup', STRING_SIGNUP_EMAIL_SUBJECT, $values);
// smtp_send_mail(['Regatten.net', MAIL_FROM_ADDRESS], [[$username, $email]], [], [], STRING_SIGNUP_EMAIL_SUBJECT, $message, [['Content-Type', 'text/html; charset="UTF-8"']]);
// // Analytics
// matomo_event('Login', 'SignUp', $username);
// return true;
// } else {
// return 2;
// }
//}
function get_perm($mysqli, $user_id) {
if ($user_id !== false) {
$result = get_user_by_id($mysqli, $user_id);
if ($result !== false) {
return $result[DB_FIELD_PERM];
} else {
return 0;
}
} else {
return 0;
}
}
// ### NEW LOGIN ####################################
function auth_login($mysqli, $username, $password, $device) {
$user = get_user($mysqli, $username);
if ($user === false) {
// User does not exist
return false;
}
$hashpassword = hash('sha512', $password . $user['salt']);
if ($hashpassword !== $user['password']) {
// Password incorrect
return false;
}
// All correct
$auth = [];
$auth['user'] = $user['id'];
$auth['username'] = $user['username'];
$auth['auth'] = str_replace('/', '-', str_replace('+', '_', base64_encode(openssl_random_pseudo_bytes(24))));
$salt = base64_encode(openssl_random_pseudo_bytes(24));
$hash = hash('sha512', $auth['auth'] . $salt);
$data = [
'user' => $user['id'],
'salt' => $salt,
'authhash' => $hash,
'device' => $device
];
$auth['id'] = db_insert_data($mysqli, DB_TABLE_LOGINS, $data);
return $auth;
}
function auth_logout($mysqli, $id) {
db_delete_data($mysqli, DB_TABLE_LOGINS, 'id = "' . mysqli_real_escape_string($mysqli, $id) . '"', 1);
return true;
}
function auth_check($mysqli, $id, $hash) {
$auth = db_get_data($mysqli, DB_TABLE_LOGINS, '*', 'id="' . mysqli_real_escape_string($mysqli, $id) . '"', 1);
if (($auth === false) or (count($auth) != 1)) return false;
$auth = array_values($auth)[0];
$hash = hash('sha512', $hash . $auth['salt']);
if ($hash != $auth['authhash']) return false;
db_update_data($mysqli, DB_TABLE_LOGINS, ['id' => $auth['id']], 'id="' . $auth['id'] . '"', 1); // update changed field => last login
return $auth['user'];
}
?>

View File

@@ -1,4 +1,4 @@
const DB_VERSION = 4; const DB_VERSION = 6;
const USER_ID = localStorage.getItem('auth_user'); const USER_ID = localStorage.getItem('auth_user');
const USER_NAME = localStorage.getItem('auth_username'); const USER_NAME = localStorage.getItem('auth_username');
@@ -397,6 +397,26 @@ function dbGetRanking(minDate, maxDate, jugend, jugstrict) {
}); });
} }
function dbSettingsGet(key) {
return new Promise(function(resolve) {
if (canUseLocalDB) {
var request = db.transaction('settings').objectStore('settings').get(key);
request.onsuccess = function (event) {
resolve(typeof request.result != 'undefined' ? request.result.value : null);
}
} else {
resolve(null);
}
});
}
function dbSettingsSet(key, value) {
if (canUseLocalDB) {
var os = db.transaction('settings', 'readwrite').objectStore('settings');
os.put({ key: key, value: value});
}
}
async function updateSyncStatus() { // TODO async function updateSyncStatus() { // TODO
// var syncStatus = document.getElementById('syncstatus'); // var syncStatus = document.getElementById('syncstatus');
// var lastSync = await dbGetData('update_times', 'last_sync'); // var lastSync = await dbGetData('update_times', 'last_sync');
@@ -433,6 +453,12 @@ async function runPageScript() {
} }
}; };
updateSyncStatus(); updateSyncStatus();
if (isLoggedIn()) {
var plannings = await dbGetDataIndex('plannings', 'user', USER_ID);
plannings = plannings.map(function (e) { return e.regatta; });
dbSettingsSet('myregattas_' + BOATCLASS, plannings);
}
} }
if (typeof updateSyncStatusTimer == 'undefined') { // TODO if (typeof updateSyncStatusTimer == 'undefined') { // TODO
// var syncStatus = document.getElementById('syncstatus'); // var syncStatus = document.getElementById('syncstatus');
@@ -465,7 +491,7 @@ function sync() {
localTimes[entry['table']] = entry['time']; localTimes[entry['table']] = entry['time'];
}); });
syncInProgress = 10; syncInProgress = 11;
var syncOkay = true; var syncOkay = true;
console.log("Sync Start"); console.log("Sync Start");
$('#i-sync').addClass('fa-spin'); $('#i-sync').addClass('fa-spin');
@@ -798,6 +824,38 @@ function sync() {
syncInProgress -= 3; syncInProgress -= 3;
} }
// NEWS
if (localTimes['news'] < serverTimes['news']) {
getJSON(QUERY_URL + 'get_news?changed-after=' + localTimes['news'], function (code, data) {
if (code == 200) {
var os = db.transaction('news', 'readwrite').objectStore('news');
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: 'news', time: serverTimes['news'] });
syncInProgress --;
}
};
} else {
console.log("Something went wrong (HTTP " + code + ")");
syncOkay = false;
syncInProgress --;
}
});
} else {
syncInProgress --;
}
// USERS // USERS
if (localTimes['users'] < serverTimes['users']) { if (localTimes['users'] < serverTimes['users']) {
getJSON(QUERY_URL + 'get_users?changed-after=' + localTimes['users'], function (code, data) { getJSON(QUERY_URL + 'get_users?changed-after=' + localTimes['users'], function (code, data) {
@@ -878,6 +936,8 @@ function initDatabase() {
canUseLocalDB = true; canUseLocalDB = true;
if (typeof onDatabaseLoaded == 'function') onDatabaseLoaded();
db.transaction('update_times').objectStore('update_times').get('last_sync').onsuccess = function (event) { db.transaction('update_times').objectStore('update_times').get('last_sync').onsuccess = function (event) {
var lastSync = event.target.result.time; var lastSync = event.target.result.time;
if (lastSync > 0) { if (lastSync > 0) {
@@ -953,6 +1013,18 @@ function initDatabase() {
osUpdateTimes.add({ table: 'loggedin', status: isLoggedIn() }); osUpdateTimes.add({ table: 'loggedin', status: isLoggedIn() });
} }
if ((oldVersion < 5) && (newVersion >= 5)) {
console.log('to version 5');
var osPushes = db.createObjectStore('settings', { keyPath: 'key' });
}
if ((oldVersion < 6) && (newVersion >= 6)) {
console.log('to version 6');
var osNews = db.createObjectStore('news', { keyPath: 'id' });
var osUpdateTimes = upgradeTransaction.objectStore('update_times');
osUpdateTimes.add({ table: 'news', time: 0 });
}
var osUpdateTimes = upgradeTransaction.objectStore('update_times'); var osUpdateTimes = upgradeTransaction.objectStore('update_times');
osUpdateTimes.put({ table: 'last_sync', time: 0 }); osUpdateTimes.put({ table: 'last_sync', time: 0 });
} }
@@ -976,6 +1048,7 @@ function resetDb(silent = true) {
osUpdateTimes.put({ table: 'trim_boats', time: 0 }); osUpdateTimes.put({ table: 'trim_boats', time: 0 });
osUpdateTimes.put({ table: 'trim_users', time: 0 }); osUpdateTimes.put({ table: 'trim_users', time: 0 });
osUpdateTimes.put({ table: 'trim_trims', time: 0 }); osUpdateTimes.put({ table: 'trim_trims', time: 0 });
osUpdateTimes.put({ table: 'news', time: 0 });
osUpdateTimes.put({ table: 'users', time: 0 }); osUpdateTimes.put({ table: 'users', time: 0 });
console.log('DB update times reset'); console.log('DB update times reset');
if (!silent) if (!silent)

View File

@@ -7,9 +7,11 @@
?> ?>
//Loading the Service Worker //Loading the Service Worker
var swRegistration = null;
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener('load', function() { window.addEventListener('load', async function() {
navigator.serviceWorker.register('<?php echo SERVER_ADDR; ?>/service-worker.js.php'); swRegistration = await navigator.serviceWorker.register('<?php echo SERVER_ADDR; ?>/service-worker.js.php');
if (typeof onServiceWorkerLoaded === 'function') onServiceWorkerLoaded();
}); });
} }

View File

@@ -6,11 +6,12 @@
?> ?>
const QUERY_URL = '<?php echo SERVER_ADDR; ?>/api/'; const QUERY_URL = '<?php echo QUERY_URL; ?>';
const BOATCLASS = '<?php echo BOATCLASS; ?>'; const BOATCLASS = '<?php echo BOATCLASS; ?>';
const LINK_PRE = '<?php echo SERVER_ADDR; ?>/'; const LINK_PRE = '<?php echo SERVER_ADDR; ?>/';
const YOUTH_AGE = '<?php echo $_CLASS['youth-age']; ?>'; const YOUTH_AGE = '<?php echo $_CLASS['youth-age']; ?>';
const YOUTH_GERMAN_NAME = '<?php echo $_CLASS['youth-german-name']; ?>'; const YOUTH_GERMAN_NAME = '<?php echo $_CLASS['youth-german-name']; ?>';
const PUSH_SERVER_KEY = '<?php echo PUSH_SERVER_KEY; ?>';
var randomId = function() { return '_' + Math.random().toString(36).substr(2, 9); } var randomId = function() { return '_' + Math.random().toString(36).substr(2, 9); }
@@ -219,6 +220,166 @@ function resetCache() {
toastInfo('The serviceWorker and the cache were deleted. A new serviceWorker will be generated on the next refresh.'); toastInfo('The serviceWorker and the cache were deleted. A new serviceWorker will be generated on the next refresh.');
} }
var pushesPossible = false;
function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function pushesSubscribe() {
console.log('Subscribing');
const applicationServerKey = urlB64ToUint8Array(PUSH_SERVER_KEY);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
.then(function(subscription) {
pushesUpdateServerSubscription(subscription, true);
updatePushSwitches();
updatePushBadge();
})
.catch(function(err) {
console.log('Failed to subscribe the user: ', err);
toastError('Da ist leider etwas schief gelaufen. Bitte stelle sicher, dass Du mit dem Internet verbunden bist und versuche es erneut.', 5000);
pushesUnSubscribe(true);
});
}
function pushesUnSubscribe(silent = false) {
console.log('Unsubscribing');
swRegistration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
pushesUpdateServerSubscription(subscription, false);
subscription.unsubscribe();
$('#menu-pushes').hideMenu();
updatePushBadge();
hideLoader();
}
})
.catch(function(error) {
console.log('Error unsubscribing', error);
$('#menu-pushes').hideMenu();
if (!silent) toastError('Da ist leider etwas schief gelaufen. Bitte versuche es erneut oder wende Dich an unseren Support.', 5000);
updatePushBadge();
hideLoader();
});
}
function pushesUpdateServerSubscription(subscription, enabled) {
console.log('updateServer', enabled, subscription);
$.ajax({
url: QUERY_URL + (enabled ? 'add' : 'remove') + '_subscription',
type: 'POST',
data: { subscription: JSON.stringify(subscription) },
success: function (data, textStatus, jqXHR) {
if (!enabled) {
toastOk('Du erhältst ab sofort keine Benachrichtigungen mehr von uns.');
}
hideLoader();
},
error: function (jqXHR, textStatus, errorThrown) {
throw 'Cannot update server subscription';
}
});
}
async function initPushSettings() {
var items = [
['notify_channel_' + BOATCLASS + '_news', true],
['notify_channel_' + BOATCLASS + '_regatta_changed_my', true],
['notify_channel_' + BOATCLASS + '_regatta_changed_all', false],
['notify_channel_' + BOATCLASS + '_result_ready_my', true],
['notify_channel_' + BOATCLASS + '_result_ready_all', true],
['notify_channel_' + BOATCLASS + '_meldeschluss', true]
];
for (var i in items) {
var item = items[i];
if ((await dbSettingsGet(item[0])) == null) dbSettingsSet(item[0], item[1]);
}
}
async function updatePushSwitches() {
$('#switch-pushes-news').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_news'));
$('#switch-pushes-regatta-changed-my').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_regatta_changed_my'));
$('#switch-pushes-regatta-changed-all').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_regatta_changed_all'));
$('#switch-pushes-result-ready-my').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_result_ready_my'));
$('#switch-pushes-result-ready-all').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_result_ready_all'));
$('#switch-pushes-meldeschluss').prop('checked', await dbSettingsGet('notify_channel_' + BOATCLASS + '_meldeschluss'));
if ($('#switch-pushes').prop('checked')) {
$('#p-pushes-info').show();
$('.a-switch-pushes-channel').show();
} else {
$('#p-pushes-info').hide();
$('.a-switch-pushes-channel').hide();
}
}
function pushesSubscribeClicked() {
showLoader();
if ($('#switch-pushes').prop('checked')) {
pushesSubscribe();
} else {
pushesUnSubscribe();
}
}
function pushesChannelClicked() {
dbSettingsSet('notify_channel_' + BOATCLASS + '_news', $('#switch-pushes-news').prop('checked'));
dbSettingsSet('notify_channel_' + BOATCLASS + '_regatta_changed_my', $('#switch-pushes-regatta-changed-my').prop('checked'));
dbSettingsSet('notify_channel_' + BOATCLASS + '_regatta_changed_all', $('#switch-pushes-regatta-changed-all').prop('checked'));
dbSettingsSet('notify_channel_' + BOATCLASS + '_result_ready_my', $('#switch-pushes-result-ready-my').prop('checked'));
dbSettingsSet('notify_channel_' + BOATCLASS + '_result_ready_all', $('#switch-pushes-result-ready-all').prop('checked'));
dbSettingsSet('notify_channel_' + BOATCLASS + '_meldeschluss', $('#switch-pushes-meldeschluss').prop('checked'));
}
function pushesOpenMenu() {
$('#menu-settings').hideMenu();
if (!pushesPossible) {
toastWarn('Dein Browser unterst&uuml;tzt leider keine Benachrichtigungen.', 5000);
return;
}
if (Notification.permission == 'denied') {
toastWarn('Benachrichtigungen werden von Deinem Browser blockiert.', 5000);
return;
}
swRegistration.pushManager.getSubscription().then(function(subscription) {
var isSub = (subscription !== null);
$('#switch-pushes').prop('checked', isSub);
updatePushSwitches();
$('#menu-pushes').showMenu();
});
}
function updatePushBadge() {
if (!pushesPossible) return;
if (Notification.permission == 'denied') {
$('#badge-pushes').removeClass('bg-green2-dark').addClass('bg-red2-dark').text('BLOCKED');
return;
}
swRegistration.pushManager.getSubscription().then(function(subscription) {
var isSub = (subscription !== null);
if (isSub) {
$('#badge-pushes').removeClass('bg-red2-dark').addClass('bg-green2-dark').text('AN');
} else {
$('#badge-pushes').removeClass('bg-green2-dark').addClass('bg-red2-dark').text('AUS');
}
});
}
var initRegatten = function() { var initRegatten = function() {
showLoader(); showLoader();
@@ -235,4 +396,22 @@ var initRegatten = function() {
$('.show-loggedin').hide(); $('.show-loggedin').hide();
$('.show-notloggedin').show(); $('.show-notloggedin').show();
} }
}
// Pushes
$('#a-switch-pushes').click(pushesSubscribeClicked);
$('.a-switch-pushes-channel').click(pushesChannelClicked);
}
var onServiceWorkerLoaded = function() {
if ((swRegistration !== null) && canUseLocalDB) {
pushesPossible = true;
updatePushBadge();
} else {
$('#badge-pushes').removeClass('bg-green2-dark').addClass('bg-red2-dark').text('NOT SUPPORTED');
}
}
var onDatabaseLoaded = function() {
onServiceWorkerLoaded();
initPushSettings();
}

View File

@@ -173,6 +173,13 @@
text-align: right; text-align: right;
} }
/*** BLOCKQUOTE ***/
blockquote {
border-left: 0.5em solid rgba(0, 0, 0, 0.125);
font-style: italic;
padding-left: 1em;
}
/*** BLINKING ICONS ***/ /*** BLINKING ICONS ***/
@keyframes fa-blink { @keyframes fa-blink {
0% { opacity: 1; } 0% { opacity: 1; }

View File

@@ -4254,7 +4254,7 @@ code {
} }
/*Contact Form*/ /*Contact Form*/
.menu input[type="text"] { /*.menu input[type="text"] {
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
} }
@@ -4271,7 +4271,7 @@ code {
.menu .form-field label { .menu .form-field label {
font-size: 12px; font-size: 12px;
margin-bottom: -10px; margin-bottom: -10px;
} }*/
.form-field span { .form-field span {
position: absolute; position: absolute;

View File

@@ -4,18 +4,18 @@
error_reporting(0); // disable error reporting in browser error_reporting(0); // disable error reporting in browser
define('SEND_ERRORS', true); // send errors via log define('SEND_ERRORS', true); // send errors via log
define('BOATCLASS', 'pirat');
date_default_timezone_set('Europe/Berlin'); date_default_timezone_set('Europe/Berlin');
define('SERVER_PATH', '/subfolder'); // path to root directory define('SERVER_PATH', '/subfolder'); // path to root directory
define('SERVER_ADDR', 'https://' . $_SERVER['SERVER_NAME'] . SERVER_PATH); // path to root directory define('SERVER_ADDR', 'https://' . $_SERVER['SERVER_NAME'] . SERVER_PATH); // path to root directory
define('QUERY_URL', 'http://' . $_SERVER['SERVER_NAME'] . '/api/' . BOATCLASS . '/'); // url to api backend
define('LOGGING_APIKEY', 'xxx'); // Apikey for Logging API -> get from ostertun.net/logging define('LOGGING_APIKEY', 'xxx'); // Apikey for Logging API -> get from ostertun.net/logging
// PUSH SERVER // PUSH
define('PUSH_AUTH', 'xxxxxxx'); // auth string for push.ostertun.net define('PUSH_SERVER_KEY', '');
define('PUSH_SERVERKEY', 'xxxxxxx'); // server key from push.ostertun.net
define('BOATCLASS', 'pirat'); // BOAT CLASS
// BOAT CLASSES
$_CLASS = array( $_CLASS = array(
'name' => 'Pirat', 'name' => 'Pirat',
'desc' => 'eine vom DSV gef&ouml;rderte Jugendmeisterschaftsklasse', 'desc' => 'eine vom DSV gef&ouml;rderte Jugendmeisterschaftsklasse',

View File

@@ -35,6 +35,11 @@
$items .= $tpl->load('menu/item-icon', ['Vereins-Website', '', 'html-id' => 'menu-item-clubwebsite', 'icon' => 'fa-globe', 'css-class' => 'border-0']); $items .= $tpl->load('menu/item-icon', ['Vereins-Website', '', 'html-id' => 'menu-item-clubwebsite', 'icon' => 'fa-globe', 'css-class' => 'border-0']);
$sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-boat', 'title' => 'Boots-Details', 'height' => 200]); $sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-boat', 'title' => 'Boots-Details', 'height' => 200]);
$items = '<p class="mb-2 mt-1" style="line-height: 1.5em;">Bitte trage hier den Bootsnamen ein:</p>';
$items .= $tpl->load('input', ['html-id' => 'input-editboatname', 'placeholder' => 'Bootsname', 'type' => 'text']);
$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-editboatname']);
$sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-editboatname', 'height' => 240]);
$sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']); $sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']);
$sp['scripts'] .= $scripts->load('boats'); $sp['scripts'] .= $scripts->load('boats');

View File

@@ -1,19 +1,38 @@
<?php <?php
// TODO: Create site $sp['title'] = 'Kontakt - Regatten.net ' . $_CLASS['name'];
$sp['title'] = 'Seite noch nicht unterstuuml;tzt - Regatten.net ' . $_CLASS['name'];
$sp['backbutton'] = true; $sp['backbutton'] = true;
$sp['activenav'] = 5;
$content = $tpl->load('error', ['404', 'Seite existiert noch nicht']); // TITLE
$content .= '<p>'; $content = '<h1>Kontakt</h1>';
$content .= 'Die gesuchte Seite ist leider noch nicht verf&uuml;gbar.<br>';
$content .= 'Wir arbeiten daran, sie schnellstm&ouml;glich zur Verf&uuml;gung zu stellen.<br>'; $sp['output'] .= $tpl->load('card', [$content]);
$content .= 'Wie w&auml;re es mit der Homepage?';
// Info
$content = '<p>';
$content .= 'Du hast eine Frage? Du hast einen Fehler in unserer Software oder in den gespeicherten Daten gefunden? Du willst Regatten.net auch f&uuml;r Deine Bootsklasse nutzen?<br>';
$content .= 'Egal was es ist, lass es uns wissen! Schreibe uns eine Mail an <a href="mailto:info@regatten.net">info@regatten.net</a> oder nutze einfach dieses Kontakt-Formular.<br>';
$content .= 'Wir werden Deine Anfrage so schnell wie m&ouml;glich bearbeiten.';
$content .= '</p>';
$content .= '<p>';
$content .= 'Alternativ erreichst Du uns auch telefonisch unter <a href="tel:+4941039659768">+49 (0) 4103 965 976 8</a><br>';
$content .= 'Mo-Fr: 7-20 Uhr<br>';
$content .= 'Sa: 9-17 Uhr';
$content .= '</p>'; $content .= '</p>';
$content .= $tpl->load('button', ['Zur Startseite', LINK_PRE . 'index', 'css-class' => 'mb-3']);
$content .= $tpl->load('button', ['Kontakt', LINK_PRE . 'contact']);
$sp['output'] = $tpl->load('card', [$content, 'css-class' => 'text-center pt-3']); $sp['output'] .= $tpl->load('card', [$content]);
// Formular
$content = '<h2>Kontakt-Formular</h2>';
$content .= $tpl->load('input', ['html-id' => 'input-name', 'placeholder' => 'Dein Name', 'type' => 'text']);
$content .= $tpl->load('input', ['html-id' => 'input-email', 'placeholder' => 'Email-Adresse', 'type' => 'email']);
$content .= $tpl->load('input', ['html-id' => 'input-subject', 'placeholder' => 'Betreff', 'type' => 'text']);
$content .= $tpl->load('textarea', ['html-id' => 'input-message', 'placeholder' => 'Deine Nachricht']);
$content .= $tpl->load('button', ['Senden', '#', 'html-id' => 'button-send']);
$sp['output'] .= $tpl->load('card', [$content]);
$sp['scripts'] .= $scripts->load('contact');
?> ?>

View File

@@ -1,19 +1,22 @@
<?php <?php
// TODO: Create site $sp['title'] = 'News - Regatten.net ' . $_CLASS['name'];
$sp['title'] = 'Seite noch nicht unterstuuml;tzt - Regatten.net ' . $_CLASS['name'];
$sp['backbutton'] = true; $sp['backbutton'] = true;
$sp['activenav'] = 5;
$content = $tpl->load('error', ['404', 'Seite existiert noch nicht']); // Title
$content .= '<p>'; $content = "<h1>Neuigkeiten</h1>";
$content .= 'Die gesuchte Seite ist leider noch nicht verf&uuml;gbar.<br>'; $content .= '<p>Aktuelles der letzten zw&ouml;lf Monate</p>';
$content .= 'Wir arbeiten daran, sie schnellstm&ouml;glich zur Verf&uuml;gung zu stellen.<br>';
$content .= 'Wie w&auml;re es mit der Homepage?';
$content .= '</p>';
$content .= $tpl->load('button', ['Zur Startseite', LINK_PRE . 'index', 'css-class' => 'mb-3']);
$content .= $tpl->load('button', ['Kontakt', LINK_PRE . 'contact']);
$sp['output'] = $tpl->load('card', [$content, 'css-class' => 'text-center pt-3']); $sp['output'] .= $tpl->load('card', [$content]);
// Menu
$sp['menus'] .= $tpl->load('menu/modal', ['html-id' => 'menu-news', 'title' => 'Details']);
$cardTemplate = $tpl->load('card', ['%CONTENT%', 'html-id' => '%ID%', 'css-class' => 'card-news']);
$cardTemplate = str_replace("\n", '', $cardTemplate);
$cardTemplate = str_replace("\r", '', $cardTemplate);
$sp['scripts'] .= "<script>const cardTemplate = '" . $cardTemplate . "';</script>";
$sp['scripts'] .= $scripts->load('news');
?> ?>

View File

@@ -35,6 +35,11 @@
$items .= $tpl->load('menu/item-icon', ['Vereins-Website', '', 'html-id' => 'menu-item-clubwebsite', 'icon' => 'fa-globe', 'css-class' => 'border-0']); $items .= $tpl->load('menu/item-icon', ['Vereins-Website', '', 'html-id' => 'menu-item-clubwebsite', 'icon' => 'fa-globe', 'css-class' => 'border-0']);
$sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-sailor', 'title' => 'Segler-Details', 'height' => 200]); $sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-sailor', 'title' => 'Segler-Details', 'height' => 200]);
$items = '<p class="mb-2 mt-1" style="line-height: 1.5em;">Bitte trage hier den Jahrgang ein:</p>';
$items .= $tpl->load('input', ['html-id' => 'input-edityear', 'placeholder' => 'Jahrgang', 'type' => 'number']);
$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-edityear']);
$sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-edityear', 'height' => 240]);
$sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']); $sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']);
$sp['scripts'] .= $scripts->load('sailors'); $sp['scripts'] .= $scripts->load('sailors');

View File

@@ -92,7 +92,7 @@
</div> </div>
</div> </div>
<div id="menu-settings" class="menu menu-box-bottom menu-box-detached rounded-m" data-menu-height="270"> <div id="menu-settings" class="menu menu-box-bottom menu-box-detached rounded-m" data-menu-height="310">
<div class="menu-title"><h1>Einstellungen</h1><p class="color-highlight">&nbsp;</p><a href="#" class="close-menu"><i class="fa fa-times"></i></a></div> <div class="menu-title"><h1>Einstellungen</h1><p class="color-highlight">&nbsp;</p><a href="#" class="close-menu"><i class="fa fa-times"></i></a></div>
<div class="divider divider-margins mb-n2"></div> <div class="divider divider-margins mb-n2"></div>
<div class="content"> <div class="content">
@@ -110,7 +110,7 @@
<span>Login</span> <span>Login</span>
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
<a href="#" data-menu="menu-signup" class="show-notloggedin border-0"> <a href="#" data-menu="menu-signup" class="show-notloggedin">
<i class="fa font-14 fa-user-plus rounded-s bg-highlight color-white"></i> <i class="fa font-14 fa-user-plus rounded-s bg-highlight color-white"></i>
<span>Registrieren</span> <span>Registrieren</span>
<span class="badge bg-red2-dark color-white">FREE</span> <span class="badge bg-red2-dark color-white">FREE</span>
@@ -120,11 +120,88 @@
<span>Account</span> <span>Account</span>
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
<a href="#" onclick="logout();" class="show-loggedin border-0"> <a href="#" onclick="logout();" class="show-loggedin">
<i class="fa font-14 fa-sign-out-alt rounded-s bg-highlight color-white"></i> <i class="fa font-14 fa-sign-out-alt rounded-s bg-highlight color-white"></i>
<span>Logout</span> <span>Logout</span>
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
<a href="#" onclick="pushesOpenMenu()" class="border-0">
<i class="fa font-14 fa-bell rounded-s bg-highlight color-white"></i>
<span>Benachrichtigungen</span>
<span id="badge-pushes" class="badge color-white"></span>
<i class="fa fa-angle-right"></i>
</a>
</div>
</div>
</div>
<div id="menu-pushes" class="menu menu-box-bottom menu-box-detached rounded-m" data-menu-height="500">
<div class="menu-title"><h1>Benachrichtigungen</h1><p class="color-highlight">Bleibe immer auf dem aktuellen Stand</p><a href="#" class="close-menu"><i class="fa fa-times"></i></a></div>
<div class="divider divider-margins mb-n2"></div>
<div class="content">
<div class="list-group list-custom-small">
<a id="a-switch-pushes" href="#" data-trigger-switch="switch-pushes" class="pb-2">
<i class="fa font-14 fa-bell rounded-s bg-highlight color-white"></i>
<span>Benachrichtigungen aktivieren</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes">
<label class="custom-control-label" for="switch-pushes"></label>
</div>
</a>
<div class="divider"></div>
<p style="line-height: 1.5em;" id="p-pushes-info">
W&auml;hle hier, &uuml;ber was Du informiert werden m&ouml;chtest.<br>
(meine) bezieht sich auf die Regatten, die in Deiner Saison-Planung sind,<br>
(alle) informiert Dich &uuml;ber alle Regatten
</p>
<a href="#" data-trigger-switch="switch-pushes-news" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-newspaper rounded-s bg-highlight color-white"></i>
<span>Neuigkeiten</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-news">
<label class="custom-control-label" for="switch-pushes-news"></label>
</div>
</a>
<a href="#" data-trigger-switch="switch-pushes-regatta-changed-my" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-calendar-check rounded-s bg-highlight color-white"></i>
<span>Regatta verschoben (meine)</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-regatta-changed-my">
<label class="custom-control-label" for="switch-pushes-regatta-changed-my"></label>
</div>
</a>
<a href="#" data-trigger-switch="switch-pushes-regatta-changed-all" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-calendar-check rounded-s bg-highlight color-white"></i>
<span>Regatta verschoben (alle)</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-regatta-changed-all">
<label class="custom-control-label" for="switch-pushes-regatta-changed-all"></label>
</div>
</a>
<a href="#" data-trigger-switch="switch-pushes-result-ready-my" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-poll rounded-s bg-highlight color-white"></i>
<span>Ergebnisse verf&uuml;gbar (meine)</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-result-ready-my">
<label class="custom-control-label" for="switch-pushes-result-ready-my"></label>
</div>
</a>
<a href="#" data-trigger-switch="switch-pushes-result-ready-all" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-poll rounded-s bg-highlight color-white"></i>
<span>Ergebnisse verf&uuml;gbar (alle)</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-result-ready-all">
<label class="custom-control-label" for="switch-pushes-result-ready-all"></label>
</div>
</a>
<a href="#" data-trigger-switch="switch-pushes-meldeschluss" class="pb-2 a-switch-pushes-channel">
<i class="fa font-14 fa-file-signature rounded-s bg-highlight color-white"></i>
<span>Melde-Erinnerungen</span>
<div class="custom-control scale-switch ios-switch">
<input type="checkbox" class="ios-input" id="switch-pushes-meldeschluss">
<label class="custom-control-label" for="switch-pushes-meldeschluss"></label>
</div>
</a>
</div> </div>
</div> </div>
</div> </div>
@@ -197,7 +274,7 @@
Vielen Dank f&uuml;r Deine Unterst&uuml;tzung! Vielen Dank f&uuml;r Deine Unterst&uuml;tzung!
</p> </p>
<p> <p>
Mehr Informationen findest <a href="https://info.ostertun.net/regatten/beta">hier</a>. Mehr Informationen findest Du <a href="https://info.ostertun.net/regatten/beta">hier</a>.
</p> </p>
<p> <p>
Mit der Nutzung dieser App erkl&auml;rst Du Dich au&szlig;erdem damit einverstanden, dass wir Cookies einsetzen. Mit der Nutzung dieser App erkl&auml;rst Du Dich au&szlig;erdem damit einverstanden, dass wir Cookies einsetzen.

View File

@@ -5,18 +5,64 @@ var page = 1;
var pageCount = 0; var pageCount = 0;
const showCount = 25; const showCount = 25;
async function onEditBoatnameClick() {
var id = $('#button-editboatname').attr('data-boat-id');
var name = $('#input-editboatname').val();
if (name != '') {
showLoader();
$.ajax({
url: QUERY_URL + 'add_boatname',
method: 'POST',
data: {
boat: id,
name: name
},
error: function (xhr, status, error) {
if (xhr.status == 0) {
toastError('Du bist momentan offline.<br>Stelle eine Internetverbindung her, um den Bootsnamen zu bearbeiten');
} else {
console.log('EditBoatname: unbekannter Fehler', status, error);
console.log(xhr);
toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000);
}
hideLoader();
},
success: function (data, status, xhr) {
if ('status' in data) {
if (data.status == 'added') {
toastOk('Bootsnamen erfolgreich hinzugefügt');
sync();
} else {
toastInfo('Wir prüfen Deine Anfrage und korrigieren den Bootsnamen schnellstmöglich', 5000);
}
} else {
toastOk('Erfolgreich');
}
hideLoader();
}
});
}
$('#menu-editboatname').hideMenu();
}
async function onListClicked(id) { async function onListClicked(id) {
var boat = await dbGetData('boats', id); var boat = await dbGetData('boats', id);
$('#menu-boat').find('.menu-title').find('p').text(boat.sailnumber); $('#menu-boat').find('.menu-title').find('p').text(boat.sailnumber);
// Edit Boatname // Edit Boatname
// TODO: create menu for edit boatname $('#button-editboatname').attr('data-boat-id', boat.id);
$('#menu-editboatname').find('.menu-title').find('p').text(boat.sailnumber);
if (boat['name'] == '') { if (boat['name'] == '') {
$('#menu-item-boatname').find('span').text('Bootsnamen hinzufügen'); $('#menu-item-boatname').find('span').text('Bootsnamen hinzufügen');
$('#menu-editboatname').find('.menu-title').find('h1').text('Bootsnamen hinzufügen');
$('#input-editboatname').val('');
} else { } else {
$('#menu-item-boatname').find('span').text('Bootsnamen bearbeiten'); $('#menu-item-boatname').find('span').text('Bootsnamen bearbeiten');
$('#menu-editboatname').find('.menu-title').find('h1').text('Bootsnamen bearbeiten');
$('#input-editboatname').val(boat.name);
} }
$('#input-editboatname').trigger('focusin').trigger('focusout');
// club website // club website
var clubwebsite = ''; var clubwebsite = '';
@@ -86,6 +132,8 @@ var siteScript = async function() {
firstCall = false; firstCall = false;
initPagination(); initPagination();
$('#input-search').on('input', reSearch); $('#input-search').on('input', reSearch);
$('#menu-item-boatname').click(function(){ $('#menu-boat').hideMenu(); $('#menu-editboatname').showMenu(); });
$('#button-editboatname').click(onEditBoatnameClick);
} }
var results = await dbGetData('boats'); var results = await dbGetData('boats');

49
server/scripts/contact.js Normal file
View File

@@ -0,0 +1,49 @@
function sendMessage() {
var name = $('#input-name').val();
var email = $('#input-email').val();
var subject = $('#input-subject').val();
var message = $('#input-message').val();
if ((name == '') || (email == '') || (subject == '') || (message == '')) {
toastError('Bitte f&uuml;lle alle Felder aus!');
return;
}
showLoader();
$.ajax({
url: QUERY_URL + 'contact',
method: 'POST',
data: {
name: name,
email: email,
subject: subject,
message: message
},
error: function (xhr, status, error) {
if (xhr.status == 0) {
toastError('Du bist momentan offline.<br>Stelle eine Internetverbindung her, um eine Nachricht zu versenden');
} else {
console.log('Contact: unbekannter Fehler', status, error);
console.log(xhr);
toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000);
}
hideLoader();
},
success: function (data, status, xhr) {
toastOk('Nachricht erfolgreich versandt!');
$('#input-subject').val('');
$('#input-message').val('');
hideLoader();
}
});
}
var siteScript = async function () {
if (isLoggedIn()) {
var user = await dbGetData('users', USER_ID);
$('#input-name').val(user.username).trigger('focusin').trigger('focusout');
$('#input-email').val(user.email).trigger('focusin').trigger('focusout');
}
$('#button-send').click(sendMessage);
hideLoader();
}

44
server/scripts/news.js Normal file
View File

@@ -0,0 +1,44 @@
async function onNewsClicked(id) {
var newsEntry = await dbGetData('news', id);
if (newsEntry == null) return;
$('#menu-news').css('height', '80%');
$('#menu-news').css('width', '90%');
$('#menu-news').find('.menu-title').find('p').text(newsEntry.title);
$('#menu-news').find('.content').addClass('pb-3');
$('#menu-news').find('.content').html(newsEntry.html);
$('#menu-news').showMenu();
}
function addCard(newsEntry) {
console.log(newsEntry);
var content = '<h2>' + newsEntry.title + '</h2>';
content += '<p class="mb-2"><i>' + formatDate('d.m.Y', newsEntry.date) + '</i></p>';
content += '<p class="mb-0">' + newsEntry.description.replace('\n', '<br>') + '</p>';
if (newsEntry.html != '') {
content += '<a class="btn btn-full rounded-s text-uppercase font-900 shadow-m bg-highlight mt-3" href="#" onclick="onNewsClicked(' + newsEntry.id + '); return false;">Mehr lesen</a>';
}
$('.page-content').append(cardTemplate.replace('%ID%', 'card-news-' + newsEntry.id).replace('%CONTENT%', content));
}
var siteScript = async function() {
$('.card-news').remove();
var news = await dbGetData('news');
news.sort(function (a,b) {
return b.date.localeCompare(a.date);
});
var today = getToday();
var lastYear = new Date(today);
lastYear.setFullYear(lastYear.getFullYear() - 1);
console.log(today, lastYear);
for (var n in news) {
var newsEntry = news[n];
newsEntry.date = parseDate(newsEntry.date.substring(0, 10));
if (newsEntry.date > today) continue;
if (newsEntry.date < lastYear) break;
addCard(newsEntry);
}
hideLoader();
}

View File

@@ -39,7 +39,7 @@ var siteScript = async function() {
tbody += '<td>' + (await dbGetData('users', planning.user)).username + '</td>'; tbody += '<td>' + (await dbGetData('users', planning.user)).username + '</td>';
if (planning.steuermann != null) { if (planning.steuermann != null) {
tbody += '<td>' + (await dbGetData('users', planning.user)).username + '</td>'; tbody += '<td>' + (await dbGetData('sailors', planning.steuermann)).name + '</td>';
} else { } else {
tbody += '<td>(noch unklar)</td>'; tbody += '<td>(noch unklar)</td>';
} }

View File

@@ -5,18 +5,64 @@ var page = 1;
var pageCount = 0; var pageCount = 0;
const showCount = 25; const showCount = 25;
async function onEditYearClick() {
var id = $('#button-edityear').attr('data-sailor-id');
var year = $('#input-edityear').val();
if (year != '') {
showLoader();
$.ajax({
url: QUERY_URL + 'add_year',
method: 'POST',
data: {
sailor: id,
year: year
},
error: function (xhr, status, error) {
if (xhr.status == 0) {
toastError('Du bist momentan offline.<br>Stelle eine Internetverbindung her, um den Jahrgang zu bearbeiten');
} else {
console.log('EditYear: unbekannter Fehler', status, error);
console.log(xhr);
toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000);
}
hideLoader();
},
success: function (data, status, xhr) {
if ('status' in data) {
if (data.status == 'added') {
toastOk('Jahrgang erfolgreich hinzugefügt');
sync();
} else {
toastInfo('Wir prüfen Deine Anfrage und korrigieren den Jahrgang schnellstmöglich', 5000);
}
} else {
toastOk('Erfolgreich');
}
hideLoader();
}
});
}
$('#menu-edityear').hideMenu();
}
async function onListClicked(id) { async function onListClicked(id) {
var sailor = await dbGetData('sailors', id); var sailor = await dbGetData('sailors', id);
$('#menu-sailor').find('.menu-title').find('p').text(sailor.name); $('#menu-sailor').find('.menu-title').find('p').text(sailor.name);
// Edit Year // Edit Year
// TODO: create menu for edit year $('#button-edityear').attr('data-sailor-id', sailor.id);
$('#menu-edityear').find('.menu-title').find('p').text(sailor.name);
if (sailor['year'] == null) { if (sailor['year'] == null) {
$('#menu-item-year').find('span').text('Jahrgang hinzufügen'); $('#menu-item-year').find('span').text('Jahrgang hinzufügen');
$('#menu-edityear').find('.menu-title').find('h1').text('Jahrgang hinzufügen');
$('#input-edityear').val('');
} else { } else {
$('#menu-item-year').find('span').text('Jahrgang bearbeiten'); $('#menu-item-year').find('span').text('Jahrgang bearbeiten');
$('#menu-edityear').find('.menu-title').find('h1').text('Jahrgang bearbeiten');
$('#input-edityear').val(sailor.year);
} }
$('#input-edityear').trigger('focusin').trigger('focusout');
// club website // club website
var clubwebsite = ''; var clubwebsite = '';
@@ -86,6 +132,8 @@ var siteScript = async function() {
firstCall = false; firstCall = false;
initPagination(); initPagination();
$('#input-search').on('input', reSearch); $('#input-search').on('input', reSearch);
$('#menu-item-year').click(function(){ $('#menu-sailor').hideMenu(); $('#menu-edityear').showMenu(); });
$('#button-edityear').click(onEditYearClick);
} }
var results = await dbGetData('sailors'); var results = await dbGetData('sailors');

View File

@@ -1,4 +1,4 @@
<div class="input-style input-style-2 input-required $$css-class;"> <div class="input-style input-style-2 input-required $$css-class;">
<span class="color-highlight">$$placeholder;</span> <span class="color-highlight">$$placeholder;</span>
<textarea id="$$html-id;" class="form-control" placeholder="$$placeholder;">$$value;</textarea> <textarea id="$$html-id;" class="form-control pt-3 pb-3" placeholder="$$placeholder;" style="height: 10em; line-height: 1.5em;">$$value;</textarea>
</div> </div>

View File

@@ -1,5 +1,5 @@
<?php <?php
define('PWA_VERSION', '1.4'); define('PWA_VERSION', '1.6');
?> ?>

View File

@@ -104,4 +104,147 @@ workbox.routing.registerRoute(
//Learn more about Service Workers and Configurations //Learn more about Service Workers and Configurations
//https://developers.google.com/web/tools/workbox/ //https://developers.google.com/web/tools/workbox/
// DB
var db = null;
if (indexedDB) {
var request = indexedDB.open('regatten_app_db_<?php echo BOATCLASS; ?>');
request.onerror = function (e) {
console.log('[sW] Cannot open DB:', e.target.errorCode);
};
request.onupgradeneeded = function (e) {
console.log('[sW] DB does not exist');
e.target.transaction.abort();
};
request.onsuccess = function (e) {
console.log('[sW] DB loaded');
db = e.target.result;
db.onerror = function (e) {
console.log('[sW] DB Error:', e)
}
};
}
function dbSettingsGet(key) {
return new Promise(function(resolve) {
if (db != null) {
var request = db.transaction('settings').objectStore('settings').get(key);
request.onsuccess = function (event) {
resolve(typeof request.result != 'undefined' ? request.result.value : null);
}
} else {
resolve(null);
}
});
}
function dbSettingsSet(key, value) {
if (db != null) {
var os = db.transaction('settings', 'readwrite').objectStore('settings');
os.put({ key: key, value: value});
}
}
// PUSHES
function getEntry(data, index, defaultValue) {
return ((typeof data[index] !== "undefined") ? data[index] : defaultValue);
}
function isMyRegatta(id) {
return new Promise(async function (resolve) {
var regattas = await dbSettingsGet('myregattas_<?php echo BOATCLASS; ?>');
if (regattas == null) resolve(false);
else resolve(regattas.includes(id.toString()));
});
}
self.addEventListener('push', async function(event) {
console.log('[sW] Push received:', event.data.text());
var data;
try {
data = JSON.parse(event.data.text());
} catch(e) {
console.log(e);
data = undefined;
}
if (typeof data.type !== "undefined") {
switch (data.type) {
case 'notification':
if (typeof data.title === "undefined") break;
if (typeof data.body === "undefined") break;
if (typeof data.channel === "undefined") break;
// check channel
var okay = false;
switch (data.channel) {
case 'news':
if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_news')) okay = true;
break;
case 'regatta_changed':
if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_regatta_changed_all')) okay = true;
else if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_regatta_changed_my')) {
if (await isMyRegatta(getEntry(data, 'id', ''))) okay = true;
}
break;
case 'result_ready':
if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_result_ready_all')) okay = true;
else if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_result_ready_my')) {
if (await isMyRegatta(getEntry(data, 'id', ''))) okay = true;
}
break;
case 'meldeschluss':
if (await dbSettingsGet('notify_channel_<?php echo BOATCLASS; ?>_meldeschluss')) {
if (await isMyRegatta(getEntry(data, 'id', ''))) okay = true;
}
break;
default:
console.log('Unknown channel:', data.channel);
break;
}
if (!okay) {
console.log('Notification channel not subscribed');
return;
}
const options = {
data: data,
body: data.body,
icon: getEntry(data, 'icon', '<?php echo SERVER_ADDR; ?>/client/app/icons/icon-512x512.png'),
badge: '<?php echo SERVER_ADDR; ?>/client/app/icons/icon-96x96.png',
vibrate: [500,100,500]
};
if ((image = getEntry(data, 'image', null)) !== null) {
options.image = image;
}
// Force refresh on next app open
var os = db.transaction('update_times', 'readwrite').objectStore('update_times');
os.put({ table: 'last_sync', time: 0 });
console.log('Showing notification');
self.registration.showNotification(data.title, options);
break;
}
}
});
self.addEventListener('notificationclick', function(event) {
var data = event.notification.data;
event.notification.close();
var url = '<?php echo SERVER_ADDR; ?>' + getEntry(data, 'url', '');
event.waitUntil(
clients.openWindow(url)
);
});