00001 <?PHP
00002
00003 #
00004 # FILE: MetadataSchema.php
00005 #
00006 # Copyright 2002-2010 Edward Almasy and Internet Scout
00007 # http://scout.wisc.edu
00008 #
00009
00010 class MetadataSchema extends ItemFactory {
00011
00012 # ---- PUBLIC INTERFACE --------------------------------------------------
00013
00014 # types of field ordering
00015 const MDFORDER_DISPLAY = 1;
00016 const MDFORDER_EDITING = 2;
00017 const MDFORDER_ALPHABETICAL = 3;
00018
00019 # metadata field types
00020 # (must parallel MetadataFields.FieldType declaration in install/CreateTables.sql
00021 # and MetadataField::$FieldTypeDBEnums declaration below)
00022 const MDFTYPE_TEXT = 1;
00023 const MDFTYPE_PARAGRAPH = 2;
00024 const MDFTYPE_NUMBER = 4;
00025 const MDFTYPE_DATE = 8;
00026 const MDFTYPE_TIMESTAMP = 16;
00027 const MDFTYPE_FLAG = 32;
00028 const MDFTYPE_TREE = 64;
00029 const MDFTYPE_CONTROLLEDNAME = 128;
00030 const MDFTYPE_OPTION = 256;
00031 const MDFTYPE_USER = 512;
00032 const MDFTYPE_IMAGE = 1024;
00033 const MDFTYPE_FILE = 2048;
00034 const MDFTYPE_URL = 4096;
00035 const MDFTYPE_POINT = 8192;
00036
00037 # error status codes
00038 const MDFSTAT_OK = 1;
00039 const MDFSTAT_DUPLICATENAME = 2;
00040 const MDFSTAT_DUPLICATEDBCOLUMN = 4;
00041 const MDFSTAT_ILLEGALNAME = 8;
00042 const MDFSTAT_FIELDDOESNOTEXIST = 16;
00043
00044 # object constructor
00045 function MetadataSchema()
00046 {
00047 # set up item factory base class
00048 $this->ItemFactory(
00049 "MetadataField", "MetadataFields", "FieldId", "FieldName");
00050
00051 # start with field info caching enabled
00052 $this->CachingOn = TRUE;
00053 }
00054
00055 # turn internal caching of field info on or off
00056 function CacheData($NewValue)
00057 {
00058 $this->CachingOn = $NewValue;
00059 }
00060
00061 # add new metadata field
00062 function AddField($FieldName, $FieldType, $Optional = TRUE, $DefaultValue = NULL)
00063 {
00064 # create new field
00065 $Field = new MetadataField(NULL, $FieldName, $FieldType, $Optional, $DefaultValue);
00066
00067 # save error code if create failed and return NULL
00068 if ($Field->Status() != MetadataSchema::MDFSTAT_OK)
00069 {
00070 $this->ErrorStatus = $Field->Status();
00071 $Field = NULL;
00072 }
00073
00074 # return new field to caller
00075 return $Field;
00076 }
00077
00078 # delete metadata field
00079 function DropField($FieldId)
00080 {
00081 $Field = new MetadataField($FieldId);
00082 $Field->Drop();
00083 }
00084
00085 # retrieve field by ID
00086 function GetField($FieldId)
00087 {
00088 static $Fields;
00089
00090 # if caching is off or field is already loaded
00091 if (($this->CachingOn != TRUE) || !isset($Fields[$FieldId]))
00092 {
00093 # retrieve field
00094 $Fields[$FieldId] = new MetadataField($FieldId);
00095 }
00096
00097 # return field to caller
00098 return $Fields[$FieldId];
00099 }
00100
00107 function GetFieldByName($FieldName, $IgnoreCase = FALSE)
00108 {
00109 $FieldId = $this->GetFieldIdByName($FieldName, $IgnoreCase);
00110 return ($FieldId === NULL) ? NULL : $this->GetField($FieldId);
00111 }
00112
00120 function GetFieldIdByName($FieldName, $IgnoreCase = FALSE)
00121 {
00122 static $FieldIdsByName;
00123
00124 # if caching is off or field ID is already loaded
00125 if (($this->CachingOn != TRUE) || !isset($FieldIdsByName[$FieldName]))
00126 {
00127 # retrieve field ID from DB
00128 $Condition = $IgnoreCase
00129 ? "WHERE LOWER(FieldName) = '".addslashes(strtolower($FieldName))."'"
00130 : "WHERE FieldName = '".addslashes($FieldName)."'";
00131 $FieldIdsByName[$FieldName] = $this->DB->Query(
00132 "SELECT FieldId FROM MetadataFields ".$Condition, "FieldId");
00133 }
00134
00135 return $FieldIdsByName[$FieldName];
00136 }
00137
00138 # check whether field with specified name exists
00139 function FieldExists($FieldName) { return $this->NameIsInUse($FieldName); }
00140
00141 # retrieve array of fields
00142 function GetFields($FieldTypes = NULL, $OrderType = NULL,
00143 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
00144 {
00145 # create empty array to pass back
00146 $Fields = array();
00147
00148 # for each field type in database
00149 if ($IncludeTempFields && $IncludeDisabledFields)
00150 {
00151 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields");
00152 }
00153 else
00154 {
00155 if ($IncludeTempFields)
00156 {
00157 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE Enabled != 0");
00158 }
00159 elseif ($IncludeDisabledFields)
00160 {
00161 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0");
00162 }
00163 else
00164 {
00165 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0 AND Enabled != 0");
00166 }
00167 }
00168 while ($Record = $this->DB->FetchRow())
00169 {
00170 # if no specific type requested or if field is of requested type
00171 if (($FieldTypes == NULL)
00172 || (MetadataField::$FieldTypePHPEnums[$Record["FieldType"]] & $FieldTypes))
00173 {
00174 # create field object and add to array to be passed back
00175 $Fields[$Record["FieldId"]] = new MetadataField($Record["FieldId"]);
00176 }
00177 }
00178
00179 # if field sorting requested
00180 if ($OrderType !== NULL)
00181 {
00182 # sort field array by requested order type
00183 $this->FieldCompareType = $OrderType;
00184 $this->FieldOrderError = FALSE;
00185 uasort($Fields, array($this, "CompareFieldOrder"));
00186
00187 # if field order error detected
00188 if ($this->FieldOrderError)
00189 {
00190 # repair (reset) field order
00191 $OrderIndex = 1;
00192 foreach ($Fields as $Field)
00193 {
00194 $Field->OrderPosition($OrderType, $OrderIndex);
00195 $OrderIndex++;
00196 }
00197 }
00198 }
00199
00200 # return array of field objects to caller
00201 return $Fields;
00202 }
00203
00204 # callback function for sorting fields
00205 function CompareFieldOrder($FieldA, $FieldB)
00206 {
00207 if ($this->FieldCompareType == MetadataSchema::MDFORDER_ALPHABETICAL)
00208 {
00209 return ($FieldA->Name() < $FieldB->Name()) ? -1 : 1;
00210 }
00211 else
00212 {
00213 if ($FieldA->OrderPosition($this->FieldCompareType)
00214 == $FieldB->OrderPosition($this->FieldCompareType))
00215 {
00216 $this->FieldOrderError = TRUE;
00217 return 0;
00218 }
00219 else
00220 {
00221 return ($FieldA->OrderPosition($this->FieldCompareType)
00222 < $FieldB->OrderPosition($this->FieldCompareType)) ? -1 : 1;
00223 }
00224 }
00225 }
00226
00227 function GetFieldNames($FieldTypes = NULL, $OrderType = NULL,
00228 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
00229 {
00230 global $DB;
00231
00232 $FieldNames=array();
00233 $Fields = $this->GetFields($FieldTypes, $OrderType, $IncludeDisabledFields, $IncludeTempFields);
00234
00235 foreach($Fields as $Field)
00236 {
00237 $DB->Query("SELECT FieldName FROM MetadataFields WHERE FieldId=".$Field->Id());
00238 $FieldNames[ $Field->Id() ] = $DB->FetchField("FieldName");
00239 }
00240
00241 return $FieldNames;
00242 }
00243
00255 function GetFieldsAsOptionList($OptionListName, $FieldTypes = NULL,
00256 $SelectedFieldId = NULL, $IncludeNullOption = TRUE)
00257 {
00258 # retrieve requested fields
00259 $FieldNames = $this->GetFieldNames($FieldTypes);
00260
00261 # begin HTML option list
00262 $Html = "<select name=\"".$OptionListName."\">\n";
00263 if ($IncludeNullOption)
00264 {
00265 $Html .= "<option value=\"-1\">--</option>\n";
00266 }
00267
00268 # for each metadata field
00269 foreach ($FieldNames as $Id => $Name)
00270 {
00271 # add entry for field to option list
00272 $Html .= "<option value=\"".$Id."\"";
00273 if ($Id == $SelectedFieldId) { $Html .= " selected"; }
00274 $Html .= ">".htmlspecialchars($Name)."</option>\n";
00275 }
00276
00277 # end HTML option list
00278 $Html .= "</select>\n";
00279
00280 # return constructed HTML to caller
00281 return $Html;
00282 }
00283
00284 # retrieve array of field types (enumerated type => field name)
00285 function GetFieldTypes()
00286 {
00287 return MetadataField::$FieldTypeDBEnums;
00288 }
00289
00290 # retrieve array of field types that user can create (enumerated type => field name)
00291 function GetAllowedFieldTypes()
00292 {
00293 return MetadataField::$FieldTypeDBAllowedEnums;
00294 }
00295
00296 # remove all metadata field associations for a given qualifier
00297 function RemoveQualifierAssociations($QualifierIdOrObject)
00298 {
00299 # sanitize qualifier ID or grab it from object
00300 $QualifierIdOrObject = is_object($QualifierIdOrObject)
00301 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00302
00303 # delete intersection records from database
00304 $this->DB->Query("DELETE FROM FieldQualifierInts WHERE QualifierId = "
00305 .$QualifierIdOrObject);
00306 }
00307
00308 # return whether qualifier is in use by metadata field
00309 function QualifierIsInUse($QualifierIdOrObject)
00310 {
00311 # sanitize qualifier ID or grab it from object
00312 $QualifierIdOrObject = is_object($QualifierIdOrObject)
00313 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00314
00315 # determine whether any fields use qualifier as default
00316 $DefaultCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM MetadataFields"
00317 ." WHERE DefaultQualifier = ".$QualifierIdOrObject,
00318 "RecordCount");
00319
00320 # determine whether any fields are associated with qualifier
00321 $AssociationCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM FieldQualifierInts"
00322 ." WHERE QualifierId = ".$QualifierIdOrObject,
00323 "RecordCount");
00324
00325 # report whether qualifier is in use based on defaults and associations
00326 return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
00327 }
00328
00329 # move fields up or down in field order
00330 function MoveUpInOrder($FieldIdOrObj, $OrderType)
00331 {
00332 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, FALSE);
00333 }
00334 function MoveDownInOrder($FieldIdOrObj, $OrderType)
00335 {
00336 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, TRUE);
00337 }
00338
00339 # return highest field ID currently in use
00340 function GetHighestFieldId() { return $this->GetHighestItemId(); }
00341
00349 static function StdNameToFieldMapping($MappedName, $FieldId = NULL)
00350 {
00351 if ($FieldId !== NULL)
00352 {
00353 self::$FieldMappings[$MappedName] = $FieldId;
00354 }
00355 return isset(self::$FieldMappings[$MappedName])
00356 ? self::$FieldMappings[$MappedName] : NULL;
00357 }
00358
00365 static function FieldToStdNameMapping($FieldId)
00366 {
00367 foreach (self::$FieldMappings as $MappedName => $MappedFieldId)
00368 {
00369 if ($MappedFieldId == $FieldId)
00370 {
00371 return $MappedName;
00372 }
00373 }
00374 return NULL;
00375 }
00376
00384 function GetFieldByMappedName($MappedName)
00385 {
00386 return ($this->StdNameToFieldMapping($MappedName) == NULL) ? NULL
00387 : $this->GetField($this->StdNameToFieldMapping($MappedName));
00388 }
00389
00390
00391 # ---- PRIVATE INTERFACE -------------------------------------------------
00392
00393 private $FieldCompareType;
00394 private $FieldOrderError;
00395 private $CachingOn;
00396 private static $FieldMappings;
00397
00398 private function MoveFieldInOrder($FieldIdOrObj, $OrderType, $MoveFieldDown)
00399 {
00400 # grab field ID
00401 $FieldId = is_object($FieldIdOrObj) ? $Field->Id() : $FieldIdOrObj;
00402
00403 # retrieve array of fields
00404 $Fields = $this->GetFields(NULL, $OrderType);
00405
00406 # reverse array of fields if we are moving field down
00407 if ($MoveFieldDown)
00408 {
00409 $Fields = array_reverse($Fields);
00410 }
00411
00412 # for each field in order
00413 $PreviousField = NULL;
00414 foreach ($Fields as $Field)
00415 {
00416 # if field is the field to be moved
00417 if ($Field->Id() == $FieldId)
00418 {
00419 # if we have a previous field
00420 if ($PreviousField !== NULL)
00421 {
00422 # swap field with previous field according to order type
00423 $TempVal = $Field->OrderPosition($OrderType);
00424 $Field->OrderPosition($OrderType, $PreviousField->OrderPosition($OrderType));
00425 $PreviousField->OrderPosition($OrderType, $TempVal);
00426 }
00427 }
00428
00429 # save field for next iteration
00430 $PreviousField = $Field;
00431 }
00432 }
00433 }
00434
00435 ?>