aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cypress/cypress/fixtures/trees.json2
-rw-r--r--.cypress/cypress/integration/northamptonshire.js8
-rw-r--r--.cypress/cypress/integration/regressions.js17
-rw-r--r--CHANGELOG.md3
-rwxr-xr-xbin/fixmystreet.com/fixture1
-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
-rw-r--r--t/Mock/MapIt.pm5
-rw-r--r--t/app/controller/contact.t31
-rw-r--r--t/app/controller/contact_enquiry.t226
-rw-r--r--t/app/controller/sample.pdfbin0 -> 12501 bytes
-rw-r--r--t/cobrand/hounslow.t44
-rw-r--r--templates/web/base/contact/enquiry/index.html79
-rw-r--r--templates/web/base/contact/enquiry/submit.html1
-rw-r--r--templates/web/hounslow/main_nav_items.html58
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js10
21 files changed, 754 insertions, 33 deletions
diff --git a/.cypress/cypress/fixtures/trees.json b/.cypress/cypress/fixtures/trees.json
index c965614f7..f3808c912 100644
--- a/.cypress/cypress/fixtures/trees.json
+++ b/.cypress/cypress/fixtures/trees.json
@@ -7,7 +7,7 @@
"geometry": {
"type": "Point",
"coordinates": [
- 99073,
+ -99073,
6843328
]
},
diff --git a/.cypress/cypress/integration/northamptonshire.js b/.cypress/cypress/integration/northamptonshire.js
index 376d72cd0..dc683e4e6 100644
--- a/.cypress/cypress/integration/northamptonshire.js
+++ b/.cypress/cypress/integration/northamptonshire.js
@@ -8,7 +8,7 @@ it('prevents clicking unless asset selected', function() {
cy.fixture('trees.json');
cy.fixture('trees_none.json');
cy.route('**/render-layer/**', 'fixture:trees_none.json').as('empty-trees-layer');
- cy.route('**/16463/10787**', 'fixture:trees.json').as('trees-layer');
+ cy.route('**/16301/10787**', 'fixture:trees.json').as('trees-layer');
cy.route('/report/new/ajax*').as('report-ajax');
cy.visit('http://northamptonshire.localhost:3001/');
cy.get('[name=pc]').type('NN1 1NS');
@@ -30,7 +30,7 @@ it('selecting an asset allows a report', function() {
cy.fixture('trees.json');
cy.fixture('trees_none.json');
cy.route('**/render-layer/**', 'fixture:trees_none.json').as('empty-trees-layer');
- cy.route('**/16463/10787**', 'fixture:trees.json').as('trees-layer');
+ cy.route('**/16301/10787**', 'fixture:trees.json').as('trees-layer');
cy.route('/report/new/ajax*').as('report-ajax');
cy.visit('http://northamptonshire.localhost:3001/');
cy.get('[name=pc]').type('NN1 2NS');
@@ -52,8 +52,8 @@ it('detects multiple assets at same location', function() {
cy.fixture('trees.json');
cy.fixture('trees_none.json');
cy.route('**/render-layer/**', 'fixture:trees_none.json').as('empty-trees-layer');
- cy.route('**/16463/10787**', 'fixture:trees.json').as('trees-layer');
- cy.route('**/16463/10788**', 'fixture:trees.json').as('trees-layer2');
+ cy.route('**/16301/10787**', 'fixture:trees.json').as('trees-layer');
+ cy.route('**/16301/10788**', 'fixture:trees.json').as('trees-layer2');
cy.route('/report/new/ajax*').as('report-ajax');
cy.visit('http://northamptonshire.localhost:3001/');
cy.get('[name=pc]').type('NN1 2NS');
diff --git a/.cypress/cypress/integration/regressions.js b/.cypress/cypress/integration/regressions.js
index af6a1d73d..7bdd8f6fe 100644
--- a/.cypress/cypress/integration/regressions.js
+++ b/.cypress/cypress/integration/regressions.js
@@ -54,4 +54,21 @@ describe('Regression tests', function() {
cy.get('.content').should('not.contain', 'toddler');
});
+ it.only('has the correct send-to text at all times', function() {
+ cy.server();
+ cy.route('/report/new/ajax*').as('report-ajax');
+ cy.visit('/');
+ cy.get('[name=pc]').type('NN1 1NS');
+ cy.get('[name=pc]').parents('form').submit();
+
+ cy.get('#map_box').click();
+ cy.wait('@report-ajax');
+ cy.get('[id=category_group]').select('Graffiti');
+ cy.contains(/These will be sent to Northampton Borough Council and also/);
+
+ cy.get('#map_box').click(200, 200);
+ cy.wait('@report-ajax');
+ cy.contains(/These will be sent to Northampton Borough Council and also/);
+ });
+
});
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9cfc0e5f9..b18762648 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
- Prevent creation of two templates with same title.
- Fix bug going between report/new pages client side
- Don't include private reports when searching by ref from front page.
+ - Set fixmystreet.bodies sooner client-side, for two-tier locations.
- Development improvements:
- Upgrade the underlying framework and a number of other packages.
- Add feature cobrand helper function.
@@ -15,6 +16,8 @@
reports made in that category private.
- Ensure any reports fetched in a category marked private are also
marked private on the site.
+ - Add new upload_files flag which sends files/photos as part of the
+ POST service request.
* v2.6 (3rd May 2019)
- New features:
diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture
index f1d004f31..3c3336811 100755
--- a/bin/fixmystreet.com/fixture
+++ b/bin/fixmystreet.com/fixture
@@ -97,6 +97,7 @@ if ($opt->test_fixtures) {
{ area_id => 2234, categories => ['Fallen Tree', 'Very Urgent'], name => 'Northamptonshire County Council' },
{ area_id => 2217, categories => ['Flytipping', 'Roads'], name => 'Buckinghamshire County Council' },
{ area_id => 2257, categories => ['Flytipping', 'Graffiti'], name => 'Chiltern District Council' },
+ { area_id => 2397, categories => [ 'Graffiti' ], name => 'Northampton Borough Council' },
) {
$bodies->{$_->{area_id}} = FixMyStreet::DB::Factory::Body->find_or_create($_);
my $cats = join(', ', @{$_->{categories}});
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;
+ }
}
};
diff --git a/t/Mock/MapIt.pm b/t/Mock/MapIt.pm
index 57174a799..60ba30fe3 100644
--- a/t/Mock/MapIt.pm
+++ b/t/Mock/MapIt.pm
@@ -39,8 +39,9 @@ my @PLACES = (
[ 'LE15 0GJ', 52.670447, -0.727877, 2600, 'Rutland County Council', 'CTY'],
[ 'BR1 3UH', 51.4021, 0.01578, 2482, 'Bromley Council', 'LBO' ],
[ 'BR1 3UH', 51.402096, 0.015784, 2482, 'Bromley Council', 'LBO' ],
- [ 'NN1 1NS', 52.236251, 0.892052, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ],
- [ 'NN1 2NS', 52.238301, 0.889992, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ],
+ [ 'NN1 1NS', 52.236251, -0.892052, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ],
+ [ 'NN1 2NS', 52.238301, -0.889992, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ],
+ [ '?', 52.238827, -0.894970, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ],
[ 'TW7 5JN', 51.482286, -0.328163, 2483, 'Hounslow Borough Council', 'LBO' ],
[ '?', 50.78301, -0.646929 ],
[ 'TA1 1QP', 51.023569, -3.099055, 2239, 'Somerset County Council', 'CTY', 2429, 'Taunton Deane Borough Council', 'DIS' ],
diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t
index 842f27dd5..c74aaa5ff 100644
--- a/t/app/controller/contact.t
+++ b/t/app/controller/contact.t
@@ -6,6 +6,16 @@ sub abuse_reports_only { 1; }
1;
+package FixMyStreet::Cobrand::GeneralEnquiries;
+
+use base 'FixMyStreet::Cobrand::Default';
+
+sub abuse_reports_only { 1 }
+
+sub setup_general_enquiries_stash { 1 }
+
+1;
+
package main;
use FixMyStreet::TestMech;
@@ -492,6 +502,27 @@ subtest 'check can limit contact to abuse reports' => sub {
}
};
+subtest 'check redirected to correct form for general enquiries cobrand' => sub {
+ FixMyStreet::override_config {
+ 'ALLOWED_COBRANDS' => [ 'generalenquiries' ],
+ }, sub {
+ $mech->get( '/contact' );
+ is $mech->res->code, 200, "got 200 for final destination";
+ is $mech->res->previous->code, 302, "got 302 for redirect";
+ is $mech->uri->path, '/contact/enquiry';
+
+ $mech->get_ok( '/contact?id=' . $problem_main->id, 'can visit for abuse report' );
+
+ my $token = FixMyStreet::App->model("DB::Token")->create({
+ scope => 'moderation',
+ data => { id => $problem_main->id }
+ });
+
+ $mech->get_ok( '/contact?m=' . $token->token, 'can visit for moderation complaint' );
+
+ }
+};
+
$problem_main->delete;
done_testing();
diff --git a/t/app/controller/contact_enquiry.t b/t/app/controller/contact_enquiry.t
new file mode 100644
index 000000000..fe3962c8e
--- /dev/null
+++ b/t/app/controller/contact_enquiry.t
@@ -0,0 +1,226 @@
+use FixMyStreet::TestMech;
+use Path::Tiny;
+use File::Temp 'tempdir';
+
+# disable info logs for this test run
+FixMyStreet::App->log->disable('info');
+END { FixMyStreet::App->log->enable('info'); }
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $body = $mech->create_body_ok( 2483, 'Hounslow Borough Council' );
+my $contact = $mech->create_contact_ok(
+ body_id => $body->id,
+ category => 'General Enquiry',
+ email => 'genenq@example.com',
+ non_public => 1,
+);
+my $contact2 = $mech->create_contact_ok(
+ body_id => $body->id,
+ category => 'FOI Request',
+ email => 'foi@example.com',
+ non_public => 1,
+);
+my $contact3 = $mech->create_contact_ok(
+ body_id => $body->id,
+ category => 'Other',
+ email => 'other@example.com',
+);
+my $contact4 = $mech->create_contact_ok(
+ body_id => $body->id,
+ category => 'Carriageway Defect',
+ email => 'potholes@example.com',
+);
+$contact->update( { extra => { group => 'General Enquiries' } } );
+$contact2->update( { extra => { group => 'General Enquiries' } } );
+$contact3->update( { extra => { group => 'Other' } } );
+
+FixMyStreet::override_config { ALLOWED_COBRANDS => ['bromley'], }, sub {
+ subtest 'redirected to / if general enquiries not enabled' => sub {
+ $mech->get( '/contact/enquiry' );
+ is $mech->res->code, 200, "got 200 for final destination";
+ is $mech->res->previous->code, 302, "got 302 for redirect";
+ is $mech->uri->path, '/';
+ };
+};
+
+FixMyStreet::override_config {
+ ALLOWED_COBRANDS => ['hounslow'],
+ MAPIT_URL => 'http://mapit.uk/',
+}, sub {
+ subtest 'Non-general enquiries category not shown' => sub {
+ $mech->get_ok( '/contact/enquiry' );
+ $mech->content_lacks('Carriageway Defect');
+ $mech->content_contains('FOI Request');
+ };
+
+ subtest 'Enquiry can be submitted when logged out' => sub {
+ my $problems = FixMyStreet::App->model('DB::Problem')->to_body( $body->id );
+
+ $mech->get_ok( '/contact/enquiry' );
+ $mech->submit_form_ok( {
+ with_fields => {
+ name => 'Test User',
+ username => 'testuser@example.org',
+ category => 'Other',
+ detail => 'This is a general enquiry',
+ }
+ } );
+ is $mech->uri->path, '/contact/enquiry/submit';
+ $mech->content_contains("Thank you for your enquiry");
+
+ is $problems->count, 1, 'problem created';
+ my $problem = $problems->first;
+ is $problem->category, 'Other', 'problem has correct category';
+ is $problem->detail, 'This is a general enquiry', 'problem has correct detail';
+ is $problem->non_public, 1, 'problem created non_public';
+ is $problem->postcode, '';
+ is $problem->used_map, 0;
+ is $problem->latitude, 51.469, 'Problem has correct latitude';
+ is $problem->longitude, -0.35, 'Problem has correct longitude';
+ ok $problem->confirmed, 'problem confirmed';
+ is $problem->user->name, undef, 'User created without name';
+ is $problem->name, 'Test User', 'Report created with correct name';
+ is $problem->user->email, 'testuser@example.org', 'User created with correct email';
+ };
+
+ subtest 'Enquiry can be submitted when logged in' => sub {
+ my $problems = FixMyStreet::App->model('DB::Problem')->to_body( $body->id );
+ my $user = $problems->first->user;
+ $problems->delete_all;
+
+ $mech->log_in_ok( $user->email );
+
+ $mech->get_ok( '/contact/enquiry' );
+ $mech->submit_form_ok( {
+ with_fields => {
+ name => 'Test User',
+ category => 'FOI Request',
+ detail => 'This is a general enquiry',
+ }
+ } );
+ is $mech->uri->path, '/contact/enquiry/submit';
+ $mech->content_contains("Thank you for your enquiry");
+
+ is $problems->count, 1, 'problem created';
+ my $problem = $problems->first;
+ is $problem->category, 'FOI Request', 'problem has correct category';
+ is $problem->detail, 'This is a general enquiry', 'problem has correct detail';
+ is $problem->non_public, 1, 'problem created non_public';
+ is $problem->postcode, '';
+ is $problem->used_map, 0;
+ is $problem->latitude, 51.469, 'Problem has correct latitude';
+ is $problem->longitude, -0.35, 'Problem has correct longitude';
+ ok $problem->confirmed, 'problem confirmed';
+ is $problem->name, 'Test User', 'Report created with correct name';
+ is $problem->user->name, 'Test User', 'User name updated in DB';
+ is $problem->user->email, 'testuser@example.org', 'Report user has correct email';
+
+ $mech->log_out_ok;
+ };
+
+ subtest 'User name not changed if logged out when making report' => sub {
+ my $problems = FixMyStreet::App->model('DB::Problem')->to_body( $body->id );
+ my $user = $problems->first->user;
+ $problems->delete_all;
+
+ is $user->name, 'Test User', 'User has correct name';
+
+ $mech->get_ok( '/contact/enquiry' );
+ $mech->submit_form_ok( {
+ with_fields => {
+ name => 'Simon Neil',
+ username => 'testuser@example.org',
+ category => 'General Enquiry',
+ detail => 'This is a general enquiry',
+ }
+ } );
+ is $mech->uri->path, '/contact/enquiry/submit';
+ $mech->content_contains("Thank you for your enquiry");
+
+ is $problems->count, 1, 'problem created';
+ my $problem = $problems->first;
+ is $problem->category, 'General Enquiry', 'problem has correct category';
+ is $problem->detail, 'This is a general enquiry', 'problem has correct detail';
+ is $problem->non_public, 1, 'problem created non_public';
+ is $problem->postcode, '';
+ is $problem->used_map, 0;
+ is $problem->latitude, 51.469, 'Problem has correct latitude';
+ is $problem->longitude, -0.35, 'Problem has correct longitude';
+ ok $problem->confirmed, 'problem confirmed';
+ is $problem->name, 'Simon Neil', 'Report created with correct name';
+ is $problem->user->email, 'testuser@example.org', 'Report user has correct email';
+ $user->discard_changes;
+ is $user->name, 'Test User', 'User name in DB not changed';
+
+ $mech->log_out_ok;
+ };
+
+};
+
+my $sample_jpeg = path(__FILE__)->parent->child("sample.jpg");
+ok $sample_jpeg->exists, "sample image $sample_jpeg exists";
+my $sample_pdf = path(__FILE__)->parent->child("sample.pdf");
+ok $sample_pdf->exists, "sample PDF $sample_pdf exists";
+
+subtest "Check photo & file upload works" => sub {
+ my $UPLOAD_DIR = tempdir( CLEANUP => 1 );
+
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'hounslow' ],
+ MAPIT_URL => 'http://mapit.uk/',
+ PHOTO_STORAGE_BACKEND => 'FileSystem',
+ PHOTO_STORAGE_OPTIONS => {
+ UPLOAD_DIR => $UPLOAD_DIR,
+ },
+ }, sub {
+ my $problems = FixMyStreet::App->model('DB::Problem')->to_body( $body->id );
+ $problems->delete_all;
+
+
+ $mech->get_ok('/contact/enquiry');
+ my ($csrf) = $mech->content =~ /name="token" value="([^"]*)"/;
+
+ $mech->post( '/contact/enquiry/submit',
+ Content_Type => 'form-data',
+ Content =>
+ {
+ submit_problem => 1,
+ token => $csrf,
+ name => 'Test User',
+ username => 'testuser@example.org',
+ category => 'Other',
+ detail => 'This is a general enquiry',
+ photo1 => [ $sample_jpeg, undef, Content_Type => 'image/jpeg' ],
+ photo2 => [ $sample_pdf, undef, Content_Type => 'application/pdf' ],
+ }
+ );
+ ok $mech->success, 'Made request with two files uploaded';
+
+ is $problems->count, 1, 'problem created';
+ my $problem = $problems->first;
+ is $problem->detail, 'This is a general enquiry', 'problem has correct detail';
+ is $problem->non_public, 1, 'problem created non_public';
+ is $problem->postcode, '';
+ is $problem->used_map, 0;
+ is $problem->latitude, 51.469, 'Problem has correct latitude';
+ is $problem->longitude, -0.35, 'Problem has correct longitude';
+ ok $problem->confirmed, 'problem confirmed';
+
+ my $image_file = path($UPLOAD_DIR, '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg');
+ ok $image_file->exists, 'Photo uploaded to temp';
+
+ my $photoset = $problem->get_photoset();
+ is $photoset->num_images, 1, 'Found just 1 image';
+ is $photoset->data, '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg';
+
+ my $pdf_file = path($UPLOAD_DIR, 'enquiry_files', '90f7a64043fb458d58de1a0703a6355e2856b15e.pdf');
+ ok $pdf_file->exists, 'PDF uploaded to temp';
+
+ is_deeply $problem->get_extra_metadata('enquiry_files'), {
+ '90f7a64043fb458d58de1a0703a6355e2856b15e.pdf' => 'sample.pdf'
+ }, 'enquiry file info stored OK';
+
+ };
+};
+done_testing();
diff --git a/t/app/controller/sample.pdf b/t/app/controller/sample.pdf
new file mode 100644
index 000000000..9ac5be44b
--- /dev/null
+++ b/t/app/controller/sample.pdf
Binary files differ
diff --git a/t/cobrand/hounslow.t b/t/cobrand/hounslow.t
new file mode 100644
index 000000000..5d9f022e7
--- /dev/null
+++ b/t/cobrand/hounslow.t
@@ -0,0 +1,44 @@
+use FixMyStreet::TestMech;
+
+ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' );
+
+my $hounslow_id = $mech->create_body_ok(2483, 'Hounslow Borough Council')->id;
+
+$mech->create_problems_for_body(1, $hounslow_id, 'An old problem made before Hounslow FMS launched', {
+ confirmed => '2018-12-25 09:00',
+ lastupdate => '2018-12-25 09:00',
+});
+$mech->create_problems_for_body(1, $hounslow_id, 'A brand new problem made on the Hounslow site', {
+ cobrand => 'hounslow'
+});
+$mech->create_problems_for_body(1, $hounslow_id, 'A brand new problem made on fixmystreet.com', {
+ cobrand => 'fixmystreet'
+});
+
+subtest "it still shows old reports on fixmystreet.com" => sub {
+ FixMyStreet::override_config {
+ MAPIT_URL => 'http://mapit.uk/',
+ ALLOWED_COBRANDS => 'fixmystreet',
+ }, sub {
+ $mech->get_ok('/reports/Hounslow');
+
+ $mech->content_contains('An old problem made before Hounslow FMS launched');
+ $mech->content_contains('A brand new problem made on the Hounslow site');
+ $mech->content_contains('A brand new problem made on fixmystreet.com');
+ };
+};
+
+subtest "it does not show old reports on Hounslow" => sub {
+ FixMyStreet::override_config {
+ MAPIT_URL => 'http://mapit.uk/',
+ ALLOWED_COBRANDS => 'hounslow',
+ }, sub {
+ $mech->get_ok('/reports/Hounslow');
+
+ $mech->content_lacks('An old problem made before Hounslow FMS launched');
+ $mech->content_contains('A brand new problem made on the Hounslow site') or diag $mech->content;
+ $mech->content_contains('A brand new problem made on fixmystreet.com');
+ };
+};
+
+done_testing();
diff --git a/templates/web/base/contact/enquiry/index.html b/templates/web/base/contact/enquiry/index.html
new file mode 100644
index 000000000..8b8bba1b7
--- /dev/null
+++ b/templates/web/base/contact/enquiry/index.html
@@ -0,0 +1,79 @@
+[% INCLUDE 'header.html',
+ bodyclass = 'twothirdswidthpage',
+ title = loc('General Enquiry')
+%]
+
+<form method="post" action="/contact/enquiry/submit" class="validate" enctype="multipart/form-data">
+ <input type="hidden" name="token" value="[% csrf_token %]">
+ <input type="hidden" name="submit_problem" value="1">
+ <fieldset>
+ [% INCLUDE 'errors.html' %]
+
+ <label for="form_name">[% loc('Your name') %]</label>
+ [% IF field_errors.name %]
+ <div class="form-error">[% field_errors.name %]</div>
+ [% END %]
+ <input type="text" class="form-control required" name="name" id="form_name" value="[% ( form_name OR c.user.name ) | html %]" size="30">
+
+ <label for="form_email">[% loc('Your email') %]</label>
+ [% IF field_errors.username %]
+ <div class="form-error">[% field_errors.username %]</div>
+ [% END %]
+ <input type="text" class="form-control required email" name="username" id="form_email" [% "disabled" IF c.user.email %] value="[% ( email OR c.user.email ) | html %]" size="30">
+
+ <div class="form-group">
+ <label for="form_phone">[% loc('Your phone number') %]</label>
+ <span class="required-text required-text--optional">optional</span>
+ <input type="text" class="form-control extra.phone" name="extra.phone" id="form_phone" value="[% report.user.phone | html %]" size="30">
+ </div>
+
+ <label for="form_category">[% loc('Subject') %]</label>
+ [% IF field_errors.category %]
+ <div class="form-error">[% field_errors.category %]</div>
+ [% END %]
+ <select class="form-control required" name="category" id="category">
+ <option value="">[% loc('-- Please select --') %]</option>
+ [% FOREACH contact IN contacts %]
+ <option value="[% contact.category | html %]" [% "selected" IF report.category == contact.category %]>[% contact.category | html %]</option>
+ [% END %]
+ </select>
+
+ <label for="form_detail">[% loc('Message') %]</label>
+ [% IF field_errors.detail %]
+ <div class="form-error">[% field_errors.detail %]</div>
+ [% END %]
+ <textarea class="form-control required" name="detail" id="form_detail" rows="7" cols="50">[% report.detail | html %]</textarea>
+
+ <input type="hidden" name="upload_fileid" value="[% upload_fileid %]">
+ <label for="form_photo">
+ [% loc('Photos/Documents') %]
+ </label>
+
+ [% IF field_errors.photo %]
+ <p class='form-error'>[% field_errors.photo %]</p>
+ [% END %]
+
+ <div id="form_photos">
+ [% IF upload_fileid OR report.get_extra_metadata('enquiry_files').keys.count %]
+ <p>[% loc('You have already attached files to this report. Note that you can attach a maximum of 3 to this report (if you try to upload more, the oldest will be removed).') %]</p>
+ [% FOREACH id IN upload_fileid.split(',') %]
+ <img align="right" src="/photo/temp.[% id %]" alt="">
+ [% END %]
+
+ [% IF report.get_extra_metadata('enquiry_files') %]
+ [% FOREACH id IN report.get_extra_metadata('enquiry_files').values %]
+ [% id %]<br />
+ [% END %]
+ <input type="hidden" name="enquiry_files" value="[% enquiry_files | html %]" />
+ [% END %]
+ [% END %]
+ <p><input type="file" name="photo1" id="form_photo"></p>
+ <p><input type="file" name="photo2" id="form_photo2"></p>
+ <p><input type="file" name="photo3" id="form_photo3"></p>
+ </div>
+
+ <input class="final-submit green-btn" type="submit" value="[% loc('Send') %]">
+
+ </fieldset></form>
+
+[% INCLUDE 'footer.html' pagefooter = 'yes' %]
diff --git a/templates/web/base/contact/enquiry/submit.html b/templates/web/base/contact/enquiry/submit.html
new file mode 100644
index 000000000..050fd31a9
--- /dev/null
+++ b/templates/web/base/contact/enquiry/submit.html
@@ -0,0 +1 @@
+[% INCLUDE 'contact/submit.html' %]
diff --git a/templates/web/hounslow/main_nav_items.html b/templates/web/hounslow/main_nav_items.html
new file mode 100644
index 000000000..f333e08f3
--- /dev/null
+++ b/templates/web/hounslow/main_nav_items.html
@@ -0,0 +1,58 @@
+[%~ IF problem ~%]
+ [%~ INCLUDE navitem uri='/report/new?longitude=' _ problem.longitude _ '&amp;latitude=' _ problem.latitude label=loc('Report another problem here') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSIF latitude AND longitude ~%]
+ [%~ INCLUDE navitem uri='/report/new?longitude=' _ longitude _ '&amp;latitude=' _ latitude label=loc('Report a problem here') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSIF homepage_template ~%]
+ [%~ INCLUDE navitem uri='/report' label=loc('Report a problem') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSE ~%]
+ [%~ INCLUDE navitem uri='/' label=loc('Report a problem') attrs='class="report-a-problem-btn"' ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists ~%]
+ [%~ INCLUDE navitem uri='/my' label=loc('Your account') ~%]
+[%~ ELSE ~%]
+ [%~ INCLUDE navitem uri='/auth' label=loc('Sign in') ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists AND c.user.has_body_permission_to('planned_reports') ~%]
+ [%~ INCLUDE navitem always_url=1 uri='/my/planned' label=loc('Shortlist') ~%]
+[%~ END ~%]
+
+
+[%~ UNLESS hide_all_reports_link ~%]
+ [%~
+ IF c.user_exists AND c.user.categories.size;
+ categories = c.user.categories_string | uri;
+ cat_suffix = "?filter_category=" _ categories;
+ END;
+
+ reports_uri = '/reports';
+ IF body_name;
+ body_name = body_name | uri;
+ reports_uri = "${reports_uri}/${body_name}";
+ END;
+
+ INCLUDE navitem uri=reports_uri label=loc('All reports') suffix=cat_suffix;
+ ~%]
+[%~ END ~%]
+
+[%~
+ IF pc;
+ pc_uri = pc | uri;
+ pc_suffix = "/list?pc=" _ pc_uri;
+ END;
+
+ INCLUDE navitem uri='/alert' label=loc('Local alerts') suffix=pc_suffix;
+~%]
+
+[%~ INCLUDE navitem uri='/contact/enquiry' label=loc('Contact') ~%]
+
+[%~ INCLUDE navitem uri='/faq' label=loc('Help') ~%]
+
+[%~ UNLESS hide_privacy_link ~%]
+ [%~ INCLUDE navitem uri=c.cobrand.privacy_policy_url label=loc('Privacy') liattrs='class="nav-menu__item--privacy"' ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists AND c.cobrand.admin_allow_user(c.user) ~%]
+ [%~ INCLUDE navitem uri='/admin' label=loc('Admin') ~%]
+[%~ END ~%]
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index 11e9045ab..d71217ad0 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -1196,6 +1196,11 @@ fixmystreet.fetch_reporting_data = function() {
fixmystreet.reporting_data = data;
+ fixmystreet.bodies = data.bodies || [];
+ if (fixmystreet.body_overrides) {
+ fixmystreet.body_overrides.clear();
+ }
+
fixmystreet.update_councils_text(data);
$('#js-top-message').html(data.top_message || '');
@@ -1218,11 +1223,6 @@ fixmystreet.fetch_reporting_data = function() {
lb.before(data.extra_name_info);
}
- fixmystreet.bodies = data.bodies || [];
- if (fixmystreet.body_overrides) {
- fixmystreet.body_overrides.clear();
- }
-
if (data.contribute_as) {
var $select = $('.js-contribute-as');
if (!$select.data('original')) {