ORCID

From EPrints Documentation
Jump to: navigation, search

This page has been created to gather information about how different EPrints repositories have integrated ORCID. At this time (April 2016) there doesn't seem to be a 'best practice' approach that has been adopted widely - this will hopefully change over time.

Field Recommendations

As at 2016-10-18 these are the recommended field names and types as discussed at the Jisc ORCID hack day in London - Alan Stiles, John Salter, Adam Vials Moore, Peter West, Andrew Beeken

New MetaField Type

  • Type 'Orcid' is a subtype of 'id', provides validation and render methods

N.B. Store the 16 digit, hyphen separated value, without the authority (host) details e.g. 'nnnn-nnnn-nnnn-nnnn' (as per the value returned by the ORCID API)

User Object

  • new field named 'orcid', type 'Orcid' to store the value of the ORCID id for that user

Suggested fields

  • 'orcid_auth_code', type text - to hold the long authorisation token returned by the ORCID API at the end of a successful OAuth2 process
  • 'orcid_granted_permissions', type text - to hold the permissions granted via the ORCID API during the authorisation process
  • 'orcid_token_expires', type time - to hold the expiry timestamp for the authorisation code above (time now plus expires duration returned by the ORCID API)
  • 'orcid_last_update_timestamp', type text - to hold the timestamp of the last time the user's ORCID record was processed, to enable quicker reprocessing in the future.
  • 'orcid_webhook_ref', type text - to hold the value provided when configuring the webhook notification to validate new notifications have come from ORCID

Also potential fields for requested permissions .e.g. boolean flags or text to capture auth codes if individual permissions captured separately? (e.g. Peter West work)

Eprints Contributors (e.g. creators, editors)

  • new sub-field of contributor_orcid (e.g. creator_orcid, editor_orcid) type 'Orcid' to store the value of the ORCID id for that contributor

Bazaar package

See: https://github.com/eprints/orcid_support for an beta-release Bazaar package to help with ORCID support.

Previous Discussions

If you are looking to retrieve publications from the ORCID service, the Import from ORCID (Tier 1 API) Bazaar package, or the ORCID Tier 2 api framework are good places to start.

2016-05-20 Alan Stiles at The Open University is working on a plugin to connect EPrints with ORCID via the Tier 2 Members API to retrieve ORCID ids and synchronise publications and affiliation details.

Some aspects that need to be considered when adopting ORCIDs, and making them available to other systems are:

NB The examples below may refer to the 'creators' field - but the same approach could be used to extend other 'person' fields - e.g. contributors.

Re-using creators.id subfield

The creators.id field is labelled 'email' by default, but as it's an 'id' type field, it can be used to store ORCIDs. To re-label the field, a simple addition to a phrase file in archives/ARCHIVEID/cfg/lang/en/phrases/ needs to be made:

<epp:phrase id="eprint_fieldname_creators_id">ORCID / Creators email (if known)</epp:phrase>

Rendering the ORCID in a citation

The rendering of this field then needs to be changed to accommodate an author with an ORCID. The following script adds methods that can be called via the EPScript language:

