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
00253 function GetFieldsAsOptionList(
00254 $OptionListName, $FieldTypes = NULL, $SelectedFieldId = NULL)
00255 {
00256 # retrieve requested fields
00257 $FieldNames = $this->GetFieldNames($FieldTypes);
00258
00259 # begin HTML option list
00260 $Html = "<select name=\"".$OptionListName."\">\n";
00261 $Html .= "<option value=\"-1\">--</option>\n";
00262
00263 # for each metadata field
00264 foreach ($FieldNames as $Id => $Name)
00265 {
00266 # add entry for field to option list
00267 $Html .= "<option value=\"".$Id."\"";
00268 if ($Id == $SelectedFieldId) { $Html .= " selected"; }
00269 $Html .= ">".htmlspecialchars($Name)."</option>\n";
00270 }
00271
00272 # end HTML option list
00273 $Html .= "</select>\n";
00274
00275 # return constructed HTML to caller
00276 return $Html;
00277 }
00278
00279 # retrieve array of field types (enumerated type => field name)
00280 function GetFieldTypes()
00281 {
00282 return MetadataField::$FieldTypeDBEnums;
00283 }
00284
00285 # retrieve array of field types that user can create (enumerated type => field name)
00286 function GetAllowedFieldTypes()
00287 {
00288 return MetadataField::$FieldTypeDBAllowedEnums;
00289 }
00290
00291 # remove all metadata field associations for a given qualifier
00292 function RemoveQualifierAssociations($QualifierIdOrObject)
00293 {
00294 # sanitize qualifier ID or grab it from object
00295 $QualifierIdOrObject = is_object($QualifierIdOrObject)
00296 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00297
00298 # delete intersection records from database
00299 $this->DB->Query("DELETE FROM FieldQualifierInts WHERE QualifierId = "
00300 .$QualifierIdOrObject);
00301 }
00302
00303 # return whether qualifier is in use by metadata field
00304 function QualifierIsInUse($QualifierIdOrObject)
00305 {
00306 # sanitize qualifier ID or grab it from object
00307 $QualifierIdOrObject = is_object($QualifierIdOrObject)
00308 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00309
00310 # determine whether any fields use qualifier as default
00311 $DefaultCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM MetadataFields"
00312 ." WHERE DefaultQualifier = ".$QualifierIdOrObject,
00313 "RecordCount");
00314
00315 # determine whether any fields are associated with qualifier
00316 $AssociationCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM FieldQualifierInts"
00317 ." WHERE QualifierId = ".$QualifierIdOrObject,
00318 "RecordCount");
00319
00320 # report whether qualifier is in use based on defaults and associations
00321 return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
00322 }
00323
00324 # move fields up or down in field order
00325 function MoveUpInOrder($FieldIdOrObj, $OrderType)
00326 {
00327 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, FALSE);
00328 }
00329 function MoveDownInOrder($FieldIdOrObj, $OrderType)
00330 {
00331 $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, TRUE);
00332 }
00333
00334 # return highest field ID currently in use
00335 function GetHighestFieldId() { return $this->GetHighestItemId(); }
00336
00344 static function StdNameToFieldMapping($MappedName, $FieldId = NULL)
00345 {
00346 if ($FieldId !== NULL)
00347 {
00348 self::$FieldMappings[$MappedName] = $FieldId;
00349 }
00350 return isset(self::$FieldMappings[$MappedName])
00351 ? self::$FieldMappings[$MappedName] : NULL;
00352 }
00353
00360 static function FieldToStdNameMapping($FieldId)
00361 {
00362 foreach (self::$FieldMappings as $MappedName => $MappedFieldId)
00363 {
00364 if ($MappedFieldId == $FieldId)
00365 {
00366 return $MappedName;
00367 }
00368 }
00369 return NULL;
00370 }
00371
00379 function GetFieldByMappedName($MappedName)
00380 {
00381 return ($this->StdNameToFieldMapping($MappedName) == NULL) ? NULL
00382 : $this->GetField($this->StdNameToFieldMapping($MappedName));
00383 }
00384
00385
00386 # ---- PRIVATE INTERFACE -------------------------------------------------
00387
00388 private $FieldCompareType;
00389 private $FieldOrderError;
00390 private $CachingOn;
00391 private static $FieldMappings;
00392
00393 private function MoveFieldInOrder($FieldIdOrObj, $OrderType, $MoveFieldDown)
00394 {
00395 # grab field ID
00396 $FieldId = is_object($FieldIdOrObj) ? $Field->Id() : $FieldIdOrObj;
00397
00398 # retrieve array of fields
00399 $Fields = $this->GetFields(NULL, $OrderType);
00400
00401 # reverse array of fields if we are moving field down
00402 if ($MoveFieldDown)
00403 {
00404 $Fields = array_reverse($Fields);
00405 }
00406
00407 # for each field in order
00408 $PreviousField = NULL;
00409 foreach ($Fields as $Field)
00410 {
00411 # if field is the field to be moved
00412 if ($Field->Id() == $FieldId)
00413 {
00414 # if we have a previous field
00415 if ($PreviousField !== NULL)
00416 {
00417 # swap field with previous field according to order type
00418 $TempVal = $Field->OrderPosition($OrderType);
00419 $Field->OrderPosition($OrderType, $PreviousField->OrderPosition($OrderType));
00420 $PreviousField->OrderPosition($OrderType, $TempVal);
00421 }
00422 }
00423
00424 # save field for next iteration
00425 $PreviousField = $Field;
00426 }
00427 }
00428 }
00429
00430 ?>