aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/fixmystreet.com/fixture39
-rw-r--r--conf/general.yml-example3
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm43
-rw-r--r--perllib/FixMyStreet/App/Controller/Moderate.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm4
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm6
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm6
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm6
-rw-r--r--perllib/FixMyStreet/Cobrand/FiksGataMi.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/FixaMinGata.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/UKCouncils.pm4
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm28
-rw-r--r--perllib/FixMyStreet/Script/Reports.pm11
-rw-r--r--perllib/Utils.pm17
-rw-r--r--t/app/controller/admin.t1
-rw-r--r--t/app/sendreport/inspection_required.t20
-rw-r--r--t/utils.t36
-rw-r--r--t/utils/email.t34
-rw-r--r--templates/web/base/admin/user-form.html25
-rw-r--r--templates/web/base/report/_main.html6
-rw-r--r--templates/web/base/report/new/after_photo.html3
-rw-r--r--templates/web/base/report/new/form_user_loggedin.html4
-rw-r--r--templates/web/base/report/update.html2
-rw-r--r--templates/web/base/report/update/form_name.html4
-rw-r--r--templates/web/zurich/maps/noscript_map.html30
25 files changed, 289 insertions, 49 deletions
diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture
new file mode 100755
index 000000000..93982af8a
--- /dev/null
+++ b/bin/fixmystreet.com/fixture
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+#
+# This script will create a test body and its categories, covering the area of
+# Westminster, and a user associated with that body, which should help testing
+# of report interactions.
+
+use strict;
+use warnings;
+
+BEGIN {
+ use File::Basename qw(dirname);
+ use File::Spec;
+ my $d = dirname(File::Spec->rel2abs($0));
+ require "$d/../../setenv.pl";
+}
+
+use FixMyStreet::DB;
+
+my $body = FixMyStreet::DB->resultset("Body")->find_or_create({ name => 'Test City Council' });
+$body->body_areas->find_or_create({ area_id => 2504 });
+foreach ("Potholes", "Street lighting", "Graffiti") {
+ (my $email = lc $_) =~ s/ /-/g;
+ $body->contacts->find_or_create({
+ category => $_,
+ email => $email . '@example.net',
+ confirmed => 't',
+ deleted => 'f',
+ whenedited => \'current_timestamp',
+ editor => 'fixture',
+ note => 'Created by fixture'
+ });
+}
+
+FixMyStreet::DB->resultset("User")->find_or_create({
+ email => 'council@example.net',
+ name => 'Test City Council User',
+ from_body => $body,
+ password => 'password',
+});
diff --git a/conf/general.yml-example b/conf/general.yml-example
index 54bbd6a7f..3b2c597b9 100644
--- a/conf/general.yml-example
+++ b/conf/general.yml-example
@@ -46,6 +46,9 @@ STAGING_SITE: 1
# reports to live places. Set this to 1 if you want a dev site to route
# reports as normal.
SEND_REPORTS_ON_STAGING: 0
+# Manual testing of multiple cobrands can be made easier by skipping some
+# checks they have in them, if this variable is set
+SKIP_CHECKS_ON_STAGING: 0
# What to use as front page/alert example places placeholder
# Defaults to High Street, Main Street
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 0a7b8d049..46ac10d36 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -345,8 +345,6 @@ sub update_contacts : Private {
my $category = $self->trim( $c->get_param('category') );
$errors{category} = _("Please choose a category") unless $category;
- my $email = $self->trim( $c->get_param('email') );
- $errors{email} = _('Please enter a valid email') unless is_valid_email($email) || $email eq 'REFUSED';
$errors{note} = _('Please enter a message') unless $c->get_param('note');
my $contact = $c->model('DB::Contact')->find_or_new(
@@ -356,6 +354,12 @@ sub update_contacts : Private {
}
);
+ my $email = $self->trim( $c->get_param('email') );
+ my $send_method = $c->get_param('send_method') || $contact->send_method || $contact->body->send_method || "";
+ unless ( $send_method eq 'Open311' ) {
+ $errors{email} = _('Please enter a valid email') unless is_valid_email($email) || $email eq 'REFUSED';
+ }
+
$contact->email( $email );
$contact->confirmed( $c->get_param('confirmed') ? 1 : 0 );
$contact->deleted( $c->get_param('deleted') ? 1 : 0 );
@@ -683,7 +687,7 @@ sub report_edit : Path('report_edit') : Args(1) {
unless (
$c->cobrand->moniker eq 'zurich'
- || $c->user->has_permission_to(report_edit => $problem->bodies_str)
+ || $c->user->has_permission_to(report_edit => $problem->bodies_str_ids)
) {
$c->detach( '/page_error_403_access_denied', [] );
}
@@ -1272,14 +1276,14 @@ sub user_edit : Path('user_edit') : Args(1) {
if (!$user->from_body) {
# Non-staff users aren't allowed any permissions or to be in an area
- $user->user_body_permissions->delete_all;
+ $user->admin_user_body_permissions->delete;
$user->area_id(undef);
delete $c->stash->{areas};
delete $c->stash->{fetched_areas_body_id};
} elsif ($c->stash->{available_permissions}) {
my @all_permissions = map { keys %$_ } values %{ $c->stash->{available_permissions} };
my @user_permissions = grep { $c->get_param("permissions[$_]") ? 1 : undef } @all_permissions;
- $user->user_body_permissions->search({
+ $user->admin_user_body_permissions->search({
body_id => $user->from_body->id,
permission_type => { '!=' => \@user_permissions },
})->delete;
@@ -1297,6 +1301,35 @@ sub user_edit : Path('user_edit') : Args(1) {
$user->area_id( $valid_areas{$new_area} ? $new_area : undef );
}
+ # Handle 'trusted' flag(s)
+ my @trusted_bodies = $c->get_param_list('trusted_bodies');
+ if ( $c->user->is_superuser ) {
+ $user->user_body_permissions->search({
+ body_id => { '!=' => \@trusted_bodies },
+ permission_type => 'trusted',
+ })->delete;
+ foreach my $body_id (@trusted_bodies) {
+ $user->user_body_permissions->find_or_create({
+ body_id => $body_id,
+ permission_type => 'trusted',
+ });
+ }
+ } elsif ( $c->user->from_body ) {
+ my %trusted = map { $_ => 1 } @trusted_bodies;
+ my $body_id = $c->user->from_body->id;
+ if ( $trusted{$body_id} ) {
+ $user->user_body_permissions->find_or_create({
+ body_id => $body_id,
+ permission_type => 'trusted',
+ });
+ } else {
+ $user->user_body_permissions->search({
+ body_id => $body_id,
+ permission_type => 'trusted',
+ })->delete;
+ }
+ }
+
unless ($user->email) {
$c->stash->{field_errors}->{email} = _('Please enter a valid email');
return;
diff --git a/perllib/FixMyStreet/App/Controller/Moderate.pm b/perllib/FixMyStreet/App/Controller/Moderate.pm
index dadec5c53..94e6cd62a 100644
--- a/perllib/FixMyStreet/App/Controller/Moderate.pm
+++ b/perllib/FixMyStreet/App/Controller/Moderate.pm
@@ -54,7 +54,7 @@ sub report : Chained('moderate') : PathPart('report') : CaptureArgs(1) {
# ... and immediately, if the user isn't authorized
$c->detach unless $c->user_exists;
- $c->detach unless $c->user->has_permission_to(moderate => $problem->bodies_str);
+ $c->detach unless $c->user->has_permission_to(moderate => $problem->bodies_str_ids);
$c->forward('/auth/check_csrf_token');
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index 1d67afd0e..34392782b 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -133,7 +133,7 @@ sub load_problem_or_display_error : Private {
}
$c->stash->{problem} = $problem;
- if ( $c->user_exists && $c->user->has_permission_to(moderate => $problem->bodies_str) ) {
+ if ( $c->user_exists && $c->user->has_permission_to(moderate => $problem->bodies_str_ids) ) {
$c->stash->{problem_original} = $problem->find_or_new_related(
moderation_original_data => {
title => $problem->title,
@@ -401,7 +401,7 @@ to the current Problem in $c->stash->{problem}. Shows the 403 page if not.
sub check_has_permission_to : Private {
my ( $self, $c, @permissions ) = @_;
- my $bodies = $c->stash->{problem}->bodies_str;
+ my $bodies = $c->stash->{problem}->bodies_str_ids;
my %permissions = map { $_ => $c->user->has_permission_to($_, $bodies) } @permissions
if $c->user_exists;
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 7b012e0a1..267f1059d 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -188,9 +188,9 @@ sub report_form_ajax : Path('ajax') : Args(0) {
my $contribute_as = {};
if ($c->user_exists) {
- my $bodies = join(',', keys %{$c->stash->{bodies}});
- my $ca_another_user = $c->user->has_permission_to('contribute_as_another_user', $bodies);
- my $ca_body = $c->user->has_permission_to('contribute_as_body', $bodies);
+ my @bodies = keys %{$c->stash->{bodies}};
+ my $ca_another_user = $c->user->has_permission_to('contribute_as_another_user', \@bodies);
+ my $ca_body = $c->user->has_permission_to('contribute_as_body', \@bodies);
$contribute_as->{another_user} = $ca_another_user if $ca_another_user;
$contribute_as->{body} = $ca_body if $ca_body;
}
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index 705e6ee99..4c2d92d5e 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -113,7 +113,7 @@ sub process_user : Private {
if ( $c->user_exists ) { {
my $user = $c->user->obj;
- if ($c->stash->{contributing_as_another_user} = $user->contributing_as('another_user', $c, $update->problem->bodies_str)) {
+ if ($c->stash->{contributing_as_another_user} = $user->contributing_as('another_user', $c, $update->problem->bodies_str_ids)) {
# Act as if not logged in (and it will be auto-confirmed later on)
last;
}
@@ -276,7 +276,7 @@ sub process_update : Private {
$update->mark_fixed($params{fixed} ? 1 : 0);
$update->mark_open($params{reopen} ? 1 : 0);
- $c->stash->{contributing_as_body} = $c->user_exists && $c->user->contributing_as('body', $c, $update->problem->bodies_str);
+ $c->stash->{contributing_as_body} = $c->user_exists && $c->user->contributing_as('body', $c, $update->problem->bodies_str_ids);
if ($c->stash->{contributing_as_body}) {
$update->name($c->user->from_body->name);
$update->anonymous(0);
@@ -286,7 +286,7 @@ sub process_update : Private {
}
if ( $params{state} ) {
- $params{state} = 'fixed - council'
+ $params{state} = 'fixed - council'
if $params{state} eq 'fixed' && $c->user && $c->user->belongs_to_body( $update->problem->bodies_str );
$update->problem_state( $params{state} );
} else {
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index b6d03621f..c44842dea 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -720,6 +720,12 @@ sub available_permissions {
planned_reports => _("Manage shortlist"),
contribute_as_another_user => _("Create reports/updates on a user's behalf"),
contribute_as_body => _("Create reports/updates as the council"),
+
+ # NB this permission is special in that it can be assigned to users
+ # without their from_body being set. It's included here for
+ # reference, but left commented out because it's not assigned in the
+ # same way as other permissions.
+ # trusted => _("Trusted to make reports that don't need to be inspected"),
},
_("Users") => {
user_edit => _("Edit other users' details"),
diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
index ba26b7a2c..242735073 100644
--- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
+++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
@@ -29,6 +29,8 @@ sub disambiguate_location {
}
sub area_types {
+ my $self = shift;
+ return $self->next::method() if FixMyStreet->config('STAGING_SITE') && FixMyStreet->config('SKIP_CHECKS_ON_STAGING');
[ 'NKO', 'NFY', 'NRA' ];
}
diff --git a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
index 9ffbf00b8..a321d5c7c 100644
--- a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
+++ b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
@@ -30,6 +30,8 @@ sub disambiguate_location {
}
sub area_types {
+ my $self = shift;
+ return $self->next::method() if FixMyStreet->config('STAGING_SITE') && FixMyStreet->config('SKIP_CHECKS_ON_STAGING');
[ 'KOM' ];
}
diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm
index 5d72c4962..42c9c5cbc 100644
--- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm
+++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm
@@ -42,11 +42,13 @@ sub restriction {
sub problems_restriction {
my ($self, $rs) = @_;
+ return $rs if FixMyStreet->config('STAGING_SITE') && FixMyStreet->config('SKIP_CHECKS_ON_STAGING');
return $rs->to_body($self->council_id);
}
sub updates_restriction {
my ($self, $rs) = @_;
+ return $rs if FixMyStreet->config('STAGING_SITE') && FixMyStreet->config('SKIP_CHECKS_ON_STAGING');
return $rs->to_body($self->council_id);
}
@@ -96,6 +98,8 @@ sub enter_postcode_text {
sub area_check {
my ( $self, $params, $context ) = @_;
+ return 1 if FixMyStreet->config('STAGING_SITE') && FixMyStreet->config('SKIP_CHECKS_ON_STAGING');
+
my $councils = $params->{all_areas};
my $council_match = defined $councils->{$self->council_id};
if ($council_match) {
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
index 363bbb930..2a2d0d5e3 100644
--- a/perllib/FixMyStreet/DB/Result/User.pm
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -257,15 +257,14 @@ sub permissions {
}
sub has_permission_to {
- my ($self, $permission_type, $body_id) = @_;
+ my ($self, $permission_type, $body_ids) = @_;
return 1 if $self->is_superuser;
+ return 0 unless $body_ids;
- return 0 unless $self->belongs_to_body($body_id);
-
- my $permission = $self->user_body_permissions->find({
+ my $permission = $self->user_body_permissions->find({
permission_type => $permission_type,
- body_id => $self->from_body->id,
+ body_id => $body_ids,
});
return $permission ? 1 : 0;
}
@@ -293,10 +292,25 @@ sub has_body_permission_to {
return $self->has_permission_to($permission_type, $self->from_body->id);
}
+=head2 admin_user_body_permissions
+
+Some permissions aren't managed in the normal way via the admin, e.g. the
+'trusted' permission. This method returns a query that excludes such exceptional
+permissions.
+
+=cut
+
+sub admin_user_body_permissions {
+ my $self = shift;
+
+ return $self->user_body_permissions->search({
+ permission_type => { '!=' => 'trusted' },
+ });
+}
+
sub contributing_as {
my ($self, $other, $c, $bodies) = @_;
- $bodies = join(',', keys %$bodies) if ref $bodies eq 'HASH';
- $c->log->error("Bad data $bodies passed to contributing_as") if ref $bodies;
+ $bodies = [ keys %$bodies ] if ref $bodies eq 'HASH';
my $form_as = $c->get_param('form_as') || '';
return 1 if $form_as eq $other && $self->has_permission_to("contribute_as_$other", $bodies);
}
diff --git a/perllib/FixMyStreet/Script/Reports.pm b/perllib/FixMyStreet/Script/Reports.pm
index ab0d90ba8..8d3b2ddbc 100644
--- a/perllib/FixMyStreet/Script/Reports.pm
+++ b/perllib/FixMyStreet/Script/Reports.pm
@@ -144,9 +144,14 @@ sub send(;$) {
$reporters{ $sender } ||= $sender->new();
my $inspection_required = $sender_info->{contact}->get_extra_metadata('inspection_required') if $sender_info->{contact};
- if ( $inspection_required && !$row->get_extra_metadata('inspected') ) {
- $skip = 1;
- debug_print("skipped because not yet inspected", $row->id) if $debug_mode;
+ if ( $inspection_required ) {
+ unless (
+ $row->get_extra_metadata('inspected') ||
+ $row->user->has_permission_to( trusted => $row->bodies_str_ids )
+ ) {
+ $skip = 1;
+ debug_print("skipped because not yet inspected", $row->id) if $debug_mode;
+ }
}
if ( $reporters{ $sender }->should_skip( $row ) ) {
diff --git a/perllib/Utils.pm b/perllib/Utils.pm
index 87c1a10d6..84c09d09d 100644
--- a/perllib/Utils.pm
+++ b/perllib/Utils.pm
@@ -15,6 +15,7 @@ use Encode;
use File::Slurp qw();
use mySociety::GeoUtil;
use mySociety::Locale;
+use FixMyStreet;
=head2 convert_latlon_to_en
@@ -199,7 +200,7 @@ sub prettify_duration {
$s = int(($s+60*60*12)/60/60/24)*60*60*24;
} elsif ($nearest eq 'hour') {
$s = int(($s+60*30)/60/60)*60*60;
- } elsif ($nearest eq 'minute') {
+ } else { # minute
$s = int(($s+30)/60)*60;
return _('less than a minute') if $s == 0;
}
@@ -221,7 +222,7 @@ sub _part {
$str = mySociety::Locale::nget("%d day", "%d days", $i);
} elsif ($m == 60*60) {
$str = mySociety::Locale::nget("%d hour", "%d hours", $i);
- } elsif ($m == 60) {
+ } else {
$str = mySociety::Locale::nget("%d minute", "%d minutes", $i);
}
push @$o, sprintf($str, $i);
@@ -229,17 +230,5 @@ sub _part {
}
}
-=head2 read_file
-
-Reads in a UTF-8 encoded file using File::Slurp and decodes it from UTF-8.
-This appears simplest, rather than getting confused with binmodes and so on.
-
-=cut
-sub read_file {
- my $filename = shift;
- my $data = File::Slurp::read_file( $filename );
- $data = Encode::decode( 'utf8', $data );
- return $data;
-}
1;
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t
index 8c3cde4b7..7ba84b652 100644
--- a/t/app/controller/admin.t
+++ b/t/app/controller/admin.t
@@ -1165,6 +1165,7 @@ my %default_perms = (
"permissions[template_edit]" => undef,
"permissions[responsepriority_edit]" => undef,
"permissions[category_edit]" => undef,
+ trusted_bodies => undef,
);
FixMyStreet::override_config {
diff --git a/t/app/sendreport/inspection_required.t b/t/app/sendreport/inspection_required.t
index 178fa2a1f..88a48e991 100644
--- a/t/app/sendreport/inspection_required.t
+++ b/t/app/sendreport/inspection_required.t
@@ -52,8 +52,28 @@ subtest 'Report is sent when inspected' => sub {
ok $report->whensent, 'Report marked as sent';
};
+subtest 'Uninspected report is sent when made by trusted user' => sub {
+ $mech->clear_emails_ok;
+ $report->unset_extra_metadata('inspected');
+ $report->whensent( undef );
+ $report->update;
+
+ $user->user_body_permissions->find_or_create({
+ body => $body,
+ permission_type => 'trusted',
+ });
+ ok $user->has_permission_to('trusted', $report->bodies_str_ids), 'User can make trusted reports';
+
+ FixMyStreet::DB->resultset('Problem')->send_reports();
+
+ $report->discard_changes;
+ $mech->email_count_is( 1 );
+ ok $report->whensent, 'Report marked as sent';
+};
+
done_testing();
END {
+ $mech->delete_user($user);
$mech->delete_body($body);
}
diff --git a/t/utils.t b/t/utils.t
index 29759cddc..ac9eb1a4a 100644
--- a/t/utils.t
+++ b/t/utils.t
@@ -4,6 +4,9 @@ use strict;
use warnings;
use Test::More;
+use mySociety::Locale;
+mySociety::Locale::gettext_domain('FixMyStreet');
+
use Utils;
my @truncate_tests = (
@@ -34,9 +37,14 @@ foreach my $test (@convert_en_to_latlon_tests) {
[ Utils::convert_en_to_latlon_truncated( $e, $n ) ], #
[ $lat, $lon ], #
"convert ($e,$n) to ($lat,$lon)";
+ is_deeply
+ [ Utils::convert_latlon_to_en( $lat, $lon ) ],
+ [ $e, $n ],
+ "convert ($lat,$lon) to ($e,$n)";
}
my @cleanup_tests = (
+ [ '', '', '' ],
[ 'dog shit', 'Dog poo', 'dog poo' ],
[ 'dog shit', 'Dog poo', 'with spaces' ],
[ 'dog shite', 'Dog poo', 'with extra e' ],
@@ -57,4 +65,32 @@ foreach my $test ( @cleanup_tests ) {
is Utils::cleanup_text( "This has new\n\n\nlines in it", { allow_multiline => 1 } ), "This has new\n\nLines in it", 'new lines allowed';
+
+is Utils::prettify_dt(), "[unknown time]";
+my $dt = DateTime->now;
+is Utils::prettify_dt($dt), $dt->strftime("%H:%M today");
+
+# Same week test
+if ($dt->day_of_week == 7) { # Sunday
+ $dt = DateTime->now->add(days => 1);
+} else {
+ $dt = DateTime->now->subtract(days => 1);
+}
+is Utils::prettify_dt($dt), $dt->strftime("%H:%M, %A");
+
+$dt = DateTime->now->subtract(days => 100);
+is Utils::prettify_dt($dt), $dt->strftime("%H:%M, %A %e %B %Y");
+is Utils::prettify_dt($dt, "date"), $dt->strftime("%A %e %B %Y");
+is Utils::prettify_dt($dt, "zurich"), $dt->strftime("%H:%M, %e. %B %Y");
+is Utils::prettify_dt($dt, "short"), $dt->strftime("%H:%M, %e %b %Y");
+is Utils::prettify_dt($dt, 1), $dt->strftime("%H:%M, %e %b %Y");
+$dt = DateTime->now->subtract(days => 400);
+is Utils::prettify_dt($dt), $dt->strftime("%H:%M, %a %e %B %Y");
+
+is Utils::prettify_duration(7*86400+3600+60+1, 'week'), '1 week';
+is Utils::prettify_duration(86400+3600+60+1, 'day'), '1 day';
+is Utils::prettify_duration(86400+3600+60+1, 'hour'), '1 day, 1 hour';
+is Utils::prettify_duration(86400+3600+60+1, 'minute'), '1 day, 1 hour, 1 minute';
+is Utils::prettify_duration(20, 'minute'), 'less than a minute';
+
done_testing();
diff --git a/t/utils/email.t b/t/utils/email.t
new file mode 100644
index 000000000..23814c1d7
--- /dev/null
+++ b/t/utils/email.t
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More;
+use Test::MockModule;
+
+use Utils::Email;
+
+my $resolver = Test::MockModule->new('Net::DNS::Resolver');
+$resolver->mock('send', sub {
+ my ($self, $domain, $type) = @_;
+ my @rrs;
+ is $type, 'TXT';
+ if ($domain eq '_dmarc.yahoo.com') {
+ @rrs = (
+ Net::DNS::RR->new(name => '_dmarc.yahoo.com', type => 'TXT', txtdata => 'p=reject'),
+ Net::DNS::RR->new(name => '_dmarc.yahoo.com', type => 'A'),
+ );
+ } elsif ($domain eq 'cname.example.com') {
+ @rrs = Net::DNS::RR->new(name => 'cname.example.com', type => 'TXT', txtdata => 'p=none');
+ } else {
+ @rrs = Net::DNS::RR->new(name => '_dmarc.example.net', type => 'CNAME', cname => 'cname.example.com');
+ }
+ my $pkt = Net::DNS::Packet->new;
+ push @{$pkt->{answer}}, @rrs;
+ return $pkt;
+});
+
+is Utils::Email::test_dmarc('BAD'), undef;
+is Utils::Email::test_dmarc('test@yahoo.com'), 1;
+is Utils::Email::test_dmarc('test@example.net'), undef;
+
+done_testing();
diff --git a/templates/web/base/admin/user-form.html b/templates/web/base/admin/user-form.html
index 38191e095..40e0b510a 100644
--- a/templates/web/base/admin/user-form.html
+++ b/templates/web/base/admin/user-form.html
@@ -85,10 +85,33 @@
[% loc("You can add an abusive user's email to the abuse list, which automatically hides (and never sends) reports they create.") %]
</p>
</div>
-
+
[% loc('Flagged:') %] <input type="checkbox" id="flagged" name="flagged"[% user.flagged ? ' checked' : '' %]>
</li>
+ [% UNLESS user.is_superuser %]
+ <li>
+ <div class="admin-hint">
+ <p>
+ [% loc("Reports made by trusted users will be sent to the responsible body without being inspected first.") %]
+ </p>
+ </div>
+ [% IF c.user.is_superuser %]
+ [% loc('Trusted by bodies:') %]<br />
+ <select id='body' name='trusted_bodies' multiple>
+ [% FOR body IN bodies %]
+ <option value="[% body.id %]"[% ' selected' IF user.has_permission_to('trusted', body.id) %]>[% body.name %]</option>
+ [% END %]
+ </select>
+ [% ELSE %]
+ <label>
+ [% loc('Trusted:') %]
+ <input type="checkbox" id="trusted_bodies" name="trusted_bodies" value="[% c.user.from_body.id %]" [% 'checked' IF user.has_permission_to('trusted', c.user.from_body.id) %]>
+ </label>
+ [% END %]
+ </li>
+ [% END %]
+
[% IF c.user.is_superuser %]
<li>
<div class="admin-hint">
diff --git a/templates/web/base/report/_main.html b/templates/web/base/report/_main.html
index e475eb3aa..a85bca08f 100644
--- a/templates/web/base/report/_main.html
+++ b/templates/web/base/report/_main.html
@@ -7,7 +7,7 @@
<div class="problem-header clearfix" problem-id="[% problem.id %]">
-[% IF c.user.has_permission_to('planned_reports', problem.bodies_str) %]
+[% IF c.user.has_permission_to('planned_reports', problem.bodies_str_ids) %]
<form method="post" action="/my/planned/change" id="planned_form" class="hidden-label-target">
<input type="hidden" name="id" value="[% problem.id %]">
<input type="hidden" name="token" value="[% csrf_token %]">
@@ -122,7 +122,7 @@
(!hide_inspect_button AND
permissions.keys.grep('report_inspect|report_edit_category|report_edit_priority').size)
OR
- (c.user.has_permission_to('planned_reports', problem.bodies_str))
+ (c.user.has_permission_to('planned_reports', problem.bodies_str_ids))
%]
<div class="moderate-display segmented-control" role="menu">
[% IF permissions.moderate %]
@@ -137,7 +137,7 @@
[%~ END ~%]
</a>
[% END %]
- [% IF c.user.has_permission_to('planned_reports', problem.bodies_str) %]
+ [% IF c.user.has_permission_to('planned_reports', problem.bodies_str_ids) %]
[%~ IF c.user.is_planned_report(problem) ~%]
<label for="shortlist-report" role="menuitem" aria-label="[% loc('Remove from shortlist') %]">[% loc('Shortlisted') %]</label>
[%~ ELSE ~%]
diff --git a/templates/web/base/report/new/after_photo.html b/templates/web/base/report/new/after_photo.html
index 3fed031a8..fb07ed056 100644
--- a/templates/web/base/report/new/after_photo.html
+++ b/templates/web/base/report/new/after_photo.html
@@ -1,8 +1,7 @@
<div class="description_tips">
<h4>[% loc('Tips for perfect photos') %]</h4>
<ul class="do">
- <li>[% loc('Show what the problem is') %]</li>
- <li>[% loc('Show where it’s located') %]</li>
+ <li>[% loc('For best results include a close-up and a wide shot') %]</li>
</ul>
<ul class="dont">
<li>[% loc('Avoid personal information and vehicle number plates') %]</li>
diff --git a/templates/web/base/report/new/form_user_loggedin.html b/templates/web/base/report/new/form_user_loggedin.html
index add4fdbd3..34dd4979c 100644
--- a/templates/web/base/report/new/form_user_loggedin.html
+++ b/templates/web/base/report/new/form_user_loggedin.html
@@ -5,8 +5,8 @@
[% INCLUDE form_as %]
</div>
[% ELSE %]
- [% can_contribute_as_another_user = c.user.has_permission_to("contribute_as_another_user", bodies.keys.join(",")) %]
- [% can_contribute_as_body = c.user.from_body AND c.user.has_permission_to("contribute_as_body", bodies.keys.join(",")) %]
+ [% can_contribute_as_another_user = c.user.has_permission_to("contribute_as_another_user", bodies.keys) %]
+ [% can_contribute_as_body = c.user.from_body AND c.user.has_permission_to("contribute_as_body", bodies.keys) %]
[% IF can_contribute_as_another_user OR can_contribute_as_body %]
[% INCLUDE form_as %]
[% END %]
diff --git a/templates/web/base/report/update.html b/templates/web/base/report/update.html
index 55fdeb3b1..104e1d6f9 100644
--- a/templates/web/base/report/update.html
+++ b/templates/web/base/report/update.html
@@ -1,4 +1,4 @@
-[% moderating = c.user && c.user.has_permission_to('moderate', problem.bodies_str) %]
+[% moderating = c.user && c.user.has_permission_to('moderate', problem.bodies_str_ids) %]
[% IF loop.first %]
<section class="full-width">
diff --git a/templates/web/base/report/update/form_name.html b/templates/web/base/report/update/form_name.html
index ef8efd296..dd4c12151 100644
--- a/templates/web/base/report/update/form_name.html
+++ b/templates/web/base/report/update/form_name.html
@@ -2,8 +2,8 @@
[% PROCESS 'user/_anonymity.html' anonymous = update.anonymous %]
- [% can_contribute_as_another_user = c.user.has_permission_to("contribute_as_another_user", problem.bodies_str) %]
- [% can_contribute_as_body = c.user.from_body AND c.user.has_permission_to("contribute_as_body", problem.bodies_str) %]
+ [% can_contribute_as_another_user = c.user.has_permission_to("contribute_as_another_user", problem.bodies_str_ids) %]
+ [% can_contribute_as_body = c.user.from_body AND c.user.has_permission_to("contribute_as_body", problem.bodies_str_ids) %]
[% IF can_contribute_as_another_user OR can_contribute_as_body %]
<label for="form_as">[% loc('Provide update as') %]</label>
diff --git a/templates/web/zurich/maps/noscript_map.html b/templates/web/zurich/maps/noscript_map.html
index dcd577c52..4925f9260 100644
--- a/templates/web/zurich/maps/noscript_map.html
+++ b/templates/web/zurich/maps/noscript_map.html
@@ -1,3 +1,4 @@
+[% IF map.cols %]
<div class="noscript square-map__outer">
<div class="square-map__inner">
<div id="[% nsm_prefix %]drag">
@@ -19,6 +20,35 @@
[% INCLUDE 'maps/_compass.html' %]
</div>
</div>
+[% ELSE %]
+<div class="noscript">
+ <div id="[% nsm_prefix %]drag">
+ <[% map.img_type %]
+ alt="NW map tile" id="[% nsm_prefix %]t2.2"
+ name="tile_[% map.x_tile - 1 %].[% map.y_tile - 1 %]"
+ src="[% map.tiles.0 %]"
+ style="top:0; left:0;">
+ <[% map.img_type %]
+ alt="NE map tile" id="[% nsm_prefix %]t2.3"
+ name="tile_[% map.x_tile %].[% map.y_tile - 1 %]"
+ src="[% map.tiles.1 %]"
+ style="top:0px; left:256px;">
+ <br>
+ <[% map.img_type %]
+ alt="SW map tile" id="[% nsm_prefix %]t3.2"
+ name="tile_[% map.x_tile - 1 %].[% map.y_tile %]"
+ src="[% map.tiles.2 %]"
+ style="top:256px; left:0;">
+ <[% map.img_type %]
+ alt="SE map tile" id="[% nsm_prefix %]t3.3"
+ name="tile_[% map.x_tile %].[% map.y_tile %]"
+ src="[% map.tiles.3 %]"
+ style="top:256px; left:256px;">
+ </div>
+ <div id="[% nsm_prefix %]pins">[% FOR pin IN map.pins %][% INCLUDE 'maps/pin.html' %][% END %]</div>
+ [% INCLUDE 'maps/_compass.html' %]
+</div>
+[% END %]
[% BLOCK pin %]
[%