LP#1729620: (follow-up) move OAI methods to open-ils.supercat
authorGalen Charlton <gmc@equinoxOLI.org>
Fri, 24 Sep 2021 16:40:20 +0000 (12:40 -0400)
committerJane Sandberg <sandbergja@gmail.com>
Mon, 28 Mar 2022 02:57:21 +0000 (19:57 -0700)
A new service isn't really necessary for the handful
of methods that OAI support needs. Since SuperCat is
where other record harvesting and export methods live, what
was open-ils.oai is moved to SuperCat via this patch.

Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/examples/opensrf.xml.example
Open-ILS/examples/opensrf_core.xml.example
Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm [deleted file]
Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat/OAI.pm [new file with mode: 0644]
Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm
docs/RELEASE_NOTES_NEXT/OAI2/new_oai_opensrf_service.adoc

index 7074a0d..447dca3 100644 (file)
@@ -796,6 +796,101 @@ vim:et:ts=4:sw=4:
                     <min_spare_children>1</min_spare_children>
                     <max_spare_children>5</max_spare_children>
                 </unix_config>
+                <app_settings>
+                    <oai>
+                        <!-- Where necessary, override the default settings here in the app_settings element. -->
+
+                        <!-- The OAI endpoint. The domain is the name of your proxy or frontend opac website. -->
+                        <!-- <base_url>http://mydomain.org/opac/extras/oai</base_url> -->
+
+                        <!-- <repository_name>My organization(s)</repository_name> -->
+                        <!-- <admin_email>admin@mydomain.org</admin_email> -->
+
+                        <!-- The maximum number of records in a ListRecords and ListIdentifiers response. -->
+                        <!-- <max_count>50</max_count> -->
+
+                        <!-- <granularity>YYYY-MM-DDThh:mm:ss</granularity> -->
+                        <!-- <earliest_datestamp>0001-01-01</earliest_datestamp> -->
+                        <!-- <deleted_record>yes</deleted_record> -->
+                        <!-- <scheme>oai</scheme> -->
+                        <!-- <repository_identifier>mydomain.org</repository_identifier> -->
+                        <!-- <delimiter>:</delimiter> -->
+                        <!-- <sample_identifier>oai:mydomain.org:12345</sample_identifier> -->
+                        <!-- <list_sets>false</list_sets> -->
+
+                        <!--
+                        The metadataformat element contains the schema for the oai_dc and marcxml metadata formats.
+                        Each schema needs a reference to an xslt document.
+                        You can replace them with your custom xslt stylesheets.
+                        Place those in the /<openils sysdir>/var/xsl folder.
+                        You can also extend the OAI2 service further with new metadata schema.
+
+                        Bibliographic and authority records share the same stylesheet.
+                        Should you want to render them differently, use the
+                        marc:datafield[@tag='901']/marc:subfield[@code='t']
+                        value to identify the record type. -->
+
+                        <!--
+                        <metadataformat>
+                            <oai_dc>
+                                <namespace_uri>http://www.openarchives.org/OAI/2.0/oai_dc/</namespace_uri>
+                                <schema_location>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</schema_location>
+                                <xslt>OAI2_OAIDC.xsl</xslt>
+                            </oai_dc>
+                            <marcxml>
+                                <namespace_uri>http://www.loc.gov/MARC21/slim</namespace_uri>
+                                <schema_location>http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd</schema_location>
+                                <xslt>OAI2_MARC21slim.xsl</xslt>
+                            </marcxml>
+                        </metadataformat> -->
+
+                        <!--
+                        You can add different schema to the metadataformat element thus:
+                            <mods>
+                                <namespace_uri>http://www.loc.gov/mods/</namespace_uri>
+                                <schema_location>http://www.loc.gov/standards/mods/mods.xsd</schema_location>
+                                <xslt>my-custom-marc2mods.xsl</xslt>
+                            </mods>
+                            <my-metadata_prefix>
+                                <namespace_uri>my-namespace_uri</namespace_uri>
+                                <schema_location>my-schema_location</schema_location>
+                                <xslt>my-marc2my-metadata.xsl</xslt>
+                            </my-metadata_prefix>
+                        -->
+
+                        <!-- Change the way the asset copy values are mapped to which subfield codes: -->
+                        <!--
+                        <copies>
+                            <a>location</a>
+                            <b>owning_lib</b>
+                            <c>callnumber</c>
+                            <d>circlib</d>
+                            <g>barcode</g>
+                            <n>status</n>
+                        </copies>
+                        -->
+                        <!-- Or add static values to the copies element like this:
+                            <z>A value that always should for example be in the 852$z</z>
+                        -->
+
+                        <!-- Accept only 852$[barcode] values that match this regular expression. E.g.
+                        <barcode_filter>^[A-Za-z0-9]+</barcode_filter>
+                        only renders 852 datafields that contain barcodes values that begin with letters and numbers.
+                        -->
+                        <!--
+                        <barcode_filter><barcode_filter>
+                        -->
+
+                        <!-- Accept only 852$[status] values that match this regular expression. E.g.
+                        <status_filter>^Available$</status_filter>
+                        only renders 852 datafields that contain status code values that exactly match the string 'Available'.
+                        -->
+                        <!--
+                        <status_filter></status_filter>
+                        -->
+                    <oai>
+                </app_settings>
+
             </open-ils.supercat>
 
             <!-- server for accessing user info -->
@@ -991,118 +1086,6 @@ vim:et:ts=4:sw=4:
                 </app_settings>
             </open-ils.cstore>
 