# add to e.g. ~/archives/ARCHIVEID/cfg/cfg.d/z_add_to_EPrints_Script_Compiled.pl
# Write into EPrints::Script::Compiled package to allow use of function in epscript
{
package EPrints::Script::Compiled;
use strict;
 
sub run_wrro_people_with_orcids
{
  my( $self, $state, $value ) = @_;
 
  my $session = $state->{session};
  my $r = $state->{session}->make_doc_fragment;
 
  my $creators = $value->[0];
 
  foreach my $i (0..$#$creators){
 
    my $creator = @$creators[$i];
 
    if( $i > 0 ){
      #not first item (or only one item)
      if( $i == $#$creators ){
        #last item
        $r->appendChild( $session->make_text( " and " ) );
      } else {
        $r->appendChild( $session->make_text( ", " ) );
      }
    }
 
    my $person_span = $session->make_element( "span", "class" => "person" );
    $person_span->appendChild( $session->render_name( $creator->{name} ) );
 
    my $id = $creator->{id};
    if( defined $id && $id =~ m/^(?:orcid.org\/)?(\d{4}\-\d{4}\-\d{4}\-\d{3}(?:\d|X))$/ )
    {
      my $orcid_span = $session->make_element( "span", "class" => "orcid" );
 
#   According to https://orcid.org/trademark-and-id-display-guidelines
#     "recommended display is a hyperlinked URI preceded by the iD icon"
#   This looks terrible in a citation. Removed for the time being.
#
#   my $orcid_link = $session->make_element(
#     "a",
#     "href" => "http://orcid.org/",
#     "target" => "_blank",
#     "class" => "orcid-icon"
#   );
#   $orcid_link->appendChild( $session->make_element(
#     "img",
#     "src" => "/images/orcid_16x16.png",
#     "title" => "ORCID"
#   ) );
#   $orcid_span->appendChild( $orcid_link );
 
      my $link = $session->make_element(
        "a",
        "href" => "http://orcid.org/$1",
        "target" => "_blank",
      );
      $link->appendChild( $session->make_text( "orcid.org/$1" ) );
 
      $orcid_span->appendChild( $session->make_text( "(" ) );
      $orcid_span->appendChild( $link );
      $orcid_span->appendChild( $session->make_text( ")" ) );
 
      $person_span->appendChild( $session->make_text( " " ) );
      $person_span->appendChild( $orcid_span );
    }
    $r->appendChild( $person_span );
  }
 
  return [ $r, "XHTML" ];
}
 
}
# END EPrints::Script::Compiled additions

You may also want a bit of CSS to style ORCID links

/* Add to e.g. ~/archives/ARCHIVEID/cfg/static/style/auto/zzz_orcid.css */
.person:hover {border-bottom: 1px dashed #a6ce39;}
.orcid {}
.orcid a:hover {
        color: #a6ce39;
}
.orcid-icon {}
.orcid-icon img { vertical-align: text-bottom; margin: 0 4px 0 2px; }

And finally, you have to call the new EPScript function from a citation file e.g. archives/ARCHIVEID/cfg/citations/eprint/default.xml

<!-- replace reference to e.g. <print expr="creators_name"/> with this: -->
    <print expr="wrro_people_with_orcids(creators)" />

Exposing the ORCID in RIOXX

Based on re-using the creators.id field, this block of code over-writes the default rioxx2_value_author function.

# Add to e.g. ~/archives/ARCHIVEID/cfg/cfg.d/zzz_rioxx2_overwrites.pl
$c->{rioxx2_value_author} = sub {
  my( $eprint ) = @_;
 
  my @authors;
  for( @{ $eprint->value( "creators" ) } )
  {
    my $id = $_->{id}; # if you use a custom 'orcid' field, reference that here instead of 'id'
    if( defined $id && $id =~ m/^(?:orcid.org\/)?(\d{4}\-\d{4}\-\d{4}\-\d{3}(?:\d|X))$/ )
    {
      push @authors, {
        author => EPrints::Utils::make_name_string( $_->{name} ),
        id => "http://orcid.org/$1"
      };
    } else {
      push @authors, {
        author => EPrints::Utils::make_name_string( $_->{name} ),
      };
    }
  }
 
  #NB If your corp_creators has DOIs or ISNIs for the entries, a similar method could be used to include these here.
  foreach my $corp ( @{ $eprint->value( "corp_creators" ) } )
  {
    my $entry = {};
    $entry->{name} = $corp;
    push @authors, { author => $corp };
  }
 
  return \@authors;
};

Add additional identifiers to creators

Another approach to storing ORCIDs is to add an additional sub-field to the creator field. This has been discussed on the Eprints tech list. If you know how to do this, please document it here!

This is how University of Bath has done it. We're getting the ORCID data from another system, so have used a text field to capture it for display. At present there is no special rendering on it, it's just displaying as a number.

{
	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 => 'orcid',
			type => 'text',
			input_cols => 20,
			allow_null => 1,
		}
 
	],
	input_boxes => 4,
},

This is how University of Pittsburgh has done it:

          {
            'name' => 'creators',
            'type' => 'compound',
            'multiple' => 1,
            'fields' => [
                          {
                            'sub_name' => 'name',
                            'type' => 'name',
                            'hide_honourific' => 1,
                            'hide_lineage' => 1,
                            'family_first' => 1,
                          },
                          {
                            'sub_name' => 'email',
                            'type' => 'text',
                            'input_cols' => 20,
                            'allow_null' => 1,
                          },
                          {
                            'sub_name' => 'id',
                            'type' => 'text',
                            'input_cols' => 20,
                            'allow_null' => 1,
                          },
                          {
                            'sub_name' => 'orcid',
                            'type' => 'id',
                            'input_cols' => 19,
                            'allow_null' => 1,
                          },
                        ],
            'input_boxes' => 4,
            'render_value' => 'custom_render_creators_with_orcid',
          },

