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 ?>