CWIS Developer Documentation
PrivilegeEditingUI.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: PrivilegeEditingUI.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2015-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
15 {
26  public function __construct($Schemas = NULL, $MetadataFields = array())
27  {
28  $this->Fields = array();
29 
30  # if Schemas was NULL, use all schemas
31  if ($Schemas === NULL)
32  {
33  $Schemas = MetadataSchema::GetAllSchemas();
34  }
35  else
36  {
37  # ensure incoming value is an array
38  if (!is_array($Schemas))
39  {
40  $Schemas = array($Schemas);
41  }
42 
43  # if we have an array of ints, convert to an array of MetadataSchema objects
44  if (is_numeric(reset($Schemas)))
45  {
46  $NewSchemas = array();
47  foreach ($Schemas as $SchemaId)
48  {
49  $NewSchemas[$SchemaId] = new MetadataSchema($SchemaId);
50  }
51  $Schemas = $NewSchemas;
52  }
53  }
54 
55  # ensure incoming value is an array
56  if (!is_array($MetadataFields))
57  {
58  $MetadataFields = array($MetadataFields);
59  }
60 
61  # if we have an array of ints, convert to an array of MetadataField objects
62  if (is_numeric(reset($MetadataFields)))
63  {
64  $NewMetadataFields = array();
65  foreach ($MetadataFields as $FieldId)
66  {
67  $NewMetadataFields[$FieldId] = new MetadataField($FieldId);
68  }
69  $MetadataFields = $NewMetadataFields;
70  }
71 
72  # types we support, in the order they should be displayed
73  $SupportedFieldTypesInOrder = array(
79 
80  # add all requested Schemas
81  foreach ($Schemas as $SchemaId => $Schema)
82  {
83  # iterate over the supported types so that fields are
84  # returned grouped by type
85  foreach ($SupportedFieldTypesInOrder as $Type)
86  {
87  $this->Fields += $Schema->GetFields($Type);
88  }
89  }
90 
91  # and add requested fields
92  foreach ($MetadataFields as $FieldId => $Field)
93  {
94  if (!in_array($Field->Type(), $SupportedFieldTypesInOrder))
95  {
96  throw new Exception(
97  "Field ".$Field->Name()." (Id=".$Field->Id().")"
98  ." is invalid for PrivilegeEditing -- ".$Field->TypeAsName()
99  ." fields are not supported.");
100  }
101  $this->Fields[$FieldId] = $Field;
102  }
103  }
104 
111  public function DisplaySet($Identifier, PrivilegeSet $PrivilegeSet,
112  $IsNested = FALSE)
113  {
114  # include needed JavaScript
115  $GLOBALS["AF"]->RequireUIFile(__CLASS__.".js");
116 
117  # build form field prefix
118  $FFPrefix = "F_Priv_".$Identifier."_";
119 
120  # retrieve privilege info as an array
121  $PrivilegeInfo = $PrivilegeSet->GetPrivilegeInfo();
122 
123  # add "Top-Level Logic" option list if we are at the top of the hierarchy
124  if (!$IsNested)
125  {
126  $Logic = $PrivilegeInfo["Logic"];
127 
128  print '<div class="priv-set">'
129  .'<fieldset class="priv-fieldset priv-logic">'
130  .'<label for="'.$FFPrefix.'Logic">Top-Level Logic:</label>';
131 
132  $OptList = new HtmlOptionList($FFPrefix."Logic",
133  array("AND" => "AND", "OR" => "OR"), $Logic);
134  $OptList->PrintHtml();
135 
136  print '</fieldset>';
137  }
138 
139  # discard logic so that the rest of the array can be iterated over
140  unset($PrivilegeInfo["Logic"]);
141 
142  # if there are no conditions set
143  if (count($PrivilegeInfo) == 0)
144  {
145  # print out message indicating no conditions yet set
146  print("<i>(no requirements)</i><br/>");
147  }
148  else
149  {
150  # print out each condition
151  foreach ($PrivilegeInfo as $Condition)
152  {
153  ?>
154  <fieldset class="priv-fieldset cw-peui-fieldset">
155  <?PHP
156 
157  # if condition tests against a user privilege
158  if (is_numeric($Condition))
159  {
160  $this->DisplaySubjectField($FFPrefix, "current_user");
161  $this->DisplayOperatorField($FFPrefix);
162  $this->DisplayValueField($FFPrefix, $Condition);
163  }
164  # else if condition tests against a metadata field value
165  else if (is_array($Condition))
166  {
167  $this->DisplaySubjectField($FFPrefix, $Condition["FieldId"]);
168  $this->DisplayOperatorField($FFPrefix, $Condition["Operator"]);
169 
170  try
171  {
172  $Field = new MetadataField($Condition["FieldId"]);
173  }
174  catch (Exception $e)
175  {
176  # do nothing here, but we'd like to continue
177  }
178 
179  if (isset($Field) && $Field->Type() == MetadataSchema::MDFTYPE_OPTION)
180  {
181  # Option fields use the selector menu, rather than a
182  # form field.
183  # Values are ControlledName Ids, prefixed with a "C"
184  # to distinguish them from privilge flag numbers.
185  $this->DisplayValueField($FFPrefix,
186  "C".$Condition["Value"], "NULL");
187  }
188  else
189  {
190  $this->DisplayValueField($FFPrefix, NULL, $Condition["Value"]);
191  }
192  }
193  # else if condition is a privilege subset
194  else if ($Condition instanceof PrivilegeSet)
195  {
196  $this->DisplaySubjectField($FFPrefix, "set_entry");
197  $this->DisplayOperatorField($FFPrefix);
198  $this->DisplayValueField($FFPrefix,
199  $Condition->AllRequired() ? "AND" : "OR");
200 
201  # end the fieldset for the set entry row
202  ?></fieldset><?PHP
203 
204  # print the nested fields
205  $this->DisplaySet($Identifier, $Condition, TRUE);
206 
207  # begin a new fieldset for the set exit row
208  ?><fieldset class="priv-fieldset cw-peui-fieldset"><?PHP
209 
210  $this->DisplaySubjectField($FFPrefix, "set_exit");
211  $this->DisplayOperatorField($FFPrefix);
212  $this->DisplayValueField($FFPrefix);
213  }
214 
215  ?></fieldset><?PHP
216  }
217  }
218 
219  # if we are at the top level
220  if (!$IsNested)
221  {
222  $NumBlankRows = 6;
223 
224  # print a number of blank rows to be used if JavaScript is disabled
225  for ($Index = 0; $Index < $NumBlankRows; $Index++)
226  {
227  ?><fieldset class="priv-fieldset cw-peui-fieldset priv-extra"><?PHP
228  $this->DisplaySubjectField($FFPrefix);
229  $this->DisplayOperatorField($FFPrefix);
230  $this->DisplayValueField($FFPrefix);
231  ?></fieldset><?PHP
232  }
233 
234  # print a blank row for cloning within JavaScript
235  ?><fieldset class="priv-fieldset cw-peui-fieldset priv-js-clone_target"><?PHP
236  $this->DisplaySubjectField($FFPrefix);
237  $this->DisplayOperatorField($FFPrefix);
238  $this->DisplayValueField($FFPrefix);
239  ?></fieldset><?PHP
240 
241  # print the button to add a new row when using JavaScript
242  ?><button class="cw-button cw-button-elegant cw-button-constrained
243  priv-js-add">Add Condition</button>
244  <?PHP
245 
246  # print the closing div for the set
247  ?></div><?PHP
248  }
249  }
250 
255  public function GetPrivilegeSetsFromForm()
256  {
257  # for each form field
258  $Sets = array();
259  $Logics = array();
260  foreach ($_POST as $FieldName => $FieldValue)
261  {
262  # if field looks like privilege set data
263  if (preg_match("/^F_Priv_/", $FieldName))
264  {
265  # extract identifier from field name
266  $Pieces = explode("_", $FieldName);
267  $Identifier = $Pieces[2];
268 
269  # if field looks like privilege set top-level logic
270  if (preg_match("/_Logic\$/", $FieldName))
271  {
272  # save logic for later use
273  $Logics[$Identifier] = $FieldValue;
274  }
275  else
276  {
277  # retrieve privilege set from field
278  $Sets[$Identifier] =
279  $this->ExtractPrivilegeSetFromFormData($FieldValue);
280  }
281  }
282  }
283 
284  # for each top-level logic found
285  foreach ($Logics as $Identifier => $Logic)
286  {
287  # if no corresponding privilege set was found
288  if (!isset($Sets[$Identifier]))
289  {
290  # load empty set for this identifier
291  $Sets[$Identifier] = new PrivilegeSet();
292  }
293 
294  # set logic in corresponding privilege set
295  $Sets[$Identifier]->AllRequired(($Logic == "AND") ? TRUE : FALSE);
296  }
297 
298  # return any privilege sets found to caller
299  return $Sets;
300  }
301 
308  public function GetPrivilegeSetFromForm($Identifier)
309  {
310  $Sets = $this->GetPrivilegeSetsFromForm();
311  return isset($Sets[$Identifier]) ? $Sets[$Identifier] : FALSE;
312  }
313 
314  # ---- PRIVATE INTERFACE -------------------------------------------------
315 
316  private $Fields;
317  private $OptionValues;
318 
326  private function DisplaySubjectField($FFPrefix, $Selected=NULL)
327  {
328  # construct the list of options to present for this field
329  # present all the metadata fields using their FieldIds
330  $ValidSelections = array_keys($this->Fields);
331 
332  # represent other elements as strings, which need to match the
333  # values in GetPrivilegeSetFromFormData()
334  $ValidSelections[] = "current_user";
335  $ValidSelections[] = "set_entry";
336  $ValidSelections[] = "set_exit";
337 
338  $AllSchemas = MetadataSchema::GetAllSchemas();
339 
340  # construct 2d array of our fields keyed by schemaid
341  $Fields = array();
342  foreach ($this->Fields as $Id => $Field)
343  {
344  $Fields[$Field->SchemaId()][$Id]= $Field;
345  }
346 
347  # build up the list options to included
348  $Options = array();
349  $OptionCSS = array();
350 
351  foreach ($Fields as $ScId => $ScFields)
352  {
353  $FieldOptions = array();
354  foreach ($ScFields as $Id => $Field)
355  {
356  $SafeClassType = strtolower($Field->TypeAsName());
357  $FieldOptions[$Id] = "[".$AllSchemas[$ScId]->AbbreviatedName()."] "
358  .$Field->GetDisplayName();
359  $OptionCSS[$Id] = "priv priv-option priv-field-subject "
360  ."priv-type-".$SafeClassType."_field";
361  }
362 
363  $OptLabel = $AllSchemas[$ScId]->Name();
364  $Options[$OptLabel] = $FieldOptions;
365  }
366 
367  # add Current User entry
368  $Options["current_user"] = "Current User";
369  $OptionCSS["current_user"] = "priv priv-option priv-field-subject "
370  ."priv-type-privilege";
371 
372  # add subgroup begin marker
373  $Options["set_entry"] = "(";
374  $OptionCSS["set_entry"] = "priv priv-option priv-field-subject "
375  ."priv-type-set_entry";
376 
377  # add subgroup end marker
378  $Options["set_exit"] = ")";
379  $OptionCSS["set_exit"] = "priv priv-option priv-field-subject "
380  ."priv-type-set_exit";
381 
382  # check if the data we were given contains an invalid field,
383  # and if so complain
384  if (!is_null($Selected) && !in_array($Selected, $ValidSelections, TRUE))
385  {
386  $Options[$Selected] = "INVALID FIELD";
387  $OptionCSS[$Selected] = "priv priv-option priv-field-subject "
388  ."priv-type-user_field";
389  }
390 
391 
392  $OptionList = new HtmlOptionList($FFPrefix."[]", $Options, $Selected);
393  $OptionList->ClassForOptions($OptionCSS);
394  $OptionList->ClassForList(
395  "priv priv-field priv-select priv-field-subject priv-type-user_field "
396  ."priv-type-flag_field priv-type-option_field priv-type-timestamp_field "
397  ."priv-type-date_field priv-type-number_field priv-type-set_entry "
398  ."priv-type-set_exit");
399 
400  $OptionList->PrintHtml();
401  }
402 
408  private function DisplayOperatorField($FFPrefix, $Selected=NULL)
409  {
410  $Options = array();
411  $OptionCSS = array();
412  $CommonStyles = "priv priv-option priv-field-operator";
413 
414  # use css styles on each option to indicate which fields it applies to
415 
416  # equal and not equal work for User, Flag, Option, and Number fields
417  foreach (array("==", "!=") as $Op)
418  {
419  $Options[$Op] = $Op;
420  $OptionCSS[$Op] = $CommonStyles." priv-type-user_field "
421  ."priv-type-flag_field priv-type-option_field priv-type-number_field";
422  }
423 
424  # less and greater work for Timestamp, Date, and Number fields
425  foreach (array("<", ">") as $Op)
426  {
427  $Options[$Op] = $Op;
428  $OptionCSS[$Op] = $CommonStyles
429  ." priv-type-timestamp_field priv-type-date_field priv-type-number_field";
430  }
431 
432  $OptionList = new HtmlOptionList($FFPrefix."[]", $Options, $Selected);
433  $OptionList->ClassForOptions($OptionCSS);
434  $OptionList->ClassForList(
435  "priv priv-field priv-select priv-field-operator priv-type-user_field "
436  ."priv-type-flag_field priv-type-option_field priv-type-timestamp_field "
437  ."priv-type-date_field priv-type-number_field");
438 
439  $OptionList->PrintHtml();
440  }
441 
448  private function DisplayValueField($FFPrefix, $Selected=NULL, $Value=NULL)
449  {
450  $this->PrintPrivilegeValueSelectorField($FFPrefix, $Selected);
451  $this->PrintPrivilegeValueInputField($FFPrefix, $Value);
452  }
453 
465  private function ExtractPrivilegeSetFromFormData(array &$FormData)
466  {
467  $NewPrivilegeSet = new PrivilegeSet();
468  $Privileges = $this->GetPrivileges();
469  $SupportedOperators = array("==", "!=", "<", ">");
470 
471  while (count($FormData))
472  {
473  # extract the form fields
474  $SubjectField = array_shift($FormData);
475  $OperatorField = array_shift($FormData);
476  $ValueSelectField = array_shift($FormData);
477  $ValueInputField = array_shift($FormData);
478 
479  # privilege condition
480  if ($SubjectField == "current_user")
481  {
482  # invalid privilege ID
483  if (!isset($Privileges[$ValueSelectField])
484  || is_null($ValueSelectField))
485  {
486  throw new Exception("Invalid privilege (".$ValueSelectField.")");
487  }
488 
489  $NewPrivilegeSet->AddPrivilege($ValueSelectField);
490  }
491 
492  # metadata field condition
493  else if (is_numeric($SubjectField))
494  {
495  # invalid field ID
496  if (!isset($this->Fields[$SubjectField]) || is_null($SubjectField))
497  {
498  throw new Exception("Invalid or unsupported field ("
499  .$SubjectField.")");
500  }
501 
502  # invalid operator
503  if (!in_array($OperatorField, $SupportedOperators))
504  {
505  throw new Exception("Invalid or unsupported operator ("
506  .$OperatorField.")");
507  }
508 
509  $MetadataField = $this->Fields[$SubjectField];
510 
511  switch ($MetadataField->Type())
512  {
514  $Value = NULL;
515  break;
516 
518  $Value = 1;
519  break;
520 
524  $Value = $ValueInputField;
525  break;
526 
528  # strip the "C" prefix used to distinguish controlled
529  # names from priv flags
530  $Value = intval( substr( $ValueSelectField, 1));
531  break;
532 
533  default:
534  $Value = NULL;
535  break;
536  }
537 
538  $NewPrivilegeSet->AddCondition($MetadataField, $Value, $OperatorField);
539  }
540 
541  # entering a nested privilege set
542  else if ($SubjectField == "set_entry")
543  {
544  # the logic is invalid
545  if ($ValueSelectField != "AND" && $ValueSelectField != "OR")
546  {
547  throw new Exception("Invalid privilege set logic ("
548  .$ValueSelectField.")");
549  }
550 
551  $NestedPrivilegeSet = $this->ExtractPrivilegeSetFromFormData($FormData);
552 
553  # only add the nested privilege set if it's not empty. use 1
554  # because the logic is in the privilege info array
555  if (count($NestedPrivilegeSet->GetPrivilegeInfo()) > 1)
556  {
557  $NestedPrivilegeSet->AllRequired($ValueSelectField == "AND");
558  $NewPrivilegeSet->AddSet($NestedPrivilegeSet);
559  }
560  }
561 
562  # exiting a privilege set
563  else if ($SubjectField == "set_exit")
564  {
565  break;
566  }
567 
568  # unknown condition type
569  else
570  {
571  throw new Exception("Unknown condition type: ".$SubjectField);
572  }
573  }
574 
575  return $NewPrivilegeSet;
576  }
577 
583  private function PrintPrivilegeValueSelectorField($FFPrefix, $Selected=NULL)
584  {
585  # build up the list of options
586  $Options = array();
587  $OptionCSS = array();
588  $OptionData = array();
589 
590  # add entries for each user privilege flag
591  $Privileges = $this->GetPrivileges();
592  foreach ($Privileges as $Id => $Privilege)
593  {
594  $Options[$Id] = $Privilege->Name();
595  $OptionCSS[$Id] = "priv priv-option priv-field-value priv-type-privilege";
596  }
597 
598  $OptionValues = $this->GetOptionValuesForPrivset();
599  foreach ($OptionValues as $FieldId => $Values)
600  {
601  foreach ($Values as $CNId => $CName)
602  {
603  $Options["C".$CNId] = $CName;
604  $OptionCSS["C".$CNId] = "priv priv-option priv-field-value "
605  ."priv-type-option_field";
606  $OptionData["C".$CNId]["field-id"] = $FieldId;
607  }
608  }
609 
610  $Options["AND"] = "AND";
611  $OptionCSS["AND"] = "priv priv-option priv-field-value priv-type-set_entry";
612 
613  $Options["OR"] = "OR";
614  $OptionCSS["OR"] = "priv priv-option priv-field-value priv-type-set_entry";
615 
616  $OptionList = new HtmlOptionList($FFPrefix."[]", $Options, $Selected);
617  $OptionList->ClassForOptions($OptionCSS);
618  $OptionList->DataForOptions($OptionData);
619  $OptionList->ClassForList(
620  "priv priv-field priv-select priv-field-value priv-type-option_field "
621  ."priv-type-privilege priv-type-set_entry priv-type-set_exit");
622 
623  $OptionList->PrintHtml();
624  }
625 
631  private function PrintPrivilegeValueInputField($FFPrefix, $Value=NULL)
632  {
633  $SafeValue = defaulthtmlentities($Value);
634  ?>
635  <input name="<?PHP print $FFPrefix; ?>[]"
636  type="text"
637  class="priv priv-field priv-input priv-field-value
638  priv-type-timestamp_field priv-type-date_field
639  priv-type-number_field"
640  value="<?PHP print $SafeValue; ?>" />
641  <?PHP
642  }
643 
650  private function GetOptionValuesForPrivset()
651  {
652  if (!isset($this->OptionValues))
653  {
654  $this->OptionValues = array();
655 
656  foreach ($this->Fields as $FieldId => $Field)
657  {
658  if ($Field->Type() == MetadataSchema::MDFTYPE_OPTION)
659  {
660  $this->OptionValues[$Field->Id()] = $Field->GetPossibleValues();
661  }
662  }
663  }
664 
665  return $this->OptionValues;
666  }
667 
672  private function GetPrivileges()
673  {
674  static $Privileges;
675 
676  if (!isset($Privileges))
677  {
678  $PrivilegeFactory = new PrivilegeFactory();
679  $Privileges = $PrivilegeFactory->GetPrivileges();
680  }
681 
682  return $Privileges;
683  }
684 }
Metadata schema (in effect a Factory class for MetadataField).
Factory which extracts all defined privileges from the database.
GetPrivilegeSetFromForm($Identifier)
Retrieve privilege set from specified form ($_POST) data fields.
User interface element for editing PrivilegeSets.
__construct($Schemas=NULL, $MetadataFields=array())
Constructor for privilege editing UI.
Set of privileges used to access resource information or other parts of the system.
GetPrivilegeSetsFromForm()
Construct new privilege sets from available form ($_POST) data.
GetPrivilegeInfo()
Get privilege information as an array, with numerical indexes except for the logic, which is contained in a element with the index "Logic".
Object representing a locally-defined type of metadata field.
DisplaySet($Identifier, PrivilegeSet $PrivilegeSet, $IsNested=FALSE)
Display interface for editing specified privilege set.
static GetAllSchemas()
Get all existing metadata schemas.
Convenience class for generating an HTML select/option form element.