Search:

CWIS Developers Documentation

  • Main Page
  • Classes
  • Files
  • File List
  • File Members

MetadataSchema.php

Go to the documentation of this file.
00001 <?PHP
00002 #
00003 #   FILE:  MetadataSchema.php
00004 #
00005 #   Part of the Collection Workflow Integration System (CWIS)
00006 #   Copyright 2011 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_ERROR =              2;
00040     const MDFSTAT_DUPLICATENAME =      4;
00041     const MDFSTAT_DUPLICATEDBCOLUMN =  8;
00042     const MDFSTAT_FIELDDOESNOTEXIST =  16;
00043     const MDFSTAT_ILLEGALNAME =        32;
00044     const MDFSTAT_DUPLICATELABEL =     64;
00045     const MDFSTAT_ILLEGALLABEL =       128;
00046 
00047     # object constructor
00048     function MetadataSchema()
00049     {
00050         # set up item factory base class
00051         $this->ItemFactory(
00052                 "MetadataField", "MetadataFields", "FieldId", "FieldName");
00053 
00054         # start with field info caching enabled
00055         $this->CachingOn = TRUE;
00056 
00057         if (self::$OwnerListRetrievalFunction)
00058         {
00059             $OwnerList = call_user_func(self::$OwnerListRetrievalFunction);
00060 
00061             if (is_array($OwnerList))
00062             {
00063                 $OwnedFields = $this->GetOwnedFields();
00064 
00065                 foreach ($OwnedFields as $OwnedField)
00066                 {
00067                     $Owner = $OwnedField->Owner();
00068 
00069                     if (in_array($Owner, $OwnerList))
00070                     {
00071                         if ($OwnedField->EnableOnOwnerReturn())
00072                         {
00073                             $OwnedField->Enabled(TRUE);
00074                             $OwnedField->EnableOnOwnerReturn(FALSE);
00075                         }
00076                     }
00077 
00078                     else
00079                     {
00080                         $Enabled = $OwnedField->Enabled();
00081 
00082                         if ($Enabled)
00083                         {
00084                             $OwnedField->EnableOnOwnerReturn($Enabled);
00085                             $OwnedField->Enabled(FALSE);
00086                         }
00087                     }
00088                 }
00089             }
00090         }
00091     }
00092 
00093     # turn internal caching of field info on or off
00094     function CacheData($NewValue)
00095     {
00096         $this->CachingOn = $NewValue;
00097     }
00098 
00099     # add new metadata field
00100     function AddField($FieldName, $FieldType, $Optional = TRUE, $DefaultValue = NULL)
00101     {
00102         # create new field
00103         $Field = new MetadataField(NULL, $FieldName, $FieldType, $Optional, $DefaultValue);
00104 
00105         # save error code if create failed and return NULL
00106         if ($Field->Status() != MetadataSchema::MDFSTAT_OK)
00107         {
00108             $this->ErrorStatus = $Field->Status();
00109             $Field = NULL;
00110         }
00111 
00112         # return new field to caller
00113         return $Field;
00114     }
00115 
00125     function AddFieldFromXml($Xml)
00126     {
00127         # assume field addition will fail
00128         $Field = self::MDFSTAT_ERROR;
00129 
00130         # add XML prefixes if needed
00131         $Xml = trim($Xml);
00132         if (!preg_match("/^<\?xml/i", $Xml))
00133         {
00134             if (!preg_match("/^<document>/i", $Xml))
00135             {
00136                 $Xml = "<document>".$Xml."</document>";
00137             }
00138             $Xml = "<?xml version='1.0'?>".$Xml;
00139         }
00140 
00141         # parse XML
00142         $XmlData = simplexml_load_string($Xml);
00143 
00144          # if required values are present
00145         if (is_object($XmlData)
00146                 && isset($XmlData->Name)
00147                 && isset($XmlData->Type)
00148                 && constant("MetadataSchema::".$XmlData->Type))
00149         {
00150             # create the metadata field
00151             $Field = new MetadataField(NULL, $XmlData->Name,
00152                     constant("MetadataSchema::".$XmlData->Type));
00153 
00154             # if field creation failed
00155             if ($Field->Status() !== self::MDFSTAT_OK)
00156             {
00157                 # reset field value to error code
00158                 $Field = $Field->Status();
00159             }
00160             else
00161             {
00162                 # for other field attributes
00163                 foreach ($XmlData as $MethodName => $Value)
00164                 {
00165                     # if they look valid and have not already been set
00166                     if (method_exists($Field, $MethodName)
00167                             && ($MethodName != "Name")
00168                             && ($MethodName != "Type"))
00169                     {
00170                         # condense down any extraneous whitespace
00171                         $Value = preg_replace("/\s+/", " ", trim($Value));
00172 
00173                         # set value for field
00174                         $Field->$MethodName($Value);
00175                     }
00176                 }
00177 
00178                 # make new field permanent
00179                 $Field->IsTempItem(FALSE);
00180             }
00181         }
00182 
00183         # return new field (if any) to caller
00184         return $Field;
00185     }
00186 
00187     # delete metadata field
00188     function DropField($FieldId)
00189     {
00190         $Field = new MetadataField($FieldId);
00191         $Field->Drop();
00192     }
00193 
00194     # retrieve field by ID
00195     function GetField($FieldId)
00196     {
00197         static $Fields;
00198 
00199         # if caching is off or field is already loaded
00200         if (($this->CachingOn != TRUE) || !isset($Fields[$FieldId]))
00201         {
00202             # retrieve field
00203             $Fields[$FieldId] = new MetadataField($FieldId);
00204         }
00205 
00206         # return field to caller
00207         return $Fields[$FieldId];
00208     }
00209 
00216     function GetFieldByName($FieldName, $IgnoreCase = FALSE)
00217     {
00218         $FieldId = $this->GetFieldIdByName($FieldName, $IgnoreCase);
00219         return ($FieldId === NULL) ? NULL : $this->GetField($FieldId);
00220     }
00221 
00228     function GetFieldByLabel($FieldLabel, $IgnoreCase = FALSE)
00229     {
00230         $FieldId = $this->GetFieldIdByLabel($FieldLabel, $IgnoreCase);
00231         return ($FieldId === NULL) ? NULL : $this->GetField($FieldId);
00232     }
00233 
00241     function GetFieldIdByName($FieldName, $IgnoreCase = FALSE)
00242     {
00243         static $FieldIdsByName;
00244 
00245         # if caching is off or field ID is already loaded
00246         if (($this->CachingOn != TRUE) || !isset($FieldIdsByName[$FieldName]))
00247         {
00248             # retrieve field ID from DB
00249             $Condition = $IgnoreCase
00250                     ? "WHERE LOWER(FieldName) = '".addslashes(strtolower($FieldName))."'"
00251                     : "WHERE FieldName = '".addslashes($FieldName)."'";
00252             $FieldIdsByName[$FieldName] = $this->DB->Query(
00253                     "SELECT FieldId FROM MetadataFields ".$Condition, "FieldId");
00254         }
00255 
00256         return $FieldIdsByName[$FieldName];
00257     }
00258 
00266     function GetFieldIdByLabel($FieldLabel, $IgnoreCase = FALSE)
00267     {
00268         static $FieldIdsByLabel;
00269 
00270         # if caching is off or field ID is already loaded
00271         if (($this->CachingOn != TRUE) || !isset($FieldIdsByLabel[$FieldLabel]))
00272         {
00273             # retrieve field ID from DB
00274             $Condition = $IgnoreCase
00275                     ? "WHERE LOWER(Label) = '".addslashes(strtolower($FieldLabel))."'"
00276                     : "WHERE Label = '".addslashes($FieldLabel)."'";
00277             $FieldIdsByLabel[$FieldLabel] = $this->DB->Query(
00278                     "SELECT FieldId FROM MetadataFields ".$Condition, "FieldId");
00279         }
00280 
00281         return $FieldIdsByLabel[$FieldLabel];
00282     }
00283 
00284     # check whether field with specified name exists
00285     function FieldExists($FieldName) {  return $this->NameIsInUse($FieldName);  }
00286 
00287     # retrieve array of fields
00288     function GetFields($FieldTypes = NULL, $OrderType = NULL,
00289             $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
00290     {
00291         # create empty array to pass back
00292         $Fields = array();
00293 
00294         # for each field type in database
00295         if ($IncludeTempFields && $IncludeDisabledFields)
00296         {
00297             $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields");
00298         }
00299         else
00300         {
00301             if ($IncludeTempFields)
00302             {
00303                 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE Enabled != 0");
00304             }
00305             elseif ($IncludeDisabledFields)
00306             {
00307                 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0");
00308             }
00309             else
00310             {
00311                 $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields WHERE FieldId >= 0 AND Enabled != 0");
00312             }
00313         }
00314         while ($Record = $this->DB->FetchRow())
00315         {
00316             # if no specific type requested or if field is of requested type
00317             if (($FieldTypes == NULL)
00318                 || (MetadataField::$FieldTypePHPEnums[$Record["FieldType"]] & $FieldTypes))
00319             {
00320                 # create field object and add to array to be passed back
00321                 $Fields[$Record["FieldId"]] = new MetadataField($Record["FieldId"]);
00322             }
00323         }
00324 
00325         # if field sorting requested
00326         if ($OrderType !== NULL)
00327         {
00328             # sort field array by requested order type
00329             $this->FieldCompareType = $OrderType;
00330             $this->FieldOrderError = FALSE;
00331             uasort($Fields, array($this, "CompareFieldOrder"));
00332 
00333             # if field order error detected
00334             if ($this->FieldOrderError)
00335             {
00336                 # repair (reset) field order
00337                 $OrderIndex = 1;
00338                 foreach ($Fields as $Field)
00339                 {
00340                     $Field->OrderPosition($OrderType, $OrderIndex);
00341                     $OrderIndex++;
00342                 }
00343             }
00344         }
00345 
00346         # return array of field objects to caller
00347         return $Fields;
00348     }
00349 
00350     # callback function for sorting fields
00351     function CompareFieldOrder($FieldA, $FieldB)
00352     {
00353         if ($this->FieldCompareType == MetadataSchema::MDFORDER_ALPHABETICAL)
00354         {
00355             return ($FieldA->GetDisplayName() < $FieldB->GetDisplayName()) ? -1 : 1;
00356         }
00357         else
00358         {
00359             if ($FieldA->OrderPosition($this->FieldCompareType)
00360                     == $FieldB->OrderPosition($this->FieldCompareType))
00361             {
00362                 $this->FieldOrderError = TRUE;
00363                 return 0;
00364             }
00365             else
00366             {
00367                 return ($FieldA->OrderPosition($this->FieldCompareType)
00368                         < $FieldB->OrderPosition($this->FieldCompareType)) ? -1 : 1;
00369             }
00370         }
00371     }
00372 
00373     function GetFieldNames($FieldTypes = NULL, $OrderType = NULL,
00374                             $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
00375     {
00376         global $DB;
00377 
00378         $FieldNames=array();
00379         $Fields = $this->GetFields($FieldTypes, $OrderType, $IncludeDisabledFields, $IncludeTempFields);
00380 
00381         foreach($Fields as $Field)
00382         {
00383             $DB->Query("SELECT FieldName FROM MetadataFields WHERE FieldId=".$Field->Id());
00384             $FieldNames[ $Field->Id() ] = $DB->FetchField("FieldName");
00385         }
00386 
00387         return $FieldNames;
00388     }
00389 
00405     function GetFieldsAsOptionList($OptionListName, $FieldTypes = NULL,
00406             $SelectedFieldId = NULL, $IncludeNullOption = TRUE,
00407             $AddEntries = NULL, $AllowMultiple = FALSE)
00408     {
00409         # retrieve requested fields
00410         $FieldNames = $this->GetFieldNames($FieldTypes);
00411 
00412         # transform field names to labels
00413         foreach ($FieldNames as $FieldId => $FieldName)
00414         {
00415             $FieldNames[$FieldId] = $this->GetField($FieldId)->GetDisplayName();
00416         }
00417 
00418         # begin HTML option list
00419         $Html = "<select id=\"".$OptionListName."\" name=\"".$OptionListName."\"";
00420 
00421         # if multiple selections should be allowed
00422         if ($AllowMultiple)
00423         {
00424             $Html .= " multiple=\"multiple\"";
00425         }
00426 
00427         $Html .= ">\n";
00428 
00429         if ($IncludeNullOption)
00430         {
00431             $Html .= "<option value=\"\">--</option>\n";
00432         }
00433 
00434         # make checking for IDs simpler
00435         if (!is_array($SelectedFieldId))
00436         {
00437             $SelectedFieldId = array($SelectedFieldId);
00438         }
00439 
00440         # for each metadata field
00441         foreach ($FieldNames as $Id => $Name)
00442         {
00443             # add entry for field to option list
00444             $Html .= "<option value=\"".$Id."\"";
00445             if (in_array($Id, $SelectedFieldId)) {  $Html .= " selected";  }
00446             $Html .= ">".htmlspecialchars($Name)."</option>\n";
00447         }
00448 
00449         # if additional entries were requested
00450         if ($AddEntries)
00451         {
00452             foreach ($AddEntries as $Value => $Label)
00453             {
00454                 $Html .= "<option value=\"".$Value."\"";
00455                 if ($Value == $SelectedFieldId) {  $Html .= " selected";  }
00456                 $Html .= ">".htmlspecialchars($Label)."</option>\n";
00457             }
00458         }
00459 
00460         # end HTML option list
00461         $Html .= "</select>\n";
00462 
00463         # return constructed HTML to caller
00464         return $Html;
00465     }
00466 
00467     # retrieve array of field types (enumerated type => field name)
00468     function GetFieldTypes()
00469     {
00470         return MetadataField::$FieldTypeDBEnums;
00471     }
00472 
00473     # retrieve array of field types that user can create (enumerated type => field name)
00474     function GetAllowedFieldTypes()
00475     {
00476         return MetadataField::$FieldTypeDBAllowedEnums;
00477     }
00478 
00479     # remove all metadata field associations for a given qualifier
00480     function RemoveQualifierAssociations($QualifierIdOrObject)
00481     {
00482         # sanitize qualifier ID or grab it from object
00483         $QualifierIdOrObject = is_object($QualifierIdOrObject)
00484                 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00485 
00486         # delete intersection records from database
00487         $this->DB->Query("DELETE FROM FieldQualifierInts WHERE QualifierId = "
00488                          .$QualifierIdOrObject);
00489     }
00490 
00491     # return whether qualifier is in use by metadata field
00492     function QualifierIsInUse($QualifierIdOrObject)
00493     {
00494         # sanitize qualifier ID or grab it from object
00495         $QualifierIdOrObject = is_object($QualifierIdOrObject)
00496                 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
00497 
00498         # determine whether any fields use qualifier as default
00499         $DefaultCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM MetadataFields"
00500                                          ." WHERE DefaultQualifier = ".$QualifierIdOrObject,
00501                                          "RecordCount");
00502 
00503         # determine whether any fields are associated with qualifier
00504         $AssociationCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM FieldQualifierInts"
00505                                          ." WHERE QualifierId = ".$QualifierIdOrObject,
00506                                          "RecordCount");
00507 
00508         # report whether qualifier is in use based on defaults and associations
00509         return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
00510     }
00511 
00512     # move fields up or down in field order
00513     function MoveUpInOrder($FieldIdOrObj, $OrderType)
00514     {
00515         $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, FALSE);
00516     }
00517     function MoveDownInOrder($FieldIdOrObj, $OrderType)
00518     {
00519         $this->MoveFieldInOrder($FieldIdOrObj, $OrderType, TRUE);
00520     }
00521 
00522     # return highest field ID currently in use
00523     function GetHighestFieldId() {  return $this->GetHighestItemId();  }
00524 
00532     static function StdNameToFieldMapping($MappedName, $FieldId = NULL)
00533     {
00534         if ($FieldId !== NULL)
00535         {
00536             self::$FieldMappings[$MappedName] = $FieldId;
00537         }
00538         return isset(self::$FieldMappings[$MappedName])
00539                 ? self::$FieldMappings[$MappedName] : NULL;
00540     }
00541 
00548     static function FieldToStdNameMapping($FieldId)
00549     {
00550         if ($FieldId != -1)
00551         {
00552             foreach (self::$FieldMappings as $MappedName => $MappedFieldId)
00553             {
00554                 if ($MappedFieldId == $FieldId)
00555                 {
00556                     return $MappedName;
00557                 }
00558             }
00559         }
00560         return NULL;
00561     }
00562 
00570     function GetFieldByMappedName($MappedName)
00571     {
00572         return ($this->StdNameToFieldMapping($MappedName) == NULL) ? NULL
00573                 : $this->GetField($this->StdNameToFieldMapping($MappedName));
00574     }
00575 
00580     public function GetOwnedFields()
00581     {
00582         $Fields = array();
00583 
00584         $this->DB->Query("
00585             SELECT * FROM MetadataFields
00586             WHERE Owner IS NOT NULL AND LENGTH(Owner) > 0");
00587 
00588         while (FALSE !== ($Row = $this->DB->FetchRow()))
00589         {
00590             $FieldId = $Row["FieldId"];
00591             $Fields[$FieldId] = new MetadataField($FieldId);
00592         }
00593 
00594         return $Fields;
00595     }
00596 
00602     public static function SetOwnerListRetrievalFunction($Callback)
00603     {
00604         if (is_callable($Callback))
00605         {
00606             self::$OwnerListRetrievalFunction = $Callback;
00607         }
00608     }
00609 
00610     # ---- PRIVATE INTERFACE -------------------------------------------------
00611 
00612     private $FieldCompareType;
00613     private $FieldOrderError;
00614     private $CachingOn;
00615     private static $FieldMappings;
00616     protected static $OwnerListRetrievalFunction;
00617 
00618     private function MoveFieldInOrder($FieldIdOrObj, $OrderType, $MoveFieldDown)
00619     {
00620         # grab field ID
00621         $FieldId = is_object($FieldIdOrObj) ? $Field->Id() : $FieldIdOrObj;
00622 
00623         # retrieve array of fields
00624         $Fields = $this->GetFields(NULL, $OrderType);
00625 
00626         # reverse array of fields if we are moving field down
00627         if ($MoveFieldDown)
00628         {
00629             $Fields = array_reverse($Fields);
00630         }
00631 
00632         # for each field in order
00633         $PreviousField = NULL;
00634         foreach ($Fields as $Field)
00635         {
00636             # if field is the field to be moved
00637             if ($Field->Id() == $FieldId)
00638             {
00639                 # if we have a previous field
00640                 if ($PreviousField !== NULL)
00641                 {
00642                     # swap field with previous field according to order type
00643                     $TempVal = $Field->OrderPosition($OrderType);
00644                     $Field->OrderPosition($OrderType, $PreviousField->OrderPosition($OrderType));
00645                     $PreviousField->OrderPosition($OrderType, $TempVal);
00646                 }
00647             }
00648 
00649             # save field for next iteration
00650             $PreviousField = $Field;
00651         }
00652     }
00653 }
00654 

CWIS logo doxygen
Copyright 2010 Internet Scout