Notes from Pittsburgh:

(Information courtesy of Brian Gregg - bdgregg@pitt.edu)

We have added some ORCID functionality in our IR System here at the University of Pittsburgh. We however have not “linked” our IR with orcid.org in anyway such as we do not have a lookup user in ORCID or the ability to add publications from ORCID to our IR. The only link is the entering of the ORCID into the system so that it can be displayed.

An example record is available here: http://d-scholarship.pitt.edu/28114/

  1. Users have an ORCID field in their profile – user_fields.pl
  2. Any “Creator” types have an ORCID field added to the workflow – eprint_fields.pl
  3. We have added ORCID validation adhering to this document: [1] - eprint_validate.pl
  4. We have added the ORCID to the “lookup” function to populate the ORCID field from any user entries in the database. – cgi/users/lookup/name
  5. We display the ORCID as such on the input form, where the green icon links out to our own orcid.pitt.edu site, but could be to orcid.org by default.
  6. We display the ORCID as such on the public display of the record where the ORCID is linked out to orcid.org (see example record)

ORCID Validator - snippet for eprints_validate.pl

        # ORCID ID Validator for individuals
        # Reference: http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
        # REGEX: ^\d{4}-\d{4}-\d{4}-(\d{3}X|\d{4})$
        #
        my @name_fields = qw( creators contributors exhibitors producers conductors lyricists );  #which ever name fields you added 'orcid' to.
        foreach my $name_field ( @name_fields )
        {
           if( $eprint->is_set( $name_field ) )
           {
              foreach my $creator ( @{ $eprint->value( $name_field ) } )
              {
                 my $orcid = $creator->{orcid};
                 next unless defined $orcid;
                 if ($repository->get_repository->can_call ( "validators", "isValidORCID" ) )
                 {
                    if (!$repository->get_repository->call( [ "validators", "isValidORCID" ], $orcid ))
                    {
                       my $field_text = $repository->html_phrase( "eprint_fieldname_$name_field" );
                       my $field_frag = $repository->make_element( "span", class=>"ep_problem_field:$name_field" );
                       $field_frag->appendChild($field_text);
                       push @problems, $repository->html_phrase( "warnings:orcid_invalid",
                            orcid => $repository->get_repository->make_text($orcid),
                            field => $field_frag,
                        );
                    }
                 } else {
                    $repository->get_repository->log( "Can not call isValidORCID." );
                 }
              }
           }
        }

Phrase for "warnings:orcid_invalid":

<epp:phrase id="warnings:orcid_invalid">The ORCID ID: <b><epc:pin name="orcid"/></b> is not a valid ORCID ID as it does not conform to the ORCID ID specification.  Please edit the <epc:pin name="field"/> with the associated ORCID or remove it from the name completely.</epp:phrase>


Additional Function that can be placed either at the end of eprints_validate.pl (after the final };) or elsewhere:

