CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

OAIServer.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: OAIServer.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2009-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
10 class OAIServer
11 {
12
13 # ---- PUBLIC INTERFACE --------------------------------------------------
14
15 # object constructor
16
24 public function __construct(
25 $RepDescr, &$ItemFactory, $SetsSupported = FALSE, $OaisqSupported = FALSE)
26 {
27 # save repository description
28 $this->RepDescr = $RepDescr;
29
30 # save supported option settings
31 $this->SetsSupported = $SetsSupported;
32 $this->OaisqSupported = $OaisqSupported;
33
34 # normalize repository description values
35 $this->RepDescr["IDPrefix"] =
36 preg_replace("/[^0-9a-z]/i", "", $this->RepDescr["IDPrefix"]);
37
38 # save item factory
39 $this->ItemFactory =& $ItemFactory;
40
41 # load OAI request type and arguments
42 $this->LoadArguments();
43
44 # set default indent size
45 $this->IndentSize = 4;
46
47 # start with empty list of formats
48 $this->FormatDescrs = array();
49 }
50
63 public function AddFormat($Name, $TagName, $SchemaNamespace, $SchemaDefinition,
64 $SchemaVersion, $NamespaceList, $ElementList, $QualifierList,
65 $DefaultMap)
66 {
67 # find highest current format ID
68 $HighestFormatId = 0;
69 foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
70 {
71 if ($FormatDescr["FormatId"] > $HighestFormatId)
72 {
73 $HighestFormatId = $FormatDescr["FormatId"];
74 }
75 }
76
77 # set new format ID to next value
78 $this->FormatDescrs[$Name]["FormatId"] = $HighestFormatId + 1;
79
80 # store values
81 $this->FormatDescrs[$Name]["TagName"] = $TagName;
82 $this->FormatDescrs[$Name]["SchemaNamespace"] = $SchemaNamespace;
83 $this->FormatDescrs[$Name]["SchemaDefinition"] = $SchemaDefinition;
84 $this->FormatDescrs[$Name]["SchemaVersion"] = $SchemaVersion;
85 $this->FormatDescrs[$Name]["ElementList"] = $ElementList;
86 $this->FormatDescrs[$Name]["QualifierList"] = $QualifierList;
87 $this->FormatDescrs[$Name]["NamespaceList"] = $NamespaceList;
88 $this->FormatDescrs[$Name]["DefaultMap"] = $DefaultMap;
89
90 # start out with empty mappings list
91 if (!isset($this->FieldMappings[$Name]))
92 {
93 $this->FieldMappings[$Name] = array();
94 }
95 }
96
101 public function FormatList()
102 {
103 $FList = array();
104 foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
105 {
106 $FList[$FormatDescr["FormatId"]] = $FormatName;
107 }
108 return $FList;
109 }
110
116 public function FormatElementList($FormatName)
117 {
118 return $this->FormatDescrs[$FormatName]["ElementList"];
119 }
120
121
127 public function FormatQualifierList($FormatName)
128 {
129 return $this->FormatDescrs[$FormatName]["QualifierList"];
130 }
131
138 public function GetFieldMapping($FormatName, $LocalFieldName)
139 {
140 # return stored value
141 if (isset($this->FieldMappings[$FormatName][$LocalFieldName]))
142 {
143 return $this->FieldMappings[$FormatName][$LocalFieldName];
144 }
145 else
146 {
147 return NULL;
148 }
149 }
150
157 public function SetFieldMapping($FormatName, $LocalFieldName, $OAIFieldName)
158 {
159 $this->FieldMappings[$FormatName][$LocalFieldName][]= $OAIFieldName;
160 }
161
168 public function GetQualifierMapping($FormatName, $LocalQualifierName)
169 {
170 # return stored value
171 if (isset($this->QualifierMappings[$FormatName][$LocalQualifierName]))
172 {
173 return $this->QualifierMappings[$FormatName][$LocalQualifierName];
174 }
175 else
176 {
177 return NULL;
178 }
179 }
180
187 public function SetQualifierMapping(
188 $FormatName, $LocalQualifierName, $OAIQualifierName)
189 {
190 $this->QualifierMappings[$FormatName][$LocalQualifierName] =
191 $OAIQualifierName;
192 }
193
198 public function GetResponse()
199 {
200 # call appropriate method based on request type
201 switch (strtoupper($this->Args["verb"]))
202 {
203 case "IDENTIFY":
204 $Response = $this->ProcessIdentify();
205 break;
206
207 case "GETRECORD":
208 $Response = $this->ProcessGetRecord();
209 break;
210
211 case "LISTIDENTIFIERS":
212 $Response = $this->ProcessListRecords(FALSE);
213 break;
214
215 case "LISTRECORDS":
216 $Response = $this->ProcessListRecords(TRUE);
217 break;
218
219 case "LISTMETADATAFORMATS":
220 $Response = $this->ProcessListMetadataFormats();
221 break;
222
223 case "LISTSETS":
224 $Response = $this->ProcessListSets();
225 break;
226
227 default:
228 # return "bad argument" response
229 $Response = $this->GetResponseBeginTags();
230 $Response .= $this->GetRequestTag();
231 $Response .= $this->GetErrorTag(
232 "badVerb", "Bad or unknown request type.");
233 $Response .= $this->GetResponseEndTags();
234 break;
235 }
236
237 # return generated response to caller
238 return $Response;
239 }
240
241
242 # ---- PRIVATE INTERFACE -------------------------------------------------
243
244 private $Args;
245 private $RepDescr;
246 private $ItemFactory;
247 private $FormatDescrs;
248 private $FormatFields;
249 private $FieldMappings;
250 private $QualifierMappings;
251 private $IndentSize;
252 private $SetsSupported;
253 private $OaisqSupported;
254
255
256 # ---- response generation methods
257
262 private function ProcessIdentify()
263 {
264 # initialize response
265 $Response = $this->GetResponseBeginTags();
266
267 # add request info tag
268 $Response .= $this->GetRequestTag("Identify");
269
270 # open response type tag
271 $Response .= $this->FormatTag("Identify");
272
273 # add repository info tags
274 $Response .= $this->FormatTag("repositoryName", $this->RepDescr["Name"]);
275 $Response .= $this->FormatTag("baseURL", $this->RepDescr["BaseURL"]);
276 $Response .= $this->FormatTag("protocolVersion", "2.0");
277 foreach ($this->RepDescr["AdminEmail"] as $AdminEmail)
278 {
279 $Response .= $this->FormatTag("adminEmail", $AdminEmail);
280 }
281 $Response .= $this->FormatTag(
282 "earliestDatestamp", $this->RepDescr["EarliestDate"]);
283 $Response .= $this->FormatTag(
284 "deletedRecord", "no");
285 $Response .= $this->FormatTag(
286 "granularity",
287 (strtoupper($this->RepDescr["DateGranularity"]) == "DATETIME")
288 ? "YYYY-MM-DDThh:mm:ssZ" : "YYYY-MM-DD");
289
290 # add repository description section
291 $Response .= $this->FormatTag("description");
292 $Attribs = array(
293 "xmlns" => "http://www.openarchives.org/OAI/2.0/oai-identifier",
294 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
295 "xsi:schemaLocation" =>
296 "http://www.openarchives.org/OAI/2.0/oai-identifier "
297 ."http://www.openarchives.org/OAI/2.0/oai-identifier.xsd",
298 );
299 $Response .= $this->FormatTag(
300 "oai-identifier", NULL, $Attribs);
301 $Response .= $this->FormatTag(
302 "scheme", "oai");
303 $Response .= $this->FormatTag(
304 "repositoryIdentifier", $this->RepDescr["IDDomain"]);
305 $Response .= $this->FormatTag(
306 "delimiter", ":");
307 $Response .= $this->FormatTag(
308 "sampleIdentifier", $this->EncodeIdentifier("12345"));
309 $Response .= $this->FormatTag();
310 $Response .= $this->FormatTag();
311
312 # close response type tag
313 $Response .= $this->FormatTag();
314
315 # close out response
316 $Response .= $this->GetResponseEndTags();
317
318 # return response to caller
319 return $Response;
320 }
321
326 private function ProcessGetRecord()
327 {
328 # initialize response
329 $Response = $this->GetResponseBeginTags();
330
331 # if arguments were bad
332 if (isset($this->Args["identifier"]))
333 {
334 $ItemId = $this->DecodeIdentifier($this->Args["identifier"]);
335 }
336 else
337 {
338 $ItemId = NULL;
339 }
340 if (isset($this->Args["metadataPrefix"]))
341 {
342 $MetadataFormat = $this->Args["metadataPrefix"];
343 }
344 else
345 {
346 $MetadataFormat = NULL;
347 }
348 if (($ItemId == NULL) || ($MetadataFormat == NULL) ||
349 !is_array($this->FieldMappings[$MetadataFormat]))
350 {
351 # add request info tag with no attributes
352 $Response .= $this->GetRequestTag("GetRecord");
353
354 # add error tag
355 $Response .= $this->GetErrorTag("badArgument", "Bad argument found.");
356 }
357 else
358 {
359 # add request info tag
360 $ReqArgList = array("identifier", "metadataPrefix");
361 $Response .= $this->GetRequestTag("GetRecord", $ReqArgList);
362
363 # attempt to load item corresponding to record
364 $Item = $this->ItemFactory->GetItem($ItemId);
365
366 # if no item found
367 if ($Item == NULL)
368 {
369 # add error tag
370 $Response .= $this->GetErrorTag(
371 "idDoesNotExist", "No item found for specified ID.");
372 }
373 else
374 {
375 # open response type tag
376 $Response .= $this->FormatTag("GetRecord");
377
378 # add tags for record
379 $Response .= $this->GetRecordTags($Item, $MetadataFormat);
380
381 # close response type tag
382 $Response .= $this->FormatTag();
383 }
384 }
385
386 # close out response
387 $Response .= $this->GetResponseEndTags();
388
389 # return response to caller
390 return $Response;
391 }
392
399 private function ProcessListRecords($IncludeMetadata)
400 {
401 # set request type
402 if ($IncludeMetadata)
403 {
404 $Request = "ListRecords";
405 }
406 else
407 {
408 $Request = "ListIdentifiers";
409 }
410
411 # initialize response
412 $Response = $this->GetResponseBeginTags();
413
414 # if resumption token supplied
415 if (isset($this->Args["resumptionToken"]))
416 {
417 # set expected argument lists
418 $ReqArgList = array("resumptionToken");
419 $OptArgList = NULL;
420
421 # parse into list parameters
422 $Args = $this->DecodeResumptionToken($this->Args["resumptionToken"]);
423 }
424 else
425 {
426 # set expected argument lists
427 $ReqArgList = array("metadataPrefix");
428 $OptArgList = array("from", "until", "set");
429
430 # get list parameters from incoming arguments
431 $Args = $this->Args;
432
433 # set list starting point to beginning
434 $Args["ListStartPoint"] = 0;
435 }
436
437 # if resumption token was supplied and was bad
438 if ($Args == NULL)
439 {
440 # add request info tag
441 $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
442
443 # add error tag indicating bad resumption token
444 $Response .= $this->GetErrorTag(
445 "badResumptionToken", "Bad resumption token.");
446
447 # if other parameter also supplied
448 if (count($this->Args) > 2)
449 {
450 # add error tag indicating exclusive argument error
451 $Response .= $this->GetErrorTag(
452 "badArgument", "Resumption token is exclusive argument.");
453 }
454 }
455 # else if resumption token supplied and other arguments also supplied
456 elseif (isset($this->Args["resumptionToken"]) && (count($this->Args) > 2))
457 {
458 # add error tag indicating exclusive argument error
459 $Response .= $this->GetRequestTag();
460 $Response .= $this->GetErrorTag(
461 "badArgument", "Resumption token is exclusive argument.");
462 }
463 # else if metadata format was not specified
464 elseif (empty($Args["metadataPrefix"]))
465 {
466 # add request info tag with no attributes
467 $Response .= $this->GetRequestTag($Request);
468
469 # add error tag indicating bad argument
470 $Response .= $this->GetErrorTag(
471 "badArgument", "No metadata format specified.");
472 }
473 # else if from or until date is specified but bad
474 elseif ((isset($Args["from"]) && $this->DateIsInvalid($Args["from"]))
475 || (isset($Args["until"]) && $this->DateIsInvalid($Args["until"])))
476 {
477 # add request info tag with no attributes
478 $Response .= $this->GetRequestTag($Request);
479
480 # add error tag indicating bad argument
481 $Response .= $this->GetErrorTag("badArgument", "Bad date format.");
482 }
483 else
484 {
485 # add request info tag
486 $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
487
488 # if set requested and we do not support sets
489 if (isset($Args["set"]) && ($this->SetsSupported != TRUE))
490 {
491 # add error tag indicating that we don't support sets
492 $Response .= $this->GetErrorTag(
493 "noSetHierarchy",
494 "This repository does not support sets.");
495 }
496 # else if requested metadata format is not supported
497 elseif (empty($this->FormatDescrs[$Args["metadataPrefix"]]))
498 {
499 # add error tag indicating that format is not supported
500 $Response .= $this->GetErrorTag(
501 "cannotDisseminateFormat",
502 "Metadata format \"".$Args["metadataPrefix"]
503 ."\" not supported by this repository.");
504 }
505 else
506 {
507 # if set requested
508 if (isset($Args["set"]))
509 {
510 # if OAI-SQ supported and set represents OAI-SQ query
511 if ($this->OaisqSupported && $this->IsOaisqQuery($Args["set"]))
512 {
513 # parse OAI-SQ search parameters out of set name
514 $SearchParams = $this->ParseOaisqQuery(
515 $Args["set"], $Args["metadataPrefix"]);
516
517 # if search parameters found
518 if (count($SearchParams))
519 {
520 # perform search for items that match OAI-SQ request
521 $ItemIds = $this->ItemFactory->SearchForItems(
522 $SearchParams,
523 (isset($Args["from"]) ? $Args["from"] : NULL),
524 (isset($Args["until"]) ? $Args["until"] : NULL));
525 }
526 else
527 {
528 # no items match
529 $ItemIds = array();
530 }
531 }
532 else
533 {
534 # get list of items in set that matches incoming criteria
535 $ItemIds = $this->ItemFactory->GetItemsInSet(
536 $Args["set"],
537 (isset($Args["from"]) ? $Args["from"] : NULL),
538 (isset($Args["until"]) ? $Args["until"] : NULL));
539 }
540 }
541 else
542 {
543 # get list of items that matches incoming criteria
544 $ItemIds = $this->ItemFactory->GetItems(
545 (isset($Args["from"]) ? $Args["from"] : NULL),
546 (isset($Args["until"]) ? $Args["until"] : NULL));
547 }
548
549 # if no items found
550 if (count($ItemIds) == 0)
551 {
552 # add error tag indicating that no records found that match spec
553 $Response .= $this->GetErrorTag(
554 "noRecordsMatch",
555 "No records were found that match the specified parameters.");
556 }
557 else
558 {
559 # open response type tag
560 $Response .= $this->FormatTag($Request);
561
562 # initialize count of processed items
563 $ListIndex = 0;
564
565 # for each item
566 foreach ($ItemIds as $ItemId)
567 {
568 # if item is within range
569 if ($ListIndex >= $Args["ListStartPoint"])
570 {
571 # retrieve item
572 $Item = $this->ItemFactory->GetItem($ItemId);
573
574 # add record for item
575 $Response .= $this->GetRecordTags(
576 $Item, $Args["metadataPrefix"], $IncludeMetadata);
577 }
578
579 # increment count of processed items
580 $ListIndex++;
581
582 # stop processing if we have processed max number of items
583 $MaxItemsPerPass = 20;
584 if (($ListIndex - $Args["ListStartPoint"]) >= $MaxItemsPerPass)
585 {
586 break;
587 }
588 }
589
590 # if items left unprocessed
591 if ($ListIndex < count($ItemIds))
592 {
593 # add resumption token tag
594 $Token = $this->EncodeResumptionToken(
595 (isset($Args["from"]) ? $Args["from"] : NULL),
596 (isset($Args["until"]) ? $Args["until"] : NULL),
597 (isset($Args["metadataPrefix"]) ?
598 $Args["metadataPrefix"] : NULL),
599 (isset($Args["set"]) ? $Args["set"] : NULL),
600 $ListIndex);
601 $Response .= $this->FormatTag("resumptionToken", $Token);
602 }
603 else
604 {
605 # if we started with a resumption token tag
606 if (isset($this->Args["resumptionToken"]))
607 {
608 # add empty resumption token tag to indicate end of set
609 $Response .= $this->FormatTag("resumptionToken", "");
610 }
611 }
612
613 # close response type tag
614 $Response .= $this->FormatTag();
615 }
616 }
617 }
618
619 # close out response
620 $Response .= $this->GetResponseEndTags();
621
622 # return response to caller
623 return $Response;
624 }
625
630 private function ProcessListMetadataFormats()
631 {
632 # initialize response
633 $Response = $this->GetResponseBeginTags();
634
635 # if arguments were bad
636 $Arg = isset($this->Args["identifier"]) ? $this->Args["identifier"] : NULL;
637 $ItemId = $this->DecodeIdentifier($Arg);
638 if (isset($this->Args["identifier"]) && ($ItemId == NULL))
639 {
640 # add error tag
641 $Response .= $this->GetRequestTag();
642 $Response .= $this->GetErrorTag(
643 "idDoesNotExist", "Identifier unknown or illegal.");
644 }
645 else
646 {
647 # add request info tag
648 $OptArgList = array("identifier");
649 $Response .= $this->GetRequestTag("ListMetadataFormats", NULL, $OptArgList);
650
651 # open response type tag
652 $Response .= $this->FormatTag("ListMetadataFormats");
653
654 # for each supported format
655 foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
656 {
657 # open format tag
658 $Response .= $this->FormatTag("metadataFormat");
659
660 # add tags describing format
661 $Response .= $this->FormatTag("metadataPrefix", $FormatName);
662 if (isset($FormatDescr["SchemaDefinition"]))
663 {
664 $Response .= $this->FormatTag("schema",
665 $FormatDescr["SchemaDefinition"]);
666 }
667 if (isset($FormatDescr["SchemaNamespace"]))
668 {
669 $Response .= $this->FormatTag("metadataNamespace",
670 $FormatDescr["SchemaNamespace"]);
671 }
672
673 # close format tag
674 $Response .= $this->FormatTag();
675 }
676
677 # close response type tag
678 $Response .= $this->FormatTag();
679 }
680
681 # close out response
682 $Response .= $this->GetResponseEndTags();
683
684 # return response to caller
685 return $Response;
686 }
687
692 private function ProcessListSets()
693 {
694 # initialize response
695 $Response = $this->GetResponseBeginTags();
696
697 # add request info tag
698 $OptArgList = array("resumptionToken");
699 $Response .= $this->GetRequestTag("ListSets", NULL, $OptArgList);
700
701 # retrieve list of supported sets
702 $SetList = $this->SetsSupported ? $this->ItemFactory->GetListOfSets() : array();
703
704 # if sets not supported or we have no sets
705 if ((!$this->SetsSupported) || (!count($SetList) && !$this->OaisqSupported))
706 {
707 # add error tag indicating that we do not support sets
708 $Response .= $this->GetErrorTag(
709 "noSetHierarchy", "This repository does not support sets.");
710 }
711 else
712 {
713 # open response type tag
714 $Response .= $this->FormatTag("ListSets");
715
716 # if OAI-SQ is enabled
717 if ($this->OaisqSupported)
718 {
719 # add OAI-SQ to list of sets
720 $SetList["OAI-SQ"] = "OAI-SQ";
721 $SetList["OAI-SQ-F"] = "OAI-SQ-F";
722 }
723
724 # for each supported set
725 foreach ($SetList as $SetName => $SetSpec)
726 {
727 # open set tag
728 $Response .= $this->FormatTag("set");
729
730 # add set spec and set name
731 $Response .= $this->FormatTag("setSpec", $SetSpec);
732 $Response .= $this->FormatTag("setName", $SetName);
733
734 # close set tag
735 $Response .= $this->FormatTag();
736 }
737
738 # close response type tag
739 $Response .= $this->FormatTag();
740 }
741
742 # close out response
743 $Response .= $this->GetResponseEndTags();
744
745 # return response to caller
746 return $Response;
747 }
748
749
750 # ---- common private methods
751
756 private function GetResponseBeginTags()
757 {
758 # start with XML declaration
759 $Tags = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?".">\n";
760
761 # add OAI-PMH root element begin tag
762 $Tags .= "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\"\n"
763 ." xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
764 ." xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/\n"
765 ." http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n";
766
767 # add response timestamp
768 $Tags .= " <responseDate>".date("Y-m-d\\TH:i:s\\Z")."</responseDate>\n";
769
770 # return tags to caller
771 return $Tags;
772 }
773
778 private function GetResponseEndTags()
779 {
780 # close out OAI-PMH root element
781 $Tags = "</OAI-PMH>\n";
782
783 # return tags to caller
784 return $Tags;
785 }
786
794 private function GetRequestTag(
795 $RequestType = NULL, $ReqArgList = NULL, $OptArgList = NULL)
796 {
797 # build attribute array
798 $AttributeList = array();
799 if ($RequestType !== NULL)
800 {
801 $AttributeList["verb"] = $RequestType;
802 }
803 if ($ReqArgList != NULL)
804 {
805 foreach ($ReqArgList as $ArgName)
806 {
807 if (isset($this->Args[$ArgName]))
808 {
809 $AttributeList[$ArgName] = $this->Args[$ArgName];
810 }
811 }
812 }
813 if ($OptArgList != NULL)
814 {
815 foreach ($OptArgList as $ArgName)
816 {
817 if (isset($this->Args[$ArgName]))
818 {
819 $AttributeList[$ArgName] = $this->Args[$ArgName];
820 }
821 }
822 }
823
824 # generate formatted tag
825 $Tag = $this->FormatTag("request",
826 $this->RepDescr["BaseURL"],
827 $AttributeList);
828
829 # return tag to caller
830 return $Tag;
831 }
832
839 private function GetErrorTag($ErrorCode, $ErrorMessage)
840 {
841 return $this->FormatTag("error", $ErrorMessage, array("code" => $ErrorCode));
842 }
843
852 private function GetRecordTags($Item, $MetadataFormat, $IncludeMetadata = TRUE)
853 {
854 # if more than identifiers requested
855 if ($IncludeMetadata)
856 {
857 # open record tag
858 $Tags = $this->FormatTag("record");
859 }
860 else
861 {
862 # just initialize tag string with empty value
863 $Tags = "";
864 }
865
866 # add header with identifier, datestamp, and set tags
867 $Tags .= $this->FormatTag("header");
868 $Tags .= $this->FormatTag("identifier",
869 $this->EncodeIdentifier($Item->GetId()));
870 $Tags .= $this->FormatTag("datestamp", $Item->GetDatestamp());
871 $Sets = $Item->GetSets();
872 foreach ($Sets as $Set)
873 {
874 $Tags .= $this->FormatTag("setSpec", $Set);
875 }
876 $Tags .= $this->FormatTag();
877
878 # if more than identifiers requested
879 if ($IncludeMetadata)
880 {
881 # open metadata tag
882 $Tags .= $this->FormatTag("metadata");
883
884 # set up attributes for metadata format tag
885 $MFAttribs["xsi:schemaLocation"] =
886 $this->FormatDescrs[$MetadataFormat]["SchemaNamespace"]." \n"
887 .$this->FormatDescrs[$MetadataFormat]["SchemaDefinition"];
888 $MFAttribs["xmlns"] = $this->FormatDescrs[$MetadataFormat]["SchemaNamespace"];
889 if (strlen($this->FormatDescrs[$MetadataFormat]["SchemaVersion"]) > 0)
890 {
891 $MFAttribs["schemaVersion"] =
892 $this->FormatDescrs[$MetadataFormat]["SchemaVersion"];
893 }
894 $MFAttribs["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
895 foreach ($this->FormatDescrs[$MetadataFormat]["NamespaceList"] as
896 $NamespaceName => $NamespaceURI)
897 {
898 $MFAttribs["xmlns:".$NamespaceName] = $NamespaceURI;
899 }
900
901 # open metadata format tag
902 $Tags .= $this->FormatTag($this->FormatDescrs[$MetadataFormat]["TagName"],
903 NULL, $MFAttribs);
904
905 # for each field mapping for this metadata format
906 foreach ($this->FieldMappings[$MetadataFormat] as
907 $LocalFieldName => $OAIFieldNames)
908 {
909 foreach ($OAIFieldNames as $OAIFieldName)
910 {
911 # if field looks like it has been mapped
912 if (strlen($OAIFieldName) > 0 && strlen($LocalFieldName) > 0 )
913 {
914 $Tags .= $this->FormatItemContent(
915 $Item, $MetadataFormat, $LocalFieldName, $OAIFieldName);
916 }
917 }
918 }
919
920 # close metadata format tag
921 $Tags .= $this->FormatTag();
922
923 # close metadata tag
924 $Tags .= $this->FormatTag();
925
926 # if there is additional search info about this item
927 $SearchInfo = $Item->GetSearchInfo();
928 if (count($SearchInfo))
929 {
930 # open about and search info tags
931 $Tags .= $this->FormatTag("about");
932 $Attribs = array(
933 "xmlns" => "http://scout.wisc.edu/XML/searchInfo/",
934 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
935 "xsi:schemaLocation" => "http://scout.wisc.edu/XML/searchInfo/ "
936 ."http://scout.wisc.edu/XML/searchInfo.xsd",
937 );
938 $Tags .= $this->FormatTag("searchInfo", NULL, $Attribs);
939
940 # for each piece of additional info
941 foreach ($SearchInfo as $InfoName => $InfoValue)
942 {
943 # add tag for info
944 $Tags .= $this->FormatTag($InfoName,
945 utf8_encode(htmlspecialchars(
946 preg_replace("/[\\x00-\\x1F]+/", "", $InfoValue))));
947 }
948
949 # close about and search info tags
950 $Tags .= $this->FormatTag();
951 $Tags .= $this->FormatTag();
952 }
953 }
954
955 # if more than identifiers requested
956 if ($IncludeMetadata)
957 {
958 # close record tag
959 $Tags .= $this->FormatTag();
960 }
961
962 # return tags to caller
963 return $Tags;
964 }
965
971 private function EncodeIdentifier($ItemId)
972 {
973 # return encoded value to caller
974 return "oai:".$this->RepDescr["IDDomain"]
975 .":".$this->RepDescr["IDPrefix"]."-".$ItemId;
976 }
977
984 private function DecodeIdentifier($Identifier)
985 {
986 # assume that decode will fail
987 $Id = NULL;
988
989 # split ID into component pieces
990 $Pieces = explode(":", $Identifier);
991
992 # if pieces look okay
993 if (($Pieces[0] == "oai") && ($Pieces[1] == $this->RepDescr["IDDomain"]))
994 {
995 # split final piece
996 $Pieces = explode("-", $Pieces[2]);
997
998 # if identifier prefix looks okay
999 if ($Pieces[0] == $this->RepDescr["IDPrefix"])
1000 {
1001 # decoded value is final piece
1002 $Id = $Pieces[1];
1003 }
1004 }
1005
1006 # return decoded value to caller
1007 return $Id;
1008 }
1009
1018 private function EncodeResumptionToken(
1019 $StartingDate, $EndingDate, $MetadataFormat, $SetSpec, $ListStartPoint)
1020 {
1021 # concatenate values to create token
1022 $Token = $StartingDate."-_-".$EndingDate."-_-".$MetadataFormat."-_-"
1023 .$SetSpec."-_-".$ListStartPoint;
1024
1025 # return token to caller
1026 return $Token;
1027 }
1028
1034 private function DecodeResumptionToken($ResumptionToken)
1035 {
1036 # split into component pieces
1037 $Pieces = preg_split("/-_-/", $ResumptionToken);
1038
1039 # if we were unable to split token
1040 if (count($Pieces) != 5)
1041 {
1042 # return NULL list
1043 $Args = NULL;
1044 }
1045 else
1046 {
1047 # assign component pieces to list parameters
1048 if (strlen($Pieces[0]) > 0) { $Args["from"] = $Pieces[0]; }
1049 if (strlen($Pieces[1]) > 0) { $Args["until"] = $Pieces[1]; }
1050 if (strlen($Pieces[2]) > 0) { $Args["metadataPrefix"] = $Pieces[2]; }
1051 if (strlen($Pieces[3]) > 0) { $Args["set"] = $Pieces[3]; }
1052 if (strlen($Pieces[4]) > 0) { $Args["ListStartPoint"] = $Pieces[4]; }
1053 }
1054
1055 # return list parameter array to caller
1056 return $Args;
1057 }
1058
1064 private function DateIsInvalid($Date)
1065 {
1066 # if date is null or matches required format
1067 if (empty($Date) || preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/", $Date))
1068 {
1069 # date is okay
1070 return FALSE;
1071 }
1072 else
1073 {
1074 # date is not okay
1075 return TRUE;
1076 }
1077 }
1078
1088 private function FormatTag(
1089 $Name = NULL, $Content = NULL, $Attributes = NULL, $NewIndentLevel = NULL)
1090 {
1091 static $IndentLevel = 1;
1092 static $OpenTagStack = array();
1093
1094 # reset indent level if requested
1095 if ($NewIndentLevel !== NULL)
1096 {
1097 $IndentLevel = $NewIndentLevel;
1098 }
1099
1100 # if tag name supplied
1101 if ($Name !== NULL)
1102 {
1103 # start out with appropriate indent
1104 $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
1105
1106 # open begin tag
1107 $Tag .= "<".$Name;
1108
1109 # if attributes supplied
1110 if ($Attributes !== NULL)
1111 {
1112 # add attributes
1113 foreach ($Attributes as $AttributeName => $AttributeValue)
1114 {
1115 $Tag .= " ".$AttributeName."=\"".$AttributeValue."\"";
1116 }
1117 }
1118
1119 # if content supplied
1120 if ($Content !== NULL)
1121 {
1122 # close begin tag
1123 $Tag .= ">";
1124
1125 # add content
1126 $Tag .= htmlspecialchars($Content);
1127
1128 # add end tag
1129 $Tag .= "</".$Name.">\n";
1130 }
1131 else
1132 {
1133 # close begin tag
1134 $Tag .= ">\n";
1135
1136 # increase indent level
1137 $IndentLevel++;
1138
1139 # add tag to open tag stack
1140 array_push($OpenTagStack, $Name);
1141 }
1142 }
1143 else
1144 {
1145 # decrease indent level
1146 if ($IndentLevel > 0) { $IndentLevel--; }
1147
1148 # pop last entry off of open tag stack
1149 $LastName = array_pop($OpenTagStack);
1150
1151 # start out with appropriate indent
1152 $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
1153
1154 # add end tag to match last open tag
1155 $Tag .= "</".$LastName.">\n";
1156 }
1157
1158 # return formatted tag to caller
1159 return $Tag;
1160 }
1161
1170 private function FormatItemContent(
1171 $Item, $MetadataFormat, $LocalFieldName, $OAIFieldName)
1172 {
1173 # retrieve content for field
1174 $Content = $Item->GetValue($LocalFieldName);
1175
1176 # retrieve qualifiers for content
1177 $Qualifier = $Item->GetQualifier($LocalFieldName);
1178
1179 # get qualifier maps, if any exists for our format
1180 $QualifierMaps = isset($this->QualifierMappings[$MetadataFormat]) ?
1181 $this->QualifierMappings[$MetadataFormat] :
1182 array();
1183
1184 # get defaults, if any exist for our format
1185 $DefaultMaps = isset($this->FormatDescrs[$MetadataFormat]["DefaultMap"]) ?
1186 $this->FormatDescrs[$MetadataFormat]["DefaultMap"] :
1187 array();
1188
1189 $Tags = "";
1190 # if content is array
1191 if (is_array($Content))
1192 {
1193 # for each element of array
1194 foreach ($Content as $ContentIndex => $ContentValue)
1195 {
1196 # if element has content
1197 if (strlen($ContentValue) > 0)
1198 {
1199 # determine if we have a mapped qualifier for this item
1200 $ContentAttribs = NULL;
1201 if (isset($Qualifier[$ContentIndex]) &&
1202 strlen($Qualifier[$ContentIndex]) &&
1203 isset($QualifierMaps[$Qualifier[$ContentIndex]]) &&
1204 strlen($QualifierMaps[$Qualifier[$ContentIndex]]) )
1205 {
1206 # if so, add the appropriate attribute
1207 $ContentAttribs["xsi:type"] =
1208 $QualifierMaps[$Qualifier[$ContentIndex]];
1209 }
1210
1211 # generate tags for this field, append them to our list
1212 $Tags .= $this->FormatTag($OAIFieldName,
1213 utf8_encode(htmlspecialchars(preg_replace(
1214 "/[\\x00-\\x1F]+/", "", $ContentValue))),
1215 $ContentAttribs);
1216 }
1217 }
1218 }
1219 else
1220 {
1221 # check for a default value, fill it in if there was one
1222 if (strlen($Content)==0 && isset($DefaultMap[$OAIFieldName]))
1223 {
1224 $Content = $DefaultMap[$OAIFieldName];
1225 }
1226
1227 # if field has content
1228 if (strlen($Content) > 0)
1229 {
1230 # generate tag for field
1231 $ContentAttribs = NULL;
1232 if (strlen($Qualifier) > 0 &&
1233 isset($QualifierMaps[$Qualifier]) &&
1234 strlen($QualifierMaps[$Qualifier]) )
1235 {
1236 $ContentAttribs["xsi:type"] =
1237 $QualifierMaps[$Qualifier];
1238
1239 }
1240
1241 $Tags .= $this->FormatTag($OAIFieldName,
1242 utf8_encode(htmlspecialchars(preg_replace(
1243 "/[\\x00-\\x1F]+/", "", $Content))),
1244 $ContentAttribs);
1245 }
1246 }
1247
1248 return $Tags;
1249 }
1250
1251
1255 private function LoadArguments()
1256 {
1257 # if request type available via POST variables
1258 if (isset($_POST["verb"]))
1259 {
1260 # retrieve arguments from POST variables
1261 $this->Args = $_POST;
1262 }
1263 # else if request type available via GET variables
1264 elseif (isset($_GET["verb"]))
1265 {
1266 # retrieve arguments from GET variables
1267 $this->Args = $_GET;
1268 }
1269 else
1270 {
1271 # ERROR OUT
1272 return;
1273 }
1274
1275 # clear out ApplicationFramework page specifier if set
1276 if (isset($this->Args["P"])) { unset($this->Args["P"]); }
1277 }
1278
1279 # ---- methods to support OAI-SQ
1280
1286 private function IsOaisqQuery($SetString)
1287 {
1288 return ((strpos($SetString, "OAI-SQ|") === 0)
1289 || (strpos($SetString, "OAI-SQ!") === 0)
1290 || (strpos($SetString, "OAI-SQ-F|") === 0)
1291 || (strpos($SetString, "OAI-SQ-F!") === 0)
1292 ) ? TRUE : FALSE;
1293 }
1294
1301 private function TranslateOaisqEscapes($Pieces)
1302 {
1303 # for each piece
1304 $N = count($Pieces);
1305 for ($Index = 0; $Index < $N; $Index++)
1306 {
1307 # replace escaped chars with equivalents
1308 $Pieces[$Index] = preg_replace_callback(
1309 "/~[a-fA-F0-9]{2,2}/",
1310 create_function(
1311 '$Matches',
1312 'for ($Index = 0; $Index < count($Matches); $Index++)'
1313 .'{'
1314 .' $Replacements = chr(intval(substr($Matches[$Index],1,2),16));'
1315 .'}'
1316 .'return $Replacements;'
1317 ),
1318 $Pieces[$Index]);
1319 }
1320
1321 # return translated array of pieces to caller
1322 return $Pieces;
1323 }
1324
1332 private function ParseOaisqQuery($SetString, $FormatName)
1333 {
1334 # if OAI-SQ fielded search requested
1335 if (strpos($SetString, "OAI-SQ-F") === 0)
1336 {
1337 # split set string into field names and values
1338 $Pieces = explode(substr($SetString, 8, 1), $SetString);
1339
1340 # discard first piece (OAI-SQ designator)
1341 array_shift($Pieces);
1342
1343 # if set string contains escaped characters
1344 if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
1345 {
1346 $Pieces = $this->TranslateOaisqEscapes($Pieces);
1347 }
1348
1349 # for every two pieces
1350 $SearchParams = array();
1351 $NumPairedPieces = round(count($Pieces) / 2) * 2;
1352 for ($Index = 0; $Index < $NumPairedPieces; $Index += 2)
1353 {
1354 # retrieve local field mapping
1355 foreach ($this->FieldMappings[$FormatName] as
1356 $LocalFieldName => $OAIFieldNames)
1357 {
1358 if (array_search($Pieces[$Index], $OAIFieldNames) !== FALSE)
1359 {
1360 $SearchParams[$LocalFieldName] = $Pieces[$Index + 1];
1361 }
1362 }
1363 }
1364 }
1365 else
1366 {
1367 # split set string to trim off query designator
1368 $Pieces = explode(substr($SetString, 6, 1), $SetString, 2);
1369
1370 # if set string contains escaped characters
1371 if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
1372 {
1373 $Pieces = $this->TranslateOaisqEscapes($Pieces);
1374 }
1375
1376 # remainder of set string is keyword search string
1377 $SearchParams["X-KEYWORD-X"] = $Pieces[1];
1378 }
1379
1380 # return array of search parameters to caller
1381 return $SearchParams;
1382 }
1383 }
OAIServer\__construct
__construct($RepDescr, &$ItemFactory, $SetsSupported=FALSE, $OaisqSupported=FALSE)
Construct an OAI server object.
Definition: OAIServer.php:24
OAIServer\FormatElementList
FormatElementList($FormatName)
Get list of elements for a specified format.
Definition: OAIServer.php:116
OAIServer\FormatQualifierList
FormatQualifierList($FormatName)
Get the list of qualifiers for a specified format.
Definition: OAIServer.php:127
OAIServer\FormatList
FormatList()
Get the list of formats.
Definition: OAIServer.php:101
OAIServer
Definition: OAIServer.php:10
OAIServer\GetResponse
GetResponse()
Get OAI response.
Definition: OAIServer.php:198
ItemFactory\GetItem
GetItem($ItemId)
Retrieve item by item ID.
Definition: ItemFactory.php:321
OAIServer\GetFieldMapping
GetFieldMapping($FormatName, $LocalFieldName)
Get mapped name for a field.
Definition: OAIServer.php:138
OAIServer\GetQualifierMapping
GetQualifierMapping($FormatName, $LocalQualifierName)
Get mapping for a qualifier.
Definition: OAIServer.php:168
PHP
PHP
Definition: RSSClient--Test.php:12
OAIServer\AddFormat
AddFormat($Name, $TagName, $SchemaNamespace, $SchemaDefinition, $SchemaVersion, $NamespaceList, $ElementList, $QualifierList, $DefaultMap)
Add a new metadata format.
Definition: OAIServer.php:63
OAIServer\SetQualifierMapping
SetQualifierMapping($FormatName, $LocalQualifierName, $OAIQualifierName)
Set mapping for a qualifier.
Definition: OAIServer.php:187
ItemFactory\GetItems
GetItems($SqlCondition=NULL)
Retrieve items.
Definition: ItemFactory.php:481
ItemFactory
Common factory class for item manipulation.
Definition: ItemFactory.php:17
OAIServer\SetFieldMapping
SetFieldMapping($FormatName, $LocalFieldName, $OAIFieldName)
Set mapping for a field.
Definition: OAIServer.php:157