Difference between revisions of "Adding multilang fields"

From EPrints Documentation
Jump to: navigation, search
(Introducing ml_title field in EPrints)
Line 165: Line 165:
 
What has happened in effect is that the '''title''' field has become a calculated field that gets or sets its value in its corresponding multilingual field. So each new record now has a multilingual field which it accesses via the interface provided by the calculated field (of type '''virtualwithvalue''').
 
What has happened in effect is that the '''title''' field has become a calculated field that gets or sets its value in its corresponding multilingual field. So each new record now has a multilingual field which it accesses via the interface provided by the calculated field (of type '''virtualwithvalue''').
  
The reason we don't set title field to be of type '''multilang''' in the first place is that many EPrints functions expect only one value from the title (and abstract) field, and '''multilang''' fields don't support such functionality. By using and extending calculated fields (like '''MetaFields'''), we can calculate and produce always a single output for the '''multilang''' field (based on some condition) via '''get_value''' function; '''set_value''' function is used for populating our '''ml_title''' field's values.
+
The reason we don't set title field to be of type '''multilang''' in the first place is that many EPrints functions expect only one value from the title (and abstract) field, and '''multilang''' fields don't support such functionality, so EPrints produces errors. By using and extending calculated fields (like '''MetaFields'''), we can calculate and produce always a single output for the '''multilang''' field (based on some condition) via '''get_value''' function; '''set_value''' function is used for populating our '''ml_title''' field's values.

Revision as of 14:48, 6 June 2016

This page explains how to add multilingual fields in EPrints and how they can be integrated with it.

How to add a custom, multilingual field

In order to add a new field in EPrints (such as the ml_title field), a few things need to take place:

  • A new field type needs to be created that will help our field to implement some of EPrints logic. Our field type is called virtualwithvalue.
  • The field's name and functionality needs to be introduced to the EPrints system via a configuration file located in ~eprints/archives/<reponame>/cfg/cfg.d/.
  • EPrints' database needs to be updated to include the new field. This is achieved when user eprints executes (from his home directory):
$ ./bin/epadmin update reponame
  • The appropriate phrases need to be added for each field and for each supported language.
  • The workflow needs to be edited in order to contain the new fields - and not include the original ones. The new fields contain custom lookup scripts.
  • EPrints advanced and simple search functionality needs to take the new fields into account.
  • The repository needs to be reloaded, by running:
$ ./bin/epadmin reload reponame

within eprints user's home directory.

The following sections explain each step in detail, where we are using ml_title as our example field. The code snippets are just for demonstration purposes (and proof of concept). If you want to see the final, working implementation, you should look at the source code of the plugin.

Adding a new field type

In order to create a multiple-language field we have to create an appropriate field type. EPrints' MetaField is a perfect candidate for this, and we need to extend it and override its get_value and set_value functions for the field to work properly with the rest of EPrints API, as well as its get_property_defaults function to sort out warnings for default values. The following code could be a rough implementation of such a field:

package EPrints::MetaField::Virtualwithvalue;

use strict;
use warnings;

use EPrints::MetaField;

our @ISA = qw( EPrints::MetaField );

use strict;

sub get_property_defaults
{
    my ( $self ) = @_;
    my %defaults = $self->SUPER::get_property_defaults;
    $defaults{get_value} = $EPrints::MetaField::UNDEF;
    $defaults{set_value} = $EPrints::MetaField::UNDEF;

    return %defaults;
}

sub get_value
{
    my( $self, $object ) = @_;
    if ( defined $self->get_property("get_value") )
    {
        return $self->call_property( "get_value", $object);
    }
    return undef;
}

sub set_value
{
     my( $self, $object, $value ) = @_;
     if ( defined $self->get_property("set_value") )
     {
         return $self->call_property( "set_value", $object, $value);
     }
     return undef;
}

We could save this file in ~eprints/lib/plugins/EPrints/MetaField/Virtualwithvalue.pm.

Introducing ml_title field in EPrints

To inform EPrints about our new field (that will be of type virtualwithvalue), we should create a configuration file, eg: ~eprints/archives/<reponame>/cfg/cfg.d/zz_multilang_field.pl with content like the following:

#define local fields
my $local_fields = [
{
    name => 'ml_title',
    type => 'multilang',
    multiple => 1,
    fields => [ { sub_name => "text", type => "longtext", input_rows => 3, make_single_value_orderkey => 'EPrints::Extras::english_title_orderkey' } ],
    input_add_boxes => 1,
},

{
    name => 'title',
    type => 'virtualwithvalue',
    virtual => 1,

    get_value => sub
    {
        my ($eprint) = @_;
        if ($eprint->is_set('ml_title'))
        {
            my $lang = $eprint->repository->get_langid;
            my $lang_set = 0;
            my $vals = $eprint->get_value('ml_title');
            my $title = '';
            if (!$lang)
            {
                $lang_set = 1;
            }
            else
            {
                # set the default lang's text as title
                foreach my $v1 (@{$vals})
                {
                    if ($v1->{lang} eq $lang)
                    {
                        $title = $v1->{text};
                    }
                }
            }
            # if the language is not set or I can't find an abstract in the 
            # user's language, get the first object's text as abstract
            if ($lang_set or $title eq '')
            {
                $title = $vals->[0]->{text};
            }
            return $title;

        }
        return undef;
    },

    set_value => sub
    {
        my ($eprint, $value) = @_;
        my $lang = 'en';
        #only use this on imports, NOT if the value is already set
        if ($eprint->is_set('ml_title'))
        {
            return;
        }
        if ($value)
        {
            $eprint->set_value('ml_title', [{lang=>$lang, text=>$value}]);
        }
    }
},
];

#create lookup hash of local field names
my $local_fieldnames = {};

foreach my $f (@{$local_fields})
{
    $local_fieldnames->{$f->{name}} = 1;
}

#merge in existing field configurations
foreach my $f (@{$c->{fields}->{eprint}})
{
    if (!$local_fieldnames->{$f->{name}})
    {
     push @{$local_fields}, $f;
    }
}

#overwrite original array of configured fields
$c->{fields}->{eprint} = $local_fields;

Where we can see that our new ml_title field is of type multilang (EPrints multilingual field type) and the field title has changed to be of type virtualwithvalue and now implements the two aforementioned functions: get_value and set_value. Both these functions are used by EPrints API, and their existence, as well as their return values, are critical for EPrints to work properly. Their names imply their functionality. In the end of our example code, one can see how a custom field can be added in the list of EPrints fields.

What has happened in effect is that the title field has become a calculated field that gets or sets its value in its corresponding multilingual field. So each new record now has a multilingual field which it accesses via the interface provided by the calculated field (of type virtualwithvalue).

The reason we don't set title field to be of type multilang in the first place is that many EPrints functions expect only one value from the title (and abstract) field, and multilang fields don't support such functionality, so EPrints produces errors. By using and extending calculated fields (like MetaFields), we can calculate and produce always a single output for the multilang field (based on some condition) via get_value function; set_value function is used for populating our ml_title field's values.