aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rwxr-xr-xbin/fixmystreet.com/fixture2
-rwxr-xr-xbin/update-schema20
-rw-r--r--db/downgrade_0054---0053.sql39
-rw-r--r--db/fixture.sql (renamed from db/alert_types.sql)52
-rw-r--r--db/schema.sql42
-rw-r--r--db/schema_0054-add-state-table.sql51
-rw-r--r--perllib/FixMyStreet/App.pm1
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/States.pm102
-rw-r--r--perllib/FixMyStreet/App/Controller/Dashboard.pm7
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm7
-rw-r--r--perllib/FixMyStreet/App/View/Web.pm8
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm29
-rw-r--r--perllib/FixMyStreet/Cobrand/Oxfordshire.pm13
-rw-r--r--perllib/FixMyStreet/DB/Result/Comment.pm68
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm46
-rw-r--r--perllib/FixMyStreet/DB/Result/State.pm48
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/State.pm78
-rw-r--r--perllib/FixMyStreet/Script/Questionnaires.pm3
-rw-r--r--perllib/FixMyStreet/TestAppProve.pm2
-rw-r--r--t/app/controller/admin_states.t24
-rw-r--r--t/app/controller/dashboard.t17
-rw-r--r--t/app/controller/report_display.t6
-rw-r--r--t/app/controller/report_inspect.t9
-rw-r--r--t/app/controller/report_updates.t124
-rw-r--r--t/app/model/state.t62
-rw-r--r--t/cobrand/bromley.t4
-rw-r--r--templates/web/base/admin/problem_row.html4
-rw-r--r--templates/web/base/admin/report_blocks.html8
-rw-r--r--templates/web/base/admin/states/index.html118
-rw-r--r--templates/web/base/dashboard/index.html11
-rw-r--r--templates/web/base/front/stats.html2
-rw-r--r--templates/web/base/report/_item.html4
-rw-r--r--templates/web/base/report/banner.html6
-rw-r--r--templates/web/base/report/inspect/state_groups_select.html7
-rw-r--r--templates/web/base/report/state-list.html21
-rw-r--r--templates/web/base/report/update.html6
-rw-r--r--templates/web/base/report/update/form_update.html4
-rw-r--r--templates/web/base/reports/_list-filters.html27
-rw-r--r--templates/web/oxfordshire/report/inspect/state_groups_select.html12
40 files changed, 760 insertions, 335 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d9326b2b4..991ad63ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
* Unreleased
- New features:
- Body and category names can now be translated in the admin. #1244
+ - Report states can be edited and translated in the admin. #1826
- Body users can now create reports as an anonymous user. #1796
- Extra fields can be added to report form site-wide. #1743
- Body users can filter reports by all states. #1790
diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture
index 6df675f7c..6cf5ad199 100755
--- a/bin/fixmystreet.com/fixture
+++ b/bin/fixmystreet.com/fixture
@@ -54,7 +54,7 @@ BEGIN
END
$func$;
}) or die $!;
- $db->dbh->do( scalar FixMyStreet->path_to('db/alert_types.sql')->slurp ) or die $!;
+ $db->dbh->do( scalar FixMyStreet->path_to('db/fixture.sql')->slurp ) or die $!;
$db->dbh->do( scalar FixMyStreet->path_to('db/generate_secret.sql')->slurp ) or die $!;
say "Emptied database";
}
diff --git a/bin/update-schema b/bin/update-schema
index 32c00ff5e..a27bbbba1 100755
--- a/bin/update-schema
+++ b/bin/update-schema
@@ -40,6 +40,7 @@ BEGIN {
}
use FixMyStreet;
+use FixMyStreet::Cobrand;
use FixMyStreet::DB;
use mySociety::MaPit;
use Getopt::Long;
@@ -112,7 +113,7 @@ if ($upgrade && $current_version eq 'EMPTY') {
if ($commit) {
run_statements(get_statements("$bin_dir/../db/schema.sql"));
run_statements(get_statements("$bin_dir/../db/generate_secret.sql"));
- run_statements(get_statements("$bin_dir/../db/alert_types.sql"));
+ run_statements(get_statements("$bin_dir/../db/fixture.sql"));
}
} elsif ($upgrade) {
if ($version) {
@@ -139,12 +140,28 @@ if ($upgrade && $current_version eq 'EMPTY') {
my $area_ids = $db->dbh->selectcol_arrayref('SELECT area_id FROM body_areas');
if ( @$area_ids ) {
my $areas = mySociety::MaPit::call('areas', $area_ids);
+ $db->txn_begin;
foreach (values %$areas) {
$db->dbh->do('UPDATE body SET name=? WHERE id=?', {}, $_->{name}, $_->{id});
}
$db->txn_commit;
}
}
+
+ if ( $commit && $current_version lt '0054' ) {
+ $nothing = 0;
+ print "States created, importing names\n";
+ my @avail = FixMyStreet::Cobrand->available_cobrand_classes;
+ # Pick first available cobrand and language for database name import
+ my $cobrand = $avail[0] ? FixMyStreet::Cobrand::class($avail[0]) : 'FixMyStreet::Cobrand::Default';
+ my $lang = $cobrand->new->set_lang_and_domain(undef, 1, FixMyStreet->path_to('locale')->stringify);
+ my $names = $db->dbh->selectcol_arrayref('SELECT name FROM state');
+ $db->txn_begin;
+ foreach (@$names) {
+ $db->dbh->do('UPDATE state SET name=? WHERE name=?', {}, _($_), $_);
+ }
+ $db->txn_commit;
+ }
}
if ($downgrade) {
@@ -195,6 +212,7 @@ else {
# (assuming schema change files are never half-applied, which should be the case)
sub get_db_version {
return 'EMPTY' if ! table_exists('problem');
+ return '0054' if table_exists('state');
return '0053' if table_exists('report_extra_fields');
return '0052' if table_exists('translation');
return '0051' if column_exists('contacts', 'state');
diff --git a/db/downgrade_0054---0053.sql b/db/downgrade_0054---0053.sql
new file mode 100644
index 000000000..6fcc41c33
--- /dev/null
+++ b/db/downgrade_0054---0053.sql
@@ -0,0 +1,39 @@
+BEGIN;
+
+DROP TABLE state;
+
+ALTER TABLE problem ADD CONSTRAINT problem_state_check CHECK (
+ state = 'unconfirmed'
+ or state = 'hidden'
+ or state = 'partial'
+ or state = 'confirmed'
+ or state = 'investigating'
+ or state = 'planned'
+ or state = 'in progress'
+ or state = 'action scheduled'
+ or state = 'fixed'
+ or state = 'fixed - council'
+ or state = 'fixed - user'
+ or state = 'closed'
+ or state = 'unable to fix'
+ or state = 'not responsible'
+ or state = 'duplicate'
+ or state = 'internal referral'
+);
+ALTER TABLE comment ADD CONSTRAINT comment_problem_state_check CHECK (
+ problem_state = 'confirmed'
+ or problem_state = 'investigating'
+ or problem_state = 'planned'
+ or problem_state = 'in progress'
+ or problem_state = 'action scheduled'
+ or problem_state = 'fixed'
+ or problem_state = 'fixed - council'
+ or problem_state = 'fixed - user'
+ or problem_state = 'closed'
+ or problem_state = 'unable to fix'
+ or problem_state = 'not responsible'
+ or problem_state = 'duplicate'
+ or problem_state = 'internal referral'
+);
+
+COMMIT;
diff --git a/db/alert_types.sql b/db/fixture.sql
index 471fd905f..840906223 100644
--- a/db/alert_types.sql
+++ b/db/fixture.sql
@@ -1,3 +1,13 @@
+INSERT INTO state (label, type, name) VALUES ('investigating', 'open', 'Investigating');
+INSERT INTO state (label, type, name) VALUES ('in progress', 'open', 'In progress');
+INSERT INTO state (label, type, name) VALUES ('planned', 'open', 'Planned');
+INSERT INTO state (label, type, name) VALUES ('action scheduled', 'open', 'Action scheduled');
+INSERT INTO state (label, type, name) VALUES ('unable to fix', 'closed', 'No further action');
+INSERT INTO state (label, type, name) VALUES ('not responsible', 'closed', 'Not responsible');
+INSERT INTO state (label, type, name) VALUES ('duplicate', 'closed', 'Duplicate');
+INSERT INTO state (label, type, name) VALUES ('internal referral', 'closed', 'Internal referral');
+INSERT INTO state (label, type, name) VALUES ('fixed', 'fixed', 'Fixed');
+
-- New updates on a particular problem report
insert into alert_type
(ref, head_sql_query, head_table,
@@ -18,11 +28,8 @@ insert into alert_type
values ('new_problems', '', '',
'New problems on FixMyStreet', '/', 'The latest problems reported by users',
'problem',
- 'problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed''
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'' )',
+ 'problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'')',
'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem');
@@ -46,11 +53,8 @@ insert into alert_type
values ('local_problems', '', '',
'New local problems on FixMyStreet', '/', 'The latest local problems reported by users',
'problem_find_nearby(?, ?, ?) as nearby,problem',
- 'nearby.problem_id = problem.id and problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed'',
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'')',
+ 'nearby.problem_id = problem.id and problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'')',
'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-nearby');
@@ -74,11 +78,8 @@ insert into alert_type
values ('postcode_local_problems', '', '',
'New problems near {{POSTCODE}} on FixMyStreet', '/', 'The latest local problems reported by users',
'problem_find_nearby(?, ?, ?) as nearby,problem',
- 'nearby.problem_id = problem.id and problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed'',
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'')',
+ 'nearby.problem_id = problem.id and problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'')',
'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-nearby');
@@ -102,11 +103,8 @@ insert into alert_type
values ('council_problems', '', '',
'New problems to {{COUNCIL}} on FixMyStreet', '/reports', 'The latest problems for {{COUNCIL}} reported by users',
'problem',
- 'problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed'',
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'' ) AND
+ 'problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'') AND
regexp_split_to_array(bodies_str, '','') && ARRAY[?]',
'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-council'
@@ -122,11 +120,8 @@ values ('ward_problems', '', '',
'New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet', '/reports',
'The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users',
'problem',
- 'problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed'',
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'' ) AND
+ 'problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'') AND
(regexp_split_to_array(bodies_str, '','') && ARRAY[?] or bodies_str is null) and
areas like ''%,''||?||'',%''',
'created desc',
@@ -142,11 +137,8 @@ insert into alert_type
values ('area_problems', '', '',
'New problems within {{NAME}}''s boundary on FixMyStreet', '/reports',
'The latest problems within {{NAME}}''s boundary reported by users', 'problem',
- 'problem.non_public = ''f'' and problem.state in
- (''confirmed'', ''investigating'', ''planned'', ''in progress'',
- ''fixed'', ''fixed - council'', ''fixed - user'', ''closed'',
- ''action scheduled'', ''not responsible'', ''duplicate'', ''unable to fix'',
- ''internal referral'' ) AND
+ 'problem.non_public = ''f'' and problem.state NOT IN
+ (''unconfirmed'', ''hidden'', ''partial'') AND
areas like ''%,''||?||'',%''',
'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-area'
diff --git a/db/schema.sql b/db/schema.sql
index ed930a13e..fedab2b9d 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -177,24 +177,7 @@ create table problem (
-- Metadata
created timestamp not null default current_timestamp,
confirmed timestamp,
- state text not null check (
- state = 'unconfirmed'
- or state = 'confirmed'
- or state = 'investigating'
- or state = 'planned'
- or state = 'in progress'
- or state = 'action scheduled'
- or state = 'closed'
- or state = 'fixed'
- or state = 'fixed - council'
- or state = 'fixed - user'
- or state = 'hidden'
- or state = 'partial'
- or state = 'unable to fix'
- or state = 'not responsible'
- or state = 'duplicate'
- or state = 'internal referral'
- ),
+ state text not null,
lang text not null default 'en-gb',
service text not null default '',
cobrand text not null default '' check (cobrand ~* '^[a-z0-9_]*$'),
@@ -327,21 +310,7 @@ create table comment (
cobrand_data text not null default '' check (cobrand_data ~* '^[a-z0-9_]*$'), -- Extra data used in cobranded versions of the site
mark_fixed boolean not null,
mark_open boolean not null default 'f',
- problem_state text check (
- problem_state = 'confirmed'
- or problem_state = 'investigating'
- or problem_state = 'planned'
- or problem_state = 'in progress'
- or problem_state = 'action scheduled'
- or problem_state = 'closed'
- or problem_state = 'fixed'
- or problem_state = 'fixed - council'
- or problem_state = 'fixed - user'
- or problem_state = 'unable to fix'
- or problem_state = 'not responsible'
- or problem_state = 'duplicate'
- or problem_state = 'internal referral'
- ),
+ problem_state text,
-- other fields? one to indicate whether this was written by the council
-- and should be highlighted in the display?
external_id text,
@@ -546,3 +515,10 @@ CREATE TABLE report_extra_fields (
language text,
extra text
);
+
+CREATE TABLE state (
+ id serial not null primary key,
+ label text not null unique,
+ type text not null check (type = 'open' OR type = 'closed' OR type = 'fixed'),
+ name text not null unique
+);
diff --git a/db/schema_0054-add-state-table.sql b/db/schema_0054-add-state-table.sql
new file mode 100644
index 000000000..c4be36015
--- /dev/null
+++ b/db/schema_0054-add-state-table.sql
@@ -0,0 +1,51 @@
+BEGIN;
+
+CREATE TABLE state (
+ id serial not null primary key,
+ label text not null unique,
+ type text not null check (type = 'open' OR type = 'closed' OR type = 'fixed'),
+ name text not null unique
+);
+
+INSERT INTO state (label, type, name) VALUES ('investigating', 'open', 'Investigating');
+INSERT INTO state (label, type, name) VALUES ('in progress', 'open', 'In progress');
+INSERT INTO state (label, type, name) VALUES ('planned', 'open', 'Planned');
+INSERT INTO state (label, type, name) VALUES ('action scheduled', 'open', 'Action scheduled');
+INSERT INTO state (label, type, name) VALUES ('unable to fix', 'closed', 'No further action');
+INSERT INTO state (label, type, name) VALUES ('not responsible', 'closed', 'Not responsible');
+INSERT INTO state (label, type, name) VALUES ('duplicate', 'closed', 'Duplicate');
+INSERT INTO state (label, type, name) VALUES ('internal referral', 'closed', 'Internal referral');
+INSERT INTO state (label, type, name) VALUES ('fixed', 'fixed', 'Fixed');
+
+ALTER TABLE problem DROP CONSTRAINT problem_state_check;
+ALTER TABLE comment DROP CONSTRAINT comment_problem_state_check;
+
+UPDATE alert_type SET item_where = 'nearby.problem_id = problem.id
+ and problem.non_public = ''f''
+ and problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')'
+ WHERE ref = 'postcode_local_problems';
+UPDATE alert_type set item_where = 'problem.non_public = ''f''
+ and problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')'
+ WHERE ref = 'new_problems';
+UPDATE alert_type set item_where = 'problem.non_public = ''f''
+ and problem.state in (''fixed'', ''fixed - user'', ''fixed - council'')'
+ WHERE ref = 'new_fixed_problems';
+UPDATE alert_type set item_where = 'nearby.problem_id = problem.id
+ and problem.non_public = ''f''
+ and problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')'
+ WHERE ref = 'local_problems';
+UPDATE alert_type set item_where = 'problem.non_public = ''f''
+ AND problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')
+ AND regexp_split_to_array(bodies_str, '','') && ARRAY[?]'
+ WHERE ref = 'council_problems';
+UPDATE alert_type set item_where = 'problem.non_public = ''f''
+ AND problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')
+ AND (regexp_split_to_array(bodies_str, '','') && ARRAY[?] or bodies_str is null) and
+ areas like ''%,''||?||'',%'''
+ WHERE ref = 'ward_problems';
+UPDATE alert_type set item_where = 'problem.non_public = ''f''
+ AND problem.state NOT IN (''hidden'', ''unconfirmed'', ''partial'')
+ AND areas like ''%,''||?||'',%'''
+ WHERE ref = 'area_problems';
+
+COMMIT;
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm
index 25d3d744e..a09f86f85 100644
--- a/perllib/FixMyStreet/App.pm
+++ b/perllib/FixMyStreet/App.pm
@@ -218,6 +218,7 @@ sub setup_request {
mySociety::MaPit::configure( "http://$host/fakemapit/" );
}
+ $c->stash->{has_fixed_state} = FixMyStreet::DB::Result::Problem::fixed_states->{fixed};
$c->cobrand->call_hook('setup_states');
if (FixMyStreet->test_mode) {
diff --git a/perllib/FixMyStreet/App/Controller/Admin/States.pm b/perllib/FixMyStreet/App/Controller/Admin/States.pm
new file mode 100644
index 000000000..e4c07c9ca
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Admin/States.pm
@@ -0,0 +1,102 @@
+package FixMyStreet::App::Controller::Admin::States;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+sub begin : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward('/admin/begin');
+}
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->forward('/auth/get_csrf_token');
+ $c->forward('/admin/fetch_languages');
+ my $rs = $c->model('DB::State');
+
+ if ($c->req->method eq 'POST') {
+ $c->forward('/auth/check_csrf_token');
+
+ $c->forward('process_new')
+ && $c->forward('delete')
+ && $c->forward('update');
+
+ $rs->clear;
+ }
+
+ $c->stash->{open_states} = $rs->open;
+ $c->stash->{closed_states} = $rs->closed;
+ $c->stash->{fixed_states} = $rs->fixed;
+}
+
+sub process_new : Private {
+ my ($self, $c) = @_;
+ if ($c->get_param('new_fixed')) {
+ $c->model('DB::State')->create({
+ label => 'fixed',
+ type => 'fixed',
+ name => _('Fixed'),
+ });
+ return 0;
+ }
+ return 1 unless $c->get_param('new');
+ my %params = map { $_ => $c->get_param($_) } qw/label type name/;
+ $c->model('DB::State')->create(\%params);
+ return 0;
+}
+
+sub delete : Private {
+ my ($self, $c) = @_;
+
+ my @params = keys %{ $c->req->params };
+ my ($to_delete) = map { /^delete:(.*)/ } grep { /^delete:/ } @params;
+ if ($to_delete) {
+ $c->model('DB::State')->search({ label => $to_delete })->delete;
+ return 0;
+ }
+ return 1;
+}
+
+sub update : Private {
+ my ($self, $c) = @_;
+
+ my $rs = $c->model('DB::State');
+ my %db_states = map { $_->label => $_ } @{$rs->states};
+ my @params = keys %{ $c->req->params };
+ my @states = map { /^type:(.*)/ } grep { /^type:/ } @params;
+
+ foreach my $state (@states) {
+ # If there is only one language, we still store confirmed/closed
+ # as translations, as that seems a sensible place to store them.
+ if ($state eq 'confirmed' or $state eq 'closed') {
+ if (my $name = $c->get_param("name:$state")) {
+ my ($lang) = keys %{$c->stash->{languages}};
+ $db_states{$state}->add_translation_for('name', $lang, $name);
+ }
+ } else {
+ $db_states{$state}->update({
+ type => $c->get_param("type:$state"),
+ name => $c->get_param("name:$state"),
+ });
+ }
+
+ foreach my $lang (keys(%{$c->stash->{languages}})) {
+ my $id = $c->get_param("translation_id:$state:$lang");
+ my $text = $c->get_param("translation:$state:$lang");
+ if ($text) {
+ $db_states{$state}->add_translation_for('name', $lang, $text);
+ } elsif ($id) {
+ $c->model('DB::Translation')->find({ id => $id })->delete;
+ }
+ }
+ }
+
+ return 1;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm
index fbe5a2dc9..f3989e760 100644
--- a/perllib/FixMyStreet/App/Controller/Dashboard.pm
+++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm
@@ -24,6 +24,8 @@ sub example : Local : Args(0) {
my ( $self, $c ) = @_;
$c->stash->{template} = 'dashboard/index.html';
+ $c->stash->{filter_states} = $c->cobrand->state_groups_inspect;
+
$c->stash->{children} = {};
for my $i (1..3) {
$c->stash->{children}{$i} = { id => $i, name => "Ward $i" };
@@ -93,6 +95,7 @@ sub index : Path : Args(0) {
$c->stash->{body} = $body;
# Set up the data for the dropdowns
+ $c->stash->{filter_states} = $c->cobrand->state_groups_inspect;
# Just take the first area ID we find
my $area_id = $body->body_areas->first->area_id;
@@ -145,12 +148,10 @@ sub index : Path : Args(0) {
# List of reports underneath summary table
$c->stash->{q_state} = $c->get_param('state') || '';
- if ( $c->stash->{q_state} eq 'fixed' ) {
+ if ( $c->stash->{q_state} eq 'fixed - council' ) {
$prob_where->{'me.state'} = [ FixMyStreet::DB::Result::Problem->fixed_states() ];
} elsif ( $c->stash->{q_state} ) {
$prob_where->{'me.state'} = $c->stash->{q_state};
- $prob_where->{'me.state'} = { IN => [ 'planned', 'action scheduled' ] }
- if $prob_where->{'me.state'} eq 'action scheduled';
}
my $params = {
%$prob_where,
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
index b597cb7a8..8f8205719 100644
--- a/perllib/FixMyStreet/App/Controller/Reports.pm
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -558,12 +558,11 @@ sub stash_report_filter_status : Private {
if ($c->user and ($c->user->is_superuser or (
$c->stash->{body} and $c->user->belongs_to_body($c->stash->{body}->id)
))) {
+ $c->stash->{filter_states} = $c->cobrand->state_groups_inspect;
foreach my $state (FixMyStreet::DB::Result::Problem->visible_states()) {
if ($status{$state}) {
- %filter_problem_states = (%filter_problem_states, ($state => 1));
- my $pretty_state = $state;
- $pretty_state =~ tr/ /_/;
- $filter_status{$pretty_state} = 1;
+ $filter_problem_states{$state} = 1;
+ $filter_status{$state} = 1;
}
}
}
diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm
index 93c459e26..93aa0e2fb 100644
--- a/perllib/FixMyStreet/App/View/Web.pm
+++ b/perllib/FixMyStreet/App/View/Web.pm
@@ -170,12 +170,8 @@ sub decode {
sub prettify_state {
my ($self, $c, $text, $single_fixed) = @_;
- # New template to prevent interaction with current one
- my $tt = FixMyStreet::Template->new({ INCLUDE_PATH => $self->{include_path} });
- my $var;
- $tt->process('report/state-list.html', { state => $text }, \$var);
- $var =~ s/ - .*// if $single_fixed;
- return $var;
+
+ return FixMyStreet::DB->resultset("State")->display($text, $single_fixed);
}
1;
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index 5dcdc9a4b..250919d09 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -179,7 +179,7 @@ sub restriction {
return $self->moniker ? { cobrand => $self->moniker } : {};
}
-=head2 base_url_with_lang
+=head2 base_url_with_lang
=cut
@@ -358,7 +358,7 @@ sub front_stats_data {
Returns any disambiguating information available. Defaults to none.
-=cut
+=cut
sub disambiguate_location { FixMyStreet->config('GEOCODING_DISAMBIGUATION') or {}; }
@@ -642,6 +642,7 @@ sub admin_pages {
# There are some pages that only super users can see
if ( $user->is_superuser ) {
$pages->{flagged} = [ _('Flagged'), 7 ];
+ $pages->{states} = [ _('States'), 8 ];
$pages->{config} = [ _('Configuration'), 9];
};
# And some that need special permissions
@@ -820,7 +821,7 @@ sub is_two_tier { 0; }
=item council_rss_alert_options
-Generate a set of options for council rss alerts.
+Generate a set of options for council rss alerts.
=cut
@@ -1066,6 +1067,28 @@ sub show_unconfirmed_reports {
0;
}
+sub state_groups_admin {
+ my $rs = FixMyStreet::DB->resultset("State");
+ my @fixed = FixMyStreet::DB::Result::Problem->fixed_states;
+ [
+ [ $rs->display('confirmed'), [ FixMyStreet::DB::Result::Problem->open_states ] ],
+ @fixed ? [ $rs->display('fixed'), [ FixMyStreet::DB::Result::Problem->fixed_states ] ] : (),
+ [ $rs->display('closed'), [ FixMyStreet::DB::Result::Problem->closed_states ] ],
+ [ $rs->display('hidden'), [ FixMyStreet::DB::Result::Problem->hidden_states ] ]
+ ]
+}
+
+sub state_groups_inspect {
+ my $rs = FixMyStreet::DB->resultset("State");
+ my @fixed = FixMyStreet::DB::Result::Problem->fixed_states;
+ [
+ [ $rs->display('confirmed'), [ grep { $_ ne 'planned' } FixMyStreet::DB::Result::Problem->open_states ] ],
+ @fixed ? [ $rs->display('fixed'), [ 'fixed - council' ] ] : (),
+ [ $rs->display('closed'), [ grep { $_ ne 'closed' } FixMyStreet::DB::Result::Problem->closed_states ] ],
+ [ $rs->display('hidden'), [ 'hidden' ] ]
+ ]
+}
+
=head2 never_confirm_updates
If true then we never send an email to confirm an update
diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
index a061ff46c..b3d6b28c3 100644
--- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
@@ -122,12 +122,19 @@ sub path_to_pin_icons {
sub pin_hover_title {
my ($self, $problem, $title) = @_;
- my $state = $self->{c}->render_fragment(
- 'report/state-list.html',
- { state => $problem->state });
+ my $state = FixMyStreet::DB->resultset("State")->display($problem->state, 1);
return "$state: $title";
}
+sub state_groups_inspect {
+ [
+ [ _('New'), [ 'confirmed', 'investigating' ] ],
+ [ _('Scheduled'), [ 'action scheduled' ] ],
+ [ _('Fixed'), [ 'fixed - council' ] ],
+ [ _('Closed'), [ 'not responsible', 'duplicate', 'unable to fix' ] ],
+ ]
+}
+
sub open311_config {
my ($self, $row, $h, $params) = @_;
diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm
index d688eb8b9..562f29693 100644
--- a/perllib/FixMyStreet/DB/Result/Comment.pm
+++ b/perllib/FixMyStreet/DB/Result/Comment.pm
@@ -227,8 +227,6 @@ sub meta_line {
my $meta = '';
- $c->stash->{last_state} ||= '';
-
if ($self->anonymous or !$self->name) {
$meta = sprintf( _( 'Posted anonymously at %s' ), Utils::prettify_dt( $self->confirmed ) )
} elsif ($self->user->from_body) {
@@ -257,60 +255,36 @@ sub meta_line {
$meta = sprintf( _( 'Posted by %s at %s' ), FixMyStreet::Template::html_filter($self->name), Utils::prettify_dt( $self->confirmed ) )
}
+ if ($self->get_extra_metadata('defect_raised')) {
+ $meta .= ', ' . _( 'and a defect raised' );
+ }
+
+ return $meta;
+};
+
+sub problem_state_display {
+ my ( $self, $c ) = @_;
+
my $update_state = '';
+ my $cobrand = $c->cobrand->moniker;
if ($self->mark_fixed) {
- $update_state = _( 'marked as fixed' );
+ return FixMyStreet::DB->resultset("State")->display('fixed', 1);
} elsif ($self->mark_open) {
- $update_state = _( 'reopened' );
+ return FixMyStreet::DB->resultset("State")->display('confirmed', 1);
} elsif ($self->problem_state) {
my $state = $self->problem_state;
-
- if ($state eq 'confirmed') {
- if ($c->stash->{last_state}) {
- $update_state = _( 'reopened' )
- }
- } elsif ($state eq 'investigating') {
- $update_state = _( 'marked as investigating' )
- } elsif ($state eq 'planned') {
- $update_state = _( 'marked as planned' )
- } elsif ($state eq 'in progress') {
- $update_state = _( 'marked as in progress' )
- } elsif ($state eq 'action scheduled') {
- $update_state = _( 'marked as action scheduled' )
- } elsif ($state eq 'closed') {
- $update_state = _( 'marked as closed' )
- } elsif ($state =~ /^fixed/) {
- $update_state = _( 'marked as fixed' )
- } elsif ($state eq 'unable to fix') {
- $update_state = _( 'marked as no further action' )
- } elsif ($state eq 'not responsible') {
- $update_state = _( "marked as not the council's responsibility" )
- } elsif ($state eq 'duplicate') {
- $update_state = _( 'closed as a duplicate report' )
- } elsif ($state eq 'internal referral') {
- $update_state = _( 'marked as an internal referral' )
- }
-
- if ($c->cobrand->moniker eq 'bromley' || $self->problem->to_body_named('Bromley')) {
- if ($state eq 'not responsible') {
- $update_state = 'marked as third party responsibility'
+ if ($state eq 'not responsible') {
+ $update_state = _( "not the council's responsibility" );
+ if ($cobrand eq 'bromley' || $self->problem->to_body_named('Bromley')) {
+ $update_state = 'third party responsibility';
}
+ } else {
+ $update_state = FixMyStreet::DB->resultset("State")->display($state, 1);
}
-
}
- if ($update_state ne $c->stash->{last_state} and $update_state) {
- $meta .= ", $update_state";
- }
-
- if ($self->get_extra_metadata('defect_raised')) {
- $meta .= ', ' . _( 'and a defect raised' );
- }
-
- $c->stash->{last_state} = $update_state;
-
- return $meta;
-};
+ return $update_state;
+}
1;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index 51805fa51..77190679b 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -220,15 +220,8 @@ HASHREF.
=cut
sub open_states {
- my $states = {
- 'confirmed' => 1,
- 'investigating' => 1,
- 'in progress' => 1,
- 'planned' => 1,
- 'action scheduled' => 1,
- };
-
- return wantarray ? keys %{$states} : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->open};
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
=head2
@@ -242,13 +235,9 @@ HASHREF.
=cut
sub fixed_states {
- my $states = {
- 'fixed' => 1,
- 'fixed - user' => 1,
- 'fixed - council' => 1,
- };
-
- return wantarray ? keys %{ $states } : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->fixed};
+ push @states, 'fixed - user', 'fixed - council' if @states;
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
=head2
@@ -262,18 +251,10 @@ HASHREF.
=cut
sub closed_states {
- my $states = {
- 'closed' => 1,
- 'unable to fix' => 1,
- 'not responsible' => 1,
- 'duplicate' => 1,
- 'internal referral' => 1,
- };
-
- return wantarray ? keys %{$states} : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->closed};
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
-
=head2
@states = FixMyStreet::DB::Problem::all_states();
@@ -289,21 +270,10 @@ sub all_states {
'hidden' => 1,
'partial' => 1,
'unconfirmed' => 1,
- 'confirmed' => 1,
- 'investigating' => 1,
- 'in progress' => 1,
- 'planned' => 1,
- 'action scheduled' => 1,
- 'fixed' => 1,
'fixed - council' => 1,
'fixed - user' => 1,
- 'unable to fix' => 1,
- 'not responsible' => 1,
- 'duplicate' => 1,
- 'closed' => 1,
- 'internal referral' => 1,
};
-
+ map { $states->{$_->label} = 1 } @{FixMyStreet::DB->resultset("State")->states};
return wantarray ? keys %{$states} : $states;
}
diff --git a/perllib/FixMyStreet/DB/Result/State.pm b/perllib/FixMyStreet/DB/Result/State.pm
new file mode 100644
index 000000000..b8a35d42b
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/State.pm
@@ -0,0 +1,48 @@
+use utf8;
+package FixMyStreet::DB::Result::State;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn");
+__PACKAGE__->table("state");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "state_id_seq",
+ },
+ "label",
+ { data_type => "text", is_nullable => 0 },
+ "type",
+ { data_type => "text", is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("state_label_key", ["label"]);
+__PACKAGE__->add_unique_constraint("state_name_key", ["name"]);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-08-22 15:17:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dvtAOpeYqEF9T3otHHgLqw
+
+use Moo;
+use namespace::clean;
+
+with 'FixMyStreet::Roles::Translatable';
+
+sub msgstr {
+ my $self = shift;
+ my $lang = $self->result_source->schema->lang;
+ return $self->name unless $lang && $self->translated->{name}{$lang};
+ return $self->translated->{name}{$lang}{msgstr};
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/State.pm b/perllib/FixMyStreet/DB/ResultSet/State.pm
new file mode 100644
index 000000000..8b6a8963e
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/State.pm
@@ -0,0 +1,78 @@
+package FixMyStreet::DB::ResultSet::State;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+use Memcached;
+
+sub _hardcoded_states {
+ my $rs = shift;
+ my $open = $rs->new({ id => -1, label => 'confirmed', type => 'open', name => _("Open") });
+ my $closed = $rs->new({ id => -2, label => 'closed', type => 'closed', name => _("Closed") });
+ return ($open, $closed);
+}
+
+# As states will change rarely, and then only through the admin,
+# we cache these in the package on first use, and clear on update.
+
+sub clear {
+ Memcached::set('states', '');
+}
+
+sub states {
+ my $rs = shift;
+
+ my $states = Memcached::get('states');
+ if ($states && !FixMyStreet->test_mode) {
+ # Need to reattach schema
+ $states->[0]->result_source->schema( $rs->result_source->schema ) if $states->[0];
+ return $states;
+ }
+
+ # Pick up and cache any translations
+ my $q = $rs->result_source->schema->resultset("Translation")->search({
+ tbl => 'state',
+ col => 'name',
+ });
+ my %trans;
+ $trans{$_->object_id}{$_->lang} = { id => $_->id, msgstr => $_->msgstr } foreach $q->all;
+
+ my @states = ($rs->_hardcoded_states, $rs->search(undef, { order_by => 'label' })->all);
+ $_->translated->{name} = $trans{$_->id} || {} foreach @states;
+ $states = \@states;
+ Memcached::set('states', $states);
+ return $states;
+}
+
+# Some functions to provide filters on the above data
+
+sub open { [ $_[0]->_filter(sub { $_->type eq 'open' }) ] }
+sub closed { [ $_[0]->_filter(sub { $_->type eq 'closed' }) ] }
+sub fixed { [ $_[0]->_filter(sub { $_->type eq 'fixed' }) ] }
+
+# We sometimes have only a state label to display, no associated object.
+# This function can be used to return that label's display name.
+
+sub display {
+ my ($rs, $label, $single_fixed) = @_;
+ my $unchanging = {
+ unconfirmed => _("Unconfirmed"),
+ hidden => _("Hidden"),
+ partial => _("Partial"),
+ 'fixed - council' => _("Fixed - Council"),
+ 'fixed - user' => _("Fixed - User"),
+ };
+ $label = 'fixed' if $single_fixed && $label =~ /^fixed - (council|user)$/;
+ return $unchanging->{$label} if $unchanging->{$label};
+ my ($state) = $rs->_filter(sub { $_->label eq $label });
+ return $label unless $state;
+ return $state->msgstr;
+}
+
+sub _filter {
+ my ($rs, $fn) = @_;
+ my $states = $rs->states;
+ grep &$fn, @$states;
+}
+
+1;
diff --git a/perllib/FixMyStreet/Script/Questionnaires.pm b/perllib/FixMyStreet/Script/Questionnaires.pm
index 3f22eb150..ec6139d2d 100644
--- a/perllib/FixMyStreet/Script/Questionnaires.pm
+++ b/perllib/FixMyStreet/Script/Questionnaires.pm
@@ -16,6 +16,9 @@ sub send {
sub send_questionnaires_period {
my ( $period, $params ) = @_;
+ # Don't send if we don't have a fixed state
+ return unless FixMyStreet::DB::Result::Problem::fixed_states->{fixed};
+
my $rs = FixMyStreet::DB->resultset('Questionnaire');
# Select all problems that need a questionnaire email sending
diff --git a/perllib/FixMyStreet/TestAppProve.pm b/perllib/FixMyStreet/TestAppProve.pm
index f6e09fbe9..7a387547d 100644
--- a/perllib/FixMyStreet/TestAppProve.pm
+++ b/perllib/FixMyStreet/TestAppProve.pm
@@ -75,7 +75,7 @@ sub run {
$SIG{__WARN__} =
sub { print STDERR @_ if $_[0] !~ m/NOTICE: CREATE TABLE/; };
$dbh->do( path('db/schema.sql')->slurp ) or die $!;
- $dbh->do( path('db/alert_types.sql')->slurp ) or die $!;
+ $dbh->do( path('db/fixture.sql')->slurp ) or die $!;
$dbh->do( path('db/generate_secret.sql')->slurp ) or die $!;
$SIG{__WARN__} = $tmpwarn;
diff --git a/t/app/controller/admin_states.t b/t/app/controller/admin_states.t
new file mode 100644
index 000000000..60ffe5b88
--- /dev/null
+++ b/t/app/controller/admin_states.t
@@ -0,0 +1,24 @@
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $user = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1);
+
+$mech->log_in_ok( $user->email );
+
+subtest 'basic states admin' => sub {
+ $mech->get_ok('/admin/states');
+ $mech->submit_form_ok({ button => 'new', with_fields => { label => 'third party', type => 'closed', name => 'Third party referral' } });
+ $mech->content_contains('Third party referral');
+ $mech->content_contains('Fixed');
+ $mech->submit_form_ok({ button => 'delete:fixed' });
+ $mech->content_lacks('Fixed');
+ $mech->submit_form_ok({ form_number => 2, button => 'new_fixed' });
+ $mech->content_contains('Fixed');
+ $mech->submit_form_ok({ with_fields => { 'name:third party' => 'Third party incident' } });
+ $mech->content_contains('Third party incident');
+};
+
+# TODO Language tests
+
+done_testing;
diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t
index 9d424c1ae..b87b58b38 100644
--- a/t/app/controller/dashboard.t
+++ b/t/app/controller/dashboard.t
@@ -545,22 +545,11 @@ FixMyStreet::override_config {
},
{
desc => 'limit by state works',
- state => 'fixed',
+ state => 'fixed - council',
report_counts => [2,0,0],
report_counts_after => [1,0,0],
},
{
- desc => 'planned counted as action scheduled',
- p1 => {
- state => 'planned',
- conf_dt => DateTime->now(),
- category => 'Potholes',
- },
- state => 'action scheduled',
- report_counts => [3,0,0],
- report_counts_after => [1,0,0],
- },
- {
desc => 'All fixed states count as fixed',
p1 => {
state => 'fixed - council',
@@ -573,7 +562,7 @@ FixMyStreet::override_config {
category => 'Potholes',
},
state => 'fixed',
- report_counts => [5,0,0],
+ report_counts => [4,0,0],
report_counts_after => [3,0,0],
},
) {
@@ -612,7 +601,7 @@ FixMyStreet::override_config {
while ( my $row = $csv->getline( $data_handle ) ) {
push @rows, $row;
}
- is scalar @rows, 7, '1 (header) + 6 (reports) = 7 lines';
+ is scalar @rows, 6, '1 (header) + 5 (reports) = 6 lines';
};
};
restore_time;
diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t
index 093ea9cf8..4d73a5204 100644
--- a/t/app/controller/report_display.t
+++ b/t/app/controller/report_display.t
@@ -313,7 +313,7 @@ for my $test (
date => DateTime->now,
state => 'investigating',
banner_id => 'progress',
- banner_text => 'progress',
+ banner_text => 'investigating',
fixed => 0
},
{
@@ -321,7 +321,7 @@ for my $test (
date => DateTime->now,
state => 'action scheduled',
banner_id => 'progress',
- banner_text => 'progress',
+ banner_text => 'action scheduled',
fixed => 0
},
{
@@ -329,7 +329,7 @@ for my $test (
date => DateTime->now,
state => 'planned',
banner_id => 'progress',
- banner_text => 'progress',
+ banner_text => 'planned',
fixed => 0
},
{
diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t
index fcd7c724d..68f9063cf 100644
--- a/t/app/controller/report_inspect.t
+++ b/t/app/controller/report_inspect.t
@@ -57,7 +57,7 @@ FixMyStreet::override_config {
};
subtest "test basic inspect submission" => sub {
- $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Yes', state => 'Action Scheduled', include_update => undef } });
+ $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Yes', state => 'Action scheduled', include_update => undef } });
$report->discard_changes;
my $alert = FixMyStreet::App->model('DB::Alert')->find(
{ user => $user, alert_type => 'new_updates', confirmed => 1, }
@@ -90,9 +90,8 @@ FixMyStreet::override_config {
is $report->user->get_extra_metadata('reputation'), $reputation, "User reputation wasn't changed";
$mech->get_ok("/report/$report_id");
my $meta = $mech->extract_update_metas;
- like $meta->[0], qr/Updated by .*action scheduled/, 'First update mentions action scheduled';
- like $meta->[1], qr/Posted by .*defect raised/, 'Update mentions defect raised';
- unlike $meta->[2], qr/Posted by .*action scheduled/, 'Update does not mention action scheduled';
+ like $meta->[0], qr/State changed to: Action scheduled/, 'First update mentions action scheduled';
+ like $meta->[2], qr/Posted by .*defect raised/, 'Update mentions defect raised';
$user->unset_extra_metadata('categories');
$user->update;
@@ -300,7 +299,7 @@ FixMyStreet::override_config {
subtest "Oxfordshire-specific traffic management options are shown" => sub {
$report->update({ state => 'confirmed' });
$mech->get_ok("/report/$report_id");
- $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Signs and Cones', state => 'Action Scheduled', include_update => undef } });
+ $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Signs and Cones', state => 'Action scheduled', include_update => undef } });
$report->discard_changes;
is $report->state, 'action scheduled', 'report state changed';
is $report->get_extra_metadata('traffic_information'), 'Signs and Cones', 'report data changed';
diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t
index 4cb035bac..0526b2fd7 100644
--- a/t/app/controller/report_updates.t
+++ b/t/app/controller/report_updates.t
@@ -93,8 +93,7 @@ for my $test (
anonymous => 't',
mark_fixed => 'true',
mark_open => 'false',
- meta =>
-'Posted anonymously at 15:47, Sat 16 April 2011, marked as fixed',
+ meta => [ 'State changed to: Fixed', 'Posted anonymously at 15:47, Sat 16 April 2011' ]
},
{
description => 'named user, anon is true, reopened',
@@ -102,7 +101,7 @@ for my $test (
anonymous => 't',
mark_fixed => 'false',
mark_open => 'true',
- meta => 'Posted anonymously at 15:47, Sat 16 April 2011, reopened',
+ meta => [ 'State changed to: Open', 'Posted anonymously at 15:47, Sat 16 April 2011' ]
}
)
{
@@ -118,8 +117,9 @@ for my $test (
$mech->content_contains('This is some update text');
my $meta = $mech->extract_update_metas;
- is scalar @$meta, 1, 'number of updates';
- is $meta->[0], $test->{meta};
+ my $test_meta = ref $test->{meta} ? $test->{meta} : [ $test->{meta} ];
+ is scalar @$meta, scalar @$test_meta, 'number of updates';
+ is_deeply $meta, $test_meta;
};
}
@@ -131,7 +131,7 @@ subtest "updates displayed on report with empty bodies_str" => sub {
$mech->get_ok("/report/$report_id");
my $meta = $mech->extract_update_metas;
- is scalar @$meta, 1, 'update displayed';
+ is scalar @$meta, 2, 'update displayed';
$report->update({ bodies_str => $old_bodies_str });
};
@@ -185,10 +185,11 @@ subtest "several updates shown in correct order" => sub {
$mech->get_ok("/report/$report_id");
my $meta = $mech->extract_update_metas;
- is scalar @$meta, 3, 'number of updates';
+ is scalar @$meta, 4, 'number of updates';
is $meta->[0], 'Posted by Other User at 12:23, Thu 10 March 2011', 'first update';
is $meta->[1], 'Posted by Main User at 12:23, Thu 10 March 2011', 'second update';
- is $meta->[2], 'Posted anonymously at 08:12, Tue 15 March 2011, marked as fixed', 'third update';
+ is $meta->[2], 'State changed to: Fixed', 'third update, part 1';
+ is $meta->[3], 'Posted anonymously at 08:12, Tue 15 March 2011', 'third update, part 2';
};
for my $test (
@@ -613,7 +614,6 @@ for my $test (
state => 'internal referral',
},
state => 'internal referral',
- meta => "an internal referral",
},
{
desc => 'from authority user marks report as not responsible',
@@ -635,7 +635,6 @@ for my $test (
state => 'duplicate',
},
state => 'duplicate',
- meta => 'a duplicate report',
},
{
desc => 'from authority user marks report as internal referral',
@@ -646,7 +645,6 @@ for my $test (
state => 'internal referral',
},
state => 'internal referral',
- meta => 'an internal referral',
},
{
desc => 'from authority user marks report sent to two councils as fixed',
@@ -711,19 +709,13 @@ for my $test (
my $update_meta = $mech->extract_update_metas;
my $meta_state = $test->{meta} || $test->{fields}->{state};
- if ( $test->{reopened} ) {
- like $update_meta->[0], qr/reopened/, 'update meta says reopened';
- } elsif ( $test->{state} eq 'duplicate' ) {
- like $update_meta->[0], qr/closed as $meta_state/, 'update meta includes state change';
- } else {
- like $update_meta->[0], qr/marked as $meta_state/, 'update meta includes state change';
- }
+ like $update_meta->[0], qr/$meta_state/i, 'update meta includes state change';
if ($test->{view_username}) {
- like $update_meta->[0], qr{Westminster City Council \(Test User\)}, 'update meta includes council and user name';
+ like $update_meta->[1], qr{Westminster City Council \(Test User\)}, 'update meta includes council and user name';
$user->user_body_permissions->delete_all;
} else {
- like $update_meta->[0], qr{Westminster City Council}, 'update meta includes council name';
+ like $update_meta->[1], qr{Westminster City Council}, 'update meta includes council name';
$mech->content_contains( '<strong>Westminster City Council</strong>', 'council name in bold');
}
@@ -751,24 +743,22 @@ subtest 'check meta correct for comments marked confirmed but not marked open' =
$mech->get_ok( "/report/" . $report->id );
my $update_meta = $mech->extract_update_metas;
- unlike $update_meta->[0], qr/reopened/,
+ unlike $update_meta->[0], qr/Open/,
'update meta does not say reopened';
$comment->update( { mark_open => 1, problem_state => undef } );
$mech->get_ok( "/report/" . $report->id );
$update_meta = $mech->extract_update_metas;
- unlike $update_meta->[0], qr/marked as open/,
- 'update meta does not says marked as open';
- like $update_meta->[0], qr/reopened/, 'update meta does say reopened';
+ like $update_meta->[0], qr/Open/, 'update meta does say open';
$comment->update( { mark_open => 0, problem_state => undef } );
$mech->get_ok( "/report/" . $report->id );
$update_meta = $mech->extract_update_metas;
- unlike $update_meta->[0], qr/marked as open/,
+ unlike $update_meta->[0], qr/Open/,
'update meta does not says marked as open';
- unlike $update_meta->[0], qr/reopened/, 'update meta does not say reopened';
+ unlike $update_meta->[0], qr/Open/, 'update meta does not say reopened';
};
subtest "check first comment with no status change has no status in meta" => sub {
@@ -782,7 +772,7 @@ subtest "check first comment with no status change has no status in meta" => sub
$mech->get_ok("/report/$report_id");
my $update_meta = $mech->extract_update_metas;
- unlike $update_meta->[0], qr/marked as|reopened/, 'update meta does not include state change';
+ unlike $update_meta->[0], qr/State changed to/, 'update meta does not include state change';
};
subtest "check comment with no status change has not status in meta" => sub {
@@ -820,7 +810,7 @@ subtest "check comment with no status change has not status in meta" => sub {
is $report->state, 'fixed - council', 'correct report state';
is $update->problem_state, 'fixed - council', 'correct update state';
my $update_meta = $mech->extract_update_metas;
- unlike $update_meta->[1], qr/marked as/, 'update meta does not include state change';
+ unlike $update_meta->[1], qr/State changed to/, 'update meta does not include state change';
$user->from_body( $body->id );
$user->update;
@@ -854,9 +844,9 @@ subtest "check comment with no status change has not status in meta" => sub {
is $report->state, 'investigating', 'correct report state';
is $update->problem_state, 'investigating', 'correct update state';
$update_meta = $mech->extract_update_metas;
- like $update_meta->[0], qr/marked as fixed/, 'first update meta says fixed';
- unlike $update_meta->[1], qr/marked as/, 'second update meta does not include state change';
- like $update_meta->[2], qr/marked as investigating/, 'third update meta says investigating';
+ like $update_meta->[0], qr/fixed/i, 'first update meta says fixed';
+ unlike $update_meta->[2], qr/State changed to/, 'second update meta does not include state change';
+ like $update_meta->[3], qr/investigating/i, 'third update meta says investigating';
my $dt = DateTime->now( time_zone => "local" )->add( seconds => 1 );
$comment = FixMyStreet::App->model('DB::Comment')->find_or_create(
@@ -883,10 +873,10 @@ subtest "check comment with no status change has not status in meta" => sub {
is $report->state, 'investigating', 'correct report state';
is $update->problem_state, undef, 'no update state';
$update_meta = $mech->extract_update_metas;
- like $update_meta->[0], qr/marked as fixed/, 'first update meta says fixed';
- unlike $update_meta->[1], qr/marked as/, 'second update meta does not include state change';
- like $update_meta->[2], qr/marked as investigating/, 'third update meta says investigating';
- unlike $update_meta->[3], qr/marked as/, 'fourth update meta has no state change';
+ like $update_meta->[0], qr/fixed/i, 'first update meta says fixed';
+ unlike $update_meta->[2], qr/State changed to/, 'second update meta does not include state change';
+ like $update_meta->[3], qr/investigating/i, 'third update meta says investigating';
+ unlike $update_meta->[5], qr/State changed to/, 'fourth update meta has no state change';
};
subtest 'check meta correct for second comment marking as reopened' => sub {
@@ -907,7 +897,7 @@ subtest 'check meta correct for second comment marking as reopened' => sub {
$mech->get_ok( "/report/" . $report->id );
my $update_meta = $mech->extract_update_metas;
- like $update_meta->[0], qr/fixed/, 'update meta says fixed';
+ like $update_meta->[0], qr/fixed/i, 'update meta says fixed';
$comment = FixMyStreet::App->model('DB::Comment')->create(
{
@@ -925,7 +915,7 @@ subtest 'check meta correct for second comment marking as reopened' => sub {
$mech->get_ok( "/report/" . $report->id );
$update_meta = $mech->extract_update_metas;
- like $update_meta->[1], qr/reopened/, 'update meta says reopened';
+ like $update_meta->[2], qr/Open/, 'update meta says reopened';
};
subtest "check first comment with status change but no text is displayed" => sub {
@@ -953,9 +943,9 @@ subtest "check first comment with status change but no text is displayed" => sub
$mech->get_ok("/report/$report_id");
my $update_meta = $mech->extract_update_metas;
- like $update_meta->[0], qr/Updated by/, 'updated by meta if no text';
- unlike $update_meta->[0], qr/Test User/, 'commenter name not included';
- like $update_meta->[0], qr/investigating/, 'update meta includes state change';
+ like $update_meta->[1], qr/Updated by/, 'updated by meta if no text';
+ unlike $update_meta->[1], qr/Test User/, 'commenter name not included';
+ like $update_meta->[0], qr/investigating/i, 'update meta includes state change';
ok $user->user_body_permissions->create({
body => $body,
@@ -964,9 +954,9 @@ subtest "check first comment with status change but no text is displayed" => sub
$mech->get_ok("/report/$report_id");
$update_meta = $mech->extract_update_metas;
- like $update_meta->[0], qr/Updated by/, 'updated by meta if no text';
- like $update_meta->[0], qr/Test User/, 'commenter name included if user has view contribute permission';
- like $update_meta->[0], qr/investigating/, 'update meta includes state change';
+ like $update_meta->[1], qr/Updated by/, 'updated by meta if no text';
+ like $update_meta->[1], qr/Test User/, 'commenter name included if user has view contribute permission';
+ like $update_meta->[0], qr/investigating/i, 'update meta includes state change';
};
@@ -1712,10 +1702,10 @@ for my $test (
desc => 'update unable to fix without marking as fixed leaves state unchanged',
initial_state => 'unable to fix',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 0,
+ reopen => 0,
},
end_state => 'unable to fix',
},
@@ -1723,10 +1713,10 @@ for my $test (
desc => 'update internal referral without marking as fixed leaves state unchanged',
initial_state => 'internal referral',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 0,
+ reopen => 0,
},
end_state => 'internal referral',
},
@@ -1734,10 +1724,10 @@ for my $test (
desc => 'update not responsible without marking as fixed leaves state unchanged',
initial_state => 'not responsible',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 0,
+ reopen => 0,
},
end_state => 'not responsible',
},
@@ -1745,10 +1735,10 @@ for my $test (
desc => 'update duplicate without marking as fixed leaves state unchanged',
initial_state => 'duplicate',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 0,
+ reopen => 0,
},
end_state => 'duplicate',
},
@@ -1808,48 +1798,48 @@ for my $test (
end_state => 'confirmed',
},
{
- desc => 'can mark unable to fix as fixed, cannot mark not closed',
+ desc => 'cannot mark unable to fix as fixed, can reopen',
initial_state => 'unable to fix',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 1,
+ reopen => 1,
},
- end_state => 'fixed - user',
+ end_state => 'confirmed',
},
{
- desc => 'can mark internal referral as fixed, cannot mark not closed',
+ desc => 'cannot mark internal referral as fixed, can reopen',
initial_state => 'internal referral',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 1,
+ reopen => 1,
},
- end_state => 'fixed - user',
+ end_state => 'confirmed',
},
{
- desc => 'can mark not responsible as fixed, cannot mark not closed',
+ desc => 'cannot mark not responsible as fixed, can reopen',
initial_state => 'not responsible',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 1,
+ reopen => 1,
},
- end_state => 'fixed - user',
+ end_state => 'confirmed',
},
{
- desc => 'can mark duplicate as fixed, cannot mark not closed',
+ desc => 'cannot mark duplicate as fixed, can reopen',
initial_state => 'duplicate',
expected_form_fields => {
- fixed => undef,
+ reopen => undef,
},
submitted_form_fields => {
- fixed => 1,
+ reopen => 1,
},
- end_state => 'fixed - user',
+ end_state => 'confirmed',
},
) {
subtest $test->{desc} => sub {
diff --git a/t/app/model/state.t b/t/app/model/state.t
new file mode 100644
index 000000000..1653e36e2
--- /dev/null
+++ b/t/app/model/state.t
@@ -0,0 +1,62 @@
+use FixMyStreet::Test;
+use Test::More;
+
+my $rs = FixMyStreet::DB->resultset('State');
+my $trans_rs = FixMyStreet::DB->resultset('Translation');
+
+for (
+ { label => 'in progress', lang => 'de' },
+ { label => 'investigating', lang => 'fr' },
+ { label => 'duplicate', lang => 'de' },
+) {
+ my $lang = $_->{lang};
+ my $obj = $rs->find({ label => $_->{label} });
+ $trans_rs->create({ tbl => 'state', col => 'name', object_id => $obj->id,
+ lang => $lang, msgstr => "$lang $_->{label}" });
+}
+
+my $states = $rs->states;
+my %states = map { $_->label => $_ } @$states;
+
+subtest 'Open/closed database data is as expected' => sub {
+ my $open = $rs->open;
+ is @$open, 5;
+ my $closed = $rs->closed;
+ is @$closed, 5;
+};
+
+is $rs->display('investigating'), 'Investigating';
+is $rs->display('bad'), 'bad';
+is $rs->display('confirmed'), 'Open';
+is $rs->display('closed'), 'Closed';
+is $rs->display('fixed - council'), 'Fixed - Council';
+is $rs->display('fixed - user'), 'Fixed - User';
+is $rs->display('fixed'), 'Fixed';
+
+subtest 'default name is untranslated' => sub {
+ is $states{'in progress'}->name, 'In progress';
+ is $states{'in progress'}->msgstr, 'In progress';
+ is $states{'action scheduled'}->name, 'Action scheduled';
+ is $states{'action scheduled'}->msgstr, 'Action scheduled';
+};
+
+subtest 'msgstr gets translated if available when the language changes' => sub {
+ FixMyStreet::DB->schema->lang('de');
+ is $states{'in progress'}->name, 'In progress';
+ is $states{'in progress'}->msgstr, 'de in progress';
+ is $states{'investigating'}->name, 'Investigating';
+ is $states{'investigating'}->msgstr, 'Investigating';
+ is $states{'unable to fix'}->name, 'No further action';
+ is $states{'unable to fix'}->msgstr, 'No further action';
+};
+
+$rs->clear;
+
+is_deeply [ sort FixMyStreet::DB::Result::Problem->open_states ],
+ ['action scheduled', 'confirmed', 'in progress', 'investigating', 'planned'], 'open states okay';
+is_deeply [ sort FixMyStreet::DB::Result::Problem->closed_states ],
+ ['closed', 'duplicate', 'internal referral', 'not responsible', 'unable to fix'], 'closed states okay';
+is_deeply [ sort FixMyStreet::DB::Result::Problem->fixed_states ],
+ ['fixed', 'fixed - council', 'fixed - user'], 'fixed states okay';
+
+done_testing();
diff --git a/t/cobrand/bromley.t b/t/cobrand/bromley.t
index 46c2472cd..f3053c29a 100644
--- a/t/cobrand/bromley.t
+++ b/t/cobrand/bromley.t
@@ -41,9 +41,9 @@ for my $update ('in progress', 'unable to fix') {
# Test Bromley special casing of 'unable to fix'
$mech->get_ok( '/report/' . $report->id );
$mech->content_contains( 'marks it as in progress' );
-$mech->content_contains( 'marked as in progress' );
+$mech->content_contains( 'State changed to: In progress' );
$mech->content_contains( 'marks it as unable to fix' );
-$mech->content_contains( 'marked as no further action' );
+$mech->content_contains( 'State changed to: No further action' );
subtest 'testing special Open311 behaviour', sub {
$report->set_extra_fields();
diff --git a/templates/web/base/admin/problem_row.html b/templates/web/base/admin/problem_row.html
index 79461367e..446e94d66 100644
--- a/templates/web/base/admin/problem_row.html
+++ b/templates/web/base/admin/problem_row.html
@@ -34,8 +34,8 @@
[% loc('Created') %]:&nbsp;[% PROCESS format_time time=problem.created %]
<br>[% loc('When sent') %]:&nbsp;[% PROCESS format_time time=problem.whensent %]
[%- IF problem.is_visible %]<br>[% loc('Confirmed:' ) %]&nbsp;[% PROCESS format_time time=problem.confirmed %][% END -%]
- [%- IF problem.is_fixed %]<br>[% loc('Fixed:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]
- [%- IF problem.is_closed %]<br>[% loc('Closed:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]
+ [%- IF problem.is_fixed %]<br>[% prettify_state('fixed') %]: [% PROCESS format_time time=problem.lastupdate %][% END -%]
+ [%- IF problem.is_closed %]<br>[% prettify_state('closed') %]: [% PROCESS format_time time=problem.lastupdate %][% END -%]
[%- IF problem.is_open %]<br>[% loc('Last&nbsp;update:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]
</small></td>
<td><a href="[% c.uri_for( 'report_edit', problem.id ) %]">[% loc('Edit') %]</a></td>
diff --git a/templates/web/base/admin/report_blocks.html b/templates/web/base/admin/report_blocks.html
index 933521b94..f5896b88f 100644
--- a/templates/web/base/admin/report_blocks.html
+++ b/templates/web/base/admin/report_blocks.html
@@ -1,13 +1,7 @@
[%
SET report_blocks_included = 1;
-
-SET state_groups = [
- [ loc('Open'), [ 'confirmed', 'investigating', 'planned', 'in progress', 'action scheduled' ] ],
- [ loc('Fixed'), [ 'fixed', 'fixed - user', 'fixed - council' ] ],
- [ loc('Closed'), [ 'unable to fix', 'not responsible', 'duplicate', 'closed', 'internal referral' ] ],
- [ loc('Hidden'), [ 'hidden', 'partial', 'unconfirmed' ] ]
-];
+SET state_groups = c.cobrand.state_groups_admin;
%]
diff --git a/templates/web/base/admin/states/index.html b/templates/web/base/admin/states/index.html
new file mode 100644
index 000000000..bd87f5013
--- /dev/null
+++ b/templates/web/base/admin/states/index.html
@@ -0,0 +1,118 @@
+[% INCLUDE 'admin/header.html' title=loc('States') ~%]
+
+[% SET rows = languages.size + 1 IF languages.size > 1 ~%]
+
+<form method="post" accept-charset="utf-8">
+
+<table>
+ <tr>
+ <th>Label</th>
+ <th>Type</th>
+ <th colspan="2">Name</th>
+ <th>&nbsp;</th>
+ </tr>
+ [% FOREACH state IN open_states.merge(closed_states).merge(fixed_states) %]
+ <tr>
+ <td rowspan="[% rows %]">
+ [% IF state.label == 'confirmed' %]
+ open
+ [% ELSE %]
+ [% state.label %]
+ [% END %]
+ </td>
+ <td rowspan="[% rows %]">
+ [% IF state.label == 'confirmed' %]
+ [% loc('Open') %]<input type="hidden" name="type:confirmed" value="open">
+ [% ELSIF state.label == 'closed' %]
+ [% loc('Closed') %]<input type="hidden" name="type:closed" value="closed">
+ [% ELSIF state.label == 'fixed' %]
+ [% loc('Fixed') %]<input type="hidden" name="type:fixed" value="fixed">
+ [% ELSE %]
+ <select name="type:[% state.label %]">
+ <option value="open"[% ' selected' IF state.type == 'open' %]>[% loc('Open') %]</option>
+ <option value="closed"[% ' selected' IF state.type == 'closed' %]>[% loc('Closed') %]</option>
+ </select>
+ [% END %]
+ </td>
+ <td colspan="2">
+ [% IF state.label != 'confirmed' AND state.label != 'closed' %]
+ <input type="text" name="name:[% state.label %]" value="[% state.name %]">
+ [% ELSIF languages.size == 1 %]
+ <input type="text" name="name:[% state.label %]" value="[% state.msgstr %]">
+ [% ELSE %]
+ [% state.name %]
+ [% END %]
+ </td>
+ <td style="text-align:center;vertical-align:middle" rowspan="[% rows %]">
+ [% IF state.label != 'confirmed' AND state.label != 'closed' %]
+ <input class="btn btn--small" type="submit" name="delete:[% state.label %]" value="Delete">
+ [% END %]
+ </td>
+ </tr>
+ [% IF languages.size > 1 %]
+ [% FOREACH language IN languages.keys.sort %]
+ <tr>
+ <td style="vertical-align:middle; text-align:right;">
+ <label style="margin:0" for="translation:[% state.label %]:[% language %]">
+ [% languages.$language.name %] ([% language %])
+ </label>
+ </td>
+ <td>
+ <input type="hidden" name="translation_id:[% state.label %]:[% language %]"
+ value="[% state.translated.name.$language.id %]">
+ <input type="text" name="translation:[% state.label %]:[% language %]"
+ id="translation:[% state.label %]:[% language %]" value="[% state.translated.name.$language.msgstr %]">
+ </td>
+ </tr>
+ [% END %]
+ [% END %]
+ </td>
+ </tr>
+ [% END %]
+</table>
+
+ <p>
+ <input type="hidden" name="token" value="[% csrf_token %]">
+ <input type="submit" class="btn" value="[% loc('Update') %]">
+ </p>
+
+</form>
+
+[% IF fixed_states.size == 0 %]
+<form method="post" accept-charset="utf-8">
+ <p>
+ <input type="hidden" name="token" value="[% csrf_token %]">
+ <input type="submit" class="btn" name="new_fixed" value="[% loc('Add fixed state') %]">
+ </p>
+</form>
+
+[% END %]
+
+<h2>[% loc('New state') %]</h2>
+
+<form method="post" accept-charset="utf-8">
+ <p>
+ <label for="label">[% loc('Label') %] <small>[% loc('(a-z and space only)') %]</small></label>
+ <input type="text" class="form-control" name="label" id="label" value="" size="20" pattern="[a-z ]+">
+ </p>
+
+ <p>
+ <label for="type">[% loc('Label') %]</label>
+ <select name="type" id="type">
+ <option value="open">[% loc('Open') %]</option>
+ <option value="closed">[% loc('Closed') %]</option>
+ </select>
+ </p>
+
+ <p>
+ <label for="name">[% loc('Name') %]</label>
+ <input type="text" class="form-control" name="name" id="name" value="" size="20">
+ </p>
+
+ <p>
+ <input type="hidden" name="token" value="[% csrf_token %]">
+ <input type="submit" class="btn" name="new" value="[% loc('Create') %]">
+ </p>
+</form>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/base/dashboard/index.html b/templates/web/base/dashboard/index.html
index 6033ef36b..e47798573 100644
--- a/templates/web/base/dashboard/index.html
+++ b/templates/web/base/dashboard/index.html
@@ -136,11 +136,12 @@
</select>
<p>[% loc('Report state:') %] <select class="form-control" name="state">
<option value=''>[% loc('All') %]</option>
- [% FOREACH state IN [ ['confirmed', loc('Open')], ['investigating',
- loc('Investigating')], ['action scheduled', loc('Planned')], ['in progress',
- loc('In Progress')], ['closed', loc('Closed')], ['fixed', loc('Fixed')] ] %]
- <option [% 'selected ' IF state.0 == q_state %] value="[% state.0 %]">[% state.1 %]</option>
- [% END %]
+ [% FOR group IN filter_states %]
+ [% FOR state IN group.1 %]
+ [% NEXT IF state == 'hidden' %]
+ <option [% 'selected ' IF state == q_state %] value="[% state %]">[% prettify_state(state, 1) %]</option>
+ [% END %]
+ [% END %]
</select>
<input type="submit" class="btn" value="[% loc('Look up') %]">
<a class="export_as_csv" href="[% c.req.uri_with({ export => 1 }) %]">[% loc('Export as CSV') %]</a>
diff --git a/templates/web/base/front/stats.html b/templates/web/base/front/stats.html
index eb671137b..41358c869 100644
--- a/templates/web/base/front/stats.html
+++ b/templates/web/base/front/stats.html
@@ -35,7 +35,9 @@
<div id="front_stats">
<div>[% tprintf( new_text, decode(new_n) ) %]</div>
+ [% IF has_fixed_state %]
<div>[% tprintf( fixed_text, decode(fixed_n) ) %]</div>
+ [% END %]
[% IF c.cobrand.moniker != 'zurich' %]
<div>[% tprintf( updates_text, decode(updates_n) ) %]</div>
[% END %]
diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html
index 3f5c5464b..9449ca55d 100644
--- a/templates/web/base/report/_item.html
+++ b/templates/web/base/report/_item.html
@@ -40,9 +40,9 @@
[% END %]
<small>
[% IF NOT no_fixed AND problem.is_fixed %]
- <span class="item-list__item__state">[% loc('Fixed') %]</span>
+ <span class="item-list__item__state">[% prettify_state('fixed') %]</span>
[% ELSIF NOT no_fixed AND problem.is_closed %]
- <span class="item-list__item__state">[% loc('Closed') %]</span>
+ <span class="item-list__item__state">[% prettify_state('closed') %]</span>
[% ELSIF (c.user.has_permission_to('report_edit_priority', problem.bodies_str_ids) OR c.user.has_permission_to('report_inspect', problem.bodies_str_ids)) AND problem.response_priority %]
<span class="item-list__item__state">[% problem.response_priority.name %]</span>
[% END %]
diff --git a/templates/web/base/report/banner.html b/templates/web/base/report/banner.html
index c80d129eb..ee73d287a 100644
--- a/templates/web/base/report/banner.html
+++ b/templates/web/base/report/banner.html
@@ -9,11 +9,11 @@
[% INCLUDE banner, id = 'unknown', text = loc('Unknown') %]
[% END %]
[% IF problem.is_fixed %]
- [% INCLUDE banner, id = 'fixed', text = loc('Fixed') %]
+ [% INCLUDE banner, id = 'fixed', text = prettify_state('fixed') %]
[% END %]
[% IF problem.is_closed %]
- [% INCLUDE banner, id = 'closed', text = loc('Closed') %]
+ [% INCLUDE banner, id = 'closed', text = prettify_state('closed') %]
[% END %]
[% IF problem.is_in_progress %]
- [% INCLUDE banner, id = 'progress', text = loc('In progress') %]
+ [% INCLUDE banner, id = 'progress', text = prettify_state(problem.state) %]
[% END %]
diff --git a/templates/web/base/report/inspect/state_groups_select.html b/templates/web/base/report/inspect/state_groups_select.html
index 2cf1a4de5..998b99739 100644
--- a/templates/web/base/report/inspect/state_groups_select.html
+++ b/templates/web/base/report/inspect/state_groups_select.html
@@ -1,11 +1,6 @@
[%
-SET state_groups = [
- [ loc('Open'), [ 'confirmed', 'investigating', 'in progress', 'action scheduled' ] ],
- [ loc('Fixed'), [ 'fixed - council' ] ],
- [ loc('Closed'), [ 'unable to fix', 'not responsible', 'duplicate', 'internal referral' ] ],
- [ loc('Hidden'), [ 'hidden' ] ]
-];
+SET state_groups = c.cobrand.state_groups_inspect;
%]
[% DEFAULT current_state = problem.state %]
diff --git a/templates/web/base/report/state-list.html b/templates/web/base/report/state-list.html
deleted file mode 100644
index e137c81e2..000000000
--- a/templates/web/base/report/state-list.html
+++ /dev/null
@@ -1,21 +0,0 @@
-[%
-SET state_pretty = {
- 'confirmed' = loc('Open')
- 'investigating' = loc('Investigating')
- 'planned' = loc('Planned')
- 'in progress' = loc('In progress')
- 'action scheduled' = loc('Action Scheduled')
- 'fixed' = loc('Fixed')
- 'fixed - user' = loc('Fixed - User')
- 'fixed - council' = loc('Fixed - Council')
- 'unable to fix' = loc('No further action')
- 'not responsible' = loc('Not Responsible')
- 'duplicate' = loc('Duplicate')
- 'closed' = loc('Closed')
- 'internal referral' = loc('Internal referral')
- 'hidden' = loc('Hidden')
- 'partial' = loc('Partial')
- 'unconfirmed' = loc('Unconfirmed')
-};
-state_pretty.$state
-~%]
diff --git a/templates/web/base/report/update.html b/templates/web/base/report/update.html
index 1f1438bfc..85624669a 100644
--- a/templates/web/base/report/update.html
+++ b/templates/web/base/report/update.html
@@ -43,6 +43,12 @@
</div>
[% END %]
+ [% SET update_state = update.problem_state_display(c) %]
+ [% IF update_state AND update_state != global.last_state AND NOT (global.last_state == "" AND update.problem_state == 'confirmed') %]
+ <p class="meta-2">[% loc('State changed to:') %] [% update_state %]</p>
+ [%- global.last_state = update_state %]
+ [% END %]
+
<p class="meta-2">
[% INCLUDE meta_line %]
[% IF c.user_exists AND c.user.id == update.user_id AND !update.anonymous %]
diff --git a/templates/web/base/report/update/form_update.html b/templates/web/base/report/update/form_update.html
index 34abf53c5..5a1b3b602 100644
--- a/templates/web/base/report/update/form_update.html
+++ b/templates/web/base/report/update/form_update.html
@@ -39,7 +39,7 @@
<label for="state">[% loc( 'State' ) %]</label>
[% INCLUDE 'report/inspect/state_groups_select.html' %]
[% ELSE %]
- [% IF (problem.is_fixed OR problem.state == 'closed') AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %]
+ [% IF (problem.is_fixed OR problem.is_closed) AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %]
<input type="checkbox" name="reopen" id="form_reopen" value="1"[% ' checked' IF (update.mark_open || c.req.params.reopen) %]>
[% IF problem.is_closed %]
@@ -48,7 +48,7 @@
<label class="inline" for="form_reopen">[% loc('This problem has not been fixed') %]</label>
[% END %]
- [% ELSIF !problem.is_fixed %]
+ [% ELSIF !problem.is_fixed AND has_fixed_state %]
<div class="checkbox-group">
<input type="checkbox" name="fixed" id="form_fixed" value="1"[% ' checked' IF update.mark_fixed %]>
diff --git a/templates/web/base/reports/_list-filters.html b/templates/web/base/reports/_list-filters.html
index ef7c7ad78..50e88857d 100644
--- a/templates/web/base/reports/_list-filters.html
+++ b/templates/web/base/reports/_list-filters.html
@@ -1,6 +1,6 @@
[% select_status = BLOCK %]
<select class="form-control js-multiple" name="status" id="statuses" multiple
- data-all="[% loc('All reports') %]" data-all-options='["open","closed","fixed"]'
+ data-all="[% loc('All') %]" data-all-options='["open","closed","fixed"]'
[%~ IF c.cobrand.on_map_default_status == 'open' %]
data-none="[% loc('Unfixed reports') %]"
[%~ END ~%]
@@ -10,19 +10,18 @@
<option value="unshortlisted"[% ' selected' IF filter_status.unshortlisted %]>[% loc('Unshortlisted') %]</option>
[% END %]
[% IF c.user_exists AND c.user.is_superuser OR c.user.belongs_to_body(body.id) %]
- <option value="confirmed"[% ' selected' IF filter_status.confirmed %]>[% loc('Open') %]</option>
- <option value="investigating"[% ' selected' IF filter_status.investigating %]>[% loc('Investigating') %]</option>
- <option value="in progress"[% ' selected' IF filter_status.in_progress %]>[% loc('In progress') %]</option>
- <option value="action scheduled"[% ' selected' IF filter_status.action_scheduled %]>[% loc('Action scheduled') %]</option>
- <option value="fixed"[% ' selected' IF filter_status.fixed %]>[% loc('Fixed reports') %]</option>
- <option value="unable to fix"[% ' selected' IF filter_status.unable_to_fix %]>[% loc('No further action') %]</option>
- <option value="not responsible"[% ' selected' IF filter_status.not_responsible %]>[% loc('Not responsible') %]</option>
- <option value="internal referral"[% ' selected' IF filter_status.internal_referral %]>[% loc('Internal referral') %]</option>
- <option value="duplicate"[% ' selected' IF filter_status.duplicate %]>[% loc('Duplicate') %]</option>
+ [% FOR group IN filter_states %]
+ [% FOR state IN group.1 %]
+ [% NEXT IF state == 'hidden' %]
+ <option value="[% state %]"[% ' selected' IF filter_status.$state %]>[% prettify_state(state, 1) %]</option>
+ [% END %]
+ [% END %]
[% ELSE %]
- <option value="open"[% ' selected' IF filter_status.open %]>[% loc('Unfixed reports') %]</option>
- <option value="closed"[% ' selected' IF filter_status.closed %]>[% loc('Closed reports') %]</option>
- <option value="fixed"[% ' selected' IF filter_status.fixed %]>[% loc('Fixed reports') %]</option>
+ <option value="open"[% ' selected' IF filter_status.open %]>[% prettify_state('confirmed') %]</option>
+ <option value="closed"[% ' selected' IF filter_status.closed %]>[% prettify_state('closed') %]</option>
+ [% IF has_fixed_state %]
+ <option value="fixed"[% ' selected' IF filter_status.fixed %]>[% prettify_state('fixed') %]</option>
+ [% END %]
[% END %]
</select>
[% END %]
@@ -46,7 +45,7 @@
[% END %]
<p class="report-list-filters">
- [% tprintf(loc('<label for="statuses">Show</label> %s <label for="filter_categories">about</label> %s', "The first %s is a dropdown of all/fixed/etc, the second is a dropdown of categories"), select_status, select_category) %]
+ [% tprintf(loc('<label for="statuses">Show</label> %s reports <label for="filter_categories">about</label> %s', "The first %s is a dropdown of all/fixed/etc, the second is a dropdown of categories"), select_status, select_category) %]
<input type="submit" name="filter_update" value="[% loc('Go') %]">
</p>
diff --git a/templates/web/oxfordshire/report/inspect/state_groups_select.html b/templates/web/oxfordshire/report/inspect/state_groups_select.html
deleted file mode 100644
index d36fb0191..000000000
--- a/templates/web/oxfordshire/report/inspect/state_groups_select.html
+++ /dev/null
@@ -1,12 +0,0 @@
-[%
-
-SET state_groups = [
- [ loc('New'), [ 'confirmed', 'investigating' ] ],
- [ loc('Scheduled'), [ 'action scheduled' ] ],
- [ loc('Fixed'), [ 'fixed - council' ] ],
- [ loc('Closed'), [ 'not responsible', 'duplicate', 'unable to fix' ] ]
-];
-
-%]
-[% DEFAULT current_state = problem.state %]
-[% INCLUDE 'report/_state_select_field.html' single_fixed=1 %]