aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rwxr-xr-xbin/browser-tests7
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/Bodies.pm9
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm8
-rw-r--r--perllib/FixMyStreet/Cobrand/BathNES.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Borsetshire.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Buckinghamshire.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm24
-rw-r--r--perllib/FixMyStreet/Cobrand/FixMyStreet.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Lincolnshire.pm1
-rw-r--r--perllib/FixMyStreet/Cobrand/Northamptonshire.pm2
-rw-r--r--perllib/Open311/PopulateServiceList.pm31
-rw-r--r--t/app/controller/admin/bodies.t43
-rw-r--r--t/app/controller/report_new.t8
-rw-r--r--t/open311/populate-service-list.t165
-rw-r--r--templates/web/base/admin/bodies/contact-form.html14
-rw-r--r--web/cobrands/fixmystreet/admin.js10
17 files changed, 284 insertions, 48 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8bda116e..7ffa67eb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
- Mobile users can now filter the pins on the `/around` map view. #2366
- Admin improvements:
- Add new roles system, to group permissions and apply to users.
+ - New features:
+ - Categories can be listed under more than one group #2475
- Bugfixes:
- Prevent creation of two templates with same title.
- Fix bug going between report/new pages client side
diff --git a/bin/browser-tests b/bin/browser-tests
index 99af08ee1..b5241490e 100755
--- a/bin/browser-tests
+++ b/bin/browser-tests
@@ -88,6 +88,9 @@ sub run {
ALLOWED_COBRANDS => $cobrand,
MAPIT_URL => $mapit_url,
BASE_URL => 'http://fixmystreet.localhost:3001',
+ COBRAND_FEATURES => {
+ category_groups => { map { $_ => 1 } @$cobrand },
+ }
});
$ENV{FMS_OVERRIDE_CONFIG} = $config_out;
@@ -112,10 +115,8 @@ sub run {
kill 'TERM', $pid if $pid;
exit $exit >> 8;
} else {
- require Test::MockModule;
- my $c = Test::MockModule->new('FixMyStreet::Cobrand::FixMyStreet');
- $c->mock('enable_category_groups', sub { 1 });
# Child, run the server on port 3001
+ require FixMyStreet;
FixMyStreet->test_mode(1); # So email doesn't try to send
local $ENV{FIXMYSTREET_APP_DEBUG} = 0;
require Plack::Runner;
diff --git a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm
index 0e47d2238..2ff69b3b5 100644
--- a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm
@@ -273,8 +273,13 @@ sub update_contacts : Private {
if ( $c->get_param('reputation_threshold') ) {
$contact->set_extra_metadata( reputation_threshold => int($c->get_param('reputation_threshold')) );
}
- if ( my $group = $c->get_param('group') ) {
- $contact->set_extra_metadata( group => $group );
+ if ( my @group = $c->get_param_list('group') ) {
+ @group = grep { $_ } @group;
+ if (scalar @group == 0) {
+ $contact->unset_extra_metadata( 'group' );
+ } else {
+ $contact->set_extra_metadata( group => \@group );
+ }
} else {
$contact->unset_extra_metadata( 'group' );
}
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 6a6040865..7c7ebd202 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -741,11 +741,13 @@ sub setup_categories_and_bodies : Private {
$c->stash->{missing_details_bodies} = \@missing_details_bodies;
$c->stash->{missing_details_body_names} = \@missing_details_body_names;
- if ( $c->cobrand->call_hook('enable_category_groups') ) {
+ if ( $c->cobrand->enable_category_groups ) {
my %category_groups = ();
for my $category (@category_options) {
- my $group = $category->{group} // $category->get_extra_metadata('group') // '';
- push @{$category_groups{$group}}, $category;
+ my $group = $category->{group} // $category->get_extra_metadata('group') // [''];
+ # this could be an array ref or a string
+ my @groups = ref $group eq 'ARRAY' ? @$group : ($group);
+ push( @{$category_groups{$_}}, $category ) for @groups;
}
my @category_groups = ();
diff --git a/perllib/FixMyStreet/Cobrand/BathNES.pm b/perllib/FixMyStreet/Cobrand/BathNES.pm
index d726c671e..ea9b26e7a 100644
--- a/perllib/FixMyStreet/Cobrand/BathNES.pm
+++ b/perllib/FixMyStreet/Cobrand/BathNES.pm
@@ -74,8 +74,6 @@ sub pin_colour {
sub send_questionnaires { 0 }
-sub enable_category_groups { 1 }
-
sub default_map_zoom { 3 }
sub map_js_extra {
diff --git a/perllib/FixMyStreet/Cobrand/Borsetshire.pm b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
index a99d2e7b4..f8650169d 100644
--- a/perllib/FixMyStreet/Cobrand/Borsetshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
@@ -27,6 +27,4 @@ sub send_questionnaires {
sub bypass_password_checks { 1 }
-sub enable_category_groups { 1 }
-
1;
diff --git a/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm b/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm
index 486e9603d..4c1d0108b 100644
--- a/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm
@@ -185,8 +185,6 @@ sub map_type { 'Buckinghamshire' }
sub default_map_zoom { 3 }
-sub enable_category_groups { 1 }
-
sub _dashboard_export_add_columns {
my $self = shift;
my $c = $self->{c};
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index cc7f03adb..eaf27e3bc 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -1085,6 +1085,30 @@ sub show_unconfirmed_reports {
0;
}
+=item enable_category_groups
+
+Whether body category groups should be displayed on the new report form. If this is
+not enabled then any groups will be ignored and a flat list of categories displayed.
+
+=cut
+
+sub enable_category_groups {
+ my $self = shift;
+ return $self->feature('category_groups');
+}
+
+=item enable_multiple_category_groups
+
+Whether a category can be included in multiple groups. Required enable_category_groups
+to alse be true.
+
+=cut
+
+sub enable_multiple_category_groups {
+ my $self = shift;
+ return $self->enable_category_groups && $self->feature('multiple_category_groups');
+}
+
sub default_problem_state { 'unconfirmed' }
sub state_groups_admin {
diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
index e01f3e23b..7c51eddd1 100644
--- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
+++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
@@ -10,8 +10,6 @@ use constant COUNCIL_ID_BROMLEY => 2482;
sub on_map_default_status { return 'open'; }
-sub enable_category_groups { 1 }
-
# Special extra
sub path_to_web_templates {
my $self = shift;
diff --git a/perllib/FixMyStreet/Cobrand/Lincolnshire.pm b/perllib/FixMyStreet/Cobrand/Lincolnshire.pm
index 3b731b273..ca88f6b8e 100644
--- a/perllib/FixMyStreet/Cobrand/Lincolnshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Lincolnshire.pm
@@ -18,7 +18,6 @@ sub council_name { return 'Lincolnshire County Council'; }
sub council_url { return 'lincolnshire'; }
sub is_two_tier { 1 }
-sub enable_category_groups { 1 }
sub send_questionnaires { 0 }
sub report_sent_confirmation_email { 'external_id' }
diff --git a/perllib/FixMyStreet/Cobrand/Northamptonshire.pm b/perllib/FixMyStreet/Cobrand/Northamptonshire.pm
index 3d5d4b6f2..21a145326 100644
--- a/perllib/FixMyStreet/Cobrand/Northamptonshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Northamptonshire.pm
@@ -47,8 +47,6 @@ sub privacy_policy_url {
'https://www3.northamptonshire.gov.uk/councilservices/council-and-democracy/transparency/information-policies/privacy-notice/place/Pages/street-doctor.aspx'
}
-sub enable_category_groups { 1 }
-
sub is_two_tier { 1 }
sub get_geocoder { 'OSM' }
diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm
index 2da67e9cd..5f7ca10a3 100644
--- a/perllib/Open311/PopulateServiceList.pm
+++ b/perllib/Open311/PopulateServiceList.pm
@@ -2,6 +2,7 @@ package Open311::PopulateServiceList;
use Moo;
use Open311;
+use Text::CSV;
has bodies => ( is => 'ro' );
has found_contacts => ( is => 'rw', default => sub { [] } );
@@ -262,11 +263,22 @@ sub _normalize_service_name {
sub _set_contact_group {
my ($self, $contact) = @_;
- my $groups_enabled = $self->_current_body_cobrand && $self->_current_body_cobrand->call_hook('enable_category_groups');
+ my $groups_enabled = $self->_current_body_cobrand && $self->_current_body_cobrand->enable_category_groups;
+ my $multi_groups_enabled = $self->_current_body_cobrand && $self->_current_body_cobrand->enable_multiple_category_groups;
my $old_group = $contact->get_extra_metadata('group') || '';
my $new_group = $groups_enabled ? $self->_current_service->{group} || '' : '';
- if ($old_group ne $new_group) {
+ if ($multi_groups_enabled && $new_group =~ /,/) {
+ my $csv = Text::CSV->new;
+ if ( $csv->parse($new_group) ) {
+ $new_group = [ $csv->fields ];
+ } else {
+ warn "error parsing groups for " . $self->_current_body_cobrand->moniker . "contact " . $contact->category . ": $new_group\n";
+ $new_group = [ $new_group ];
+ }
+ }
+
+ if ($self->_groups_different($old_group, $new_group)) {
if ($new_group) {
$contact->set_extra_metadata(group => $new_group);
$contact->update({
@@ -300,6 +312,21 @@ sub _set_contact_non_public {
}) if $keywords{private};
}
+sub _groups_different {
+ my ($self, $old, $new) = @_;
+
+ my $diff = 1;
+ if ($old && $new) {
+ $old = [ $old ] unless ref $old eq 'ARRAY';
+ $new = [ $new ] unless ref $new eq 'ARRAY';
+ $diff = join( ',', sort(@$old) ) ne join( ',', sort(@$new) );
+ } elsif (!$old && !$new) {
+ $diff = 0;
+ }
+
+ return $diff;
+}
+
sub _delete_contacts_not_in_service_list {
my $self = shift;
diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t
index f67e45bf6..db53b7cda 100644
--- a/t/app/controller/admin/bodies.t
+++ b/t/app/controller/admin/bodies.t
@@ -1,10 +1,3 @@
-package FixMyStreet::Cobrand::Tester;
-
-use parent 'FixMyStreet::Cobrand::Default';
-
-sub enable_category_groups { 1 }
-
-package main;
use FixMyStreet::TestMech;
my $mech = FixMyStreet::TestMech->new;
@@ -210,10 +203,12 @@ subtest 'check text output' => sub {
}; # END of override wrap
FixMyStreet::override_config {
- ALLOWED_COBRANDS => ['tester'],
MAPIT_URL => 'http://mapit.uk/',
MAPIT_TYPES => [ 'UTA' ],
BASE_URL => 'http://www.example.org',
+ COBRAND_FEATURES => {
+ category_groups => { default => 1 },
+ }
}, sub {
subtest 'group editing works' => sub {
$mech->get_ok('/admin/body/' . $body->id);
@@ -229,7 +224,7 @@ FixMyStreet::override_config {
} } );
my $contact = $body->contacts->find({ category => 'grouped category' });
- is $contact->get_extra_metadata('group'), 'group a', "group stored correctly";
+ is_deeply $contact->get_extra_metadata('group'), ['group a'], "group stored correctly";
};
subtest 'group can be unset' => sub {
@@ -251,5 +246,35 @@ FixMyStreet::override_config {
};
+FixMyStreet::override_config {
+ MAPIT_URL => 'http://mapit.uk/',
+ MAPIT_TYPES => [ 'UTA' ],
+ BASE_URL => 'http://www.example.org',
+ COBRAND_FEATURES => {
+ category_groups => { default => 1 },
+ multiple_category_groups => { default => 1 },
+ }
+}, sub {
+ subtest 'multi group editing works' => sub {
+ $mech->get_ok('/admin/body/' . $body->id);
+ $mech->content_contains( 'group</strong> is used for the top-level category' );
+
+ # have to do this as a post as adding a second group requires
+ # javascript
+ $mech->post_ok( '/admin/body/' . $body->id, {
+ posted => 'new',
+ token => $mech->form_id('category_edit')->value('token'),
+ category => 'grouped category',
+ email => 'test@example.com',
+ note => 'test note',
+ 'group' => [ 'group a', 'group b'],
+ non_public => undef,
+ state => 'unconfirmed',
+ } );
+
+ my $contact = $body->contacts->find({ category => 'grouped category' });
+ is_deeply $contact->get_extra_metadata('group'), ['group a', 'group b'], "group stored correctly";
+ };
+};
done_testing();
diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t
index 848529686..f65ca243d 100644
--- a/t/app/controller/report_new.t
+++ b/t/app/controller/report_new.t
@@ -1271,16 +1271,18 @@ subtest "Test inactive categories" => sub {
};
subtest "category groups" => sub {
- my $cobrand = Test::MockModule->new('FixMyStreet::Cobrand::FixMyStreet');
- $cobrand->mock('enable_category_groups', sub { 1 });
FixMyStreet::override_config {
ALLOWED_COBRANDS => 'fixmystreet',
MAPIT_URL => 'http://mapit.uk/',
+ COBRAND_FEATURES => {
+ category_groups => { fixmystreet => 1 }
+ }
}, sub {
- $contact2->update( { extra => { group => 'Roads' } } );
+ $contact2->update( { extra => { group => ['Roads','Pavements'] } } );
$contact9->update( { extra => { group => 'Roads' } } );
$contact10->update( { extra => { group => 'Roads' } } );
$mech->get_ok("/report/new?lat=$saved_lat&lon=$saved_lon");
+ $mech->content_like(qr{<optgroup label="Pavements">\s*<option value='Potholes'>Potholes</option></optgroup>});
$mech->content_like(qr{<optgroup label="Roads">\s*<option value='Potholes'>Potholes</option>\s*<option value='Street lighting'>Street lighting</option></optgroup>});
};
};
diff --git a/t/open311/populate-service-list.t b/t/open311/populate-service-list.t
index c67fae9bd..9f8b4d9f0 100644
--- a/t/open311/populate-service-list.t
+++ b/t/open311/populate-service-list.t
@@ -5,20 +5,11 @@ use parent 'FixMyStreet::Cobrand::Default';
sub council_area_id { 1 }
-
-package FixMyStreet::Cobrand::TesterGroups;
-
-use parent 'FixMyStreet::Cobrand::Default';
-
-sub council_area_id { 1 }
-
-sub enable_category_groups { 1 }
-
-
package main;
use FixMyStreet::Test;
use FixMyStreet::DB;
+use Test::Warn;
use utf8;
use_ok( 'Open311::PopulateServiceList' );
@@ -51,13 +42,16 @@ $bucks->body_areas->create({
});
for my $test (
- { desc => 'groups not set for new contacts', cobrand => 'tester', groups => 0, delete => 1 },
- { desc => 'groups set for new contacts', cobrand => 'testergroups', groups => 1, delete => 1},
- { desc => 'groups removed for existing contacts', cobrand => 'tester', groups => 0, delete => 0 },
- { desc => 'groups added for existing contacts', cobrand => 'testergroups', groups => 1, delete => 0},
+ { desc => 'groups not set for new contacts', enable_groups => 0, groups => 0, delete => 1 },
+ { desc => 'groups set for new contacts', enable_groups => 1, groups => 1, delete => 1},
+ { desc => 'groups removed for existing contacts', enable_groups => 0, groups => 0, delete => 0 },
+ { desc => 'groups added for existing contacts', enable_groups => 1, groups => 1, delete => 0},
) {
FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ $test->{cobrand} ],
+ ALLOWED_COBRANDS => [ 'tester' ],
+ COBRAND_FEATURES => {
+ category_groups => { tester => $test->{enable_groups} },
+ }
}, sub {
subtest 'check basic functionality, ' . $test->{desc} => sub {
FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete() if $test->{delete};
@@ -83,6 +77,147 @@ for my $test (
};
}
+my $last_update = {};
+for my $test (
+ { desc => 'set multiple groups for contact', enable_multi => 1, groups => ['sanitation', 'street'] },
+ { desc => 'groups not edited if unchanged', enable_multi => 1, groups => ['sanitation', 'street'], unchanged => 1 },
+ { desc => 'multiple groups has to be configured', enable_multi => 0, groups => 'sanitation,street'},
+) {
+ subtest $test->{desc} => sub {
+ FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete();
+
+ my $services_xml = '<?xml version="1.0" encoding="utf-8"?>
+ <services>
+ <service>
+ <service_code>100</service_code>
+ <service_name>Cans left out 24x7</service_name>
+ <description>Garbage or recycling cans that have been left out for more than 24 hours after collection. Violators will be cited.</description>
+ <metadata>false</metadata>
+ <type>realtime</type>
+ <keywords>lorem, ipsum, dolor</keywords>
+ <group>sanitation,street</group>
+ </service>
+ <service>
+ <service_code>002</service_code>
+ <metadata>false</metadata>
+ <type>realtime</type>
+ <keywords>lorem, ipsum, dolor</keywords>
+ <group>street</group>
+ <service_name>Construction plate shifted</service_name>
+ <description>Metal construction plate covering the street or sidewalk has been moved.</description>
+ </service>
+ </services>
+ ';
+
+ my $service_list = get_xml_simple_object($services_xml);
+
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'tester' ],
+ COBRAND_FEATURES => {
+ category_groups => { tester => 1 },
+ multiple_category_groups => { tester => $test->{enable_multi} },
+ }
+ }, sub {
+ my $processor = Open311::PopulateServiceList->new();
+ $processor->_current_body( $body );
+ $processor->process_services( $service_list );
+ };
+ my $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->count();
+ is $contact_count, 2, 'correct number of contacts';
+
+ my $contact = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, email => 100 } )->first;
+ is_deeply $contact->get_extra->{group}, $test->{groups}, "Multi groups set correctly";
+ if ($test->{unchanged}) {
+ is $contact->whenedited, $last_update->{100}, "contact unchanged";
+ }
+ $last_update->{100} = $contact->whenedited;
+
+ $contact = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, email => '002'} )->first;
+ is $contact->get_extra->{group}, 'street', "Single groups set correctly";
+ if ($test->{unchanged}) {
+ is $contact->whenedited, $last_update->{002}, "contact unchanged";
+ }
+ $last_update->{002} = $contact->whenedited;
+ };
+}
+
+subtest "set multiple groups with quoted csv" => sub {
+ FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete();
+
+ my $services_xml = '<?xml version="1.0" encoding="utf-8"?>
+ <services>
+ <service>
+ <service_code>100</service_code>
+ <service_name>Cans left out 24x7</service_name>
+ <description>Garbage or recycling cans that have been left out for more than 24 hours after collection. Violators will be cited.</description>
+ <metadata>false</metadata>
+ <type>realtime</type>
+ <keywords>lorem, ipsum, dolor</keywords>
+ <group>&quot;sanitation &amp; cleaning&quot;,street</group>
+ </service>
+ </services>
+ ';
+
+ my $service_list = get_xml_simple_object($services_xml);
+
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'tester' ],
+ COBRAND_FEATURES => {
+ category_groups => { tester => 1 },
+ multiple_category_groups => { tester => 1 },
+ }
+ }, sub {
+ my $processor = Open311::PopulateServiceList->new();
+ $processor->_current_body( $body );
+ $processor->process_services( $service_list );
+ };
+ my $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->count();
+ is $contact_count, 1, 'correct number of contacts';
+
+ my $contact = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, email => 100 } )->first;
+ is_deeply $contact->get_extra->{group}, ['sanitation & cleaning','street'], "groups set correctly";
+};
+
+subtest "set multiple groups with bad csv" => sub {
+ FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete();
+
+ my $services_xml = '<?xml version="1.0" encoding="utf-8"?>
+ <services>
+ <service>
+ <service_code>100</service_code>
+ <service_name>Cans left out 24x7</service_name>
+ <description>Garbage or recycling cans that have been left out for more than 24 hours after collection. Violators will be cited.</description>
+ <metadata>false</metadata>
+ <type>realtime</type>
+ <keywords>lorem, ipsum, dolor</keywords>
+ <group>"sanitation,street</group>
+ </service>
+ </services>
+ ';
+
+ my $service_list = get_xml_simple_object($services_xml);
+
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'tester' ],
+ COBRAND_FEATURES => {
+ category_groups => { tester => 1 },
+ multiple_category_groups => { tester => 1 },
+ }
+ }, sub {
+ my $processor = Open311::PopulateServiceList->new();
+ $processor->_current_body( $body );
+ warning_like {
+ $processor->process_services( $service_list );
+ } qr/error parsing groups for testercontact Cans left out 24x7: "sanitation,street/,
+ "warning printed for bad csv";
+ };
+ my $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->count();
+ is $contact_count, 1, 'correct number of contacts';
+
+ my $contact = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, email => 100 } )->first;
+ is_deeply $contact->get_extra->{group}, ['"sanitation,street'], "groups set correctly";
+};
+
subtest 'check non open311 contacts marked as deleted' => sub {
FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete();
diff --git a/templates/web/base/admin/bodies/contact-form.html b/templates/web/base/admin/bodies/contact-form.html
index c55c5c036..efc576b24 100644
--- a/templates/web/base/admin/bodies/contact-form.html
+++ b/templates/web/base/admin/bodies/contact-form.html
@@ -132,7 +132,21 @@ as well.") %]
<p>
<label>
[% loc('Group') %]
+ [% IF body.get_cobrand_handler.enable_multiple_category_groups %]
+ [% IF contact.extra.group %]
+ [% FOR group IN contact.extra.group %]
+ <input class="form-control" type="text" name="group" value="[% group | html %]" size="30">
+ [% END %]
+ [% ELSE %]
+ <input class="form-control" type="text" name="group" value="" size="30">
+ [% END %]
+ <input class="hidden-js js-group-item-template form-control" type="text" name="group" value="" size="30">
+ <p class="hidden-nojs">
+ <button class="btn btn--small js-group-item-add">[% loc('Add group') %]</button>
+ </p>
+ [% ELSE %]
<input class="form-control" type="text" name="group" value="[% contact.extra.group | html %]" size="30">
+ [% END %]
</label>
</p>
[% END %]
diff --git a/web/cobrands/fixmystreet/admin.js b/web/cobrands/fixmystreet/admin.js
index 8bc956c57..25c7651eb 100644
--- a/web/cobrands/fixmystreet/admin.js
+++ b/web/cobrands/fixmystreet/admin.js
@@ -165,6 +165,16 @@ $(function(){
return true;
});
+ $(".js-group-item-add").on("click", function(e) {
+ e.preventDefault();
+ var $template_item = $(".js-group-item-template");
+ var $new_item = $template_item.clone();
+ $new_item.removeClass("hidden-js js-group-item-template");
+ $new_item.insertBefore($template_item);
+ $new_item.focus();
+ return true;
+ });
+
// Fields can be added/removed
$(".js-metadata-item-add").on("click", function(e) {
e.preventDefault();