aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet')
-rw-r--r--perllib/FixMyStreet/App/Controller/Waste.pm131
-rw-r--r--perllib/FixMyStreet/App/Form/Field/Postcode.pm50
-rw-r--r--perllib/FixMyStreet/App/Form/Waste/UPRN.pm37
-rw-r--r--perllib/FixMyStreet/Cobrand/Bromley.pm137
-rw-r--r--perllib/FixMyStreet/Template.pm9
5 files changed, 295 insertions, 69 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Waste.pm b/perllib/FixMyStreet/App/Controller/Waste.pm
index 3cdd9fd42..6319b5f22 100644
--- a/perllib/FixMyStreet/App/Controller/Waste.pm
+++ b/perllib/FixMyStreet/App/Controller/Waste.pm
@@ -5,86 +5,83 @@ use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' }
use utf8;
-use Open311::GetServiceRequestUpdates;
+use FixMyStreet::App::Form::Waste::UPRN;
-sub receive_echo_event_notification : Path('/waste/echo') : Args(0) {
- my ($self, $c) = @_;
- $c->stash->{format} = 'xml';
- $c->response->header(Content_Type => 'application/soap+xml');
+sub auto : Private {
+ my ( $self, $c ) = @_;
+ my $cobrand_check = $c->cobrand->feature('waste');
+ $c->detach( '/page_error_404_not_found' ) if !$cobrand_check;
+ return 1;
+}
- require SOAP::Lite;
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ if (my $uprn = $c->get_param('address')) {
+ $c->detach('redirect_to_uprn', [ $uprn ]);
+ }
+
+ $c->stash->{title} = 'What is your address?';
+ my $form = FixMyStreet::App::Form::Waste::UPRN->new( cobrand => $c->cobrand );
+ $form->process( params => $c->req->body_params );
+ if ($form->validated) {
+ my $addresses = $form->value->{postcode};
+ $form = address_list_form($addresses);
+ }
+ $c->stash->{form} = $form;
+}
- $c->detach('soap_error', [ 'Invalid method', 405 ]) unless $c->req->method eq 'POST';
+sub address_list_form {
+ my $addresses = shift;
+ HTML::FormHandler->new(
+ field_list => [
+ address => {
+ required => 1,
+ type => 'Select',
+ widget => 'RadioGroup',
+ label => 'Select an address',
+ tags => { last_differs => 1, small => 1 },
+ options => $addresses,
+ },
+ go => {
+ type => 'Submit',
+ value => 'Continue',
+ element_attr => { class => 'govuk-button' },
+ },
+ ],
+ );
+}
- my $echo = $c->cobrand->feature('echo');
- $c->detach('soap_error', [ 'Missing config', 500 ]) unless $echo;
+sub redirect_to_uprn : Private {
+ my ($self, $c, $uprn) = @_;
+ my $uri = '/waste/uprn/' . $uprn;
+ $c->res->redirect($uri);
+ $c->detach;
+}
- # Make sure we log entire request for debugging
- $c->detach('soap_error', [ 'Missing body' ]) unless $c->req->body;
- my $soap = join('', $c->req->body->getlines);
- $c->log->info($soap);
+sub uprn : Chained('/') : PathPart('waste/uprn') : CaptureArgs(1) {
+ my ($self, $c, $uprn) = @_;
- my $body = $c->cobrand->body;
- $c->detach('soap_error', [ 'Bad jurisdiction' ]) unless $body;
+ if ($uprn eq 'missing') {
+ $c->stash->{template} = 'waste/missing.html';
+ $c->detach;
+ }
- my $env = SOAP::Deserializer->deserialize($soap);
+ $c->forward('/auth/get_csrf_token');
- my $header = $env->header;
- $c->detach('soap_error', [ 'Missing SOAP header' ]) unless $header;
- my $action = $header->{Action};
- $c->detach('soap_error', [ 'Incorrect Action' ]) unless $action && $action eq $echo->{receive_action};
- $header = $header->{Security};
- $c->detach('soap_error', [ 'Missing Security header' ]) unless $header;
- my $token = $header->{UsernameToken};
- $c->detach('soap_error', [ 'Authentication failed' ])
- unless $token && $token->{Username} eq $echo->{receive_username} && $token->{Password} eq $echo->{receive_password};
+ my $property = $c->stash->{property} = $c->cobrand->call_hook(look_up_property => $uprn);
+ $c->detach( '/page_error_404_not_found', [] ) unless $property;
- # Still want to say it is okay, even if we did nothing with it
- $c->forward('soap_ok');
-}
+ $c->stash->{uprn} = $uprn;
+ $c->stash->{latitude} = $property->{latitude};
+ $c->stash->{longitude} = $property->{longitude};
-sub soap_error : Private {
- my ($self, $c, $comment, $code) = @_;
- $code ||= 400;
- $c->response->status($code);
- my $type = $code == 500 ? 'Server' : 'Client';
- $c->response->body(SOAP::Serializer->fault($type, "Bad request: $comment", soap_header()));
+ $c->stash->{service_data} = $c->cobrand->call_hook(bin_services_for_address => $property) || [];
+ $c->stash->{services} = { map { $_->{service_id} => $_ } @{$c->stash->{service_data}} };
}
-sub soap_ok : Private {
+sub bin_days : Chained('uprn') : PathPart('') : Args(0) {
my ($self, $c) = @_;
- $c->response->status(200);
- my $method = SOAP::Data->name("NotifyEventUpdatedResponse")->attr({
- xmlns => "http://www.twistedfish.com/xmlns/echo/api/v1"
- });
- $c->response->body(SOAP::Serializer->envelope(method => $method, soap_header()));
-}
-
-sub soap_header {
- my $attr = "http://www.twistedfish.com/xmlns/echo/api/v1";
- my $action = "NotifyEventUpdatedResponse";
- my $header = SOAP::Header->name("Action")->attr({
- xmlns => 'http://www.w3.org/2005/08/addressing',
- 'soap:mustUnderstand' => 1,
- })->value("$attr/ReceiverService/$action");
-
- my $dt = DateTime->now();
- my $dt2 = $dt->clone->add(minutes => 5);
- my $w3c = DateTime::Format::W3CDTF->new;
- my $header2 = SOAP::Header->name("Security")->attr({
- 'soap:mustUnderstand' => 'true',
- 'xmlns' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
- })->value(
- \SOAP::Header->name(
- "Timestamp" => \SOAP::Header->value(
- SOAP::Header->name('Created', $w3c->format_datetime($dt)),
- SOAP::Header->name('Expires', $w3c->format_datetime($dt2)),
- )
- )->attr({
- xmlns => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
- })
- );
- return ($header, $header2);
}
__PACKAGE__->meta->make_immutable;
diff --git a/perllib/FixMyStreet/App/Form/Field/Postcode.pm b/perllib/FixMyStreet/App/Form/Field/Postcode.pm
new file mode 100644
index 000000000..093ae66a3
--- /dev/null
+++ b/perllib/FixMyStreet/App/Form/Field/Postcode.pm
@@ -0,0 +1,50 @@
+package FixMyStreet::App::Form::Field::Postcode;
+
+use HTML::FormHandler::Moose;
+extends 'HTML::FormHandler::Field::Text';
+
+use mySociety::PostcodeUtil;
+
+apply(
+ [
+ {
+ transform => sub {
+ my ( $value, $field ) = @_;
+ $value =~ s/[^A-Z0-9]//i;
+ return mySociety::PostcodeUtil::canonicalise_postcode($value);
+ }
+ },
+ {
+ check => sub { mySociety::PostcodeUtil::is_valid_postcode(shift) },
+ message => 'Sorry, we did not recognise that postcode.',
+ }
+ ]
+);
+
+
+__PACKAGE__->meta->make_immutable;
+use namespace::autoclean;
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+FixMyStreet::App::Form::Field::Postcode - validates postcode using mySociety::PostcodeUtil
+
+=head1 DESCRIPTION
+
+Validates that the input looks like a postcode using L<mySociety::PostcodeUtil>.
+Widget type is 'text'.
+
+=head1 DEPENDENCIES
+
+L<mySociety::PostcodeUtil>
+
+=cut
+
diff --git a/perllib/FixMyStreet/App/Form/Waste/UPRN.pm b/perllib/FixMyStreet/App/Form/Waste/UPRN.pm
new file mode 100644
index 000000000..d0ac7b3cb
--- /dev/null
+++ b/perllib/FixMyStreet/App/Form/Waste/UPRN.pm
@@ -0,0 +1,37 @@
+package FixMyStreet::App::Form::Waste::UPRN;
+
+use utf8;
+use HTML::FormHandler::Moose;
+extends 'HTML::FormHandler';
+
+use mySociety::PostcodeUtil qw(is_valid_postcode);
+
+has '+field_name_space' => ( default => 'FixMyStreet::App::Form::Field' );
+
+has cobrand => ( is => 'ro' );
+
+has_field postcode => (
+ required => 1,
+ type => 'Postcode',
+ validate_method => sub {
+ my $self = shift;
+ return if $self->has_errors; # Called even if already failed
+ my $data = $self->form->cobrand->bin_addresses_for_postcode($self->value);
+ if (!@$data) {
+ $self->add_error('Sorry, we did not find any results for that postcode');
+ }
+ push @$data, { value => 'missing', label => 'I can’t find my address' };
+ $self->value($data);
+ },
+ tags => { autofocus => 1 },
+);
+
+has_field go => (
+ type => 'Submit',
+ value => 'Go',
+ element_attr => { class => 'govuk-button' },
+);
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm
index c8da01226..e4e509f77 100644
--- a/perllib/FixMyStreet/Cobrand/Bromley.pm
+++ b/perllib/FixMyStreet/Cobrand/Bromley.pm
@@ -6,6 +6,8 @@ use warnings;
use utf8;
use DateTime::Format::W3CDTF;
use DateTime::Format::Flexible;
+use Integrations::Echo;
+use Sort::Key::Natural qw(natkeysort_inplace);
use Try::Tiny;
use FixMyStreet::DateRange;
@@ -376,5 +378,138 @@ sub munge_load_and_group_problems {
}
}
-1;
+sub bin_addresses_for_postcode {
+ my $self = shift;
+ my $pc = shift;
+
+ my $echo = $self->feature('echo');
+ $echo = Integrations::Echo->new(%$echo);
+ my $points = $echo->FindPoints($pc);
+ my $data = [ map { {
+ value => $_->{SharedRef}{Value}{anyType},
+ label => FixMyStreet::Template::title($_->{Description}),
+ } } @$points ];
+ natkeysort_inplace { $_->{label} } @$data;
+ return $data;
+}
+
+sub look_up_property {
+ my $self = shift;
+ my $uprn = shift;
+
+ my $echo = $self->feature('echo');
+ $echo = Integrations::Echo->new(%$echo);
+ my $result = $echo->GetPointAddress($uprn);
+ return {
+ id => $result->{Id},
+ uprn => $uprn,
+ address => FixMyStreet::Template::title($result->{Description}),
+ latitude => $result->{Coordinates}{GeoPoint}{Latitude},
+ longitude => $result->{Coordinates}{GeoPoint}{Longitude},
+ };
+}
+
+my %irregulars = ( 1 => 'st', 2 => 'nd', 3 => 'rd', 11 => 'th', 12 => 'th', 13 => 'th');
+sub ordinal {
+ my $n = shift;
+ $irregulars{$n % 100} || $irregulars{$n % 10} || 'th';
+}
+
+sub construct_bin_date {
+ my $str = shift;
+ return unless $str;
+ my $offset = ($str->{OffsetMinutes} || 0) * 60;
+ my $zone = DateTime::TimeZone->offset_as_string($offset);
+ my $date = DateTime::Format::W3CDTF->parse_datetime($str->{DateTime});
+ $date->set_time_zone($zone);
+ return $date;
+}
+
+sub bin_services_for_address {
+ my $self = shift;
+ my $property = shift;
+
+ my %service_name_override = (
+ 531 => 'Non-Recyclable Refuse',
+ 532 => 'Non-Recyclable Refuse',
+ 533 => 'Non-Recyclable Refuse',
+ 535 => 'Mixed Recycling (Cans, Plastics & Glass)',
+ 536 => 'Mixed Recycling (Cans, Plastics & Glass)',
+ 537 => 'Paper & Cardboard',
+ 541 => 'Paper & Cardboard',
+ 542 => 'Food Waste',
+ 544 => 'Food Waste',
+ 545 => 'Garden Waste',
+ );
+
+ my $echo = $self->feature('echo');
+ $echo = Integrations::Echo->new(%$echo);
+ my $result = $echo->GetServiceUnitsForObject($property->{uprn});
+ return [] unless @$result;
+
+ my @out;
+ foreach (@$result) {
+ next unless $_->{ServiceTasks};
+
+ my $servicetask = $_->{ServiceTasks}{ServiceTask};
+ my $schedules = _parse_schedules($servicetask);
+
+ next unless $schedules->{next} or $schedules->{last};
+
+ my $row = {
+ id => $_->{Id},
+ service_id => $_->{ServiceId},
+ service_name => $service_name_override{$_->{ServiceId}} || $_->{ServiceName},
+ service_task_id => $servicetask->{Id},
+ service_task_name => $servicetask->{TaskTypeName},
+ service_task_type_id => $servicetask->{TaskTypeId},
+ schedule => $servicetask->{ScheduleDescription},
+ last => $schedules->{last},
+ next => $schedules->{next},
+ };
+ push @out, $row;
+ }
+ return \@out;
+}
+
+sub _parse_schedules {
+ my $servicetask = shift;
+ my $schedules = $servicetask->{ServiceTaskSchedules}{ServiceTaskSchedule};
+ $schedules = [ $schedules ] unless ref $schedules eq 'ARRAY';
+
+ my $today = DateTime->now->set_time_zone(FixMyStreet->local_time_zone)->strftime("%F");
+ my ($min_next, $max_last, $next_changed);
+ foreach my $schedule (@$schedules) {
+ my $end_date = construct_bin_date($schedule->{EndDate})->strftime("%F");
+ next if $end_date lt $today;
+
+ my $next = $schedule->{NextInstance};
+ my $d = construct_bin_date($next->{CurrentScheduledDate});
+ if ($d && (!$min_next || $d < $min_next->{date})) {
+ $next_changed = $next->{CurrentScheduledDate}{DateTime} ne $next->{OriginalScheduledDate}{DateTime};
+ $min_next = {
+ date => $d,
+ ordinal => ordinal($d->day),
+ changed => $next_changed,
+ };
+ }
+
+ my $last = $schedule->{LastInstance};
+ $d = construct_bin_date($last->{CurrentScheduledDate});
+ if ($d && (!$max_last || $d > $max_last->{date})) {
+ $max_last = {
+ date => $d,
+ ordinal => ordinal($d->day),
+ };
+ }
+ }
+
+ return {
+ next => $min_next,
+ last => $max_last,
+ };
+}
+
+
+1;
diff --git a/perllib/FixMyStreet/Template.pm b/perllib/FixMyStreet/Template.pm
index afb977ac9..275089a35 100644
--- a/perllib/FixMyStreet/Template.pm
+++ b/perllib/FixMyStreet/Template.pm
@@ -161,7 +161,6 @@ sub sanitize {
return $text;
}
-
=head2 email_sanitize_text
Intended for use in the _email_comment_list.txt template to allow HTML
@@ -291,4 +290,12 @@ sub _space_slash {
return $t;
}
+sub title : Filter {
+ my $text = shift;
+ $text =~ s{(\w[\w']*)}{\u\L$1}g;
+ # Postcode special handling
+ $text =~ s{(\w?\w\d[\d\w]?\s*\d\w\w)}{\U$1}g;
+ return $text;
+}
+
1;