3 # FILE: MetadataSchema.php
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2011 Edward Almasy and Internet Scout
7 # http://scout.wisc.edu
12 # ---- PUBLIC INTERFACE --------------------------------------------------
14 # types of field ordering
19 # metadata field types
20 # (must parallel MetadataFields.FieldType declaration in install/CreateTables.sql
21 # and MetadataField::$FieldTypeDBEnums declaration below)
55 # set up item factory base class
57 "MetadataField",
"MetadataFields",
"FieldId",
"FieldName");
59 # start with field info caching enabled
60 $this->CachingOn = TRUE;
63 # turn internal caching of field info on or off
66 $this->CachingOn = $NewValue;
69 # add new metadata field
70 function AddField($FieldName, $FieldType, $Optional = TRUE, $DefaultValue = NULL)
73 $Field =
new MetadataField(NULL, $FieldName, $FieldType, $Optional, $DefaultValue);
75 # save error code if create failed and return NULL
78 $this->ErrorStatus = $Field->Status();
82 # return new field to caller
97 # assume field addition will fail
98 $Field = self::MDFSTAT_ERROR;
100 # add XML prefixes if needed
102 if (!preg_match(
"/^<\?xml/i", $Xml))
104 if (!preg_match(
"/^<document>/i", $Xml))
106 $Xml =
"<document>".$Xml.
"</document>";
108 $Xml =
"<?xml version='1.0'?>".$Xml;
112 $XmlData = simplexml_load_string($Xml);
114 # if required values are present
115 if (is_object($XmlData)
116 && isset($XmlData->Name)
117 && isset($XmlData->Type)
118 && constant(
"MetadataSchema::".$XmlData->Type))
120 # create the metadata field
122 constant(
"MetadataSchema::".$XmlData->Type));
124 # if field creation failed
125 if ($Field->Status() !== self::MDFSTAT_OK)
127 # reset field value to error code
128 $Field = $Field->Status();
132 # for other field attributes
133 foreach ($XmlData as $MethodName => $Value)
135 # if they look valid and have not already been set
136 if (method_exists($Field, $MethodName)
137 && ($MethodName !=
"Name")
138 && ($MethodName !=
"Type"))
140 # condense down any extraneous whitespace
141 $Value = preg_replace(
"/\s+/",
" ", trim($Value));
143 # set value for field
144 $Field->$MethodName($Value);
148 # make new field permanent
149 $Field->IsTempItem(FALSE);
153 # return new field (if any) to caller
157 # delete metadata field
164 # retrieve field by ID
169 # if caching is off or field is already loaded
170 if (($this->CachingOn != TRUE) || !isset($Fields[
$FieldId]))
176 # return field to caller
213 static $FieldIdsByName;
215 # if caching is off or field ID is already loaded
216 if (($this->CachingOn != TRUE) || !isset($FieldIdsByName[$FieldName]))
218 # retrieve field ID from DB
219 $Condition = $IgnoreCase
220 ?
"WHERE LOWER(FieldName) = '".addslashes(strtolower($FieldName)).
"'"
221 :
"WHERE FieldName = '".addslashes($FieldName).
"'";
222 $FieldIdsByName[$FieldName] = $this->DB->Query(
223 "SELECT FieldId FROM MetadataFields ".$Condition,
"FieldId");
226 return $FieldIdsByName[$FieldName];
238 static $FieldIdsByLabel;
240 # if caching is off or field ID is already loaded
241 if (($this->CachingOn != TRUE) || !isset($FieldIdsByLabel[$FieldLabel]))
243 # retrieve field ID from DB
244 $Condition = $IgnoreCase
245 ?
"WHERE LOWER(Label) = '".addslashes(strtolower($FieldLabel)).
"'"
246 :
"WHERE Label = '".addslashes($FieldLabel).
"'";
247 $FieldIdsByLabel[$FieldLabel] = $this->DB->Query(
248 "SELECT FieldId FROM MetadataFields ".$Condition,
"FieldId");
251 return $FieldIdsByLabel[$FieldLabel];
254 # check whether field with specified name exists
257 # retrieve array of fields
258 function GetFields($FieldTypes = NULL, $OrderType = NULL,
259 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
261 # create empty array to pass back
264 # for each field type in database
265 if ($IncludeTempFields && $IncludeDisabledFields)
267 $this->DB->Query(
"SELECT FieldId, FieldType FROM MetadataFields");
271 if ($IncludeTempFields)
273 $this->DB->Query(
"SELECT FieldId, FieldType FROM MetadataFields WHERE Enabled != 0");
275 elseif ($IncludeDisabledFields)
277 $this->DB->Query(
"SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0");
281 $this->DB->Query(
"SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0 AND Enabled != 0");
284 while ($Record = $this->DB->FetchRow())
286 # if no specific type requested or if field is of requested type
287 if (($FieldTypes == NULL)
290 # create field object and add to array to be passed back
291 $Fields[$Record[
"FieldId"]] = $this->
GetField($Record[
"FieldId"]);
295 # if field sorting requested
296 if ($OrderType !== NULL)
298 # update field comparison ordering if not set yet
299 if (!self::FieldCompareOrdersSet())
301 self::UpdateFieldCompareOrders();
304 $this->FieldCompareType = $OrderType;
305 $this->FieldOrderError = FALSE;
307 # determine whether to use the old ordering API or not
308 $Callback = self::$UseOldOrderingApi
309 ?
"CompareFieldOrderDeprecated" :
"CompareFieldOrder";
311 # sort field array by requested order type
312 uasort($Fields, array($this, $Callback));
314 # if field order error detected
315 if ($this->FieldOrderError)
317 # repair (reset) field order
319 foreach ($Fields as $Field)
321 $Field->OrderPosition($OrderType, $OrderIndex);
327 # return array of field objects to caller
332 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
337 $Fields = $this->
GetFields($FieldTypes, $OrderType, $IncludeDisabledFields, $IncludeTempFields);
339 foreach($Fields as $Field)
341 $DB->Query(
"SELECT FieldName FROM MetadataFields WHERE FieldId=".$Field->Id());
342 $FieldNames[ $Field->Id() ] = $DB->FetchField(
"FieldName");
364 $SelectedFieldId = NULL, $IncludeNullOption = TRUE,
365 $AddEntries = NULL, $AllowMultiple = FALSE)
367 # retrieve requested fields
370 # transform field names to labels
371 foreach ($FieldNames as
$FieldId => $FieldName)
376 # begin HTML option list
377 $Html =
"<select id=\"".$OptionListName.
"\" name=\"".$OptionListName.
"\"";
379 # if multiple selections should be allowed
382 $Html .=
" multiple=\"multiple\"";
387 if ($IncludeNullOption)
389 $Html .=
"<option value=\"\">--</option>\n";
392 # make checking for IDs simpler
393 if (!is_array($SelectedFieldId))
395 $SelectedFieldId = array($SelectedFieldId);
398 # for each metadata field
399 foreach ($FieldNames as $Id => $Name)
401 # add entry for field to option list
402 $Html .=
"<option value=\"".$Id.
"\"";
403 if (in_array($Id, $SelectedFieldId)) { $Html .=
" selected"; }
404 $Html .=
">".htmlspecialchars($Name).
"</option>\n";
407 # if additional entries were requested
410 foreach ($AddEntries as $Value => $Label)
412 $Html .=
"<option value=\"".$Value.
"\"";
413 if (in_array($Value,$SelectedFieldId)) { $Html .=
" selected"; }
414 $Html .=
">".htmlspecialchars($Label).
"</option>\n";
418 # end HTML option list
419 $Html .=
"</select>\n";
421 # return constructed HTML to caller
425 # retrieve array of field types (enumerated type => field name)
431 # retrieve array of field types that user can create (enumerated type => field name)
437 # remove all metadata field associations for a given qualifier
440 # sanitize qualifier ID or grab it from object
441 $QualifierIdOrObject = is_object($QualifierIdOrObject)
442 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
444 # delete intersection records from database
445 $this->DB->Query(
"DELETE FROM FieldQualifierInts WHERE QualifierId = "
446 .$QualifierIdOrObject);
449 # return whether qualifier is in use by metadata field
452 # sanitize qualifier ID or grab it from object
453 $QualifierIdOrObject = is_object($QualifierIdOrObject)
454 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
456 # determine whether any fields use qualifier as default
457 $DefaultCount = $this->DB->Query(
"SELECT COUNT(*) AS RecordCount FROM MetadataFields"
458 .
" WHERE DefaultQualifier = ".$QualifierIdOrObject,
461 # determine whether any fields are associated with qualifier
462 $AssociationCount = $this->DB->Query(
"SELECT COUNT(*) AS RecordCount FROM FieldQualifierInts"
463 .
" WHERE QualifierId = ".$QualifierIdOrObject,
466 # report whether qualifier is in use based on defaults and associations
467 return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
470 # return highest field ID currently in use
484 self::$FieldMappings[$MappedName] =
$FieldId;
486 return isset(self::$FieldMappings[$MappedName])
487 ? self::$FieldMappings[$MappedName] : NULL;
500 foreach (self::$FieldMappings as $MappedName => $MappedFieldId)
533 SELECT * FROM MetadataFields
534 WHERE Owner IS NOT NULL AND LENGTH(Owner) > 0");
536 while (FALSE !== ($Row = $this->DB->FetchRow()))
552 if (is_callable($Callback))
554 self::$OwnerListRetrievalFunction = $Callback;
565 # if an owner list retrieval function exists
566 if (self::$OwnerListRetrievalFunction)
568 # retrieve the list of owners that currently exist
569 $OwnerList = call_user_func(self::$OwnerListRetrievalFunction);
571 # an array is expected
572 if (is_array($OwnerList))
576 # get each metadata field that is owned by a plugin
577 $OwnedFields = $Schema->GetOwnedFields();
579 # loop through each owned field
580 foreach ($OwnedFields as $OwnedField)
582 # the owner of the current field
583 $Owner = $OwnedField->Owner();
585 # if the owner of the field is in the list of owners that
586 # currently exist, i.e., available plugins
587 if (in_array($Owner, $OwnerList))
589 # enable the field and reset its "enable on owner return"
590 # flag if the "enable on owner return" flag is currently
591 # set to true. in other words, re-enable the field since
592 # the owner has returned to the list of existing owners
593 if ($OwnedField->EnableOnOwnerReturn())
595 $OwnedField->Enabled(TRUE);
596 $OwnedField->EnableOnOwnerReturn(FALSE);
600 # if the owner of the field is *not* in the list of owners
601 # that currently exist, i.e., available plugins
604 # first, see if the field is currently enabled since it
605 # will determine whether the field is re-enabled when
606 # the owner becomes available again
607 $Enabled = $OwnedField->Enabled();
609 # if the field is enabled, set its "enable on owner
610 # return" flag to true and disable the field. nothing
611 # needs to be done if the field is already disabled
614 $OwnedField->EnableOnOwnerReturn($Enabled);
615 $OwnedField->Enabled(FALSE);
637 foreach ($DisplayOrder->GetFields() as $Field)
639 self::$FieldCompareDisplayOrder[$Field->Id()] = $Index++;
644 foreach ($EditOrder->GetFields() as $Field)
646 self::$FieldCompareEditOrder[$Field->Id()] = $Index++;
650 catch (Exception $Exception)
652 # there was an error, so make no assumptions about the order
653 self::$FieldCompareDisplayOrder = array();
654 self::$FieldCompareEditOrder = array();
664 return self::$FieldCompareDisplayOrder && self::$FieldCompareEditOrder;
678 return ($FieldA->GetDisplayName() < $FieldB->GetDisplayName()) ? -1 : 1;
683 $Order = self::$FieldCompareEditOrder;
688 $Order = self::$FieldCompareDisplayOrder;
691 $PositionA = GetArrayValue($Order, $FieldA->Id(), 0);
692 $PositionB = GetArrayValue($Order, $FieldB->Id(), 0);
694 return $PositionA < $PositionB ? -1 : 1;
697 # ---- PRIVATE INTERFACE -------------------------------------------------
699 private $FieldCompareType;
701 private static $FieldMappings;
714 # ---- DEPRECATED --------------------------------------------------------
716 private $FieldOrderError;
728 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, FALSE);
741 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, TRUE);
756 return ($FieldA->GetDisplayName() < $FieldB->GetDisplayName()) ? -1 : 1;
760 if ($FieldA->OrderPosition($this->FieldCompareType)
761 == $FieldB->OrderPosition($this->FieldCompareType))
763 $this->FieldOrderError = TRUE;
768 return ($FieldA->OrderPosition($this->FieldCompareType)
769 < $FieldB->OrderPosition($this->FieldCompareType)) ? -1 : 1;
781 private function MoveFieldInOrder($FieldIdOrObj, $OrderType, $MoveFieldDown)
784 $FieldId = is_object($FieldIdOrObj) ? $Field->Id() : $FieldIdOrObj;
786 # retrieve array of fields
787 $Fields = $this->
GetFields(NULL, $OrderType);
789 # reverse array of fields if we are moving field down
792 $Fields = array_reverse($Fields);
795 # for each field in order
796 $PreviousField = NULL;
797 foreach ($Fields as $Field)
799 # if field is the field to be moved
802 # if we have a previous field
803 if ($PreviousField !== NULL)
805 # swap field with previous field according to order type
806 $TempVal = $Field->OrderPosition($OrderType);
807 $Field->OrderPosition($OrderType, $PreviousField->OrderPosition($OrderType));
808 $PreviousField->OrderPosition($OrderType, $TempVal);
812 # save field for next iteration
813 $PreviousField = $Field;