CWIS Developer Documentation
MetadataSchema.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: MetadataSchema.php
4 #
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
8 #
9 
14 {
15 
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17 
18  # metadata field base types
19  # (must parallel MetadataFields.FieldType declaration in install/CreateTables.sql
20  # and MetadataField::$FieldTypeDBEnums declaration below)
21  const MDFTYPE_TEXT = 1;
22  const MDFTYPE_PARAGRAPH = 2;
23  const MDFTYPE_NUMBER = 4;
24  const MDFTYPE_DATE = 8;
25  const MDFTYPE_TIMESTAMP = 16;
26  const MDFTYPE_FLAG = 32;
27  const MDFTYPE_TREE = 64;
29  const MDFTYPE_OPTION = 256;
30  const MDFTYPE_USER = 512;
31  const MDFTYPE_IMAGE = 1024;
32  const MDFTYPE_FILE = 2048;
33  const MDFTYPE_URL = 4096;
34  const MDFTYPE_POINT = 8192;
35  const MDFTYPE_REFERENCE = 16384;
36 
37  # types of field ordering
38  const MDFORDER_DISPLAY = 1;
39  const MDFORDER_EDITING = 2;
41 
42  # error status codes
43  const MDFSTAT_OK = 1;
44  const MDFSTAT_ERROR = 2;
48  const MDFSTAT_ILLEGALNAME = 32;
50  const MDFSTAT_ILLEGALLABEL = 128;
51 
52  # special schema IDs
53  const SCHEMAID_DEFAULT = 0;
54  const SCHEMAID_RESOURCES = 0;
55  const SCHEMAID_USER = 1;
56  const SCHEMAID_USERS = 1;
57 
58  # resource names
59  const RESOURCENAME_DEFAULT = "Resource";
60  const RESOURCENAME_USER = "User";
61 
62  # names used for display and edit orders
63  const ORDER_DISPLAY_NAME = "Display";
64  const ORDER_EDIT_NAME = "Edit";
65 
74  public function __construct($SchemaId = self::SCHEMAID_DEFAULT)
75  {
76  # set schema ID
77  $this->Id = $SchemaId;
78 
79  # set up item factory base class
80  parent::__construct(
81  "MetadataField", "MetadataFields", "FieldId", "FieldName", FALSE,
82  "SchemaId = ".intval($this->Id()));
83 
84  # load schema info from database
85  $this->DB->Query("SELECT * FROM MetadataSchemas"
86  ." WHERE SchemaId = ".intval($SchemaId));
87  if ($this->DB->NumRowsSelected() < 1)
88  {
89  throw new Exception("Attempt to load metadata schema with "
90  ." invalid ID (".$SchemaId.") at "
91  .StdLib::GetMyCaller().".");
92  }
93  $Info = $this->DB->FetchRow();
94  $this->Name = $Info["Name"];
95  $this->AuthoringPrivileges = new PrivilegeSet($Info["AuthoringPrivileges"]);
96  $this->EditingPrivileges = new PrivilegeSet($Info["EditingPrivileges"]);
97  $this->ViewingPrivileges = new PrivilegeSet($Info["ViewingPrivileges"]);
98  $this->ViewPage = $Info["ViewPage"];
99 
100  if (!isset(self::$FieldMappings[$this->Id]))
101  {
102  self::$FieldMappings[$this->Id] = array();
103  }
104 
105  # start with field info caching enabled
106  $this->CachingOn = TRUE;
107  }
108 
118  public static function GetConstantName($Value, $Prefix = NULL)
119  {
120  # retrieve all constants for class
121  $Reflect = new ReflectionClass(get_class());
122  $Constants = $Reflect->getConstants();
123 
124  # for each constant
125  foreach ($Constants as $CName => $CValue)
126  {
127  # if value matches and prefix (if supplied) matches
128  if (($CValue == $Value)
129  && (($Prefix === NULL) || (strpos($CName, $Prefix) === 0)))
130  {
131  # return name to caller
132  return $CName;
133  }
134  }
135 
136  # report to caller that no matching constant was found
137  return NULL;
138  }
139 
157  public static function Create($Name,
158  PrivilegeSet $AuthorPrivs = NULL,
159  PrivilegeSet $EditPrivs = NULL,
160  PrivilegeSet $ViewPrivs = NULL,
161  $ViewPage = "",
162  $ResourceName = NULL)
163  {
164  # supply privilege settings if none provided
165  if ($AuthorPrivs === NULL) { $AuthorPrivs = new PrivilegeSet(); }
166  if ($EditPrivs === NULL) { $EditPrivs = new PrivilegeSet(); }
167  if ($ViewPrivs === NULL) { $ViewPrivs = new PrivilegeSet(); }
168 
169  # add schema to database
170  $DB = new Database;
171  if (strtoupper($Name) == "RESOURCES")
172  {
173  $Id = self::SCHEMAID_DEFAULT;
174  }
175  elseif (strtoupper($Name) == "USER")
176  {
177  $Id = self::SCHEMAID_USER;
178  }
179  else
180  {
181  $Id = $DB->Query("SELECT SchemaId FROM MetadataSchemas"
182  ." ORDER BY SchemaId DESC LIMIT 1", "SchemaId") + 1;
183  }
184  $DB->Query("INSERT INTO MetadataSchemas"
185  ." (SchemaId, Name, ViewPage,"
186  ." AuthoringPrivileges, EditingPrivileges, ViewingPrivileges)"
187  ." VALUES (".intval($Id).","
188  ."'".addslashes($Name)."',"
189  ."'".$DB->EscapeString($ViewPage)."',"
190  ."'".$DB->EscapeString($AuthorPrivs->Data())."',"
191  ."'".$DB->EscapeString($EditPrivs->Data())."',"
192  ."'".$DB->EscapeString($ViewPrivs->Data())."')");
193 
194  # construct the new schema
195  $Schema = new MetadataSchema($Id);
196 
197  # set schema name if none supplied
198  if (!strlen($Name))
199  {
200  $Schema->Name("Metadata Schema ".$Id);
201  }
202 
203  # set the resource name if one is supplied
204  if ($ResourceName === NULL)
205  {
206  $ResourceName = StdLib::Singularize($Name);
207  }
208  $Schema->ResourceName($ResourceName);
209 
210  # create display and edit orders
211  MetadataFieldOrder::Create($Schema, self::ORDER_DISPLAY_NAME, array() );
212  MetadataFieldOrder::Create($Schema, self::ORDER_EDIT_NAME, array() );
213 
214  # return the new schema
215  return $Schema;
216  }
217 
222  public function Delete()
223  {
224  # delete resources associated with schema
225  $RFactory = new ResourceFactory($this->Id);
226  $ResourceIds = $RFactory->GetItemIds();
227  foreach ($ResourceIds as $ResourceId)
228  {
229  $Resource = new Resource($ResourceId);
230  $Resource->Delete();
231  }
232 
233  # delete fields associated with schema
234  $Fields = $this->GetFields(NULL, NULL, TRUE, TRUE);
235  foreach ($Fields as $FieldId => $Field)
236  {
237  $this->DropField($FieldId);
238  }
239 
240  # delete metadata field orders associated with schema
241  foreach (MetadataFieldOrder::GetOrdersForSchema($this) as $Order)
242  {
243  $Order->Delete();
244  }
245 
246  # remove schema info from database
247  $this->DB->Query("DELETE FROM MetadataSchemas WHERE SchemaId = "
248  .intval($this->Id));
249  }
250 
256  public static function SchemaExistsWithId($SchemaId)
257  {
258  $DB = new Database();
259  $DB->Query("SELECT * FROM MetadataSchemas"
260  ." WHERE SchemaId = ".intval($SchemaId));
261  return ($DB->NumRowsSelected() > 0) ? TRUE : FALSE;
262  }
263 
269  public function Id()
270  {
271  # return value to caller
272  return intval($this->Id);
273  }
274 
280  public function Name($NewValue = NULL)
281  {
282  # set new name if one supplied
283  if ($NewValue !== NULL)
284  {
285  $this->DB->Query("UPDATE MetadataSchemas"
286  ." SET Name = '".addslashes($NewValue)."'"
287  ." WHERE SchemaId = '".intval($this->Id)."'");
288  $this->Name = $NewValue;
289  }
290 
291  # get the name if it hasn't been cached yet
292  if (!isset($this->Name))
293  {
294  $this->Name = $this->DB->Query("SELECT * FROM MetadataSchemas"
295  ." WHERE SchemaId = '".intval($this->Id)."'", "Name");
296  }
297 
298  # return current value to caller
299  return $this->Name;
300  }
301 
309  public function AbbreviatedName($NewValue = NULL)
310  {
311  # set new abbreviated name if one supplied
312  if ($NewValue !== NULL)
313  {
314  $this->DB->Query("UPDATE MetadataSchemas"
315  ." SET AbbreviatedName = '".addslashes($NewValue)."'"
316  ." WHERE SchemaId = '".intval($this->Id)."'");
317  $this->AbbreviatedName = $NewValue;
318  }
319 
320  # get the abbreviated name if it hasn't been cached yet
321  if (!isset($this->AbbreviatedName))
322  {
323  $this->AbbreviatedName = $this->DB->Query(
324  "SELECT AbbreviatedName FROM MetadataSchemas"
325  ." WHERE SchemaId = '".intval($this->Id)."'", "AbbreviatedName");
326  }
327 
328  # most schema won't have this field set
329  # fall back to the default value: the first letter of
330  # the schema name
331  if ($this->AbbreviatedName === NULL || $this->AbbreviatedName == "")
332  {
333  $this->AbbreviatedName = strtoupper(substr($this->Name(), 0, 1));
334  }
335 
336  # return current value to caller
337  return $this->AbbreviatedName;
338  }
339 
345  public function ResourceName($NewValue = NULL)
346  {
347  # set new resource name if one supplied
348  if ($NewValue !== NULL)
349  {
350  $this->DB->Query("
351  UPDATE MetadataSchemas
352  SET ResourceName = '".addslashes($NewValue)."'
353  WHERE SchemaId = '".intval($this->Id)."'");
354  $this->ResourceName = $NewValue;
355  }
356 
357  # get the name if it hasn't been cached yet
358  if (!isset($this->ResourceName))
359  {
360  $this->ResourceName = $this->DB->Query("
361  SELECT * FROM MetadataSchemas
362  WHERE SchemaId = '".intval($this->Id)."'",
363  "ResourceName");
364 
365  # use the default resource name if one isn't set
366  if (!strlen(trim($this->ResourceName)))
367  {
368  $this->ResourceName = self::RESOURCENAME_DEFAULT;
369  }
370  }
371 
372  # return current value to caller
373  return $this->ResourceName;
374  }
375 
381  public function ViewPage($NewValue = NULL)
382  {
383  # set new viewing page if one supplied
384  if ($NewValue !== NULL)
385  {
386  $this->DB->Query("UPDATE MetadataSchemas"
387  ." SET ViewPage = '".addslashes($NewValue)."'"
388  ." WHERE SchemaId = '".intval($this->Id)."'");
389  $this->ViewPage = $NewValue;
390  }
391 
392  # get the view page if it hasn't been cached yet
393  if (!isset($this->ViewPage))
394  {
395  $this->ViewPage = $this->DB->Query("SELECT * FROM MetadataSchemas"
396  ." WHERE SchemaId = '".intval($this->Id)."'", "ViewPage");
397  }
398 
399  # return current value to caller
400  return $this->ViewPage;
401  }
402 
408  public function AuthoringPrivileges(PrivilegeSet $NewValue = NULL)
409  {
410  # if new privileges supplied
411  if ($NewValue !== NULL)
412  {
413  # store new privileges in database
414  $this->DB->Query("UPDATE MetadataSchemas"
415  ." SET AuthoringPrivileges = '"
416  .$this->DB->EscapeString($NewValue->Data())."'"
417  ." WHERE SchemaId = ".intval($this->Id));
418  $this->AuthoringPrivileges = $NewValue;
419  }
420 
421  # return current value to caller
422  return $this->AuthoringPrivileges;
423  }
424 
430  public function EditingPrivileges(PrivilegeSet $NewValue = NULL)
431  {
432  # if new privileges supplied
433  if ($NewValue !== NULL)
434  {
435  # store new privileges in database
436  $this->DB->Query("UPDATE MetadataSchemas"
437  ." SET EditingPrivileges = '"
438  .$this->DB->EscapeString($NewValue->Data())."'"
439  ." WHERE SchemaId = ".intval($this->Id));
440  $this->EditingPrivileges = $NewValue;
441  }
442 
443  # return current value to caller
444  return $this->EditingPrivileges;
445  }
446 
452  public function ViewingPrivileges(PrivilegeSet $NewValue = NULL)
453  {
454  # if new privileges supplied
455  if ($NewValue !== NULL)
456  {
457  # store new privileges in database
458  $this->DB->Query("UPDATE MetadataSchemas"
459  ." SET ViewingPrivileges = '"
460  .$this->DB->EscapeString($NewValue->Data())."'"
461  ." WHERE SchemaId = ".intval($this->Id));
462  $this->ViewingPrivileges = $NewValue;
463  }
464 
465  # return current value to caller
466  return $this->ViewingPrivileges;
467  }
468 
476  public function UserCanAuthor($User)
477  {
478  # get authoring privilege set for schema
479  $AuthorPrivs = $this->AuthoringPrivileges();
480 
481  # user can author if privileges are greater than resource set
482  $CanAuthor = $AuthorPrivs->MeetsRequirements($User);
483 
484  # allow plugins to modify result of permission check
485  $SignalResult = $GLOBALS["AF"]->SignalEvent(
486  "EVENT_RESOURCE_AUTHOR_PERMISSION_CHECK", array(
487  "Schema" => $this,
488  "User" => $User,
489  "CanAuthor" => $CanAuthor));
490  $CanAuthor = $SignalResult["CanAuthor"];
491 
492  # report back to caller whether user can author field
493  return $CanAuthor;
494  }
495 
503  public function UserCanEdit($User)
504  {
505  # get editing privilege set for schema
506  $EditPrivs = $this->EditingPrivileges();
507 
508  # user can edit if privileges are greater than resource set
509  $CanEdit = $EditPrivs->MeetsRequirements($User);
510 
511  # allow plugins to modify result of permission check
512  $SignalResult = $GLOBALS["AF"]->SignalEvent(
513  "EVENT_RESOURCE_EDIT_PERMISSION_CHECK", array(
514  "Resource" => NULL,
515  "User" => $User,
516  "CanEdit" => $CanEdit,
517  "Schema" => $this, ));
518  $CanEdit = $SignalResult["CanEdit"];
519 
520  # report back to caller whether user can edit field
521  return $CanEdit;
522  }
523 
531  public function UserCanView($User)
532  {
533  # get viewing privilege set for schema
534  $ViewPrivs = $this->ViewingPrivileges();
535 
536  # user can view if privileges are greater than resource set
537  $CanView = $ViewPrivs->MeetsRequirements($User);
538 
539  # allow plugins to modify result of permission check
540  $SignalResult = $GLOBALS["AF"]->SignalEvent(
541  "EVENT_RESOURCE_VIEW_PERMISSION_CHECK", array(
542  "Resource" => NULL,
543  "User" => $User,
544  "CanView" => $CanView,
545  "Schema" => $this, ));
546  $CanView = $SignalResult["CanView"];
547 
548  # report back to caller whether user can view field
549  return $CanView;
550  }
551 
557  public function GetViewPageIdParameter()
558  {
559  # get the query/GET parameters for the view page
560  $Query = parse_url($this->ViewPage(), PHP_URL_QUERY);
561 
562  # the URL couldn't be parsed
563  if (!is_string($Query))
564  {
565  return NULL;
566  }
567 
568  # parse the GET parameters out of the query string
569  $GetVars = ParseQueryString($Query);
570 
571  # search for the ID parameter
572  $Result = array_search("\$ID", $GetVars);
573 
574  return $Result !== FALSE ? $Result : NULL;
575  }
576 
589  public function PathMatchesViewPage($Path)
590  {
591  # get the query/GET parameters for the view page
592  $Query = parse_url($this->ViewPage(), PHP_URL_QUERY);
593 
594  # can't perform matching if the URL couldn't be parsed
595  if (!is_string($Query))
596  {
597  return FALSE;
598  }
599 
600  # parse the GET parameters out of the query string
601  $GetVars = ParseQueryString($Query);
602 
603  # now, get the query/GET parameters from the path given
604  $PathQuery = parse_url($Path, PHP_URL_QUERY);
605 
606  # can't perform matching if the URL couldn't be parsed
607  if (!is_string($PathQuery))
608  {
609  return FALSE;
610  }
611 
612  # parse the GET parameters out of the path's query string
613  $PathGetVars = ParseQueryString($PathQuery);
614 
615  # make sure the given path GET parameters contain at least the GET
616  # parameters from the view page and that all non-variable parameters are
617  # equal. the path GET parameters may contain more, which is okay
618  foreach ($GetVars as $GetVarName => $GetVarValue)
619  {
620  # there's a required parameter that is not included in the path GET
621  # parameters
622  if (!array_key_exists($GetVarName, $PathGetVars))
623  {
624  return FALSE;
625  }
626 
627  # require the path's value to be equal to the view page's value if
628  # the view page's value is not a variable,
629  if ($PathGetVars[$GetVarName] != $GetVarValue
630  && (!strlen($GetVarValue) || $GetVarValue{0} != "$"))
631  {
632  return FALSE;
633  }
634  }
635 
636  # the path matches the view page path
637  return TRUE;
638  }
639 
644  public function CacheData($NewValue)
645  {
646  $this->CachingOn = $NewValue;
647  }
648 
658  public function AddField(
659  $FieldName, $FieldType, $Optional = TRUE, $DefaultValue = NULL)
660  {
661  # clear any existing error messages
662  if (array_key_exists(__METHOD__, $this->ErrorMsgs))
663  { unset($this->ErrorMsgs[__METHOD__]); }
664 
665  # create new field
666  try
667  {
668  $Field = MetadataField::Create($this->Id(), $FieldType,
669  $FieldName, $Optional, $DefaultValue);
670  }
671  catch (Exception $Exception)
672  {
673  $this->ErrorMsgs[__METHOD__][] = $Exception->getMessage();
674  $Field = NULL;
675  }
676 
677  # return new field to caller
678  return $Field;
679  }
680 
695  public function AddFieldsFromXmlFile($FileName, $Owner = NULL, $TestRun = FALSE)
696  {
697  # clear loading status
698  $this->NewFields = array();
699  if (array_key_exists(__METHOD__, $this->ErrorMsgs))
700  { unset($this->ErrorMsgs[__METHOD__]); }
701 
702  # check that file exists and is readable
703  if (!file_exists($FileName))
704  {
705  $this->ErrorMsgs[__METHOD__][] = "Could not find XML file '"
706  .$FileName."'.";
707  return FALSE;
708  }
709  elseif (!is_readable($FileName))
710  {
711  $this->ErrorMsgs[__METHOD__][] = "Could not read from XML file '"
712  .$FileName."'.";
713  return FALSE;
714  }
715 
716  # load XML from file
717  libxml_use_internal_errors(TRUE);
718  $XmlData = simplexml_load_file($FileName);
719  $Errors = libxml_get_errors();
720  libxml_use_internal_errors(FALSE);
721 
722  # if XML load failed
723  if ($XmlData === FALSE)
724  {
725  # retrieve XML error messages
726  foreach ($Errors as $Err)
727  {
728  $ErrType = ($Err->level == LIBXML_ERR_WARNING) ? "Warning"
729  : (($Err->level == LIBXML_ERR_WARNING) ? "Error"
730  : "Fatal Error");
731  $this->ErrorMsgs[__METHOD__][] = "XML ".$ErrType.": ".$Err->message
732  ." (".$Err->file.":".$Err->line.",".$Err->column.")";
733  }
734  }
735  # else if no metadata fields found record error message
736  elseif (!count($XmlData->MetadataField))
737  {
738  $this->ErrorMsgs[__METHOD__][] = "No metadata fields found.";
739  }
740  # else process metadata fields
741  else
742  {
743  # for each metadata field entry found
744  $FieldsAdded = 0;
745  $FieldIndex = 0;
746  foreach ($XmlData->MetadataField as $FieldXml)
747  {
748  $FieldIndex++;
749 
750  # pull out field type if present
751  if (isset($FieldXml->Type))
752  {
753  $FieldType = "MetadataSchema::".$FieldXml->Type;
754  if (!defined($FieldType))
755  {
756  $FieldType = "MetadataSchema::MDFTYPE_"
757  .strtoupper(preg_replace("/\\s+/", "",
758  $FieldXml->Type));
759  }
760  }
761 
762  # if required values are missing
763  if (!isset($FieldXml->Name) || !isset($FieldXml->Type)
764  || !defined($FieldType))
765  {
766  # add error message about required value missing
767  if (!isset($FieldXml->Name))
768  {
769  $this->ErrorMsgs[__METHOD__][] =
770  "Field name not found (MetadataField #"
771  .$FieldIndex.").";
772  }
773  else
774  {
775  $this->ErrorMsgs[__METHOD__][] =
776  "Valid type not found for field '"
777  .$FieldXml->Name."' (MetadataField #"
778  .$FieldIndex.").";
779  }
780  }
781  # else if there is not already a field with this name
782  elseif (!$this->NameIsInUse(trim($FieldXml->Name)))
783  {
784  # create new field
785  $Field = $this->AddField($FieldXml->Name, constant($FieldType));
786 
787  # if field creation failed
788  if ($Field === NULL)
789  {
790  # add any error message to our error list
791  $ErrorMsgs = $this->ErrorMessages("AddField");
792  foreach ($ErrorMsgs as $Msg)
793  {
794  $this->ErrorMsgs[__METHOD__][] =
795  $Msg." (AddField)";
796  }
797  }
798  else
799  {
800  # add field to list of created fields
801  $this->NewFields[$Field->Id()] = $Field;
802 
803  # for other field attributes
804  foreach ($FieldXml as $MethodName => $Value)
805  {
806  # if tags look valid and have not already been set
807  if (method_exists($Field, $MethodName)
808  && ($MethodName != "Name")
809  && ($MethodName != "Type"))
810  {
811  # if tag indicates privilege set
812  if (preg_match("/^[a-z]+Privileges\$/i",
813  $MethodName))
814  {
815  # save element for later processing
816  $PrivilegesToSet[$Field->Id()][$MethodName] = $Value;
817  }
818  else
819  {
820  # condense down any extraneous whitespace
821  $Value = preg_replace("/\s+/", " ", trim($Value));
822 
823  # set value for field
824  $Field->$MethodName($Value);
825  }
826  }
827  }
828 
829  # save the temp ID so that any privileges to set
830  # can be mapped to the actual ID when the field is
831  # made permanent
832  $TempId = $Field->Id();
833 
834  # make new field permanent
835  $Field->IsTempItem(FALSE);
836 
837  # map privileges to set to the permanent field ID
838  if (isset($PrivilegesToSet) &&
839  isset($PrivilegesToSet[$TempId]) )
840  {
841  # copy the privileges over
842  $PrivilegesToSet[$Field->Id()] =
843  $PrivilegesToSet[$TempId];
844 
845  # remove the values for the temp ID
846  unset($PrivilegesToSet[$TempId]);
847  }
848  }
849  }
850  }
851 
852  # if we have privileges to set
853  if (isset($PrivilegesToSet))
854  {
855  # for each field with privileges
856  foreach ($PrivilegesToSet as $FieldId => $Privileges)
857  {
858  # load the field for which to set the privileges
859  $Field = new MetadataField($FieldId);
860 
861  # for each set of privileges for field
862  foreach ($Privileges as $MethodName => $Value)
863  {
864  # convert privilege value
865  $Value = $this->ConvertXmlToPrivilegeSet($Value);
866 
867  # if conversion failed
868  if ($Value === NULL)
869  {
870  # add resulting error messages to our list
871  $ErrorMsgs = $this->ErrorMessages(
872  "ConvertXmlToPrivilegeSet");
873  foreach ($ErrorMsgs as $Msg)
874  {
875  $this->ErrorMsgs[__METHOD__][] =
876  $Msg." (ConvertXmlToPrivilegeSet)";
877  }
878  }
879  else
880  {
881  # set value for field
882  $Field->$MethodName($Value);
883  }
884  }
885  }
886  }
887 
888  # if errors were found during creation
889  if (array_key_exists(__METHOD__, $this->ErrorMsgs) || $TestRun)
890  {
891  # remove any fields that were created
892  foreach ($this->NewFields as $Field)
893  {
894  $Field->Drop();
895  }
896  $this->NewFields = array();
897  }
898  else
899  {
900  # set owner for new fields (if supplied)
901  if ($Owner !== NULL)
902  {
903  foreach ($this->NewFields as $Field)
904  {
905  $Field->Owner($Owner);
906  }
907  }
908  }
909  }
910 
911  # report success or failure based on whether errors were recorded
912  return (array_key_exists(__METHOD__, $this->ErrorMsgs)) ? FALSE : TRUE;
913  }
914 
920  public function NewFields()
921  {
922  return $this->NewFields;
923  }
924 
934  public function ErrorMessages($Method = NULL)
935  {
936  if ($Method === NULL)
937  {
938  return $this->ErrorMsgs;
939  }
940  else
941  {
942  if (!method_exists($this, $Method))
943  {
944  throw new Exception("Error messages requested for non-existent"
945  ." method (".$Method.").");
946  }
947  return array_key_exists(__CLASS__."::".$Method, $this->ErrorMsgs)
948  ? $this->ErrorMsgs[__CLASS__."::".$Method] : array();
949  }
950  }
951 
961  public function AddFieldFromXml($Xml)
962  {
963  # assume field addition will fail
964  $Field = self::MDFSTAT_ERROR;
965 
966  # add XML prefixes if needed
967  $Xml = trim($Xml);
968  if (!preg_match("/^<\?xml/i", $Xml))
969  {
970  if (!preg_match("/^<document>/i", $Xml))
971  {
972  $Xml = "<document>".$Xml."</document>";
973  }
974  $Xml = "<?xml version='1.0'?".">".$Xml;
975  }
976 
977  # parse XML
978  $XmlData = simplexml_load_string($Xml);
979 
980  # if required values are present
981  if (is_object($XmlData)
982  && isset($XmlData->Name)
983  && isset($XmlData->Type)
984  && constant("MetadataSchema::".$XmlData->Type))
985  {
986  # create the metadata field
987  $Field = $this->AddField(
988  $XmlData->Name,
989  constant("MetadataSchema::".$XmlData->Type));
990 
991  # if field creation succeeded
992  if ($Field != NULL)
993  {
994  # for other field attributes
995  foreach ($XmlData as $MethodName => $Value)
996  {
997  # if they look valid and have not already been set
998  if (method_exists($Field, $MethodName)
999  && ($MethodName != "Name")
1000  && ($MethodName != "Type"))
1001  {
1002  # if tag indicates privilege set
1003  if (preg_match("/^[a-z]+Privileges\$/i",
1004  $MethodName))
1005  {
1006  # save element for later processing
1007  $PrivilegesToSet[$MethodName] = $Value;
1008  }
1009  else
1010  {
1011  # condense down any extraneous whitespace
1012  $Value = preg_replace("/\s+/", " ", trim($Value));
1013 
1014  # set value for field
1015  $Field->$MethodName($Value);
1016  }
1017  }
1018  }
1019 
1020  # make new field permanent
1021  $Field->IsTempItem(FALSE);
1022 
1023  # if we have privileges to set
1024  if (isset($PrivilegesToSet))
1025  {
1026  # for each set of privileges for field
1027  foreach ($PrivilegesToSet as $MethodName => $Value)
1028  {
1029  # convert privilege value
1030  $Value = $this->ConvertXmlToPrivilegeSet($Value);
1031 
1032  # if conversion failed
1033  if ($Value === NULL)
1034  {
1035  # add resulting error messages to our list
1036  $ErrorMsgs = $this->ErrorMessages(
1037  "ConvertXmlToPrivilegeSet");
1038  foreach ($ErrorMsgs as $Msg)
1039  {
1040  $this->ErrorMsgs[__METHOD__][] =
1041  $Msg." (ConvertXmlToPrivilegeSet)";
1042  }
1043  }
1044  else
1045  {
1046  # set value for field
1047  $Field->$MethodName($Value);
1048  }
1049  }
1050  }
1051  }
1052  }
1053 
1054  # return new field (if any) to caller
1055  return $Field;
1056  }
1057 
1063  public function DropField($FieldId)
1064  {
1065  $Field = $this->GetField($FieldId);
1066  if ($Field !== NULL)
1067  {
1068  global $AF;
1069  $AF->SignalEvent("EVENT_PRE_FIELD_DELETE",
1070  array("FieldId" => $Field->Id()) );
1071 
1072  $Field->Drop();
1073  return TRUE;
1074  }
1075  else
1076  {
1077  return FALSE;
1078  }
1079  }
1080 
1086  public function GetField($FieldId)
1087  {
1088  static $Fields;
1089 
1090  # convert field name to ID if necessary
1091  if (!is_numeric($FieldId))
1092  {
1093  $FieldName = $FieldId;
1094  $FieldId = $this->GetFieldIdByName($FieldName);
1095  if ($FieldId === FALSE)
1096  {
1097  throw new InvalidArgumentException("Attempt to retrieve field"
1098  ." with unknown name (".$FieldName.").");
1099  }
1100  }
1101 
1102  # if caching is off or field is not already loaded
1103  if (($this->CachingOn != TRUE) || !isset($Fields[$FieldId]))
1104  {
1105  # retrieve field
1106  try
1107  {
1108  $Fields[$FieldId] = new MetadataField($FieldId);
1109  }
1110  catch (Exception $Exception)
1111  {
1112  $Fields[$FieldId] = NULL;
1113  }
1114  }
1115 
1116  # return field to caller if it comes from our schema
1117  return ($Fields[$FieldId] !== NULL &&
1118  $this->Id() == $Fields[$FieldId]->SchemaId()) ?
1119  $Fields[$FieldId] : NULL;
1120  }
1121 
1131  public function GetFieldByName($FieldName, $IgnoreCase = FALSE)
1132  {
1133  $FieldId = $this->GetFieldIdByName($FieldName, $IgnoreCase);
1134  return ($FieldId === FALSE) ? NULL : $this->GetField($FieldId);
1135  }
1136 
1143  public function GetFieldByLabel($FieldLabel, $IgnoreCase = FALSE)
1144  {
1145  $FieldId = $this->GetFieldIdByLabel($FieldLabel, $IgnoreCase);
1146  return ($FieldId === NULL) ? NULL : $this->GetField($FieldId);
1147  }
1148 
1156  public function GetFieldIdByName($FieldName, $IgnoreCase = FALSE)
1157  {
1158  return $this->GetItemIdByName($FieldName, $IgnoreCase);
1159  }
1160 
1168  public function GetFieldIdByLabel($FieldLabel, $IgnoreCase = FALSE)
1169  {
1170  static $FieldIdsByLabel;
1171 
1172  # if caching is off or field ID is already loaded
1173  if (($this->CachingOn != TRUE) || !isset($FieldIdsByLabel[$FieldLabel]))
1174  {
1175  # retrieve field ID from DB
1176  $Condition = $IgnoreCase
1177  ? "WHERE LOWER(Label) = '".addslashes(strtolower($FieldLabel))."'"
1178  : "WHERE Label = '".addslashes($FieldLabel)."'";
1179  $Condition .= " AND SchemaId = ".intval($this->Id);
1180  $FieldIdsByLabel[$FieldLabel] = $this->DB->Query(
1181  "SELECT FieldId FROM MetadataFields ".$Condition, "FieldId");
1182  }
1183 
1184  return $FieldIdsByLabel[$FieldLabel];
1185  }
1186 
1192  public function FieldExists($Field)
1193  {
1194  return is_numeric($Field)
1195  ? $this->ItemExists($Field)
1196  : $this->NameIsInUse($Field);
1197  }
1198 
1212  public function GetFields($FieldTypes = NULL, $OrderType = NULL,
1213  $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
1214  {
1215  # create empty array to pass back
1216  $Fields = array();
1217 
1218  # for each field type in database
1219  $this->DB->Query("SELECT FieldId, FieldType FROM MetadataFields"
1220  ." WHERE SchemaId = ".intval($this->Id)
1221  .(!$IncludeDisabledFields ? " AND Enabled != 0" : "")
1222  .(!$IncludeTempFields ? " AND FieldId >= 0" : ""));
1223  while ($Record = $this->DB->FetchRow())
1224  {
1225  # if field type is known
1226  if (array_key_exists($Record["FieldType"], MetadataField::$FieldTypePHPEnums))
1227  {
1228  # if no specific type requested or if field is of requested type
1229  if (($FieldTypes == NULL)
1230  || (MetadataField::$FieldTypePHPEnums[$Record["FieldType"]]
1231  & $FieldTypes))
1232  {
1233  # create field object and add to array to be passed back
1234  $Fields[$Record["FieldId"]] = $this->GetField($Record["FieldId"]);
1235  }
1236  }
1237  }
1238 
1239  # if field sorting requested
1240  if ($OrderType !== NULL)
1241  {
1242  # update field comparison ordering if not set yet
1243  if (!$this->FieldCompareOrdersSet())
1244  {
1245  $this->UpdateFieldCompareOrders();
1246  }
1247 
1248  $this->FieldCompareType = $OrderType;
1249 
1250  # sort field array by requested order type
1251  uasort($Fields, array($this, "CompareFieldOrder"));
1252  }
1253 
1254  # return array of field objects to caller
1255  return $Fields;
1256  }
1257 
1272  public function GetFieldNames($FieldTypes = NULL, $OrderType = NULL,
1273  $IncludeDisabledFields = FALSE, $IncludeTempFields = FALSE)
1274  {
1275  $Fields = $this->GetFields($FieldTypes, $OrderType,
1276  $IncludeDisabledFields, $IncludeTempFields);
1277 
1278  $FieldNames = array();
1279  foreach($Fields as $Field)
1280  {
1281  $FieldNames[$Field->Id()] = $Field->Name();
1282  }
1283 
1284  return $FieldNames;
1285  }
1286 
1305  public function GetFieldsAsOptionList($OptionListName, $FieldTypes = NULL,
1306  $SelectedFieldId = NULL, $IncludeNullOption = TRUE,
1307  $AddEntries = NULL, $AllowMultiple = FALSE, $Disabled = FALSE)
1308  {
1309  # retrieve requested fields
1310  $FieldNames = $this->GetFieldNames($FieldTypes);
1311 
1312  # transform field names to labels
1313  foreach ($FieldNames as $FieldId => $FieldName)
1314  {
1315  $FieldNames[$FieldId] = $this->GetField($FieldId)->GetDisplayName();
1316  }
1317 
1318  # add in null entry if requested
1319  if ($IncludeNullOption)
1320  {
1321  $FieldNames = array("" => "--") + $FieldNames;
1322  }
1323 
1324  # add additional entries if supplied
1325  if ($AddEntries)
1326  {
1327  $FieldNames = $FieldNames + $AddEntries;
1328  }
1329 
1330  # construct option list
1331  $OptList = new HtmlOptionList($OptionListName, $FieldNames, $SelectedFieldId);
1332  $OptList->MultipleAllowed($AllowMultiple);
1333  $OptList->Disabled($Disabled);
1334 
1335  # return option list HTML to caller
1336  return $OptList->GetHtml();
1337  }
1338 
1344  public function GetFieldTypes()
1345  {
1347  }
1348 
1354  public function GetAllowedFieldTypes()
1355  {
1357  }
1358 
1363  public function RemoveQualifierAssociations($QualifierIdOrObject)
1364  {
1365  # sanitize qualifier ID or grab it from object
1366  $QualifierIdOrObject = is_object($QualifierIdOrObject)
1367  ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
1368 
1369  # delete intersection records from database
1370  $this->DB->Query("DELETE FROM FieldQualifierInts"
1371  ." WHERE QualifierId = ".$QualifierIdOrObject);
1372  }
1373 
1379  public function QualifierIsInUse($QualifierIdOrObject)
1380  {
1381  # sanitize qualifier ID or grab it from object
1382  $QualifierIdOrObject = is_object($QualifierIdOrObject)
1383  ? $QualifierIdOrObject->Id() : intval($QualifierIdOrObject);
1384 
1385  # determine whether any fields use qualifier as default
1386  $DefaultCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount"
1387  ." FROM MetadataFields"
1388  ." WHERE DefaultQualifier = ".$QualifierIdOrObject,
1389  "RecordCount");
1390 
1391  # determine whether any fields are associated with qualifier
1392  $AssociationCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount"
1393  ." FROM FieldQualifierInts"
1394  ." WHERE QualifierId = ".$QualifierIdOrObject,
1395  "RecordCount");
1396 
1397  # report whether qualifier is in use based on defaults and associations
1398  return (($DefaultCount + $AssociationCount) > 0) ? TRUE : FALSE;
1399  }
1400 
1405  public function GetHighestFieldId()
1406  {
1407  return $this->GetHighestItemId();
1408  }
1409 
1417  public function StdNameToFieldMapping($MappedName, $FieldId = NULL)
1418  {
1419  if ($FieldId !== NULL)
1420  {
1421  self::$FieldMappings[$this->Id][$MappedName] = $FieldId;
1422  }
1423  return isset(self::$FieldMappings[$this->Id][$MappedName])
1424  ? self::$FieldMappings[$this->Id][$MappedName] : NULL;
1425  }
1426 
1433  public function FieldToStdNameMapping($FieldId)
1434  {
1435  if ($FieldId != -1)
1436  {
1437  foreach (self::$FieldMappings[$this->Id] as $MappedName => $MappedFieldId)
1438  {
1439  if ($MappedFieldId == $FieldId)
1440  {
1441  return $MappedName;
1442  }
1443  }
1444  }
1445  return NULL;
1446  }
1447 
1455  public function GetFieldByMappedName($MappedName)
1456  {
1457  return ($this->StdNameToFieldMapping($MappedName) == NULL) ? NULL
1458  : $this->GetField($this->StdNameToFieldMapping($MappedName));
1459  }
1460 
1468  public function GetFieldIdByMappedName($MappedName)
1469  {
1470  return $this->StdNameToFieldMapping($MappedName);
1471  }
1472 
1477  public function GetOwnedFields()
1478  {
1479  $Fields = array();
1480 
1481  $this->DB->Query("SELECT * FROM MetadataFields"
1482  ." WHERE Owner IS NOT NULL AND LENGTH(Owner) > 0"
1483  ." AND SchemaId = ".intval($this->Id));
1484 
1485  while (FALSE !== ($Row = $this->DB->FetchRow()))
1486  {
1487  $FieldId = $Row["FieldId"];
1488  $Fields[$FieldId] = $this->GetField($FieldId);
1489  }
1490 
1491  return $Fields;
1492  }
1493 
1499  public static function FieldExistsInAnySchema($Field)
1500  {
1501  static $ValidFieldIds;
1502 
1503  # first time through, set up our lookup array
1504  if (!isset($ValidFieldIds))
1505  {
1506  $DB = new Database();
1507  $DB->Query("SELECT FieldId FROM MetadataFields");
1508  $ValidFieldIds = array_flip($DB->FetchColumn("FieldId"));
1509  }
1510 
1511  # if we were given a field id, check to see if it exists
1512  if (is_numeric($Field) &&
1513  array_key_exists($Field, $ValidFieldIds))
1514  {
1515  return TRUE;
1516  }
1517 
1518  # otherwise, try to look up this field
1519  try
1520  {
1521  $FieldId = self::GetCanonicalFieldIdentifier($Field);
1522  return array_key_exists($FieldId, $ValidFieldIds) ?
1523  TRUE : FALSE;
1524  }
1525  catch (Exception $e)
1526  {
1527  # if we can't find the field, then it doesn't exist
1528  return FALSE;
1529  }
1530  }
1531 
1547  public static function GetCanonicalFieldIdentifier($Field)
1548  {
1549  # if field object was passed in
1550  if ($Field instanceof MetadataField)
1551  {
1552  # return identifier from field to caller
1553  return $Field->Id();
1554  }
1555  # else if field ID was passed in
1556  elseif (is_numeric($Field))
1557  {
1558  # return supplied field ID to caller
1559  return (int)$Field;
1560  }
1561  # else if field name was passed in
1562  elseif (is_string($Field))
1563  {
1564  # retrieve field names if not already loaded
1565  static $FieldNames;
1566  static $FieldNamesWithSchema;
1567  static $FieldSchemaIds;
1568  if (!isset($FieldNames))
1569  {
1570  $DB = new Database();
1571 
1572  $DB->Query("SELECT SchemaId, Name FROM MetadataSchemas");
1573  $SchemaNames = $DB->FetchColumn("Name", "SchemaId");
1574 
1575  $DB->Query("SELECT FieldId, FieldName, SchemaId"
1576  ." FROM MetadataFields");
1577  while ($Row = $DB->FetchRow())
1578  {
1579  if ($Row["SchemaId"] != self::SCHEMAID_DEFAULT)
1580  {
1581  $FieldNamesWithSchema[$Row["FieldId"]] =
1582  $SchemaNames[$Row["SchemaId"]].": ".
1583  $Row["FieldName"];
1584  }
1585  $FieldNames[$Row["FieldId"]] = $Row["FieldName"];
1586  $FieldSchemaIds[$Row["FieldId"]] = $Row["SchemaId"];
1587  }
1588  }
1589 
1590  # look for the specified name with a schema prefix
1591  $Ids = array_keys($FieldNamesWithSchema, $Field);
1592 
1593  # if we find a unique schema-prefixed name, use that
1594  # (note that more than one schema-prefixed name shouldn't
1595  # be possible because fields must be unique inside a schema)
1596  if (count($Ids) == 1)
1597  {
1598  return (int)$Ids[0];
1599  }
1600  elseif (count($Ids) > 1)
1601  {
1602  throw new Exception("Multiple schema-name prefixed "
1603  ."metadata fields found, which should be impossible.");
1604  }
1605 
1606  # look for field with specified name
1607  $Ids = array_keys($FieldNames, $Field);
1608 
1609  # if one matching field was found
1610  $FieldsFound = count($Ids);
1611  if ($FieldsFound == 1)
1612  {
1613  # return ID for matching field
1614  return (int)$Ids[0];
1615  }
1616  # else if multiple matching fields were found
1617  elseif ($FieldsFound > 1)
1618  {
1619  # return ID of field with the lowest schema ID
1620  $LowestSchemaId = PHP_INT_MAX;
1621  foreach ($Ids as $Id)
1622  {
1623  if ($FieldSchemaIds[$Id] < $LowestSchemaId)
1624  {
1625  $FieldId = $Id;
1626  $LowestSchemaId = $FieldSchemaIds[$Id];
1627  }
1628  }
1629  return $FieldId;
1630  }
1631  # else error out because no matching field found
1632  else
1633  {
1634  throw new Exception("No metadata field found with the"
1635  ." name \"".$Field."\".");
1636  }
1637  }
1638  # else error out because we were given an illegal field argument
1639  else
1640  {
1641  throw new InvalidArgumentException(
1642  "Illegal field argument supplied.");
1643  }
1644  }
1645 
1660  public static function GetPrintableFieldName($Field)
1661  {
1662  # retrieve field ID
1663  $Id = self::GetCanonicalFieldIdentifier($Field);
1664 
1665  # retrieve printable labels if not yet populated
1666  static $FieldNames;
1667 
1668  if (!isset($FieldNames))
1669  {
1670  $DB = new Database();
1671  $DB->Query("SELECT SchemaId, Name FROM MetadataSchemas");
1672  $SchemaNames = $DB->FetchColumn("Name", "SchemaId");
1673 
1674  $DB->Query("SELECT SchemaId, FieldId, FieldName, Label FROM MetadataFields");
1675  while ($Row = $DB->FetchRow())
1676  {
1677  $SchemaPrefix = ($Row["SchemaId"] != self::SCHEMAID_DEFAULT)
1678  ? $SchemaNames[$Row["SchemaId"]].": " : "";
1679 
1680  $TrimmedLabel = trim($Row["Label"]);
1681  $FieldNames[$Row["FieldId"]] = $SchemaPrefix
1682  .(strlen($TrimmedLabel) ? $TrimmedLabel : $Row["FieldName"]);
1683  }
1684  }
1685 
1686  return isset($FieldNames[$Id]) ? $FieldNames[$Id] : "" ;
1687  }
1688 
1696  public static function TranslateLegacySearchValues(
1697  $FieldId, $Values)
1698  {
1699  # start out assuming we won't find any values to translate
1700  $ReturnValues = array();
1701 
1702  # try to grab the specified field
1703  try
1704  {
1705  $Field = new MetadataField($FieldId);
1706  }
1707  catch (Exception $e)
1708  {
1709  # field no longer exists, so there are no values to translate
1710  return $ReturnValues;
1711  }
1712 
1713  # if incoming value is not an array
1714  if (!is_array($Values))
1715  {
1716  # convert incoming value to an array
1717  $Values = array($Values);
1718  }
1719 
1720  # for each incoming value
1721  foreach ($Values as $Value)
1722  {
1723  # look up value for index
1724  if ($Field->Type() == self::MDFTYPE_FLAG)
1725  {
1726  # (for flag fields the value index (0 or 1) is used in Database)
1727  if ($Value >= 0)
1728  {
1729  $ReturnValues[] = "=".$Value;
1730  }
1731  }
1732  elseif ($Field->Type() == self::MDFTYPE_NUMBER)
1733  {
1734  # (for flag fields the value index (0 or 1) is used in Database)
1735  if ($Value >= 0)
1736  {
1737  $ReturnValues[] = ">=".$Value;
1738  }
1739  }
1740  elseif ($Field->Type() == self::MDFTYPE_USER)
1741  {
1742  $User = new CWUser(intval($Value));
1743  if ($User)
1744  {
1745  $ReturnValues[] = "=".$User->Get("UserName");
1746  }
1747  }
1748  elseif ($Field->Type() == self::MDFTYPE_OPTION)
1749  {
1750  if (!isset($PossibleFieldValues))
1751  {
1752  $PossibleFieldValues = $Field->GetPossibleValues();
1753  }
1754 
1755  if (isset($PossibleFieldValues[$Value]))
1756  {
1757  $ReturnValues[] = "=".$PossibleFieldValues[$Value];
1758  }
1759  }
1760  else
1761  {
1762  $NewValue = $Field->GetValueForId($Value);
1763  if ($NewValue !== NULL)
1764  {
1765  $ReturnValues[] = "=".$NewValue;
1766  }
1767  }
1768  }
1769 
1770  # return array of translated values to caller
1771  return $ReturnValues;
1772  }
1773 
1778  public static function GetAllSchemaIds()
1779  {
1780  # fetch IDs of all metadata schemas
1781  $Database = new Database();
1782  $Database->Query("SELECT * FROM MetadataSchemas");
1783  $SchemaIds = $Database->FetchColumn("SchemaId");
1784 
1785  # return IDs to caller
1786  return $SchemaIds;
1787  }
1788 
1794  public static function GetAllSchemas()
1795  {
1796  # fetch IDs of all metadata schemas
1797  $SchemaIds = self::GetAllSchemaIds();
1798 
1799  # construct objects from the IDs
1800  $Schemas = array();
1801  foreach ($SchemaIds as $SchemaId)
1802  {
1803  $Schemas[$SchemaId] = new MetadataSchema($SchemaId);
1804  }
1805 
1806  # return schemas to caller
1807  return $Schemas;
1808  }
1809 
1815  public static function SetOwnerListRetrievalFunction($Callback)
1816  {
1817  if (is_callable($Callback))
1818  {
1819  self::$OwnerListRetrievalFunction = $Callback;
1820  }
1821  }
1822 
1828  public static function NormalizeOwnedFields()
1829  {
1830  # if an owner list retrieval function and default schema exists
1831  if (self::$OwnerListRetrievalFunction
1832  && self::SchemaExistsWithId(self::SCHEMAID_DEFAULT))
1833  {
1834  # retrieve the list of owners that currently exist
1835  $OwnerList = call_user_func(self::$OwnerListRetrievalFunction);
1836 
1837  # an array is expected
1838  if (is_array($OwnerList))
1839  {
1840  $Schema = new MetadataSchema(self::SCHEMAID_DEFAULT);
1841 
1842  # get each metadata field that is owned by a plugin
1843  $OwnedFields = $Schema->GetOwnedFields();
1844 
1845  # loop through each owned field
1846  foreach ($OwnedFields as $OwnedField)
1847  {
1848  # the owner of the current field
1849  $Owner = $OwnedField->Owner();
1850 
1851  # if the owner of the field is in the list of owners that
1852  # currently exist, i.e., available plugins
1853  if (in_array($Owner, $OwnerList))
1854  {
1855  # enable the field and reset its "enable on owner return"
1856  # flag if the "enable on owner return" flag is currently
1857  # set to true. in other words, re-enable the field since
1858  # the owner has returned to the list of existing owners
1859  if ($OwnedField->EnableOnOwnerReturn())
1860  {
1861  $OwnedField->Enabled(TRUE);
1862  $OwnedField->EnableOnOwnerReturn(FALSE);
1863  }
1864  }
1865 
1866  # if the owner of the field is *not* in the list of owners
1867  # that currently exist, i.e., available plugins
1868  else
1869  {
1870  # first, see if the field is currently enabled since it
1871  # will determine whether the field is re-enabled when
1872  # the owner becomes available again
1873  $Enabled = $OwnedField->Enabled();
1874 
1875  # if the field is enabled, set its "enable on owner
1876  # return" flag to true and disable the field. nothing
1877  # needs to be done if the field is already disabled
1878  if ($Enabled)
1879  {
1880  $OwnedField->EnableOnOwnerReturn($Enabled);
1881  $OwnedField->Enabled(FALSE);
1882  }
1883  }
1884  }
1885  }
1886  }
1887  }
1888 
1893  protected function UpdateFieldCompareOrders()
1894  {
1895  $Index = 0;
1896 
1897  foreach ($this->GetDisplayOrder()->GetFields() as $Field)
1898  {
1899  $this->FieldCompareDisplayOrder[$Field->Id()] = $Index++;
1900  }
1901 
1902  $Index = 0;
1903 
1904  foreach ($this->GetEditOrder()->GetFields() as $Field)
1905  {
1906  $this->FieldCompareEditOrder[$Field->Id()] = $Index++;
1907  }
1908  }
1909 
1914  public function GetDisplayOrder()
1915  {
1916  return MetadataFieldOrder::GetOrderForSchema($this, self::ORDER_DISPLAY_NAME);
1917  }
1918 
1923  public function GetEditOrder()
1924  {
1925  return MetadataFieldOrder::GetOrderForSchema($this, self::ORDER_EDIT_NAME);
1926  }
1927 
1932  protected function FieldCompareOrdersSet()
1933  {
1934  return $this->FieldCompareDisplayOrder && $this->FieldCompareEditOrder;
1935  }
1936 
1944  protected function CompareFieldOrder($FieldA, $FieldB)
1945  {
1946  if ($this->FieldCompareType == self::MDFORDER_ALPHABETICAL)
1947  {
1948  return ($FieldA->GetDisplayName() < $FieldB->GetDisplayName()) ? -1 : 1;
1949  }
1950 
1951  if ($this->FieldCompareType == self::MDFORDER_EDITING)
1952  {
1954  }
1955 
1956  else
1957  {
1959  }
1960 
1961  $PositionA = GetArrayValue($Order, $FieldA->Id(), 0);
1962  $PositionB = GetArrayValue($Order, $FieldB->Id(), 0);
1963 
1964  return $PositionA < $PositionB ? -1 : 1;
1965  }
1966 
1967 
1968  # ---- PRIVATE INTERFACE -------------------------------------------------
1969 
1970  private $Id;
1971  private $Name;
1972  private $AbbreviatedName;
1973  private $ResourceName;
1974  private $AuthoringPrivileges;
1975  private $EditingPrivileges;
1976  private $ViewingPrivileges;
1977  private $ViewPage;
1978  private $FieldCompareType;
1979  private $CachingOn;
1980  private $NewFields = array();
1981  private $ErrorMsgs = array();
1982  private static $FieldMappings = array();
1984 
1988  protected $FieldCompareDisplayOrder = array();
1989 
1993  protected $FieldCompareEditOrder = array();
1994 
2002  private function ConvertXmlToPrivilegeSet($Xml)
2003  {
2004  # clear any existing errors
2005  if (array_key_exists(__METHOD__, $this->ErrorMsgs))
2006  { unset($this->ErrorMsgs[__METHOD__]); }
2007 
2008  # create new privilege set
2009  $PrivSet = new PrivilegeSet();
2010 
2011  # for each XML child
2012  foreach ($Xml as $Tag => $Value)
2013  {
2014  # take action based on element name
2015  switch ($Tag)
2016  {
2017  case "PrivilegeSet":
2018  # convert child data to new set
2019  $NewSet = $this->ConvertXmlToPrivilegeSet($Value);
2020 
2021  # add new set to our privilege set
2022  $PrivSet->AddSet($NewSet);
2023  break;
2024 
2025  case "AddCondition":
2026  # start with default values for optional parameters
2027  unset($ConditionField);
2028  $ConditionValue = NULL;
2029  $ConditionOperator = "==";
2030 
2031  # pull out parameters
2032  foreach ($Value as $ParamName => $ParamValue)
2033  {
2034  $ParamValue = trim($ParamValue);
2035  switch ($ParamName)
2036  {
2037  case "Field":
2038  $ConditionField = $this->GetField($ParamValue, TRUE);
2039  if ($ConditionField === NULL)
2040  {
2041  # record error about unknown field
2042  $this->ErrorMsgs[__METHOD__][] =
2043  "Unknown metadata field name found"
2044  ." in AddCondition (".$ParamValue.").";
2045 
2046  # bail out
2047  return NULL;
2048  }
2049  break;
2050 
2051  case "Value":
2052  $ConditionValue = ($ParamValue == "NULL")
2053  ? NULL : (string)$ParamValue;
2054  break;
2055 
2056  case "Operator":
2057  $ConditionOperator = (string)$ParamValue;
2058  break;
2059 
2060  default:
2061  # record error about unknown parameter name
2062  $this->ErrorMsgs[__METHOD__][] =
2063  "Unknown tag found in AddCondition ("
2064  .$ParamName.").";
2065 
2066  # bail out
2067  return NULL;
2068  break;
2069  }
2070  }
2071 
2072  # if no field value
2073  if (!isset($ConditionField))
2074  {
2075  # record error about no field value
2076  $this->ErrorMsgs[__METHOD__][] =
2077  "No metadata field specified in AddCondition.";
2078 
2079  # bail out
2080  return NULL;
2081  }
2082 
2083  # add conditional to privilege set
2084  $PrivSet->AddCondition($ConditionField,
2085  $ConditionValue, $ConditionOperator);
2086  break;
2087 
2088  default:
2089  # strip any excess whitespace off of value
2090  $Value = trim($Value);
2091 
2092  # if child looks like valid method name
2093  if (method_exists("PrivilegeSet", $Tag))
2094  {
2095  # convert constants if needed
2096  if (defined($Value))
2097  {
2098  $Value = constant($Value);
2099  }
2100  # convert booleans if needed
2101  elseif (strtoupper($Value) == "TRUE")
2102  {
2103  $Value = TRUE;
2104  }
2105  elseif (strtoupper($Value) == "FALSE")
2106  {
2107  $Value = FALSE;
2108  }
2109  # convert privilege flag names if needed and appropriate
2110  elseif (preg_match("/Privilege$/", $Tag))
2111  {
2112  static $Privileges;
2113  if (!isset($Privileges))
2114  {
2115  $PFactory = new PrivilegeFactory();
2116  $Privileges = $PFactory->GetPrivileges(TRUE, FALSE);
2117  }
2118  if (in_array($Value, $Privileges))
2119  {
2120  $Value = array_search($Value, $Privileges);
2121  }
2122  }
2123 
2124  # set value using child data
2125  $PrivSet->$Tag((string)$Value);
2126  }
2127  else
2128  {
2129  # record error about bad tag
2130  $this->ErrorMsgs[__METHOD__][] =
2131  "Unknown tag encountered (".$Tag.").";
2132 
2133  # bail out
2134  return NULL;
2135  }
2136  break;
2137  }
2138  }
2139 
2140  # return new privilege set to caller
2141  return $PrivSet;
2142  }
2143 }
const MDFSTAT_ILLEGALLABEL
const ORDER_DISPLAY_NAME
GetHighestItemId($IgnoreSqlCondition=FALSE)
Retrieve highest item ID in use.
static $FieldTypeDBEnums
const RESOURCENAME_DEFAULT
static $FieldTypeDBAllowedEnums
GetFieldIdByName($FieldName, $IgnoreCase=FALSE)
Retrieve metadata field ID by name.
GetHighestFieldId()
Get highest field ID currently in use.
const SCHEMAID_RESOURCES
$FieldCompareEditOrder
The cache for metadata field edit ordering.
GetViewPageIdParameter()
Get the resource ID GET parameter for the view page for the schema.
GetAllowedFieldTypes()
Retrieve array of field types that user can create.
ViewingPrivileges(PrivilegeSet $NewValue=NULL)
Get/set privileges that allowing viewing resources with this schema.
Metadata schema (in effect a Factory class for MetadataField).
ErrorMessages($Method=NULL)
Get error messages (if any) from recent calls.
ResourceName($NewValue=NULL)
Get/set name of resources using this schema.
static $FieldTypePHPEnums
static TranslateLegacySearchValues($FieldId, $Values)
Translate search values from a legacy URL string to their modern equivalents.
UserCanView($User)
Determine if the given user can view resources using this schema.
const MDFORDER_ALPHABETICAL
UserCanEdit($User)
Determine if the given user can edit resources using this schema.
static Create($SchemaId, $FieldType, $FieldName, $Optional=NULL, $DefaultValue=NULL)
Create a new metadata field.
GetItemIdByName($Name, $IgnoreCase=FALSE)
Retrieve item ID by name.
static NormalizeOwnedFields()
Disable owned fields that have an owner that is unavailable and re-enable fields if an owner has retu...
SQL database abstraction object with smart query caching.
Definition: Database.php:22
UserCanAuthor($User)
Determine if the given user can author resources using this schema.
EditingPrivileges(PrivilegeSet $NewValue=NULL)
Get/set privileges that allowing editing resources with this schema.
GetFieldByName($FieldName, $IgnoreCase=FALSE)
Retrieve metadata field by name.
GetFieldIdByLabel($FieldLabel, $IgnoreCase=FALSE)
Retrieve metadata field ID by label.
static GetPrintableFieldName($Field)
Retrieve label for field.
static SchemaExistsWithId($SchemaId)
Check with schema exists with specified ID.
Factory which extracts all defined privileges from the database.
const MDFSTAT_FIELDDOESNOTEXIST
GetFieldsAsOptionList($OptionListName, $FieldTypes=NULL, $SelectedFieldId=NULL, $IncludeNullOption=TRUE, $AddEntries=NULL, $AllowMultiple=FALSE, $Disabled=FALSE)
Retrieve fields of specified type as HTML option list with field names as labels and field IDs as val...
CompareFieldOrder($FieldA, $FieldB)
Field sorting callback.
GetFieldIdByMappedName($MappedName)
Get field ID by standard field name.
Set of privileges used to access resource information or other parts of the system.
GetFieldNames($FieldTypes=NULL, $OrderType=NULL, $IncludeDisabledFields=FALSE, $IncludeTempFields=FALSE)
Retrieve field names.
Delete()
Destroy metadata schema.
AddFieldFromXml($Xml)
Add new metadata field based on supplied XML.
CacheData($NewValue)
Enable/disable caching of metadata field info.
static SetOwnerListRetrievalFunction($Callback)
Allow external dependencies, i.e., the current list of owners that are available, to be injected...
StdNameToFieldMapping($MappedName, $FieldId=NULL)
Get/set mapping of standard field name to specific field.
ItemExists($ItemId, $IgnoreSqlCondition=FALSE)
Check that item exists with specified ID.
FieldToStdNameMapping($FieldId)
Get mapping of field ID to standard field name.
Name($NewValue=NULL)
Get/set name of schema.
ViewPage($NewValue=NULL)
Get/set name of page to go to for viewing resources using this schema.
const MDFTYPE_CONTROLLEDNAME
static GetAllSchemaIds()
Get IDs for all existing metadata schemas.
AddFieldsFromXmlFile($FileName, $Owner=NULL, $TestRun=FALSE)
Add new metadata fields from XML file.
static GetCanonicalFieldIdentifier($Field)
Retrieve canonical identifier for field.
GetOwnedFields()
Get fields that have an owner associated with them.
PathMatchesViewPage($Path)
Determine if a path matches the view page path for the schema.
const MDFSTAT_DUPLICATEDBCOLUMN
AbbreviatedName($NewValue=NULL)
Get/set abbreviated name of schema.
GetEditOrder()
Get the editing order for the schema.
static FieldExistsInAnySchema($Field)
Determine if a Field exists in any schema.
static $OwnerListRetrievalFunction
const MDFSTAT_DUPLICATELABEL
static Create(MetadataSchema $Schema, $Name, array $FieldOrder=array())
Create a new metadata field order, optionally specifying the order of the fields. ...
NewFields()
Get new fields recently added (if any) via XML file.
AddField($FieldName, $FieldType, $Optional=TRUE, $DefaultValue=NULL)
Add new metadata field.
GetFields($FieldTypes=NULL, $OrderType=NULL, $IncludeDisabledFields=FALSE, $IncludeTempFields=FALSE)
Retrieve array of fields.
GetDisplayOrder()
Get the display order for the schema.
FieldCompareOrdersSet()
Determine whether the field comparison ordering caches are set.
static Create($Name, PrivilegeSet $AuthorPrivs=NULL, PrivilegeSet $EditPrivs=NULL, PrivilegeSet $ViewPrivs=NULL, $ViewPage="", $ResourceName=NULL)
Create new metadata schema.
Object representing a locally-defined type of metadata field.
AuthoringPrivileges(PrivilegeSet $NewValue=NULL)
Get/set privileges that allowing authoring resources with this schema.
__construct($SchemaId=self::SCHEMAID_DEFAULT)
Object constructor, used to load an existing schema.
FieldExists($Field)
Check whether field with specified name exists.
Represents a "resource" in CWIS.
Definition: Resource.php:13
DropField($FieldId)
Delete metadata field and all associated data.
RemoveQualifierAssociations($QualifierIdOrObject)
Remove all metadata field associations for a given qualifier.
static Singularize($Word)
Singularize an English word.
Definition: StdLib.php:141
UpdateFieldCompareOrders()
Update the field comparison ordering cache that is used for sorting fields.
static GetOrderForSchema(MetadataSchema $Schema, $Name)
Get a metadata field order with a specific name for a given metadata schema.
const MDFSTAT_ILLEGALNAME
static GetOrdersForSchema(MetadataSchema $Schema)
Get all of the orders associated with a schema.
GetField($FieldId)
Retrieve metadata field.
GetFieldByMappedName($MappedName)
Get field by standard field name.
Id()
Get schema ID.
static GetConstantName($Value, $Prefix=NULL)
Get name (string) for constant.
Common factory class for item manipulation.
Definition: ItemFactory.php:17
const MDFSTAT_DUPLICATENAME
$FieldCompareDisplayOrder
The cache for metadata field display ordering.
NameIsInUse($Name, $IgnoreCase=FALSE)
Check whether item name is currently in use.
Factory for Resource objects.
CWIS-specific user class.
Definition: CWUser.php:13
GetFieldTypes()
Retrieve array of field types.
static GetAllSchemas()
Get all existing metadata schemas.
Convenience class for generating an HTML select/option form element.
GetFieldByLabel($FieldLabel, $IgnoreCase=FALSE)
Retrieve metadata field by label.
QualifierIsInUse($QualifierIdOrObject)
Check whether qualifier is in use by any metadata field (in any schema).