diff --git a/DataCiteDoi.epmi b/DataCiteDoi.epmi index 9d33088..626d016 100644 --- a/DataCiteDoi.epmi +++ b/DataCiteDoi.epmi @@ -1,42 +1,78 @@ - + DataCiteDoi - http://eprints/id/epm/DataCiteDoi + http://clean.ep.devorkin:8082/id/epm/DataCiteDoi - epm/DataCiteDoi/cfg/cfg.d/z_datacitedoi.pl + document + lang/en/phrases/coinDOI.xml + application/xml + ea8da6c0e471cea13c5b573d115cbbe8 + MD5 + 641 + + + document + static/images/epm/datacitedoi.png + image/png + 14e9e2be0a73f452533488634aaea66e + MD5 + 101504 + + + document + plugins/EPrints/Plugin/Event/DataCiteEvent.pm text/plain - fcb3176e88cf0bccb6c8e000bd1ac0da + 3fe07ce4a3a6e765b647b16118cc23f2 MD5 - 1070 + 5213 + document plugins/EPrints/Plugin/Export/DataCiteXML.pm - application/x-symlink - 84f4180876ff95bdde9f78a6a4c300e6 + text/plain + 696d1ecb13af35cdf6cba01198219b30 MD5 - 3898 + 2806 - static/images/epm/datacitedoi.png - application/x-symlink - 14e9e2be0a73f452533488634aaea66e + document + plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm + text/plain + 0ceae1dd50779c660b2e2a6bb4e4ff27 MD5 - 101504 + 2237 + + + document + cfg.d/z_datacite_mapping.pl + text/plain + 4f2807b6c4cd8fba51cada676ebf9278 + MD5 + 8691 + + + document + epm/DataCiteDoi/cfg/cfg.d/z_datacitedoi.pl + text/plain + 670a102e25b0b7636091ccf15d200847 + MD5 + 5046 - application/x-symlink - other -
static/images/epm/datacitedoi.png
+ text/plain + text +
epm/DataCiteDoi/cfg/cfg.d/z_datacitedoi.pl
install
+ document static/images/epm/datacitedoi.png - application/x-symlink + image/png 14e9e2be0a73f452533488634aaea66e MD5 101504 @@ -47,15 +83,19 @@ coverimage
- 0.9.0 + 2.0.0 - Milsted - Andrew + McNicholl + Rory + repositories@ulcc.ac.uk - Data Cite DOI Registration + 2017-09-20 15:27:51 + DataCiteDoi - DataCite DOIs for EPrints + A plugin integrate into the workflow the process of to minting and registering DataCite DOIs + https://github.com/eprintsug/DataCiteDoi images/epm/datacitedoi.png
diff --git a/README.md b/README.md index 96b6d88..8e0658a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ Installation Install the plugin from the bazaar and edit the following config files. ``` -z_datacite_core.pl z_datacitedoi.pl ``` @@ -52,9 +51,14 @@ $c->{datacitedoi}{pass} = "PASS"; # prints, distributes, releases, issues, or produces the # resource. This property will be used to formulate the # citation, so consider the prominence of the role. -# eg World Data Center for Climate (WDCC); +# eg World Data Center for Climate (WDCC); $c->{datacitedoi}{publisher} = "Eprints Repo"; +# Namespace and location for datacite XML schema +# feel free to update, though no guarantees it'll be accepted if you do +$c->{datacitedoi}{xmlns} = "http://datacite.org/schema/kernel-2.2"; +$c->{datacitedoi}{schemaLocation} = $c->{datacitedoi}{xmlns}." ".$c->{datacitedoi}{xmlns}."/metadata.xsd"; + # need to map eprint type (article, dataset etc) to ResourceType # Controled list http://schema.datacite.org/meta/kernel-2.2/doc/DataCite-MetadataKernel_v2.2.pdf # where v is the ResourceType and a is the resourceTypeGeneral @@ -74,42 +78,84 @@ $c->{datacitedoi}{typemap}{other} = {v=>'Misc',a=>'Collection'}; $c->{datacitedoi}{typemap}{dataset} = {v=>'Dataset',a=>'Dataset'}; $c->{datacitedoi}{typemap}{audio} = {v=>'Audio',a=>'Sound'}; $c->{datacitedoi}{typemap}{video} = {v=>'Video',a=>'Film'}; -``` -z_datacite_core.pl +########################### +#### DOI syntax config #### +########################### -Adds the minting plugin, by default it the plugin will run when ever the status (draft,review,deposit,delete) changes. -The plugin will then run asynchronously after the change using the eprints queue so the datacite registration wont affect the users view. +# Set config of DOI delimiters +# Feel free to change, but they must conform to DOI syntax +# If not set will default to prefix/repoid/id the example below gives prefix/repoid.id +$c->{datacitedoi}{delimiters} = ["/","."]; -```perl -# Adds the minting plugin to the EP_TRIGGER_STATUS_CHANGE -$c->add_dataset_trigger( "eprint", EP_TRIGGER_STATUS_CHANGE , sub { +# If set, plugin will attempt to register what is found in the EP DOI field ($c->{datacitedoi}{eprintdoifield}) +# Will only work if what is found adheres to DOI syntax rules (obvioulsy) +$c->{datacitedoi}{allow_custom_doi} = 0; + +#Datacite recommend digits of length 8-10 set this param to pad the id to required length +$c->{datacitedoi}{zero_padding} = 8; + +########################################## +### Override which URL gets registered ### +########################################## + +#Only useful for testing from "wrong" domain (eg an unregistered test server) should be undef for normal operation +$c->{datacitedoi}{override_url} = undef; + +########################## +##### When to coin ? ##### +########################## + +#If auto_coin is set DOIs will be minted on Status change (provided all else is well) +$c->{datacitedoi}{auto_coin} = 0; +#If action_coin is set then a button will be displayed under action tab (for staff) to mint DOIs on an adhoc basis +$c->{datacitedoi}{action_coin} = 1; + +# NB setting auto_coin renders action coin redundant as only published items can be registered + +####### Formerly in cfg.d/datacite_core.pl ######### + +# Including datacite_core.pl below as we can make some useful decisions based on the above config. + +## Adds the minting plugin to the EP_TRIGGER_STATUS_CHANGE +if($c->{datacitedoi}{auto_coin}){ + $c->add_dataset_trigger( "eprint", EP_TRIGGER_STATUS_CHANGE , sub { my ( %params ) = @_; - + my $repository = %params->{repository}; return undef if (!defined $repository); - - if (defined %params->{dataobj}) { - my $dataobj = %params->{dataobj}; - my $eprint_id = $dataobj->id; - + + if (defined %params->{dataobj}) { + my $dataobj = %params->{dataobj}; + my $eprint_id = $dataobj->id; $repository->dataset( "event_queue" )->create_dataobj({ - pluginid => "Event::DataCiteEvent", - action => "datacite_doi", - params => [$dataobj->internal_uri], - }); - }); - } + pluginid => "Event::DataCiteEvent", + action => "datacite_doi", + params => [$dataobj->internal_uri], + }); + } -}); + }); +} + +# Activate an action button, the plugin for whcih is at +# /plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm +if($c->{datacitedoi}{action_coin}){ + $c->{plugins}{"Screen::EPrint::Staff::CoinDOI"}{params}{disable} = 0; +} + ``` -Who it works + +How it works ------------- /lib/plugins/EPrints/Plugin/Event/DataCiteEvent.pm This is added to the queue and actually mints the doi. +lib/plugins/EPrints/Plugin/Sreen/EPrint/Staff/CoinDOI.pm +This adds a button to enable staff to choose when to coin the DOI and request registration + /lib/plugins/EPrints/Plugin/Export/DataCiteXML.pm This exports the metadata xml required for minting, this can be used independently and through the user interface. diff --git a/cfg/cfg.d/z_datacite_core.pl b/cfg/cfg.d/z_datacite_core.pl deleted file mode 100644 index 576d8e0..0000000 --- a/cfg/cfg.d/z_datacite_core.pl +++ /dev/null @@ -1,22 +0,0 @@ -# Addes the miniting plugin to the EP_TRIGGER_STATUS_CHANGE -$c->add_dataset_trigger( "eprint", EP_TRIGGER_STATUS_CHANGE , sub { - my ( %params ) = @_; - - my $repository = %params->{repository}; - - return undef if (!defined $repository); - - if (defined %params->{dataobj}) { - my $dataobj = %params->{dataobj}; - my $eprint_id = $dataobj->id; - - $repository->dataset( "event_queue" )->create_dataobj({ - pluginid => "Event::DataCiteEvent", - action => "datacite_doi", - params => [$dataobj->internal_uri], - }); - }); - } - -}); - \ No newline at end of file diff --git a/cfg/cfg.d/z_datacitedoi.pl b/cfg/cfg.d/z_datacitedoi.pl index 4e719d9..00f7352 100644 --- a/cfg/cfg.d/z_datacitedoi.pl +++ b/cfg/cfg.d/z_datacitedoi.pl @@ -5,6 +5,9 @@ #which field do use for the doi $c->{datacitedoi}{eprintdoifield} = "id_number"; +#for xml:lang attributes in XML +$c->{datacitedoi}{defaultlangtag} = "en-GB"; + #When should you register/update doi info. $c->{datacitedoi}{eprintstatus} = {inbox=>0,buffer=>1,archive=>1,deletion=>0}; @@ -16,14 +19,19 @@ $c->{datacitedoi}{user} = "USER"; $c->{datacitedoi}{pass} = "PASS"; -# datacite requires a Publisher -# The name of the entity that holds, archives, publishes, -# prints, distributes, releases, issues, or produces the -# resource. This property will be used to formulate the +# datacite requires a Publisher +# The name of the entity that holds, archives, publishes, +# prints, distributes, releases, issues, or produces the +# resource. This property will be used to formulate the # citation, so consider the prominence of the role. -# eg World Data Center for Climate (WDCC); +# eg World Data Center for Climate (WDCC); $c->{datacitedoi}{publisher} = "Eprints Repo"; +# Namespace and location for datacite XML schema +# feel free to update, though no guarantees it'll be accepted if you do +$c->{datacitedoi}{xmlns} = "http://datacite.org/schema/kernel-4.0"; +$c->{datacitedoi}{schemaLocation} = $c->{datacitedoi}{xmlns}." http://schema.datacite.org/meta/kernel-4.0/metadata.xsd"; + # need to map eprint type (article, dataset etc) to ResourceType # Controled list http://schema.datacite.org/meta/kernel-2.2/doc/DataCite-MetadataKernel_v2.2.pdf # where v is the ResourceType and a is the resourceTypeGeneral @@ -42,4 +50,72 @@ $c->{datacitedoi}{typemap}{other} = {v=>'Misc',a=>'Collection'}; $c->{datacitedoi}{typemap}{dataset} = {v=>'Dataset',a=>'Dataset'}; $c->{datacitedoi}{typemap}{audio} = {v=>'Audio',a=>'Sound'}; -$c->{datacitedoi}{typemap}{video} = {v=>'Video',a=>'Film'}; \ No newline at end of file +$c->{datacitedoi}{typemap}{video} = {v=>'Video',a=>'Film'}; + +########################### +#### DOI syntax config #### +########################### + +# Set config of DOI delimiters +# Feel free to change, but they must conform to DOI syntax +# If not set will default to prefix/repoid/id the example below gives prefix/repoid.id +$c->{datacitedoi}{delimiters} = ["/","."]; + +# If set, plugin will attempt to register what is found in the EP DOI field ($c->{datacitedoi}{eprintdoifield}) +# Will only work if what is found adheres to DOI syntax rules (obvioulsy) +$c->{datacitedoi}{allow_custom_doi} = 0; + +#Datacite recommend digits of length 8-10 set this param to pad the id to required length +$c->{datacitedoi}{zero_padding} = 8; + +########################################## +### Override which URL gets registered ### +########################################## + +#Only useful for testing from "wrong" domain (eg an unregistered test server) should be undef for normal operation +$c->{datacitedoi}{override_url} = undef; + +########################## +##### When to coin ? ##### +########################## + +#If auto_coin is set DOIs will be minted on Status change (provided all else is well) +$c->{datacitedoi}{auto_coin} = 0; +#If action_coin is set then a button will be displayed under action tab (for staff) to mint DOIs on an adhoc basis +$c->{datacitedoi}{action_coin} = 1; + +# NB setting auto_coin renders action coin redundant as only published items can be registered + +####### Formerly in cfg.d/datacite_core.pl ######### + +# Including datacite_core.pl below as we can make some useful decisions based on the above config. + +## Adds the minting plugin to the EP_TRIGGER_STATUS_CHANGE +if($c->{datacitedoi}{auto_coin}){ + $c->add_dataset_trigger( "eprint", EP_TRIGGER_STATUS_CHANGE , sub { + my ( %params ) = @_; + + my $repository = $params{repository}; + + return undef if (!defined $repository); + + if (defined $params{dataobj}) { + my $dataobj = $params{dataobj}; + my $eprint_id = $dataobj->id; + $repository->dataset( "event_queue" )->create_dataobj({ + pluginid => "Event::DataCiteEvent", + action => "datacite_doi", + params => [$dataobj->internal_uri], + }); + } + + }); +} + +# Activate an action button, the plugin for whcih is at +# /plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm +if($c->{datacitedoi}{action_coin}){ + $c->{plugins}{"Screen::EPrint::Staff::CoinDOI"}{params}{disable} = 0; +} + + diff --git a/lib/cfg.d/z_datacite_mapping.pl b/lib/cfg.d/z_datacite_mapping.pl new file mode 100644 index 0000000..1496d81 --- /dev/null +++ b/lib/cfg.d/z_datacite_mapping.pl @@ -0,0 +1,267 @@ +##################################################### +# New architecture +# for print => datacite mapping#################################################### + +# These first two both map to resourceType(and resourceTypeGeneral) the first is +# for pubs repos the second +# for data(but either can be used +# for ether f the eprint field is there) +$c->{datacite_mapping_type} = sub { + + my($xml, $dataobj, $repo, $value) = @_; + + my $pub_resourceType = $repo->get_conf("datacitedoi", "typemap", $value); + if (defined $pub_resourceType) { + return $xml->create_data_element("resourceType", $pub_resourceType->{'v'}, resourceTypeGeneral=>$pub_resourceType->{'a'}); + } + + return undef; +}; + +$c->{datacite_mapping_data_type} = sub { + + my($xml, $dataobj, $repo, $value) = @_; + + return $xml->create_data_element("resourceType", $value, resourceTypeGeneral=>$value); +}; + +$c->{datacite_mapping_creators} = sub { + + my($xml, $dataobj, $repo, $value) = @_; + + my $creators = $xml->create_element("creators"); + + foreach my $name(@$value) { + my $author = $xml->create_element("creator"); + + my $name_str = EPrints::Utils::make_name_string($name->{name}); + + my $family = $name->{name}->{family}; + my $given = $name->{name}->{given}; + my $orcid = $name->{orcid}; + + if ($family eq '' && $given eq '') { + $creators->appendChild($author); + } else { + $author->appendChild($xml->create_data_element("creatorName", $name_str)); + } + if ($given eq '') { + $creators->appendChild($author); + } else { + $author->appendChild($xml->create_data_element("givenName", $given)); + } + if ($family eq '') { + $creators->appendChild($author); + } else { + $author->appendChild($xml->create_data_element("familyName", $family)); + } + if ($dataobj->exists_and_set("creators_orcid")) { + + if ($orcid eq '') { + $creators->appendChild($author); + } else { + $author->appendChild($xml->create_data_element("nameIdentifier", $orcid, schemeURI =>"http://orcid.org/", nameIdentifierScheme=>"ORCID")); + } + } + + $creators->appendChild($author); + } + return $creators +}; + + +$c->{datacite_mapping_title} = sub { + my($xml, $dataobj, $repo, $value) = @_; + + + + my $titles = $xml->create_element("titles"); + $titles->appendChild($xml->create_data_element("title", $dataobj->render_value("title"), "xml:lang"=>"en-us")); + + + + + + + return $titles# of somedescription +}; + + + + + + +$c->{datacite_mapping_abstract} = sub { + my($xml, $dataobj, $repo, $value) = @_; + + my $abstract = $dataobj->get_value("abstract"); + my $description = $xml->create_element("descriptions"); + + $description->appendChild($xml->create_data_element("description", $abstract, "xml:lang"=>"en-us", descriptionType=>"Abstract")); + + if ($dataobj->exists_and_set("collection_method")) { + my $collection = $dataobj->get_value("collection_method"); + $description->appendChild($xml->create_data_element("description", $collection, descriptionType =>"Methods")); + } + + if ($dataobj->exists_and_set("provenance")) { + my $processing = $dataobj->get_value("provenance"); + $description->appendChild($xml->create_data_element("description", $processing, descriptionType =>"Methods")); + } + + + + + return $description# of somedescription +}; + + + + + + +$c->{datacite_mapping_date} = sub { + my ( $xml, $dataobj, $repo, $value ) = @_; + $dataobj->get_value( "date" ) =~ /^([0-9]{4})/; + return $xml->create_data_element( "publicationYear", $1 ) if $1; + +}; + + +$c->{datacite_mapping_keywords} = sub { + my($xml, $dataobj, $repo, $value) = @_; + + if ($dataobj->exists_and_set("keywords")) { + my $subjects = $xml->create_element("subjects"); + my $keywords = $dataobj->get_value("keywords"); + if (ref($keywords) eq "ARRAY") { + foreach my $keyword(@$keywords) { + $subjects->appendChild($xml->create_data_element("subject", $keyword, "xml:lang"=>"en-us")); + } + return $subjects + + } else { + $subjects->appendChild($xml->create_data_element("subject", $keywords, "xml:lang"=>"en-us")); + return $subjects + } + } +}; + +$c->{datacite_mapping_geographic_cover} = sub { + my($xml, $dataobj, $repo, $value) = @_; + + my $geo_locations = $xml->create_element("geoLocations"); + my $geo_location = $xml->create_element("geoLocation"); + if ($dataobj->exists_and_set("geographic_cover")) { + + # + #Create XML elements + + # Get value of geographic_cover field and append to $geo_location XML element + my $geographic_cover = $dataobj->get_value("geographic_cover"); + $geo_location->appendChild($xml->create_data_element("geoLocationPlace", $geographic_cover)); + + # + # Get values of bounding box + my $west = $dataobj->get_value("bounding_box_west_edge"); + my $east = $dataobj->get_value("bounding_box_east_edge"); + my $south = $dataobj->get_value("bounding_box_south_edge"); + my $north = $dataobj->get_value("bounding_box_north_edge"); + + # + # Check to see + # if $north, $south, $east, or $west values are defined + if (defined $north && defined $south && defined $east && defined $west) {# + #Created $geo_location_box XML element + my $geo_location_box = $xml->create_element("geoLocationBox");# + #If $long / lat is defined, created XML element with the appropriate value + $geo_location_box->appendChild($xml->create_data_element("westBoundLongitude", $west)); + $geo_location_box->appendChild($xml->create_data_element("eastBoundLongitude", $east)); + $geo_location_box->appendChild($xml->create_data_element("southBoundLatitude", $south)); + $geo_location_box->appendChild($xml->create_data_element("northBoundLatitude", $north));# + #Append child $geo_location_box XML element to parent $geo_location XML element + $geo_location->appendChild($geo_location_box); + } + #Append child $geo_location XML element to parent $geo_locations XML element + $geo_locations->appendChild($geo_location); + #Append $geo_locations XML element to XML document# $entry - > appendChild($geo_locations); + } + + return $geo_locations; +}; + +$c->{datacite_mapping_funders} = sub { + my($xml, $dataobj, $repo, $value) = @_; + + + # + # if ($repo - >can_call("datacite_custom_funder")) {# + # return $repo - > call("datacite_custom_funder", $xml, $dataobj);# + # } + + my $funders = $dataobj->get_value("funders"); + my $grant = $dataobj->get_value("grant"); + my $projects = $dataobj->get_value("projects"); + if ($dataobj->exists_and_set("funders")) { + my $thefunders = $xml->create_element("funders"); + foreach my $funder(@$funders) { + foreach my $project(@$projects) { + $thefunders->appendChild($xml->create_data_element("funderName", $funder)); + $thefunders->appendChild($xml->create_data_element("awardNumber", $grant)); + } + } + return $thefunders; + } +}; + + + +$c->{datacite_mapping_rights} = sub { + my ( $xml, $dataobj, $repo, $value ) = @_; + my $author = $xml->create_element("rightsList"); + my $previous = {}; + foreach my $doc ( $dataobj->get_all_documents() ) { + + my $license = $doc->get_value("license"); + + if ( defined $license && $license ne '' ) { + unless ( defined $previous->{$license} ) { + if ( $license eq "attached" ) { + $author->appendChild($xml->create_data_element("rights", $repo->phrase("licenses_typename_attached"), rightsURI => $doc->get_url)); + } + else { + my $licenseuri = $repo->phrase("licenses_uri_$license"); + $author->appendChild($xml->create_data_element("rights", $license, rightsURI => $licenseuri)); + } + } + } + $previous->{$license} = "anything really"; + + print STDERR $previous->{$license}, "\n"; + } + + return $author; +}; + + + + +$c->{datacite_mapping_repo_link} = sub { + + my($xml, $entry, $dataobj) = @_; + + my $relatedIdentifiers = undef; + #default codein plugin (for reference) + # my $theurls = $dataobj->get_value( "repo_link" ); + # my $relatedIdentifiers = $xml->create_element( "relatedIdentifiers" ); + # foreach my $theurl ( @$theurls ) { + # my $linkk = $theurl->{link}; + # if (!$linkk eq ''){ + # $relatedIdentifiers->appendChild( $xml->create_data_element( "relatedIdentifier", $linkk, relatedIdentifierType=>"URL", relationType=>"IsReferencedBy" ) ); + # } + # } + + + return $relatedIdentifiers; + +}; diff --git a/lib/lang/en/phrases/coinDOI.xml b/lib/lang/en/phrases/coinDOI.xml new file mode 100644 index 0000000..f4c1175 --- /dev/null +++ b/lib/lang/en/phrases/coinDOI.xml @@ -0,0 +1,9 @@ + + + + + This will add a coin-DOI event to the event queue (i.e. Will not happen immediately) + Coin DOI + This item will have a DOI coined when the indexer gets around to it. + + diff --git a/lib/plugins/EPrints/Plugin/Event/DataCiteEvent.pm b/lib/plugins/EPrints/Plugin/Event/DataCiteEvent.pm index 270151c..1b988d9 100644 --- a/lib/plugins/EPrints/Plugin/Event/DataCiteEvent.pm +++ b/lib/plugins/EPrints/Plugin/Event/DataCiteEvent.pm @@ -5,54 +5,70 @@ EPrints::Plugin::Event::DataCiteEvent =cut package EPrints::Plugin::Event::DataCiteEvent; - + use EPrints::Plugin::Event; use LWP; use Crypt::SSLeay; +use HTTP::Headers::Util; @ISA = qw( EPrints::Plugin::Event ); - + sub datacite_doi { - my( $self, $eprint) = @_; + my( $self, $dataobj) = @_; my $repository = $self->repository(); - - my $thisdoi = $repository->get_conf( "datacitedoi", "prefix")."/". $repository->get_conf( "datacitedoi", "repoid")."/".$eprint->id; - - my $eprintdoifield = $repository->get_conf( "datacitedoi", "eprintdoifield"); - - my $task; - - my $shoulddoi = $repository->get_conf( "datacitedoi", "eprintstatus", $eprint->value( "eprint_status" )); - + + #Check object status first.... TODO Make work for dataobj == doument (just in case) + my $shoulddoi = $repository->get_conf( "datacitedoi", "eprintstatus", $dataobj->value( "eprint_status" )); #Check Doi Status - if(!$shoulddoi){ return; } - - #check if doi has been set; - if( $eprint->exists_and_set( $eprintdoifield )) { - if( $eprint->value( $eprintdoifield ) ne $thisdoi ){ - #Skipping because its has a diff doi; - return; - } - }else{ - $eprint->set_value($eprintdoifield, $thisdoi); - $eprint->commit(); + if(!$shoulddoi){ + $repository->log("Attempt to coin DOI for item that is not in the required area (see \$c->{datacitedoi}->{eprintstatus})"); + return EPrints::Const::HTTP_INTERNAL_SERVER_ERROR; } - - - my $xml = $eprint->export( "DataCiteXML" ); + + my $thisdoi = $self->coin_doi($repository,$dataobj); + #coin_doi may return an event error code if no prefix present assume this is the case + my $prefix = $repository->get_conf( "datacitedoi", "prefix"); + return $thisdoi if($thisdoi !~ /^$prefix/); + + #Pass doi into Export::DataCiteXML... + my $xml = $dataobj->export( "DataCiteXML", doi=>$thisdoi ); + return $xml if($xml =~ /^\d+$/); #just a number? coin_doi has passed back an error code pass it on... + + #print STDERR "XML: $xml\n"; my $url = $repository->get_conf( "datacitedoi", "apiurl"); + $url.="/" if($url !~ /\/$/); #attach slash if config has forgotten my $user_name = $repository->get_conf( "datacitedoi", "user"); my $user_pw = $repository->get_conf( "datacitedoi", "pass"); - + #register metadata; - $response_code = datacite_request("POST", $url."metadata", $user_name, $user_pw, $xml, "application/xml;charset=UTF-8"); - + my($response_content, $response_code) = datacite_request("POST", $url."metadata", $user_name, $user_pw, $xml, "application/xml;charset=UTF-8"); + if($response_code !~ /20(1|0)/){ + $repository->log("Metadata response from datacite api: $response_code: $response_content"); + $repository->log("BTW the \$doi was:\n$thisdoi"); + return EPrints::Const::HTTP_INTERNAL_SERVER_ERROR; + } #register doi - my $doi_reg = "doi=$thisdoi\nurl=".$eprint->uri(); - $response_code = datacite_request("POST", $url."doi", $user_name, $user_pw, $doi_reg, "text/plain;charset=UTF-8"); + my $repo_url =$dataobj->uri(); + #RM special override to allow testing from "wrong" domain + if(defined $repository->get_conf( "datacitedoi", "override_url")){ + $repo_url = $repository->get_conf( "datacitedoi", "override_url"); + $repo_url.= $dataobj->internal_uri; + } + my $doi_reg = "doi=$thisdoi\nurl=".$repo_url; + ($response_content, $response_code)= datacite_request("POST", $url."doi", $user_name, $user_pw, $doi_reg, "text/plain; charset=utf8"); + if($response_code !~ /20(1|0)/){ + $repository->log("Registration response from datacite api: $response_code: $response_content"); + $repository->log("BTW the \$doi_reg was:\n$doi_reg"); + return EPrints::Const::HTTP_INTERNAL_SERVER_ERROR; + } + #now it is safe to set DOI value. + my $eprintdoifield = $repository->get_conf( "datacitedoi", "eprintdoifield"); + $dataobj->set_value($eprintdoifield, $thisdoi); + $dataobj->commit(); + #success return undef; } @@ -65,9 +81,10 @@ sub datacite_request { 'Accept' => 'application/xml', 'Content-Type' => $content_type ); + my $req = HTTP::Request->new( $method => $url, - $headers, $content + $headers, Encode::encode_utf8( $content ) ); $req->authorization_basic($user_name, $user_pw); @@ -75,10 +92,50 @@ sub datacite_request { my $ua = LWP::UserAgent->new; my $res = $ua->request($req); - return $res->content(); + return ($res->content(),$res->code()); } +#RM lets do the DOI coining somewhere (reasonably) accessible +sub coin_doi { + + my( $self, $repository, $dataobj) = @_; + #RM zero padds eprintid as per config + my $z_pad = $repository->get_conf( "datacitedoi", "zero_padding") || 0; + my $id = sprintf("%0".$z_pad."d", $dataobj->id); + #Check for custom delimiters + my ($delim1, $delim2) = @{$repository->get_conf( "datacitedoi", "delimiters")}; + #default to slash + $delim1 = "/" if(!defined $delim1); + #second defaults to first + $delim2 = $delim1 if(!defined $delim2); + #construct the DOI string + my $prefix = $repository->get_conf( "datacitedoi", "prefix"); + my $thisdoi = $prefix.$delim1.$repository->get_conf( "datacitedoi", "repoid").$delim2.$id; + my $eprintdoifield = $repository->get_conf( "datacitedoi", "eprintdoifield"); -1; \ No newline at end of file + #Custom DOIS + #if DOI field is set attempt to use that if config allows + if($dataobj->exists_and_set( $eprintdoifield) ){ + + #if config does not allow ... bail + if( !$repository->get_conf( "datacitedoi", "allow_custom_doi" ) ){ + $repository->log("DOI is already set and custom overrides are disaallowed by config"); + return EPrints::Const::HTTP_INTERNAL_SERVER_ERROR; + } + #we are allowed (check prefix just in case) + $thisdoi = $dataobj->get_value( $eprintdoifield ); + # AH commented out because when there is an existing DOI (e.g. one issued by the publisher) + # the condition is always true and therefore, existing DOI becomes an empty string + # if($thisdoi !~ /^$prefix/){ + # $repository->log("Prefix does not match ($prefix) for custom DOI: $thisdoi"); + # $dataobj->set_value($eprintdoifield, ""); #unset the bad DOI!! + # $dataobj->commit(); + # return EPrints::Const::HTTP_INTERNAL_SERVER_ERROR; + # }#We'll leave Datacite to do any further syntax checking etc... + } + + return $thisdoi; +} +1; diff --git a/lib/plugins/EPrints/Plugin/Export/DataCiteXML.pm b/lib/plugins/EPrints/Plugin/Export/DataCiteXML.pm index 5d0ca0c..dbf5146 100644 --- a/lib/plugins/EPrints/Plugin/Export/DataCiteXML.pm +++ b/lib/plugins/EPrints/Plugin/Export/DataCiteXML.pm @@ -1,17 +1,15 @@ =head1 NAME - EPrints::Plugin::Export::DataCiteXML - =cut package EPrints::Plugin::Export::DataCiteXML; - use EPrints::Plugin::Export::Feed; @ISA = ('EPrints::Plugin::Export::Feed'); use strict; +use Data::Dumper; sub new { my ($class, %opts) = @_; @@ -23,89 +21,58 @@ sub new $self->{visible} = 'all'; $self->{suffix} = '.xml'; $self->{mimetype} = 'application/xml; charset=utf-8'; - + $self->{arguments}->{doi} = undef; + return $self; } sub output_dataobj { - my ($self, $dataobj, %opts) = @_; - - my $repo = $self->{repository}; - my $xml = $repo->xml; - + my ($self, $dataobj, %opts) = @_; + + my $repo = $self->{repository}; + my $xml = $repo->xml; + + #reference the datacite schema from config + our $entry = $xml->create_element( "resource", + xmlns=> $repo->get_conf( "datacitedoi", "xmlns"), + "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", + "xsi:schemaLocation" => $repo->get_conf( "datacitedoi", "schemaLocation")); + + #RM We pass in the DOI from Event::DataCite... or from --args on the cmd line + + # AH my $thisdoi = $opts{doi}; always returns undefined, even when DOI exists + # Ideally coining should NOT happen in this script but opts{doi} should have it + # but is always blank + my $thisdoi = $dataobj->get_value("id_number"); + #RM coin a DOI if either + # - not come via event or + # - no doi arg passed in via cmd_line + # ie when someone exports DataCiteXML from the Action tab + if(!defined $thisdoi){ + #nick the coining sub from event plugin + my $event = $repo->plugin("Event::DataCiteEvent"); + $thisdoi = $event->coin_doi($repo, $dataobj); + #coin_doi may return an event error code if no prefix present assume this is the case + my $prefix = $repo->get_conf( "datacitedoi", "prefix"); + return $thisdoi if($thisdoi !~ /^$prefix/); + } + $entry->appendChild( $xml->create_data_element( "identifier", $thisdoi , identifierType=>"DOI" ) ); + + foreach my $field ( $dataobj->{dataset}->get_fields ) + { + my $mapping_fn = "datacite_mapping_".$field->get_name; + if($repo->can_call($mapping_fn) && $dataobj->exists_and_set($field->get_name)){ + my $mapped_element = $repo->call( $mapping_fn, $xml, $dataobj, $repo, $dataobj->value($field->get_name) ); + $entry->appendChild( $mapped_element ) if(defined $mapped_element); + } + } - my $thisdoi = $repo->get_conf( "datacitedoi", "prefix")."/". $repo->get_conf( "datacitedoi", "repoid")."/".$dataobj->id; +####### From here on in you can redefine datacite_ampping_[fieldname] sub routines in lib/cfg.d/zzz_datacite_mapping.pl ####################### - my $entry = $xml->create_element( "resource", xmlns=>"http://datacite.org/schema/kernel-2.2", "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation"=>"http://datacite.org/schema/kernel-2.2 http://schema.datacite.org/meta/kernel-2.2/metadata.xsd" ); - - $entry->appendChild( $xml->create_data_element( "identifier", $dataobj->get_value( $repo->get_conf( "datacitedoi", "eprintdoifield") ) , identifierType=>"DOI" ) ); - - my $creators = $xml->create_element( "creators" ); - if( $dataobj->exists_and_set( "creators" ) ) - { - - my $names = $dataobj->get_value( "creators" ); - foreach my $name ( @$names ) - { - my $author = $xml->create_element( "creator" ); - - my $name_str = EPrints::Utils::make_name_string( $name->{name}); - $author->appendChild( $xml->create_data_element( - "creatorName", - $name_str ) ); - - #$author->appendChild( $xml->create_data_element( - # "email", - # $name->{id} ) ); - - $creators->appendChild( $author ); - } - } - $entry->appendChild( $creators ); - - if ($dataobj->exists_and_set( "title" )) { - my $titles = $xml->create_element( "titles" ); - $titles->appendChild( $xml->create_data_element( "title", $dataobj->render_value( "title" ) ) ); - $entry->appendChild( $titles ); - } - - $entry->appendChild( $xml->create_data_element( "publisher", $repo->get_conf( "datacitedoi", "repoid") ) ); - - if ($dataobj->exists_and_set( "datestamp" )) { - $dataobj->get_value( "datestamp" ) =~ /^([0-9]{4})/; - $entry->appendChild( $xml->create_data_element( "publicationYear", $1 ) ) if $1; - - } - - - if ($dataobj->exists_and_set( "subjects" )) { - my $subjects = $dataobj->get_value("subjects"); - if( EPrints::Utils::is_set( $subjects ) ){ - my $subjects_tag = $xml->create_element( "subjects" ); - foreach my $val (@$subjects){ - my $subject = EPrints::DataObj::Subject->new( $repo, $val ); - next unless defined $subject; - $subjects_tag->appendChild( $xml->create_data_element( "subject", $subject->render_description ) ); - - } - $entry->appendChild( $subjects_tag ); - } - } - - - my $thisresourceType = $repo->get_conf( "datacitedoi", "typemap", $dataobj->get_value("type") ); - if($thisresourceType!= undef ){ - $entry->appendChild( $xml->create_data_element( "resourceType", $thisresourceType->{'v'}, resourceTypeGeneral=>$thisresourceType->{'a'}) ); - } - - - my $alternateIdentifiers = $xml->create_element( "alternateIdentifiers" ); - $alternateIdentifiers->appendChild( $xml->create_data_element( "alternateIdentifier", $dataobj->get_url() , alternateIdentifierType=>"URL" ) ); - $entry->appendChild( $alternateIdentifiers ); - - return ''."\n".$xml->to_string($entry); + return ''."\n".$xml->to_string($entry); } -1; \ No newline at end of file + +1; diff --git a/lib/plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm b/lib/plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm new file mode 100644 index 0000000..9e13fbd --- /dev/null +++ b/lib/plugins/EPrints/Plugin/Screen/EPrint/Staff/CoinDOI.pm @@ -0,0 +1,97 @@ +package EPrints::Plugin::Screen::EPrint::Staff::CoinDOI; + +#use EPrints::Plugin::Screen::EPrint; + +@ISA = ( 'EPrints::Plugin::Screen::EPrint' ); + +use strict; + +sub new +{ + my( $class, %params ) = @_; + + my $self = $class->SUPER::new(%params); + + # $self->{priv} = # no specific priv - one per action + + $self->{actions} = [qw/ coindoi /]; + + $self->{appears} = [ { + place => "eprint_editor_actions", + action => "coindoi", + position => 1977, + }, ]; + + return $self; +} + +sub obtain_lock +{ + my( $self ) = @_; + + return $self->could_obtain_eprint_lock; +} + +sub about_to_render +{ + my( $self ) = @_; + + $self->EPrints::Plugin::Screen::EPrint::View::about_to_render; +} + +sub allow_coindoi +{ + my( $self ) = @_; + + return 0 unless $self->could_obtain_eprint_lock; + + my $repository = $self->{repository}; + #TODO a version that works for documents too + my $dataobj = $self->{processor}->{eprint}; + return 0 unless $repository->get_conf( "datacitedoi", "eprintstatus", $dataobj->value( "eprint_status" )); + #TODO don't allow the coinDOI button if a DOI is already registered (may require a db flag for successful reg) + return $self->allow( "eprint/edit:editor" ); +} + +sub action_coindoi +{ + my( $self ) = @_; + + my $repository = $self->{repository}; + + return undef if (!defined $repository); + + my $eprint = $self->{processor}->{eprint}; + + if (defined $eprint) { + my $eprint_id = $eprint->id; + + $repository->dataset( "event_queue" )->create_dataobj({ + pluginid => "Event::DataCiteEvent", + action => "datacite_doi", + params => [$eprint->internal_uri], + }); + + $self->add_result_message( 1 ); + } +} + +sub add_result_message +{ + my( $self, $ok ) = @_; + + if( $ok ) + { + $self->{processor}->add_message( "message", + $self->html_phrase( "coiningdoi" ) ); + } + else + { + # Error? + $self->{processor}->add_message( "error" ); + } + + $self->{processor}->{screenid} = "EPrint::View"; +} + +1;