aboutsummaryrefslogtreecommitdiffstats
path: root/perllib
diff options
context:
space:
mode:
Diffstat (limited to 'perllib')
-rw-r--r--perllib/FixMyStreet/App/Controller/Contact.pm16
-rw-r--r--perllib/FixMyStreet/App/Controller/Contact/Enquiry.pm119
-rw-r--r--perllib/FixMyStreet/App/Model/PhotoSet.pm19
-rw-r--r--perllib/FixMyStreet/Cobrand/Hounslow.pm49
-rw-r--r--perllib/FixMyStreet/PhotoStorage.pm28
-rw-r--r--perllib/FixMyStreet/SendReport/Open311.pm1
-rw-r--r--perllib/Open311.pm70
7 files changed, 281 insertions, 21 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm
index fb525fc1f..e2b76ca60 100644
--- a/perllib/FixMyStreet/App/Controller/Contact.pm
+++ b/perllib/FixMyStreet/App/Controller/Contact.pm
@@ -26,9 +26,14 @@ Functions to run on both GET and POST contact requests.
sub auto : Private {
my ($self, $c) = @_;
+ $c->forward('/auth/get_csrf_token');
+}
+
+sub begin : Private {
+ my ($self, $c) = @_;
+ $c->forward('/begin');
$c->forward('setup_request');
$c->forward('determine_contact_type');
- $c->forward('/auth/get_csrf_token');
}
=head2 index
@@ -106,7 +111,14 @@ sub determine_contact_type : Private {
$c->stash->{rejecting_report} = 1;
}
} elsif ( $c->cobrand->abuse_reports_only ) {
- $c->detach( '/page_error_404_not_found' );
+ # General enquiries replaces contact form if enabled
+ if ( $c->cobrand->can('setup_general_enquiries_stash') ) {
+ $c->res->redirect( '/contact/enquiry' );
+ $c->detach;
+ return 1;
+ } else {
+ $c->detach( '/page_error_404_not_found' );
+ }
}
return 1;
diff --git a/perllib/FixMyStreet/App/Controller/Contact/Enquiry.pm b/perllib/FixMyStreet/App/Controller/Contact/Enquiry.pm
new file mode 100644
index 000000000..5b1c4980f
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Contact/Enquiry.pm
@@ -0,0 +1,119 @@
+package FixMyStreet::App::Controller::Contact::Enquiry;
+
+use Moose;
+use namespace::autoclean;
+use Path::Tiny;
+use File::Copy;
+use Digest::SHA qw(sha1_hex);
+use File::Basename;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+sub auto : Private {
+ my ($self, $c) = @_;
+
+ unless ( $c->cobrand->call_hook('setup_general_enquiries_stash') ) {
+ $c->res->redirect( '/' );
+ $c->detach;
+ }
+}
+
+# This needs to be defined here so /contact/begin doesn't get run instead.
+sub begin : Private {
+ my ($self, $c) = @_;
+
+ $c->forward('/begin');
+}
+
+sub index : Path : Args(0) {
+ my ( $self, $c, $preserve_session ) = @_;
+
+ # Make sure existing files aren't lost if we're rendering this
+ # page as a result of validation error.
+ delete $c->session->{enquiry_files} unless $preserve_session;
+
+ $c->stash->{field_errors}->{name} = _("Please enter your full name.") if $c->stash->{field_errors}->{name};
+}
+
+sub submit : Path('submit') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ unless ($c->req->method eq 'POST' && $c->forward("/report/new/check_form_submitted") ) {
+ $c->res->redirect( '/contact/enquiry' );
+ return;
+ }
+
+ # General enquiries are always private reports, and aren't
+ # located by the user on the map
+ $c->set_param('non_public', 1);
+ $c->set_param('pc', '');
+ $c->set_param('skipped', 1);
+
+ $c->forward('/report/new/initialize_report');
+ $c->forward('/report/new/check_for_category');
+ $c->forward('/auth/check_csrf_token');
+ $c->forward('/report/new/process_report');
+ $c->forward('/report/new/process_user');
+ $c->forward('handle_uploads');
+ $c->forward('/photo/process_photo');
+ $c->go('index', [ 1 ]) unless $c->forward('/report/new/check_for_errors');
+ $c->forward('/report/new/save_user_and_report');
+ $c->forward('confirm_report');
+ $c->stash->{success} = 1;
+
+ # Don't want these lingering around for the next time.
+ delete $c->session->{enquiry_files};
+}
+
+sub confirm_report : Private {
+ my ( $self, $c ) = @_;
+
+ my $report = $c->stash->{report};
+
+ # We don't ever want to modify an existing user, as general enquiries don't
+ # require any kind of email confirmation.
+ $report->user->insert unless $report->user->in_storage;
+ $report->confirm();
+ $report->update;
+}
+
+sub handle_uploads : Private {
+ my ( $self, $c ) = @_;
+
+ # NB. For simplicity's sake this relies on the UPLOAD_DIR config key provided
+ # when using the FileSystem PHOTO_STORAGE_BACKEND. Should your FMS site not
+ # be using this storage backend, you must ensure that UPLOAD_DIR is set
+ # in order for general enquiries uploads to work.
+ my $cfg = FixMyStreet->config('PHOTO_STORAGE_OPTIONS');
+ my $dir = $cfg ? $cfg->{UPLOAD_DIR} : FixMyStreet->config('UPLOAD_DIR');
+ $dir = path($dir, "enquiry_files")->absolute(FixMyStreet->path_to());
+ $dir->mkpath;
+
+ my $files = $c->session->{enquiry_files} || {};
+ foreach ($c->req->upload) {
+ my $upload = $c->req->upload($_);
+ if ($upload->type !~ /^image/) {
+ # It's not a photo so remove it before /photo/process_photo rejects it
+ delete $c->req->uploads->{$_};
+
+ # For each file, copy it into place in a subdir of PHOTO_STORAGE_OPTIONS.UPLOAD_DIR
+ FixMyStreet::PhotoStorage::base64_decode_upload($c, $upload);
+ # Hash each file to get its filename, but preserve the file extension
+ # so content-type is correct when POSTing to Open311.
+ my ($p, $n, $ext) = fileparse($upload->filename, qr/\.[^.]*/);
+ my $key = sha1_hex($upload->slurp) . $ext;
+ my $out = path($dir, $key);
+ unless (copy($upload->tempname, $out)) {
+ $c->log->info('Couldn\'t copy temp file to destination: ' . $!);
+ $c->stash->{photo_error} = _("Sorry, we couldn't save your file(s), please try again.");
+ return;
+ }
+ # Then store the file hashes in report->extra along with the original filenames
+ $files->{$key} = $upload->raw_basename;
+ }
+ }
+ $c->session->{enquiry_files} = $files;
+ $c->stash->{report}->set_extra_metadata(enquiry_files => $files);
+}
+
+1;
diff --git a/perllib/FixMyStreet/App/Model/PhotoSet.pm b/perllib/FixMyStreet/App/Model/PhotoSet.pm
index 8621286b0..1d9ccd7cd 100644
--- a/perllib/FixMyStreet/App/Model/PhotoSet.pm
+++ b/perllib/FixMyStreet/App/Model/PhotoSet.pm
@@ -121,23 +121,8 @@ has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data proc
return ();
}
- # base64 decode the file if it's encoded that way
- # Catalyst::Request::Upload doesn't do this automatically
- # unfortunately.
- my $transfer_encoding = $upload->headers->header('Content-Transfer-Encoding');
- if (defined $transfer_encoding && $transfer_encoding eq 'base64') {
- my $decoded = decode_base64($upload->slurp);
- if (open my $fh, '>', $upload->tempname) {
- binmode $fh;
- print $fh $decoded;
- close $fh
- } else {
- my $c = $self->c;
- $c->log->info('Couldn\'t open temp file to save base64 decoded image: ' . $!);
- $c->stash->{photo_error} = _("Sorry, we couldn't save your image(s), please try again.");
- return ();
- }
- }
+ # Make sure any base64 encoding is handled.
+ FixMyStreet::PhotoStorage::base64_decode_upload($self->c, $upload);
# get the photo into a variable
my $photo_blob = eval {
diff --git a/perllib/FixMyStreet/Cobrand/Hounslow.pm b/perllib/FixMyStreet/Cobrand/Hounslow.pm
index 39f8e902f..491384847 100644
--- a/perllib/FixMyStreet/Cobrand/Hounslow.pm
+++ b/perllib/FixMyStreet/Cobrand/Hounslow.pm
@@ -119,6 +119,7 @@ sub open311_config {
$row->set_extra_fields(@$extra);
$params->{multi_photos} = 1;
+ $params->{upload_files} = 1;
}
sub open311_munge_update_params {
@@ -140,6 +141,45 @@ sub open311_skip_report_fetch {
# Make sure fetched report description isn't shown.
sub filter_report_description { "" }
+sub setup_general_enquiries_stash {
+ my $self = shift;
+
+ my @bodies = $self->{c}->model('DB::Body')->active->for_areas(( $self->council_area_id ))->all;
+ my %bodies = map { $_->id => $_ } @bodies;
+ my @contacts #
+ = $self->{c} #
+ ->model('DB::Contact') #
+ ->active
+ ->search(
+ {
+ 'me.body_id' => [ keys %bodies ]
+ },
+ {
+ prefetch => 'body',
+ order_by => 'me.category',
+ }
+ )->all;
+ @contacts = grep {
+ my $group = $_->get_extra_metadata('group') || '';
+ $group eq 'Other' || $group eq 'General Enquiries';
+ } @contacts;
+ $self->{c}->stash->{bodies} = \%bodies;
+ $self->{c}->stash->{bodies_to_list} = \%bodies;
+ $self->{c}->stash->{contacts} = \@contacts;
+ $self->{c}->stash->{missing_details_bodies} = [];
+ $self->{c}->stash->{missing_details_body_names} = [];
+
+ $self->{c}->set_param('title', "General Enquiry");
+ # Can't use (0, 0) for lat lon so default to the rough location
+ # of Hounslow Highways HQ.
+ $self->{c}->stash->{latitude} = 51.469;
+ $self->{c}->stash->{longitude} = -0.35;
+
+ return 1;
+}
+
+sub abuse_reports_only { 1 }
+
sub lookup_site_code_config { {
buffer => 50, # metres
url => "https://tilma.mysociety.org/mapserver/hounslow",
@@ -149,4 +189,13 @@ sub lookup_site_code_config { {
accept_feature => sub { 1 }
} }
+# Hounslow don't want any reports made before their go-live date visible on
+# their cobrand at all.
+sub problems_restriction {
+ my ($self, $rs) = @_;
+ return $rs->to_body($self->body)->search({
+ 'me.confirmed' => { '>=', '2019-05-06' }
+ });
+}
+
1;
diff --git a/perllib/FixMyStreet/PhotoStorage.pm b/perllib/FixMyStreet/PhotoStorage.pm
index a441fb718..558c93749 100644
--- a/perllib/FixMyStreet/PhotoStorage.pm
+++ b/perllib/FixMyStreet/PhotoStorage.pm
@@ -37,5 +37,33 @@ sub get_fileid {
}
+=head2 base64_decode_upload
+
+base64 decode the temporary on-disk uploaded file if
+it's encoded that way. Modifies the file in-place.
+Catalyst::Request::Upload doesn't do this automatically
+unfortunately.
+
+=cut
+
+sub base64_decode_upload {
+ my ( $c, $upload ) = @_;
+
+ my $transfer_encoding = $upload->headers->header('Content-Transfer-Encoding');
+ if (defined $transfer_encoding && $transfer_encoding eq 'base64') {
+ my $decoded = decode_base64($upload->slurp);
+ if (open my $fh, '>', $upload->tempname) {
+ binmode $fh;
+ print $fh $decoded;
+ close $fh
+ } else {
+ $c->log->info('Couldn\'t open temp file to save base64 decoded image: ' . $!);
+ $c->stash->{photo_error} = _("Sorry, we couldn't save your file(s), please try again.");
+ return ();
+ }
+ }
+
+}
+
1;
diff --git a/perllib/FixMyStreet/SendReport/Open311.pm b/perllib/FixMyStreet/SendReport/Open311.pm
index a661ff206..8a6b992fd 100644
--- a/perllib/FixMyStreet/SendReport/Open311.pm
+++ b/perllib/FixMyStreet/SendReport/Open311.pm
@@ -29,6 +29,7 @@ sub send {
use_service_as_deviceid => 0,
extended_description => 1,
multi_photos => 0,
+ upload_files => 0,
fixmystreet_body => $body,
);
diff --git a/perllib/Open311.pm b/perllib/Open311.pm
index ec3390ee7..a902a7213 100644
--- a/perllib/Open311.pm
+++ b/perllib/Open311.pm
@@ -12,6 +12,7 @@ use HTTP::Request::Common qw(GET POST);
use FixMyStreet::Cobrand;
use FixMyStreet::DB;
use Utils;
+use Path::Tiny 'path';
has jurisdiction => ( is => 'ro', isa => Str );;
has api_key => ( is => 'ro', isa => Str );
@@ -32,6 +33,7 @@ has use_service_as_deviceid => ( is => 'ro', isa => Bool, default => 0 );
has extended_statuses => ( is => 'ro', isa => Bool, default => 0 );
has always_send_email => ( is => 'ro', isa => Bool, default => 0 );
has multi_photos => ( is => 'ro', isa => Bool, default => 0 );
+has upload_files => ( is => 'ro', isa => Bool, default => 0 );
has use_customer_reference => ( is => 'ro', isa => Bool, default => 0 );
has mark_reopen => ( is => 'ro', isa => Bool, default => 0 );
has fixmystreet_body => ( is => 'ro', isa => InstanceOf['FixMyStreet::DB::Result::Body'] );
@@ -90,8 +92,9 @@ sub send_service_request {
my $params = $self->_populate_service_request_params(
$problem, $extra, $service_code
);
+ my $uploads = $self->_populate_service_request_uploads($problem, $params);
- my $response = $self->_post( $self->endpoints->{requests}, $params );
+ my $response = $self->_post( $self->endpoints->{requests}, $params, $uploads );
if ( $response ) {
my $obj = $self->_get_xml_object( $response );
@@ -191,6 +194,43 @@ sub _populate_service_request_params {
return $params;
}
+sub _populate_service_request_uploads {
+ my $self = shift;
+ my $problem = shift;
+ my $params = shift;
+
+ return unless $self->upload_files;
+
+ my $uploads = {};
+
+ if ( $problem->get_extra_metadata('enquiry_files') ) {
+ my $cfg = FixMyStreet->config('PHOTO_STORAGE_OPTIONS');
+ my $dir = $cfg ? $cfg->{UPLOAD_DIR} : FixMyStreet->config('UPLOAD_DIR');
+ $dir = path($dir, "enquiry_files")->absolute(FixMyStreet->path_to());
+
+ my $files = $problem->get_extra_metadata('enquiry_files') || {};
+ for my $key (keys %$files) {
+ my $name = $files->{$key};
+ $uploads->{"file_$key"} = [ path($dir, $key)->canonpath, $name ];
+ }
+ }
+
+ if ( $problem->photo && $problem->non_public ) {
+ # open311-adapter won't be able to download any photos if they're on
+ # a private report, so instead of sending the media_url parameter
+ # send the actual photo content with the POST request.
+ my $i = 0;
+ my $photoset = $problem->get_photoset;
+ for ( $photoset->all_ids ) {
+ my $photo = $photoset->get_image_data( num => $i++, size => 'full' );
+ $uploads->{"photo$i"} = [ undef, $_, Content_Type => $photo->{content_type}, Content => $photo->{data} ];
+ }
+ delete $params->{media_url};
+ }
+
+ return $uploads;
+}
+
sub _generate_service_request_description {
my $self = shift;
my $problem = shift;
@@ -442,6 +482,7 @@ sub _request {
my $method = shift;
my $path = shift;
my $params = shift || {};
+ my $uploads = shift;
my $uri = URI->new( $self->endpoint );
$uri->path( $uri->path . $path );
@@ -458,7 +499,32 @@ sub _request {
$uri->query_form( $params );
GET $uri->as_string;
} elsif ($method eq 'POST') {
- POST $uri->as_string, $params;
+ if ($uploads) {
+ # HTTP::Request::Common needs to be constructed slightly
+ # differently if there are files to upload.
+
+ my @media_urls = ();
+ # HTTP::Request::Common treats an arrayref as a filespec,
+ # so we need to rejig the media_url parameter so it doesn't
+ # get confused...
+ # https://stackoverflow.com/questions/50705344/perl-httprequestcommon-post-file-and-array
+ if ($self->multi_photos) {
+ my $media_urls = $params->{media_url};
+ @media_urls = map { ( media_url => $_ ) } @$media_urls;
+ delete $params->{media_url};
+ }
+ $params = {
+ Content_Type => 'form-data',
+ Content => [
+ %$params,
+ @media_urls,
+ %$uploads
+ ]
+ };
+ POST $uri->as_string, %$params;
+ } else {
+ POST $uri->as_string, $params;
+ }
}
};