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