more('poi', [], [], 999); $geojson = pois2geojson($pois, $Parsedown); return render('poi', ['geojson'=>$geojson]); }); route('', '/poi/{slug}/', function ($req, $params){ $rows = di('rows'); $poi = $rows->one('poi', ['slug'=>$params['slug']]); if (!$poi) return notFound(); $Parsedown = new Parsedown(); $poi['description'] = $Parsedown->text($poi['description']); $poi['cheatsheet'] = $Parsedown->text($poi['cheatsheet']); $pois = $rows->more('poi', [], [], 999); $geojson = pois2geojson($pois, $Parsedown); return render('poi', ['geojson'=>$geojson, 'poi'=>$poi]); }); route('', '/login/', function ($req){ /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $form = new form(['method'=>'POST']); $form->field('username', ['required'=>true, 'label'=>'Jméno']); $form->field('password', ['type'=>'password', 'required'=>true, 'label'=>'Heslo']); $form->field('_login', ['type'=>'submit', 'label'=>'Přihlásit se']); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $uz = $rows->one('users', ['username'=>$form->values['username'], 'is_active'=>1]); if (!$uz) { $form->error('username', 'Uživatel nenalezen'); } elseif (password_verify($form->values['password'], $uz['password'])) { unset($uz['password']); $_SESSION['user'] = $uz; return redirect('/'); } else { $form->error('password', 'Špatné heslo.'); } } } return render('form', ['form'=>$form]); }); route('', '/logout/', function ($req){ unset($_SESSION['user']); unset($_SESSION['flashes']); return redirect('/'); }); route('', '/zmena-hesla/', function ($req){ if (!user()) return redirect('/login/'); $user = user(); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $form = new form(['method'=>'post']); $form->field('password_current', ['required'=>true, 'type'=>'password', 'label'=>'Stávající heslo']); $form->field('password', ['required'=>true, 'type'=>'password', 'label'=>'Nové heslo']); $form->field('password_again', ['required'=>true, 'type'=>'password', 'label'=>'Nové heslo znovu']); $form->field('_sbt', ['label'=>'Změnit heslo', 'type'=>'submit']); $form->rule('password_again', function ($v, $o){ return $v==$o['password']; }, 'Hesla se neshodují!'); $uz = $rows->one('users', $user['id']); $form->rule('password_current', function ($v, $o) use ($uz) { return password_verify($v, $uz['password']); }, 'Špatné zadané současné heslo!'); if ($req->getMethod()=='POST' && $form->fill($req->getParsedBody()) && $form->validate()) { $rows->update('users', [ 'password'=>password_hash($form->values['password'], PASSWORD_DEFAULT) ], [ 'id'=>$user['id'] ]); flash('Heslo změněno.'); return redirect('/'); } return render('form', ['form'=>$form, 'title'=>'Změnit heslo']); }); $singletons['poi_form'] = function () { $form = new severak\forms\form(['method'=>'post']); $form->field('name', ['label'=>'Název', 'required'=>true]); $form->field('description', ['label'=>'Popis', 'type'=>'textarea']); $form->field('cheatsheet', ['label'=>'Tahák', 'type'=>'textarea']); $form->field('internal', ['label'=>'Poznámka', 'type'=>'textarea']); $form->field('is_public', ['label'=>'Zveřejněno?', 'type'=>'checkbox']); $form->field('lon', ['type'=>'hidden']); $form->field('lat', ['type'=>'hidden']); $form->field('_sbt', ['label'=>'Uložit?', 'type'=>'submit']); $form->rule('lon', function ($v, $o) { return !empty($o['lon']) && !empty($o['lat']); }, 'Musí být vyplněny souřadnice'); return $form; }; // správa POI route('GET', '/pois/', function ($req){ return redirect('/pois/edit/'); }); route('GET', '/pois/edit/', function ($req){ if (!user()) return redirect('/login/'); /** @var severak\database\rows $rows */ $rows = di('rows'); $page = $_GET['page'] ?? 1; $pois = $rows->page('poi', [], ['name'=>'asc'], $page, 30); $pages = $rows->pages; return render('poi_list', ['pois'=>$pois, 'pages'=>$pages, 'page'=>$page]); }); route('', '/pois/add/', function ($req){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('poi_form'); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $slug = slugify($form->values['name']); $rows->insert('poi', [ 'name'=>$form->values['name'], 'description'=>$form->values['description'], 'cheatsheet'=>$form->values['cheatsheet'], 'internal'=>$form->values['internal'], 'slug'=>$slug, 'lon'=>$form->values['lon'], 'lat'=>$form->values['lat'], 'is_public'=>$form->values['is_public'] ?? 0, ]); return redirect('/poi/' . $slug . '/'); } } return render('form', ['form'=>$form, 'title'=>'Přidat položku']); }); route('', '/pois/edit/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('poi_form'); $item = $rows->one('poi', $params['id']); if (!$item) return notFound(); $form->fill($item); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $slug = slugify($form->values['name']); $rows->update('poi', [ 'name'=>$form->values['name'], 'description'=>$form->values['description'], 'cheatsheet'=>$form->values['cheatsheet'], 'internal'=>$form->values['internal'], 'slug'=>$slug, 'lon'=>$form->values['lon'], 'lat'=>$form->values['lat'], 'is_public'=>$form->values['is_public'] ?? 0, ], $params['id']); return redirect('/poi/' . $slug . '/'); } } return render('form', ['form'=>$form, 'title'=>'Upravit položku']); }); // TODO - tohle nechceme přes GET route('', '/sklad/smazat/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); /** @var severak\database\rows $rows */ $rows = di('rows'); $rows->update('items', ['is_active'=>0], ['id'=>$params['id'] ]); return redirect('/sklad/'); }); // procházky $singletons['event_form'] = function () { $form = new severak\forms\form(['method'=>'post']); $form->field('name', ['label'=>'Název', 'required'=>true]); $form->field('subtitle', ['label'=>'Upoutávka', 'type'=>'textarea']); $form->field('text', ['label'=>'Popis', 'type'=>'textarea']); $form->field('date', ['label'=>'Datum', 'type'=>'date', 'required'=>true]); $form->field('time', ['label'=>'Čas', 'type'=>'time', 'required'=>true]); $form->field('facebook_url', ['label'=>'URL události na FB']); $form->field('is_future', ['label'=>'Pozvánka?', 'type'=>'checkbox']); $form->field('stop_ids', ['label'=>'ID zastávek']); $form->field('place_description', ['label'=>'Místo srazu']); $form->field('lon', ['type'=>'hidden']); $form->field('lat', ['type'=>'hidden']); $form->field('_sbt', ['label'=>'Uložit?', 'type'=>'submit']); $form->rule('lon', function ($v, $o) { return !empty($o['lon']) && !empty($o['lat']); }, 'Musí být vyplněny souřadnice'); return $form; }; route('GET', '/event/edit/', function ($req){ if (!user()) return redirect('/login/'); /** @var severak\database\rows $rows */ $rows = di('rows'); $page = $_GET['page'] ?? 1; $pois = $rows->page('events', [], ['datetime'=>'asc'], $page, 30); $pages = $rows->pages; return render('event_list', ['pois'=>$pois, 'pages'=>$pages, 'page'=>$page]); }); route('', '/event/add/', function ($req){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('event_form'); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $slug = slugify($form->values['name']); $rows->insert('events', [ 'name'=>$form->values['name'], 'text'=>$form->values['text'], 'subtitle'=>$form->values['subtitle'], 'place_description'=>$form->values['place_description'], 'facebook_url'=>$form->values['facebook_url'], 'stop_ids'=>$form->values['stop_ids'], 'slug'=>$slug, 'lon'=>$form->values['lon'], 'lat'=>$form->values['lat'], 'is_future'=>$form->values['is_future'] ?? 0, 'datetime'=>strtotime($form->values['date'] . 'T' . $form->values['time']) ]); return redirect('/prochazka/' . $slug . '/'); } } return render('form', ['form'=>$form, 'title'=>'Přidat položku']); }); route('', '/event/edit/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('event_form'); $item = $rows->one('events', $params['id']); if (!$item) return notFound(); $item['date'] = date('Y-m-d', (int) $item['datetime']); $item['time'] = date('H:i', (int) $item['datetime']); $form->fill($item); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $slug = slugify($form->values['name']); $rows->update('events', [ 'name'=>$form->values['name'], 'text'=>$form->values['text'], 'subtitle'=>$form->values['subtitle'], 'place_description'=>$form->values['place_description'], 'facebook_url'=>$form->values['facebook_url'], 'stop_ids'=>$form->values['stop_ids'], 'slug'=>$slug, 'lon'=>$form->values['lon'], 'lat'=>$form->values['lat'], 'is_future'=>$form->values['is_future'] ?? 0, 'datetime'=>strtotime($form->values['date'] . 'T' . $form->values['time']) ], $params['id']); return redirect('/prochazky/' . $slug . '/'); } } return render('form', ['form'=>$form, 'title'=>'Upravit položku']); }); route('', '/prochazky/{slug}/', function ($req, $params){ /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $prochazka = $rows->one('events', ['slug'=>$params['slug']]); $Parsedown = new Parsedown(); $prochazka['text'] = $Parsedown->text($prochazka['text']); if (!$prochazka) return notFound(); return render('prochazka', ['prochazka'=>$prochazka]); }); // TRASY $singletons['trasy_form'] = function () { $form = new severak\forms\form(['method'=>'post']); $form->field('name', ['label'=>'Název', 'required'=>true]); $form->field('cheatsheet', ['label'=>'Tahák', 'type'=>'textarea']); $form->field('stop_ids', ['label'=>'ID zastávek']); $form->field('_sbt', ['type'=>'submit', 'label'=>'Uložit']); return $form; }; route('', '/trasy/', function (){ return redirect('/trasy/edit/'); }); route('GET', '/trasy/edit/', function ($req){ if (!user()) return redirect('/login/'); /** @var severak\database\rows $rows */ $rows = di('rows'); $trasy = $rows->more('trasy', [], ['name'=>'asc'], 999); return render('trasy', ['trasy'=>$trasy ]); }); route('', '/trasy/add/', function ($req){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('trasy_form'); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $slug = slugify($form->values['name']); $rows->insert('trasy', [ 'name'=>$form->values['name'], 'cheatsheet'=>$form->values['cheatsheet'], 'stop_ids'=>$form->values['stop_ids'], ]); return redirect('/trasy/edit/'); } } return render('form', ['form'=>$form, 'title'=>'Přidat položku']); }); route('', '/trasy/edit/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); /** @var severak\forms\form $form */ $form = di('trasy_form'); $item = $rows->one('trasy', $params['id']); if (!$item) return notFound(); $form->fill($item); if ($req->getMethod()=='POST') { $form->fill($req->getParsedBody()); if ($form->validate()) { $rows->update('trasy', [ 'name'=>$form->values['name'], 'cheatsheet'=>$form->values['cheatsheet'], 'stop_ids'=>$form->values['stop_ids'], ], $params['id']); return redirect('/trasy/edit/'); } } return render('form', ['form'=>$form, 'title'=>'Upravit položku']); }); route('', '/trasy/tahak/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $Parsedown = new Parsedown(); $trasa = $rows->one('trasy', $params['id']); if (!$trasa) return notFound(); $trasa['cheatsheet'] = $Parsedown->text($trasa['cheatsheet']); $ids = explode(' ', $trasa['stop_ids']); $zastavky = $rows->more('poi', ['id'=>$ids], [], 999); $zastavky = array_map(function ($zastavka) use ($Parsedown) { $zastavka['cheatsheet'] = $Parsedown->text($zastavka['cheatsheet']); return $zastavka; }, $zastavky); return render('trasa_tahak', ['trasa'=>$trasa, 'zastavky'=>$zastavky]); }); // OBSLUHA route('GET', '/obsluha/', function ($req){ if (!user()) return redirect('/login/'); /** @var severak\database\rows $rows */ $rows = di('rows'); $items = $rows->page('users', [], ['is_active'=>'desc', 'name'=>'asc']); return render('users', ['users'=>$items]); }); route('', '/obsluha/pridat/', function ($req){ if (!user()) return redirect('/login/'); $user = user(); if (!$user['is_superuser']) { flash('Obsluhu může přidávat jen admin.', 'warning'); return redirect('/'); } /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $form = new form(['method'=>'post']); $form->field('username', ['label'=>'Uživatelské jméno']); $form->field('password', ['required'=>true, 'type'=>'password', 'label'=>'Heslo']); $form->field('password_again', ['required'=>true, 'type'=>'password', 'label'=>'Heslo znovu']); $form->field('name', ['required'=>true, 'type'=>'text', 'label'=>'Jméno']); $form->field('card_id', ['type'=>'number', 'label'=>'Číslo členské karty', 'id'=>'qrcode']); $form->field('_sbt', ['label'=>'Přidat', 'type'=>'submit']); $form->rule('password_again', function ($v, $o){ return $v==$o['password']; }, 'Hesla se neshodují!'); if ($req->getMethod()=='POST' && $form->fill($req->getParsedBody()) && $form->validate()) { $duplicateUser = $rows->one('users', ['username'=>$form->values['username'] ]); if ($duplicateUser) { $form->error('username', 'Uživatel tohoto jména již v systému je.'); } $memberId = null; if ($form->values['card_id']) { $card = $rows->one('cards', ['id'=>$form->values['card_id'], 'is_active'=>1]); $memberId = $card['member_id']; } if ($form->isValid) { $rows->insert('users', [ 'username' => $form->values['username'], 'name' => $form->values['name'], 'password' => password_hash($form->values['password'], PASSWORD_DEFAULT), 'member_id'=> $memberId ]); flash('Uživatel přidán.', 'success'); return redirect('/obsluha/'); } } return render('form', ['form'=>$form, 'title'=>'Přidat obsluhu']); }); route('', '/obsluha/upravit/{id}/', function ($req, $params){ if (!user()) return redirect('/login/'); $user = user(); if (!$user['is_superuser']) { flash('Obsluhu může upravovat jen admin.', 'warning'); return redirect('/'); } $id = $params['id']; /** @var Psr\Http\Message\ServerRequestInterface $req */ /** @var severak\database\rows $rows */ $rows = di('rows'); $form = new form(['method'=>'post']); $form->field('username', ['label'=>'Uživatelské jméno']); $form->field('password', ['type'=>'password', 'label'=>'Heslo']); $form->field('password_again', ['type'=>'password', 'label'=>'Heslo znovu']); $form->field('name', ['required'=>true, 'type'=>'text', 'label'=>'Jméno']); $form->field('card_id', ['type'=>'number', 'label'=>'Číslo členské karty', 'id'=>'qrcode']); $form->field('is_active', ['type'=>'checkbox', 'label'=>'Aktivní?']); $form->field('is_superuser', ['type'=>'checkbox', 'label'=>'Je admin?']); $form->field('note', ['type'=>'textarea', 'label'=>'Poznámka']); $form->field('_sbt', ['label'=>'Uložit', 'type'=>'submit']); $form->rule('password_again', function ($v, $o){ return $v==$o['password']; }, 'Hesla se neshodují!'); if ($req->getMethod()=='POST' && $form->fill($req->getParsedBody())) { $form->validate(); $duplicateUser = $rows->one('users', ['username'=>$form->values['username'] ]); if ($duplicateUser && $duplicateUser['id']!=$id) { $form->error('username', 'Uživatel tohoto jména již v systému je.'); } if ($form->values['password'] && $form->values['password']!=$form->values['password_again']) { $form->error('password', 'Hesla se musí shodovat!'); } if ($form->isValid) { $update = $form->values; // TODO tohle je prasárna unset($update['id'], $update['password'], $update['password_again'], $update['card_id'], $update['_sbt']); if ($form->values['password'] && $form->values['password']!=$form->values['password_again']) { $update['password'] = password_hash($form->values['password'], PASSWORD_DEFAULT); } if ($form->values['card_id']) { $card = $rows->one('cards', ['id'=>$form->values['card_id'], 'is_active'=>1]); $update['member_id'] = $card['member_id']; } $rows->update('users', $update, $id); flash('Uživatel upraven.', 'success'); return redirect('/obsluha/'); } } else { $editedUser = $rows->one('users', $id); unset($editedUser['password']); if ($editedUser['member_id']) { $card = $rows->one('cards', ['member_id'=>$editedUser['member_id'], 'is_active'=>1]); if ($card) { $editedUser['card_id'] = $card['id']; } } $form->fill($editedUser); } return render('form', ['form'=>$form, 'title'=>'Upravit obsluhu']); }); function pois2geojson($pois, $Parsedown) { $geojson = ['type'=>'FeatureCollection', 'features'=>[]]; foreach ($pois as $poi) { $geojson['features'][] = [ 'type' => 'Feature', 'properties' => [ 'name' => $poi['name'], 'description' => $Parsedown->text($poi['description']), 'cheatsheet' => $Parsedown->text($poi['cheatsheet']), 'slug' => $poi['slug'], 'id' => $poi['id'], ], 'geometry'=>[ 'type'=>'Point', 'coordinates'=>[$poi['lon'], $poi['lat']] ] ]; } return $geojson; } function slugify($text, $divider = '-') { // replace non letter or digits by divider $text = preg_replace('~[^\pL\d]+~u', $divider, $text); // transliterate $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); // remove unwanted characters $text = preg_replace('~[^-\w]+~', '', $text); // trim $text = trim($text, $divider); // remove duplicate divider $text = preg_replace('~-+~', $divider, $text); // lowercase $text = strtolower($text); if (empty($text)) { return 'n-a'; } return $text; }