Giter Site home page Giter Site logo

oaipmh's Introduction

Latest Version Build Status Coverage Status Total Downloads

Description

Provides an wrapper to produce a OAI-PMH endpoint.

Repository

To use the OAI-PMH Library it is required to make your own implementation of the \Picturae\OaiPmh\Interfaces\Repository interface

Below is an example implementation needs to be modified to return your data

<?php

namespace My\Repository;

use DateTime;
use OpenSkos2\OaiPmh\Concept as OaiConcept;
use Picturae\OaiPmh\Exception\IdDoesNotExistException;
use Picturae\OaiPmh\Implementation\MetadataFormatType as ImplementationMetadataFormatType;
use Picturae\OaiPmh\Implementation\RecordList as OaiRecordList;
use Picturae\OaiPmh\Implementation\Repository\Identity as ImplementationIdentity;
use Picturae\OaiPmh\Implementation\Set;
use Picturae\OaiPmh\Implementation\SetList;
use Picturae\OaiPmh\Interfaces\MetadataFormatType;
use Picturae\OaiPmh\Interfaces\Record;
use Picturae\OaiPmh\Interfaces\RecordList;
use Picturae\OaiPmh\Interfaces\Repository as InterfaceRepository;
use Picturae\OaiPmh\Interfaces\Repository\Identity;
use Picturae\OaiPmh\Interfaces\SetList as InterfaceSetList;

class Repository implements InterfaceRepository
{
    /**
     * @return string the base URL of the repository
     */
    public function getBaseUrl()
    {
        return 'http://my-data-provider/oai-pmh';
    }

    /**
     * @return Identity
     */
    public function identify()
    {
        return new ImplementationIdentity();
    }

    /**
     * @return InterfaceSetList
     */
    public function listSets()
    {
        $items = [];
        $items[] = new Set('my:spec', 'Title of spec');        
        return new SetList($items);
    }

    /**
     * @param string $token
     * @return InterfaceSetList
     */
    public function listSetsByToken($token)
    {
        $params = $this->decodeResumptionToken($token);
        return $this->listSets();
    }

    /**
     * @param string $metadataFormat
     * @param string $identifier
     * @return Record
     */
    public function getRecord($metadataFormat, $identifier)
    {
        // Fetch record
        $record = $this->getSomeRecord($identifier);

        // Throw exception if it does not exists
        if (!record) {
            throw new IdDoesNotExistException('No matching identifier ' . $identifier, $exc->getCode(), $exc);
        }
        
        return new Record($record);
    }

    /**
     * @param string $metadataFormat metadata format of the records to be fetch or null if only headers are fetched
     * (listIdentifiers)
     * @param DateTime $from
     * @param DateTime $until
     * @param string $set name of the set containing this record
     * @return RecordList
     */
    public function listRecords($metadataFormat = null, DateTime $from = null, DateTime $until = null, $set = null)
    {        
        $items = [];
        $items[] = new OaiConcept($concept);

        // Show token only if more records exists then are shown
        $token = $this->encodeResumptionToken($this->limit, $from, $until, $metadataFormat, $set);
        
        return new OaiRecordList($items, $token);
    }

    /**
     * @param string $token
     * @return RecordList
     */
    public function listRecordsByToken($token)
    {
        $params = $this->decodeResumptionToken($token);

        $records = $this->GetRecords($params);

        // Only show if there are more records available else $token = null;
        $token = $this->encodeResumptionToken(
            $params['offset'] + 100,
            $params['from'],
            $params['until'],
            $params['metadataPrefix'],
            $params['set']
        );    

        return new OaiRecordList($items, $token);
    }

    /**
     * @param string $identifier
     * @return MetadataFormatType[]
     */
    public function listMetadataFormats($identifier = null)
    {
        $formats = [];
        $formats[] = new ImplementationMetadataFormatType(
            'oai_dc',
            'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
            'http://www.openarchives.org/OAI/2.0/oai_dc/'
        );

        $formats[] = new ImplementationMetadataFormatType(
            'oai_rdf',
            'http://www.openarchives.org/OAI/2.0/rdf.xsd',
            'http://www.w3.org/2004/02/skos/core#'
        );

        return $formats;
    }    

    /**
     * Decode resumption token
     * possible properties are:
     *
     * ->offset
     * ->metadataPrefix
     * ->set
     * ->from (timestamp)
     * ->until (timestamp)
     *
     * @param string $token
     * @return array
     */
    private function decodeResumptionToken($token)
    {
        $params = (array) json_decode(base64_decode($token));

        if (!empty($params['from'])) {
            $params['from'] = new \DateTime('@' . $params['from']);
        }

        if (!empty($params['until'])) {
            $params['until'] = new \DateTime('@' . $params['until']);
        }

        return $params;
    }

    /**
     * Get resumption token
     *
     * @param int $offset
     * @param DateTime $from
     * @param DateTime $util
     * @param string $metadataPrefix
     * @param string $set
     * @return string
     */
    private function encodeResumptionToken(
        $offset = 0,
        DateTime $from = null,
        DateTime $util = null,
        $metadataPrefix = null,
        $set = null
    ) {
        $params = [];
        $params['offset'] = $offset;
        $params['metadataPrefix'] = $metadataPrefix;
        $params['set'] = $set;
        $params['from'] = null;
        $params['until'] = null;

        if ($from) {
            $params['from'] = $from->getTimestamp();
        }

        if ($util) {
            $params['until'] = $util->getTimestamp();
        }

        return base64_encode(json_encode($params));
    }

    /**
     * Get earliest modified timestamp
     *
     * @return DateTime
     */
    private function getEarliestDateStamp()
    {
        // Fetch earliest timestamp
        return new DateTime();
    }
}

Sending a response