-            <open-ils.oai>
-                <keepalive>5</keepalive>
-                <stateless>1</stateless>
-                <language>perl</language>
-                <implementation>OpenILS::Application::OAI</implementation>
-                <max_requests>199</max_requests>
-                <unix_config>
-                    <unix_sock>open-ils.oai_unix.sock</unix_sock>
-                    <unix_pid>open-ils.oai_unix.pid</unix_pid>
-                    <max_requests>1000</max_requests>
-                    <unix_log>open-ils.oai_unix.log</unix_log>
-                    <min_children>1</min_children>
-                    <max_children>5</max_children>
-                    <min_spare_children>1</min_spare_children>
-                    <max_spare_children>2</max_spare_children>
-                </unix_config>
-                <app_settings>
-
-                    <!-- Where necessary, override the default settings here in the app_settings element. -->
-
-                    <!-- The OAI endpoint. The domain is the name of your proxy or frontend opac website. -->
-                    <!-- <base_url>http://mydomain.org/opac/extras/oai</base_url> -->
-
-                    <!-- <repository_name>My organization(s)</repository_name> -->
-                    <!-- <admin_email>admin@mydomain.org</admin_email> -->
-
-                    <!-- The maximum number of records in a ListRecords and ListIdentifiers response. -->
-                    <!-- <max_count>50</max_count> -->
-
-                    <!-- <granularity>YYYY-MM-DDThh:mm:ss</granularity> -->
-                    <!-- <earliest_datestamp>0001-01-01</earliest_datestamp> -->
-                    <!-- <deleted_record>yes</deleted_record> -->
-                    <!-- <scheme>oai</scheme> -->
-                    <!-- <repository_identifier>mydomain.org</repository_identifier> -->
-                    <!-- <delimiter>:</delimiter> -->
-                    <!-- <sample_identifier>oai:mydomain.org:12345</sample_identifier> -->
-                    <!-- <list_sets>false</list_sets> -->
-
-                    <!--
-                    The metadataformat element contains the schema for the oai_dc and marcxml metadata formats.
-                    Each schema needs a reference to an xslt document.
-                    You can replace them with your custom xslt stylesheets.
-                    Place those in the /<openils sysdir>/var/xsl folder.
-                    You can also extend the OAI2 service further with new metadata schema.
-
-                    Bibliographic and authority records share the same stylesheet.
-                    Should you want to render them differently, use the
-                    marc:datafield[@tag='901']/marc:subfield[@code='t']
-                    value to identify the record type. -->
-
-                    <!--
-                    <metadataformat>
-                        <oai_dc>
-                            <namespace_uri>http://www.openarchives.org/OAI/2.0/oai_dc/</namespace_uri>
-                            <schema_location>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</schema_location>
-                            <xslt>OAI2_OAIDC.xsl</xslt>
-                        </oai_dc>
-                        <marcxml>
-                            <namespace_uri>http://www.loc.gov/MARC21/slim</namespace_uri>
-                            <schema_location>http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd</schema_location>
-                            <xslt>OAI2_MARC21slim.xsl</xslt>
-                        </marcxml>
-                    </metadataformat> -->
-
-                    <!--
-                    You can add different schema to the metadataformat element thus:
-                        <mods>
-                            <namespace_uri>http://www.loc.gov/mods/</namespace_uri>
-                            <schema_location>http://www.loc.gov/standards/mods/mods.xsd</schema_location>
-                            <xslt>my-custom-marc2mods.xsl</xslt>
-                        </mods>
-                        <my-metadata_prefix>
-                            <namespace_uri>my-namespace_uri</namespace_uri>
-                            <schema_location>my-schema_location</schema_location>
-                            <xslt>my-marc2my-metadata.xsl</xslt>
-                        </my-metadata_prefix>
-                    -->
-
-                    <!-- Change the way the asset copy values are mapped to which subfield codes: -->
-                    <!--
-                    <copies>
-                        <a>location</a>
-                        <b>owning_lib</b>
-                        <c>callnumber</c>
-                        <d>circlib</d>
-                        <g>barcode</g>
-                        <n>status</n>
-                    </copies>
-                    -->
-                    <!-- Or add static values to the copies element like this:
-                        <z>A value that always should for example be in the 852$z</z>
-                    -->
-
-                    <!-- Accept only 852$[barcode] values that match this regular expression. E.g.
-                    <barcode_filter>^[A-Za-z0-9]+</barcode_filter>
-                    only renders 852 datafields that contain barcodes values that begin with letters and numbers.
-                    -->
-                    <!--
-                    <barcode_filter><barcode_filter>
-                    -->
-
-                    <!-- Accept only 852$[status] values that match this regular expression. E.g.
-                    <status_filter>^Available$</status_filter>
-                    only renders 852 datafields that contain status code values that exactly match the string 'Available'.
-                    -->
-                    <!--
-                    <status_filter></status_filter>
-                    -->
-
-                </app_settings>
-            </open-ils.oai>
-
             <open-ils.pcrud>
                 <keepalive>6</keepalive>
                 <migratable>1</migratable>
@@ -1477,13 +1460,11 @@ vim:et:ts=4:sw=4:
                 <appname>open-ils.justintime</appname>  
                 <appname>open-ils.cstore</appname>  
                 <appname>open-ils.collections</appname>
-                <appname>open-ils.oai</appname>
                 <appname>open-ils.qstore</appname>
                 <appname>open-ils.reporter</appname>  
                 <appname>open-ils.reporter-store</appname>  
                 <!-- <appname>open-ils.resolver</appname> -->
                 <appname>open-ils.permacrud</appname>  
-                <appname>open-ils.oai</appname>  
                 <appname>open-ils.pcrud</appname>  
                 <appname>open-ils.trigger</appname>  
                 <appname>open-ils.url_verify</appname>
index 1a09e9e..aeba4ad 100644 (file)
@@ -30,7 +30,6 @@ Example OpenSRF bootstrap configuration file for Evergreen
           <service>open-ils.courses</service>
           <service>open-ils.curbside</service>
           <service>open-ils.fielder</service>
-          <service>open-ils.oai</service>
           <service>open-ils.pcrud</service>
           <service>open-ils.permacrud</service>
           <service>open-ils.reporter</service>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm
