From 4ba6424991e06b8a46a950deddad886650a52e6b Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Wed, 9 Oct 2024 22:03:16 +0200 Subject: [PATCH] Bug 38136: Refactor database translations (alternative) This has the same goals of bug 24975 but with a different approach. Bug 24975 tried the "POT/PO files as database tables" approach but it appeared that is not what best fits our needs. Instead of a "source text / translated text" mapping, we need translations to be attached to a specific "object" (or table row), which has the added benefit of keeping translations unaffected when the original string changes. It may or may not be what you want but at least this avoids the problem where fixing an error in the original string forces users to re-translate everything. We also need to be able to translate all translatable properties of a particular entity. The solution proposed here is to add a DBIC component that will add several DBIC relations to the `localization` table: * a relation named `localizations` that will return all translations for all translatable properties for all languages. * for each translatable property: * a relation named `{$property}_localizations` that will return all translations for a given property, for all languages * a relation named `{$property}_localization` that will return the translation for a given property, for the current language To enable translations for a particular entity, we will need to: * add two method calls in the "result class" corresponding to the localizable object type * in the admin interface, add a link to the the translation popup. Unlike bug 24975 there is not a page where you can translate everything. I don't know if it is really useful (probably better to translate itemtypes when you are on the itemtype page). It can be added later if needed. To give an example, this is what needs to be added manually in Koha/Schema/Result/Itemtype.pm: __PACKAGE__->load_components('+Koha::DBIx::Class::Localization'); __PACKAGE__->localization_add_relationships( 'itemtype', # the PK column 'description' # a list of translatable columns ); (see POD in Koha/Schema/Component/Localization.pm for more information) Final notes: * I wanted this patch to have as few changes as possible so I kept methods like `search_with_localization`, `children_with_localization` even if I believe they can be removed (replaced by search/children) Localizations do not need to be prefetched/joined unless you want to sort on them. For the same reason, $itemtype->unblessed now always return a `translated_description` key. This can be fixed in followup patches * There is cache involved (see Koha::Schema::Component::Localization::localization). Localizations in cache are grouped by object type and language (keys look like 'KOHA:localization:Itemtype:en') * By keeping a single table for all translations of all entities, we cannot have data integrity at the RDBMS level, but automatic deletion of translations is handled by the DBIC `cascade_delete` feature, so integrity should be fine as long as no raw SQL update/delete operation is involved. Signed-off-by: Caroline Cyr La Rose --- C4/Biblio.pm | 17 +- Koha/ItemType.pm | 100 +++----- Koha/ItemTypes.pm | 34 ++- Koha/Localization.pm | 73 ------ Koha/Localizations.pm | 34 --- Koha/Object.pm | 26 +++ Koha/Schema/Component/Localization.pm | 214 ++++++++++++++++++ Koha/Schema/Result/Itemtype.pm | 22 +- Koha/Schema/Result/ItemtypeLocalization.pm | 32 --- Koha/UI/Form/Builder/Item.pm | 7 +- admin/itemtypes.pl | 1 - admin/localization.pl | 35 +-- .../data/mysql/atomicupdate/bug-38136.pl | 24 ++ installer/data/mysql/kohastructure.sql | 3 +- .../prog/en/includes/localization-link.inc | 3 + .../prog/en/modules/admin/itemtypes.tt | 4 +- .../prog/en/modules/admin/localization.tt | 83 ++++--- .../prog/js/fetch/localization-api-client.js | 22 +- svc/localization | 157 ++++++------- .../Koha/Filter/ExpandCodedFields.t | 3 +- t/db_dependent/Koha/Item.t | 16 +- t/db_dependent/Koha/ItemTypes.t | 24 +- .../Koha/Template/Plugin/ItemTypes.t | 17 +- t/db_dependent/api/v1/item_types.t | 27 +-- 24 files changed, 525 insertions(+), 453 deletions(-) delete mode 100644 Koha/Localization.pm delete mode 100644 Koha/Localizations.pm create mode 100644 Koha/Schema/Component/Localization.pm delete mode 100644 Koha/Schema/Result/ItemtypeLocalization.pm create mode 100755 installer/data/mysql/atomicupdate/bug-38136.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/localization-link.inc diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 21cb96741a..3e494ba174 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -100,6 +100,7 @@ use C4::Linker; use C4::OAI::Sets; use Koha::Logger; +use Koha::Cache::Memory::Lite; use Koha::Caches; use Koha::ClassSources; use Koha::Authority::Types; @@ -1526,15 +1527,15 @@ sub GetAuthorisedValueDesc { #---- itemtypes if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) { - my $lang = C4::Languages::getlanguage; - $lang //= 'en'; - $cache_key = 'itemtype:description:' . $lang; - my $itypes = $cache->get_from_cache( $cache_key, { unsafe => 1 } ); - if ( !$itypes ) { - $itypes = { map { $_->itemtype => $_->translated_description } Koha::ItemTypes->search()->as_list }; - $cache->set_in_cache( $cache_key, $itypes ); + my $memory_cache = Koha::Cache::Memory::Lite->get_instance(); + my $cache_key = 'GetAuthorisedValueDesc:itemtypes'; + my $itemtypes = $memory_cache->get_from_cache($cache_key); + unless ($itemtypes) { + $itemtypes = { map { $_->itemtype => $_ } Koha::ItemTypes->as_list }; + $memory_cache->set_in_cache( $cache_key, $itemtypes ); } - return $itypes->{$value}; + my $itemtype = $itemtypes->{$value}; + return $itemtype ? $itemtype->translated_description : undef; } if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "cn_source" ) { diff --git a/Koha/ItemType.pm b/Koha/ItemType.pm index f4c0c4aee9..b94e24ec71 100644 --- a/Koha/ItemType.pm +++ b/Koha/ItemType.pm @@ -19,15 +19,11 @@ use Modern::Perl; use C4::Koha qw( getitemtypeimagelocation ); use C4::Languages; -use Koha::Caches; use Koha::Database; use Koha::CirculationRules; -use Koha::Localizations; use base qw(Koha::Object Koha::Object::Limit::Library); -my $cache = Koha::Caches->get_instance(); - =head1 NAME Koha::ItemType - Koha Item type Object class @@ -36,48 +32,6 @@ Koha::ItemType - Koha Item type Object class =head2 Class methods -=cut - -=head3 store - -ItemType specific store to ensure relevant caches are flushed on change - -=cut - -sub store { - my ($self) = @_; - - my $flush = 0; - - if ( !$self->in_storage ) { - $flush = 1; - } else { - my $self_from_storage = $self->get_from_storage; - $flush = 1 if ( $self_from_storage->description ne $self->description ); - } - - $self = $self->SUPER::store; - - if ($flush) { - my $key = "itemtype:description:en"; - $cache->clear_from_cache($key); - } - - return $self; -} - -=head2 delete - -ItemType specific C to clear relevant caches on delete. - -=cut - -sub delete { - my $self = shift @_; - $cache->clear_from_cache('itemtype:description:en'); - $self->SUPER::delete(@_); -} - =head3 image_location =cut @@ -93,26 +47,8 @@ sub image_location { sub translated_description { my ( $self, $lang ) = @_; - if ( my $translated_description = eval { $self->get_column('translated_description') } ) { - - # If the value has already been fetched (eg. from sarch_with_localization), - # do not search for it again - # Note: This is a bit hacky but should be fast - return $translated_description - ? $translated_description - : $self->description; - } - $lang ||= C4::Languages::getlanguage; - my $translated_description = Koha::Localizations->search( - { - code => $self->itemtype, - entity => 'itemtypes', - lang => $lang - } - )->next; - return $translated_description - ? $translated_description->translation - : $self->description; + + my $localization = $self->localization( 'description', $lang || C4::Languages::getlanguage() ); } =head3 translated_descriptions @@ -121,19 +57,14 @@ sub translated_description { sub translated_descriptions { my ($self) = @_; - my @translated_descriptions = Koha::Localizations->search( - { - entity => 'itemtypes', - code => $self->itemtype, - } - )->as_list; + return [ map { { lang => $_->lang, translation => $_->translation, } - } @translated_descriptions + } $self->_result->description_localizations ]; } @@ -235,9 +166,32 @@ sub to_api_mapping { rentalcharge_hourly => 'hourly_rental_charge', rentalcharge_hourly_calendar => 'hourly_rental_charge_calendar', bookable_itemtype => 'bookable_itemtype', + + # TODO Remove after having updated all code using unblessed translated_description + translated_description => undef, }; } +=head3 unblessed + +See L + +Overridden to add a C key for backward compatibility. +This should not be relied on as it may be removed in the future. + +=cut + +# TODO Remove after having updated all code using unblessed translated_description +sub unblessed { + my ($self) = @_; + + my $unblessed = $self->SUPER::unblessed(); + + $unblessed->{translated_description} = $self->translated_description; + + return $unblessed; +} + =head2 Internal methods =head3 _type diff --git a/Koha/ItemTypes.pm b/Koha/ItemTypes.pm index 5fbf340de1..58989da3de 100644 --- a/Koha/ItemTypes.pm +++ b/Koha/ItemTypes.pm @@ -32,8 +32,6 @@ Koha::ItemTypes - Koha ItemType Object set class =head2 Class methods -=cut - =head3 search_with_localization my $itemtypes = Koha::ItemTypes->search_with_localization @@ -43,22 +41,22 @@ my $itemtypes = Koha::ItemTypes->search_with_localization sub search_with_localization { my ( $self, $params, $attributes ) = @_; - my $language = C4::Languages::getlanguage(); - $Koha::Schema::Result::Itemtype::LANGUAGE = $language; - $attributes->{order_by} = 'translated_description' unless exists $attributes->{order_by}; - $attributes->{join} = 'localization'; - $attributes->{'+select'} = [ - { - coalesce => [qw( localization.translation me.description )], - -as => 'translated_description' - } - ]; - if ( defined $params->{branchcode} ) { - my $branchcode = delete $params->{branchcode}; - $self->search_with_library_limits( $params, $attributes, $branchcode ); - } else { - $self->SUPER::search( $params, $attributes ); - } + return $self->search( $params, $attributes )->order_by_translated_description; +} + +=head3 order_by_translated_description + +=cut + +sub order_by_translated_description { + my ($self) = @_; + + my $attributes = { + join => 'description_localization', + order_by => \['COALESCE(description_localization.translation, me.description)'], + }; + + return $self->search( {}, $attributes ); } =head2 Internal methods diff --git a/Koha/Localization.pm b/Koha/Localization.pm deleted file mode 100644 index 3ee44fd12c..0000000000 --- a/Koha/Localization.pm +++ /dev/null @@ -1,73 +0,0 @@ -package Koha::Localization; - -# 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 . - -use Modern::Perl; - -use Koha::Database; - -use base qw(Koha::Object); - -my $cache = Koha::Caches->get_instance(); - -=head1 NAME - -Koha::Localization - Koha Localization type Object class - -=head1 API - -=head2 Class methods - -=cut - -=head3 store - -Localization specific store to ensure relevant caches are flushed on change - -=cut - -sub store { - my ($self) = @_; - $self = $self->SUPER::store; - - if ( $self->entity eq 'itemtypes' ) { - my $key = "itemtype:description:" . $self->lang; - $cache->clear_from_cache($key); - } - - return $self; -} - -=head2 delete - -Localization specific C to clear relevant caches on delete. - -=cut - -sub delete { - my $self = shift @_; - if ( $self->entity eq 'itemtypes' ) { - my $key = "itemtype:description:" . $self->lang; - $cache->clear_from_cache($key); - } - $self->SUPER::delete(@_); -} - -sub _type { - return 'Localization'; -} - -1; diff --git a/Koha/Localizations.pm b/Koha/Localizations.pm deleted file mode 100644 index 8720334acd..0000000000 --- a/Koha/Localizations.pm +++ /dev/null @@ -1,34 +0,0 @@ -package Koha::Localizations; - -# 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 . - -use Modern::Perl; - -use Koha::Database; - -use Koha::Localization; - -use base qw(Koha::Objects); - -sub _type { - return 'Localization'; -} - -sub object_class { - return 'Koha::Localization'; -} - -1; diff --git a/Koha/Object.pm b/Koha/Object.pm index c128391c74..15e66b44d1 100644 --- a/Koha/Object.pm +++ b/Koha/Object.pm @@ -956,6 +956,32 @@ sub unblessed_all_relateds { return \%data; } +=head3 localization + +Returns a localized (translated) value of the property, or the original +property value if no translation exist + + $localization = $object->localization($property, $lang); + +C<$property> is the property name. Often this will correspond to an SQL column name + +C<$lang> is the language code (for instance: 'en-GB') + +=cut + +sub localization { + my ( $self, $property, $lang ) = @_; + + my $result = $self->_result; + if ( $result->can('localization') ) { + if ( my $localization = $result->localization( $property, $lang ) ) { + return $localization; + } + } + + return $result->get_column($property); +} + =head3 $object->_result(); Returns the internal DBIC Row object diff --git a/Koha/Schema/Component/Localization.pm b/Koha/Schema/Component/Localization.pm new file mode 100644 index 0000000000..1f0423a755 --- /dev/null +++ b/Koha/Schema/Component/Localization.pm @@ -0,0 +1,214 @@ +package Koha::Schema::Component::Localization; + +=head1 NAME + +Koha::Schema::Component::Localization + +=head1 SYNOPSIS + + package Koha::Schema::Result::SomeTable; + + # ... generated code ... + + __PACKAGE__->load_components('+Koha::Schema::Component::Localization'); + + __PACKAGE__->localization_add_relationships( + 'some_table_localizations', + 'some_table_id' => 'some_table_id', + 'first_column', + 'second_column', + # ... + ); + + package main; + + my $rows = $schema->resultset('SomeTable'); + my $row = $rows->first; + $row->localizations->search($cond); + $row->create_related('first_column_localizations', { lang => $lang, translation => $translation }) + + while (my $row = $rows->next) + # first call will fetch all localizations for the current language and + # the result will be cached, next calls will not execute a query + $row->localization('first_column', $lang); + + # no query executed + $row->localization('second_column', $lang); + } + +=head1 DESCRIPTION + +This is a DBIx::Class component that helps to manage database localizations by +adding several relationships and methods to a "Result Class" + +This can handle several localizable columns (also referred as "properties") per +table + +To add database localizations to an existing database table, you need to: + +=over + +=item * Create a new table with: + +=over + +=item * An auto incremented column as primary key + +=item * A foreign key column referencing the existing table + +=item * 3 string (varchar or text) columns named 'property', 'lang', +'translation' + +=item * A unique key comprising the foreign key column, 'property' and 'lang' + +=back + +=item * Regenerate the DBIx::Class schema with +misc/devel/update_dbix_class_files.pl + +=item * Add calls to load_components and localization_add_relationships at the +end of the result class + +=back + +This will give you a relationship named 'localizations' through which you can +access all localizations of a particular table row. + +And for every property, you will have: + +=over + +=item * a "has_many" relationship named _localizations, giving access +to all localizations of a particular table row for this particular property + +=item * a "might_have" relationship named _localization, giving +access to the localization of a particular table row for this particular +property and for the current language (uses C4::Languages::getlanguage) + +=back + +The "row" object will also gain a method C +which returns a specific translation and uses cache to avoid executing lots of +queries + +=cut + +use Modern::Perl; +use Carp; + +use base qw(DBIx::Class); + +=head2 localization_add_relationships + +Add relationships to the localization table + +=cut + +sub localization_add_relationships { + my ( $class, $pk_column, @properties ) = @_; + + my $rel_class = 'Koha::Schema::Result::Localization'; + my $source_name = $class =~ s/.*:://r; + + $class->has_many( + 'localizations', + $rel_class, + sub { + my ($args) = @_; + + return ( + { + "$args->{foreign_alias}.code" => { -ident => "$args->{self_alias}.$pk_column" }, + "$args->{foreign_alias}.entity" => $source_name, + }, + !$args->{self_result_object} ? () : { + "$args->{foreign_alias}.code" => $args->{self_result_object}->get_column($pk_column), + "$args->{foreign_alias}.entity" => $source_name, + }, + ); + }, + { cascade_copy => 0, cascade_delete => 1, cascade_update => 0 }, + ); + + foreach my $property (@properties) { + $class->might_have( + $property . '_localization', + $rel_class, + sub { + my ($args) = @_; + + # Not a 'use' because we don't want to load C4::Languages (and + # thus C4::Context) while loading the schema + require C4::Languages; + my $lang = C4::Languages::getlanguage(); + + return ( + { + "$args->{foreign_alias}.code" => { -ident => "$args->{self_alias}.$pk_column" }, + "$args->{foreign_alias}.entity" => $source_name, + "$args->{foreign_alias}.property" => $property, + "$args->{foreign_alias}.lang" => $lang, + }, + !$args->{self_result_object} ? () : { + "$args->{foreign_alias}.code" => $args->{self_result_object}->get_column($pk_column), + "$args->{foreign_alias}.entity" => $source_name, + "$args->{foreign_alias}.property" => $property, + "$args->{foreign_alias}.lang" => $lang, + }, + ); + }, + { cascade_copy => 0, cascade_delete => 0, cascade_update => 0 }, + ); + + $class->has_many( + $property . '_localizations', + $rel_class, + sub { + my ($args) = @_; + + return ( + { + "$args->{foreign_alias}.code" => { -ident => "$args->{self_alias}.$pk_column" }, + "$args->{foreign_alias}.entity" => $source_name, + "$args->{foreign_alias}.property" => $property, + }, + !$args->{self_result_object} ? () : { + "$args->{foreign_alias}.code" => $args->{self_result_object}->get_column($pk_column), + "$args->{foreign_alias}.entity" => $source_name, + "$args->{foreign_alias}.property" => $property, + }, + ); + }, + { cascade_copy => 0, cascade_delete => 0, cascade_update => 0 }, + ); + } +} + +sub localization { + my ( $self, $property, $lang ) = @_; + + my $result_source = $self->result_source; + + my $cache = Koha::Caches->get_instance('localization'); + my $cache_key = sprintf( '%s:%s', $result_source->source_name, $lang ); + my $localizations_map = $cache->get_from_cache($cache_key); + unless ($localizations_map) { + $localizations_map = {}; + + my $localizations = $result_source->schema->resultset('Localization')->search( { lang => $lang } ); + while ( my $localization = $localizations->next ) { + my $fk = $localization->get_column('code'); + my $localization_key = sprintf( '%s:%s', $fk, $localization->property ); + $localizations_map->{$localization_key} = $localization->translation; + } + + $cache->set_in_cache( $cache_key, $localizations_map ); + } + + my ($pk) = $self->id; + my $localization_key = sprintf( '%s:%s', $pk, $property ); + + return $localizations_map->{$localization_key}; +} + +1; diff --git a/Koha/Schema/Result/Itemtype.pm b/Koha/Schema/Result/Itemtype.pm index 46aa24926d..a29f154722 100644 --- a/Koha/Schema/Result/Itemtype.pm +++ b/Koha/Schema/Result/Itemtype.pm @@ -344,8 +344,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-10-25 13:25:14 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jd0dYE700dpg1IiRnfbcEg +# Created by DBIx::Class::Schema::Loader v0.07052 @ 2024-12-03 14:37:10 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:l3ycM1L5bmCeM0Pby8GceA __PACKAGE__->add_columns( '+automatic_checkin' => { is_boolean => 1 }, @@ -356,22 +356,8 @@ __PACKAGE__->add_columns( '+bookable' => { is_boolean => 1, is_nullable => 1 }, ); -# Use the ItemtypeLocalization view to create the join on localization -our $LANGUAGE; -__PACKAGE__->has_many( - "localization" => "Koha::Schema::Result::ItemtypeLocalization", - sub { - my $args = shift; - - die "no lang specified!" unless $LANGUAGE; - - return ({ - "$args->{self_alias}.itemtype" => { -ident => "$args->{foreign_alias}.code" }, - "$args->{foreign_alias}.lang" => $LANGUAGE, - }); - - } -); +__PACKAGE__->load_components('+Koha::Schema::Component::Localization'); +__PACKAGE__->localization_add_relationships('itemtype', 'description'); sub koha_object_class { 'Koha::ItemType'; diff --git a/Koha/Schema/Result/ItemtypeLocalization.pm b/Koha/Schema/Result/ItemtypeLocalization.pm deleted file mode 100644 index d5966128c3..0000000000 --- a/Koha/Schema/Result/ItemtypeLocalization.pm +++ /dev/null @@ -1,32 +0,0 @@ -package Koha::Schema::Result::ItemtypeLocalization; - -use base 'DBIx::Class::Core'; - -use Modern::Perl; - -__PACKAGE__->table_class('DBIx::Class::ResultSource::View'); - -__PACKAGE__->table('itemtype_localizations'); -__PACKAGE__->result_source_instance->is_virtual(1); -__PACKAGE__->result_source_instance->view_definition( - "SELECT localization_id, code, lang, translation FROM localization WHERE entity='itemtypes'" -); - -__PACKAGE__->add_columns( - "localization_id", - { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, - "code", - { data_type => "varchar", is_nullable => 0, size => 64 }, - "lang", - { data_type => "varchar", is_nullable => 0, size => 25 }, - "translation", - { data_type => "text", is_nullable => 1 }, -); - -__PACKAGE__->belongs_to( - "itemtype", - "Koha::Schema::Result::Itemtype", - { code => 'itemtype' } -); - -1; diff --git a/Koha/UI/Form/Builder/Item.pm b/Koha/UI/Form/Builder/Item.pm index 7a2f54466f..83710f8ae6 100644 --- a/Koha/UI/Form/Builder/Item.pm +++ b/Koha/UI/Form/Builder/Item.pm @@ -202,12 +202,11 @@ sub generate_subfield_form { } } elsif ( $subfield->{authorised_value} eq "itemtypes" ) { push @authorised_values, ""; - my $itemtypes; + my $itemtypes = Koha::ItemTypes->new; if ($branch_limit) { - $itemtypes = Koha::ItemTypes->search_with_localization( { branchcode => $branch_limit } ); - } else { - $itemtypes = Koha::ItemTypes->search_with_localization; + $itemtypes = $itemtypes->search_with_library_limits( {}, {}, $branch_limit ); } + $itemtypes = $itemtypes->order_by_translated_description; while ( my $itemtype = $itemtypes->next ) { push @authorised_values, $itemtype->itemtype; $authorised_lib{ $itemtype->itemtype } = $itemtype->translated_description; diff --git a/admin/itemtypes.pl b/admin/itemtypes.pl index 58258c62fa..f3ab0042d0 100755 --- a/admin/itemtypes.pl +++ b/admin/itemtypes.pl @@ -31,7 +31,6 @@ use C4::Auth qw( get_template_and_user ); use C4::Output qw( output_html_with_http_headers ); use Koha::ItemTypes; use Koha::ItemType; -use Koha::Localizations; my $input = CGI->new; my $searchfield = $input->param('description'); diff --git a/admin/localization.pl b/admin/localization.pl index 557840de44..0d943a2c53 100755 --- a/admin/localization.pl +++ b/admin/localization.pl @@ -20,8 +20,9 @@ use Modern::Perl; use C4::Auth qw( get_template_and_user ); use C4::Output qw( output_html_with_http_headers ); -use Koha::Localization; -use Koha::Localizations; +use Koha::Database; + +my $schema = Koha::Database->schema; use CGI qw( -utf8 ); @@ -36,19 +37,20 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); -my $entity = $query->param('entity'); -my $code = $query->param('code'); -my $rs = Koha::Localizations->search( { entity => $entity, code => $code } ); +my $source = $query->param('source'); +my $object_id = $query->param('object_id'); +my $property = $query->param('property'); + +my $row = $schema->resultset($source)->find($object_id); + my @translations; -while ( my $s = $rs->next ) { - push @translations, - { - id => $s->localization_id, - entity => $s->entity, - code => $s->code, - lang => $s->lang, - translation => $s->translation, - }; +my $localizations = $row->localizations->search( { property => $property } ); +while ( my $localization = $localizations->next ) { + push @translations, { + localization_id => $localization->id, + lang => $localization->lang, + translation => $localization->translation, + }; } my $translated_languages = C4::Languages::getTranslatedLanguages(); # opac and intranet @@ -56,8 +58,9 @@ my $translated_languages = C4::Languages::getTranslatedLanguages(); # opac an $template->param( translations => \@translations, languages => $translated_languages, - entity => $entity, - code => $code, + source => $source, + object_id => $object_id, + property => $property, ); output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/atomicupdate/bug-38136.pl b/installer/data/mysql/atomicupdate/bug-38136.pl new file mode 100755 index 0000000000..f357e4d11c --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug-38136.pl @@ -0,0 +1,24 @@ +use Modern::Perl; +use Koha::Installer::Output qw(say_warning say_failure say_success say_info); + +return { + bug_number => '38136', + description => 'Add localization.property', + up => sub { + my ($args) = @_; + my ( $dbh, $out ) = @$args{qw(dbh out)}; + + unless ( column_exists( 'localization', 'property' ) ) { + $dbh->do("alter table `localization` add `property` varchar(100) null after `code`"); + $dbh->do("update `localization` set `property` = 'description', entity = 'Itemtype'"); + $dbh->do("alter table `localization` modify `property` varchar(100) not null"); + $dbh->do("alter table `localization` drop key `entity_code_lang`"); + $dbh->do( + "alter table `localization` add unique key `entity_code_property_lang` (`entity`, `code`, `property`, `lang`)" + ); + + say_success( $out, 'Added column localization.property and updated localization.entity values' ); + } + + }, +}; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 9b7dc7fc6e..72d08865e3 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -4488,10 +4488,11 @@ CREATE TABLE `localization` ( `localization_id` int(11) NOT NULL AUTO_INCREMENT, `entity` varchar(16) NOT NULL, `code` varchar(64) NOT NULL, + `property` varchar(100) NOT NULL, `lang` varchar(25) NOT NULL COMMENT 'could be a foreign key', `translation` mediumtext DEFAULT NULL, PRIMARY KEY (`localization_id`), - UNIQUE KEY `entity_code_lang` (`entity`,`code`,`lang`) + UNIQUE KEY `entity_code_property_lang` (`entity`,`code`,`property`,`lang`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/localization-link.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/localization-link.inc new file mode 100644 index 0000000000..332bfcebff --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/localization-link.inc @@ -0,0 +1,3 @@ + Translate into other languages diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt index 15550f97b2..841d8e369d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt @@ -194,9 +194,7 @@ Required [% IF can_be_translated %] - Translate into other languages + [% INCLUDE 'localization-link.inc' source='Itemtype' object_id=itemtype.itemtype property='description' %] [% END %]
  • diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt index 2fae79bbd2..7e4155f753 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt @@ -18,20 +18,20 @@ -
    +

    Localization

    [% INCLUDE 'csrf-token.inc' %] - - - + + +
    1. Authorized value: - [% code | html %] + [% object_id | html %]
    2. @@ -76,9 +76,6 @@ - - - @@ -86,10 +83,7 @@ [% FOR t IN translations %] - - - - +
      IdEntityCode Language Translation  
      [% t.id | html %][% t.entity | html %][% t.code | html %]
      [% t.lang | html %] [% t.translation | html %] - + [% MACRO jsinclude BLOCK %] [% INCLUDE 'datatables.inc' %] @@ -116,7 +110,7 @@ var message; if ( type == 'success_on_update' ) { message = $('
      '); - message.text(_("Entity %s (code %s) for lang %s has correctly been updated with '%s'").format(data.entity, data.code, data.lang, data.translation)); + message.text(_("Translation for lang %s has correctly been updated with '%s'").format(data.lang, data.translation)); } else if ( type == 'error_on_update' ) { message = $('
      '); if ( data.error_code == 'already_exists' ) { @@ -126,13 +120,13 @@ } } else if ( type == 'success_on_delete' ) { message = $('
      '); - message.text(_("The translation (id %s) has been removed successfully").format(data.id)); + message.text(_("The translation has been removed successfully")); } else if ( type == 'error_on_delete' ) { message = $('
      '); message.text(_("An error occurred when deleting this translation")); } else if ( type == 'success_on_insert' ) { message = $('
      '); - message.text(_("Translation (id %s) has been added successfully").format(data.id)); + message.text(_("Translation has been added successfully")); } else if ( type == 'error_on_insert' ) { message = $('
      '); if ( data.error_code == 'already_exists' ) { @@ -149,14 +143,18 @@ }, 3000); } - function send_update_request( data, cell ) { + function send_update_request( _data, cell ) { + const form = document.forms.add_translation; + const source = form.elements.source.value; + const object_id = form.elements.object_id.value; + const data = Object.assign({}, _data, { source, object_id }); const client = APIClient.localization; client.localizations.update(data).then( success => { if ( success.error ) { $(cell).css('background-color', '#FF0000'); show_message({ type: 'error_on_update', data: success }); - } else if ( success.is_changed == 1 ) { + } else { $(cell).css('background-color', '#00FF00'); show_message({ type: 'success_on_update', data: success }); } @@ -180,12 +178,18 @@ ); } - function send_delete_request( id, cell ) { + function send_delete_request( localization_id, cell ) { + const form = document.forms.add_translation; + const source = form.elements.source.value; + const object_id = form.elements.object_id.value; + const property = form.elements.property.value; + const data = { source, object_id, property, localization_id }; + const client = APIClient.localization; - client.localizations.delete(id).then( + client.localizations.delete(data).then( success => { - $("#localization").DataTable().row( '#row_id_' + id ).remove().draw(); - show_message({ type: 'success_on_delete', data: {id} }); + $("#localization").DataTable().row( '#row_id_' + localization_id ).remove().draw(); + show_message({ type: 'success_on_delete', data: {localization_id} }); }, error => { $(cell).css('background-color', '#FF9090'); @@ -224,6 +228,11 @@ }); $("td.lang").on('click', function(){ var td = this; + if (td.childElementCount > 0) { + // do nothing if there is already something there (like a select for instance) + return; + } + var lang = $(td).text(); $(td).css('background-color', ''); var my_select = $(languages_select).clone(); @@ -233,10 +242,9 @@ }); $(my_select).on('change', function(){ var tr = $(this).parent().parent(); - var id = $(tr).data('id'); + var localization_id = $(tr).data('id'); var lang = $(this).find('option:selected').val(); - var translation = $(this).text(); - send_update_request( {id, lang, translation}, td ); + send_update_request( {localization_id, lang}, td ); }); $(my_select).on('blur', function(){ $(td).html(lang); @@ -246,10 +254,9 @@ $("td.translation").on('blur', function(){ var tr = $(this).parent(); - var id = $(tr).data('id'); - var lang = $(tr).find('td.lang').text(); + var localization_id = $(tr).data('id'); var translation = $(this).text(); - send_update_request( {id, lang, translation}, this ); + send_update_request( {localization_id, translation}, this ); }); $("body").on("click", "a.delete", function(e){ @@ -264,20 +271,24 @@ $("#add_translation").on('submit', function(e){ e.preventDefault(); - let localization = { - entity: $(this).find('input[name="entity"]').val(), - code: $(this).find('input[name="code"]').val(), - lang: $(this).find('select[name="lang"] option:selected').val(), - translation: $(this).find('input[name="translation"]').val(), - }; + + const form = this; + const source = form.elements.source.value; + const object_id = form.elements.object_id.value; + const property = form.elements.property.value; + const lang = form.elements.lang.value + const translation = form.elements.translation.value + + let localization = { source, object_id, property, lang, translation }; const client = APIClient.localization; client.localizations.create(localization).then( success => { if ( success.error ) { show_message({ type: 'error_on_insert', data: success }); } else { - var new_row = table.row.add( [ success.id, success.entity, success.code, success.lang, success.translation, " Delete" ] ).draw().node(); - $( new_row ).attr("id", "row_id_" + success.id ).data("id", success.id ); + let delete_str = _("Delete"); + var new_row = table.api().row.add( [ success.lang, success.translation, " %s".format(escape_str(delete_str)) ] ).draw().node(); + $( new_row ).attr("id", "row_id_" + success.localization_id ).data("id", success.localization_id ); show_message({ type: 'success_on_insert', data: success }); } }, diff --git a/koha-tmpl/intranet-tmpl/prog/js/fetch/localization-api-client.js b/koha-tmpl/intranet-tmpl/prog/js/fetch/localization-api-client.js index 36d4b521d9..f27b0427b3 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/fetch/localization-api-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/fetch/localization-api-client.js @@ -10,12 +10,7 @@ export class LocalizationAPIClient { create: localization => this.httpClient.post({ endpoint: "", - body: "entity=%s&code=%s&lang=%s&translation=%s".format( - encodeURIComponent(localization.entity), - encodeURIComponent(localization.code), - encodeURIComponent(localization.lang), - encodeURIComponent(localization.translation) - ), + body: new URLSearchParams(localization).toString(), headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", @@ -24,23 +19,16 @@ export class LocalizationAPIClient { update: localization => this.httpClient.put({ endpoint: "", - body: "id=%s&lang=%s&translation=%s".format( - encodeURIComponent(localization.id), - encodeURIComponent(localization.lang), - encodeURIComponent(localization.translation) - ), + body: new URLSearchParams(localization).toString(), headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", }, }), - delete: id => + delete: localization => this.httpClient.delete({ - endpoint: "/?id=%s".format(id), - headers: { - "Content-Type": - "application/x-www-form-urlencoded;charset=utf-8", - }, + endpoint: + "?" + new URLSearchParams(localization).toString(), }), }; } diff --git a/svc/localization b/svc/localization index f44d1a6c04..ffd1a0315f 100755 --- a/svc/localization +++ b/svc/localization @@ -2,111 +2,114 @@ use Modern::Perl; use Encode qw( encode ); +use Try::Tiny; +use JSON qw( to_json ); use C4::Service; -use Koha::Localizations; +use Koha::Caches; +use Koha::Database; +use C4::Output qw( output_with_http_headers ); our ( $query, $response ) = C4::Service->init( parameters => 'manage_itemtypes' ); -sub get_translations { - my $rs = Koha::Localizations->search( { entity => $query->param('entity'), code => $query->param('code') } ); - my @translations; - while ( my $s = $rs->next ) { - push @translations, { - id => $s->localization_id, - entity => $s->entity, - code => $s->code, - lang => $s->lang, - translation => $s->translation, - }; - } - $response->param( translations => \@translations ); - C4::Service->return_success($response); -} - sub update_translation { - my $id = $query->param('id'); - my $translation = $query->param('translation'); - my $lang = $query->param('lang'); + my $source = $query->param('source'); + my $localization_id = $query->param('localization_id'); + my $object_id = $query->param('object_id'); + my $lang = $query->param('lang'); + my $translation = $query->param('translation'); - my $localization = Koha::Localizations->find($id); - if ( defined $lang and $localization->lang ne $lang ) { - $localization->lang($lang); - } - if ( defined $translation and $localization->translation ne $translation ) { - $localization->translation($translation); - } - my %params; - my $is_changed; - if ( $localization->is_changed ) { - $is_changed = 1; - unless ( - Koha::Localizations->search( - { - entity => $localization->entity, code => $localization->code, lang => $lang, - localization_id => { '!=' => $localization->localization_id }, - } - )->count - ) - { - $localization->store; - } else { - $params{error} = 1; - $params{error_code} = 'already_exists'; + my $schema = Koha::Database->schema; + my $row = $schema->resultset($source)->find($object_id); + if ($row) { + my $localization = $row->localizations->find($localization_id); + if ($localization) { + try { + my $original_lang = $localization->lang; + $localization->lang($lang) if $lang; + $localization->translation($translation) if $translation; + $localization->update(); + Koha::Caches->get_instance('localization')->clear_from_cache("$source:$original_lang"); + Koha::Caches->get_instance('localization')->clear_from_cache("$source:$lang") if $lang; + } catch { + $localization->discard_changes(); + $response->param( error => 1, error_code => 'already_exists' ); + }; } + + $response->param( + lang => $localization->lang, + translation => $localization->translation, + ); } - $response->param( - %params, - id => $localization->localization_id, - entity => $localization->entity, - code => $localization->code, - lang => $localization->lang, - translation => $localization->translation, - is_changed => $is_changed, - ); + C4::Service->return_success($response); } sub add_translation { - my $entity = $query->param('entity'); - my $code = $query->param('code'); - my $lang = $query->param('lang'); - my $translation = $query->param('translation'); + my $source = $query->param('source'); + my $localization_id = $query->param('localization_id'); + my $object_id = $query->param('object_id'); + my $property = $query->param('property'); + my $lang = $query->param('lang'); + my $translation = $query->param('translation'); - unless ( Koha::Localizations->search( { entity => $entity, code => $code, lang => $lang, } )->count ) { - my $localization = Koha::Localization->new( + my $schema = Koha::Database->schema; + my $row = $schema->resultset($source)->find($object_id); + try { + my $localization = $row->create_related( + "${property}_localizations", { - entity => $entity, - code => $code, + entity => $source, lang => $lang, translation => $translation, } ); - $localization->store; + Koha::Caches->get_instance('localization')->clear_from_cache("$source:$lang"); + $response->param( - id => $localization->localization_id, - entity => $localization->entity, - code => $localization->code, - lang => $localization->lang, - translation => $localization->translation, + lang => $localization->lang, + translation => $localization->translation, + localization_id => $localization->id, ); + } catch { + $response->param( error => 1, error_code => 'already_exists' ); + }; - } else { - $response->param( error => 1, error_code => 'already_exists', ); - } C4::Service->return_success($response); } sub delete_translation { - my $id = $query->param('id'); - Koha::Localizations->find($id)->delete; - $response->param( id => $id ); + my $source = $query->param('source'); + my $object_id = $query->param('object_id'); + my $localization_id = $query->param('localization_id'); + + my $schema = Koha::Database->schema; + my $row = $schema->resultset($source)->find($object_id); + if ( $row && $row->can('localizations') ) { + my $localization = $row->localizations->find($localization_id); + + unless ($localization) { + my $json = to_json( { errors => 'Not found' } ); + output_with_http_headers $query, undef, $json, 'js', '404 Not Found'; + exit; + } + + if ($localization) { + $localization->delete(); + Koha::Caches->get_instance('localization')->clear_from_cache( "$source:" . $localization->lang ); + } + + $response->param( + localization_id => $localization_id, + ); + } + C4::Service->return_success($response); } C4::Service->dispatch( - [ 'GET /', ['id'], \&get_translations ], - [ 'PUT /', ['id'], \&update_translation ], - [ 'POST /', [ 'entity', 'code', 'lang', 'translation' ], \&add_translation ], - [ 'DELETE /', ['id'], \&delete_translation ], + [ 'PUT /', [], \&update_translation ], + [ 'POST /', [], \&add_translation ], + [ 'DELETE /', [], \&delete_translation ], ); diff --git a/t/db_dependent/Koha/Filter/ExpandCodedFields.t b/t/db_dependent/Koha/Filter/ExpandCodedFields.t index 3a5ec895b8..ed2426a8c4 100755 --- a/t/db_dependent/Koha/Filter/ExpandCodedFields.t +++ b/t/db_dependent/Koha/Filter/ExpandCodedFields.t @@ -71,10 +71,11 @@ subtest 'ExpandCodedFields tests' => sub { # Clear GetAuthorisedValueDesc-generated cache $cache->clear_from_cache("libraries:name"); - $cache->clear_from_cache("itemtype:description:en"); $cache->clear_from_cache("cn_sources:description"); $cache->clear_from_cache( "AV_descriptions:" . $av->category ); + Koha::Caches->get_instance('localization')->clear_from_cache('Itemtype:en'); + C4::Biblio::ModBiblio( $record, $biblio->biblionumber ); $biblio = Koha::Biblios->find( $biblio->biblionumber ); $record = $biblio->metadata->record; diff --git a/t/db_dependent/Koha/Item.t b/t/db_dependent/Koha/Item.t index 06fe7af209..76721f33ec 100755 --- a/t/db_dependent/Koha/Item.t +++ b/t/db_dependent/Koha/Item.t @@ -30,6 +30,7 @@ use C4::Biblio qw( GetMarcSubfieldStructure ); use C4::Circulation qw( AddIssue AddReturn ); use Koha::Caches; +use Koha::Cache::Memory::Lite; use Koha::Items; use Koha::Database; use Koha::DateUtils qw( dt_from_string ); @@ -2114,10 +2115,11 @@ subtest 'columns_to_str' => sub { $cache->clear_from_cache("MarcStructure-1-"); $cache->clear_from_cache("MarcSubfieldStructure-"); $cache->clear_from_cache("libraries:name"); - $cache->clear_from_cache("itemtype:description:en"); $cache->clear_from_cache("cn_sources:description"); $cache->clear_from_cache("AV_descriptions:LOST"); + Koha::Caches->get_instance('localization')->clear_from_cache('Itemtype:en'); + # Creating subfields 'é', 'è' that are not linked with a kohafield Koha::MarcSubfieldStructures->search( { @@ -2199,10 +2201,11 @@ subtest 'columns_to_str' => sub { $cache->clear_from_cache("MarcStructure-1-"); $cache->clear_from_cache("MarcSubfieldStructure-"); $cache->clear_from_cache("libraries:name"); - $cache->clear_from_cache("itemtype:description:en"); $cache->clear_from_cache("cn_sources:description"); $cache->clear_from_cache("AV_descriptions:LOST"); + Koha::Caches->get_instance('localization')->clear_from_cache('Itemtype:en'); + $schema->storage->txn_rollback; }; @@ -2219,10 +2222,11 @@ subtest 'strings_map() tests' => sub { $cache->clear_from_cache("MarcStructure-1-"); $cache->clear_from_cache("MarcSubfieldStructure-"); $cache->clear_from_cache("libraries:name"); - $cache->clear_from_cache("itemtype:description:en"); $cache->clear_from_cache("cn_sources:description"); $cache->clear_from_cache("AV_descriptions:LOST"); + Koha::Caches->get_instance('localization')->clear_from_cache('Itemtype:en'); + # Recreating subfields just to be sure tests will be ok # 1 => av (LOST) # 3 => no link @@ -2334,6 +2338,7 @@ subtest 'strings_map() tests' => sub { )->store(); Koha::Caches->get_instance->flush_all; + Koha::Cache::Memory::Lite->get_instance->flush; $item->set( { @@ -2406,9 +2411,10 @@ subtest 'strings_map() tests' => sub { $cache->clear_from_cache("MarcStructure-1-"); $cache->clear_from_cache("MarcSubfieldStructure-"); $cache->clear_from_cache("libraries:name"); - $cache->clear_from_cache("itemtype:description:en"); $cache->clear_from_cache("cn_sources:description"); + Koha::Caches->get_instance('localization')->clear_from_cache('Itemtype:en'); + $schema->storage->txn_rollback; }; @@ -2542,7 +2548,7 @@ subtest 'store() tests' => sub { { borrowernumber => $patron->id, date => '1970-01-01 14:00:01', - amountoutstanding => 0, + amountoutstanding => 0, amount => -5, interface => 'commandline', credit_type_code => 'PAYMENT' diff --git a/t/db_dependent/Koha/ItemTypes.t b/t/db_dependent/Koha/ItemTypes.t index 352aac8535..b8a4abd0b1 100755 --- a/t/db_dependent/Koha/ItemTypes.t +++ b/t/db_dependent/Koha/ItemTypes.t @@ -27,6 +27,7 @@ use t::lib::TestBuilder; use C4::Calendar qw( new ); use Koha::Biblioitems; +use Koha::Caches; use Koha::Libraries; use Koha::Database; use Koha::DateUtils qw(dt_from_string); @@ -73,30 +74,29 @@ my $child3 = $builder->build_object( } ); -Koha::Localization->new( +$child1->_result->localizations->create( { - entity => 'itemtypes', - code => $child1->itemtype, + property => 'description', lang => 'en', translation => 'b translated itemtype desc' } -)->store; -Koha::Localization->new( +); +$child2->_result->localizations->create( { - entity => 'itemtypes', - code => $child2->itemtype, + property => 'description', lang => 'en', translation => 'a translated itemtype desc' } -)->store; -Koha::Localization->new( +); +$child3->_result->localizations->create( { - entity => 'something_else', - code => $child2->itemtype, + property => 'description', lang => 'en', translation => 'another thing' } -)->store; +); + +Koha::Caches->get_instance('localization')->flush_all(); my $type = Koha::ItemTypes->find( $child1->itemtype ); ok( defined($type), 'first result' ); diff --git a/t/db_dependent/Koha/Template/Plugin/ItemTypes.t b/t/db_dependent/Koha/Template/Plugin/ItemTypes.t index f783764f47..348d494435 100755 --- a/t/db_dependent/Koha/Template/Plugin/ItemTypes.t +++ b/t/db_dependent/Koha/Template/Plugin/ItemTypes.t @@ -20,6 +20,7 @@ use Test::NoWarnings; use Test::More tests => 11; use C4::Context; +use Koha::Caches; use Koha::Database; use Koha::ItemTypes; @@ -53,14 +54,13 @@ my $itemtypeA = $builder->build_object( } } ); -Koha::Localization->new( +$itemtypeA->_result->localizations->create( { - entity => 'itemtypes', - code => $itemtypeA->itemtype, + property => 'description', lang => 'en', translation => 'Translated itemtype A' } -)->store; +); my $itemtypeB = $builder->build_object( { class => 'Koha::ItemTypes', @@ -70,14 +70,13 @@ my $itemtypeB = $builder->build_object( } } ); -Koha::Localization->new( +$itemtypeB->_result->localizations->create( { - entity => 'itemtypes', - code => $itemtypeB->itemtype, + property => 'description', lang => 'en', translation => 'Translated itemtype B' } -)->store; +); my $itemtypeC = $builder->build_object( { class => 'Koha::ItemTypes', @@ -88,6 +87,8 @@ my $itemtypeC = $builder->build_object( } ); +Koha::Caches->get_instance('localization')->flush_all(); + my $GetDescriptionA1 = $plugin->GetDescription( $itemtypeA->itemtype ); is( $GetDescriptionA1, "Translated itemtype A", "ItemType without parent - GetDescription without want parent" ); my $GetDescriptionA2 = $plugin->GetDescription( $itemtypeA->itemtype, 1 ); diff --git a/t/db_dependent/api/v1/item_types.t b/t/db_dependent/api/v1/item_types.t index 9d2786ef7c..2f08d41977 100755 --- a/t/db_dependent/api/v1/item_types.t +++ b/t/db_dependent/api/v1/item_types.t @@ -26,6 +26,7 @@ use t::lib::Mocks; use Mojo::JSON qw(encode_json); +use Koha::Caches; use Koha::ItemTypes; use Koha::Database; @@ -67,29 +68,23 @@ subtest 'list() tests' => sub { } ); - my $en = $builder->build_object( + $item_type->_result->localizations->create( { - class => 'Koha::Localizations', - value => { - entity => 'itemtypes', - code => $item_type->id, - lang => 'en', - translation => 'English word "test"', - } + property => 'description', + lang => 'en', + translation => 'English word "test"', } ); - my $sv = $builder->build_object( + $item_type->_result->localizations->create( { - class => 'Koha::Localizations', - value => { - entity => 'itemtypes', - code => $item_type->id, - lang => 'sv_SE', - translation => 'Swedish word "test"', - } + property => 'description', + lang => 'sv_SE', + translation => 'Swedish word "test"', } ); + Koha::Caches->get_instance('localization')->flush_all(); + my $librarian = $builder->build_object( { class => 'Koha::Patrons', -- 2.43.0