To create a request and send a response you will need something that can create a PSR 7 server request and emit a PSR 7 response

In this example we use zend-diactoros

composer require zendframework/zend-diactoros
// Where $repository is an instance of \Picturae\OaiPmh\Interfaces\Repository
$repository = new \Your\Implementation\Repository();
$provider = new \Picturae\OaiPmh\Provider($repository);

$request = Zend\Diactoros\ServerRequestFactory::fromGlobals();
$provider = new Picturae\OaiPmh\Provider($repository, $request);
$response = $provider->getResponse();
// Send PSR 7 Response
(new Zend\Diactoros\Response\SapiEmitter())->emit($response);

Validators

Values for setSpec must be validated yourself before adding them to the header

$header = new \Picturae\OaiPmh\Validator\SetSpecValidator();
$boolean = $header->isValid($value);

oaipmh's People

Contributors

burki avatar cdekok avatar db4y avatar dthornley avatar netsensei avatar olhsha avatar olivier1980 avatar picturae-build avatar pin0 avatar saidatom avatar vbuch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oaipmh's Issues

Is this project still active?

Hi, currently @kennisnet this lib is used in a legacy project.
For a new project I copied the code and made it PHP7.4 compatible and added support for different record formats. In our case json-ld

If this project is still active i could provide a full update for PHP7.4 and support for multiple formats, but it will be a big PR.

Best regards,
Andreas

Namespaces of records are inherited by the OAI elements

Problem

I'm implementing this library in a Symfony2 bundle as a test. I've noticed that namespaces in the output for getRecord and listRecords verbs aren't entirely correct.

  1. The namespaces "leak" from the encapsulated record to the OAI container elements
  2. The xmlns:schemaLocation declaration is removed / omitted from the original record. Only the xmlns:ns attributes remain.

This is a sample output of how it looks using the OAI-PMH library:

<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
  <responseDate>2016-12-22T14:37:39Z</responseDate>
  <request identifier="oai:lcoa1.loc.gov:loc.gmd/g3791p.rr002300" metadataPrefix="mods" verb="GetRecord">http://memory.loc.gov/cgi-bin/oai2_0</request>
  **<GetRecord xmlns:mods="http://www.loc.gov/mods/v3">**
    **<record xmlns:mods="http://www.loc.gov/mods/v3">**
      <header>
        <identifier>oai:lcoa1.loc.gov:loc.gmd/g3791p.rr002300</identifier>
        <datestamp>2004-08-13T15:32:50Z</datestamp>
      </header>
      **<metadata xmlns:mods="http://www.loc.gov/mods/v3">**
          **<mods xmlns="http://www.loc.gov/mods/v3" xmlns:xlink="http://www.w3.org/1999/xlink" >**
          <titleInfo>
            <title>
              New railroad map of the state of Maryland, Delaware, and the District of Columbia. Compiled and drawn by Frank Arnold Gray
            </title>
          </titleInfo>
....

Expected output should be:

<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:marc="http://www.loc.gov/MARC21/slim" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/          http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
   <responseDate>2016-12-22T14:38:22Z</responseDate>
   <request verb="GetRecord" identifier="oai:lcoa1.loc.gov:loc.gmd/g3791p.rr002300" metadataPrefix="mods">http://memory.loc.gov/cgi-bin/oai2_0</request>
   **<GetRecord>**
      **<record>**
         <header>
            <identifier>oai:lcoa1.loc.gov:loc.gmd/g3791p.rr002300</identifier>
            <datestamp>2004-08-13T15:32:50Z</datestamp>
            <setSpec>gmd</setSpec>
         </header>
         **<metadata>**
            **<mods xmlns="http://www.loc.gov/mods/v3" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-1.xsd">**
               <titleInfo>
                  <title>New railroad map of the state of Maryland, Delaware, and the District of Columbia. Compiled and drawn by Frank Arnold Gray</title>
               </titleInfo>
....

Also: see the output of the LoC OAI example record: https://memory.loc.gov/cgi-bin/oai2_0?verb=GetRecord&metadataPrefix=mods&identifier=oai:lcoa1.loc.gov:loc.gmd/g3791p.rr002300

Another example which is correct:
http://collections.britishart.yale.edu/oaicatmuseum/OAIHandler?verb=GetRecord&identifier=oai:tms.ycba.yale.edu:499&metadataPrefix=lido

Notice how the LIDO namespace declarations doesn't leak into the OAI namespace.

Cause

In oai-pmh\src\ResponseDocument.php you do this:

    public function createElement($name, $value = null)
    {
        $nameSpace = 'http://www.openarchives.org/OAI/2.0/';

        if ($value instanceof \DOMDocument) {
            $element = $this->document->createElementNS($nameSpace, $name, null);
            **$node = $this->document->importNode($value->documentElement, true);**
            $element->appendChild($node);
        } else {
            $element = $this->document->createElementNS($nameSpace, $name, htmlspecialchars($value, ENT_XML1));
        }
        return $element;
    }

ImportNode will perform a deep copy of the documentElement and import it into the Metadata element and from there appendChild will add it to the overall OAI DOMDocument. Somewhere around this point, the xmlns namespace declarations are pushed onto the parent nodes.

Fatal error: Class 'GuzzleHttp\Psr7\Response' not found

ResponseDocument.php uses class GuzzleHttp\Psr7\Response but you give example with zend diactoros in readme. It would be helpful if this dependency was mentioned (or removed).

Line 6 should be replaced with use Zend\Diactoros\Response\TextResponse;

Multiple description nodes Identify

When using the identify verb, I can add just one description node to the xml. According to the OAI PMH documentation, it should be possible to add more than one.

New release needed :)

Last release was in 2016, we could use a new release for some of the latest commits..

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.