############################################
# Function:     isValidORCID
# Description:  Used to validate ORCID IDs
# 
# From: http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier (Checksum)
#
$c->{validators}->{isValidORCID} = sub {
   my $ORCID = $_[0];
   #Check for valid length of 19 characters.
   my $size = length($ORCID);
   if ($size > 19 )
   {
        return 0;
   } else {
      #Check actual validity of the ID mathematically.
      $ORCID =~ s/\-//g;
      my @chars = split(//, $ORCID);
      my $total = 0;
      for ( my $i=0; $i<15; $i++) {
         $total = ($total + $chars[$i]) *2;
      }
      my $remainder = $total % 11;
      my $result = (12 - $remainder) % 11;
      return ($chars[15] == ($result==10 ? 'X' : $result))
  }
};

Additionally you will have to define a custom render to add the fields to be displayed in the abstract/summary page.

Rendering with Bootstrap

ORCIDs displaying in the Bootstrap theme
ORCID and Bootstrap

In a recent redesign of White Rose Research Online the Bootstrap theme was used. The ORCID links are now enhanced with a tooltip and the green icon. There may be examples on the latest 20 additions page.

Bootstrap tooltop

This block of code finds anything with a class of 'orcid', and adds the green logo to it, and a tooltip that reads 'ORCID: orcid-value', as shown in the image.

The example code below is based on the following rendering of a person who may have an ORCID:

<!-- NB line-breaks added here for readability are not normally output by EPrints! -->
<span class="person">
  <span class="person_name">Castillo Ortiz, P.J.</span>
  <a href="http://orcid.org/0000-0003-4540-1855" target="_blank" class="orcid">
    <span>orcid.org/0000-0003-4540-1855</span>
  </a>
</span>

jQuery

// selector used here depends on your rendering. You may want to use something like a[href*='orcid.org/0000-']
jQuery('.orcid').tooltip({
	'placement': 'auto',
	'title': function(){ return "ORCID: "+ jQuery(this).text()},
}).append('<img src="/images/orcid_16x16.png" alt="" />').find('span').hide();

CSS

The green colouring is added using the following css, which also makes the background of the tooltip opaque, not semi-transparent as it is by default.

.person + .tooltip > .tooltip-inner,
.orcid + .tooltip > .tooltip-inner {
  background-color: #A6CE39;
  border: 1px solid #000000;
  white-space: pre;
  max-width: none;
}
.person + .tooltip.in,
.orcid + .tooltip.in {
  opacity: 1;
  filter: alpha(opacity=100);
}
/* This adds a green dashed line under an author's name when hovered. This helps in big author lists to tie initials and surname together */
.person:hover {border-bottom: 1px dashed #a6ce39;}
.orcid a:hover {
        color: #a6ce39;
}

OAI-PMH (Experimental)

User:Libjlrs 2016-09-29 Warning: this is experimental. I hope others will read, understand, question and improve on this before it becomes some form of best practice!

Some metadata profiles support attributes that are useful to propagate ORCIDs e.g. RIOXX (see the #Exposing_the_ORCID_in_RIOXX section above, and the 'rioxxterms:author' section on http://www.rioxx.net/profiles/v2-0-final/ ).

A lot of harvesters use the oai_dc metadata profile when harvesting records. The DC schema doesn't allow attributes on the dc:creator element, so ORCIDs cannot be added in a sensible way to this profile.

The details below create an additional metadata prefix that adds an attribute to the dc:creator field. The original metadataPrefix=oai_dc output remains unchanged. Using metadataPrefix=oai_dc_orcid will add the orcid attribute where available.

Currently there is not an XSD that describes this change, so a harvester that validates the feed may stop when it encounters an ORCID.

This example is based on the EPrints::Plugin::Export::OAI_DC module from EPrints 3.3.10. You may need to tweak it to make it work for you!

File: ~/archives/ARCHIVEID/cfg/plugins/EPrints/Plugin/Export/OAI_DC_ORCID.pm

package EPrints::Plugin::Export::OAI_DC_ORCID;
 
use EPrints::Plugin::Export::OAI_DC;
 
@ISA = ( "EPrints::Plugin::Export::OAI_DC", "EPrints::Plugin::Export::DC" );
 
use strict;
 
sub new
{
        my( $class, %opts ) = @_;
 
        my $self = $class->SUPER::new( %opts );
 
        $self->{name} = "Dublin Core - OAI + ORCID Schema";
        $self->{accept} = [ 'dataobj/eprint' ];
        $self->{visible} = "";
        $self->{suffix} = ".xml";
        $self->{mimetype} = "text/xml";
 
        $self->{metadataPrefix} = "oai_dc_orcid";
        $self->{xmlns} = "http://www.openarchives.org/OAI/2.0/oai_dc/";
        $self->{schemaLocation} = "http://www.openarchives.org/OAI/2.0/oai_dc.xsd";
 
        return $self;
}
 
sub xml_dataobj
{
        my( $plugin, $dataobj ) = @_;
 
        my $data = $plugin->convert_dataobj( $dataobj );
 
        my $dc = $plugin->{session}->make_element(
                "oai_dc:dc",
                "xmlns:oai_dc" => "http://www.openarchives.org/OAI/2.0/oai_dc/",
                "xmlns:dc" => "http://purl.org/dc/elements/1.1/",
                "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
                "xsi:schemaLocation" =>
        "http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd" );
 
        # turn the list of pairs into XML blocks (indented by 8) and add them
        # them to the DC element.
        foreach( @{$data} )
        {
                $dc->appendChild(  $plugin->{session}->render_data_element( 8, "dc:".$_->[0], $_->[1], %{$_->[2]} ) );
                # produces <key>value</key>
        }
 
        return $dc;
}
 
sub convert_dataobj
{
        my( $plugin, $eprint ) = @_;
 
        my $dataset = $eprint->{dataset};
 
        my @dcdata = ();
 
        push @dcdata, $plugin->simple_value( $eprint, title => "title" );
 
        if( $eprint->exists_and_set( "creators" ) )
        {
                my $creators = $eprint->get_value( "creators" );
                foreach my $creator ( @$creators )
                {
                        next if !defined $creator;
 
                        # NOTE THIS IS VERY CRUDE, and should be replaced with more elegant methods (possibly from examples elsewhere on this page).
                        if( defined $creator->{id} && $creator->{id} =~ /0000/ ){
                                push @dcdata, [ "creator", EPrints::Utils::make_name_string( $creator->{name} ), { 'orcid' => $creator->{id} } ];
                        } else {
                                push @dcdata, [ "creator", EPrints::Utils::make_name_string( $creator->{name} ) ];
                        }
                }
        }
 
        if( $eprint->exists_and_set( "subjects" ) )
        {
                my $subjectid;
                foreach $subjectid ( @{$eprint->get_value( "subjects" )} )
                {
                        my $subject = EPrints::DataObj::Subject->new( $plugin->{session}, $subjectid );
                        # avoid problems with bad subjects
                                next unless( defined $subject );
                        push @dcdata, [ "subject", EPrints::Utils::tree_to_utf8( $subject->render_description() ) ];
                }
        }
 
        push @dcdata, $plugin->simple_value( $eprint, abstract => "description" );
        push @dcdata, $plugin->simple_value( $eprint, publisher => "publisher" );
 
        if( $eprint->exists_and_set( "editors_name" ) )
        {
                my $editors = $eprint->get_value( "editors_name" );
                if( defined $editors )
                {
                        foreach my $editor ( @{$editors} )
                        {
                                push @dcdata, [ "contributor", EPrints::Utils::make_name_string( $editor ) ];
                        }
                }
        }
        ## Date for discovery. For a month/day we don't have, assume 01.
        if( $eprint->exists_and_set( "date" ) )
        {
                my $date = $eprint->get_value( "date" );
                if( defined $date )
                {
                        $date =~ s/(-0+)+$//;
                        push @dcdata, [ "date", $date ];
                }
        }
 
        if( $eprint->exists_and_set( "type" ) )
        {
                push @dcdata, [ "type", EPrints::Utils::tree_to_utf8( $eprint->render_value( "type" ) ) ];
        }
 
        my $ref = "NonPeerReviewed";
        if( $eprint->exists_and_set( "refereed" ) && $eprint->get_value( "refereed" ) eq "TRUE" )
        {
                $ref = "PeerReviewed";
        }
        push @dcdata, [ "type", $ref ];
 
 
        my @documents = $eprint->get_all_documents();
        my $mimetypes = $plugin->{session}->get_repository->get_conf( "oai", "mime_types" );
        foreach( @documents )
        {
                my $format = $mimetypes->{$_->get_value("format")};
                $format = $_->get_value("format") unless defined $format;
                #$format = "application/octet-stream" unless defined $format;
                push @dcdata, [ "format", $format ];
                push @dcdata, [ "identifier", $_->get_url() ];
        }
 
        # Most commonly a DOI or journal link
        push @dcdata, $plugin->simple_value( $eprint, official_url => "relation" );
 
        # The citation for this eprint
        push @dcdata, [ "identifier",
                EPrints::Utils::tree_to_utf8( $eprint->render_citation() ) ];
 
        # The URL of the abstract page
        if( $eprint->is_set( "eprintid" ) )
        {
                push @dcdata, [ "relation", $eprint->get_url() ];
        }
        # dc.language not handled yet.
        # dc.source not handled yet.
        # dc.coverage not handled yet.
        # dc.rights not handled yet.
 
        return \@dcdata;
}
 
1;