Using Internal IDs for Staff Browse Views

From EPrints Documentation
Revision as of 13:49, 21 June 2017 by Drn@ecs.soton.ac.uk (talk | contribs) (Added render_internalid to render number rather than name when appropriate.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Scenario

Your institution/organisation has a unique identfiers for your members/staff and you want to be able to use these to create a browse view listing each individuals publication list.

By default EPrints provides a browse view based on the given name and family name of those listed in the Creators field of an EPrints publication. This is not useful for most institutions/organisations for a number of reasons:

  1. Given name and family name is not necessary unique
  2. Some creators will not be members of your institution/organisation and therefore you do not want them listed.
  3. You have an existing ID that you want to be able to use programmatically to link to their EPrints publication list.

Solution

By default, when adding creators to an EPrint, as well as a field for given name and family name there is also a column for Email, this is just labelled as such but is actually named creators_id and therefore can be any unique identifier for the user. However, it is important that the unique identifier used is consistent, (i.e. it is always an email address or another unique identifier used by your institution/organisation). In general, it is best to retain email address as a unique identifier, as you may wish to use this to allow visitors to you repository to contact creators (authors), also you may want to record the email address of creators who are not part of your institution/organisation. Therefore for this solution, we will assume that another unique identifier will be used separate to email address.

Lets assume that your institution/organisation has a public members/staff ID that you want to use. To ensure that this members/staff ID is adding whenever this person is added as a creator, you can use Autocompletion. Autocompletion can be setup to use the users records within EPrints. This assumes that you have generated accounts for all your members/staff. This can often be done using LDAP.

Procedure

N.B. The following instructions assume EPrints has been installed in the directory /opt/eprints3/ some EPrints install will appear under /usr/share/eprints3/ or other locations. Make sure you amend these as appropriate for your instaltion

Add Internal ID field to User

1. In your archive's cfg/cfg.d/user_fields.pl file ad the following snippet of code at the end:

push @{$c->{fields}->{user}},
{
  name => 'internalid',
  type => 'id',
};

2. Run EPrints database update command to add this new field:

/opt/eprints3/bin/epadmin update ARCHIVENAME

3. Assuming the above has been successful add the phrases required for this new field to cfg/lang/en/phrases/local.xml

<epp:phrase id="user_fieldname_internalid">Internal ID</epp:phrase>
<epp:phrase id="user_fieldhelp_internalid">Unique ID used to identify a user, used by <epc:phrase ref="archive_name" /></epp:phrase>

4. Test that EPrints is happy with these changes and if so reload Apache:

/opt/eprints3/bin/epadmin test ARCHIVENAME
apachectl graceful


Add Internal ID to creators/editors/contributors fields for an EPrint

1. Add the following code the a file at /opt/eprints3/lib/plugins/EPrints/MetaField/Internalidname.pm:

package EPrints::MetaField::Internalidname;

use strict;
use warnings;

use EPrints::MetaField::Text;

BEGIN
{
  our( @ISA );
  @ISA = qw( EPrints::MetaField::Text );
}

sub _get_name
{
  my( $self, $session, $internalid ) = @_;

  my $format = $session->config( 'internalid_to_name', 'format' );

  return unless defined $format;

  if( $session->can_call( 'internalid_to_name', 'parser' ) )
  {
    my $name = $session->call( [ 'internalid_to_name', 'parser' ], $session, $internalid );
    return $session->render_name( $name, $format ) if defined $name;
  }

  return;
}

sub get_value_label
{
  my( $self, $session, $value ) = @_;

  my $name = $self->_get_name( $session, $value );
  return $name if defined $name;

  return $session->make_text( $value );
}

sub ordervalue_basic
{
  my( $self, $value, $session, $langid ) = @_;

  my $name = $self->_get_name( $session, $value );
  return EPrints::Utils::tree_to_utf8( $name ) if defined $name;
 
  return $value;
}

sub render_single_value
{
  my( $self, $session, $value ) = @_;
  return $self->get_value_label( $session, $value );
}

1;

2. Edit your archive's cfg/cfg.d/eprint_fields.pl. Under the field named creators, you will see a block labelled fields. Copy and paste the block that has the sub_name id and copy and paste it just before the end fields block, ensuring to add a comma a comma prior to this. Then modify the sub_name from id to internalid. The creators block should then look something like below:

{
  name => 'creators',
  type => 'compound',
  multiple => 1,
  fields => [
    {
      sub_name => 'name',
      type => 'name',
      hide_honourific => 1,
      hide_lineage => 1,
      family_first => 1,
    },
    {
      sub_name => 'id',
      type => 'text',
      input_cols => 20,
      allow_null => 1,
    },
    {
      sub_name => 'internalid',
      type => 'internalidname',
      input_cols => 10,
      allow_null => 1,
      render_single_value => 'render_internalid',
    },
  ],
},

3. Repeat this change with the contributors' and editors fields.


4. Create a file called internalid_to_name.pl in cfg/cfg.d/ of your archive, with the following contents:

$c->{internalid_to_name}->{parser} = sub
{
  my( $session, $internalid ) = @_;
  return unless EPrints::Utils::is_set( $internalid );

  my $user_ds = $session->get_repository->get_dataset( "user" );
  my $user_search = new EPrints::Search( session => $session, dataset => $user_ds );
  $user_search->add_field( $user_ds->get_field( "internalid" ), $internalid );
  my $users = $user_search->perform_search;
  return unless $users->count() > 0;

  return $users->item( 0 )->get_value("name");
};

$c->{internalid_to_name}->{format} = 0; # family, given

$c->{render_internalid} = sub
{
  my( $session, $field, $value, $obj ) = @_;

  my $fragment = $session->make_doc_fragment;
  $fragment->appendChild($session->xml->create_text_node($value));
  return $fragment;
}

5. Run EPrints database update command to add these new sub-fields:

/opt/eprints3/bin/epadmin update ARCHIVENAME 

6. Assuming the above has been successful add the phrases required for this new field to cfg/lang/en/phrases/local.xml

<epp:phrase id="eprint_fieldname_creators_internalid">Internal ID</epp:phrase>
<epp:phrase id="eprint_fieldhelp_creators_internalid">Unique ID used to identify a user, used by <epc:phrase ref="archive_name" /></epp:phrase>
<epp:phrase id="eprint_fieldname_contributors_internalid">Internal ID</epp:phrase>
<epp:phrase id="eprint_fieldhelp_contributors_internalid">Unique ID used to identify a user, used by <epc:phrase ref="archive_name" /></epp:phrase>
<epp:phrase id="eprint_fieldname_editors_internalid">Internal ID</epp:phrase>
<epp:phrase id="eprint_fieldhelp_editors_internalid">Unique ID used to identify a user, used by <epc:phrase ref="archive_name" /></epp:phrase>

7. Test that EPrints is happy with these changes and if so reload Apache:

/opt/eprints3/bin/epadmin test ARCHIVENAME
apachectl graceful


Add Autocompletion for creators/editors/contributors fields

1. Create a new file in your archive under cgi/users/lookup/staffinternal and edit:

use EPrints;
use strict;

my $session = EPrints::Session->new();

my $content = "text/xml";
$session->send_http_header( content_type=>$content );
my $family = $session->param( "_name_family" );
my $given = $session->param( "_name_given" );
my $id = $session->param( "_id" );
my $internalid = $session->param( "_internalid" );

my $database = $session->get_database;
my $dataset = $session->get_repository->get_dataset( "user" );
my $name_field = $dataset->get_field( "name" );
my $email_field = $dataset->get_field( "email" );
my $internalid_field = $dataset->get_field( "internalid" );

# the field to use as the creator ID
my $id_fieldname = "email";
my $id_field = $dataset->get_field( $id_fieldname );

my @fields = ($name_field->get_sql_names, $id_field->get_sql_names, $email_field->get_sql_names, $internalid_field->get_sql_names);

my $Q_table = $database->quote_identifier($dataset->get_sql_table_name);
my $Q_usertype = $database->quote_identifier( "usertype" );
my $Q_id = $database->quote_identifier( $id_fieldname );
 
my $sql = "SELECT " . join(",", map { $database->quote_identifier($_) } @fields ) .
  " FROM $Q_table " .
  " WHERE $Q_usertype != ".$database->quote_value( "minuser" ) .
  " AND $Q_id IS NOT NULL ";

if( EPrints::Utils::is_set( $family ) )
{
  $sql .= " AND ".$database->quote_identifier("name_family").$database->sql_LIKE().$database->quote_value(EPrints::Database::prep_like_value($family).'%');
}
if( EPrints::Utils::is_set( $given ) )
{
  $sql .= " AND ".$database->quote_identifier("name_given").$database->sql_LIKE().$database->quote_value(EPrints::Database::prep_like_value($given).'%');
}
if( EPrints::Utils::is_set( $id ) )
{
  $sql .= " AND ".$database->quote_identifier($id_field->get_name).$database->sql_LIKE().$database->quote_value(EPrints::Database::prep_like_value($id).'%');
}
if( EPrints::Utils::is_set( $internalid ) )
{
  $sql .= " AND ".$database->quote_identifier($internalid_field->get_name).$database->sql_LIKE().$database->quote_value(EPrints::Database::prep_like_value($internalid).'%'); 
}

$sql .= " ORDER BY " .
  $database->quote_identifier("name_family").",".
  $database->quote_identifier("name_given");

my @rows;

my $sth = $session->get_database->prepare_select( $sql, 'limit' => 40 );
$session->get_database->execute( $sth , $sql );
while( my @row = $sth->fetchrow_array )
{
  my $name = $name_field->value_from_sql_row( $session, \@row );
  my $email = $email_field->value_from_sql_row( $session, \@row );
  my $id = $id_field->value_from_sql_row( $session, \@row );
  my $internalid = $internalid_field->value_from_sql_row( $session, \@row );

  my $item = {};
  push @rows, $item;

  my $frag = $session->make_doc_fragment;
  $frag->appendChild( $name_field->render_single_value( $session, $name ) );
  if ( EPrints::Utils::is_set( $internalid ) ) 
  {
    $frag->appendChild( $session->make_text( " [" ) );
    $frag->appendChild( $email_field->render_single_value( $session, $internalid ) );
    $frag->appendChild( $session->make_text( "]" ) );
  }
  if ( EPrints::Utils::is_set( $email ) ) 
  {
    $frag->appendChild( $session->make_text( " (" ) );
    $frag->appendChild( $email_field->render_single_value( $session, $email ) );
    $frag->appendChild( $session->make_text( ")" ) );
  }
  
  $item->{xhtml} = $frag;
  $item->{values} = [
    "for:value:relative:_name_family" => $name->{family},
    "for:value:relative:_name_given" => $name->{given},
    "for:value:relative:_name_honourific" => $name->{honourific},
    "for:value:relative:_name_lineage" => $name->{lineage},
    "for:value:relative:_internalid" => $internalid,
    "for:value:relative:_id" => $id,
  ];
}

$sth->finish();
my $ul = EPrints::Extras::render_lookup_list( $session, \@rows );
$session->send_http_header( content_type => "text/xml; charset=UTF-8" );
binmode(STDOUT,":utf8");
print <<END;
<?xml version="1.0" encoding="UTF-8" ?>

END
print EPrints::XML::to_string( $ul, "utf-8", 1 );
EPrints::XML::dispose( $ul );
$session->terminate;


2. Edit your archive's cfg/workflows/eprint/default.xml and replace /users/lookup/name with /users/lookup/staffinternal where it appears as the input_lookup_url for creators, editors, and contributors. N.B. each of these fields may appear more than once in this file due to the use of conditional statements.

3. Now test the configuration and although it should not be necessary ,it is worth gracefully reloading Apache to make sure the latest configuration is loaded.

/opt/eprints3/bin/epadmin test ARCHIVENAME
apachectl graceful

Create Browse View based on Internal IDs

1. Edit your archives cfg/cfg.d/views.pl and add the following code snippet at the end of the file:

push @{ $c->{browse_views} }, (
{
  id => "creators_internalid",
  allow_null => 0,
  hideempty => 1,
  menus => [
    {
      fields => [ "creators_internalid", "contributors_internalid", "editors_internalid"  ],
      new_column_at => [1, 1],
      mode => "sections",
      open_first_section => 1,
      group_range_function => "EPrints::Update::Views::cluster_ranges_30",
      grouping_function => "EPrints::Update::Views::group_by_a_to_z",
    },
  ],
  order => "-date/title",
  variations => [
    "type",
    "date;truncate=4,reverse",
    "DEFAULT",
  ],
});

2. Add the following phrase to your archives's cfg/lang/en/phrases/local.xml:

<epp:phrase id="viewname_eprint_creators_internalid">Internal Creators</epp:phrase>

3. Test that EPrints is happy with these changes and if so reload Apache:

/opt/eprints3/bin/epadmin test ARCHIVENAME
apachectl graceful

4. Request the path /views/creators_internalid under your repository's hostname in a web browser (e.g. http://example.eprints.org/views/creators_internalid).