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 <caroline.cyr-la-rose@inlibro.com>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
parent
99eb693cd9
commit
e7f483def5
17
C4/Biblio.pm
17
C4/Biblio.pm
|
|
@ -95,6 +95,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;
|
||||
|
|
@ -1570,15 +1571,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" ) {
|
||||
|
|
|
|||
|
|
@ -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<delete> 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
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -234,9 +165,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<Koha::Object/unblessed>
|
||||
|
||||
Overridden to add a C<translated_description> 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,74 +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 <https://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use Koha::Database;
|
||||
|
||||
use base qw(Koha::Object);
|
||||
|
||||
use Koha::Caches;
|
||||
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<delete> 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;
|
||||
|
|
@ -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 <https://www.gnu.org/licenses>.
|
||||
|
||||
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;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <property>_localizations, giving access
|
||||
to all localizations of a particular table row for this particular property
|
||||
|
||||
=item * a "might_have" relationship named <property>_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<localization($property, $lang)>
|
||||
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;
|
||||
|
|
@ -377,22 +377,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');
|
||||
|
||||
=head2 koha_object_class
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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' );
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
|
|
@ -4583,10 +4583,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 */;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<a href="/cgi-bin/koha/admin/localization.pl?source=[% source | uri %]&object_id=[% object_id | uri %]&property=[% property | uri %]"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Translate into other languages</a>
|
||||
|
|
@ -194,7 +194,7 @@
|
|||
<label for="description" class="required">Description: </label>
|
||||
<input type="text" id="description" name="description" size="48" value="[% itemtype.description | html %]" required="required" /> <span class="required">Required</span>
|
||||
[% IF can_be_translated %]
|
||||
<a href="/cgi-bin/koha/admin/localization.pl?entity=itemtypes&code=[% itemtype.itemtype | uri %]"> <i class="fa-solid fa-pencil" aria-hidden="true"></i> Translate into other languages </a>
|
||||
[% INCLUDE 'localization-link.inc' source='Itemtype' object_id=itemtype.itemtype property='description' %]
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>
|
||||
[% FILTER collapse %]
|
||||
[% SWITCH entity %]
|
||||
[% CASE "itemtypes" %]
|
||||
[% tx("Add translations for item type '{code}'", { code = code }) | html %]
|
||||
[% SWITCH source %]
|
||||
[% CASE "Itemtype" %]
|
||||
[% tx("Add translations for item type '{code}'", { code = object_id }) | html %]
|
||||
› [% t("Item types") | html %] › [% t("Administration") | html %]
|
||||
[% CASE %]
|
||||
[% tx("Add translations for '{code}'", { code = code }) | html %]
|
||||
[% tx("Add translations for '{code}'", { code = object_id }) | html %]
|
||||
[% END %]
|
||||
› [% t("Koha") | html %]
|
||||
[% END %]
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
[% END %]
|
||||
[% WRAPPER 'sub-header.inc' %]
|
||||
[% WRAPPER breadcrumbs %]
|
||||
[% SWITCH entity %]
|
||||
[% CASE "itemtypes" %]
|
||||
[% SWITCH source %]
|
||||
[% CASE "Itemtype" %]
|
||||
[% WRAPPER breadcrumb_item %]
|
||||
<a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
|
||||
[% END %]
|
||||
|
|
@ -38,37 +38,37 @@
|
|||
<a href="/cgi-bin/koha/admin/itemtypes.pl">Item types</a>
|
||||
[% END %]
|
||||
[% WRAPPER breadcrumb_item %]
|
||||
<a href="/cgi-bin/koha/admin/itemtypes.pl?op=add_form&itemtype=[% code | uri %]"> [% tx("Modify item type '{code}'", { code = code }) | html %] </a>
|
||||
<a href="/cgi-bin/koha/admin/itemtypes.pl?op=add_form&itemtype=[% object_id | uri %]"> [% tx("Modify item type '{code}'", { code = object_id }) | html %] </a>
|
||||
[% END %]
|
||||
[% WRAPPER breadcrumb_item bc_active= 1 %]
|
||||
<span>Add translations</span>
|
||||
[% END %]
|
||||
[% CASE %]
|
||||
[% WRAPPER breadcrumb_item bc_active= 1 %]
|
||||
[% tx("Add translations for '{code}'", { code = code }) | html %]
|
||||
[% tx("Add translations for '{code}'", { code = object_id }) | html %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
[% END #/ WRAPPER breadcrumbs %]
|
||||
[% END #/ WRAPPER sub-header.inc %]
|
||||
|
||||
[% WRAPPER 'main-container.inc' %]
|
||||
[% SWITCH entity %]
|
||||
[% CASE "itemtypes" %]
|
||||
<h1>[% tx("Add translations for item type '{code}'", { code = code }) | html %]</h1>
|
||||
[% SWITCH source %]
|
||||
[% CASE "Itemtype" %]
|
||||
<h1>[% tx("Add translations for item type '{code}'", { code = object_id }) | html %]</h1>
|
||||
[% CASE %]
|
||||
<h1>[% tx("Add translations for '{code}'", { code = code }) | html %]</h1>
|
||||
[% END %]
|
||||
|
||||
<form id="add_translation" method="get">
|
||||
[% INCLUDE 'csrf-token.inc' %]
|
||||
<input type="hidden" name="entity" value="[% entity | html %]" />
|
||||
<input type="hidden" name="code" value="[% code | html %]" />
|
||||
<input type="hidden" name="interface" value="[% interface_side | html %]" />
|
||||
<input type="hidden" name="source" value="[% source | html %]" />
|
||||
<input type="hidden" name="object_id" value="[% object_id | html %]" />
|
||||
<input type="hidden" name="property" value="[% property | html %]" />
|
||||
<fieldset class="rows clearfix">
|
||||
<ol>
|
||||
<li>
|
||||
<span class="label">Authorized value:</span>
|
||||
[% code | html %]
|
||||
<span class="label">Code:</span>
|
||||
[% object_id | html %]
|
||||
</li>
|
||||
<li>
|
||||
<label for="lang">Language:</label>
|
||||
|
|
@ -100,9 +100,6 @@
|
|||
<table id="localization">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Entity</th>
|
||||
<th>Code</th>
|
||||
<th class="lang">Language</th>
|
||||
<th class="translation">Translation</th>
|
||||
<th class="no-sort"> </th>
|
||||
|
|
@ -110,17 +107,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
[% FOR t IN translations %]
|
||||
<tr id="row_id_[% t.id | html %]" data-id="[% t.id | html %]">
|
||||
<td>[% t.id | html %]</td>
|
||||
<td>
|
||||
[% SWITCH entity %]
|
||||
[% CASE "itemtypes" %]
|
||||
Item type
|
||||
[% CASE %]
|
||||
[% t.entity | html %]
|
||||
[% END %]
|
||||
</td>
|
||||
<td>[% t.code | html %]</td>
|
||||
<tr id="row_id_[% t.localization_id | html %]" data-id="[% t.localization_id | html %]">
|
||||
<td class="lang">[% t.lang | html %]</td>
|
||||
<td class="translation" contenteditable="true">[% t.translation | html %]</td>
|
||||
<td class="actions"
|
||||
|
|
@ -149,7 +136,7 @@
|
|||
var message;
|
||||
if (type == "success_on_update") {
|
||||
message = $('<div class="alert alert-info"></div>');
|
||||
message.text(_("Entity %s (code %s) for lang %s has 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 = $('<div class="alert alert-warning"></div>');
|
||||
if (data.error_code == "already_exists") {
|
||||
|
|
@ -159,13 +146,13 @@
|
|||
}
|
||||
} else if (type == "success_on_delete") {
|
||||
message = $('<div class="alert alert-info"></div>');
|
||||
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 = $('<div class="alert alert-warning"></div>');
|
||||
message.text(_("An error occurred when deleting this translation"));
|
||||
} else if (type == "success_on_insert") {
|
||||
message = $('<div class="alert alert-info"></div>');
|
||||
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 = $('<div class="alert alert-warning"></div>');
|
||||
if (data.error_code == "already_exists") {
|
||||
|
|
@ -182,14 +169,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 });
|
||||
}
|
||||
|
|
@ -213,16 +204,22 @@
|
|||
);
|
||||
}
|
||||
|
||||
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)
|
||||
.row("#row_id_" + localization_id)
|
||||
.remove()
|
||||
.draw();
|
||||
show_message({ type: "success_on_delete", data: { id } });
|
||||
show_message({ type: "success_on_delete", data: { localization_id } });
|
||||
},
|
||||
error => {
|
||||
$(cell).css("background-color", "#FF9090");
|
||||
|
|
@ -247,6 +244,10 @@
|
|||
});
|
||||
$("body").on("click", "td.lang", function () {
|
||||
var td = $(this);
|
||||
if (td[0].childElementCount > 0) {
|
||||
// do nothing if there is already something there (like a select for instance)
|
||||
return;
|
||||
}
|
||||
var tr = td.parent();
|
||||
var id = tr.data("id");
|
||||
var lang = td.text();
|
||||
|
|
@ -258,9 +259,9 @@
|
|||
});
|
||||
var lang;
|
||||
my_select.on("change", function () {
|
||||
lang = td.find("option:selected").val();
|
||||
var translation = tr.find("td.translation").text();
|
||||
send_update_request({ id, lang, translation }, td[0]);
|
||||
var localization_id = $(tr).data('id');
|
||||
lang = td.find('option:selected').val();
|
||||
send_update_request( {localization_id, lang}, td[0] );
|
||||
});
|
||||
my_select.on("blur", function () {
|
||||
td.html(lang);
|
||||
|
|
@ -270,10 +271,9 @@
|
|||
|
||||
$("body").on("blur", "td.translation", 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) {
|
||||
|
|
@ -291,25 +291,28 @@
|
|||
|
||||
$("#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_dt.row
|
||||
.add([success.id, success.entity, success.code, success.lang, success.translation, '<a href="#" class="btn btn-default btn-xs delete"><i class="fa fa-trash-can"></i> Delete</a>'])
|
||||
var new_row = table.api().row
|
||||
.add([success.lang, success.translation, '<a href="#" class="btn btn-xs btn-default delete"><i class="fa fa-trash-can"></i> %s</a>'.format(escape_str(_("Delete")))])
|
||||
.draw()
|
||||
.node();
|
||||
$(new_row)
|
||||
.attr("id", "row_id_" + success.id)
|
||||
.data("id", success.id);
|
||||
.attr("id", "row_id_" + success.localization_id)
|
||||
.data("id", success.localization_id);
|
||||
$(new_row.children[th_lang_index]).prop("contenteditable", true).addClass("lang");
|
||||
$(new_row.children[th_translation_index]).prop("contenteditable", true).addClass("translation");
|
||||
show_message({ type: "success_on_insert", data: success });
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
161
svc/localization
161
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;
|
||||
$response->param(
|
||||
id => $localization->localization_id,
|
||||
entity => $localization->entity,
|
||||
code => $localization->code,
|
||||
lang => $localization->lang,
|
||||
translation => $localization->translation,
|
||||
);
|
||||
Koha::Caches->get_instance('localization')->clear_from_cache("$source:$lang");
|
||||
|
||||
$response->param(
|
||||
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 ],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
@ -2455,10 +2456,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(
|
||||
{
|
||||
|
|
@ -2540,10 +2542,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;
|
||||
};
|
||||
|
||||
|
|
@ -2560,10 +2563,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
|
||||
|
|
@ -2675,6 +2679,7 @@ subtest 'strings_map() tests' => sub {
|
|||
)->store();
|
||||
|
||||
Koha::Caches->get_instance->flush_all;
|
||||
Koha::Cache::Memory::Lite->get_instance->flush;
|
||||
|
||||
$item->set(
|
||||
{
|
||||
|
|
@ -2747,9 +2752,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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use t::lib::TestBuilder;
|
|||
|
||||
use C4::Calendar;
|
||||
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' );
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -26,6 +26,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',
|
||||
|
|
|
|||
Loading…
Reference in New Issue