Replace generic alert, form and table templates with new Bootstrap equivalents

This commit is contained in:
2023-03-11 13:20:59 +01:00
parent daf6b6b264
commit f9eefe7b41
18 changed files with 681 additions and 573 deletions

View File

@@ -3,7 +3,8 @@
* Form.php
* Contains key class Form.
*
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
* Global Data Lab code (C) Radboud University Nijmegen
* Programming (C) Aaron van Geffen, 2015-2022
*****************************************************************************/
class Form
@@ -12,9 +13,11 @@ class Form
public $request_url;
public $content_above;
public $content_below;
private $fields;
private $data;
private $missing;
private $fields = [];
private $data = [];
private $missing = [];
private $submit_caption;
private $trim_inputs;
// NOTE: this class does not verify the completeness of form options.
public function __construct($options)
@@ -24,9 +27,42 @@ class Form
$this->fields = !empty($options['fields']) ? $options['fields'] : [];
$this->content_below = !empty($options['content_below']) ? $options['content_below'] : null;
$this->content_above = !empty($options['content_above']) ? $options['content_above'] : null;
$this->submit_caption = !empty($options['submit_caption']) ? $options['submit_caption'] : 'Save information';
$this->trim_inputs = !empty($options['trim_inputs']);
}
public function verify($post)
public function getFields()
{
return $this->fields;
}
public function getData()
{
return $this->data;
}
public function getSubmitButtonCaption()
{
return $this->submit_caption;
}
public function getMissing()
{
return $this->missing;
}
public function setData($data)
{
$this->verify($data, true);
$this->missing = [];
}
public function setFieldAsMissing($field)
{
$this->missing[] = $field;
}
public function verify($post, $initalisation = false)
{
$this->data = [];
$this->missing = [];
@@ -41,30 +77,43 @@ class Form
}
// No data present at all for this field?
if ((!isset($post[$field_id]) || $post[$field_id] == '') && empty($field['is_optional']))
if ((!isset($post[$field_id]) || $post[$field_id] == '') &&
$field['type'] !== 'captcha')
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
if (empty($field['is_optional']))
$this->missing[] = $field_id;
if ($field['type'] === 'select' && !empty($field['multiple']))
$this->data[$field_id] = [];
else
$this->data[$field_id] = '';
continue;
}
// Verify data for all fields
// Should we trim this?
if ($this->trim_inputs && $field['type'] !== 'captcha' && empty($field['multiple']))
$post[$field_id] = trim($post[$field_id]);
// Using a custom validation function?
if (isset($field['validate']) && is_callable($field['validate']))
{
// Validation functions can clean up the data if passed by reference
$this->data[$field_id] = $post[$field_id];
// Evaluate validation functions as boolean to see if data is missing
if (!$field['validate']($post[$field_id]))
$this->missing[] = $field_id;
continue;
}
// Verify data by field type
switch ($field['type'])
{
case 'select':
case 'radio':
// Skip validation? Dangerous territory!
if (isset($field['verify_options']) && $field['verify_options'] === false)
$this->data[$field_id] = $post[$field_id];
// Check whether selected option is valid.
elseif (isset($post[$field_id]) && !isset($field['options'][$post[$field_id]]))
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
continue 2;
}
else
$this->data[$field_id] = $post[$field_id];
$this->validateSelect($field_id, $field, $post);
break;
case 'checkbox':
@@ -73,61 +122,22 @@ class Form
break;
case 'color':
// Colors are stored as a string of length 3 or 6 (hex)
if (!isset($post[$field_id]) || (strlen($post[$field_id]) != 3 && strlen($post[$field_id]) != 6))
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
continue 2;
}
else
$this->data[$field_id] = $post[$field_id];
$this->validateColor($field_id, $field, $post);
break;
case 'file':
// Needs to be verified elsewhere!
// Asset needs to be processed out of POST! This is just a filename.
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
break;
case 'numeric':
$data = isset($post[$field_id]) ? $post[$field_id] : '';
// Do we need to check bounds?
if (isset($field['min_value']) && is_numeric($data))
{
if (is_float($field['min_value']) && (float) $data < $field['min_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0.0;
}
elseif (is_int($field['min_value']) && (int) $data < $field['min_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
}
else
$this->data[$field_id] = $data;
}
elseif (isset($field['max_value']) && is_numeric($data))
{
if (is_float($field['max_value']) && (float) $data > $field['max_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0.0;
}
elseif (is_int($field['max_value']) && (int) $data > $field['max_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
}
else
$this->data[$field_id] = $data;
}
// Does it look numeric?
elseif (is_numeric($data))
{
$this->data[$field_id] = $data;
}
// Let's consider it missing, then.
else
$this->validateNumeric($field_id, $field, $post);
break;
case 'captcha':
if (isset($_POST['g-recaptcha-response']) && !$initalisation)
$this->validateCaptcha($field_id);
elseif (!$initalisation)
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
@@ -137,29 +147,200 @@ class Form
case 'text':
case 'textarea':
default:
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
$this->validateText($field_id, $field, $post);
}
}
}
public function setData($data)
private function validateCaptcha($field_id)
{
$this->verify($data);
$this->missing = [];
$postdata = http_build_query([
'secret' => RECAPTCHA_API_SECRET,
'response' => $_POST['g-recaptcha-response'],
]);
$opts = [
'http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata,
]
];
$context = stream_context_create($opts);
$result = file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context);
$check = json_decode($result);
if ($check->success)
{
$this->data[$field_id] = 1;
}
else
{
$this->data[$field_id] = 0;
$this->missing[] = $field_id;
}
}
public function getFields()
private function validateColor($field_id, array $field, array $post)
{
return $this->fields;
// Colors are stored as a string of length 3 or 6 (hex)
if (!isset($post[$field_id]) || (strlen($post[$field_id]) != 3 && strlen($post[$field_id]) != 6))
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
}
else
$this->data[$field_id] = $post[$field_id];
}
public function getData()
private function validateNumeric($field_id, array $field, array $post)
{
return $this->data;
$data = isset($post[$field_id]) ? $post[$field_id] : '';
// Sanity check: does this even look numeric?
if (!is_numeric($data))
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
return;
}
// Do we need to a minimum bound?
if (isset($field['min_value']))
{
if (is_float($field['min_value']) && (float) $data < $field['min_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0.0;
}
elseif (is_int($field['min_value']) && (int) $data < $field['min_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
}
}
// What about a maximum bound?
if (isset($field['max_value']))
{
if (is_float($field['max_value']) && (float) $data > $field['max_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0.0;
}
elseif (is_int($field['max_value']) && (int) $data > $field['max_value'])
{
$this->missing[] = $field_id;
$this->data[$field_id] = 0;
}
}
$this->data[$field_id] = $data;
}
public function getMissing()
private function validateSelect($field_id, array $field, array $post)
{
return $this->missing;
// Skip validation? Dangerous territory!
if (isset($field['verify_options']) && $field['verify_options'] === false)
{
$this->data[$field_id] = $post[$field_id];
return;
}
// Check whether selected option is valid.
if (($field['type'] !== 'select' || empty($field['multiple'])) && empty($field['has_groups']))
{
if (isset($post[$field_id]) && !isset($field['options'][$post[$field_id]]))
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
return;
}
else
$this->data[$field_id] = $post[$field_id];
}
// Multiple selections involve a bit more work.
elseif (!empty($field['multiple']) && empty($field['has_groups']))
{
$this->data[$field_id] = [];
if (!is_array($post[$field_id]))
{
if (isset($field['options'][$post[$field_id]]))
$this->data[$field_id][] = $post[$field_id];
else
$this->missing[] = $field_id;
return;
}
foreach ($post[$field_id] as $option)
{
if (isset($field['options'][$option]))
$this->data[$field_id][] = $option;
}
if (empty($this->data[$field_id]))
$this->missing[] = $field_id;
}
// Any optgroups involved?
elseif (!empty($field['has_groups']))
{
if (!isset($post[$field_id]))
{
$this->missing[] = $field_id;
$this->data[$field_id] = '';
return;
}
// Expensive: iterate over all groups until the value selected has been found.
foreach ($field['options'] as $label => $options)
{
if (is_array($options))
{
// Consider each of the options as a valid a value.
foreach ($options as $value => $label)
{
if ($post[$field_id] === $value)
{
$this->data[$field_id] = $options;
return;
}
}
}
else
{
// This is an ungrouped value in disguise! Treat it as such.
if ($post[$field_id] === $options)
{
$this->data[$field_id] = $options;
return;
}
else
continue;
}
}
// If we've reached this point, we'll consider the data invalid.
$this->missing[] = $field_id;
$this->data[$field_id] = '';
}
else
{
throw new UnexpectedValueException('Unexpected field configuration in validateSelect!');
}
}
private function validateText($field_id, array $field, array $post)
{
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
// Trim leading and trailing whitespace?
if (!empty($field['trim']))
$this->data[$field_id] = trim($this->data[$field_id]);
// Is there a length limit to enforce?
if (isset($field['maxlength']) && strlen($post[$field_id]) > $field['maxlength']) {
$post[$field_id] = substr($post[$field_id], 0, $field['maxlength']);
}
}
}