summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2019-04-04 18:26:53 +0200
committerDaniel Friesel <derf@finalrewind.org>2019-04-04 18:26:53 +0200
commite2598df25ba089bbdef12777048ba68b7b119926 (patch)
tree6e0d8293729f527c26acce193e50b435fe18feca
parentb0ad209a234ffb447ac56b89be6e43a3ecec4ae7 (diff)
Allow journey deletion
See issue #3
-rwxr-xr-xlib/Travelynx.pm61
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm33
-rw-r--r--public/static/js/travelynx-actions.js13
-rw-r--r--public/static/js/travelynx-actions.min.js2
-rw-r--r--templates/journey.html.ep15
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
@@ -348,6 +348,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 {
@@ -892,6 +907,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='<i class="material-icons">error</i>',o=$('<div class="progress"><div class="indeterminate"></div></div>');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='<i class="material-icons">error</i>',e=$('<div class="progress"><div class="indeterminate"></div></div>');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 @@
</table>
</div>
</div>
+ <div class="row">
+ <div class="col s3 m3 l3">
+ </div>
+ <div class="col s6 m6 l6 center-align">
+ <a class="waves-effect waves-light red btn action-delete"
+ data-id="<%= join(q{,}, @{$journey->{ids}}) %>"
+ data-checkin="<%= $journey->{checkin}->epoch %>"
+ data-checkout="<%= $journey->{checkout}->epoch %>">
+ <i class="material-icons left">delete_forever</i>
+ Löschen
+ </a>
+ </div>
+ <div class="col s3 m3 l3">
+ </div>
+ </div>
% }