From e2598df25ba089bbdef12777048ba68b7b119926 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Thu, 4 Apr 2019 18:26:53 +0200 Subject: Allow journey deletion See issue #3 --- lib/Travelynx.pm | 61 ++++++++++++++++++++++++++++++- lib/Travelynx/Controller/Traveling.pm | 33 ++++++++++++++--- public/static/js/travelynx-actions.js | 13 +++++++ public/static/js/travelynx-actions.min.js | 2 +- templates/journey.html.ep | 15 ++++++++ 5 files changed, 117 insertions(+), 7 deletions(-) diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 6bdc133..25d45d3 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -347,6 +347,19 @@ sub startup { ); } ); + $self->attr( + drop_journey_query => sub { + my ($self) = @_; + + return $self->app->dbh->prepare( + qq{ + delete from user_actions + where user_id = ? + and (id = ? or id = ?) + } + ); + } + ); $self->attr( get_userid_query => sub { my ($self) = @_; @@ -493,7 +506,9 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} }, ); - $self->helper(sendmail => sub { state $sendmail = Travelynx::Helper::Sendmail->new; }); + $self->helper( + sendmail => sub { state $sendmail = Travelynx::Helper::Sendmail->new; } + ); $self->helper( 'get_departures' => sub { @@ -891,6 +906,50 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); + $self->helper( + 'delete_journey' => sub { + my ( $self, $checkin_id, $checkout_id, $checkin_epoch, + $checkout_epoch ) + = @_; + my $uid = $self->current_user->{id}; + + my @journeys = $self->get_user_travels( + uid => $uid, + checkout_id => $checkout_id + ); + if ( @journeys == 0 ) { + return 'Journey not found'; + } + my $journey = $journeys[0]; + + # Double-check (comparing both ID and action epoch) to make sure we + # are really deleting the right journey and the user isn't just + # playing around with POST requests. + if ( $journey->{ids}[0] != $checkin_id + or $journey->{ids}[1] != $checkout_id + or $journey->{checkin}->epoch != $checkin_epoch + or $journey->{checkout}->epoch != $checkout_epoch ) + { + return 'Invalid journey data'; + } + my $query = $self->app->drop_journey_query; + my $success = $query->execute( $uid, $checkin_id, $checkout_id ); + if ($success) { + if ( $query->rows == 2 ) { + return undef; + } + else { + return + sprintf( 'Deleted %d rows, expected 2', $query->rows ); + } + } + my $err = $self->app->drop_journey_query->errstr; + $self->app->log->error( + "Delete($uid, $checkin_id, $checkout_id): DELETE failed: $err"); + return 'DELETE failed: ' . $err; + } + ); + $self->helper( 'get_user_travels' => sub { my ( $self, %opt ) = @_; diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index bf61741..a83adb0 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -175,6 +175,26 @@ sub log_action { ); } } + elsif ( $params->{action} eq 'delete' ) { + my ( $from, $to ) = split( qr{,}, $params->{ids} ); + my $error = $self->delete_journey( $from, $to, $params->{checkin}, + $params->{checkout} ); + if ($error) { + $self->render( + json => { + success => 0, + error => $error, + }, + ); + } + else { + $self->render( + json => { + success => 1, + }, + ); + } + } else { $self->render( json => { @@ -312,7 +332,7 @@ sub journey_details { my ($self) = @_; my ( $uid, $checkout_id ) = split( qr{-}, $self->stash('id') ); - if ( not ($uid == $self->current_user->{id} and $checkout_id)) { + if ( not( $uid == $self->current_user->{id} and $checkout_id ) ) { $self->render( 'journey', error => 'notfound', @@ -322,11 +342,14 @@ sub journey_details { } my @journeys = $self->get_user_travels( - uid => $uid, - checkout_id => $checkout_id, - verbose => 1, + uid => $uid, + checkout_id => $checkout_id, + verbose => 1, ); - if ( @journeys == 0 or not $journeys[0]{completed} or $journeys[0]{ids}[1] != $checkout_id) { + if ( @journeys == 0 + or not $journeys[0]{completed} + or $journeys[0]{ids}[1] != $checkout_id ) + { $self->render( 'journey', error => 'notfound', diff --git a/public/static/js/travelynx-actions.js b/public/static/js/travelynx-actions.js index 8235889..3f1eb89 100644 --- a/public/static/js/travelynx-actions.js +++ b/public/static/js/travelynx-actions.js @@ -65,4 +65,17 @@ $(document).ready(function() { }; tvly_run(link, req, '/'); }); + $('.action-delete').click(function() { + var link = $(this); + var req = { + action: 'delete', + ids: link.data('id'), + checkin: link.data('checkin'), + checkout: link.data('checkout'), + }; + really_delete = confirm("Diese Zugfahrt wirklich löschen? Der Eintrag wird sofort aus der Datenbank entfernt und kann nicht wiederhergestellt werden."); + if (really_delete) { + tvly_run(link, req, '/history'); + } + }); }); diff --git a/public/static/js/travelynx-actions.min.js b/public/static/js/travelynx-actions.min.js index 2999816..57b6284 100644 --- a/public/static/js/travelynx-actions.min.js +++ b/public/static/js/travelynx-actions.min.js @@ -1 +1 @@ -function tvly_run(t,a,n,c){var i='error',o=$('
');t.hide(),t.after(o),$.post("/action",a,function(a){a.success?$(location).attr("href",n):(M.toast({html:i+" "+a.error}),o.remove(),c&&c(),t.append(" "+i),t.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")},"/")}),$(".action-checkout").click(function(){var t=$(this),a={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,a,"/s/"+a.station,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){var t=$(this);tvly_run(t,{action:"undo",undo_id:t.data("id")},"/")}),$(".action-cancelled-from").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_from",station:t.data("station"),train:t.data("train")},"/")}),$(".action-cancelled-to").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_to",station:t.data("station"),force:!0},"/")})}); +function tvly_run(n,t,a,c){var i='error',e=$('
');n.hide(),n.after(e),$.post("/action",t,function(t){t.success?$(location).attr("href",a):(M.toast({html:i+" "+t.error}),e.remove(),c&&c(),n.append(" "+i),n.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")},"/")}),$(".action-checkout").click(function(){var t=$(this),n={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,n,"/s/"+n.station,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){var t=$(this);tvly_run(t,{action:"undo",undo_id:t.data("id")},"/")}),$(".action-cancelled-from").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_from",station:t.data("station"),train:t.data("train")},"/")}),$(".action-cancelled-to").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_to",station:t.data("station"),force:!0},"/")}),$(".action-delete").click(function(){var t=$(this),n={action:"delete",ids:t.data("id"),checkin:t.data("checkin"),checkout:t.data("checkout")};really_delete=confirm("Diese Zugfahrt wirklich löschen? Der Eintrag wird sofort aus der Datenbank entfernt und kann nicht wiederhergestellt werden."),really_delete&&tvly_run(t,n,"/history")})}); diff --git a/templates/journey.html.ep b/templates/journey.html.ep index 4050e61..99285c1 100644 --- a/templates/journey.html.ep +++ b/templates/journey.html.ep @@ -128,4 +128,19 @@ +
+
+
+ +
+
+
% } -- cgit v1.2.3