From b958c9489d7253551bad245f8d5ceecec335b40c 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. --- C4/Biblio.pm | 19 +- Koha/ItemType.pm | 91 ++------ Koha/ItemTypes.pm | 34 ++- Koha/Localization.pm | 73 ------ Koha/Localizations.pm | 34 --- Koha/Object.pm | 27 +++ Koha/Schema/Component/Localization.pm | 214 ++++++++++++++++++ Koha/Schema/Result/Itemtype.pm | 22 +- Koha/Schema/Result/ItemtypeLocalization.pm | 32 --- Koha/Schema/Result/ItemtypesLocalization.pm | 124 ++++++++++ Koha/Schema/Result/Localization.pm | 108 --------- Koha/UI/Form/Builder/Item.pm | 9 +- admin/itemtypes.pl | 1 - admin/localization.pl | 34 +-- .../data/mysql/atomicupdate/bug-38136.pl | 22 ++ installer/data/mysql/kohastructure.sql | 3 +- .../prog/en/includes/localization-link.inc | 1 + .../prog/en/modules/admin/itemtypes.tt | 2 +- .../prog/en/modules/admin/localization.tt | 79 ++++--- .../prog/js/fetch/localization-api-client.js | 21 +- svc/localization | 143 ++++++------ .../Koha/Filter/ExpandCodedFields.t | 3 +- t/db_dependent/Koha/Item.t | 12 +- t/db_dependent/Koha/ItemTypes.t | 24 +- .../Koha/Template/Plugin/ItemTypes.t | 17 +- t/db_dependent/api/v1/item_types.t | 27 +-- 26 files changed, 628 insertions(+), 548 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 100644 Koha/Schema/Result/ItemtypesLocalization.pm delete mode 100644 Koha/Schema/Result/Localization.pm create mode 100644 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 6c62b21311..f9bcb1f0d5 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -99,6 +99,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; @@ -1473,17 +1474,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..414013b1db 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,23 @@ 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, }; } +# 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 13caa648a8..fc138a6478 100644 --- a/Koha/ItemTypes.pm +++ b/Koha/ItemTypes.pm @@ -33,8 +33,6 @@ Koha::ItemTypes - Koha ItemType Object set class =head2 Class methods -=cut - =head3 search_with_localization my $itemtypes = Koha::ItemTypes->search_with_localization @@ -44,22 +42,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 90201b8870..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 8e254623bd..19e97d0fdd 100644 --- a/Koha/Object.pm +++ b/Koha/Object.pm @@ -956,6 +956,33 @@ 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..6abce2f862 --- /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/Schema/Result/ItemtypesLocalization.pm b/Koha/Schema/Result/ItemtypesLocalization.pm new file mode 100644 index 0000000000..d7c2721b69 --- /dev/null +++ b/Koha/Schema/Result/ItemtypesLocalization.pm @@ -0,0 +1,124 @@ +use utf8; +package Koha::Schema::Result::ItemtypesLocalization; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +=head1 NAME + +Koha::Schema::Result::ItemtypesLocalization + +=cut + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +=head1 TABLE: C + +=cut + +__PACKAGE__->table("itemtypes_localizations"); + +=head1 ACCESSORS + +=head2 itemtypes_localizations_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 itemtype + + data_type: 'varchar' + is_foreign_key: 1 + is_nullable: 0 + size: 10 + +=head2 property + + data_type: 'varchar' + is_nullable: 0 + size: 100 + +=head2 lang + + data_type: 'varchar' + is_nullable: 0 + size: 25 + +=head2 translation + + data_type: 'mediumtext' + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "itemtypes_localizations_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "itemtype", + { data_type => "varchar", is_foreign_key => 1, is_nullable => 0, size => 10 }, + "property", + { data_type => "varchar", is_nullable => 0, size => 100 }, + "lang", + { data_type => "varchar", is_nullable => 0, size => 25 }, + "translation", + { data_type => "mediumtext", is_nullable => 1 }, +); + +=head1 PRIMARY KEY + +=over 4 + +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("itemtypes_localizations_id"); + +=head1 UNIQUE CONSTRAINTS + +=head2 C + +=over 4 + +=item * L + +=item * L + +=item * L + +=back + +=cut + +__PACKAGE__->add_unique_constraint("itemtype_property_lang", ["itemtype", "property", "lang"]); + +=head1 RELATIONS + +=head2 itemtype + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "itemtype", + "Koha::Schema::Result::Itemtype", + { itemtype => "itemtype" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07052 @ 2024-10-08 14:32:00 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9xi1V/qaD/R9u3KHcW7saA + + +# You can replace this text with custom code or comments, and it will be preserved on regeneration +1; diff --git a/Koha/Schema/Result/Localization.pm b/Koha/Schema/Result/Localization.pm deleted file mode 100644 index 1919258b36..0000000000 --- a/Koha/Schema/Result/Localization.pm +++ /dev/null @@ -1,108 +0,0 @@ -use utf8; -package Koha::Schema::Result::Localization; - -# Created by DBIx::Class::Schema::Loader -# DO NOT MODIFY THE FIRST PART OF THIS FILE - -=head1 NAME - -Koha::Schema::Result::Localization - -=cut - -use strict; -use warnings; - -use base 'DBIx::Class::Core'; - -=head1 TABLE: C - -=cut - -__PACKAGE__->table("localization"); - -=head1 ACCESSORS - -=head2 localization_id - - data_type: 'integer' - is_auto_increment: 1 - is_nullable: 0 - -=head2 entity - - data_type: 'varchar' - is_nullable: 0 - size: 16 - -=head2 code - - data_type: 'varchar' - is_nullable: 0 - size: 64 - -=head2 lang - - data_type: 'varchar' - is_nullable: 0 - size: 25 - -could be a foreign key - -=head2 translation - - data_type: 'mediumtext' - is_nullable: 1 - -=cut - -__PACKAGE__->add_columns( - "localization_id", - { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, - "entity", - { data_type => "varchar", is_nullable => 0, size => 16 }, - "code", - { data_type => "varchar", is_nullable => 0, size => 64 }, - "lang", - { data_type => "varchar", is_nullable => 0, size => 25 }, - "translation", - { data_type => "mediumtext", is_nullable => 1 }, -); - -=head1 PRIMARY KEY - -=over 4 - -=item * L - -=back - -=cut - -__PACKAGE__->set_primary_key("localization_id"); - -=head1 UNIQUE CONSTRAINTS - -=head2 C - -=over 4 - -=item * L - -=item * L - -=item * L - -=back - -=cut - -__PACKAGE__->add_unique_constraint("entity_code_lang", ["entity", "code", "lang"]); - - -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-01-21 13:39:29 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Elbup2i+1JON+xa38uzd6A - - -# You can replace this text with custom code or comments, and it will be preserved on regeneration -1; diff --git a/Koha/UI/Form/Builder/Item.pm b/Koha/UI/Form/Builder/Item.pm index 228ba982e6..15fe3bf5f2 100644 --- a/Koha/UI/Form/Builder/Item.pm +++ b/Koha/UI/Form/Builder/Item.pm @@ -213,14 +213,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 } = diff --git a/admin/itemtypes.pl b/admin/itemtypes.pl index 7eceef6b1a..42f1fa2a0d 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 58205d8df7..2440b11a26 100755 --- a/admin/localization.pl +++ b/admin/localization.pl @@ -21,8 +21,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,18 +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( 'intranet', C4::Context->preference('template') ); @@ -55,8 +58,9 @@ my $translated_languages = C4::Languages::getTranslatedLanguages( 'intranet', C4 $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 100644 index 0000000000..1113b6e559 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug-38136.pl @@ -0,0 +1,22 @@ +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 d368659571..6fc19c06c2 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -4406,10 +4406,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..e4d5c1111c --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/localization-link.inc @@ -0,0 +1 @@ + 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 278d10815f..1f5f7ef5af 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt @@ -192,7 +192,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 fc500a8e31..023b9ea9cf 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/localization.tt @@ -18,14 +18,14 @@

    Localization

    [% INCLUDE 'csrf-token.inc' %] - - - + + +
    1. Authorized value: - [% code | html %] + [% object_id | html %]
    2. @@ -66,9 +66,6 @@ - - - @@ -76,10 +73,7 @@ [% FOR t IN translations %] - - - - + @@ -102,7 +96,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' ) { @@ -112,13 +106,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' ) { @@ -135,14 +129,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 }); } @@ -166,12 +164,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'); @@ -212,6 +216,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(); @@ -221,10 +230,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); @@ -234,10 +242,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){ @@ -252,20 +259,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.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 4fbede4c67..c6d81f16b9 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 @@ -13,12 +13,7 @@ export class LocalizationAPIClient extends HttpClient { create: localization => this.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", @@ -27,23 +22,15 @@ export class LocalizationAPIClient extends HttpClient { update: localization => this.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.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 6eed60632c..d07d3b2635 100755 --- a/svc/localization +++ b/svc/localization @@ -2,103 +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 $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 $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 ac33dc9a14..234b8cde5e 100755 --- a/t/db_dependent/Koha/Filter/ExpandCodedFields.t +++ b/t/db_dependent/Koha/Filter/ExpandCodedFields.t @@ -68,10 +68,11 @@ subtest 'ExpandCodedFields tests' => sub { $cache->clear_from_cache("MarcCodedFields-"); # 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 7d115cdb3a..82df6e8902 100755 --- a/t/db_dependent/Koha/Item.t +++ b/t/db_dependent/Koha/Item.t @@ -1950,10 +1950,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( { @@ -2034,10 +2035,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; }; @@ -2054,10 +2056,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 @@ -2240,9 +2243,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; }; diff --git a/t/db_dependent/Koha/ItemTypes.t b/t/db_dependent/Koha/ItemTypes.t index d62cf05e48..09073e320a 100755 --- a/t/db_dependent/Koha/ItemTypes.t +++ b/t/db_dependent/Koha/ItemTypes.t @@ -26,6 +26,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);; @@ -66,30 +67,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 59726397e5..cfc0c23b67 100755 --- a/t/db_dependent/Koha/Template/Plugin/ItemTypes.t +++ b/t/db_dependent/Koha/Template/Plugin/ItemTypes.t @@ -19,6 +19,7 @@ use Modern::Perl; use Test::More tests => 10; use C4::Context; +use Koha::Caches; use Koha::Database; use Koha::ItemTypes; @@ -52,14 +53,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', @@ -69,14 +69,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', @@ -87,6 +86,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 3196bbbb3b..9589f00ff1 100755 --- a/t/db_dependent/api/v1/item_types.t +++ b/t/db_dependent/api/v1/item_types.t @@ -25,6 +25,7 @@ use t::lib::Mocks; use Mojo::JSON qw(encode_json); +use Koha::Caches; use Koha::ItemTypes; use Koha::Database; @@ -66,29 +67,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.39.2
      IdEntityCode Language Translation  
      [% t.id | html %][% t.entity | html %][% t.code | html %]
      [% t.lang | html %] [% t.translation | html %] Delete