deleted file mode 100644 (file)
index 3605167..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-# OpenILS::WWW::OAI manages OAI2 requests and responses.
-#
-# Copyright (c) 2014-2017 International Institute of Social History
-#
-# This program 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 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-#
-# Author: Lucien van Wouw <lwo@iisg.nl>
-
-
-package OpenILS::Application::OAI;
-use strict; use warnings;
-
-use base qw/OpenILS::Application/;
-use OpenSRF::AppSession;
-use OpenSRF::EX qw(:try);
-use MARC::Record;
-use MARC::File::XML ( BinaryEncoding => 'UTF-8' );
-use OpenSRF::Utils::SettingsClient;
-use OpenSRF::Utils::Logger qw($logger);
-use XML::LibXML;
-use XML::LibXSLT;
-
-my (
-  $_parser,
-  $_xslt,
-  %record_xslt,
-  %metarecord_xslt,
-  %holdings_data_cache,
-  %authority_browse_axis_cache,
-  %copies,
-  $barcode_filter,
-  $status_filter
-);
-
-
-sub child_init {
-
-    # set the XML parser
-    $_parser = new XML::LibXML;
-
-    # and the xslt parser
-    $_xslt = new XML::LibXSLT;
-
-    # Load the metadataformats that are configured.
-    my $metadata_format = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.oai')->{'app_settings'}->{'metadataformat'};
-    if ( $metadata_format ) {
-        for my $schema ( keys %$metadata_format ) {
-            $logger->info('Loading schema ' . $schema) ;
-            $record_xslt{$schema}{namespace_uri}   = $metadata_format->{$schema}->{namespace_uri};
-            $record_xslt{$schema}{schema_location} = $metadata_format->{$schema}->{schema_location};
-            $record_xslt{$schema}{xslt}            = $_xslt->parse_stylesheet( $_parser->parse_file(
-                OpenSRF::Utils::SettingsClient->new->config_value( dirs => 'xsl' ) . '/' . $metadata_format->{$schema}->{xslt}
-            ) );
-        }
-    }
-
-    # Fall back on system defaults if oai_dc is not set in the configuration.
-    unless ( exists $record_xslt{oai_dc} ) {
-        $logger->info('Loading default oai_dc schema') ;
-        my $xslt = $_parser->parse_file(
-            OpenSRF::Utils::SettingsClient
-                ->new
-                ->config_value( dirs => 'xsl' ).
-            "/OAI2_OAIDC.xsl"
-        );
-        # and stash a transformer
-        $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $xslt );
-        $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
-        $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
-    }
-
-    # If not defined, use the natural marcxml metadata setting
-    unless ( exists $record_xslt{marcxml}) {
-        $logger->info('Loading default marcxml schema') ;
-        my $xslt = $_parser->parse_file(
-            OpenSRF::Utils::SettingsClient
-                ->new
-                ->config_value( dirs => 'xsl' ).
-            "/OAI2_MARC21slim.xsl"
-        );
-        # and stash a transformer
-        $record_xslt{marcxml}{xslt} = $_xslt->parse_stylesheet( $xslt );
-        $record_xslt{marcxml}{namespace_uri} = 'http://www.loc.gov/MARC21/slim';
-        $record_xslt{marcxml}{docs} = 'http://www.loc.gov/MARC21/slim';
-        $record_xslt{marcxml}{schema_location} = 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd';
-    }
-
-    # Load the mapping of 852 holdings.
-    my $copies = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.oai')->{'app_settings'}->{'copies'} ;
-    if ( $copies ) {
-        foreach my $subfield_code (keys %$copies) {
-            my $value = $copies->{$subfield_code};
-            $logger->info('Set 852 map ' . $subfield_code . '=' . $value );
-            $copies{$subfield_code} = $value;
-        }
-    } else { # if not defined, fall back on these defaults.
-        %copies = (
-            a => 'location',
-            b => 'owning_lib',
-            c => 'callnumber',
-            d => 'circlib',
-            g => 'barcode',
-            n => 'status'
-        );
-    }
-
-    # Set the barcode filter and status filter
-    $barcode_filter = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.oai')->{'app_settings'}->{'barcode_filter'};
-    $status_filter = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.oai')->{'app_settings'}->{'status_filter'};
-
-    return 1;
-}
-
-
-sub list_record_formats {
-
-    my @list;
-    for my $type ( keys %record_xslt ) {
-        push @list,
-            { $type =>
-                { namespace_uri   => $record_xslt{$type}{namespace_uri},
-                  docs        => $record_xslt{$type}{docs},
-                  schema_location => $record_xslt{$type}{schema_location},
-                }
-            };
-    }
-
-    return \@list;
-}
-
-__PACKAGE__->register_method(
-    method    => 'list_record_formats',
-    api_name  => 'open-ils.oai.record.formats',
-    api_level => 1,
-    argc      => 0,
-    signature =>
-    {
-        desc     => 'Returns the list of valid record formats that oai understands.',
-        'return' =>
-        {
-            desc => 'The format list.',
-            type => 'array'
-        }
-    }
-);
-
-
-sub oai_biblio_retrieve {
-
-    my $self = shift;
-    my $client = shift;
-    my $rec_id = shift;
-    my $metadataPrefix = shift;
-
-    #  holdings hold an array of call numbers, which hold an array of copies
-    #  holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
-    my %holdings;
-
-    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
-
-    # Retrieve the bibliographic record and it's copies
-    my $tree = $_storage->request(
-        "open-ils.cstore.direct.biblio.record_entry.retrieve",
-        $rec_id,
-        { flesh     => 5,
-          flesh_fields  => {
-                    bre => [qw/marc edit_date call_numbers/],
-                    acn => [qw/edit_date copies owning_lib prefix suffix/],
-                    acp => [qw/edit_date location status circ_lib parts/],
-                }
-        }
-    )->gather(1);
-
-    # Create a MARC::Record object with the marc.
-    my $marc = MARC::Record->new_from_xml( $tree->marc, 'UTF8', 'XML');
-
-    # Retrieve the MFHD where we can find them.
-    my %serials;
-    if ( substr($marc->leader, 7, 1) eq 's' ) { # serial
-        my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
-        my $_serials = $_search->request('open-ils.search.serial.record.bib.retrieve', $rec_id, 1, 0)->gather(1);
-        my $order = 0 ;
-        for my $sre (@$_serials) {
-            if ( $sre->location ) {
-                $order++ ;
-                my @svr = split( ' -- ', $sre->location );
-                my $cn_label = $svr[-1];
-                $serials{$order}{'label'} = $cn_label ;
-                my $display = @{$sre->basic_holdings_add} ? $sre->basic_holdings_add : $sre->basic_holdings;
-                $serials{$order}{'ser'} = join(', ', @{$display});
-            }
-        }
-    }
-
-    my $edit_date = $tree->edit_date ;
-
-    # Prepare a hash of all holdings and serials
-    for my $cn (@{$tree->call_numbers}) {
-
-        next unless ( $cn->deleted eq 'f' || !$cn->deleted );
-        my $_visible = 0;
-        for my $c (@{$cn->copies}) {
-            $_visible = _cp_is_visible($cn, $c);
-            last if ( $_visible );
-        }
-        next unless $_visible;
-
-        my $cn_label = $cn->label;
-        $holdings{$cn_label}{'owning_lib'} = $cn->owning_lib->shortname;
-
-        $edit_date =  most_recent_date( $cn->edit_date, $edit_date );
-
-        for my $cp (@{$cn->copies}) {
-
-            next unless _cp_is_visible($cn, $cp);
-            $edit_date = most_recent_date( $cp->edit_date, $edit_date );
-
-            # find the corresponding serial.
-            # There is no way of knowing here if the barcode 852$p is a correct match.
-            my $order = 0 ;
-            my $ser;
-            foreach my $key (sort keys %serials) {
-                my $serial = $serials{$key};
-                if ( $serial->{'label'} eq $cn_label ) {
-                    $ser = $serial->{'ser'};
-                    $order = $key;
-                    delete $serials{$key}; # in case we have several serial holdings with the same call number
-                    last;
-               }
-            }
-            $holdings{$cn_label}{'order'} = $order ;
-
-            my $circlib = $cp->circ_lib->shortname ;
-            push @{$holdings{$cn->label}{'copies'}}, {
-                owning_lib => $cn->owning_lib->shortname,
-                callnumber => $cn->label,
-                barcode    => $cp->barcode,
-                status     => $cp->status->name,
-                location   => $cp->location->name,
-                circlib    => $circlib,
-                ser        => $ser
-            };
-        }
-    }
-
-    ## Append the holdings and MFHD data to the marc record and apply the stylesheet.
-    if ( %holdings ) {
-
-        # Force record leader to 'a' as our data is always UTF8
-        # Avoids marc8_to_utf8 from being invoked with horrible results
-        # on the off-chance the record leader isn't correct
-        my $ldr = $marc->leader;
-        substr($ldr, 9, 1, 'a');
-        $marc->leader($ldr);
-
-        # Expects the record ID in the 001
-        $marc->delete_field($_) for ($marc->field('001'));
-        if (!$marc->field('001')) {
-            $marc->insert_fields_ordered(
-                MARC::Field->new( '001', $rec_id )
-            );
-        }
-
-        # Our reference node to prepend nodes to.
-        my $reference = $marc->field('901');
-
-        $marc->delete_field($_) for ($marc->field('852')); # remove any legacy 852s
-        foreach my $cn (sort { $holdings{$a}->{'order'} <=> $holdings{$b}->{'order'}} keys %holdings) {
-            foreach my $cp (@{$holdings{$cn}->{'copies'}}) {
-                my $marc_852 = MARC::Field->new(
-                   '852', '4', ' ', 0 => 'dummy'); # The dummy is necessary to prevent a validation error.
-                foreach my $subfield_code (sort keys %copies) {
-                    my $_cp = $copies{$subfield_code} ;
-                    $marc_852->add_subfields($subfield_code, $cp->{$_cp} || $_cp) if ($_cp);
-                }
-                $marc_852->delete_subfield(code => '0');
-                $marc->insert_fields_before($reference, $marc_852);
-                if ( $cp->{'ser'} ) {
-                    my $marc_866_a = MARC::Field->new( '866', '4', ' ', 'a' => $cp->{'ser'});
-                    $marc->insert_fields_after( $marc_852, $marc_866_a ) ;
-                }
-            }
-        }
-
-    }
-
-    my $xslt = $record_xslt{$metadataPrefix}{xslt} ;
-    my $xml = $xslt->transform( $_parser->parse_string( $marc->as_xml_record()) );
-    return $xslt->output_as_chars( $xml ) ;
-}
-
-
-__PACKAGE__->register_method(
-    method    => 'oai_biblio_retrieve',
-    api_name  => 'open-ils.oai.biblio.retrieve',
-    api_level => 1,
-    argc      => 1,
-    signature =>
-    {
-        desc     => 'Returns the MARCXML representation of the requested bibliographic record.',
-        params   =>
-        [
-            {
-                name => 'rec_id',
-                desc => 'An OpenILS biblio::record_entry id.',
-                type => 'number'
-            },
-            {
-                name => 'metadataPrefix',
-                desc => 'The metadataPrefix of the schema.',
-                type => 'string'
-            }
-        ],
-        'return' =>
-        {
-            desc => 'An string of the XML in the desired schema.',
-            type => 'string'
-        }
-    }
-);
-
-
-sub most_recent_date {
-
-    my $date1 = substr(shift, 0, 19) ;  # e.g. '2001-02-03T04:05:06+0000' becomes '2001-02-03T04:05:06'
-    my $date2 = substr(shift, 0, 19) ;
-    my $_date1 = $date1 ;
-    my $_date2 = $date2 ;
-
-    $date1 =~ s/[-T:\.\+]//g ; # '2001-02-03T04:05:06' becomes '20010203040506'
-    $date2 =~ s/[-T:\.\+]//g ;
-
-    return $_date1 if ( $date1 > $date2) ;
-    return $_date2 ;
-}
-
-
-sub _cp_is_visible {
-
-    my $cn = shift;
-    my $cp = shift;
-
-    my $visible = 0;
-    if ( ($cp->deleted eq 'f' || !$cp->deleted) &&
-         ( ! $barcode_filter || $cp->barcode =~ /$barcode_filter/ ) &&
-         $cp->location->opac_visible eq 't' &&
-         $cp->status->opac_visible eq 't' &&
-         $cp->opac_visible eq 't' &&
-         $cp->circ_lib->opac_visible eq 't' &&
-         $cn->owning_lib->opac_visible eq 't' &&
-         (! $status_filter || $cp->status->name =~ /$status_filter/ )
-    ) {
-        $visible = 1;
-    }
-
-    return $visible;
-}
-
-
-sub oai_authority_retrieve {
-
-    my $self = shift;
-    my $client = shift;
-    my $rec_id = shift;
-    my $metadataPrefix = shift;
-
-    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
-
-    # Retrieve the authority record
-    my $record = $_storage->request('open-ils.cstore.direct.authority.record_entry.retrieve', $rec_id)->gather(1);
-    my $o = Fieldmapper::authority::record_entry->new($record) ;
-    my $marc = MARC::Record->new_from_xml( $o->marc, 'UTF8', 'XML');
-
-    # Expects the record ID in the 001
-    $marc->delete_field($_) for ($marc->field('001'));
-    if (!$marc->field('001')) {
-        $marc->insert_fields_ordered(
-            MARC::Field->new( '001', $rec_id )
-        );
-    }
-
-    my $xslt = $record_xslt{$metadataPrefix}{xslt} ;
-    my $xml = $record_xslt{$metadataPrefix}{xslt}->transform(
-       $_parser->parse_string( $marc->as_xml_record())
-    );
-    return $record_xslt{$metadataPrefix}{xslt}->output_as_chars( $xml ) ;
-}
-
-
-__PACKAGE__->register_method(
-    method    => 'oai_authority_retrieve',
-    api_name  => 'open-ils.oai.authority.retrieve',
-    api_level => 1,
-    argc      => 1,
-    signature =>
-    {
-        desc     => 'Returns the MARCXML representation of the requested authority record.',
-        params   =>
-        [
-            {
-                name => 'rec_id',
-                desc => 'An OpenILS authority::record_entry id.',
-                type => 'number'
-            },
-            {
-                name => 'metadataPrefix',
-                desc => 'The metadataPrefix of the schema.',
-                type => 'string'
-            }
-        ],
-        'return' =>
-        {
-            desc => 'An string of the XML in the desired schema.',
-            type => 'string'
-        }
-    }
-);
-
-
-sub oai_list_retrieve {
-
-    my $self            = shift;
-    my $client          = shift;
-    my $record_class    = shift || 'biblio';
-    my $rec_id          = shift || 0;
-    my $from            = shift;
-    my $until           = shift;
-    my $set             = shift ;
-    my $max_count       = shift;
-    my $deleted_record  = shift || 'yes';
-
-    my $query = {};
-    $query->{'rec_id'}    = ($max_count eq 1) ? $rec_id : {'>=' => $rec_id} ;
-    $query->{'set_spec'}  = $set                     if ( $set ); # unsupported
-    $query->{'deleted'}   = 'f'                      unless ( $deleted_record eq 'yes' );
-    $query->{'datestamp'} = {'>=', $from}            if ( $from && !$until ) ;
-    $query->{'datestamp'} = {'<=', $until}           if ( !$from && $until ) ;
-    $query->{'-and'}      = [{'datestamp'=>{'>=' => $from}}, {'datestamp'=>{'<=' => $until}}] if ( $from && $until ) ;
-
-    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
-    return $_storage->request('open-ils.cstore.direct.oai.' . $record_class . '.search.atomic',
-            $query,
-            {
-                limit => $max_count + 1
-            }
-        )->gather(1);
-}
-
-__PACKAGE__->register_method(
-    method    => 'oai_list_retrieve',
-    api_name  => 'open-ils.oai.list.retrieve',
-    api_level => 1,
-    argc      => 1,
-    signature =>
-    {
-        desc => 'Returns a list of record identifiers.',
-        params =>
-        [
-            {
-                name => 'record_class',
-                desc => '\'biblio\' for bibliographic records or \'authority\' for authority records',
-                type => 'string'
-            },            {
-                name => 'rec_id',
-                desc => 'An optional rec_id number used as a cursor.',
-                type => 'number'
-            },
-            {
-                name => 'from',
-                desc => 'The datestamp the resultset range should begin with.',
-                type => 'string'
-            },
-            {
-                name => 'until',
-                desc => 'The datestamp the resultset range should end with.',
-                type => 'string'
-            },
-            {
-                name => 'set',
-                desc => 'A setspec.',
-                type => 'string'
-            },
-            {
-                name => 'max_count',
-                desc => 'The number of identifiers to return.',
-                type => 'number'
-            },
-            {
-                name => 'deleted_record',
-                desc => 'If set to \'no\' the response will only include active records.',
-                type => 'string'
-            }
-        ],
-        'return' =>
-        {
-            desc => 'An OAI type record.',
-            type => 'array'
-        }
-    }
-);
-
-
-1;
index edaa48c..8aac6ab 100644 (file)
@@ -6,6 +6,8 @@ use Unicode::Normalize;
 # ... and this has some handy common methods
 use OpenILS::Application::AppUtils;
 
