diff options
Diffstat (limited to 'lib/Travelynx.pm')
| -rwxr-xr-x | lib/Travelynx.pm | 674 |
1 files changed, 580 insertions, 94 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index af46e5a..a0661bf 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -1,6 +1,7 @@ package Travelynx; # Copyright (C) 2020-2023 Birte Kristina Friesel +# Copyright (C) 2025 networkException <git@nwex.de> # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -20,10 +21,12 @@ use List::Util; use List::UtilsBy qw(uniq_by); use List::MoreUtils qw(first_index); use Travel::Status::DE::DBRIS::Formation; -use Travelynx::Helper::DBDB; use Travelynx::Helper::DBRIS; +use Travelynx::Helper::EFA; use Travelynx::Helper::HAFAS; use Travelynx::Helper::IRIS; +use Travelynx::Helper::Locales; +use Travelynx::Helper::MOTIS; use Travelynx::Helper::Sendmail; use Travelynx::Helper::Traewelling; use Travelynx::Model::InTransit; @@ -65,6 +68,8 @@ sub startup { $self->types->type( csv => 'text/csv; charset=utf-8' ); $self->types->type( json => 'application/json; charset=utf-8' ); + $self->types->type( gpx => 'application/gpx+xml; charset=utf-8' ); + $self->types->type( xml => 'text/xml; charset=utf-8' ); $self->plugin('Config'); @@ -154,15 +159,24 @@ sub startup { } ); + $self->hook( + 'before_render' => sub { + my ($self) = @_; + + $self->stash( loc_handle => $self->loc_handle ); + } + ); + $self->attr( cache_iris_main => sub { my ($self) = @_; - return Cache::File->new( + state $cache = Cache::File->new( cache_root => $self->app->config->{cache}->{schedule}, default_expires => '6 hours', lock_level => Cache::File::LOCK_LOCAL(), ); + return $cache; } ); @@ -170,11 +184,12 @@ sub startup { cache_iris_rt => sub { my ($self) = @_; - return Cache::File->new( + state $cache = Cache::File->new( cache_root => $self->app->config->{cache}->{realtime}, default_expires => '70 seconds', lock_level => Cache::File::LOCK_LOCAL(), ); + return $cache; } ); @@ -192,7 +207,7 @@ sub startup { $self->attr( renamed_station => sub { - my $legacy_to_new = JSON->new->utf8->decode( + state $legacy_to_new = JSON->new->utf8->decode( scalar read_file('share/old_station_names.json') ); return $legacy_to_new; } @@ -218,12 +233,27 @@ sub startup { ); $self->helper( + efa => sub { + my ($self) = @_; + state $efa = Travelynx::Helper::EFA->new( + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->base_url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->app->config->{version}, + ); + } + ); + + $self->helper( dbris => sub { my ($self) = @_; state $dbris = Travelynx::Helper::DBRIS->new( log => $self->app->log, service_config => $self->app->config->{dbris}, - cache => $self->app->cache_iris_rt, + realtime_cache => $self->app->cache_iris_rt, + main_cache => $self->app->cache_iris_main, root_url => $self->base_url_for('/')->to_abs, user_agent => $self->ua, version => $self->app->config->{version}, @@ -260,6 +290,20 @@ sub startup { ); $self->helper( + motis => sub { + my ($self) = @_; + state $motis = Travelynx::Helper::MOTIS->new( + log => $self->app->log, + cache => $self->app->cache_iris_rt, + user_agent => $self->ua, + root_url => $self->base_url_for('/')->to_abs, + version => $self->app->config->{version}, + time_zone => 'Europe/Berlin', + ); + } + ); + + $self->helper( traewelling => sub { my ($self) = @_; state $trwl = Travelynx::Model::Traewelling->new( pg => $self->pg ); @@ -365,16 +409,59 @@ sub startup { ); $self->helper( - dbdb => sub { + loc_handle => sub { my ($self) = @_; - state $dbdb = Travelynx::Helper::DBDB->new( - log => $self->app->log, - main_cache => $self->app->cache_iris_main, - realtime_cache => $self->app->cache_iris_rt, - root_url => $self->base_url_for('/')->to_abs, - user_agent => $self->ua, - version => $self->app->config->{version}, - ); + + my @languages; + if ( $self->is_user_authenticated + and @{ $self->current_user->{languages} } ) + { + @languages = @{ $self->current_user->{languages} }; + } + elsif ( my $languages = $self->req->headers->accept_language ) { + for my $lang ( split( qr{ \s* , \s* }x, $languages ) ) { + if ( $lang =~ m{ ^ de }x ) { + push( @languages, 'de-DE' ); + } + elsif ( $lang =~ m{ ^ en }x ) { + push( @languages, 'en-GB' ); + } + elsif ( $lang =~ m{ ^ fr }x ) { + push( @languages, 'fr-FR' ); + } + elsif ( $lang =~ m{ ^ hu }x ) { + push( @languages, 'hu-HU' ); + } + elsif ( $lang =~ m{ ^ pl }x ) { + push( @languages, 'pl-PL' ); + } + } + } + + # en-GB and de-DE serve as fall-back languages, both in case + # we do not have the handle we need (here) and in case a string + # has only been translated to some languages (below). + + my $handle + = Travelynx::Helper::Locales->get_handle( @languages, 'en-GB', + 'de-DE' ); + my $first_fallback + = Travelynx::Helper::Locales->get_handle('en-GB'); + my $second_fallback + = Travelynx::Helper::Locales->get_handle('de-DE'); + + $handle->fail_with( + sub { $first_fallback->maketext( @_[ 1 .. $#_ ] ) } ); + $first_fallback->fail_with( + sub { $second_fallback->maketext( @_[ 1 .. $#_ ] ) } ); + return $handle; + } + ); + + $self->helper( + 'L' => sub { + my ( $self, @args ) = @_; + $self->stash('loc_handle')->maketext(@args); } ); @@ -415,6 +502,32 @@ sub startup { ); $self->helper( + 'efa_load_icon' => sub { + my ( $self, $occupancy ) = @_; + + my @symbols + = ( + qw(help_outline person_outline people priority_high not_interested) + ); + + if ( $occupancy eq 'MANY_SEATS' ) { + $occupancy = 1; + } + elsif ( $occupancy eq 'FEW_SEATS' ) { + $occupancy = 2; + } + elsif ( $occupancy eq 'STANDING_ONLY' ) { + $occupancy = 3; + } + elsif ( $occupancy eq 'FULL' ) { + $occupancy = 4; + } + + return $symbols[$occupancy] // 'help_outline'; + } + ); + + $self->helper( 'load_icon' => sub { my ( $self, $load ) = @_; my $first = $load->{FIRST} // 0; @@ -478,9 +591,15 @@ sub startup { if ( $opt{dbris} ) { return $self->_checkin_dbris_p(%opt); } + if ( $opt{efa} ) { + return $self->_checkin_efa_p(%opt); + } if ( $opt{hafas} ) { return $self->_checkin_hafas_p(%opt); } + if ( $opt{motis} ) { + return $self->_checkin_motis_p(%opt); + } my $promise = Mojo::Promise->new; @@ -557,6 +676,158 @@ sub startup { ); $self->helper( + '_checkin_motis_p' => sub { + my ( $self, %opt ) = @_; + + my $station = $opt{station}; + my $train_id = $opt{train_id}; + my $ts = $opt{ts}; + my $uid = $opt{uid} // $self->current_user->{id}; + my $db = $opt{db} // $self->pg->db; + my $hafas; + + my $promise = Mojo::Promise->new; + + $self->motis->get_trip_p( + service => $opt{motis}, + trip_id => $train_id, + )->then( + sub { + my ($trip) = @_; + my $found_stopover; + + for my $stopover ( $trip->stopovers ) { + if ( $stopover->stop->id eq $station ) { + $found_stopover = $stopover; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts + and $stopover->scheduled_departure->epoch + == $ts ) + { + last; + } + } + } + + if ( not $found_stopover ) { + $promise->reject( +"Did not find stopover at '$station' within trip '$train_id'" + ); + return; + } + + for my $stopover ( $trip->stopovers ) { + $self->stations->add_or_update( + stop => $stopover->stop, + db => $db, + motis => $opt{motis}, + ); + } + + $self->stations->add_or_update( + stop => $found_stopover->stop, + db => $db, + motis => $opt{motis}, + ); + + eval { + $self->in_transit->add( + uid => $uid, + db => $db, + journey => $trip, + stopover => $found_stopover, + data => { trip_id => $train_id }, + backend_id => $self->stations->get_backend_id( + motis => $opt{motis} + ), + ); + }; + + if ($@) { + $self->app->log->error( + "Checkin($uid): INSERT failed: $@"); + $promise->reject( 'INSERT failed: ' . $@ ); + return; + } + + my $polyline; + if ( $trip->polyline ) { + my @station_list; + my @coordinate_list; + for my $coordinate ( $trip->polyline ) { + if ( $coordinate->{stop} ) { + if ( not defined $coordinate->{stop}->{eva} ) { + die(); + } + + push( + @coordinate_list, + [ + $coordinate->{lon}, + $coordinate->{lat}, + $coordinate->{stop}->{eva} + ] + ); + + push( @station_list, + $coordinate->{stop}->name ); + } + else { + push( @coordinate_list, + [ $coordinate->{lon}, $coordinate->{lat} ] + ); + } + } + + # equal length → polyline only consists of straight + # lines between stops. that's not helpful. + if ( @station_list == @coordinate_list ) { + $self->log->debug( 'Ignoring polyline for ' + . $trip->route_name + . ' as it only consists of straight lines between stops.' + ); + } + else { + $polyline = { + from_eva => + ( $trip->stopovers )[0]->stop->{eva}, + to_eva => ( $trip->stopovers )[-1]->stop->{eva}, + coords => \@coordinate_list, + }; + } + } + + if ($polyline) { + $self->in_transit->set_polyline( + uid => $uid, + db => $db, + polyline => $polyline, + ); + } + + # mustn't be called during a transaction + if ( not $opt{in_transaction} ) { + $self->run_hook( $uid, 'checkin' ); + } + + $promise->resolve($trip); + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + + return $promise; + } + ); + + $self->helper( '_checkin_dbris_p' => sub { my ( $self, %opt ) = @_; @@ -699,6 +970,137 @@ sub startup { ); $self->helper( + '_checkin_efa_p' => sub { + my ( $self, %opt ) = @_; + my $station = $opt{station}; + my $trip_id = $opt{train_id}; + my $ts = $opt{ts}; + my $uid = $opt{uid} // $self->current_user->{id}; + my $db = $opt{db} // $self->pg->db; + + my $promise = Mojo::Promise->new; + $self->efa->get_journey_p( + service => $opt{efa}, + trip_id => $trip_id + )->then( + sub { + my ($journey) = @_; + + my $found; + for my $stop ( $journey->route ) { + if ( $stop->id_num == $station ) { + $found = $stop; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts and $stop->sched_dep->epoch == $ts ) { + last; + } + } + } + if ( not $found ) { + $promise->reject( +"Did not find stop '$station' within journey '$trip_id'" + ); + return; + } + + for my $stop ( $journey->route ) { + $self->stations->add_or_update( + stop => $stop, + db => $db, + efa => $opt{efa}, + ); + } + + eval { + $self->in_transit->add( + uid => $uid, + db => $db, + journey => $journey, + stop => $found, + trip_id => $trip_id, + backend_id => $self->stations->get_backend_id( + efa => $opt{efa} + ), + ); + }; + if ($@) { + $self->app->log->error( + "Checkin($uid): INSERT failed: $@"); + $promise->reject( 'INSERT failed: ' . $@ ); + return; + } + + my $polyline; + if ( $journey->polyline ) { + my @station_list; + my @coordinate_list; + for my $coord ( $journey->polyline ) { + if ( $coord->{stop} ) { + push( + @coordinate_list, + [ + $coord->{lon}, $coord->{lat}, + $coord->{stop}->id_num + ] + ); + push( @station_list, + $coord->{stop}->full_name ); + } + else { + push( @coordinate_list, + [ $coord->{lon}, $coord->{lat} ] ); + } + } + + # equal length → polyline only consists of straight + # lines between stops. that's not helpful. + if ( @station_list == @coordinate_list ) { + $self->log->debug( 'Ignoring polyline for ' + . $journey->line + . ' as it only consists of straight lines between stops.' + ); + } + else { + $polyline = { + from_eva => ( $journey->route )[0]->id_num, + to_eva => ( $journey->route )[-1]->id_num, + coords => \@coordinate_list, + }; + } + } + + if ($polyline) { + $self->in_transit->set_polyline( + uid => $uid, + db => $db, + polyline => $polyline, + ); + } + + # mustn't be called during a transaction + if ( not $opt{in_transaction} ) { + $self->run_hook( $uid, 'checkin' ); + } + + $promise->resolve($journey); + + return; + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + return $promise; + } + ); + + $self->helper( '_checkin_hafas_p' => sub { my ( $self, %opt ) = @_; @@ -707,7 +1109,6 @@ sub startup { my $ts = $opt{ts}; my $uid = $opt{uid} // $self->current_user->{id}; my $db = $opt{db} // $self->pg->db; - my $hafas; my $promise = Mojo::Promise->new; @@ -814,7 +1215,7 @@ sub startup { # mustn't be called during a transaction if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'checkin' ); - if ( $opt{hafas} eq 'DB' and $journey->class <= 16 ) { + if ( $opt{hafas} eq 'ÖBB' and $journey->class <= 16 ) { $self->add_wagonorder( uid => $uid, train_id => $journey->id, @@ -966,7 +1367,12 @@ sub startup { return $promise->resolve( 0, 'race condition' ); } - if ( $user->{is_dbris} or $user->{is_hafas} ) { + if ( $user->{is_dbris} + or $user->{is_efa} + or $user->{is_hafas} + or $user->{is_motis} + or $train_id eq 'manual' ) + { return $self->_checkout_journey_p(%opt); } @@ -1096,7 +1502,9 @@ sub startup { if ($has_arrived) { my @unknown_stations = $self->stations->grep_unknown( - $train->route ); + backend_id => $user->{backend_id}, + names => [ $train->route ] + ); if (@unknown_stations) { $self->app->log->warn( sprintf( @@ -1263,6 +1671,13 @@ sub startup { rt_arrival => ( $stop->[2]{rt_arr} || $stop->[2]{sched_arr} ) ); + if ( $stop->[2]{platform} ) { + $self->in_transit->set_arrival_platform( + uid => $uid, + db => $db, + arrival_platform => $stop->[2]{platform} + ); + } if ( $now > ( $stop->[2]{rt_arr} || $stop->[2]{sched_arr} ) ) { @@ -1456,38 +1871,25 @@ sub startup { my $db = $self->pg->db; if ( $datetime and $train_no ) { - $self->dbdb->has_wagonorder_p(%opt)->then( - sub { - return $self->dbdb->get_wagonorder_p(%opt); - } - )->then( + $self->dbris->get_wagonorder_p(%opt)->then( sub { - my ($wagonorder) = @_; + my ($status) = @_; + my $wr = $status->result; my $data = {}; my $user_data = {}; - my $wr; - eval { - $wr - = Travel::Status::DE::DBRIS::Formation->new( - json => $wagonorder ); - }; - if ( $opt{is_departure} - and $wr - and not exists $wagonorder->{error} ) + and $wr ) { my $dt = $opt{datetime}->clone->set_time_zone('UTC'); - $data->{wagonorder_dep} = $wagonorder; + $data->{wagonorder_dep} = $status->{raw_json}; $data->{wagonorder_param} = { - time => $dt->rfc3339 =~ s{(?=Z)}{.000}r, - number => $opt{train_no}, - evaNumber => $opt{eva}, - administrationId => 80, - date => $dt->strftime('%Y-%m-%d'), - category => $opt{train_type}, + tt => $opt{train_type}, + tn => $opt{train_no}, + dt => $dt->epoch, + eva => $opt{eva} }; $user_data->{wagongroups} = []; for my $group ( $wr->groups ) { @@ -1534,10 +1936,8 @@ sub startup { train_id => $train_id, ); } - elsif ( $opt{is_arrival} - and not exists $wagonorder->{error} ) - { - $data->{wagonorder_arr} = $wagonorder; + elsif ( $opt{is_arrival} ) { + $data->{wagonorder_arr} = $status->{raw_json}; $self->in_transit->update_data( uid => $uid, db => $db, @@ -1549,6 +1949,10 @@ sub startup { } )->catch( sub { + my ($err) = @_; + $self->log->debug( + "add_wagonorder: promise rejected with ${err}"); + # no wagonorder? no problem. return; } @@ -1736,47 +2140,36 @@ sub startup { my $db = $self->pg->db; if ($is_departure) { - $self->dbdb->get_stationinfo_p($dep_eva)->then( - sub { - my ($station_info) = @_; - my $data = { stationinfo_dep => $station_info }; + if ( my $si + = $self->stations->get_bahn_stationinfo( eva => $dep_eva ) ) + { + my $data = { stationinfo_dep => $si }; - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data, - train_id => $train_id, - ); - return; - } - )->catch( - sub { - # no stationinfo? no problem. - return; - } - )->wait; + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + return; + } } if ( $arr_eva and not $is_departure ) { - $self->dbdb->get_stationinfo_p($arr_eva)->then( - sub { - my ($station_info) = @_; - my $data = { stationinfo_arr => $station_info }; + if ( my $si + = $self->stations->get_bahn_stationinfo( eva => $arr_eva ) ) + { - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data, - train_id => $train_id, - ); - return; - } - )->catch( - sub { - # no stationinfo? no problem. - return; - } - )->wait; + my $data = { stationinfo_arr => $si }; + + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + return; + } } } ); @@ -1792,7 +2185,9 @@ sub startup { $ret =~ s{[{]tn[}]}{$opt{tn}}g; $ret =~ s{[{]id[}]}{$opt{id}}g; $ret =~ s{[{]dbris[}]}{$opt{dbris}}g; + $ret =~ s{[{]efa[}]}{$opt{efa}}g; $ret =~ s{[{]hafas[}]}{$opt{hafas}}g; + $ret =~ s{[{]motis[}]}{$opt{motis}}g; if ( $opt{id} and not $opt{is_iris} ) { $ret =~ s{[{]id_or_tttn[}]}{$opt{id}}g; @@ -2050,8 +2445,10 @@ sub startup { backend_id => $latest->{backend_id}, backend_name => $latest->{backend_name}, is_dbris => $latest->{is_dbris}, + is_efa => $latest->{is_efa}, is_iris => $latest->{is_iris}, is_hafas => $latest->{is_hafas}, + is_motis => $latest->{is_motis}, journey_id => $latest->{journey_id}, timestamp => $action_time, timestamp_delta => $now->epoch - $action_time->epoch, @@ -2063,6 +2460,7 @@ sub startup { real_departure => epoch_to_dt( $latest->{real_dep_ts} ), dep_ds100 => $latest->{dep_ds100}, dep_eva => $latest->{dep_eva}, + dep_external_id => $latest->{dep_external_id}, dep_name => $latest->{dep_name}, dep_lat => $latest->{dep_lat}, dep_lon => $latest->{dep_lon}, @@ -2071,6 +2469,7 @@ sub startup { real_arrival => epoch_to_dt( $latest->{real_arr_ts} ), arr_ds100 => $latest->{arr_ds100}, arr_eva => $latest->{arr_eva}, + arr_external_id => $latest->{arr_external_id}, arr_name => $latest->{arr_name}, arr_lat => $latest->{arr_lat}, arr_lon => $latest->{arr_lon}, @@ -2113,7 +2512,9 @@ sub startup { backend => { id => $status->{backend_id}, type => $status->{is_dbris} ? 'DBRIS' + : $status->{is_efa} ? 'EFA' : $status->{is_hafas} ? 'HAFAS' + : $status->{is_motis} ? 'MOTIS' : 'IRIS-TTS', name => $status->{backend_name}, }, @@ -2339,14 +2740,46 @@ sub startup { latlon => $_->{from_latlon} // $_->{dep_latlon} } } @journeys; + my @extra_stations; + + if ( $opt{show_all_stops} ) { + for my $journey (@journeys) { + my @j_stops = map { + { + name => $_->[2], + latlon => [ $_->[1], $_->[0] ] + } + } grep { defined $_->[2] } + @{ $journey->{polyline} // [] }; + @extra_stations + = uniq_by { $_->{name} } ( @extra_stations, @j_stops ); + } + } my @station_coordinates = map { [ $_->{latlon}, $_->{name} ] } @stations; + my @extra_station_coordinates + = map { [ $_->{latlon}, $_->{name} ] } @extra_stations; + + my @now_coordinates; + if ( $opt{with_now_markers} ) { + @now_coordinates = map { + [ + $_->{now_latlon}, + $_->{train_type} . ' ' + . ( $_->{train_line} // $_->{train_no} ) + ] + } @journeys; + } my @station_pairs; my @polylines; my %seen; + # not part of the travelled route, but trip route before/after the journey. + # Only used if show_full_route is set. + my @extra_polylines; + my @skipped_journeys; my @polyline_journeys = grep { $_->{polyline} } @journeys; my @beeline_journeys = grep { not $_->{polyline} } @journeys; @@ -2364,10 +2797,14 @@ sub startup { my $from_eva = $journey->{from_eva} // $journey->{dep_eva}; my $to_eva = $journey->{to_eva} // $journey->{arr_eva}; - my $from_index - = first_index { $_->[2] and $_->[2] == $from_eva } @polyline; - my $to_index - = first_index { $_->[2] and $_->[2] == $to_eva } @polyline; + # poly_dep_index, poly_arr_index are only available for + # journeys that were processed by get_travel_distance + # beforehand. However, they are much less error-prone than this + # first_index / last_index kludge when it comes to ring lines. + my $from_index = $journey->{poly_dep_index} + // first_index { $_->[2] and $_->[2] == $from_eva } @polyline; + my $to_index = $journey->{poly_arr_index} + // first_index { $_->[2] and $_->[2] == $to_eva } @polyline; # Work around inconsistencies caused by a multiple EVA IDs mapping to the same station name if ( $from_index == -1 ) { @@ -2410,7 +2847,6 @@ sub startup { if ( $seen{$key} ) { next; } - $seen{$key} = 1; # direction does not matter at the moment @@ -2423,12 +2859,28 @@ sub startup { if ( $from_index > $to_index ) { ( $to_index, $from_index ) = ( $from_index, $to_index ); } + if ( $opt{show_full_route} ) { + my @pre_polyline = @polyline[ 0 .. $from_index ]; + my @post_polyline = @polyline[ $to_index .. $#polyline ]; + my @pre_polyline_coords; + for my $coord (@pre_polyline) { + push( @pre_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + my @post_polyline_coords; + for my $coord (@post_polyline) { + push( @post_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + push( @extra_polylines, + ( \@pre_polyline_coords, \@post_polyline_coords ) ); + } @polyline = @polyline[ $from_index .. $to_index ]; my @polyline_coords; for my $coord (@polyline) { push( @polyline_coords, [ $coord->[1], $coord->[0] ] ); } - push( @polylines, [@polyline_coords] ); + push( @polylines, \@polyline_coords ); } for my $journey (@beeline_journeys) { @@ -2536,7 +2988,7 @@ sub startup { { polylines => $json->encode( \@station_pairs ), color => '#673ab7', - opacity => @polylines + opacity => scalar @polylines ? $with_polyline ? 0.4 : 0.6 @@ -2546,8 +2998,14 @@ sub startup { polylines => $json->encode( \@polylines ), color => '#673ab7', opacity => 0.8, - } + }, + { + polylines => $json->encode( \@extra_polylines ), + color => '#665577', + opacity => 0.6, + }, ], + markers => \@now_coordinates, }; if (@station_coordinates) { @@ -2561,6 +3019,13 @@ sub startup { = [ [ $min_lat, $min_lon ], [ $max_lat, $max_lon ] ]; } + if (@extra_station_coordinates) { + $ret->{station_coordinates} = [ + uniq_by { $_->[1] } + ( @station_coordinates, @extra_station_coordinates ) + ]; + } + return $ret; } ); @@ -2628,10 +3093,17 @@ sub startup { if ( $self->is_user_authenticated ) { return 1; } - $self->render( - 'login', - redirect_to => $self->req->url, - from => 'auth_required' + $self->respond_to( + json => { + json => { error => 'authentication required' }, + status => 401 + }, + any => { + template => 'login', + status => 401, + redirect_to => $self->req->url, + from => 'auth_required' + } ); return undef; } @@ -2645,9 +3117,11 @@ sub startup { $authed_r->get('/account/hooks')->to('account#webhook'); $authed_r->get('/account/traewelling')->to('traewelling#settings'); $authed_r->get('/account/insight')->to('account#insight'); + $authed_r->get('/account/language')->to('account#change_language'); $authed_r->get('/ajax/status_card.html')->to('traveling#status_card'); $authed_r->get( '/cancelled' => [ format => [ 'html', 'json' ] ] ) ->to( 'traveling#cancelled', format => undef ); + $authed_r->get('/checkin/add')->to('traveling#add_intransit_form'); $authed_r->get('/fgr')->to('passengerrights#list_candidates'); $authed_r->get('/account/password')->to('account#password_form'); $authed_r->get('/account/mail')->to('account#change_mail'); @@ -2662,10 +3136,19 @@ sub startup { $authed_r->get('/history/:year')->to('traveling#yearly_history'); $authed_r->get('/history/:year/review')->to('traveling#year_in_review'); $authed_r->get('/history/:year/:month')->to('traveling#monthly_history'); - $authed_r->get('/journey/add')->to('traveling#add_journey_form'); + $authed_r->get('/journey/add') + ->to('traveling#add_journey_form') + ->name('add_journey'); $authed_r->get('/journey/comment')->to('traveling#comment_form'); $authed_r->get('/journey/visibility')->to('traveling#visibility_form'); - $authed_r->get('/journey/:id')->to('traveling#journey_details'); + $authed_r->get( '/journey/:id' => [ format => [ 'html', 'json' ] ] ) + ->to( 'traveling#journey_details', format => undef ) + ->name('journey'); + $authed_r->get( '/polyline/:id' => [ format => [ 'gpx', 'json' ] ] )->to( + 'traveling#journey_details', + format => undef, + polyline_export => 1 + )->name('polyline_download'); $authed_r->get('/s/*station')->to('traveling#station'); $authed_r->get('/confirm_mail/:token')->to('account#confirm_mail'); $authed_r->post('/account/privacy')->to('account#privacy'); @@ -2674,8 +3157,11 @@ sub startup { $authed_r->post('/account/hooks')->to('account#webhook'); $authed_r->post('/account/traewelling')->to('traewelling#settings'); $authed_r->post('/account/insight')->to('account#insight'); + $authed_r->post('/account/language')->to('account#change_language'); $authed_r->post('/account/select_backend')->to('account#change_backend'); + $authed_r->post('/checkin/add')->to('traveling#add_intransit_form'); $authed_r->post('/journey/add')->to('traveling#add_journey_form'); + $authed_r->post('/polyline/set')->to('traveling#set_polyline'); $authed_r->post('/journey/comment')->to('traveling#comment_form'); $authed_r->post('/journey/visibility')->to('traveling#visibility_form'); $authed_r->post('/journey/edit')->to('traveling#edit_journey'); |
