3 # FILE: MetadataSchema.php 5 # Part of the Collection Workflow Integration System (CWIS) 6 # Copyright 2012-2013 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu/cwis 16 # ---- PUBLIC INTERFACE -------------------------------------------------- 18 # metadata field base types 19 # (must parallel MetadataFields.FieldType declaration in install/CreateTables.sql 20 # and MetadataField::$FieldTypeDBEnums declaration below) 37 # types of field ordering 62 # names used for display and edit orders 77 public function __construct($SchemaId = self::SCHEMAID_DEFAULT)
79 # set up item factory base class 80 parent::__construct(
"MetadataField",
"MetadataFields",
81 "FieldId",
"FieldName", FALSE,
82 "SchemaId = ".intval($SchemaId));
84 # make sure schema info cache is loaded 85 if (self::$ValueCache === NULL)
87 $this->DB->Query(
"SELECT * FROM MetadataSchemas");
88 self::$ValueCache = array();
89 foreach ($this->DB->FetchRows() as $Row)
91 self::$ValueCache[$Row[
"SchemaId"]] = $Row;
95 # if standard field mappings have not yet been loaded 96 if (!isset(self::$FieldMappings))
98 # load metadata field IDs to check against 99 $this->DB->Query(
"SELECT SchemaId, FieldId" 100 .
" FROM StandardMetadataFieldMappings");
101 $FieldSchemaIds = $this->DB->FetchColumn(
"SchemaId",
"FieldId");
103 # for each standard field mapping 104 $this->DB->Query(
"SELECT * FROM StandardMetadataFieldMappings");
105 foreach ($this->DB->FetchRows() as $Row)
107 # if mapping is for a valid field in appropriate schema 108 if (isset($FieldSchemaIds[$Row[
"FieldId"]])
109 && ($FieldSchemaIds[$Row[
"FieldId"]] == $Row[
"SchemaId"]))
112 self::$FieldMappings[$Row[
"SchemaId"]][$Row[
"Name"]] =
118 throw new Exception(
"Standard field mapping for" 119 .
" \"".$Row[
"Name"].
"\" found with" 120 .
" invalid schema/field ID combination" 121 .
" (".$Row[
"SchemaId"].
"/".$Row[
"FieldId"].
").");
126 # make sure specified schema ID is valid 127 if (!isset(self::$ValueCache[$SchemaId]))
129 throw new InvalidArgumentException(
"Attempt to load metadata schema" 130 .
" with invalid ID (".$SchemaId.
") at " 131 .StdLib::GetMyCaller().
".");
134 # load schema info from cache 135 $Info = self::$ValueCache[$SchemaId];
136 $this->
Id = $SchemaId;
140 $this->
ViewPage = $Info[
"ViewPage"];
141 if (!isset(self::$FieldMappings[$this->
Id]))
143 self::$FieldMappings[$this->Id] = array();
158 # retrieve all constants for class 159 $Reflect =
new ReflectionClass(get_class());
160 $Constants = $Reflect->getConstants();
163 foreach ($Constants as $CName => $CValue)
165 # if value matches and prefix (if supplied) matches 166 if (($CValue == $Value)
167 && (($Prefix === NULL) || (strpos($CName, $Prefix) === 0)))
169 # return name to caller 174 # report to caller that no matching constant was found 200 $ResourceName = NULL)
202 # supply privilege settings if none provided 203 if ($AuthorPrivs === NULL) { $AuthorPrivs =
new PrivilegeSet(); }
204 if ($EditPrivs === NULL) { $EditPrivs =
new PrivilegeSet(); }
205 if ($ViewPrivs === NULL) { $ViewPrivs =
new PrivilegeSet(); }
207 # add schema to database 209 if (strtoupper($Name) ==
"RESOURCES")
211 $Id = self::SCHEMAID_DEFAULT;
213 elseif (strtoupper($Name) ==
"USER")
215 $Id = self::SCHEMAID_USER;
219 $Id =
$DB->Query(
"SELECT SchemaId FROM MetadataSchemas" 220 .
" ORDER BY SchemaId DESC LIMIT 1",
"SchemaId") + 1;
222 $DB->Query(
"INSERT INTO MetadataSchemas" 223 .
" (SchemaId, Name, ViewPage," 224 .
" AuthoringPrivileges, EditingPrivileges, ViewingPrivileges)" 225 .
" VALUES (".intval($Id).
"," 226 .
"'".addslashes($Name).
"'," 227 .
"'".
$DB->EscapeString($ViewPage).
"'," 228 .
"'".
$DB->EscapeString($AuthorPrivs->Data()).
"'," 229 .
"'".
$DB->EscapeString($EditPrivs->Data()).
"'," 230 .
"'".
$DB->EscapeString($ViewPrivs->Data()).
"')");
232 # clear schema data cache so it will be reloaded 233 self::$ValueCache = NULL;
235 # construct the new schema 238 # set schema name if none supplied 241 $Schema->Name(
"Metadata Schema ".$Id);
244 # set the resource name if one is supplied 245 if ($ResourceName === NULL)
249 $Schema->ResourceName($ResourceName);
251 # create display and edit orders 255 # return the new schema 265 # delete resources associated with schema 267 $ResourceIds = $RFactory->GetItemIds();
268 foreach ($ResourceIds as $ResourceId)
270 $Resource =
new Resource($ResourceId);
274 # delete fields associated with schema 275 $Fields = $this->
GetFields(NULL, NULL, TRUE, TRUE);
276 foreach ($Fields as $FieldId => $Field)
281 # delete metadata field orders associated with schema 287 # remove schema info from database 288 $this->DB->Query(
"DELETE FROM MetadataSchemas WHERE SchemaId = " 299 if ($SchemaId === NULL) {
return FALSE; }
301 $DB->Query(
"SELECT * FROM MetadataSchemas" 302 .
" WHERE SchemaId = ".intval($SchemaId));
303 return (
$DB->NumRowsSelected() > 0) ? TRUE : FALSE;
313 # return value to caller 314 return intval($this->
Id);
336 $AName = $this->
UpdateValue(
"AbbreviatedName", $NewValue);
339 $AName = strtoupper(substr($this->
Name(), 0, 1));
351 $RName = $this->
UpdateValue(
"ResourceName", $NewValue);
354 $RName = self::RESOURCENAME_DEFAULT;
376 # if new privileges supplied 377 if ($NewValue !== NULL)
379 # store new privileges in database 380 $this->
UpdateValue(
"AuthoringPrivileges", $NewValue->Data());
384 # return current value to caller 385 return $this->AuthoringPrivileges;
395 # if new privileges supplied 396 if ($NewValue !== NULL)
398 # store new privileges in database 399 $this->
UpdateValue(
"EditingPrivileges", $NewValue->Data());
403 # return current value to caller 404 return $this->EditingPrivileges;
414 # if new privileges supplied 415 if ($NewValue !== NULL)
417 # store new privileges in database 418 $this->
UpdateValue(
"ViewingPrivileges", $NewValue->Data());
422 # return current value to caller 423 return $this->ViewingPrivileges;
435 # get authoring privilege set for schema 438 # user can author if privileges are greater than resource set 439 $CanAuthor = $AuthorPrivs->MeetsRequirements($User);
441 # allow plugins to modify result of permission check 442 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
443 "EVENT_RESOURCE_AUTHOR_PERMISSION_CHECK", array(
446 "CanAuthor" => $CanAuthor));
447 $CanAuthor = $SignalResult[
"CanAuthor"];
449 # report back to caller whether user can author field 462 # get editing privilege set for schema 465 # user can edit if privileges are greater than resource set 466 $CanEdit = $EditPrivs->MeetsRequirements($User);
468 # allow plugins to modify result of permission check 469 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
470 "EVENT_RESOURCE_EDIT_PERMISSION_CHECK", array(
473 "CanEdit" => $CanEdit,
474 "Schema" => $this, ));
475 $CanEdit = $SignalResult[
"CanEdit"];
477 # report back to caller whether user can edit field 490 # get viewing privilege set for schema 493 # user can view if privileges are greater than resource set 494 $CanView = $ViewPrivs->MeetsRequirements($User);
496 # allow plugins to modify result of permission check 497 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
498 "EVENT_RESOURCE_VIEW_PERMISSION_CHECK", array(
501 "CanView" => $CanView,
502 "Schema" => $this, ));
503 $CanView = $SignalResult[
"CanView"];
505 # report back to caller whether user can view field 516 # get the query/GET parameters for the view page 517 $Query = parse_url($this->
ViewPage(), PHP_URL_QUERY);
519 # the URL couldn't be parsed 520 if (!is_string($Query))
525 # parse the GET parameters out of the query string 526 $GetVars = ParseQueryString($Query);
528 # search for the ID parameter 529 $Result = array_search(
"\$ID", $GetVars);
531 return $Result !== FALSE ? $Result : NULL;
548 # get the query/GET parameters for the view page 549 $Query = parse_url($this->
ViewPage(), PHP_URL_QUERY);
551 # can't perform matching if the URL couldn't be parsed 552 if (!is_string($Query))
557 # parse the GET parameters out of the query string 558 $GetVars = ParseQueryString($Query);
560 # now, get the query/GET parameters from the path given 561 $PathQuery = parse_url($Path, PHP_URL_QUERY);
563 # can't perform matching if the URL couldn't be parsed 564 if (!is_string($PathQuery))
569 # parse the GET parameters out of the path's query string 570 $PathGetVars = ParseQueryString($PathQuery);
572 # make sure the given path GET parameters contain at least the GET 573 # parameters from the view page and that all non-variable parameters are 574 # equal. the path GET parameters may contain more, which is okay 575 foreach ($GetVars as $GetVarName => $GetVarValue)
577 # there's a required parameter that is not included in the path GET 579 if (!array_key_exists($GetVarName, $PathGetVars))
584 # require the path's value to be equal to the view page's value if 585 # the view page's value is not a variable, 586 if ($PathGetVars[$GetVarName] != $GetVarValue
587 && (!strlen($GetVarValue) || $GetVarValue{0} !=
"$"))
593 # the path matches the view page path 607 $FieldName, $FieldType, $Optional = TRUE, $DefaultValue = NULL)
609 # clear any existing error messages 610 if (array_key_exists(__METHOD__, $this->ErrorMsgs))
611 { unset($this->ErrorMsgs[__METHOD__]); }
617 $FieldName, $Optional, $DefaultValue);
619 catch (Exception $Exception)
621 $this->ErrorMsgs[__METHOD__][] = $Exception->getMessage();
625 # clear internal caches to make sure new field is recognized going forward 628 # return new field to caller 648 # clear loading status 650 if (array_key_exists(__METHOD__, $this->ErrorMsgs))
651 { unset($this->ErrorMsgs[__METHOD__]); }
653 # check that file exists and is readable 654 if (!file_exists($FileName))
656 $this->ErrorMsgs[__METHOD__][] =
"Could not find XML file '" 660 elseif (!is_readable($FileName))
662 $this->ErrorMsgs[__METHOD__][] =
"Could not read from XML file '" 668 libxml_use_internal_errors(TRUE);
669 $XmlData = simplexml_load_file($FileName);
670 $Errors = libxml_get_errors();
671 libxml_use_internal_errors(FALSE);
674 if ($XmlData === FALSE)
676 # retrieve XML error messages 677 foreach ($Errors as $Err)
679 $ErrType = ($Err->level == LIBXML_ERR_WARNING) ?
"Warning" 680 : (($Err->level == LIBXML_ERR_WARNING) ?
"Error" 682 $this->ErrorMsgs[__METHOD__][] =
"XML ".$ErrType.
": ".$Err->message
683 .
" (".$Err->file.
":".$Err->line.
",".$Err->column.
")";
686 # else if no metadata fields found record error message 687 elseif (!count($XmlData->MetadataField))
689 $this->ErrorMsgs[__METHOD__][] =
"No metadata fields found.";
691 # else process metadata fields 694 # for each metadata field entry found 697 foreach ($XmlData->MetadataField as $FieldXml)
701 # pull out field type if present 702 if (isset($FieldXml->Type))
704 $FieldType =
"MetadataSchema::".$FieldXml->Type;
705 if (!defined($FieldType))
707 $FieldType =
"MetadataSchema::MDFTYPE_" 708 .strtoupper(preg_replace(
"/\\s+/",
"",
713 # if required values are missing 714 if (!isset($FieldXml->Name) || !isset($FieldXml->Type)
715 || !defined($FieldType))
717 # add error message about required value missing 718 if (!isset($FieldXml->Name))
720 $this->ErrorMsgs[__METHOD__][] =
721 "Field name not found (MetadataField #" 724 if (!isset($FieldXml->Type) || !defined($FieldType))
726 $this->ErrorMsgs[__METHOD__][] =
727 "Valid type not found for field '" 728 .$FieldXml->Name.
"' (MetadataField #" 732 # else if there is not already a field with this name 733 elseif (!$this->
NameIsInUse(trim($FieldXml->Name)))
736 $Field = $this->
AddField($FieldXml->Name, constant($FieldType));
738 # if field creation failed 741 # add any error message to our error list 743 foreach ($ErrorMsgs as $Msg)
745 $this->ErrorMsgs[__METHOD__][] =
751 # add field to list of created fields 754 # assume no vocabulary to load 757 # for other field attributes 758 foreach ($FieldXml as $MethodName => $Value)
760 # if tags look valid and have not already been set 761 if (method_exists($Field, $MethodName)
762 && ($MethodName !=
"Name")
763 && ($MethodName !=
"Type"))
765 # if tag indicates privilege set 766 if (preg_match(
"/^[a-z]+Privileges\$/i",
769 # save element for later processing 770 $PrivilegesToSet[$Field->Id()][$MethodName] = $Value;
774 # condense down any extraneous whitespace 775 $Value = preg_replace(
"/\s+/",
" ", trim($Value));
777 # set value for field 778 $Field->$MethodName($Value);
781 elseif ($MethodName ==
"VocabularyFile")
783 $VocabToLoad = $Value;
787 # save the temp ID so that any privileges to set 788 # can be mapped to the actual ID when the field is 790 $TempId = $Field->Id();
792 # make new field permanent 793 $Field->IsTempItem(FALSE);
795 # load any vocabularies 796 if ($VocabToLoad !== NULL)
798 $Field->LoadVocabulary($VocabToLoad);
801 # map privileges to set to the permanent field ID 802 if (isset($PrivilegesToSet) &&
803 isset($PrivilegesToSet[$TempId]) )
805 # copy the privileges over 806 $PrivilegesToSet[$Field->Id()] =
807 $PrivilegesToSet[$TempId];
809 # remove the values for the temp ID 810 unset($PrivilegesToSet[$TempId]);
816 # if we have schema-level privileges to set 817 if (count($XmlData->SchemaPrivileges))
819 foreach ($XmlData->SchemaPrivileges->children() as $PrivName => $PrivXml)
821 # if our current value for this privset is empty, 822 # take the one from the file 823 if ($this->$PrivName()->ComparisonCount() == 0)
825 # extract the values to set from the XML 826 $Value = $this->ConvertXmlToPrivilegeSet($PrivXml);
828 $this->$PrivName($Value);
833 # if we have privileges to set 834 if (isset($PrivilegesToSet))
836 # for each field with privileges 837 foreach ($PrivilegesToSet as $FieldId => $Privileges)
839 # load the field for which to set the privileges 842 # for each set of privileges for field 843 foreach ($Privileges as $MethodName => $Value)
845 # convert privilege value 846 $Value = $this->ConvertXmlToPrivilegeSet($Value);
848 # if conversion failed 851 # add resulting error messages to our list 853 "ConvertXmlToPrivilegeSet");
854 foreach ($ErrorMsgs as $Msg)
856 $this->ErrorMsgs[__METHOD__][] =
857 $Msg.
" (ConvertXmlToPrivilegeSet)";
862 # set value for field 863 $Field->$MethodName($Value);
869 # if errors were found during creation 870 if (array_key_exists(__METHOD__, $this->ErrorMsgs) || $TestRun)
872 # remove any fields that were created 881 # set owner for new fields (if supplied) 886 $Field->Owner($Owner);
890 # if there were standard field mappings included 891 if (isset($XmlData->StandardFieldMapping))
893 # for each standard field mapping found 894 foreach ($XmlData->StandardFieldMapping as $MappingXml)
896 # if required values are supplied 897 if (isset($MappingXml->Name)
898 && isset($MappingXml->StandardName))
900 # get ID for specified field 901 $FieldName = (string)$MappingXml->Name;
902 $StandardName = (
string)$MappingXml->StandardName;
905 # if field ID was found 906 if ($FieldId !== FALSE)
908 # set standard field mapping 910 $StandardName, $FieldId);
914 # log error about field not found 915 $this->ErrorMsgs[__METHOD__][] =
916 "Field not found with name '".$FieldName
917 .
"' to map to standard field name '" 923 # log error about missing value 924 if (!isset($MappingXml->Name))
926 $this->ErrorMsgs[__METHOD__][] =
927 "Field name missing for standard" 930 if (!isset($MappingXml->StandardName))
932 $this->ErrorMsgs[__METHOD__][] =
933 "Standard field name missing for" 934 .
" standard field mapping.";
942 # report success or failure based on whether errors were recorded 943 return (array_key_exists(__METHOD__, $this->ErrorMsgs)) ? FALSE : TRUE;
953 return $this->NewFields;
967 if ($Method === NULL)
969 return $this->ErrorMsgs;
973 if (!method_exists($this, $Method))
975 throw new Exception(
"Error messages requested for non-existent" 976 .
" method (".$Method.
").");
978 return array_key_exists(__CLASS__.
"::".$Method, $this->ErrorMsgs)
979 ? $this->ErrorMsgs[__CLASS__.
"::".$Method] : array();
994 # assume field addition will fail 995 $Field = self::MDFSTAT_ERROR;
997 # add XML prefixes if needed 999 if (!preg_match(
"/^<\?xml/i", $Xml))
1001 if (!preg_match(
"/^<document>/i", $Xml))
1003 $Xml =
"<document>".$Xml.
"</document>";
1005 $Xml =
"<?xml version='1.0'?".
">".$Xml;
1009 $XmlData = simplexml_load_string($Xml);
1011 # if required values are present 1012 if (is_object($XmlData)
1013 && isset($XmlData->Name)
1014 && isset($XmlData->Type)
1015 && constant(
"MetadataSchema::".$XmlData->Type))
1017 # create the metadata field 1020 constant(
"MetadataSchema::".$XmlData->Type));
1022 # if field creation succeeded 1025 # for other field attributes 1026 foreach ($XmlData as $MethodName => $Value)
1028 # if they look valid and have not already been set 1029 if (method_exists($Field, $MethodName)
1030 && ($MethodName !=
"Name")
1031 && ($MethodName !=
"Type"))
1033 # if tag indicates privilege set 1034 if (preg_match(
"/^[a-z]+Privileges\$/i",
1037 # save element for later processing 1038 $PrivilegesToSet[$MethodName] = $Value;
1042 # condense down any extraneous whitespace 1043 $Value = preg_replace(
"/\s+/",
" ", trim($Value));
1045 # set value for field 1046 $Field->$MethodName($Value);
1051 # make new field permanent 1052 $Field->IsTempItem(FALSE);
1054 # if we have privileges to set 1055 if (isset($PrivilegesToSet))
1057 # for each set of privileges for field 1058 foreach ($PrivilegesToSet as $MethodName => $Value)
1060 # convert privilege value 1061 $Value = $this->ConvertXmlToPrivilegeSet($Value);
1063 # if conversion failed 1064 if ($Value === NULL)
1066 # add resulting error messages to our list 1068 "ConvertXmlToPrivilegeSet");
1069 foreach ($ErrorMsgs as $Msg)
1071 $this->ErrorMsgs[__METHOD__][] =
1072 $Msg.
" (ConvertXmlToPrivilegeSet)";
1077 # set value for field 1078 $Field->$MethodName($Value);
1085 # return new field (if any) to caller 1096 $Field = $this->
GetField($FieldId);
1097 if ($Field !== NULL)
1100 $AF->SignalEvent(
"EVENT_PRE_FIELD_DELETE",
1101 array(
"FieldId" => $Field->Id()) );
1119 # convert field name to ID if necessary 1120 if (!is_numeric($FieldId))
1122 $FieldName = $FieldId;
1124 if ($FieldId === FALSE)
1126 throw new InvalidArgumentException(
"Attempt to retrieve field" 1127 .
" with unknown name (".$FieldName.
").");
1131 # if caching is off or field is not already loaded 1132 if (!isset(self::$FieldCache[$FieldId]))
1137 # if field was from a different schema, bail 1138 if (self::$FieldCache[$FieldId]->SchemaId() != $this->
Id())
1140 throw new Exception(
1141 "Attempt to retrieve a field from a different schema");
1144 return self::$FieldCache[$FieldId];
1159 return ($FieldId === FALSE) ? NULL : $this->
GetField($FieldId);
1181 return is_numeric($Field)
1199 public function GetFields($FieldTypes = NULL, $OrderType = NULL,
1200 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
1202 # create empty array to pass back 1205 # for each field type in database 1206 $this->DB->Query(
"SELECT FieldId, FieldType FROM MetadataFields" 1207 .
" WHERE SchemaId = ".intval($this->
Id)
1208 .(!$IncludeDisabledFields ?
" AND Enabled != 0" :
"")
1209 .(!$IncludeTempFields ?
" AND FieldId >= 0" :
""));
1210 while ($Record = $this->DB->FetchRow())
1212 # if field type is known 1215 # if no specific type requested or if field is of requested type 1216 if (($FieldTypes == NULL)
1220 # create field object and add to array to be passed back 1221 $Fields[$Record[
"FieldId"]] = $this->
GetField($Record[
"FieldId"]);
1226 # if field sorting requested 1227 if ($OrderType !== NULL)
1229 # update field comparison ordering if not set yet 1235 $this->FieldCompareType = $OrderType;
1237 # sort field array by requested order type 1238 uasort($Fields, array($this,
"CompareFieldOrder"));
1241 # return array of field objects to caller 1260 $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
1262 $Fields = $this->
GetFields($FieldTypes, $OrderType,
1263 $IncludeDisabledFields, $IncludeTempFields);
1265 $FieldNames = array();
1266 foreach($Fields as $Field)
1268 $FieldNames[$Field->Id()] = $Field->Name();
1293 $SelectedFieldId = NULL, $IncludeNullOption = TRUE,
1294 $AddEntries = NULL, $AllowMultiple = FALSE, $Disabled = FALSE)
1296 # retrieve requested fields 1299 # transform field names to labels 1300 foreach ($FieldNames as $FieldId => $FieldName)
1302 $FieldNames[$FieldId] = $this->
GetField($FieldId)->GetDisplayName();
1305 # add in null entry if requested 1306 if ($IncludeNullOption)
1308 $FieldNames = array(
"" =>
"--") + $FieldNames;
1311 # add additional entries if supplied 1314 $FieldNames = $FieldNames + $AddEntries;
1317 # construct option list 1318 $OptList =
new HtmlOptionList($OptionListName, $FieldNames, $SelectedFieldId);
1319 $OptList->MultipleAllowed($AllowMultiple);
1320 $OptList->Disabled($Disabled);
1322 # return option list HTML to caller 1323 return $OptList->GetHtml();
1352 # sanitize qualifier ID or grab it from object 1353 $QualifierIdOrObject = is_object($QualifierIdOrObject)
1354 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
1356 # delete intersection records from database 1357 $this->DB->Query(
"DELETE FROM FieldQualifierInts" 1358 .
" WHERE QualifierId = ".$QualifierIdOrObject);
1368 # sanitize qualifier ID or grab it from object 1369 $QualifierIdOrObject = is_object($QualifierIdOrObject)
1370 ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
1372 # determine whether any fields use qualifier as default 1373 $DefaultCount = $this->DB->Query(
"SELECT COUNT(*) AS RecordCount" 1374 .
" FROM MetadataFields" 1375 .
" WHERE DefaultQualifier = ".$QualifierIdOrObject,
1378 # determine whether any fields are associated with qualifier 1379 $AssociationCount = $this->DB->Query(
"SELECT COUNT(*) AS RecordCount" 1380 .
" FROM FieldQualifierInts" 1381 .
" WHERE QualifierId = ".$QualifierIdOrObject,
1384 # report whether qualifier is in use based on defaults and associations 1385 return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
1407 if (func_num_args() > 1)
1409 if (!isset(self::$FieldMappings[$this->
Id][$MappedName])
1410 || (self::$FieldMappings[$this->
Id][$MappedName] != $FieldId))
1412 if (($FieldId !== NULL) && !$this->
FieldExists($FieldId))
1414 throw new InvalidArgumentException(
"Attempt to set" 1415 .
" standard field mapping to invalid field ID" 1419 # if a mapping is set and is not NULL 1420 if (isset(self::$FieldMappings[$this->
Id][$MappedName]))
1422 $this->DB->Query(
"DELETE FROM StandardMetadataFieldMappings" 1423 .
" WHERE SchemaId = '".addslashes($this->
Id)
1424 .
"' AND Name = '".addslashes($MappedName).
"'");
1425 unset(self::$FieldMappings[$this->
Id][$MappedName]);
1428 if ($FieldId !== NULL)
1430 $this->DB->Query(
"INSERT INTO StandardMetadataFieldMappings" 1431 .
" (SchemaId, Name, FieldId) VALUES ('" 1432 .addslashes($this->
Id).
"', '".addslashes($MappedName)
1433 .
"', '".addslashes($FieldId).
"')");
1434 self::$FieldMappings[$this->Id][$MappedName] = $FieldId;
1438 return isset(self::$FieldMappings[$this->
Id][$MappedName])
1439 ? self::$FieldMappings[$this->Id][$MappedName] : NULL;
1450 $MappedName = array_search($FieldId, self::$FieldMappings[$this->
Id]);
1451 return ($MappedName === FALSE) ? NULL : $MappedName;
1487 $this->DB->Query(
"SELECT * FROM MetadataFields" 1488 .
" WHERE Owner IS NOT NULL AND LENGTH(Owner) > 0" 1489 .
" AND SchemaId = ".intval($this->
Id));
1491 while (FALSE !== ($Row = $this->DB->FetchRow()))
1493 $FieldId = $Row[
"FieldId"];
1494 $Fields[$FieldId] = $this->
GetField($FieldId);
1507 # first time through, set up our lookup array 1508 if (!isset(self::$FieldNamesCache))
1510 self::LoadFieldNamesCache();
1513 # if we were given a field id, check to see if it exists 1514 if (is_numeric($Field) &&
1515 array_key_exists($Field, self::$FieldNamesCache))
1520 # otherwise, try to look up this field 1523 $FieldId = self::GetCanonicalFieldIdentifier($Field);
1524 return array_key_exists($FieldId, $ValidFieldIds) ?
1527 catch (Exception $e)
1529 # if we can't find the field, then it doesn't exist 1551 # if field object was passed in 1554 # return identifier from field to caller 1555 return $Field->Id();
1557 # else if field ID was passed in 1558 elseif (is_numeric($Field))
1560 # return supplied field ID to caller 1563 # else if field name was passed in 1564 elseif (is_string($Field))
1566 # retrieve field names if not already loaded 1567 if (!isset(self::$FieldNamesCache))
1569 self::LoadFieldNamesCache();
1572 # look for the specified name with a schema prefix 1573 $MatchingFields = array_filter(
1574 self::$FieldNamesCache,
function($FieldInfo) use ($Field)
1576 return ($FieldInfo[
"SchemaId"] != self::SCHEMAID_DEFAULT &&
1577 $FieldInfo[
"SchemaPrefix"].$FieldInfo[
"FieldName"] == $Field);
1579 $Ids = array_keys($MatchingFields);
1581 # if we find a unique schema-prefixed name, use that 1582 # (note that more than one schema-prefixed name shouldn't 1583 # be possible because fields must be unique inside a schema) 1584 if (count($Ids) == 1)
1586 return (
int)$Ids[0];
1588 elseif (count($Ids) > 1)
1590 throw new Exception(
"Multiple schema-name prefixed " 1591 .
"metadata fields found, which should be impossible.");
1594 # look for field with specified name 1595 $MatchingFields = array_filter(
1596 self::$FieldNamesCache,
function($FieldInfo) use ($Field)
1598 return ($FieldInfo[
"FieldName"] == $Field);
1600 $Ids = array_keys($MatchingFields);
1602 # if one matching field was found 1603 $FieldsFound = count($Ids);
1604 if ($FieldsFound == 1)
1606 # return ID for matching field 1607 return (
int)$Ids[0];
1609 # else if multiple matching fields were found 1610 elseif ($FieldsFound > 1)
1612 # return ID of field with the lowest schema ID 1613 $LowestSchemaId = PHP_INT_MAX;
1614 $ResultFieldId = NULL;
1615 foreach ($MatchingFields as $FieldId => $FieldInfo)
1617 if ($FieldInfo[
"SchemaId"] < $LowestSchemaId)
1619 $ResultFieldId = $FieldId;
1620 $LowestSchemaId = $FieldInfo[
"SchemaId"];
1624 if ($ResultFieldId === NULL)
1626 throw new Exception(
1627 "Multiple fields called '".$Field.
"' found " 1628 .
"but none of them have a SchemaId less than" 1629 .
"PHP_INT_MAX, which should be impossible.");
1632 return $ResultFieldId;
1634 # else error out because no matching field found 1637 throw new Exception(
"No metadata field found with the" 1638 .
" name \"".$Field.
"\".");
1641 # else error out because we were given an illegal field argument 1644 throw new InvalidArgumentException(
1645 "Illegal field argument supplied.");
1666 $Id = self::GetCanonicalFieldIdentifier($Field);
1668 # retrieve printable labels if not yet populated 1669 if (!isset(self::$FieldNamesCache))
1671 self::LoadFieldNamesCache();
1674 # if we have a label for this field, return it 1675 if (isset(self::$FieldNamesCache[$Id]))
1677 $DisplayName = strlen(self::$FieldNamesCache[$Id][
"FieldLabel"]) ?
1678 self::$FieldNamesCache[$Id][
"FieldLabel"] :
1679 self::$FieldNamesCache[$Id][
"FieldName"] ;
1680 return self::$FieldNamesCache[$Id][
"SchemaPrefix"].$DisplayName;
1683 # otherwise return a blank string 1697 # start out assuming we won't find any values to translate 1698 $ReturnValues = array();
1700 # try to grab the specified field 1705 catch (Exception $e)
1707 # field no longer exists, so there are no values to translate 1708 return $ReturnValues;
1711 # if incoming value is not an array 1712 if (!is_array($Values))
1714 # convert incoming value to an array 1715 $Values = array($Values);
1718 # for each incoming value 1719 foreach ($Values as $Value)
1721 # look up value for index 1722 if ($Field->Type() == self::MDFTYPE_FLAG)
1724 # (for flag fields the value index (0 or 1) is used in Database) 1727 $ReturnValues[] =
"=".$Value;
1730 elseif ($Field->Type() == self::MDFTYPE_NUMBER)
1732 # (for flag fields the value index (0 or 1) is used in Database) 1735 $ReturnValues[] =
">=".$Value;
1738 elseif ($Field->Type() == self::MDFTYPE_USER)
1740 $User =
new CWUser(intval($Value));
1743 $ReturnValues[] =
"=".$User->Get(
"UserName");
1746 elseif ($Field->Type() == self::MDFTYPE_OPTION)
1748 if (!isset($PossibleFieldValues))
1750 $PossibleFieldValues = $Field->GetPossibleValues();
1753 if (isset($PossibleFieldValues[$Value]))
1755 $ReturnValues[] =
"=".$PossibleFieldValues[$Value];
1760 $NewValue = $Field->GetValueForId($Value);
1761 if ($NewValue !== NULL)
1763 $ReturnValues[] =
"=".$NewValue;
1768 # return array of translated values to caller 1769 return $ReturnValues;
1778 return array_keys(self::GetAllSchemaNames());
1788 $DB->Query(
"SELECT SchemaId, Name FROM MetadataSchemas");
1789 return $DB->FetchColumn(
"Name",
"SchemaId");
1799 # fetch IDs of all metadata schemas 1800 $SchemaIds = self::GetAllSchemaIds();
1802 # construct objects from the IDs 1804 foreach ($SchemaIds as $SchemaId)
1809 # return schemas to caller 1821 # list of priv types we'll be checking 1823 "AuthoringPrivileges",
1824 "EditingPrivileges",
1825 "ViewingPrivileges");
1827 # iterate over each schema 1828 foreach (self::GetAllSchemas() as $Schema)
1830 # see if the provided field is checked in any of the 1831 # schema-level privs, returning TRUE if so 1832 foreach ($PrivTypes as $PrivType)
1834 if ($Schema->$PrivType()->ChecksField($FieldId))
1840 # otherwise, iterate over all the field-level privs, returning true 1841 # if any of those check the provided field 1842 foreach ($Schema->GetFields() as $Field)
1844 foreach ($PrivTypes as $PrivType)
1846 if ($Field->$PrivType()->ChecksField($FieldId))
1854 # nothing checks this field, return FALSE 1866 $Id =
$DB->Query(
"SELECT SchemaId FROM MetadataSchemas" 1867 .
" WHERE Name = '".addslashes($Name).
"'",
"SchemaId");
1868 return ($Id === FALSE) ? NULL : $Id;
1878 if (is_callable($Callback))
1880 self::$OwnerListRetrievalFunction = $Callback;
1891 # if an owner list retrieval function and default schema exists 1892 if (self::$OwnerListRetrievalFunction
1893 && self::SchemaExistsWithId(self::SCHEMAID_DEFAULT))
1895 # retrieve the list of owners that currently exist 1896 $OwnerList = call_user_func(self::$OwnerListRetrievalFunction);
1898 # an array is expected 1899 if (is_array($OwnerList))
1903 # get each metadata field that is owned by a plugin 1904 $OwnedFields = $Schema->GetOwnedFields();
1906 # loop through each owned field 1907 foreach ($OwnedFields as $OwnedField)
1909 # the owner of the current field 1910 $Owner = $OwnedField->Owner();
1912 # if the owner of the field is in the list of owners that 1913 # currently exist, i.e., available plugins 1914 if (in_array($Owner, $OwnerList))
1916 # enable the field and reset its "enable on owner return" 1917 # flag if the "enable on owner return" flag is currently 1918 # set to true. in other words, re-enable the field since 1919 # the owner has returned to the list of existing owners 1920 if ($OwnedField->EnableOnOwnerReturn())
1922 $OwnedField->Enabled(TRUE);
1923 $OwnedField->EnableOnOwnerReturn(FALSE);
1927 # if the owner of the field is *not* in the list of owners 1928 # that currently exist, i.e., available plugins 1931 # first, see if the field is currently enabled since it 1932 # will determine whether the field is re-enabled when 1933 # the owner becomes available again 1934 $Enabled = $OwnedField->Enabled();
1936 # if the field is enabled, set its "enable on owner 1937 # return" flag to true and disable the field. nothing 1938 # needs to be done if the field is already disabled 1941 $OwnedField->EnableOnOwnerReturn($Enabled);
1942 $OwnedField->Enabled(FALSE);
1960 $this->FieldCompareDisplayOrder[$Field->Id()] = $Index++;
1967 $this->FieldCompareEditOrder[$Field->Id()] = $Index++;
2007 if ($this->FieldCompareType == self::MDFORDER_ALPHABETICAL)
2009 return ($FieldA->GetDisplayName() < $FieldB->GetDisplayName()) ? -1 : 1;
2012 if ($this->FieldCompareType == self::MDFORDER_EDITING)
2022 $PositionA = GetArrayValue($Order, $FieldA->Id(), 0);
2023 $PositionB = GetArrayValue($Order, $FieldB->Id(), 0);
2025 return $PositionA < $PositionB ? -1 : 1;
2034 self::$FieldCache = NULL;
2035 self::$FieldNamesCache = NULL;
2038 # ---- PRIVATE INTERFACE ------------------------------------------------- 2040 private $AuthoringPrivileges;
2041 private $EditingPrivileges;
2042 private $ErrorMsgs = array();
2043 private $FieldCompareType;
2045 private $NewFields = array();
2046 private $ViewingPrivileges;
2049 private static $FieldMappings;
2050 private static $ValueCache = NULL;
2052 private static $FieldCache = NULL;
2053 private static $FieldNamesCache = NULL;
2070 private static function LoadFieldNamesCache()
2072 $SchemaNames = self::GetAllSchemaNames();
2075 $DB->Query(
"SELECT SchemaId, FieldId, FieldName, Label FROM MetadataFields");
2076 while ($Row =
$DB->FetchRow())
2078 $SchemaPrefix = ($Row[
"SchemaId"] != self::SCHEMAID_DEFAULT)
2079 ? $SchemaNames[$Row[
"SchemaId"]].
": " :
"";
2081 $TrimmedLabel = trim($Row[
"Label"]);
2082 $TrimmedName = trim($Row[
"FieldName"]);
2084 self::$FieldNamesCache[$Row[
"FieldId"]] = [
2085 "SchemaId" => $Row[
"SchemaId"],
2086 "SchemaPrefix" => $SchemaPrefix,
2087 "FieldName" => $TrimmedName,
2088 "FieldLabel" => $TrimmedLabel,
2100 private function ConvertXmlToPrivilegeSet($Xml)
2102 # clear any existing errors 2103 if (array_key_exists(__METHOD__, $this->ErrorMsgs))
2104 { unset($this->ErrorMsgs[__METHOD__]); }
2106 # create new privilege set 2109 # for each XML child 2110 foreach ($Xml as $Tag => $Value)
2112 # take action based on element name 2115 case "PrivilegeSet":
2116 # convert child data to new set 2117 $NewSet = $this->ConvertXmlToPrivilegeSet($Value);
2119 # add new set to our privilege set 2120 $PrivSet->AddSet($NewSet);
2123 case "AddCondition":
2124 # start with default values for optional parameters 2125 unset($ConditionField);
2126 $ConditionValue = NULL;
2127 $ConditionOperator =
"==";
2129 # pull out parameters 2130 foreach ($Value as $ParamName => $ParamValue)
2132 $ParamValue = trim($ParamValue);
2136 $ConditionField = $this->
GetField($ParamValue, TRUE);
2137 if ($ConditionField === NULL)
2139 # record error about unknown field 2140 $this->ErrorMsgs[__METHOD__][] =
2141 "Unknown metadata field name found" 2142 .
" in AddCondition (".$ParamValue.
").";
2150 $ConditionValue = (string)$ParamValue;
2152 if ($ConditionValue ==
"NULL")
2154 $ConditionValue = NULL;
2156 elseif ($ConditionValue ==
"TRUE")
2158 $ConditionValue = TRUE;
2160 elseif ($ConditionValue ==
"FALSE")
2162 $ConditionValue = FALSE;
2167 $ConditionOperator = (string)$ParamValue;
2171 # record error about unknown parameter name 2172 $this->ErrorMsgs[__METHOD__][] =
2173 "Unknown tag found in AddCondition (" 2183 if (!isset($ConditionField))
2185 # record error about no field value 2186 $this->ErrorMsgs[__METHOD__][] =
2187 "No metadata field specified in AddCondition.";
2193 # if this is a vocabulary field 2196 if ($Factory !== NULL)
2198 # look up the id of the provided value 2199 $ConditionValue = $Factory->GetItemIdByName(
2202 # if none was found, error out 2203 if ($ConditionValue === NULL)
2205 $this->ErrorMsgs[__METHOD__][] =
2206 "Invalid value for field specified in AddCondition.";
2211 # add conditional to privilege set 2212 $PrivSet->AddCondition($ConditionField,
2213 $ConditionValue, $ConditionOperator);
2217 # strip any excess whitespace off of value 2218 $Value = trim($Value);
2220 # if child looks like valid method name 2221 if (method_exists(
"PrivilegeSet", $Tag))
2223 # convert constants if needed 2224 if (defined($Value))
2226 $Value = constant($Value);
2228 # convert booleans if needed 2229 elseif (strtoupper($Value) ==
"TRUE")
2233 elseif (strtoupper($Value) ==
"FALSE")
2237 # convert privilege flag names if needed and appropriate 2238 elseif (preg_match(
"/Privilege$/", $Tag))
2241 if (!isset($Privileges))
2244 $Privileges = $PFactory->GetPrivileges(TRUE, FALSE);
2246 if (in_array($Value, $Privileges))
2248 $Value = array_search($Value, $Privileges);
2252 # set value using child data 2253 $PrivSet->$Tag((
string)$Value);
2257 # record error about bad tag 2258 $this->ErrorMsgs[__METHOD__][] =
2259 "Unknown tag encountered (".$Tag.
").";
2268 # return new privilege set to caller 2281 return $this->DB->UpdateValue(
"MetadataSchemas", $ColumnName, $NewValue,
2282 "SchemaId = ".intval($this->
Id),
2283 self::$ValueCache[$this->
Id]);
GetHighestItemId($IgnoreSqlCondition=FALSE)
Retrieve highest item ID in use.
GetItemIdByName($Name, $IgnoreCase=FALSE)
Retrieve item ID by name.
SQL database abstraction object with smart query caching.
Factory which extracts all defined privileges from the database.
static GetMyCaller()
Get string with file and line number for call to current function.
Set of privileges used to access resource information or other parts of the system.
ItemExists($ItemId, $IgnoreSqlCondition=FALSE)
Check that item exists with specified ID.
ClearCaches()
Clear item information caches.
Represents a "resource" in CWIS.
static Singularize($Word)
Singularize an English word.
Common factory class for item manipulation.
NameIsInUse($Name, $IgnoreCase=FALSE)
Check whether item name is currently in use.
Factory for Resource objects.
CWIS-specific user class.
Convenience class for generating an HTML select/option form element.