+use OpenILS::Application::SuperCat::OAI;
+
 my $parser = new XML::LibXML;
 my $U = 'OpenILS::Application::AppUtils';
 
@@ -182,6 +184,8 @@ sub child_init {
 
     register_new_authorities_methods();
 
+    OpenILS::Application::SuperCat::OAI->child_init();
+
     return 1;
 }
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat/OAI.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat/OAI.pm
new file mode 100644 (file)
index 0000000..8c6f810
--- /dev/null
@@ -0,0 +1,515 @@
+# OpenILS::Application::SuperCat::OAI manages OAI2 requests and responses.
+#
+# Copyright (c) 2014-2017 International Institute of Social History
+#
+# This program 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Author: Lucien van Wouw <lwo@iisg.nl>
+
+
+package OpenILS::Application::SuperCat::OAI;
+use strict; use warnings;
+
+use base qw/OpenILS::Application/;
+use OpenSRF::AppSession;
+use OpenSRF::EX qw(:try);
+use MARC::Record;
+use MARC::File::XML ( BinaryEncoding => 'UTF-8' );
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Logger qw($logger);
+use XML::LibXML;
+use XML::LibXSLT;
+
+my (
+  $_parser,
+  $_xslt,
+  %record_xslt,
+  %metarecord_xslt,
+  %holdings_data_cache,
+  %authority_browse_axis_cache,
+  %copies,
+  $barcode_filter,
+  $status_filter
+);
+
+
+sub child_init {
+
+    # set the XML parser
+    $_parser = new XML::LibXML;
+
+    # and the xslt parser
+    $_xslt = new XML::LibXSLT;
+
+    # Load the metadataformats that are configured.
+    my $metadata_format = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.supercat')->{'app_settings'}->{'oai'}->{'metadataformat'};
+    if ( $metadata_format ) {
+        for my $schema ( keys %$metadata_format ) {
+            $logger->info('Loading schema ' . $schema) ;
+            $record_xslt{$schema}{namespace_uri}   = $metadata_format->{$schema}->{namespace_uri};
+            $record_xslt{$schema}{schema_location} = $metadata_format->{$schema}->{schema_location};
+            $record_xslt{$schema}{xslt}            = $_xslt->parse_stylesheet( $_parser->parse_file(
+                OpenSRF::Utils::SettingsClient->new->config_value( dirs => 'xsl' ) . '/' . $metadata_format->{$schema}->{xslt}
+            ) );
+        }
+    }
+
+    # Fall back on system defaults if oai_dc is not set in the configuration.
+    unless ( exists $record_xslt{oai_dc} ) {
+        $logger->info('Loading default oai_dc schema') ;
+        my $xslt = $_parser->parse_file(
+            OpenSRF::Utils::SettingsClient
+                ->new
+                ->config_value( dirs => 'xsl' ).
+            "/OAI2_OAIDC.xsl"
+        );
+        # and stash a transformer
+        $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $xslt );
+        $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
+        $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
+    }
+
+    # If not defined, use the natural marcxml metadata setting
+    unless ( exists $record_xslt{marcxml}) {
+        $logger->info('Loading default marcxml schema') ;
+        my $xslt = $_parser->parse_file(
+            OpenSRF::Utils::SettingsClient
+                ->new
+                ->config_value( dirs => 'xsl' ).
+            "/OAI2_MARC21slim.xsl"
+        );
+        # and stash a transformer
+        $record_xslt{marcxml}{xslt} = $_xslt->parse_stylesheet( $xslt );
+        $record_xslt{marcxml}{namespace_uri} = 'http://www.loc.gov/MARC21/slim';
+        $record_xslt{marcxml}{docs} = 'http://www.loc.gov/MARC21/slim';
+        $record_xslt{marcxml}{schema_location} = 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd';
+    }
+
+    # Load the mapping of 852 holdings.
+    my $copies = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.supercat')->{'app_settings'}->{'oai'}->{'copies'} ;
+    if ( $copies ) {
+        foreach my $subfield_code (keys %$copies) {
+            my $value = $copies->{$subfield_code};
+            $logger->info('Set 852 map ' . $subfield_code . '=' . $value );
+            $copies{$subfield_code} = $value;
+        }
+    } else { # if not defined, fall back on these defaults.
+        %copies = (
+            a => 'location',
+            b => 'owning_lib',
+            c => 'callnumber',
+            d => 'circlib',
+            g => 'barcode',
+            n => 'status'
+        );
+    }
+
+    # Set the barcode filter and status filter
+    $barcode_filter = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.supercat')->{'app_settings'}->{'oai'}->{'barcode_filter'};
+    $status_filter = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.supercat')->{'app_settings'}->{'oai'}->{'status_filter'};
+
+    return 1;
+}
+
+
+sub list_record_formats {
+
+    my @list;
+    for my $type ( keys %record_xslt ) {
+        push @list,
+            { $type =>
+                { namespace_uri   => $record_xslt{$type}{namespace_uri},
+                  docs        => $record_xslt{$type}{docs},
+                  schema_location => $record_xslt{$type}{schema_location},
+                }
+            };
+    }
+
+    return \@list;
+}
+
+__PACKAGE__->register_method(
+    method    => 'list_record_formats',
+    api_name  => 'open-ils.supercat.oai.record.formats',
+    api_level => 1,
+    argc      => 0,
+    signature =>
+    {
+        desc     => 'Returns the list of valid record formats that oai understands.',
+        'return' =>
+        {
+            desc => 'The format list.',
+            type => 'array'
+        }
+    }
+);
+
+
+sub oai_biblio_retrieve {
+
+    my $self = shift;
+    my $client = shift;
+    my $rec_id = shift;
+    my $metadataPrefix = shift;
+
+    #  holdings hold an array of call numbers, which hold an array of copies
+    #  holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
+    my %holdings;
+
+    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
+
+    # Retrieve the bibliographic record and it's copies
+    my $tree = $_storage->request(
+        "open-ils.cstore.direct.biblio.record_entry.retrieve",
+        $rec_id,
+        { flesh     => 5,
+          flesh_fields  => {
+                    bre => [qw/marc edit_date call_numbers/],
+                    acn => [qw/edit_date copies owning_lib prefix suffix/],
+                    acp => [qw/edit_date location status circ_lib parts/],
+                }
+        }
+    )->gather(1);
+
+    # Create a MARC::Record object with the marc.
+    my $marc = MARC::Record->new_from_xml( $tree->marc, 'UTF8', 'XML');
+
+    # Retrieve the MFHD where we can find them.
+    my %serials;
+    if ( substr($marc->leader, 7, 1) eq 's' ) { # serial
+        my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
+        my $_serials = $_search->request('open-ils.search.serial.record.bib.retrieve', $rec_id, 1, 0)->gather(1);
+        my $order = 0 ;
+        for my $sre (@$_serials) {
+            if ( $sre->location ) {
+                $order++ ;
+                my @svr = split( ' -- ', $sre->location );
+                my $cn_label = $svr[-1];
+                $serials{$order}{'label'} = $cn_label ;
+                my $display = @{$sre->basic_holdings_add} ? $sre->basic_holdings_add : $sre->basic_holdings;
+                $serials{$order}{'ser'} = join(', ', @{$display});
+            }
+        }
+    }
+
+    my $edit_date = $tree->edit_date ;
+
+    # Prepare a hash of all holdings and serials
+    for my $cn (@{$tree->call_numbers}) {
+
+        next unless ( $cn->deleted eq 'f' || !$cn->deleted );
+        my $_visible = 0;
+        for my $c (@{$cn->copies}) {
+            $_visible = _cp_is_visible($cn, $c);
+            last if ( $_visible );
+        }
+        next unless $_visible;
+
+        my $cn_label = $cn->label;
+        $holdings{$cn_label}{'owning_lib'} = $cn->owning_lib->shortname;
+
+        $edit_date =  most_recent_date( $cn->edit_date, $edit_date );
+
+        for my $cp (@{$cn->copies}) {
+
+            next unless _cp_is_visible($cn, $cp);
+            $edit_date = most_recent_date( $cp->edit_date, $edit_date );
+
+            # find the corresponding serial.
+            # There is no way of knowing here if the barcode 852$p is a correct match.
+            my $order = 0 ;
+            my $ser;
+            foreach my $key (sort keys %serials) {
+                my $serial = $serials{$key};
+                if ( $serial->{'label'} eq $cn_label ) {
+                    $ser = $serial->{'ser'};
+                    $order = $key;
+                    delete $serials{$key}; # in case we have several serial holdings with the same call number
+                    last;
+               }
+            }
+            $holdings{$cn_label}{'order'} = $order ;
+
+            my $circlib = $cp->circ_lib->shortname ;
+            push @{$holdings{$cn->label}{'copies'}}, {
+                owning_lib => $cn->owning_lib->shortname,
+                callnumber => $cn->label,
+                barcode    => $cp->barcode,
+                status     => $cp->status->name,
+                location   => $cp->location->name,
+                circlib    => $circlib,
+                ser        => $ser
+            };
+        }
+    }
+
+    ## Append the holdings and MFHD data to the marc record and apply the stylesheet.
+    if ( %holdings ) {
+
+        # Force record leader to 'a' as our data is always UTF8
+        # Avoids marc8_to_utf8 from being invoked with horrible results
+        # on the off-chance the record leader isn't correct
+        my $ldr = $marc->leader;
+        substr($ldr, 9, 1, 'a');
+        $marc->leader($ldr);
+
+        # Expects the record ID in the 001
+        $marc->delete_field($_) for ($marc->field('001'));
+        if (!$marc->field('001')) {
+            $marc->insert_fields_ordered(
+                MARC::Field->new( '001', $rec_id )
+            );
+        }
+
+        # Our reference node to prepend nodes to.
+        my $reference = $marc->field('901');
+
+        $marc->delete_field($_) for ($marc->field('852')); # remove any legacy 852s
+        foreach my $cn (sort { $holdings{$a}->{'order'} <=> $holdings{$b}->{'order'}} keys %holdings) {
+            foreach my $cp (@{$holdings{$cn}->{'copies'}}) {
+                my $marc_852 = MARC::Field->new(
+                   '852', '4', ' ', 0 => 'dummy'); # The dummy is necessary to prevent a validation error.
+                foreach my $subfield_code (sort keys %copies) {
+                    my $_cp = $copies{$subfield_code} ;
+                    $marc_852->add_subfields($subfield_code, $cp->{$_cp} || $_cp) if ($_cp);
+                }
+                $marc_852->delete_subfield(code => '0');
+                $marc->insert_fields_before($reference, $marc_852);
+                if ( $cp->{'ser'} ) {
+                    my $marc_866_a = MARC::Field->new( '866', '4', ' ', 'a' => $cp->{'ser'});
+                    $marc->insert_fields_after( $marc_852, $marc_866_a ) ;
+                }
+            }
+        }
+
+    }
+
+    my $xslt = $record_xslt{$metadataPrefix}{xslt} ;
+    my $xml = $xslt->transform( $_parser->parse_string( $marc->as_xml_record()) );
+    return $xslt->output_as_chars( $xml ) ;
+}
+
+
+__PACKAGE__->register_method(
+    method    => 'oai_biblio_retrieve',
+    api_name  => 'open-ils.supercat.oai.biblio.retrieve',
+    api_level => 1,
+    argc      => 1,
+    signature =>
+    {
+        desc     => 'Returns the MARCXML representation of the requested bibliographic record.',
+        params   =>
+        [
+            {
+                name => 'rec_id',
+                desc => 'An OpenILS biblio::record_entry id.',
+                type => 'number'
+            },
+            {
+                name => 'metadataPrefix',
+                desc => 'The metadataPrefix of the schema.',
+                type => 'string'
+            }
+        ],
+        'return' =>
+        {
+            desc => 'An string of the XML in the desired schema.',
+            type => 'string'
+        }
+    }
+);
+
+
+sub most_recent_date {
+
+    my $date1 = substr(shift, 0, 19) ;  # e.g. '2001-02-03T04:05:06+0000' becomes '2001-02-03T04:05:06'
+    my $date2 = substr(shift, 0, 19) ;
+    my $_date1 = $date1 ;
+    my $_date2 = $date2 ;
+
+    $date1 =~ s/[-T:\.\+]//g ; # '2001-02-03T04:05:06' becomes '20010203040506'
+    $date2 =~ s/[-T:\.\+]//g ;
+
+    return $_date1 if ( $date1 > $date2) ;
+    return $_date2 ;
+}
+
+
+sub _cp_is_visible {
+
+    my $cn = shift;
+    my $cp = shift;
+
+    my $visible = 0;
+    if ( ($cp->deleted eq 'f' || !$cp->deleted) &&
+         ( ! $barcode_filter || $cp->barcode =~ /$barcode_filter/ ) &&
+         $cp->location->opac_visible eq 't' &&
+         $cp->status->opac_visible eq 't' &&
+         $cp->opac_visible eq 't' &&
+         $cp->circ_lib->opac_visible eq 't' &&
+         $cn->owning_lib->opac_visible eq 't' &&
+         (! $status_filter || $cp->status->name =~ /$status_filter/ )
+    ) {
+        $visible = 1;
+    }
+
+    return $visible;
+}
+
+
+sub oai_authority_retrieve {
+
+    my $self = shift;
+    my $client = shift;
+    my $rec_id = shift;
+    my $metadataPrefix = shift;
+
+    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
+
+    # Retrieve the authority record
+    my $record = $_storage->request('open-ils.cstore.direct.authority.record_entry.retrieve', $rec_id)->gather(1);
+    my $o = Fieldmapper::authority::record_entry->new($record) ;
+    my $marc = MARC::Record->new_from_xml( $o->marc, 'UTF8', 'XML');
+
+    # Expects the record ID in the 001
+    $marc->delete_field($_) for ($marc->field('001'));
+    if (!$marc->field('001')) {
+        $marc->insert_fields_ordered(
+            MARC::Field->new( '001', $rec_id )
+        );
+    }
+
+    my $xslt = $record_xslt{$metadataPrefix}{xslt} ;
+    my $xml = $record_xslt{$metadataPrefix}{xslt}->transform(
+       $_parser->parse_string( $marc->as_xml_record())
+    );
+    return $record_xslt{$metadataPrefix}{xslt}->output_as_chars( $xml ) ;
+}
+
+
+__PACKAGE__->register_method(
+    method    => 'oai_authority_retrieve',
+    api_name  => 'open-ils.supercat.oai.authority.retrieve',
+    api_level => 1,
+    argc      => 1,
+    signature =>
+    {
+        desc     => 'Returns the MARCXML representation of the requested authority record.',
+        params   =>
+        [
+            {
+                name => 'rec_id',
+                desc => 'An OpenILS authority::record_entry id.',
+                type => 'number'
+            },
+            {
+                name => 'metadataPrefix',
+                desc => 'The metadataPrefix of the schema.',
+                type => 'string'
+            }
+        ],
+        'return' =>
+        {
+            desc => 'An string of the XML in the desired schema.',
+            type => 'string'
+        }
+    }
+);
+
+
+sub oai_list_retrieve {
+
+    my $self            = shift;
+    my $client          = shift;
+    my $record_class    = shift || 'biblio';
+    my $rec_id          = shift || 0;
+    my $from            = shift;
+    my $until           = shift;
+    my $set             = shift ;
+    my $max_count       = shift;
+    my $deleted_record  = shift || 'yes';
+
+    my $query = {};
+    $query->{'rec_id'}    = ($max_count eq 1) ? $rec_id : {'>=' => $rec_id} ;
+    $query->{'set_spec'}  = $set                     if ( $set ); # unsupported
+    $query->{'deleted'}   = 'f'                      unless ( $deleted_record eq 'yes' );
+    $query->{'datestamp'} = {'>=', $from}            if ( $from && !$until ) ;
+    $query->{'datestamp'} = {'<=', $until}           if ( !$from && $until ) ;
+    $query->{'-and'}      = [{'datestamp'=>{'>=' => $from}}, {'datestamp'=>{'<=' => $until}}] if ( $from && $until ) ;
+
+    my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
+    return $_storage->request('open-ils.cstore.direct.oai.' . $record_class . '.search.atomic',
+            $query,
+            {
+                limit => $max_count + 1
+            }
+        )->gather(1);
+}
+
+__PACKAGE__->register_method(
+    method    => 'oai_list_retrieve',
+    api_name  => 'open-ils.supercat.oai.list.retrieve',
+    api_level => 1,
+    argc      => 1,
+    signature =>
+    {
+        desc => 'Returns a list of record identifiers.',
+        params =>
+        [
+            {
+                name => 'record_class',
+                desc => '\'biblio\' for bibliographic records or \'authority\' for authority records',
+                type => 'string'
+            },            {
+                name => 'rec_id',
+                desc => 'An optional rec_id number used as a cursor.',
+                type => 'number'
+            },
+            {
+                name => 'from',
+                desc => 'The datestamp the resultset range should begin with.',
+                type => 'string'
+            },
+            {
+                name => 'until',
+                desc => 'The datestamp the resultset range should end with.',
+                type => 'string'
+            },
+            {
+                name => 'set',
+                desc => 'A setspec.',
+                type => 'string'
+            },
+            {
+                name => 'max_count',
+                desc => 'The number of identifiers to return.',
+                type => 'number'
+            },
+            {
+                name => 'deleted_record',
+                desc => 'If set to \'no\' the response will only include active records.',
+                type => 'string'
+            }
+        ],
+        'return' =>
+        {
+            desc => 'An OAI type record.',
+            type => 'array'
+        }
+    }
+);
+
+
+1;
index 0ca150c..13961b3 100644 (file)
@@ -71,11 +71,11 @@ sub child_init {
     my $idl = OpenSRF::Utils::SettingsClient->new->config_value('IDL');
     Fieldmapper->import(IDL => $idl);
 
-    $oai = OpenSRF::AppSession->create('open-ils.oai');
+    $oai = OpenSRF::AppSession->create('open-ils.supercat');
     $parser = new XML::LibXML;
     $xslt = new XML::LibXSLT;
 
-    my $app_settings = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.oai')->{'app_settings'};
+    my $app_settings = OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.supercat')->{'app_settings'}->{'oai'};
     $base_url = $app_settings->{'base_url'} || 'localhost';
     $base_url =~/(.*)\/$/ ; # Keep all minus the trailing forward slash.
     $repository_identifier = $app_settings->{'repository_identifier'} || 'localhost';
@@ -284,7 +284,7 @@ sub getRecord {
         my $rec_id = $1 ;
 
         # Do we have a record ?
-        my $record = $oai->request('open-ils.oai.list.retrieve', $record_class, $rec_id, undef, undef, undef, 1, $deleted_record)->gather(1) ;
+        my $record = $oai->request('open-ils.supercat.oai.list.retrieve', $record_class, $rec_id, undef, undef, undef, 1, $deleted_record)->gather(1) ;
         if (@$record) {
             $response = HTTP::OAI::GetRecord->new();
             my $o = "Fieldmapper::oai::$record_class"->new(@$record[0]);
@@ -311,7 +311,7 @@ sub listIdentifiers {
     my ($record_class, $requestURL, $from, $until, $set, $metadataPrefix, $offset ) = @_;
     my $response;
 
-    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
+    my $r = $oai->request('open-ils.supercat.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
     if (@$r) {
         my $cursor = 0 ;
         $response = HTTP::OAI::ListIdentifiers->new();
@@ -340,7 +340,7 @@ sub listRecords {
     my ($record_class, $requestURL, $from, $until, $set, $metadataPrefix, $offset ) = @_;
     my $response;
 
-    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
+    my $r = $oai->request('open-ils.supercat.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
     if (@$r) {
         my $cursor = 0 ;
         $response = HTTP::OAI::ListRecords->new();
@@ -395,7 +395,7 @@ sub _record {
 
     if ( $o->deleted eq 'f' ) {
         my $md = new HTTP::OAI::Metadata() ;
-        my $xml = $oai->request('open-ils.oai.' . $record_class . '.retrieve', $o->rec_id, $metadataPrefix)->gather(1) ;
+        my $xml = $oai->request('open-ils.supercat.oai.' . $record_class . '.retrieve', $o->rec_id, $metadataPrefix)->gather(1) ;
         $md->dom( $parser->parse_string('<metadata>' . $xml . '</metadata>') ); # Not sure why I need to add the metadata element,
         $record->metadata( $md );                                               # because I expect ->metadata() would provide the wrapper for it.
     }
@@ -464,7 +464,7 @@ sub _load_oaisets_biblio {
 # oai_metadataformats = { metadataPrefix => { schema, metadataNamespace } }
 sub _load_oai_metadataformats {
 
-    my $list = $oai->request('open-ils.oai.record.formats')->gather(1);
+    my $list = $oai->request('open-ils.supercat.oai.record.formats')->gather(1);
     for my $record_browse_format ( @$list ) {
         my %h = %$record_browse_format ;
         my $metadataPrefix = (keys %h)[0] ;
index c03ddcb..942afa5 100644 (file)
@@ -1,7 +1,8 @@
-New OAI Service
-^^^^^^^^^^^^^^^
+OAI-PMH Data Provider Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-This module is an optional service that exposes your catalog through the [OAI2 protocol](http://www.openarchives.org/OAI/openarchivesprotocol.html).
+Evergreen can now act as an OAI-PMH data provider, exposing the catalog to
+harvesting through the [OAI2 protocol](http://www.openarchives.org/OAI/openarchivesprotocol.html).
 
 
 Entry points
@@ -74,37 +75,12 @@ This mapping can be customized and extended with static subfields:
 Default configuration
 +++++++++++++++++++++
 
-See comments in opensrf.xml (in the open-ils.oai app_settings element)
-for default configuration and customization instructions. is commented
-in the open-ils.oai app_settings element.
+See comments in opensrf.xml (in the open-ils.supercat app_settings/oai element)
+for default configuration and customization instructions.
 
 Upgrade Instructions
 ++++++++++++++++++++
 
-**Activate the service**
-
-Refer to the service in the opensrf.xml activeapps element:
-```xml
-....
-<activeapps>
-    <appname>open-ils.oai</appname>
-```
-
-**Register the service with the router**
-
-Add the service to the public router in your opensrf_core.xml
-```xml
-<config>
-    <opensrf>
-        <routers>
-            <router>
-                <name>router</name>
-                <domain>public.realm</domain>
-                <services>
-                    <service>open-ils.oai</service>
-                    ...
-```
-
 Optional: Setting the datestamp
 +++++++++++++++++++++++++++++++