CWIS Developer Documentation
OAIServer.php
Go to the documentation of this file.
1 <?PHP
2 
3 #
4 # FILE: Scout--OAIServer.php
5 #
6 # METHODS PROVIDED:
7 # OAIServer()
8 # - constructor
9 #
10 # AUTHOR: Edward Almasy
11 #
12 # Copyright 2002-2004 Internet Scout Project
13 # http://scout.wisc.edu
14 #
15 
16 class OAIServer {
17 
18  # ---- PUBLIC INTERFACE --------------------------------------------------
19 
20  # object constructor
22  {
23  # save DB handle for our use
24  $this->DB =& $DB;
25 
26  # save repository description
27  $this->RepDescr = $RepDescr;
28 
29  # save supported option settings
30  $this->SetsSupported = $SetsSupported;
31  $this->OaisqSupported = $OaisqSupported;
32 
33  # normalize repository description values
34  $this->RepDescr["IDPrefix"] =
35  preg_replace("/[^0-9a-z]/i", "", $this->RepDescr["IDPrefix"]);
36 
37  # save item factory
38  $this->ItemFactory =& $ItemFactory;
39 
40  # load OAI request type and arguments
41  $this->LoadArguments();
42 
43  # set default indent size
44  $this->IndentSize = 4;
45 
46  # start with empty list of formats
47  $this->FormatDescrs = array();
48 
49  # initialize description of mandatory format
50  $OaidcNamespaceList = array(
51  "oai_dc" => "http://www.openarchives.org/OAI/2.0/oai_dc/",
52  "dc" => "http://purl.org/dc/elements/1.1/",
53  );
54  $OaidcElements = array(
55  "dc:title",
56  "dc:creator",
57  "dc:subject",
58  "dc:description",
59  "dc:publisher",
60  "dc:contributor",
61  "dc:date",
62  "dc:type",
63  "dc:format",
64  "dc:identifier",
65  "dc:source",
66  "dc:language",
67  "dc:relation",
68  "dc:coverage",
69  "dc:rights",
70  );
71  $OaidcQualifiers = array();
72  $this->AddFormat("oai_dc", "oai_dc:dc",
73  "http://www.openarchives.org/OAI/2.0/oai_dc/"
74  ." http://www.openarchives.org/OAI/2.0/oai_dc.xsd",
75  NULL,
76  $OaidcNamespaceList, $OaidcElements, $OaidcQualifiers);
77  }
78 
79  # add metadata format to export
80  function AddFormat($Name, $TagName, $SchemaLocation, $SchemaVersion, $NamespaceList, $ElementList, $QualifierList)
81  {
82  # find highest current format ID
83  $HighestFormatId = 0;
84  foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
85  {
86  if ($FormatDescr["FormatId"] > $HighestFormatId)
87  {
88  $HighestFormatId = $FormatDescr["FormatId"];
89  }
90  }
91 
92  # set new format ID to next value
93  $this->FormatDescrs[$Name]["FormatId"] = $HighestFormatId + 1;
94 
95  # store values
96  $this->FormatDescrs[$Name]["TagName"] = $TagName;
97  $this->FormatDescrs[$Name]["SchemaLocation"] = $SchemaLocation;
98  $this->FormatDescrs[$Name]["SchemaVersion"] = $SchemaVersion;
99  $this->FormatDescrs[$Name]["ElementList"] = $ElementList;
100  $this->FormatDescrs[$Name]["QualifierList"] = $QualifierList;
101  $this->FormatDescrs[$Name]["NamespaceList"] = $NamespaceList;
102 
103  # start out with empty mappings list
104  if (!isset($this->FieldMappings[$Name]))
105  {
106  $this->FieldMappings[$Name] = array();
107  }
108  }
109 
110  # return list of formats
111  function FormatList()
112  {
113  $FList = array();
114  foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
115  {
116  $FList[$FormatDescr["FormatId"]] = $FormatName;
117  }
118  return $FList;
119  }
120 
121  # return list of elements for a given format
122  function FormatElementList($FormatName)
123  {
124  return $this->FormatDescrs[$FormatName]["ElementList"];
125  }
126 
127  # return list of qualifiers for a given format
128  function FormatQualifierList($FormatName)
129  {
130  return $this->FormatDescrs[$FormatName]["QualifierList"];
131  }
132 
133  # get/set mapping of local field to OAI field
134  function GetFieldMapping($FormatName, $LocalFieldName)
135  {
136  # return stored value
137  if (isset($this->FieldMappings[$FormatName][$LocalFieldName]))
138  {
139  return $this->FieldMappings[$FormatName][$LocalFieldName];
140  }
141  else
142  {
143  return NULL;
144  }
145  }
146  function SetFieldMapping($FormatName, $LocalFieldName, $OAIFieldName)
147  {
148  $this->FieldMappings[$FormatName][$LocalFieldName] = $OAIFieldName;
149  }
150 
151  # get/set mapping of local qualifier to OAI qualifier
152  function GetQualifierMapping($FormatName, $LocalQualifierName)
153  {
154  # return stored value
155  if (isset($this->QualifierMappings[$FormatName][$LocalQualifierName]))
156  {
157  return $this->QualifierMappings[$FormatName][$LocalQualifierName];
158  }
159  else
160  {
161  return NULL;
162  }
163  }
164  function SetQualifierMapping($FormatName, $LocalQualifierName, $OAIQualifierName)
165  {
166  $this->QualifierMappings[$FormatName][$LocalQualifierName] = $OAIQualifierName;
167  }
168 
169  function GetResponse()
170  {
171  # call appropriate method based on request type
172  switch (strtoupper($this->Args["verb"]))
173  {
174  case "IDENTIFY":
175  $Response = $this->ProcessIdentify();
176  break;
177 
178  case "GETRECORD":
179  $Response = $this->ProcessGetRecord();
180  break;
181 
182  case "LISTIDENTIFIERS":
183  $Response = $this->ProcessListRecords(FALSE);
184  break;
185 
186  case "LISTRECORDS":
187  $Response = $this->ProcessListRecords(TRUE);
188  break;
189 
190  case "LISTMETADATAFORMATS":
191  $Response = $this->ProcessListMetadataFormats();
192  break;
193 
194  case "LISTSETS":
195  $Response = $this->ProcessListSets();
196  break;
197 
198  default:
199  # return "bad argument" response
200  $Response = $this->GetResponseBeginTags();
201  $Response .= $this->GetRequestTag();
202  $Response .= $this->GetErrorTag("badVerb", "Bad or unknown request type.");
203  $Response .= $this->GetResponseEndTags();
204  break;
205  }
206 
207  # return generated response to caller
208  return $Response;
209  }
210 
211 
212  # ---- PRIVATE INTERFACE -------------------------------------------------
213 
214  var $DB;
215  var $Args;
225 
226 
227  # ---- response generation methods
228 
229  function ProcessIdentify()
230  {
231  # initialize response
232  $Response = $this->GetResponseBeginTags();
233 
234  # add request info tag
235  $Response .= $this->GetRequestTag("Identify");
236 
237  # open response type tag
238  $Response .= $this->FormatTag("Identify");
239 
240  # add repository info tags
241  $Response .= $this->FormatTag("repositoryName", $this->RepDescr["Name"]);
242  $Response .= $this->FormatTag("baseURL", $this->RepDescr["BaseURL"]);
243  $Response .= $this->FormatTag("protocolVersion", "2.0");
244  foreach ($this->RepDescr["AdminEmail"] as $AdminEmail)
245  {
246  $Response .= $this->FormatTag("adminEmail", $AdminEmail);
247  }
248  $Response .= $this->FormatTag("earliestDatestamp", $this->RepDescr["EarliestDate"]);
249  $Response .= $this->FormatTag("deletedRecord", "no");
250  $Response .= $this->FormatTag("granularity",
251  (strtoupper($this->RepDescr["DateGranularity"]) == "DATETIME")
252  ? "YYYY-MM-DDThh:mm:ssZ" : "YYYY-MM-DD");
253 
254  # add repository description section
255  $Response .= $this->FormatTag("description");
256  $Attribs = array(
257  "xmlns" => "http://www.openarchives.org/OAI/2.0/oai-identifier",
258  "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
259  "xsi:schemaLocation" => "http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd",
260  );
261  $Response .= $this->FormatTag("oai-identifier", NULL, $Attribs);
262  $Response .= $this->FormatTag("scheme", "oai");
263  $Response .= $this->FormatTag("repositoryIdentifier", $this->RepDescr["IDDomain"]);
264  $Response .= $this->FormatTag("delimiter", ":");
265  $Response .= $this->FormatTag("sampleIdentifier", $this->EncodeIdentifier("12345"));
266  $Response .= $this->FormatTag();
267  $Response .= $this->FormatTag();
268 
269  # close response type tag
270  $Response .= $this->FormatTag();
271 
272  # close out response
273  $Response .= $this->GetResponseEndTags();
274 
275  # return response to caller
276  return $Response;
277  }
278 
279  function ProcessGetRecord()
280  {
281  # initialize response
282  $Response = $this->GetResponseBeginTags();
283 
284  # if arguments were bad
285  if (isset($this->Args["identifier"]))
286  {
287  $ItemId = $this->DecodeIdentifier($this->Args["identifier"]);
288  }
289  else
290  {
291  $ItemId = NULL;
292  }
293  if (isset($this->Args["metadataPrefix"]))
294  {
295  $MetadataFormat = $this->Args["metadataPrefix"];
296  }
297  else
298  {
299  $MetadataFormat = NULL;
300  }
301  if (($ItemId == NULL) || ($MetadataFormat == NULL) || !is_array($this->FieldMappings[$MetadataFormat]))
302  {
303  # add request info tag with no attributes
304  $Response .= $this->GetRequestTag("GetRecord");
305 
306  # add error tag
307  $Response .= $this->GetErrorTag("badArgument", "Bad argument found.");
308  }
309  else
310  {
311  # add request info tag
312  $ReqArgList = array("identifier", "metadataPrefix");
313  $Response .= $this->GetRequestTag("GetRecord", $ReqArgList);
314 
315  # attempt to load item corresponding to record
316  $Item = $this->ItemFactory->GetItem($ItemId);
317 
318  # if no item found
319  if ($Item == NULL)
320  {
321  # add error tag
322  $Response .= $this->GetErrorTag("idDoesNotExist", "No item found for specified ID.");
323  }
324  else
325  {
326  # open response type tag
327  $Response .= $this->FormatTag("GetRecord");
328 
329  # add tags for record
330  $Response .= $this->GetRecordTags($Item, $MetadataFormat);
331 
332  # close response type tag
333  $Response .= $this->FormatTag();
334  }
335  }
336 
337  # close out response
338  $Response .= $this->GetResponseEndTags();
339 
340  # return response to caller
341  return $Response;
342  }
343 
344  function ProcessListRecords($IncludeMetadata)
345  {
346  # set request type
347  if ($IncludeMetadata)
348  {
349  $Request = "ListRecords";
350  }
351  else
352  {
353  $Request = "ListIdentifiers";
354  }
355 
356  # initialize response
357  $Response = $this->GetResponseBeginTags();
358 
359  # if resumption token supplied
360  if (isset($this->Args["resumptionToken"]))
361  {
362  # set expected argument lists
363  $ReqArgList = array("resumptionToken");
364  $OptArgList = NULL;
365 
366  # parse into list parameters
367  $Args = $this->DecodeResumptionToken($this->Args["resumptionToken"]);
368  }
369  else
370  {
371  # set expected argument lists
372  $ReqArgList = array("metadataPrefix");
373  $OptArgList = array("from", "until", "set");
374 
375  # get list parameters from incoming arguments
376  $Args = $this->Args;
377 
378  # set list starting point to beginning
379  $Args["ListStartPoint"] = 0;
380  }
381 
382  # if resumption token was supplied and was bad
383  if ($Args == NULL)
384  {
385  # add request info tag
386  $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
387 
388  # add error tag indicating bad resumption token
389  $Response .= $this->GetErrorTag("badResumptionToken", "Bad resumption token.");
390 
391  # if other parameter also supplied
392  if (count($this->Args) > 2)
393  {
394  # add error tag indicating exclusive argument error
395  $Response .= $this->GetErrorTag("badArgument", "Resumption token is exclusive argument.");
396  }
397  }
398  # else if resumption token supplied and other arguments also supplied
399  elseif (isset($this->Args["resumptionToken"]) && (count($this->Args) > 2))
400  {
401  # add error tag indicating exclusive argument error
402  $Response .= $this->GetRequestTag();
403  $Response .= $this->GetErrorTag("badArgument", "Resumption token is exclusive argument.");
404  }
405  # else if metadata format was not specified
406  elseif (empty($Args["metadataPrefix"]))
407  {
408  # add request info tag with no attributes
409  $Response .= $this->GetRequestTag($Request);
410 
411  # add error tag indicating bad argument
412  $Response .= $this->GetErrorTag("badArgument", "No metadata format specified.");
413  }
414  # else if from or until date is specified but bad
415  elseif ((isset($Args["from"]) && $this->DateIsInvalid($Args["from"]))
416  || (isset($Args["until"]) && $this->DateIsInvalid($Args["until"])))
417  {
418  # add request info tag with no attributes
419  $Response .= $this->GetRequestTag($Request);
420 
421  # add error tag indicating bad argument
422  $Response .= $this->GetErrorTag("badArgument", "Bad date format.");
423  }
424  else
425  {
426  # add request info tag
427  $Response .= $this->GetRequestTag($Request, $ReqArgList, $OptArgList);
428 
429  # if set requested and we do not support sets
430  if (isset($Args["set"]) && ($this->SetsSupported != TRUE))
431  {
432  # add error tag indicating that we don't support sets
433  $Response .= $this->GetErrorTag("noSetHierarchy", "This repository does not support sets.");
434  }
435  # else if requested metadata format is not supported
436  elseif (empty($this->FormatDescrs[$Args["metadataPrefix"]]))
437  {
438  # add error tag indicating that format is not supported
439  $Response .= $this->GetErrorTag("cannotDisseminateFormat", "Metadata format \"".$Args["metadataPrefix"]."\" not supported by this repository.");
440  }
441  else
442  {
443  # if set requested
444  if (isset($Args["set"]))
445  {
446  # if OAI-SQ supported and set represents OAI-SQ query
447  if ($this->OaisqSupported && $this->IsOaisqQuery($Args["set"]))
448  {
449  # parse OAI-SQ search parameters out of set name
450  $SearchParams = $this->ParseOaisqQuery($Args["set"], $Args["metadataPrefix"]);
451 
452  # if search parameters found
453  if (count($SearchParams))
454  {
455  # perform search for items that match OAI-SQ request
456  $ItemIds = $this->ItemFactory->SearchForItems(
457  $SearchParams,
458  (isset($Args["from"]) ? $Args["from"] : NULL),
459  (isset($Args["until"]) ? $Args["until"] : NULL));
460  }
461  else
462  {
463  # no items match
464  $ItemIds = array();
465  }
466  }
467  else
468  {
469  # get list of items in set that matches incoming criteria
470  $ItemIds = $this->ItemFactory->GetItemsInSet(
471  $Args["set"],
472  (isset($Args["from"]) ? $Args["from"] : NULL),
473  (isset($Args["until"]) ? $Args["until"] : NULL));
474  }
475  }
476  else
477  {
478  # get list of items that matches incoming criteria
479  $ItemIds = $this->ItemFactory->GetItems(
480  (isset($Args["from"]) ? $Args["from"] : NULL),
481  (isset($Args["until"]) ? $Args["until"] : NULL));
482  }
483 
484  # if no items found
485  if (count($ItemIds) == 0)
486  {
487  # add error tag indicating that no records found that match spec
488  $Response .= $this->GetErrorTag("noRecordsMatch", "No records were found that match the specified parameters.");
489  }
490  else
491  {
492  # open response type tag
493  $Response .= $this->FormatTag($Request);
494 
495  # initialize count of processed items
496  $ListIndex = 0;
497 
498  # for each item
499  foreach ($ItemIds as $ItemId)
500  {
501  # if item is within range
502  if ($ListIndex >= $Args["ListStartPoint"])
503  {
504  # retrieve item
505  $Item = $this->ItemFactory->GetItem($ItemId);
506 
507  # add record for item
508  $Response .= $this->GetRecordTags($Item, $Args["metadataPrefix"], $IncludeMetadata);
509  }
510 
511  # increment count of processed items
512  $ListIndex++;
513 
514  # stop processing if we have processed max number of items in a pass
515  $MaxItemsPerPass = 20;
516  if (($ListIndex - $Args["ListStartPoint"]) >= $MaxItemsPerPass) { break; }
517  }
518 
519  # if items left unprocessed
520  if ($ListIndex < count($ItemIds))
521  {
522  # add resumption token tag
523  $Token = $this->EncodeResumptionToken((isset($Args["from"]) ? $Args["from"] : NULL),
524  (isset($Args["until"]) ? $Args["until"] : NULL),
525  (isset($Args["metadataPrefix"]) ? $Args["metadataPrefix"] : NULL),
526  (isset($Args["set"]) ? $Args["set"] : NULL),
527  $ListIndex);
528  $Response .= $this->FormatTag("resumptionToken", $Token);
529  }
530  else
531  {
532  # if we started with a resumption token tag
533  if (isset($this->Args["resumptionToken"]))
534  {
535  # add empty resumption token tag to indicate end of set
536  $Response .= $this->FormatTag("resumptionToken", "");
537  }
538  }
539 
540  # close response type tag
541  $Response .= $this->FormatTag();
542  }
543  }
544  }
545 
546  # close out response
547  $Response .= $this->GetResponseEndTags();
548 
549  # return response to caller
550  return $Response;
551  }
552 
554  {
555  # initialize response
556  $Response = $this->GetResponseBeginTags();
557 
558  # if arguments were bad
559  $Arg = isset($this->Args["identifier"]) ? $this->Args["identifier"] : NULL;
560  $ItemId = $this->DecodeIdentifier($Arg);
561  if (isset($this->Args["identifier"]) && ($ItemId == NULL))
562  {
563  # add error tag
564  $Response .= $this->GetRequestTag();
565  $Response .= $this->GetErrorTag("idDoesNotExist", "Identifier unknown or illegal.");
566  }
567  else
568  {
569  # add request info tag
570  $OptArgList = array("identifier");
571  $Response .= $this->GetRequestTag("ListMetadataFormats", NULL, $OptArgList);
572 
573  # open response type tag
574  $Response .= $this->FormatTag("ListMetadataFormats");
575 
576  # for each supported format
577  foreach ($this->FormatDescrs as $FormatName => $FormatDescr)
578  {
579  # open format tag
580  $Response .= $this->FormatTag("metadataFormat");
581 
582  # add tags describing format
583  $Response .= $this->FormatTag("metadataPrefix", $FormatName);
584  $Pieces = preg_split("/[\s]+/", $FormatDescr["SchemaLocation"]);
585  $Response .= $this->FormatTag("schema", $Pieces[1]);
586  $Response .= $this->FormatTag("metadataNamespace", $FormatDescr["NamespaceList"][$FormatName]);
587 
588  # close format tag
589  $Response .= $this->FormatTag();
590  }
591 
592  # close response type tag
593  $Response .= $this->FormatTag();
594  }
595 
596  # close out response
597  $Response .= $this->GetResponseEndTags();
598 
599  # return response to caller
600  return $Response;
601  }
602 
603  function ProcessListSets()
604  {
605  # initialize response
606  $Response = $this->GetResponseBeginTags();
607 
608  # add request info tag
609  $OptArgList = array("resumptionToken");
610  $Response .= $this->GetRequestTag("ListSets", NULL, $OptArgList);
611 
612  # retrieve list of supported sets
613  $SetList = $this->SetsSupported ? $this->ItemFactory->GetListOfSets() : array();
614 
615  # if sets not supported or we have no sets
616  if ((!$this->SetsSupported) || (!count($SetList) && !$this->OaisqSupported))
617  {
618  # add error tag indicating that we do not support sets
619  $Response .= $this->GetErrorTag("noSetHierarchy", "This repository does not support sets.");
620  }
621  else
622  {
623  # open response type tag
624  $Response .= $this->FormatTag("ListSets");
625 
626  # if OAI-SQ is enabled
627  if ($this->OaisqSupported)
628  {
629  # add OAI-SQ to list of sets
630  $SetList["OAI-SQ"] = "OAI-SQ";
631  $SetList["OAI-SQ-F"] = "OAI-SQ-F";
632  }
633 
634  # for each supported set
635  foreach ($SetList as $SetName => $SetSpec)
636  {
637  # open set tag
638  $Response .= $this->FormatTag("set");
639 
640  # add set spec and set name
641  $Response .= $this->FormatTag("setSpec", $SetSpec);
642  $Response .= $this->FormatTag("setName", $SetName);
643 
644  # close set tag
645  $Response .= $this->FormatTag();
646  }
647 
648  # close response type tag
649  $Response .= $this->FormatTag();
650  }
651 
652  # close out response
653  $Response .= $this->GetResponseEndTags();
654 
655  # return response to caller
656  return $Response;
657  }
658 
659 
660  # ---- common private methods
661 
663  {
664  # start with XML declaration
665  $Tags = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
666 
667  # add OAI-PMH root element begin tag
668  $Tags .= "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\"\n"
669  ." xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
670  ." xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/\n"
671  ." http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n";
672 
673  # add response timestamp
674  $Tags .= " <responseDate>".date("Y-m-d\\TH:i:s\\Z")."</responseDate>\n";
675 
676  # return tags to caller
677  return $Tags;
678  }
679 
681  {
682  # close out OAI-PMH root element
683  $Tags = "</OAI-PMH>\n";
684 
685  # return tags to caller
686  return $Tags;
687  }
688 
689  function GetRequestTag($RequestType = NULL, $ReqArgList = NULL, $OptArgList = NULL)
690  {
691  # build attribute array
692  $AttributeList = array();
693  if ($RequestType !== NULL)
694  {
695  $AttributeList["verb"] = $RequestType;
696  }
697  if ($ReqArgList != NULL)
698  {
699  foreach ($ReqArgList as $ArgName)
700  {
701  if (isset($this->Args[$ArgName]))
702  {
703  $AttributeList[$ArgName] = $this->Args[$ArgName];
704  }
705  }
706  }
707  if ($OptArgList != NULL)
708  {
709  foreach ($OptArgList as $ArgName)
710  {
711  if (isset($this->Args[$ArgName]))
712  {
713  $AttributeList[$ArgName] = $this->Args[$ArgName];
714  }
715  }
716  }
717 
718  # generate formatted tag
719  $Tag = $this->FormatTag("request",
720  $this->RepDescr["BaseURL"],
721  $AttributeList);
722 
723  # return tag to caller
724  return $Tag;
725  }
726 
727  function GetErrorTag($ErrorCode, $ErrorMessage)
728  {
729  return $this->FormatTag("error", $ErrorMessage, array("code" => $ErrorCode));
730  }
731 
732  function GetRecordTags($Item, $MetadataFormat, $IncludeMetadata = TRUE)
733  {
734  # if more than identifiers requested
735  if ($IncludeMetadata)
736  {
737  # open record tag
738  $Tags = $this->FormatTag("record");
739  }
740  else
741  {
742  # just initialize tag string with empty value
743  $Tags = "";
744  }
745 
746  # add header with identifier, datestamp, and set tags
747  $Tags .= $this->FormatTag("header");
748  $Tags .= $this->FormatTag("identifier",
749  $this->EncodeIdentifier($Item->GetId()));
750  $Tags .= $this->FormatTag("datestamp", $Item->GetDatestamp());
751  $Sets = $Item->GetSets();
752  foreach ($Sets as $Set)
753  {
754  $Tags .= $this->FormatTag("setSpec", $Set);
755  }
756  $Tags .= $this->FormatTag();
757 
758  # if more than identifiers requested
759  if ($IncludeMetadata)
760  {
761  # open metadata tag
762  $Tags .= $this->FormatTag("metadata");
763 
764  # set up attributes for metadata format tag
765  $MFAttribs["xsi:schemaLocation"] = $this->FormatDescrs[$MetadataFormat]["SchemaLocation"];
766  if (strlen($this->FormatDescrs[$MetadataFormat]["SchemaVersion"]) > 0)
767  {
768  $MFAttribs["schemaVersion"] = $this->FormatDescrs[$MetadataFormat]["SchemaVersion"];
769  }
770  $MFAttribs["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
771  foreach ($this->FormatDescrs[$MetadataFormat]["NamespaceList"] as $NamespaceName => $NamespaceURI)
772  {
773  $MFAttribs["xmlns:".$NamespaceName] = $NamespaceURI;
774  }
775 
776  # open metadata format tag
777  $Tags .= $this->FormatTag($this->FormatDescrs[$MetadataFormat]["TagName"], NULL, $MFAttribs);
778 
779  # for each field mapping for this metadata format
780  foreach ($this->FieldMappings[$MetadataFormat] as $LocalFieldName => $OAIFieldName)
781  {
782  # if field looks like it has been mapped
783  if (strlen($OAIFieldName) > 0)
784  {
785  # retrieve content for field
786  $Content = $Item->GetValue($LocalFieldName);
787 
788  # retrieve qualifiers for content
789  $Qualifier = $Item->GetQualifier($LocalFieldName);
790 
791  # if content is array
792  if (is_array($Content))
793  {
794  # for each element of array
795  foreach ($Content as $ContentIndex => $ContentValue)
796  {
797  # if element has content
798  if (strlen($ContentValue) > 0)
799  {
800  # generate tag for element
801  if (isset($Qualifier[$ContentIndex]) && strlen($Qualifier[$ContentIndex]))
802  {
803  if (isset($this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]])
804  && (strlen($this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]]) > 0))
805  {
806  $ContentAttribs["xsi:type"] = $this->QualifierMappings[$MetadataFormat][$Qualifier[$ContentIndex]];
807  }
808  }
809  else
810  {
811  $ContentAttribs = NULL;
812  }
813  $Tags .= $this->FormatTag($OAIFieldName,
814  utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $ContentValue))),
815  $ContentAttribs);
816  }
817  }
818  }
819  else
820  {
821  # if field has content
822  if (strlen($Content) > 0)
823  {
824  # generate tag for field
825  if (strlen($Qualifier) > 0)
826  {
827  if (isset($this->QualifierMappings[$MetadataFormat][$Qualifier])
828  && (strlen($this->QualifierMappings[$MetadataFormat][$Qualifier]) > 0))
829  {
830  $ContentAttribs["xsi:type"] = $this->QualifierMappings[$MetadataFormat][$Qualifier];
831  }
832  }
833  else
834  {
835  $ContentAttribs = NULL;
836  }
837  $Tags .= $this->FormatTag($OAIFieldName,
838  utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $Content))),
839  $ContentAttribs);
840  }
841  }
842  }
843  }
844 
845  # close metadata format tag
846  $Tags .= $this->FormatTag();
847 
848  # close metadata tag
849  $Tags .= $this->FormatTag();
850 
851  # if there is additional search info about this item
852  $SearchInfo = $Item->GetSearchInfo();
853  if (count($SearchInfo))
854  {
855  # open about and search info tags
856  $Tags .= $this->FormatTag("about");
857  $Attribs = array(
858  "xmlns" => "http://scout.wisc.edu/XML/searchInfo/",
859  "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
860  "xsi:schemaLocation" => "http://scout.wisc.edu/XML/searchInfo/ http://scout.wisc.edu/XML/searchInfo.xsd",
861  );
862  $Tags .= $this->FormatTag("searchInfo", NULL, $Attribs);
863 
864  # for each piece of additional info
865  foreach ($SearchInfo as $InfoName => $InfoValue)
866  {
867  # add tag for info
868  $Tags .= $this->FormatTag($InfoName,
869  utf8_encode(htmlspecialchars(preg_replace("/[\\x00-\\x1F]+/", "", $InfoValue))));
870  }
871 
872  # close about and search info tags
873  $Tags .= $this->FormatTag();
874  $Tags .= $this->FormatTag();
875  }
876  }
877 
878  # if more than identifiers requested
879  if ($IncludeMetadata)
880  {
881  # close record tag
882  $Tags .= $this->FormatTag();
883  }
884 
885  # return tags to caller
886  return $Tags;
887  }
888 
889  function EncodeIdentifier($ItemId)
890  {
891  # return encoded value to caller
892  return "oai:".$this->RepDescr["IDDomain"]
893  .":".$this->RepDescr["IDPrefix"]."-".$ItemId;
894  }
895 
896  function DecodeIdentifier($Identifier)
897  {
898  # assume that decode will fail
899  $Id = NULL;
900 
901  # split ID into component pieces
902  $Pieces = split(":", $Identifier);
903 
904  # if pieces look okay
905  if (($Pieces[0] == "oai") && ($Pieces[1] == $this->RepDescr["IDDomain"]))
906  {
907  # split final piece
908  $Pieces = split("-", $Pieces[2]);
909 
910  # if identifier prefix looks okay
911  if ($Pieces[0] == $this->RepDescr["IDPrefix"])
912  {
913  # decoded value is final piece
914  $Id = $Pieces[1];
915  }
916  }
917 
918  # return decoded value to caller
919  return $Id;
920  }
921 
922  function EncodeResumptionToken($StartingDate, $EndingDate, $MetadataFormat, $SetSpec, $ListStartPoint)
923  {
924  # concatenate values to create token
925  $Token = $StartingDate."-_-".$EndingDate."-_-".$MetadataFormat."-_-"
926  .$SetSpec."-_-".$ListStartPoint;
927 
928  # return token to caller
929  return $Token;
930  }
931 
932  function DecodeResumptionToken($ResumptionToken)
933  {
934  # split into component pieces
935  $Pieces = preg_split("/-_-/", $ResumptionToken);
936 
937  # if we were unable to split token
938  if (count($Pieces) != 5)
939  {
940  # return NULL list
941  $Args = NULL;
942  }
943  else
944  {
945  # assign component pieces to list parameters
946  if (strlen($Pieces[0]) > 0) { $Args["from"] = $Pieces[0]; }
947  if (strlen($Pieces[1]) > 0) { $Args["until"] = $Pieces[1]; }
948  if (strlen($Pieces[2]) > 0) { $Args["metadataPrefix"] = $Pieces[2]; }
949  if (strlen($Pieces[3]) > 0) { $Args["set"] = $Pieces[3]; }
950  if (strlen($Pieces[4]) > 0) { $Args["ListStartPoint"] = $Pieces[4]; }
951  }
952 
953  # return list parameter array to caller
954  return $Args;
955  }
956 
957  function DateIsInvalid($Date)
958  {
959  # if date is null or matches required format
960  if (empty($Date) || preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/", $Date))
961  {
962  # date is okay
963  return FALSE;
964  }
965  else
966  {
967  # date is not okay
968  return TRUE;
969  }
970  }
971 
972  function FormatTag($Name = NULL, $Content = NULL, $Attributes = NULL, $NewIndentLevel = NULL)
973  {
974  static $IndentLevel = 1;
975  static $OpenTagStack = array();
976 
977  # reset indent level if requested
978  if ($NewIndentLevel !== NULL)
979  {
980  $IndentLevel = $NewIndentLevel;
981  }
982 
983  # if tag name supplied
984  if ($Name !== NULL)
985  {
986  # start out with appropriate indent
987  $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
988 
989  # open begin tag
990  $Tag .= "<".$Name;
991 
992  # if attributes supplied
993  if ($Attributes !== NULL)
994  {
995  # add attributes
996  foreach ($Attributes as $AttributeName => $AttributeValue)
997  {
998  $Tag .= " ".$AttributeName."=\"".$AttributeValue."\"";
999  }
1000  }
1001 
1002  # if content supplied
1003  if ($Content !== NULL)
1004  {
1005  # close begin tag
1006  $Tag .= ">";
1007 
1008  # add content
1009  $Tag .= $Content;
1010 
1011  # add end tag
1012  $Tag .= "</".$Name.">\n";
1013  }
1014  else
1015  {
1016  # close begin tag
1017  $Tag .= ">\n";
1018 
1019  # increase indent level
1020  $IndentLevel++;
1021 
1022  # add tag to open tag stack
1023  array_push($OpenTagStack, $Name);
1024  }
1025  }
1026  else
1027  {
1028  # decrease indent level
1029  if ($IndentLevel > 0) { $IndentLevel--; }
1030 
1031  # pop last entry off of open tag stack
1032  $LastName = array_pop($OpenTagStack);
1033 
1034  # start out with appropriate indent
1035  $Tag = str_repeat(" ", ($IndentLevel * $this->IndentSize));
1036 
1037  # add end tag to match last open tag
1038  $Tag .= "</".$LastName.">\n";
1039  }
1040 
1041  # return formatted tag to caller
1042  return $Tag;
1043  }
1044 
1045  function LoadArguments()
1046  {
1047  # if request type available via POST variables
1048  if (isset($_POST["verb"]))
1049  {
1050  # retrieve arguments from POST variables
1051  $this->Args = $_POST;
1052  }
1053  # else if request type available via GET variables
1054  elseif (isset($_GET["verb"]))
1055  {
1056  # retrieve arguments from GET variables
1057  $this->Args = $_GET;
1058 
1059  # strip off page designator GET parameter
1060  if (isset($this->Args["P"])) { unset($this->Args["P"]); }
1061  }
1062  else
1063  {
1064  # ERROR OUT
1065  # ???
1066  }
1067  }
1068 
1069  # ---- methods to support OAI-SQ
1070 
1071  function IsOaisqQuery($SetString)
1072  {
1073  return ((strpos($SetString, "OAI-SQ|") === 0)
1074  || (strpos($SetString, "OAI-SQ!") === 0)
1075  || (strpos($SetString, "OAI-SQ-F|") === 0)
1076  || (strpos($SetString, "OAI-SQ-F!") === 0)
1077  ) ? TRUE : FALSE;
1078  }
1079 
1080  function TranslateOaisqEscapes($Pieces)
1081  {
1082  # for each piece
1083  for ($Index = 0; $Index < count($Pieces); $Index++)
1084  {
1085  # replace escaped chars with equivalents
1086  $Pieces[$Index] = preg_replace_callback(
1087  "/~[a-fA-F0-9]{2,2}/",
1088  create_function(
1089  '$Matches',
1090  'for ($Index = 0; $Index < count($Matches); $Index++)'
1091  .'{'
1092  .' $Replacements = chr(intval(substr($Matches[$Index], 1, 2), 16));'
1093  .'}'
1094  .'return $Replacements;'
1095  ),
1096  $Pieces[$Index]);
1097  }
1098 
1099  # return translated array of pieces to caller
1100  return $Pieces;
1101  }
1102 
1103  function ParseOaisqQuery($SetString, $FormatName)
1104  {
1105  # if OAI-SQ fielded search requested
1106  if (strpos($SetString, "OAI-SQ-F") === 0)
1107  {
1108  # split set string into field names and values
1109  $Pieces = explode(substr($SetString, 8, 1), $SetString);
1110 
1111  # discard first piece (OAI-SQ designator)
1112  array_shift($Pieces);
1113 
1114  # if set string contains escaped characters
1115  if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
1116  {
1117  $Pieces = $this->TranslateOaisqEscapes($Pieces);
1118  }
1119 
1120  # for every two pieces
1121  $SearchParams = array();
1122  $NumPairedPieces = round(count($Pieces) / 2) * 2;
1123  for ($Index = 0; $Index < $NumPairedPieces; $Index += 2)
1124  {
1125  # retrieve local field mapping
1126  $LocalFieldName = array_search($Pieces[$Index], $this->FieldMappings[$FormatName]);
1127 
1128  # if local field mapping found
1129  if (strlen($LocalFieldName))
1130  {
1131  # add mapped values to search parameters
1132  $SearchParams[$LocalFieldName] = $Pieces[$Index + 1];
1133  }
1134  }
1135  }
1136  else
1137  {
1138  # split set string to trim off query designator
1139  $Pieces = explode(substr($SetString, 6, 1), $SetString, 2);
1140 
1141  # if set string contains escaped characters
1142  if (preg_match("/~[a-fA-F0-9]{2,2}/", $SetString))
1143  {
1144  $Pieces = $this->TranslateOaisqEscapes($Pieces);
1145  }
1146 
1147  # remainder of set string is keyword search string
1148  $SearchParams["X-KEYWORD-X"] = $Pieces[1];
1149  }
1150 
1151  # return array of search parameters to caller
1152  return $SearchParams;
1153  }
1154 }
1155 
1157 
1158  # ---- PUBLIC INTERFACE --------------------------------------------------
1159 
1160  # object constructor
1161  function OAIItemFactory()
1162  {
1163  }
1164 
1165  function GetItem($ItemId) { exit("OAIItemFactory method GetItem() not implemented"); }
1166  function GetItems($StartingDate = NULL, $EndingDate = NULL)
1167  {
1168  exit("OAIItemFactory method GetItems() not implemented");
1169  }
1170 
1171  # retrieve IDs of items that matches set spec (only needed if sets supported)
1172  function GetItemsInSet($SetSpec, $StartingDate = NULL, $EndingDate = NULL)
1173  {
1174  exit("OAIItemFactory method GetItemsInSet() not implemented");
1175  }
1176 
1177  # return array containing all set specs (with human-readable set names as keys)
1178  # (only needed if sets supported)
1179  function GetListOfSets()
1180  {
1181  exit("OAIItemFactory method GetListOfSets() not implemented");
1182  }
1183 
1184  # retrieve IDs of items that match search parameters (only needed if OAI-SQ supported)
1185  function SearchForItems($SearchParams, $StartingDate = NULL, $EndingDate = NULL)
1186  {
1187  exit("OAIItemFactory method SearchForItems() not implemented");
1188  }
1189 }
1190 
1191 class OAIItem {
1192 
1193  # ---- PUBLIC INTERFACE --------------------------------------------------
1194 
1195  # object constructor
1196  function OAIItem($ItemId, $ExtraItemInfo = NULL)
1197  {
1198  }
1199 
1200  function GetId() { exit("OAIItem method GetId() not implemented"); }
1201  function GetDatestamp() { exit("OAIItem method GetDatestamp() not implemented"); }
1202  function GetValue($ElementName) { exit("OAIItem method GetValue() not implemented"); }
1203  function GetQualifier($ElementName) { exit("OAIItem method GetQualifiers() not implemented"); }
1204  function GetSets() { exit("OAIItem method GetSets() not implemented"); }
1205  function GetSearchInfo() { return array(); }
1206  function Status() { exit("OAIItem method Status() not implemented"); }
1207 }
1208 
1209 
1210 ?>
$QualifierMappings
Definition: OAIServer.php:221
GetResponseEndTags()
Definition: OAIServer.php:680
GetQualifier($ElementName)
Definition: OAIServer.php:1203
FormatElementList($FormatName)
Definition: OAIServer.php:122
FormatQualifierList($FormatName)
Definition: OAIServer.php:128
GetValue($ElementName)
Definition: OAIServer.php:1202
OAIServer(&$DB, $RepDescr, &$ItemFactory, $SetsSupported=FALSE, $OaisqSupported=FALSE)
Definition: OAIServer.php:21
GetRecordTags($Item, $MetadataFormat, $IncludeMetadata=TRUE)
Definition: OAIServer.php:732
ProcessListSets()
Definition: OAIServer.php:603
ParseOaisqQuery($SetString, $FormatName)
Definition: OAIServer.php:1103
ProcessListMetadataFormats()
Definition: OAIServer.php:553
SearchForItems($SearchParams, $StartingDate=NULL, $EndingDate=NULL)
Definition: OAIServer.php:1185
AddFormat($Name, $TagName, $SchemaLocation, $SchemaVersion, $NamespaceList, $ElementList, $QualifierList)
Definition: OAIServer.php:80
GetItem($ItemId)
Retrieve item by item ID.
GetFieldMapping($FormatName, $LocalFieldName)
Definition: OAIServer.php:134
GetDatestamp()
Definition: OAIServer.php:1201
GetResponseBeginTags()
Definition: OAIServer.php:662
EncodeIdentifier($ItemId)
Definition: OAIServer.php:889
GetRequestTag($RequestType=NULL, $ReqArgList=NULL, $OptArgList=NULL)
Definition: OAIServer.php:689
DecodeResumptionToken($ResumptionToken)
Definition: OAIServer.php:932
ProcessGetRecord()
Definition: OAIServer.php:279
GetItems($StartingDate=NULL, $EndingDate=NULL)
Definition: OAIServer.php:1166
OAIItem($ItemId, $ExtraItemInfo=NULL)
Definition: OAIServer.php:1196
GetQualifierMapping($FormatName, $LocalQualifierName)
Definition: OAIServer.php:152
DateIsInvalid($Date)
Definition: OAIServer.php:957
GetErrorTag($ErrorCode, $ErrorMessage)
Definition: OAIServer.php:727
SetQualifierMapping($FormatName, $LocalQualifierName, $OAIQualifierName)
Definition: OAIServer.php:164
ProcessListRecords($IncludeMetadata)
Definition: OAIServer.php:344
FormatTag($Name=NULL, $Content=NULL, $Attributes=NULL, $NewIndentLevel=NULL)
Definition: OAIServer.php:972
IsOaisqQuery($SetString)
Definition: OAIServer.php:1071
GetItems($SqlCondition=NULL)
Retrieve items.
GetSearchInfo()
Definition: OAIServer.php:1205
GetItem($ItemId)
Definition: OAIServer.php:1165
Common factory class for item manipulation.
Definition: ItemFactory.php:17
EncodeResumptionToken($StartingDate, $EndingDate, $MetadataFormat, $SetSpec, $ListStartPoint)
Definition: OAIServer.php:922
SetFieldMapping($FormatName, $LocalFieldName, $OAIFieldName)
Definition: OAIServer.php:146
DecodeIdentifier($Identifier)
Definition: OAIServer.php:896
TranslateOaisqEscapes($Pieces)
Definition: OAIServer.php:1080
GetItemsInSet($SetSpec, $StartingDate=NULL, $EndingDate=NULL)
Definition: OAIServer.php:1172
ProcessIdentify()
Definition: OAIServer.php:229