CWIS Developer Documentation
FormUI_Base.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: FormUI_Base.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
14 abstract class FormUI_Base
15 {
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17 
19  const FTYPE_FILE = "File";
20  const FTYPE_FLAG = "Flag";
21  const FTYPE_IMAGE = "Image";
22  const FTYPE_METADATAFIELD = "MetadataField";
23  const FTYPE_NUMBER = "Number";
24  const FTYPE_OPTION = "Option";
25  const FTYPE_PARAGRAPH = "Paragraph";
26  const FTYPE_PASSWORD = "Password";
27  const FTYPE_PRIVILEGES = "Privileges";
28  const FTYPE_SEARCHPARAMS = "Search Parameters";
29  const FTYPE_TEXT = "Text";
30  const FTYPE_URL = "URL";
31  const FTYPE_USER = "User";
32  const FTYPE_QUICKSEARCH = "Quick Search";
34  const FTYPE_HEADING = "Heading";
35 
46  public function __construct(
47  $FieldParams, $FieldValues = array(), $UniqueKey = NULL)
48  {
49  # make sure parameters are legal and complete
50  $BooleanParams = array(
51  "AllowMultiple",
52  "ReadOnly",
53  "Required",
54  "UseWYSIWYG",
55  );
56  foreach ($FieldParams as $FieldName => $Params)
57  {
58  if (!isset($Params["Type"]))
59  {
60  $ErrMsgs[] = "Type missing for form field ".$FieldName.".";
61  }
62  elseif (($Params["Type"] == self::FTYPE_OPTION)
63  && !isset($Params["Options"]))
64  {
65  $ErrMsgs[] = "Option values missing for form field ".$FieldName.".";
66  }
67  elseif ($Params["Type"] == self::FTYPE_QUICKSEARCH)
68  {
69  if (!isset($Params["Field"]))
70  {
71  $ErrMsgs[]= "Field to search missing for quicksearch form field "
72  .$FieldName.".";
73  }
74  elseif (!MetadataSchema::FieldExistsInAnySchema($Params["Field"]))
75  {
76  $ErrMsgs[]= "Specified search field for quicksearch form field "
77  .$FieldName." does not exist.";
78  }
79  else
80  {
81  $FieldParams[$FieldName]["Field"] =
83  $MField = new MetadataField(
84  $FieldParams[$FieldName]["Field"]);
85 
86  $AllowedMFieldTypes = [
91  if (!in_array($MField->Type(), $AllowedMFieldTypes))
92  {
93  $ErrMsgs[]= "Specified search field for quicksearch form field "
94  .$FieldName." is not a type that supports quicksearches.";
95  }
96  }
97  }
98 
99  if (!isset($Params["Label"]))
100  {
101  $ErrMsgs[] = "Label missing for form field ".$FieldName.".";
102  }
103  if (isset($Params["ValidateFunction"])
104  && !is_callable($Params["ValidateFunction"]))
105  {
106  $ErrMsgs[] = "Uncallable validation function for form field "
107  .$FieldName.".";
108  }
109  if (isset($Params["InsertIntoField"])
110  && !isset($FieldParams[$Params["InsertIntoField"]]))
111  {
112  $ErrMsgs[] = "Unknown insertion field (".$Params["InsertIntoField"]
113  .") found for form field ".$FieldName.".";
114  }
115  foreach ($BooleanParams as $ParamName)
116  {
117  if (!isset($Params[$ParamName]))
118  {
119  $FieldParams[$FieldName][$ParamName] = FALSE;
120  }
121  }
122  }
123  if (isset($ErrMsgs))
124  {
125  $ErrMsgString = implode(" ", $ErrMsgs);
126  throw new InvalidArgumentException($ErrMsgString);
127  }
128 
129  # save form parameters and values
130  $this->FieldParams = $FieldParams;
131  $this->FieldValues = $FieldValues;
132  $this->UniqueKey = $UniqueKey;
133  }
134 
140  abstract public function DisplayFormTable($TableId = NULL, $TableStyle = NULL);
141 
148  public static function LogError($Msg, $Field = NULL)
149  {
150  self::$ErrorMessages[$Field][] = $Msg;
151  }
152 
159  public static function GetLoggedErrors()
160  {
161  return self::$ErrorMessages;
162  }
163 
170  public static function ErrorsLogged($Field = FALSE)
171  {
172  if ($Field === FALSE)
173  {
174  return count(self::$ErrorMessages) ? TRUE : FALSE;
175  }
176  else
177  {
178  return isset(self::$ErrorMessages[$Field]) ? TRUE : FALSE;
179  }
180  }
181 
186  public static function ClearLoggedErrors($Field = FALSE)
187  {
188  if ($Field === FALSE)
189  {
190  self::$ErrorMessages = array();
191  }
192  else
193  {
194  unset(self::$ErrorMessages[$Field]);
195  }
196  }
197 
205  public function ValidateFieldInput()
206  {
207  # retrieve field values
208  $Values = $this->GetNewValuesFromForm();
209 
210  # for each field
211  $ErrorsFound = 0;
212  foreach ($this->FieldParams as $Name => $Params)
213  {
214  # skip header fields
215  if ($Params["Type"] == self::FTYPE_HEADING) { continue; }
216 
217  # determine if field has a value set
218  switch ($Params["Type"])
219  {
220  case self::FTYPE_SEARCHPARAMS:
221  $IsEmpty = !$Values[$Name]->ParameterCount();
222  break;
223 
224  case self::FTYPE_PRIVILEGES:
225  $IsEmpty = !$Values[$Name]->ComparisonCount();
226  break;
227 
228  default:
229  if (is_array($Values[$Name]))
230  {
231  $IsEmpty = !count($Values[$Name]);
232  }
233  else
234  {
235  $IsEmpty = !strlen(trim($Values[$Name]));
236  }
237  break;
238  }
239 
240  # if field has validation function
241  if (isset($Params["ValidateFunction"]))
242  {
243  # swap in our object if this is one of our methods
244  $VFunc = $Params["ValidateFunction"];
245  if (is_array($VFunc) && ($VFunc[0] == "FormUI"))
246  {
247  $VFunc[0] = $this;
248  }
249 
250  # call validation function for value
251  $Args = array_merge(array($Name, $Values[$Name], $Values),
252  $this->ExtraValidationParams);
253  $ErrMsg = call_user_func_array($VFunc, $Args);
254  if ($ErrMsg === FALSE)
255  {
256  throw new Exception("Calling validation function for"
257  ." parameter \"".$Name."\" failed.");
258  }
259 
260  # log any resulting error
261  if ($ErrMsg !== NULL)
262  {
263  self::LogError($ErrMsg, $Name);
264  $ErrorsFound++;
265  }
266  }
267 
268  # if field is required and empty
269  if ($IsEmpty && isset($Params["Required"]) && $Params["Required"])
270  {
271  # log error to indicate required value is missing
272  self::LogError("<i>".$Params["Label"]."</i> is required.", $Name);
273  $ErrorsFound++;
274  }
275  # else validate based on field type
276  else
277  {
278  switch ($Params["Type"])
279  {
280  case self::FTYPE_NUMBER:
281  # make sure value is within any specified range
282  if ((isset($Params["MinVal"])
283  && ($Values[$Name] < $Params["MinVal"]))
284  || (isset($Params["MaxVal"])
285  && ($Values[$Name] > $Params["MaxVal"])))
286  {
287  if (!isset($Params["MaxVal"]))
288  {
289  self::LogError("<i>".$Params["Label"]."</i> must be "
290  .$Params["MinVal"]." or greater.", $Name);
291  }
292  elseif (!isset($Params["MinVal"]))
293  {
294  self::LogError("<i>".$Params["Label"]."</i> must be "
295  .$Params["MaxVal"]." or less.", $Name);
296  }
297  else
298  {
299  self::LogError("<i>".$Params["Label"]."</i> must be"
300  ." in the range ".$Params["MinVal"]
301  ." to ".$Params["MaxVal"].".", $Name);
302  }
303  $ErrorsFound++;
304  }
305  break;
306 
307  case self::FTYPE_URL:
308  # make sure URL entered looks valid
309  if (!$IsEmpty && (filter_var(
310  $Values[$Name], FILTER_VALIDATE_URL) === FALSE))
311  {
312  self::LogError("Value \"".$Values[$Name]
313  ."\" does not appear to be a valid URL for <i>"
314  .$Params["Label"]."</i>.", $Name);
315  $ErrorsFound++;
316  }
317  break;
318 
319  case self::FTYPE_USER:
320  # make sure user name entered is valid
321  $UFactory = new CWUserFactory();
322  foreach ($Values[$Name] as $UId)
323  {
324  if (strlen($UId) && !$UFactory->UserExists($UId))
325  {
326  self::LogError("User ID \"".$UId
327  ."\" not found for <i>"
328  .$Params["Label"]."</i>.", $Name);
329  $ErrorsFound++;
330  }
331  }
332  break;
333  }
334  }
335  }
336 
337  # report number of fields with invalid values found to caller
338  return $ErrorsFound;
339  }
340 
346  public function AddValidationParameters()
347  {
348  $this->ExtraValidationParams = func_get_args();
349  }
350 
356  public function GetNewValuesFromForm()
357  {
358  # for each form field
359  $NewSettings = array();
360  foreach ($this->FieldParams as $Name => $Params)
361  {
362  # skip header fields
363  if ($Params["Type"] == self::FTYPE_HEADING) { continue; }
364 
365  # determine form field name (matches mechanism in HTML)
366  $FieldName = $this->GetFormFieldName($Name,
367  ($Params["Type"] != self::FTYPE_PRIVILEGES));
368 
369  # assume the value will not change
370  $DidValueChange = FALSE;
371  $OldValue = isset($this->FieldValues[$Name])
372  ? $this->FieldValues[$Name]
373  : (isset($Params["Value"]) ? $Params["Value"] : NULL);
374  $NewSettings[$Name] = $OldValue;
375 
376  # retrieve value based on configuration parameter type
377  switch ($Params["Type"])
378  {
379  case self::FTYPE_FLAG:
380  # if radio buttons were used
381  if (array_key_exists("OnLabel", $Params)
382  && array_key_exists("OffLabel", $Params))
383  {
384  if (isset($_POST[$FieldName]))
385  {
386  $NewValue = ($_POST[$FieldName] == "1") ? TRUE : FALSE;
387 
388  # flag that the values changed if they did
389  $DidValueChange = self::DidValueChange(
390  $OldValue, $NewValue);
391 
392  $NewSettings[$Name] = $NewValue;
393  }
394  }
395  # else checkbox was used
396  else
397  {
398  $NewValue = isset($_POST[$FieldName]) ? TRUE : FALSE;
399 
400  # flag that the values changed if they did
401  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
402 
403  $NewSettings[$Name] = $NewValue;
404  }
405  break;
406 
407  case self::FTYPE_OPTION:
408  $NewValue = GetArrayValue($_POST, $FieldName, array());
409 
410  # flag that the values changed if they did
411  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
412 
413  $NewSettings[$Name] = $NewValue;
414  break;
415 
416  case self::FTYPE_METADATAFIELD:
417  $NewValue = GetArrayValue($_POST, $FieldName, array());
418  if ($NewValue == "-1") { $NewValue = array(); }
419 
420  # flag that the values changed if they did
421  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
422 
423  $NewSettings[$Name] = $NewValue;
424  break;
425 
426  case self::FTYPE_PRIVILEGES:
427  $Schemas = GetArrayValue($Params, "Schemas");
428  $MFields = GetArrayValue($Params, "MetadataFields", array());
429  $PEditor = new PrivilegeEditingUI($Schemas, $MFields);
430  $NewValues = $PEditor->GetPrivilegeSetsFromForm();
431  $NewValue = $NewValues[$FieldName];
432  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
433  $NewSettings[$Name] = $NewValue;
434  break;
435 
436  case self::FTYPE_SEARCHPARAMS:
437  $SPEditor = new SearchParameterSetEditingUI($FieldName);
438  $NewValue = $SPEditor->GetValuesFromFormData();
439  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
440  $NewSettings[$Name] = $NewValue;
441  break;
442 
443  case self::FTYPE_FILE:
444  $NewSettings[$Name] = GetArrayValue(
445  $_POST, $FieldName."_ID", array());
446  foreach ($NewSettings[$Name] as $Index => $FileId)
447  {
448  if ($FileId == self::NO_VALUE_FOR_FIELD ||
449  in_array($FileId, $this->DeletedFiles))
450  {
451  unset($NewSettings[$Name][$Index]);
452  }
453  }
454 
455  # if we have any files in ExtraValues after a HandleUploads,
456  # include those too
457  $ExtraSettings = GetArrayValue(
458  $this->ExtraValues, $FieldName."_ID", []);
459  foreach ($ExtraSettings as $FileId)
460  {
461  if (!in_array($FileId, $NewSettings[$Name]) &&
462  !in_array($FileId, $this->DeletedFiles))
463  {
464  $NewSettings[$Name][]= $FileId;
465  }
466  }
467 
468  break;
469 
470  case self::FTYPE_IMAGE:
471  $NewSettings[$Name] = GetArrayValue(
472  $_POST, $FieldName."_ID", array());
473 
474  foreach ($NewSettings[$Name] as $Index => $ImageId)
475  {
476  if ($ImageId == self::NO_VALUE_FOR_FIELD ||
477  in_array($ImageId, $this->DeletedImages) )
478  {
479  unset($NewSettings[$Name][$Index]);
480  }
481  elseif (isset($_POST[$FieldName."_AltText_".$ImageId]))
482  {
483  $Image = new SPTImage($ImageId);
484  $Image->AltText($_POST[$FieldName."_AltText_".$ImageId]);
485  }
486  }
487 
488  # if we have any images in ExtraValues after a HandleUploads,
489  # include those too
490  $ExtraSettings = GetArrayValue(
491  $this->ExtraValues, $FieldName."_ID", []);
492  foreach ($ExtraSettings as $ImageId)
493  {
494  if (!in_array($ImageId, $NewSettings[$Name]) &&
495  !in_array($ImageId, $this->DeletedImages))
496  {
497  $NewSettings[$Name][]= $ImageId;
498  $AltTextKey = $FieldName."_AltText_".$ImageId;
499  if (isset($this->ExtraValues[$AltTextKey]))
500  {
501  $Image = new SPTImage($ImageId);
502  $Image->AltText(
503  $this->ExtraValues[$AltTextKey]);
504  }
505  }
506  }
507 
508  break;
509 
510  case self::FTYPE_USER:
511  if (isset($_POST[$FieldName]))
512  {
513  # filter blank entries out of input
514  # (these come up when a user was deleted from the list)
515  $NewValue = array_filter(
516  $_POST[$FieldName],
517  function($v) {
518  return strlen($v) > 0 ? TRUE : FALSE;
519  });
520 
521  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
522 
523  $NewSettings[$Name] = $NewValue;
524  }
525  break;
526 
527  default:
528  if (isset($_POST[$FieldName]))
529  {
530  $NewValue = $_POST[$FieldName];
531 
532  # flag that the values changed if they did
533  $DidValueChange = self::DidValueChange($OldValue, $NewValue);
534 
535  $NewSettings[$Name] = $NewValue;
536  }
537  break;
538  }
539 
540  # if value changed and there is an event to signal for changes
541  if ($DidValueChange && $this->SettingChangeEventName)
542  {
543  # set info about changed value in event parameters if appropriate
544  $EventParams = $this->SettingChangeEventParams;
545  foreach ($EventParams as $ParamName => $ParamValue)
546  {
547  switch ($ParamName)
548  {
549  case "SettingName":
550  $EventParams[$ParamName] = $Name;
551  break;
552 
553  case "OldValue":
554  $EventParams[$ParamName] = $OldValue;
555  break;
556 
557  case "NewValue":
558  $EventParams[$ParamName] = $NewValue;
559  break;
560  }
561  }
562 
563  # signal event
564  $GLOBALS["AF"]->SignalEvent(
565  $this->SettingChangeEventName, $EventParams);
566  }
567  }
568 
569  # return updated setting values to caller
570  return $NewSettings;
571  }
572 
578  public function GetFieldValue($FieldName)
579  {
580  # get base form field name
581  $FieldType = $this->FieldParams[$FieldName]["Type"];
582  $FormFieldName = $this->GetFormFieldName($FieldName,
583  ($FieldType != self::FTYPE_PRIVILEGES));
584 
585  # get fallback value for field (in case no value from form)
586  if (isset($this->FieldValues[$FieldName]))
587  {
588  $Value = $this->FieldValues[$FieldName];
589  }
590  elseif (isset($this->FieldParams[$FieldName]["Value"]))
591  {
592  $Value = self::LoadValue($FieldType,
593  $this->FieldParams[$FieldName]["Value"]);
594  }
595  elseif (isset($this->FieldParams[$FieldName]["Default"]))
596  {
597  $Value = self::LoadValue($FieldType,
598  $this->FieldParams[$FieldName]["Default"]);
599  }
600  else
601  {
602  $Value = self::LoadValue($FieldType, NULL);
603  }
604 
605  switch ($FieldType)
606  {
607  case self::FTYPE_FILE:
608  case self::FTYPE_IMAGE:
609  # get name of image ID form field
610  $FileIdFieldName = $FormFieldName."_ID";
611 
612  # use an updated value for this field if available
613  if (isset($this->HiddenFields[$FileIdFieldName]))
614  {
615  $Value = $this->HiddenFields[$FileIdFieldName];
616  }
617  # else use a value from form if available
618  elseif (isset($_POST[$FileIdFieldName]))
619  {
620  $Value = $_POST[$FileIdFieldName];
621  }
622 
623  # add in any previously-set extra values
624  if (isset($this->ExtraValues[$FileIdFieldName])
625  && count($this->ExtraValues[$FileIdFieldName]))
626  {
627  if (!is_array($Value))
628  {
629  $Value = array($Value);
630  }
631  $Value = array_merge($Value,
632  $this->ExtraValues[$FileIdFieldName]);
633  }
634  break;
635 
636  case self::FTYPE_SEARCHPARAMS:
637  # use incoming form value if available
638  if (isset($_POST[$FormFieldName]))
639  {
640  # use incoming form value
641  $SPEditor = new SearchParameterSetEditingUI($FormFieldName);
642  $Value = $SPEditor->GetValuesFromFormData();
643  }
644  break;
645 
646  case self::FTYPE_PRIVILEGES:
647  # use incoming form value if available
648  $Schemas = GetArrayValue(
649  $this->FieldParams[$FieldName], "Schemas");
650  $MFields = GetArrayValue(
651  $this->FieldParams[$FieldName], "MetadataFields", array());
652  $PEditor = new PrivilegeEditingUI($Schemas, $MFields);
653  $PSet = $PEditor->GetPrivilegeSetFromForm($FormFieldName);
654  if ($PSet instanceof PrivilegeSet)
655  {
656  # use incoming form value
657  $Value = $PSet;
658  }
659  break;
660 
661  case self::FTYPE_QUICKSEARCH:
662  $MField = new MetadataField(
663  $this->FieldParams[$FieldName]["Field"]);
664 
665  # use incoming values if available
666  if (isset($_POST[$FormFieldName]))
667  {
668  $Value = $this->ConvertItemIdsToNames(
669  $MField, $_POST[$FormFieldName]);
670  }
671  break;
672 
673  default:
674  # use incoming form value if available
675  if (isset($_POST[$FormFieldName]))
676  {
677  # use incoming form value
678  $Value = $_POST[$FormFieldName];
679  }
680  break;
681  }
682 
683  # return value found to caller
684  return $Value;
685  }
686 
690  public function HandleUploads()
691  {
692  # for each form field
693  foreach ($this->FieldParams as $FieldName => $FieldParams)
694  {
695  # move on to next field if this field does not allow uploads
696  if (($FieldParams["Type"] != self::FTYPE_FILE)
697  && ($FieldParams["Type"] != self::FTYPE_IMAGE))
698  {
699  continue;
700  }
701 
702  # move on to next field if this field does not have an uploaded file
703  $FormFieldName = $this->GetFormFieldName($FieldName);
704  if (!isset($_FILES[$FormFieldName]["name"]))
705  {
706  continue;
707  }
708  $UploadedFileName = $_FILES[$FormFieldName]["name"];
709  if (!strlen($UploadedFileName))
710  {
711  continue;
712  }
713 
714  # create temp copy of file with correct name
715  $TmpFile = "tmp/".$UploadedFileName;
716  $CopyResult = copy($_FILES[$FormFieldName]["tmp_name"], $TmpFile);
717 
718  switch ($FieldParams["Type"])
719  {
720  case self::FTYPE_FILE:
721  # create new file object from uploaded file
722  $File = File::Create($TmpFile, $UploadedFileName);
723 
724  # check for errors during file object creation
725  if (!is_object($File))
726  {
727  switch ($File)
728  {
730  $this->LogError("Uploaded file "
731  .$UploadedFileName." was empty"
732  ." (zero length).", $FieldName);
733  break;
734 
735  default:
736  $this->LogError("Error encountered with"
737  ." uploaded file "
738  .$UploadedFileName." ("
739  .$File.").", $FieldName);
740  break;
741  }
742  unlink($TmpFile);
743  }
744  else
745  {
746  # add file ID to extra values
747  $this->ExtraValues[$FormFieldName."_ID"][] = $File->Id();
748  }
749  break;
750 
751  case self::FTYPE_IMAGE:
752  # create new image object from uploaded file
753  $Image = new SPTImage($TmpFile,
754  $FieldParams["MaxWidth"],
755  $FieldParams["MaxHeight"],
756  $FieldParams["MaxPreviewWidth"],
757  $FieldParams["MaxPreviewHeight"],
758  $FieldParams["MaxThumbnailWidth"],
759  $FieldParams["MaxThumbnailHeight"]);
760 
761  # check for errors during image object creation
762  if ($Image->Status() != AI_OKAY)
763  {
764  switch ($Image->Status())
765  {
766  case AI_UNKNOWNTYPE:
767  $this->LogError("Unknown format for image file "
768  .$UploadedFileName.".", $FieldName);
769  break;
770 
772  $this->LogError("Unsupported format for image file "
773  .$UploadedFileName." ("
774  .$Image->Format().").", $FieldName);
775  break;
776 
777  default:
778  $this->LogError("Error encountered when"
779  ." processing uploaded image file "
780  .$UploadedFileName.".", $FieldName);
781  break;
782  }
783  unlink($TmpFile);
784  }
785  else
786  {
787  # set image object alternate text
788  $Image->AltText($_POST[$FormFieldName."_AltText_NEW"]);
789 
790  # add image ID to extra values
791  $this->ExtraValues[$FormFieldName."_ID"][] = $Image->Id();
792  }
793  break;
794  }
795  }
796  }
797 
801  public function HandleDeletes()
802  {
803  # if image ID to delete was supplied
804  $DeleteFieldName = $this->GetFormFieldName("ImageToDelete");
805  $IncomingValue = GetFormValue($DeleteFieldName);
806  if (is_numeric($IncomingValue)
807  || (is_array($IncomingValue) && count($IncomingValue)))
808  {
809  # retrieve ID of image
810  $ImageId = is_array($IncomingValue)
811  ? array_shift($IncomingValue)
812  : $IncomingValue;
813 
814  # add ID to deleted images list
815  if (is_numeric($ImageId))
816  {
817  $this->DeletedImages[] = $ImageId;
818  }
819  }
820 
821  # if file ID to delete was supplied
822  $DeleteFieldName = $this->GetFormFieldName("FileToDelete");
823  $IncomingValue = GetFormValue($DeleteFieldName);
824  if (is_numeric($IncomingValue)
825  || (is_array($IncomingValue) && count($IncomingValue)))
826  {
827  # retrieve ID of file
828  $FileId = is_array($IncomingValue)
829  ? array_shift($IncomingValue)
830  : $IncomingValue;
831 
832  # add ID to deleted files list
833  if (is_numeric($FileId))
834  {
835  $this->DeletedFiles[] = $FileId;
836  }
837  }
838  }
839 
851  public function SetEventToSignalOnChange($EventName, $EventParams = array())
852  {
853  $this->SettingChangeEventName = $EventName;
854  $this->SettingChangeEventParams = $EventParams;
855  }
856 
863  public static function DidValueChange($OldValue, $NewValue)
864  {
865  # didn't change if they are identical
866  if ($OldValue === $NewValue)
867  {
868  return FALSE;
869  }
870 
871  # need special cases from this point because PHP returns some odd results
872  # when performing loose equality comparisons:
873  # http://php.net/manual/en/types.comparisons.php#types.comparisions-loose
874 
875  # consider NULL and an empty string to be the same. this is in case a field
876  # is currently set to NULL and receives an empty value from the form.
877  # $_POST values are always strings
878  if ((is_null($OldValue) && is_string($NewValue) && !strlen($NewValue))
879  || (is_null($NewValue) && is_string($OldValue) && !strlen($OldValue)))
880  {
881  return FALSE;
882  }
883 
884  # if they both appear to be numbers and are equal
885  if (is_numeric($OldValue) && is_numeric($NewValue) && $OldValue == $NewValue)
886  {
887  return FALSE;
888  }
889 
890  # true-like values
891  if (($OldValue === TRUE && ($NewValue === 1 || $NewValue === "1"))
892  || ($NewValue === TRUE && ($OldValue === 1 || $OldValue === "1")))
893  {
894  return FALSE;
895  }
896 
897  # false-like values
898  if (($OldValue === FALSE && ($NewValue === 0 || $NewValue === "0"))
899  || ($NewValue === FALSE && ($OldValue === 0 || $OldValue === "0")))
900  {
901  return FALSE;
902  }
903 
904  # arrays
905  if (is_array($OldValue) && is_array($NewValue))
906  {
907  # they certainly changed if the counts are different
908  if (count($OldValue) != count($NewValue))
909  {
910  return TRUE;
911  }
912 
913  # the algorithm for associative arrays is slightly different from
914  # sequential ones. the values for associative arrays must match the keys
915  if (count(array_filter(array_keys($OldValue), "is_string")))
916  {
917  foreach ($OldValue as $Key => $Value)
918  {
919  # it changed if the keys don't match
920  if (!array_key_exists($Key, $NewValue))
921  {
922  return TRUE;
923  }
924 
925  # the arrays changed if a value changed
926  if (self::DidValueChange($Value, $NewValue[$Key]))
927  {
928  return TRUE;
929  }
930  }
931  }
932 
933  # sequential values don't have to have the same keys, just the same
934  # values
935  else
936  {
937  # sort them so all the values match up if they're equal
938  sort($OldValue);
939  sort($NewValue);
940 
941  foreach ($OldValue as $Key => $Value)
942  {
943  # the arrays changed if a value changed
944  if (self::DidValueChange($Value, $NewValue[$Key]))
945  {
946  return TRUE;
947  }
948  }
949  }
950 
951  # the arrays are equal
952  return FALSE;
953  }
954 
955  # they changed
956  return TRUE;
957  }
958 
965  static public function LoadValue($Type, $Data)
966  {
967  switch ($Type)
968  {
969  case self::FTYPE_FILE:
970  case self::FTYPE_IMAGE:
971  $Value = ($Data === NULL) ? array() : $Data;
972  break;
973 
974  case self::FTYPE_PRIVILEGES:
975  $Value = ($Data instanceof PrivilegeSet) ? $Data
976  : new PrivilegeSet($Data);
977  break;
978 
979  case self::FTYPE_SEARCHPARAMS:
980  $Value = ($Data instanceof SearchParameterSet) ? $Data
981  : new SearchParameterSet($Data);
982  break;
983 
984  default:
985  $Value = $Data;
986  break;
987  }
988 
989  return $Value;
990  }
991 
1004  public function ValidateEmail($FieldName, $FieldValues)
1005  {
1006  if (!is_array($FieldValues)) { $FieldValues = array($FieldValues); }
1007  foreach ($FieldValues as $Value)
1008  {
1009  if (trim($Value) == "") { continue; }
1010  if (filter_var($Value, FILTER_VALIDATE_EMAIL) === FALSE)
1011  {
1012  return "The value for <i>".$this->FieldParams[$FieldName]["Label"]
1013  ."</i> does not appear to be a valid email address.";
1014  }
1015  }
1016  return NULL;
1017  }
1018 
1031  public function ValidateUrl($FieldName, $FieldValues)
1032  {
1033  if (!is_array($FieldValues)) { $FieldValues = array($FieldValues); }
1034  foreach ($FieldValues as $Value)
1035  {
1036  if (trim($Value) == "") { continue; }
1037  if (filter_var($Value, FILTER_VALIDATE_URL) === FALSE)
1038  {
1039  return "The value for <i>".$this->FieldParams[$FieldName]["Label"]
1040  ."</i> does not appear to be a valid URL.";
1041  }
1042  }
1043  return NULL;
1044  }
1045 
1059  public function ValidateHostName($FieldName, $FieldValues)
1060  {
1061  if (!is_array($FieldValues)) { $FieldValues = array($FieldValues); }
1062  foreach ($FieldValues as $Value)
1063  {
1064  if (trim($Value) == "") { continue; }
1065  if (gethostbyname($Value) === $Value)
1066  {
1067  return "The value for <i>".$this->FieldParams[$FieldName]["Label"]
1068  ."</i> does not appear to be a valid host name.";
1069  }
1070  }
1071  return NULL;
1072  }
1073 
1074 
1075  # ---- PRIVATE INTERFACE -------------------------------------------------
1076 
1077  protected $DeletedFiles = array();
1078  protected $DeletedImages = array();
1079  protected $ExtraValidationParams = array();
1080  protected $ExtraValues = array();
1081  protected $FieldParams;
1082  protected $FieldValues;
1083  protected $HiddenFields = array();
1084  protected $SettingChangeEventName = NULL;
1085  protected $SettingChangeEventParams = array();
1086 
1087  protected static $ErrorMessages = array();
1088 
1090  const NO_VALUE_FOR_FIELD = "NO VALUE";
1091 
1098  abstract protected function DisplayFormField($Name, $Value, $Params);
1099 
1107  protected function GetFormFieldName($FieldName, $IncludePrefix = TRUE)
1108  {
1109  return ($IncludePrefix ? "F_" : "")
1110  .($this->UniqueKey ? $this->UniqueKey."_" : "")
1111  .preg_replace("/[^a-zA-Z0-9]/", "", $FieldName);
1112  }
1113 
1117  protected function GetHiddenFieldsHtml()
1118  {
1119  $Html = "";
1120  if (count($this->HiddenFields))
1121  {
1122  foreach ($this->HiddenFields as $FieldName => $Value)
1123  {
1124  if (is_array($Value))
1125  {
1126  foreach ($Value as $EachValue)
1127  {
1128  $Html .= '<input type="hidden" name="'.$FieldName
1129  .'[]" value="'.htmlspecialchars($EachValue).'">';
1130  }
1131  }
1132  else
1133  {
1134  $Html .= '<input type="hidden" name="'.$FieldName
1135  .'[]" id="'.$FieldName.'" value="'
1136  .htmlspecialchars($Value).'">';
1137  }
1138  }
1139  }
1140  return $Html;
1141  }
1142 
1149  protected function ConvertItemIdsToNames($MField, $ItemIds)
1150  {
1151  # define $ExistsFn to determine if an ItemId exists and
1152  # $NameFn to get its name
1153  switch ($MField->Type())
1154  {
1157  $Factory = $MField->GetFactory();
1158 
1159  $ExistsFn = function($Id) use ($Factory) {
1160  return $Factory->ItemExists($Id);
1161  };
1162 
1163  $NameFn = function ($Id) use ($Factory) {
1164  return ($Factory->GetItem($Id)->Name());
1165  };
1166  break;
1167 
1169  $Factory = new CWUserFactory();
1170 
1171  $ExistsFn = function($Id) use ($Factory) {
1172  return $Factory->UserExists($Id);
1173  };
1174 
1175  $NameFn = function ($Id) {
1176  return (new CWUser($Id))->Name();
1177  };
1178  break;
1179 
1181  $ExistsFn = function($Id) {
1182  return Resource::ItemExists($Id);
1183  };
1184 
1185  $NameFn = function ($Id) {
1186  return (new Resource($Id))->GetMapped("Title");
1187  };
1188  break;
1189 
1190  default:
1191  throw new Exception(
1192  "Invalid field type for ItemId to ItemName conversion.");
1193  }
1194 
1195  # iterate over incoming values, extracting names for all the ones that exist
1196  $Value = [];
1197  foreach ($ItemIds as $Id)
1198  {
1199  if ($ExistsFn($Id))
1200  {
1201  $Value[$Id] = $NameFn($Id);
1202  }
1203  }
1204 
1205  return $Value;
1206  }
1207 }
static LogError($Msg, $Field=NULL)
Log error message for later display.
const FILESTAT_ZEROLENGTH
Definition: File.php:22
Base class (covering non-presentation elements) supplying a standard user interface for presenting an...
Definition: FormUI_Base.php:14
const FTYPE_NUMBER
Definition: FormUI_Base.php:23
const AI_UNKNOWNTYPE
Definition: Image.php:557
GetNewValuesFromForm()
Retrieve values set by form.
Set of parameters used to perform a search.
const FTYPE_SEARCHPARAMS
Definition: FormUI_Base.php:28
const FTYPE_OPTION
Definition: FormUI_Base.php:24
static Create($SourceFile, $DesiredFileName=NULL)
Create a new File object using an existing file.
Definition: File.php:34
const AI_OKAY
Definition: Image.php:552
GetHiddenFieldsHtml()
Get HTML for hidden form fields associated with form processing.
ValidateHostName($FieldName, $FieldValues)
Validate value as valid host name (i.e.
__construct($FieldParams, $FieldValues=array(), $UniqueKey=NULL)
Class constructor.
Definition: FormUI_Base.php:46
SetEventToSignalOnChange($EventName, $EventParams=array())
Set event to signal when retrieving values from form when settings have changed.
const FTYPE_IMAGE
Definition: FormUI_Base.php:21
ConvertItemIdsToNames($MField, $ItemIds)
Take an array of ItemIds and convert it to [ ItemId => ItemName ].
static GetCanonicalFieldIdentifier($Field, $SchemaId=NULL)
Retrieve canonical identifier for field.
const FTYPE_PARAGRAPH
Definition: FormUI_Base.php:25
ValidateFieldInput()
Validate field values on submitted form.
const FTYPE_QUICKSEARCH
Definition: FormUI_Base.php:32
const FTYPE_TEXT
Definition: FormUI_Base.php:29
User interface element for editing PrivilegeSets.
Set of privileges used to access resource information or other parts of the system.
const FTYPE_PRIVILEGES
Definition: FormUI_Base.php:27
static $ErrorMessages
const MDFTYPE_CONTROLLEDNAME
static LoadValue($Type, $Data)
Load value of requested type from supplied data.
CWIS-specific user factory class.
const NO_VALUE_FOR_FIELD
Marker used to indicate currently no value for field.
ValidateEmail($FieldName, $FieldValues)
Validate value as valid-appearing email address.
const FTYPE_PASSWORD
Definition: FormUI_Base.php:26
static ClearLoggedErrors($Field=FALSE)
Clear logged errors.
const FTYPE_FLAG
Definition: FormUI_Base.php:20
const FTYPE_URL
Definition: FormUI_Base.php:30
const FTYPE_USER
Definition: FormUI_Base.php:31
static FieldExistsInAnySchema($Field)
Determine if a Field exists in any schema.
Encapsulates a full-size, preview, and thumbnail image.
Definition: SPTImage.php:13
const FTYPE_FILE
Supported field types.
Definition: FormUI_Base.php:19
Object representing a locally-defined type of metadata field.
Represents a "resource" in CWIS.
Definition: Resource.php:13
static ErrorsLogged($Field=FALSE)
Report whether errors have been logged.
HandleUploads()
Handle image and file uploads.
GetFormFieldName($FieldName, $IncludePrefix=TRUE)
Get HTML form field name for specified field.
static GetLoggedErrors()
Get logged errors.
const FTYPE_HEADING
Supported field pseudo-types.
Definition: FormUI_Base.php:34
ValidateUrl($FieldName, $FieldValues)
Validate value as valid-appearing URL.
GetFieldValue($FieldName)
Get value for form field.
static DidValueChange($OldValue, $NewValue)
Determine if a new form field value is different from an old one.
const FTYPE_METADATAFIELD
Definition: FormUI_Base.php:22
DisplayFormTable($TableId=NULL, $TableStyle=NULL)
Display HTML table with settings parameters.
HandleDeletes()
Handle image and file deletions.
static ItemExists($Id)
Check whether an item exists with the specified ID.
Definition: Item.php:162
CWIS-specific user class.
Definition: CWUser.php:13
AddValidationParameters()
Add values to be passed to input validation functions, in addition to field name and value...
Class to create a user interface for editing SearchParameterSets.
DisplayFormField($Name, $Value, $Params)
Display HTML form field for specified field.
const AI_UNSUPPORTEDFORMAT
Definition: Image.php:558