Search:

# CWIS Developers Documentation

- Main Page
- Classes
- Files

- Search for |

- File List
- File Members

# OAIServer.php

Go to the documentation of this file.

    00001 <?PHP
    00002
    00003 #
    00004 #   FILE:  Scout--OAIServer.php
    00005 #
    00006 #   METHODS PROVIDED:
    00007 #       OAIServer()
    00008 #           - constructor
    00009 #
    00010 #   AUTHOR:  Edward Almasy
    00011 #
    00012 #   Copyright 2002-2004 Internet Scout Project
    00013 #   http://scout.wisc.edu
    00014 #
    00015
    00016 class OAIServer {
    00017
    00018     # ---- PUBLIC INTERFACE --------------------------------------------------
    00019
    00020     # object constructor
    00021     function OAIServer(&$DB, $RepDescr, &$ItemFactory, $SetsSupported = FALSE, $OaisqSupported = FALSE)
    00022     {
    00023         # save DB handle for our use
    00024         $this->DB =& $DB;
    00025
    00026         # save repository description
    00027         $this->RepDescr = $RepDescr;
    00028
    00029         # save supported option settings
    00030         $this->SetsSupported = $SetsSupported;
    00031         $this->OaisqSupported = $OaisqSupported;
    00032
    00033         # normalize repository description values
    00034         $this->RepDescr["IDPrefix"] =
    00035                     preg_replace("/[^0-9a-z]/i", "", $this->RepDescr["IDPrefix"]);
    00036
    00037         # save item factory
    00038         $this->ItemFactory =& $ItemFactory;
    00039
    00040         # load OAI request type and arguments
    00041         $this->LoadArguments();
    00042
    00043         # set default indent size
    00044         $this->IndentSize = 4;
    00045
    00046         # start with empty list of formats
    00047         $this->FormatDescrs = array();
    00048
    00049         # initialize description of mandatory format
    00050         $OaidcNamespaceList = array(
    00051             "oai_dc" => "http://www.openarchives.org/OAI/2.0/oai_dc/",
    00052             "dc" => "http://purl.org/dc/elements/1.1/",
    00053             );
    00054         $OaidcElements = array(
    00055             "dc:title",
    00056             "dc:creator",
    00057             "dc:subject",
    00058             "dc:description",
    00059             "dc:publisher",
    00060             "dc:contributor",
    00061             "dc:date",
    00062             "dc:type",
    00063             "dc:format",
    00064             "dc:identifier",
    00065             "dc:source",
    00066             "dc:language",
    00067             "dc:relation",
    00068             "dc:coverage",
    00069             "dc:rights",
    00070             );
    00071         $OaidcQualifiers = array();
    00072         $this->AddFormat("oai_dc", "oai_dc:dc",
    00073                          "http://www.openarchives.org/OAI/2.0/oai_dc/"
    00074                          ." http://www.openarchives.org/OAI/2.0/oai_dc.xsd",
    00075                          NULL,
    00076                          $OaidcNamespaceList, $OaidcElements, $OaidcQualifiers);
    00077     }
    00078
    00079     # add metadata format to export
    00080     function AddFormat($Name, $TagName, $SchemaLocation, $SchemaVersion, $NamespaceList, $ElementList, $QualifierList)
    00081     {
    00082         # find highest current format ID
    00083         $HighestFormatId = 0;
    00084         foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
    00085         {
    00086             if ($FormatDescr["FormatId"] > $HighestFormatId)
    00087             {
    00088                 $HighestFormatId = $FormatDescr["FormatId"];
    00089             }
    00090         }
    00091
    00092         # set new format ID to next value
    00093         $this->FormatDescrs[$Name]["FormatId"] = $HighestFormatId + 1;
    00094
    00095         # store values
    00096         $this->FormatDescrs[$Name]["TagName"] = $TagName;
    00097         $this->FormatDescrs[$Name]["SchemaLocation"] = $SchemaLocation;
    00098         $this->FormatDescrs[$Name]["SchemaVersion"] = $SchemaVersion;
    00099         $this->FormatDescrs[$Name]["ElementList"] = $ElementList;
    00100         $this->FormatDescrs[$Name]["QualifierList"] = $QualifierList;
    00101         $this->FormatDescrs[$Name]["NamespaceList"] = $NamespaceList;
    00102
    00103         # start out with empty mappings list
    00104         if (!isset($this->FieldMappings[$Name]))
    00105         {
    00106             $this->FieldMappings[$Name] = array();
    00107         }
    00108     }
    00109
    00110     # return list of formats
    00111     function FormatList()
    00112     {
    00113         $FList = array();
    00114         foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
    00115         {
    00116             $FList[$FormatDescr["FormatId"]] = $FormatName;
    00117         }
    00118         return $FList;
    00119     }
    00120
    00121     # return list of elements for a given format
    00122     function FormatElementList($FormatName)
    00123     {
    00124         return $this->FormatDescrs[$FormatName]["ElementList"];
    00125     }
    00126
    00127     # return list of qualifiers for a given format
    00128     function FormatQualifierList($FormatName)
    00129     {
    00130         return $this->FormatDescrs[$FormatName]["QualifierList"];
    00131     }
    00132
    00133     # get/set mapping of local field to OAI field
    00134     function GetFieldMapping($FormatName, $LocalFieldName)
    00135     {
    00136         # return stored value
    00137         if (isset($this->FieldMappings[$FormatName][$LocalFieldName]))
    00138         {
    00139             return $this->FieldMappings[$FormatName][$LocalFieldName];
    00140         }
    00141         else
    00142         {
    00143             return NULL;
    00144         }
    00145     }
    00146     function SetFieldMapping($FormatName, $LocalFieldName, $OAIFieldName)
    00147     {
    00148         $this->FieldMappings[$FormatName][$LocalFieldName] = $OAIFieldName;
    00149     }
    00150
    00151     # get/set mapping of local qualifier to OAI qualifier
    00152     function GetQualifierMapping($FormatName, $LocalQualifierName)
    00153     {
    00154         # return stored value
    00155         if (isset($this->QualifierMappings[$FormatName][$LocalQualifierName]))
    00156         {
    00157             return $this->QualifierMappings[$FormatName][$LocalQualifierName];
    00158         }
    00159         else
    00160         {
    00161             return NULL;
    00162         }
    00163     }
    00164     function SetQualifierMapping($FormatName, $LocalQualifierName, $OAIQualifierName)
    00165     {
    00166         $this->QualifierMappings[$FormatName][$LocalQualifierName] = $OAIQualifierName;
    00167     }
    00168
    00169     function GetResponse()
    00170     {
    00171         # call appropriate method based on request type
    00172         switch (strtoupper($this->Args["verb"]))
    00173         {
    00174             case "IDENTIFY":
    00175                 $Response = $this->ProcessIdentify();
    00176                 break;
    00177
    00178             case "GETRECORD":
    00179                 $Response = $this->ProcessGetRecord();
    00180                 break;
    00181
    00182             case "LISTIDENTIFIERS":
    00183                 $Response = $this->ProcessListRecords(FALSE);
    00184                 break;
    00185
    00186             case "LISTRECORDS":
    00187                 $Response = $this->ProcessListRecords(TRUE);
    00188                 break;
    00189
    00190             case "LISTMETADATAFORMATS":
    00191                 $Response = $this->ProcessListMetadataFormats();
    00192                 break;
    00193
    00194             case "LISTSETS":
    00195                 $Response = $this->ProcessListSets();
    00196                 break;
    00197
    00198             default:
    00199                 # return "bad argument" response
    00200                 $Response = $this->GetResponseBeginTags();
    00201                 $Response .= $this->GetRequestTag();
    00202                 $Response .= $this->GetErrorTag("badVerb", "Bad or unknown request type.");
    00203                 $Response .= $this->GetResponseEndTags();
    00204                 break;
    00205         }
    00206
    00207         # return generated response to caller
    00208         return $Response;
    00209     }
    00210
    00211
    00212     # ---- PRIVATE INTERFACE -------------------------------------------------
    00213
    00214     var $DB;
    00215     var $Args;
    00216     var $RepDescr;
    00217     var $ItemFactory;
    00218     var $FormatDescrs;
    00219     var $FormatFields;
    00220     var $FieldMappings;
    00221     var $QualifierMappings;
    00222     var $IndentSize;
    00223     var $SetsSupported;
    00224     var $OaisqSupported;
    00225
    00226
    00227     # ---- response generation methods
    00228
    00229     function ProcessIdentify()
    00230     {
    00231         # initialize response
    00232         $Response = $this->GetResponseBeginTags();
    00233
    00234         # add request info tag
    00235         $Response .= $this->GetRequestTag("Identify");
    00236
    00237         # open response type tag
    00238         $Response .= $this->FormatTag("Identify");
    00239
    00240         # add repository info tags
    00241         $Response .= $this->FormatTag("repositoryName", $this->RepDescr["Name"]);
    00242         $Response .= $this->FormatTag("baseURL", $this->RepDescr["BaseURL"]);
    00243         $Response .= $this->FormatTag("protocolVersion", "2.0");
    00244         foreach ($this->RepDescr["AdminEmail"] as $AdminEmail)
    00245         {
    00246             $Response .= $this->FormatTag("adminEmail", $AdminEmail);
    00247         }
    00248         $Response .= $this->FormatTag("earliestDatestamp", $this->RepDescr["EarliestDate"]);
    00249         $Response .= $this->FormatTag("deletedRecord", "no");
    00250         $Response .= $this->FormatTag("granularity",
    00251                                       (strtoupper($this->RepDescr["DateGranularity"]) == "DATETIME")
    00252                                           ? "YYYY-MM-DDThh:mm:ssZ" : "YYYY-MM-DD");
    00253
    00254         # add repository description section
    00255         $Response .= $this->FormatTag("description");
    00256         $Attribs = array(
    00257                 "xmlns" => "http://www.openarchives.org/OAI/2.0/oai-identifier",
    00258                 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
    00259                 "xsi:schemaLocation" => "http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd",
    00260                 );
    00261         $Response .= $this->FormatTag("oai-identifier", NULL, $Attribs);
    00262         $Response .= $this->FormatTag("scheme", "oai");
    00263         $Response .= $this->FormatTag("repositoryIdentifier", $this->RepDescr["IDDomain"]);
    00264         $Response .= $this->FormatTag("delimiter", ":");
    00265         $Response .= $this->FormatTag("sampleIdentifier", $this->EncodeIdentifier("12345"));
    00266         $Response .= $this->FormatTag();
    00267         $Response .= $this->FormatTag();
    00268
    00269         # close response type tag
    00270         $Response .= $this->FormatTag();
    00271
    00272         # close out response
    00273         $Response .= $this->GetResponseEndTags();
    00274
    00275         # return response to caller
    00276         return $Response;
    00277     }
    00278
    00279     function ProcessGetRecord()
    00280     {
    00281         # initialize response
    00282         $Response = $this->GetResponseBeginTags();
    00283
    00284         # if arguments were bad
    00285         if (isset($this->Args["identifier"]))
    00286         {
    00287             $ItemId = $this->DecodeIdentifier($this->Args["identifier"]);
    00288         }
    00289         else
    00290         {
    00291             $ItemId = NULL;
    00292         }
    00293         if (isset($this->Args["metadataPrefix"]))
    00294         {
    00295             $MetadataFormat = $this->Args["metadataPrefix"];
    00296         }
    00297         else
    00298         {
    00299             $MetadataFormat = NULL;
    00300         }
    00301         if (($ItemId == NULL) || ($MetadataFormat == NULL) || !is_array($this->FieldMappings[$MetadataFormat]))
    00302         {
    00303             # add request info tag with no attributes
    00304             $Response .= $this->GetRequestTag("GetRecord");
    00305
    00306             # add error tag
    00307             $Response .= $this->GetErrorTag("badArgument", "Bad argument found.");
    00308         }
    00309         else
    00310         {
    00311             # add request info tag
    00312             $ReqArgList = array("identifier", "metadataPrefix");
    00313             $Response .= $this->GetRequestTag("GetRecord", $ReqArgList);
    00314
    00315             # attempt to load item corresponding to record
    00316             $Item = $this->ItemFactory->GetItem($ItemId);
    00317
    00318             # if no item found
    00319             if ($Item == NULL)
    00320             {
    00321                 # add error tag
    00322                 $Response .= $this->GetErrorTag("idDoesNotExist", "No item found for specified ID.");
    00323             }
    00324             else
    00325             {
    00326                 # open response type tag
    00327                 $Response .= $this->FormatTag("GetRecord");
    00328
    00329                 # add tags for record
    00330                 $Response .= $this->GetRecordTags($Item, $MetadataFormat);
    00331
    00332                 # close response type tag
    00333                 $Response .= $this->FormatTag();
    00334             }
    00335         }
    00336
    00337         # close out response
    00338         $Response .= $this->GetResponseEndTags();
    00339
    00340         # return response to caller
    00341         return $Response;
    00342     }
    00343
    00344     function ProcessListRecords($IncludeMetadata)
    00345     {
    00346         # set request type
    00347         if ($IncludeMetadata)
    00348         {
    00349             $Request = "ListRecords";
    00350         }
    00351         else
    00352         {
    00353             $Request = "ListIdentifiers";
    00354         }
    00355
    00356         # initialize response
    00357         $Response = $this->GetResponseBeginTags();
    00358
    00359         # if resumption token supplied
    00360         if (isset($this->Args["resumptionToken"]))
    00361         {
    00362             # set expected argument lists
    00363             $ReqArgList = array("resumptionToken");
    00364             $OptArgList = NULL;
    00365
    00366             # parse into list parameters
    00367             $Args = $this->DecodeResumptionToken($this->Args["resumptionToken"]);
    00368         }
    00369         else
    00370         {
    00371             # set expected argument lists
    00372             $ReqArgList = array("metadataPrefix");
    00373             $OptArgList = array("from", "until", "set");
    00374
    00375             # get list parameters from incoming arguments
    00376             $Args = $this->Args;
    00377
    00378             # set list starting point to beginning
    00379             $Args["ListStartPoint"] = 0;
    00380         }
    00381
    00382         # if resumption token was supplied and was bad
    00383         if ($Args == NULL)
    00384         {
    00385             # add request info tag
    00386             $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
    00387
    00388             # add error tag indicating bad resumption token
    00389             $Response .= $this->GetErrorTag("badResumptionToken", "Bad resumption token.");
    00390
    00391             # if other parameter also supplied
    00392             if (count($this->Args) > 2)
    00393             {
    00394                 # add error tag indicating exclusive argument error
    00395                 $Response .= $this->GetErrorTag("badArgument", "Resumption token is exclusive argument.");
    00396             }
    00397         }
    00398         # else if resumption token supplied and other arguments also supplied
    00399         elseif (isset($this->Args["resumptionToken"]) && (count($this->Args) > 2))
    00400         {
    00401             # add error tag indicating exclusive argument error
    00402             $Response .= $this->GetRequestTag();
    00403             $Response .= $this->GetErrorTag("badArgument", "Resumption token is exclusive argument.");
    00404         }
    00405         # else if metadata format was not specified
    00406         elseif (empty($Args["metadataPrefix"]))
    00407         {
    00408             # add request info tag with no attributes
    00409             $Response .= $this->GetRequestTag($Request);
    00410
    00411             # add error tag indicating bad argument
    00412             $Response .= $this->GetErrorTag("badArgument", "No metadata format specified.");
    00413         }
    00414         # else if from or until date is specified but bad
    00415         elseif ((isset($Args["from"]) && $this->DateIsInvalid($Args["from"]))
    00416                 || (isset($Args["until"]) && $this->DateIsInvalid($Args["until"])))
    00417         {
    00418             # add request info tag with no attributes
    00419             $Response .= $this->GetRequestTag($Request);
    00420
    00421             # add error tag indicating bad argument
    00422             $Response .= $this->GetErrorTag("badArgument", "Bad date format.");
    00423         }
    00424         else
    00425         {
    00426             # add request info tag
    00427             $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
    00428
    00429             # if set requested and we do not support sets
    00430             if (isset($Args["set"]) && ($this->SetsSupported != TRUE))
    00431             {
    00432                 # add error tag indicating that we don't support sets
    00433                 $Response .= $this->GetErrorTag("noSetHierarchy", "This repository does not support sets.");
    00434             }
    00435             # else if requested metadata format is not supported
    00436             elseif (empty($this->FormatDescrs[$Args["metadataPrefix"]]))
    00437             {
    00438                 # add error tag indicating that format is not supported
    00439                 $Response .= $this->GetErrorTag("cannotDisseminateFormat", "Metadata format \"".$Args["metadataPrefix"]."\" not supported by this repository.");
    00440             }
    00441             else
    00442             {
    00443                 # if set requested
    00444                 if (isset($Args["set"]))
    00445                 {
    00446                     # if OAI-SQ supported and set represents OAI-SQ query
    00447                     if ($this->OaisqSupported && $this->IsOaisqQuery($Args["set"]))
    00448                     {
    00449                         # parse OAI-SQ search parameters out of set name
    00450                         $SearchParams = $this->ParseOaisqQuery($Args["set"], $Args["metadataPrefix"]);
    00451
    00452                         # if search parameters found
    00453                         if (count($SearchParams))
    00454                         {
    00455                             # perform search for items that match OAI-SQ request
    00456                             $ItemIds = $this->ItemFactory->SearchForItems(
    00457                                 $SearchParams,
    00458                                 (isset($Args["from"]) ? $Args["from"] : NULL),
    00459                                 (isset($Args["until"]) ? $Args["until"] : NULL));
    00460                         }
    00461                         else
    00462                         {
    00463                             # no items match
    00464                             $ItemIds = array();
    00465                         }
    00466                     }
    00467                     else
    00468                     {
    00469                         # get list of items in set that matches incoming criteria
    00470                         $ItemIds = $this->ItemFactory->GetItemsInSet(
    00471                             $Args["set"],
    00472                             (isset($Args["from"]) ? $Args["from"] : NULL),
    00473                             (isset($Args["until"]) ? $Args["until"] : NULL));
    00474                     }
    00475                 }
    00476                 else
    00477                 {
    00478                     # get list of items that matches incoming criteria
    00479                     $ItemIds = $this->ItemFactory->GetItems(
    00480                         (isset($Args["from"]) ? $Args["from"] : NULL),
    00481                         (isset($Args["until"]) ? $Args["until"] : NULL));
    00482                 }
    00483
    00484                 # if no items found
    00485                 if (count($ItemIds) == 0)
    00486                 {
    00487                     # add error tag indicating that no records found that match spec
    00488                     $Response .= $this->GetErrorTag("noRecordsMatch", "No records were found that match the specified parameters.");
    00489                 }
    00490                 else
    00491                 {
    00492                     # open response type tag
    00493                     $Response .= $this->FormatTag($Request);
    00494
    00495                     # initialize count of processed items
    00496                     $ListIndex = 0;
    00497
    00498                     # for each item
    00499                     foreach ($ItemIds as $ItemId)
    00500                     {
    00501                         # if item is within range
    00502                         if ($ListIndex >= $Args["ListStartPoint"])
    00503                         {
    00504                             # retrieve item
    00505                             $Item = $this->ItemFactory->GetItem($ItemId);
    00506
    00507                             # add record for item
    00508                             $Response .= $this->GetRecordTags($Item, $Args["metadataPrefix"], $IncludeMetadata);
    00509                         }
    00510
    00511                         # increment count of processed items
    00512                         $ListIndex++;
    00513
    00514                         # stop processing if we have processed max number of items in a pass
    00515                         $MaxItemsPerPass = 20;
    00516                         if (($ListIndex - $Args["ListStartPoint"]) >= $MaxItemsPerPass) {  break;  }
    00517                     }
    00518
    00519                     # if items left unprocessed
    00520                     if ($ListIndex < count($ItemIds))
    00521                     {
    00522                         # add resumption token tag
    00523                         $Token = $this->EncodeResumptionToken((isset($Args["from"]) ? $Args["from"] : NULL),
    00524                                                               (isset($Args["until"]) ? $Args["until"] : NULL),
    00525                                                               (isset($Args["metadataPrefix"]) ? $Args["metadataPrefix"] : NULL),
    00526                                                               (isset($Args["set"]) ? $Args["set"] : NULL),
    00527                                                               $ListIndex);
    00528                         $Response .= $this->FormatTag("resumptionToken", $Token);
    00529                     }
    00530                     else
    00531                     {
    00532                         # if we started with a resumption token tag
    00533                         if (isset($this->Args["resumptionToken"]))
    00534                         {
    00535                             # add empty resumption token tag to indicate end of set
    00536                             $Response .= $this->FormatTag("resumptionToken", "");
    00537                         }
    00538                     }
    00539
    00540                     # close response type tag
    00541                     $Response .= $this->FormatTag();
    00542                 }
    00543             }
    00544         }
    00545
    00546         # close out response
    00547         $Response .= $this->GetResponseEndTags();
    00548
    00549         # return response to caller
    00550         return $Response;
    00551     }
    00552
    00553     function ProcessListMetadataFormats()
    00554     {
    00555         # initialize response
    00556         $Response = $this->GetResponseBeginTags();
    00557
    00558         # if arguments were bad
    00559         $Arg = isset($this->Args["identifier"]) ? $this->Args["identifier"] : NULL;
    00560         $ItemId = $this->DecodeIdentifier($Arg);
    00561         if (isset($this->Args["identifier"]) && ($ItemId == NULL))
    00562         {
    00563             # add error tag
    00564             $Response .= $this->GetRequestTag();
    00565             $Response .= $this->GetErrorTag("idDoesNotExist", "Identifier unknown or illegal.");
    00566         }
    00567         else
    00568         {
    00569             # add request info tag
    00570             $OptArgList = array("identifier");
    00571             $Response .= $this->GetRequestTag("ListMetadataFormats", NULL, $OptArgList);
    00572
    00573             # open response type tag
    00574             $Response .= $this->FormatTag("ListMetadataFormats");
    00575
    00576             # for each supported format
    00577             foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
    00578             {
    00579                 # open format tag
    00580                 $Response .= $this->FormatTag("metadataFormat");
    00581
    00582                 # add tags describing format
    00583                 $Response .= $this->FormatTag("metadataPrefix", $FormatName);
    00584                 $Pieces = preg_split("/[\s]+/", $FormatDescr["SchemaLocation"]);
    00585                 $Response .= $this->FormatTag("schema", $Pieces[1]);
    00586                 $Response .= $this->FormatTag("metadataNamespace", $FormatDescr["NamespaceList"][$FormatName]);
    00587
    00588                 # close format tag
    00589                 $Response .= $this->FormatTag();
    00590             }
    00591
    00592             # close response type tag
    00593             $Response .= $this->FormatTag();
    00594         }
    00595
    00596         # close out response
    00597         $Response .= $this->GetResponseEndTags();
    00598
    00599         # return response to caller
    00600         return $Response;
    00601     }
    00602
    00603     function ProcessListSets()
    00604     {
    00605         # initialize response
    00606         $Response = $this->GetResponseBeginTags();
    00607
    00608         # add request info tag
    00609         $OptArgList = array("resumptionToken");
    00610         $Response .= $this->GetRequestTag("ListSets", NULL, $OptArgList);
    00611
    00612         # retrieve list of supported sets
    00613         $SetList = $this->SetsSupported ? $this->ItemFactory->GetListOfSets() : array();
    00614
    00615         # if sets not supported or we have no sets
    00616         if ((!$this->SetsSupported) || (!count($SetList) && !$this->OaisqSupported))
    00617         {
    00618             # add error tag indicating that we do not support sets
    00619             $Response .= $this->GetErrorTag("noSetHierarchy", "This repository does not support sets.");
    00620         }
    00621         else
    00622         {
    00623             # open response type tag
    00624             $Response .= $this->FormatTag("ListSets");
    00625
    00626             # if OAI-SQ is enabled
    00627             if ($this->OaisqSupported)
    00628             {
    00629                 # add OAI-SQ to list of sets
    00630                 $SetList["OAI-SQ"] = "OAI-SQ";
    00631                 $SetList["OAI-SQ-F"] = "OAI-SQ-F";
    00632             }
    00633
    00634             # for each supported set
    00635             foreach ($SetList as $SetName => $SetSpec)
    00636             {
    00637                 # open set tag
    00638                 $Response .= $this->FormatTag("set");
    00639
    00640                 # add set spec and set name
    00641                 $Response .= $this->FormatTag("setSpec", $SetSpec);
    00642                 $Response .= $this->FormatTag("setName", $SetName);
    00643
    00644                 # close set tag
    00645                 $Response .= $this->FormatTag();
    00646             }
    00647
    00648             # close response type tag
    00649             $Response .= $this->FormatTag();
    00650         }
    00651
    00652         # close out response
    00653         $Response .= $this->GetResponseEndTags();
    00654
    00655         # return response to caller
    00656         return $Response;
    00657     }
    00658
    00659
    00660     # ---- common private methods
    00661
    00662     function GetResponseBeginTags()
    00663     {
    00664         # start with XML declaration
    00665         $Tags = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
    00666
    00667         # add OAI-PMH root element begin tag
    00668         $Tags .= "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\"\n"
    00669                 ."        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
    00670                 ."        xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/\n"
    00671                 ."            http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n";
    00672
    00673         # add response timestamp
    00674         $Tags .= "    <responseDate>".date("Y-m-d\\TH:i:s\\Z")."</responseDate>\n";
    00675
    00676         # return tags to caller
    00677         return $Tags;
    00678     }
    00679
    00680     function GetResponseEndTags()
    00681     {
    00682         # close out OAI-PMH root element
    00683         $Tags = "</OAI-PMH>\n";
    00684
    00685         # return tags to caller
    00686         return $Tags;
    00687     }
    00688
    00689     function GetRequestTag($RequestType = NULL, $ReqArgList = NULL, $OptArgList = NULL)
    00690     {
    00691         # build attribute array
    00692         $AttributeList = array();
    00693         if ($RequestType !== NULL)
    00694         {
    00695             $AttributeList["verb"] = $RequestType;
    00696         }
    00697         if ($ReqArgList != NULL)
    00698         {
    00699             foreach ($ReqArgList as $ArgName)
    00700             {
    00701                 if (isset($this->Args[$ArgName]))
    00702                 {
    00703                     $AttributeList[$ArgName] = $this->Args[$ArgName];
    00704                 }
    00705             }
    00706         }
    00707         if ($OptArgList != NULL)
    00708         {
    00709             foreach ($OptArgList as $ArgName)
    00710             {
    00711                 if (isset($this->Args[$ArgName]))
    00712                 {
    00713                     $AttributeList[$ArgName] = $this->Args[$ArgName];
    00714                 }
    00715             }
    00716         }
    00717
    00718         # generate formatted tag
    00719         $Tag = $this->FormatTag("request",
    00720                                 $this->RepDescr["BaseURL"],
    00721                                 $AttributeList);
    00722
    00723         # return tag to caller
    00724         return $Tag;
    00725     }
    00726
    00727     function GetErrorTag($ErrorCode, $ErrorMessage)
    00728     {
    00729         return $this->FormatTag("error", $ErrorMessage, array("code" => $ErrorCode));
    00730     }
    00731
    00732     function GetRecordTags($Item, $MetadataFormat, $IncludeMetadata = TRUE)
    00733     {
    00734         # if more than identifiers requested
    00735         if ($IncludeMetadata)
    00736         {
    00737             # open record tag
    00738             $Tags = $this->FormatTag("record");
    00739         }
    00740         else
    00741         {
    00742             # just initialize tag string with empty value
    00743             $Tags = "";
    00744         }
    00745
    00746         # add header with identifier, datestamp, and set tags
    00747         $Tags .= $this->FormatTag("header");
    00748         $Tags .= $this->FormatTag("identifier",
    00749                                   $this->EncodeIdentifier($Item->GetId()));
    00750         $Tags .= $this->FormatTag("datestamp", $Item->GetDatestamp());
    00751         $Sets = $Item->GetSets();
    00752         foreach ($Sets as $Set)
    00753         {
    00754             $Tags .= $this->FormatTag("setSpec", $Set);
    00755         }
    00756         $Tags .= $this->FormatTag();
    00757
    00758         # if more than identifiers requested
    00759         if ($IncludeMetadata)
    00760         {
    00761             # open metadata tag
    00762             $Tags .= $this->FormatTag("metadata");
    00763
    00764             # set up attributes for metadata format tag
    00765             $MFAttribs["xsi:schemaLocation"] = $this->FormatDescrs[$MetadataFormat]["SchemaLocation"];
    00766             if (strlen($this->FormatDescrs[$MetadataFormat]["SchemaVersion"]) > 0)
    00767             {
    00768                 $MFAttribs["schemaVersion"] = $this->FormatDescrs[$MetadataFormat]["SchemaVersion"];
    00769             }
    00770             $MFAttribs["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
    00771             foreach ($this->FormatDescrs[$MetadataFormat]["NamespaceList"] as $NamespaceName => $NamespaceURI)
    00772             {
    00773                 $MFAttribs["xmlns:".$NamespaceName] = $NamespaceURI;
    00774             }
    00775
    00776             # open metadata format tag
    00777             $Tags .= $this->FormatTag($this->FormatDescrs[$MetadataFormat]["TagName"], NULL, $MFAttribs);
    00778
    00779             # for each field mapping for this metadata format
    00780             foreach ($this->FieldMappings[$MetadataFormat] as $LocalFieldName => $OAIFieldName)
    00781             {
    00782                 # if field looks like it has been mapped
    00783                 if (strlen($OAIFieldName) > 0)
    00784                 {
    00785                     # retrieve content for field
    00786                     $Content = $Item->GetValue($LocalFieldName);
    00787
    00788                     # retrieve qualifiers for content
    00789                     $Qualifier = $Item->GetQualifier($LocalFieldName);
    00790
    00791                     # if content is array
    00792                     if (is_array($Content))
    00793                     {
    00794                         # for each element of array
    00795                         foreach ($Content as $ContentIndex => $ContentValue)
    00796                         {
    00797                             # if element has content
    00798                             if (strlen($ContentValue) > 0)
    00799                             {
    00800                                 # generate tag for element
    00801                                 if (isset($Qualifier[$ContentIndex]) && strlen($Qualifier[$ContentIndex]))
    00802                                 {
    00803                                     if (isset($this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]])
    00804                                             && (strlen($this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]]) > 0))
    00805                                     {
    00806                                         $ContentAttribs["xsi:type"] = $this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]];
    00807                                     }
    00808                                 }
    00809                                 else
    00810                                 {
    00811                                     $ContentAttribs = NULL;
    00812                                 }
    00813                                 $Tags .= $this->FormatTag($OAIFieldName,
    00814                                                           utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $ContentValue))),
    00815                                                           $ContentAttribs);
    00816                             }
    00817                         }
    00818                     }
    00819                     else
    00820                     {
    00821                         # if field has content
    00822                         if (strlen($Content) > 0)
    00823                         {
    00824                             # generate tag for field
    00825                             if (strlen($Qualifier) > 0)
    00826                             {
    00827                                 if (isset($this->QualifierMappings[$MetadataFormat][$Qualifier])
    00828                                         && (strlen($this->QualifierMappings[$MetadataFormat][$Qualifier]) > 0))
    00829                                 {
    00830                                     $ContentAttribs["xsi:type"] = $this->QualifierMappings[$MetadataFormat][$Qualifier];
    00831                                 }
    00832                             }
    00833                             else
    00834                             {
    00835                                 $ContentAttribs = NULL;
    00836                             }
    00837                             $Tags .= $this->FormatTag($OAIFieldName,
    00838                                                       utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $Content))),
    00839                                                       $ContentAttribs);
    00840                         }
    00841                     }
    00842                 }
    00843             }
    00844
    00845             # close metadata format tag
    00846             $Tags .= $this->FormatTag();
    00847
    00848             # close metadata tag
    00849             $Tags .= $this->FormatTag();
    00850
    00851             # if there is additional search info about this item
    00852             $SearchInfo = $Item->GetSearchInfo();
    00853             if (count($SearchInfo))
    00854             {
    00855                 # open about and search info tags
    00856                 $Tags .= $this->FormatTag("about");
    00857                 $Attribs = array(
    00858                         "xmlns" => "http://scout.wisc.edu/XML/searchInfo/",
    00859                         "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
    00860                         "xsi:schemaLocation" => "http://scout.wisc.edu/XML/searchInfo/ http://scout.wisc.edu/XML/searchInfo.xsd",
    00861                         );
    00862                 $Tags .= $this->FormatTag("searchInfo", NULL, $Attribs);
    00863
    00864                 # for each piece of additional info
    00865                 foreach ($SearchInfo as $InfoName => $InfoValue)
    00866                 {
    00867                     # add tag for info
    00868                     $Tags .= $this->FormatTag($InfoName,
    00869                             utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $InfoValue))));
    00870                 }
    00871
    00872                 # close about and search info tags
    00873                 $Tags .= $this->FormatTag();
    00874                 $Tags .= $this->FormatTag();
    00875             }
    00876         }
    00877
    00878         # if more than identifiers requested
    00879         if ($IncludeMetadata)
    00880         {
    00881             # close record tag
    00882             $Tags .= $this->FormatTag();
    00883         }
    00884
    00885         # return tags to caller
    00886         return $Tags;
    00887     }
    00888
    00889     function EncodeIdentifier($ItemId)
    00890     {
    00891         # return encoded value to caller
    00892         return "oai:".$this->RepDescr["IDDomain"]
    00893                 .":".$this->RepDescr["IDPrefix"]."-".$ItemId;
    00894     }
    00895
    00896     function DecodeIdentifier($Identifier)
    00897     {
    00898         # assume that decode will fail
    00899         $Id = NULL;
    00900
    00901         # split ID into component pieces
    00902         $Pieces = split(":", $Identifier);
    00903
    00904         # if pieces look okay
    00905         if (($Pieces[0] == "oai") && ($Pieces[1] == $this->RepDescr["IDDomain"]))
    00906         {
    00907             # split final piece
    00908             $Pieces = split("-", $Pieces[2]);
    00909
    00910             # if identifier prefix looks okay
    00911             if ($Pieces[0] == $this->RepDescr["IDPrefix"])
    00912             {
    00913                 # decoded value is final piece
    00914                 $Id = $Pieces[1];
    00915             }
    00916         }
    00917
    00918         # return decoded value to caller
    00919         return $Id;
    00920     }
    00921
    00922     function EncodeResumptionToken($StartingDate, $EndingDate, $MetadataFormat, $SetSpec, $ListStartPoint)
    00923     {
    00924         # concatenate values to create token
    00925         $Token = $StartingDate."-_-".$EndingDate."-_-".$MetadataFormat."-_-"
    00926                 .$SetSpec."-_-".$ListStartPoint;
    00927
    00928         # return token to caller
    00929         return $Token;
    00930     }
    00931
    00932     function DecodeResumptionToken($ResumptionToken)
    00933     {
    00934         # split into component pieces
    00935         $Pieces = preg_split("/-_-/", $ResumptionToken);
    00936
    00937         # if we were unable to split token
    00938         if (count($Pieces) != 5)
    00939         {
    00940             # return NULL list
    00941             $Args = NULL;
    00942         }
    00943         else
    00944         {
    00945             # assign component pieces to list parameters
    00946             if (strlen($Pieces[0]) > 0) {  $Args["from"] = $Pieces[0];  }
    00947             if (strlen($Pieces[1]) > 0) {  $Args["until"] = $Pieces[1];  }
    00948             if (strlen($Pieces[2]) > 0) {  $Args["metadataPrefix"] = $Pieces[2];  }
    00949             if (strlen($Pieces[3]) > 0) {  $Args["set"] = $Pieces[3];  }
    00950             if (strlen($Pieces[4]) > 0) {  $Args["ListStartPoint"] = $Pieces[4];  }
    00951         }
    00952
    00953         # return list parameter array to caller
    00954         return $Args;
    00955     }
    00956
    00957     function DateIsInvalid($Date)
    00958     {
    00959         # if date is null or matches required format
    00960         if (empty($Date) || preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/", $Date))
    00961         {
    00962             # date is okay
    00963             return FALSE;
    00964         }
    00965         else
    00966         {
    00967             # date is not okay
    00968             return TRUE;
    00969         }
    00970     }
    00971
    00972     function FormatTag($Name = NULL, $Content = NULL, $Attributes = NULL, $NewIndentLevel = NULL)
    00973     {
    00974         static $IndentLevel = 1;
    00975         static $OpenTagStack = array();
    00976
    00977         # reset indent level if requested
    00978         if ($NewIndentLevel !== NULL)
    00979         {
    00980             $IndentLevel = $NewIndentLevel;
    00981         }
    00982
    00983         # if tag name supplied
    00984         if ($Name !== NULL)
    00985         {
    00986             # start out with appropriate indent
    00987             $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
    00988
    00989             # open begin tag
    00990             $Tag .= "<".$Name;
    00991
    00992             # if attributes supplied
    00993             if ($Attributes !== NULL)
    00994             {
    00995                 # add attributes
    00996                 foreach ($Attributes as $AttributeName => $AttributeValue)
    00997                 {
    00998                     $Tag .= " ".$AttributeName."=\"".$AttributeValue."\"";
    00999                 }
    01000             }
    01001
    01002             # if content supplied
    01003             if ($Content !== NULL)
    01004             {
    01005                 # close begin tag
    01006                 $Tag .= ">";
    01007
    01008                 # add content
    01009                 $Tag .= $Content;
    01010
    01011                 # add end tag
    01012                 $Tag .= "</".$Name.">\n";
    01013             }
    01014             else
    01015             {
    01016                 # close begin tag
    01017                 $Tag .= ">\n";
    01018
    01019                 # increase indent level
    01020                 $IndentLevel++;
    01021
    01022                 # add tag to open tag stack
    01023                 array_push($OpenTagStack, $Name);
    01024             }
    01025         }
    01026         else
    01027         {
    01028             # decrease indent level
    01029             if ($IndentLevel > 0) {  $IndentLevel--;  }
    01030
    01031             # pop last entry off of open tag stack
    01032             $LastName = array_pop($OpenTagStack);
    01033
    01034             # start out with appropriate indent
    01035             $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
    01036
    01037             # add end tag to match last open tag
    01038             $Tag .= "</".$LastName.">\n";
    01039         }
    01040
    01041         # return formatted tag to caller
    01042         return $Tag;
    01043     }
    01044
    01045     function LoadArguments()
    01046     {
    01047         global $_GET;
    01048         global $_POST;
    01049
    01050         # if request type available via POST variables
    01051         if (isset($_POST["verb"]))
    01052         {
    01053             # retrieve arguments from POST variables
    01054             $this->Args = $_POST;
    01055         }
    01056         # else if request type available via GET variables
    01057         elseif (isset($_GET["verb"]))
    01058         {
    01059             # retrieve arguments from GET variables
    01060             $this->Args = $_GET;
    01061         }
    01062         else
    01063         {
    01064             # ERROR OUT
    01065             # ???
    01066         }
    01067     }
    01068
    01069     # ---- methods to support OAI-SQ
    01070
    01071     function IsOaisqQuery($SetString)
    01072     {
    01073         return ((strpos($SetString, "OAI-SQ|") === 0)
    01074                 || (strpos($SetString, "OAI-SQ!") === 0)
    01075                 || (strpos($SetString, "OAI-SQ-F|") === 0)
    01076                 || (strpos($SetString, "OAI-SQ-F!") === 0)
    01077         ) ? TRUE : FALSE;
    01078     }
    01079
    01080     function TranslateOaisqEscapes($Pieces)
    01081     {
    01082         # for each piece
    01083     for ($Index = 0;  $Index < count($Pieces);  $Index++)
    01084     {
    01085         # replace escaped chars with equivalents
    01086         $Pieces[$Index] = preg_replace_callback(
    01087                 "/~[a-fA-F0-9]{2,2}/",
    01088             create_function(
    01089             '$Matches',
    01090             'for ($Index = 0;  $Index < count($Matches);  $Index++)'
    01091                 .'{'
    01092                 .'    $Replacements = chr(intval(substr($Matches[$Index], 1, 2), 16));'
    01093                 .'}'
    01094                 .'return $Replacements;'
    01095             ),
    01096             $Pieces[$Index]);
    01097     }
    01098
    01099     # return translated array of pieces to caller
    01100     return $Pieces;
    01101     }
    01102
    01103     function ParseOaisqQuery($SetString, $FormatName)
    01104     {
    01105         # if OAI-SQ fielded search requested
    01106         if (strpos($SetString, "OAI-SQ-F") === 0)
    01107         {
    01108             # split set string into field names and values
    01109             $Pieces = explode(substr($SetString, 8, 1), $SetString);
    01110
    01111             # discard first piece (OAI-SQ designator)
    01112             array_shift($Pieces);
    01113
    01114         # if set string contains escaped characters
    01115         if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
    01116         {
    01117             $Pieces = $this->TranslateOaisqEscapes($Pieces);
    01118         }
    01119
    01120             # for every two pieces
    01121             $SearchParams = array();
    01122             $NumPairedPieces = round(count($Pieces) / 2) * 2;
    01123             for ($Index = 0;  $Index < $NumPairedPieces;  $Index += 2)
    01124             {
    01125                 # retrieve local field mapping
    01126                 $LocalFieldName = array_search($Pieces[$Index], $this->FieldMappings[$FormatName]);
    01127
    01128                 # if local field mapping found
    01129                 if (strlen($LocalFieldName))
    01130                 {
    01131                     # add mapped values to search parameters
    01132                     $SearchParams[$LocalFieldName] = $Pieces[$Index + 1];
    01133                 }
    01134             }
    01135         }
    01136         else
    01137         {
    01138             # split set string to trim off query designator
    01139             $Pieces = explode(substr($SetString, 6, 1), $SetString, 2);
    01140
    01141         # if set string contains escaped characters
    01142         if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
    01143         {
    01144             $Pieces = $this->TranslateOaisqEscapes($Pieces);
    01145         }
    01146
    01147             # remainder of set string is keyword search string
    01148             $SearchParams["X-KEYWORD-X"] = $Pieces[1];
    01149         }
    01150
    01151         # return array of search parameters to caller
    01152         return $SearchParams;
    01153     }
    01154 }
    01155
    01156 class OAIItemFactory {
    01157
    01158     # ---- PUBLIC INTERFACE --------------------------------------------------
    01159
    01160     # object constructor
    01161     function OAIItemFactory()
    01162     {
    01163     }
    01164
    01165     function GetItem($ItemId) {  exit("OAIItemFactory method GetItem() not implemented");  }
    01166     function GetItems($StartingDate = NULL, $EndingDate = NULL)
    01167     {
    01168         exit("OAIItemFactory method GetItems() not implemented");
    01169     }
    01170
    01171     # retrieve IDs of items that matches set spec (only needed if sets supported)
    01172     function GetItemsInSet($SetSpec, $StartingDate = NULL, $EndingDate = NULL)
    01173     {
    01174         exit("OAIItemFactory method GetItemsInSet() not implemented");
    01175     }
    01176
    01177     # return array containing all set specs (with human-readable set names as keys)
    01178     # (only needed if sets supported)
    01179     function GetListOfSets()
    01180     {
    01181         exit("OAIItemFactory method GetListOfSets() not implemented");
    01182     }
    01183
    01184     # retrieve IDs of items that match search parameters (only needed if OAI-SQ supported)
    01185     function SearchForItems($SearchParams, $StartingDate = NULL, $EndingDate = NULL)
    01186     {
    01187         exit("OAIItemFactory method SearchForItems() not implemented");
    01188     }
    01189 }
    01190
    01191 class OAIItem {
    01192
    01193     # ---- PUBLIC INTERFACE --------------------------------------------------
    01194
    01195     # object constructor
    01196     function OAIItem($ItemId, $ExtraItemInfo = NULL)
    01197     {
    01198     }
    01199
    01200     function GetId()                     {  exit("OAIItem method GetId() not implemented");  }
    01201     function GetDatestamp()              {  exit("OAIItem method GetDatestamp() not implemented");  }
    01202     function GetValue($ElementName)      {  exit("OAIItem method GetValue() not implemented");  }
    01203     function GetQualifier($ElementName)  {  exit("OAIItem method GetQualifiers() not implemented");  }
    01204     function GetSets()                   {  exit("OAIItem method GetSets() not implemented");  }
    01205     function GetSearchInfo()             {  return array();  }
    01206     function Status()                    {  exit("OAIItem method Status() not implemented");  }
    01207 }
    01208
    01209
    01210 ?>