aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Arter <davea@mysociety.org>2016-09-06 15:37:42 +0100
committerDave Arter <davea@mysociety.org>2016-09-09 14:14:37 +0100
commit54a2b63fac54d01914fd2bb456da483e6982ee21 (patch)
tree574fe83a0dcf2cba219d32f440f44cde1dbc3132
parentf8f870be0f9f648b48896cb6411446b7f9e049ce (diff)
Refactor problem response priority into its own model
This moves the response priority values from a cobrand-specific method to a full DB model, and includes management screens in the admin for administering them. For mysociety/fixmystreetforcouncils#66
-rwxr-xr-xbin/update-schema1
-rw-r--r--db/downgrade_0045---0044.sql7
-rw-r--r--db/schema.sql18
-rw-r--r--db/schema_0045-response-priorities.sql20
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/ResponsePriorities.pm107
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm15
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm19
-rw-r--r--perllib/FixMyStreet/Cobrand/Oxfordshire.pm9
-rw-r--r--perllib/FixMyStreet/DB/Result/Body.pm10
-rw-r--r--perllib/FixMyStreet/DB/Result/Contact.pm37
-rw-r--r--perllib/FixMyStreet/DB/Result/ContactResponsePriority.pm46
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm112
-rw-r--r--perllib/FixMyStreet/DB/Result/ResponsePriority.pm57
-rw-r--r--perllib/FixMyStreet/TestMech.pm2
-rw-r--r--t/app/controller/admin.t73
-rw-r--r--templates/web/base/admin/responsepriorities/edit.html41
-rw-r--r--templates/web/base/admin/responsepriorities/index.html11
-rw-r--r--templates/web/base/admin/responsepriorities/list.html22
-rw-r--r--templates/web/base/report/_inspect.html6
19 files changed, 525 insertions, 88 deletions
diff --git a/bin/update-schema b/bin/update-schema
index eb44e4dda..bb0360fb2 100755
--- a/bin/update-schema
+++ b/bin/update-schema
@@ -194,6 +194,7 @@ else {
# By querying the database schema, we can see where we're currently at
# (assuming schema change files are never half-applied, which should be the case)
sub get_db_version {
+ return '0045' if table_exists('response_priorities');
return '0044' if table_exists('contact_response_templates');
return '0043' if column_exists('users', 'area_id');
return '0042' if table_exists('user_planned_reports');
diff --git a/db/downgrade_0045---0044.sql b/db/downgrade_0045---0044.sql
new file mode 100644
index 000000000..7ab7ab6fd
--- /dev/null
+++ b/db/downgrade_0045---0044.sql
@@ -0,0 +1,7 @@
+BEGIN;
+
+ALTER TABLE problem DROP COLUMN response_priority_id;
+DROP TABLE contact_response_priorities;
+DROP TABLE response_priorities;
+
+COMMIT;
diff --git a/db/schema.sql b/db/schema.sql
index c96059fb1..27f4bad13 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -127,6 +127,15 @@ create trigger contacts_update_trigger after update on contacts
create trigger contacts_insert_trigger after insert on contacts
for each row execute procedure contacts_updated();
+-- Problems can have priorities. This table must be created before problem.
+CREATE TABLE response_priorities (
+ id serial not null primary key,
+ body_id int references body(id) not null,
+ deleted boolean not null default 'f',
+ name text not null,
+ unique(body_id, name)
+);
+
-- Problems reported by users of site
create table problem (
id serial not null primary key,
@@ -185,7 +194,8 @@ create table problem (
extra text, -- extra fields required for open311
flagged boolean not null default 'f',
geocode bytea,
-
+ response_priority_id int REFERENCES response_priorities(id),
+
-- logging sending failures (used by webservices)
send_fail_count integer not null default 0,
send_fail_reason text,
@@ -482,3 +492,9 @@ CREATE TABLE contact_response_templates (
contact_id int REFERENCES contacts(id) NOT NULL,
response_template_id int REFERENCES response_templates(id) NOT NULL
);
+
+CREATE TABLE contact_response_priorities (
+ id serial NOT NULL PRIMARY KEY,
+ contact_id int REFERENCES contacts(id) NOT NULL,
+ response_priority_id int REFERENCES response_priorities(id) NOT NULL
+);
diff --git a/db/schema_0045-response-priorities.sql b/db/schema_0045-response-priorities.sql
new file mode 100644
index 000000000..16215e40c
--- /dev/null
+++ b/db/schema_0045-response-priorities.sql
@@ -0,0 +1,20 @@
+BEGIN;
+
+CREATE TABLE response_priorities (
+ id serial not null primary key,
+ body_id int references body(id) not null,
+ name text not null,
+ deleted boolean not null default 'f',
+ unique(body_id, name)
+);
+
+CREATE TABLE contact_response_priorities (
+ id serial NOT NULL PRIMARY KEY,
+ contact_id int REFERENCES contacts(id) NOT NULL,
+ response_priority_id int REFERENCES response_priorities(id) NOT NULL
+);
+
+ALTER TABLE problem
+ ADD COLUMN response_priority_id int REFERENCES response_priorities(id);
+
+COMMIT;
diff --git a/perllib/FixMyStreet/App/Controller/Admin/ResponsePriorities.pm b/perllib/FixMyStreet/App/Controller/Admin/ResponsePriorities.pm
new file mode 100644
index 000000000..f1110fbf0
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Admin/ResponsePriorities.pm
@@ -0,0 +1,107 @@
+package FixMyStreet::App::Controller::Admin::ResponsePriorities;
+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 ) = @_;
+
+ my $user = $c->user;
+
+ if ($user->is_superuser) {
+ $c->forward('fetch_all_bodies');
+ } elsif ( $user->from_body ) {
+ $c->forward('load_user_body', [ $user->from_body->id ]);
+ $c->res->redirect( $c->uri_for( '', $c->stash->{body}->id ) );
+ } else {
+ $c->detach( '/page_error_404_not_found' );
+ }
+}
+
+sub list : Path : Args(1) {
+ my ($self, $c, $body_id) = @_;
+
+ $c->forward('load_user_body', [ $body_id ]);
+
+ my @priorities = $c->stash->{body}->response_priorities->search(
+ undef,
+ {
+ order_by => 'name'
+ }
+ );
+
+ $c->stash->{response_priorities} = \@priorities;
+}
+
+sub edit : Path : Args(2) {
+ my ( $self, $c, $body_id, $priority_id ) = @_;
+
+ $c->forward('load_user_body', [ $body_id ]);
+
+ my $priority;
+ if ($priority_id eq 'new') {
+ $priority = $c->stash->{body}->response_priorities->new({});
+ }
+ else {
+ $priority = $c->stash->{body}->response_priorities->find( $priority_id )
+ or $c->detach( '/page_error_404_not_found' );
+ }
+
+ $c->forward('/admin/fetch_contacts');
+ my @contacts = $priority->contacts->all;
+ my @live_contacts = $c->stash->{live_contacts}->all;
+ my %active_contacts = map { $_->id => 1 } @contacts;
+ my @all_contacts = map { {
+ id => $_->id,
+ category => $_->category,
+ active => $active_contacts{$_->id},
+ } } @live_contacts;
+ $c->stash->{contacts} = \@all_contacts;
+
+ if ($c->req->method eq 'POST') {
+ $priority->deleted( $c->get_param('deleted') ? 1 : 0 );
+ $priority->name( $c->get_param('name') );
+ $priority->update_or_insert;
+
+ my @live_contact_ids = map { $_->id } @live_contacts;
+ my @new_contact_ids = grep { $c->get_param("contacts[$_]") } @live_contact_ids;
+ $priority->contact_response_priorities->search({
+ contact_id => { '!=' => \@new_contact_ids },
+ })->delete;
+ foreach my $contact_id (@new_contact_ids) {
+ $priority->contact_response_priorities->find_or_create({
+ contact_id => $contact_id,
+ });
+ }
+
+ $c->res->redirect( $c->uri_for( '', $c->stash->{body}->id ) );
+ }
+
+ $c->stash->{response_priority} = $priority;
+}
+
+sub load_user_body : Private {
+ my ($self, $c, $body_id) = @_;
+
+ my $has_permission = $c->user->has_body_permission_to('responsepriority_edit') &&
+ $c->user->from_body->id eq $body_id;
+
+ unless ( $c->user->is_superuser || $has_permission ) {
+ $c->detach( '/page_error_404_not_found' );
+ }
+
+ $c->stash->{body} = $c->model('DB::Body')->find($body_id)
+ or $c->detach( '/page_error_404_not_found' );
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index 7f1132117..6a7a14b5c 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -307,21 +307,10 @@ sub inspect : Private {
$c->stash->{categories} = $c->forward('/admin/categories_for_point');
- # The available priorities for this problem are dependent on the cobrand it
- # was reported to, not necessarily the active cobrand (e.g. inspecting a
- # report on fms.com that was sent to Oxfordshire), so make sure the correct
- # priorities are available for selection.
- if ( $c->cobrand->can('get_body_handler_for_problem') ) {
- my $handler = $c->cobrand->get_body_handler_for_problem($c->stash->{problem});
- if ( $handler->can('problem_response_priorities') ) {
- $c->stash->{priorities} = $handler->problem_response_priorities;
- }
- }
-
if ( $c->get_param('save') || $c->get_param('save_inspected') ) {
$c->forward('/auth/check_csrf_token');
- foreach (qw/priority detailed_location detailed_information traffic_information/) {
+ foreach (qw/detailed_location detailed_information traffic_information/) {
$problem->set_extra_metadata( $_ => $c->get_param($_) );
}
@@ -342,6 +331,8 @@ sub inspect : Private {
$c->forward( '/admin/log_edit', [ $id, 'problem', 'state_change' ] );
}
+ $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) );
+
my $valid = 1;
if ( !$c->forward( '/admin/report_edit_location', [ $problem ] ) ) {
# New lat/lon isn't valid, show an error
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index b5487ea5b..48484d4bb 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -646,10 +646,10 @@ sub admin_pages {
'summary' => [_('Summary'), 0],
'bodies' => [_('Bodies'), 1],
'reports' => [_('Reports'), 2],
- 'timeline' => [_('Timeline'), 4],
- 'users' => [_('Users'), 5],
- 'flagged' => [_('Flagged'), 6],
- 'stats' => [_('Stats'), 7],
+ 'timeline' => [_('Timeline'), 5],
+ 'users' => [_('Users'), 6],
+ 'flagged' => [_('Flagged'), 7],
+ 'stats' => [_('Stats'), 8],
'user_edit' => [undef, undef],
'body' => [undef, undef],
'report_edit' => [undef, undef],
@@ -659,12 +659,16 @@ sub admin_pages {
# There are some pages that only super users can see
if ( $user->is_superuser ) {
- $pages->{config} = [ _('Configuration'), 8];
+ $pages->{config} = [ _('Configuration'), 9];
};
# And some that need special permissions
if ( $user->is_superuser || $user->has_body_permission_to('template_edit') ) {
- $pages->{templates} = [_('Templates'), 3],
- $pages->{template_edit} = [undef, undef],
+ $pages->{templates} = [_('Templates'), 3];
+ $pages->{template_edit} = [undef, undef];
+ };
+ if ( $user->is_superuser || $user->has_body_permission_to('responsepriority_edit') ) {
+ $pages->{responsepriorities} = [_('Priorities'), 4];
+ $pages->{responsepriority_edit} = [undef, undef];
};
@@ -719,6 +723,7 @@ sub available_permissions {
},
_("Bodies") => {
template_edit => _("Add/edit response templates"),
+ responsepriority_edit => _("Add/edit response priorities"),
},
};
}
diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
index f8079565d..989d6f622 100644
--- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
@@ -99,15 +99,6 @@ sub problem_response_days {
return undef;
}
-sub problem_response_priorities {
- return {
- cat1a => 'Cat 1A (4 hours)',
- cat1b => 'Cat 1B (24 hours)',
- cat2 => 'Cat 2 (2 days)',
- cat3 => 'Cat 3 (10 days)',
- };
-}
-
sub reports_ordering {
return { -desc => 'confirmed' };
}
diff --git a/perllib/FixMyStreet/DB/Result/Body.pm b/perllib/FixMyStreet/DB/Result/Body.pm
index a2e004c6a..037b69352 100644
--- a/perllib/FixMyStreet/DB/Result/Body.pm
+++ b/perllib/FixMyStreet/DB/Result/Body.pm
@@ -87,6 +87,12 @@ __PACKAGE__->belongs_to(
},
);
__PACKAGE__->has_many(
+ "response_priorities",
+ "FixMyStreet::DB::Result::ResponsePriority",
+ { "foreign.body_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
"response_templates",
"FixMyStreet::DB::Result::ResponseTemplate",
{ "foreign.body_id" => "self.id" },
@@ -106,8 +112,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2015-02-19 16:13:43
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:d6GuQm8vrNmCc4NWw58srA
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-06 15:33:04
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZuzscnLqcx0k512cTZ/kdg
sub url {
my ( $self, $c, $args ) = @_;
diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm
index ea9b656aa..b37734e7a 100644
--- a/perllib/FixMyStreet/DB/Result/Contact.pm
+++ b/perllib/FixMyStreet/DB/Result/Contact.pm
@@ -11,29 +11,29 @@ use base 'DBIx::Class::Core';
__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn");
__PACKAGE__->table("contacts");
__PACKAGE__->add_columns(
- "id",
- {
- data_type => "integer",
- is_auto_increment => 1,
- is_nullable => 0,
- sequence => "contacts_id_seq",
- },
"body_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
- "category",
- { data_type => "text", default_value => "Other", is_nullable => 0 },
"email",
{ data_type => "text", is_nullable => 0 },
- "confirmed",
- { data_type => "boolean", is_nullable => 0 },
- "deleted",
- { data_type => "boolean", is_nullable => 0 },
"editor",
{ data_type => "text", is_nullable => 0 },
"whenedited",
{ data_type => "timestamp", is_nullable => 0 },
"note",
{ data_type => "text", is_nullable => 0 },
+ "confirmed",
+ { data_type => "boolean", is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
+ "deleted",
+ { data_type => "boolean", is_nullable => 0 },
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "contacts_id_seq",
+ },
"extra",
{ data_type => "text", is_nullable => 1 },
"non_public",
@@ -56,6 +56,12 @@ __PACKAGE__->belongs_to(
{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
);
__PACKAGE__->has_many(
+ "contact_response_priorities",
+ "FixMyStreet::DB::Result::ContactResponsePriority",
+ { "foreign.contact_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
"contact_response_templates",
"FixMyStreet::DB::Result::ContactResponseTemplate",
{ "foreign.contact_id" => "self.id" },
@@ -63,8 +69,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-08-24 11:29:04
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CXUabm3Yd11OoIYJceSPag
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-06 15:33:04
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ocmQGeFJtO3wmvyx6W+EKQ
__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn");
__PACKAGE__->rabx_column('extra');
@@ -75,6 +81,7 @@ use namespace::clean -except => [ 'meta' ];
with 'FixMyStreet::Roles::Extra';
__PACKAGE__->many_to_many( response_templates => 'contact_response_templates', 'response_template' );
+__PACKAGE__->many_to_many( response_priorities => 'contact_response_priorities', 'response_priority' );
sub get_metadata_for_input {
my $self = shift;
diff --git a/perllib/FixMyStreet/DB/Result/ContactResponsePriority.pm b/perllib/FixMyStreet/DB/Result/ContactResponsePriority.pm
new file mode 100644
index 000000000..d5afd75a7
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/ContactResponsePriority.pm
@@ -0,0 +1,46 @@
+use utf8;
+package FixMyStreet::DB::Result::ContactResponsePriority;
+
+# 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("contact_response_priorities");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "contact_response_priorities_id_seq",
+ },
+ "contact_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "response_priority_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->belongs_to(
+ "contact",
+ "FixMyStreet::DB::Result::Contact",
+ { id => "contact_id" },
+ { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
+);
+__PACKAGE__->belongs_to(
+ "response_priority",
+ "FixMyStreet::DB::Result::ResponsePriority",
+ { id => "response_priority_id" },
+ { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-06 15:33:04
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:kM/9jY1QSgakyPTvutS+hw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index 8236524d6..27648ddad 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -20,36 +20,14 @@ __PACKAGE__->add_columns(
},
"postcode",
{ data_type => "text", is_nullable => 0 },
- "latitude",
- { data_type => "double precision", is_nullable => 0 },
- "longitude",
- { data_type => "double precision", is_nullable => 0 },
- "bodies_str",
- { data_type => "text", is_nullable => 1 },
- "areas",
- { data_type => "text", is_nullable => 0 },
- "category",
- { data_type => "text", default_value => "Other", is_nullable => 0 },
"title",
{ data_type => "text", is_nullable => 0 },
"detail",
{ data_type => "text", is_nullable => 0 },
"photo",
{ data_type => "bytea", is_nullable => 1 },
- "used_map",
- { data_type => "boolean", is_nullable => 0 },
- "user_id",
- { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"name",
{ data_type => "text", is_nullable => 0 },
- "anonymous",
- { data_type => "boolean", is_nullable => 0 },
- "external_id",
- { data_type => "text", is_nullable => 1 },
- "external_body",
- { data_type => "text", is_nullable => 1 },
- "external_team",
- { data_type => "text", is_nullable => 1 },
"created",
{
data_type => "timestamp",
@@ -57,18 +35,22 @@ __PACKAGE__->add_columns(
is_nullable => 0,
original => { default_value => \"now()" },
},
- "confirmed",
- { data_type => "timestamp", is_nullable => 1 },
"state",
{ data_type => "text", is_nullable => 0 },
- "lang",
- { data_type => "text", default_value => "en-gb", is_nullable => 0 },
- "service",
- { data_type => "text", default_value => "", is_nullable => 0 },
- "cobrand",
- { data_type => "text", default_value => "", is_nullable => 0 },
- "cobrand_data",
- { data_type => "text", default_value => "", is_nullable => 0 },
+ "whensent",
+ { data_type => "timestamp", is_nullable => 1 },
+ "used_map",
+ { data_type => "boolean", is_nullable => 0 },
+ "bodies_str",
+ { data_type => "text", is_nullable => 1 },
+ "anonymous",
+ { data_type => "boolean", is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
+ "confirmed",
+ { data_type => "timestamp", is_nullable => 1 },
+ "send_questionnaire",
+ { data_type => "boolean", default_value => \"true", is_nullable => 0 },
"lastupdate",
{
data_type => "timestamp",
@@ -76,14 +58,32 @@ __PACKAGE__->add_columns(
is_nullable => 0,
original => { default_value => \"now()" },
},
- "whensent",
- { data_type => "timestamp", is_nullable => 1 },
- "send_questionnaire",
- { data_type => "boolean", default_value => \"true", is_nullable => 0 },
- "extra",
+ "areas",
+ { data_type => "text", is_nullable => 0 },
+ "service",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "lang",
+ { data_type => "text", default_value => "en-gb", is_nullable => 0 },
+ "cobrand",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "cobrand_data",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "latitude",
+ { data_type => "double precision", is_nullable => 0 },
+ "longitude",
+ { data_type => "double precision", is_nullable => 0 },
+ "external_id",
+ { data_type => "text", is_nullable => 1 },
+ "external_body",
{ data_type => "text", is_nullable => 1 },
+ "external_team",
+ { data_type => "text", is_nullable => 1 },
+ "user_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"flagged",
{ data_type => "boolean", default_value => \"false", is_nullable => 0 },
+ "extra",
+ { data_type => "text", is_nullable => 1 },
"geocode",
{ data_type => "bytea", is_nullable => 1 },
"send_fail_count",
@@ -106,6 +106,8 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_nullable => 1 },
"bodies_missing",
{ data_type => "text", is_nullable => 1 },
+ "response_priority_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->has_many(
@@ -127,6 +129,17 @@ __PACKAGE__->has_many(
{ cascade_copy => 0, cascade_delete => 0 },
);
__PACKAGE__->belongs_to(
+ "response_priority",
+ "FixMyStreet::DB::Result::ResponsePriority",
+ { id => "response_priority_id" },
+ {
+ is_deferrable => 0,
+ join_type => "LEFT",
+ on_delete => "NO ACTION",
+ on_update => "NO ACTION",
+ },
+);
+__PACKAGE__->belongs_to(
"user",
"FixMyStreet::DB::Result::User",
{ id => "user_id" },
@@ -140,8 +153,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-07-20 15:00:41
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:PMOhd1uloLTAYovW/fxgSg
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-07 11:01:40
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:iH9c4VZZN/ONnhN6g89DFw
# Add fake relationship to stored procedure table
__PACKAGE__->has_one(
@@ -656,6 +669,27 @@ sub response_templates {
);
}
+=head2 response_priorities
+
+Returns all ResponsePriorities attached to this problem's category/contact, in
+alphabetical order of name.
+
+=cut
+
+sub response_priorities {
+ my $self = shift;
+ return $self->result_source->schema->resultset('ResponsePriority')->search(
+ {
+ 'me.body_id' => $self->bodies_str_ids,
+ 'contact.category' => $self->category,
+ },
+ {
+ order_by => 'name',
+ join => { 'contact_response_priorities' => 'contact' },
+ }
+ );
+}
+
# returns true if the external id is the council's ref, i.e., useful to publish it
# (by way of an example, the Oxfordshire send method returns a useful reference when
# it succeeds, so that is the ref we should show on the problem report page).
diff --git a/perllib/FixMyStreet/DB/Result/ResponsePriority.pm b/perllib/FixMyStreet/DB/Result/ResponsePriority.pm
new file mode 100644
index 000000000..d312fbcea
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/ResponsePriority.pm
@@ -0,0 +1,57 @@
+use utf8;
+package FixMyStreet::DB::Result::ResponsePriority;
+
+# 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("response_priorities");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "response_priorities_id_seq",
+ },
+ "body_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+ "deleted",
+ { data_type => "boolean", default_value => \"false", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("response_priorities_body_id_name_key", ["body_id", "name"]);
+__PACKAGE__->belongs_to(
+ "body",
+ "FixMyStreet::DB::Result::Body",
+ { id => "body_id" },
+ { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
+);
+__PACKAGE__->has_many(
+ "contact_response_priorities",
+ "FixMyStreet::DB::Result::ContactResponsePriority",
+ { "foreign.response_priority_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
+ "problems",
+ "FixMyStreet::DB::Result::Problem",
+ { "foreign.response_priority_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-07 11:01:40
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:B1swGtQzC3qRa0LUM4IyzA
+
+__PACKAGE__->many_to_many( contacts => 'contact_response_priorities', 'contact' );
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/TestMech.pm b/perllib/FixMyStreet/TestMech.pm
index ee9631bdf..54c39ffb9 100644
--- a/perllib/FixMyStreet/TestMech.pm
+++ b/perllib/FixMyStreet/TestMech.pm
@@ -603,6 +603,7 @@ sub delete_body {
$mech->delete_contact($_) for $body->contacts;
$mech->delete_user($_) for $body->users;
$_->delete for $body->response_templates;
+ $_->delete for $body->response_priorities;
$body->body_areas->delete;
$body->delete;
}
@@ -612,6 +613,7 @@ sub delete_contact {
my $contact = shift;
$contact->contact_response_templates->delete_all;
+ $contact->contact_response_priorities->delete_all;
$contact->delete;
}
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t
index f7dcd4023..61e8b21e4 100644
--- a/t/app/controller/admin.t
+++ b/t/app/controller/admin.t
@@ -1177,6 +1177,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
name => 'Changed User',
@@ -1208,6 +1209,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
email => 'changed@example.com',
@@ -1239,6 +1241,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
body => $southend->id,
@@ -1270,6 +1273,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
flagged => 'on',
@@ -1301,6 +1305,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
flagged => undef,
@@ -1332,6 +1337,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
changes => {
is_superuser => 'on',
@@ -1351,6 +1357,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]",
"permissions[user_assign_areas]",
"permissions[template_edit]",
+ "permissions[responsepriority_edit]",
],
log_count => 5,
log_entries => [qw/edit edit edit edit edit/],
@@ -1384,6 +1391,7 @@ FixMyStreet::override_config {
"permissions[user_assign_body]" => undef,
"permissions[user_assign_areas]" => undef,
"permissions[template_edit]" => undef,
+ "permissions[responsepriority_edit]" => undef,
},
log_count => 5,
log_entries => [qw/edit edit edit edit edit/],
@@ -1529,13 +1537,78 @@ subtest "response templates are included on page" => sub {
};
};
+$mech->log_in_ok( $superuser->email );
+
+subtest "response priorities can be added" => sub {
+ is $oxfordshire->response_priorities->count, 0, "No response priorities yet";
+ $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/new" );
+
+ my $fields = {
+ name => "Cat 1A",
+ deleted => undef,
+ "contacts[".$oxfordshirecontact->id."]" => 1,
+ };
+ $mech->submit_form_ok( { with_fields => $fields } );
+
+ is $oxfordshire->response_priorities->count, 1, "Response template was added to body";
+ is $oxfordshirecontact->response_priorities->count, 1, "Response template was added to contact";
+};
+
+subtest "response priorities can be listed" => sub {
+ $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id );
+
+ $mech->content_contains( $oxfordshire->response_priorities->first->name );
+};
+
+subtest "response priorities are limited by body" => sub {
+ my $bromleypriority = $bromley->response_priorities->create( {
+ deleted => 0,
+ name => "Bromley Cat 0",
+ } );
+
+ is $bromley->response_priorities->count, 1, "Response template was added to Bromley";
+ is $oxfordshire->response_priorities->count, 1, "Response template wasn't added to Oxfordshire";
+
+ $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id );
+ $mech->content_lacks( $bromleypriority->name );
+
+ $mech->get_ok( "/admin/responsepriorities/" . $bromley->id );
+ $mech->content_contains( $bromleypriority->name );
+};
+
$mech->log_out_ok;
+subtest "response priorities can't be viewed across councils" => sub {
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'oxfordshire' ],
+ }, sub {
+ $oxfordshireuser->user_body_permissions->create({
+ body => $oxfordshire,
+ permission_type => 'responsepriority_edit',
+ });
+ $mech->log_in_ok( $oxfordshireuser->email );
+ $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id );
+ $mech->content_contains( $oxfordshire->response_priorities->first->name );
+
+
+ $mech->get( "/admin/responsepriorities/" . $bromley->id );
+ ok !$mech->res->is_success(), "want a bad response";
+ is $mech->res->code, 404, "got 404";
+
+ my $bromley_priority_id = $bromley->response_priorities->first->id;
+ $mech->get( "/admin/responsepriorities/" . $bromley->id . "/" . $bromley_priority_id );
+ ok !$mech->res->is_success(), "want a bad response";
+ is $mech->res->code, 404, "got 404";
+ };
+};
+
+
$mech->delete_user( $user );
$mech->delete_user( $user2 );
$mech->delete_user( $user3 );
$mech->delete_user( $superuser );
$mech->delete_user( 'test4@example.com' );
$mech->delete_body( $oxfordshire );
+$mech->delete_body( $bromley );
done_testing();
diff --git a/templates/web/base/admin/responsepriorities/edit.html b/templates/web/base/admin/responsepriorities/edit.html
new file mode 100644
index 000000000..b05ebfaf9
--- /dev/null
+++ b/templates/web/base/admin/responsepriorities/edit.html
@@ -0,0 +1,41 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Response Priority for %s'), body.name) -%]
+[% rp = response_priority %]
+
+[% UNLESS rp.id %]<h3>[% loc('New priority') %]</h3>[% END %]
+
+<form method="post"
+ action="[% c.uri_for('', body.id, rp.id || 'new' ) %]"
+ enctype="application/x-www-form-urlencoded"
+ accept-charset="utf-8"
+ class="validate">
+
+ <p>
+ <strong>[% loc('Name:') %] </strong>
+ <input type="text" name="name" class="required" size="30" value="[% rp.name | html %]">
+ </p>
+ <p>
+ <strong>[% loc('Categories:') %]</strong>
+ <ul>
+ [% FOR contact IN contacts %]
+ <li>
+ <label>
+ <input type="checkbox" name="contacts[[% contact.id %]]" [% 'checked' IF contact.active %]/>
+ [% contact.category %]
+ </label>
+ </li>
+ [% END %]
+ </ul>
+ </p>
+ <p>
+ <label>
+ <input type="checkbox" name="deleted" id="deleted" value="1"[% ' checked' IF rp.deleted %]>
+ [% loc('Flag as deleted') %]
+ </label>
+ </p>
+ <p>
+ <input type="hidden" name="token" value="[% csrf_token %]" >
+ <input type="submit" name="Edit priorities" value="[% rp.id ? loc('Save changes') : loc('Create priority') %]" >
+ </p>
+</form>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/base/admin/responsepriorities/index.html b/templates/web/base/admin/responsepriorities/index.html
new file mode 100644
index 000000000..d8a79420d
--- /dev/null
+++ b/templates/web/base/admin/responsepriorities/index.html
@@ -0,0 +1,11 @@
+[% INCLUDE 'admin/header.html' title=loc('Response Priorities') -%]
+
+<ul>
+ [% FOR body IN bodies %]
+ <li>
+ <a href="/admin/responsepriorities/[% body.id %]">[% body.name %]</a>
+ </li>
+ [% END %]
+</ul>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/base/admin/responsepriorities/list.html b/templates/web/base/admin/responsepriorities/list.html
new file mode 100644
index 000000000..b64941f86
--- /dev/null
+++ b/templates/web/base/admin/responsepriorities/list.html
@@ -0,0 +1,22 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Response Priorities for %s'), body.name) -%]
+
+<table>
+ <thead>
+ <tr>
+ <th> [% loc('Name') %] </th>
+ <th> &nbsp; </th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR p IN response_priorities %]
+ <tr [% 'class="is-deleted"' IF p.deleted %]>
+ <td> [% p.name %] </td>
+ <td> <a href="/admin/responsepriorities/[% body.id %]/[% p.id %]" class="btn">[% loc('Edit') %]</a> </td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+
+<a href="[% c.uri_for('', body.id, 'new') %]" class="btn">[% loc('New priority') %]</a>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index 188816ec1..891d94c43 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -10,9 +10,9 @@
<p class="right">
<label for="problem_priority">[% loc('Priority:') %]</label>
<select name="priority" id="problem_priority">
- <option value="" [% 'selected' UNLESS problem.get_extra_metadata('priority') %]>-</option>
- [% FOREACH priority IN priorities %]
- <option value="[% priority.key %]" [% 'selected' IF problem.get_extra_metadata('priority') == priority.key %]>[% priority.value %]</option>
+ <option value="" [% 'selected' UNLESS problem.response_priority_id %]>-</option>
+ [% FOREACH priority IN problem.response_priorities %]
+ <option value="[% priority.id %]" [% 'selected' IF problem.response_priority_id == priority.id %] [% 'disabled' IF priority.deleted %]>[% priority.name %]</option>
[% END %]
</select>
</p>