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 # if request type available via POST variables
01048 if (isset($_POST["verb"]))
01049 {
01050 # retrieve arguments from POST variables
01051 $this->Args = $_POST;
01052 }
01053 # else if request type available via GET variables
01054 elseif (isset($_GET["verb"]))
01055 {
01056 # retrieve arguments from GET variables
01057 $this->Args = $_GET;
01058
01059 # strip off page designator GET parameter
01060 if (isset($this->Args["P"])) { unset($this->Args["P"]); }
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 ?>