diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rwxr-xr-x | bin/update-schema | 1 | ||||
-rw-r--r-- | db/downgrade_0066---0065.sql | 8 | ||||
-rw-r--r-- | db/schema.sql | 2 | ||||
-rw-r--r-- | db/schema_0066-user-area-ids.sql | 7 | ||||
-rw-r--r-- | docs/_includes/admin-tasks-content.md | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Dashboard.pm | 19 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Reports.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 39 | ||||
-rw-r--r-- | t/app/controller/admin/permissions.t | 4 | ||||
-rw-r--r-- | t/app/controller/admin/users.t | 14 | ||||
-rw-r--r-- | t/app/controller/dashboard.t | 8 | ||||
-rw-r--r-- | templates/web/base/admin/user-form.html | 10 | ||||
-rw-r--r-- | templates/web/base/dashboard/index.html | 10 | ||||
-rw-r--r-- | web/cobrands/fixmystreet/admin.js | 2 |
16 files changed, 89 insertions, 46 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index da8db9838..8b1f41378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Spot moderation conflicts and raise an error. #2384 - Allow searching for <email> in admin. - Make staff JavaScript more self-contained. + - Alow staff user to be associated with multiple areas. - Bugfixes: - Check cached reports do still have photos before being shown. #2374 - Delete cache photos upon photo moderation. #2374 diff --git a/bin/update-schema b/bin/update-schema index 1e1afa72c..9aff9ec5b 100755 --- a/bin/update-schema +++ b/bin/update-schema @@ -212,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 '0066' if column_exists('users', 'area_ids'); return '0065' if constraint_contains('admin_log_object_type_check', 'moderation'); return '0064' if index_exists('moderation_original_data_problem_id_comment_id_idx'); return '0063' if column_exists('moderation_original_data', 'extra'); diff --git a/db/downgrade_0066---0065.sql b/db/downgrade_0066---0065.sql new file mode 100644 index 000000000..7c2b14660 --- /dev/null +++ b/db/downgrade_0066---0065.sql @@ -0,0 +1,8 @@ +BEGIN; + +ALTER TABLE users ADD COLUMN area_id integer; +UPDATE users SET area_id = area_ids[1]; +ALTER TABLE users DROP COLUMN area_ids; + +COMMIT; + diff --git a/db/schema.sql b/db/schema.sql index c97e8d585..98005028c 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -35,7 +35,7 @@ create table users ( title text, twitter_id bigint unique, facebook_id bigint unique, - area_id integer, + area_ids integer ARRAY, extra text ); CREATE UNIQUE INDEX users_email_verified_unique ON users (email) WHERE email_verified; diff --git a/db/schema_0066-user-area-ids.sql b/db/schema_0066-user-area-ids.sql new file mode 100644 index 000000000..53ffc31f0 --- /dev/null +++ b/db/schema_0066-user-area-ids.sql @@ -0,0 +1,7 @@ +BEGIN; + +ALTER TABLE users ADD COLUMN area_ids integer ARRAY; +UPDATE users SET area_ids = ARRAY[area_id] WHERE area_id IS NOT NULL; +ALTER TABLE users DROP COLUMN area_id; + +COMMIT; diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 3a93b284f..abc73da1f 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -706,7 +706,7 @@ For a more detailed breakdown, visit the stats dashboard. This can be accessed b From here, you can access statistics on: - All reports made across the council area -- Reports made within any specific ward +- Reports made within any specific ward or wards - Reports made within any specific category - Reports made between specific dates - Reports that have a specific status, eg ‘open’ or ‘fixed’ diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index acd6ddbf2..84651ad07 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -1647,7 +1647,7 @@ sub user_edit : Path('user_edit') : Args(1) { if (!$user->from_body) { # Non-staff users aren't allowed any permissions or to be in an area $user->admin_user_body_permissions->delete; - $user->area_id(undef); + $user->area_ids(undef); delete $c->stash->{areas}; delete $c->stash->{fetched_areas_body_id}; } elsif ($c->stash->{available_permissions}) { @@ -1667,8 +1667,8 @@ sub user_edit : Path('user_edit') : Args(1) { if ( $user->from_body && $c->user->has_permission_to('user_assign_areas', $user->from_body->id) ) { my %valid_areas = map { $_->{id} => 1 } @{ $c->stash->{areas} }; - my $new_area = $c->get_param('area_id'); - $user->area_id( $valid_areas{$new_area} ? $new_area : undef ); + my @area_ids = grep { $valid_areas{$_} } $c->get_param_list('area_ids'); + $user->area_ids( @area_ids ? \@area_ids : undef ); } # Handle 'trusted' flag(s) diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 7979f31f6..d597ff0ea 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -105,15 +105,18 @@ sub index : Path : Args(0) { # See if we've had anything from the body dropdowns $c->stash->{category} = $c->get_param('category'); - $c->stash->{ward} = $c->get_param('ward'); - if ($c->user_exists && $c->user->area_id) { - $c->stash->{ward} = $c->user->area_id; - $c->stash->{body_name} = join "", map { $children->{$_}->{name} } grep { $children->{$_} } $c->user->area_id; + $c->stash->{ward} = [ $c->get_param_list('ward') ]; + if ($c->user_exists) { + if (my @areas = @{$c->user->area_ids || []}) { + $c->stash->{ward} = $c->user->area_ids; + $c->stash->{body_name} = join " / ", sort map { $children->{$_}->{name} } grep { $children->{$_} } @areas; + } } } else { my @bodies = $c->model('DB::Body')->search(undef, { columns => [ "id", "name" ], })->active->translated->with_area_count->all_sorted; + $c->stash->{ward} = []; $c->stash->{bodies} = \@bodies; } @@ -142,8 +145,8 @@ sub construct_rs_filter : Private { my ($self, $c, $updates) = @_; my %where; - $where{areas} = { 'like', '%,' . $c->stash->{ward} . ',%' } - if $c->stash->{ward}; + $where{areas} = [ map { { 'like', "%,$_,%" } } @{$c->stash->{ward}} ] + if @{$c->stash->{ward}}; $where{category} = $c->stash->{category} if $c->stash->{category}; @@ -298,7 +301,7 @@ sub csv_filename { my %where = ( category => $c->stash->{category}, state => $c->stash->{q_state}, - ward => $c->stash->{ward}, + ward => join(',', @{$c->stash->{ward}}), ); $where{body} = $c->stash->{body}->id if $c->stash->{body}; join '-', @@ -475,7 +478,7 @@ sub generate_csv : Private { my $filename = $c->stash->{csv}->{filename}; $c->res->content_type('text/csv; charset=utf-8'); - $c->res->header('content-disposition' => "attachment; filename=${filename}.csv"); + $c->res->header('content-disposition' => "attachment; filename=\"${filename}.csv\""); $c->res->body( join "", @body ); } diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 975b2fdd5..4c28563de 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -450,7 +450,7 @@ sub summary : Private { # required to stop errors in generate_grouped_data $c->stash->{q_state} = ''; - $c->stash->{ward} = $c->get_param('area'); + $c->stash->{ward} = [ $c->get_param('area') || () ]; $c->stash->{start_date} = $dtf->format_date($start_date); $c->stash->{end_date} = $c->get_param('end_date'); diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index c50f8834c..bf74e6934 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -36,16 +36,6 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "is_superuser", { data_type => "boolean", default_value => \"false", is_nullable => 0 }, - "title", - { data_type => "text", is_nullable => 1 }, - "twitter_id", - { data_type => "bigint", is_nullable => 1 }, - "facebook_id", - { data_type => "bigint", is_nullable => 1 }, - "area_id", - { data_type => "integer", is_nullable => 1 }, - "extra", - { data_type => "text", is_nullable => 1 }, "created", { data_type => "timestamp", @@ -60,6 +50,16 @@ __PACKAGE__->add_columns( is_nullable => 0, original => { default_value => \"now()" }, }, + "title", + { data_type => "text", is_nullable => 1 }, + "twitter_id", + { data_type => "bigint", is_nullable => 1 }, + "facebook_id", + { data_type => "bigint", is_nullable => 1 }, + "area_ids", + { data_type => "integer[]", is_nullable => 1 }, + "extra", + { data_type => "text", is_nullable => 1 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->add_unique_constraint("users_facebook_id_key", ["facebook_id"]); @@ -119,8 +119,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07035 @ 2018-05-23 18:54:36 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/V7+Ygv/t6VX8dDhNGN16w +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2019-02-12 15:14:37 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:4NBO3A+LfZXOh4Kj/II2LQ # These are not fully unique constraints (they only are when the *_verified # is true), but this is managed in ResultSet::User's find() wrapper. @@ -597,4 +597,19 @@ sub set_last_active { $self->last_active($time or \'current_timestamp'); } +has areas_hash => ( + is => 'ro', + lazy => 1, + default => sub { + my $self = shift; + my %ids = map { $_ => 1 } @{$self->area_ids || []}; + return \%ids; + }, +); + +sub in_area { + my ($self, $area) = @_; + return $self->areas_hash->{$area}; +} + 1; diff --git a/t/app/controller/admin/permissions.t b/t/app/controller/admin/permissions.t index 3fc221a63..036ec4509 100644 --- a/t/app/controller/admin/permissions.t +++ b/t/app/controller/admin/permissions.t @@ -181,6 +181,7 @@ FixMyStreet::override_config { subtest "Unsetting user from_body removes all permissions and area " => sub { is $user2->user_body_permissions->count, 1, 'user2 has 1 permission'; + $user2->update({ area_ids => [123] }); # Set to check cleared $mech->get_ok("/admin/user_edit/$user2_id"); $mech->content_contains('Moderate report details'); @@ -204,8 +205,9 @@ FixMyStreet::override_config { "permissions[user_assign_areas]" => undef, } } ); + $user2->discard_changes; is $user2->user_body_permissions->count, 0, 'user2 has had permissions removed'; - is $user2->area_id, undef, 'user2 has had area removed'; + is $user2->area_ids, undef, 'user2 has had area removed'; }; }; diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t index be781d3ae..d82a7eaef 100644 --- a/t/app/controller/admin/users.t +++ b/t/app/controller/admin/users.t @@ -209,7 +209,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -229,7 +229,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -249,7 +249,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -269,7 +269,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -289,7 +289,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => 'on', is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -309,7 +309,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => undef, - area_id => '', + area_ids => undef, %default_perms, }, changes => { @@ -332,7 +332,7 @@ FixMyStreet::override_config { phone_verified => undef, flagged => undef, is_superuser => 'on', - area_id => '', + area_ids => undef, }, changes => { is_superuser => undef, diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t index a5fa8772a..37903513c 100644 --- a/t/app/controller/dashboard.t +++ b/t/app/controller/dashboard.t @@ -113,7 +113,7 @@ FixMyStreet::override_config { }; subtest 'area user can only see their area' => sub { - $counciluser->update({area_id => $area_id}); + $counciluser->update({area_ids => [ $area_id ]}); $mech->get_ok("/dashboard"); $mech->content_contains('<h1>Trowbridge</h1>'); @@ -122,7 +122,11 @@ FixMyStreet::override_config { $mech->get_ok("/dashboard?ward=$alt_area_id"); $mech->content_contains('<h1>Trowbridge</h1>'); - $counciluser->update({area_id => undef}); + $counciluser->update({area_ids => [ $area_id, $alt_area_id ]}); + $mech->get_ok("/dashboard"); + $mech->content_contains('<h1>Bradford-on-Avon / Trowbridge</h1>'); + + $counciluser->update({area_ids => undef}); }; subtest 'The correct categories and totals shown by default' => sub { diff --git a/templates/web/base/admin/user-form.html b/templates/web/base/admin/user-form.html index bb8511726..735e15e87 100644 --- a/templates/web/base/admin/user-form.html +++ b/templates/web/base/admin/user-form.html @@ -79,11 +79,13 @@ %] </p> </div> - <label for="area_id">[% loc('Area:') %]</label> - <select class="form-control" id='area_id' name='area_id' [% 'disabled' UNLESS c.user.has_permission_to('user_assign_areas', user.from_body.id) %]> - <option value=''>[% loc('No area') %]</option> + <label for="area_ids">[% loc('Area:') %]</label> + <select class="form-control js-multiple" id="area_ids" name="area_ids" + multiple data-none="-- [% loc('Select an area') %] --" + [% 'disabled' UNLESS c.user.has_permission_to('user_assign_areas', user.from_body.id) %]> [% FOREACH area IN areas %] - <option value="[% area.id %]"[% ' selected' IF area.id == user.area_id %]>[% area.name | html %]</option> + [% SET aid = area.id %] + <option value="[% aid %]"[% ' selected' IF user.in_area(aid) %]>[% area.name | html %]</option> [% END %] </select> </li> diff --git a/templates/web/base/dashboard/index.html b/templates/web/base/dashboard/index.html index 7c3966b04..c90823d37 100644 --- a/templates/web/base/dashboard/index.html +++ b/templates/web/base/dashboard/index.html @@ -24,12 +24,12 @@ <div class="filters"> [% IF body %] <input type="hidden" name="body" value="[% body.id | html %]"> - [% IF NOT c.user.area_id %] + [% IF NOT c.user.area_ids.size %] <p> <label for="ward">[% loc('Ward:') %]</label> - <select class="form-control" name="ward" id="ward"><option value=''>[% loc('All') %]</option> + <select class="form-control js-multiple" multiple name="ward" id="ward"> [% FOR w IN children.values.sort('name') %] - <option value="[% w.id %]"[% ' selected' IF w.id == ward %]>[% w.name %]</option> + <option value="[% w.id %]"[% ' selected' IF ward.grep(w.id).size %]>[% w.name %]</option> [% END %] </select> </p> @@ -47,7 +47,7 @@ [% ELSE %] <p> - <label for="ward">[% loc('Council:') %]</label> + <label for="body">[% loc('Council:') %]</label> <select class="form-control" name="body" id="body"><option value=''>[% loc('All') %]</option> [% FOR b IN bodies %] [% NEXT IF NOT b.area_count %] @@ -59,7 +59,7 @@ [% END %] <p> - <label for="state">[% loc('Report state:') %]</label> + <label for="state">[% loc('Report state:') | replace(' ', ' ') %]</label> <select class="form-control" name="state" id="state"> <option value=''>[% loc('All') %]</option> [% FOR group IN filter_states %] diff --git a/web/cobrands/fixmystreet/admin.js b/web/cobrands/fixmystreet/admin.js index c40a7f960..b947b3e49 100644 --- a/web/cobrands/fixmystreet/admin.js +++ b/web/cobrands/fixmystreet/admin.js @@ -76,7 +76,7 @@ $(function(){ // On user edit page, hide the area/categories fields if body changes $("form#user_edit select#body").change(function() { var show_area = $(this).val() == $(this).find("[data-originally-selected]").val(); - $("form#user_edit select#area_id").closest("li").toggle(show_area); + $("form#user_edit select#area_ids").closest("li").toggle(show_area); $("form#user_edit .js-user-categories").toggle(show_area); }); |