diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 183 | ||||
-rw-r--r-- | t/app/controller/admin.t | 2 | ||||
-rw-r--r-- | t/app/controller/admin_translations.t | 191 | ||||
-rw-r--r-- | templates/web/base/admin/_translations.html | 19 | ||||
-rw-r--r-- | templates/web/base/admin/body-form.html | 2 | ||||
-rw-r--r-- | templates/web/base/admin/body.html | 5 | ||||
-rw-r--r-- | templates/web/base/admin/contact-form.html | 2 |
8 files changed, 371 insertions, 35 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2bb5673..0f8e64bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Releases * Unreleased + - New features: + - Body and category names can now be translated in the admin. #1244 - Front end improvements: - Always show pagination figures even if only one page. - Admin improvements: diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index acbc62fb8..0fa58eb9d 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -244,6 +244,9 @@ sub bodies : Path('bodies') : Args(0) { $c->stash->{edit_activity} = $edit_activity; + $c->forward( 'fetch_languages' ); + $c->forward( 'fetch_translations' ); + my $posted = $c->get_param('posted') || ''; if ( $posted eq 'body' ) { $c->forward('check_for_super_user'); @@ -257,6 +260,9 @@ sub bodies : Path('bodies') : Args(0) { $c->model('DB::BodyArea')->create( { body => $body, area_id => $_ } ); } + $c->stash->{object} = $body; + $c->stash->{translation_col} = 'name'; + $c->forward('update_translations'); $c->stash->{updated} = _('New body added'); } } @@ -300,30 +306,6 @@ sub body_form_dropdowns : Private { $c->stash->{send_methods} = \@methods; } -sub body : Path('body') : Args(1) { - my ( $self, $c, $body_id ) = @_; - - $c->stash->{body_id} = $body_id; - - unless ($c->user->has_permission_to('category_edit', $body_id)) { - $c->forward('check_for_super_user'); - } - - $c->forward( '/auth/get_csrf_token' ); - $c->forward( 'lookup_body' ); - $c->forward( 'fetch_all_bodies' ); - $c->forward( 'body_form_dropdowns' ); - - if ( $c->get_param('posted') ) { - $c->log->debug( 'posted' ); - $c->forward('update_contacts'); - } - - $c->forward('fetch_contacts'); - - return 1; -} - sub check_for_super_user : Private { my ( $self, $c ) = @_; @@ -407,6 +389,12 @@ sub update_contacts : Private { $contact->insert; } + unless ( %errors ) { + $c->stash->{translation_col} = 'category'; + $c->stash->{object} = $contact; + $c->forward('update_translations'); + } + } elsif ( $posted eq 'update' ) { $c->forward('/auth/check_csrf_token'); @@ -446,11 +434,45 @@ sub update_contacts : Private { # Remove any others $c->stash->{body}->body_areas->search( { area_id => [ keys %current ] } )->delete; + $c->stash->{translation_col} = 'name'; + $c->stash->{object} = $c->stash->{body}; + $c->forward('update_translations'); + $c->stash->{updated} = _('Values updated'); } } } +sub update_translations : Private { + my ( $self, $c ) = @_; + + foreach my $lang (keys(%{$c->stash->{languages}})) { + my $id = $c->get_param('translation_id_' . $lang); + my $text = $c->get_param('translation_' . $lang); + if ($id) { + my $translation = $c->model('DB::Translation')->find( + { + id => $id, + } + ); + + if ($text) { + $translation->msgstr($text); + $translation->update; + } else { + $translation->delete; + } + } elsif ($text) { + my $col = $c->stash->{translation_col}; + $c->stash->{object}->add_translation_for( + $col, $lang, $text + ); + } + } + + $c->stash->{updated} = _('Translations updated'); +} + sub body_params : Private { my ( $self, $c ) = @_; @@ -497,6 +519,44 @@ sub fetch_contacts : Private { return 1; } +sub fetch_languages : Private { + my ( $self, $c ) = @_; + + my $lang_map = {}; + foreach my $lang (sort @{$c->cobrand->languages}) { + my ($id, $name, $code) = split(',', $lang); + $lang_map->{$id} = { name => $name, code => $code }; + } + + $c->stash->{languages} = $lang_map; + + return 1; +} + +sub fetch_translations : Private { + my ( $self, $c ) = @_; + + my $translations = {}; + if ($c->get_param('posted')) { + foreach my $lang (keys %{$c->stash->{languages}}) { + if (my $msgstr = $c->get_param('translation_' . $lang)) { + $translations->{$lang} = { msgstr => $msgstr }; + } + if (my $id = $c->get_param('translation_id_' . $lang)) { + $translations->{$lang}->{id} = $id; + } + } + } elsif ($c->stash->{object}) { + my @translations = $c->stash->{object}->translation_for($c->stash->{translation_col})->all; + + foreach my $tx (@translations) { + $translations->{$tx->lang} = { id => $tx->id, msgstr => $tx->msgstr }; + } + } + + $c->stash->{translations} = $translations; +} + sub lookup_body : Private { my ( $self, $c ) = @_; @@ -516,35 +576,94 @@ sub lookup_body : Private { return 1; } +sub body_base : Chained('/') : PathPart('admin/body') : CaptureArgs(0) { } + # This is for if the category name contains a '/' -sub category_edit_all : Path('body') { +sub category_edit_all : Chained('body_base') : PathPart('') { my ( $self, $c, $body_id, @category ) = @_; my $category = join( '/', @category ); - $c->go( 'category_edit', [ $body_id, $category ] ); -} -sub category_edit : Path('body') : Args(2) { - my ( $self, $c, $body_id, $category ) = @_; + $c->stash->{body_id} = $body_id; + $c->forward( 'lookup_body' ); + my $contact = $c->stash->{body}->contacts->search( { category => $category } )->first; + $c->stash->{contact} = $contact; + + $c->stash->{template} = 'admin/category_edit.html'; + $c->forward( 'category_edit' ); +} + +sub body : Chained('body_base') : PathPart('') : CaptureArgs(1) { + my ( $self, $c, $body_id ) = @_; $c->stash->{body_id} = $body_id; +} + +sub edit_body : Chained('body') : PathPart('') : Args(0) { + my ( $self, $c ) = @_; + + unless ($c->user->has_permission_to('category_edit', $c->stash->{body_id})) { + $c->forward('check_for_super_user'); + } + + $c->forward( '/auth/get_csrf_token' ); + $c->forward( 'lookup_body' ); + $c->forward( 'fetch_all_bodies' ); + $c->forward( 'body_form_dropdowns' ); + $c->forward('fetch_languages'); + + if ( $c->get_param('posted') ) { + $c->forward('update_contacts'); + } + + $c->stash->{object} = $c->stash->{body}; + $c->stash->{translation_col} = 'name'; + + # if there's a contact then it's because we're displaying error + # messages about adding a contact so grabbing translations will + # fetch the contact submitted translations. So grab them, stash + # them and then clear posted so we can fetch the body translations + if ($c->stash->{contact}) { + $c->forward('fetch_translations'); + $c->stash->{contact_translations} = $c->stash->{translations}; + } + $c->set_param('posted', ''); + + $c->forward('fetch_translations'); + $c->forward('fetch_contacts'); + + $c->stash->{template} = 'admin/body.html'; + return 1; +} + +sub category : Chained('body') : PathPart('') : CaptureArgs(1) { + my ( $self, $c, $category ) = @_; $c->forward( '/auth/get_csrf_token' ); $c->forward( 'lookup_body' ); my $contact = $c->stash->{body}->contacts->search( { category => $category } )->first; $c->stash->{contact} = $contact; +} + +sub category_edit : Chained('category') : PathPart('') : Args(0) { + my ( $self, $c ) = @_; + + $c->stash->{translation_col} = 'category'; + $c->stash->{object} = $c->stash->{contact}; + + $c->forward('fetch_languages'); + $c->forward('fetch_translations'); my $history = $c->model('DB::ContactsHistory')->search( { - body_id => $body_id, - category => $category + body_id => $c->stash->{body_id}, + category => $c->stash->{contact}->category }, { order_by => ['contacts_history_id'] }, ); $c->stash->{history} = $history; - my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders }; $c->stash->{send_methods} = \@methods; diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index db7f9fc3c..069e00b5e 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -182,7 +182,7 @@ subtest 'check contact creation' => sub { non_public => 'on', } } ); $mech->get_ok('/admin/body/' . $body->id . '/test/category'); - + $mech->content_contains('<h1>test/category</h1>'); }; subtest 'check contact editing' => sub { diff --git a/t/app/controller/admin_translations.t b/t/app/controller/admin_translations.t new file mode 100644 index 000000000..f5c32baa6 --- /dev/null +++ b/t/app/controller/admin_translations.t @@ -0,0 +1,191 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +$mech->log_in_ok( $superuser->email ); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->create_contact_ok( body_id => $body->id, category => 'Traffic lights', email => 'lights@example.com' ); + +subtest 'check no translations if one language' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'Translations' ); + +}; + +}; + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], + LANGUAGES => [ + 'en-gb,English,en_GB', + 'de,German,de_DE' + ] +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->create_contact_ok( body_id => $body->id, category => 'Traffic lights', email => 'lights@example.com' ); + +my $body2 = $mech->create_body_ok(2643, 'Arun District Council'); + +FixMyStreet::DB->resultset("Translation")->create({ + lang => "de", + tbl => "body", + object_id => $body2->id, + col => "name", + msgstr => "DE Arun", +}); + +subtest 'check translations if multiple languages' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'Translations' ); +}; + +subtest 'check add category with translation' => sub { + $mech->get_ok('/admin/body/' . $body2->id); + + $mech->content_contains('DE Arun'); + + $mech->submit_form_ok( { with_fields => { + category => 'Potholes', + translation_de => 'DE potholes', + email => 'potholes@example.org', + } } ); + + # check that error page includes translations + $mech->content_lacks('DE Arun'); + $mech->content_contains('DE potholes'); + + $mech->submit_form_ok( { with_fields => { + category => 'Potholes', + translation_de => 'DE potholes', + email => 'potholes@example.org', + note => 'adding category with translation', + } } ); + + $mech->content_contains('DE Arun'); + $mech->content_lacks('DE potholes'); + + $mech->get_ok('/admin/body/' . $body2->id . '/Potholes'); + + $mech->content_contains( 'DE potholes' ); +}; + +subtest 'check add category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => 'DE Traffic lights', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'DE Traffic lights' ); +}; + +subtest 'check replace category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'DE Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => 'German Traffic lights', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE Traffic lights' ); + $mech->content_contains( 'German Traffic lights' ); +}; + +subtest 'delete category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + $mech->content_contains( 'German Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => '', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE German Traffic lights' ); +}; + +subtest 'check add body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->content_lacks( 'DE Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => 'DE Aberdeen', + } } ); + + $mech->content_contains( 'DE Aberdeen' ); +}; + +subtest 'check replace body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->content_contains( 'DE Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => 'German Aberdeen', + } } ); + + $mech->content_lacks( 'DE Aberdeen' ); + $mech->content_contains( 'German Aberdeen' ); +}; + +subtest 'delete body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + $mech->content_contains( 'German Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => '', + } } ); + + $mech->content_lacks( 'DE German Aberdeen' ); +}; + +subtest 'check add body with translation' => sub { + $mech->get_ok('/admin/bodies/'); + $mech->submit_form_ok( { with_fields => { + area_ids => 2643, + send_method => 'email', + translation_de => 'DE A Body', + } } ); + + # check that error page includes translations + $mech->content_contains( 'DE A Body' ); + + $mech->submit_form_ok( { with_fields => { + name => 'A body', + area_ids => 2643, + send_method => 'email', + translation_de => 'DE A Body', + } } ); + + $mech->follow_link_ok({ text => 'A body' }); + $mech->content_contains( 'DE A Body' ); +} +}; + +done_testing(); diff --git a/templates/web/base/admin/_translations.html b/templates/web/base/admin/_translations.html new file mode 100644 index 000000000..d2e0ba322 --- /dev/null +++ b/templates/web/base/admin/_translations.html @@ -0,0 +1,19 @@ +[% IF languages.size > 1 %] +<h2>[% loc('Translations') %]</h2> + <input type="hidden" name="token" value="[% csrf_token %]" > +<table> + <tr> + <th>[% loc('Language') %]</th> + <th>[% loc('Translation') %]</th> + </tr> + [% FOREACH language IN languages.keys %] + <tr> + <td> + <label for="translation_[% language %]">[% languages.$language.name %] ([% language %])</label> + <input type="hidden" name="translation_id_[% language %]" value="[% translations.$language.id %]"> + </td> + <td><input type="text" name="translation_[% language %]" id="translation_[% language %]" value="[% translations.$language.msgstr %]"></td> + </tr> + [% END %] +</table> +[% END %] diff --git a/templates/web/base/admin/body-form.html b/templates/web/base/admin/body-form.html index 6c750bcaf..55d0e500c 100644 --- a/templates/web/base/admin/body-form.html +++ b/templates/web/base/admin/body-form.html @@ -25,6 +25,8 @@ <input type="text" class="form-control" name="name" id="name" value="[% body.name | html %]" size="50"> </p> + [% INCLUDE 'admin/_translations.html' %] + <div class="admin-hint"> <p> [% loc( diff --git a/templates/web/base/admin/body.html b/templates/web/base/admin/body.html index 47dce4a9c..7fa446f44 100644 --- a/templates/web/base/admin/body.html +++ b/templates/web/base/admin/body.html @@ -130,9 +130,10 @@ <div class="fms-admin-warning"> [% errors.values.join('<br>') %] </div> + [% INCLUDE 'admin/contact-form.html' translations=contact_translations %] + [% ELSE %] + [% INCLUDE 'admin/contact-form.html' translations={} %] [% END %] - - [% INCLUDE 'admin/contact-form.html' %] </div> [% IF NOT errors and c.user.is_superuser %] diff --git a/templates/web/base/admin/contact-form.html b/templates/web/base/admin/contact-form.html index cd35743ad..375b3eb99 100644 --- a/templates/web/base/admin/contact-form.html +++ b/templates/web/base/admin/contact-form.html @@ -20,6 +20,8 @@ </p> [% END %] + [% INCLUDE 'admin/_translations.html' %] + <div class="admin-hint"> <p> [% loc("The <strong>email address</strong> is the destination to which reports about this category will be sent. |