summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xlib/Travelynx.pm26
-rw-r--r--lib/Travelynx/Command/database.pm114
-rw-r--r--lib/Travelynx/Controller/Account.pm78
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm668
-rw-r--r--lib/Travelynx/Model/InTransit.pm51
-rwxr-xr-xlib/Travelynx/Model/Journeys.pm66
-rw-r--r--lib/Travelynx/Model/Users.pm53
-rw-r--r--public/static/js/travelynx-actions.js7
-rw-r--r--templates/_checked_in.html.ep23
-rw-r--r--templates/_public_status_card.html.ep23
-rw-r--r--templates/account.html.ep30
-rw-r--r--templates/journey.html.ep10
-rw-r--r--templates/landingpage.html.ep2
-rw-r--r--templates/privacy.html.ep88
-rw-r--r--templates/profile.html.ep12
-rw-r--r--templates/user_status.html.ep2
16 files changed, 877 insertions, 376 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index 98b22d8..6d903e6 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -421,6 +421,25 @@ sub startup {
);
$self->helper(
+ 'visibility_icon' => sub {
+ my ( $self, $visibility ) = @_;
+ if ( $visibility eq 'public' ) {
+ return 'language';
+ }
+ if ( $visibility eq 'travelynx' ) {
+ return 'lock_open';
+ }
+ if ( $visibility eq 'unlisted' ) {
+ return 'lock_outline';
+ }
+ if ( $visibility eq 'private' ) {
+ return 'lock';
+ }
+ return 'help_outline';
+ }
+ );
+
+ $self->helper(
'checkin' => sub {
my ( $self, %opt ) = @_;
@@ -1348,7 +1367,8 @@ sub startup {
uid => $uid,
db => $db,
with_data => 1,
- with_timestamps => 1
+ with_timestamps => 1,
+ with_visibility => 1,
);
if ($in_transit) {
@@ -1425,6 +1445,8 @@ sub startup {
messages => $in_transit->{messages},
extra_data => $in_transit->{data},
comment => $in_transit->{user_data}{comment},
+ visibility => $in_transit->{visibility},
+ visibility_str => $in_transit->{visibility_str},
};
my $traewelling = $self->traewelling->get(
@@ -2205,6 +2227,7 @@ sub startup {
$authed_r->get('/history/:year/:month')->to('traveling#monthly_history');
$authed_r->get('/journey/add')->to('traveling#add_journey_form');
$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('/s/*station')->to('traveling#station');
$authed_r->get('/confirm_mail/:token')->to('account#confirm_mail');
@@ -2215,6 +2238,7 @@ sub startup {
$authed_r->post('/account/services')->to('account#services');
$authed_r->post('/journey/add')->to('traveling#add_journey_form');
$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');
$authed_r->post('/journey/passenger_rights/*filename')
->to('passengerrights#generate');
diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm
index 5ef80f0..82f1f47 100644
--- a/lib/Travelynx/Command/database.pm
+++ b/lib/Travelynx/Command/database.pm
@@ -1312,6 +1312,120 @@ my @migrations = (
}
);
},
+
+ # v32 -> v33
+ # add optional per-status visibility that overrides global visibility
+ sub {
+ my ($db) = @_;
+ $db->query(
+ qq{
+ alter table journeys add column visibility smallint;
+ alter table in_transit add column visibility smallint;
+ drop view journeys_str;
+ drop view in_transit_str;
+ create view journeys_str as select
+ journeys.id as journey_id, user_id,
+ train_type, train_line, train_no, train_id,
+ extract(epoch from checkin_time) as checkin_ts,
+ extract(epoch from sched_departure) as sched_dep_ts,
+ extract(epoch from real_departure) as real_dep_ts,
+ checkin_station_id as dep_eva,
+ dep_station.ds100 as dep_ds100,
+ dep_station.name as dep_name,
+ dep_station.lat as dep_lat,
+ dep_station.lon as dep_lon,
+ extract(epoch from checkout_time) as checkout_ts,
+ extract(epoch from sched_arrival) as sched_arr_ts,
+ extract(epoch from real_arrival) as real_arr_ts,
+ checkout_station_id as arr_eva,
+ arr_station.ds100 as arr_ds100,
+ arr_station.name as arr_name,
+ arr_station.lat as arr_lat,
+ arr_station.lon as arr_lon,
+ polylines.polyline as polyline,
+ visibility,
+ cancelled, edited, route, messages, user_data,
+ dep_platform, arr_platform
+ from journeys
+ left join polylines on polylines.id = polyline_id
+ left join stations as dep_station on checkin_station_id = dep_station.eva
+ left join stations as arr_station on checkout_station_id = arr_station.eva
+ ;
+ create view in_transit_str as select
+ user_id,
+ train_type, train_line, train_no, train_id,
+ extract(epoch from checkin_time) as checkin_ts,
+ extract(epoch from sched_departure) as sched_dep_ts,
+ extract(epoch from real_departure) as real_dep_ts,
+ checkin_station_id as dep_eva,
+ dep_station.ds100 as dep_ds100,
+ dep_station.name as dep_name,
+ dep_station.lat as dep_lat,
+ dep_station.lon as dep_lon,
+ extract(epoch from checkout_time) as checkout_ts,
+ extract(epoch from sched_arrival) as sched_arr_ts,
+ extract(epoch from real_arrival) as real_arr_ts,
+ checkout_station_id as arr_eva,
+ arr_station.ds100 as arr_ds100,
+ arr_station.name as arr_name,
+ arr_station.lat as arr_lat,
+ arr_station.lon as arr_lon,
+ polylines.polyline as polyline,
+ visibility,
+ cancelled, route, messages, user_data,
+ dep_platform, arr_platform, data
+ from in_transit
+ left join polylines on polylines.id = polyline_id
+ left join stations as dep_station on checkin_station_id = dep_station.eva
+ left join stations as arr_station on checkout_station_id = arr_station.eva
+ ;
+ }
+ );
+ my $res = $db->select( 'users', [ 'id', 'public_level' ] );
+ while ( my $row = $res->hash ) {
+ my $old_level = $row->{public_level};
+ my $new_level = 0;
+ if ( $old_level & 0x01 ) {
+
+ # status: account required
+ $new_level = 80;
+ }
+ if ( $old_level & 0x02 ) {
+
+ # status: public
+ $new_level = 100;
+ }
+ if ( $old_level & 0x04 ) {
+
+ # comment public
+ $new_level |= 0x80;
+ }
+ if ( $old_level & 0x10 ) {
+
+ # past: account required
+ $new_level |= 0x100;
+ }
+ if ( $old_level & 0x20 ) {
+
+ # past: public
+ $new_level |= 0x200;
+ }
+ if ( $old_level & 0x40 ) {
+
+ # past: infinite (default is 4 weeks)
+ $new_level |= 0x400;
+ }
+ my $r = $db->update(
+ 'users',
+ { public_level => $new_level },
+ { id => $row->{id} }
+ )->rows;
+ if ( $r != 1 ) {
+ die("oh no");
+ }
+ }
+ $db->update( 'schema_version', { version => 33 } );
+ },
);
sub sync_stations {
diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm
index f0e9346..e8bfcaf 100644
--- a/lib/Travelynx/Controller/Account.pm
+++ b/lib/Travelynx/Controller/Account.pm
@@ -9,6 +9,22 @@ use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
use JSON;
use UUID::Tiny qw(:std);
+my %visibility_itoa = (
+ 100 => 'public',
+ 80 => 'travelynx',
+ 60 => 'followers',
+ 30 => 'unlisted',
+ 10 => 'private',
+);
+
+my %visibility_atoi = (
+ public => 100,
+ travelynx => 80,
+ followers => 60,
+ unlisted => 30,
+ private => 10,
+);
+
# Internal Helpers
sub hash_password {
@@ -438,50 +454,30 @@ sub privacy {
my $public_level = $user->{is_public};
if ( $self->param('action') and $self->param('action') eq 'save' ) {
- if ( $self->param('status_level') eq 'intern' ) {
- $public_level |= 0x01;
- $public_level &= ~0x02;
- }
- elsif ( $self->param('status_level') eq 'extern' ) {
- $public_level |= 0x02;
- $public_level &= ~0x01;
- }
- else {
- $public_level &= ~0x03;
+ my %opt;
+ my $default_visibility
+ = $visibility_atoi{ $self->param('status_level') };
+ if ( defined $default_visibility ) {
+ $opt{default_visibility} = $default_visibility;
}
- # public comment with non-public status does not make sense
- if ( $self->param('public_comment')
- and $self->param('status_level') ne 'private' )
- {
- $public_level |= 0x04;
- }
- else {
- $public_level &= ~0x04;
- }
+ $opt{comments_visible} = $self->param('public_comment') ? 1 : 0;
+
+ $opt{past_all} = $self->param('history_age') eq 'infinite' ? 1 : 0;
if ( $self->param('history_level') eq 'intern' ) {
- $public_level |= 0x10;
- $public_level &= ~0x20;
+ $opt{past_visible} = 1;
}
elsif ( $self->param('history_level') eq 'extern' ) {
- $public_level |= 0x20;
- $public_level &= ~0x10;
- }
- else {
- $public_level &= ~0x30;
- }
-
- if ( $self->param('history_age') eq 'infinite' ) {
- $public_level |= 0x40;
+ $opt{past_visible} = 2;
}
else {
- $public_level &= ~0x40;
+ $opt{past_visible} = 0;
}
$self->users->set_privacy(
- uid => $user->{id},
- level => $public_level
+ uid => $user->{id},
+ %opt
);
$self->flash( success => 'privacy' );
@@ -489,18 +485,14 @@ sub privacy {
}
else {
$self->param(
- status_level => $public_level & 0x01 ? 'intern'
- : $public_level & 0x02 ? 'extern'
- : 'private'
- );
- $self->param( public_comment => $public_level & 0x04 ? 1 : 0 );
+ status_level => $visibility_itoa{ $user->{default_visibility} } );
+ $self->param( public_comment => $user->{comments_visible} );
$self->param(
- history_level => $public_level & 0x10 ? 'intern'
- : $public_level & 0x20 ? 'extern'
- : 'private'
+ history_level => $user->{past_visible} & 0x01 ? 'intern'
+ : $user->{past_visible} & 0x02 ? 'extern'
+ : 'private'
);
- $self->param(
- history_age => $public_level & 0x40 ? 'infinite' : 'month' );
+ $self->param( history_age => $user->{past_all} ? 'infinite' : 'month' );
$self->render( 'privacy', name => $user->{name} );
}
}
diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm
index 3f39996..15b131a 100755
--- a/lib/Travelynx/Controller/Traveling.pm
+++ b/lib/Travelynx/Controller/Traveling.pm
@@ -370,6 +370,14 @@ sub get_connecting_trains_p {
return $promise;
}
+sub compute_effective_visibility {
+ my ( $self, $default_visibility, $journey_visibility ) = @_;
+ if ( $journey_visibility eq 'default' ) {
+ return $default_visibility;
+ }
+ return $journey_visibility;
+}
+
# Controllers
sub homepage {
@@ -378,6 +386,10 @@ sub homepage {
my $status = $self->get_user_status;
my @recent_targets;
if ( $status->{checked_in} ) {
+ my $journey_visibility
+ = $self->compute_effective_visibility(
+ $self->current_user->{default_visibility_str},
+ $status->{visibility_str} );
if ( defined $status->{arrival_countdown}
and $status->{arrival_countdown} < ( 40 * 60 ) )
{
@@ -389,11 +401,10 @@ sub homepage {
'landingpage',
version => $self->app->config->{version}
// 'UNKNOWN',
- user_status => $status,
- connections => $connecting_trains,
- transit_fyi => $transit_fyi,
- with_autocomplete => 1,
- with_geolocation => 1
+ user_status => $status,
+ journey_visibility => $journey_visibility,
+ connections => $connecting_trains,
+ transit_fyi => $transit_fyi,
);
$self->users->mark_seen(
uid => $self->current_user->{id} );
@@ -404,9 +415,8 @@ sub homepage {
'landingpage',
version => $self->app->config->{version}
// 'UNKNOWN',
- user_status => $status,
- with_autocomplete => 1,
- with_geolocation => 1
+ user_status => $status,
+ journey_visibility => $journey_visibility,
);
$self->users->mark_seen(
uid => $self->current_user->{id} );
@@ -414,6 +424,16 @@ sub homepage {
)->wait;
return;
}
+ else {
+ $self->render(
+ 'landingpage',
+ version => $self->app->config->{version} // 'UNKNOWN',
+ user_status => $status,
+ journey_visibility => $journey_visibility,
+ );
+ $self->users->mark_seen( uid => $self->current_user->{id} );
+ return;
+ }
}
else {
@recent_targets = uniq_by { $_->{eva} }
@@ -439,6 +459,26 @@ sub homepage {
}
}
+sub status_token_ok {
+ my ( $self, $status, $ts2_ext ) = @_;
+ my $token = $self->param('token') // q{};
+
+ my ( $eva, $ts, $ts2 ) = split( qr{-}, $token );
+ if ( not $ts ) {
+ return;
+ }
+
+ $ts2 //= $ts2_ext;
+
+ if ( $eva == $status->{dep_eva}
+ and $ts == $status->{timestamp}->epoch
+ and $ts2 == $status->{sched_departure}->epoch )
+ {
+ return 1;
+ }
+ return;
+}
+
sub user_status {
my ($self) = @_;
@@ -446,47 +486,33 @@ sub user_status {
my $ts = $self->stash('ts') // 0;
my $user = $self->users->get_privacy_by_name( name => $name );
- if ( not $user or not $user->{public_level} & 0x03 ) {
+ if ( not $user ) {
$self->render('not_found');
return;
}
- if ( $user->{public_level} & 0x01 and not $self->is_user_authenticated ) {
- $self->render( 'login', redirect_to => $self->req->url );
- return;
- }
-
my $status = $self->get_user_status( $user->{id} );
- my $journey;
if (
$ts
and ( not $status->{checked_in}
or $status->{sched_departure}->epoch != $ts )
- and (
- $user->{public_level} & 0x20
- or ( $user->{public_level} & 0x10
- and $self->is_user_authenticated )
- )
)
{
for my $candidate (
$self->journeys->get(
uid => $user->{id},
- limit => 10,
+ limit => 20,
)
)
{
if ( $candidate->{sched_dep_ts} eq $ts ) {
- $journey = $self->journeys->get_single(
- uid => $user->{id},
- journey_id => $candidate->{id},
- verbose => 1,
- with_datetime => 1,
- with_polyline => 1,
- );
+ $self->redirect_to("/p/${name}/j/$candidate->{id}");
+ return;
}
}
+ $self->render('not_found');
+ return;
}
my %tw_data = (
@@ -502,24 +528,30 @@ sub user_status {
site_name => 'travelynx',
);
- if ($journey) {
- $og_data{title} = $tw_data{title} = sprintf( 'Fahrt von %s nach %s',
- $journey->{from_name}, $journey->{to_name} );
- $og_data{description} = $tw_data{description}
- = $journey->{rt_arrival}->strftime('Ankunft am %d.%m.%Y um %H:%M');
- $og_data{url} .= "/${ts}";
- }
- elsif (
- $ts
- and ( not $status->{checked_in}
- or $status->{sched_departure}->epoch != $ts )
- )
- {
- $og_data{title} = $tw_data{title} = "Bahnfahrt beendet";
- $og_data{description} = $tw_data{description}
- = "${name} hat das Ziel erreicht";
+ my $visibility;
+ if ( $status->{checked_in} ) {
+ $visibility
+ = $self->compute_effective_visibility(
+ $user->{default_visibility_str},
+ $status->{visibility_str} );
+ if (
+ not(
+ $visibility eq 'public'
+ or ( $visibility eq 'unlisted'
+ and $self->status_token_ok( $status, $ts ) )
+ or (
+ $visibility eq 'travelynx'
+ and ( $self->is_user_authenticated
+ or $self->status_token_ok( $status, $ts ) )
+ )
+ )
+ )
+ {
+ $status->{checked_in} = 0;
+ }
}
- elsif ( $status->{checked_in} ) {
+
+ if ( $status->{checked_in} ) {
$og_data{url} .= '/' . $status->{sched_departure}->epoch;
$og_data{title} = $tw_data{title} = "${name} ist unterwegs";
$og_data{description} = $tw_data{description} = sprintf(
@@ -537,39 +569,18 @@ sub user_status {
else {
$og_data{title} = $tw_data{title}
= "${name} ist gerade nicht eingecheckt";
- $og_data{description} = $tw_data{description}
- = "Letztes Fahrtziel: $status->{arr_name}";
+ $og_data{description} = $tw_data{description} = q{};
}
- if ($journey) {
- if ( not $user->{public_level} & 0x04 ) {
- delete $journey->{user_data}{comment};
- }
- my $map_data = $self->journeys_to_map_data(
- journeys => [$journey],
- include_manual => 1,
- );
- $self->render(
- 'journey',
- error => undef,
- with_map => 1,
- readonly => 1,
- journey => $journey,
- twitter => \%tw_data,
- opengraph => \%og_data,
- %{$map_data},
- );
- }
- else {
- $self->render(
- 'user_status',
- name => $name,
- public_level => $user->{public_level},
- journey => $status,
- twitter => \%tw_data,
- opengraph => \%og_data,
- );
- }
+ $self->render(
+ 'user_status',
+ name => $name,
+ public_level => $user->{public_level},
+ journey => $status,
+ journey_visibility => $visibility,
+ twitter => \%tw_data,
+ opengraph => \%og_data,
+ );
}
sub public_profile {
@@ -578,48 +589,77 @@ sub public_profile {
my $name = $self->stash('name');
my $user = $self->users->get_privacy_by_name( name => $name );
+ if ( not $user ) {
+ $self->render('not_found');
+ }
+
+ my $status = $self->get_user_status( $user->{id} );
+ my $visibility;
+ if ( $status->{checked_in} ) {
+ $visibility
+ = $self->compute_effective_visibility(
+ $user->{default_visibility_str},
+ $status->{visibility_str} );
+ if (
+ not(
+ $visibility eq 'public'
+ or ( $visibility eq 'unlisted'
+ and $self->status_token_ok($status) )
+ or (
+ $visibility eq 'travelynx'
+ and ( $self->is_user_authenticated
+ or $self->status_token_ok($status) )
+ )
+ )
+ )
+ {
+ $status->{checked_in} = 0;
+ }
+ }
+
+ my %opt = (
+ uid => $user->{id},
+ limit => 10,
+ with_datetime => 1
+ );
+
+ if ( not $user->{past_all} ) {
+ my $now = DateTime->now( time_zone => 'Europe/Berlin' );
+ $opt{before} = DateTime->now( time_zone => 'Europe/Berlin' );
+ $opt{after} = $now->clone->subtract( weeks => 4 );
+ }
+
if (
- $user
- and (
- $user->{public_level} & 0x22
- or ( $user->{public_level} & 0x11
- and $self->is_user_authenticated )
- )
+ $user->{default_visibility_str} eq 'public'
+ or ( $user->{default_visibility_str} eq 'travelynx'
+ and $self->is_user_authenticated )
)
{
- my $status = $self->get_user_status( $user->{id} );
- my @journeys;
- if ( $user->{public_level} & 0x40 ) {
- @journeys = $self->journeys->get(
- uid => $user->{id},
- limit => 10,
- with_datetime => 1
- );
- }
- else {
- my $now = DateTime->now( time_zone => 'Europe/Berlin' );
- my $month_ago = $now->clone->subtract( weeks => 4 );
- @journeys = $self->journeys->get(
- uid => $user->{id},
- limit => 10,
- with_datetime => 1,
- after => $month_ago,
- before => $now
- );
- }
- $self->render(
- 'profile',
- name => $name,
- uid => $user->{id},
- public_level => $user->{public_level},
- journey => $status,
- journeys => [@journeys],
- version => $self->app->config->{version} // 'UNKNOWN',
- );
+ $opt{with_default_visibility} = 1;
}
else {
- $self->render('not_found');
+ $opt{with_default_visibility} = 0;
}
+
+ if ( $self->is_user_authenticated ) {
+ $opt{min_visibility} = 'travelynx';
+ }
+ else {
+ $opt{min_visibility} = 'public';
+ }
+
+ my @journeys = $self->journeys->get(%opt);
+
+ $self->render(
+ 'profile',
+ name => $name,
+ uid => $user->{id},
+ public_level => $user->{public_level},
+ journey => $status,
+ journey_visibility => $visibility,
+ journeys => [@journeys],
+ version => $self->app->config->{version} // 'UNKNOWN',
+ );
}
sub public_journey_details {
@@ -630,7 +670,26 @@ sub public_journey_details {
$self->param( journey_id => $journey_id );
- if ( not( $journey_id and $journey_id =~ m{ ^ \d+ $ }x ) ) {
+ if ( not( $user and $journey_id and $journey_id =~ m{ ^ \d+ $ }x ) ) {
+ $self->render(
+ 'journey',
+ status => 404,
+ error => 'notfound',
+ journey => {}
+ );
+ return;
+ }
+
+ my $journey = $self->journeys->get_single(
+ uid => $user->{id},
+ journey_id => $journey_id,
+ verbose => 1,
+ with_datetime => 1,
+ with_polyline => 1,
+ with_visibility => 1,
+ );
+
+ if ( not $journey ) {
$self->render(
'journey',
status => 404,
@@ -640,98 +699,93 @@ sub public_journey_details {
return;
}
+ my $visibility
+ = $self->compute_effective_visibility( $user->{default_visibility_str},
+ $journey->{visibility_str} );
+
if (
- $user
- and (
- $user->{public_level} & 0x20
- or ( $user->{public_level} & 0x10
- and $self->is_user_authenticated )
+ not(
+ $visibility eq 'public'
+ or ( $visibility eq 'unlisted'
+ and $self->status_token_ok($journey) )
+ or (
+ $visibility eq 'travelynx'
+ and ( $self->is_user_authenticated
+ or $self->status_token_ok($journey) )
+ )
)
)
{
- my $journey = $self->journeys->get_single(
- uid => $user->{id},
- journey_id => $journey_id,
- verbose => 1,
- with_datetime => 1,
- with_polyline => 1,
+ $self->render(
+ 'journey',
+ status => 404,
+ error => 'notfound',
+ journey => {}
);
+ return;
+ }
- if ( not( $user->{public_level} & 0x40 ) ) {
- my $month_ago = DateTime->now( time_zone => 'Europe/Berlin' )
- ->subtract( weeks => 4 )->epoch;
- if ( $journey and $journey->{rt_dep_ts} < $month_ago ) {
- $journey = undef;
- }
- }
-
- if ($journey) {
- my $title = sprintf( 'Fahrt von %s nach %s am %s',
- $journey->{from_name}, $journey->{to_name},
- $journey->{rt_arrival}->strftime('%d.%m.%Y') );
- my $delay = 'pünktlich ';
- if ( $journey->{rt_arrival} != $journey->{sched_arrival} ) {
- $delay = sprintf(
- 'mit %+d ',
- (
- $journey->{rt_arrival}->epoch
- - $journey->{sched_arrival}->epoch
- ) / 60
- );
- }
- my $description = sprintf( 'Ankunft mit %s %s %s',
- $journey->{type}, $journey->{no},
- $journey->{rt_arrival}->strftime('um %H:%M') );
- if ( $journey->{km_route} > 0.1 ) {
- $description = sprintf( '%.0f km mit %s %s – Ankunft %sum %s',
- $journey->{km_route}, $journey->{type}, $journey->{no},
- $delay, $journey->{rt_arrival}->strftime('%H:%M') );
- }
- my %tw_data = (
- card => 'summary',
- site => '@derfnull',
- image => $self->url_for('/static/icons/icon-512x512.png')
- ->to_abs->scheme('https'),
- title => $title,
- description => $description,
- );
- my %og_data = (
- type => 'article',
- image => $tw_data{image},
- url => $self->url_for->to_abs,
- site_name => 'travelynx',
- title => $title,
- description => $description,
- );
+ # TODO re-add age check unless status_token_ok (helper function?)
- my $map_data = $self->journeys_to_map_data(
- journeys => [$journey],
- include_manual => 1,
- );
- if ( $journey->{user_data}{comment}
- and not $user->{public_level} & 0x04 )
- {
- delete $journey->{user_data}{comment};
- }
- $self->render(
- 'journey',
- error => undef,
- journey => $journey,
- with_map => 1,
- username => $name,
- readonly => 1,
- twitter => \%tw_data,
- opengraph => \%og_data,
- %{$map_data},
- );
- }
- else {
- $self->render('not_found');
- }
+ my $title = sprintf( 'Fahrt von %s nach %s am %s',
+ $journey->{from_name}, $journey->{to_name},
+ $journey->{rt_arrival}->strftime('%d.%m.%Y') );
+ my $delay = 'pünktlich ';
+ if ( $journey->{rt_arrival} != $journey->{sched_arrival} ) {
+ $delay = sprintf(
+ 'mit %+d ',
+ (
+ $journey->{rt_arrival}->epoch
+ - $journey->{sched_arrival}->epoch
+ ) / 60
+ );
}
- else {
- $self->render('not_found');
+ my $description = sprintf( 'Ankunft mit %s %s %s',
+ $journey->{type}, $journey->{no},
+ $journey->{rt_arrival}->strftime('um %H:%M') );
+ if ( $journey->{km_route} > 0.1 ) {
+ $description = sprintf( '%.0f km mit %s %s – Ankunft %sum %s',
+ $journey->{km_route}, $journey->{type}, $journey->{no},
+ $delay, $journey->{rt_arrival}->strftime('%H:%M') );
}
+ my %tw_data = (
+ card => 'summary',
+ site => '@derfnull',
+ image => $self->url_for('/static/icons/icon-512x512.png')
+ ->to_abs->scheme('https'),
+ title => $title,
+ description => $description,
+ );
+ my %og_data = (
+ type => 'article',
+ image => $tw_data{image},
+ url => $self->url_for->to_abs,
+ site_name => 'travelynx',
+ title => $title,
+ description => $description,
+ );
+
+ my $map_data = $self->journeys_to_map_data(
+ journeys => [$journey],
+ include_manual => 1,
+ );
+ if ( $journey->{user_data}{comment}
+ and not $user->{public_level} & 0x04 )
+ {
+ delete $journey->{user_data}{comment};
+ }
+ $self->render(
+ 'journey',
+ error => undef,
+ journey => $journey,
+ with_map => 1,
+ username => $name,
+ readonly => 1,
+ twitter => \%tw_data,
+ opengraph => \%og_data,
+ journey_visibility => $visibility,
+ %{$map_data},
+ );
}
sub public_status_card {
@@ -743,26 +797,42 @@ sub public_status_card {
delete $self->stash->{layout};
- if (
- $user
- and (
- $user->{public_level} & 0x02
- or ( $user->{public_level} & 0x01
- and $self->is_user_authenticated )
- )
- )
- {
- my $status = $self->get_user_status( $user->{id} );
- $self->render(
- '_public_status_card',
- name => $name,
- public_level => $user->{public_level},
- journey => $status
- );
- }
- else {
+ if ( not $user ) {
$self->render('not_found');
+ return;
+ }
+
+ my $status = $self->get_user_status( $user->{id} );
+ my $visibility;
+ if ( $status->{checked_in} ) {
+ $visibility
+ = $self->compute_effective_visibility(
+ $user->{default_visibility_str},
+ $status->{visibility_str} );
+ if (
+ not(
+ $visibility eq 'public'
+ or ( $visibility eq 'unlisted'
+ and $self->status_token_ok($status) )
+ or (
+ $visibility eq 'travelynx'
+ and ( $self->is_user_authenticated
+ or $self->status_token_ok($status) )
+ )
+ )
+ )
+ {
+ $status->{checked_in} = 0;
+ }
}
+
+ $self->render(
+ '_public_status_card',
+ name => $name,
+ public_level => $user->{public_level},
+ journey => $status,
+ journey_visibility => $visibility,
+ );
}
sub status_card {
@@ -772,6 +842,10 @@ sub status_card {
delete $self->stash->{layout};
if ( $status->{checked_in} ) {
+ my $journey_visibility
+ = $self->compute_effective_visibility(
+ $self->current_user->{default_visibility_str},
+ $status->{visibility_str} );
if ( defined $status->{arrival_countdown}
and $status->{arrival_countdown} < ( 40 * 60 ) )
{
@@ -781,19 +855,28 @@ sub status_card {
my ( $connecting_trains, $transit_fyi ) = @_;
$self->render(
'_checked_in',
- journey => $status,
- connections => $connecting_trains,
- transit_fyi => $transit_fyi
+ journey => $status,
+ journey_visibility => $journey_visibility,
+ connections => $connecting_trains,
+ transit_fyi => $transit_fyi
);
}
)->catch(
sub {
- $self->render( '_checked_in', journey => $status );
+ $self->render(
+ '_checked_in',
+ journey => $status,
+ journey_visibility => $journey_visibility,
+ );
}
)->wait;
return;
}
- $self->render( '_checked_in', journey => $status );
+ $self->render(
+ '_checked_in',
+ journey => $status,
+ journey_visibility => $journey_visibility,
+ );
}
elsif ( $status->{cancellation} ) {
$self->render_later;
@@ -1693,11 +1776,12 @@ sub journey_details {
}
my $journey = $self->journeys->get_single(
- uid => $uid,
- journey_id => $journey_id,
- verbose => 1,
- with_datetime => 1,
- with_polyline => 1,
+ uid => $uid,
+ journey_id => $journey_id,
+ verbose => 1,
+ with_datetime => 1,
+ with_polyline => 1,
+ with_visibility => 1,
);
if ($journey) {
@@ -1705,15 +1789,18 @@ sub journey_details {
journeys => [$journey],
include_manual => 1,
);
+ my $with_share;
my $share_text;
- my $with_share = $user->{is_public} & 0x40 ? 1 : 0;
- if ( not $with_share and $user->{is_public} & 0x20 ) {
- my $month_ago = DateTime->now( time_zone => 'Europe/Berlin' )
- ->subtract( weeks => 4 )->epoch;
- $with_share = $journey->{rt_dep_ts} > $month_ago ? 1 : 0;
- }
- if ($with_share) {
+ my $visibility
+ = $self->compute_effective_visibility(
+ $user->{default_visibility_str},
+ $journey->{visibility_str} );
+
+ if ( $visibility eq 'public'
+ or $visibility eq 'travelynx'
+ or $visibility eq 'unlisted' )
+ {
my $delay = 'pünktlich ';
if ( $journey->{rt_arrival} != $journey->{sched_arrival} ) {
$delay = sprintf(
@@ -1724,6 +1811,7 @@ sub journey_details {
) / 60
);
}
+ $with_share = 1;
$share_text
= $journey->{km_route}
? sprintf( '%.0f km', $journey->{km_route} )
@@ -1733,13 +1821,15 @@ sub journey_details {
$delay, $journey->{rt_arrival}->strftime('%H:%M') );
}
+ # TODO add token if visibility != public
$self->render(
'journey',
- error => undef,
- journey => $journey,
- with_map => 1,
- with_share => $with_share,
- share_text => $share_text,
+ error => undef,
+ journey => $journey,
+ journey_visibility => $visibility,
+ with_map => 1,
+ with_share => $with_share,
+ share_text => $share_text,
%{$map_data},
);
}
@@ -1754,6 +1844,112 @@ sub journey_details {
}
+sub visibility_form {
+ my ($self) = @_;
+ my $dep_ts = $self->param('dep_ts');
+ my $journey_id = $self->param('id');
+ my $action = $self->param('action') // 'none';
+ my $user = $self->current_user;
+ my $user_level = $user->{default_visibility_str};
+ my $uid = $user->{id};
+ my $status = $self->get_user_status;
+ my $visibility = $status->{visibility};
+ my $journey;
+
+ if ($journey_id) {
+ $journey = $self->journeys->get_single(
+ uid => $uid,
+ journey_id => $journey_id,
+ with_datetime => 1,
+ with_visibility => 1,
+ );
+ $visibility = $journey->{visibility};
+ }
+
+ if ( $action eq 'save' ) {
+ if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
+ $self->render(
+ 'edit_visibility',
+ error => 'csrf',
+ user_level => $user_level,
+ journey => {}
+ );
+ }
+ elsif ( $dep_ts and $dep_ts != $status->{sched_departure}->epoch ) {
+
+ # TODO find and update appropriate past journey (if it exists)
+ $self->render(
+ 'edit_visibility',
+ error => 'old',
+ user_level => $user_level,
+ journey => {}
+ );
+ }
+ else {
+ $self->app->log->debug("set visibility");
+ if ($dep_ts) {
+ $self->in_transit->update_visibility(
+ uid => $uid,
+ visibility => $self->param('status_level'),
+ );
+ $self->redirect_to('/');
+ }
+ elsif ($journey_id) {
+ $self->journeys->update_visibility(
+ uid => $uid,
+ id => $journey_id,
+ visibility => $self->param('status_level'),
+ );
+ $self->redirect_to( '/journey/' . $journey_id );
+ }
+ }
+ return;
+ }
+
+ # todo use visibility_str
+ if ( not defined $visibility ) {
+ $self->param( status_level => 'default' );
+ }
+ elsif ( $visibility == 100 ) {
+ $self->param( status_level => 'public' );
+ }
+ elsif ( $visibility == 80 ) {
+ $self->param( status_level => 'travelynx' );
+ }
+ elsif ( $visibility == 30 ) {
+ $self->param( status_level => 'unlisted' );
+ }
+ elsif ( $visibility == 10 ) {
+ $self->param( status_level => 'private' );
+ }
+
+ if ($journey_id) {
+ $self->render(
+ 'edit_visibility',
+ error => undef,
+ user_level => $user_level,
+ journey => $journey
+ );
+ }
+ elsif ( $status->{checked_in} ) {
+ $self->param( dep_ts => $status->{sched_departure}->epoch );
+ $self->render(
+ 'edit_visibility',
+ error => undef,
+ user_level => $user_level,
+ journey => $status
+ );
+ }
+ else {
+ $self->render(
+ 'edit_visibility',
+ error => 'notfound',
+ user_level => $user_level,
+ journey => {}
+ );
+ }
+}
+
sub comment_form {
my ($self) = @_;
my $dep_ts = $self->param('dep_ts');
diff --git a/lib/Travelynx/Model/InTransit.pm b/lib/Travelynx/Model/InTransit.pm
index d3b6d6b..78fd297 100644
--- a/lib/Travelynx/Model/InTransit.pm
+++ b/lib/Travelynx/Model/InTransit.pm
@@ -11,6 +11,22 @@ use 5.020;
use DateTime;
use JSON;
+my %visibility_itoa = (
+ 100 => 'public',
+ 80 => 'travelynx',
+ 60 => 'followers',
+ 30 => 'unlisted',
+ 10 => 'private',
+);
+
+my %visibility_atoi = (
+ public => 100,
+ travelynx => 80,
+ followers => 60,
+ unlisted => 30,
+ private => 10,
+);
+
sub new {
my ( $class, %opt ) = @_;
@@ -117,11 +133,23 @@ sub get {
}
my $res = $db->select( $table, '*', { user_id => $uid } );
+ my $ret;
if ( $opt{with_data} ) {
- return $res->expand->hash;
+ $ret = $res->expand->hash;
+ }
+ else {
+ $ret = $res->hash;
}
- return $res->hash;
+
+ if ( $opt{with_visibility} and $ret ) {
+ $ret->{visibility_str}
+ = $ret->{visibility}
+ ? $visibility_itoa{ $ret->{visibility} }
+ : 'default';
+ }
+
+ return $ret;
}
sub get_all_active {
@@ -449,4 +477,23 @@ sub update_user_data {
);
}
+sub update_visibility {
+ my ( $self, %opt ) = @_;
+
+ my $uid = $opt{uid};
+ my $db = $opt{db} // $self->{pg}->db;
+
+ my $visibility;
+
+ if ( $opt{visibility} and $visibility_atoi{ $opt{visibility} } ) {
+ $visibility = $visibility_atoi{ $opt{visibility} };
+ }
+
+ $db->update(
+ 'in_transit',
+ { visibility => $visibility },
+ { user_id => $uid }
+ );
+}
+
1;
diff --git a/lib/Travelynx/Model/Journeys.pm b/lib/Travelynx/Model/Journeys.pm
index 08d9ff0..a9c6200 100755
--- a/lib/Travelynx/Model/Journeys.pm
+++ b/lib/Travelynx/Model/Journeys.pm
@@ -15,6 +15,22 @@ use utf8;
use DateTime;
use JSON;
+my %visibility_itoa = (
+ 100 => 'public',
+ 80 => 'travelynx',
+ 60 => 'followers',
+ 30 => 'unlisted',
+ 10 => 'private',
+);
+
+my %visibility_atoi = (
+ public => 100,
+ travelynx => 80,
+ followers => 60,
+ unlisted => 30,
+ private => 10,
+);
+
my @month_name
= (
qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember)
@@ -509,7 +525,7 @@ sub get {
my @select
= (
- qw(journey_id train_type train_line train_no checkin_ts sched_dep_ts real_dep_ts dep_eva dep_ds100 dep_name dep_lat dep_lon checkout_ts sched_arr_ts real_arr_ts arr_eva arr_ds100 arr_name arr_lat arr_lon cancelled edited route messages user_data)
+ qw(journey_id train_type train_line train_no checkin_ts sched_dep_ts real_dep_ts dep_eva dep_ds100 dep_name dep_lat dep_lon checkout_ts sched_arr_ts real_arr_ts arr_eva arr_ds100 arr_name arr_lat arr_lon cancelled edited route messages user_data visibility)
);
my %where = (
user_id => $uid,
@@ -548,6 +564,24 @@ sub get {
push( @select, 'polyline' );
}
+ if ( $opt{min_visibility} ) {
+ if ( $visibility_atoi{ $opt{min_visibility} } ) {
+ $opt{min_visibility} = $visibility_atoi{ $opt{min_visibility} };
+ }
+ if ( $opt{with_default_visibility} ) {
+ $where{visibility} = [
+ -or => { '=', undef },
+ { '>=', $opt{min_visibility} }
+ ];
+ }
+ else {
+ $where{visibility} = [
+ -and => { '!=', undef },
+ { '>=', $opt{min_visibility} }
+ ];
+ }
+ }
+
my @travels;
my $res = $db->select( 'journeys_str', \@select, \%where, \%order );
@@ -577,8 +611,16 @@ sub get {
route => $entry->{route},
edited => $entry->{edited},
user_data => $entry->{user_data},
+ visibility => $entry->{visibility},
};
+ if ( $opt{with_visibility} ) {
+ $ref->{visibility_str}
+ = $ref->{visibility}
+ ? $visibility_itoa{ $ref->{visibility} }
+ : 'default';
+ }
+
if ( $opt{with_polyline} ) {
$ref->{polyline} = $entry->{polyline};
}
@@ -1703,4 +1745,26 @@ sub get_connection_targets {
return @destinations;
}
+sub update_visibility {
+ my ( $self, %opt ) = @_;
+
+ my $uid = $opt{uid};
+ my $db = $opt{db} // $self->{pg}->db;
+
+ my $visibility;
+
+ if ( $opt{visibility} and $visibility_atoi{ $opt{visibility} } ) {
+ $visibility = $visibility_atoi{ $opt{visibility} };
+ }
+
+ $db->update(
+ 'journeys',
+ { visibility => $visibility },
+ {
+ user_id => $uid,
+ id => $opt{id}
+ }
+ );
+}
+
1;
diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm
index 8f5ce1f..63cc261 100644
--- a/lib/Travelynx/Model/Users.pm
+++ b/lib/Travelynx/Model/Users.pm
@@ -11,6 +11,22 @@ use 5.020;
use DateTime;
use JSON;
+my %visibility_itoa = (
+ 100 => 'public',
+ 80 => 'travelynx',
+ 60 => 'followers',
+ 30 => 'unlisted',
+ 10 => 'private',
+);
+
+my %visibility_atoi = (
+ public => 100,
+ travelynx => 80,
+ followers => 60,
+ unlisted => 30,
+ private => 10,
+);
+
my @sb_templates = (
undef,
[ 'DBF', 'https://dbf.finalrewind.org/{name}?rt=1#{tt}{tn}' ],
@@ -153,7 +169,16 @@ sub get_privacy_by_name {
);
if ( my $user = $res->hash ) {
- return $user;
+ return {
+ id => $user->{id},
+ public_level => $user->{public_level}, # todo remove?
+ default_visibility => $user->{public_level} & 0x7f,
+ default_visibility_str =>
+ $visibility_itoa{ $user->{public_level} & 0x7f },
+ comments_visible => $user->{public_level} & 0x80 ? 1 : 0,
+ past_visible => ( $user->{public_level} & 0x300 ) >> 8,
+ past_all => $user->{public_level} & 0x400 ? 1 : 0,
+ };
}
return;
}
@@ -164,6 +189,14 @@ sub set_privacy {
my $uid = $opt{uid};
my $public_level = $opt{level};
+ if ( not defined $public_level and defined $opt{default_visibility} ) {
+ $public_level
+ = ( $opt{default_visibility} & 0x7f )
+ | ( $opt{comments_visible} ? 0x80 : 0x00 )
+ | ( ( ( $opt{past_visible} // 0 ) << 8 ) & 0x300 )
+ | ( $opt{past_all} ? 0x400 : 0 );
+ }
+
$db->update( 'users', { public_level => $public_level }, { id => $uid } );
}
@@ -333,12 +366,18 @@ sub get {
)->hash;
if ($user) {
return {
- id => $user->{id},
- name => $user->{name},
- status => $user->{status},
- is_public => $user->{public_level},
- email => $user->{email},
- sb_name => $user->{external_services}
+ id => $user->{id},
+ name => $user->{name},
+ status => $user->{status},
+ is_public => $user->{public_level},
+ default_visibility => $user->{public_level} & 0x7f,
+ default_visibility_str =>
+ $visibility_itoa{ $user->{public_level} & 0x7f },
+ comments_visible => $user->{public_level} & 0x80 ? 1 : 0,
+ past_visible => ( $user->{public_level} & 0x300 ) >> 8,
+ past_all => $user->{public_level} & 0x400 ? 1 : 0,
+ email => $user->{email},
+ sb_name => $user->{external_services}
? $sb_templates[ $user->{external_services} & 0x07 ][0]
: undef,
sb_template => $user->{external_services}
diff --git a/public/static/js/travelynx-actions.js b/public/static/js/travelynx-actions.js
index 054b600..7e95a43 100644
--- a/public/static/js/travelynx-actions.js
+++ b/public/static/js/travelynx-actions.js
@@ -8,8 +8,13 @@ var j_duration = 0;
var j_arrival = 0;
var j_dest = '';
var j_stops = [];
+var j_token = '';
function upd_journey_data() {
$('.countdown').each(function() {
+ const journey_token = $(this).data('token');
+ if (journey_token) {
+ j_token = journey_token;
+ }
var journey_data = $(this).data('journey');
if (journey_data) {
journey_data = journey_data.split(';');
@@ -105,7 +110,7 @@ function tvly_update_public() {
$('.publicstatuscol').each(function() {
user_name = $(this).data('user');
});
- $.get('/ajax/status/' + user_name + '.html', function(data) {
+ $.get('/ajax/status/' + user_name + '.html', {token: j_token}, function(data) {
$('.publicstatuscol').html(data);
upd_journey_data();
setTimeout(tvly_update_public, 40000);
diff --git a/templates/_checked_in.html.ep b/templates/_checked_in.html.ep
index f4acc9f..1775986 100644
--- a/templates/_checked_in.html.ep
+++ b/templates/_checked_in.html.ep
@@ -244,13 +244,16 @@
</div>
<div class="card-action">
% if ($journey->{arr_name}) {
- <a style="margin-right: 0;" href="/journey/comment">
- <i class="material-icons left" aria-hidden="true">comment</i> Kommentar
+ <a href="/journey/comment">
+ <i class="material-icons">comment</i>
+ </a>
+ <a style="margin-right: 0;" href="/journey/visibility">
+ <i class="material-icons"><%= visibility_icon($journey_visibility) %></i>
</a>
% }
% else {
<a class="action-undo blue-text" data-id="in_transit" data-checkints="<%= $journey->{timestamp}->epoch %>" style="margin-right: 0;">
- <i class="material-icons left" aria-hidden="true">undo</i> Checkin Rückgängig
+ <i class="material-icons left" aria-hidden="true">undo</i> Rückgängig
</a>
% }
% if (defined $journey->{arrival_countdown} and $journey->{arrival_countdown} <= 0) {
@@ -259,7 +262,7 @@
style="margin-right: 0;"
data-station="<%= $journey->{arr_name}%>">
<i class="material-icons left">done</i>
- Jetzt auschecken
+ Auschecken
</a>
% }
% elsif ($journey->{arr_name}) {
@@ -270,7 +273,7 @@
<a class="action-share blue-text right"
style="margin-right: 0;"
% my $arr_text = q{};
- % if ($journey->{real_arrival}->epoch and not $user->{is_public} & 0x02) {
+ % if ($journey->{real_arrival}->epoch and $journey_visibility eq 'private') {
% $arr_text = $journey->{real_arrival}->strftime(' – Ankunft gegen %H:%M Uhr');
% }
% if ($user->{is_public} & 0x04 and $journey->{comment}) {
@@ -279,13 +282,21 @@
% else {
data-text="Ich bin gerade <%= $attrib %> <%= $journey->{train_type} %> <%= $journey->{train_no} %> nach <%= $journey->{arr_name} . $arr_text %> #travelynx"
% }
- % if ($user->{is_public} & 0x02) {
+ % if ($journey_visibility eq 'public') {
data-url="<%= url_for('/status')->to_abs->scheme('https') %>/<%= $user->{name} %>/<%= $journey->{sched_departure}->epoch %>"
% }
+ % elsif ($journey_visibility eq 'travelynx' or $journey_visibility eq 'unlisted') {
+ data-url="<%= url_for('/status')->to_abs->scheme('https') %>/<%= $user->{name} %>/<%= $journey->{sched_departure}->epoch %>?token=<%= $journey->{dep_eva} %>-<%= $journey->{timestamp}->epoch %>"
+ % }
>
<i class="material-icons left" aria-hidden="true">share</i> Teilen
</a>
% }
+ % else {
+ <a class="right" href="/journey/visibility">
+ <i class="material-icons left"><%= visibility_icon($journey_visibility) %></i> Sichtbarkeit
+ </a>
+ % }
</div>
</div>
% if ($journey->{arr_name}) {
diff --git a/templates/_public_status_card.html.ep b/templates/_public_status_card.html.ep
index 43b9926..cb0fb0d 100644
--- a/templates/_public_status_card.html.ep
+++ b/templates/_public_status_card.html.ep
@@ -3,7 +3,11 @@
<div class="card">
<div class="card-content">
<i class="material-icons small right sync-failed-marker grey-text" style="display: none;">sync_problem</i>
- <span class="card-title"><%= $name %> ist unterwegs</span>
+ <span class="card-title"><%= $name %> ist unterwegs
+ % if ($journey_visibility) {
+ <i class="material-icons right"><%= visibility_icon($journey_visibility) %></i>
+ % }
+ </span>
% if ($public_level & 0x04 and $journey->{comment}) {
<p>„<%= $journey->{comment} %>“</p>
% }
@@ -16,6 +20,9 @@
% }
<div class="center-align countdown"
data-duration="<%= $journey->{journey_duration} // 0 %>"
+ % if (param('token')) {
+ data-token="<%= $journey->{dep_eva} %>-<%= $journey->{timestamp}->epoch %>-<%= $journey->{sched_departure}->epoch %>"
+ % }
data-arrival="<%= $journey->{real_arrival}->epoch %>">
% if ($journey->{departure_countdown} > 120) {
Abfahrt in <%= sprintf('%.f', $journey->{departure_countdown} / 60) %> Minuten
@@ -166,20 +173,6 @@
<i class="material-icons small right sync-failed-marker grey-text" style="display: none;">sync_problem</i>
<span class="card-title"><%= $name %> ist gerade nicht eingecheckt</span>
<p>
- % if ($journey->{arr_name}) {
- Zuletzt gesehen
- % if ($journey->{real_arrival}->epoch and ($public_level & 0x20 or ($public_level & 0x10 and is_user_authenticated()))) {
- %= $journey->{real_arrival}->strftime('am %d.%m.%Y')
- in <b><%= $journey->{arr_name} %></b>
- %= $journey->{real_arrival}->strftime('(Ankunft um %H:%M Uhr)')
- % }
- % else {
- in <b><%= $journey->{arr_name} %></b>
- % }
- % }
- % else {
- Noch keine Zugfahrten geloggt.
- % }
</p>
</div>
</div>
diff --git a/templates/account.html.ep b/templates/account.html.ep
index 9c5d88e..e5dba60 100644
--- a/templates/account.html.ep
+++ b/templates/account.html.ep
@@ -70,36 +70,10 @@
</td>
</tr>
<tr>
- <th scope="row">Öffentliche Daten</th>
+ <th scope="row">Sichtbarkeit</th>
<td>
<a href="/account/privacy"><i class="material-icons">edit</i></a>
- % if ($acc->{is_public} == 0) {
- <span style="color: #999999;">Keine</span>
- % }
- % if ($acc->{is_public} & 0x01) {
- Aktueller Status (nur mit Anmeldung)
- % }
- % elsif ($acc->{is_public} & 0x02) {
- Aktueller Status
- % }
- % if ($acc->{is_public} & 0x0f and $acc->{is_public} & 0xf0) {
- <br/>
- % }
- % if ($acc->{is_public} & 0x30) {
- % if ($acc->{is_public} & 0x40) {
- Vergangene Fahrten
- % }
- % else {
- Fahrten der letzten vier Wochen
- % }
- % if ($acc->{is_public} & 0x10) {
- (nur mit Anmeldung)
- % }
- % }
- % if ($acc->{is_public} & 0x04) {
- <br/>
- Kommentare
- % }
+ <span><i class="material-icons"><%= visibility_icon($acc->{default_visibility_str}) %></i></span>
</td>
</tr>
<tr>
diff --git a/templates/journey.html.ep b/templates/journey.html.ep
index 50dcb3e..74c032b 100644
--- a/templates/journey.html.ep
+++ b/templates/journey.html.ep
@@ -38,6 +38,16 @@
% }
am
<b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b>
+ % if ($journey_visibility) {
+ % if (stash('username')) {
+ <i class="material-icons right"><%= visibility_icon($journey_visibility) %></i>
+ % }
+ % else {
+ <a class="right" href="/journey/visibility?id=<%= $journey->{id} %>">
+ <i class="material-icons"><%= visibility_icon($journey_visibility) %></i>
+ </a>
+ % }
+ % }
</p>
% if ($journey->{edited}) {
<p>
diff --git a/templates/landingpage.html.ep b/templates/landingpage.html.ep
index a4bf10c..205e35c 100644
--- a/templates/landingpage.html.ep
+++ b/templates/landingpage.html.ep
@@ -15,7 +15,7 @@
<div class="row">
<div class="col s12 statuscol">
% if ($status->{checked_in}) {
- %= include '_checked_in', journey => $status;
+ %= include '_checked_in', journey => $status, journey_visibility => stash('journey_visibility');
% }
% elsif ($status->{cancelled}) {
<div class="card info-color">
diff --git a/templates/privacy.html.ep b/templates/privacy.html.ep
index b5f3bb3..efeb6e1 100644
--- a/templates/privacy.html.ep
+++ b/templates/privacy.html.ep
@@ -1,10 +1,11 @@
<h1>Öffentliche Daten</h1>
<div class="row">
<div class="col s12">
- Hier kannst du auswählen, welche Aspekte deines Accounts bzw. deiner
- Bahnfahrten öffentlich einsehbar sind. Öffentliche Daten sind
- grundsätzlich für <i>alle</i> einsehbar, die die (leicht erratbare) URL
- kennen.
+ Hier kannst du auswählen, welche Personengruppen deine Fahrten bei
+ travelynx einsehen können und ob dies auch für vergangene Fahrten
+ gelten soll. Nach dem Einchecken in einen Zug hast du im
+ Checkin-Fenster die Möglichkeit, für die aktuelle Fahrt eine
+ abweichende Sichtbarkeit einzustellen.
</div>
</div>
%= form_for '/account/privacy' => (method => 'POST') => begin
@@ -13,33 +14,62 @@
<div class="row">
<div class="input-field col s12">
<div>
- <label>
- %= radio_button status_level => 'private'
- <span>Nicht sichtbar</span>
- </label>
- </div><div>
- <label>
- %= radio_button status_level => 'intern'
- <span>Nur mit Anmeldung</span>
- </label>
- </div><div>
- <label>
- %= radio_button status_level => 'extern'
- <span>Öffentlich</span>
- </label>
+ <label>
+ %= radio_button status_level => 'public'
+ <span><i class="material-icons left"><%= visibility_icon('public') %></i>Öffentlich: Im Profil verlinkt und beliebig zugänglich.</span>
+ </label>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="input-field col s12">
+ <div>
+ <label>
+ %= radio_button status_level => 'travelynx'
+ <span><i class="material-icons left"><%= visibility_icon('travelynx') %></i>Lokal: Nur für<!-- Personen, die dir folgen oder auf dieser Seite angemeldet sind --> auf dieser Seite angemeldete Acounts sowie nicht angemeldete Personen, denen du mithilfe der Teilen-Funktion einen Link schickst.</span>
+ </label>
+ </div>
+ </div>
+ </div>
+ <!--
+ <div class="row">
+ <div class="input-field col s12">
+ <div>
+ <label>
+ %= radio_button status_level => 'fedi'
+ <span><i class="material-icons left"><%= visibility_icon('fedi') %></i>Fedi: nur für Personen, die deinem Account folgen.</span>
+ </label>
+ </div>
+ </div>
+ </div>
+ -->
+ <div class="row">
+ <div class="input-field col s12">
+ <div>
+ <label>
+ %= radio_button status_level => 'unlisted'
+ <span><i class="material-icons left"><%= visibility_icon('unlisted') %></i>Verlinkbar: Nur für Personen zugänglich, denen du mithilfe der Teilen-Funktion einen Link schickst.</span>
+ </label>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="input-field col s12">
+ <div>
+ <label>
+ %= radio_button status_level => 'private'
+ <span><i class="material-icons left"><%= visibility_icon('private') %></i>Privat: nur für dich sichtbar.</span>
+ </label>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
- Hier kannst du auswählen, ob dein aktueller Status unter <a
- href="/status/<%= $name %>">/status/<%= $name %></a> sowie <a
- href="/p/<%= $name %>">/p/<%= $name %></a> abrufbar ist.
- Wenn du eingecheckt bist, werden dort Zug, Start- und Zielstation
- sowie Abfahrts- und Ankunftszeit gezeigt, andernfalls lediglich der
- Zielbahnhof der letzten Reise. Wann die letzte Reise beendet wurde,
- wird nur angegeben, wenn deine vergangenen Zugfahrten sichtbar sind
- (siehe unten).
+ Wenn du (mit passender Sichtbarkeit) eingecheckt bist, werden unter
+ <a href="/status/<%= $name %>">/status/<%= $name %></a> sowie <a
+ href="/p/<%= $name %>">/p/<%= $name %></a> Zug, Start- und
+ Zielstation sowie Abfahrts- und Ankunftszeit gezeigt. Andernfalls
+ wird angegeben, dass du gerade nicht eingecheckt seist.
</div>
</div>
<h2>Vergangene Zugfahrten</h2>
@@ -85,7 +115,10 @@
dort nicht eingetragene Fahrten sind jedoch weiterhin über /p/<%=
$name %>/j/ID zugänglich. Da die ID (mit Lücken) aufsteigend vergeben
wird, sind effektiv alle deiner vergangenen Fahrten (oder alle Fahrten
- der letzten vier Wochen) öffentlich.
+ der letzten vier Wochen) öffentlich. Auch hier besteht die
+ Möglichkeit, für einzelne Fahrten eine abweichende Sichtbarkeit
+ einzustellen. Sofern die Sichtbarkeit auf die letzten vier Wochen
+ beschränkt ist, hat dies jedoch Vorrang.
</div>
</div>
<h2>Sonstiges</h2>
@@ -101,6 +134,7 @@
<div class="col s12">
Wenn aktiv, sind von dir eingetragene Freitext-Kommentare in deinem
aktuellen Status sowie bei deinen vergangenen Zugfahrten sichtbar.
+ Diese Einstellung kann nicht pro Fahrt verändert werden.
</div>
</div>
<div class="row">
diff --git a/templates/profile.html.ep b/templates/profile.html.ep
index 6a8d67d..63bc3ff 100644
--- a/templates/profile.html.ep
+++ b/templates/profile.html.ep
@@ -10,14 +10,12 @@
</div>
</div>
% }
-% if ($public_level & 0x02 or ($public_level & 0x01 and is_user_authenticated())) {
- <div class="row">
- <div class="col s12 publicstatuscol" data-user="<%= $name %>">
- %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey
- </div>
+<div class="row">
+ <div class="col s12 publicstatuscol" data-user="<%= $name %>">
+ %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey, journey_visibility => $journey_visibility
</div>
-% }
-% if ($public_level & 0x20 or ($public_level & 0x10 and is_user_authenticated())) {
+</div>
+% if ($journeys and @{$journeys}) {
<div class="row">
<div class="col s12">
<h2>Letzte Fahrten von <%= $name %></h1>
diff --git a/templates/user_status.html.ep b/templates/user_status.html.ep
index 78ef547..783f430 100644
--- a/templates/user_status.html.ep
+++ b/templates/user_status.html.ep
@@ -1,5 +1,5 @@
<div class="row">
<div class="col s12 publicstatuscol" data-user="<%= $name %>">
- %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey
+ %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey, journey_visibility => $journey_visibility
</div>
</div>