diff options
author | Matthew Somerville <matthew-github@dracos.co.uk> | 2017-08-24 19:26:24 +0100 |
---|---|---|
committer | Matthew Somerville <matthew-github@dracos.co.uk> | 2017-08-31 15:37:09 +0100 |
commit | b1adf1baaf422a43c17e9a33f698c97d76a5f027 (patch) | |
tree | df2a8e7aee412b2e53483a6dc335191a1adccf87 | |
parent | 986c97311c4cb1f3ec092c0605dcb15ab2527da1 (diff) |
Admin interface for editing report states.
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin/States.pm | 102 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Default.pm | 1 | ||||
-rw-r--r-- | t/app/controller/admin_states.t | 24 | ||||
-rw-r--r-- | templates/web/base/admin/states/index.html | 118 |
5 files changed, 246 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 499bd55e0..d8547af48 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/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/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 4e5228a25..250919d09 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -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 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/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> </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' %] |