aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm183
-rw-r--r--t/app/controller/admin.t2
-rw-r--r--t/app/controller/admin_translations.t191
-rw-r--r--templates/web/base/admin/_translations.html19
-rw-r--r--templates/web/base/admin/body-form.html2
-rw-r--r--templates/web/base/admin/body.html5
-rw-r--r--templates/web/base/admin/contact-form.html2
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.