- ######################################################################
- #
- # EPrints OAI 2.0 Handler
- #
- # Responds to incoming OAI requests
- #
- ######################################################################
- #
- # This file is part of GNU EPrints 2.
- #
- # Copyright (c) 2000-2004 University of Southampton, UK. SO17 1BJ.
- #
- # EPrints 2 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 2 of the License, or
- # (at your option) any later version.
- #
- # EPrints 2 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 EPrints 2; if not, write to the Free Software
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- #
- ######################################################################
-
- #cjg need to recomment all subs
-
- use EPrints;
-
- # use FileHandle;
- # use IO::File;
- # use POSIX;
- # use XML::Writer;
-
- use URI::Escape;
- # use Time::Local;
-
- use strict;
-
- # New session
- my $session = new EPrints::Session();
- exit( 0 ) unless( defined $session );
-
- # Allowed exports (OAI restricts the prefixes that can be used)
- our %FORMATS = %{$session->get_repository->get_conf( "oai", "v2", "output_plugins" )};
-
- # What are we begin asked?
- my $verb = $session->param( "verb" );
- #$session->get_database->set_debug(1);
- my $NS = "http://www.openarchives.org/OAI/2.0/";
- my $response = $session->make_element(
- "OAI-PMH",
- "xmlns"=>$NS,
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
- "xsi:schemaLocation"=>$NS." http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd" );
-
- $response->appendChild(
- $session->render_data_element(
- 2,
- "responseDate",
- EPrints::Time::get_iso_timestamp() ) );
-
-
- my @bits = ();
- my $request_desc = $session->make_element( "request" );
-
- foreach my $attr (
- "verb",
- "identifier",
- "metadataPrefix",
- "from",
- "until",
- "set",
- "resumptionToken" )
- {
- my $value = $session->param( $attr );
-
- # check it matches schema
- if( $attr eq "verb" )
- {
- next unless(
- $value eq "Identify" ||
- $value eq "GetRecord" ||
- $value eq "ListRecords" ||
- $value eq "ListIdentifiers" ||
- $value eq "ListMetadataFormats" ||
- $value eq "ListSets" );
- }
- if( $attr eq "identifier" )
- {
- next unless( $value =~ m/^[a-z]+:.*$/ );
- }
- if( $attr eq "metadataPrefix" )
- {
- next unless( $value =~ m/^[A-Za-z0-9attr!'$\(\)\+\-\.\*]+$/ );
- }
- if( $attr eq "from" || $attr eq "until")
- {
- next unless( $value =~ m/^\d\d\d\d-\d\d-\d\dT(\d\d:\d\d:\d\dZ)?$/ );
- }
- if( $attr eq "set" )
- {
- next unless( $value =~ m/^([A-Za-z0-9_!'$\(\)\+\-\.\*])+(:[A-Za-z0-9_!'$\(\)\+\-\.\*]+)*$/ );
- }
- # if( $attr eq "resumptionToken" ) { } # just any string
-
- # may be setting it to undef - but that won't matter as that
- # unsets it.
- next unless defined $value;
- $request_desc->setAttribute( $attr=>$value );
- }
- my $url = $session->get_repository->get_conf( "base_url" );
- $url .= $session->get_uri;
- $request_desc->appendChild( $session->make_text( $url ) );
-
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild( $request_desc );
-
- $response->appendChild(
- render_verb( $session, $session->param( "verb" ) ) );
-
- my $content = "text/xml";
- if( $session->param( "debug" ) eq "yes" )
- {
- $content = "text/plain";
- }
-
- $session->send_http_header( content_type=>$content );
-
- print <<END;
- <?xml version="1.0" encoding="UTF-8" ?>
- <?xml-stylesheet type='text/xsl' href='/oai2.xsl' ?>
-
- END
- print EPrints::XML::to_string( $response );
- EPrints::XML::dispose( $response );
- $session->terminate();
- exit;
-
- # OAI 2 Error conditions:
- #
- # badArgument
- # badResumptionToken
- # badVerb
- # caonnot DisseminateFormat
- # idDoesNotExist
- # noRecordsMatch
- # noSetHierachy
-
-
- sub render_verb
- {
- my( $session , $verb ) = @_;
-
- if( !defined $verb )
- {
- return render_oai_error( $session, "badVerb", "No verb was specified" );
- }
-
- if( $verb eq "Identify" )
- {
- return Identify( $session );
- }
-
- if( $verb eq "GetRecord" )
- {
- return GetRecord( $session );
- }
-
- if( $verb eq "ListRecords" )
- {
- return ListRecords( $session );
- }
-
- if( $verb eq "ListIdentifiers" )
- {
- return ListIdentifiers( $session );
- }
-
- if( $verb eq "ListMetadataFormats" )
- {
- return ListMetadataFormats( $session );
- }
-
- if( $verb eq "ListSets" )
- {
- return ListSets( $session );
- }
-
- return render_oai_error( $session, "badVerb", "Unknown verb: '$verb'" );
- }
-
- ######################################################################
- #
- # Identify( $session )
- #
- # Identify ourselves
- #
- ######################################################################
-
- sub Identify
- {
- my( $session ) = @_;
-
- my( $args, $errors ) = get_oai_args( $session, [], [] );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- my $response = $session->make_element( "Identify" );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "repositoryName",
- $session->phrase( "archive_name" ) ) );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "baseURL",
- $session->get_repository->get_conf( "oai","v2","base_url" ) ) );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "protocolVersion",
- "2.0" ) );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "adminEmail",
- $session->get_repository->get_conf( "adminemail" ) ) );
-
- # Later this may be either calcualted from the
- # database, or configurable.
- $response->appendChild( $session->render_data_element(
- 4,
- "earliestDatestamp",
- "0001-01-01" ) );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "deletedRecord",
- "persistent" ) );
-
- $response->appendChild( $session->render_data_element(
- 4,
- "granularity",
- "YYYY-MM-DD" ) );
-
- my $d1 = $session->make_element( "description" );
- my $NS = "http://www.openarchives.org/OAI/2.0/oai-identifier";
- my $XSD = "http://www.openarchives.org/OAI/2.0/oai-identifier.xsd";
- my $oaiid = $session->make_element(
- "oai-identifier",
- "xmlns"=>$NS,
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
- "xsi:schemaLocation"=>"$NS $XSD" );
-
- $d1->appendChild( $session->make_indent( 6 ) );
- $d1->appendChild( $oaiid );
- $response->appendChild( $session->make_indent( 4 ) );
- $response->appendChild( $d1 );
-
- $oaiid->appendChild( $session->render_data_element(
- 8,
- "scheme",
- "oai" ) );
-
- $oaiid->appendChild( $session->render_data_element(
- 8,
- "repositoryIdentifier",
- $session->get_repository->get_conf( "oai","v2","archive_id" ) ) );
-
- $oaiid->appendChild( $session->render_data_element(
- 8,
- "delimiter",
- ":" ) );
-
- $oaiid->appendChild( $session->render_data_element(
- 8,
- "sampleIdentifier",
- $session->get_repository->get_conf( "oai","v2","sample_identifier" ) ) );
-
- my $d2 = $session->make_element( "description" );
- my $eprints = $session->make_element(
- "eprints",
- "xmlns"=>"http://www.openarchives.org/OAI/1.1/eprints",
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
- "xsi:schemaLocation"=>"http://www.openarchives.org/OAI/1.1/eprints http://www.openarchives.org/OAI/1.1/eprints.xsd" );
- $d2->appendChild( $session->make_indent( 6 ) );
- $d2->appendChild( $eprints );
- $response->appendChild( $session->make_indent( 4 ) );
- $response->appendChild( $d2 );
-
- $eprints->appendChild( render_text_url(
- $session,
- "content",
- $session->get_repository->get_conf( "oai","content" ) ) );
-
- $eprints->appendChild( render_text_url(
- $session,
- "metadataPolicy",
- $session->get_repository->get_conf( "oai","metadata_policy" ) ) );
-
- $eprints->appendChild( render_text_url(
- $session,
- "dataPolicy",
- $session->get_repository->get_conf( "oai","data_policy" ) ) );
-
- $eprints->appendChild( render_text_url(
- $session,
- "submissionPolicy",
- $session->get_repository->get_conf( "oai","submission_policy" ) ) );
-
- foreach( @{$session->get_repository->get_conf( "oai","comments" )} )
- {
- $eprints->appendChild( $session->render_data_element(
- 8,
- "comment",
- $_ ) );
- }
-
- my $f = $session->make_doc_fragment();
- $f->appendChild( $session->make_indent( 2 ) );
- $f->appendChild( $response );
- return $f;
-
-
- }
-
- ######################################################################
- #
- # write_text_url( $writer, $name, $texturl )
- # hashref
- # Write a TextURL type block to writer, of name $name. Block will
- # contain a text and/or url element, defined in %texturl.
- # If texturl contains neither then this method returns without action.
- #
- ######################################################################
-
- sub render_text_url
- {
- my( $session, $name, $texturl ) = @_;
-
- my $f = $session->make_doc_fragment();
-
- $f->appendChild( $session->make_indent( 8 ) );
- my $e = $session->make_element( $name );
- $f->appendChild( $e );
-
- if ( defined $texturl->{"text"} )
- {
- $e->appendChild( $session->render_data_element(
- 10,
- "text",
- $texturl->{"text"} ) );
- }
-
- if ( defined $texturl->{"url"} )
- {
- $e->appendChild( $session->render_data_element(
- 10,
- "URL",
- $texturl->{"url"} ) );
- }
-
- return $f;
- }
-
-
- ######################################################################
- #
- # GetRecord( $session )
- #
- # Respond to a GetRecord verb: Retrieve a single metadata record
- #
- ######################################################################
-
- sub GetRecord
- {
- my( $session ) = @_;
-
- my( $args, $errors ) = get_oai_args( $session, [ "identifier", "metadataPrefix" ], [] );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- if( !defined $session->get_repository->get_conf( "oai", "v2", "metadata_namespaces" )->{$args->{metadataPrefix}} )
- {
- return render_oai_error(
- $session,
- "cannotDisseminateFormat",
- "Record not available as metadata type: ".$args->{metadataPrefix} );
-
- }
-
- my $id = EPrints::OpenArchives::from_oai_identifier(
- $session,
- $args->{identifier} );
-
- my $eprint = new EPrints::DataObj::EPrint(
- $session,
- $id,
- $session->get_repository->get_dataset( "archive" ) );
-
- if( !defined $eprint )
- {
- # try deleted records
- $eprint = new EPrints::DataObj::EPrint(
- $session,
- $id,
- $session->get_repository->get_dataset( "deletion" ) );
- }
-
- my $response = $session->make_element( "GetRecord" );
-
- # The eprint exists, so write the record
- # if the metadataFormat isn't available for
- # this record, only the header will be output.
-
- my $plugin_id = "Export::" . $FORMATS{ $args->{metadataPrefix} };
- my $plugin = $session->plugin( $plugin_id );
-
- unless( defined $plugin )
- {
- EPrints::abort( "Could not find plugin $plugin_id" );
- }
-
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild(
- EPrints::OpenArchives::make_record(
- $session,
- $eprint,
- $plugin,
- 1 ) );
-
- my $f = $session->make_doc_fragment();
- $f->appendChild( $session->make_indent( 2 ) );
- $f->appendChild( $response );
- return $f;
- }
-
-
-
-
- ######################################################################
- #
- # ListIdentifiers( $session )
- # ListRecords( $session )
- #
- # Respond to ListIdentifiers & ListRecords verbs.
- #
- ######################################################################
-
- sub ListIdentifiers
- {
- my( $session ) = @_;
-
- return _list( $session, 1 );
- }
-
- sub ListRecords
- {
- my( $session ) = @_;
-
- return _list( $session, 2 );
- }
-
- sub _list
- {
- my( $session , $mode ) = @_;
-
- #mode 1 = ID
- #mode 2 = full metadata
-
- my $PAGESIZE = 100;
-
- # different params depending if we have a resumptionToken
- # or not
-
- my( $searchexp, $offset, $metadata_format );
-
- if( defined $session->param( "resumptionToken" ) )
- {
- my( $args, $errors ) = get_oai_args( $session, [ "resumptionToken" ], [] );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- unless( $args->{"resumptionToken"} =~
- m#^(archive|deletion)/(\d+)/(\d+)(/(.*))?$# )
- {
- return render_oai_error(
- $session,
- "badResumptionToken",
- "Token is invalid (does not match regexp)" );
- }
- my $filters => $session->get_repository->get_conf( "oai", "filters" );
- $filters = [] unless defined $filters;
- my $cache_id;
- ( $offset, $cache_id, $metadata_format ) = ( $2, $3, $5 );
- $searchexp = EPrints::Search->new(
- dataset => $session->get_repository->get_dataset( "$1" ),
- session => $session,
- keep_cache => 1,
- filters => [
- @{$filters},
- ],
- cache_id => $cache_id );
-
- unless( defined $searchexp )
- {
- return render_oai_error(
- $session,
- "badResumptionToken",
- "Token has expired" );
- }
- }
- else
- {
- my $optf = [ "until", "from", "set"];
- my $reqf = [ "metadataPrefix" ];
-
- # We ignore metadataPrefix if doing ListIdentifiers
- # which is not quite the Right Thing, but saves much CPU.
-
- my( $args, $errors ) = get_oai_args( $session, $reqf, $optf );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- $metadata_format = $args->{metadataPrefix};
-
- if( !defined($FORMATS{ $metadata_format }) )
- {
- return render_oai_error(
- $session,
- "cannotDisseminateFormat",
- "Record not available as metadata type: ".$args->{metadataPrefix} );
- }
-
- my $date_range;
- my( $date, $date, $errors, $g1, $g2, $e );
- $errors = [];
- if( defined $args->{from} )
- {
- ( $date , $g1 , $e ) = munge_date( $session, $args->{from}, 0 );
- push @{$errors}, @{$e};
- $date_range = $date."-";
- }
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
- if( defined $args->{until} )
- {
- ( $date , $g2 , $e ) = munge_date( $session, $args->{until}, 1 );
- push @{$errors}, @{$e};
- $date_range.= "-" if( !defined $date_range );
- $date_range.= $date;
- }
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- if( defined $g1 && defined $g2 && $g1 ne $g2 )
- {
- return render_oai_error(
- $session,
- "badArgument",
- "from and until dates have different granularity ($g1 and $g2)" );
- }
-
- my $ds = $session->get_repository->get_dataset( "archive" );
- my $filters => $session->get_repository->get_conf( "oai", "filters" );
- $filters = [] unless defined $filters;
- $searchexp = new EPrints::Search(
- session => $session,
- keep_cache => 1,
- allow_blank => 1,
- filters => [
- @{$filters},
- ],
- dataset => $ds );
-
- if( defined $args->{set} )
- {
- my( $head , @tail ) = EPrints::OpenArchives::decode_setspec( $args->{set} );
- my( $key , $value ) = split( /=/ , $head );
- $value = pop @tail if( scalar @tail > 0 );
- my $views = $session->get_repository->get_conf( "oai","sets" ); #cjg
- my $info;
- foreach( @{$views} )
- {
- $info = $_ if( $_->{id} eq $key );
- }
- if( !defined $info )
- {
- return render_oai_error(
- $session,
- "badArgument",
- "Invalid set parameter; unknown key ( $key )" );
- }
- my @fields;
- my $match = "EX";
- foreach( split( "/", $info->{fields} ) )
- {
- my $field = EPrints::Utils::field_from_config_string( $ds, $_ );
- unless( $field->is_browsable() )
- {
- # Eeep. This is really bad. Just die now.
- my $type = $field->get_type();
- EPrints::abort( <<END );
- Cannot generate OAI set for field "$_"
- - Type "$type" cannot be browsed.
- END
- }
- push @fields, $field;
- if( $field->is_type( "subject" ) )
- {
- $match = "EQ";
- }
- }
- $searchexp->add_field( \@fields, $value, $match );
- }
-
- if( defined $date_range )
- {
- $searchexp->add_field(
- $ds->get_field( "datestamp" ),
- $date_range );
- }
-
- $offset = 0;
-
- if( !defined $searchexp )
- {
- # something went wrong
- return render_oai_error(
- $session,
- "badArgument",
- "Could not make Search (system error)." );
- }
- }
-
- $searchexp->perform_search();
- my $count = $searchexp->count();
-
- if( $count == 0 )
- {
- ## End of archive items, time to do deleted ones.
- my $delsearchexp = $searchexp->clone;
- $delsearchexp->set_dataset(
- $session->get_repository->get_dataset( "deletion" ) );
- $delsearchexp->perform_search();
- $searchexp->dispose();
- $searchexp = $delsearchexp;
-
- $count = $searchexp->count();
- }
-
- if( $count == 0 )
- {
- # no items at all
-
- return render_oai_error(
- $session,
- "noRecordsMatch",
- "No items match. None. None at all. Not even deleted ones." );
- }
-
- my $cache = $searchexp->get_cache_id();
- my $searchdsid = $searchexp->get_dataset()->id();
- my %opts = ();
- $opts{status}="deleted" if( $searchdsid eq "deletion" );
-
- my $response;
- my $mdtoken = "";
-
- my $response;
-
- if( $mode == 1 )
- {
- $response = $session->make_element( "ListIdentifiers" );
-
- my @records = $searchexp->get_records( $offset, $PAGESIZE );
- my $eprint;
- foreach $eprint ( @records )
- {
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild(
- EPrints::OpenArchives::make_header(
- $session,
- $eprint,
- 1 ) );
-
- }
- }
-
- if( $mode == 2 )
- {
- $response = $session->make_element( "ListRecords" );
-
- my $plugin_id = "Export::" . $FORMATS{ $metadata_format };
- my $plugin = $session->plugin( $plugin_id );
-
- unless( defined $plugin )
- {
- EPrints::abort( "Could not find plugin $plugin_id" );
- }
-
-
- my @records = $searchexp->get_records( $offset, $PAGESIZE );
- my $eprint;
- foreach $eprint ( @records )
- {
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild(
- EPrints::OpenArchives::make_record(
- $session,
- $eprint,
- $plugin,
- 1 ) );
- }
-
- $mdtoken = "/".$metadata_format;
- }
-
- $searchexp->dispose();
-
- my $tokenvalue = "";
- if( $count > $offset+$PAGESIZE )
- {
- $tokenvalue = $searchdsid."/".
- ($offset+$PAGESIZE)."/".
- $cache.$mdtoken;
- }
- elsif( $searchdsid eq "archive" )
- {
- ## End of archive items, time to do deleted ones.
- my $delsearchexp = $searchexp->clone;
- $delsearchexp->set_dataset(
- $session->get_repository->get_dataset( "deletion" ) );
- $delsearchexp->perform_search();
- my $cache = $delsearchexp->get_cache_id();
- my $count = $delsearchexp->count();
- $delsearchexp->dispose();
- # no point giving a token if we have no
- # deleted items.
- if( $count > 0 )
- {
- $tokenvalue = "deletion/0/$cache".$mdtoken;
- }
- }
-
- if( EPrints::Utils::is_set( $tokenvalue ) )
- {
- $response->appendChild( $session->render_data_element(
- 2,
- "resumptionToken",
- $tokenvalue ) );
- }
-
- my $f = $session->make_doc_fragment();
- $f->appendChild( $session->make_indent( 2 ) );
- $f->appendChild( $response );
- return $f;
- }
-
-
- ######################################################################
- #
- # ListMetadataFormats( $session )
- #
- ######################################################################
-
- sub ListMetadataFormats
- {
- my( $session ) = @_;
-
- my( $args, $errors ) = get_oai_args( $session, [], [ "identifier" ] );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- my $eprint;
- if( defined $args->{identifier} )
- {
- $eprint = new EPrints::DataObj::EPrint(
- $session,
- EPrints::OpenArchives::from_oai_identifier(
- $session,
- $args->{identifier} ),
- $session->get_repository->get_dataset( "archive" ) );
-
- }
-
- my $response = $session->make_element( "ListMetadataFormats" );
-
- foreach ( keys %FORMATS )
- {
- my $plugin_id = "Export::" . $FORMATS{ $_ };
- my $plugin = $session->plugin( $plugin_id ) or next;
- if( defined($eprint) )
- {
- my $md = $eprint->export( $FORMATS{ $_ } ) or next;
- EPrints::XML::dispose( $md );
- }
-
- my $mdf = $session->make_element( "metadataFormat" );
-
- $mdf->appendChild( $session->render_data_element(
- 4,
- "metadataPrefix",
- $_ ) );
-
- $mdf->appendChild( $session->render_data_element(
- 4,
- "schema",
- $plugin->{ 'schemaLocation' }
- ));
- $mdf->appendChild( $session->render_data_element(
- 4,
- "metadataNamespace",
- $plugin->{ 'xmlns' }
- ));
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild( $mdf );
- }
-
- my $f = $session->make_doc_fragment();
- $f->appendChild( $session->make_indent( 2 ) );
- $f->appendChild( $response );
- return $f;
- }
-
-
-
- ######################################################################
- #
- # ListSets( $session )
- #
- # Respond to a ListSets verb.
- #
- ######################################################################
-
- sub ListSets
- {
- my( $session ) = @_;
-
- my( $args, $errors ) = get_oai_args( $session, [], [ 'resumptionToken' ] );
-
- return join_errors( $session, $errors ) if( scalar @{$errors} > 0 );
-
- if( defined $args->{resumptionToken} )
- {
- return render_oai_error(
- $session,
- "badResumptionToken",
- "Resumption Tokens not supported for ListSets" );
- }
-
- my @sets = ();
- my %setnames = ();
-
- my $response = $session->make_element( "ListSets" );
-
- my $ds = $session->get_repository->get_dataset( "archive" );
- my $ds_del = $session->get_repository->get_dataset( "deletion" );
-
- my $viewconf = $session->get_repository->get_conf( "oai","sets" );
- my $info;
- foreach $info ( @{$viewconf} )
- {
- my $fieldname;
- my %v = ();
- foreach $fieldname ( split( "/" , $info->{fields} ) )
- {
- my $field = EPrints::Utils::field_from_config_string( $ds, $fieldname );
- if( $field->is_type( "subject" ) )
- {
- my $topsubj = EPrints::DataObj::Subject->new(
- $session,
- $field->get_property( "top" ) );
- my $i;
- foreach $i ( @{$topsubj->get_subjects( 0, 0, 1 )} )
- {
- my @kb = split( ":", $i->[0] );
- foreach( @kb )
- {
- $_ = EPrints::OpenArchives::encode_setspec( $_ );
- }
- my $key = join( ":", @kb );
- $v{$key} = $i->[1];
- }
- }
- else
- {
- my $v1 = $field->get_values( $session, $ds );
- my $delfield = $field->clone();
- #cjg why clone with new style datasets?
- #$delfield->set_dataset( $ds_del );
- my $v2 = $delfield->get_values( $session, $ds_del );
- foreach( @{$v1}, @{$v2} )
- {
- my $key = EPrints::OpenArchives::encode_setspec( $_ );
- if( !defined $key ) { $key=""; }
- $v{$key} = EPrints::Utils::tree_to_utf8( $field->get_value_label( $session, $_ ) );
- }
- }
- }
- unless( $info->{allow_null} ) { delete $v{""}; }
- foreach( keys %v )
- {
- my $set = $session->make_element( "set" );
- $response->appendChild( $session->make_indent( 2 ) );
- $response->appendChild( $set );
- my $spec = EPrints::OpenArchives::encode_setspec( $info->{id}."=" ).$_;
- $set->appendChild( $session->render_data_element(
- 4,
- "setSpec",
- $spec ) );
- my $name = $session->get_view_name( $ds, $info->{id} )." = ".$v{$_};
- $set->appendChild( $session->render_data_element(
- 4,
- "setName",
- $name ) );
- }
- }
-
- my $f = $session->make_doc_fragment();
- $f->appendChild( $session->make_indent( 2 ) );
- $f->appendChild( $response );
- return $f;
- }
-
-
- ######################################################################
- #
- # send_http_error( $session, $code, $message )
- #
- # Send an HTTP error as a response
- #
- ######################################################################
-
- sub send_http_error
- {
- my( $session, $code, $message ) = @_;
-
- my $r = Apache->request;
- $r->content_type( 'text/html' );
- $r->status_line( "$code $message" );
- $r->send_http_header;
- my $title = "Error $code in OAI request";
- $r->print( <<END );
- <html>
- <head><title>$title</title></head>
- <body>
- <h1>$title</h1>
- <p>$message</p>
- </body>
- END
- }
-
-
-
-
- sub render_oai_error
- {
- my( $session, $code, $message ) = @_;
-
- return $session->render_data_element(
- 2,
- "error",
- $message,
- code => $code );
- }
-
- sub get_oai_args
- {
- my( $session, $required, $optional ) = @_;
-
- my %a;
- foreach( @{$required}, @{$optional} ) { $a{$_}=1; }
- $a{verb} = 1;
-
- my %args;
- my @errors;
- foreach( $session->param() )
- {
- if( $a{$_} == 1 )
- {
- my @p = $session->param( $_ );
- $args{$_} = $p[0];
- delete $a{$_};
- if( scalar @p > 1 )
- {
- # Repeated Arg
- push @errors, render_oai_error(
- $session,
- "badArgument",
- "Repeated argument: $_" );
- }
- next;
- }
-
- push @errors, render_oai_error(
- $session,
- "badArgument",
- "Illegal argument: $_" );
- }
-
- foreach( @{$required} )
- {
- next unless( $a{ $_ } );
- push @errors, render_oai_error(
- $session,
- "badArgument",
- "Missing required argument: $_" );
- }
-
- if( defined $args{identifier} &&
- $args{identifier} !~ m/^oai:[a-zA-Z][a-zA-Z0-9\-]*(\.[a-zA-Z][a-zA-Z0-9\-]+)+:[a-zA-Z0-9\-_\.!~\*'\(\);\/\?:\@\&=\+\$,\%]+$/ )
- {
- push @errors, render_oai_error(
- $session,
- "badArgument",
- "identifier does not match regexp: $args{identifier}" );
- }
-
-
- return( \%args, \@errors );
- }
-
- sub join_errors
- {
- my( $session, $errors ) = @_;
-
- my $f = $session->make_doc_fragment;
- foreach( @{$errors} )
- {
- $f->appendChild( $_ );
- }
- return $f;
- }
-
- sub munge_date
- {
- my( $session, $string, $roundup ) = @_;
- if( $string !~ m/^(\d\d\d\d)-(\d\d)-(\d\d)(T(\d\d):(\d\d):(\d\d(\.\d+)?)Z)?$/ )
- {
- return( "", "", [render_oai_error(
- $session,
- "badArgument",
- "not valid datetime: $string" )] );
- }
- my( $year, $month, $day, $hour, $min, $sec ) = ( $1 , $2 , $3 , $5, $6 , $7 );
-
- if( defined $hour )
- {
- return( "", "", [render_oai_error(
- $session,
- "badArgument",
- "Max granularity is YYYY-MM-DD: $string" )] );
- }
-
- my $granularity = "YYYY-MM-DD";
-
- # $granularity = "YYYY-MM" unless( defined $day );
- # $granularity = "YYYY" unless( defined $month );
-
- # YYYY and YYYY-MM granularities disappeard between OAI2.0alpha3
- # and OAI2.0 but I'll keep the code around just in case.
-
- # if( !defined $month )
- # {
- # $month = ( $roundup ? 12 : 1 );
- # }
- # if( !defined $day )
- # {
- # $day = 1;
- # if( $roundup )
- # {
- # $month+=1;
- # if( $month == 13 ) { $month = 1; $year+=1; }
- # my $gt = timegm(0,0,0,$day,$month-1,$year);
- # # $gt is one month too far so we want one day less.
- # my @bits = gmtime( $gt - 24*60*60 );
- # $day = $bits[3];
- # $month = $bits[4]+1;
- # $year = $bits[5]+1900;
- # }
- # }
-
- my $date = sprintf( "%04d-%02d-%02d",$year,$month,$day );
-
- return( $date, $granularity, []);
- }