3 # FILE: FormUI_Base.php 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/ 16 # ---- PUBLIC INTERFACE -------------------------------------------------- 49 # make sure parameters are legal and complete 50 $BooleanParams = array(
58 if (!isset($Params[
"Type"]))
60 $ErrMsgs[] =
"Type missing for form field ".$FieldName.
".";
62 elseif (($Params[
"Type"] == self::FTYPE_OPTION)
63 && !isset($Params[
"Options"]))
65 $ErrMsgs[] =
"Option values missing for form field ".$FieldName.
".";
67 elseif ($Params[
"Type"] == self::FTYPE_QUICKSEARCH)
69 if (!isset($Params[
"Field"]))
71 $ErrMsgs[]=
"Field to search missing for quicksearch form field " 76 $ErrMsgs[]=
"Specified search field for quicksearch form field " 77 .$FieldName.
" does not exist.";
86 $AllowedMFieldTypes = [
91 if (!in_array($MField->Type(), $AllowedMFieldTypes))
93 $ErrMsgs[]=
"Specified search field for quicksearch form field " 94 .$FieldName.
" is not a type that supports quicksearches.";
99 if (!isset($Params[
"Label"]))
101 $ErrMsgs[] =
"Label missing for form field ".$FieldName.
".";
103 if (isset($Params[
"ValidateFunction"])
104 && !is_callable($Params[
"ValidateFunction"]))
106 $ErrMsgs[] =
"Uncallable validation function for form field " 109 if (isset($Params[
"InsertIntoField"])
112 $ErrMsgs[] =
"Unknown insertion field (".$Params[
"InsertIntoField"]
113 .
") found for form field ".$FieldName.
".";
115 foreach ($BooleanParams as $ParamName)
117 if (!isset($Params[$ParamName]))
125 $ErrMsgString = implode(
" ", $ErrMsgs);
126 throw new InvalidArgumentException($ErrMsgString);
129 # save form parameters and values 132 $this->UniqueKey = $UniqueKey;
140 abstract public function DisplayFormTable($TableId = NULL, $TableStyle = NULL);
148 public static function LogError($Msg, $Field = NULL)
150 self::$ErrorMessages[$Field][] = $Msg;
161 return self::$ErrorMessages;
172 if ($Field === FALSE)
174 return count(self::$ErrorMessages) ? TRUE : FALSE;
178 return isset(self::$ErrorMessages[$Field]) ? TRUE : FALSE;
188 if ($Field === FALSE)
190 self::$ErrorMessages = array();
194 unset(self::$ErrorMessages[$Field]);
207 # retrieve field values 212 foreach ($this->FieldParams as $Name => $Params)
215 if ($Params[
"Type"] == self::FTYPE_HEADING) {
continue; }
217 # determine if field has a value set 218 switch ($Params[
"Type"])
220 case self::FTYPE_SEARCHPARAMS:
221 $IsEmpty = !$Values[$Name]->ParameterCount();
224 case self::FTYPE_PRIVILEGES:
225 $IsEmpty = !$Values[$Name]->ComparisonCount();
229 if (is_array($Values[$Name]))
231 $IsEmpty = !count($Values[$Name]);
235 $IsEmpty = !strlen(trim($Values[$Name]));
240 # if field has validation function 241 if (isset($Params[
"ValidateFunction"]))
243 # swap in our object if this is one of our methods 244 $VFunc = $Params[
"ValidateFunction"];
245 if (is_array($VFunc) && ($VFunc[0] ==
"FormUI"))
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)
256 throw new Exception(
"Calling validation function for" 257 .
" parameter \"".$Name.
"\" failed.");
260 # log any resulting error 261 if ($ErrMsg !== NULL)
263 self::LogError($ErrMsg, $Name);
268 # if field is required and empty 269 if ($IsEmpty && isset($Params[
"Required"]) && $Params[
"Required"])
271 # log error to indicate required value is missing 272 self::LogError(
"<i>".$Params[
"Label"].
"</i> is required.", $Name);
275 # else validate based on field type 278 switch ($Params[
"Type"])
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"])))
287 if (!isset($Params[
"MaxVal"]))
289 self::LogError(
"<i>".$Params[
"Label"].
"</i> must be " 290 .$Params[
"MinVal"].
" or greater.", $Name);
292 elseif (!isset($Params[
"MinVal"]))
294 self::LogError(
"<i>".$Params[
"Label"].
"</i> must be " 295 .$Params[
"MaxVal"].
" or less.", $Name);
299 self::LogError(
"<i>".$Params[
"Label"].
"</i> must be" 300 .
" in the range ".$Params[
"MinVal"]
301 .
" to ".$Params[
"MaxVal"].
".", $Name);
307 case self::FTYPE_URL:
308 # make sure URL entered looks valid 309 if (!$IsEmpty && (filter_var(
310 $Values[$Name], FILTER_VALIDATE_URL) === FALSE))
312 self::LogError(
"Value \"".$Values[$Name]
313 .
"\" does not appear to be a valid URL for <i>" 314 .$Params[
"Label"].
"</i>.", $Name);
319 case self::FTYPE_USER:
320 # make sure user name entered is valid 322 foreach ($Values[$Name] as $UId)
324 if (strlen($UId) && !$UFactory->UserExists($UId))
326 self::LogError(
"User ID \"".$UId
327 .
"\" not found for <i>" 328 .$Params[
"Label"].
"</i>.", $Name);
337 # report number of fields with invalid values found to caller 348 $this->ExtraValidationParams = func_get_args();
358 # for each form field 359 $NewSettings = array();
360 foreach ($this->FieldParams as $Name => $Params)
363 if ($Params[
"Type"] == self::FTYPE_HEADING) {
continue; }
365 # determine form field name (matches mechanism in HTML) 367 ($Params[
"Type"] != self::FTYPE_PRIVILEGES));
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;
376 # retrieve value based on configuration parameter type 377 switch ($Params[
"Type"])
379 case self::FTYPE_FLAG:
380 # if radio buttons were used 381 if (array_key_exists(
"OnLabel", $Params)
382 && array_key_exists(
"OffLabel", $Params))
384 if (isset($_POST[$FieldName]))
386 $NewValue = ($_POST[$FieldName] ==
"1") ? TRUE : FALSE;
388 # flag that the values changed if they did 389 $DidValueChange = self::DidValueChange(
390 $OldValue, $NewValue);
392 $NewSettings[$Name] = $NewValue;
395 # else checkbox was used 398 $NewValue = isset($_POST[$FieldName]) ? TRUE : FALSE;
400 # flag that the values changed if they did 401 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
403 $NewSettings[$Name] = $NewValue;
407 case self::FTYPE_OPTION:
408 $NewValue = GetArrayValue($_POST, $FieldName, array());
410 # flag that the values changed if they did 411 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
413 $NewSettings[$Name] = $NewValue;
416 case self::FTYPE_METADATAFIELD:
417 $NewValue = GetArrayValue($_POST, $FieldName, array());
418 if ($NewValue ==
"-1") { $NewValue = array(); }
420 # flag that the values changed if they did 421 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
423 $NewSettings[$Name] = $NewValue;
426 case self::FTYPE_PRIVILEGES:
427 $Schemas = GetArrayValue($Params,
"Schemas");
428 $MFields = GetArrayValue($Params,
"MetadataFields", array());
430 $NewValues = $PEditor->GetPrivilegeSetsFromForm();
431 $NewValue = $NewValues[$FieldName];
432 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
433 $NewSettings[$Name] = $NewValue;
436 case self::FTYPE_SEARCHPARAMS:
438 $NewValue = $SPEditor->GetValuesFromFormData();
439 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
440 $NewSettings[$Name] = $NewValue;
443 case self::FTYPE_FILE:
444 $NewSettings[$Name] = GetArrayValue(
445 $_POST, $FieldName.
"_ID", array());
446 foreach ($NewSettings[$Name] as $Index => $FileId)
448 if ($FileId == self::NO_VALUE_FOR_FIELD ||
449 in_array($FileId, $this->DeletedFiles))
451 unset($NewSettings[$Name][$Index]);
455 # if we have any files in ExtraValues after a HandleUploads, 457 $ExtraSettings = GetArrayValue(
458 $this->ExtraValues, $FieldName.
"_ID", []);
459 foreach ($ExtraSettings as $FileId)
461 if (!in_array($FileId, $NewSettings[$Name]) &&
462 !in_array($FileId, $this->DeletedFiles))
464 $NewSettings[$Name][]= $FileId;
470 case self::FTYPE_IMAGE:
471 $NewSettings[$Name] = GetArrayValue(
472 $_POST, $FieldName.
"_ID", array());
474 foreach ($NewSettings[$Name] as $Index => $ImageId)
476 if ($ImageId == self::NO_VALUE_FOR_FIELD ||
477 in_array($ImageId, $this->DeletedImages) )
479 unset($NewSettings[$Name][$Index]);
481 elseif (isset($_POST[$FieldName.
"_AltText_".$ImageId]))
484 $Image->AltText($_POST[$FieldName.
"_AltText_".$ImageId]);
488 # if we have any images in ExtraValues after a HandleUploads, 490 $ExtraSettings = GetArrayValue(
491 $this->ExtraValues, $FieldName.
"_ID", []);
492 foreach ($ExtraSettings as $ImageId)
494 if (!in_array($ImageId, $NewSettings[$Name]) &&
495 !in_array($ImageId, $this->DeletedImages))
497 $NewSettings[$Name][]= $ImageId;
498 $AltTextKey = $FieldName.
"_AltText_".$ImageId;
499 if (isset($this->ExtraValues[$AltTextKey]))
503 $this->ExtraValues[$AltTextKey]);
510 case self::FTYPE_USER:
511 if (isset($_POST[$FieldName]))
513 # filter blank entries out of input 514 # (these come up when a user was deleted from the list) 515 $NewValue = array_filter(
518 return strlen($v) > 0 ? TRUE : FALSE;
521 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
523 $NewSettings[$Name] = $NewValue;
528 if (isset($_POST[$FieldName]))
530 $NewValue = $_POST[$FieldName];
532 # flag that the values changed if they did 533 $DidValueChange = self::DidValueChange($OldValue, $NewValue);
535 $NewSettings[$Name] = $NewValue;
540 # if value changed and there is an event to signal for changes 541 if ($DidValueChange && $this->SettingChangeEventName)
543 # set info about changed value in event parameters if appropriate 545 foreach ($EventParams as $ParamName => $ParamValue)
550 $EventParams[$ParamName] = $Name;
554 $EventParams[$ParamName] = $OldValue;
558 $EventParams[$ParamName] = $NewValue;
564 $GLOBALS[
"AF"]->SignalEvent(
565 $this->SettingChangeEventName, $EventParams);
569 # return updated setting values to caller 580 # get base form field name 581 $FieldType = $this->FieldParams[$FieldName][
"Type"];
583 ($FieldType != self::FTYPE_PRIVILEGES));
585 # get fallback value for field (in case no value from form) 586 if (isset($this->FieldValues[$FieldName]))
588 $Value = $this->FieldValues[$FieldName];
590 elseif (isset($this->FieldParams[$FieldName][
"Value"]))
592 $Value = self::LoadValue($FieldType,
593 $this->FieldParams[$FieldName][
"Value"]);
595 elseif (isset($this->FieldParams[$FieldName][
"Default"]))
597 $Value = self::LoadValue($FieldType,
598 $this->FieldParams[$FieldName][
"Default"]);
602 $Value = self::LoadValue($FieldType, NULL);
607 case self::FTYPE_FILE:
608 case self::FTYPE_IMAGE:
609 # get name of image ID form field 610 $FileIdFieldName = $FormFieldName.
"_ID";
612 # use an updated value for this field if available 613 if (isset($this->HiddenFields[$FileIdFieldName]))
615 $Value = $this->HiddenFields[$FileIdFieldName];
617 # else use a value from form if available 618 elseif (isset($_POST[$FileIdFieldName]))
620 $Value = $_POST[$FileIdFieldName];
623 # add in any previously-set extra values 624 if (isset($this->ExtraValues[$FileIdFieldName])
625 && count($this->ExtraValues[$FileIdFieldName]))
627 if (!is_array($Value))
629 $Value = array($Value);
631 $Value = array_merge($Value,
632 $this->ExtraValues[$FileIdFieldName]);
636 case self::FTYPE_SEARCHPARAMS:
637 # use incoming form value if available 638 if (isset($_POST[$FormFieldName]))
640 # use incoming form value 642 $Value = $SPEditor->GetValuesFromFormData();
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());
653 $PSet = $PEditor->GetPrivilegeSetFromForm($FormFieldName);
656 # use incoming form value 661 case self::FTYPE_QUICKSEARCH:
663 $this->FieldParams[$FieldName][
"Field"]);
665 # use incoming values if available 666 if (isset($_POST[$FormFieldName]))
669 $MField, $_POST[$FormFieldName]);
674 # use incoming form value if available 675 if (isset($_POST[$FormFieldName]))
677 # use incoming form value 678 $Value = $_POST[$FormFieldName];
683 # return value found to caller 692 # for each form field 693 foreach ($this->FieldParams as $FieldName =>
$FieldParams)
695 # move on to next field if this field does not allow uploads 702 # move on to next field if this field does not have an uploaded file 704 if (!isset($_FILES[$FormFieldName][
"name"]))
708 $UploadedFileName = $_FILES[$FormFieldName][
"name"];
709 if (!strlen($UploadedFileName))
714 # create temp copy of file with correct name 715 $TmpFile =
"tmp/".$UploadedFileName;
716 $CopyResult = copy($_FILES[$FormFieldName][
"tmp_name"], $TmpFile);
720 case self::FTYPE_FILE:
721 # create new file object from uploaded file 724 # check for errors during file object creation 725 if (!is_object($File))
731 .$UploadedFileName.
" was empty" 732 .
" (zero length).", $FieldName);
736 $this->
LogError(
"Error encountered with" 738 .$UploadedFileName.
" (" 739 .$File.
").", $FieldName);
746 # add file ID to extra values 747 $this->ExtraValues[$FormFieldName.
"_ID"][] = $File->Id();
751 case self::FTYPE_IMAGE:
752 # create new image object from uploaded file 761 # check for errors during image object creation 762 if ($Image->Status() !=
AI_OKAY)
764 switch ($Image->Status())
767 $this->
LogError(
"Unknown format for image file " 768 .$UploadedFileName.
".", $FieldName);
772 $this->
LogError(
"Unsupported format for image file " 773 .$UploadedFileName.
" (" 774 .$Image->Format().
").", $FieldName);
778 $this->
LogError(
"Error encountered when" 779 .
" processing uploaded image file " 780 .$UploadedFileName.
".", $FieldName);
787 # set image object alternate text 788 $Image->AltText($_POST[$FormFieldName.
"_AltText_NEW"]);
790 # add image ID to extra values 791 $this->ExtraValues[$FormFieldName.
"_ID"][] = $Image->Id();
803 # if image ID to delete was supplied 805 $IncomingValue = GetFormValue($DeleteFieldName);
806 if (is_numeric($IncomingValue)
807 || (is_array($IncomingValue) && count($IncomingValue)))
809 # retrieve ID of image 810 $ImageId = is_array($IncomingValue)
811 ? array_shift($IncomingValue)
814 # add ID to deleted images list 815 if (is_numeric($ImageId))
817 $this->DeletedImages[] = $ImageId;
821 # if file ID to delete was supplied 823 $IncomingValue = GetFormValue($DeleteFieldName);
824 if (is_numeric($IncomingValue)
825 || (is_array($IncomingValue) && count($IncomingValue)))
827 # retrieve ID of file 828 $FileId = is_array($IncomingValue)
829 ? array_shift($IncomingValue)
832 # add ID to deleted files list 833 if (is_numeric($FileId))
835 $this->DeletedFiles[] = $FileId;
853 $this->SettingChangeEventName = $EventName;
854 $this->SettingChangeEventParams = $EventParams;
865 # didn't change if they are identical 866 if ($OldValue === $NewValue)
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 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)))
884 # if they both appear to be numbers and are equal 885 if (is_numeric($OldValue) && is_numeric($NewValue) && $OldValue == $NewValue)
891 if (($OldValue === TRUE && ($NewValue === 1 || $NewValue ===
"1"))
892 || ($NewValue === TRUE && ($OldValue === 1 || $OldValue ===
"1")))
898 if (($OldValue === FALSE && ($NewValue === 0 || $NewValue ===
"0"))
899 || ($NewValue === FALSE && ($OldValue === 0 || $OldValue ===
"0")))
905 if (is_array($OldValue) && is_array($NewValue))
907 # they certainly changed if the counts are different 908 if (count($OldValue) != count($NewValue))
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")))
917 foreach ($OldValue as $Key => $Value)
919 # it changed if the keys don't match 920 if (!array_key_exists($Key, $NewValue))
925 # the arrays changed if a value changed 926 if (self::DidValueChange($Value, $NewValue[$Key]))
933 # sequential values don't have to have the same keys, just the same 937 # sort them so all the values match up if they're equal 941 foreach ($OldValue as $Key => $Value)
943 # the arrays changed if a value changed 944 if (self::DidValueChange($Value, $NewValue[$Key]))
951 # the arrays are equal 969 case self::FTYPE_FILE:
970 case self::FTYPE_IMAGE:
971 $Value = ($Data === NULL) ? array() : $Data;
974 case self::FTYPE_PRIVILEGES:
979 case self::FTYPE_SEARCHPARAMS:
1009 if (trim($Value) ==
"") {
continue; }
1010 if (filter_var($Value, FILTER_VALIDATE_EMAIL) === FALSE)
1012 return "The value for <i>".$this->FieldParams[$FieldName][
"Label"]
1013 .
"</i> does not appear to be a valid email address.";
1036 if (trim($Value) ==
"") {
continue; }
1037 if (filter_var($Value, FILTER_VALIDATE_URL) === FALSE)
1039 return "The value for <i>".$this->FieldParams[$FieldName][
"Label"]
1040 .
"</i> does not appear to be a valid URL.";
1064 if (trim($Value) ==
"") {
continue; }
1065 if (gethostbyname($Value) === $Value)
1067 return "The value for <i>".$this->FieldParams[$FieldName][
"Label"]
1068 .
"</i> does not appear to be a valid host name.";
1075 # ---- PRIVATE INTERFACE ------------------------------------------------- 1109 return ($IncludePrefix ?
"F_" :
"")
1110 .($this->UniqueKey ? $this->UniqueKey.
"_" :
"")
1111 .preg_replace(
"/[^a-zA-Z0-9]/",
"", $FieldName);
1120 if (count($this->HiddenFields))
1122 foreach ($this->HiddenFields as $FieldName => $Value)
1124 if (is_array($Value))
1126 foreach ($Value as $EachValue)
1128 $Html .=
'<input type="hidden" name="'.$FieldName
1129 .
'[]" value="'.htmlspecialchars($EachValue).
'">';
1134 $Html .=
'<input type="hidden" name="'.$FieldName
1135 .
'[]" id="'.$FieldName.
'" value="' 1136 .htmlspecialchars($Value).
'">';
1151 # define $ExistsFn to determine if an ItemId exists and 1152 # $NameFn to get its name 1153 switch ($MField->Type())
1157 $Factory = $MField->GetFactory();
1159 $ExistsFn =
function($Id) use ($Factory) {
1160 return $Factory->ItemExists($Id);
1163 $NameFn =
function ($Id) use ($Factory) {
1164 return ($Factory->GetItem($Id)->Name());
1171 $ExistsFn =
function($Id) use ($Factory) {
1172 return $Factory->UserExists($Id);
1175 $NameFn =
function ($Id) {
1176 return (
new CWUser($Id))->Name();
1181 $ExistsFn =
function($Id) {
1185 $NameFn =
function ($Id) {
1186 return (
new Resource($Id))->GetMapped(
"Title");
1191 throw new Exception(
1192 "Invalid field type for ItemId to ItemName conversion.");
1195 # iterate over incoming values, extracting names for all the ones that exist 1197 foreach ($ItemIds as $Id)
1201 $Value[$Id] = $NameFn($Id);
const FILESTAT_ZEROLENGTH
Set of parameters used to perform a search.
static Create($SourceFile, $DesiredFileName=NULL)
Create a new File object using an existing file.
User interface element for editing PrivilegeSets.
Set of privileges used to access resource information or other parts of the system.
CWIS-specific user factory class.
Encapsulates a full-size, preview, and thumbnail image.
Represents a "resource" in CWIS.
static ItemExists($Id)
Check whether an item exists with the specified ID.
CWIS-specific user class.
Class to create a user interface for editing SearchParameterSets.
const AI_UNSUPPORTEDFORMAT