Prepare settings and templates for opt-in public travel status

This commit is contained in:
Daniel Friesel 2019-04-30 23:23:49 +02:00
parent ddea9abc6e
commit f0d61a4083
7 changed files with 243 additions and 0 deletions

View file

@ -774,6 +774,38 @@ sub startup {
}
);
$self->helper(
'get_privacy_by_name' => sub {
my ( $self, $name ) = @_;
my $res = $self->pg->db->select(
'users',
[ 'id', 'public_level' ],
{
name => $name,
status => 1
}
);
if ( my $user = $res->hash ) {
return $user;
}
return;
}
);
$self->helper(
'set_privacy' => sub {
my ( $self, $uid, $public_level ) = @_;
$self->pg->db->update(
'users',
{ public_level => $public_level },
{ id => $uid }
);
}
);
$self->helper(
'mark_for_password_reset' => sub {
my ( $self, $db, $uid, $token ) = @_;
@ -1696,6 +1728,7 @@ sub startup {
$r->get('/recover/:id/:token')->to('account#recover_password');
$r->get('/register')->to('account#registration_form');
$r->get('/reg/:id/:token')->to('account#verify');
$r->get('/status/:name')->to('traveling#user_status');
$r->post('/action')->to('traveling#log_action');
$r->post('/geolocation')->to('traveling#geolocation');
$r->post('/list_departures')->to('traveling#redirect_to_station');
@ -1715,6 +1748,7 @@ sub startup {
);
$authed_r->get('/account')->to('account#account');
$authed_r->get('/account/privacy')->to('account#privacy');
$authed_r->get('/ajax/status_card.html')->to('traveling#status_card');
$authed_r->get('/cancelled')->to('traveling#cancelled');
$authed_r->get('/change_password')->to('account#password_form');
@ -1728,6 +1762,7 @@ sub startup {
$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');
$authed_r->post('/account/privacy')->to('account#privacy');
$authed_r->post('/journey/add')->to('traveling#add_journey_form');
$authed_r->post('/journey/edit')->to('traveling#edit_journey');
$authed_r->post('/change_password')->to('account#change_password');

View file

@ -208,6 +208,28 @@ sub do_logout {
$self->redirect_to('/login');
}
sub privacy {
my ($self) = @_;
my $user = $self->current_user;
my $public_level = $user->{is_public};
if ( $self->param('action') and $self->param('action') eq 'save' ) {
if ( $self->param('public_status') ) {
$public_level |= 0x02;
}
else {
$public_level &= ~0x02;
}
$self->set_privacy( $user->{id}, $public_level );
}
else {
$self->param( public_status => $public_level & 0x02 ? 1 : 0 );
}
$self->render( 'privacy', name => $user->{name} );
}
sub change_mail {
my ($self) = @_;

View file

@ -25,6 +25,44 @@ sub homepage {
}
}
sub user_status {
my ($self) = @_;
my $name = $self->stash('name');
my $user = $self->get_privacy_by_name($name);
if ( $user and ( $user->{public_level} & 0x02 ) ) {
my $status = $self->get_user_status( $user->{id} );
$self->render(
'user_status',
name => $name,
journey => $status
);
}
else {
$self->render('not_found');
}
}
sub public_status_card {
my ($self) = @_;
my $name = $self->stash('name');
my $user = $self->get_privacy_by_name($name);
if ( $user and ( $user->{public_level} & 0x02 ) ) {
my $status = $self->get_user_status( $user->{id} );
$self->render(
'_public_status_card',
name => $name,
journey => $status
);
}
else {
$self->render('not_found');
}
}
sub status_card {
my ($self) = @_;
my $status = $self->get_user_status;

View file

@ -0,0 +1,87 @@
% if ($journey->{checked_in}) {
<div class="card green darken-4">
<div class="card-content white-text">
<span class="card-title"><%= $name %> ist unterwegs</span>
<p>
In <b><%= $journey->{train_type} %> <%= $journey->{train_no} %></b>
% if ($journey->{arr_name}) {
von <b><%= $journey->{dep_name} %></b> nach <b><%= $journey->{arr_name} %></b>.
% }
% else {
ab <b><%= $journey->{dep_name} %></b>.
% }
</p>
<p>
<b><%= $journey->{real_departure}->strftime('%H:%M') %></b>
% if ($journey->{real_departure}->epoch != $journey->{sched_departure}->epoch) {
(<%= sprintf('%+d', ($journey->{real_departure}->epoch - $journey->{sched_departure}->epoch)/60) %>)
% }
% if ($journey->{real_arrival}->epoch) {
<b><%= $journey->{real_arrival}->strftime('%H:%M') %></b>
% if ($journey->{real_arrival}->epoch != $journey->{sched_arrival}->epoch) {
(<%= sprintf('%+d', ($journey->{real_arrival}->epoch - $journey->{sched_arrival}->epoch)/60) %>)
% }
% }
% elsif ($journey->{arr_name}) {
noch nicht bekannt
% }
% else {
unbekannt
% }
</p>
<p>
<div class="center">
% if ($journey->{departure_countdown} > 120) {
Abfahrt in <%= sprintf('%.f', $journey->{departure_countdown} / 60) %> Minuten
% }
% elsif ($journey->{departure_countdown} > 60) {
Abfahrt in einer Minute
% }
% elsif ($journey->{departure_countdown} > 0) {
Abfahrt in weniger als einer Minute
% }
% elsif (defined $journey->{arrival_countdown}) {
% if ($journey->{arrival_countdown} > 60) {
Ankunft in <%= sprintf('%.f', $journey->{arrival_countdown} / 60) %>
Minute<%= sprintf('%.f', $journey->{arrival_countdown} / 60) == 1 ? '' : 'n' %>
% }
% elsif ($journey->{arrival_countdown} > 0) {
Ankunft in weniger als einer Minute
% }
% else {
Ziel erreicht
% }
% }
% elsif ($journey->{arr_name}) {
Ankunft in mehr als zwei Stunden
% }
</div>
<div class="progress green darken-3" style="height: 1ex;">
<div class="determinate white" style="width: <%= sprintf('%.2f', 100 * ($journey->{journey_completion} // 0)); %>%;"></div>
</div>
</p>
% if (@{$journey->{messages} // []} > 0 and $journey->{messages}[0]) {
<p style="margin-bottom: 2ex;">
<ul>
% for my $message (reverse @{$journey->{messages} // []}) {
% if ($journey->{sched_departure}->epoch - $message->[0]->epoch < 1800) {
<li> <i class="material-icons tiny">warning</i> <%= $message->[0]->strftime('%H:%M') %>: <%= $message->[1] %></li>
% }
% }
</ul>
</p>
% }
</div>
</div>
% }
% else {
<div class="card grey darken-4">
<div class="card-content white-text">
<span class="card-title"><%= $name %> ist gerade nicht eingecheckt</span>
<p>
Zuletzt gesehen in <%= $journey->{arr_name} %>.
</p>
</div>
</div>
% }

View file

@ -36,6 +36,20 @@
<th scope="row">Passwort</th>
<td><a href="/change_password"><i class="material-icons">edit</i> ändern</a></td>
</tr>
<tr>
<th scope="row">Privatsphäre</th>
<td>
% if ($acc->{is_public} == 0) {
Keine öffentlichen Daten
% }
% else {
Öffentliche Daten:
% }
% if ($acc->{is_public} & 0x02) {
Status
% }
<a href="/account/privacy"><i class="material-icons">edit</i> ändern</a></td>
</tr>
<tr>
<th scope="row">Registriert am</th>
<td><%= $acc->{registered_at}->strftime('%d.%m.%Y %H:%M') %></td>

42
templates/privacy.html.ep Normal file
View file

@ -0,0 +1,42 @@
<h1>Privatsphäre</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.
</div>
</div>
<h2>Öffentliche Daten:</h2>
%= form_for '/account/privacy' => (method => 'POST') => begin
%= csrf_field
<div class="row">
<div class="input-field col s12">
<label>
%= check_box public_status => 1
<span class="black-text">Aktueller Status</span>
</label>
</div>
</div>
<div class="row">
<div class="col s12">
Wenn aktiv, ist dein aktueller Status unter <a href="/status/<%= $name
%>">/status/<%= $name %></a> abrufbar. Wenn du eingecheckt bist,
werden dort Zug, Start- und Zielstation, Abfahrts- und Ankunftszeit
gezeigt; andernfalls lediglich der Zielbahnhof der letzten Reise.
Wann die letzte Reise beendet wurde, wird bewusst nicht angegeben.
</div>
</div>
<div class="row">
<div class="col s3 m3 l3">
</div>
<div class="col s6 m6 l6 center-align">
<button class="btn waves-effect waves-light" type="submit" name="action" value="save">
Speichern
<i class="material-icons right">send</i>
</button>
</div>
<div class="col s3 m3 l3">
</div>
</div>
%= end

View file

@ -0,0 +1,5 @@
<div class="row">
<div class="col s12">
%= include '_public_status_card', name => $name, journey => $journey
</div>
</div>