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 #
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
21 function OAIServer(&$DB, $RepDescr, &$ItemFactory, $SetsSupported = FALSE, $OaisqSupported = FALSE)
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;
216 var $RepDescr;
217 var $ItemFactory;
218 var $FormatDescrs;
219 var $FormatFields;
220 var $FieldMappings;
221 var $QualifierMappings;
222 var $IndentSize;
223 var $SetsSupported;
224 var $OaisqSupported;
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
553 function ProcessListMetadataFormats()
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
662 function GetResponseBeginTags()
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
680 function GetResponseEndTags()
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
1156 class OAIItemFactory {
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 ?>