aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/States.pm102
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm1
-rw-r--r--t/app/controller/admin_states.t24
-rw-r--r--templates/web/base/admin/states/index.html118
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>&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' %]