Bug 38630: Make the REST API respect KohaOpacLanguage cookie
The REST API can return translated strings to the opac or staff
interface, so it should respect the KohaOpacLanguage cookie and return
translations in the same language than the interface.
Test plan:
1. Do not apply patch yet
2. Log in to koha staff interface
3. Use the browser's devtools to get the value of CGISESSID cookie
4. Make sure you have at least 2 languages installed ('en' + another
one - I'll use 'fr-FR' as an example in this test plan)
5. Find an item and note its itemnumber and itemtype
6. Go translate this itemtype's description (admin » item types » edit
item type » translate). Add a translation for 'en' and another one
for your language.
7. Close your browser tab (do not logout) and restart Koha to flush the
memory cache.
8. Execute the following commands (replace variables by the correct
value):
curl -sSL "$KOHA_URL/api/v1/items/$ITEMNUMBER" \
-H 'x-koha-embed: +strings' \
-H "Cookie: KohaOpacLanguage=en; CGISESSID=$CGISESSID" \
| jq -r '._strings.item_type_id.str'
If you do not have jq, it can be found in most distributions
packages. Or you could inspect the JSON manually at the path
/_strings/item_type_id/str
Restart Koha again, and execute the same command but with your
language set in KohaOpacLanguage cookie:
curl -sSL "$KOHA_URL/api/v1/items/$ITEMNUMBER" \
-H 'x-koha-embed: +strings' \
-H "Cookie: KohaOpacLanguage=fr-FR; CGISESSID=$CGISESSID" \
| jq -r '._strings.item_type_id.str'
Depending on how Koha sort the languages (not sure how it's done),
you should get either the 'en' one or the 'fr-FR' one, but the
important thing is you should get the same translation, no matter
what the value of KohaOpacLanguage is, which is the issue this patch
is trying to fix.
9. Apply the patch and restart Koha
10. Repeat step 8. This time you should get the translation
corresponding to the language set in KohaOpacLanguage cookie
Side note:
* The need to restart Koha before each request is due to a bug (see bug
38454). If already pushed when you're testing this one, you don't have
to restart.
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
parent
427e4d582a
commit
ca5db9ffb7
|
|
@ -28,6 +28,7 @@ use List::MoreUtils qw( any );
|
|||
use C4::Context;
|
||||
use Koha::Caches;
|
||||
use Koha::Cache::Memory::Lite;
|
||||
use Koha::Language;
|
||||
|
||||
our (@ISA, @EXPORT_OK);
|
||||
BEGIN {
|
||||
|
|
@ -111,12 +112,17 @@ Returns a reference to an array of hashes:
|
|||
=cut
|
||||
|
||||
sub getTranslatedLanguages {
|
||||
my ($interface, $theme, $current_language, $which) = @_;
|
||||
my ($interface, $theme, $current_language) = @_;
|
||||
my @languages;
|
||||
my @enabled_languages =
|
||||
( $interface && $interface eq 'intranet' )
|
||||
? split ",", C4::Context->preference('StaffInterfaceLanguages')
|
||||
: split ",", C4::Context->preference('OPACLanguages');
|
||||
|
||||
my @enabled_languages;
|
||||
if ($interface) {
|
||||
if ($interface eq 'intranet') {
|
||||
@enabled_languages = split ",", C4::Context->preference('StaffInterfaceLanguages');
|
||||
} elsif ($interface eq 'opac') {
|
||||
@enabled_languages = split ",", C4::Context->preference('OPACLanguages');
|
||||
}
|
||||
}
|
||||
|
||||
my $cache = Koha::Caches->get_instance;
|
||||
my $cache_key = "languages_${interface}_${theme}";
|
||||
|
|
@ -640,8 +646,26 @@ sub accept_language {
|
|||
|
||||
=head2 getlanguage
|
||||
|
||||
Select a language based on the URL parameter 'language', a cookie,
|
||||
syspref available languages & browser
|
||||
Select a language based on:
|
||||
|
||||
=over
|
||||
|
||||
=item * the URL parameter 'language' (staff and opac interfaces only),
|
||||
|
||||
=item * the 'KohaOpacLanguage' cookie (staff and opac interfaces only),
|
||||
|
||||
=item * the language set by C<Koha::Language::set_requested_language>
|
||||
(interfaces other than staff and opac only). The REST API sets this to the
|
||||
value of 'KohaOpacLanguage' cookie,
|
||||
|
||||
=item * syspref StaffInterfaceLanguages (staff only)
|
||||
|
||||
=item * syspref OpacLanguages (opac only),
|
||||
|
||||
=item * HTTP header Accept-Language (more precisely, environment variable
|
||||
HTTP_ACCEPT_LANGUAGE)
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -655,29 +679,36 @@ sub getlanguage {
|
|||
return $cached if $cached;
|
||||
}
|
||||
|
||||
$cgi //= CGI->new;
|
||||
my $interface = C4::Context->interface;
|
||||
my $theme = C4::Context->preference( ( $interface eq 'opac' ) ? 'opacthemes' : 'template' );
|
||||
my $theme = '';
|
||||
my $language;
|
||||
|
||||
my $preference_to_check =
|
||||
$interface eq 'intranet' ? 'StaffInterfaceLanguages' : 'OPACLanguages';
|
||||
# Get the available/valid languages list
|
||||
my @languages;
|
||||
my $preference_value = C4::Context->preference($preference_to_check);
|
||||
if ($preference_value) {
|
||||
@languages = split /,/, $preference_value;
|
||||
}
|
||||
|
||||
# Chose language from the URL
|
||||
my $cgi_param_language = $cgi->param( 'language' );
|
||||
if ( defined $cgi_param_language && any { $_ eq $cgi_param_language } @languages) {
|
||||
$language = $cgi_param_language;
|
||||
}
|
||||
if ($interface eq 'opac' || $interface eq 'intranet') {
|
||||
$cgi //= CGI->new;
|
||||
$theme = C4::Context->preference( ( $interface eq 'opac' ) ? 'opacthemes' : 'template' );
|
||||
|
||||
# cookie
|
||||
if (not $language and my $cgi_cookie_language = $cgi->cookie('KohaOpacLanguage') ) {
|
||||
( $language = $cgi_cookie_language ) =~ s/[^a-zA-Z_-]*//; # sanitize cookie
|
||||
my $preference_to_check =
|
||||
$interface eq 'intranet' ? 'StaffInterfaceLanguages' : 'OPACLanguages';
|
||||
# Get the available/valid languages list
|
||||
my $preference_value = C4::Context->preference($preference_to_check);
|
||||
if ($preference_value) {
|
||||
@languages = split /,/, $preference_value;
|
||||
}
|
||||
|
||||
# Chose language from the URL
|
||||
my $cgi_param_language = $cgi->param( 'language' );
|
||||
if ( defined $cgi_param_language && any { $_ eq $cgi_param_language } @languages) {
|
||||
$language = $cgi_param_language;
|
||||
}
|
||||
|
||||
# cookie
|
||||
if (not $language and my $cgi_cookie_language = $cgi->cookie('KohaOpacLanguage') ) {
|
||||
( $language = $cgi_cookie_language ) =~ s/[^a-zA-Z_-]*//; # sanitize cookie
|
||||
}
|
||||
} else {
|
||||
@languages = map { $_->{rfc4646_subtag} } @{ getTranslatedLanguages($interface, $theme) };
|
||||
$language = Koha::Language->get_requested_language();
|
||||
}
|
||||
|
||||
# HTTP_ACCEPT_LANGUAGE
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ sub startup {
|
|||
$self->plugin('RESTV1');
|
||||
|
||||
$self->plugin('CSRF');
|
||||
$self->plugin('Language');
|
||||
|
||||
$self->hook(before_dispatch => \&_before_dispatch);
|
||||
$self->hook(around_action => \&_around_action);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ sub startup {
|
|||
$self->plugin('RESTV1');
|
||||
|
||||
$self->plugin('CSRF');
|
||||
$self->plugin('Language');
|
||||
|
||||
$self->hook(before_dispatch => \&_before_dispatch);
|
||||
$self->hook(around_action => \&_around_action);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package Koha::App::Plugin::Language;
|
||||
|
||||
# This file is part of Koha.
|
||||
#
|
||||
# Koha is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Koha is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::App::Plugin::Language
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$app->plugin('Language');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Sets the current language based on request (cookies, headers, ...)
|
||||
|
||||
=cut
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Plugin';
|
||||
|
||||
use Koha::Language;
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 register
|
||||
|
||||
Called by Mojolicious when the plugin is loaded.
|
||||
|
||||
Defines an `around_action` hook that will sets the current language based on
|
||||
the current request.
|
||||
|
||||
=cut
|
||||
|
||||
sub register {
|
||||
my ( $self, $app, $conf ) = @_;
|
||||
|
||||
$app->hook(
|
||||
around_action => sub {
|
||||
my ( $next, $c, $action, $last ) = @_;
|
||||
|
||||
# Make the value of Accept-Language header and KohaOpacLanguage
|
||||
# cookie accessible to C4::Languages::getlanguage when not in a CGI
|
||||
# context
|
||||
|
||||
if ( my $accept_language = $c->req->headers->accept_language ) {
|
||||
$ENV{HTTP_ACCEPT_LANGUAGE} = $accept_language;
|
||||
}
|
||||
if ( my $KohaOpacLanguage = $c->cookie('KohaOpacLanguage') ) {
|
||||
Koha::Language->set_requested_language($KohaOpacLanguage);
|
||||
}
|
||||
|
||||
return $next->();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package Koha::Language;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Language
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Koha::Language;
|
||||
|
||||
Koha::Language->set_requested_language('xx-XX');
|
||||
my $language = Koha::Language->get_requested_language();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is essentially a communication tool between the REST API and
|
||||
C4::Languages::getlanguage so that getlanguage can be aware of the value of
|
||||
KohaOpacLanguage cookie when not in CGI context.
|
||||
|
||||
It can also be used in other contexts, like command line scripts for instance.
|
||||
|
||||
=cut
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use Koha::Cache::Memory::Lite;
|
||||
|
||||
use constant REQUESTED_LANGUAGE_CACHE_KEY => 'requested_language';
|
||||
|
||||
sub set_requested_language {
|
||||
my ($class, $language) = @_;
|
||||
|
||||
my $cache = Koha::Cache::Memory::Lite->get_instance;
|
||||
|
||||
$cache->set_in_cache(REQUESTED_LANGUAGE_CACHE_KEY, $language);
|
||||
}
|
||||
|
||||
sub get_requested_language {
|
||||
my ($class) = @_;
|
||||
|
||||
my $cache = Koha::Cache::Memory::Lite->get_instance;
|
||||
|
||||
return $cache->get_from_cache(REQUESTED_LANGUAGE_CACHE_KEY);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -153,6 +153,7 @@ sub startup {
|
|||
$self->app->log->warn( "Warning: Failed to fetch oauth configuration: " . $_ );
|
||||
};
|
||||
|
||||
$self->plugin('Koha::App::Plugin::Language');
|
||||
$self->plugin('Koha::REST::Plugin::Pagination');
|
||||
$self->plugin('Koha::REST::Plugin::Query');
|
||||
$self->plugin('Koha::REST::Plugin::Objects');
|
||||
|
|
|
|||
|
|
@ -155,11 +155,6 @@ sub authenticate_api_request {
|
|||
$c->stash_embed( { spec => $spec } );
|
||||
$c->stash_overrides();
|
||||
|
||||
# FIXME: Remove once CGI is not used
|
||||
my $accept_language = $c->req->headers->accept_language;
|
||||
$ENV{HTTP_ACCEPT_LANGUAGE} = $accept_language
|
||||
if $accept_language;
|
||||
|
||||
my $cookie_auth = 0;
|
||||
|
||||
my $authorization = $spec->{'x-koha-authorization'};
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@
|
|||
# with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
use Test::More tests => 4;
|
||||
use Test::More tests => 5;
|
||||
use Test::MockModule;
|
||||
use CGI qw ( -utf8 );
|
||||
use Koha::Cache::Memory::Lite;
|
||||
use Koha::Language;
|
||||
|
||||
BEGIN {
|
||||
use_ok('C4::Languages', qw( getlanguage ));
|
||||
|
|
@ -32,6 +33,7 @@ my @languages = (); # stores the list of active languages
|
|||
my $return_undef = 0;
|
||||
|
||||
my $module_context = Test::MockModule->new('C4::Context');
|
||||
my $module_languages = Test::MockModule->new('C4::Languages');
|
||||
|
||||
$module_context->mock(
|
||||
preference => sub {
|
||||
|
|
@ -46,6 +48,12 @@ $module_context->mock(
|
|||
},
|
||||
);
|
||||
|
||||
$module_languages->mock(
|
||||
_get_language_dirs => sub {
|
||||
return @languages
|
||||
}
|
||||
);
|
||||
|
||||
delete $ENV{HTTP_ACCEPT_LANGUAGE};
|
||||
|
||||
my $query = CGI->new();
|
||||
|
|
@ -59,3 +67,24 @@ is(C4::Languages::getlanguage($query), 'en', 'default to English if no language
|
|||
Koha::Cache::Memory::Lite->get_instance()->clear_from_cache('getlanguage');
|
||||
$return_undef = 1;
|
||||
is(C4::Languages::getlanguage($query), 'en', 'default to English if no database');
|
||||
|
||||
subtest 'when interface is not intranet or opac' => sub {
|
||||
plan tests => 3;
|
||||
|
||||
my $cache = Koha::Cache::Memory::Lite->get_instance;
|
||||
C4::Context->interface('api');
|
||||
|
||||
@languages = ('fr-FR', 'de-DE', 'en');
|
||||
|
||||
$ENV{HTTP_ACCEPT_LANGUAGE} = 'de-DE';
|
||||
$cache->clear_from_cache('getlanguage');
|
||||
is(C4::Languages::getlanguage(), 'de-DE', 'Accept-Language HTTP header is respected');
|
||||
|
||||
Koha::Language->set_requested_language('fr-FR');
|
||||
$cache->clear_from_cache('getlanguage');
|
||||
is(C4::Languages::getlanguage(), 'fr-FR', 'but language requested through Koha::Language is prefered');
|
||||
|
||||
@languages = ();
|
||||
$cache->clear_from_cache('getlanguage');
|
||||
is(C4::Languages::getlanguage(), 'en', 'fallback to english when no language is available');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use Mojo::JSON qw(encode_json);
|
|||
use C4::Auth;
|
||||
use Koha::Items;
|
||||
use Koha::Database;
|
||||
use Koha::Language;
|
||||
|
||||
my $schema = Koha::Database->new->schema;
|
||||
my $builder = t::lib::TestBuilder->new;
|
||||
|
|
@ -256,7 +257,7 @@ subtest 'list_public() tests' => sub {
|
|||
|
||||
subtest 'get() tests' => sub {
|
||||
|
||||
plan tests => 34;
|
||||
plan tests => 37;
|
||||
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
|
|
@ -346,6 +347,13 @@ subtest 'get() tests' => sub {
|
|||
->json_is( '/not_for_loan_status' => 0, 'not_for_loan_status is 0' )
|
||||
->json_is( '/effective_not_for_loan_status' => 3, 'effective_not_for_loan_status now picks up itemtype level - item-level_itypes:0' );
|
||||
|
||||
my $module = Test::MockModule->new('C4::Languages');
|
||||
$module->mock( _get_language_dirs => sub { return 'en', 'de-DE' } );
|
||||
$schema->resultset('Localization')->create({ entity => 'itemtypes', code => $itype->itemtype, lang => 'de-DE', translation => 'translated' });
|
||||
$t->get_ok( "//$userid:$password@/api/v1/items/" . $item->itemnumber, { 'X-Koha-Embed' => '+strings', Cookie => 'KohaOpacLanguage=de-DE', 'Accept-Language' => 'en' } )
|
||||
->status_is( 200, 'SWAGGER3.2.2' )
|
||||
->json_is( '/_strings/item_type_id/str' => 'translated', 'translations in _strings use language set in KohaOpacLanguage cookie' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue