diff options
| -rwxr-xr-x | lib/Travelynx.pm | 61 | ||||
| -rwxr-xr-x | lib/Travelynx/Controller/Traveling.pm | 33 | ||||
| -rw-r--r-- | public/static/js/travelynx-actions.js | 13 | ||||
| -rw-r--r-- | public/static/js/travelynx-actions.min.js | 2 | ||||
| -rw-r--r-- | 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 @@ -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>  % } | 
