diff options
-rw-r--r-- | db/schema.sql | 6 | ||||
-rw-r--r-- | db/schema_0005-add_abuse_flags_to_users_and_reports.sql | 9 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 323 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Contact.pm | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Problem.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 2 | ||||
-rw-r--r-- | t/app/controller/admin.t | 195 | ||||
-rw-r--r-- | t/app/controller/contact.t | 11 | ||||
-rw-r--r-- | templates/web/default/admin/list_flagged.html | 51 | ||||
-rw-r--r-- | templates/web/default/admin/problem_row.html | 30 | ||||
-rw-r--r-- | templates/web/default/admin/report_blocks.html | 10 | ||||
-rw-r--r-- | templates/web/default/admin/report_edit.html | 3 | ||||
-rw-r--r-- | templates/web/default/admin/search_abuse.html | 21 | ||||
-rw-r--r-- | templates/web/default/admin/search_reports.html | 31 | ||||
-rw-r--r-- | templates/web/default/admin/update_edit.html | 2 |
15 files changed, 580 insertions, 120 deletions
diff --git a/db/schema.sql b/db/schema.sql index 9c5b3d8fd..53c236dd6 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -130,7 +130,8 @@ create table users ( email text not null unique, name text, phone text, - password text not null default '' + password text not null default '', + flagged boolean not null default 'f' ); -- Problems reported by users of site @@ -175,7 +176,8 @@ create table problem ( cobrand_data text not null default '' check (cobrand_data ~* '^[a-z0-9]*$'), -- Extra data used in cobranded versions of the site lastupdate timestamp not null default ms_current_timestamp(), whensent timestamp, - send_questionnaire boolean not null default 't' + send_questionnaire boolean not null default 't', + flagged boolean not null default 'f' ); create index problem_state_latitude_longitude_idx on problem(state, latitude, longitude); create index problem_user_id_idx on problem ( user_id ); diff --git a/db/schema_0005-add_abuse_flags_to_users_and_reports.sql b/db/schema_0005-add_abuse_flags_to_users_and_reports.sql new file mode 100644 index 000000000..040d3294d --- /dev/null +++ b/db/schema_0005-add_abuse_flags_to_users_and_reports.sql @@ -0,0 +1,9 @@ +begin; + +ALTER table problem + ADD column flagged BOOL NOT NULL DEFAULT 'f'; + +ALTER table users + ADD column flagged BOOL NOT NULL DEFAULT 'f'; + +commit; diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 3854e27aa..a7b0bda0f 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -448,9 +448,11 @@ sub search_reports : Path('search_reports') { } ); + # we need to pass this in as an array as we can't + # query the object in the template as the quoting + # will have been turned off $c->stash->{problems} = [ $problems->all ]; - $c->stash->{edit_council_contacts} = 1 if ( grep {$_ eq 'councilcontacts'} keys %{$c->stash->{allowed_pages}}); @@ -499,6 +501,7 @@ sub report_edit : Path('report_edit') : Args(1) { $c->forward('get_token'); $c->forward('check_page_allowed'); + $c->forward('check_email_for_abuse', [ $problem->user->email ] ); $c->stash->{updates} = [ $c->model('DB::Comment') @@ -515,6 +518,17 @@ sub report_edit : Path('report_edit') : Args(1) { $c->forward( 'log_edit', [ $id, 'problem', 'resend' ] ); } + elsif ( $c->req->param('flaguser') ) { + $c->forward('flag_user'); + $c->stash->{problem}->discard_changes; + } + elsif ( $c->req->param('removeuserflag') ) { + $c->forward('remove_user_flag'); + $c->stash->{problem}->discard_changes; + } + elsif ( $c->req->param('banuser') ) { + $c->forward('ban_user'); + } elsif ( $c->req->param('submit') ) { $c->forward('check_token'); @@ -534,12 +548,15 @@ sub report_edit : Path('report_edit') : Args(1) { $done = 1; } + my $flagged = $c->req->param('flagged') ? 1 : 0; + # do this here so before we update the values in problem if ( $c->req->param('anonymous') ne $problem->anonymous || $c->req->param('name') ne $problem->name || $c->req->param('email') ne $problem->user->email || $c->req->param('title') ne $problem->title - || $c->req->param('detail') ne $problem->detail ) + || $c->req->param('detail') ne $problem->detail + || $flagged != $problem->flagged ) { $edited = 1; } @@ -549,6 +566,7 @@ sub report_edit : Path('report_edit') : Args(1) { $problem->detail( $c->req->param('detail') ); $problem->state( $c->req->param('state') ); $problem->name( $c->req->param('name') ); + $problem->flagged( $flagged ); if ( $c->req->param('email') ne $problem->user->email ) { my $user = $c->model('DB::User')->find_or_create( @@ -596,6 +614,147 @@ sub report_edit : Path('report_edit') : Args(1) { return 1; } +sub update_edit : Path('update_edit') : Args(1) { + my ( $self, $c, $id ) = @_; + + my ( $site_res_sql, $site_key, $site_restriction ) = + $c->cobrand->site_restriction; + my $update = $c->model('DB::Comment')->search( + { + id => $id, + %{$site_restriction}, + } + )->first; + + $c->detach( '/page_error_404_not_found', + [ _('The requested URL was not found on this server.') ] ) + unless $update; + + $c->forward('get_token'); + $c->forward('check_page_allowed'); + + $c->stash->{update} = $update; + + $c->forward('check_email_for_abuse', [ $update->user->email ] ); + + if ( $c->req->param('banuser') ) { + $c->forward('ban_user'); + } + elsif ( $c->req->param('flaguser') ) { + $c->forward('flag_user'); + $c->stash->{update}->discard_changes; + } + elsif ( $c->req->param('removeuserflag') ) { + $c->forward('remove_user_flag'); + $c->stash->{update}->discard_changes; + } + elsif ( $c->req->param('submit') ) { + $c->forward('check_token'); + + my $old_state = $update->state; + my $new_state = $c->req->param('state'); + + my $edited = 0; + + # $update->name can be null which makes ne unhappy + my $name = $update->name || ''; + + if ( $c->req->param('name') ne $name + || $c->req->param('email') ne $update->user->email + || $c->req->param('anonymous') ne $update->anonymous + || $c->req->param('text') ne $update->text ){ + $edited = 1; + } + + if ( $c->req->param('remove_photo') ) { + $update->photo(undef); + } + + $update->name( $c->req->param('name') || '' ); + $update->text( $c->req->param('text') ); + $update->anonymous( $c->req->param('anonymous') ); + $update->state( $c->req->param('state') ); + + if ( $c->req->param('email') ne $update->user->email ) { + my $user = + $c->model('DB::User') + ->find_or_create( { email => $c->req->param('email') } ); + + $user->insert unless $user->in_storage; + $update->user($user); + } + + if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { + $update->confirmed( \'ms_current_timestamp()' ); + } + + $update->update; + + $c->stash->{status_message} = '<p><em>' . _('Updated!') . '</em></p>'; + + # If we're hiding an update, see if it marked as fixed and unfix if so + if ( $new_state eq 'hidden' && $update->mark_fixed ) { + if ( $update->problem->state eq 'fixed' ) { + $update->problem->state('confirmed'); + $update->problem->update; + } + + $c->stash->{status_message} .= + '<p><em>' . _('Problem marked as open.') . '</em></p>'; + } + + if ( $new_state ne $old_state ) { + $c->forward( 'log_edit', + [ $update->id, 'update', 'state_change' ] ); + } + + if ($edited) { + $c->forward( 'log_edit', [ $update->id, 'update', 'edit' ] ); + } + + } + + return 1; +} + +sub search_abuse : Path('search_abuse') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); + + my $search = $c->req->param('search'); + + if ($search) { + my $emails = $c->model('DB::Abuse')->search( + { + email => { ilike => "\%$search\%" } + } + ); + + $c->stash->{emails} = [ $emails->all ]; + } + + return 1; +} + +sub list_flagged : Path('list_flagged') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); + + my $problems = $c->model('DB::Problem')->search( { flagged => 1 } ); + + # pass in as array ref as using same template as search_reports + # which has to use an array ref for sql quoting reasons + $c->stash->{problems} = [ $problems->all ]; + + my $users = $c->model('DB::User')->search( { flagged => 1 } ); + + $c->stash->{users} = $users; + + return 1; +} + =head2 set_allowed_pages Sets up the allowed_pages stash entry for checking if the current page is @@ -615,10 +774,13 @@ sub set_allowed_pages : Private { 'search_reports' => [_('Search Reports'), 2], 'timeline' => [_('Timeline'), 3], 'questionnaire' => [_('Survey Results'), 4], - 'council_contacts' => [undef, undef], - 'council_edit' => [undef, undef], - 'report_edit' => [undef, undef], - 'update_edit' => [undef, undef], + 'search_abuse' => [_('Search Abuse'), 5], + 'list_flagged' => [_('List Flagged'), 6], + 'council_contacts' => [undef, undef], + 'council_edit' => [undef, undef], + 'report_edit' => [undef, undef], + 'update_edit' => [undef, undef], + 'abuse_edit' => [undef, undef], } } @@ -688,98 +850,115 @@ sub log_edit : Private { )->insert(); } -sub update_edit : Path('update_edit') : Args(1) { - my ( $self, $c, $id ) = @_; +=head2 ban_user - my ( $site_res_sql, $site_key, $site_restriction ) = - $c->cobrand->site_restriction; - my $update = $c->model('DB::Comment')->search( - { - id => $id, - %{$site_restriction}, - } - )->first; +Add the email address in the email param of the request object to +the abuse table if they are not already in there and sets status_message +accordingly - $c->detach( '/page_error_404_not_found', - [ _('The requested URL was not found on this server.') ] ) - unless $update; +=cut - $c->forward('get_token'); - $c->forward('check_page_allowed'); +sub ban_user : Private { + my ( $self, $c ) = @_; - $c->stash->{update} = $update; + my $email = $c->req->param('email'); - my $status_message = ''; - if ( $c->req->param('submit') ) { - $c->forward('check_token'); + return unless $email; - my $old_state = $update->state; - my $new_state = $c->req->param('state'); + my $abuse = $c->model('DB::Abuse')->find_or_new({ email => $email }); - my $edited = 0; + if ( $abuse->in_storage ) { + $c->stash->{status_message} = _('Email already in abuse list'); + } else { + $abuse->insert; + $c->stash->{status_message} = _('Email added to abuse list'); + } - # $update->name can be null which makes ne unhappy - my $name = $update->name || ''; + $c->stash->{email_in_abuse} = 1; - if ( $c->req->param('name') ne $name - || $c->req->param('email') ne $update->user->email - || $c->req->param('anonymous') ne $update->anonymous - || $c->req->param('text') ne $update->text ){ - $edited = 1; - } + return 1; +} - if ( $c->req->param('remove_photo') ) { - $update->photo(undef); - } +=head2 flag_user - $update->name( $c->req->param('name') || '' ); - $update->text( $c->req->param('text') ); - $update->anonymous( $c->req->param('anonymous') ); - $update->state( $c->req->param('state') ); +Sets the flag on a user with the given email - if ( $c->req->param('email') ne $update->user->email ) { - my $user = - $c->model('DB::User') - ->find_or_create( { email => $c->req->param('email') } ); +=cut - $user->insert unless $user->in_storage; - $update->user($user); - } +sub flag_user : Private { + my ( $self, $c ) = @_; - if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { - $update->confirmed( \'ms_current_timestamp()' ); - } + my $email = $c->req->param('email'); - $update->update; + return unless $email; - $status_message = '<p><em>' . _('Updated!') . '</em></p>'; + my $user = $c->model('DB::User')->find({ email => $email }); - # If we're hiding an update, see if it marked as fixed and unfix if so - if ( $new_state eq 'hidden' && $update->mark_fixed ) { - if ( $update->problem->state eq 'fixed' ) { - $update->problem->state('confirmed'); - $update->problem->update; - } + if ( !$user ) { + $c->stash->{status_message} = _('Could not find user'); + } else { + $user->flagged(1); + $user->update; + $c->stash->{status_message} = _('User flagged'); + } - $status_message .= - '<p><em>' . _('Problem marked as open.') . '</em></p>'; - } + $c->stash->{user_flagged} = 1; - if ( $new_state ne $old_state ) { - $c->forward( 'log_edit', - [ $update->id, 'update', 'state_change' ] ); - } + return 1; +} - if ($edited) { - $c->forward( 'log_edit', [ $update->id, 'update', 'edit' ] ); - } +=head2 remove_user_flag + +Remove the flag on a user with the given email + +=cut + +sub remove_user_flag : Private { + my ( $self, $c ) = @_; + + my $email = $c->req->param('email'); + + return unless $email; + + my $user = $c->model('DB::User')->find({ email => $email }); + if ( !$user ) { + $c->stash->{status_message} = _('Could not find user'); + } else { + $user->flagged(0); + $user->update; + $c->stash->{status_message} = _('User flag removed'); } - $c->stash->{status_message} = $status_message; return 1; } + +=head2 check_email_for_abuse + + $c->forward('check_email_for_abuse', [ $email ] ); + +Checks if $email is in the abuse table and sets email_in_abuse accordingly + +=cut + +sub check_email_for_abuse : Private { + my ( $self, $c, $email ) =@_; + + my $is_abuse = $c->model('DB::Abuse')->find({ email => $email }); + + $c->stash->{email_in_abuse} = 1 if $is_abuse; + + return 1; +} + +=head2 check_page_allowed + +Checks if the current catalyst action is in the list of allowed pages and +if not then redirects to 404 error page. + +=cut + sub check_page_allowed : Private { my ( $self, $c ) = @_; diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 9d7051e2f..f28d37989 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -164,6 +164,10 @@ sub prepare_params_for_email : Private { $c->stash->{problem}->id, $problem_url, $admin_url ); + + # flag this so it's automatically listed in the admin interface + $c->stash->{problem}->flagged(1); + $c->stash->{problem}->update; } return 1; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 2df26fde3..6472b91db 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -78,6 +78,8 @@ __PACKAGE__->add_columns( { data_type => "timestamp", is_nullable => 1 }, "send_questionnaire", { data_type => "boolean", default_value => \"true", is_nullable => 0 }, + "flagged", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->has_many( diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 4ee413a58..cf4fc56d5 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -26,6 +26,8 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 1 }, "password", { data_type => "text", default_value => "", is_nullable => 0 }, + "flagged", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->add_unique_constraint("users_email_key", ["email"]); diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index 4e2ec82fe..8d55bbc18 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -233,6 +233,7 @@ foreach my $test ( name => 'Test User', email => $user->email, anonymous => 0, + flagged => undef, }, changes => { title => 'Edited Report', @@ -250,6 +251,7 @@ foreach my $test ( name => 'Test User', email => $user->email, anonymous => 0, + flagged => undef, }, changes => { detail => 'Edited Detail', @@ -267,6 +269,7 @@ foreach my $test ( name => 'Test User', email => $user->email, anonymous => 0, + flagged => undef, }, changes => { name => 'Edited User', @@ -277,7 +280,7 @@ foreach my $test ( user => $user, }, { - description => 'edit report user email', + description => 'edit report set flagged true', fields => { title => 'Edited Report', detail => 'Edited Detail', @@ -285,13 +288,33 @@ foreach my $test ( name => 'Edited User', email => $user->email, anonymous => 0, + flagged => undef, }, changes => { - email => $user2->email, + flagged => 'on', }, log_count => 4, log_entries => [ qw/edit edit edit edit/ ], resend => 0, + user => $user, + }, + { + description => 'edit report user email', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + email => $user->email, + anonymous => 0, + flagged => 'on', + }, + changes => { + email => $user2->email, + }, + log_count => 5, + log_entries => [ qw/edit edit edit edit edit/ ], + resend => 0, user => $user2, }, { @@ -303,12 +326,13 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 0, + flagged => 'on', }, changes => { state => 'unconfirmed' }, - log_count => 5, - log_entries => [ qw/state_change edit edit edit edit/ ], + log_count => 6, + log_entries => [ qw/state_change edit edit edit edit edit/ ], resend => 0, }, { @@ -320,12 +344,13 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 0, + flagged => 'on', }, changes => { state => 'confirmed' }, - log_count => 6, - log_entries => [ qw/state_change state_change edit edit edit edit/ ], + log_count => 7, + log_entries => [ qw/state_change state_change edit edit edit edit edit/ ], resend => 0, }, { @@ -337,12 +362,13 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 0, + flagged => 'on', }, changes => { state => 'fixed' }, - log_count => 7, - log_entries => [ qw/state_change state_change state_change edit edit edit edit/ ], + log_count => 8, + log_entries => [ qw/state_change state_change state_change edit edit edit edit edit/ ], resend => 0, }, { @@ -354,12 +380,13 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 0, + flagged => 'on', }, changes => { state => 'hidden' }, - log_count => 8, - log_entries => [ qw/state_change state_change state_change state_change edit edit edit edit/ ], + log_count => 9, + log_entries => [ qw/state_change state_change state_change state_change edit edit edit edit edit/ ], resend => 0, }, { @@ -371,13 +398,14 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 0, + flagged => 'on', }, changes => { state => 'confirmed', anonymous => 1, }, - log_count => 10, - log_entries => [ qw/edit state_change state_change state_change state_change state_change edit edit edit edit/ ], + log_count => 11, + log_entries => [ qw/edit state_change state_change state_change state_change state_change edit edit edit edit edit/ ], resend => 0, }, { @@ -389,11 +417,12 @@ foreach my $test ( name => 'Edited User', email => $user2->email, anonymous => 1, + flagged => 'on', }, changes => { }, - log_count => 11, - log_entries => [ qw/resend edit state_change state_change state_change state_change state_change edit edit edit edit/ ], + log_count => 12, + log_entries => [ qw/resend edit state_change state_change state_change state_change state_change edit edit edit edit edit/ ], resend => 1, }, ) { @@ -426,6 +455,7 @@ foreach my $test ( $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' ); } + $test->{changes}->{flagged} = 1 if $test->{changes}->{flagged}; is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'email' } keys %{ $test->{changes} }; if ( $test->{user} ) { @@ -449,6 +479,7 @@ subtest 'change email to new user' => sub { name => $report->name, email => $report->user->email, anonymous => 1, + flagged => 'on', }; is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); @@ -488,6 +519,65 @@ subtest 'change email to new user' => sub { is $report->user_id, $user3->id, 'user changed to new user'; }; +subtest 'adding email to abuse list from report page' => sub { + my $email = $report->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Ban email address'); + + $mech->click_ok('banuser'); + + $mech->content_contains('Email added to abuse list'); + $mech->content_contains('<small>(Email in abuse table)</small>'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + ok $abuse, 'entry created in abuse table'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('<small>(Email in abuse table)</small>'); +}; + +subtest 'flagging user from report page' => sub { + $report->user->flagged(0); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $report->discard_changes; + ok $report->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from report page' => sub { + $report->user->flagged(1); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $report->discard_changes; + ok !$report->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); +}; + $log_entries->delete; my $update = FixMyStreet::App->model('DB::Comment')->create( @@ -668,6 +758,65 @@ subtest 'editing update email creates new user if required' => sub { is $update->user->id, $user->id, 'update set to new user'; }; +subtest 'adding email to abuse list from update page' => sub { + my $email = $update->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Ban email address'); + + $mech->click_ok('banuser'); + + $mech->content_contains('Email added to abuse list'); + $mech->content_contains('<small>(Email in abuse table)</small>'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + ok $abuse, 'entry created in abuse table'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('<small>(Email in abuse table)</small>'); +}; + +subtest 'flagging user from update page' => sub { + $update->user->flagged(0); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $update->discard_changes; + ok $update->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from update page' => sub { + $update->user->flagged(1); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $update->discard_changes; + ok !$update->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); +}; + subtest 'hiding comment marked as fixed reopens report' => sub { $update->mark_fixed( 1 ); $update->update; @@ -724,6 +873,24 @@ subtest 'report search' => sub { $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $r_id \s* </td>}xs ); }; +subtest 'search abuse' => sub { + $mech->get_ok( '/admin/search_abuse?search=example' ); + + $mech->content_contains('test4@example.com'); +}; + +subtest 'show flagged entries' => sub { + $report->flagged( 1 ); + $report->update; + + $user->flagged( 1 ); + $user->update; + + $mech->get_ok('/admin/list_flagged'); + $mech->content_contains( $report->title ); + $mech->content_contains( $user->email ); +}; + $mech->delete_user( $user ); $mech->delete_user( $user2 ); $mech->delete_user( $user3 ); diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t index bbb3a0f83..86b845eb3 100644 --- a/t/app/controller/contact.t +++ b/t/app/controller/contact.t @@ -248,6 +248,9 @@ for my $test ( ) { subtest 'check email sent correctly' => sub { + $problem_main->discard_changes; + ok !$problem_main->flagged, 'problem not flagged'; + $mech->clear_emails_ok; if ($test->{fields}{id}) { $mech->get_ok('/contact?id=' . $test->{fields}{id}); @@ -267,6 +270,14 @@ for my $test ( my $problem_id = $test->{fields}{id}; like $email->body, qr/Complaint about report $problem_id/, 'reporting a report' if $test->{fields}{id}; + + $problem_main->discard_changes; + if ( $problem_id ) { + ok $problem_main->flagged, 'problem flagged'; + } else { + ok !$problem_main->flagged, 'problem not flagged'; + } + }; } diff --git a/templates/web/default/admin/list_flagged.html b/templates/web/default/admin/list_flagged.html new file mode 100644 index 000000000..0b22bccb2 --- /dev/null +++ b/templates/web/default/admin/list_flagged.html @@ -0,0 +1,51 @@ +[% INCLUDE 'admin/header.html' title=loc('Search Reports') %] +[% PROCESS 'admin/report_blocks.html' %] + + +<h2>[% loc( 'Problems' ) %]</h2> +[% IF problems.size > 0 %] +<table cellspacing="0" cellpadding="2" border="1"> + <tr> + <th>[% loc('ID') %]</th> + <th>[% loc('Title') %]</th> + <th>[% loc('Name') %]</th> + <th>[% loc('Email') %]</th> + <th>[% loc('Council') %]</th> + <th>[% loc('Category') %]</th> + <th>[% loc('Anonymous') %]</th> + <th>[% loc('Cobrand') %]</th> + <th>[% loc('Created') %]</th> + <th>[% loc('State') %]</th> + <th>[% loc('When sent') %]</th> + <th>*</th> + </tr> + [% INCLUDE 'admin/problem_row.html' %] +</table> +[% ELSE %] +<p> +[% loc('No flagged problems found') %] +</p> +[% END %] + +<h2>[% loc( 'Users' ) %]</h2> +[% IF users%] +<table cellspacing="0" cellpadding="2" border="1"> + <tr> + <th>[% loc('Name') %]</th> + <th>[% loc('Email') %]</th> + </tr> +[% WHILE ( user = users.next ) -%] + <tr> + <td>[% user.name | html %]</td> + <td>[% user.email | html %]</td> + <td><a href="[% c.uri_for( 'search_reports', search => user.email ) %]">list content</a></td> + </tr> +[%- END %] +</table> +[%- ELSE %] +<p> +[% loc('No flagged users found') %] +</p> +[%- END %] + +[% INCLUDE 'admin/footer.html' %] diff --git a/templates/web/default/admin/problem_row.html b/templates/web/default/admin/problem_row.html new file mode 100644 index 000000000..664ff1b13 --- /dev/null +++ b/templates/web/default/admin/problem_row.html @@ -0,0 +1,30 @@ +[%- FOR problem IN problems %] + <tr[% ' class="hidden"' IF problem.state == 'hidden' %]> + <td>[%- IF problem.state == 'confirmed' || problem.state == 'fixed' -%] + [%- cobrand_data = problem.cobrand_data %] + [%- cobrand_data = c.data_for_generic_problem IF !problem.cobrand %] + <a href="[% c.uri_for_email( '/report', problem.id, cobrand_data ) %]">[% problem.id %]</a> + [%- ELSE %] + [%- problem.id %] + [%- END -%]</td> + <td>[% PROCESS value_or_nbsp value=problem.title %]</td> + <td>[% PROCESS value_or_nbsp value=problem.name %]</td> + <td>[% PROCESS value_or_nbsp value=problem.user.email %]</td> + <td>[%- IF edit_council_contacts -%] + <a href="[% c.uri_for('council_contacts', problem.council ) %]">[% PROCESS value_or_nbsp value=problem.council %]</a> + [%- ELSE -%] + [%- PROCESS value_or_nbsp value=problem.council -%] + [%- END -%]</td> + <td>[% PROCESS value_or_nbsp value=problem.category %]</td> + <td>[% IF problem.anonymous %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td> + <td>[% problem.cobrand %]<br>[% problem.cobrand_data | html %]</td> + <td>[% PROCESS format_time time=problem.created %]</td> + <td>[% problem.state %]<small> + [%- IF problem.state == 'fixed' || problem.state == 'confirmed' %]<br>[% loc('Confirmed:' ) %] [% PROCESS format_time time=problem.confirmed %][% END -%] + [%- IF problem.state == 'fixed' %]<br>[% loc('Fixed:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%] + [%- IF problem.state == 'confirmed' %]<br>[% loc('Last update:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]</small> + </td> + <td>[% PROCESS format_time time=problem.whensent %]</td> + <td><a href="[% c.uri_for( 'report_edit', problem.id ) %]">[% loc('Edit') %]</a></td> + </tr> +[%- END -%] diff --git a/templates/web/default/admin/report_blocks.html b/templates/web/default/admin/report_blocks.html index 1fe650f15..f08529ce7 100644 --- a/templates/web/default/admin/report_blocks.html +++ b/templates/web/default/admin/report_blocks.html @@ -5,3 +5,13 @@ [% BLOCK format_time -%] [%- IF time %][% time.ymd %] [% time.hms %][% ELSE %][% no_time || ' ' %][% END %][% no_time = '' %] [%- END %] + +[% BLOCK abuse_button -%] +[% IF allowed_pages.abuse_edit -%] +[% IF email_in_abuse %]<small>[% loc('(Email in abuse table)') %]</small>[% ELSE %]<input type="submit" name="banuser" value="[% loc('Ban email address') %]" />[% END %] +[%- END %] +[%- END %] + +[% BLOCK flag_button -%] +[% IF user.flagged || user_flagged %]<input type="submit" name="removeuserflag" value="[% loc('Remove flag') %]">[% ELSE %]<input type="submit" name="flaguser" value="[% loc('Flag user') %]" />[% END %] +[%- END %] diff --git a/templates/web/default/admin/report_edit.html b/templates/web/default/admin/report_edit.html index 9c38b014e..69d5d0b11 100644 --- a/templates/web/default/admin/report_edit.html +++ b/templates/web/default/admin/report_edit.html @@ -25,7 +25,7 @@ </select></li> <li>[% loc('Category:') %] [% problem.category | html %] </li> <li>[% loc('Name:') %] <input type='text' name='name' id='name' value='[% problem.name | html %]'></li> -<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% problem.user.email | html %]'></li> +<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% problem.user.email | html %]'> [% PROCESS abuse_button %] [% PROCESS flag_button user=problem.user %]</li> <li>[% loc('Phone:') %] [% problem.user.phone | html %]</li> <li>[% loc('Created:') %] [% PROCESS format_time time=problem.created %]</li> <li>[% loc('Confirmed:') %] [% PROCESS format_time time=problem.confirmed no_time='-' %]</li> @@ -35,6 +35,7 @@ <li>[% loc('Cobrand:') %] [% problem.cobrand %]</li> <li>[% loc('Cobrand data:') %] [% problem.cobrand_data %]</li> <li>[% loc('Going to send questionnaire?') %] [% IF problem.send_questionnaire %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</li> +<li><label for="flagged">[% loc('Flagged:') %]</label> <input type="checkbox" name="flagged"[% ' checked' IF problem.flagged %]></li> [% IF problem.photo %] [% photo = problem.get_photo_params %] diff --git a/templates/web/default/admin/search_abuse.html b/templates/web/default/admin/search_abuse.html new file mode 100644 index 000000000..0984e85cf --- /dev/null +++ b/templates/web/default/admin/search_abuse.html @@ -0,0 +1,21 @@ +[% INCLUDE 'admin/header.html' title=loc('Search Abuse Table') %] + +<form method="get" action="[% c.uri_for('search_abuse') %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8"> + <label for="search">[% loc('Search:') %]</label> <input type="text" name="search" size="30" id="search"> +</form> + + +[% IF emails.size > 0 %] +<table cellspacing="0" cellpadding="2" border="1"> + <tr> + <th>[% loc('Email') %]</th> + </tr> +[%- FOREACH foo IN emails %] + <tr> + <td>[%- foo.email | html -%]</td> + </tr> +[%- END -%] +</table> +[% END %] + +[% INCLUDE 'admin/footer.html' %] diff --git a/templates/web/default/admin/search_reports.html b/templates/web/default/admin/search_reports.html index 3809965f6..9702c16a7 100644 --- a/templates/web/default/admin/search_reports.html +++ b/templates/web/default/admin/search_reports.html @@ -22,36 +22,7 @@ <th>[% loc('When sent') %]</th> <th>*</th> </tr> -[%- FOREACH problem IN problems %] - <tr[% ' class="hidden"' IF problem.state == 'hidden' %]> - <td>[%- IF problem.state == 'confirmed' || problem.state == 'fixed' -%] - [%- cobrand_data = problem.cobrand_data %] - [%- cobrand_data = c.data_for_generic_problem IF !problem.cobrand %] - <a href="[% c.uri_for_email( '/report', problem.id, cobrand_data ) %]">[% problem.id %]</a> - [%- ELSE %] - [%- problem.id %] - [%- END -%]</td> - <td>[% PROCESS value_or_nbsp value=problem.title %]</td> - <td>[% PROCESS value_or_nbsp value=problem.name %]</td> - <td>[% PROCESS value_or_nbsp value=problem.user.email %]</td> - <td>[%- IF edit_council_contacts -%] - <a href="[% c.uri_for('council_contacts', problem.council ) %]">[% PROCESS value_or_nbsp value=problem.council %]</a> - [%- ELSE -%] - [%- PROCESS value_or_nbsp value=problem.council -%] - [%- END -%]</td> - <td>[% PROCESS value_or_nbsp value=problem.category %]</td> - <td>[% IF problem.anonymous %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td> - <td>[% problem.cobrand %]<br>[% problem.cobrand_data | html %]</td> - <td>[% PROCESS format_time time=problem.created %]</td> - <td>[% problem.state %]<small> - [%- IF problem.state == 'fixed' || problem.state == 'confirmed' %]<br>[% loc('Confirmed:' ) %] [% PROCESS format_time time=problem.confirmed %][% END -%] - [%- IF problem.state == 'fixed' %]<br>[% loc('Fixed:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%] - [%- IF problem.state == 'confirmed' %]<br>[% loc('Last update:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]</small> - </td> - <td>[% PROCESS format_time time=problem.whensent %]</td> - <td><a href="[% c.uri_for( 'report_edit', problem.id ) %]">[% loc('Edit') %]</a></td> - </tr> -[%- END -%] + [% INCLUDE 'admin/problem_row.html' %] </table> [% INCLUDE 'admin/list_updates.html' %] diff --git a/templates/web/default/admin/update_edit.html b/templates/web/default/admin/update_edit.html index d7f212052..d4ac1b440 100644 --- a/templates/web/default/admin/update_edit.html +++ b/templates/web/default/admin/update_edit.html @@ -21,7 +21,7 @@ [% END %] </select></li> <li>[% loc('Name:') %] <input type='text' name='name' id='name' value='[% update.name | html %]'></li> -<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% update.user.email | html %]'></li> +<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% update.user.email | html %]'> [% PROCESS abuse_button %] [% PROCESS flag_button user=update.user %]</li> <li>[% loc('Cobrand:') %] [% update.cobrand %]</li> <li>[% loc('Cobrand data:') %] [% update.cobrand_data %]</li> <li>[% loc('Created:') %] [% PROCESS format_time time=update.created %]</li> |