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

Copyright 2010 Internet Scout